dependency-cruiser 18.3.1 → 18.5.0

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 (36) hide show
  1. package/README.md +1 -1
  2. package/bin/dependency-cruiser.mjs +2 -2
  3. package/package.json +4 -4
  4. package/src/analyze/summarize/get-stats.mjs +38 -3
  5. package/src/analyze/summarize/index.mjs +30 -6
  6. package/src/analyze/summarize/summarize-options.mjs +1 -0
  7. package/src/cache/cache.mjs +1 -1
  8. package/src/cache/options-compatible.mjs +2 -0
  9. package/src/cli/init-config/build-config.mjs +14 -0
  10. package/src/cli/init-config/config-template.mjs +5 -0
  11. package/src/cli/init-config/environment-helpers.mjs +5 -1
  12. package/src/cli/init-config/get-user-input.mjs +11 -0
  13. package/src/cli/init-config/index.mjs +1 -0
  14. package/src/cli/init-config/normalize-init-options.mjs +1 -1
  15. package/src/cli/init-config/types.d.mts +4 -0
  16. package/src/cli/init-config/write-run-scripts-to-manifest.mjs +2 -2
  17. package/src/extract/transpile/meta.mjs +2 -2
  18. package/src/extract/transpile/try-import-available.mjs +1 -1
  19. package/src/extract/tsc/extract-typescript-deps.mjs +57 -12
  20. package/src/extract/tsc/extract.mjs +2 -0
  21. package/src/main/options/defaults.mjs +1 -0
  22. package/src/main/options/normalize.mjs +4 -1
  23. package/src/meta.cjs +1 -1
  24. package/src/report/azure-devops.mjs +6 -2
  25. package/src/report/baseline.mjs +22 -7
  26. package/src/report/error-html/error-html-template.mjs +2 -0
  27. package/src/report/error-html/index.mjs +84 -8
  28. package/src/report/error.mjs +62 -13
  29. package/src/report/markdown.mjs +89 -19
  30. package/src/report/null.mjs +6 -3
  31. package/src/report/teamcity.mjs +4 -1
  32. package/src/schema/configuration.validate.mjs +1 -1
  33. package/src/schema/cruise-result.validate.mjs +1 -1
  34. package/types/cruise-result.d.mts +14 -0
  35. package/types/options.d.mts +21 -2
  36. package/types/reporter-options.d.mts +30 -5
package/README.md CHANGED
@@ -45,7 +45,7 @@ a `.dependency-cruiser.js` configuration file attuned to your project[^1][^2].
45
45
  If you don't want to use `npx`, but instead `pnpx` (from the `pnpm`
46
46
  package manager) or `yarn` - please refer to that tool's documentation.
47
47
  Particularly `pnpx` has semantics that differ from `npx` quite significantly
48
- and that you want to be aware of before using it. In the mean time: `npx`
48
+ and that you want to be aware of before using it. In the meantime: `npx`
49
49
  _should_ work even when you installed the dependency with a package manager
50
50
  different from `npm`.
51
51
 
@@ -103,8 +103,8 @@ try {
103
103
  .addOption(
104
104
  new Option(
105
105
  "--baseline-mode <mode>",
106
- '"shrink-only" - remove fixed violations but don\'t add new ones; "full" (default) - replace everything',
107
- ).choices(["full", "shrink-only"]),
106
+ '"shrink-only" - remove fixed violations but don\'t add new ones; "format" - rewrite but add nor remove anything; "full" (default) - replace everything',
107
+ ).choices(["full", "shrink-only", "format"]),
108
108
  )
