dependency-cruiser 16.10.4 → 17.0.1-beta-1
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/bin/depcruise-fmt.mjs +1 -1
- package/package.json +5 -7
- package/src/cli/format-meta-info.mjs +8 -6
- package/src/cli/index.mjs +10 -7
- package/src/cli/init-config/write-config.mjs +2 -2
- package/src/cli/init-config/write-run-scripts-to-manifest.mjs +3 -3
- package/src/cli/listeners/cli-feedback.mjs +3 -2
- package/src/cli/listeners/performance-log/format-helpers.mjs +15 -16
- package/src/main/resolve-options/normalize.mjs +3 -1
- package/src/meta.cjs +2 -2
- package/src/report/error.mjs +19 -18
- package/src/report/metrics.mjs +2 -2
- package/src/report/teamcity.mjs +105 -15
- package/src/report/text.mjs +4 -2
- package/types/shared-types.d.mts +1 -5
package/bin/depcruise-fmt.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dependency-cruiser",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "17.0.1-beta-1",
|
|
4
4
|
"description": "Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"static analysis",
|
|
@@ -146,20 +146,18 @@
|
|
|
146
146
|
"acorn-loose": "^8.5.2",
|
|
147
147
|
"acorn-walk": "^8.3.4",
|
|
148
148
|
"ajv": "^8.17.1",
|
|
149
|
-
"commander": "^
|
|
150
|
-
"enhanced-resolve": "^5.18.
|
|
149
|
+
"commander": "^14.0.1",
|
|
150
|
+
"enhanced-resolve": "^5.18.3",
|
|
151
151
|
"ignore": "^7.0.5",
|
|
152
152
|
"interpret": "^3.1.1",
|
|
153
153
|
"is-installed-globally": "^1.0.0",
|
|
154
154
|
"json5": "^2.2.3",
|
|
155
155
|
"memoize": "^10.1.0",
|
|
156
|
-
"
|
|
157
|
-
"picomatch": "^4.0.2",
|
|
156
|
+
"picomatch": "^4.0.3",
|
|
158
157
|
"prompts": "^2.4.2",
|
|
159
158
|
"rechoir": "^0.8.0",
|
|
160
159
|
"safe-regex": "^2.1.1",
|
|
161
160
|
"semver": "^7.7.2",
|
|
162
|
-
"teamcity-service-messages": "^0.1.14",
|
|
163
161
|
"tsconfig-paths-webpack-plugin": "^4.2.0",
|
|
164
162
|
"watskeburt": "^4.2.3"
|
|
165
163
|
},
|
|
@@ -172,7 +170,7 @@
|
|
|
172
170
|
"nanoid": "^3.3.8"
|
|
173
171
|
},
|
|
174
172
|
"engines": {
|
|
175
|
-
"node": "^
|
|
173
|
+
"node": "^20.12||^22||>=24"
|
|
176
174
|
},
|
|
177
175
|
"scripts": {
|
|
178
176
|
"test": "echo see github for test, build and analysis scripts"
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { release, platform, arch } from "node:os";
|
|
2
|
-
import
|
|
2
|
+
import { styleText } from "node:util";
|
|
3
3
|
|
|
4
4
|
import { getAvailableTranspilers, allExtensions } from "#main/index.mjs";
|
|
5
5
|
import meta from "#meta.cjs";
|
|
6
6
|
|
|
7
7
|
function bool2Symbol(pBool) {
|
|
8
|
-
return pBool ?
|
|
8
|
+
return pBool ? styleText("green", "✔") : styleText("red", "x");
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const MAX_VERSION_RANGE_STRING_LENGTH = 19;
|
|
@@ -13,7 +13,8 @@ const MAX_TRANSPILER_NAME_LENGTH = 22;
|
|
|
13
13
|
const MAX_VERSION_STRING_LENGTH = 24;
|
|
14
14
|
|
|
15
15
|
function formatTranspilers() {
|
|
16
|
-
let lTranspilerTableHeader =
|
|
16
|
+
let lTranspilerTableHeader = styleText(
|
|
17
|
+
"bold",
|
|
17
18
|
` ✔ ${"transpiler".padEnd(MAX_TRANSPILER_NAME_LENGTH)} ${"versions supported".padEnd(MAX_VERSION_RANGE_STRING_LENGTH)} version found`,
|
|
18
19
|
);
|
|
19
20
|
let lTranspilerTableDivider = ` - ${"-".repeat(MAX_TRANSPILER_NAME_LENGTH)} ${"-".repeat(MAX_VERSION_RANGE_STRING_LENGTH)} ${"-".repeat(MAX_VERSION_STRING_LENGTH)}`;
|
|
@@ -36,20 +37,21 @@ function formatExtensions(pExtensions) {
|
|
|
36
37
|
|
|
37
38
|
export default function formatMetaInfo() {
|
|
38
39
|
return `
|
|
39
|
-
${
|
|
40
|
+
${styleText("bold", "dependency-cruiser")}@${meta.version}
|
|
40
41
|
|
|
41
42
|
node version supported : ${meta.engines.node}
|
|
42
43
|
node version found : ${process.version}
|
|
43
44
|
os version found : ${arch()} ${platform()}@${release()}
|
|
44
45
|
|
|
45
|
-
If you need a supported, but not enabled transpiler ('${
|
|
46
|
+
If you need a supported, but not enabled transpiler ('${styleText(
|
|
47
|
+
"red",
|
|
46
48
|
"x",
|
|
47
49
|
)}' below), just install
|
|
48
50
|
it in the same folder dependency-cruiser is installed. E.g. 'npm i livescript'
|
|
49
51
|
will enable livescript support if it's installed in your project folder.
|
|
50
52
|
|
|
51
53
|
${formatTranspilers()}
|
|
52
|
-
${
|
|
54
|
+
${styleText("bold", "✔ extension")}
|
|
53
55
|
- ---------
|
|
54
56
|
${formatExtensions(allExtensions)}
|
|
55
57
|
`;
|
package/src/cli/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
|
+
import { styleText } from "node:util";
|
|
2
3
|
import picomatch from "picomatch";
|
|
3
4
|
import isInstalledGlobally from "is-installed-globally";
|
|
4
|
-
import pc from "picocolors";
|
|
5
5
|
|
|
6
6
|
import assertFileExistence from "./utl/assert-file-existence.mjs";
|
|
7
7
|
import normalizeCliOptions from "./normalize-cli-options.mjs";
|
|
@@ -144,6 +144,7 @@ async function runCruise(pFileDirectoryArray, pCruiseOptions) {
|
|
|
144
144
|
* @returns {number}
|
|
145
145
|
*/
|
|
146
146
|
|
|
147
|
+
// eslint-disable-next-line max-lines-per-function
|
|
147
148
|
export default async function executeCli(
|
|
148
149
|
pFileDirectoryArray,
|
|
149
150
|
pCruiseOptions,
|
|
@@ -161,13 +162,13 @@ export default async function executeCli(
|
|
|
161
162
|
/* c8 ignore start */
|
|
162
163
|
if (isInstalledGlobally) {
|
|
163
164
|
lStreams.stderr.write(
|
|
164
|
-
`\n ${
|
|
165
|
+
`\n ${styleText(
|
|
166
|
+
"yellow",
|
|
165
167
|
"WARNING",
|
|
166
168
|
)}: You're running a globally installed dependency-cruiser.\n\n` +
|
|
167
|
-
` We recommend to ${
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
),
|
|
169
|
+
` We recommend to ${styleText(
|
|
170
|
+
"underline",
|
|
171
|
+
"install and run it as a local devDependency",
|
|
171
172
|
)} in\n` +
|
|
172
173
|
` your project instead. There it has your project's environment and\n` +
|
|
173
174
|
` transpilers at its disposal. That will ensure it can find e.g.\n` +
|
|
@@ -187,7 +188,9 @@ export default async function executeCli(
|
|
|
187
188
|
lExitCode = await runCruise(pFileDirectoryArray, lCruiseOptions);
|
|
188
189
|
}
|
|
189
190
|
} catch (pError) {
|
|
190
|
-
lStreams.stderr.write(
|
|
191
|
+
lStreams.stderr.write(
|
|
192
|
+
`\n ${styleText("red", "ERROR")}: ${pError.message}\n`,
|
|
193
|
+
);
|
|
191
194
|
bus.emit("end");
|
|
192
195
|
lExitCode = 1;
|
|
193
196
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { writeFileSync } from "node:fs";
|
|
2
|
-
import
|
|
2
|
+
import { styleText } from "node:util";
|
|
3
3
|
import {
|
|
4
4
|
fileExists,
|
|
5
5
|
getDefaultConfigFileName,
|
|
@@ -29,7 +29,7 @@ export default function writeConfig(
|
|
|
29
29
|
try {
|
|
30
30
|
writeFileSync(pFileName, pConfig);
|
|
31
31
|
pOutStream.write(
|
|
32
|
-
`\n ${
|
|
32
|
+
`\n ${styleText("green", "✔")} Successfully created '${pFileName}'\n\n`,
|
|
33
33
|
);
|
|
34
34
|
/* c8 ignore start */
|
|
35
35
|
} catch (pError) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/* eslint-disable prefer-template */
|
|
2
2
|
/* eslint-disable security/detect-object-injection */
|
|
3
3
|
import { writeFileSync } from "node:fs";
|
|
4
|
+
import { styleText } from "node:util";
|
|
4
5
|
import { EOL } from "node:os";
|
|
5
|
-
import pc from "picocolors";
|
|
6
6
|
import { PACKAGE_MANIFEST as _PACKAGE_MANIFEST } from "../defaults.mjs";
|
|
7
7
|
import { readManifest } from "./environment-helpers.mjs";
|
|
8
8
|
import { folderNameArrayToRE } from "./utl.mjs";
|
|
@@ -124,11 +124,11 @@ function getSuccessMessage(pDestinationManifestFileName) {
|
|
|
124
124
|
return EXPERIMENTAL_SCRIPT_DOC.reduce(
|
|
125
125
|
(pAll, pScript) => {
|
|
126
126
|
return `${pAll}${
|
|
127
|
-
`\n ${
|
|
127
|
+
`\n ${styleText("green", "►")} ${pScript.headline}` +
|
|
128
128
|
`\n${pScript.description}\n\n`
|
|
129
129
|
}`;
|
|
130
130
|
},
|
|
131
|
-
` ${
|
|
131
|
+
` ${styleText("green", "✔")} Run scripts added to '${pDestinationManifestFileName}':\n`,
|
|
132
132
|
);
|
|
133
133
|
}
|
|
134
134
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { styleText } from "node:util";
|
|
2
2
|
import { SUMMARY } from "#utl/bus.mjs";
|
|
3
3
|
|
|
4
4
|
const FULL_ON = 100;
|
|
@@ -17,7 +17,8 @@ function getPercentageBar(pPercentage, pParameters) {
|
|
|
17
17
|
const lBlocks = Math.floor(lParameters.barSize * lPercentage);
|
|
18
18
|
const lBlanks = lParameters.barSize - lBlocks;
|
|
19
19
|
|
|
20
|
-
return `${
|
|
20
|
+
return `${styleText("green", lParameters.block.repeat(lBlocks))}${styleText(
|
|
21
|
+
"green",
|
|
21
22
|
lParameters.blank.repeat(lBlanks),
|
|
22
23
|
)} ${Math.round(FULL_ON * lPercentage)}%`;
|
|
23
24
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { styleText } from "node:util";
|
|
2
2
|
import { INFO } from "#utl/bus.mjs";
|
|
3
3
|
|
|
4
4
|
const MS_PER_SECOND = 1000;
|
|
@@ -43,23 +43,22 @@ export function formatDividerLine() {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export function formatHeader() {
|
|
46
|
-
return
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
.concat(formatDividerLine());
|
|
46
|
+
return styleText(
|
|
47
|
+
"bold",
|
|
48
|
+
`${
|
|
49
|
+
pad("∆ rss") +
|
|
50
|
+
pad("∆ heapTotal") +
|
|
51
|
+
pad("∆ heapUsed") +
|
|
52
|
+
pad("∆ external") +
|
|
53
|
+
pad("⏱ system") +
|
|
54
|
+
pad("⏱ user") +
|
|
55
|
+
pad("⏱ real")
|
|
56
|
+
}after step...\n`,
|
|
57
|
+
).concat(formatDividerLine());
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
function formatMessage(pMessage, pLevel) {
|
|
62
|
-
return pLevel >= INFO ?
|
|
61
|
+
return pLevel >= INFO ? styleText("dim", pMessage) : pMessage;
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
export function formatTime(
|
|
@@ -81,7 +80,7 @@ export function formatMemory(pBytes, pLevel) {
|
|
|
81
80
|
);
|
|
82
81
|
|
|
83
82
|
return formatMessage(
|
|
84
|
-
(pBytes < 0 ?
|
|
83
|
+
(pBytes < 0 ? styleText("blue", lReturnValue) : lReturnValue).concat(" "),
|
|
85
84
|
pLevel,
|
|
86
85
|
);
|
|
87
86
|
}
|
|
@@ -116,7 +116,9 @@ async function compileResolveOptions(
|
|
|
116
116
|
// so we do it ourselves - either with the extensions passed
|
|
117
117
|
// or with the supported ones.
|
|
118
118
|
extensions:
|
|
119
|
-
|
|
119
|
+
pResolveOptionsFromDCConfig.extensions ||
|
|
120
|
+
pResolveOptions.extensions ||
|
|
121
|
+
DEFAULT_RESOLVE_OPTIONS.extensions,
|
|
120
122
|
}),
|
|
121
123
|
);
|
|
122
124
|
}
|
package/src/meta.cjs
CHANGED
package/src/report/error.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EOL } from "node:os";
|
|
2
|
-
import
|
|
2
|
+
import { styleText } from "node:util";
|
|
3
3
|
import {
|
|
4
4
|
formatPercentage,
|
|
5
5
|
formatViolation as _formatViolation,
|
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
import { findRuleByName } from "#graph-utl/rule-set.mjs";
|
|
8
8
|
import wrapAndIndent from "#utl/wrap-and-indent.mjs";
|
|
9
9
|
|
|
10
|
-
const
|
|
11
|
-
["error",
|
|
12
|
-
["warn",
|
|
13
|
-
["info",
|
|
14
|
-
["ignore",
|
|
10
|
+
const SEVERITY2COLOR = new Map([
|
|
11
|
+
["error", "red"],
|
|
12
|
+
["warn", "yellow"],
|
|
13
|
+
["info", "cyan"],
|
|
14
|
+
["ignore", "gray"],
|
|
15
15
|
]);
|
|
16
16
|
|
|
17
17
|
const EXTRA_PATH_INFORMATION_INDENT = 6;
|
|
@@ -26,25 +26,24 @@ function formatMiniDependency(pMiniDependency) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
function formatModuleViolation(pViolation) {
|
|
29
|
-
return
|
|
29
|
+
return styleText("bold", pViolation.from);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
function formatDependencyViolation(pViolation) {
|
|
33
|
-
return `${
|
|
33
|
+
return `${styleText("bold", pViolation.from)} → ${styleText("bold", pViolation.to)}`;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
function formatCycleViolation(pViolation) {
|
|
37
|
-
return `${
|
|
37
|
+
return `${styleText("bold", pViolation.from)} → ${formatMiniDependency(pViolation.cycle)}`;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
function formatReachabilityViolation(pViolation) {
|
|
41
|
-
return `${
|
|
42
|
-
pViolation.to,
|
|
43
|
-
)}${formatMiniDependency(pViolation.via)}`;
|
|
41
|
+
return `${styleText("bold", pViolation.from)} → ${styleText("bold", pViolation.to)}${formatMiniDependency(pViolation.via)}`;
|
|
44
42
|
}
|
|
45
43
|
|
|
46
44
|
function formatInstabilityViolation(pViolation) {
|
|
47
|
-
return `${formatDependencyViolation(pViolation)}${EOL}${
|
|
45
|
+
return `${formatDependencyViolation(pViolation)}${EOL}${styleText(
|
|
46
|
+
"dim",
|
|
48
47
|
wrapAndIndent(
|
|
49
48
|
`instability: ${formatPercentage(pViolation.metrics.from.instability)} → ${formatPercentage(pViolation.metrics.to.instability)}`,
|
|
50
49
|
EXTRA_PATH_INFORMATION_INDENT,
|
|
@@ -67,12 +66,13 @@ function formatViolation(pViolation) {
|
|
|
67
66
|
);
|
|
68
67
|
|
|
69
68
|
return (
|
|
70
|
-
`${
|
|
69
|
+
`${styleText(
|
|
70
|
+
SEVERITY2COLOR.get(pViolation.rule.severity),
|
|
71
71
|
pViolation.rule.severity,
|
|
72
72
|
)} ${pViolation.rule.name}: ${lFormattedViolators}` +
|
|
73
73
|
`${
|
|
74
74
|
pViolation.comment
|
|
75
|
-
? `${EOL}${
|
|
75
|
+
? `${EOL}${styleText("dim", wrapAndIndent(pViolation.comment))}${EOL}`
|
|
76
76
|
: ""
|
|
77
77
|
}`
|
|
78
78
|
);
|
|
@@ -93,7 +93,7 @@ function formatSummary(pSummary) {
|
|
|
93
93
|
pSummary.totalCruised
|
|
94
94
|
} modules, ${pSummary.totalDependenciesCruised} dependencies cruised.${EOL}`;
|
|
95
95
|
|
|
96
|
-
return pSummary.error > 0 ?
|
|
96
|
+
return pSummary.error > 0 ? styleText("red", lMessage) : lMessage;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
function addExplanation(pRuleSet, pLong) {
|
|
@@ -107,7 +107,8 @@ function addExplanation(pRuleSet, pLong) {
|
|
|
107
107
|
|
|
108
108
|
function formatIgnoreWarning(pNumberOfIgnoredViolations) {
|
|
109
109
|
if (pNumberOfIgnoredViolations > 0) {
|
|
110
|
-
return
|
|
110
|
+
return styleText(
|
|
111
|
+
"yellow",
|
|
111
112
|
`‼ ${pNumberOfIgnoredViolations} known violations ignored. Run with --no-ignore-known to see them.${EOL}`,
|
|
112
113
|
);
|
|
113
114
|
}
|
|
@@ -120,7 +121,7 @@ function report(pResults, pLong) {
|
|
|
120
121
|
);
|
|
121
122
|
|
|
122
123
|
if (lNonIgnorableViolations.length === 0) {
|
|
123
|
-
return `${EOL}${
|
|
124
|
+
return `${EOL}${styleText("green", "✔")} no dependency violations found (${
|
|
124
125
|
pResults.summary.totalCruised
|
|
125
126
|
} modules, ${
|
|
126
127
|
pResults.summary.totalDependenciesCruised
|
package/src/report/metrics.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EOL } from "node:os";
|
|
2
|
-
import
|
|
2
|
+
import { styleText } from "node:util";
|
|
3
3
|
import { formatNumber, formatPercentage } from "./utl/index.mjs";
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -224,7 +224,7 @@ function formatToTextData(pData, pMetaData) {
|
|
|
224
224
|
* @returns {string}
|
|
225
225
|
*/
|
|
226
226
|
function formatToTextTable(pData, pMetaData) {
|
|
227
|
-
return [
|
|
227
|
+
return [styleText("bold", formatToTextHeader(pMetaData))]
|
|
228
228
|
.concat(formatToTextDemarcationLine(pMetaData))
|
|
229
229
|
.concat(formatToTextData(pData, pMetaData))
|
|
230
230
|
.join(EOL)
|
package/src/report/teamcity.mjs
CHANGED
|
@@ -1,6 +1,100 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { randomInt } from "node:crypto";
|
|
2
|
+
import memoize, { memoizeClear } from "memoize";
|
|
3
|
+
|
|
2
4
|
import { formatPercentage, formatViolation } from "./utl/index.mjs";
|
|
3
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Escape string for TeamCity output.
|
|
8
|
+
* @see https://confluence.jetbrains.com/display/TCD65/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-servMsgsServiceMessages
|
|
9
|
+
* Copied from https://github.com/pifantastic/teamcity-service-messages/blob/master/lib/message.js#L72
|
|
10
|
+
*
|
|
11
|
+
* @param {String} pMessageString the string to escape
|
|
12
|
+
* @return {String}
|
|
13
|
+
*/
|
|
14
|
+
function escape(pMessageString) {
|
|
15
|
+
if (pMessageString === null) {
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
pMessageString
|
|
21
|
+
.toString()
|
|
22
|
+
.replace(/\|/g, "||")
|
|
23
|
+
.replace(/\n/g, "|n")
|
|
24
|
+
.replace(/\r/g, "|r")
|
|
25
|
+
.replace(/\[/g, "|[")
|
|
26
|
+
.replace(/\]/g, "|]")
|
|
27
|
+
// next line
|
|
28
|
+
.replace(/\u0085/g, "|x")
|
|
29
|
+
// line separator
|
|
30
|
+
.replace(/\u2028/g, "|l")
|
|
31
|
+
// paragraph separator
|
|
32
|
+
.replace(/\u2029/g, "|p")
|
|
33
|
+
.replace(/'/g, "|'")
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Returns a random flowId consisting of 10 numeric digits. TeamCity doesn't
|
|
39
|
+
* currently seem to have demands on the format (it's just a string as far
|
|
40
|
+
* as I can tell), but this is what teamcity-service-messages used, so as
|
|
41
|
+
* per the rule of least surprise, this is what we use.
|
|
42
|
+
*
|
|
43
|
+
* @return {string} a random flowId consisting of 10 numeric digits
|
|
44
|
+
*/
|
|
45
|
+
function getRandomFlowIdBare() {
|
|
46
|
+
const lFlowIdLength = 10;
|
|
47
|
+
// eslint-disable-next-line no-magic-numbers
|
|
48
|
+
const lFlowIdMax = 10 ** lFlowIdLength;
|
|
49
|
+
|
|
50
|
+
return randomInt(1, lFlowIdMax).toString().padStart(lFlowIdLength, "0");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const getRandomFlowId = memoize(getRandomFlowIdBare);
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Returns a timestamp in ISO format without the trailing 'Z'. It used to be
|
|
57
|
+
* an issue with TeamCity that it didn't use the trailing 'Z' (this is
|
|
58
|
+
* documented in the teamcity-service-messages source code) - not sure whether
|
|
59
|
+
* this is still the case, but this is what we do to be on the safe side.
|
|
60
|
+
*
|
|
61
|
+
* @returns {string} a timestamp in ISO format without the trailing 'Z'
|
|
62
|
+
*/
|
|
63
|
+
function getTimeStamp() {
|
|
64
|
+
return new Date().toISOString().slice(0, -1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function inspectionType(pData) {
|
|
68
|
+
const lAttributes = [];
|
|
69
|
+
lAttributes.push(
|
|
70
|
+
`id='${pData.id}'`,
|
|
71
|
+
`name='${pData.name}'`,
|
|
72
|
+
`description='${escape(pData.description)}'`,
|
|
73
|
+
`category='${pData.category}'`,
|
|
74
|
+
`flowId='${getRandomFlowId()}'`,
|
|
75
|
+
`timestamp='${getTimeStamp()}'`,
|
|
76
|
+
);
|
|
77
|
+
return `##teamcity[inspectionType ${lAttributes.join(" ")}]`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function inspection(pData) {
|
|
81
|
+
const lAttributes = [];
|
|
82
|
+
lAttributes.push(
|
|
83
|
+
`typeId='${pData.typeId}'`,
|
|
84
|
+
`message='${escape(pData.message)}'`,
|
|
85
|
+
);
|
|
86
|
+
if (pData.file) {
|
|
87
|
+
lAttributes.push(`file='${pData.file}'`);
|
|
88
|
+
}
|
|
89
|
+
lAttributes.push(
|
|
90
|
+
`SEVERITY='${pData.SEVERITY}'`,
|
|
91
|
+
`flowId='${getRandomFlowId()}'`,
|
|
92
|
+
`timestamp='${getTimeStamp()}'`,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
return `##teamcity[inspection ${lAttributes.join(" ")}]`;
|
|
96
|
+
}
|
|
97
|
+
|
|
4
98
|
const CATEGORY = "dependency-cruiser";
|
|
5
99
|
const SEVERITY2TEAMCITY_SEVERITY = new Map([
|
|
6
100
|
["error", "ERROR"],
|
|
@@ -19,7 +113,7 @@ function reportRules(pRules, pViolations) {
|
|
|
19
113
|
pViolations.some((pViolation) => pRule.name === pViolation.rule.name),
|
|
20
114
|
)
|
|
21
115
|
.map((pRule) =>
|
|
22
|
-
|
|
116
|
+
inspectionType({
|
|
23
117
|
id: pRule.name,
|
|
24
118
|
name: pRule.name,
|
|
25
119
|
description: pRule.comment || pRule.name,
|
|
@@ -35,7 +129,7 @@ function reportAllowedRule(pAllowedRule, pViolations) {
|
|
|
35
129
|
pAllowedRule.length > 0 &&
|
|
36
130
|
pViolations.some((pViolation) => pViolation.rule.name === "not-in-allowed")
|
|
37
131
|
) {
|
|
38
|
-
lReturnValue =
|
|
132
|
+
lReturnValue = inspectionType({
|
|
39
133
|
id: "not-in-allowed",
|
|
40
134
|
name: "not-in-allowed",
|
|
41
135
|
description: "dependency is not in the 'allowed' set of rules",
|
|
@@ -49,7 +143,7 @@ function reportIgnoredRules(pIgnoredCount) {
|
|
|
49
143
|
let lReturnValue = [];
|
|
50
144
|
|
|
51
145
|
if (pIgnoredCount > 0) {
|
|
52
|
-
lReturnValue =
|
|
146
|
+
lReturnValue = inspectionType({
|
|
53
147
|
id: "ignored-known-violations",
|
|
54
148
|
name: "ignored-known-violations",
|
|
55
149
|
description:
|
|
@@ -114,7 +208,7 @@ function reportIgnoredViolation(pIgnoredCount) {
|
|
|
114
208
|
let lReturnValue = [];
|
|
115
209
|
|
|
116
210
|
if (pIgnoredCount > 0) {
|
|
117
|
-
lReturnValue =
|
|
211
|
+
lReturnValue = inspection({
|
|
118
212
|
typeId: "ignored-known-violations",
|
|
119
213
|
message: `${pIgnoredCount} known violations ignored. Run with --no-ignore-known to see them.`,
|
|
120
214
|
SEVERITY: "WARNING",
|
|
@@ -126,7 +220,7 @@ function reportIgnoredViolation(pIgnoredCount) {
|
|
|
126
220
|
function reportViolations(pViolations, pIgnoredCount) {
|
|
127
221
|
return pViolations
|
|
128
222
|
.map((pViolation) =>
|
|
129
|
-
|
|
223
|
+
inspection({
|
|
130
224
|
typeId: pViolation.rule.name,
|
|
131
225
|
message: bakeViolationMessage(pViolation),
|
|
132
226
|
file: pViolation.from,
|
|
@@ -138,21 +232,17 @@ function reportViolations(pViolations, pIgnoredCount) {
|
|
|
138
232
|
|
|
139
233
|
/**
|
|
140
234
|
* Returns a bunch of TeamCity service messages:
|
|
141
|
-
* - for each violated rule in the passed results: an `inspectionType` with the
|
|
142
|
-
*
|
|
235
|
+
* - for each violated rule in the passed results: an `inspectionType` with the
|
|
236
|
+
* name and comment of that rule
|
|
237
|
+
* - for each violation in the passed results: an `inspection` with the
|
|
238
|
+
* violated rule name and the tos and froms
|
|
143
239
|
*
|
|
144
240
|
* @param {import("../../types/dependency-cruiser.js").ICruiseResult} pResults
|
|
145
241
|
* @returns {import("../../types/dependency-cruiser.js").IReporterOutput}
|
|
146
242
|
*/
|
|
147
243
|
// eslint-disable-next-line complexity
|
|
148
244
|
export default function teamcity(pResults) {
|
|
149
|
-
|
|
150
|
-
// Alternatively we could've used the 'low level API', which
|
|
151
|
-
// involves creating new `Message`s and stringifying those.
|
|
152
|
-
// The abstraction of the 'higher level API' makes this
|
|
153
|
-
// reporter more easy to implement and maintain, despite
|
|
154
|
-
// setting this property directly
|
|
155
|
-
tsm.stdout = false;
|
|
245
|
+
memoizeClear(getRandomFlowId);
|
|
156
246
|
|
|
157
247
|
const lRuleSet = pResults?.summary?.ruleSetUsed ?? [];
|
|
158
248
|
const lViolations = (pResults?.summary?.violations ?? []).filter(
|
package/src/report/text.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { styleText } from "node:util";
|
|
2
2
|
|
|
3
3
|
const DEFAULT_OPTIONS = {
|
|
4
4
|
highlightFocused: false,
|
|
@@ -36,7 +36,9 @@ function toFlatDependencies(pModules, pModulesInFocus, pHighlightFocused) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
function stringifyModule(pModule) {
|
|
39
|
-
return pModule.highlight
|
|
39
|
+
return pModule.highlight
|
|
40
|
+
? styleText("underline", pModule.name)
|
|
41
|
+
: pModule.name;
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
function stringify(pFlatDependency) {
|
package/types/shared-types.d.mts
CHANGED
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
export type ModuleSystemType = "cjs" | "amd" | "es6" | "tsd";
|
|
3
3
|
|
|
4
4
|
// cruise options, dependency-cruiser
|
|
5
|
-
/* as we don't care about types beyond code completion, we ignore the
|
|
6
|
-
* eslint warning that the 'string' type is redundant here.
|
|
7
|
-
*/
|
|
8
|
-
/* eslint-disable @typescript-eslint/no-redundant-type-constituents */
|
|
9
5
|
export type OutputType =
|
|
10
6
|
| "json"
|
|
11
7
|
| "html"
|
|
@@ -29,7 +25,7 @@ export type OutputType =
|
|
|
29
25
|
| "null"
|
|
30
26
|
// for plugins: string. TODO: research whether it's possible to
|
|
31
27
|
// tie this down to the `^plugin:[^:]+-reporter-plugin.[cm]?js$` regex
|
|
32
|
-
| string;
|
|
28
|
+
| (string & {}); // autocompletion hack
|
|
33
29
|
|
|
34
30
|
export type SeverityType = "error" | "warn" | "info" | "ignore";
|
|
35
31
|
|