dependency-cruiser 18.0.0-beta-1 → 18.0.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.
- package/package.json +6 -5
- package/src/config-utl/extract-babel-config.mjs +2 -2
- package/src/config-utl/extract-depcruise-config/index.mjs +1 -1
- package/src/config-utl/extract-depcruise-config/read-config.mjs +1 -1
- package/src/config-utl/extract-known-violations.mjs +2 -1
- package/src/extract/resolve/resolve.mjs +3 -5
- package/src/graph-utl/compare.mjs +69 -1
- package/src/main/cruise.mjs +1 -1
- package/src/main/resolve-options/normalize.mjs +53 -10
- package/src/meta.cjs +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dependency-cruiser",
|
|
3
|
-
"version": "18.0.0-beta-
|
|
3
|
+
"version": "18.0.0-beta-2",
|
|
4
4
|
"description": "Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"static analysis",
|
|
@@ -143,13 +143,13 @@
|
|
|
143
143
|
"README.md"
|
|
144
144
|
],
|
|
145
145
|
"dependencies": {
|
|
146
|
-
"acorn": "8.
|
|
146
|
+
"acorn": "8.17.0",
|
|
147
147
|
"acorn-jsx": "5.3.2",
|
|
148
148
|
"acorn-jsx-walk": "2.0.0",
|
|
149
149
|
"acorn-loose": "8.5.2",
|
|
150
150
|
"acorn-walk": "8.3.5",
|
|
151
151
|
"commander": "15.0.0",
|
|
152
|
-
"enhanced-resolve": "5.
|
|
152
|
+
"enhanced-resolve": "5.24.0",
|
|
153
153
|
"ignore": "7.0.5",
|
|
154
154
|
"interpret": "3.1.1",
|
|
155
155
|
"is-installed-globally": "1.0.0",
|
|
@@ -158,8 +158,9 @@
|
|
|
158
158
|
"prompts": "2.4.2",
|
|
159
159
|
"rechoir": "0.8.0",
|
|
160
160
|
"safe-regex": "2.1.1",
|
|
161
|
-
"semver": "7.8.
|
|
162
|
-
"
|
|
161
|
+
"semver": "7.8.5",
|
|
162
|
+
"tsconfig-paths-webpack-plugin": "4.2.0",
|
|
163
|
+
"watskeburt": "6.0.0"
|
|
163
164
|
},
|
|
164
165
|
"engines": {
|
|
165
166
|
"node": "^22||^24||>=26"
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
|
|
3
3
|
import { extname } from "node:path";
|
|
4
|
-
import json5 from "json5";
|
|
5
4
|
import makeAbsolute from "./make-absolute.mjs";
|
|
6
5
|
import tryImport from "#utl/try-import.mjs";
|
|
7
6
|
import meta from "#meta.cjs";
|
|
@@ -36,6 +35,7 @@ async function getJSConfig(pBabelConfigFileName) {
|
|
|
36
35
|
|
|
37
36
|
async function getJSON5Config(pBabelConfigFileName) {
|
|
38
37
|
let lReturnValue = {};
|
|
38
|
+
const { default: json5 } = await import("json5");
|
|
39
39
|
|
|
40
40
|
try {
|
|
41
41
|
lReturnValue = json5.parse(await readFile(pBabelConfigFileName, "utf8"));
|
|
@@ -95,7 +95,7 @@ export default async function extractBabelConfig(pBabelConfigFileName) {
|
|
|
95
95
|
filename: pBabelConfigFileName,
|
|
96
96
|
};
|
|
97
97
|
lReturnValue = {
|
|
98
|
-
...babel.
|
|
98
|
+
...babel.loadOptionsSync(lConfig),
|
|
99
99
|
// according to the babel documentation a config parsed & expanded through
|
|
100
100
|
// loadOptions can be passed to the parser. With some plugins/ presets
|
|
101
101
|
// this does not seem to be true anymore, though
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { extname } from "node:path";
|
|
3
|
-
import json5 from "json5";
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
*
|
|
@@ -16,5 +15,6 @@ export default async function readConfig(pAbsolutePathToConfigFile) {
|
|
|
16
15
|
);
|
|
17
16
|
return config;
|
|
18
17
|
}
|
|
18
|
+
const { default: json5 } = await import("json5");
|
|
19
19
|
return json5.parse(await readFile(pAbsolutePathToConfigFile, "utf8"));
|
|
20
20
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
|
-
import json5 from "json5";
|
|
3
2
|
import makeAbsolute from "./make-absolute.mjs";
|
|
4
3
|
|
|
5
4
|
/**
|
|
@@ -53,6 +52,8 @@ function makeForwardCompatible(pKnownViolation) {
|
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
export default async function extractKnownViolations(pKnownViolationsFileName) {
|
|
55
|
+
const { default: json5 } = await import("json5");
|
|
56
|
+
|
|
56
57
|
try {
|
|
57
58
|
const lFileContents = await readFile(
|
|
58
59
|
makeAbsolute(pKnownViolationsFileName),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { resolve as resolvePath } from "node:path";
|
|
2
1
|
import enhancedResolve from "enhanced-resolve";
|
|
3
2
|
import { stripQueryParameters } from "../helpers.mjs";
|
|
3
|
+
import pathToPosix from "#utl/path-to-posix.mjs";
|
|
4
4
|
|
|
5
5
|
/** @import {IResolveOptions} from "../../../types/resolve-options.mjs" */
|
|
6
6
|
|
|
@@ -50,10 +50,8 @@ export function resolve(
|
|
|
50
50
|
return stripQueryParameters(
|
|
51
51
|
gResolvers.get(pCachingContext).resolveSync(
|
|
52
52
|
{},
|
|
53
|
-
// lookupStartPath
|
|
54
|
-
|
|
55
|
-
// treated as PathType.Normal and POSIX-normalised, breaking ".." resolution)
|
|
56
|
-
resolvePath(pFileDirectory),
|
|
53
|
+
// lookupStartPath
|
|
54
|
+
pathToPosix(pFileDirectory),
|
|
57
55
|
// request
|
|
58
56
|
pModuleName,
|
|
59
57
|
),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** @import { IViolation } from "../../types/dependency-cruiser.mjs" */
|
|
1
2
|
/* eslint-disable no-magic-numbers */
|
|
2
3
|
function severity2number(pSeverity) {
|
|
3
4
|
const lSeverity2Number = new Map([
|
|
@@ -16,6 +17,63 @@ export function compareSeverities(pFirstSeverity, pSecondSeverity) {
|
|
|
16
17
|
);
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Compare two arrays of mini dependencies by their 'name' field.
|
|
22
|
+
* Used to deterministically order violations that have the same severity/rule/from/to
|
|
23
|
+
* but differ in their cycle or via paths.
|
|
24
|
+
*
|
|
25
|
+
* @param {Array<{name: string}>} pFirstArray - First array (or undefined)
|
|
26
|
+
* @param {Array<{name: string}>} pSecondArray - Second array (or undefined)
|
|
27
|
+
* @returns {number} - -1/0/1 following comparison semantics
|
|
28
|
+
*/
|
|
29
|
+
function compareArraysByName(pFirstArray, pSecondArray) {
|
|
30
|
+
const lFirst = pFirstArray || [];
|
|
31
|
+
const lSecond = pSecondArray || [];
|
|
32
|
+
const lMinLength = Math.min(lFirst.length, lSecond.length);
|
|
33
|
+
|
|
34
|
+
// eslint-disable-next-line unicorn/prevent-abbreviations, no-plusplus
|
|
35
|
+
for (let i = 0; i < lMinLength; i++) {
|
|
36
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
37
|
+
const lComparison = lFirst[i].name.localeCompare(lSecond[i].name);
|
|
38
|
+
if (lComparison !== 0) {
|
|
39
|
+
return lComparison;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return Math.sign(lFirst.length - lSecond.length);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Compare two arrays of strings.
|
|
48
|
+
*
|
|
49
|
+
* @param {Array<string>} pFirstArray - First array (or undefined)
|
|
50
|
+
* @param {Array<string>} pSecondArray - Second array (or undefined)
|
|
51
|
+
* @returns {number} - -1/0/1 following comparison semantics
|
|
52
|
+
*/
|
|
53
|
+
function compareArrays(pFirstArray, pSecondArray) {
|
|
54
|
+
const lFirst = pFirstArray || [];
|
|
55
|
+
const lSecond = pSecondArray || [];
|
|
56
|
+
const lMinLength = Math.min(lFirst.length, lSecond.length);
|
|
57
|
+
|
|
58
|
+
// eslint-disable-next-line unicorn/prevent-abbreviations, no-plusplus
|
|
59
|
+
for (let i = 0; i < lMinLength; i++) {
|
|
60
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
61
|
+
const lComparison = lFirst[i].localeCompare(lSecond[i]);
|
|
62
|
+
if (lComparison !== 0) {
|
|
63
|
+
return lComparison;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return Math.sign(lFirst.length - lSecond.length);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
*
|
|
72
|
+
* @param {IViolation} pFirstViolation
|
|
73
|
+
* @param {IViolation} pSecondViolation
|
|
74
|
+
* @returns
|
|
75
|
+
*/
|
|
76
|
+
// eslint-disable-next-line complexity
|
|
19
77
|
export function compareViolations(pFirstViolation, pSecondViolation) {
|
|
20
78
|
return (
|
|
21
79
|
compareSeverities(
|
|
@@ -24,7 +82,17 @@ export function compareViolations(pFirstViolation, pSecondViolation) {
|
|
|
24
82
|
) ||
|
|
25
83
|
pFirstViolation.rule.name.localeCompare(pSecondViolation.rule.name) ||
|
|
26
84
|
pFirstViolation.from.localeCompare(pSecondViolation.from) ||
|
|
27
|
-
pFirstViolation.to.localeCompare(pSecondViolation.to)
|
|
85
|
+
pFirstViolation.to.localeCompare(pSecondViolation.to) ||
|
|
86
|
+
(pFirstViolation.unresolvedTo ?? "").localeCompare(
|
|
87
|
+
pSecondViolation.unresolvedTo ?? "",
|
|
88
|
+
) ||
|
|
89
|
+
(pFirstViolation.type ?? "").localeCompare(pSecondViolation.type ?? "") ||
|
|
90
|
+
compareArrays(
|
|
91
|
+
pFirstViolation.dependencyTypes,
|
|
92
|
+
pSecondViolation.dependencyTypes,
|
|
93
|
+
) ||
|
|
94
|
+
compareArraysByName(pFirstViolation.cycle, pSecondViolation.cycle) ||
|
|
95
|
+
compareArraysByName(pFirstViolation.via, pSecondViolation.via)
|
|
28
96
|
);
|
|
29
97
|
}
|
|
30
98
|
|
package/src/main/cruise.mjs
CHANGED
|
@@ -78,7 +78,7 @@ export default async function cruise(
|
|
|
78
78
|
);
|
|
79
79
|
|
|
80
80
|
bus.summary("startup: get resolve options", c(5));
|
|
81
|
-
const lNormalizedResolveOptions = normalizeResolveOptions(
|
|
81
|
+
const lNormalizedResolveOptions = await normalizeResolveOptions(
|
|
82
82
|
pResolveOptions,
|
|
83
83
|
lCruiseOptions,
|
|
84
84
|
pTranspileOptions?.tsConfig,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import { resolve as resolvePath } from "node:path";
|
|
3
2
|
import enhancedResolve from "enhanced-resolve";
|
|
4
3
|
import { scannableExtensions } from "#extract/transpile/meta.mjs";
|
|
5
4
|
import {
|
|
@@ -62,20 +61,64 @@ function getNonOverridableResolveOptions(pCacheDuration) {
|
|
|
62
61
|
};
|
|
63
62
|
}
|
|
64
63
|
|
|
65
|
-
function
|
|
64
|
+
function pushPlugin(pPlugins, pPluginToPush) {
|
|
65
|
+
return (pPlugins || []).concat(pPluginToPush);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// eslint-disable-next-line max-lines-per-function
|
|
69
|
+
async function compileResolveOptions(
|
|
66
70
|
pResolveOptions,
|
|
67
71
|
pTSConfig,
|
|
68
72
|
pResolveOptionsFromDCConfig,
|
|
69
73
|
) {
|
|
70
74
|
let lResolveOptions = {};
|
|
71
75
|
|
|
76
|
+
// There's a performance impact of ~1 ms per resolve even when there
|
|
77
|
+
// are 0 paths in the tsconfig, so not loading it when not necessary
|
|
78
|
+
// will be a win.
|
|
79
|
+
// Also: requiring the plugin only when it's necessary will save some
|
|
80
|
+
// startup time (especially on a cold require cache)
|
|
72
81
|
if (pResolveOptions.tsConfig) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
//
|
|
78
|
-
|
|
82
|
+
const { default: TsConfigPathsPlugin } =
|
|
83
|
+
await import("tsconfig-paths-webpack-plugin");
|
|
84
|
+
lResolveOptions.plugins = pushPlugin(
|
|
85
|
+
lResolveOptions.plugins,
|
|
86
|
+
// @ts-expect-error TS2351 "TsConfPathsPlugin is not constructable" - is unjustified
|
|
87
|
+
new TsConfigPathsPlugin({
|
|
88
|
+
configFile: pResolveOptions.tsConfig,
|
|
89
|
+
// TsConfigPathsPlugin requires a baseUrl to be present in the tsconfig,
|
|
90
|
+
// otherwise it prints scary messages that it didn't and read the tsConfig
|
|
91
|
+
// (potentially making users think it's dependency-cruiser disregarding the
|
|
92
|
+
// tsconfig). Hence up till version 13.0.4 dependency-cruiser only loaded
|
|
93
|
+
// TsConfigPathsPlugin when an options.baseUrl existed. However, this
|
|
94
|
+
// isn't necessary anymore:
|
|
95
|
+
// - [tsconfig#baseUrl documentation](https://www.typescriptlang.org/tsconfig#baseUrl)
|
|
96
|
+
// UNrecommends the use of the baseUrl for non-AMD projects
|
|
97
|
+
// - [tsconfig-paths PR #207](https://github.com/dividab/tsconfig-paths/pull/208)
|
|
98
|
+
// 'tolerates' undefined baseUrls
|
|
99
|
+
//
|
|
100
|
+
// Hence, until
|
|
101
|
+
// [tpwp issue #99](https://github.com/dividab/tsconfig-paths-webpack-plugin/issues/99)
|
|
102
|
+
// lands:
|
|
103
|
+
// - pass a default baseUrl to TsConfigPathsPlugin if the baseUrl isn't available
|
|
104
|
+
// - pass undefined in all other cases; TsConfigPathsPlugin will read
|
|
105
|
+
// it from the tsconfig.json in that case. Passing the processed baseUrl
|
|
106
|
+
// (pTSConfig?.options?.baseUrl) instead would've been more obvious, but
|
|
107
|
+
// doesn't work, as that is an absolute path and tsconfig-paths(-wpp)
|
|
108
|
+
// seems to process that again resulting in invalid paths and unresolved
|
|
109
|
+
// or erroneous dependencies
|
|
110
|
+
// eslint-disable-next-line no-undefined
|
|
111
|
+
baseUrl: pTSConfig?.options?.baseUrl ? undefined : "./",
|
|
112
|
+
// TsConfigPathsPlugin doesn't (can't) read enhanced-resolve's
|
|
113
|
+
// list of extensions, and the default it uses for extensions
|
|
114
|
+
// so we do it ourselves - either with the extensions passed
|
|
115
|
+
// or with the supported ones.
|
|
116
|
+
extensions:
|
|
117
|
+
pResolveOptionsFromDCConfig.extensions ||
|
|
118
|
+
pResolveOptions.extensions ||
|
|
119
|
+
DEFAULT_RESOLVE_OPTIONS.extensions,
|
|
120
|
+
}),
|
|
121
|
+
);
|
|
79
122
|
}
|
|
80
123
|
|
|
81
124
|
return {
|
|
@@ -97,14 +140,14 @@ function compileResolveOptions(
|
|
|
97
140
|
* @returns
|
|
98
141
|
*/
|
|
99
142
|
// eslint-disable-next-line complexity
|
|
100
|
-
export default function normalizeResolveOptions(
|
|
143
|
+
export default async function normalizeResolveOptions(
|
|
101
144
|
pResolveOptions,
|
|
102
145
|
pOptions,
|
|
103
146
|
pTSConfig,
|
|
104
147
|
) {
|
|
105
148
|
const lRuleSet = pOptions?.ruleSet ?? {};
|
|
106
149
|
|
|
107
|
-
return compileResolveOptions(
|
|
150
|
+
return await compileResolveOptions(
|
|
108
151
|
{
|
|
109
152
|
// EnhancedResolve's symlinks:
|
|
110
153
|
// - true => symlinks are followed (vv)
|