dependency-cruiser 18.0.0 → 18.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dependency-cruiser",
3
- "version": "18.0.0",
3
+ "version": "18.1.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,12 +149,12 @@
149
149
  "acorn-loose": "8.5.2",
150
150
  "acorn-walk": "8.3.5",
151
151
  "commander": "15.0.0",
152
- "enhanced-resolve": "5.24.1",
153
- "ignore": "7.0.5",
152
+ "enhanced-resolve": "5.24.2",
153
+ "ignore": "7.0.6",
154
154
  "interpret": "3.1.1",
155
155
  "is-installed-globally": "1.0.0",
156
156
  "json5": "2.2.3",
157
- "picomatch": "4.0.4",
157
+ "picomatch": "4.0.5",
158
158
  "prompts": "2.4.2",
159
159
  "rechoir": "0.8.0",
160
160
  "safe-regex": "2.1.1",
@@ -1,3 +1,4 @@
1
+ import { EOL } from "node:os";
1
2
  import addRuleSetUsed from "./add-rule-set-used.mjs";
2
3
  import summarizeModules from "./summarize-modules.mjs";
3
4
  import summarizeFolders from "./summarize-folders.mjs";
@@ -8,31 +9,146 @@ import {
8
9
  getDependenciesCruised,
9
10
  } from "./get-stats.mjs";
10
11
  import { compareViolations } from "#graph-utl/compare.mjs";
12
+ import { getEnvironmentInfo } from "#environment.mjs";
13
+
14
+ /**
15
+ * @import { IOptions, IEnvironmentIssue, ISummary, IModule, IFolder } from "../../../types/cruise-result.mjs"
16
+ * @import { IAvailableTranspiler } from "../../../types/environment.mjs"
17
+ * @import { IStrictCruiseOptions } from "../../../types/strict-options.mjs"
18
+ */
19
+
20
+ /**
21
+ * @param { IOptions } pOptions
22
+ * @param { IAvailableTranspiler[] } pAvailableTranspilers
23
+ * @returns { boolean }
24
+ */
25
+ function swcIsConsistent(pOptions, pAvailableTranspilers) {
26
+ const lSWCNeeded = (pOptions?.parser ?? "acorn") === "swc";
27
+ return lSWCNeeded
28
+ ? pAvailableTranspilers.some(
29
+ (pTranspiler) => pTranspiler.name === "swc" && pTranspiler.available,
30
+ )
31
+ : true;
32
+ }
33
+
34
+ /**
35
+ * @param { IOptions } pOptions
36
+ * @param { IAvailableTranspiler[] } pAvailableTranspilers
37
+ * @returns { boolean }
38
+ */
39
+ function typescriptIsConsistent(pOptions, pAvailableTranspilers) {
40
+ const lTypeScriptNeeded =
41
+ Boolean(pOptions?.tsConfig) ||
42
+ Boolean(pOptions?.tsPreCompilationDeps) ||
43
+ Boolean(pOptions?.detectJSDocImports) ||
44
+ (pOptions?.parser ?? "acorn") === "tsc";
45
+ return lTypeScriptNeeded
46
+ ? pAvailableTranspilers.some(
47
+ (pTranspiler) =>
48
+ pTranspiler.name === "typescript" && pTranspiler.available,
49
+ )
50
+ : true;
51
+ }
52
+ /**
53
+ * @param { IOptions } pOptions
54
+ * @param { IAvailableTranspiler[] } pAvailableTranspilers
55
+ * @returns { boolean }
56
+ */
57
+ function babelIsConsistent(pOptions, pAvailableTranspilers) {
58
+ return Boolean(pOptions?.babelConfig)
59
+ ? pAvailableTranspilers.some(
60
+ (pTranspiler) => pTranspiler.name === "babel" && pTranspiler.available,
61
+ )
62
+ : true;
63
+ }
64
+
65
+ /**
66
+ * @param {IOptions} pOptions
67
+ * @param {IAvailableTranspiler[]} pAvailableTranspilers
68
+ * @returns {{issues?: IEnvironmentIssue[]}}
69
+ */
70
+ function determineEnvironmentIssues(pOptions, pAvailableTranspilers) {
71
+ const lIssues = [];
72
+ if (!typescriptIsConsistent(pOptions, pAvailableTranspilers)) {
73
+ const tsTranspiler = pAvailableTranspilers.find(
74
+ (pTranspiler) => pTranspiler.name === "typescript",
75
+ );
76
+
77
+ lIssues.push({
78
+ severity: "warn",
79
+ name: "missing-typescript-transpiler",
80
+ description:
81
+ `dependency-cruiser detected a TypeScript environment,${EOL}` +
82
+ ` but not a compatible TypeScript compiler (${tsTranspiler.name}: ${tsTranspiler.version}). This means${EOL}` +
83
+ ` it's likely to have missed TypeScript sources and dependencies.${EOL}${EOL}` +
84
+ ` Install typescript to get better results (e.g. npm i -D typescript@^6).${EOL}${EOL}` +
85
+ ` => Support for typescript@>=7 will follow when its API is published and stable.${EOL}`,
86
+ });
87
+ }
88
+ if (!swcIsConsistent(pOptions, pAvailableTranspilers)) {
89
+ const swcTranspiler = pAvailableTranspilers.find(
90
+ (pTranspiler) => pTranspiler.name === "swc",
91
+ );
92
+
93
+ lIssues.push({
94
+ severity: "warn",
95
+ name: "missing-swc-transpiler",
96
+ description:
97
+ `dependency-cruiser found swc configured as parser, but ${EOL}` +
98
+ ` it did not find a compatible version of swc (${swcTranspiler.name}: ${swcTranspiler.version}).${EOL}` +
99
+ ` dependency-cruiser might have missed some sources or dependencies.${EOL}${EOL}` +
100
+ ` Install swc (e.g. npm i -D @swc/core) to get better results.${EOL}`,
101
+ });
102
+ }
103
+ if (!babelIsConsistent(pOptions, pAvailableTranspilers)) {
104
+ const babelTranspiler = pAvailableTranspilers.find(
105
+ (pTranspiler) => pTranspiler.name === "babel",
106
+ );
107
+
108
+ lIssues.push({
109
+ severity: "warn",
110
+ name: "missing-babel-transpiler",
111
+ description:
112
+ `dependency-cruiser detected a babel configuration, but not a${EOL}` +
113
+ ` compatible babel transpiler (${babelTranspiler.name}: ${babelTranspiler.version}). If you indeed use babel,${EOL}` +
114
+ ` dependency-cruiser might have missed some sources or dependencies.${EOL}${EOL}` +
115
+ ` Install babel (e.g. npm i -D @babel/core@^7) to get better results.${EOL}`,
116
+ });
117
+ }
118
+ return lIssues.length > 0 ? { issues: lIssues } : {};
119
+ }
11
120
 
