netlify-cli 17.10.1 → 17.11.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/npm-shrinkwrap.json +956 -203
- package/package.json +2 -3
- package/src/commands/base-command.js +47 -102
- package/src/commands/deploy/deploy.js +5 -8
- package/src/commands/dev/dev.js +0 -1
- package/src/commands/functions/functions-create.js +0 -4
- package/src/commands/link/link.js +0 -5
- package/src/commands/lm/lm-setup.js +0 -2
- package/src/commands/main.js +0 -2
- package/src/commands/serve/serve.js +3 -1
- package/src/commands/sites/sites-create-template.js +0 -3
- package/src/lib/blobs/blobs.js +1 -1
- package/src/lib/edge-functions/proxy.js +2 -21
- package/src/lib/edge-functions/registry.js +42 -10
- package/src/lib/exec-fetcher.js +0 -2
- package/src/lib/functions/local-proxy.js +1 -3
- package/src/lib/functions/netlify-function.js +3 -3
- package/src/lib/functions/registry.js +17 -27
- package/src/lib/functions/runtimes/go/index.js +0 -3
- package/src/lib/functions/runtimes/index.js +0 -34
- package/src/lib/functions/runtimes/js/builders/netlify-lambda.js +10 -10
- package/src/lib/functions/runtimes/rust/index.js +0 -2
- package/src/lib/functions/server.js +23 -13
- package/src/utils/banner.js +2 -3
- package/src/utils/build-info.js +22 -39
- package/src/utils/command-helpers.js +3 -11
- package/src/utils/deploy/deploy-site.js +0 -6
- package/src/utils/detect-server-settings.js +15 -101
- package/src/utils/execa.js +1 -4
- package/src/utils/feature-flags.js +1 -7
- package/src/utils/framework-server.js +6 -10
- package/src/utils/functions/functions.js +2 -9
- package/src/utils/init/utils.js +17 -32
- package/src/utils/live-tunnel.js +0 -2
- package/src/utils/lm/requirements.js +0 -5
- package/src/utils/sites/utils.js +0 -1
- package/src/utils/telemetry/report-error.js +0 -2
- package/src/utils/telemetry/telemetry.js +1 -21
- package/src/lib/edge-functions/internal.js +0 -46
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import { readFile } from 'fs/promises';
|
|
2
|
+
import { join } from 'path';
|
|
1
3
|
import { fileURLToPath } from 'url';
|
|
2
4
|
import { NETLIFYDEVERR, NETLIFYDEVLOG, NETLIFYDEVWARN, nonNullable, chalk, log, warn, watchDebounced, isNodeError, } from '../../utils/command-helpers.js';
|
|
5
|
+
import { getPathInProject } from '../settings.js';
|
|
6
|
+
import { INTERNAL_EDGE_FUNCTIONS_FOLDER } from './consts.js';
|
|
3
7
|
const featureFlags = { edge_functions_correct_order: true };
|
|
4
8
|
export class EdgeFunctionsRegistry {
|
|
5
|
-
constructor({ bundler, config, configPath, directories, env, getUpdatedConfig,
|
|
9
|
+
constructor({ bundler, config, configPath, directories, env, getUpdatedConfig, importMapFromTOML, projectDir, runIsolate, servePath, }) {
|
|
6
10
|
this.buildError = null;
|
|
11
|
+
this.declarationsFromDeployConfig = [];
|
|
7
12
|
this.dependencyPaths = new Map();
|
|
8
13
|
this.directoryWatchers = new Map();
|
|
9
14
|
this.functionPaths = new Map();
|
|
@@ -14,11 +19,11 @@ export class EdgeFunctionsRegistry {
|
|
|
14
19
|
this.bundler = bundler;
|
|
15
20
|
this.configPath = configPath;
|
|
16
21
|
this.directories = directories;
|
|
17
|
-
this.internalDirectories = internalDirectories;
|
|
18
22
|
this.getUpdatedConfig = getUpdatedConfig;
|
|
19
23
|
this.runIsolate = runIsolate;
|
|
20
24
|
this.servePath = servePath;
|
|
21
|
-
this.
|
|
25
|
+
this.projectDir = projectDir;
|
|
26
|
+
this.importMapFromTOML = importMapFromTOML;
|
|
22
27
|
this.declarationsFromTOML = EdgeFunctionsRegistry.getDeclarationsFromTOML(config);
|
|
23
28
|
this.env = EdgeFunctionsRegistry.getEnvironmentVariables(env);
|
|
24
29
|
this.buildError = null;
|
|
@@ -28,7 +33,7 @@ export class EdgeFunctionsRegistry {
|
|
|
28
33
|
this.userFunctions = [];
|
|
29
34
|
this.internalFunctions = [];
|
|
30
35
|
this.initialScan = this.doInitialScan();
|
|
31
|
-
this.setupWatchers(
|
|
36
|
+
this.setupWatchers();
|
|
32
37
|
}
|
|
33
38
|
async doInitialScan() {
|
|
34
39
|
await this.scanForFunctions();
|
|
@@ -306,13 +311,40 @@ export class EdgeFunctionsRegistry {
|
|
|
306
311
|
}
|
|
307
312
|
const { functionsConfig, graph, npmSpecifiersWithExtraneousFiles, success } = await this.runIsolate(this.functions, this.env, {
|
|
308
313
|
getFunctionsConfig: true,
|
|
314
|
+
importMapPaths: [this.importMapFromTOML, this.importMapFromDeployConfig].filter(nonNullable),
|
|
309
315
|
});
|
|
310
316
|
return { functionsConfig, graph, npmSpecifiersWithExtraneousFiles, success };
|
|
311
317
|
}
|
|
318
|
+
get internalDirectory() {
|
|
319
|
+
return join(this.projectDir, getPathInProject([INTERNAL_EDGE_FUNCTIONS_FOLDER]));
|
|
320
|
+
}
|
|
321
|
+
async readDeployConfig() {
|
|
322
|
+
const manifestPath = join(this.internalDirectory, 'manifest.json');
|
|
323
|
+
try {
|
|
324
|
+
const contents = await readFile(manifestPath, 'utf8');
|
|
325
|
+
const manifest = JSON.parse(contents);
|
|
326
|
+
return manifest;
|
|
327
|
+
}
|
|
328
|
+
catch { }
|
|
329
|
+
}
|
|
330
|
+
async scanForDeployConfig() {
|
|
331
|
+
const deployConfig = await this.readDeployConfig();
|
|
332
|
+
if (!deployConfig) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (deployConfig.version !== 1) {
|
|
336
|
+
throw new Error('Unsupported manifest format');
|
|
337
|
+
}
|
|
338
|
+
this.declarationsFromDeployConfig = deployConfig.functions;
|
|
339
|
+
this.importMapFromDeployConfig = deployConfig.import_map
|
|
340
|
+
? join(this.internalDirectory, deployConfig.import_map)
|
|
341
|
+
: undefined;
|
|
342
|
+
}
|
|
312
343
|
async scanForFunctions() {
|
|
313
344
|
const [internalFunctions, userFunctions] = await Promise.all([
|
|
314
|
-
this.bundler.find(this.
|
|
345
|
+
this.bundler.find([this.internalDirectory]),
|
|
315
346
|
this.bundler.find(this.directories),
|
|
347
|
+
this.scanForDeployConfig(),
|
|
316
348
|
]);
|
|
317
349
|
const functions = [...internalFunctions, ...userFunctions];
|
|
318
350
|
const newFunctions = functions.filter((func) => {
|
|
@@ -327,7 +359,7 @@ export class EdgeFunctionsRegistry {
|
|
|
327
359
|
this.userFunctions = userFunctions;
|
|
328
360
|
return { all: functions, new: newFunctions, deleted: deletedFunctions };
|
|
329
361
|
}
|
|
330
|
-
async setupWatchers(
|
|
362
|
+
async setupWatchers() {
|
|
331
363
|
if (!this.configPath) {
|
|
332
364
|
return;
|
|
333
365
|
}
|
|
@@ -344,16 +376,16 @@ export class EdgeFunctionsRegistry {
|
|
|
344
376
|
// directories, they might be importing files that are located in
|
|
345
377
|
// parent directories. So we watch the entire project directory for
|
|
346
378
|
// changes.
|
|
347
|
-
await this.setupWatcherForDirectory(
|
|
379
|
+
await this.setupWatcherForDirectory();
|
|
348
380
|
}
|
|
349
|
-
async setupWatcherForDirectory(
|
|
381
|
+
async setupWatcherForDirectory() {
|
|
350
382
|
const ignored = [`${this.servePath}/**`];
|
|
351
|
-
const watcher = await watchDebounced(
|
|
383
|
+
const watcher = await watchDebounced(this.projectDir, {
|
|
352
384
|
ignored,
|
|
353
385
|
onAdd: () => this.checkForAddedOrDeletedFunctions(),
|
|
354
386
|
onChange: (paths) => this.handleFileChange(paths),
|
|
355
387
|
onUnlink: () => this.checkForAddedOrDeletedFunctions(),
|
|
356
388
|
});
|
|
357
|
-
this.directoryWatchers.set(
|
|
389
|
+
this.directoryWatchers.set(this.projectDir, watcher);
|
|
358
390
|
}
|
|
359
391
|
}
|
package/src/lib/exec-fetcher.js
CHANGED
|
@@ -4,7 +4,6 @@ import { fetchLatest, fetchVersion, newerVersion, updateAvailable } from 'gh-rel
|
|
|
4
4
|
// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module 'isex... Remove this comment to see the full error message
|
|
5
5
|
import isExe from 'isexe';
|
|
6
6
|
import { NETLIFYDEVWARN, error, getTerminalLink, log } from '../utils/command-helpers.js';
|
|
7
|
-
// @ts-expect-error TS(7034) FIXME: Variable 'execa' implicitly has type 'any' in some... Remove this comment to see the full error message
|
|
8
7
|
import execa from '../utils/execa.js';
|
|
9
8
|
const isWindows = () => process.platform === 'win32';
|
|
10
9
|
// @ts-expect-error TS(7031) FIXME: Binding element 'packageName' implicitly has an 'a... Remove this comment to see the full error message
|
|
@@ -47,7 +46,6 @@ pattern, }) => {
|
|
|
47
46
|
if (!exists) {
|
|
48
47
|
return true;
|
|
49
48
|
}
|
|
50
|
-
// @ts-expect-error TS(7005) FIXME: Variable 'execa' implicitly has an 'any' type.
|
|
51
49
|
const { stdout } = await execa(execPath, execArgs);
|
|
52
50
|
if (!stdout) {
|
|
53
51
|
return false;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { stdout } from 'process';
|
|
2
2
|
// @ts-expect-error TS(7016) FIXME: Could not find a declaration file for module '@net... Remove this comment to see the full error message
|
|
3
3
|
import { getBinaryPath as getFunctionsProxyPath } from '@netlify/local-functions-proxy';
|
|
4
|
-
// @ts-expect-error TS(7034) FIXME: Variable 'execa' implicitly has type 'any' in some... Remove this comment to see the full error message
|
|
5
4
|
import execa from '../../utils/execa.js';
|
|
6
5
|
// @ts-expect-error TS(7031) FIXME: Binding element 'binaryPath' implicitly has an 'an... Remove this comment to see the full error message
|
|
7
6
|
export const runFunctionsProxy = ({ binaryPath, context, directory, event, name, timeout }) => {
|
|
@@ -34,8 +33,7 @@ export const runFunctionsProxy = ({ binaryPath, context, directory, event, name,
|
|
|
34
33
|
'--timeout',
|
|
35
34
|
`${timeout}s`,
|
|
36
35
|
];
|
|
37
|
-
// @ts-expect-error TS(7005) FIXME: Variable 'execa' implicitly has an 'any' type.
|
|
38
36
|
const proxyProcess = execa(functionsProxyPath, parameters);
|
|
39
|
-
proxyProcess.stderr
|
|
37
|
+
proxyProcess.stderr?.pipe(stdout);
|
|
40
38
|
return proxyProcess;
|
|
41
39
|
};
|
|
@@ -51,7 +51,6 @@ export default class NetlifyFunction {
|
|
|
51
51
|
this.name = name;
|
|
52
52
|
this.displayName = displayName ?? name;
|
|
53
53
|
this.projectRoot = projectRoot;
|
|
54
|
-
// @ts-expect-error TS(2339) FIXME: Property 'runtime' does not exist on type 'Netlify... Remove this comment to see the full error message
|
|
55
54
|
this.runtime = runtime;
|
|
56
55
|
this.timeoutBackground = timeoutBackground;
|
|
57
56
|
this.timeoutSynchronous = timeoutSynchronous;
|
|
@@ -142,7 +141,9 @@ export default class NetlifyFunction {
|
|
|
142
141
|
return { includedFiles, srcFilesDiff };
|
|
143
142
|
}
|
|
144
143
|
catch (error) {
|
|
145
|
-
|
|
144
|
+
if (error instanceof Error) {
|
|
145
|
+
this.buildError = error;
|
|
146
|
+
}
|
|
146
147
|
return { error };
|
|
147
148
|
}
|
|
148
149
|
}
|
|
@@ -164,7 +165,6 @@ export default class NetlifyFunction {
|
|
|
164
165
|
async invoke(event = {}, context = {}) {
|
|
165
166
|
await this.buildQueue;
|
|
166
167
|
if (this.buildError) {
|
|
167
|
-
// @ts-expect-error TS(2339) FIXME: Property 'buildError' does not exist on type 'Netl... Remove this comment to see the full error message
|
|
168
168
|
return { result: null, error: { errorMessage: this.buildError.message } };
|
|
169
169
|
}
|
|
170
170
|
const timeout = this.isBackground ? this.timeoutBackground : this.timeoutSynchronous;
|
|
@@ -13,6 +13,7 @@ import runtimes from './runtimes/index.js';
|
|
|
13
13
|
export const DEFAULT_FUNCTION_URL_EXPRESSION = /^\/.netlify\/(functions|builders)\/([^/]+).*/;
|
|
14
14
|
const TYPES_PACKAGE = '@netlify/functions';
|
|
15
15
|
const ZIP_EXTENSION = '.zip';
|
|
16
|
+
const isInternalFunction = (func) => func.mainFile.includes(getPathInProject([INTERNAL_FUNCTIONS_FOLDER]));
|
|
16
17
|
/**
|
|
17
18
|
* @typedef {"buildError" | "extracted" | "loaded" | "missing-types-package" | "reloaded" | "reloading" | "removed"} FunctionEvent
|
|
18
19
|
*/
|
|
@@ -149,7 +150,6 @@ export class FunctionsRegistry {
|
|
|
149
150
|
? `rename the function file to ${chalk.underline(newFilename)}. Refer to https://ntl.fyi/functions-runtime for more information`
|
|
150
151
|
: `refer to https://ntl.fyi/functions-runtime`;
|
|
151
152
|
const warning = `The function is using the legacy CommonJS format. To start using ES modules, ${action}.`;
|
|
152
|
-
// @ts-expect-error TS(2322) FIXME: Type 'string' is not assignable to type 'never'.
|
|
153
153
|
FunctionsRegistry.logEvent(event, { func, warnings: [warning] });
|
|
154
154
|
}
|
|
155
155
|
else {
|
|
@@ -230,14 +230,7 @@ export class FunctionsRegistry {
|
|
|
230
230
|
}
|
|
231
231
|
/**
|
|
232
232
|
* Logs an event associated with functions.
|
|
233
|
-
*
|
|
234
|
-
* @param {FunctionEvent} event
|
|
235
|
-
* @param {object} data
|
|
236
|
-
* @param {NetlifyFunction} [data.func]
|
|
237
|
-
* @param {string[]} [data.warnings]
|
|
238
|
-
* @returns
|
|
239
233
|
*/
|
|
240
|
-
// @ts-expect-error TS(7006) FIXME: Parameter 'event' implicitly has an 'any' type.
|
|
241
234
|
static logEvent(event, { func, warnings = [] }) {
|
|
242
235
|
let warningsText = '';
|
|
243
236
|
if (warnings.length !== 0) {
|
|
@@ -280,18 +273,13 @@ export class FunctionsRegistry {
|
|
|
280
273
|
}
|
|
281
274
|
/**
|
|
282
275
|
* Adds a function to the registry
|
|
283
|
-
*
|
|
284
|
-
* @param {string} name
|
|
285
|
-
* @param {NetlifyFunction} funcBeforeHook
|
|
286
|
-
* @param {boolean} [isReload]
|
|
287
|
-
* @returns
|
|
288
276
|
*/
|
|
289
|
-
// @ts-expect-error TS(7006) FIXME: Parameter 'name' implicitly has an 'any' type.
|
|
290
277
|
async registerFunction(name, funcBeforeHook, isReload = false) {
|
|
291
278
|
const { runtime } = funcBeforeHook;
|
|
292
279
|
// The `onRegister` hook allows runtimes to modify the function before it's
|
|
293
280
|
// registered, or to prevent it from being registered altogether if the
|
|
294
281
|
// hook returns `null`.
|
|
282
|
+
// @ts-expect-error FIXME
|
|
295
283
|
const func = typeof runtime.onRegister === 'function' ? runtime.onRegister(funcBeforeHook) : funcBeforeHook;
|
|
296
284
|
if (func === null) {
|
|
297
285
|
return;
|
|
@@ -339,31 +327,24 @@ export class FunctionsRegistry {
|
|
|
339
327
|
/**
|
|
340
328
|
* A proxy to zip-it-and-ship-it's `listFunctions` method. It exists just so
|
|
341
329
|
* that we can mock it in tests.
|
|
342
|
-
* @param {Parameters<listFunctions>} args
|
|
343
|
-
* @returns
|
|
344
330
|
*/
|
|
345
|
-
// @ts-expect-error TS(7019) FIXME: Rest parameter 'args' implicitly has an 'any[]' ty... Remove this comment to see the full error message
|
|
346
331
|
// eslint-disable-next-line class-methods-use-this
|
|
347
332
|
async listFunctions(...args) {
|
|
348
|
-
// @ts-expect-error TS(2556) FIXME: A spread argument must either have a tuple type or... Remove this comment to see the full error message
|
|
349
333
|
return await listFunctions(...args);
|
|
350
334
|
}
|
|
351
335
|
/**
|
|
352
336
|
* Takes a list of directories and scans for functions. It keeps tracks of
|
|
353
337
|
* any functions in those directories that we've previously seen, and takes
|
|
354
338
|
* care of registering and unregistering functions as they come and go.
|
|
355
|
-
*
|
|
356
|
-
* @param {string[]} relativeDirs
|
|
357
339
|
*/
|
|
358
|
-
// @ts-expect-error TS(7006) FIXME: Parameter 'relativeDirs' implicitly has an 'any' t... Remove this comment to see the full error message
|
|
359
340
|
async scan(relativeDirs) {
|
|
360
|
-
|
|
361
|
-
|
|
341
|
+
const directories = relativeDirs
|
|
342
|
+
.filter((dir) => Boolean(dir))
|
|
343
|
+
.map((dir) => (isAbsolute(dir) ? dir : join(this.projectRoot, dir)));
|
|
362
344
|
// check after filtering to filter out [undefined] for example
|
|
363
345
|
if (directories.length === 0) {
|
|
364
346
|
return;
|
|
365
347
|
}
|
|
366
|
-
// @ts-expect-error TS(7006) FIXME: Parameter 'path' implicitly has an 'any' type.
|
|
367
348
|
await Promise.all(directories.map((path) => FunctionsRegistry.prepareDirectoryScan(path)));
|
|
368
349
|
const functions = await this.listFunctions(directories, {
|
|
369
350
|
featureFlags: {
|
|
@@ -373,10 +354,18 @@ export class FunctionsRegistry {
|
|
|
373
354
|
// @ts-expect-error TS(2339) FIXME: Property 'config' does not exist on type 'Function... Remove this comment to see the full error message
|
|
374
355
|
config: this.config.functions,
|
|
375
356
|
});
|
|
357
|
+
// user-defined functions take precedence over internal functions,
|
|
358
|
+
// so we want to ignore any internal functions where there's a user-defined one with the same name
|
|
359
|
+
const ignoredFunctions = new Set(functions
|
|
360
|
+
.filter((func) => isInternalFunction(func) &&
|
|
361
|
+
this.functions.has(func.name) &&
|
|
362
|
+
!isInternalFunction(this.functions.get(func.name)))
|
|
363
|
+
.map((func) => func.name));
|
|
376
364
|
// Before registering any functions, we look for any functions that were on
|
|
377
365
|
// the previous list but are missing from the new one. We unregister them.
|
|
378
366
|
const deletedFunctions = [...this.functions.values()].filter((oldFunc) => {
|
|
379
|
-
const isFound = functions.some((newFunc) => newFunc.name
|
|
367
|
+
const isFound = functions.some((newFunc) => ignoredFunctions.has(newFunc.name) ||
|
|
368
|
+
(newFunc.name === oldFunc.name && newFunc.mainFile === oldFunc.mainFile));
|
|
380
369
|
return !isFound;
|
|
381
370
|
});
|
|
382
371
|
await Promise.all(deletedFunctions.map((func) => this.unregisterFunction(func)));
|
|
@@ -386,6 +375,9 @@ export class FunctionsRegistry {
|
|
|
386
375
|
// where the last ones precede the previous ones. This is why
|
|
387
376
|
// we reverse the array so we get the right functions precedence in the CLI.
|
|
388
377
|
functions.reverse().map(async ({ displayName, mainFile, name, runtime: runtimeName }) => {
|
|
378
|
+
if (ignoredFunctions.has(name)) {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
389
381
|
const runtime = runtimes[runtimeName];
|
|
390
382
|
// If there is no matching runtime, it means this function is not yet
|
|
391
383
|
// supported in Netlify Dev.
|
|
@@ -400,7 +392,6 @@ export class FunctionsRegistry {
|
|
|
400
392
|
blobsContext: this.blobsContext,
|
|
401
393
|
// @ts-expect-error TS(2339) FIXME: Property 'config' does not exist on type 'Function... Remove this comment to see the full error message
|
|
402
394
|
config: this.config,
|
|
403
|
-
// @ts-expect-error TS(7006) FIXME: Parameter 'directory' implicitly has an 'any' type... Remove this comment to see the full error message
|
|
404
395
|
directory: directories.find((directory) => mainFile.startsWith(directory)),
|
|
405
396
|
mainFile,
|
|
406
397
|
name,
|
|
@@ -430,7 +421,6 @@ export class FunctionsRegistry {
|
|
|
430
421
|
}
|
|
431
422
|
FunctionsRegistry.logEvent('removed', { func });
|
|
432
423
|
});
|
|
433
|
-
// @ts-expect-error TS(7006) FIXME: Parameter 'path' implicitly has an 'any' type.
|
|
434
424
|
await Promise.all(directories.map((path) => this.setupDirectoryWatcher(path)));
|
|
435
425
|
}
|
|
436
426
|
/**
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { dirname, extname } from 'path';
|
|
2
2
|
import { platform } from 'process';
|
|
3
3
|
import { temporaryFile } from 'tempy';
|
|
4
|
-
// @ts-expect-error TS(7034) FIXME: Variable 'execa' implicitly has type 'any' in some... Remove this comment to see the full error message
|
|
5
4
|
import execa from '../../../../utils/execa.js';
|
|
6
5
|
import { runFunctionsProxy } from '../../local-proxy.js';
|
|
7
6
|
const isWindows = platform === 'win32';
|
|
@@ -9,7 +8,6 @@ export const name = 'go';
|
|
|
9
8
|
// @ts-expect-error TS(7031) FIXME: Binding element 'binaryPath' implicitly has an 'an... Remove this comment to see the full error message
|
|
10
9
|
const build = async ({ binaryPath, functionDirectory }) => {
|
|
11
10
|
try {
|
|
12
|
-
// @ts-expect-error TS(7005) FIXME: Variable 'execa' implicitly has an 'any' type.
|
|
13
11
|
await execa('go', ['build', '-o', binaryPath], { cwd: functionDirectory });
|
|
14
12
|
return { binaryPath, srcFiles: [functionDirectory] };
|
|
15
13
|
}
|
|
@@ -24,7 +22,6 @@ const build = async ({ binaryPath, functionDirectory }) => {
|
|
|
24
22
|
// @ts-expect-error TS(7031) FIXME: Binding element 'cwd' implicitly has an 'any' type... Remove this comment to see the full error message
|
|
25
23
|
const checkGoInstallation = async ({ cwd }) => {
|
|
26
24
|
try {
|
|
27
|
-
// @ts-expect-error TS(7005) FIXME: Variable 'execa' implicitly has an 'any' type.
|
|
28
25
|
await execa('go', ['version'], { cwd });
|
|
29
26
|
return true;
|
|
30
27
|
}
|
|
@@ -1,40 +1,6 @@
|
|
|
1
1
|
import * as go from './go/index.js';
|
|
2
2
|
import * as js from './js/index.js';
|
|
3
3
|
import * as rust from './rust/index.js';
|
|
4
|
-
/* eslint-enable import/no-namespace */
|
|
5
|
-
/**
|
|
6
|
-
* @callback BuildFunction
|
|
7
|
-
* @param {object} func
|
|
8
|
-
* @returns {Promise<{srcFiles: string[], buildPath?: string}>}
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* @callback GetBuildFunction
|
|
12
|
-
* @param {{ config: object, context: object, errorExit: function, func: object, functionsDirectory: string, projectRoot: string }} params
|
|
13
|
-
* @returns {Promise<BuildFunction>}
|
|
14
|
-
*/
|
|
15
|
-
/**
|
|
16
|
-
* @callback InvokeFunction
|
|
17
|
-
* @param {{ context: object, event: object, func: object, timeout: number }} params
|
|
18
|
-
* @returns {Promise<{ body: object, statusCode: number }>}
|
|
19
|
-
*/
|
|
20
|
-
/**
|
|
21
|
-
* @callback OnDirectoryScanFunction
|
|
22
|
-
* @param {{ directory: string }} params
|
|
23
|
-
* @returns {Promise<undefined>}
|
|
24
|
-
*/
|
|
25
|
-
/**
|
|
26
|
-
* @callback OnRegisterFunction
|
|
27
|
-
* @param {object} func
|
|
28
|
-
* @returns {object|null}
|
|
29
|
-
*/
|
|
30
|
-
/**
|
|
31
|
-
* @typedef {object} Runtime
|
|
32
|
-
* @property {GetBuildFunction} getBuildFunction
|
|
33
|
-
* @property {InvokeFunction} invokeFunction
|
|
34
|
-
* @property {OnDirectoryScanFunction} [onDirectoryScan]
|
|
35
|
-
* @property {OnRegisterFunction} [onRegister]
|
|
36
|
-
* @property {string} name
|
|
37
|
-
*/
|
|
38
4
|
const runtimes = {
|
|
39
5
|
[go.name]: go,
|
|
40
6
|
[js.name]: js,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { readFile } from 'fs/promises';
|
|
2
2
|
import { resolve } from 'path';
|
|
3
|
-
import
|
|
4
|
-
// @ts-expect-error TS(7034) FIXME: Variable 'execa' implicitly has type 'any' in some... Remove this comment to see the full error message
|
|
3
|
+
import { program } from 'commander';
|
|
5
4
|
import execa from '../../../../../utils/execa.js';
|
|
6
5
|
import { fileExistsAsync } from '../../../../fs.js';
|
|
7
6
|
import { memoizedBuild } from '../../../memoized-build.js';
|
|
@@ -15,18 +14,19 @@ export const detectNetlifyLambda = async function ({ packageJson } = {}) {
|
|
|
15
14
|
const matchingScripts = Object.entries(scripts).filter(([, script]) => script.match(/netlify-lambda\s+build/));
|
|
16
15
|
for (const [key, script] of matchingScripts) {
|
|
17
16
|
// E.g. ["netlify-lambda", "build", "functions/folder"]
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
// these are all valid options for netlify-lambda
|
|
18
|
+
program
|
|
19
|
+
.option('-s, --static')
|
|
20
|
+
.option('-c, --config [file]')
|
|
21
|
+
.option('-p, --port [number]')
|
|
22
|
+
.option('-b, --babelrc [file]')
|
|
23
|
+
.option('-t, --timeout [delay]');
|
|
24
|
+
program.parse(script.split(' ') ?? []);
|
|
24
25
|
// We are not interested in 'netlify-lambda' and 'build' commands
|
|
25
|
-
const functionDirectories =
|
|
26
|
+
const functionDirectories = program.args.filter((arg) => !['netlify-lambda', 'build'].includes(arg));
|
|
26
27
|
if (functionDirectories.length === 1) {
|
|
27
28
|
const srcFiles = [resolve(functionDirectories[0])];
|
|
28
29
|
const yarnExists = await fileExistsAsync('yarn.lock');
|
|
29
|
-
// @ts-expect-error TS(7005) FIXME: Variable 'execa' implicitly has an 'any' type.
|
|
30
30
|
const buildCommand = () => execa(yarnExists ? 'yarn' : 'npm', ['run', key]);
|
|
31
31
|
return {
|
|
32
32
|
build: async ({ cache = {} } = {}) => {
|
|
@@ -3,7 +3,6 @@ import { dirname, extname, join, resolve } from 'path';
|
|
|
3
3
|
import { platform } from 'process';
|
|
4
4
|
import { findUp } from 'find-up';
|
|
5
5
|
import toml from 'toml';
|
|
6
|
-
// @ts-expect-error TS(7034) FIXME: Variable 'execa' implicitly has type 'any' in some... Remove this comment to see the full error message
|
|
7
6
|
import execa from '../../../../utils/execa.js';
|
|
8
7
|
import { SERVE_FUNCTIONS_FOLDER } from '../../../../utils/functions/functions.js';
|
|
9
8
|
import { getPathInProject } from '../../../settings.js';
|
|
@@ -18,7 +17,6 @@ const build = async ({ func }) => {
|
|
|
18
17
|
const crateName = await getCrateName(functionDirectory);
|
|
19
18
|
const binaryName = `${crateName}${isWindows ? '.exe' : ''}`;
|
|
20
19
|
const binaryPath = join(targetDirectory, 'debug', binaryName);
|
|
21
|
-
// @ts-expect-error TS(7005) FIXME: Variable 'execa' implicitly has an 'any' type.
|
|
22
20
|
await execa('cargo', ['build', '--target-dir', targetDirectory], {
|
|
23
21
|
cwd: functionDirectory,
|
|
24
22
|
});
|
|
@@ -25,20 +25,31 @@ const buildClientContext = function (headers) {
|
|
|
25
25
|
const parts = headers.authorization.split(' ');
|
|
26
26
|
if (parts.length !== 2 || parts[0] !== 'Bearer')
|
|
27
27
|
return;
|
|
28
|
+
const identity = {
|
|
29
|
+
url: 'https://netlify-dev-locally-emulated-identity.netlify.com/.netlify/identity',
|
|
30
|
+
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzb3VyY2UiOiJuZXRsaWZ5IGRldiIsInRlc3REYXRhIjoiTkVUTElGWV9ERVZfTE9DQUxMWV9FTVVMQVRFRF9JREVOVElUWSJ9.2eSDqUOZAOBsx39FHFePjYj12k0LrxldvGnlvDu3GMI',
|
|
31
|
+
// you can decode this with https://jwt.io/
|
|
32
|
+
// just says
|
|
33
|
+
// {
|
|
34
|
+
// "source": "netlify dev",
|
|
35
|
+
// "testData": "NETLIFY_DEV_LOCALLY_EMULATED_IDENTITY"
|
|
36
|
+
// }
|
|
37
|
+
};
|
|
38
|
+
// This data is available on both the context root and under custom.netlify for retro-compatibility.
|
|
39
|
+
// In the future it will only be available in custom.netlify.
|
|
40
|
+
// @ts-expect-error
|
|
41
|
+
const user = jwtDecode(parts[1]);
|
|
42
|
+
const netlifyContext = JSON.stringify({
|
|
43
|
+
identity: identity,
|
|
44
|
+
user: user,
|
|
45
|
+
});
|
|
28
46
|
try {
|
|
29
47
|
return {
|
|
30
|
-
identity:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// just says
|
|
35
|
-
// {
|
|
36
|
-
// "source": "netlify dev",
|
|
37
|
-
// "testData": "NETLIFY_DEV_LOCALLY_EMULATED_IDENTITY"
|
|
38
|
-
// }
|
|
48
|
+
identity: identity,
|
|
49
|
+
user: user,
|
|
50
|
+
custom: {
|
|
51
|
+
netlify: Buffer.from(netlifyContext).toString('base64'),
|
|
39
52
|
},
|
|
40
|
-
// @ts-expect-error
|
|
41
|
-
user: jwtDecode(parts[1]),
|
|
42
53
|
};
|
|
43
54
|
}
|
|
44
55
|
catch {
|
|
@@ -114,7 +125,7 @@ export const createHandler = function (options) {
|
|
|
114
125
|
'client-ip': [remoteAddress],
|
|
115
126
|
'x-nf-client-connection-ip': [remoteAddress],
|
|
116
127
|
'x-nf-account-id': [options.accountId],
|
|
117
|
-
'x-nf-site-id': [options?.siteInfo?.id
|
|
128
|
+
'x-nf-site-id': [options?.siteInfo?.id ?? 'unlinked'],
|
|
118
129
|
[efHeaders.Geo]: Buffer.from(JSON.stringify(geoLocation)).toString('base64'),
|
|
119
130
|
}).reduce((prev, [key, value]) => ({ ...prev, [key]: Array.isArray(value) ? value : [value] }), {});
|
|
120
131
|
const rawQuery = new URLSearchParams(requestQuery).toString();
|
|
@@ -217,7 +228,6 @@ const getFunctionsServer = (options) => {
|
|
|
217
228
|
// @ts-expect-error TS(7006) FIXME: Parameter 'options' implicitly has an 'any' type.
|
|
218
229
|
export const startFunctionsServer = async (options) => {
|
|
219
230
|
const { blobsContext, capabilities, command, config, debug, loadDistFunctions, settings, site, siteInfo, siteUrl, timeouts, } = options;
|
|
220
|
-
// @ts-expect-error TS(2345) FIXME: Argument of type '{ base: any; }' is not assignabl... Remove this comment to see the full error message
|
|
221
231
|
const internalFunctionsDir = await getInternalFunctionsDir({ base: site.root });
|
|
222
232
|
const functionsDirectories = [];
|
|
223
233
|
let manifest;
|
package/src/utils/banner.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import boxen from 'boxen';
|
|
2
2
|
import { chalk, log, NETLIFYDEVLOG } from './command-helpers.js';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const banner = chalk.bold(`${NETLIFYDEVLOG} Server now ready on ${url}`);
|
|
3
|
+
export const printBanner = (options) => {
|
|
4
|
+
const banner = chalk.bold(`${NETLIFYDEVLOG} Server now ready on ${options.url}`);
|
|
6
5
|
log(boxen(banner, {
|
|
7
6
|
padding: 1,
|
|
8
7
|
margin: 1,
|
package/src/utils/build-info.js
CHANGED
|
@@ -3,28 +3,19 @@ import inquirer from 'inquirer';
|
|
|
3
3
|
import { chalk, log } from './command-helpers.js';
|
|
4
4
|
/**
|
|
5
5
|
* Filters the inquirer settings based on the input
|
|
6
|
-
* @param {ReturnType<typeof formatSettingsArrForInquirer>} scriptInquirerOptions
|
|
7
|
-
* @param {string} input
|
|
8
6
|
*/
|
|
9
|
-
// @ts-expect-error TS(7006) FIXME: Parameter 'scriptInquirerOptions' implicitly has a... Remove this comment to see the full error message
|
|
10
7
|
const filterSettings = function (scriptInquirerOptions, input) {
|
|
11
|
-
// @ts-expect-error TS(7006) FIXME: Parameter 'scriptInquirerOption' implicitly has an... Remove this comment to see the full error message
|
|
12
8
|
const filterOptions = scriptInquirerOptions.map((scriptInquirerOption) => scriptInquirerOption.name);
|
|
13
9
|
// TODO: remove once https://github.com/sindresorhus/eslint-plugin-unicorn/issues/1394 is fixed
|
|
14
10
|
// eslint-disable-next-line unicorn/no-array-method-this-argument
|
|
15
11
|
const filteredSettings = fuzzy.filter(input, filterOptions);
|
|
16
12
|
const filteredSettingNames = new Set(filteredSettings.map((filteredSetting) => (input ? filteredSetting.string : filteredSetting)));
|
|
17
|
-
// @ts-expect-error TS(7006) FIXME: Parameter 't' implicitly has an 'any' type.
|
|
18
13
|
return scriptInquirerOptions.filter((t) => filteredSettingNames.has(t.name));
|
|
19
14
|
};
|
|
20
|
-
/** @typedef {import('@netlify/build-info').Settings} Settings */
|
|
21
15
|
/**
|
|
22
|
-
*
|
|
23
|
-
* @param {'dev' | 'build'} type The type of command (dev or build)
|
|
16
|
+
* Formats the settings to present it as an array for the inquirer input so that it can choose one
|
|
24
17
|
*/
|
|
25
|
-
// @ts-expect-error TS(7006) FIXME: Parameter 'settings' implicitly has an 'any' type.
|
|
26
18
|
const formatSettingsArrForInquirer = function (settings, type = 'dev') {
|
|
27
|
-
// @ts-expect-error TS(7006) FIXME: Parameter 'setting' implicitly has an 'any' type.
|
|
28
19
|
return settings.map((setting) => {
|
|
29
20
|
const cmd = type === 'dev' ? setting.devCommand : setting.buildCommand;
|
|
30
21
|
return {
|
|
@@ -35,13 +26,26 @@ const formatSettingsArrForInquirer = function (settings, type = 'dev') {
|
|
|
35
26
|
});
|
|
36
27
|
};
|
|
37
28
|
/**
|
|
38
|
-
*
|
|
29
|
+
* Detects and filters the build setting for a project and a command
|
|
30
|
+
*/
|
|
31
|
+
export async function detectBuildSettings(command) {
|
|
32
|
+
const { project, workspacePackage } = command;
|
|
33
|
+
const buildSettings = await project.getBuildSettings(project.workspace ? workspacePackage : '');
|
|
34
|
+
return buildSettings
|
|
35
|
+
.filter((setting) => {
|
|
36
|
+
if (project.workspace && project.relativeBaseDirectory && setting.packagePath) {
|
|
37
|
+
return project.relativeBaseDirectory.startsWith(setting.packagePath);
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
})
|
|
41
|
+
.filter((setting) => setting.devCommand);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Uses `@netlify/build-info` to detect the dev settings and port based on the framework
|
|
39
45
|
* and the build system that is used.
|
|
40
|
-
* @param
|
|
41
|
-
* @param
|
|
42
|
-
* @returns {Promise<Settings | undefined>}
|
|
46
|
+
* @param command The base command
|
|
47
|
+
* @param type The type of command (dev or build)
|
|
43
48
|
*/
|
|
44
|
-
// @ts-expect-error TS(7006) FIXME: Parameter 'command' implicitly has an 'any' type.
|
|
45
49
|
export const detectFrameworkSettings = async (command, type = 'dev') => {
|
|
46
50
|
const { relConfigFilePath } = command.netlify;
|
|
47
51
|
const settings = await detectBuildSettings(command);
|
|
@@ -49,16 +53,14 @@ export const detectFrameworkSettings = async (command, type = 'dev') => {
|
|
|
49
53
|
return settings[0];
|
|
50
54
|
}
|
|
51
55
|
if (settings.length > 1) {
|
|
52
|
-
|
|
56
|
+
// multiple matching detectors, make the user choose
|
|
53
57
|
const scriptInquirerOptions = formatSettingsArrForInquirer(settings, type);
|
|
54
|
-
/** @type {{chosenSettings: Settings}} */
|
|
55
58
|
const { chosenSettings } = await inquirer.prompt({
|
|
56
59
|
name: 'chosenSettings',
|
|
57
60
|
message: `Multiple possible ${type} commands found`,
|
|
58
|
-
// @ts-expect-error
|
|
61
|
+
// @ts-expect-error is not known by the types as it uses the autocomplete plugin
|
|
59
62
|
type: 'autocomplete',
|
|
60
|
-
|
|
61
|
-
source(/** @type {string} */ _, input = '') {
|
|
63
|
+
source(_, input = '') {
|
|
62
64
|
if (!input)
|
|
63
65
|
return scriptInquirerOptions;
|
|
64
66
|
// only show filtered results
|
|
@@ -78,22 +80,3 @@ command = "${chosenSettings.devCommand}"
|
|
|
78
80
|
return chosenSettings;
|
|
79
81
|
}
|
|
80
82
|
};
|
|
81
|
-
/**
|
|
82
|
-
* Detects and filters the build setting for a project and a command
|
|
83
|
-
* @param {import('../commands/base-command.js').default} command
|
|
84
|
-
*/
|
|
85
|
-
// @ts-expect-error TS(7006) FIXME: Parameter 'command' implicitly has an 'any' type.
|
|
86
|
-
export const detectBuildSettings = async (command) => {
|
|
87
|
-
const { project, workspacePackage } = command;
|
|
88
|
-
const buildSettings = await project.getBuildSettings(project.workspace ? workspacePackage : '');
|
|
89
|
-
return (buildSettings
|
|
90
|
-
// @ts-expect-error TS(7006) FIXME: Parameter 'setting' implicitly has an 'any' type.
|
|
91
|
-
.filter((setting) => {
|
|
92
|
-
if (project.workspace && project.relativeBaseDirectory && setting.packagePath) {
|
|
93
|
-
return project.relativeBaseDirectory.startsWith(setting.packagePath);
|
|
94
|
-
}
|
|
95
|
-
return true;
|
|
96
|
-
})
|
|
97
|
-
// @ts-expect-error TS(7006) FIXME: Parameter 'setting' implicitly has an 'any' type.
|
|
98
|
-
.filter((setting) => setting.devCommand));
|
|
99
|
-
};
|
|
@@ -158,22 +158,14 @@ export const warn = (message = '') => {
|
|
|
158
158
|
const bang = chalk.yellow(BANG);
|
|
159
159
|
log(` ${bang} Warning: ${message}`);
|
|
160
160
|
};
|
|
161
|
-
/**
|
|
162
|
-
* throws an error or log it
|
|
163
|
-
* @param {unknown} message
|
|
164
|
-
* @param {object} [options]
|
|
165
|
-
* @param {boolean} [options.exit]
|
|
166
|
-
*/
|
|
161
|
+
/** Throws an error or logs it */
|
|
167
162
|
export const error = (message = '', options = {}) => {
|
|
168
|
-
const err =
|
|
169
|
-
// @ts-expect-error TS(2358) FIXME: The left-hand side of an 'instanceof' expression m... Remove this comment to see the full error message
|
|
170
|
-
message instanceof Error
|
|
163
|
+
const err = message instanceof Error
|
|
171
164
|
? message
|
|
172
165
|
: // eslint-disable-next-line unicorn/no-nested-ternary
|
|
173
166
|
typeof message === 'string'
|
|
174
167
|
? new Error(message)
|
|
175
|
-
:
|
|
176
|
-
// @ts-expect-error TS(2339) FIXME: Property 'exit' does not exist on type '{}'.
|
|
168
|
+
: { message, stack: undefined, name: 'Error' };
|
|
177
169
|
if (options.exit === false) {
|
|
178
170
|
const bang = chalk.red(BANG);
|
|
179
171
|
if (process.env.DEBUG) {
|