-
Notifications
You must be signed in to change notification settings - Fork 393
/
Copy pathindex.ts
148 lines (125 loc) · 5.38 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import { createConnection } from 'net'
import { dirname } from 'path'
import { pathToFileURL } from 'url'
import { Worker } from 'worker_threads'
import lambdaLocal from 'lambda-local'
import { BLOBS_CONTEXT_VARIABLE } from '../../../blobs/blobs.js'
import type NetlifyFunction from '../../netlify-function.js'
import detectNetlifyLambdaBuilder from './builders/netlify-lambda.js'
import detectZisiBuilder, { parseFunctionForMetadata } from './builders/zisi.js'
import { SECONDS_TO_MILLISECONDS } from './constants.js'
import { $TSFixMe } from '../../../../commands/types.js'
export const name = 'js'
// @ts-expect-error TS(7034) FIXME: Variable 'netlifyLambdaDetectorCache' implicitly h... Remove this comment to see the full error message
let netlifyLambdaDetectorCache
lambdaLocal.getLogger().level = 'alert'
// The netlify-lambda builder can't be enabled or disabled on a per-function
// basis and its detection mechanism is also quite expensive, so we detect
// it once and cache the result.
const detectNetlifyLambdaWithCache = () => {
// @ts-expect-error TS(7005) FIXME: Variable 'netlifyLambdaDetectorCache' implicitly h... Remove this comment to see the full error message
if (netlifyLambdaDetectorCache === undefined) {
netlifyLambdaDetectorCache = detectNetlifyLambdaBuilder()
}
// @ts-expect-error TS(7005) FIXME: Variable 'netlifyLambdaDetectorCache' implicitly h... Remove this comment to see the full error message
return netlifyLambdaDetectorCache
}
// @ts-expect-error TS(7031) FIXME: Binding element 'config' implicitly has an 'any' t... Remove this comment to see the full error message
export const getBuildFunction = async ({ config, directory, errorExit, func, projectRoot }) => {
const netlifyLambdaBuilder = await detectNetlifyLambdaWithCache()
if (netlifyLambdaBuilder) {
return netlifyLambdaBuilder.build
}
const metadata = await parseFunctionForMetadata({ mainFile: func.mainFile, config, projectRoot })
const zisiBuilder = await detectZisiBuilder({ config, directory, errorExit, func, metadata, projectRoot })
if (zisiBuilder) {
return zisiBuilder.build
}
// If there's no function builder, we create a simple one on-the-fly which
// returns as `srcFiles` the function directory, if there is one, or its
// main file otherwise.
const functionDirectory = dirname(func.mainFile)
const srcFiles = functionDirectory === directory ? [func.mainFile] : [functionDirectory]
// @ts-expect-error TS(2532) FIXME: Object is possibly 'undefined'.
return () => ({ schedule: metadata.schedule, srcFiles })
}
const workerURL = new URL('worker.js', import.meta.url)
// @ts-expect-error TS(7031) FIXME: Binding element 'context' implicitly has an 'any' ... Remove this comment to see the full error message
export const invokeFunction = async ({ context, environment, event, func, timeout }) => {
if (func.buildData?.runtimeAPIVersion !== 2) {
return await invokeFunctionDirectly({ context, event, func, timeout })
}
const workerData = {
clientContext: JSON.stringify(context),
environment,
event,
// If a function builder has defined a `buildPath` property, we use it.
// Otherwise, we'll invoke the function's main file.
// Because we use import() we have to use file:// URLs for Windows.
entryFilePath: pathToFileURL(func.buildData?.buildPath ?? func.mainFile).href,
timeoutMs: timeout * SECONDS_TO_MILLISECONDS,
}
const worker = new Worker(workerURL, { workerData })
return await new Promise((resolve, reject) => {
worker.on('message', (result) => {
if (result?.streamPort) {
const client = createConnection(
{
port: result.streamPort,
host: func.settings.functionsLoopback,
},
() => {
result.body = client
resolve(result)
},
)
client.on('error', reject)
} else {
resolve(result)
}
})
worker.on('error', reject)
})
}
export const invokeFunctionDirectly = async ({
context,
event,
func,
timeout,
}: {
context: $TSFixMe
event: $TSFixMe
func: NetlifyFunction
timeout: number
}) => {
// If a function builder has defined a `buildPath` property, we use it.
// Otherwise, we'll invoke the function's main file.
const { buildPath } = await func.getBuildData()
const lambdaPath = buildPath ?? func.mainFile
const result = await lambdaLocal.execute({
clientContext: JSON.stringify(context),
environment: {
// We've set the Blobs context on the parent process, which means it will
// be available to the Lambda. This would be inconsistent with production
// where only V2 functions get the context injected. To fix it, unset the
// context variable before invoking the function.
// This has the side-effect of also removing the variable from `process.env`.
[BLOBS_CONTEXT_VARIABLE]: undefined,
},
event,
lambdaPath,
timeoutMs: timeout * SECONDS_TO_MILLISECONDS,
verboseLevel: 3,
esm: lambdaPath.endsWith('.mjs'),
})
return result
}
export const onDirectoryScan = async () => {
const netlifyLambdaBuilder = await detectNetlifyLambdaWithCache()
// Before we start a directory scan, we check whether netlify-lambda is being
// used. If it is, we run it, so that the functions directory is populated
// with the compiled files before the scan begins.
if (netlifyLambdaBuilder) {
await netlifyLambdaBuilder.build()
}
}