Skip to content

Commit dfb4a07

Browse files
feat: add functions.generate util (#6487)
* feat: support return values from plugin steps * feat: log generated functions * feat: log generated functions * chore: add test * refactor: add `functions.generate` util * chore: add debug log * chore: fix Windows test * refactor: simplify `generate` util
1 parent 5f0728b commit dfb4a07

File tree

18 files changed

+197
-17
lines changed

18 files changed

+197
-17
lines changed

packages/build/src/log/messages/core_steps.js

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,20 +67,40 @@ export const logFunctionsToBundle = function ({
6767
internalFunctions,
6868
internalFunctionsSrc,
6969
frameworkFunctions,
70+
generatedFunctions,
7071
type = 'Functions',
7172
}) {
73+
let needsSpace = false
74+
75+
if (generatedFunctions.length !== 0) {
76+
for (const id in generatedFunctions) {
77+
const { displayName, generatorType, functionNames } = generatedFunctions[id]
78+
79+
if (needsSpace) log(logs, '')
80+
81+
log(logs, `Packaging ${type} generated by ${THEME.highlightWords(displayName)} ${generatorType}:`)
82+
logArray(logs, functionNames, { indent: false })
83+
84+
needsSpace = true
85+
}
86+
}
87+
7288
if (internalFunctions.length !== 0) {
89+
if (needsSpace) log(logs, '')
90+
7391
log(logs, `Packaging ${type} from ${THEME.highlightWords(internalFunctionsSrc)} directory:`)
7492
logArray(logs, internalFunctions, { indent: false })
93+
94+
needsSpace = true
7595
}
7696

7797
if (frameworkFunctions.length !== 0) {
78-
if (internalFunctions.length !== 0) {
79-
log(logs, '')
80-
}
98+
if (needsSpace) log(logs, '')
8199

82100
log(logs, `Packaging ${type} generated by your framework:`)
83101
logArray(logs, frameworkFunctions, { indent: false })
102+
103+
needsSpace = true
84104
}
85105

86106
if (!userFunctionsSrcExists) {
@@ -93,9 +113,7 @@ export const logFunctionsToBundle = function ({
93113
return
94114
}
95115

96-
if (internalFunctions.length !== 0 || frameworkFunctions.length !== 0) {
97-
log(logs, '')
98-
}
116+
if (needsSpace) log(logs, '')
99117

100118
log(logs, `Packaging ${type} from ${THEME.highlightWords(userFunctionsSrc)} directory:`)
101119
logArray(logs, userFunctions, { indent: false })

packages/build/src/plugins/child/run.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ export const run = async function (
1919
return context.with(getGlobalContext(), async () => {
2020
const method = methods[event]
2121
const runState = {}
22+
const generatedFunctions = []
2223
const systemLog = getSystemLog()
23-
const utils = getUtils({ event, constants, runState })
24+
const utils = getUtils({ event, constants, generatedFunctions, runState })
2425
const netlifyConfigCopy = cloneNetlifyConfig(netlifyConfig)
2526
const runOptions = {
2627
utils,
@@ -43,6 +44,7 @@ export const run = async function (
4344
const newEnvChanges = getNewEnvChanges(envBefore, netlifyConfig, netlifyConfigCopy)
4445

4546
const configMutations = getConfigMutations(netlifyConfig, netlifyConfigCopy, event)
46-
return { ...runState, newEnvChanges, configMutations }
47+
const returnValue = generatedFunctions.length ? { generatedFunctions } : undefined
48+
return { ...runState, newEnvChanges, configMutations, returnValue }
4749
})
4850
}

packages/build/src/plugins/child/utils.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ import { show } from './status.js'
1313
export const getUtils = function ({
1414
event,
1515
constants: { FUNCTIONS_SRC, INTERNAL_FUNCTIONS_SRC, CACHE_DIR },
16+
generatedFunctions = [],
1617
runState,
1718
}) {
1819
run.command = runCommand
1920

2021
const build = getBuildUtils(event)
2122
const cache = getCacheUtils(CACHE_DIR)
22-
const functions = getFunctionsUtils(FUNCTIONS_SRC, INTERNAL_FUNCTIONS_SRC)
23+
const functions = getFunctionsUtils(FUNCTIONS_SRC, INTERNAL_FUNCTIONS_SRC, generatedFunctions)
2324
const status = getStatusUtils(runState)
2425
const utils = { build, cache, run, functions, status }
2526
addLazyProp(utils, 'git', () => getGitUtils())
@@ -42,12 +43,15 @@ const getCacheUtils = function (CACHE_DIR) {
4243
return cacheBindOpts({ cacheDir: CACHE_DIR })
4344
}
4445

45-
const getFunctionsUtils = function (FUNCTIONS_SRC, INTERNAL_FUNCTIONS_SRC) {
46+
const getFunctionsUtils = function (FUNCTIONS_SRC, INTERNAL_FUNCTIONS_SRC, generatedFunctions) {
4647
const functionsDirectories = [INTERNAL_FUNCTIONS_SRC, FUNCTIONS_SRC].filter(Boolean)
4748
const add = (src) => functionsAdd(src, INTERNAL_FUNCTIONS_SRC, { fail: failBuild })
4849
const list = functionsList.bind(null, functionsDirectories, { fail: failBuild })
4950
const listAll = functionsListAll.bind(null, functionsDirectories, { fail: failBuild })
50-
return { add, list, listAll }
51+
const generate = (functionPath) => generatedFunctions.push({ path: functionPath })
52+
53+
/** @type import('../../types/options/netlify_plugin_functions_util.js').NetlifyPluginFunctionsUtil */
54+
return { add, list, listAll, generate }
5155
}
5256

5357
const getStatusUtils = function (runState) {

packages/build/src/plugins_core/edge_functions/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ const logFunctions = async ({
206206
internalFunctionsSrc: internalSrcDirectory,
207207
frameworkFunctions: frameworkFunctions.map(({ name }) => name),
208208
type: 'Edge Functions',
209+
generatedFunctions: [],
209210
})
210211
}
211212

packages/build/src/plugins_core/functions/index.ts

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { resolve } from 'path'
1+
import { basename, resolve } from 'path'
22

33
import { type NodeBundlerName, RUNTIME, zipFunctions, type FunctionResult } from '@netlify/zip-it-and-ship-it'
44
import { pathExists } from 'path-exists'
55

66
import { addErrorInfo } from '../../error/info.js'
77
import { log } from '../../log/logger.js'
8+
import type { ReturnValue } from '../../types/step.js'
89
import { logBundleResults, logFunctionsNonExistingDir, logFunctionsToBundle } from '../../log/messages/core_steps.js'
910
import { FRAMEWORKS_API_FUNCTIONS_ENDPOINT } from '../../utils/frameworks_api.js'
1011

@@ -68,6 +69,7 @@ const zipFunctionsAndLogResults = async ({
6869
functionsConfig,
6970
functionsDist,
7071
functionsSrc,
72+
generatedFunctions,
7173
frameworkFunctionsSrc,
7274
internalFunctionsSrc,
7375
isRunningLocally,
@@ -94,7 +96,9 @@ const zipFunctionsAndLogResults = async ({
9496
// Printing an empty line before bundling output.
9597
log(logs, '')
9698

97-
const sourceDirectories = [internalFunctionsSrc, frameworkFunctionsSrc, functionsSrc].filter(Boolean)
99+
const sourceDirectories = [internalFunctionsSrc, frameworkFunctionsSrc, functionsSrc, ...generatedFunctions].filter(
100+
Boolean,
101+
)
98102
const results = await zipItAndShipIt.zipFunctions(sourceDirectories, functionsDist, zisiParameters)
99103

100104
validateCustomRoutes(results)
@@ -128,6 +132,7 @@ const coreStep = async function ({
128132
repositoryRoot,
129133
userNodeVersion,
130134
systemLog,
135+
returnValues,
131136
}) {
132137
const functionsSrc = relativeFunctionsSrc === undefined ? undefined : resolve(buildDir, relativeFunctionsSrc)
133138
const functionsDist = resolve(buildDir, relativeFunctionsDist)
@@ -154,6 +159,8 @@ const coreStep = async function ({
154159
}
155160
}
156161

162+
const generatedFunctions = getGeneratedFunctionPaths(returnValues)
163+
157164
logFunctionsToBundle({
158165
logs,
159166
userFunctions,
@@ -162,9 +169,15 @@ const coreStep = async function ({
162169
internalFunctions,
163170
internalFunctionsSrc: relativeInternalFunctionsSrc,
164171
frameworkFunctions,
172+
generatedFunctions: getGeneratedFunctionsByGenerator(returnValues),
165173
})
166174

167-
if (userFunctions.length === 0 && internalFunctions.length === 0 && frameworkFunctions.length === 0) {
175+
if (
176+
userFunctions.length === 0 &&
177+
internalFunctions.length === 0 &&
178+
frameworkFunctions.length === 0 &&
179+
generatedFunctions.length === 0
180+
) {
168181
return {}
169182
}
170183

@@ -183,6 +196,7 @@ const coreStep = async function ({
183196
repositoryRoot,
184197
userNodeVersion,
185198
systemLog,
199+
generatedFunctions,
186200
})
187201

188202
const metrics = getMetrics(internalFunctions, userFunctions)
@@ -203,6 +217,7 @@ const hasFunctionsDirectories = async function ({
203217
buildDir,
204218
constants: { INTERNAL_FUNCTIONS_SRC, FUNCTIONS_SRC },
205219
packagePath,
220+
returnValues,
206221
}) {
207222
const hasFunctionsSrc = FUNCTIONS_SRC !== undefined && FUNCTIONS_SRC !== ''
208223

@@ -218,7 +233,49 @@ const hasFunctionsDirectories = async function ({
218233

219234
const frameworkFunctionsSrc = resolve(buildDir, packagePath || '', FRAMEWORKS_API_FUNCTIONS_ENDPOINT)
220235

221-
return await pathExists(frameworkFunctionsSrc)
236+
if (await pathExists(frameworkFunctionsSrc)) {
237+
return true
238+
}
239+
240+
// We must run the core step if the return value of any of the previous steps
241+
// has declared generated functions.
242+
for (const id in returnValues) {
243+
if (returnValues[id].generatedFunctions && returnValues[id].generatedFunctions.length !== 0) {
244+
return true
245+
}
246+
}
247+
248+
return false
249+
}
250+
251+
// Takes a list of return values and produces an array with the paths of all
252+
// generated functions.
253+
const getGeneratedFunctionPaths = (returnValues: Record<string, ReturnValue>) => {
254+
return Object.values(returnValues).flatMap(
255+
(returnValue) => returnValue.generatedFunctions?.map((func) => func.path) || [],
256+
)
257+
}
258+
259+
// Takes a list of return values and produces an object with the names of the
260+
// generated functions for each generator. This is used for printing logs only.
261+
const getGeneratedFunctionsByGenerator = (returnValues: Record<string, ReturnValue>) => {
262+
const result: Record<string, { displayName: string; generatorType: string; functionNames: string[] }> = {}
263+
264+
for (const id in returnValues) {
265+
const { displayName, generatedFunctions, generatorType } = returnValues[id]
266+
267+
if (!generatedFunctions || generatedFunctions.length === 0) {
268+
continue
269+
}
270+
271+
result[id] = {
272+
displayName,
273+
generatorType,
274+
functionNames: generatedFunctions.map((func) => basename(func.path)),
275+
}
276+
}
277+
278+
return result
222279
}
223280

224281
export const bundleFunctions = {

packages/build/src/steps/core_step.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export const fireCoreStep = async function ({
4242
deployId,
4343
outputFlusher,
4444
api,
45+
returnValues,
4546
}) {
4647
const logsA = outputFlusher ? addOutputFlusher(logs, outputFlusher) : logs
4748

@@ -83,6 +84,7 @@ export const fireCoreStep = async function ({
8384
enhancedSecretScan,
8485
edgeFunctionsBootstrapURL,
8586
deployId,
87+
returnValues,
8688
})
8789
const {
8890
netlifyConfig: netlifyConfigA,

packages/build/src/steps/plugin.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export const firePluginStep = async function ({
5454
const {
5555
newEnvChanges,
5656
configMutations: newConfigMutations,
57+
returnValue,
5758
status,
5859
} = await callChild({
5960
childProcess,
@@ -100,6 +101,7 @@ export const firePluginStep = async function ({
100101
headersPath: headersPathA,
101102
redirectsPath: redirectsPathA,
102103
newStatus,
104+
returnValue,
103105
}
104106
} catch (newError) {
105107
const errorType = getPluginErrorType(newError, loadedFrom, packageName)

packages/build/src/steps/return.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export const getStepReturn = function ({
2929
systemLog,
3030
quiet,
3131
metrics,
32+
returnValue,
3233
}) {
3334
if (newError !== undefined) {
3435
return handleStepError({
@@ -51,5 +52,15 @@ export const getStepReturn = function ({
5152
logTimer(logs, durationNs, timerName, systemLog, outputFlusher)
5253
}
5354

54-
return { newEnvChanges, netlifyConfig, configMutations, headersPath, redirectsPath, newStatus, timers, metrics }
55+
return {
56+
newEnvChanges,
57+
netlifyConfig,
58+
configMutations,
59+
headersPath,
60+
redirectsPath,
61+
newStatus,
62+
timers,
63+
metrics,
64+
returnValue,
65+
}
5566
}

packages/build/src/steps/run_step.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export const runStep = async function ({
7171
enhancedSecretScan,
7272
edgeFunctionsBootstrapURL,
7373
extensionMetadata,
74+
returnValues,
7475
}) {
7576
// Add relevant attributes to the upcoming span context
7677
const attributes: StepExecutionAttributes = {
@@ -111,6 +112,7 @@ export const runStep = async function ({
111112
explicitSecretKeys,
112113
enhancedSecretScan,
113114
deployId,
115+
returnValues,
114116
featureFlags,
115117
})
116118
span.setAttribute('build.execution.step.should_run', shouldRun)
@@ -140,6 +142,7 @@ export const runStep = async function ({
140142
timers: timersA,
141143
durationNs,
142144
metrics,
145+
returnValue,
143146
} = await fireStep({
144147
extensionMetadata,
145148
defaultConfig,
@@ -188,6 +191,7 @@ export const runStep = async function ({
188191
edgeFunctionsBootstrapURL,
189192
deployId,
190193
api,
194+
returnValues,
191195
})
192196

193197
const newValues = await getStepReturn({
@@ -216,6 +220,7 @@ export const runStep = async function ({
216220
systemLog,
217221
quiet: quiet || coreStepQuiet,
218222
metrics,
223+
returnValue,
219224
})
220225

221226
span.end()
@@ -269,6 +274,7 @@ const shouldRunStep = async function ({
269274
explicitSecretKeys,
270275
enhancedSecretScan,
271276
deployId,
277+
returnValues,
272278
featureFlags = {},
273279
}) {
274280
if (
@@ -284,6 +290,7 @@ const shouldRunStep = async function ({
284290
explicitSecretKeys,
285291
enhancedSecretScan,
286292
deployId,
293+
returnValues,
287294
featureFlags,
288295
})))
289296
) {
@@ -354,6 +361,7 @@ const tFireStep = function ({
354361
deployId,
355362
extensionMetadata,
356363
api,
364+
returnValues,
357365
}) {
358366
if (coreStep !== undefined) {
359367
return fireCoreStep({
@@ -393,6 +401,7 @@ const tFireStep = function ({
393401
edgeFunctionsBootstrapURL,
394402
deployId,
395403
api,
404+
returnValues,
396405
})
397406
}
398407

0 commit comments

Comments
 (0)