dependency-cruiser 17.2.0 → 17.3.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.
Files changed (45) hide show
  1. package/package.json +54 -61
  2. package/src/cache/content-strategy.mjs +9 -9
  3. package/src/cache/find-content-changes.mjs +1 -0
  4. package/src/cache/options-compatible.mjs +2 -0
  5. package/src/cli/index.mjs +10 -15
  6. package/src/cli/init-config/build-config.mjs +15 -0
  7. package/src/cli/init-config/config-template.mjs +7 -0
  8. package/src/cli/init-config/get-user-input.mjs +8 -1
  9. package/src/cli/init-config/index.mjs +1 -0
  10. package/src/cli/init-config/normalize-init-options.mjs +5 -5
  11. package/src/enrich/derive/{dependents/index.mjs → dependents.mjs} +9 -2
  12. package/src/enrich/derive/folders/aggregate-to-folders.mjs +7 -7
  13. package/src/enrich/derive/reachable.mjs +18 -14
  14. package/src/enrich/enrich-modules.mjs +1 -1
  15. package/src/enrich/summarize/summarize-options.mjs +1 -0
  16. package/src/extract/acorn/estree-helpers.mjs +44 -22
  17. package/src/extract/acorn/extract-amd-deps.mjs +24 -18
  18. package/src/extract/acorn/extract-cjs-deps.mjs +43 -15
  19. package/src/extract/acorn/extract-es6-deps.mjs +6 -3
  20. package/src/extract/acorn/extract.mjs +13 -2
  21. package/src/extract/gather-initial-sources.mjs +16 -14
  22. package/src/extract/index.mjs +38 -42
  23. package/src/extract/resolve/module-classifiers.mjs +3 -3
  24. package/src/extract/tsc/extract-typescript-deps.mjs +81 -11
  25. package/src/extract/tsc/extract.mjs +8 -1
  26. package/src/graph-utl/consolidate-module-dependencies.mjs +18 -21
  27. package/src/graph-utl/consolidate-modules.mjs +19 -19
  28. package/src/graph-utl/filter-bank.mjs +11 -8
  29. package/src/graph-utl/indexed-module-graph.mjs +21 -19
  30. package/src/main/options/defaults.mjs +1 -0
  31. package/src/main/resolve-options/normalize.mjs +6 -4
  32. package/src/meta.cjs +1 -1
  33. package/src/report/d2.mjs +1 -0
  34. package/src/report/mermaid.mjs +7 -6
  35. package/src/schema/baseline-violations.schema.mjs +1 -1
  36. package/src/schema/configuration.schema.mjs +1 -1
  37. package/src/schema/cruise-result.schema.mjs +1 -1
  38. package/src/utl/find-all-files.mjs +20 -28
  39. package/src/validate/match-folder-dependency-rule.mjs +2 -4
  40. package/src/validate/matchers.mjs +7 -7
  41. package/types/options.d.mts +6 -0
  42. package/types/shared-types.d.mts +1 -0
  43. package/types/strict-restrictions.d.mts +4 -8
  44. package/types/strict-rule-set.d.mts +2 -4
  45. package/src/enrich/derive/dependents/get-dependents.mjs +0 -8
@@ -1,12 +1,16 @@
1
1
  import { simple as walk_simple, base as walk_base } from "acorn-walk";
2
- import estreeHelpers from "./estree-helpers.mjs";
2
+ import {
3
+ firstArgumentIsAString,
4
+ firstArgumentIsATemplateLiteral,
5
+ isRequireOfSomeSort,
6
+ } from "./estree-helpers.mjs";
3
7
 
