dependency-cruiser 18.3.0 → 18.4.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.
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.0",
3
+ "version": "18.4.0",
4
4
  "description": "Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.",
5
5
  "keywords": [
6
6
  "static analysis",
@@ -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,
@@ -4,10 +4,14 @@ import tryImport from "#utl/try-import.mjs";
4
4
  import meta from "#meta.cjs";
5
5
 
6
6
  /** @type {import('@swc/core/Visitor')} */
7
- const { Visitor } = await tryImport(
7
+ const lSwcVisitorModule = await tryImport(
8
8
  "@swc/core/Visitor.js",
9
9
  meta.supportedTranspilers.swc,
10
10
  );
11
+ // under bun lSwcVisitorModule resolves to something different
12
+ // as under node.js - see https://github.com/sverweij/dependency-cruiser/issues/1092
13
+ // for details. In order to enable running dependency-cruiser on both:
14
+ const Visitor = lSwcVisitorModule.Visitor ?? lSwcVisitorModule;
11
15
 
12
16
  function pryStringsFromArguments(pArguments) {
13
17
  let lReturnValue = null;
package/src/meta.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /* generated - don't edit */
2
2
  module.exports = {
3
- version: "18.3.0",
3
+ version: "18.4.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>
@@ -6,6 +6,12 @@ import {
6
6
  import template from "./error-html-template.mjs";
7
7
  import meta from "#meta.cjs";
8
8
  import { getOneLetterDependencyType } from "#report/utl/index.mjs";
9
+ import { diffViolationArrays } from "#graph-utl/compare.mjs";
10
+ /**
11
+ * @import { ICruiseResult, IViolation } from "../../../types/cruise-result.mjs"
12
+ * @import { IErrorReporterOptions } from "../../../types/reporter-options.mjs"
13
+ * @import { IReporterOutput } from "../../../types/dependency-cruiser.mjs"
14
+ */
9
15
 
10
16
  function getViolatedRuleRowClass(pViolatedRule) {
11
17
  return pViolatedRule.unviolated ? ' class="unviolated"' : "";
@@ -34,7 +40,7 @@ function buildViolatedRuleRow(pViolatedRule) {
34
40
  }
35
41
 
36
42
  /**
37
- * @param {import('../../../types/cruise-result.mjs')} pResults
43
+ * @param {ICruiseResult} pResults
38
44
  * @returns {string}
39
45
  */
40
46
  function constructViolatedRulesTable(pResults) {
@@ -71,15 +77,17 @@ function constructViolatedRulesTable(pResults) {
71
77
  }
72
78
 
73
79
  /**
74
- * @param {import('../../../types/cruise-result.mjs').IViolation} pViolation
80
+ * @param {IViolation} pViolation
75
81
  * @returns {string}
76
82
  */
77
83
  function getViolationRowClass(pViolation) {
78
84
  return pViolation.rule.severity === "ignore" ? ' class="ignored"' : "";
79
85
  }
86
+
80
87
  /**
81
- * @param {import('../../../types/cruise-result.mjs').IViolation} pViolation
82
- * @returns {string}
88
+ * @param {string} pPrefix
89
+ * @param {IErrorReporterOptions} pOptions
90
+ * @returns {(pViolation: IViolation) => string}
83
91
  */
84
92
  function constructViolationRow(pPrefix, pOptions) {
85
93
  return (pViolation) => {
@@ -101,7 +109,8 @@ function constructViolationRow(pPrefix, pOptions) {
101
109
  }
102
110
 
103
111
  /**
104
- * @param {import('../../../types/cruise-result.mjs').ICruiseResult} pResults
112
+ * @param {ICruiseResult} pResults
113
+ * @param {IErrorReporterOptions} pOptions
105
114
  * @returns {string}
106
115
  */
107
116
  function constructViolationsList(pResults, pOptions) {
@@ -154,7 +163,68 @@ function constructViolationsList(pResults, pOptions) {
154
163
  }
155
164
 
156
165
  /**
157
- * @param {import('../../../types/cruise-result.mjs')} pResults
166
+ * @param {number} pBaselineStaleCount
167
+ * @returns {string}
168
+ */
169
+ function constructStaleDiv(pBaselineStaleCount) {
170
+ if ((pBaselineStaleCount ?? 0) > 0) {
171
+ return `<div style="float:left;padding-right:20px" class="ignore">
172
+ <strong>${pBaselineStaleCount}</strong> stale entries in baseline
173
+ </div>`;
174
+ }
175
+ return "";
176
+ }
177
+
178
+ /**
179
+ * @param {ICruiseResult} pResults
180
+ * @param {IErrorReporterOptions} pOptions
181
+ * @returns {string}
182
+ */
183
+ function constructBaselineStaleTable(pResults, pOptions) {
184
+ if ((pResults.summary.baselineStale ?? 0) > 0) {
185
+ const { old } = diffViolationArrays(
186
+ pResults.summary.optionsUsed.knownViolations,
187
+ pResults.summary.violations,
188
+ );
189
+ return `<span id="stale-baseline-entries">
190
+ <h2><svg class="p__svg--inline" viewBox="144 144 512 512" version="1.1" aria-hidden="true"><path d="M583.48 299.64a202 202 0 0 0-108.32-109.58 194.4 194.4 0 0 0-150.29 0 202.3 202.3 0 0 0-123.43 186.4v224.26a25.2 25.2 0 0 0 5.04 14.7q4.46.63 8.96.61c8.52.2 16.98-1.52 24.74-5.04a89 89 0 0 0 26.8-22.57c2.57-2.82 5.04-5.54 7.66-8.06 3.6-3.67 7.7-6.84 12.14-9.42a41 41 0 0 1 20.81-5.04c19.75 0 29.43 10.53 40.6 22.73a101 101 0 0 0 22.58 20.15 63 63 0 0 0 4.48 2.42 60.4 60.4 0 0 0 49.48 0q2.33-1.08 4.43-2.37c8.5-5.54 16.1-12.34 22.57-20.15 11.18-12.2 20.86-22.73 40.6-22.73A41 41 0 0 1 513.2 571a58 58 0 0 1 12.15 9.42 132 132 0 0 1 7.65 8.07 89 89 0 0 0 27 22.36 57 57 0 0 0 24.75 5.04q4.53 0 9.01-.6c3.2-4.25 4.95-9.4 5.04-14.71v-224.1a202 202 0 0 0-15.32-76.83zm0 124.74v176.33c-18.9-.45-28.41-10.73-39.35-22.67-2.42-2.62-5.04-5.34-7.5-7.96a83 83 0 0 0-11.44-9.73 55.4 55.4 0 0 0-32.8-10.07c-26.4 0-39.9 14.71-51.74 27.66s-20.9 23.02-40.65 23.02-29.43-10.53-40.61-22.72-25.2-27.66-51.74-27.66a55.4 55.4 0 0 0-32.75 10.08 83 83 0 0 0-11.44 9.72c-2.62 2.62-5.04 5.34-7.56 7.96-10.93 11.94-20.45 22.17-39.3 22.67V376.46c0-102.78 82.33-186.4 183.5-186.4 101.16 0 183.49 83.63 183.49 186.4z"/><path d="M511.69 280.75a59.6 59.6 0 1 0 59.6 63.83q.4-3.98 0-7.95a59.55 59.55 0 0 0-59.6-55.88m-44.53 59.65a44.45 44.45 0 0 1 66.1-38.84h-.91a38.92 38.92 0 0 0 0 77.84h.9a44.45 44.45 0 0 1-66.1-38.84zm-38.59-3.98a59.6 59.6 0 1 0 0 7.96q.4-3.99 0-7.96m-104.14 3.98a44.44 44.44 0 0 1 66.1-38.84h-.6a38.91 38.91 0 1 0 0 77.84h.9a44.45 44.45 0 0 1-66.4-39"/>
191
+ </svg> Stale entries in the baseline</h2>
192
+ <p>
193
+ These violations are in the baseline (typically <tt>.dependency-cruiser-known-violations.json</tt>),
194
+ but don't match any real violations anymore, e.g. because they were fixed in the meantime. You
195
+ can remove them with dependency-cruiser's
196
+ <a href="https://github.com/sverweij/dependency-cruiser/blob/main/doc/cli.md#--baseline-create-or-update-a-known-violations-baseline"><tt>--baseline --baseline-mode shrink-only</tt>
197
+ command line options</a>.
198
+ </p>
199
+ <table>
200
+ <thead>
201
+ <tr>
202
+ <th>severity</th>
203
+ <th>rule</th>
204
+ <th>from</th>
205
+ <th>types</th>
206
+ <th>to</th>
207
+ </tr>
208
+ </thead>
209
+ <tbody>
210
+ ${old
211
+ .map(
212
+ constructViolationRow(
213
+ pResults.summary.optionsUsed.prefix ?? "",
214
+ pOptions,
215
+ ),
216
+ )
217
+ .join("\n")}
218
+ </tbody>
219
+ </table>
220
+ </span>`;
221
+ }
222
+ return "";
223
+ }
224
+
225
+ /**
226
+ * @param {ICruiseResult} pResults
227
+ * @param {IErrorReporterOptions} pOptions
158
228
  * @returns {string}
159
229
  */
160
230
  function report(pResults, pOptions) {
@@ -173,8 +243,13 @@ function report(pResults, pOptions) {
173
243
  .replace("{{warn}}", pResults.summary.warn)
174
244
  .replace("{{info}}", pResults.summary.info)
175
245
  .replace("{{ignore}}", pResults.summary.ignore ?? 0)
246
+ .replace("{{staleDiv}}", constructStaleDiv(pResults.summary.baselineStale))
176
247
  .replace("{{violatedRulesTable}}", constructViolatedRulesTable(pResults))
177
248
  .replace("{{violationsList}}", constructViolationsList(pResults, lOptions))
249
+ .replace(
250
+ "{{baselineStaleTable}}",
251
+ constructBaselineStaleTable(pResults, pOptions),
252
+ )
178
253
  .replace("{{depcruiseVersion}}", `dependency-cruiser@${meta.version}`)
179
254
  .replace("{{runDate}}", new Date().toISOString());
180
255
  }
@@ -182,8 +257,9 @@ function report(pResults, pOptions) {
182
257
  /**
183
258
  * Returns the results of a cruise in an 'incidence matrix'
184
259
  *
185
- * @param {import("../../../types/cruise-result.mjs").ICruiseResult} pResults - the output of a dependency-cruise adhering to ../../schema/cruise-result.schema.json
186
- * @returns {import("../../../types/dependency-cruiser.js").IReporterOutput} - output: an html program showing the summary & the violations (if any)
260
+ * @param {ICruiseResult} pResults - the output of a dependency-cruise adhering to ../../schema/cruise-result.schema.json
261
+ * @param {IErrorReporterOptions} pOptions
262
+ * @returns {IReporterOutput} - output: an html program showing the summary & the violations (if any)
187
263
  * exitCode: 0
188
264
  */
189
265
  export default function errorHtml(pResults, pOptions) {
@@ -8,6 +8,13 @@ import {
8
8
  import { findRuleByName } from "#graph-utl/rule-set.mjs";
9
9
  import wrapAndIndent from "#utl/wrap-and-indent.mjs";
10
10
 
11
+ /**
12
+ * @import { ICruiseResult, IEnvironmentIssue } from "../../types/cruise-result.mjs"
13
+ * @import { IErrorReporterOptions } from "../../types/reporter-options.mjs"
14
+ * @import { IReporterOutput } from "../../types/dependency-cruiser.js"
15
+ * @import { SeverityType } from "../../types/shared-types.mjs"
16
+ */
17
+
11
18
  const SEVERITY2COLOR = new Map([
12
19
  ["error", "red"],
13
20
  ["warn", "yellow"],
@@ -114,6 +121,10 @@ function addExplanation(pRuleSet, pLong) {
114
121
  : (pViolation) => pViolation;
115
122
  }
116
123
 
124
+ /**
125
+ * @param {number} pNumberOfIgnoredViolations
126
+ * @returns {string}
127
+ */
117
128
  function formatIgnoreWarning(pNumberOfIgnoredViolations) {
118
129
  if (pNumberOfIgnoredViolations > 0) {
119
130
  return styleText(
@@ -123,8 +134,9 @@ function formatIgnoreWarning(pNumberOfIgnoredViolations) {
123
134
  }
124
135
  return "";
125
136
  }
137
+
126
138
  /**
127
- * @param {import("../../types/cruise-result.mjs").IEnvironmentIssue} pEnvironmentIssue
139
+ * @param {IEnvironmentIssue} pEnvironmentIssue
128
140
  * @returns {string}
129
141
  */
130
142
  function formatEnvironmentIssue(pEnvironmentIssue) {
@@ -135,7 +147,7 @@ function formatEnvironmentIssue(pEnvironmentIssue) {
135
147
  }
136
148
 
137
149
  /**
138
- * @param {import("../../types/cruise-result.mjs").IEnvironmentIssue[]} pEnvironmentIssues
150
+ * @param {IEnvironmentIssue[]} pEnvironmentIssues
139
151
  * @returns {string}
140
152
  */
141
153
  function formatEnvironmentIssues(pEnvironmentIssues) {
@@ -147,6 +159,30 @@ function formatEnvironmentIssues(pEnvironmentIssues) {
147
159
  );
148
160
  }
149
161
 
162
+ /**
163
+ * @param {number} pBaselineStaleCount
164
+ * @param {SeverityType} pStaleEntriesSeverity
165
+ * @returns {string}
166
+ */
167
+ function formatStaleBaselineMessage(
168
+ pBaselineStaleCount,
169
+ pStaleEntriesSeverity,
170
+ ) {
171
+ if ((pBaselineStaleCount ?? 0) > 0) {
172
+ const lStaleEntriesSeverity = pStaleEntriesSeverity ?? "warn";
173
+ return styleText(
174
+ SEVERITY2COLOR.get(lStaleEntriesSeverity),
175
+ `${SEVERITY2ICON.get(lStaleEntriesSeverity)} ${pBaselineStaleCount} stale known violations in the baseline. Run with '--baseline --baseline-mode shrink-only' to remove them.${EOL}`,
176
+ );
177
+ }
178
+ return "";
179
+ }
180
+
181
+ /**
182
+ * @param {ICruiseResult} pResults
183
+ * @param {IErrorReporterOptions} pOptions
184
+ * @returns {string}
185
+ */
150
186
  function report(pResults, pOptions) {
151
187
  const lOptions = {
152
188
  long: false,
@@ -163,13 +199,20 @@ function report(pResults, pOptions) {
163
199
  pResults.summary.totalCruised
164
200
  } modules, ${
165
201
  pResults.summary.totalDependenciesCruised
166
- } dependencies cruised)${EOL}${formatIgnoreWarning(
167
- pResults.summary.ignore,
168
- )}${formatEnvironmentIssues(pResults.summary.environment?.issues)}${EOL}`;
202
+ } dependencies cruised)${EOL}`
203
+ .concat(formatIgnoreWarning(pResults.summary.ignore))
204
+ .concat(
205
+ formatStaleBaselineMessage(
206
+ pResults.summary.baselineStale,
207
+ pResults.summary.optionsUsed?.baseline?.staleEntriesSeverity,
208
+ ),
209
+ )
210
+ .concat(formatEnvironmentIssues(pResults.summary.environment?.issues))
211
+ .concat(EOL);
169
212
  }
170
213
 
171
214
  return lNonIgnorableViolations
172
- .reverse()
215
+ .toReversed()
173
216
  .map(addExplanation(pResults.summary.ruleSetUsed, lOptions.long))
174
217
  .reduce(
175
218
  (pAll, pThis) => `${pAll} ${formatViolation(pThis, lOptions)}${EOL}`,
@@ -177,6 +220,12 @@ function report(pResults, pOptions) {
177
220
  )
178
221
  .concat(formatSummary(pResults.summary))
179
222
  .concat(formatIgnoreWarning(pResults.summary.ignore))
223
+ .concat(
224
+ formatStaleBaselineMessage(
225
+ pResults.summary.baselineStale,
226
+ pResults.summary.optionsUsed?.baseline?.staleEntriesSeverity,
227
+ ),
228
+ )
180
229
  .concat(formatEnvironmentIssues(pResults.summary.environment?.issues))
181
230
  .concat(EOL);
182
231
  }
@@ -187,16 +236,16 @@ function report(pResults, pOptions) {
187
236
  * - for each violation a message stating the violation name and the to and from
188
237
  * - a summary with total number of errors and warnings found, and the total
189
238
  * number of files cruised
190
- * @param {import("../../types/cruise-result.mjs").ICruiseResult} pResults -
191
- * @param {any} pOptions - An object with options;
192
- * {boolean} long - whether or not to include an explanation
193
- * (/ comment) which each violation
194
- * @returns {import("../../types/dependency-cruiser.js").IReporterOutput} - output: the formatted text in a string
195
- * exitCode: the number of errors found
239
+ * @param {ICruiseResult} pResults
240
+ * @param {IErrorReporterOptions} pOptions
241
+ * @returns {IReporterOutput}
196
242
  */
197
243
  export default function error(pResults, pOptions) {
198
244
  return {
199
245
  output: report(pResults, pOptions || {}),
200
- exitCode: pResults.summary.error,
246
+ // advisedExitCode is a mandatory attribute as of dependency-cruiser 18.4.0
247
+ // however, this reporter can still encounter dependency-cruiser results
248
+ // from before that, hence the fallback
249
+ exitCode: pResults.summary.advisedExitCode ?? pResults.summary.error,
201
250
  };
202
251
  }
@@ -54,7 +54,18 @@ function severity2Icon(pSeverity) {
54
54
  function formatStatsSummary(pSummary) {
55
55
  const lSpacerLength = 4;
56
56
  const lSpacer = "&nbsp;".repeat(lSpacerLength);
57
- return `**${pSummary.totalCruised}** modules${lSpacer}**${pSummary.totalDependenciesCruised}** dependencies${lSpacer}**${pSummary.error}** errors${lSpacer}**${pSummary.warn}** warnings${lSpacer}**${pSummary.info}** informational${lSpacer}**${pSummary.ignore}** ignored\n`;
57
+ return `**${pSummary.totalCruised}** modules${lSpacer}`
58
+ .concat(`**${pSummary.totalDependenciesCruised}** dependencies${lSpacer}`)
59
+ .concat(`**${pSummary.error}** errors${lSpacer}`)
60
+ .concat(`**${pSummary.warn}** warnings${lSpacer}`)
61
+ .concat(`**${pSummary.info}** informational${lSpacer}`)
62
+ .concat(`**${pSummary.ignore}** ignored`)
63
+ .concat(
64
+ (pSummary.baselineStale ?? 0) > 0
65
+ ? `${lSpacer}**${pSummary.baselineStale}** stale entries in baseline`
66
+ : "",
67
+ )
68
+ .concat("\n");
58
69
  }
59
70
 
60
71
  /**
@@ -86,7 +97,7 @@ function formatRulesSummary(pCruiseResult, pIncludeIgnoredInSummary) {
86
97
  /**
87
98
  *
88
99
  * @param {import("../../types/cruise-result.mjs").IViolation[]} pViolations
89
- * @param {object} pOptions
100
+ * @param {import("../../types/reporter-options.mjs").IMarkdownReporterOptions} pOptions
90
101
  * @returns {string}
91
102
  */
92
103
  function formatViolations(pViolations, pOptions) {
@@ -108,19 +119,16 @@ function formatViolations(pViolations, pOptions) {
108
119
  }, lTableHead);
109
120
  }
110
121
 
111
- function details(pResults, pOptions) {
122
+ function details(pViolations, pOptions) {
112
123
  let lReturnValue = "";
113
- if (pResults.summary.violations.length > 0) {
124
+ if (pViolations.length > 0) {
114
125
  if (pOptions.showDetailsHeader) {
115
126
  lReturnValue += `${pOptions.detailsHeader}\n\n`;
116
127
  }
117
128
  if (pOptions.collapseDetails) {
118
129
  lReturnValue += `<details><summary>${pOptions.collapsedMessage}</summary>\n\n`;
119
130
  }
120
- lReturnValue += `${formatViolations(
121
- pResults.summary.violations,
122
- pOptions,
123
- )}\n\n`;
131
+ lReturnValue += `${formatViolations(pViolations, pOptions)}\n\n`;
124
132
  if (pOptions.collapseDetails) {
125
133
  lReturnValue += "</details>\n\n";
126
134
  }
@@ -165,7 +173,7 @@ function report(pResults, pOptions) {
165
173
  }
166
174
 
167
175
  if (lOptions.showDetails) {
168
- lReturnValue += details(pResults, lOptions);
176
+ lReturnValue += details(pResults.summary.violations, lOptions);
169
177
  }
170
178
 
171
179
  if (lOptions.showFooter) {
@@ -1,12 +1,15 @@
1
1
  /**
2
2
  * Returns the results of a cruise _only_ in an exitCode
3
3
  *
4
- * @param {import("../../types/cruise-result").ICruiseResult} pResults
5
- * @returns {import("../../types/dependency-cruiser").IReporterOutput}
4
+ * @param {import("../../types/cruise-result.mjs").ICruiseResult} pResults
5
+ * @returns {import("../../types/dependency-cruiser.mjs").IReporterOutput}
6
6
  */
7
7
  export default function nullReporter(pResults) {
8
8
  return {
9
9
  output: "",
10
- exitCode: pResults.summary.error,
10
+ // advisedExitCode is a mandatory attribute as of dependency-cruiser 18.4.0
11
+ // however, this reporter can still encounter dependency-cruiser results
12
+ // from before that, hence the fallback
13
+ exitCode: pResults.summary.advisedExitCode ?? pResults.summary.error,
11
14
  };
12
15
  }
@@ -273,6 +273,9 @@ export default function teamcity(pResults) {
273
273
  reportViolatedRules(lRuleSet, lViolations, lIgnoredCount, lFlowId)
274
274
  .concat(reportViolations(lViolations, lIgnoredCount, lFlowId))
275
275
  .reduce((pAll, pCurrent) => `${pAll}${pCurrent}${EOL}`, "") || EOL,
276
- exitCode: pResults.summary.error,
276
+ // advisedExitCode is a mandatory attribute as of dependency-cruiser 18.4.0
277
+ // however, this reporter can still encounter dependency-cruiser results
278
+ // from before that, hence the fallback
279
+ exitCode: pResults.summary.advisedExitCode ?? pResults.summary.error,
277
280
  };
278
281
  }