dependency-cruiser 17.3.1 → 17.3.3-beta-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,6 +1,6 @@
1
1
  {
2
2
  "name": "dependency-cruiser",
3
- "version": "17.3.1",
3
+ "version": "17.3.3-beta-1",
4
4
  "description": "Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.",
5
5
  "keywords": [
6
6
  "static analysis",
@@ -163,14 +163,6 @@
163
163
  "tsconfig-paths-webpack-plugin": "4.2.0",
164
164
  "watskeburt": "5.0.0"
165
165
  },
166
- "overrides": {
167
- "cross-spawn": ">=6.0.6",
168
- "nanoid": "^3.3.8"
169
- },
170
- "resolutions": {
171
- "cross-spawn": ">=6.0.6",
172
- "nanoid": "^3.3.8"
173
- },
174
166
  "engines": {
175
167
  "node": "^20.12||^22||>=24"
176
168
  },
@@ -87,15 +87,15 @@ export default class ContentStrategy {
87
87
  revisionDataEqual(pExistingRevisionData, pNewRevisionData) {
88
88
  return Boolean(
89
89
  pExistingRevisionData &&
90
- pNewRevisionData &&
91
- // Even though we don't really have a SHA1, it might be the previous version
92
- // of the cache did, e.g. because it was rendered with the metadata cache
93
- // strategy. In that case the SHA1 comparison is a reliable, fast bailout.
94
- pExistingRevisionData.SHA1 === pNewRevisionData.SHA1 &&
95
- isDeepStrictEqual(
96
- pExistingRevisionData.changes,
97
- pNewRevisionData.changes,
98
- ),
90
+ pNewRevisionData &&
91
+ // Even though we don't really have a SHA1, it might be the previous version
92
+ // of the cache did, e.g. because it was rendered with the metadata cache
93
+ // strategy. In that case the SHA1 comparison is a reliable, fast bailout.
94
+ pExistingRevisionData.SHA1 === pNewRevisionData.SHA1 &&
95
+ isDeepStrictEqual(
96
+ pExistingRevisionData.changes,
97
+ pNewRevisionData.changes,
98
+ ),
99
99
  );
100
100
  }
101
101
 
@@ -19,6 +19,7 @@ import findAllFiles from "#utl/find-all-files.mjs";
19
19
 
20
20
  /**
21
21
  * @param {Set<string>} pFileSet
22
+ * @param {string} pBaseDirectory
22
23
  * @param {typeof getFileHashSync} pFileHashFunction
23
24
  * @returns {(pModule:IModule) => IRevisionChange}
24
25
  */
package/src/cli/index.mjs CHANGED
@@ -20,9 +20,8 @@ async function extractResolveOptions(pCruiseOptions) {
20
20
  pCruiseOptions?.ruleSet?.options?.webpackConfig?.fileName ?? null;
21
21
 
22
22
  if (lWebPackConfigFileName) {
23
- const { default: extractWebpackResolveConfig } = await import(
24
- "#config-utl/extract-webpack-resolve-config.mjs"
25
- );
23
+ const { default: extractWebpackResolveConfig } =
24
+ await import("#config-utl/extract-webpack-resolve-config.mjs");
26
25
  lResolveOptions = await extractWebpackResolveConfig(
27
26
  lWebPackConfigFileName,
28
27
  pCruiseOptions?.ruleSet?.options?.webpackConfig?.env ?? null,
@@ -34,9 +33,8 @@ async function extractResolveOptions(pCruiseOptions) {
34
33
 
35
34
  async function addKnownViolations(pCruiseOptions) {
36
35
  if (pCruiseOptions.knownViolationsFile) {
37
- const { default: extractKnownViolations } = await import(
38
- "#config-utl/extract-known-violations.mjs"
39
- );
36
+ const { default: extractKnownViolations } =
37
+ await import("#config-utl/extract-known-violations.mjs");
40
38
  const lKnownViolations = await extractKnownViolations(
41
39
  pCruiseOptions.knownViolationsFile,
42
40
  );
@@ -56,9 +54,8 @@ async function extractTSConfigOptions(pCruiseOptions) {
56
54
  pCruiseOptions?.ruleSet?.options?.tsConfig?.fileName ?? null;
57
55
 
58
56
  if (lTSConfigFileName) {
59
- const { default: extractTSConfig } = await import(
60
- "#config-utl/extract-ts-config.mjs"
61
- );
57
+ const { default: extractTSConfig } =
58
+ await import("#config-utl/extract-ts-config.mjs");
62
59
  lReturnValue = extractTSConfig(lTSConfigFileName);
63
60
  }
64
61
 
@@ -70,9 +67,8 @@ async function extractBabelConfigOptions(pCruiseOptions) {
70
67
  const lBabelConfigFileName =
71
68
  pCruiseOptions?.ruleSet?.options?.babelConfig?.fileName ?? null;
72
69
  if (lBabelConfigFileName) {
73
- const { default: extractBabelConfig } = await import(
74
- "#config-utl/extract-babel-config.mjs"
75
- );
70
+ const { default: extractBabelConfig } =
71
+ await import("#config-utl/extract-babel-config.mjs");
76
72
  lReturnValue = extractBabelConfig(lBabelConfigFileName);
77
73
  }
78
74
 
@@ -177,9 +173,8 @@ export default async function executeCli(
177
173
  }
178
174
  /* c8 ignore stop */
179
175
  if (lCruiseOptions.info === true) {
180
- const { default: formatMetaInfo } = await import(
181
- "./format-meta-info.mjs"
182
- );
176
+ const { default: formatMetaInfo } =
177
+ await import("./format-meta-info.mjs");
183
178
  lStreams.stdout.write(await formatMetaInfo());
184
179
  } else if (lCruiseOptions.init) {
185
180
  const { default: initConfig } = await import("./init-config/index.mjs");
@@ -19,12 +19,12 @@ import meta from "#meta.cjs";
19
19
  function usesTypeScript(pInitOptions, pExtensions) {
20
20
  return Boolean(
21
21
  pInitOptions.tsConfig ||
22
- pInitOptions.tsPreCompilationDeps ||
23
- (pExtensions || []).some((pExtension) =>
24
- [".ts", ".tsx", ".d.ts", ".mts", ".d.mts", ".cts", ".d.cts"].includes(
25
- pExtension,
26
- ),
22
+ pInitOptions.tsPreCompilationDeps ||
23
+ (pExtensions || []).some((pExtension) =>
24
+ [".ts", ".tsx", ".d.ts", ".mts", ".d.mts", ".cts", ".d.cts"].includes(
25
+ pExtension,
27
26
  ),
27
+ ),
28
28
  );
29
29
  }
30
30
 
@@ -1,6 +1,6 @@
1
- import getDependents from "./get-dependents.mjs";
1
+ import { isDependent } from "./module-utl.mjs";
2
2
 
3
- /** @import { IFlattenedRuleSet } from "../../../../types/rule-set.mjs" */
3
+ /** @import { IFlattenedRuleSet } from "../../../types/rule-set.mjs" */
4
4
 
5
5
  function isDependentsRule(pRule) {
6
6
  // used in folder rules and when moreUnstable is in the 'to' => governed by
@@ -16,6 +16,13 @@ function isDependentsRule(pRule) {
16
16
  );
17
17
  }
18
18
 
19
+ export function getDependents(pModule, pModules) {
20
+ // perf between O(n) in an unconnected graph and O(n^2) in a fully connected one
21
+ return pModules
22
+ .filter(isDependent(pModule.source))
23
+ .map((pDependentModule) => pDependentModule.source);
24
+ }
25
+
19
26
  /**
20
27
  * @param {IFlattenedRuleSet} pRuleSet
21
28
  * @returns {boolean}
@@ -171,25 +171,29 @@ function addReachabilityToGraph(pGraph, pIndexedGraph, pReachableRule) {
171
171
  const lFromModules = pGraph.filter(isModuleInRuleFrom(pReachableRule));
172
172
 
173
173
  return pGraph.map((pModule) => {
174
- let lClonedModule = structuredClone(pModule);
175
-
176
- if (shouldAddReaches(pReachableRule, lClonedModule)) {
177
- lClonedModule = addReachesToModule(
178
- lClonedModule,
174
+ // strictly speaking we should clone pModule to prevent mutating it,
175
+ // but in practice this function is called with a structuredClone'd graph
176
+ // anyway and the (reference only) copy we use now is faster (observable
177
+ // even on dependency-cruiser's self scan) and less memory intentsive.
178
+ let lModule = pModule;
179
+
180
+ if (shouldAddReaches(pReachableRule, lModule)) {
181
+ lModule = addReachesToModule(
182
+ lModule,
179
183
  pGraph,
180
184
  pIndexedGraph,
181
185
  pReachableRule,
182
186
  );
183
187
  }
184
- if (shouldAddReachable(pReachableRule, lClonedModule, pGraph)) {
185
- lClonedModule = addReachableToModule(
186
- lClonedModule,
188
+ if (shouldAddReachable(pReachableRule, lModule, pGraph)) {
189
+ lModule = addReachableToModule(
190
+ lModule,
187
191
  pIndexedGraph,
188
192
  pReachableRule,
189
193
  lFromModules,
190
194
  );
191
195
  }
192
- return lClonedModule;
196
+ return lModule;
193
197
  });
194
198
  }
195
199
 
@@ -199,11 +203,11 @@ export default function deriveReachables(pGraph, pRuleSet) {
199
203
  if (lReachableRules.length > 0) {
200
204
  const lIndexedGraph = new IndexedModuleGraph(pGraph);
201
205
 
202
- return lReachableRules.reduce(
203
- (pReturnGraph, pRule) =>
204
- addReachabilityToGraph(pReturnGraph, lIndexedGraph, pRule),
205
- structuredClone(pGraph),
206
- );
206
+ let lResultGraph = structuredClone(pGraph);
207
+ for (const lRule of lReachableRules) {
208
+ lResultGraph = addReachabilityToGraph(lResultGraph, lIndexedGraph, lRule);
209
+ }
210
+ return lResultGraph;
207
211
  }
208
212
  return pGraph;
209
213
  }
@@ -1,6 +1,6 @@
1
1
  import deriveCycles from "./derive/circular.mjs";
2
2
  import deriveOrphans from "./derive/orphan/index.mjs";
3
- import addDependents from "./derive/dependents/index.mjs";
3
+ import addDependents from "./derive/dependents.mjs";
4
4
  import deriveReachable from "./derive/reachable.mjs";
5
5
  import addValidations from "./add-validations.mjs";
6
6
  import softenKnownViolations from "./soften-known-violations.mjs";
@@ -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) {
@@ -15,6 +15,14 @@ const typescript = await tryImport(
15
15
  meta.supportedTranspilers.typescript,
16
16
  );
17
17
 
18
+ const INTERESTING_NODE_KINDS = new Set([
19
+ typescript.SyntaxKind.CallExpression,
20
+ typescript.SyntaxKind.ExportDeclaration,
21
+ typescript.SyntaxKind.ImportDeclaration,
22
+ typescript.SyntaxKind.ImportEqualsDeclaration,
23
+ typescript.SyntaxKind.LastTypeNode,
24
+ ]);
25
+
18
26
  function isTypeOnlyImport(pStatement) {
19
27
  return (
20
28
  pStatement.importClause &&
@@ -60,7 +68,7 @@ function extractImports(pAST) {
60
68
  return pAST.statements
61
69
  .filter(
62
70
  (pStatement) =>
63
- typescript.SyntaxKind[pStatement.kind] === "ImportDeclaration" &&
71
+ pStatement.kind === typescript.SyntaxKind.ImportDeclaration &&
64
72
  pStatement.moduleSpecifier,
65
73
  )
66
74
  .map((pStatement) => ({
@@ -84,7 +92,7 @@ function extractExports(pAST) {
84
92
  return pAST.statements
85
93
  .filter(
86
94
  (pStatement) =>
87
- typescript.SyntaxKind[pStatement.kind] === "ExportDeclaration" &&
95
+ pStatement.kind === typescript.SyntaxKind.ExportDeclaration &&
88
96
  pStatement.moduleSpecifier,
89
97
  )
90
98
  .map((pStatement) => ({
@@ -112,7 +120,7 @@ function extractImportEquals(pAST) {
112
120
  return pAST.statements
113
121
  .filter(
114
122
  (pStatement) =>
115
- typescript.SyntaxKind[pStatement.kind] === "ImportEqualsDeclaration" &&
123
+ pStatement.kind === typescript.SyntaxKind.ImportEqualsDeclaration &&
116
124
  pStatement.moduleReference &&
117
125
  pStatement.moduleReference.expression &&
118
126
  pStatement.moduleReference.expression.text,
@@ -173,15 +181,15 @@ function firstArgumentIsAString(pASTNode) {
173
181
  return (
174
182
  lFirstArgument &&
175
183
  // "thing" or 'thing'
176
- (typescript.SyntaxKind[lFirstArgument.kind] === "StringLiteral" ||
184
+ (lFirstArgument.kind === typescript.SyntaxKind.StringLiteral ||
177
185
  // `thing`
178
- typescript.SyntaxKind[lFirstArgument.kind] === "FirstTemplateToken")
186
+ lFirstArgument.kind === typescript.SyntaxKind.FirstTemplateToken)
179
187
  );
180
188
  }
181
189
 
182
190
  function isRequireCallExpression(pASTNode) {
183
191
  if (
184
- typescript.SyntaxKind[pASTNode.kind] === "CallExpression" &&
192
+ pASTNode.kind === typescript.SyntaxKind.CallExpression &&
185
193
  pASTNode.expression
186
194
  ) {
187
195
  /*
@@ -204,7 +212,7 @@ function isRequireCallExpression(pASTNode) {
204
212
 
205
213
  function isSingleExoticRequire(pASTNode, pString) {
206
214
  return (
207
- typescript.SyntaxKind[pASTNode.kind] === "CallExpression" &&
215
+ pASTNode.kind === typescript.SyntaxKind.CallExpression &&
208
216
  pASTNode.expression &&
209
217
  pASTNode.expression.text === pString &&
210
218
  firstArgumentIsAString(pASTNode)
@@ -214,16 +222,15 @@ function isSingleExoticRequire(pASTNode, pString) {
214
222
  /* eslint complexity:0 */
215
223
  function isCompositeExoticRequire(pASTNode, pObjectName, pPropertyName) {
216
224
  return (
217
- typescript.SyntaxKind[pASTNode.kind] === "CallExpression" &&
225
+ pASTNode.kind === typescript.SyntaxKind.CallExpression &&
218
226
  pASTNode.expression &&
219
- typescript.SyntaxKind[pASTNode.expression.kind] ===
220
- "PropertyAccessExpression" &&
227
+ pASTNode.expression.kind ===
228
+ typescript.SyntaxKind.PropertyAccessExpression &&
221
229
  pASTNode.expression.expression &&
222
- typescript.SyntaxKind[pASTNode.expression.expression.kind] ===
223
- "Identifier" &&
230
+ pASTNode.expression.expression.kind === typescript.SyntaxKind.Identifier &&
224
231
  pASTNode.expression.expression.escapedText === pObjectName &&
225
232
  pASTNode.expression.name &&
226
- typescript.SyntaxKind[pASTNode.expression.name.kind] === "Identifier" &&
233
+ pASTNode.expression.name.kind === typescript.SyntaxKind.Identifier &&
227
234
  pASTNode.expression.name.escapedText === pPropertyName &&
228
235
  firstArgumentIsAString(pASTNode)
229
236
  );
@@ -236,25 +243,25 @@ function isTripleCursedCompositeExoticRequire(
236
243
  pPropertyName,
237
244
  ) {
238
245
  return (
239
- typescript.SyntaxKind[pASTNode.kind] === "CallExpression" &&
246
+ pASTNode.kind === typescript.SyntaxKind.CallExpression &&
240
247
  pASTNode.expression &&
241
- typescript.SyntaxKind[pASTNode.expression.kind] ===
242
- "PropertyAccessExpression" &&
248
+ pASTNode.expression.kind ===
249
+ typescript.SyntaxKind.PropertyAccessExpression &&
243
250
  pASTNode.expression.expression &&
244
- typescript.SyntaxKind[pASTNode.expression.expression.kind] ===
245
- "PropertyAccessExpression" &&
251
+ pASTNode.expression.expression.kind ===
252
+ typescript.SyntaxKind.PropertyAccessExpression &&
246
253
  // globalThis
247
254
  pASTNode.expression.expression.expression &&
248
- typescript.SyntaxKind[pASTNode.expression.expression.expression.kind] ===
249
- "Identifier" &&
255
+ pASTNode.expression.expression.expression.kind ===
256
+ typescript.SyntaxKind.Identifier &&
250
257
  pASTNode.expression.expression.expression.escapedText === pObjectName1 &&
251
258
  // process
252
- typescript.SyntaxKind[pASTNode.expression.expression.name.kind] ===
253
- "Identifier" &&
259
+ pASTNode.expression.expression.name.kind ===
260
+ typescript.SyntaxKind.Identifier &&
254
261
  pASTNode.expression.expression.name.escapedText === pObjectName2 &&
255
262
  // getBuiltinModule
256
263
  pASTNode.expression.name &&
257
- typescript.SyntaxKind[pASTNode.expression.name.kind] === "Identifier" &&
264
+ pASTNode.expression.name.kind === typescript.SyntaxKind.Identifier &&
258
265
  pASTNode.expression.name.escapedText === pPropertyName &&
259
266
  firstArgumentIsAString(pASTNode)
260
267
  );
@@ -270,23 +277,22 @@ function isExoticRequire(pASTNode, pString) {
270
277
 
271
278
  function isDynamicImportExpression(pASTNode) {
272
279
  return (
273
- typescript.SyntaxKind[pASTNode.kind] === "CallExpression" &&
280
+ pASTNode.kind === typescript.SyntaxKind.CallExpression &&
274
281
  pASTNode.expression &&
275
- typescript.SyntaxKind[pASTNode.expression.kind] === "ImportKeyword" &&
282
+ pASTNode.expression.kind === typescript.SyntaxKind.ImportKeyword &&
276
283
  firstArgumentIsAString(pASTNode)
277
284
  );
278
285
  }
279
286
 
280
287
  function isTypeImport(pASTNode) {
281
288
  return (
282
- typescript.SyntaxKind[pASTNode.kind] === "LastTypeNode" &&
289
+ pASTNode.kind === typescript.SyntaxKind.LastTypeNode &&
283
290
  pASTNode.argument &&
284
- typescript.SyntaxKind[pASTNode.argument.kind] === "LiteralType" &&
291
+ pASTNode.argument.kind === typescript.SyntaxKind.LiteralType &&
285
292
  ((pASTNode.argument.literal &&
286
- typescript.SyntaxKind[pASTNode.argument.literal.kind] ===
287
- "StringLiteral") ||
288
- typescript.SyntaxKind[pASTNode.argument.literal.kind] ===
289
- "FirstTemplateToken")
293
+ pASTNode.argument.literal.kind === typescript.SyntaxKind.StringLiteral) ||
294
+ pASTNode.argument.literal.kind ===
295
+ typescript.SyntaxKind.FirstTemplateToken)
290
296
  );
291
297
  }
292
298
 
@@ -296,7 +302,7 @@ function extractJSDocImportTags(pJSDocTags) {
296
302
  (pTag) =>
297
303
  pTag.tagName.escapedText === "import" &&
298
304
  pTag.moduleSpecifier &&
299
- typescript.SyntaxKind[pTag.moduleSpecifier.kind] === "StringLiteral" &&
305
+ pTag.moduleSpecifier.kind === typescript.SyntaxKind.StringLiteral &&
300
306
  pTag.moduleSpecifier.text,
301
307
  )
302
308
  .map((pTag) => ({
@@ -310,10 +316,9 @@ function extractJSDocImportTags(pJSDocTags) {
310
316
  function isJSDocImport(pTypeNode) {
311
317
  // import('./hello.mjs') within jsdoc
312
318
  return (
313
- typescript.SyntaxKind[pTypeNode?.kind] === "LastTypeNode" &&
314
- typescript.SyntaxKind[pTypeNode.argument?.kind] === "LiteralType" &&
315
- typescript.SyntaxKind[pTypeNode.argument?.literal?.kind] ===
316
- "StringLiteral" &&
319
+ pTypeNode?.kind === typescript.SyntaxKind.LastTypeNode &&
320
+ pTypeNode.argument?.kind === typescript.SyntaxKind.LiteralType &&
321
+ pTypeNode.argument?.literal?.kind === typescript.SyntaxKind.StringLiteral &&
317
322
  pTypeNode.argument.literal.text
318
323
  );
319
324
  }
@@ -362,7 +367,7 @@ function extractJSDocBracketImports(pJSDocTags) {
362
367
  .filter(
363
368
  (pTag) =>
364
369
  pTag.tagName.escapedText !== "import" &&
365
- typescript.SyntaxKind[pTag.typeExpression?.kind] === "FirstJSDocNode",
370
+ pTag.typeExpression?.kind === typescript.SyntaxKind.FirstJSDocNode,
366
371
  )
367
372
  .flatMap((pTag) => getJSDocImports(pTag))
368
373
  .map((pImportName) => ({
@@ -404,81 +409,87 @@ function walk(
404
409
  ) {
405
410
  // eslint-disable-next-line max-lines-per-function
406
411
  return (pASTNode) => {
407
- // require('a-string'), require(`a-template-literal`)
408
- if (isRequireCallExpression(pASTNode)) {
409
- pResult.push({
410
- module: pASTNode.arguments[0].text,
411
- moduleSystem: "cjs",
412
- exoticallyRequired: false,
413
- dependencyTypes: ["require"],
414
- });
412
+ // /** @import thing from './module' */ etc
413
+ // /** @type {import('module').thing}*/ etc
414
+ if (pDetectJSDocImports && pASTNode.jsDoc) {
415
+ const lJSDocImports = extractJSDocImports(pASTNode.jsDoc);
416
+
417
+ // pResult = pResult.concat(lJSDocImports) looks like the more obvious
418
+ // way to do this, but it re-assigns the pResult parameter
419
+ for (const lImport of lJSDocImports) {
420
+ pResult.push(lImport);
421
+ }
415
422
  }
416
423
 
417
- // const want = require; {lalala} = want('yudelyo'), window.require('elektron')
418
- for (const lExoticRequireString of pExoticRequireStrings) {
419
- if (isExoticRequire(pASTNode, lExoticRequireString)) {
424
+ if (INTERESTING_NODE_KINDS.has(pASTNode.kind)) {
425
+ // require('a-string'), require(`a-template-literal`)
426
+ if (isRequireCallExpression(pASTNode)) {
420
427
  pResult.push({
421
428
  module: pASTNode.arguments[0].text,
422
429
  moduleSystem: "cjs",
423
- exoticallyRequired: true,
424
- exoticRequire: lExoticRequireString,
425
- dependencyTypes: ["exotic-require"],
430
+ exoticallyRequired: false,
431
+ dependencyTypes: ["require"],
426
432
  });
427
433
  }
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
- }
448
434
 
449
- // import('a-string'), import(`a-template-literal`)
450
- if (isDynamicImportExpression(pASTNode)) {
451
- pResult.push({
452
- module: pASTNode.arguments[0].text,
453
- moduleSystem: "es6",
454
- dynamic: true,
455
- exoticallyRequired: false,
456
- dependencyTypes: ["dynamic-import"],
457
- });
458
- }
435
+ // const want = require; {lalala} = want('yudelyo'), window.require('elektron')
436
+ if (pASTNode.kind === typescript.SyntaxKind.CallExpression) {
437
+ for (const lExoticRequireString of pExoticRequireStrings) {
438
+ // eslint-disable-next-line max-depth
439
+ if (isExoticRequire(pASTNode, lExoticRequireString)) {
440
+ pResult.push({
441
+ module: pASTNode.arguments[0].text,
442
+ moduleSystem: "cjs",
443
+ exoticallyRequired: true,
444
+ exoticRequire: lExoticRequireString,
445
+ dependencyTypes: ["exotic-require"],
446
+ });
447
+ }
448
+ }
449
+ }
459
450
 
460
- // const atype: import('./types').T
461
- // const atype: import(`./types`).T
462
- if (isTypeImport(pASTNode)) {
463
- pResult.push({
464
- module: pASTNode.argument.literal.text,
465
- moduleSystem: "es6",
466
- exoticallyRequired: false,
467
- dependencyTypes: ["type-import"],
468
- });
469
- }
451
+ // const path = process.getBuiltinModule('node:path'); const fs = globalThis.process.getBuiltinModule(`node:fs`);
452
+ if (
453
+ pDetectProcessBuiltinModuleCalls &&
454
+ (isCompositeExoticRequire(pASTNode, "process", "getBuiltinModule") ||
455
+ isTripleCursedCompositeExoticRequire(
456
+ pASTNode,
457
+ "globalThis",
458
+ "process",
459
+ "getBuiltinModule",
460
+ ))
461
+ ) {
462
+ pResult.push({
463
+ module: pASTNode.arguments[0].text,
464
+ moduleSystem: "cjs",
465
+ exoticallyRequired: false,
466
+ dependencyTypes: ["process-get-builtin-module"],
467
+ });
468
+ }
470
469
 
471
- // /** @import thing from './module' */ etc
472
- // /** @type {import('module').thing}*/ etc
473
- if (pDetectJSDocImports && pASTNode.jsDoc) {
474
- const lJSDocImports = extractJSDocImports(pASTNode.jsDoc);
470
+ // import('a-string'), import(`a-template-literal`)
471
+ if (isDynamicImportExpression(pASTNode)) {
472
+ pResult.push({
473
+ module: pASTNode.arguments[0].text,
474
+ moduleSystem: "es6",
475
+ dynamic: true,
476
+ exoticallyRequired: false,
477
+ dependencyTypes: ["dynamic-import"],
478
+ });
479
+ }
475
480
 
476
- // pResult = pResult.concat(lJSDocImports) looks like the more obvious
477
- // way to do this, but it re-assigns the pResult parameter
478
- for (const lImport of lJSDocImports) {
479
- pResult.push(lImport);
481
+ // const atype: import('./types').T
482
+ // const atype: import(`./types`).T
483
+ if (isTypeImport(pASTNode)) {
484
+ pResult.push({
485
+ module: pASTNode.argument.literal.text,
486
+ moduleSystem: "es6",
487
+ exoticallyRequired: false,
488
+ dependencyTypes: ["type-import"],
489
+ });
480
490
  }
481
491
  }
492
+
482
493
  typescript.forEachChild(
483
494
  pASTNode,
484
495
  walk(
@@ -1,4 +1,3 @@
1
- // @ts-check
2
1
  /* eslint-disable security/detect-object-injection */
3
2
  /**
4
3
  * @import { IFolderDependency, IDependency, IFolder, IModule } from "../../types/cruise-result.mjs"
@@ -68,7 +67,7 @@ export default class IndexedModuleGraph {
68
67
  ) {
69
68
  /** @type {string[]} */
70
69
  let lReturnValue = [];
71
- const lModule = this.findVertexByName(pName);
70
+ const lModule = this.#indexedGraph.get(pName);
72
71
 
73
72
  if (lModule && (!pMaxDepth || pDepth <= pMaxDepth)) {
74
73
  let lDependents = lModule.dependents || [];
@@ -110,24 +109,26 @@ export default class IndexedModuleGraph {
110
109
  ) {
111
110
  /** @type {string[]} */
112
111
  let lReturnValue = [];
113
- const lModule = this.findVertexByName(pName);
112
+ /** @type {IVertex} */
113
+ const lModule = this.#indexedGraph.get(pName);
114
114
 
115
115
  if (lModule && (!pMaxDepth || pDepth <= pMaxDepth)) {
116
116
  let lDependencies = lModule.dependencies;
117
117
  const lVisited = pVisited.add(pName);
118
118
 
119
119
  if (lDependencies.length > 0) {
120
- lDependencies
121
- .map(({ name }) => name)
122
- .filter((pDependency) => !lVisited.has(pDependency))
123
- .forEach((pDependency) =>
120
+ // eslint-disable-next-line budapestian/local-variable-pattern
121
+ for (const { name } of lDependencies) {
122
+ // eslint-disable-next-line max-depth
123
+ if (!lVisited.has(name)) {
124
124
  this.findTransitiveDependencies(
125
- pDependency,
125
+ name,
126
126
  pMaxDepth,
127
127
  pDepth + 1,
128
128
  lVisited,
129
- ),
130
- );
129
+ );
130
+ }
131
+ }
131
132
  }
132
133
  lReturnValue = Array.from(lVisited);
133
134
  }
@@ -140,12 +141,10 @@ export default class IndexedModuleGraph {
140
141
  * @returns {IMiniDependency}
141
142
  */
142
143
  #geldEdge(pEdge) {
143
- let lReturnValue = {};
144
- lReturnValue.name = pEdge.name;
145
- lReturnValue.dependencyTypes = pEdge.dependencyTypes
146
- ? pEdge.dependencyTypes
147
- : [];
148
- return lReturnValue;
144
+ return {
145
+ name: pEdge.name,
146
+ dependencyTypes: pEdge.dependencyTypes ?? [],
147
+ };
149
148
  }
150
149
 
151
150
  /**
@@ -155,7 +154,8 @@ export default class IndexedModuleGraph {
155
154
  * @returns {Array<IMiniDependency>}
156
155
  */
157
156
  getPath(pFrom, pTo, pVisited = new Set()) {
158
- const lFromNode = this.findVertexByName(pFrom);
157
+ /** @type {IVertex} */
158
+ const lFromNode = this.#indexedGraph.get(pFrom);
159
159
 
160
160
  pVisited.add(pFrom);
161
161
 
@@ -195,7 +195,8 @@ export default class IndexedModuleGraph {
195
195
  */
196
196
  #getCycle(pInitialSource, pCurrentDependency, pVisited) {
197
197
  const lVisited = pVisited || new Set();
198
- const lCurrentVertex = this.findVertexByName(pCurrentDependency.name);
198
+ /** @type {IVertex} */
199
+ const lCurrentVertex = this.#indexedGraph.get(pCurrentDependency.name);
199
200
  const lEdges = lCurrentVertex.dependencies.filter(
200
201
  (pDependency) => !lVisited.has(pDependency.name),
201
202
  );
@@ -245,7 +246,8 @@ export default class IndexedModuleGraph {
245
246
  * @return {Array<IMiniDependency>} see description above
246
247
  */
247
248
  getCycle(pInitialSource, pCurrentSource) {
248
- const lInitialNode = this.findVertexByName(pInitialSource);
249
+ /** @type {IVertex} */
250
+ const lInitialNode = this.#indexedGraph.get(pInitialSource);
249
251
  const lCurrentDependency = lInitialNode.dependencies.find(
250
252
  (pDependency) => pDependency.name === pCurrentSource,
251
253
  );
@@ -80,9 +80,8 @@ async function compileResolveOptions(
80
80
  // Also: requiring the plugin only when it's necessary will save some
81
81
  // startup time (especially on a cold require cache)
82
82
  if (pResolveOptions.tsConfig) {
83
- const { default: TsConfigPathsPlugin } = await import(
84
- "tsconfig-paths-webpack-plugin"
85
- );
83
+ const { default: TsConfigPathsPlugin } =
84
+ await import("tsconfig-paths-webpack-plugin");
86
85
  lResolveOptions.plugins = pushPlugin(
87
86
  lResolveOptions.plugins,
88
87
  // @ts-expect-error TS2351 "TsConfPathsPlugin is not constructable" - is unjustified
package/src/meta.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  /* generated - don't edit */
2
2
 
3
3
  module.exports = {
4
- version: "17.3.1",
4
+ version: "17.3.3-beta-1",
5
5
  engines: {
6
6
  node: "^20.12||^22||>=24",
7
7
  },
@@ -28,37 +28,29 @@ function walk(
28
28
  pDirectoryName,
29
29
  { baseDir, ignoreFilterFn, excludeFilterFn, includeOnlyFilterFn },
30
30
  ) {
31
- return readdirSync(join(baseDir, pDirectoryName))
31
+ const lFilesInCurrentDirectory = readdirSync(join(baseDir, pDirectoryName))
32
32
  .map((pFileName) => join(pDirectoryName, pFileName))
33
33
  .filter(ignoreFilterFn)
34
34
  .filter(excludeFilterFn)
35
- .filter(includeOnlyFilterFn)
36
- .map((pFullPathToFile) => ({
37
- fullPathToFile: pFullPathToFile,
38
- isDirectory: fileIsDirectory(pFullPathToFile, baseDir),
39
- }))
40
- .reduce(
41
- /**
42
- * @param {string[]} pSum
43
- * @param {{fullPathToFile: string; isDirectory: boolean}} pCurrentValue
44
- * @returns {string[]}
45
- */
46
- (pSum, { fullPathToFile, isDirectory }) => {
47
- if (isDirectory) {
48
- return pSum.concat(
49
- walk(fullPathToFile, {
50
- baseDir,
51
- ignoreFilterFn,
52
- excludeFilterFn,
53
- includeOnlyFilterFn,
54
- }),
55
- );
56
- }
57
- return pSum.concat(fullPathToFile);
58
- },
59
- [],
60
- )
61
- .map((pFullPathToFile) => pathToPosix(pFullPathToFile));
35
+ .filter(includeOnlyFilterFn);
36
+
37
+ const lFiles = [];
38
+ for (const lFile of lFilesInCurrentDirectory) {
39
+ if (fileIsDirectory(lFile, baseDir)) {
40
+ lFiles.push(
41
+ ...walk(lFile, {
42
+ baseDir,
43
+ ignoreFilterFn,
44
+ excludeFilterFn,
45
+ includeOnlyFilterFn,
46
+ }),
47
+ );
48
+ } else {
49
+ lFiles.push(pathToPosix(lFile));
50
+ }
51
+ }
52
+
53
+ return lFiles;
62
54
  }
63
55
 
64
56
  function readIgnoreFile(pFileName) {
@@ -15,16 +15,14 @@ function fromFolderPathNot(pRule, pFromFolder) {
15
15
  function toFolderPath(pRule, pToFolder, pGroups) {
16
16
  return Boolean(
17
17
  !pRule.to.path ||
18
- pToFolder.name.match(replaceGroupPlaceholders(pRule.to.path, pGroups)),
18
+ pToFolder.name.match(replaceGroupPlaceholders(pRule.to.path, pGroups)),
19
19
  );
20
20
  }
21
21
 
22
22
  function toFolderPathNot(pRule, pToFolder, pGroups) {
23
23
  return Boolean(
24
24
  !pRule.to.pathNot ||
25
- !pToFolder.name.match(
26
- replaceGroupPlaceholders(pRule.to.pathNot, pGroups),
27
- ),
25
+ !pToFolder.name.match(replaceGroupPlaceholders(pRule.to.pathNot, pGroups)),
28
26
  );
29
27
  }
30
28
 
@@ -31,8 +31,8 @@ export function propertyEquals(pRule, pDependency, pProperty) {
31
31
  export function propertyMatches(pRule, pDependency, pRuleProperty, pProperty) {
32
32
  return Boolean(
33
33
  !pRule.to[pRuleProperty] ||
34
- (pDependency[pProperty] &&
35
- pDependency[pProperty].match(pRule.to[pRuleProperty])),
34
+ (pDependency[pProperty] &&
35
+ pDependency[pProperty].match(pRule.to[pRuleProperty])),
36
36
  );
37
37
  }
38
38
 
@@ -44,8 +44,8 @@ export function propertyMatchesNot(
44
44
  ) {
45
45
  return Boolean(
46
46
  !pRule.to[pRuleProperty] ||
47
- (pDependency[pProperty] &&
48
- !pDependency[pProperty].match(pRule.to[pRuleProperty])),
47
+ (pDependency[pProperty] &&
48
+ !pDependency[pProperty].match(pRule.to[pRuleProperty])),
49
49
  );
50
50
  }
51
51
 
@@ -72,7 +72,7 @@ export function matchesModulePathNot(pRule, pModule) {
72
72
  function _matchesToPath(pRule, pString, pGroups = []) {
73
73
  return Boolean(
74
74
  !pRule.to.path ||
75
- pString.match(replaceGroupPlaceholders(pRule.to.path, pGroups)),
75
+ pString.match(replaceGroupPlaceholders(pRule.to.path, pGroups)),
76
76
  );
77
77
  }
78
78
 
@@ -102,14 +102,14 @@ export function matchToModulePathNot(pRule, pModule, pGroups) {
102
102
  export function matchesToDependencyTypes(pRule, pDependency) {
103
103
  return Boolean(
104
104
  !pRule.to.dependencyTypes ||
105
- intersects(pDependency.dependencyTypes, pRule.to.dependencyTypes),
105
+ intersects(pDependency.dependencyTypes, pRule.to.dependencyTypes),
106
106
  );
107
107
  }
108
108
 
109
109
  export function matchesToDependencyTypesNot(pRule, pDependency) {
110
110
  return Boolean(
111
111
  !pRule.to.dependencyTypesNot ||
112
- !intersects(pDependency.dependencyTypes, pRule.to.dependencyTypesNot),
112
+ !intersects(pDependency.dependencyTypes, pRule.to.dependencyTypesNot),
113
113
  );
114
114
  }
115
115
 
@@ -18,8 +18,7 @@ export interface IStrictFromRestriction extends IFromRestriction {
18
18
  pathNot?: string;
19
19
  }
20
20
 
21
- export interface IStrictMiniDependencyRestriction
22
- extends IStrictBaseRestrictionType {
21
+ export interface IStrictMiniDependencyRestriction extends IStrictBaseRestrictionType {
23
22
  dependencyTypes?: DependencyType[];
24
23
  dependencyTypesNot?: DependencyType[];
25
24
  }
@@ -35,19 +34,16 @@ interface IStrictToRestriction extends IToRestriction {
35
34
  licenseNot?: string;
36
35
  }
37
36
 
38
- export interface IStrictReachabilityToRestrictionType
39
- extends IReachabilityToRestrictionType {
37
+ export interface IStrictReachabilityToRestrictionType extends IReachabilityToRestrictionType {
40
38
  path?: string;
41
39
  pathNot?: string;
42
40
  }
43
41
 
44
- export interface IStrictRequiredToRestrictionType
45
- extends IRequiredToRestrictionType {
42
+ export interface IStrictRequiredToRestrictionType extends IRequiredToRestrictionType {
46
43
  path?: string;
47
44
  }
48
45
 
49
- export interface IStrictDependentsModuleRestrictionType
50
- extends IDependentsModuleRestrictionType {
46
+ export interface IStrictDependentsModuleRestrictionType extends IDependentsModuleRestrictionType {
51
47
  path?: string;
52
48
  pathNot?: string;
53
49
  }
@@ -24,14 +24,12 @@ export interface IStrictRegularForbiddenRuleType extends IStrictBaseRuleType {
24
24
  scope: RuleScopeType;
25
25
  }
26
26
 
27
- export interface IStrictReachabilityForbiddenRuleType
28
- extends IStrictBaseRuleType {
27
+ export interface IStrictReachabilityForbiddenRuleType extends IStrictBaseRuleType {
29
28
  from: IStrictBaseRestrictionType;
30
29
  to: IStrictReachabilityToRestrictionType;
31
30
  }
32
31
 
33
- export interface IStrictDependentsForbiddenRuleType
34
- extends IStrictBaseRuleType {
32
+ export interface IStrictDependentsForbiddenRuleType extends IStrictBaseRuleType {
35
33
  module: IStrictDependentsModuleRestrictionType;
36
34
  from: IStrictBaseRestrictionType;
37
35
  }
@@ -1,8 +0,0 @@
1
- import { isDependent } from "../module-utl.mjs";
2
-
3
- export default function getDependents(pModule, pModules) {
4
- // perf between O(n) in an unconnected graph and O(n^2) in a fully connected one
5
- return pModules
6
- .filter(isDependent(pModule.source))
7
- .map((pDependentModule) => pDependentModule.source);
8
- }