4
8
  function pryStringsFromArguments(pArguments) {
5
9
  let lReturnValue = null;
6
10
 
7
- if (estreeHelpers.firstArgumentIsAString(pArguments)) {
11
+ if (firstArgumentIsAString(pArguments)) {
8
12
  lReturnValue = pArguments[0].value;
9
- } else if (estreeHelpers.firstArgumentIsATemplateLiteral(pArguments)) {
13
+ } else if (firstArgumentIsATemplateLiteral(pArguments)) {
10
14
  lReturnValue = pArguments[0].quasis[0].value.cooked;
11
15
  }
12
16
 
@@ -20,6 +24,28 @@ function getExoticRequireTypes(pModuleSystem) {
20
24
  return pModuleSystem === "amd" ? ["amd-exotic-require"] : ["exotic-require"];
21
25
  }
22
26
 
27
+ function getDependencyTypeAttributes(pName, pModuleSystem) {
28
+ switch (pName) {
29
+ case "require":
30
+ return {
31
+ exoticallyRequired: false,
32
+ dependencyTypes: getRequireTypes(pModuleSystem),
33
+ };
34
+ case "process.getBuiltinModule":
35
+ case "globalThis.process.getBuiltinModule":
36
+ return {
37
+ exoticallyRequired: false,
38
+ dependencyTypes: ["process-get-builtin-module"],
39
+ };
40
+ default:
41
+ return {
42
+ exoticallyRequired: true,
43
+ exoticRequire: pName,
44
+ dependencyTypes: getExoticRequireTypes(pModuleSystem),
45
+ };
46
+ }
47
+ }
48
+
23
49
  function pushRequireCallsToDependencies(
24
50
  pDependencies,
25
51
  pModuleSystem,
@@ -27,23 +53,14 @@ function pushRequireCallsToDependencies(
27
53
  ) {
28
54
  return (pNode) => {
29
55
  for (let lName of pRequireStrings) {
30
- if (estreeHelpers.isRequireOfSomeSort(pNode, lName)) {
56
+ if (isRequireOfSomeSort(pNode, lName)) {
31
57
  const lModuleName = pryStringsFromArguments(pNode.arguments);
32
58
  if (lModuleName) {
33
59
  pDependencies.push({
34
60
  module: lModuleName,
35
61
  moduleSystem: pModuleSystem,
36
62
  dynamic: false,
37
- ...(lName === "require"
38
- ? {
39
- exoticallyRequired: false,
40
- dependencyTypes: getRequireTypes(pModuleSystem),
41
- }
42
- : {
43
- exoticallyRequired: true,
44
- exoticRequire: lName,
45
- dependencyTypes: getExoticRequireTypes(pModuleSystem),
46
- }),
63
+ ...getDependencyTypeAttributes(lName, pModuleSystem),
47
64
  });
48
65
  }
49
66
  }
@@ -51,20 +68,31 @@ function pushRequireCallsToDependencies(
51
68
  };
52
69
  }
53
70
 
71
+ // eslint-disable-next-line max-params
54
72
  export default function extractCommonJSDependencies(
55
73
  pAST,
56
74
  pDependencies,
57
75
  pModuleSystem,
58
76
  pExoticRequireStrings,
77
+ pDetectProcessBuiltinModuleCalls,
59
78
  ) {
60
79
  // var/const lalala = require('./lalala');
61
80
  // require('./lalala');
62
81
  // require('./lalala').doFunkyStuff();
63
82
  // require('zoinks!./wappie')
64
83
  // require(`./withatemplateliteral`)
84
+ // when feature switched as such it will also detect
85
+ // process.getBuiltinModule('fs')
86
+ // globalThis.process.getBuiltinModule('path')
65
87
  // as well as renamed requires/ require wrappers
66
88
  // as passed in pExoticRequireStrings ("need", "window.require")
67
- const lRequireStrings = ["require"].concat(pExoticRequireStrings);
89
+ const lRequireStrings = ["require"]
90
+ .concat(
91
+ pDetectProcessBuiltinModuleCalls
92
+ ? ["process.getBuiltinModule", "globalThis.process.getBuiltinModule"]
93
+ : [],
94
+ )
95
+ .concat(pExoticRequireStrings);
68
96
 
69
97
  walk_simple(
70
98
  pAST,
@@ -1,12 +1,15 @@
1
1
  import { simple as walk_simple, base as walk_base } from "acorn-walk";
2
2
  import { extend } from "acorn-jsx-walk";
3
- import estreeHelpers from "./estree-helpers.mjs";
3
+ import {
4
+ isStringLiteral,
5
+ isPlaceholderLessTemplateLiteral,
6
+ } from "./estree-helpers.mjs";
4
7
 
5
8
  extend(walk_base);
6
9
 
7
10
  function pushImportNodeValue(pDependencies) {
8
11
  return (pNode) => {
9
- if (estreeHelpers.isStringLiteral(pNode.source)) {
12
+ if (isStringLiteral(pNode.source)) {
10
13
  pDependencies.push({
11
14
  module: pNode.source.value,
12
15
  moduleSystem: "es6",
@@ -14,7 +17,7 @@ function pushImportNodeValue(pDependencies) {
14
17
  exoticallyRequired: false,
15
18
  dependencyTypes: ["dynamic-import"],
16
19
  });
17
- } else if (estreeHelpers.isPlaceholderlessTemplateLiteral(pNode.source)) {
20
+ } else if (isPlaceholderLessTemplateLiteral(pNode.source)) {
18
21
  pDependencies.push({
19
22
  module: pNode.source.quasis[0].value.cooked,
20
23
  moduleSystem: "es6",
@@ -6,7 +6,12 @@ import { getASTCached } from "./parse.mjs";
6
6
  import extractStats from "./extract-stats.mjs";
7
7
 
8
8
  export function extract(
9
- { baseDir, moduleSystems, exoticRequireStrings },
9
+ {
10
+ baseDir,
11
+ moduleSystems,
12
+ exoticRequireStrings,
13
+ detectProcessBuiltinModuleCalls,
14
+ },
10
15
  pFileName,
11
16
  pTranspileOptions,
12
17
  ) {
@@ -14,7 +19,13 @@ export function extract(
14
19
  const lAST = getASTCached(join(baseDir, pFileName), pTranspileOptions);
15
20
 
16
21
  if (moduleSystems.includes("cjs")) {
17
- extractCommonJSDeps(lAST, lDependencies, "cjs", exoticRequireStrings);
22
+ extractCommonJSDeps(
23
+ lAST,
24
+ lDependencies,
25
+ "cjs",
26
+ exoticRequireStrings,
27
+ detectProcessBuiltinModuleCalls,
28
+ );
18
29
  }
19
30
  if (moduleSystems.includes("es6")) {
20
31
  extractES6Deps(lAST, lDependencies);
@@ -31,12 +31,12 @@ function shouldBeIncluded(pFullPathToFile, pOptions) {
31
31
  );
32
32
  }
33
33
 
34
- function shouldNotBeExcluded(pFullPathToFile, pOptions) {
34
+ function shouldBeExcluded(pFullPathToFile, pOptions) {
35
35
  return (
36
- (!pOptions?.exclude?.path ||
37
- !filenameMatchesPattern(pFullPathToFile, pOptions.exclude.path)) &&
38
- (!pOptions?.doNotFollow?.path ||
39
- !filenameMatchesPattern(pFullPathToFile, pOptions.doNotFollow.path))
36
+ (pOptions?.exclude?.path &&
37
+ filenameMatchesPattern(pFullPathToFile, pOptions.exclude.path)) ||
38
+ (pOptions?.doNotFollow?.path &&
39
+ filenameMatchesPattern(pFullPathToFile, pOptions.doNotFollow.path))
40
40
  );
41
41
  }
42
42
 
@@ -48,11 +48,12 @@ function shouldNotBeExcluded(pFullPathToFile, pOptions) {
48
48
  function gatherScannableFilesFromDirectory(pDirectoryName, pOptions) {
49
49
  return readdirSync(join(pOptions.baseDir, pDirectoryName))
50
50
  .map((pFileName) => join(pDirectoryName, pFileName))
51
- .filter((pFullPathToFile) =>
52
- shouldNotBeExcluded(pathToPosix(pFullPathToFile), pOptions),
53
- )
54
51
  .flatMap((pFullPathToFile) => {
55
- let lStat = statSync(join(pOptions.baseDir, pFullPathToFile), {
52
+ const lPosixPath = pathToPosix(pFullPathToFile);
53
+ if (shouldBeExcluded(lPosixPath, pOptions)) {
54
+ return [];
55
+ }
56
+ const lStat = statSync(join(pOptions.baseDir, pFullPathToFile), {
56
57
  throwIfNoEntry: false,
57
58
  });
58
59
 
@@ -60,14 +61,15 @@ function gatherScannableFilesFromDirectory(pDirectoryName, pOptions) {
60
61
  if (lStat.isDirectory()) {
61
62
  return gatherScannableFilesFromDirectory(pFullPathToFile, pOptions);
62
63
  }
63
- if (fileIsScannable(pOptions, pFullPathToFile)) {
64
- return pFullPathToFile;
64
+ if (
65
+ fileIsScannable(pOptions, pFullPathToFile) &&
66
+ shouldBeIncluded(lPosixPath, pOptions)
67
+ ) {
68
+ return lPosixPath;
65
69
  }
66
70
  }
67
71
  return [];
68
- })
69
- .map((pFullPathToFile) => pathToPosix(pFullPathToFile))
70
- .filter((pFullPathToFile) => shouldBeIncluded(pFullPathToFile, pOptions));
72
+ });
71
73
  }
72
74
 
73
75
  function expandGlob(pBaseDirectory, pScannedGlob) {
@@ -28,43 +28,38 @@ function extractRecursive(
28
28
  pTranspileOptions,
29
29
  )
30
30
  : [];
31
-
32
- return lDependencies
33
- .filter(
34
- ({ followable, matchesDoNotFollow }) => followable && !matchesDoNotFollow,
35
- )
36
- .reduce(
37
- (pAll, { resolved }) => {
38
- if (!pVisited.has(resolved)) {
39
- return pAll.concat(
40
- extractRecursive(
41
- resolved,
31
+ const lReturnValue = [
32
+ {
33
+ source: pFileName,
34
+ ...(pCruiseOptions.experimentalStats
35
+ ? {
36
+ experimentalStats: extractStats(
37
+ pFileName,
42
38
  pCruiseOptions,
43
- pVisited,
44
- pDepth + 1,
45
- pResolveOptions,
46
39
  pTranspileOptions,
47
40
  ),
48
- );
49
- }
50
- return pAll;
51
- },
52
- [
53
- {
54
- source: pFileName,
55
- ...(pCruiseOptions.experimentalStats
56
- ? {
57
- experimentalStats: extractStats(
58
- pFileName,
59
- pCruiseOptions,
60
- pTranspileOptions,
61
- ),
62
- }
63
- : {}),
64
- dependencies: lDependencies,
65
- },
66
- ],
67
- );
41
+ }
42
+ : {}),
43
+ dependencies: lDependencies,
44
+ },
45
+ ];
46
+
47
+ // eslint-disable-next-line budapestian/local-variable-pattern
48
+ for (const { resolved, followable, matchesDoNotFollow } of lDependencies) {
49
+ if (followable && !matchesDoNotFollow && !pVisited.has(resolved)) {
50
+ lReturnValue.push(
51
+ ...extractRecursive(
52
+ resolved,
53
+ pCruiseOptions,
54
+ pVisited,
55
+ pDepth + 1,
56
+ pResolveOptions,
57
+ pTranspileOptions,
58
+ ),
59
+ );
60
+ }
61
+ }
62
+ return lReturnValue;
68
63
  }
69
64
 
70
65
  function extractFileDirectoryArray(
@@ -82,12 +77,13 @@ function extractFileDirectoryArray(
82
77
  );
83
78
 
84
79
  bus.info("read files: visit dependencies");
85
- return lInitialSources.reduce((pDependencies, pFilename) => {
86
- if (!lVisited.has(pFilename)) {
87
- lVisited.add(pFilename);
88
- return pDependencies.concat(
89
- extractRecursive(
90
- pFilename,
80
+ const lResult = [];
81
+ for (const lFilename of lInitialSources) {
82
+ if (!lVisited.has(lFilename)) {
83
+ lVisited.add(lFilename);
84
+ lResult.push(
85
+ ...extractRecursive(
86
+ lFilename,
91
87
  pCruiseOptions,
92
88
  lVisited,
93
89
  0,
@@ -96,8 +92,8 @@ function extractFileDirectoryArray(
96
92
  ),
97
93
  );
98
94
  }
99
- return pDependencies;
100
- }, []);
95
+ }
96
+ return lResult;
101
97
  }
102
98
 
103
99
  function isNotFollowable({ followable }) {
@@ -74,9 +74,9 @@ function determineFollowableExtensions(pResolveOptions) {
74
74
  ".less",
75
75
  ];
76
76
 
77
- lKnownUnfollowables.forEach((pUnfollowable) => {
78
- lReturnValue.delete(pUnfollowable);
79
- });
77
+ for (const lUnfollowable of lKnownUnfollowables) {
78
+ lReturnValue.delete(lUnfollowable);
79
+ }
80
80
  return lReturnValue;
81
81
  }
82
82
 
@@ -229,6 +229,37 @@ function isCompositeExoticRequire(pASTNode, pObjectName, pPropertyName) {
229
229
  );
230
230
  }
231
231
 
232
+ function isTripleCursedCompositeExoticRequire(
233
+ pASTNode,
234
+ pObjectName1,
235
+ pObjectName2,
236
+ pPropertyName,
237
+ ) {
238
+ return (
239
+ typescript.SyntaxKind[pASTNode.kind] === "CallExpression" &&
240
+ pASTNode.expression &&
241
+ typescript.SyntaxKind[pASTNode.expression.kind] ===
242
+ "PropertyAccessExpression" &&
243
+ pASTNode.expression.expression &&
244
+ typescript.SyntaxKind[pASTNode.expression.expression.kind] ===
245
+ "PropertyAccessExpression" &&
246
+ // globalThis
247
+ pASTNode.expression.expression.expression &&
248
+ typescript.SyntaxKind[pASTNode.expression.expression.expression.kind] ===
249
+ "Identifier" &&
250
+ pASTNode.expression.expression.expression.escapedText === pObjectName1 &&
251
+ // process
252
+ typescript.SyntaxKind[pASTNode.expression.expression.name.kind] ===
253
+ "Identifier" &&
254
+ pASTNode.expression.expression.name.escapedText === pObjectName2 &&
255
+ // getBuiltinModule
256
+ pASTNode.expression.name &&
257
+ typescript.SyntaxKind[pASTNode.expression.name.kind] === "Identifier" &&
258
+ pASTNode.expression.name.escapedText === pPropertyName &&
259
+ firstArgumentIsAString(pASTNode)
260
+ );
261
+ }
262
+
232
263
  function isExoticRequire(pASTNode, pString) {
233
264
  const lRequireStringElements = pString.split(".");
234
265
 
@@ -307,7 +338,9 @@ export function walkJSDoc(pObject, pCollection = new Set()) {
307
338
  if (isJSDocImport(pObject)) {
308
339
  pCollection.add(pObject.argument.literal.text);
309
340
  } else if (Array.isArray(pObject)) {
310
- pObject.forEach((pValue) => walkJSDoc(pValue, pCollection));
341
+ for (const lValue of pObject) {
342
+ walkJSDoc(lValue, pCollection);
343
+ }
311
344
  } else if (typeof pObject === "object") {
312
345
  for (const lKey in pObject) {
313
346
  if (!keyInJSDocIsIgnorable(lKey) && pObject[lKey]) {
@@ -363,7 +396,12 @@ function extractJSDocImports(pJSDocNodes) {
363
396
  * @returns {(pASTNode: Node) => void} - the walker function
364
397
  */
365
398
  // eslint-disable-next-line max-lines-per-function
366
- function walk(pResult, pExoticRequireStrings, pDetectJSDocImports) {
399
+ function walk(
400
+ pResult,
401
+ pExoticRequireStrings,
402
+ pDetectJSDocImports,
403
+ pDetectProcessBuiltinModuleCalls,
404
+ ) {
367
405
  // eslint-disable-next-line max-lines-per-function
368
406
  return (pASTNode) => {
369
407
  // require('a-string'), require(`a-template-literal`)
@@ -377,17 +415,36 @@ function walk(pResult, pExoticRequireStrings, pDetectJSDocImports) {
377
415
  }
378
416
 
379
417
  // const want = require; {lalala} = want('yudelyo'), window.require('elektron')
380
- pExoticRequireStrings.forEach((pExoticRequireString) => {
381
- if (isExoticRequire(pASTNode, pExoticRequireString)) {
418
+ for (const lExoticRequireString of pExoticRequireStrings) {
419
+ if (isExoticRequire(pASTNode, lExoticRequireString)) {
382
420
  pResult.push({
383
421
  module: pASTNode.arguments[0].text,
384
422
  moduleSystem: "cjs",
385
423
  exoticallyRequired: true,
386
- exoticRequire: pExoticRequireString,
424
+ exoticRequire: lExoticRequireString,
387
425
  dependencyTypes: ["exotic-require"],
388
426
  });
389
427
  }
390
- });
428
+ }
429
+
430
+ // const path = process.getBuiltinModule('node:path'); const fs = globalThis.process.getBuiltinModule(`node:fs`);
431
+ if (
432
+ pDetectProcessBuiltinModuleCalls &&
433
+ (isCompositeExoticRequire(pASTNode, "process", "getBuiltinModule") ||
434
+ isTripleCursedCompositeExoticRequire(
435
+ pASTNode,
436
+ "globalThis",
437
+ "process",
438
+ "getBuiltinModule",
439
+ ))
440
+ ) {
441
+ pResult.push({
442
+ module: pASTNode.arguments[0].text,
443
+ moduleSystem: "cjs",
444
+ exoticallyRequired: false,
445
+ dependencyTypes: ["process-get-builtin-module"],
446
+ });
447
+ }
391
448
 
392
449
  // import('a-string'), import(`a-template-literal`)
393
450
  if (isDynamicImportExpression(pASTNode)) {
@@ -418,13 +475,18 @@ function walk(pResult, pExoticRequireStrings, pDetectJSDocImports) {
418
475
 
419
476
  // pResult = pResult.concat(lJSDocImports) looks like the more obvious
420
477
  // way to do this, but it re-assigns the pResult parameter
421
- lJSDocImports.forEach((pImport) => {
422
- pResult.push(pImport);
423
- });
478
+ for (const lImport of lJSDocImports) {
479
+ pResult.push(lImport);
480
+ }
424
481
  }
425
482
  typescript.forEachChild(
426
483
  pASTNode,
427
- walk(pResult, pExoticRequireStrings, pDetectJSDocImports),
484
+ walk(
485
+ pResult,
486
+ pExoticRequireStrings,
487
+ pDetectJSDocImports,
488
+ pDetectProcessBuiltinModuleCalls,
489
+ ),
428
490
  );
429
491
  };
430
492
  }
@@ -442,10 +504,16 @@ function extractNestedDependencies(
442
504
  pAST,
443
505
  pExoticRequireStrings,
444
506
  pDetectJSDocImports,
507
+ pDetectProcessBuiltinModuleCalls,
445
508
  ) {
446
509
  let lResult = [];
447
510
 
448
- walk(lResult, pExoticRequireStrings, pDetectJSDocImports)(pAST);
511
+ walk(
512
+ lResult,
513
+ pExoticRequireStrings,
514
+ pDetectJSDocImports,
515
+ pDetectProcessBuiltinModuleCalls,
516
+ )(pAST);
449
517
 
450
518
  return lResult;
451
519
  }
@@ -459,6 +527,7 @@ export default function extractTypeScriptDependencies(
459
527
  pTypeScriptAST,
460
528
  pExoticRequireStrings,
461
529
  pDetectJSDocImports,
530
+ pDetectProcessBuiltinModuleCalls,
462
531
  ) {
463
532
  // console.dir(pTypeScriptAST, { depth: 100 });
464
533
  return typescript
@@ -471,6 +540,7 @@ export default function extractTypeScriptDependencies(
471
540
  pTypeScriptAST,
472
541
  pExoticRequireStrings,
473
542
  pDetectJSDocImports,
543
+ pDetectProcessBuiltinModuleCalls,
474
544
  ),
475
545
  )
476
546
  .map((pModule) => ({ dynamic: false, ...pModule }))
@@ -13,7 +13,13 @@ export function shouldUse({ tsPreCompilationDeps, parser }, pFileName) {
13
13
  }
14
14
 
15
15
  export function extract(
16
- { baseDir, exoticRequireStrings, moduleSystems, detectJSDocImports },
16
+ {
17
+ baseDir,
18
+ exoticRequireStrings,
19
+ moduleSystems,
20
+ detectJSDocImports,
21
+ detectProcessBuiltinModuleCalls,
22
+ },
17
23
  pFileName,
18
24
  pTranspileOptions,
19
25
  ) {
@@ -21,6 +27,7 @@ export function extract(
21
27
  getASTCached(join(baseDir, pFileName), pTranspileOptions),
22
28
  exoticRequireStrings,
23
29
  detectJSDocImports,
30
+ detectProcessBuiltinModuleCalls,
24
31
  ).filter(({ moduleSystem }) => moduleSystems.includes(moduleSystem));
25
32
  }
26
33
 
@@ -20,17 +20,17 @@ function mergeDependency(pLeftDependency, pRightDependency) {
20
20
  }
21
21
 
22
22
  function mergeDependencies(pResolvedName, pDependencies) {
23
- return pDependencies
24
- .filter((pDependency) => pDependency.resolved === pResolvedName)
25
- .reduce(
26
- (pAllDependencies, pCurrentDependency) =>
27
- mergeDependency(pAllDependencies, pCurrentDependency),
28
- {
29
- dependencyTypes: [],
30
- rules: [],
31
- valid: true,
32
- },
33
- );
23
+ let lReturnValue = {
24
+ dependencyTypes: [],
25
+ rules: [],
26
+ valid: true,
27
+ };
28
+ for (const lDependency of pDependencies) {
29
+ if (lDependency.resolved === pResolvedName) {
30
+ lReturnValue = mergeDependency(lReturnValue, lDependency);
31
+ }
32
+ }
33
+ return lReturnValue;
34
34
  }
35
35
 
36
36
  /**
@@ -38,17 +38,14 @@ function mergeDependencies(pResolvedName, pDependencies) {
38
38
  * @returns {IDependency[]}
39
39
  */
40
40
  function consolidateDependencies(pDependencies) {
41
- let lDependencies = structuredClone(pDependencies);
42
- let lReturnValue = [];
41
+ const lProcessed = new Set();
42
+ const lReturnValue = [];
43
43
 
44
- while (lDependencies.length > 0) {
45
- lReturnValue.push(
46
- mergeDependencies(lDependencies[0].resolved, lDependencies),
47
- );
48
- lDependencies = lDependencies.filter(
49
- // eslint-disable-next-line no-loop-func
50
- (pDependency) => pDependency.resolved !== lDependencies[0].resolved,
51
- );
44
+ for (const lDependency of pDependencies) {
45
+ if (!lProcessed.has(lDependency.resolved)) {
46
+ lReturnValue.push(mergeDependencies(lDependency.resolved, pDependencies));
47
+ lProcessed.add(lDependency.resolved);
48
+ }
52
49
  }
53
50
 
54
51
  return lReturnValue;
@@ -18,29 +18,29 @@ function mergeModule(pLeftModule, pRightModule) {
18
18
  }
19
19
 
20
20
  function mergeModules(pSourceString, pModules) {
21
- return pModules
22
- .filter((pModule) => pModule.source === pSourceString)
23
- .reduce(
24
- (pMergedModule, pCurrentModule) =>
25
- mergeModule(pMergedModule, pCurrentModule),
26
- {
27
- dependencies: [],
28
- rules: [],
29
- valid: true,
30
- },
31
- );
21
+ let lReturnValue = {
22
+ dependencies: [],
23
+ rules: [],
24
+ valid: true,
25
+ };
26
+
27
+ for (const lModule of pModules) {
28
+ if (lModule.source === pSourceString) {
29
+ lReturnValue = mergeModule(lReturnValue, lModule);
30
+ }
31
+ }
32
+ return lReturnValue;
32
33
  }
33
34
 
34
35
  export default function consolidateModules(pModules) {
35
- let lModules = structuredClone(pModules);
36
- let lReturnValue = [];
36
+ const lProcessed = new Set();
37
+ const lReturnValue = [];
37
38
 
38
- while (lModules.length > 0) {
39
- lReturnValue.push(mergeModules(lModules[0].source, lModules));
40
- lModules = lModules.filter(
41
- // eslint-disable-next-line no-loop-func
42
- (pModule) => pModule.source !== lModules[0].source,
43
- );
39
+ for (const lModule of pModules) {
40
+ if (!lProcessed.has(lModule.source)) {
41
+ lReturnValue.push(mergeModules(lModule.source, pModules));
42
+ lProcessed.add(lModule.source);
43
+ }
44
44
  }
45
45
  return lReturnValue;
46
46
  }
@@ -47,22 +47,25 @@ function filterReaches(pModules, pReachesFilter) {
47
47
  .filter((pModule) => moduleMatchesFilter(pModule, pReachesFilter))
48
48
  .map(({ source }) => source);
49
49
 
50
- /** @type {Array<string>} */
51
- let lReachingModules = [];
52
- let lIndexedModules = new IndexedModuleGraph(pModules);
50
+ /** @type {Set<string>} */
51
+ const lReachingModules = new Set();
52
+ const lIndexedModules = new IndexedModuleGraph(pModules);
53
53
 
54
54
  for (let lModuleToReach of lModuleNamesToReach) {
55
- lReachingModules = lReachingModules.concat(
56
- lIndexedModules.findTransitiveDependents(lModuleToReach),
57
- );
55
+ for (let lDependent of lIndexedModules.findTransitiveDependents(
56
+ lModuleToReach,
57
+ )) {
58
+ lReachingModules.add(lDependent);
59
+ }
58
60
  }
61
+
59
62
  return pModules
60
- .filter(({ source }) => lReachingModules.includes(source))
63
+ .filter(({ source }) => lReachingModules.has(source))
61
64
  .map((pModule) => ({
62
65
  ...pModule,
63
66
  matchesReaches: lModuleNamesToReach.includes(pModule.source),
64
67
  dependencies: pModule.dependencies.filter(({ resolved }) =>
65
- lReachingModules.includes(resolved),
68
+ lReachingModules.has(resolved),
66
69
  ),
67
70
  }));
68
71
  }