netlify-cli 17.13.0 → 17.13.1

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/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.0",
4
+ "version": "17.13.1",
5
5
  "author": "Netlify Inc.",
6
6
  "type": "module",
7
7
  "engines": {
@@ -46,12 +46,12 @@
46
46
  "@bugsnag/js": "7.20.2",
47
47
  "@fastify/static": "6.10.2",
48
48
  "@netlify/blobs": "6.3.1",
49
- "@netlify/build": "29.31.0",
50
- "@netlify/build-info": "7.11.3",
51
- "@netlify/config": "20.10.0",
49
+ "@netlify/build": "29.31.5",
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.1",
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.11",
120
- "netlify-headers-parser": "7.1.2",
121
- "netlify-redirect-parser": "14.2.0",
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",
@@ -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
- this.dependencyPaths = new Map();
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.functionPaths = new Map();
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
- // Creating a Map from `this.functions` that maps function paths to function
267
- // names. This allows us to match modules against functions in O(1) time as
268
- // opposed to O(n).
269
- // eslint-disable-next-line unicorn/prefer-spread
270
- const functionPaths = new Map(Array.from(this.functions, (func) => [func.path, func.name]));
271
- // Mapping file URLs to names of functions that use them as dependencies.
272
- const dependencyPaths = new Map();
273
- const { modules } = graph;
274
- modules.forEach(({ dependencies = [], specifier }) => {
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
- const functionMatch = functionPaths.get(path);
280
- if (!functionMatch) {
281
- return;
302
+ modulesByPath.set(path, module);
303
+ const functionName = this.functionPaths.get(path);
304
+ if (functionName) {
305
+ functionModules.add({ functionName, module });
282
306
  }
283
- dependencies.forEach((dependency) => {
284
- // We're interested in tracking local dependencies, so we only look at
285
- // specifiers with the `file:` protocol.
286
- if (dependency.code === undefined ||
287
- typeof dependency.code.specifier !== 'string' ||
288
- !dependency.code.specifier.startsWith('file://')) {
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}/**`];
@@ -0,0 +1,11 @@
1
+ export class MultiMap {
2
+ constructor() {
3
+ this.map = new Map();
4
+ }
5
+ add(key, value) {
6
+ this.map.set(key, [...(this.map.get(key) ?? []), value]);
7
+ }
8
+ get(key) {
9
+ return this.map.get(key) ?? [];
10
+ }
11
+ }