12
121
  /**
13
122
  *
14
- * @param {import("../../../types/cruise-result.mjs").IModule[]} pModules -
123
+ * @param {IModule[]} pModules -
15
124
  * cruised modules that have been enriched with mandatory attributes &
16
125
  * have been validated against rules as passed in the options
17
- * @param {import("../../../types/options.mjs").IStrictCruiseOptions} pOptions -
126
+ * @param {IStrictCruiseOptions} pOptions -
18
127
  * @param {string[]} pFileDirectoryArray -
19
128
  * the files/ directories originally passed to be cruised
20
- * @param {import("../../../types/dependency-cruiser.js").IFolder[]} pFolders -
129
+ * @param {IFolder[]} pFolders -
21
130
  * the pModules collapsed to folders, with their own metrics & deps
22
131
  *
23
- * @returns {import("../../../types/cruise-result.mjs").ISummary} -
132
+ * @returns {ISummary} -
24
133
  * a summary of the found modules, dependencies and any violations
25
134
  */
135
+ // eslint-disable-next-line max-params
26
136
  export default function summarize(
27
137
  pModules,
28
138
  pOptions,
29
139
  pFileDirectoryArray,
30
140
  pFolders,
141
+ pGetEnvironmentInfoFunction = getEnvironmentInfo,
31
142
  ) {
32
143
  const lViolations = summarizeModules(pModules, pOptions.ruleSet)
33
144
  .concat(summarizeFolders(pFolders || [], pOptions.ruleSet))
34
145
  .sort(compareViolations);
35
146
 
147
+ const lEnvironment = pGetEnvironmentInfoFunction();
148
+ const lEnvironmentWithIssues = {
149
+ ...lEnvironment,
150
+ ...determineEnvironmentIssues(pOptions, lEnvironment.transpilersFound),
151
+ };
36
152
  return {
37
153
  violations: lViolations,
38
154
  ...getViolationStats(lViolations),
@@ -40,5 +156,6 @@ export default function summarize(
40
156
  totalDependenciesCruised: getDependenciesCruised(pModules),
41
157
  ...summarizeOptions(pFileDirectoryArray, pOptions),
42
158
  ...(pOptions.ruleSet ? { ruleSetUsed: addRuleSetUsed(pOptions) } : {}),
159
+ environment: lEnvironmentWithIssues,
43
160
  };
44
161
  }
@@ -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 = 16.2;
36
+ const CACHE_FORMAT_VERSION = 18.1;
37
37
 
38
38
  export default class Cache {
39
39
  /** @type {(IRevisionData | null)=} */
@@ -1,8 +1,5 @@
1
- import { release, platform, arch } from "node:os";
2
1
  import { styleText } from "node:util";
3
-
4
- import { getAvailableTranspilers, allExtensions } from "#main/index.mjs";
5
- import meta from "#meta.cjs";
2
+ import { getEnvironmentInfo } from "#environment.mjs";
6
3
 
7
4
  function bool2Symbol(pBool) {
8
5
  return pBool ? styleText("green", "✔") : styleText("red", "x");
@@ -12,13 +9,13 @@ const MAX_VERSION_RANGE_STRING_LENGTH = 19;
12
9
  const MAX_TRANSPILER_NAME_LENGTH = 22;
13
10
  const MAX_VERSION_STRING_LENGTH = 24;
14
11
 
15
- function formatTranspilers() {
12
+ function formatTranspilers(pAvailableTranspilers) {
16
13
  let lTranspilerTableHeader = styleText(
17
14
  "bold",
18
15
  ` ✔ ${"transpiler".padEnd(MAX_TRANSPILER_NAME_LENGTH)} ${"versions supported".padEnd(MAX_VERSION_RANGE_STRING_LENGTH)} version found`,
19
16
  );
20
17
  let lTranspilerTableDivider = ` - ${"-".repeat(MAX_TRANSPILER_NAME_LENGTH)} ${"-".repeat(MAX_VERSION_RANGE_STRING_LENGTH)} ${"-".repeat(MAX_VERSION_STRING_LENGTH)}`;
21
- let lTranspilerTable = getAvailableTranspilers()
18
+ let lTranspilerTable = pAvailableTranspilers
22
19
  .map(
23
20
  (pTranspiler) =>
24
21
  ` ${bool2Symbol(pTranspiler.available)} ${pTranspiler.name.padEnd(MAX_TRANSPILER_NAME_LENGTH)} ${pTranspiler.version.padEnd(MAX_VERSION_RANGE_STRING_LENGTH)} ${pTranspiler.currentVersion}`,
@@ -36,12 +33,13 @@ function formatExtensions(pExtensions) {
36
33
  }
37
34
 
38
35
  export default function formatMetaInfo() {
36
+ const lMetaInfo = getEnvironmentInfo();
39
37
  return `
40
- ${styleText("bold", "dependency-cruiser")}@${meta.version}
38
+ ${styleText("bold", "dependency-cruiser")}@${lMetaInfo.version}
41
39
 
42
- node version supported : ${meta.engines.node}
43
- node version found : ${process.version}
44
- os version found : ${arch()} ${platform()}@${release()}
40
+ node version supported : ${lMetaInfo.nodeVersionSupported}
41
+ node version found : ${lMetaInfo.nodeVersionFound}
42
+ os version found : ${lMetaInfo.osVersionFound}
45
43
 
46
44
  If you need a supported, but not enabled transpiler ('${styleText(
47
45
  "red",
@@ -50,10 +48,10 @@ export default function formatMetaInfo() {
50
48
  it in the same folder dependency-cruiser is installed. E.g. 'npm i livescript'
51
49
  will enable livescript support if it's installed in your project folder.
52
50
 
53
- ${formatTranspilers()}
51
+ ${formatTranspilers(lMetaInfo.transpilersFound)}
54
52
  ${styleText("bold", "✔ extension")}
55
53
  - ---------
56
- ${formatExtensions(allExtensions)}
54
+ ${formatExtensions(lMetaInfo.extensionsFound)}
57
55
  `;
58
56
  }
59
57
 
package/src/cli/index.mjs CHANGED
@@ -135,11 +135,10 @@ async function runCruise(pFileDirectoryArray, pCruiseOptions) {
135
135
  /**
136
136
  *
137
137
  * @param {string[]} pFileDirectoryArray
138
- * @param {import("../../types/options.mjs").ICruiseOptions} lCruiseOptions
138
+ * @param {import("../../types/options.mjs").ICruiseOptions} pCruiseOptions
139
139
  * @param {{stdout: NodeJS.WritableStream, stderr: NodeJS.WritableStream}=} pStreams
140
140
  * @returns {number}
141
141
  */
142
-
143
142
  // eslint-disable-next-line max-lines-per-function
144
143
  export default async function executeCli(
145
144
  pFileDirectoryArray,
@@ -196,7 +196,7 @@ function normalizeCache(pCliOptions) {
196
196
  *
197
197
  * @param {object} pOptionsAsPassedFromCommander [description]
198
198
  * @param {any} pKnownCliOptions [description]
199
- * @return {object} [description]
199
+ * @return {import("../../types/options.mjs").ICruiseOptions} [description]
200
200
  */
201
201
  export default async function normalizeOptions(pOptionsAsPassedFromCommander) {
202
202
  let lOptions = {
@@ -0,0 +1,17 @@
1
+ import { release, platform, arch } from "node:os";
2
+ import meta from "#meta.cjs";
3
+ import {
4
+ getAvailableTranspilers,
5
+ allExtensions,
6
+ } from "#extract/transpile/meta.mjs";
7
+
8
+ export function getEnvironmentInfo() {
9
+ return {
10
+ version: meta.version,
11
+ nodeVersionSupported: meta.engines.node,
12
+ nodeVersionFound: process.version,
13
+ osVersionFound: `${arch()} ${platform()}@${release()}`,
14
+ transpilersFound: getAvailableTranspilers(),
15
+ extensionsFound: structuredClone(allExtensions),
16
+ };
17
+ }
@@ -39,6 +39,7 @@ function reSummarizeResults(pResult, pFormatOptions) {
39
39
  // TODO: apply filters to the folders too
40
40
  pResult.folders,
41
41
  ),
42
+ environment: pResult.summary.environment,
42
43
  },
43
44
  modules: lModules,
44
45
  };
package/src/meta.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /* generated - don't edit */
2
2
  module.exports = {
3
- version: "18.0.0",
3
+ version: "18.1.0",
4
4
  engines: {
5
5
  node: "^22||^24||>=26",
6
6
  },
@@ -42,6 +42,13 @@ export default `<!DOCTYPE html>
42
42
  width: 100%;
43
43
  }
44
44
 
45
+ th {
46
+ position: sticky;
47
+ top: 0;
48
+ z-index: 2;
49
+ background-color: white
50
+ }
51
+
45
52
  th,
46
53
  td {
47
54
  text-align: left;
@@ -15,6 +15,13 @@ const SEVERITY2COLOR = new Map([
15
15
  ["ignore", "gray"],
16
16
  ]);
17
17
 
18
+ const SEVERITY2ICON = new Map([
19
+ ["error", "x"],
20
+ ["warn", "‼"],
21
+ ["info", "i"],
22
+ ["ignore", "-"],
23
+ ]);
24
+
18
25
  const EXTRA_PATH_INFORMATION_INDENT = 6;
19
26
 
20
27
  function formatMiniDependency(pMiniDependency) {
@@ -116,6 +123,29 @@ function formatIgnoreWarning(pNumberOfIgnoredViolations) {
116
123
  }
117
124
  return "";
118
125
  }
126
+ /**
127
+ * @param {import("../../types/cruise-result.mjs").IEnvironmentIssue} pEnvironmentIssue
128
+ * @returns {string}
129
+ */
130
+ function formatEnvironmentIssue(pEnvironmentIssue) {
131
+ return styleText(
132
+ SEVERITY2COLOR.get(pEnvironmentIssue.severity),
133
+ `${SEVERITY2ICON.get(pEnvironmentIssue.severity)} ${styleText("bold", pEnvironmentIssue.name)}: ${pEnvironmentIssue.description}`,
134
+ );
135
+ }
136
+
137
+ /**
138
+ * @param {import("../../types/cruise-result.mjs").IEnvironmentIssue[]} pEnvironmentIssues
139
+ * @returns {string}
140
+ */
141
+ function formatEnvironmentIssues(pEnvironmentIssues) {
142
+ const lEnvironmentIssues = pEnvironmentIssues ?? [];
143
+
144
+ return (
145
+ (lEnvironmentIssues.length > 0 ? EOL : "") +
146
+ lEnvironmentIssues.map(formatEnvironmentIssue).join(EOL)
147
+ );
148
+ }
119
149
 
120
150
  function report(pResults, pOptions) {
121
151
  const lOptions = {
@@ -135,7 +165,7 @@ function report(pResults, pOptions) {
135
165
  pResults.summary.totalDependenciesCruised
136
166
  } dependencies cruised)${EOL}${formatIgnoreWarning(
137
167
  pResults.summary.ignore,
138
- )}${EOL}`;
168
+ )}${formatEnvironmentIssues(pResults.summary.environment?.issues)}${EOL}`;
139
169
  }
140
170
 
141
171
  return lNonIgnorableViolations
@@ -147,6 +177,7 @@ function report(pResults, pOptions) {
147
177
  )
148
178
  .concat(formatSummary(pResults.summary))
149
179
  .concat(formatIgnoreWarning(pResults.summary.ignore))
180
+ .concat(formatEnvironmentIssues(pResults.summary.environment?.issues))
150
181
  .concat(EOL);
151
182
  }
152
183