netlify-cli 17.13.0 → 17.13.2
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 +9355 -11864
- package/package.json +9 -9
- package/src/commands/dev/dev.js +7 -6
- package/src/commands/serve/serve.js +2 -1
- package/src/lib/blobs/blobs.js +7 -0
- package/src/lib/edge-functions/registry.js +58 -40
- package/src/lib/functions/runtimes/js/index.js +9 -0
- package/src/utils/multimap.js +11 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify-cli",
|
|
3
3
|
"description": "Netlify command line tool",
|
|
4
|
-
"version": "17.13.
|
|
4
|
+
"version": "17.13.2",
|
|
5
5
|
"author": "Netlify Inc.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -45,13 +45,13 @@
|
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@bugsnag/js": "7.20.2",
|
|
47
47
|
"@fastify/static": "6.10.2",
|
|
48
|
-
"@netlify/blobs": "6.
|
|
49
|
-
"@netlify/build": "29.
|
|
50
|
-
"@netlify/build-info": "7.11.
|
|
51
|
-
"@netlify/config": "20.10.
|
|
48
|
+
"@netlify/blobs": "6.4.1",
|
|
49
|
+
"@netlify/build": "29.32.0",
|
|
50
|
+
"@netlify/build-info": "7.11.4",
|
|
51
|
+
"@netlify/config": "20.10.1",
|
|
52
52
|
"@netlify/edge-bundler": "11.0.0",
|
|
53
53
|
"@netlify/local-functions-proxy": "1.1.1",
|
|
54
|
-
"@netlify/zip-it-and-ship-it": "9.28.
|
|
54
|
+
"@netlify/zip-it-and-ship-it": "9.28.2",
|
|
55
55
|
"@octokit/rest": "19.0.13",
|
|
56
56
|
"ansi-escapes": "6.2.0",
|
|
57
57
|
"ansi-styles": "6.2.1",
|
|
@@ -116,9 +116,9 @@
|
|
|
116
116
|
"log-symbols": "5.1.0",
|
|
117
117
|
"log-update": "5.0.1",
|
|
118
118
|
"multiparty": "4.2.3",
|
|
119
|
-
"netlify": "13.1.
|
|
120
|
-
"netlify-headers-parser": "7.1.
|
|
121
|
-
"netlify-redirect-parser": "14.2.
|
|
119
|
+
"netlify": "13.1.12",
|
|
120
|
+
"netlify-headers-parser": "7.1.3",
|
|
121
|
+
"netlify-redirect-parser": "14.2.1",
|
|
122
122
|
"netlify-redirector": "0.5.0",
|
|
123
123
|
"node-fetch": "2.6.12",
|
|
124
124
|
"node-version-alias": "3.4.1",
|
package/src/commands/dev/dev.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import process from 'process';
|
|
2
2
|
import { Option } from 'commander';
|
|
3
|
-
import { getBlobsContext } from '../../lib/blobs/blobs.js';
|
|
3
|
+
import { BLOBS_CONTEXT_VARIABLE, encodeBlobsContext, getBlobsContext } from '../../lib/blobs/blobs.js';
|
|
4
4
|
import { promptEditorHelper } from '../../lib/edge-functions/editor-helper.js';
|
|
5
5
|
import { startFunctionsServer } from '../../lib/functions/server.js';
|
|
6
6
|
import { printBanner } from '../../utils/banner.js';
|
|
@@ -76,6 +76,12 @@ export const dev = async (options, command) => {
|
|
|
76
76
|
};
|
|
77
77
|
let { env } = cachedConfig;
|
|
78
78
|
env.NETLIFY_DEV = { sources: ['internal'], value: 'true' };
|
|
79
|
+
const blobsContext = await getBlobsContext({
|
|
80
|
+
debug: options.debug,
|
|
81
|
+
projectRoot: command.workingDir,
|
|
82
|
+
siteID: site.id ?? 'unknown-site-id',
|
|
83
|
+
});
|
|
84
|
+
env[BLOBS_CONTEXT_VARIABLE] = { sources: ['internal'], value: encodeBlobsContext(blobsContext) };
|
|
79
85
|
if (!options.offline && siteInfo.use_envelope) {
|
|
80
86
|
env = await getEnvelopeEnv({ api, context: options.context, env, siteInfo });
|
|
81
87
|
log(`${NETLIFYDEVLOG} Injecting environment variable values for ${chalk.yellow('all scopes')}`);
|
|
@@ -117,11 +123,6 @@ export const dev = async (options, command) => {
|
|
|
117
123
|
DEPLOY_URL: url,
|
|
118
124
|
},
|
|
119
125
|
});
|
|
120
|
-
const blobsContext = await getBlobsContext({
|
|
121
|
-
debug: options.debug,
|
|
122
|
-
projectRoot: command.workingDir,
|
|
123
|
-
siteID: site.id ?? 'unknown-site-id',
|
|
124
|
-
});
|
|
125
126
|
const functionsRegistry = await startFunctionsServer({
|
|
126
127
|
api,
|
|
127
128
|
blobsContext,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import process from 'process';
|
|
2
|
-
import { getBlobsContext } from '../../lib/blobs/blobs.js';
|
|
2
|
+
import { BLOBS_CONTEXT_VARIABLE, encodeBlobsContext, getBlobsContext } from '../../lib/blobs/blobs.js';
|
|
3
3
|
import { promptEditorHelper } from '../../lib/edge-functions/editor-helper.js';
|
|
4
4
|
import { startFunctionsServer } from '../../lib/functions/server.js';
|
|
5
5
|
import { printBanner } from '../../utils/banner.js';
|
|
@@ -70,6 +70,7 @@ export const serve = async (options, command) => {
|
|
|
70
70
|
projectRoot: command.workingDir,
|
|
71
71
|
siteID: site.id ?? 'unknown-site-id',
|
|
72
72
|
});
|
|
73
|
+
process.env[BLOBS_CONTEXT_VARIABLE] = encodeBlobsContext(blobsContext);
|
|
73
74
|
const functionsRegistry = await startFunctionsServer({
|
|
74
75
|
api,
|
|
75
76
|
blobsContext,
|
package/src/lib/blobs/blobs.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
1
2
|
import path from 'path';
|
|
2
3
|
import { BlobsServer } from '@netlify/blobs';
|
|
3
4
|
import { v4 as uuidv4 } from 'uuid';
|
|
4
5
|
import { log, NETLIFYDEVLOG } from '../../utils/command-helpers.js';
|
|
5
6
|
import { getPathInProject } from '../settings.js';
|
|
6
7
|
let hasPrintedLocalBlobsNotice = false;
|
|
8
|
+
export const BLOBS_CONTEXT_VARIABLE = 'NETLIFY_BLOBS_CONTEXT';
|
|
7
9
|
const printLocalBlobsNotice = () => {
|
|
8
10
|
if (hasPrintedLocalBlobsNotice) {
|
|
9
11
|
return;
|
|
@@ -39,3 +41,8 @@ export const getBlobsContext = async ({ debug, projectRoot, siteID }) => {
|
|
|
39
41
|
};
|
|
40
42
|
return context;
|
|
41
43
|
};
|
|
44
|
+
/**
|
|
45
|
+
* Returns a Base-64, JSON-encoded representation of the Blobs context. This is
|
|
46
|
+
* the format that the `@netlify/blobs` package expects to find the context in.
|
|
47
|
+
*/
|
|
48
|
+
export const encodeBlobsContext = (context) => Buffer.from(JSON.stringify(context)).toString('base64');
|
|
@@ -2,20 +2,49 @@ import { readFile } from 'fs/promises';
|
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { NETLIFYDEVERR, NETLIFYDEVLOG, NETLIFYDEVWARN, nonNullable, chalk, log, warn, watchDebounced, isNodeError, } from '../../utils/command-helpers.js';
|
|
5
|
+
import { MultiMap } from '../../utils/multimap.js';
|
|
5
6
|
import { getPathInProject } from '../settings.js';
|
|
6
7
|
import { INTERNAL_EDGE_FUNCTIONS_FOLDER } from './consts.js';
|
|
7
8
|
const featureFlags = { edge_functions_correct_order: true };
|
|
9
|
+
/**
|
|
10
|
+
* Helper method which, given a edge bundler graph module and an index of modules by path, traverses its dependency tree
|
|
11
|
+
* and returns an array of all of ist local dependencies
|
|
12
|
+
*/
|
|
13
|
+
function traverseLocalDependencies({ dependencies = [] }, modulesByPath) {
|
|
14
|
+
return dependencies.flatMap((dependency) => {
|
|
15
|
+
// We're interested in tracking local dependencies, so we only look at
|
|
16
|
+
// specifiers with the `file:` protocol.
|
|
17
|
+
if (dependency.code === undefined ||
|
|
18
|
+
typeof dependency.code.specifier !== 'string' ||
|
|
19
|
+
!dependency.code.specifier.startsWith('file://')) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
const { specifier: dependencyURL } = dependency.code;
|
|
23
|
+
const dependencyPath = fileURLToPath(dependencyURL);
|
|
24
|
+
const dependencyModule = modulesByPath.get(dependencyPath);
|
|
25
|
+
// No module indexed for this dependency
|
|
26
|
+
if (dependencyModule === undefined) {
|
|
27
|
+
return [dependencyPath];
|
|
28
|
+
}
|
|
29
|
+
// Keep traversing the child dependencies and return the current dependency path
|
|
30
|
+
return [...traverseLocalDependencies(dependencyModule, modulesByPath), dependencyPath];
|
|
31
|
+
});
|
|
32
|
+
}
|
|
8
33
|
export class EdgeFunctionsRegistry {
|
|
9
34
|
constructor({ bundler, config, configPath, directories, env, getUpdatedConfig, importMapFromTOML, projectDir, runIsolate, servePath, }) {
|
|
10
35
|
this.buildError = null;
|
|
11
36
|
this.declarationsFromDeployConfig = [];
|
|
12
|
-
|
|
37
|
+
// Mapping file URLs to names of functions that use them as dependencies.
|
|
38
|
+
this.dependencyPaths = new MultiMap();
|
|
13
39
|
this.directoryWatchers = new Map();
|
|
14
|
-
this.
|
|
40
|
+
this.userFunctions = [];
|
|
15
41
|
this.internalFunctions = [];
|
|
42
|
+
// a Map from `this.functions` that maps function paths to function
|
|
43
|
+
// names. This allows us to match modules against functions in O(1) time as
|
|
44
|
+
// opposed to O(n).
|
|
45
|
+
this.functionPaths = new Map();
|
|
16
46
|
this.manifest = null;
|
|
17
47
|
this.routes = [];
|
|
18
|
-
this.userFunctions = [];
|
|
19
48
|
this.bundler = bundler;
|
|
20
49
|
this.configPath = configPath;
|
|
21
50
|
this.directories = directories;
|
|
@@ -26,12 +55,6 @@ export class EdgeFunctionsRegistry {
|
|
|
26
55
|
this.importMapFromTOML = importMapFromTOML;
|
|
27
56
|
this.declarationsFromTOML = EdgeFunctionsRegistry.getDeclarationsFromTOML(config);
|
|
28
57
|
this.env = EdgeFunctionsRegistry.getEnvironmentVariables(env);
|
|
29
|
-
this.buildError = null;
|
|
30
|
-
this.directoryWatchers = new Map();
|
|
31
|
-
this.dependencyPaths = new Map();
|
|
32
|
-
this.functionPaths = new Map();
|
|
33
|
-
this.userFunctions = [];
|
|
34
|
-
this.internalFunctions = [];
|
|
35
58
|
this.initialScan = this.doInitialScan();
|
|
36
59
|
this.setupWatchers();
|
|
37
60
|
}
|
|
@@ -263,39 +286,32 @@ export class EdgeFunctionsRegistry {
|
|
|
263
286
|
}
|
|
264
287
|
return;
|
|
265
288
|
}
|
|
266
|
-
|
|
267
|
-
//
|
|
268
|
-
|
|
269
|
-
//
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
289
|
+
this.dependencyPaths = new MultiMap();
|
|
290
|
+
// Mapping file URLs to modules. Used by the traversal function.
|
|
291
|
+
const modulesByPath = new Map();
|
|
292
|
+
// a set of edge function modules that we'll use to start traversing the dependency tree from
|
|
293
|
+
const functionModules = new Set();
|
|
294
|
+
graph.modules.forEach((module) => {
|
|
295
|
+
// We're interested in tracking local dependencies, so we only look at
|
|
296
|
+
// specifiers with the `file:` protocol.
|
|
297
|
+
const { specifier } = module;
|
|
275
298
|
if (!specifier.startsWith('file://')) {
|
|
276
299
|
return;
|
|
277
300
|
}
|
|
278
301
|
const path = fileURLToPath(specifier);
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
302
|
+
modulesByPath.set(path, module);
|
|
303
|
+
const functionName = this.functionPaths.get(path);
|
|
304
|
+
if (functionName) {
|
|
305
|
+
functionModules.add({ functionName, module });
|
|
282
306
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
const { specifier: dependencyURL } = dependency.code;
|
|
292
|
-
const dependencyPath = fileURLToPath(dependencyURL);
|
|
293
|
-
const functions = dependencyPaths.get(dependencyPath) || [];
|
|
294
|
-
dependencyPaths.set(dependencyPath, [...functions, functionMatch]);
|
|
307
|
+
});
|
|
308
|
+
// We start from our functions and we traverse through their dependency tree
|
|
309
|
+
functionModules.forEach(({ functionName, module }) => {
|
|
310
|
+
const traversedPaths = traverseLocalDependencies(module, modulesByPath);
|
|
311
|
+
traversedPaths.forEach((dependencyPath) => {
|
|
312
|
+
this.dependencyPaths.add(dependencyPath, functionName);
|
|
295
313
|
});
|
|
296
314
|
});
|
|
297
|
-
this.dependencyPaths = dependencyPaths;
|
|
298
|
-
this.functionPaths = functionPaths;
|
|
299
315
|
}
|
|
300
316
|
/**
|
|
301
317
|
* Thin wrapper for `#runIsolate` that skips running a build and returns an
|
|
@@ -357,9 +373,16 @@ export class EdgeFunctionsRegistry {
|
|
|
357
373
|
});
|
|
358
374
|
this.internalFunctions = internalFunctions;
|
|
359
375
|
this.userFunctions = userFunctions;
|
|
376
|
+
// eslint-disable-next-line unicorn/prefer-spread
|
|
377
|
+
this.functionPaths = new Map(Array.from(this.functions, (func) => [func.path, func.name]));
|
|
360
378
|
return { all: functions, new: newFunctions, deleted: deletedFunctions };
|
|
361
379
|
}
|
|
362
380
|
async setupWatchers() {
|
|
381
|
+
// While functions are guaranteed to be inside one of the configured
|
|
382
|
+
// directories, they might be importing files that are located in
|
|
383
|
+
// parent directories. So we watch the entire project directory for
|
|
384
|
+
// changes.
|
|
385
|
+
await this.setupWatcherForDirectory();
|
|
363
386
|
if (!this.configPath) {
|
|
364
387
|
return;
|
|
365
388
|
}
|
|
@@ -372,11 +395,6 @@ export class EdgeFunctionsRegistry {
|
|
|
372
395
|
await this.checkForAddedOrDeletedFunctions();
|
|
373
396
|
},
|
|
374
397
|
});
|
|
375
|
-
// While functions are guaranteed to be inside one of the configured
|
|
376
|
-
// directories, they might be importing files that are located in
|
|
377
|
-
// parent directories. So we watch the entire project directory for
|
|
378
|
-
// changes.
|
|
379
|
-
await this.setupWatcherForDirectory();
|
|
380
398
|
}
|
|
381
399
|
async setupWatcherForDirectory() {
|
|
382
400
|
const ignored = [`${this.servePath}/**`];
|
|
@@ -3,6 +3,7 @@ import { dirname } from 'path';
|
|
|
3
3
|
import { pathToFileURL } from 'url';
|
|
4
4
|
import { Worker } from 'worker_threads';
|
|
5
5
|
import lambdaLocal from 'lambda-local';
|
|
6
|
+
import { BLOBS_CONTEXT_VARIABLE } from '../../../blobs/blobs.js';
|
|
6
7
|
import detectNetlifyLambdaBuilder from './builders/netlify-lambda.js';
|
|
7
8
|
import detectZisiBuilder, { parseFunctionForMetadata } from './builders/zisi.js';
|
|
8
9
|
import { SECONDS_TO_MILLISECONDS } from './constants.js';
|
|
@@ -83,6 +84,14 @@ export const invokeFunctionDirectly = async ({ context, event, func, timeout })
|
|
|
83
84
|
const lambdaPath = func.buildData?.buildPath ?? func.mainFile;
|
|
84
85
|
const result = await lambdaLocal.execute({
|
|
85
86
|
clientContext: JSON.stringify(context),
|
|
87
|
+
environment: {
|
|
88
|
+
// We've set the Blobs context on the parent process, which means it will
|
|
89
|
+
// be available to the Lambda. This would be inconsistent with production
|
|
90
|
+
// where only V2 functions get the context injected. To fix it, unset the
|
|
91
|
+
// context variable before invoking the function.
|
|
92
|
+
// This has the side-effect of also removing the variable from `process.env`.
|
|
93
|
+
[BLOBS_CONTEXT_VARIABLE]: undefined,
|
|
94
|
+
},
|
|
86
95
|
event,
|
|
87
96
|
lambdaPath,
|
|
88
97
|
timeoutMs: timeout * SECONDS_TO_MILLISECONDS,
|