109
109
  .option(
110
110
  "--ignore-known [file]",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dependency-cruiser",
3
- "version": "18.3.1",
3
+ "version": "18.5.0",
4
4
  "description": "Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.",
5
5
  "keywords": [
6
6
  "static analysis",
@@ -149,10 +149,10 @@
149
149
  "acorn-loose": "8.5.2",
150
150
  "acorn-walk": "8.3.5",
151
151
  "commander": "15.0.0",
152
- "enhanced-resolve": "5.25.1",
153
- "ignore": "7.0.9",
152
+ "enhanced-resolve": "5.26.0",
153
+ "ignore": "7.0.11",
154
154
  "interpret": "3.1.1",
155
- "is-installed-globally": "1.0.0",
155
+ "is-installed-globally": "1.0.1",
156
156
  "json5": "2.2.3",
157
157
  "picomatch": "4.0.7",
158
158
  "prompts": "2.4.2",
@@ -1,4 +1,14 @@
1
- export function getViolationStats(pViolations) {
1
+ import { diffViolationArrays } from "#graph-utl/compare.mjs";
2
+ /**
3
+ * @import { IViolation } from "../../../types/violations.mjs"
4
+ * @import { IModule } from "../../../types/cruise-result.d.mts"
5
+ */
6
+
7
+ /**
8
+ * @param {IViolation[]} pViolations
9
+ * @returns {{error:number; warn: number; info: number; ignore:number; }}
10
+ */
11
+ export function getViolationCounts(pViolations) {
2
12
  return pViolations.reduce(
3
13
  (pAll, pThis) => {
4
14
  pAll[pThis.rule.severity] += 1;
@@ -13,13 +23,38 @@ export function getViolationStats(pViolations) {
13
23
  );
14
24
  }
15
25
 
16
- export function getModulesCruised(pModules) {
26
+ /**
27
+ * @param {IModule[]} pModules
28
+ * @returns {number} number of modules cruised
29
+ */
30
+ export function getModulesCruisedCount(pModules) {
17
31
  return pModules.length;
18
32
  }
19
33
 
20
- export function getDependenciesCruised(pModules) {
34
+ /**
35
+ * @param {IModule[]} pModules
36
+ * @returns {number} number of dependencies cruised
37
+ */
38
+ export function getDependenciesCruisedCount(pModules) {
21
39
  return pModules.reduce(
22
40
  (pAll, pModule) => pAll + pModule.dependencies.length,
23
41
  0,
24
42
  );
25
43
  }
44
+
45
+ /**
46
+ * @param {IViolation[]} pKnownViolations
47
+ * @param {IViolation[]} pCurrentViolations
48
+ * @returns {{baselineSize:number; baselineStale:number; }}
49
+ */
50
+ export function getBaselineDiffCounts(pKnownViolations, pCurrentViolations) {
51
+ const lViolationArrayDiff = diffViolationArrays(
52
+ pKnownViolations,
53
+ pCurrentViolations,
54
+ );
55
+ return {
56
+ baselineSize: pKnownViolations.length,
57
+ baselineStale: lViolationArrayDiff.old.length,
58
+ // lViolationArrayDiff.same.length is already present in the ignore attribute
59
+ };
60
+ }
@@ -4,9 +4,10 @@ import summarizeModules from "./summarize-modules.mjs";
4
4
  import summarizeFolders from "./summarize-folders.mjs";
5
5
  import summarizeOptions from "./summarize-options.mjs";
6
6
  import {
7
- getViolationStats,
8
- getModulesCruised,
9
- getDependenciesCruised,
7
+ getViolationCounts,
8
+ getModulesCruisedCount,
9
+ getDependenciesCruisedCount,
10
+ getBaselineDiffCounts,
10
11
  } from "./get-stats.mjs";
11
12
  import { compareViolations } from "#graph-utl/compare.mjs";
12
13
  import { getEnvironmentInfo } from "#environment.mjs";
@@ -118,6 +119,17 @@ function determineEnvironmentIssues(pOptions, pAvailableTranspilers) {
118
119
  return lIssues.length > 0 ? { issues: lIssues } : {};
119
120
  }
120
121
 
122
+ function getAdvisedExitCode(
123
+ pErrorCount,
124
+ pBaselineStaleCount,
125
+ pStaleEntriesSeverity,
126
+ ) {
127
+ if ((pStaleEntriesSeverity ?? "warn") === "error") {
128
+ return pErrorCount + (pBaselineStaleCount ?? 0);
129
+ }
130
+ return pErrorCount;
131
+ }
132
+
121
133
  /**
122
134
  *
123
135
  * @param {IModule[]} pModules -
@@ -149,11 +161,23 @@ export default function summarize(
149
161
  ...lEnvironment,
150
162
  ...determineEnvironmentIssues(pOptions, lEnvironment.transpilersFound),
151
163
  };
164
+ const lViolationCounts = getViolationCounts(lViolations);
165
+ const lBaselineDiffCounts = pOptions.knownViolations
166
+ ? getBaselineDiffCounts(pOptions.knownViolations, lViolations)
167
+ : {};
168
+ const lAdvisedExitCode = getAdvisedExitCode(
169
+ lViolationCounts.error,
170
+ lBaselineDiffCounts?.baselineStale,
171
+ pOptions?.baseline?.staleEntriesSeverity,
172
+ );
173
+
152
174
  return {
153
175
  violations: lViolations,
154
- ...getViolationStats(lViolations),
155
- totalCruised: getModulesCruised(pModules),
156
- totalDependenciesCruised: getDependenciesCruised(pModules),
176
+ ...lViolationCounts,
177
+ ...lBaselineDiffCounts,
178
+ advisedExitCode: lAdvisedExitCode,
179
+ totalCruised: getModulesCruisedCount(pModules),
180
+ totalDependenciesCruised: getDependenciesCruisedCount(pModules),
157
181
  ...summarizeOptions(pFileDirectoryArray, pOptions),
158
182
  ...(pOptions.ruleSet ? { ruleSetUsed: addRuleSetUsed(pOptions) } : {}),
159
183
  environment: lEnvironmentWithIssues,
@@ -5,6 +5,7 @@ const SHAREABLE_OPTIONS = [
5
5
  "cache",
6
6
  "collapse",
7
7
  "combinedDependencies",
8
+ "detectImportsInAmbientModules",
8
9
  "detectJSDocImports",
9
10
  "detectProcessBuiltinModuleCalls",
10
11
  "doNotFollow",
@@ -33,7 +33,7 @@ const EMPTY_CACHE = {
33
33
  };
34
34
 
35
35
  // see ./cache-rationales.md#cache-format-versioning for rationale & bump instructions
36
- const CACHE_FORMAT_VERSION = 18.1;
36
+ const CACHE_FORMAT_VERSION = 18.5;
37
37
 
38
38
  export default class Cache {
39
39
  /** @type {(IRevisionData | null)=} */
@@ -52,6 +52,8 @@ export function optionsAreCompatible(pOldOptions, pNewOptions) {
52
52
  pOldOptions.preserveSymlinks === pNewOptions.preserveSymlinks &&
53
53
  pOldOptions.combinedDependencies === pNewOptions.combinedDependencies &&
54
54
  pOldOptions.experimentalStats === pNewOptions.experimentalStats &&
55
+ pOldOptions.detectImportsInAmbientModules ===
56
+ pNewOptions.detectImportsInAmbientModules &&
55
57
  pOldOptions.detectJSDocImports === pNewOptions.detectJSDocImports &&
56
58
  pOldOptions.detectProcessBuiltinModuleCalls ===
57
59
  pNewOptions.detectProcessBuiltinModuleCalls &&
@@ -73,6 +73,16 @@ function buildDetectJSDocumentImportsAttribute(pInitOptions) {
73
73
  : "// detectJSDocImports: true,";
74
74
  }
75
75
 
76
+ /**
77
+ * @param {IInitConfig} pInitOptions
78
+ * @returns {string}
79
+ */
80
+ function buildDetectImportsInAmbientModulesAttribute(pInitOptions) {
81
+ return pInitOptions.detectImportsInAmbientModules
82
+ ? "detectImportsInAmbientModules: true,"
83
+ : "// detectImportsInAmbientModules: true,";
84
+ }
85
+
76
86
  /**
77
87
  *
78
88
  * @param {IInitConfig} pInitOptions
@@ -224,6 +234,10 @@ export default function buildConfig(pInitOptions) {
224
234
  "{{detectJSDocImportsAttribute}}",
225
235
  buildDetectJSDocumentImportsAttribute(pInitOptions),
226
236
  )
237
+ .replace(
238
+ "{{detectImportsInAmbientModulesAttribute}}",
239
+ buildDetectImportsInAmbientModulesAttribute(pInitOptions),
240
+ )
227
241
  .replace(
228
242
  "{{detectProcessBuiltinModuleCalls}}",
229
243
  buildDetectProcessBuiltinModuleCalls(pInitOptions),
@@ -227,6 +227,11 @@ module.exports = {
227
227
  // to be installed in the same spot you run dependency-cruiser from.
228
228
  {{detectJSDocImportsAttribute}}
229
229
 
230
+ // false: don't look at imports in ambient module declarations (the default)
231
+ // true: detect imports, re-exports and import-equals declarations inside
232
+ // ambient modules and namespaces. Implies parser: 'tsc'.
233
+ {{detectImportsInAmbientModulesAttribute}}
234
+
230
235
  // false: don't look at process.getBuiltinModule calls (the default)
231
236
  // true: dependency-cruiser will detect calls to process.getBuiltinModule/
232
237
  // globalThis.process.getBuiltinModule as imports.
@@ -105,6 +105,10 @@ export function isLikelyMonoRepo(pFolderNames = getFolderNames(process.cwd())) {
105
105
  return pFolderNames.includes("packages");
106
106
  }
107
107
 
108
+ /**
109
+ * @param {Record<string,any>} [pManifest]
110
+ * @returns
111
+ */
108
112
  function getPackageManager(pManifest) {
109
113
  let lReturnValue = "";
110
114
  try {
@@ -119,7 +123,7 @@ function getPackageManager(pManifest) {
119
123
  }
120
124
 
121
125
  /**
122
- * @param {Record<string,any>} pManifest
126
+ * @param {Record<string,any>} [pManifest]
123
127
  * @returns {boolean}
124
128
  */
125
129
  export function likelyUsesBun(pManifest) {
@@ -21,6 +21,10 @@ import {
21
21
  import { validateLocation } from "./validators.mjs";
22
22
  import { isAvailable as tscIsAvailable } from "#extract/tsc/parse.mjs";
23
23
 
24
+ /**
25
+ * @param {string} pString
26
+ * @returns {{title:string;value:string}}
27
+ */
24
28
  function toPromptChoice(pString) {
25
29
  return {
26
30
  title: pString,
@@ -116,6 +120,13 @@ const QUESTIONS = [
116
120
  message: "Do you want to detect JSDoc imports (slightly slower)?",
117
121
  initial: false,
118
122
  },
123
+ {
124
+ name: "detectImportsInAmbientModules",
125
+ type: () => (tscIsAvailable() ? "confirm" : false),
126
+ message:
127
+ "Do you want to detect imports inside ambient modules (slightly slower)?",
128
+ initial: false,
129
+ },
119
130
  {
120
131
  name: "detectProcessBuiltinModuleCalls",
121
132
  type: "confirm",
@@ -46,6 +46,7 @@ function getOneShotConfig(pOneShotConfigId) {
46
46
  useTsConfig: hasTSConfigCandidates(),
47
47
  tsConfig: getTSConfigCandidates().shift(),
48
48
  detectJSDocImports: false,
49
+ detectImportsInAmbientModules: false,
49
50
  detectProcessBuiltinModuleCalls: true,
50
51
  tsPreCompilationDeps: hasTSConfigCandidates(),
51
52
  useWebpackConfig: hasWebpackConfigCandidates(),
@@ -20,7 +20,7 @@ function usesTypeScript(pInitOptions, pExtensions) {
20
20
  return Boolean(
21
21
  pInitOptions.tsConfig ||
22
22
  pInitOptions.tsPreCompilationDeps ||
23
- (pExtensions || []).some((pExtension) =>
23
+ (pExtensions /* c8 ignore next */ || []).some((pExtension) =>
24
24
  [".ts", ".tsx", ".d.ts", ".mts", ".d.mts", ".cts", ".d.cts"].includes(
25
25
  pExtension,
26
26
  ),
@@ -36,6 +36,10 @@ export interface IInitConfig {
36
36
  * Whether or not to detect JSDoc imports
37
37
  */
38
38
  detectJSDocImports?: boolean;
39
+ /**
40
+ * Whether or not to detect imports in ambient module and namespace declarations
41
+ */
42
+ detectImportsInAmbientModules?: boolean;
39
43
  /**
40
44
  * Whether or not to detect calls to `process.getBuiltinModule`/
41
45
  * `globalThis.process.getBuiltinModule` as imports
@@ -72,8 +72,8 @@ export function compileRunScripts(pInitOptions) {
72
72
 
73
73
  lReturnValue = {
74
74
  depcruise: `dependency-cruiser ${lSourceLocations} ${lTestLocations}`,
75
- "depcruise:graph": `dependency-cruiser ${lSourceLocations} --include-only '${lSourceLocationRE}' --output-type dot | dot -T svg | depcruise-wrap-stream-in-html > dependency-graph.html`,
76
- "depcruise:graph:dev": `dependency-cruiser ${lSourceLocations} --include-only '${lSourceLocationRE}' --prefix vscode://file/$(pwd)/ --output-type dot | dot -T svg | depcruise-wrap-stream-in-html | browser`,
75
+ "depcruise:graph": `dependency-cruiser ${lSourceLocations} --include-only '${lSourceLocationRE}' --output-type dot-webpage > dependency-graph.html`,
76
+ "depcruise:graph:dev": `dependency-cruiser ${lSourceLocations} --include-only '${lSourceLocationRE}' --prefix vscode://file/$(pwd)/ --output-type dot-webpage | browser`,
77
77
  "depcruise:graph:archi": `dependency-cruiser ${lSourceLocations} --include-only '${lSourceLocationRE}' --output-type archi | dot -T svg | depcruise-wrap-stream-in-html > high-level-dependency-graph.html`,
78
78
  "depcruise:html": `dependency-cruiser ${lSourceLocations} ${lTestLocations} --progress --output-type err-html --output-to dependency-violation-report.html`,
79
79
  "depcruise:text": `dependency-cruiser ${lSourceLocations} ${lTestLocations} --progress --output-type text`,
@@ -30,8 +30,8 @@ function gotCoffee() {
30
30
  tryImportAvailable(
31
31
  "coffeescript",
32
32
  meta.supportedTranspilers.coffeescript,
33
+ /* c8 ignore start */
33
34
  ) ||
34
- /* c8 ignore start */
35
35
  // fallback for ancient versions of the coffeescript compilers
36
36
  tryImportAvailable(
37
37
  "coffee-script",
@@ -96,7 +96,7 @@ export const EXTENSION2AVAILABLE = new Map([
96
96
  [".d.mts", TRANSPILER2AVAILABLE.typescript],
97
97
  [
98
98
  ".vue",
99
- TRANSPILER2AVAILABLE["vue-template-compiler"] ||
99
+ TRANSPILER2AVAILABLE["vue-template-compiler"] /* c8 ignore next */ ||
100
100
  TRANSPILER2AVAILABLE["@vue/compiler-sfc"],
101
101
  ],
102
102
  [".svelte", TRANSPILER2AVAILABLE.svelte],
@@ -9,7 +9,7 @@ const SCOPED_PACKAGE_RE = "@[^/]+(?:/[^/]+)";
9
9
  const ROOT_MODULE_RE = new RegExp(`^(${SCOPED_PACKAGE_RE}|${PACKAGE_RE})`, "g");
10
10
 
11
11
  function extractRootModuleName(pModuleName) {
12
- return (pModuleName.match(ROOT_MODULE_RE) || []).shift();
12
+ return (pModuleName.match(ROOT_MODULE_RE) || /* c8 ignore next */ []).shift();
13
13
  }
14
14
 
15
15
  function getVersion(pModuleName) {
@@ -55,11 +55,53 @@ function isTypeOnlyExport(pStatement) {
55
55
  }
56
56
 
57
57
  /*
58
- * Both extractImport* assume the imports/ exports can only occur at
59
- * top level. AFAIK this the only place they're allowed, so we should
60
- * be good. Otherwise we'll need to walk the tree.
58
+ * The extractImport* below take their statements from getStatements, because
59
+ * imports and exports are allowed in one place besides the top level: inside a
60
+ * module block, which is what `declare module "some-package" { ... }` and
61
+ * `declare namespace Thing { ... }` compile to.
61
62
  */
62
63
 
64
+ /**
65
+ * Get the statements of the passed AST node, plus the statements of any module
66
+ * blocks nested in it
67
+ *
68
+ * @param {Node} pAST - an AST node with statements
69
+ * @returns {Node[]} - the node's statements, flattened over module blocks
70
+ */
71
+ function getModuleBlock(pStatement) {
72
+ if (pStatement.kind !== typescript.SyntaxKind.ModuleDeclaration) {
73
+ return null;
74
+ }
75
+
76
+ // A qualified name nests: `declare namespace A.B {}` is a ModuleDeclaration
77
+ // whose body is another ModuleDeclaration, and only the innermost one holds
78
+ // the block.
79
+ let lBody = pStatement.body;
80
+
81
+ while (lBody?.kind === typescript.SyntaxKind.ModuleDeclaration) {
82
+ lBody = lBody.body;
83
+ }
84
+
85
+ return lBody?.kind === typescript.SyntaxKind.ModuleBlock ? lBody : null;
86
+ }
87
+
88
+ function getStatements(pAST, pDetectImportsInAmbientModules) {
89
+ if (!pDetectImportsInAmbientModules) {
90
+ return pAST.statements ?? [];
91
+ }
92
+
93
+ return (pAST.statements ?? []).flatMap((pStatement) => {
94
+ const lModuleBlock = getModuleBlock(pStatement);
95
+
96
+ return lModuleBlock
97
+ ? [
98
+ pStatement,
99
+ ...getStatements(lModuleBlock, pDetectImportsInAmbientModules),
100
+ ]
101
+ : [pStatement];
102
+ });
103
+ }
104
+
63
105
  /**
64
106
  * Get all import statements from the top level AST node
65
107
  *
@@ -67,8 +109,8 @@ function isTypeOnlyExport(pStatement) {
67
109
  * @returns {{module: string; moduleSystem: string; exoticallyRequired: boolean; dependencyTypes?: string[];}[]} -
68
110
  * all import statements in the (top level) AST node
69
111
  */
70
- function extractImports(pAST) {
71
- return pAST.statements
112
+ function extractImports(pAST, pDetectImportsInAmbientModules) {
113
+ return getStatements(pAST, pDetectImportsInAmbientModules)
72
114
  .filter(
73
115
  (pStatement) =>
74
116
  pStatement.kind === typescript.SyntaxKind.ImportDeclaration &&
@@ -91,8 +133,8 @@ function extractImports(pAST) {
91
133
  * @returns {{module: string; moduleSystem: string; exoticallyRequired: boolean; dependencyTypes?: string[];}[]} -
92
134
  * all export statements in the (top level) AST node
93
135
  */
94
- function extractExports(pAST) {
95
- return pAST.statements
136
+ function extractExports(pAST, pDetectImportsInAmbientModules) {
137
+ return getStatements(pAST, pDetectImportsInAmbientModules)
96
138
  .filter(
97
139
  (pStatement) =>
98
140
  pStatement.kind === typescript.SyntaxKind.ExportDeclaration &&
@@ -119,8 +161,8 @@ function extractExports(pAST) {
119
161
  * @returns {{module: string, moduleSystem: string;exoticallyRequired: boolean;}[]} - all import equals statements in the
120
162
  * (top level) AST node
121
163
  */
122
- function extractImportEquals(pAST) {
123
- return pAST.statements
164
+ function extractImportEquals(pAST, pDetectImportsInAmbientModules) {
165
+ return getStatements(pAST, pDetectImportsInAmbientModules)
124
166
  .filter(
125
167
  (pStatement) =>
126
168
  pStatement.kind === typescript.SyntaxKind.ImportEqualsDeclaration &&
@@ -562,11 +604,14 @@ export default function extractTypeScriptDependencies(
562
604
  pExoticRequireStrings,
563
605
  pDetectJSDocImports,
564
606
  pDetectProcessBuiltinModuleCalls,
607
+ pDetectImportsInAmbientModules,
565
608
  ) {
566
609
  return typescript
567
- ? extractImports(pTypeScriptAST)
568
- .concat(extractExports(pTypeScriptAST))
569
- .concat(extractImportEquals(pTypeScriptAST))
610
+ ? extractImports(pTypeScriptAST, pDetectImportsInAmbientModules)
611
+ .concat(extractExports(pTypeScriptAST, pDetectImportsInAmbientModules))
612
+ .concat(
613
+ extractImportEquals(pTypeScriptAST, pDetectImportsInAmbientModules),
614
+ )
570
615
  .concat(extractTripleSlashDirectives(pTypeScriptAST))
571
616
  .concat(
572
617
  extractNestedDependencies(
@@ -19,6 +19,7 @@ export function extract(
19
19
  moduleSystems,
20
20
  detectJSDocImports,
21
21
  detectProcessBuiltinModuleCalls,
22
+ detectImportsInAmbientModules,
22
23
  },
23
24
  pFileName,
24
25
  pTranspileOptions,
@@ -28,6 +29,7 @@ export function extract(
28
29
  exoticRequireStrings,
29
30
  detectJSDocImports,
30
31
  detectProcessBuiltinModuleCalls,
32
+ detectImportsInAmbientModules,
31
33
  ).filter(({ moduleSystem }) => moduleSystems.includes(moduleSystem));
32
34
  }
33
35
 
@@ -4,6 +4,7 @@ export default {
4
4
  maxDepth: 0,
5
5
  moduleSystems: ["es6", "cjs", "tsd", "amd"],
6
6
  detectJSDocImports: false,
7
+ detectImportsInAmbientModules: false,
7
8
  detectProcessBuiltinModuleCalls: false,
8
9
  skipAnalysisNotInRules: false,
9
10
  tsPreCompilationDeps: false,
@@ -223,7 +223,10 @@ export function normalizeCruiseOptions(pOptions, pFileAndDirectoryArray = []) {
223
223
  if (lReturnValue.cache) {
224
224
  lReturnValue.cache = normalizeCacheOptions(lReturnValue.cache);
225
225
  }
226
- if (lReturnValue.detectJSDocImports) {
226
+ if (
227
+ lReturnValue.detectJSDocImports ||
228
+ lReturnValue.detectImportsInAmbientModules
229
+ ) {
227
230
  lReturnValue.parser = "tsc";
228
231
  }
229
232
 
package/src/meta.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /* generated - don't edit */
2
2
  module.exports = {
3
- version: "18.3.1",
3
+ version: "18.5.0",
4
4
  engines: {
5
5
  node: "^22||^24||>=26",
6
6
  },
@@ -154,7 +154,8 @@ function formatResultMessage(pSummary) {
154
154
  */
155
155
  function formatSummary(pSummary) {
156
156
  return `##vso[task.complete result=${formatResultStatus(
157
- pSummary.error,
157
+ // see below on the why of the fallback
158
+ pSummary.advisedExitCode ?? pSummary.error,
158
159
  )};]${formatResultMessage(pSummary)}${EOL}`;
159
160
  }
160
161
 
@@ -180,6 +181,9 @@ export default function azureDevOps(pResults) {
180
181
  .map(formatViolation)
181
182
  .join("")
182
183
  .concat(formatSummary(pResults.summary)),
183
- exitCode: pResults.summary.error,
184
+ // advisedExitCode is a mandatory attribute as of dependency-cruiser 18.4.0
185
+ // however, this reporter can still encounter dependency-cruiser results
186
+ // from before that, hence the fallback
187
+ exitCode: pResults.summary.advisedExitCode ?? pResults.summary.error,
184
188
  };
185
189
  }
@@ -1,4 +1,4 @@
1
- import { diffViolationArrays } from "#graph-utl/compare.mjs";
1
+ import { compareViolations, diffViolationArrays } from "#graph-utl/compare.mjs";
2
2
  /**
3
3
  * @import { ICruiseResult, IReporterOutput } from "../../types/dependency-cruiser.mjs";
4
4
  * @import { IViolation} from "../../types/violations.mjs"
@@ -23,20 +23,35 @@ function getBaseline(pKnownViolations, pCurrentViolations, pMode) {
23
23
  pCurrentViolations,
24
24
  );
25
25
  let lViolationsToEmit = pCurrentViolations;
26
- let lModeAddition = " (running in 'full' mode => added to the baseline)";
26
+ let lModeAdditionTotal = "";
27
+ let lModeAdditionNew = " (running in 'full' mode => added to the baseline)";
28
+ let lModeAdditionOld =
29
+ " (running in 'full' mode => removed from the baseline)";
27
30
 
28
31
  if (pMode === "shrink-only") {
29
32
  lViolationsToEmit = lViolationArrayDiff.same;
30
- lModeAddition =
33
+ lModeAdditionNew =
31
34
  " (running in 'shrink-only' mode => not added to the baseline)";
35
+ lModeAdditionOld =
36
+ " (running in 'shrink-only' mode => removed from the baseline)";
37
+ }
38
+ if (pMode === "format") {
39
+ lViolationsToEmit = lKnownViolations;
40
+ lModeAdditionTotal = " (running in 'format' mode so no updates made)";
41
+ lModeAdditionNew = "";
42
+ lModeAdditionOld = "";
32
43
  }
33
44
  lReturnValue.meta =
34
- `${EOL}baseline : ${lViolationsToEmit.length} violations${EOL}${EOL}` +
35
- ` new : ${lViolationArrayDiff.new.length}${lViolationArrayDiff.new.length > 0 ? lModeAddition : ""}${EOL}` +
45
+ `${EOL}baseline : ${lViolationsToEmit.length} violations${lModeAdditionTotal}${EOL}${EOL}` +
46
+ ` new : ${lViolationArrayDiff.new.length}${lViolationArrayDiff.new.length > 0 ? lModeAdditionNew : ""}${EOL}` +
36
47
  ` same : ${lViolationArrayDiff.same.length}${EOL}` +
37
- ` removed : ${lViolationArrayDiff.old.length}${EOL}`;
48
+ ` stale : ${lViolationArrayDiff.old.length}${lViolationArrayDiff.old.length > 0 ? lModeAdditionOld : ""}${EOL}`;
38
49
  lReturnValue.output =
39
- JSON.stringify(lViolationsToEmit, null, DEFAULT_JSON_INDENT) + EOL;
50
+ JSON.stringify(
51
+ lViolationsToEmit.toSorted(compareViolations),
52
+ null,
53
+ DEFAULT_JSON_INDENT,
54
+ ) + EOL;
40
55
 
41
56
  return lReturnValue;
42
57
  }
@@ -230,12 +230,14 @@ export default `<!DOCTYPE html>
230
230
  <div style="float:left;padding-right:20px" class="ignore">
231
231
  <strong>{{ignore}}</strong> ignored
232
232
  </div>
233
+ {{staleDiv}}
233
234
  &nbsp;
234
235
  </p>
235
236
  {{violatedRulesTable}}
236
237
  </span>
237
238
 
238
239
  {{violationsList}}
240
+ {{baselineStaleTable}}
239
241
  <footer>
240
242
  <p><a href="https://github.com/sverweij/dependency-cruiser">{{depcruiseVersion}}</a> /
241
243
  {{runDate}}</p>