netlify-cli 15.9.1-rc.0 → 15.10.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/run.mjs +6 -5
- package/npm-shrinkwrap.json +235 -734
- package/package.json +5 -5
- package/src/commands/base-command.mjs +122 -79
- package/src/commands/build/build.mjs +9 -1
- package/src/commands/deploy/deploy.mjs +1 -0
- package/src/commands/dev/dev.mjs +2 -3
- package/src/commands/functions/functions-create.mjs +118 -86
- package/src/commands/functions/functions-invoke.mjs +2 -2
- package/src/commands/functions/functions-list.mjs +2 -2
- package/src/commands/serve/serve.mjs +4 -6
- package/src/functions-templates/typescript/hello-world/package-lock.json +6 -6
- package/src/lib/edge-functions/bootstrap.mjs +1 -1
- package/src/lib/edge-functions/headers.mjs +1 -0
- package/src/lib/edge-functions/proxy.mjs +1 -0
- package/src/lib/functions/runtimes/js/index.mjs +1 -1
- package/src/lib/functions/runtimes/js/worker.mjs +1 -1
- package/src/recipes/vscode/index.mjs +24 -6
- package/src/utils/build-info.mjs +81 -0
- package/src/utils/detect-server-settings.mjs +26 -87
- package/src/utils/framework-server.mjs +4 -3
- package/src/utils/functions/functions.mjs +1 -5
- package/src/utils/proxy.mjs +0 -3
- package/src/utils/read-repo-url.mjs +4 -0
- package/src/utils/run-build.mjs +40 -65
- package/src/utils/shell.mjs +10 -4
|
@@ -3,7 +3,7 @@ import cp from 'child_process'
|
|
|
3
3
|
import fs from 'fs'
|
|
4
4
|
import { mkdir, readdir, unlink } from 'fs/promises'
|
|
5
5
|
import { createRequire } from 'module'
|
|
6
|
-
import path, { dirname } from 'path'
|
|
6
|
+
import path, { dirname, join, relative } from 'path'
|
|
7
7
|
import process from 'process'
|
|
8
8
|
import { fileURLToPath, pathToFileURL } from 'url'
|
|
9
9
|
import { promisify } from 'util'
|
|
@@ -30,8 +30,10 @@ const templatesDir = path.resolve(dirname(fileURLToPath(import.meta.url)), '../.
|
|
|
30
30
|
|
|
31
31
|
const showRustTemplates = process.env.NETLIFY_EXPERIMENTAL_BUILD_RUST_SOURCE === 'true'
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Ensure that there's a sub-directory in `src/functions-templates` named after
|
|
35
|
+
* each `value` property in this list.
|
|
36
|
+
*/
|
|
35
37
|
const languages = [
|
|
36
38
|
{ name: 'JavaScript', value: 'javascript' },
|
|
37
39
|
{ name: 'TypeScript', value: 'typescript' },
|
|
@@ -90,23 +92,28 @@ const filterRegistry = function (registry, input) {
|
|
|
90
92
|
})
|
|
91
93
|
}
|
|
92
94
|
|
|
95
|
+
/**
|
|
96
|
+
* @param {string} lang
|
|
97
|
+
* @param {'edge' | 'serverless'} funcType
|
|
98
|
+
*/
|
|
93
99
|
const formatRegistryArrayForInquirer = async function (lang, funcType) {
|
|
94
|
-
const
|
|
100
|
+
const folders = await readdir(path.join(templatesDir, lang), { withFileTypes: true })
|
|
95
101
|
|
|
96
102
|
const imports = await Promise.all(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
folders
|
|
104
|
+
.filter((folder) => Boolean(folder?.isDirectory()))
|
|
105
|
+
.map(async ({ name }) => {
|
|
106
|
+
try {
|
|
107
|
+
const templatePath = path.join(templatesDir, lang, name, '.netlify-function-template.mjs')
|
|
108
|
+
const template = await import(pathToFileURL(templatePath))
|
|
109
|
+
return template.default
|
|
110
|
+
} catch {
|
|
111
|
+
// noop if import fails we don't break the whole inquirer
|
|
112
|
+
}
|
|
105
113
|
}),
|
|
106
114
|
)
|
|
107
|
-
|
|
108
115
|
const registry = imports
|
|
109
|
-
.filter((template) => template
|
|
116
|
+
.filter((template) => template?.functionType === funcType)
|
|
110
117
|
.sort((templateA, templateB) => {
|
|
111
118
|
const priorityDiff = (templateA.priority || DEFAULT_PRIORITY) - (templateB.priority || DEFAULT_PRIORITY)
|
|
112
119
|
|
|
@@ -135,7 +142,7 @@ const formatRegistryArrayForInquirer = async function (lang, funcType) {
|
|
|
135
142
|
/**
|
|
136
143
|
* pick template from our existing templates
|
|
137
144
|
* @param {import('commander').OptionValues} config
|
|
138
|
-
*
|
|
145
|
+
* @param {'edge' | 'serverless'} funcType
|
|
139
146
|
*/
|
|
140
147
|
const pickTemplate = async function ({ language: languageFromFlag }, funcType) {
|
|
141
148
|
const specialCommands = [
|
|
@@ -204,6 +211,7 @@ const pickTemplate = async function ({ language: languageFromFlag }, funcType) {
|
|
|
204
211
|
|
|
205
212
|
const DEFAULT_PRIORITY = 999
|
|
206
213
|
|
|
214
|
+
/** @returns {Promise<'edge' | 'serverless'>} */
|
|
207
215
|
const selectTypeOfFunc = async () => {
|
|
208
216
|
const functionTypes = [
|
|
209
217
|
{ name: 'Edge function (Deno)', value: 'edge' },
|
|
@@ -221,92 +229,100 @@ const selectTypeOfFunc = async () => {
|
|
|
221
229
|
return functionType
|
|
222
230
|
}
|
|
223
231
|
|
|
232
|
+
/**
|
|
233
|
+
* @param {import('../base-command.mjs').default} command
|
|
234
|
+
*/
|
|
224
235
|
const ensureEdgeFuncDirExists = function (command) {
|
|
225
236
|
const { config, site } = command.netlify
|
|
226
237
|
const siteId = site.id
|
|
227
|
-
let functionsDirHolder = config.build.edge_functions
|
|
228
238
|
|
|
229
239
|
if (!siteId) {
|
|
230
240
|
error(`${NETLIFYDEVERR} No site id found, please run inside a site directory or \`netlify link\``)
|
|
231
241
|
}
|
|
232
242
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
243
|
+
const functionsDir = config.build?.edge_functions ?? join(command.workingDir, 'netlify/edge-functions')
|
|
244
|
+
const relFunctionsDir = relative(command.workingDir, functionsDir)
|
|
236
245
|
|
|
237
|
-
if (!fs.existsSync(
|
|
246
|
+
if (!fs.existsSync(functionsDir)) {
|
|
238
247
|
log(
|
|
239
248
|
`${NETLIFYDEVLOG} Edge Functions directory ${chalk.magenta.inverse(
|
|
240
|
-
|
|
249
|
+
relFunctionsDir,
|
|
241
250
|
)} does not exist yet, creating it...`,
|
|
242
251
|
)
|
|
243
252
|
|
|
244
|
-
fs.mkdirSync(
|
|
253
|
+
fs.mkdirSync(functionsDir, { recursive: true })
|
|
245
254
|
|
|
246
|
-
log(`${NETLIFYDEVLOG} Edge Functions directory ${chalk.magenta.inverse(
|
|
255
|
+
log(`${NETLIFYDEVLOG} Edge Functions directory ${chalk.magenta.inverse(relFunctionsDir)} created.`)
|
|
247
256
|
}
|
|
248
|
-
|
|
257
|
+
|
|
258
|
+
return functionsDir
|
|
249
259
|
}
|
|
250
260
|
|
|
251
261
|
/**
|
|
252
|
-
*
|
|
262
|
+
* Prompts the user to choose a functions directory
|
|
253
263
|
* @param {import('../base-command.mjs').default} command
|
|
254
|
-
* @returns {Promise<string
|
|
264
|
+
* @returns {Promise<string>} - functions directory or throws an error
|
|
255
265
|
*/
|
|
256
|
-
const
|
|
257
|
-
const { api,
|
|
258
|
-
|
|
259
|
-
let functionsDirHolder = config.functionsDirectory
|
|
260
|
-
|
|
261
|
-
if (!functionsDirHolder) {
|
|
262
|
-
log(`${NETLIFYDEVLOG} functions directory not specified in netlify.toml or UI settings`)
|
|
263
|
-
|
|
264
|
-
if (!siteId) {
|
|
265
|
-
error(`${NETLIFYDEVERR} No site id found, please run inside a site directory or \`netlify link\``)
|
|
266
|
-
}
|
|
266
|
+
const promptFunctionsDirectory = async (command) => {
|
|
267
|
+
const { api, relConfigFilePath, site } = command.netlify
|
|
268
|
+
log(`\n${NETLIFYDEVLOG} functions directory not specified in ${relConfigFilePath} or UI settings`)
|
|
267
269
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
name: 'functionsDir',
|
|
272
|
-
message:
|
|
273
|
-
'Enter the path, relative to your site’s base directory in your repository, where your functions should live:',
|
|
274
|
-
default: 'netlify/functions',
|
|
275
|
-
},
|
|
276
|
-
])
|
|
270
|
+
if (!site.id) {
|
|
271
|
+
error(`${NETLIFYDEVERR} No site id found, please run inside a site directory or \`netlify link\``)
|
|
272
|
+
}
|
|
277
273
|
|
|
278
|
-
|
|
274
|
+
const { functionsDir } = await inquirer.prompt([
|
|
275
|
+
{
|
|
276
|
+
type: 'input',
|
|
277
|
+
name: 'functionsDir',
|
|
278
|
+
message: 'Enter the path, relative to your site, where your functions should live:',
|
|
279
|
+
default: 'netlify/functions',
|
|
280
|
+
},
|
|
281
|
+
])
|
|
279
282
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
},
|
|
283
|
+
try {
|
|
284
|
+
log(`${NETLIFYDEVLOG} updating site settings with ${chalk.magenta.inverse(functionsDir)}`)
|
|
285
|
+
|
|
286
|
+
// @ts-ignore Typings of API are not correct
|
|
287
|
+
await api.updateSite({
|
|
288
|
+
siteId: site.id,
|
|
289
|
+
body: {
|
|
290
|
+
build_settings: {
|
|
291
|
+
functions_dir: functionsDir,
|
|
290
292
|
},
|
|
291
|
-
}
|
|
293
|
+
},
|
|
294
|
+
})
|
|
292
295
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
296
|
+
log(`${NETLIFYDEVLOG} functions directory ${chalk.magenta.inverse(functionsDir)} updated in site settings`)
|
|
297
|
+
} catch {
|
|
298
|
+
throw error('Error updating site settings')
|
|
297
299
|
}
|
|
300
|
+
return functionsDir
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Get functions directory (and make it if necessary)
|
|
305
|
+
* @param {import('../base-command.mjs').default} command
|
|
306
|
+
* @returns {Promise<string>} - functions directory or throws an error
|
|
307
|
+
*/
|
|
308
|
+
const ensureFunctionDirExists = async function (command) {
|
|
309
|
+
const { config } = command.netlify
|
|
310
|
+
const functionsDirHolder =
|
|
311
|
+
config.functionsDirectory || join(command.workingDir, await promptFunctionsDirectory(command))
|
|
312
|
+
const relFunctionsDirHolder = relative(command.workingDir, functionsDirHolder)
|
|
298
313
|
|
|
299
|
-
if (!(
|
|
314
|
+
if (!fs.existsSync(functionsDirHolder)) {
|
|
300
315
|
log(
|
|
301
316
|
`${NETLIFYDEVLOG} functions directory ${chalk.magenta.inverse(
|
|
302
|
-
|
|
317
|
+
relFunctionsDirHolder,
|
|
303
318
|
)} does not exist yet, creating it...`,
|
|
304
319
|
)
|
|
305
320
|
|
|
306
321
|
await mkdir(functionsDirHolder, { recursive: true })
|
|
307
322
|
|
|
308
|
-
log(`${NETLIFYDEVLOG} functions directory ${chalk.magenta.inverse(
|
|
323
|
+
log(`${NETLIFYDEVLOG} functions directory ${chalk.magenta.inverse(relFunctionsDirHolder)} created`)
|
|
309
324
|
}
|
|
325
|
+
|
|
310
326
|
return functionsDirHolder
|
|
311
327
|
}
|
|
312
328
|
|
|
@@ -367,20 +383,24 @@ const downloadFromURL = async function (command, options, argumentName, function
|
|
|
367
383
|
}
|
|
368
384
|
}
|
|
369
385
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
386
|
+
/**
|
|
387
|
+
* Takes a list of existing packages and a list of packages required by a
|
|
388
|
+
* function, and returns the packages from the latter that aren't present
|
|
389
|
+
* in the former. The packages are returned as an array of strings with the
|
|
390
|
+
* name and version range (e.g. '@netlify/functions@0.1.0').
|
|
391
|
+
*/
|
|
374
392
|
const getNpmInstallPackages = (existingPackages = {}, neededPackages = {}) =>
|
|
375
393
|
Object.entries(neededPackages)
|
|
376
394
|
.filter(([name]) => existingPackages[name] === undefined)
|
|
377
395
|
.map(([name, version]) => `${name}@${version}`)
|
|
378
396
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
397
|
+
/**
|
|
398
|
+
* When installing a function's dependencies, we first try to find a site-level
|
|
399
|
+
* `package.json` file. If we do, we look for any dependencies of the function
|
|
400
|
+
* that aren't already listed as dependencies of the site and install them. If
|
|
401
|
+
* we don't do this check, we may be upgrading the version of a module used in
|
|
402
|
+
* another part of the project, which we don't want to do.
|
|
403
|
+
*/
|
|
384
404
|
const installDeps = async ({ functionPackageJson, functionPath, functionsDir }) => {
|
|
385
405
|
const { dependencies: functionDependencies, devDependencies: functionDevDependencies } = require(functionPackageJson)
|
|
386
406
|
const sitePackageJson = await findUp('package.json', { cwd: functionsDir })
|
|
@@ -427,8 +447,8 @@ const installDeps = async ({ functionPackageJson, functionPath, functionsDir })
|
|
|
427
447
|
* @param {import('../base-command.mjs').default} command
|
|
428
448
|
* @param {import('commander').OptionValues} options
|
|
429
449
|
* @param {string} argumentName
|
|
430
|
-
* @param {string} functionsDir
|
|
431
|
-
* @param {
|
|
450
|
+
* @param {string} functionsDir Absolute path of the functions directory
|
|
451
|
+
* @param {'edge' | 'serverless'} funcType
|
|
432
452
|
*/
|
|
433
453
|
// eslint-disable-next-line max-params
|
|
434
454
|
const scaffoldFromTemplate = async function (command, options, argumentName, functionsDir, funcType) {
|
|
@@ -440,7 +460,7 @@ const scaffoldFromTemplate = async function (command, options, argumentName, fun
|
|
|
440
460
|
name: 'chosenUrl',
|
|
441
461
|
message: 'URL to clone: ',
|
|
442
462
|
type: 'input',
|
|
443
|
-
validate: (val) => Boolean(validateRepoURL(val)),
|
|
463
|
+
validate: (/** @type {string} */ val) => Boolean(validateRepoURL(val)),
|
|
444
464
|
// make sure it is not undefined and is a valid filename.
|
|
445
465
|
// this has some nuance i have ignored, eg crossenv and i18n concerns
|
|
446
466
|
},
|
|
@@ -503,7 +523,7 @@ const scaffoldFromTemplate = async function (command, options, argumentName, fun
|
|
|
503
523
|
}
|
|
504
524
|
|
|
505
525
|
if (funcType === 'edge') {
|
|
506
|
-
registerEFInToml(name)
|
|
526
|
+
registerEFInToml(name, command.netlify)
|
|
507
527
|
}
|
|
508
528
|
|
|
509
529
|
await installAddons(command, addons, path.resolve(functionPath))
|
|
@@ -628,9 +648,15 @@ const installAddons = async function (command, functionAddons, fnPath) {
|
|
|
628
648
|
return Promise.all(arr)
|
|
629
649
|
}
|
|
630
650
|
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
651
|
+
/**
|
|
652
|
+
*
|
|
653
|
+
* @param {string} funcName
|
|
654
|
+
* @param {import('../types.js').NetlifyOptions} options
|
|
655
|
+
*/
|
|
656
|
+
const registerEFInToml = async (funcName, options) => {
|
|
657
|
+
const { configFilePath, relConfigFilePath } = options
|
|
658
|
+
if (!fs.existsSync(configFilePath)) {
|
|
659
|
+
log(`${NETLIFYDEVLOG} \`${relConfigFilePath}\` file does not exist yet. Creating it...`)
|
|
634
660
|
}
|
|
635
661
|
|
|
636
662
|
let { funcPath } = await inquirer.prompt([
|
|
@@ -653,17 +679,22 @@ const registerEFInToml = async (funcName) => {
|
|
|
653
679
|
const functionRegister = `\n\n[[edge_functions]]\nfunction = "${funcName}"\npath = "${funcPath}"`
|
|
654
680
|
|
|
655
681
|
try {
|
|
656
|
-
fs.promises.appendFile(
|
|
682
|
+
fs.promises.appendFile(configFilePath, functionRegister)
|
|
657
683
|
log(
|
|
658
|
-
`${NETLIFYDEVLOG} Function '${funcName}' registered for route \`${funcPath}\`. To change, edit your \`
|
|
684
|
+
`${NETLIFYDEVLOG} Function '${funcName}' registered for route \`${funcPath}\`. To change, edit your \`${relConfigFilePath}\` file.`,
|
|
659
685
|
)
|
|
660
686
|
} catch {
|
|
661
|
-
error(`${NETLIFYDEVERR} Unable to register function. Please check your \`
|
|
687
|
+
error(`${NETLIFYDEVERR} Unable to register function. Please check your \`${relConfigFilePath}\` file.`)
|
|
662
688
|
}
|
|
663
689
|
}
|
|
664
690
|
|
|
665
|
-
|
|
666
|
-
|
|
691
|
+
/**
|
|
692
|
+
* we used to allow for a --dir command,
|
|
693
|
+
* but have retired that to force every scaffolded function to be a directory
|
|
694
|
+
* @param {string} functionsDir
|
|
695
|
+
* @param {string} name
|
|
696
|
+
* @returns
|
|
697
|
+
*/
|
|
667
698
|
const ensureFunctionPathIsOk = function (functionsDir, name) {
|
|
668
699
|
const functionPath = path.join(functionsDir, name)
|
|
669
700
|
if (fs.existsSync(functionPath)) {
|
|
@@ -675,6 +706,7 @@ const ensureFunctionPathIsOk = function (functionsDir, name) {
|
|
|
675
706
|
|
|
676
707
|
/**
|
|
677
708
|
* The functions:create command
|
|
709
|
+
* @param {string} name
|
|
678
710
|
* @param {import('commander').OptionValues} options
|
|
679
711
|
* @param {import('../base-command.mjs').default} command
|
|
680
712
|
*/
|
|
@@ -144,11 +144,11 @@ const getFunctionToTrigger = function (options, argumentName) {
|
|
|
144
144
|
* @param {import('../base-command.mjs').default} command
|
|
145
145
|
*/
|
|
146
146
|
const functionsInvoke = async (nameArgument, options, command) => {
|
|
147
|
-
const { config } = command.netlify
|
|
147
|
+
const { config, relConfigFilePath } = command.netlify
|
|
148
148
|
|
|
149
149
|
const functionsDir = options.functions || (config.dev && config.dev.functions) || config.functionsDirectory
|
|
150
150
|
if (typeof functionsDir === 'undefined') {
|
|
151
|
-
error(
|
|
151
|
+
error(`functions directory is undefined, did you forget to set it in ${relConfigFilePath}?`)
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
if (!options.port)
|
|
@@ -16,7 +16,7 @@ const normalizeFunction = function (deployedFunctions, { name, urlPath: url }) {
|
|
|
16
16
|
* @param {import('../base-command.mjs').default} command
|
|
17
17
|
*/
|
|
18
18
|
const functionsList = async (options, command) => {
|
|
19
|
-
const { config, siteInfo } = command.netlify
|
|
19
|
+
const { config, relConfigFilePath, siteInfo } = command.netlify
|
|
20
20
|
|
|
21
21
|
const deploy = siteInfo.published_deploy || {}
|
|
22
22
|
const deployedFunctions = deploy.available_functions || []
|
|
@@ -25,7 +25,7 @@ const functionsList = async (options, command) => {
|
|
|
25
25
|
|
|
26
26
|
if (typeof functionsDir === 'undefined') {
|
|
27
27
|
log('Functions directory is undefined')
|
|
28
|
-
log(
|
|
28
|
+
log(`Please verify functions.directory is set in your Netlify configuration file ${relConfigFilePath}`)
|
|
29
29
|
log('See https://docs.netlify.com/configure-builds/file-based-configuration/ for more information')
|
|
30
30
|
exit(1)
|
|
31
31
|
}
|
|
@@ -38,6 +38,7 @@ const serve = async (options, command) => {
|
|
|
38
38
|
const devConfig = {
|
|
39
39
|
...(config.functionsDirectory && { functions: config.functionsDirectory }),
|
|
40
40
|
...(config.build.publish && { publish: config.build.publish }),
|
|
41
|
+
|
|
41
42
|
...config.dev,
|
|
42
43
|
...options,
|
|
43
44
|
// Override the `framework` value so that we start a static server and not
|
|
@@ -69,8 +70,7 @@ const serve = async (options, command) => {
|
|
|
69
70
|
// Netlify Build are loaded.
|
|
70
71
|
await getInternalFunctionsDir({ base: site.root, ensureExists: true })
|
|
71
72
|
|
|
72
|
-
/** @type {
|
|
73
|
-
let settings = {}
|
|
73
|
+
let settings = /** @type {import('../../utils/types.js').ServerSettings} */ ({})
|
|
74
74
|
try {
|
|
75
75
|
settings = await detectServerSettings(devConfig, options, command)
|
|
76
76
|
|
|
@@ -88,11 +88,9 @@ const serve = async (options, command) => {
|
|
|
88
88
|
)
|
|
89
89
|
|
|
90
90
|
const { configPath: configPathOverride } = await runBuildTimeline({
|
|
91
|
-
|
|
92
|
-
options,
|
|
91
|
+
command,
|
|
93
92
|
settings,
|
|
94
|
-
|
|
95
|
-
site,
|
|
93
|
+
options,
|
|
96
94
|
})
|
|
97
95
|
|
|
98
96
|
await startFunctionsServer({
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"node_modules/@types/node": {
|
|
29
|
-
"version": "14.18.
|
|
30
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.
|
|
31
|
-
"integrity": "sha512-
|
|
29
|
+
"version": "14.18.54",
|
|
30
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.54.tgz",
|
|
31
|
+
"integrity": "sha512-uq7O52wvo2Lggsx1x21tKZgqkJpvwCseBBPtX/nKQfpVlEsLOb11zZ1CRsWUKvJF0+lzuA9jwvA7Pr2Wt7i3xw=="
|
|
32
32
|
},
|
|
33
33
|
"node_modules/is-promise": {
|
|
34
34
|
"version": "4.0.0",
|
|
@@ -58,9 +58,9 @@
|
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
"@types/node": {
|
|
61
|
-
"version": "14.18.
|
|
62
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.
|
|
63
|
-
"integrity": "sha512-
|
|
61
|
+
"version": "14.18.54",
|
|
62
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.54.tgz",
|
|
63
|
+
"integrity": "sha512-uq7O52wvo2Lggsx1x21tKZgqkJpvwCseBBPtX/nKQfpVlEsLOb11zZ1CRsWUKvJF0+lzuA9jwvA7Pr2Wt7i3xw=="
|
|
64
64
|
},
|
|
65
65
|
"is-promise": {
|
|
66
66
|
"version": "4.0.0",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { env } from 'process'
|
|
2
2
|
|
|
3
|
-
const latestBootstrapURL = 'https://
|
|
3
|
+
const latestBootstrapURL = 'https://64c264287e9cbb0008621df3--edge.netlify.com/bootstrap/index-combined.ts'
|
|
4
4
|
|
|
5
5
|
export const getBootstrapURL = () => env.NETLIFY_EDGE_BOOTSTRAP || latestBootstrapURL
|
|
@@ -139,6 +139,7 @@ export const initializeProxy = async ({
|
|
|
139
139
|
|
|
140
140
|
// Setting header with geolocation and site info.
|
|
141
141
|
req.headers[headers.Geo] = Buffer.from(JSON.stringify(geoLocation)).toString('base64')
|
|
142
|
+
req.headers[headers.DeployID] = '0'
|
|
142
143
|
req.headers[headers.Site] = createSiteInfoHeader(siteInfo)
|
|
143
144
|
req.headers[headers.Account] = createAccountInfoHeader({ id: accountId })
|
|
144
145
|
|
|
@@ -3,7 +3,7 @@ import { dirname } from 'path'
|
|
|
3
3
|
import { pathToFileURL } from 'url'
|
|
4
4
|
import { Worker } from 'worker_threads'
|
|
5
5
|
|
|
6
|
-
import lambdaLocal from '
|
|
6
|
+
import lambdaLocal from 'lambda-local'
|
|
7
7
|
import winston from 'winston'
|
|
8
8
|
|
|
9
9
|
import detectNetlifyLambdaBuilder from './builders/netlify-lambda.mjs'
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createServer } from 'net'
|
|
2
2
|
import { isMainThread, workerData, parentPort } from 'worker_threads'
|
|
3
3
|
|
|
4
|
-
import lambdaLocal from '@skn0tt/lambda-local'
|
|
5
4
|
import { isStream } from 'is-stream'
|
|
5
|
+
import lambdaLocal from 'lambda-local'
|
|
6
6
|
import sourceMapSupport from 'source-map-support'
|
|
7
7
|
|
|
8
8
|
if (isMainThread) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-check
|
|
1
2
|
import { join } from 'path'
|
|
2
3
|
|
|
3
4
|
import { DenoBridge } from '@netlify/edge-bundler'
|
|
@@ -27,15 +28,24 @@ const getPrompt = ({ fileExists, path }) => {
|
|
|
27
28
|
const getEdgeFunctionsPath = ({ config, repositoryRoot }) =>
|
|
28
29
|
config.build.edge_functions || join(repositoryRoot, 'netlify', 'edge-functions')
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* @param {string} repositoryRoot
|
|
33
|
+
*/
|
|
30
34
|
const getSettingsPath = (repositoryRoot) => join(repositoryRoot, '.vscode', 'settings.json')
|
|
31
35
|
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
/**
|
|
37
|
+
* @param {string} repositoryRoot
|
|
38
|
+
*/
|
|
39
|
+
const hasDenoVSCodeExt = async (repositoryRoot) => {
|
|
40
|
+
const { stdout: extensions } = await execa('code', ['--list-extensions'], { stderr: 'inherit', cwd: repositoryRoot })
|
|
34
41
|
return extensions.split('\n').includes('denoland.vscode-deno')
|
|
35
42
|
}
|
|
36
43
|
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
/**
|
|
45
|
+
* @param {string} repositoryRoot
|
|
46
|
+
*/
|
|
47
|
+
const getDenoVSCodeExt = async (repositoryRoot) => {
|
|
48
|
+
await execa('code', ['--install-extension', 'denoland.vscode-deno'], { stdio: 'inherit', cwd: repositoryRoot })
|
|
39
49
|
}
|
|
40
50
|
|
|
41
51
|
const getDenoExtPrompt = () => {
|
|
@@ -49,6 +59,12 @@ const getDenoExtPrompt = () => {
|
|
|
49
59
|
})
|
|
50
60
|
}
|
|
51
61
|
|
|
62
|
+
/**
|
|
63
|
+
* @param {object} params
|
|
64
|
+
* @param {*} params.config
|
|
65
|
+
* @param {string} params.repositoryRoot
|
|
66
|
+
* @returns
|
|
67
|
+
*/
|
|
52
68
|
export const run = async ({ config, repositoryRoot }) => {
|
|
53
69
|
const deno = new DenoBridge({
|
|
54
70
|
onBeforeDownload: () =>
|
|
@@ -66,9 +82,11 @@ export const run = async ({ config, repositoryRoot }) => {
|
|
|
66
82
|
}
|
|
67
83
|
|
|
68
84
|
try {
|
|
69
|
-
if (!(await hasDenoVSCodeExt())) {
|
|
85
|
+
if (!(await hasDenoVSCodeExt(repositoryRoot))) {
|
|
70
86
|
const { confirm: denoExtConfirm } = await getDenoExtPrompt()
|
|
71
|
-
if (denoExtConfirm)
|
|
87
|
+
if (denoExtConfirm) {
|
|
88
|
+
getDenoVSCodeExt(repositoryRoot)
|
|
89
|
+
}
|
|
72
90
|
}
|
|
73
91
|
} catch {
|
|
74
92
|
log(
|
package/src/utils/build-info.mjs
CHANGED
|
@@ -1,5 +1,86 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
import fuzzy from 'fuzzy'
|
|
4
|
+
import inquirer from 'inquirer'
|
|
5
|
+
|
|
6
|
+
import { chalk, log } from './command-helpers.mjs'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Filters the inquirer settings based on the input
|
|
10
|
+
* @param {ReturnType<typeof formatSettingsArrForInquirer>} scriptInquirerOptions
|
|
11
|
+
* @param {string} input
|
|
12
|
+
*/
|
|
13
|
+
const filterSettings = function (scriptInquirerOptions, input) {
|
|
14
|
+
const filterOptions = scriptInquirerOptions.map((scriptInquirerOption) => scriptInquirerOption.name)
|
|
15
|
+
// TODO: remove once https://github.com/sindresorhus/eslint-plugin-unicorn/issues/1394 is fixed
|
|
16
|
+
// eslint-disable-next-line unicorn/no-array-method-this-argument
|
|
17
|
+
const filteredSettings = fuzzy.filter(input, filterOptions)
|
|
18
|
+
const filteredSettingNames = new Set(
|
|
19
|
+
filteredSettings.map((filteredSetting) => (input ? filteredSetting.string : filteredSetting)),
|
|
20
|
+
)
|
|
21
|
+
return scriptInquirerOptions.filter((t) => filteredSettingNames.has(t.name))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** @typedef {import('@netlify/build-info').Settings} Settings */
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {Settings[]} settings
|
|
28
|
+
* @param {'dev' | 'build'} type The type of command (dev or build)
|
|
29
|
+
*/
|
|
30
|
+
const formatSettingsArrForInquirer = function (settings, type = 'dev') {
|
|
31
|
+
return settings.map((setting) => {
|
|
32
|
+
const cmd = type === 'dev' ? setting.devCommand : setting.buildCommand
|
|
33
|
+
return {
|
|
34
|
+
name: `[${chalk.yellow(setting.framework.name)}] '${cmd}'`,
|
|
35
|
+
value: { ...setting, commands: [cmd] },
|
|
36
|
+
short: `${setting.name}-${cmd}`,
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Uses @netlify/build-info to detect the dev settings and port based on the framework
|
|
43
|
+
* and the build system that is used.
|
|
44
|
+
* @param {import('../commands/base-command.mjs').default} command
|
|
45
|
+
* @param {'dev' | 'build'} type The type of command (dev or build)
|
|
46
|
+
* @returns {Promise<Settings | undefined>}
|
|
47
|
+
*/
|
|
48
|
+
export const detectFrameworkSettings = async (command, type = 'dev') => {
|
|
49
|
+
const { relConfigFilePath } = command.netlify
|
|
50
|
+
const settings = await detectBuildSettings(command)
|
|
51
|
+
if (settings.length === 1) {
|
|
52
|
+
return settings[0]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (settings.length > 1) {
|
|
56
|
+
/** multiple matching detectors, make the user choose */
|
|
57
|
+
const scriptInquirerOptions = formatSettingsArrForInquirer(settings, type)
|
|
58
|
+
/** @type {{chosenSettings: Settings}} */
|
|
59
|
+
const { chosenSettings } = await inquirer.prompt({
|
|
60
|
+
name: 'chosenSettings',
|
|
61
|
+
message: `Multiple possible ${type} commands found`,
|
|
62
|
+
type: 'autocomplete',
|
|
63
|
+
source(/** @type {string} */ _, input = '') {
|
|
64
|
+
if (!input) return scriptInquirerOptions
|
|
65
|
+
// only show filtered results
|
|
66
|
+
return filterSettings(scriptInquirerOptions, input)
|
|
67
|
+
},
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
log(`
|
|
71
|
+
Update your ${relConfigFilePath} to avoid this selection prompt next time:
|
|
72
|
+
|
|
73
|
+
[build]
|
|
74
|
+
command = "${chosenSettings.buildCommand}"
|
|
75
|
+
publish = "${chosenSettings.dist}"
|
|
76
|
+
|
|
77
|
+
[dev]
|
|
78
|
+
command = "${chosenSettings.devCommand}"
|
|
79
|
+
`)
|
|
80
|
+
return chosenSettings
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
3
84
|
/**
|
|
4
85
|
* Detects and filters the build setting for a project and a command
|
|
5
86
|
* @param {import('../commands/base-command.mjs').default} command
|