dependency-cruiser 16.7.0-beta-1 → 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.
- package/bin/depcruise-baseline.mjs +1 -1
- package/package.json +6 -6
- package/src/cli/index.mjs +4 -2
- package/src/cli/listeners/performance-log/format-helpers.mjs +1 -1
- package/src/cli/normalize-cli-options.mjs +1 -0
- package/src/cli/utl/io.mjs +1 -2
- package/src/config-utl/extract-babel-config.mjs +0 -1
- package/src/config-utl/extract-known-violations.mjs +4 -4
- package/src/config-utl/extract-webpack-resolve-config.mjs +1 -1
- package/src/enrich/soften-known-violations.mjs +1 -1
- package/src/enrich/summarize/add-rule-set-used.mjs +1 -0
- package/src/enrich/summarize/summarize-modules.mjs +0 -1
- package/src/extract/extract-dependencies.mjs +2 -2
- package/src/extract/gather-initial-sources.mjs +1 -0
- package/src/extract/resolve/determine-dependency-types.mjs +2 -2
- package/src/extract/resolve/external-module-helpers.mjs +4 -4
- package/src/extract/resolve/module-classifiers.mjs +2 -2
- package/src/extract/resolve/resolve-helpers.mjs +3 -3
- package/src/extract/swc/dependency-visitor.mjs +2 -6
- package/src/extract/tsc/extract-typescript-deps.mjs +41 -8
- package/src/graph-utl/filter-bank.mjs +0 -1
- package/src/main/cruise.mjs +2 -2
- package/src/main/format.mjs +0 -1
- package/src/main/options/assert-validity.mjs +7 -7
- package/src/main/resolve-options/normalize.mjs +1 -1
- package/src/main/rule-set/normalize.mjs +3 -3
- package/src/meta.cjs +1 -1
- package/src/report/anon/anonymize-path.mjs +1 -1
- package/src/report/d2.mjs +2 -2
- package/src/report/dot/index.mjs +3 -3
- package/src/report/dot-webpage/svg-in-html-snippets/script.cjs +4 -4
- package/src/report/markdown.mjs +41 -30
- package/src/report/mermaid.mjs +2 -2
- package/src/report/teamcity.mjs +1 -0
- package/src/utl/get-extension.mjs +1 -1
- package/src/utl/try-import.mjs +0 -1
- package/src/utl/try-require.cjs +2 -2
- package/src/validate/match-module-rule-helpers.mjs +1 -1
- package/src/validate/matchers.mjs +1 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dependency-cruiser",
|
|
3
|
-
"version": "16.7.0-beta-
|
|
3
|
+
"version": "16.7.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",
|
|
@@ -159,19 +159,19 @@
|
|
|
159
159
|
"safe-regex": "^2.1.1",
|
|
160
160
|
"semver": "^7.6.3",
|
|
161
161
|
"teamcity-service-messages": "^0.1.14",
|
|
162
|
-
"tsconfig-paths-webpack-plugin": "^4.
|
|
163
|
-
"watskeburt": "^4.1.
|
|
162
|
+
"tsconfig-paths-webpack-plugin": "^4.2.0",
|
|
163
|
+
"watskeburt": "^4.1.1"
|
|
164
164
|
},
|
|
165
165
|
"overrides": {
|
|
166
|
-
"
|
|
166
|
+
"cross-spawn": ">=6.0.6"
|
|
167
167
|
},
|
|
168
168
|
"resolutions": {
|
|
169
|
-
"
|
|
169
|
+
"cross-spawn": ">=6.0.6"
|
|
170
170
|
},
|
|
171
171
|
"engines": {
|
|
172
172
|
"node": "^18.17||>=20"
|
|
173
173
|
},
|
|
174
174
|
"scripts": {
|
|
175
|
-
"test": "echo for test, build and
|
|
175
|
+
"test": "echo see github for test, build and analysis scripts"
|
|
176
176
|
}
|
|
177
177
|
}
|
package/src/cli/index.mjs
CHANGED
|
@@ -13,6 +13,7 @@ import { set } from "#utl/object-util.mjs";
|
|
|
13
13
|
import cruise from "#main/cruise.mjs";
|
|
14
14
|
import { INFO, bus } from "#utl/bus.mjs";
|
|
15
15
|
|
|
16
|
+
// eslint-disable-next-line complexity
|
|
16
17
|
async function extractResolveOptions(pCruiseOptions) {
|
|
17
18
|
let lResolveOptions = {};
|
|
18
19
|
const lWebPackConfigFileName =
|
|
@@ -78,6 +79,7 @@ async function extractBabelConfigOptions(pCruiseOptions) {
|
|
|
78
79
|
return lReturnValue;
|
|
79
80
|
}
|
|
80
81
|
|
|
82
|
+
// eslint-disable-next-line complexity
|
|
81
83
|
function setUpListener(pCruiseOptions) {
|
|
82
84
|
const lString2Listener = new Map([
|
|
83
85
|
["cli-feedback", setUpCliFeedbackListener],
|
|
@@ -90,7 +92,7 @@ function setUpListener(pCruiseOptions) {
|
|
|
90
92
|
|
|
91
93
|
const lListenerFunction = lString2Listener.get(lListenerID);
|
|
92
94
|
/* c8 ignore next 6 */
|
|
93
|
-
if (
|
|
95
|
+
if (lListenerFunction) {
|
|
94
96
|
lListenerFunction(
|
|
95
97
|
bus,
|
|
96
98
|
pCruiseOptions?.ruleSet?.options?.progress?.maximumLevel ?? INFO,
|
|
@@ -141,7 +143,7 @@ async function runCruise(pFileDirectoryArray, pCruiseOptions) {
|
|
|
141
143
|
* @param {{stdout: NodeJS.WritableStream, stderr: NodeJS.WritableStream}=} pStreams
|
|
142
144
|
* @returns {number}
|
|
143
145
|
*/
|
|
144
|
-
|
|
146
|
+
|
|
145
147
|
export default async function executeCli(
|
|
146
148
|
pFileDirectoryArray,
|
|
147
149
|
pCruiseOptions,
|
|
@@ -17,6 +17,7 @@ function getOptionValue(pDefault) {
|
|
|
17
17
|
return (pValue) => (typeof pValue === "string" ? pValue : pDefault);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
// eslint-disable-next-line complexity
|
|
20
21
|
function normalizeConfigFileName(pCliOptions, pConfigWrapperName, pDefault) {
|
|
21
22
|
let lOptions = structuredClone(pCliOptions);
|
|
22
23
|
|
package/src/cli/utl/io.mjs
CHANGED
|
@@ -28,10 +28,9 @@ function writeToFile(pOutputTo, pDependencyString) {
|
|
|
28
28
|
*/
|
|
29
29
|
function writeToStdOut(pString, pBufferSize = PIPE_BUFFER_SIZE) {
|
|
30
30
|
const lNumberOfChunks = Math.ceil(pString.length / pBufferSize);
|
|
31
|
-
let lIndex = 0;
|
|
32
31
|
|
|
33
32
|
/* eslint no-plusplus: 0 */
|
|
34
|
-
for (lIndex = 0; lIndex < lNumberOfChunks; lIndex++) {
|
|
33
|
+
for (let lIndex = 0; lIndex < lNumberOfChunks; lIndex++) {
|
|
35
34
|
const lChunkStart = lIndex * pBufferSize;
|
|
36
35
|
process.stdout.write(
|
|
37
36
|
pString.substring(lChunkStart, lChunkStart + pBufferSize),
|
|
@@ -68,7 +68,6 @@ async function getConfig(pBabelConfigFileName) {
|
|
|
68
68
|
`${`The babel config '${pBabelConfigFileName}' is in a format ('${lExtension}')\n`} dependency-cruiser doesn't support yet.\n`,
|
|
69
69
|
);
|
|
70
70
|
}
|
|
71
|
-
// eslint-disable-next-line no-return-await
|
|
72
71
|
return await lExtensionToParseFunction.get(lExtension)(pBabelConfigFileName);
|
|
73
72
|
}
|
|
74
73
|
|
|
@@ -21,11 +21,11 @@ import makeAbsolute from "./make-absolute.mjs";
|
|
|
21
21
|
*/
|
|
22
22
|
function makeForwardCompatible(pKnownViolation) {
|
|
23
23
|
let lReturnValue = pKnownViolation;
|
|
24
|
-
if (
|
|
24
|
+
if (pKnownViolation.cycle) {
|
|
25
25
|
lReturnValue = {
|
|
26
26
|
...pKnownViolation,
|
|
27
27
|
cycle: pKnownViolation.cycle.map((pModule) => {
|
|
28
|
-
if (
|
|
28
|
+
if (pModule.name) {
|
|
29
29
|
return pModule;
|
|
30
30
|
}
|
|
31
31
|
return {
|
|
@@ -35,11 +35,11 @@ function makeForwardCompatible(pKnownViolation) {
|
|
|
35
35
|
}),
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
-
if (
|
|
38
|
+
if (pKnownViolation.via) {
|
|
39
39
|
lReturnValue = {
|
|
40
40
|
...pKnownViolation,
|
|
41
41
|
via: pKnownViolation.via.map((pModule) => {
|
|
42
|
-
if (
|
|
42
|
+
if (pModule.name) {
|
|
43
43
|
return pModule;
|
|
44
44
|
}
|
|
45
45
|
return {
|
|
@@ -90,7 +90,7 @@ async function attemptImport(pAbsoluteWebpackConfigFileName) {
|
|
|
90
90
|
* module system. If we'd use a dynamic import, these monkey-patches wouldn't
|
|
91
91
|
* be used.
|
|
92
92
|
*/
|
|
93
|
-
/* eslint
|
|
93
|
+
/* eslint n/global-require:0, security/detect-non-literal-require:0, import/no-dynamic-require:0 */
|
|
94
94
|
return require(pAbsoluteWebpackConfigFileName);
|
|
95
95
|
}
|
|
96
96
|
} catch (pError) {
|
|
@@ -116,7 +116,7 @@ export default function softenKnownViolations(
|
|
|
116
116
|
pKnownViolations,
|
|
117
117
|
pSoftenedSeverity = "ignore",
|
|
118
118
|
) {
|
|
119
|
-
if (
|
|
119
|
+
if (pKnownViolations) {
|
|
120
120
|
bus.info("analyzing: comparing against known errors");
|
|
121
121
|
return pModules.map((pModule) =>
|
|
122
122
|
softenKnownViolation(pModule, pKnownViolations, pSoftenedSeverity),
|
|
@@ -78,10 +78,10 @@ function extractDependencies(pCruiseOptions, pFileName, pTranspileOptions) {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
function matchesDoNotFollow({ resolved, dependencyTypes }, pDoNotFollow) {
|
|
81
|
-
const lMatchesPath =
|
|
81
|
+
const lMatchesPath = pDoNotFollow.path
|
|
82
82
|
? RegExp(pDoNotFollow.path, "g").test(resolved)
|
|
83
83
|
: false;
|
|
84
|
-
const lMatchesDependencyTypes =
|
|
84
|
+
const lMatchesDependencyTypes = pDoNotFollow.dependencyTypes
|
|
85
85
|
? intersects(dependencyTypes, pDoNotFollow.dependencyTypes)
|
|
86
86
|
: false;
|
|
87
87
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readdirSync, statSync } from "node:fs";
|
|
2
2
|
import { join, normalize } from "node:path";
|
|
3
|
+
// eslint-disable-next-line n/prefer-global/process
|
|
3
4
|
import { platform } from "node:process";
|
|
4
5
|
import picomatch from "picomatch";
|
|
5
6
|
import { scannableExtensions } from "./transpile/meta.mjs";
|
|
@@ -59,7 +59,7 @@ function determineManifestDependencyTypes(
|
|
|
59
59
|
/** @type {DependencyType[]} */
|
|
60
60
|
let lReturnValue = ["npm-unknown"];
|
|
61
61
|
|
|
62
|
-
if (
|
|
62
|
+
if (pPackageDependencies) {
|
|
63
63
|
lReturnValue = findModuleInPackageDependencies(
|
|
64
64
|
pPackageDependencies,
|
|
65
65
|
pModuleName,
|
|
@@ -89,7 +89,7 @@ function determineManifestDependencyTypes(
|
|
|
89
89
|
function dependencyIsBundled(pModule, pPackageDeps) {
|
|
90
90
|
let lReturnValue = false;
|
|
91
91
|
|
|
92
|
-
if (
|
|
92
|
+
if (pPackageDeps) {
|
|
93
93
|
const lBundledDependencies =
|
|
94
94
|
pPackageDeps.bundledDependencies || pPackageDeps.bundleDependencies;
|
|
95
95
|
|
|
@@ -32,7 +32,7 @@ import { isScoped, isRelativeModuleName } from "./module-classifiers.mjs";
|
|
|
32
32
|
* @return {string} the module name root
|
|
33
33
|
*/
|
|
34
34
|
export function getPackageRoot(pModule) {
|
|
35
|
-
if (!
|
|
35
|
+
if (!pModule || isRelativeModuleName(pModule)) {
|
|
36
36
|
return pModule;
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -78,7 +78,7 @@ function bareGetPackageJson(pModuleName, pFileDirectory, pResolveOptions) {
|
|
|
78
78
|
try {
|
|
79
79
|
const lPackageJsonFilename = resolve(
|
|
80
80
|
join(getPackageRoot(pModuleName), "package.json"),
|
|
81
|
-
pFileDirectory
|
|
81
|
+
pFileDirectory ?? process.cwd(),
|
|
82
82
|
{
|
|
83
83
|
...pResolveOptions,
|
|
84
84
|
// if a module has exports fields _and_ does not expose package.json
|
|
@@ -129,7 +129,7 @@ export function dependencyIsDeprecated(
|
|
|
129
129
|
pResolveOptions,
|
|
130
130
|
);
|
|
131
131
|
|
|
132
|
-
if (
|
|
132
|
+
if (lPackageJson) {
|
|
133
133
|
lReturnValue =
|
|
134
134
|
Object.hasOwn(lPackageJson, "deprecated") && lPackageJson.deprecated;
|
|
135
135
|
}
|
|
@@ -154,7 +154,7 @@ export function getLicense(pModuleName, pFileDirectory, pResolveOptions) {
|
|
|
154
154
|
);
|
|
155
155
|
|
|
156
156
|
if (
|
|
157
|
-
|
|
157
|
+
lPackageJson &&
|
|
158
158
|
Object.hasOwn(lPackageJson, "license") &&
|
|
159
159
|
typeof lPackageJson.license === "string"
|
|
160
160
|
) {
|
|
@@ -150,7 +150,7 @@ function isWorkspaceAliased(pModuleName, pResolvedModuleName, pManifest) {
|
|
|
150
150
|
// an object. To prevent the code from borking we check whether it's an array
|
|
151
151
|
// see https://github.com/sverweij/dependency-cruiser/issues/919
|
|
152
152
|
const lWorkspaces = getWorkspacesArray(pManifest?.workspaces);
|
|
153
|
-
if (lWorkspaces.length
|
|
153
|
+
if (lWorkspaces.length > 0) {
|
|
154
154
|
// workspaces are an array of globs that match the (sub) workspace
|
|
155
155
|
// folder itself only.
|
|
156
156
|
//
|
|
@@ -193,7 +193,7 @@ function isWorkspaceAliased(pModuleName, pResolvedModuleName, pManifest) {
|
|
|
193
193
|
// of the workspace, not the path of the workspace itself. So if it's
|
|
194
194
|
// in node_modules we need to check against the unresolved modulename.
|
|
195
195
|
//
|
|
196
|
-
// Other
|
|
196
|
+
// Other than the detection for when symlinks are resolved to their realpath
|
|
197
197
|
// (the if above), this is a 'best effort' detection only for now; there's
|
|
198
198
|
// guaranteed to be scenarios where this will fail. How often is the
|
|
199
199
|
// --preserve-symlinks flag used in practice, though?
|
|
@@ -5,7 +5,7 @@ export function addLicenseAttribute(
|
|
|
5
5
|
pModuleName,
|
|
6
6
|
pResolvedModuleName,
|
|
7
7
|
{ baseDirectory, fileDirectory },
|
|
8
|
-
pResolveOptions
|
|
8
|
+
pResolveOptions,
|
|
9
9
|
) {
|
|
10
10
|
let lReturnValue = {};
|
|
11
11
|
if (
|
|
@@ -13,12 +13,12 @@ export function addLicenseAttribute(
|
|
|
13
13
|
isExternalModule(
|
|
14
14
|
pResolvedModuleName,
|
|
15
15
|
pResolveOptions.modules,
|
|
16
|
-
baseDirectory
|
|
16
|
+
baseDirectory,
|
|
17
17
|
)
|
|
18
18
|
) {
|
|
19
19
|
const lLicense = getLicense(pModuleName, fileDirectory, pResolveOptions);
|
|
20
20
|
|
|
21
|
-
if (
|
|
21
|
+
if (lLicense) {
|
|
22
22
|
lReturnValue.license = lLicense;
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -41,7 +41,7 @@ function argumentsAreUsable(pArguments) {
|
|
|
41
41
|
["StringLiteral", "TemplateLiteral"].includes(
|
|
42
42
|
pArguments[0].expression.type,
|
|
43
43
|
) &&
|
|
44
|
-
(
|
|
44
|
+
(pArguments[0].expression.type !== "TemplateLiteral" ||
|
|
45
45
|
isPlaceholderlessTemplateLiteral(pArguments[0]))
|
|
46
46
|
);
|
|
47
47
|
}
|
|
@@ -250,11 +250,7 @@ export default Visitor
|
|
|
250
250
|
// as visitors for some shapes of type annotations aren't completely
|
|
251
251
|
// implemented yet (1.2.51) pNode can come in as null (also see
|
|
252
252
|
// comments in accompanying unit test)
|
|
253
|
-
if (
|
|
254
|
-
Boolean(pNode) &&
|
|
255
|
-
Boolean(pNode.typeAnnotation) &&
|
|
256
|
-
Boolean(pNode.typeAnnotation.argument)
|
|
257
|
-
)
|
|
253
|
+
if (pNode && pNode.typeAnnotation && pNode.typeAnnotation.argument)
|
|
258
254
|
this.lResult.push({
|
|
259
255
|
module: pNode.typeAnnotation.argument.value,
|
|
260
256
|
moduleSystem: "es6",
|
|
@@ -60,7 +60,7 @@ function extractImports(pAST) {
|
|
|
60
60
|
.filter(
|
|
61
61
|
(pStatement) =>
|
|
62
62
|
typescript.SyntaxKind[pStatement.kind] === "ImportDeclaration" &&
|
|
63
|
-
|
|
63
|
+
pStatement.moduleSpecifier,
|
|
64
64
|
)
|
|
65
65
|
.map((pStatement) => ({
|
|
66
66
|
module: pStatement.moduleSpecifier.text,
|
|
@@ -84,7 +84,7 @@ function extractExports(pAST) {
|
|
|
84
84
|
.filter(
|
|
85
85
|
(pStatement) =>
|
|
86
86
|
typescript.SyntaxKind[pStatement.kind] === "ExportDeclaration" &&
|
|
87
|
-
|
|
87
|
+
pStatement.moduleSpecifier,
|
|
88
88
|
)
|
|
89
89
|
.map((pStatement) => ({
|
|
90
90
|
module: pStatement.moduleSpecifier.text,
|
|
@@ -265,7 +265,7 @@ function extractJSDocImportTags(pJSDocTags) {
|
|
|
265
265
|
pTag.tagName.escapedText === "import" &&
|
|
266
266
|
pTag.moduleSpecifier?.kind &&
|
|
267
267
|
typescript.SyntaxKind[pTag.moduleSpecifier.kind] === "StringLiteral" &&
|
|
268
|
-
|
|
268
|
+
pTag.moduleSpecifier.text,
|
|
269
269
|
)
|
|
270
270
|
.map((pTag) => ({
|
|
271
271
|
module: pTag.moduleSpecifier.text,
|
|
@@ -275,10 +275,43 @@ function extractJSDocImportTags(pJSDocTags) {
|
|
|
275
275
|
}));
|
|
276
276
|
}
|
|
277
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
|
+
|
|
278
304
|
function extractJSDocImports(pJSDocNodes) {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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);
|
|
282
315
|
}
|
|
283
316
|
|
|
284
317
|
/**
|
|
@@ -346,7 +379,7 @@ function walk(pResult, pExoticRequireStrings, pDetectJSDocImports) {
|
|
|
346
379
|
// TODO: all the kinds of tags that can have import statements as type declarations
|
|
347
380
|
// (e.g. @type, @param, @returns, @typedef, @property, @prop, @arg, ...)
|
|
348
381
|
// https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html
|
|
349
|
-
if (pDetectJSDocImports &&
|
|
382
|
+
if (pDetectJSDocImports && pASTNode.jsDoc) {
|
|
350
383
|
const lJSDocImports = extractJSDocImports(pASTNode.jsDoc);
|
|
351
384
|
|
|
352
385
|
// pResult = pResult.concat(lJSDocImports) looks like the more obvious
|
|
@@ -394,7 +427,7 @@ export default function extractTypeScriptDependencies(
|
|
|
394
427
|
pDetectJSDocImports,
|
|
395
428
|
) {
|
|
396
429
|
// console.dir(pTypeScriptAST, { depth: 100 });
|
|
397
|
-
return
|
|
430
|
+
return typescript
|
|
398
431
|
? extractImports(pTypeScriptAST)
|
|
399
432
|
.concat(extractExports(pTypeScriptAST))
|
|
400
433
|
.concat(extractImportEquals(pTypeScriptAST))
|
package/src/main/cruise.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* eslint-disable no-
|
|
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 (
|
|
68
|
+
if (lCruiseOptions.ruleSet) {
|
|
69
69
|
bus.summary("parsing rule set", c(4));
|
|
70
70
|
lCruiseOptions.ruleSet = normalizeRuleSet(
|
|
71
71
|
assertRuleSetValid(lCruiseOptions.ruleSet),
|
package/src/main/format.mjs
CHANGED
|
@@ -34,10 +34,10 @@ function deepMerge(pTarget, pSource) {
|
|
|
34
34
|
|
|
35
35
|
function assertModuleSystemsValid(pModuleSystems) {
|
|
36
36
|
if (
|
|
37
|
-
|
|
37
|
+
pModuleSystems &&
|
|
38
38
|
Array.isArray(pModuleSystems) &&
|
|
39
39
|
!pModuleSystems.every((pModuleSystem) =>
|
|
40
|
-
|
|
40
|
+
pModuleSystem.match(MODULE_SYSTEM_LIST_RE),
|
|
41
41
|
)
|
|
42
42
|
) {
|
|
43
43
|
throw new Error(
|
|
@@ -47,7 +47,7 @@ function assertModuleSystemsValid(pModuleSystems) {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
function assertRegExpSafety(pPattern) {
|
|
50
|
-
if (
|
|
50
|
+
if (pPattern && !safeRegex(pPattern)) {
|
|
51
51
|
throw new Error(
|
|
52
52
|
`The pattern '${pPattern}' will probably run very slowly - cowardly refusing to run.\n`,
|
|
53
53
|
);
|
|
@@ -56,7 +56,7 @@ function assertRegExpSafety(pPattern) {
|
|
|
56
56
|
|
|
57
57
|
function assertOutputTypeValid(pOutputType) {
|
|
58
58
|
if (
|
|
59
|
-
|
|
59
|
+
pOutputType &&
|
|
60
60
|
!getAvailableReporters().includes(pOutputType) &&
|
|
61
61
|
!pOutputType.startsWith("plugin:")
|
|
62
62
|
) {
|
|
@@ -65,7 +65,7 @@ function assertOutputTypeValid(pOutputType) {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
function assertMaxDepthValid(pDepth) {
|
|
68
|
-
if (
|
|
68
|
+
if (pDepth && !pDepth.toString().match(VALID_DEPTH_RE)) {
|
|
69
69
|
throw new Error(
|
|
70
70
|
`'${pDepth}' is not a valid depth - use an integer between 0 and 99`,
|
|
71
71
|
);
|
|
@@ -77,7 +77,7 @@ function assertFocusDepthValid(pFocusDepth) {
|
|
|
77
77
|
const lMaxFocusDepth = 99;
|
|
78
78
|
|
|
79
79
|
if (
|
|
80
|
-
|
|
80
|
+
pFocusDepth &&
|
|
81
81
|
(Number.isNaN(lFocusDepth) ||
|
|
82
82
|
lFocusDepth < 0 ||
|
|
83
83
|
lFocusDepth > lMaxFocusDepth)
|
|
@@ -105,7 +105,7 @@ function assertPathsSafety(pFilterOption) {
|
|
|
105
105
|
export function assertCruiseOptionsValid(pOptions) {
|
|
106
106
|
let lReturnValue = {};
|
|
107
107
|
|
|
108
|
-
if (
|
|
108
|
+
if (pOptions) {
|
|
109
109
|
// necessary because can slip through the cracks when passed as a cli parameter
|
|
110
110
|
assertModuleSystemsValid(pOptions.moduleSystems);
|
|
111
111
|
|
|
@@ -146,7 +146,7 @@ export default async function normalizeResolveOptions(
|
|
|
146
146
|
pTSConfig,
|
|
147
147
|
) {
|
|
148
148
|
const lRuleSet = pOptions?.ruleSet ?? {};
|
|
149
|
-
|
|
149
|
+
|
|
150
150
|
return await compileResolveOptions(
|
|
151
151
|
{
|
|
152
152
|
// EnhancedResolve's symlinks:
|
|
@@ -16,7 +16,7 @@ const DEFAULT_RULE = "unnamed";
|
|
|
16
16
|
const DEFAULT_SCOPE = "module";
|
|
17
17
|
|
|
18
18
|
function normalizeSeverity(pSeverity) {
|
|
19
|
-
const lSeverity = pSeverity
|
|
19
|
+
const lSeverity = pSeverity ?? DEFAULT_SEVERITY;
|
|
20
20
|
|
|
21
21
|
return VALID_SEVERITIES.test(lSeverity) ? lSeverity : DEFAULT_SEVERITY;
|
|
22
22
|
}
|
|
@@ -26,7 +26,7 @@ function normalizeSeverity(pSeverity) {
|
|
|
26
26
|
* @returns {string}
|
|
27
27
|
*/
|
|
28
28
|
function normalizeName(pRuleName) {
|
|
29
|
-
return pRuleName
|
|
29
|
+
return pRuleName ?? DEFAULT_RULE;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/**
|
|
@@ -34,7 +34,7 @@ function normalizeName(pRuleName) {
|
|
|
34
34
|
* @returns {RuleScopeType}
|
|
35
35
|
*/
|
|
36
36
|
function normalizeScope(pScope) {
|
|
37
|
-
return pScope
|
|
37
|
+
return pScope ?? DEFAULT_SCOPE;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
package/src/meta.cjs
CHANGED
|
@@ -2,7 +2,7 @@ import { anonymizePathElement } from "./anonymize-path-element.mjs";
|
|
|
2
2
|
|
|
3
3
|
export const WHITELIST_RE =
|
|
4
4
|
// eslint-disable-next-line security/detect-unsafe-regex
|
|
5
|
-
/^(
|
|
5
|
+
/^(?:[.]+|~|bin|apps?|cli|src|libs?|configs?|components?|fixtures?|helpers?|i18n|index\.(?:jsx?|[mc]js|d\.ts|tsx?|vue|coffee|ls)|_?_?mocks?_?_?|node_modules|packages?|package\.json|scripts?|services?|sources?|specs?|_?_?tests?_?_?|types?|uti?ls?|tools)$/;
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Kind of smartly anonymizes paths, by
|
package/src/report/d2.mjs
CHANGED
|
@@ -51,7 +51,7 @@ function getVertexName(pSource) {
|
|
|
51
51
|
* @param {import("../../types/cruise-result").IOptions} pOptions
|
|
52
52
|
* @returns {string}
|
|
53
53
|
*/
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
function getModuleAttributes(pModule, pOptions) {
|
|
56
56
|
let lReturnValue = "class: module";
|
|
57
57
|
if (pModule.consolidated) {
|
|
@@ -90,7 +90,7 @@ function getModuleAttributes(pModule, pOptions) {
|
|
|
90
90
|
* @param {import("../../types/cruise-result").IDependency} pDependency
|
|
91
91
|
* @returns {string}
|
|
92
92
|
*/
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
function getDependencyAttributes(pDependency) {
|
|
95
95
|
let lThing = "";
|
|
96
96
|
if (pDependency.valid === false) {
|
package/src/report/dot/index.mjs
CHANGED
|
@@ -25,15 +25,15 @@ const GRANULARITY2REPORTER_OPTIONS = new Map([
|
|
|
25
25
|
]);
|
|
26
26
|
|
|
27
27
|
function buildGraphAttributes(pGraph) {
|
|
28
|
-
return
|
|
28
|
+
return pGraph ? ` ${attributizeObject(pGraph || {})}` : "";
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
function buildNodeAttributes(pNode) {
|
|
32
|
-
return
|
|
32
|
+
return pNode ? ` node [${attributizeObject(pNode || {})}]` : "";
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
function buildEdgeAttributes(pEdge) {
|
|
36
|
-
return
|
|
36
|
+
return pEdge ? ` edge [${attributizeObject(pEdge || {})}]` : "";
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
function buildGeneralAttributes(pTheme) {
|
|
@@ -18,7 +18,7 @@ function getHoverHandler(pTitle2ElementMap) {
|
|
|
18
18
|
var closestTitleText = getTitleText(closestNodeOrEdge);
|
|
19
19
|
|
|
20
20
|
if (
|
|
21
|
-
|
|
21
|
+
currentHighlightedTitle !== closestTitleText &&
|
|
22
22
|
gMode.get() === gMode.HOVER
|
|
23
23
|
) {
|
|
24
24
|
resetNodesAndEdges();
|
|
@@ -40,12 +40,12 @@ function getSelectHandler(pTitle2ElementMap) {
|
|
|
40
40
|
var closestNodeOrEdge = pMouseEvent.target.closest(".edge, .node");
|
|
41
41
|
var closestTitleText = getTitleText(closestNodeOrEdge);
|
|
42
42
|
|
|
43
|
-
if (
|
|
43
|
+
if (closestNodeOrEdge) {
|
|
44
44
|
gMode.setToSelect();
|
|
45
45
|
} else {
|
|
46
46
|
gMode.setToHover();
|
|
47
47
|
}
|
|
48
|
-
if (
|
|
48
|
+
if (currentHighlightedTitle !== closestTitleText) {
|
|
49
49
|
resetNodesAndEdges();
|
|
50
50
|
addHighlight(closestNodeOrEdge);
|
|
51
51
|
pTitle2ElementMap.get(closestTitleText).forEach(addHighlight);
|
|
@@ -216,7 +216,7 @@ function addHighlight(pGroup) {
|
|
|
216
216
|
var gHints = {
|
|
217
217
|
HIDDEN: 1,
|
|
218
218
|
SHOWN: 2,
|
|
219
|
-
state:
|
|
219
|
+
state: 1, // === HIDDEN
|
|
220
220
|
show: function () {
|
|
221
221
|
document.getElementById("hints").removeAttribute("style");
|
|
222
222
|
gHints.state = gHints.SHOWN;
|
package/src/report/markdown.mjs
CHANGED
|
@@ -105,13 +105,50 @@ function formatViolations(pViolations, pIncludeIgnoredInDetails) {
|
|
|
105
105
|
}, lTableHead);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
function details(pResults, pOptions) {
|
|
109
|
+
let lReturnValue = "";
|
|
110
|
+
if (pResults.summary.violations.length > 0) {
|
|
111
|
+
if (pOptions.showDetailsHeader) {
|
|
112
|
+
lReturnValue += `${pOptions.detailsHeader}\n\n`;
|
|
113
|
+
}
|
|
114
|
+
if (pOptions.collapseDetails) {
|
|
115
|
+
lReturnValue += `<details><summary>${pOptions.collapsedMessage}</summary>\n\n`;
|
|
116
|
+
}
|
|
117
|
+
lReturnValue += `${formatViolations(
|
|
118
|
+
pResults.summary.violations,
|
|
119
|
+
pOptions.includeIgnoredInDetails,
|
|
120
|
+
)}\n\n`;
|
|
121
|
+
if (pOptions.collapseDetails) {
|
|
122
|
+
lReturnValue += "</details>\n\n";
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
lReturnValue += `${pOptions.noViolationsMessage}\n\n`;
|
|
126
|
+
}
|
|
127
|
+
return lReturnValue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function summary(pResults, pOptions) {
|
|
131
|
+
let lReturnValue = "";
|
|
132
|
+
|
|
133
|
+
if (pOptions.showSummaryHeader) {
|
|
134
|
+
lReturnValue += `${pOptions.summaryHeader}\n\n`;
|
|
135
|
+
}
|
|
136
|
+
lReturnValue += `${formatStatsSummary(pResults.summary)}\n\n`;
|
|
137
|
+
|
|
138
|
+
if (pResults.summary.violations.length > 0 && pOptions.showRulesSummary) {
|
|
139
|
+
lReturnValue += `${formatRulesSummary(
|
|
140
|
+
pResults,
|
|
141
|
+
pOptions.includeIgnoredInSummary,
|
|
142
|
+
)}\n\n`;
|
|
143
|
+
}
|
|
144
|
+
return lReturnValue;
|
|
145
|
+
}
|
|
146
|
+
|
|
108
147
|
/**
|
|
109
|
-
*
|
|
110
148
|
* @param {import("../../types/cruise-result.mjs").ICruiseResult} pResults
|
|
111
149
|
* @param {import("../../types/reporter-options.js").IMarkdownReporterOptions} pOptions
|
|
112
150
|
* @returns {string}
|
|
113
151
|
*/
|
|
114
|
-
// eslint-disable-next-line complexity, max-statements
|
|
115
152
|
function report(pResults, pOptions) {
|
|
116
153
|
const lOptions = { ...REPORT_DEFAULTS, ...(pOptions || {}) };
|
|
117
154
|
let lReturnValue = "";
|
|
@@ -121,37 +158,11 @@ function report(pResults, pOptions) {
|
|
|
121
158
|
}
|
|
122
159
|
|
|
123
160
|
if (lOptions.showSummary) {
|
|
124
|
-
|
|
125
|
-
lReturnValue += `${lOptions.summaryHeader}\n\n`;
|
|
126
|
-
}
|
|
127
|
-
lReturnValue += `${formatStatsSummary(pResults.summary)}\n\n`;
|
|
128
|
-
|
|
129
|
-
if (pResults.summary.violations.length > 0 && lOptions.showRulesSummary) {
|
|
130
|
-
lReturnValue += `${formatRulesSummary(
|
|
131
|
-
pResults,
|
|
132
|
-
lOptions.includeIgnoredInSummary,
|
|
133
|
-
)}\n\n`;
|
|
134
|
-
}
|
|
161
|
+
lReturnValue += summary(pResults, lOptions);
|
|
135
162
|
}
|
|
136
163
|
|
|
137
164
|
if (lOptions.showDetails) {
|
|
138
|
-
|
|
139
|
-
if (lOptions.showDetailsHeader) {
|
|
140
|
-
lReturnValue += `${lOptions.detailsHeader}\n\n`;
|
|
141
|
-
}
|
|
142
|
-
if (lOptions.collapseDetails) {
|
|
143
|
-
lReturnValue += `<details><summary>${lOptions.collapsedMessage}</summary>\n\n`;
|
|
144
|
-
}
|
|
145
|
-
lReturnValue += `${formatViolations(
|
|
146
|
-
pResults.summary.violations,
|
|
147
|
-
lOptions.includeIgnoredInDetails,
|
|
148
|
-
)}\n\n`;
|
|
149
|
-
if (lOptions.collapseDetails) {
|
|
150
|
-
lReturnValue += "</details>\n\n";
|
|
151
|
-
}
|
|
152
|
-
} else {
|
|
153
|
-
lReturnValue += `${lOptions.noViolationsMessage}\n\n`;
|
|
154
|
-
}
|
|
165
|
+
lReturnValue += details(pResults, lOptions);
|
|
155
166
|
}
|
|
156
167
|
|
|
157
168
|
if (lOptions.showFooter) {
|
package/src/report/mermaid.mjs
CHANGED
|
@@ -37,7 +37,7 @@ function convertEdgeSources(pCruiseResult, pNamesHashMap) {
|
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
const indent = (pDepth
|
|
40
|
+
const indent = (pDepth, pMinify) => (pMinify ? "" : " ".repeat(pDepth));
|
|
41
41
|
|
|
42
42
|
const renderSubgraph = (pNode, pText, pChildren, pIndent) =>
|
|
43
43
|
`${pIndent}subgraph ${renderNode(pNode, pText)}
|
|
@@ -96,7 +96,7 @@ function focusHighlights(pModules, pNamesHashMap) {
|
|
|
96
96
|
(pModule) =>
|
|
97
97
|
pModule.matchesFocus ||
|
|
98
98
|
pModule.matchesReaches ||
|
|
99
|
-
pModule.matchesHighlight
|
|
99
|
+
pModule.matchesHighlight,
|
|
100
100
|
)
|
|
101
101
|
.reduce((pAll, pModule) => {
|
|
102
102
|
const lSource = pNamesHashMap.get(pModule.source);
|
package/src/report/teamcity.mjs
CHANGED
|
@@ -144,6 +144,7 @@ function reportViolations(pViolations, pIgnoredCount) {
|
|
|
144
144
|
* @param {import("../../types/dependency-cruiser.js").ICruiseResult} pResults
|
|
145
145
|
* @returns {import("../../types/dependency-cruiser.js").IReporterOutput}
|
|
146
146
|
*/
|
|
147
|
+
// eslint-disable-next-line complexity
|
|
147
148
|
export default function teamcity(pResults) {
|
|
148
149
|
// this is the documented way to get tsm to emit strings
|
|
149
150
|
// Alternatively we could've used the 'low level API', which
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import { extname } from "node:path";
|
|
3
3
|
|
|
4
|
-
const EXTENSION_RE = /(?<extension>(?:(?:\.d\.(?:
|
|
4
|
+
const EXTENSION_RE = /(?<extension>(?:(?:\.d\.(?:[cm])?ts)|\.coffee\.md)$)/;
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Returns the extension of the given file name path.
|
package/src/utl/try-import.mjs
CHANGED
|
@@ -45,7 +45,6 @@ function getVersion(pModuleName) {
|
|
|
45
45
|
* @returns {Promise<NodeModule | false>} - The imported module or false if the import fails or the version does not satisfy the provided semantic version.
|
|
46
46
|
*/
|
|
47
47
|
|
|
48
|
-
// eslint-disable-next-line complexity
|
|
49
48
|
export default async function tryImport(pModuleName, pSemanticVersion) {
|
|
50
49
|
try {
|
|
51
50
|
if (pSemanticVersion) {
|
package/src/utl/try-require.cjs
CHANGED
|
@@ -12,7 +12,7 @@ function getVersion(pModuleName) {
|
|
|
12
12
|
// @ts-expect-error TS2345 extractRootModuleName can return either a string or
|
|
13
13
|
// undefined. If undefined this function will throw. Which is _fine_, even
|
|
14
14
|
// _expected_ in the context it's currently used
|
|
15
|
-
// eslint-disable-next-line import/no-dynamic-require,
|
|
15
|
+
// eslint-disable-next-line import/no-dynamic-require, n/global-require, security/detect-non-literal-require
|
|
16
16
|
return require(join(extractRootModuleName(pModuleName), "package.json"))
|
|
17
17
|
.version;
|
|
18
18
|
}
|
|
@@ -41,7 +41,7 @@ function tryRequire(pModuleName, pSemanticVersion) {
|
|
|
41
41
|
return false;
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
// eslint-disable-next-line import/no-dynamic-require,
|
|
44
|
+
// eslint-disable-next-line import/no-dynamic-require, n/global-require, security/detect-non-literal-require
|
|
45
45
|
return require(pModuleName);
|
|
46
46
|
} catch (pError) {
|
|
47
47
|
return false;
|
|
@@ -115,7 +115,7 @@ function dependentsCountsMatch(pRule, pDependents) {
|
|
|
115
115
|
* @param {IModule} pModule
|
|
116
116
|
* @returns {boolean}
|
|
117
117
|
*/
|
|
118
|
-
|
|
118
|
+
|
|
119
119
|
export function matchesDependentsRule(pRule, pModule) {
|
|
120
120
|
if (
|
|
121
121
|
(Object.hasOwn(pModule, "dependents") &&
|
|
@@ -85,7 +85,7 @@ export function matchToModulePath(pRule, pModule, pGroups) {
|
|
|
85
85
|
|
|
86
86
|
function _toPathNot(pRule, pString, pGroups = []) {
|
|
87
87
|
return (
|
|
88
|
-
!
|
|
88
|
+
!pRule.to.pathNot ||
|
|
89
89
|
!pString.match(replaceGroupPlaceholders(pRule.to.pathNot, pGroups))
|
|
90
90
|
);
|
|
91
91
|
}
|
|
@@ -112,7 +112,6 @@ export function matchesToDependencyTypesNot(pRule, pDependency) {
|
|
|
112
112
|
);
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
// eslint-disable-next-line complexity
|
|
116
115
|
export function matchesToVia(pRule, pDependency, pGroups) {
|
|
117
116
|
let lReturnValue = true;
|
|
118
117
|
if (pRule.to.via && pDependency.cycle) {
|
|
@@ -144,7 +143,6 @@ export function matchesToVia(pRule, pDependency, pGroups) {
|
|
|
144
143
|
return lReturnValue;
|
|
145
144
|
}
|
|
146
145
|
|
|
147
|
-
// eslint-disable-next-line complexity
|
|
148
146
|
export function matchesToViaOnly(pRule, pDependency, pGroups) {
|
|
149
147
|
let lReturnValue = true;
|
|
150
148
|
if (pRule.to.viaOnly && pDependency.cycle) {
|