dependency-cruiser 16.6.0 → 16.7.0-beta-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 (71) hide show
  1. package/bin/depcruise-baseline.mjs +1 -1
  2. package/package.json +6 -6
  3. package/src/cache/cache.mjs +3 -4
  4. package/src/cache/content-strategy.mjs +5 -7
  5. package/src/cache/find-content-changes.mjs +11 -5
  6. package/src/cache/helpers.mjs +8 -8
  7. package/src/cache/metadata-strategy.mjs +6 -8
  8. package/src/cache/options-compatible.mjs +2 -1
  9. package/src/cli/index.mjs +4 -2
  10. package/src/cli/init-config/build-config.mjs +13 -9
  11. package/src/cli/init-config/config-template.mjs +11 -0
  12. package/src/cli/init-config/index.mjs +10 -6
  13. package/src/cli/init-config/normalize-init-options.mjs +10 -6
  14. package/src/cli/listeners/performance-log/format-helpers.mjs +1 -1
  15. package/src/cli/normalize-cli-options.mjs +1 -0
  16. package/src/cli/utl/io.mjs +1 -2
  17. package/src/config-utl/extract-babel-config.mjs +0 -1
  18. package/src/config-utl/extract-depcruise-options.mjs +8 -4
  19. package/src/config-utl/extract-known-violations.mjs +6 -6
  20. package/src/config-utl/extract-webpack-resolve-config.mjs +1 -1
  21. package/src/enrich/soften-known-violations.mjs +1 -1
  22. package/src/enrich/summarize/add-rule-set-used.mjs +1 -0
  23. package/src/enrich/summarize/summarize-modules.mjs +0 -1
  24. package/src/enrich/summarize/summarize-options.mjs +2 -0
  25. package/src/extract/extract-dependencies.mjs +16 -11
  26. package/src/extract/extract-stats.mjs +9 -4
  27. package/src/extract/gather-initial-sources.mjs +8 -5
  28. package/src/extract/index.mjs +9 -4
  29. package/src/extract/resolve/determine-dependency-types.mjs +22 -15
  30. package/src/extract/resolve/external-module-helpers.mjs +4 -4
  31. package/src/extract/resolve/index.mjs +11 -5
  32. package/src/extract/resolve/module-classifiers.mjs +9 -4
  33. package/src/extract/resolve/resolve-helpers.mjs +3 -3
  34. package/src/extract/swc/dependency-visitor.mjs +2 -6
  35. package/src/extract/swc/parse.mjs +8 -4
  36. package/src/extract/tsc/extract-typescript-deps.mjs +121 -16
  37. package/src/extract/tsc/extract.mjs +2 -1
  38. package/src/graph-utl/consolidate-module-dependencies.mjs +8 -4
  39. package/src/graph-utl/filter-bank.mjs +8 -4
  40. package/src/graph-utl/indexed-module-graph.mjs +7 -11
  41. package/src/graph-utl/rule-set.mjs +9 -5
  42. package/src/main/cruise.mjs +2 -2
  43. package/src/main/format.mjs +0 -1
  44. package/src/main/options/assert-validity.mjs +13 -9
  45. package/src/main/options/defaults.mjs +1 -0
  46. package/src/main/options/normalize.mjs +23 -13
  47. package/src/main/report-wrap.mjs +12 -7
  48. package/src/main/resolve-options/normalize.mjs +10 -5
  49. package/src/main/rule-set/assert-validity.mjs +5 -2
  50. package/src/main/rule-set/normalize.mjs +21 -11
  51. package/src/meta.cjs +1 -1
  52. package/src/report/anon/anonymize-path.mjs +1 -1
  53. package/src/report/d2.mjs +2 -2
  54. package/src/report/dot/index.mjs +3 -3
  55. package/src/report/dot-webpage/svg-in-html-snippets/script.cjs +4 -4
  56. package/src/report/markdown.mjs +41 -30
  57. package/src/report/mermaid.mjs +2 -2
  58. package/src/report/teamcity.mjs +1 -0
  59. package/src/schema/baseline-violations.schema.mjs +1 -1
  60. package/src/schema/configuration.schema.mjs +1 -1
  61. package/src/schema/cruise-result.schema.mjs +1 -1
  62. package/src/utl/get-extension.mjs +1 -1
  63. package/src/utl/try-import.mjs +0 -1
  64. package/src/utl/try-require.cjs +2 -2
  65. package/src/validate/index.mjs +6 -2
  66. package/src/validate/match-dependency-rule.mjs +8 -4
  67. package/src/validate/match-module-rule-helpers.mjs +16 -10
  68. package/src/validate/match-module-rule.mjs +8 -5
  69. package/src/validate/matchers.mjs +1 -3
  70. package/types/options.d.mts +5 -0
  71. package/types/shared-types.d.mts +3 -0
@@ -1,9 +1,14 @@
1
+ /* eslint-disable unicorn/prevent-abbreviations */
1
2
  /* eslint-disable max-lines */
2
3
  /* eslint-disable no-inline-comments */
3
4
  import tryImport from "#utl/try-import.mjs";
4
5
  import meta from "#meta.cjs";
5
6
 
6
- /** @type {import("typescript")} */
7
+ /**
8
+ * @import typescript, {Node} from "typescript"
9
+ */
10
+
11
+ /** @type {typescript} */
7
12
  const typescript = await tryImport(
8
13
  "typescript",
9
14
  meta.supportedTranspilers.typescript,
@@ -24,7 +29,7 @@ function isTypeOnlyImport(pStatement) {
24
29
  function isTypeOnlyExport(pStatement) {
25
30
  return (
26
31
  // for some reason the isTypeOnly indicator is on _statement_ level
27
- // and not in exportClause as it is in the importClause ¯\_ (ツ)_/¯.
32
+ // and not in exportClause as it is in the importClause ¯\_(ツ)_/¯.
28
33
  // Also in the case of the omission of an alias the exportClause
29
34
  // is not there entirely. So regardless whether there is a
30
35
  // pStatement.exportClause or not, we can directly test for the
@@ -46,7 +51,7 @@ function isTypeOnlyExport(pStatement) {
46
51
  /**
47
52
  * Get all import statements from the top level AST node
48
53
  *
49
- * @param {import("typescript").Node} pAST - the (top-level in this case) AST node
54
+ * @param {Node} pAST - the (top-level in this case) AST node
50
55
  * @returns {{module: string; moduleSystem: string; exoticallyRequired: boolean; dependencyTypes?: string[];}[]} -
51
56
  * all import statements in the (top level) AST node
52
57
  */
@@ -55,7 +60,7 @@ function extractImports(pAST) {
55
60
  .filter(
56
61
  (pStatement) =>
57
62
  typescript.SyntaxKind[pStatement.kind] === "ImportDeclaration" &&
58
- Boolean(pStatement.moduleSpecifier),
63
+ pStatement.moduleSpecifier,
59
64
  )
60
65
  .map((pStatement) => ({
61
66
  module: pStatement.moduleSpecifier.text,
@@ -70,7 +75,7 @@ function extractImports(pAST) {
70
75
  /**
71
76
  * Get all export statements from the top level AST node
72
77
  *
73
- * @param {import("typescript").Node} pAST - the (top-level in this case) AST node
78
+ * @param {Node} pAST - the (top-level in this case) AST node
74
79
  * @returns {{module: string; moduleSystem: string; exoticallyRequired: boolean; dependencyTypes?: string[];}[]} -
75
80
  * all export statements in the (top level) AST node
76
81
  */
@@ -79,7 +84,7 @@ function extractExports(pAST) {
79
84
  .filter(
80
85
  (pStatement) =>
81
86
  typescript.SyntaxKind[pStatement.kind] === "ExportDeclaration" &&
82
- Boolean(pStatement.moduleSpecifier),
87
+ pStatement.moduleSpecifier,
83
88
  )
84
89
  .map((pStatement) => ({
85
90
  module: pStatement.moduleSpecifier.text,
@@ -98,7 +103,7 @@ function extractExports(pAST) {
98
103
  * Ignores import equals of variables (e.g. import protocol = ts.server.protocol
99
104
  * which happens in typescript/lib/protocol.d.ts)
100
105
  *
101
- * @param {import("typescript").Node} pAST - the (top-level in this case) AST node
106
+ * @param {Node} pAST - the (top-level in this case) AST node
102
107
  * @returns {{module: string, moduleSystem: string;exoticallyRequired: boolean;}[]} - all import equals statements in the
103
108
  * (top level) AST node
104
109
  */
@@ -123,7 +128,7 @@ function extractImportEquals(pAST) {
123
128
  * might be wise to distinguish the three types of /// directive that
124
129
  * can come out of this as the resolution algorithm might differ
125
130
  *
126
- * @param {import("typescript").Node} pAST - typescript syntax tree
131
+ * @param {Node} pAST - typescript syntax tree
127
132
  * @returns {{module: string, moduleSystem: string}[]} - 'tripple slash' dependencies
128
133
  */
129
134
  function extractTripleSlashDirectives(pAST) {
@@ -252,7 +257,74 @@ function isTypeImport(pASTNode) {
252
257
  "FirstTemplateToken")
253
258
  );
254
259
  }
255
- function walk(pResult, pExoticRequireStrings) {
260
+
261
+ function extractJSDocImportTags(pJSDocTags) {
262
+ return pJSDocTags
263
+ .filter(
264
+ (pTag) =>
265
+ pTag.tagName.escapedText === "import" &&
266
+ pTag.moduleSpecifier?.kind &&
267
+ typescript.SyntaxKind[pTag.moduleSpecifier.kind] === "StringLiteral" &&
268
+ pTag.moduleSpecifier.text,
269
+ )
270
+ .map((pTag) => ({
271
+ module: pTag.moduleSpecifier.text,
272
+ moduleSystem: "es6",
273
+ exoticallyRequired: false,
274
+ dependencyTypes: ["type-only", "import", "jsdoc", "jsdoc-import-tag"],
275
+ }));
276
+ }
277
+
278
+ function extractJSDocBracketImports(pJSDocTags) {
279
+ return pJSDocTags
280
+ .filter(
281
+ (pTag) =>
282
+ pTag.tagName.escapedText !== "import" &&
283
+ /* c8 ignore start */
284
+ typescript.SyntaxKind[pTag.typeExpression?.kind ?? -1] ===
285
+ "FirstJSDocNode" &&
286
+ typescript.SyntaxKind[pTag.typeExpression.type?.kind ?? -1] ===
287
+ "LastTypeNode" &&
288
+ typescript.SyntaxKind[pTag.typeExpression.type.argument?.kind ?? -1] ===
289
+ "LiteralType" &&
290
+ typescript.SyntaxKind[
291
+ pTag.typeExpression.type.argument?.literal?.kind ?? -1
292
+ ] === "StringLiteral" &&
293
+ /* c8 ignore stop*/
294
+ pTag.typeExpression.type.argument.literal.text,
295
+ )
296
+ .map((pTag) => ({
297
+ module: pTag.typeExpression.type.argument.literal.text,
298
+ moduleSystem: "es6",
299
+ exoticallyRequired: false,
300
+ dependencyTypes: ["type-only", "import", "jsdoc", "jsdoc-bracket-import"],
301
+ }));
302
+ }
303
+
304
+ function extractJSDocImports(pJSDocNodes) {
305
+ const lJSDocNodesWithTags = pJSDocNodes.filter(
306
+ (pJSDocLine) => pJSDocLine.tags,
307
+ );
308
+ const lJSDocImportTags = lJSDocNodesWithTags.flatMap((pJSDocLine) =>
309
+ extractJSDocImportTags(pJSDocLine.tags),
310
+ );
311
+ const lJSDocBracketImports = lJSDocNodesWithTags.flatMap((pJSDocLine) =>
312
+ extractJSDocBracketImports(pJSDocLine.tags),
313
+ );
314
+ return lJSDocImportTags.concat(lJSDocBracketImports);
315
+ }
316
+
317
+ /**
318
+ * Walks the AST and collects all dependencies
319
+ *
320
+ * @param {Node} pASTNode - the AST node to start from
321
+ * @param {string[]} pExoticRequireStrings - exotic require strings to look for
322
+ * @param {boolean} pDetectJSDocImports - whether to detect jsdoc imports
323
+ * @returns {(pASTNode: Node) => void} - the walker function
324
+ */
325
+ // eslint-disable-next-line max-lines-per-function
326
+ function walk(pResult, pExoticRequireStrings, pDetectJSDocImports) {
327
+ // eslint-disable-next-line max-lines-per-function
256
328
  return (pASTNode) => {
257
329
  // require('a-string'), require(`a-template-literal`)
258
330
  if (isRequireCallExpression(pASTNode)) {
@@ -298,7 +370,28 @@ function walk(pResult, pExoticRequireStrings) {
298
370
  dependencyTypes: ["type-import"],
299
371
  });
300
372
  }
301
- typescript.forEachChild(pASTNode, walk(pResult, pExoticRequireStrings));
373
+
374
+ // /** @import thing from './module' */
375
+ // /** @import {thing} from './module' */
376
+ // /** @import * as thing from './module' */
377
+ // see https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/#the-jsdoc-import-tag
378
+ //
379
+ // TODO: all the kinds of tags that can have import statements as type declarations
380
+ // (e.g. @type, @param, @returns, @typedef, @property, @prop, @arg, ...)
381
+ // https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html
382
+ if (pDetectJSDocImports && pASTNode.jsDoc) {
383
+ const lJSDocImports = extractJSDocImports(pASTNode.jsDoc);
384
+
385
+ // pResult = pResult.concat(lJSDocImports) looks like the more obvious
386
+ // way to do this, but it re-assigns the pResult parameter
387
+ lJSDocImports.forEach((pImport) => {
388
+ pResult.push(pImport);
389
+ });
390
+ }
391
+ typescript.forEachChild(
392
+ pASTNode,
393
+ walk(pResult, pExoticRequireStrings, pDetectJSDocImports),
394
+ );
302
395
  };
303
396
  }
304
397
 
@@ -306,13 +399,19 @@ function walk(pResult, pExoticRequireStrings) {
306
399
  * returns an array of dependencies that come potentially nested within
307
400
  * a source file, like commonJS or dynamic imports
308
401
  *
309
- * @param {import("typescript").Node} pAST - typescript syntax tree
402
+ * @param {Node} pAST - typescript syntax tree
403
+ * @param {string[]} pExoticRequireStrings - exotic require strings to look for
404
+ * @param {boolean} pDetectJSDocImports - whether to detect jsdoc imports
310
405
  * @returns {{module: string, moduleSystem: string}[]} - all commonJS dependencies
311
406
  */
312
- function extractNestedDependencies(pAST, pExoticRequireStrings) {
407
+ function extractNestedDependencies(
408
+ pAST,
409
+ pExoticRequireStrings,
410
+ pDetectJSDocImports,
411
+ ) {
313
412
  let lResult = [];
314
413
 
315
- walk(lResult, pExoticRequireStrings)(pAST);
414
+ walk(lResult, pExoticRequireStrings, pDetectJSDocImports)(pAST);
316
415
 
317
416
  return lResult;
318
417
  }
@@ -320,19 +419,25 @@ function extractNestedDependencies(pAST, pExoticRequireStrings) {
320
419
  /**
321
420
  * returns all dependencies in the AST
322
421
  *
323
- * @type {(pTypeScriptAST: (import("typescript").Node), pExoticRequireStrings: string[]) => {module: string, moduleSystem: string, dynamic: boolean}[]}
422
+ * @type {(pTypeScriptAST: (Node), pExoticRequireStrings: string[], pDetectJSDocImports: boolean) => {module: string, moduleSystem: string, dynamic: boolean}[]}
324
423
  */
325
424
  export default function extractTypeScriptDependencies(
326
425
  pTypeScriptAST,
327
426
  pExoticRequireStrings,
427
+ pDetectJSDocImports,
328
428
  ) {
329
- return Boolean(typescript)
429
+ // console.dir(pTypeScriptAST, { depth: 100 });
430
+ return typescript
330
431
  ? extractImports(pTypeScriptAST)
331
432
  .concat(extractExports(pTypeScriptAST))
332
433
  .concat(extractImportEquals(pTypeScriptAST))
333
434
  .concat(extractTripleSlashDirectives(pTypeScriptAST))
334
435
  .concat(
335
- extractNestedDependencies(pTypeScriptAST, pExoticRequireStrings),
436
+ extractNestedDependencies(
437
+ pTypeScriptAST,
438
+ pExoticRequireStrings,
439
+ pDetectJSDocImports,
440
+ ),
336
441
  )
337
442
  .map((pModule) => ({ dynamic: false, ...pModule }))
338
443
  : /* c8 ignore next */ [];
@@ -13,13 +13,14 @@ export function shouldUse({ tsPreCompilationDeps, parser }, pFileName) {
13
13
  }
14
14
 
15
15
  export function extract(
16
- { baseDir, exoticRequireStrings, moduleSystems },
16
+ { baseDir, exoticRequireStrings, moduleSystems, detectJSDocImports },
17
17
  pFileName,
18
18
  pTranspileOptions,
19
19
  ) {
20
20
  return extractTypeScriptDeps(
21
21
  getASTCached(join(baseDir, pFileName), pTranspileOptions),
22
22
  exoticRequireStrings,
23
+ detectJSDocImports,
23
24
  ).filter(({ moduleSystem }) => moduleSystems.includes(moduleSystem));
24
25
  }
25
26
 
@@ -1,6 +1,10 @@
1
1
  import { compareRules } from "./compare.mjs";
2
2
  import { uniq } from "#utl/array-util.mjs";
3
3
 
4
+ /**
5
+ * @import { IDependency, IModule } from "../../types/cruise-result.mjs";
6
+ */
7
+
4
8
  function mergeDependency(pLeftDependency, pRightDependency) {
5
9
  return {
6
10
  ...pLeftDependency,
@@ -30,8 +34,8 @@ function mergeDependencies(pResolvedName, pDependencies) {
30
34
  }
31
35
 
32
36
  /**
33
- * @param {import('../../types/dependency-cruiser.mjs').IDependency[]} pDependencies
34
- * @returns {import('../../types/dependency-cruiser.mjs').IDependency[]}
37
+ * @param {IDependency[]} pDependencies
38
+ * @returns {IDependency[]}
35
39
  */
36
40
  function consolidateDependencies(pDependencies) {
37
41
  let lDependencies = structuredClone(pDependencies);
@@ -51,8 +55,8 @@ function consolidateDependencies(pDependencies) {
51
55
  }
52
56
 
53
57
  /**
54
- * @param {import('../../types/dependency-cruiser.mjs').IModule} pModule
55
- * @returns {import('../../types/dependency-cruiser.mjs').IModule}
58
+ * @param {IModule} pModule
59
+ * @returns {IModule}
56
60
  */
57
61
  export default function consolidateModuleDependencies(pModule) {
58
62
  return {
@@ -5,6 +5,11 @@ import {
5
5
  dependencyMatchesFilter,
6
6
  } from "./match-facade.mjs";
7
7
 
8
+ /**
9
+ * @import { IModule } from "../../types/cruise-result.mjs";
10
+ * @import { IStrictReachesType } from "../../types/strict-filter-types.mjs";
11
+ */
12
+
8
13
  function includeOnly(pModules, pIncludeFilter) {
9
14
  return pIncludeFilter.path
10
15
  ? pModules
@@ -33,9 +38,9 @@ function exclude(pModules, pExcludeFilter) {
33
38
  }
34
39
  /**
35
40
  *
36
- * @param {import("../../types/cruise-result.mjs").IModule[]} pModules
37
- * @param {import("../../types/strict-filter-types.js").IStrictReachesType} pReachesFilter
38
- * @returns {import("../../types/cruise-result.mjs").IModule[]}
41
+ * @param {IModule[]} pModules
42
+ * @param {IStrictReachesType} pReachesFilter
43
+ * @returns {IModule[]}
39
44
  */
40
45
  function filterReaches(pModules, pReachesFilter) {
41
46
  const lModuleNamesToReach = pModules
@@ -69,7 +74,6 @@ function tagHighlight(pModules, pHighlightFilter) {
69
74
  }));
70
75
  }
71
76
 
72
- // eslint-disable-next-line complexity
73
77
  export function applyFilters(pModules, pFilters) {
74
78
  if (pFilters) {
75
79
  let lReturnValue = structuredClone(pModules);
@@ -1,14 +1,10 @@
1
1
  // @ts-check
2
2
  /* eslint-disable security/detect-object-injection */
3
3
  /**
4
- * @typedef {import("../../types/cruise-result.d.mts").IFolderDependency} IFolderDependency
5
- * @typedef {import("../../types/cruise-result.d.mts").IDependency} IDependency
6
- * @typedef {import("../../types/cruise-result.d.mts").IFolder} IFolder
7
- * @typedef {import("../../types/cruise-result.d.mts").IModule} IModule
8
- * @typedef {import("../../types/shared-types.d.mts").DependencyType} DependencyType
9
- * @typedef {import("../../types/shared-types.d.mjs").IMiniDependency} IMiniDependency
10
- *
11
- * @typedef {(IDependency|IFolderDependency) & {name:string; dependencyTypes?: DependencyType[]}} IEdge
4
+ * @import { IFolderDependency, IDependency, IFolder, IModule } from "../../types/cruise-result.mjs"
5
+ * @import { DependencyType, IMiniDependency } from "../../types/shared-types.mjs"
6
+
7
+ * @typedef {(IDependency|IFolderDependency) & {name:string; dependencyTypes?: DependencyType[]}} IEdge
12
8
  * @typedef {IModule|IFolder} IModuleOrFolder
13
9
  * @typedef {IModuleOrFolder & {dependencies: IEdge[]}} IVertex
14
10
  */
@@ -201,7 +197,7 @@ export default class IndexedModuleGraph {
201
197
  * @param {Set<string>=} pVisited Technical parameter - best to leave out of direct calls
202
198
  * @return {Array<IMiniDependency>} see description above
203
199
  */
204
- #getCycleNew(pInitialSource, pCurrentDependency, pVisited) {
200
+ #getCycle(pInitialSource, pCurrentDependency, pVisited) {
205
201
  let lVisited = pVisited || new Set();
206
202
  const lCurrentVertex = this.findVertexByName(pCurrentDependency.name);
207
203
  const lEdges = lCurrentVertex.dependencies.filter(
@@ -226,7 +222,7 @@ export default class IndexedModuleGraph {
226
222
  */
227
223
  (pAll, pDependency) => {
228
224
  if (!pAll.some((pSome) => pSome.name === pCurrentDependency.name)) {
229
- const lCycle = this.#getCycleNew(
225
+ const lCycle = this.#getCycle(
230
226
  pInitialSource,
231
227
  pDependency,
232
228
  lVisited.add(pDependency.name),
@@ -266,6 +262,6 @@ export default class IndexedModuleGraph {
266
262
  if (!lCurrentDependency) {
267
263
  return [];
268
264
  }
269
- return this.#getCycleNew(pInitialSource, lCurrentDependency);
265
+ return this.#getCycle(pInitialSource, lCurrentDependency);
270
266
  }
271
267
  }
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @import {IFlattenedRuleSet, IForbiddenRuleType} from '../../types/rule-set.mjs';
3
+ */
4
+
1
5
  /**
2
6
  * Finds the first rule in the rule set that has name pName,
3
7
  * and undefined if no such rule exists/ the rule is an 'allowed'
@@ -5,9 +9,9 @@
5
9
  *
6
10
  * (this thing probably belongs in a model-like folder and not in utl)
7
11
  *
8
- * @param {import("../../types/dependency-cruiser").IFlattenedRuleSet} pRuleSet - The rule set to search in
12
+ * @param {IFlattenedRuleSet} pRuleSet - The rule set to search in
9
13
  * @param {string} pName - The rule name to look for
10
- * @return {import("../../types/rule-set").IForbiddenRuleType|undefined} - a rule (or 'undefined' if nothing found)
14
+ * @return {IForbiddenRuleType|undefined} - a rule (or 'undefined' if nothing found)
11
15
  */
12
16
  export function findRuleByName(pRuleSet, pName) {
13
17
  const lNamedRules = (pRuleSet?.forbidden ?? []).concat(
@@ -29,7 +33,7 @@ function ruleHasALicenseLikeAttribute(pRule) {
29
33
  *
30
34
  * Returns false in all other cases
31
35
  *
32
- * @param {import('../../types/dependency-cruiser').IFlattenedRuleSet} pRuleSet
36
+ * @param {IFlattenedRuleSet} pRuleSet
33
37
  * @return {boolean}
34
38
  */
35
39
  export function ruleSetHasLicenseRule(pRuleSet) {
@@ -38,9 +42,9 @@ export function ruleSetHasLicenseRule(pRuleSet) {
38
42
  (pRuleSet?.allowed ?? []).some(ruleHasALicenseLikeAttribute)
39
43
  );
40
44
  }
45
+
41
46
  /**
42
- *
43
- * @param {import('../../types/dependency-cruiser').IFlattenedRuleSet} pRuleSet
47
+ * @param {IFlattenedRuleSet} pRuleSet
44
48
  * @return {boolean}
45
49
  */
46
50
  export function ruleSetHasDeprecationRule(pRuleSet) {
@@ -1,4 +1,4 @@
1
- /* eslint-disable no-return-await, no-magic-numbers */
1
+ /* eslint-disable no-magic-numbers */
2
2
  import { assertCruiseOptionsValid } from "./options/assert-validity.mjs";
3
3
  import { normalizeCruiseOptions } from "./options/normalize.mjs";
4
4
  import reportWrap from "./report-wrap.mjs";
@@ -65,7 +65,7 @@ export default async function cruise(
65
65
  import("#enrich/index.mjs"),
66
66
  ]);
67
67
 
68
- if (Boolean(lCruiseOptions.ruleSet)) {
68
+ if (lCruiseOptions.ruleSet) {
69
69
  bus.summary("parsing rule set", c(4));
70
70
  lCruiseOptions.ruleSet = normalizeRuleSet(
71
71
  assertRuleSetValid(lCruiseOptions.ruleSet),
@@ -21,6 +21,5 @@ export default async function format(pResult, pFormatOptions = {}) {
21
21
 
22
22
  validateResultAgainstSchema(pResult);
23
23
 
24
- // eslint-disable-next-line no-return-await
25
24
  return await reportWrap(pResult, lFormatOptions);
26
25
  }
@@ -2,6 +2,10 @@
2
2
  import safeRegex from "safe-regex";
3
3
  import { getAvailableReporters } from "#report/index.mjs";
4
4
 
5
+ /**
6
+ * @import { ICruiseOptions, IFormatOptions } from "../../../types/options.mjs";
7
+ */
8
+
5
9
  const MODULE_SYSTEM_LIST_RE = /^(?:(?:cjs|amd|es6|tsd)(?:,|$)){1,4}/gi;
6
10
  const VALID_DEPTH_RE = /^\d{1,2}$/g;
7
11
 
@@ -30,10 +34,10 @@ function deepMerge(pTarget, pSource) {
30
34
 
31
35
  function assertModuleSystemsValid(pModuleSystems) {
32
36
  if (
33
- Boolean(pModuleSystems) &&
37
+ pModuleSystems &&
34
38
  Array.isArray(pModuleSystems) &&
35
39
  !pModuleSystems.every((pModuleSystem) =>
36
- Boolean(pModuleSystem.match(MODULE_SYSTEM_LIST_RE)),
40
+ pModuleSystem.match(MODULE_SYSTEM_LIST_RE),
37
41
  )
38
42
  ) {
39
43
  throw new Error(
@@ -43,7 +47,7 @@ function assertModuleSystemsValid(pModuleSystems) {
43
47
  }
44
48
 
45
49
  function assertRegExpSafety(pPattern) {
46
- if (Boolean(pPattern) && !safeRegex(pPattern)) {
50
+ if (pPattern && !safeRegex(pPattern)) {
47
51
  throw new Error(
48
52
  `The pattern '${pPattern}' will probably run very slowly - cowardly refusing to run.\n`,
49
53
  );
@@ -52,7 +56,7 @@ function assertRegExpSafety(pPattern) {
52
56
 
53
57
  function assertOutputTypeValid(pOutputType) {
54
58
  if (
55
- Boolean(pOutputType) &&
59
+ pOutputType &&
56
60
  !getAvailableReporters().includes(pOutputType) &&
57
61
  !pOutputType.startsWith("plugin:")
58
62
  ) {
@@ -61,7 +65,7 @@ function assertOutputTypeValid(pOutputType) {
61
65
  }
62
66
 
63
67
  function assertMaxDepthValid(pDepth) {
64
- if (Boolean(pDepth) && !pDepth.toString().match(VALID_DEPTH_RE)) {
68
+ if (pDepth && !pDepth.toString().match(VALID_DEPTH_RE)) {
65
69
  throw new Error(
66
70
  `'${pDepth}' is not a valid depth - use an integer between 0 and 99`,
67
71
  );
@@ -73,7 +77,7 @@ function assertFocusDepthValid(pFocusDepth) {
73
77
  const lMaxFocusDepth = 99;
74
78
 
75
79
  if (
76
- Boolean(pFocusDepth) &&
80
+ pFocusDepth &&
77
81
  (Number.isNaN(lFocusDepth) ||
78
82
  lFocusDepth < 0 ||
79
83
  lFocusDepth > lMaxFocusDepth)
@@ -96,12 +100,12 @@ function assertPathsSafety(pFilterOption) {
96
100
  /**
97
101
  * @param {any} pOptions
98
102
  * @throws {Error}
99
- * @returns {import("../../../types/dependency-cruiser.js").ICruiseOptions}
103
+ * @returns {ICruiseOptions}
100
104
  */
101
105
  export function assertCruiseOptionsValid(pOptions) {
102
106
  let lReturnValue = {};
103
107
 
104
- if (Boolean(pOptions)) {
108
+ if (pOptions) {
105
109
  // necessary because can slip through the cracks when passed as a cli parameter
106
110
  assertModuleSystemsValid(pOptions.moduleSystems);
107
111
 
@@ -132,7 +136,7 @@ export function assertCruiseOptionsValid(pOptions) {
132
136
 
133
137
  /**
134
138
  *
135
- * @param {import("../../../types/dependency-cruiser.js").IFormatOptions} pFormatOptions
139
+ * @param {IFormatOptions} pFormatOptions
136
140
  * @throws {Error}
137
141
  */
138
142
  export function assertFormatOptionsValid(pFormatOptions) {
@@ -3,6 +3,7 @@ export default {
3
3
  validate: false,
4
4
  maxDepth: 0,
5
5
  moduleSystems: ["es6", "cjs", "tsd", "amd"],
6
+ detectJSDocImports: false,
6
7
  tsPreCompilationDeps: false,
7
8
  preserveSymlinks: false,
8
9
  combinedDependencies: false,
@@ -3,6 +3,14 @@ import { normalizeREProperties } from "../helpers.mjs";
3
3
  import defaults from "./defaults.mjs";
4
4
  import { uniq } from "#utl/array-util.mjs";
5
5
 
6
+ /**
7
+ * @import { ICruiseResult } from "../../../types/cruise-result.mjs";
8
+ * @import { ICruiseOptions, IFormatOptions } from "../../../types/options.mjs";
9
+ * @import { IStrictCruiseOptions, IStrictFormatOptions } from "../../../types/strict-options.mjs";
10
+ * @import { IForbiddenRuleType, IFlattenedRuleSet } from "../../../types/rule-set.mjs";
11
+ * @import { ICacheOptions } from "../../../types/cache-options.mjs";
12
+ */
13
+
6
14
  const DEFAULT_CACHE_FOLDER = "node_modules/.cache/dependency-cruiser";
7
15
  const DEFAULT_CACHE_STRATEGY = "metadata";
8
16
 
@@ -65,7 +73,7 @@ function normalizeCollapse(pCollapse) {
65
73
  }
66
74
 
67
75
  function normalizeFocusDepth(pFormatOptions) {
68
- /** @type {import("../../../types/dependency-cruiser.js").IFormatOptions}*/
76
+ /** @type {IFormatOptions}*/
69
77
  let lFormatOptions = structuredClone(pFormatOptions);
70
78
  if (Object.hasOwn(lFormatOptions, "focusDepth")) {
71
79
  if (lFormatOptions?.focus) {
@@ -81,7 +89,7 @@ function normalizeFocusDepth(pFormatOptions) {
81
89
 
82
90
  /**
83
91
  *
84
- * @param {import("../../../types/dependency-cruiser.js").IForbiddenRuleType} pRule
92
+ * @param {IForbiddenRuleType} pRule
85
93
  * @returns {boolean}
86
94
  */
87
95
  function hasMetricsRule(pRule) {
@@ -96,7 +104,7 @@ function hasMetricsRule(pRule) {
96
104
 
97
105
  /**
98
106
  *
99
- * @param {import("../../../types/dependency-cruiser.js").IFlattenedRuleSet} pRuleSet
107
+ * @param {IFlattenedRuleSet} pRuleSet
100
108
  * @returns {boolean}
101
109
  */
102
110
  function ruleSetHasMetricsRule(pRuleSet) {
@@ -109,7 +117,7 @@ function ruleSetHasMetricsRule(pRuleSet) {
109
117
 
110
118
  /**
111
119
  *
112
- * @param {import('../../../types/dependency-cruiser.js').ICruiseOptions} pOptions
120
+ * @param {ICruiseOptions} pOptions
113
121
  * @returns Boolean
114
122
  */
115
123
  function reporterShowsMetrics(pOptions) {
@@ -122,7 +130,7 @@ function reporterShowsMetrics(pOptions) {
122
130
  /**
123
131
  * Determines whether (instability) metrics should be calculated
124
132
  *
125
- * @param {import('../../../types/dependency-cruiser.js').ICruiseOptions} pOptions
133
+ * @param {ICruiseOptions} pOptions
126
134
  * @returns Boolean
127
135
  */
128
136
  function shouldCalculateMetrics(pOptions) {
@@ -135,8 +143,8 @@ function shouldCalculateMetrics(pOptions) {
135
143
  }
136
144
 
137
145
  /**
138
- * @param {string|boolean|Partial<import("../../../types/cache-options.js").ICacheOptions>} pCacheOptions
139
- * @returns {import("../../../types/cache-options.js").ICacheOptions}
146
+ * @param {string|boolean|Partial<ICacheOptions>} pCacheOptions
147
+ * @returns {ICacheOptions}
140
148
  */
141
149
  function normalizeCacheOptions(pCacheOptions) {
142
150
  let lNormalizedCacheOptions = pCacheOptions;
@@ -163,12 +171,12 @@ function normalizeCacheOptions(pCacheOptions) {
163
171
 
164
172
  /**
165
173
  *
166
- * @param {import('../../../types/options.mjs').ICruiseOptions} pOptions
174
+ * @param {ICruiseOptions} pOptions
167
175
  * @param {string[]} pFileAndDirectoryArray
168
- * @returns {import('../../../types/strict-options.js').IStrictCruiseOptions}
176
+ * @returns {IStrictCruiseOptions}
169
177
  */
170
178
  export function normalizeCruiseOptions(pOptions, pFileAndDirectoryArray = []) {
171
- /** @type {import('../../../types/strict-options.js').IStrictCruiseOptions} */
179
+ /** @type {IStrictCruiseOptions} */
172
180
  let lReturnValue = {
173
181
  baseDir: process.cwd(),
174
182
  ...defaults,
@@ -211,14 +219,16 @@ export function normalizeCruiseOptions(pOptions, pFileAndDirectoryArray = []) {
211
219
  if (lReturnValue.cache) {
212
220
  lReturnValue.cache = normalizeCacheOptions(lReturnValue.cache);
213
221
  }
222
+ if (lReturnValue.detectJSDocImports) {
223
+ lReturnValue.parser = "tsc";
224
+ }
214
225
 
215
226
  return normalizeFocusDepth(lReturnValue);
216
227
  }
217
228
 
218
229
  /**
219
- *
220
- * @param {import("../../../types/dependency-cruiser.js").IFormatOptions} pFormatOptions
221
- * @returns {import("../../../types/strict-options.js").IStrictFormatOptions}
230
+ * @param {IFormatOptions} pFormatOptions
231
+ * @returns {IStrictFormatOptions}
222
232
  */
223
233
  export function normalizeFormatOptions(pFormatOptions) {
224
234
  let lFormatOptions = structuredClone(pFormatOptions);
@@ -6,10 +6,15 @@ import { compareModules } from "#graph-utl/compare.mjs";
6
6
  import stripSelfTransitions from "#graph-utl/strip-self-transitions.mjs";
7
7
 
8
8
  /**
9
- *
10
- * @param {import('../../types/dependency-cruiser.js').ICruiseResult} pResult
11
- * @param {import('../../types/dependency-cruiser.js').IFormatOptions} pFormatOptions
12
- * @returns {import('../../types/dependency-cruiser.js').ICruiseResult}
9
+ * @import { ICruiseResult } from "../../types/cruise-result.mjs";
10
+ * @import { IFormatOptions } from "../../types/options.mjs";
11
+ * @import { IReporterOutput } from "../../types/dependency-cruiser.mjs";
12
+ */
13
+
14
+ /**
15
+ * @param {ICruiseResult} pResult
16
+ * @param {IFormatOptions} pFormatOptions
17
+ * @returns {ICruiseResult}
13
18
  */
14
19
  function reSummarizeResults(pResult, pFormatOptions) {
15
20
  let lModules = applyFilters(pResult.modules, pFormatOptions);
@@ -44,9 +49,9 @@ function getReporterSection(pOutputType) {
44
49
 
45
50
  /**
46
51
  *
47
- * @param {import("../..).ICruiseResult} pResult result of a previous run of dependency-cruiser
48
- * @param {import("../../types/dependency-cruiser.js").IFormatOptions} pFormatOptions
49
- * @returns {import("../../types/dependency-cruiser.js").IReporterOutput}
52
+ * @param {ICruiseResult} pResult result of a previous run of dependency-cruiser
53
+ * @param {IFormatOptions} pFormatOptions
54
+ * @returns {IReporterOutput}
50
55
  */
51
56
  export default async function reportWrap(pResult, pFormatOptions) {
52
57
  const lReportFunction = await getReporter(pFormatOptions.outputType);