dependency-cruiser 10.8.0-beta-1 → 10.9.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 +1 -1
- package/package.json +33 -22
- package/src/enrich/derive/metrics/folder.js +8 -0
- package/src/enrich/derive/{folders → metrics}/get-stability-metrics.js +4 -4
- package/src/enrich/derive/{folders → metrics}/module-utl.js +0 -0
- package/src/enrich/derive/metrics/module.js +28 -0
- package/src/enrich/derive/{folders → metrics}/utl.js +2 -2
- package/src/enrich/derive/utl.js +26 -10
- package/src/enrich/enrich-modules.js +5 -0
- package/src/enrich/index.js +2 -2
- package/src/enrich/soften-known-violations.js +4 -2
- package/src/extract/ast-extractors/swc-dependency-visitor.js +14 -2
- package/src/main/options/normalize.js +28 -0
- package/src/main/report-wrap.js +7 -2
- package/src/meta.js +1 -1
- package/src/report/dot/dot.template.js +1 -1
- package/src/report/dot/index.js +7 -2
- package/src/report/dot/module-utl.js +47 -19
- package/src/report/dot/prepare-custom-level.js +2 -2
- package/src/report/dot/prepare-flat-level.js +2 -2
- package/src/report/dot/prepare-folder-level.js +2 -2
- package/src/report/metrics.js +96 -47
- package/src/schema/configuration.schema.js +22 -0
- package/src/schema/cruise-result.schema.js +24 -0
- package/src/validate/match-dependency-rule.js +1 -0
- package/src/validate/matchers.js +12 -0
- package/types/baseline-violations.d.ts +1 -1
- package/types/cruise-result.d.ts +18 -48
- package/types/options.d.ts +5 -0
- package/types/reporter-options.d.ts +23 -0
- package/types/restrictions.d.ts +12 -0
- package/types/rule-summary.d.ts +23 -0
- package/types/violations.d.ts +24 -0
- package/src/enrich/derive/folders/index.js +0 -12
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ you can stick on the wall to impress your grandma.
|
|
|
21
21
|
### Install it
|
|
22
22
|
|
|
23
23
|
- `npm install --save-dev dependency-cruiser` to use it as a validator in your project (recommended) or...
|
|
24
|
-
- `npm install --global dependency-cruiser` if you just want to
|
|
24
|
+
- `npm install --global dependency-cruiser` if you just want to inspect multiple projects.
|
|
25
25
|
|
|
26
26
|
### Show stuff to your grandma
|
|
27
27
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dependency-cruiser",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.9.0",
|
|
4
4
|
"description": "Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"static analysis",
|
|
@@ -76,7 +76,6 @@
|
|
|
76
76
|
"depcruise:all": "node ./bin/dependency-cruise.js src bin test configs types tools --config",
|
|
77
77
|
"depcruise:baseline": "node ./bin/depcruise-baseline.js src bin test configs types tools",
|
|
78
78
|
"depcruise:explain": "node ./bin/dependency-cruise.js src bin test configs types tools --output-type err-long --config --progress none",
|
|
79
|
-
"depcruise:graph:dev": "node ./bin/dependency-cruise.js bin src --prefix vscode://file/$(pwd)/ --config --output-type dot --progress cli-feedback | dot -T svg | node ./bin/wrap-stream-in-html.js | browser",
|
|
80
79
|
"depcruise:graph:doc": "npm-run-all depcruise:graph:doc:json --parallel depcruise:graph:doc:fmt-* depcruise:graph:doc:samples",
|
|
81
80
|
"depcruise:graph:doc:json": "node ./bin/dependency-cruise.js bin src test --config --output-type json --output-to tmp_graph_deps.json --progress",
|
|
82
81
|
"depcruise:graph:doc:fmt-detail": "./bin/depcruise-fmt.js -T dot -f - tmp_graph_deps.json | dot -T svg | tee doc/real-world-samples/dependency-cruiser-without-node_modules.svg | node bin/wrap-stream-in-html.js > docs/dependency-cruiser-dependency-graph.html",
|
|
@@ -88,7 +87,9 @@
|
|
|
88
87
|
"depcruise:graph:dot": "node ./bin/dependency-cruise.js bin src --config --output-type dot | dot -T svg > tmp_deps.svg",
|
|
89
88
|
"depcruise:graph:fdp": "node ./bin/dependency-cruise.js bin src --config --output-type dot | fdp -GK=0.1 -Gsplines=true -T svg > tmp_deps.svg",
|
|
90
89
|
"depcruise:graph:osage": "node ./bin/dependency-cruise.js bin src --config --output-type dot | osage -Gpack=32 -GpackMode=array2 -T svg > tmp_deps.svg",
|
|
91
|
-
"depcruise:
|
|
90
|
+
"depcruise:graph:view": "node ./bin/dependency-cruise.js bin src --prefix vscode://file/$(pwd)/ --config configs/.dependency-cruiser-show-metrics-config.json --output-type dot --progress cli-feedback | dot -T svg | node ./bin/wrap-stream-in-html.js | browser",
|
|
91
|
+
"depcruise:report": "node ./bin/dependency-cruise.js src bin test configs types --output-type err-html --config configs/.dependency-cruiser-show-metrics-config.json --output-to dependency-violations.html",
|
|
92
|
+
"depcruise:report:view": "node ./bin/dependency-cruise.js src bin test configs types --output-type err-html --config configs/.dependency-cruiser-show-metrics-config.json --output-to - | browser",
|
|
92
93
|
"depcruise:focus": "node ./bin/dependency-cruise.js src bin test configs types --progress --config --output-type text --focus",
|
|
93
94
|
"lint": "npm-run-all --parallel --aggregate-output lint:eslint lint:prettier:check lint:types",
|
|
94
95
|
"lint:eslint": "eslint bin/dependency-cruise.js src test configs tools/**/*.mjs --cache --cache-location .cache/eslint/",
|
|
@@ -131,13 +132,13 @@
|
|
|
131
132
|
"version": "npm-run-all build depcruise:graph:doc scm:stage"
|
|
132
133
|
},
|
|
133
134
|
"dependencies": {
|
|
134
|
-
"acorn": "8.
|
|
135
|
+
"acorn": "8.6.0",
|
|
135
136
|
"acorn-jsx": "5.3.2",
|
|
136
137
|
"acorn-jsx-walk": "2.0.0",
|
|
137
138
|
"acorn-loose": "8.2.1",
|
|
138
139
|
"acorn-walk": "8.2.0",
|
|
139
|
-
"ajv": "8.
|
|
140
|
-
"chalk": "4.1.2",
|
|
140
|
+
"ajv": "8.8.2",
|
|
141
|
+
"chalk": "^4.1.2",
|
|
141
142
|
"commander": "8.3.0",
|
|
142
143
|
"enhanced-resolve": "5.8.3",
|
|
143
144
|
"figures": "^3.2.0",
|
|
@@ -151,18 +152,18 @@
|
|
|
151
152
|
"safe-regex": "2.1.1",
|
|
152
153
|
"semver": "^7.3.5",
|
|
153
154
|
"semver-try-require": "^5.0.1",
|
|
154
|
-
"teamcity-service-messages": "0.1.
|
|
155
|
-
"tsconfig-paths-webpack-plugin": "3.5.
|
|
155
|
+
"teamcity-service-messages": "0.1.12",
|
|
156
|
+
"tsconfig-paths-webpack-plugin": "3.5.2",
|
|
156
157
|
"wrap-ansi": "^7.0.0"
|
|
157
158
|
},
|
|
158
159
|
"devDependencies": {
|
|
159
|
-
"@babel/core": "7.
|
|
160
|
-
"@babel/plugin-transform-modules-commonjs": "7.
|
|
161
|
-
"@babel/preset-typescript": "7.
|
|
162
|
-
"@swc/core": "1.2.
|
|
163
|
-
"@typescript-eslint/eslint-plugin": "5.
|
|
164
|
-
"@typescript-eslint/parser": "5.
|
|
165
|
-
"@vue/compiler-sfc": "3.2.
|
|
160
|
+
"@babel/core": "7.16.5",
|
|
161
|
+
"@babel/plugin-transform-modules-commonjs": "7.16.5",
|
|
162
|
+
"@babel/preset-typescript": "7.16.5",
|
|
163
|
+
"@swc/core": "1.2.120",
|
|
164
|
+
"@typescript-eslint/eslint-plugin": "5.7.0",
|
|
165
|
+
"@typescript-eslint/parser": "5.7.0",
|
|
166
|
+
"@vue/compiler-sfc": "3.2.26",
|
|
166
167
|
"c8": "7.10.0",
|
|
167
168
|
"chai": "4.3.4",
|
|
168
169
|
"chai-json-schema": "1.5.1",
|
|
@@ -171,23 +172,23 @@
|
|
|
171
172
|
"eslint-config-moving-meadow": "2.0.9",
|
|
172
173
|
"eslint-config-prettier": "8.3.0",
|
|
173
174
|
"eslint-plugin-budapestian": "3.0.1",
|
|
174
|
-
"eslint-plugin-import": "2.25.
|
|
175
|
-
"eslint-plugin-mocha": "9.0.0",
|
|
175
|
+
"eslint-plugin-import": "2.25.3",
|
|
176
|
+
"eslint-plugin-mocha": "^9.0.0",
|
|
176
177
|
"eslint-plugin-node": "11.1.0",
|
|
177
178
|
"eslint-plugin-security": "1.4.0",
|
|
178
|
-
"eslint-plugin-unicorn": "
|
|
179
|
+
"eslint-plugin-unicorn": "39.0.0",
|
|
179
180
|
"husky": "^4.3.8",
|
|
180
181
|
"intercept-stdout": "0.1.2",
|
|
181
|
-
"lint-staged": "
|
|
182
|
+
"lint-staged": "12.1.2",
|
|
182
183
|
"mocha": "9.1.3",
|
|
183
184
|
"normalize-newline": "^3.0.0",
|
|
184
185
|
"npm-run-all": "4.1.5",
|
|
185
|
-
"prettier": "2.
|
|
186
|
+
"prettier": "2.5.1",
|
|
186
187
|
"proxyquire": "2.1.3",
|
|
187
188
|
"shx": "0.3.3",
|
|
188
|
-
"svelte": "3.44.
|
|
189
|
+
"svelte": "3.44.3",
|
|
189
190
|
"symlink-dir": "5.0.1",
|
|
190
|
-
"typescript": "4.
|
|
191
|
+
"typescript": "4.5.4",
|
|
191
192
|
"upem": "^7.0.0",
|
|
192
193
|
"vue-template-compiler": "2.6.14",
|
|
193
194
|
"yarn": "1.22.17"
|
|
@@ -199,6 +200,11 @@
|
|
|
199
200
|
"policy": "wanted",
|
|
200
201
|
"because": "some eslint plugins (eslint-plugin-budapestian) are not compatible with eslint 8 yet "
|
|
201
202
|
},
|
|
203
|
+
{
|
|
204
|
+
"package": "chalk",
|
|
205
|
+
"policy": "wanted",
|
|
206
|
+
"because": "version 5 only exports ejs - and we use cjs and don't transpile"
|
|
207
|
+
},
|
|
202
208
|
{
|
|
203
209
|
"package": "figures",
|
|
204
210
|
"policy": "wanted",
|
|
@@ -214,6 +220,11 @@
|
|
|
214
220
|
"policy": "wanted",
|
|
215
221
|
"because": "version 5 only exports ejs - and we use cjs and don't transpile"
|
|
216
222
|
},
|
|
223
|
+
{
|
|
224
|
+
"package": "eslint-plugin-mocha",
|
|
225
|
+
"policy": "wanted",
|
|
226
|
+
"because": "version 10 dropped support for node 12, which we still do support"
|
|
227
|
+
},
|
|
217
228
|
{
|
|
218
229
|
"package": "normalize-newline",
|
|
219
230
|
"policy": "wanted",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable security/detect-object-injection */
|
|
2
2
|
const path = require("path").posix;
|
|
3
|
-
const {
|
|
3
|
+
const { object2Array, getParentFolders } = require("./utl");
|
|
4
4
|
const {
|
|
5
5
|
getAfferentCouplings,
|
|
6
6
|
getEfferentCouplings,
|
|
@@ -63,8 +63,8 @@ function getFolderLevelCouplings(pCouplingArray) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
function calculateFolderMetrics(pFolder) {
|
|
66
|
-
const lModuleDependents =
|
|
67
|
-
const lModuleDependencies =
|
|
66
|
+
const lModuleDependents = object2Array(pFolder.dependents);
|
|
67
|
+
const lModuleDependencies = object2Array(pFolder.dependencies);
|
|
68
68
|
const lAfferentCouplings = lModuleDependents.reduce(sumCounts, 0);
|
|
69
69
|
const lEfferentCouplings = lModuleDependencies.reduce(sumCounts, 0);
|
|
70
70
|
|
|
@@ -83,7 +83,7 @@ function calculateFolderMetrics(pFolder) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
module.exports = function getStabilityMetrics(pModules) {
|
|
86
|
-
return
|
|
86
|
+
return object2Array(
|
|
87
87
|
pModules.filter(metricsAreCalculable).reduce((pAllFolders, pModule) => {
|
|
88
88
|
getParentFolders(path.dirname(pModule.source)).forEach(
|
|
89
89
|
(pParentDirectory) =>
|
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const { findModuleByName, clearCache } = require("../utl");
|
|
2
|
+
const { metricsAreCalculable } = require("./module-utl");
|
|
3
|
+
|
|
4
|
+
module.exports = function deriveModuleMetrics(pModules, pOptions) {
|
|
5
|
+
if (pOptions.metrics) {
|
|
6
|
+
const lModules = pModules.map((pModule) => ({
|
|
7
|
+
...pModule,
|
|
8
|
+
...(metricsAreCalculable(pModule)
|
|
9
|
+
? {
|
|
10
|
+
instability:
|
|
11
|
+
pModule.dependencies.length /
|
|
12
|
+
(pModule.dependents.length + pModule.dependencies.length) || 0,
|
|
13
|
+
}
|
|
14
|
+
: {}),
|
|
15
|
+
}));
|
|
16
|
+
clearCache();
|
|
17
|
+
return lModules.map((pModule) => ({
|
|
18
|
+
...pModule,
|
|
19
|
+
dependencies: pModule.dependencies.map((pDependency) => ({
|
|
20
|
+
...pDependency,
|
|
21
|
+
instability:
|
|
22
|
+
(findModuleByName(lModules, pDependency.resolved) || {})
|
|
23
|
+
.instability || 0,
|
|
24
|
+
})),
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
return pModules;
|
|
28
|
+
};
|
|
@@ -10,7 +10,7 @@ function getParentFolders(pPath) {
|
|
|
10
10
|
return lReturnValue.reverse();
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
function
|
|
13
|
+
function object2Array(pObject) {
|
|
14
14
|
return Object.keys(pObject).map((pKey) => ({
|
|
15
15
|
name: pKey,
|
|
16
16
|
...pObject[pKey],
|
|
@@ -19,5 +19,5 @@ function foldersObject2folderArray(pObject) {
|
|
|
19
19
|
|
|
20
20
|
module.exports = {
|
|
21
21
|
getParentFolders,
|
|
22
|
-
|
|
22
|
+
object2Array,
|
|
23
23
|
};
|
package/src/enrich/derive/utl.js
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
let gIndexedGraph = null;
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Returns the module with attribute pSource, when it exists in the pModuleGraph.
|
|
5
|
+
* Returns undefined when it doesn't.
|
|
6
|
+
*
|
|
7
|
+
* This function uses an indexed cache for efficiency reasons. If you need to
|
|
8
|
+
* call this function consecutively for different module graphs, you can clear
|
|
9
|
+
* this cache with the clearCache function from this module.
|
|
10
|
+
*
|
|
11
|
+
* @param {import('../../../types/cruise-result').IModule[]} pModuleGraph
|
|
12
|
+
* @param {string} pSource
|
|
13
|
+
* @returns {import('../../../types/cruise-result').IModule | undefined}
|
|
14
|
+
*/
|
|
15
|
+
function findModuleByName(pModuleGraph, pSource) {
|
|
16
|
+
if (!gIndexedGraph) {
|
|
17
|
+
gIndexedGraph = new Map(
|
|
18
|
+
pModuleGraph.map((pModule) => [pModule.source, pModule])
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
return gIndexedGraph.get(pSource);
|
|
5
22
|
}
|
|
6
23
|
|
|
7
|
-
const findModuleByName = _memoize(
|
|
8
|
-
bareFindModuleByName,
|
|
9
|
-
(_pGraph, pSource) => pSource
|
|
10
|
-
);
|
|
11
|
-
|
|
12
24
|
function isDependent(pResolvedName) {
|
|
13
25
|
return (pModule) =>
|
|
14
26
|
pModule.dependencies.some(
|
|
@@ -17,7 +29,11 @@ function isDependent(pResolvedName) {
|
|
|
17
29
|
}
|
|
18
30
|
|
|
19
31
|
function clearCache() {
|
|
20
|
-
|
|
32
|
+
gIndexedGraph = null;
|
|
21
33
|
}
|
|
22
34
|
|
|
23
|
-
module.exports = {
|
|
35
|
+
module.exports = {
|
|
36
|
+
findModuleByName,
|
|
37
|
+
clearCache,
|
|
38
|
+
isDependent,
|
|
39
|
+
};
|
|
@@ -8,6 +8,7 @@ const addDependents = require("./derive/dependents");
|
|
|
8
8
|
const deriveReachable = require("./derive/reachable");
|
|
9
9
|
const addValidations = require("./add-validations");
|
|
10
10
|
const softenKnownViolations = require("./soften-known-violations");
|
|
11
|
+
const deriveModuleMetrics = require("./derive/metrics/module");
|
|
11
12
|
|
|
12
13
|
module.exports = function enrichModules(pModules, pOptions) {
|
|
13
14
|
bus.emit("progress", "analyzing: cycles", { level: busLogLevels.INFO });
|
|
@@ -18,6 +19,10 @@ module.exports = function enrichModules(pModules, pOptions) {
|
|
|
18
19
|
lModules = deriveOrphans(lModules);
|
|
19
20
|
bus.emit("progress", "analyzing: reachables", { level: busLogLevels.INFO });
|
|
20
21
|
lModules = deriveReachable(lModules, pOptions.ruleSet);
|
|
22
|
+
bus.emit("progress", "analyzing: module metrics", {
|
|
23
|
+
level: busLogLevels.INFO,
|
|
24
|
+
});
|
|
25
|
+
lModules = deriveModuleMetrics(lModules, pOptions);
|
|
21
26
|
bus.emit("progress", "analyzing: add focus (if any)", {
|
|
22
27
|
level: busLogLevels.INFO,
|
|
23
28
|
});
|
package/src/enrich/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const enrichModules = require("./enrich-modules");
|
|
2
|
-
const
|
|
2
|
+
const deriveFolderMetrics = require("./derive/metrics/folder.js");
|
|
3
3
|
const summarize = require("./summarize");
|
|
4
4
|
const clearCaches = require("./clear-caches");
|
|
5
5
|
|
|
@@ -10,7 +10,7 @@ module.exports = function enrich(pModules, pOptions, pFileAndDirectoryArray) {
|
|
|
10
10
|
clearCaches();
|
|
11
11
|
return {
|
|
12
12
|
modules: lModules,
|
|
13
|
-
...
|
|
13
|
+
...deriveFolderMetrics(lModules, pOptions),
|
|
14
14
|
summary: summarize(lModules, pOptions, pFileAndDirectoryArray),
|
|
15
15
|
};
|
|
16
16
|
};
|
|
@@ -78,7 +78,8 @@ function softenKnownViolation(pModule, pKnownViolations, pSoftenedSeverity) {
|
|
|
78
78
|
pRule,
|
|
79
79
|
pModule.source,
|
|
80
80
|
pKnownViolations.filter(
|
|
81
|
-
(pKnownError) =>
|
|
81
|
+
(pKnownError) =>
|
|
82
|
+
pKnownError.from === pKnownError.to && !pKnownError.cycle
|
|
82
83
|
),
|
|
83
84
|
pSoftenedSeverity
|
|
84
85
|
)
|
|
@@ -93,7 +94,8 @@ function softenKnownViolation(pModule, pKnownViolations, pSoftenedSeverity) {
|
|
|
93
94
|
pDependency,
|
|
94
95
|
pModule.source,
|
|
95
96
|
pKnownViolations.filter(
|
|
96
|
-
(pKnownError) =>
|
|
97
|
+
(pKnownError) =>
|
|
98
|
+
pKnownError.from !== pKnownError.to || pKnownError.cycle
|
|
97
99
|
),
|
|
98
100
|
pSoftenedSeverity
|
|
99
101
|
)
|
|
@@ -119,7 +119,13 @@ if (VisitorModule) {
|
|
|
119
119
|
// also include the same method, but with the correct spelling.
|
|
120
120
|
visitExportAllDeclration(pNode) {
|
|
121
121
|
this.pushImportExportSource(pNode);
|
|
122
|
-
|
|
122
|
+
/* c8 ignore start */
|
|
123
|
+
if (super.visitExportAllDeclration) {
|
|
124
|
+
return super.visitExportAllDeclration(pNode);
|
|
125
|
+
} else {
|
|
126
|
+
/* c8 ignore stop */
|
|
127
|
+
return super.visitExportAllDeclaration(pNode);
|
|
128
|
+
}
|
|
123
129
|
}
|
|
124
130
|
|
|
125
131
|
/* c8 ignore start */
|
|
@@ -131,7 +137,13 @@ if (VisitorModule) {
|
|
|
131
137
|
// same spelling error as the above - same solution
|
|
132
138
|
visitExportNamedDeclration(pNode) {
|
|
133
139
|
this.pushImportExportSource(pNode);
|
|
134
|
-
|
|
140
|
+
/* c8 ignore start */
|
|
141
|
+
if (super.visitExportNamedDeclration) {
|
|
142
|
+
return super.visitExportNamedDeclration(pNode);
|
|
143
|
+
} else {
|
|
144
|
+
/* c8 ignore stop */
|
|
145
|
+
return super.visitExportNamedDeclaration(pNode);
|
|
146
|
+
}
|
|
135
147
|
}
|
|
136
148
|
/* c8 ignore start */
|
|
137
149
|
visitExportNamedDeclaration(pNode) {
|
|
@@ -66,6 +66,32 @@ function normalizeCollapse(pCollapse) {
|
|
|
66
66
|
return lReturnValue;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
function hasMetricsRule(pRule) {
|
|
70
|
+
return _has(pRule, "to.moreUnstable");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function ruleSetHasMetricsRule(pRuleSet) {
|
|
74
|
+
const lRuleSet = pRuleSet || {};
|
|
75
|
+
return (
|
|
76
|
+
(lRuleSet.forbidden || []).some(hasMetricsRule) ||
|
|
77
|
+
(lRuleSet.allowed || []).some(hasMetricsRule)
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Determines whether (instability) metrics should be calculated
|
|
83
|
+
*
|
|
84
|
+
* @param {import('../../../types/options').ICruiseOptions pOptions
|
|
85
|
+
* @returns Boolean
|
|
86
|
+
*/
|
|
87
|
+
function shouldCalculateMetrics(pOptions) {
|
|
88
|
+
return (
|
|
89
|
+
pOptions.metrics ||
|
|
90
|
+
pOptions.outputType === "metrics" ||
|
|
91
|
+
ruleSetHasMetricsRule(pOptions.ruleSet)
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
69
95
|
/**
|
|
70
96
|
*
|
|
71
97
|
* @param {Partial <import('../../../types/options').ICruiseOptions>} pOptions
|
|
@@ -101,6 +127,8 @@ function normalizeCruiseOptions(pOptions) {
|
|
|
101
127
|
lReturnValue.reporterOptions
|
|
102
128
|
);
|
|
103
129
|
}
|
|
130
|
+
lReturnValue.metrics = shouldCalculateMetrics(pOptions);
|
|
131
|
+
|
|
104
132
|
return lReturnValue;
|
|
105
133
|
}
|
|
106
134
|
|
package/src/main/report-wrap.js
CHANGED
|
@@ -28,13 +28,18 @@ function reSummarizeResults(pResult, pFormatOptions) {
|
|
|
28
28
|
|
|
29
29
|
module.exports = function reportWrap(pResult, pFormatOptions) {
|
|
30
30
|
const lReportFunction = report.getReporter(pFormatOptions.outputType);
|
|
31
|
+
const lReportOptions = _get(
|
|
32
|
+
pResult,
|
|
33
|
+
`summary.optionsUsed.reporterOptions.${pFormatOptions.outputType}`,
|
|
34
|
+
{}
|
|
35
|
+
);
|
|
31
36
|
|
|
32
37
|
return lReportFunction(
|
|
33
38
|
reSummarizeResults(pResult, pFormatOptions),
|
|
34
39
|
// passing format options here so reporters that read collapse patterns
|
|
35
40
|
// from the result take the one passed in the format options instead
|
|
36
41
|
_has(pFormatOptions, "collapse")
|
|
37
|
-
? { collapsePattern: pFormatOptions.collapse }
|
|
38
|
-
:
|
|
42
|
+
? { ...lReportOptions, collapsePattern: pFormatOptions.collapse }
|
|
43
|
+
: lReportOptions
|
|
39
44
|
);
|
|
40
45
|
};
|
package/src/meta.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var Handlebars=require("handlebars/runtime"),template=Handlebars.template,templates=Handlebars.templates=Handlebars.templates||{};templates["dot.template.hbs"]=template({1:function(l,n,e,t,o){var a=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return null!=(a="function"==typeof(a=null!=(a=a(e,"graphAttrs")||(null!=n?a(n,"graphAttrs"):n))?a:l.hooks.helperMissing)?a.call(null!=n?n:l.nullContext||{},{name:"graphAttrs",hash:{},data:o,loc:{start:{line:2,column:22},end:{line:2,column:38}}}):a)?a:""},3:function(l,n,e,t,o){var a=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return"node ["+(null!=(a="function"==typeof(a=null!=(a=a(e,"nodeAttrs")||(null!=n?a(n,"nodeAttrs"):n))?a:l.hooks.helperMissing)?a.call(null!=n?n:l.nullContext||{},{name:"nodeAttrs",hash:{},data:o,loc:{start:{line:3,column:27},end:{line:3,column:42}}}):a)?a:"")+"]"},5:function(l,n,e,t,o){var a=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return"edge ["+(null!=(a="function"==typeof(a=null!=(a=a(e,"edgeAttrs")||(null!=n?a(n,"edgeAttrs"):n))?a:l.hooks.helperMissing)?a.call(null!=n?n:l.nullContext||{},{name:"edgeAttrs",hash:{},data:o,loc:{start:{line:4,column:27},end:{line:4,column:42}}}):a)?a:"")+"]"},7:function(l,n,e,t,o,a,r){var u=null!=n?n:l.nullContext||{},
|
|
1
|
+
var Handlebars=require("handlebars/runtime"),template=Handlebars.template,templates=Handlebars.templates=Handlebars.templates||{};templates["dot.template.hbs"]=template({1:function(l,n,e,t,o){var a=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return null!=(a="function"==typeof(a=null!=(a=a(e,"graphAttrs")||(null!=n?a(n,"graphAttrs"):n))?a:l.hooks.helperMissing)?a.call(null!=n?n:l.nullContext||{},{name:"graphAttrs",hash:{},data:o,loc:{start:{line:2,column:22},end:{line:2,column:38}}}):a)?a:""},3:function(l,n,e,t,o){var a=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return"node ["+(null!=(a="function"==typeof(a=null!=(a=a(e,"nodeAttrs")||(null!=n?a(n,"nodeAttrs"):n))?a:l.hooks.helperMissing)?a.call(null!=n?n:l.nullContext||{},{name:"nodeAttrs",hash:{},data:o,loc:{start:{line:3,column:27},end:{line:3,column:42}}}):a)?a:"")+"]"},5:function(l,n,e,t,o){var a=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return"edge ["+(null!=(a="function"==typeof(a=null!=(a=a(e,"edgeAttrs")||(null!=n?a(n,"edgeAttrs"):n))?a:l.hooks.helperMissing)?a.call(null!=n?n:l.nullContext||{},{name:"edgeAttrs",hash:{},data:o,loc:{start:{line:4,column:27},end:{line:4,column:42}}}):a)?a:"")+"]"},7:function(l,n,e,t,o,a,r){var u=null!=n?n:l.nullContext||{},i=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]},c=null!=(p=i(e,"if").call(u,null!=n?i(n,"folder"):n,{name:"if",hash:{},fn:l.program(8,o,0,a,r),inverse:l.program(18,o,0,a,r),data:o,loc:{start:{line:7,column:4},end:{line:12,column:11}}}))?p:"",s=null!=(s=i(e,"dependencies")||(null!=n?i(n,"dependencies"):n))?s:l.hooks.helperMissing,o={name:"dependencies",hash:{},fn:l.program(20,o,0,a,r),inverse:l.noop,data:o,loc:{start:{line:13,column:4},end:{line:15,column:21}}},p="function"==typeof s?s.call(u,o):s;return null!=(p=!i(e,"dependencies")?l.hooks.blockHelperMissing.call(n,p,o):p)&&(c+=p),c},8:function(l,n,e,t,o,a,r){var u,i,c=null!=n?n:l.nullContext||{},s=l.hooks.helperMissing,p="function",m=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return" "+(null!=(u=m(e,"each").call(c,null!=n?m(n,"path"):n,{name:"each",hash:{},fn:l.program(9,o,0,a,r),inverse:l.noop,data:o,loc:{start:{line:8,column:4},end:{line:8,column:201}}}))?u:"")+'"'+(null!=(u=typeof(i=null!=(i=m(e,"source")||(null!=n?m(n,"source"):n))?i:s)==p?i.call(c,{name:"source",hash:{},data:o,loc:{start:{line:9,column:9},end:{line:9,column:21}}}):i)?u:"")+'" [label='+(null!=(u=typeof(i=null!=(i=m(e,"label")||(null!=n?m(n,"label"):n))?i:s)==p?i.call(c,{name:"label",hash:{},data:o,loc:{start:{line:9,column:30},end:{line:9,column:41}}}):i)?u:"")+' tooltip="'+(null!=(u=typeof(i=null!=(i=m(e,"tooltip")||(null!=n?m(n,"tooltip"):n))?i:s)==p?i.call(c,{name:"tooltip",hash:{},data:o,loc:{start:{line:9,column:51},end:{line:9,column:64}}}):i)?u:"")+'" '+(null!=(u=m(e,"if").call(c,null!=n?m(n,"URL"):n,{name:"if",hash:{},fn:l.program(12,o,0,a,r),inverse:l.noop,data:o,loc:{start:{line:9,column:66},end:{line:9,column:100}}}))?u:"")+(null!=(u=m(e,"if").call(c,null!=n?m(n,"themeAttrs"):n,{name:"if",hash:{},fn:l.program(14,o,0,a,r),inverse:l.noop,data:o,loc:{start:{line:9,column:100},end:{line:9,column:141}}}))?u:"")+"]"+(null!=(u=m(e,"each").call(c,null!=n?m(n,"path"):n,{name:"each",hash:{},fn:l.program(16,o,0,a,r),inverse:l.noop,data:o,loc:{start:{line:9,column:142},end:{line:9,column:167}}}))?u:"")+"\n"},9:function(l,n,e,t,o,a,r){var u,i,c=null!=n?n:l.nullContext||{},s=l.hooks.helperMissing,p="function",m=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return'subgraph "cluster_'+(null!=(u=typeof(i=null!=(i=m(e,"aggregateSnippet")||(null!=n?m(n,"aggregateSnippet"):n))?i:s)==p?i.call(c,{name:"aggregateSnippet",hash:{},data:o,loc:{start:{line:8,column:36},end:{line:8,column:58}}}):i)?u:"")+'" {label="'+(null!=(u=typeof(i=null!=(i=m(e,"snippet")||(null!=n?m(n,"snippet"):n))?i:s)==p?i.call(c,{name:"snippet",hash:{},data:o,loc:{start:{line:8,column:68},end:{line:8,column:81}}}):i)?u:"")+'" '+(null!=(u=m(e,"if").call(c,null!=r[2]?m(r[2],"clustersHaveOwnNode"):r[2],{name:"if",hash:{},fn:l.program(10,o,0,a,r),inverse:l.noop,data:o,loc:{start:{line:8,column:83},end:{line:8,column:191}}}))?u:"")},10:function(l,n,e,t,o){var a=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return'"'+(null!=(a="function"==typeof(a=null!=(a=a(e,"aggregateSnippet")||(null!=n?a(n,"aggregateSnippet"):n))?a:l.hooks.helperMissing)?a.call(null!=n?n:l.nullContext||{},{name:"aggregateSnippet",hash:{},data:o,loc:{start:{line:8,column:117},end:{line:8,column:139}}}):a)?a:"")+'" [width="0.05" shape="point" style="invis"] '},12:function(l,n,e,t,o){var a=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return'URL="'+(null!=(a="function"==typeof(a=null!=(a=a(e,"URL")||(null!=n?a(n,"URL"):n))?a:l.hooks.helperMissing)?a.call(null!=n?n:l.nullContext||{},{name:"URL",hash:{},data:o,loc:{start:{line:9,column:82},end:{line:9,column:91}}}):a)?a:"")+'" '},14:function(l,n,e,t,o){var a=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return null!=(a="function"==typeof(a=null!=(a=a(e,"themeAttrs")||(null!=n?a(n,"themeAttrs"):n))?a:l.hooks.helperMissing)?a.call(null!=n?n:l.nullContext||{},{name:"themeAttrs",hash:{},data:o,loc:{start:{line:9,column:118},end:{line:9,column:134}}}):a)?a:""},16:function(l,n,e,t,o){return" }"},18:function(l,n,e,t,o,a,r){var u,i,c=null!=n?n:l.nullContext||{},s=l.hooks.helperMissing,p="function",m=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return' "'+(null!=(u=typeof(i=null!=(i=m(e,"source")||(null!=n?m(n,"source"):n))?i:s)==p?i.call(c,{name:"source",hash:{},data:o,loc:{start:{line:11,column:5},end:{line:11,column:17}}}):i)?u:"")+'" [label='+(null!=(u=typeof(i=null!=(i=m(e,"label")||(null!=n?m(n,"label"):n))?i:s)==p?i.call(c,{name:"label",hash:{},data:o,loc:{start:{line:11,column:26},end:{line:11,column:37}}}):i)?u:"")+' tooltip="'+(null!=(u=typeof(i=null!=(i=m(e,"tooltip")||(null!=n?m(n,"tooltip"):n))?i:s)==p?i.call(c,{name:"tooltip",hash:{},data:o,loc:{start:{line:11,column:47},end:{line:11,column:60}}}):i)?u:"")+'" '+(null!=(u=m(e,"if").call(c,null!=n?m(n,"URL"):n,{name:"if",hash:{},fn:l.program(12,o,0,a,r),inverse:l.noop,data:o,loc:{start:{line:11,column:62},end:{line:11,column:96}}}))?u:"")+(null!=(u=m(e,"if").call(c,null!=n?m(n,"themeAttrs"):n,{name:"if",hash:{},fn:l.program(14,o,0,a,r),inverse:l.noop,data:o,loc:{start:{line:11,column:96},end:{line:11,column:137}}}))?u:"")+"]\n"},20:function(l,n,e,t,o,a,r){var u,i,c=null!=n?n:l.nullContext||{},s=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return' "'+(null!=(u=l.lambda(null!=r[1]?s(r[1],"source"):r[1],n))?u:"")+'" -> "'+(null!=(u="function"==typeof(i=null!=(i=s(e,"resolved")||(null!=n?s(n,"resolved"):n))?i:l.hooks.helperMissing)?i.call(c,{name:"resolved",hash:{},data:o,loc:{start:{line:14,column:26},end:{line:14,column:40}}}):i)?u:"")+'"'+(null!=(u=s(e,"if").call(c,null!=n?s(n,"hasExtraAttributes"):n,{name:"if",hash:{},fn:l.program(21,o,0,a,r),inverse:l.noop,data:o,loc:{start:{line:14,column:41},end:{line:14,column:193}}}))?u:"")+"\n"},21:function(l,n,e,t,o,a,r){var u,i=null!=n?n:l.nullContext||{},c=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return" ["+(null!=(u=c(e,"if").call(i,null!=(u=null!=n?c(n,"rule"):n)?c(u,"name"):u,{name:"if",hash:{},fn:l.program(22,o,0,a,r),inverse:l.noop,data:o,loc:{start:{line:14,column:69},end:{line:14,column:144}}}))?u:"")+(null!=(u=c(e,"if").call(i,null!=n?c(n,"themeAttrs"):n,{name:"if",hash:{},fn:l.program(14,o,0,a,r),inverse:l.noop,data:o,loc:{start:{line:14,column:144},end:{line:14,column:185}}}))?u:"")+"]"},22:function(l,n,e,t,o){var a=l.lambda,r=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]};return'xlabel="'+(null!=(l=a(null!=(l=null!=n?r(n,"rule"):n)?r(l,"name"):l,n))?l:"")+'" tooltip="'+(null!=(l=a(null!=(l=null!=n?r(n,"rule"):n)?r(l,"name"):l,n))?l:"")+'" '},compiler:[8,">= 4.3.0"],main:function(l,n,e,t,o,a,r){var u=null!=n?n:l.nullContext||{},i=l.lookupProperty||function(l,n){if(Object.prototype.hasOwnProperty.call(l,n))return l[n]},c='strict digraph "dependency-cruiser output"{\n '+(null!=(p=i(e,"if").call(u,null!=n?i(n,"graphAttrs"):n,{name:"if",hash:{},fn:l.program(1,o,0,a,r),inverse:l.noop,data:o,loc:{start:{line:2,column:4},end:{line:2,column:45}}}))?p:"")+"\n "+(null!=(p=i(e,"if").call(u,null!=n?i(n,"nodeAttrs"):n,{name:"if",hash:{},fn:l.program(3,o,0,a,r),inverse:l.noop,data:o,loc:{start:{line:3,column:4},end:{line:3,column:50}}}))?p:"")+"\n "+(null!=(p=i(e,"if").call(u,null!=n?i(n,"edgeAttrs"):n,{name:"if",hash:{},fn:l.program(5,o,0,a,r),inverse:l.noop,data:o,loc:{start:{line:4,column:4},end:{line:4,column:50}}}))?p:"")+"\n\n",s=null!=(s=i(e,"modules")||(null!=n?i(n,"modules"):n))?s:l.hooks.helperMissing,o={name:"modules",hash:{},fn:l.program(7,o,0,a,r),inverse:l.noop,data:o,loc:{start:{line:6,column:0},end:{line:16,column:12}}},p="function"==typeof s?s.call(u,o):s;return null!=(p=!i(e,"modules")?l.hooks.blockHelperMissing.call(n,p,o):p)&&(c+=p),c+"}\n"},useData:!0,useDepths:!0});
|
package/src/report/dot/index.js
CHANGED
|
@@ -17,7 +17,11 @@ const GRANULARITY2FUNCTION = {
|
|
|
17
17
|
flat: prepareFlatLevel,
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
function report(
|
|
20
|
+
function report(
|
|
21
|
+
pResults,
|
|
22
|
+
pGranularity,
|
|
23
|
+
{ theme, collapsePattern, filters, showMetrics }
|
|
24
|
+
) {
|
|
21
25
|
const lTheme = theming.normalizeTheme(theme);
|
|
22
26
|
const lResults = filters
|
|
23
27
|
? {
|
|
@@ -35,7 +39,8 @@ function report(pResults, pGranularity, { theme, collapsePattern, filters }) {
|
|
|
35
39
|
modules: (GRANULARITY2FUNCTION[pGranularity] || prepareCustomLevel)(
|
|
36
40
|
lResults,
|
|
37
41
|
lTheme,
|
|
38
|
-
collapsePattern
|
|
42
|
+
collapsePattern,
|
|
43
|
+
showMetrics
|
|
39
44
|
),
|
|
40
45
|
});
|
|
41
46
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
const path = require("path").posix;
|
|
2
|
+
const _has = require("lodash/has");
|
|
2
3
|
const theming = require("./theming");
|
|
3
4
|
|
|
5
|
+
const PROTOCOL_PREFIX_RE = /^[a-z]+:\/\//;
|
|
6
|
+
|
|
4
7
|
function attributizeObject(pObject) {
|
|
5
8
|
return (
|
|
6
9
|
Object.keys(pObject)
|
|
@@ -12,7 +15,9 @@ function attributizeObject(pObject) {
|
|
|
12
15
|
|
|
13
16
|
function extractFirstTransgression(pModule) {
|
|
14
17
|
return {
|
|
15
|
-
...(pModule
|
|
18
|
+
...(_has(pModule, "rules[0]")
|
|
19
|
+
? { ...pModule, tooltip: pModule.rules[0].name }
|
|
20
|
+
: pModule),
|
|
16
21
|
dependencies: pModule.dependencies.map((pDependency) =>
|
|
17
22
|
pDependency.rules
|
|
18
23
|
? {
|
|
@@ -57,20 +62,37 @@ function aggregate(pPathSnippet, pCounter, pPathArray) {
|
|
|
57
62
|
};
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
function
|
|
61
|
-
let
|
|
62
|
-
let lDirectoryName = path.dirname(pModule.source);
|
|
65
|
+
function makeInstabilityString(pModule, pShowMetrics = false) {
|
|
66
|
+
let lInstabilityString = "";
|
|
63
67
|
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
|
|
68
|
+
if (pShowMetrics && _has(pModule, "instability") && !pModule.consolidated) {
|
|
69
|
+
lInstabilityString = ` <FONT color="#808080" point-size="8">${Math.round(
|
|
70
|
+
// eslint-disable-next-line no-magic-numbers
|
|
71
|
+
100 * pModule.instability
|
|
72
|
+
)}</FONT>`;
|
|
67
73
|
}
|
|
74
|
+
return lInstabilityString;
|
|
75
|
+
}
|
|
68
76
|
|
|
69
|
-
|
|
77
|
+
function folderify(pShowMetrics) {
|
|
78
|
+
return (pModule) => {
|
|
79
|
+
let lAdditions = {};
|
|
80
|
+
let lDirectoryName = path.dirname(pModule.source);
|
|
70
81
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
82
|
+
if (lDirectoryName !== ".") {
|
|
83
|
+
lAdditions.folder = lDirectoryName;
|
|
84
|
+
lAdditions.path = lDirectoryName.split(path.sep).map(aggregate);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
lAdditions.label = `<${path.basename(
|
|
88
|
+
pModule.source
|
|
89
|
+
)}${makeInstabilityString(pModule, pShowMetrics)}>`;
|
|
90
|
+
lAdditions.tooltip = path.basename(pModule.source);
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
...pModule,
|
|
94
|
+
...lAdditions,
|
|
95
|
+
};
|
|
74
96
|
};
|
|
75
97
|
}
|
|
76
98
|
|
|
@@ -87,7 +109,7 @@ function folderify(pModule) {
|
|
|
87
109
|
* @return {string} prefix and filename concatenated
|
|
88
110
|
*/
|
|
89
111
|
function smartURIConcat(pPrefix, pSource) {
|
|
90
|
-
if (pPrefix.match(
|
|
112
|
+
if (pPrefix.match(PROTOCOL_PREFIX_RE)) {
|
|
91
113
|
return `${pPrefix}${pSource}`;
|
|
92
114
|
} else {
|
|
93
115
|
return path.join(pPrefix, pSource);
|
|
@@ -103,15 +125,21 @@ function addURL(pPrefix) {
|
|
|
103
125
|
});
|
|
104
126
|
}
|
|
105
127
|
|
|
106
|
-
function
|
|
107
|
-
return {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
pModule.source
|
|
111
|
-
)}</B>>`,
|
|
112
|
-
};
|
|
128
|
+
function makeLabel(pModule, pShowMetrics) {
|
|
129
|
+
return `<${path.dirname(pModule.source)}/<BR/><B>${path.basename(
|
|
130
|
+
pModule.source
|
|
131
|
+
)}</B>${makeInstabilityString(pModule, pShowMetrics)}>`;
|
|
113
132
|
}
|
|
114
133
|
|
|
134
|
+
function flatLabel(pShowMetrics) {
|
|
135
|
+
return (pModule) => {
|
|
136
|
+
return {
|
|
137
|
+
...pModule,
|
|
138
|
+
label: makeLabel(pModule, pShowMetrics),
|
|
139
|
+
tooltip: path.basename(pModule.source),
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
}
|
|
115
143
|
module.exports = {
|
|
116
144
|
folderify,
|
|
117
145
|
applyTheme,
|
|
@@ -4,15 +4,15 @@ const compare = require("../../graph-utl/compare");
|
|
|
4
4
|
const stripSelfTransitions = require("../../graph-utl/strip-self-transitions");
|
|
5
5
|
const moduleUtl = require("./module-utl");
|
|
6
6
|
|
|
7
|
-
module.exports = (pResults, pTheme, pCollapsePattern) => {
|
|
7
|
+
module.exports = (pResults, pTheme, pCollapsePattern, pShowMetrics) => {
|
|
8
8
|
return (
|
|
9
9
|
pCollapsePattern
|
|
10
10
|
? consolidateToPattern(pResults.modules, pCollapsePattern)
|
|
11
11
|
: pResults.modules
|
|
12
12
|
)
|
|
13
13
|
.sort(compare.modules)
|
|
14
|
+
.map(moduleUtl.folderify(pShowMetrics))
|
|
14
15
|
.map(moduleUtl.extractFirstTransgression)
|
|
15
|
-
.map(moduleUtl.folderify)
|
|
16
16
|
.map(stripSelfTransitions)
|
|
17
17
|
.map(moduleUtl.applyTheme(pTheme))
|
|
18
18
|
.map(moduleUtl.addURL(_get(pResults, "summary.optionsUsed.prefix", "")));
|
|
@@ -2,10 +2,10 @@ const _get = require("lodash/get");
|
|
|
2
2
|
const compare = require("../../graph-utl/compare");
|
|
3
3
|
const moduleUtl = require("./module-utl");
|
|
4
4
|
|
|
5
|
-
module.exports = (pResults, pTheme) => {
|
|
5
|
+
module.exports = (pResults, pTheme, _, pShowMetrics) => {
|
|
6
6
|
return pResults.modules
|
|
7
7
|
.sort(compare.modules)
|
|
8
|
-
.map(moduleUtl.flatLabel)
|
|
8
|
+
.map(moduleUtl.flatLabel(pShowMetrics))
|
|
9
9
|
.map(moduleUtl.extractFirstTransgression)
|
|
10
10
|
.map(moduleUtl.applyTheme(pTheme))
|
|
11
11
|
.map(moduleUtl.addURL(_get(pResults, "summary.optionsUsed.prefix", "")));
|
|
@@ -4,11 +4,11 @@ const compare = require("../../graph-utl/compare");
|
|
|
4
4
|
const stripSelfTransitions = require("../../graph-utl/strip-self-transitions");
|
|
5
5
|
const moduleUtl = require("./module-utl");
|
|
6
6
|
|
|
7
|
-
module.exports = (pResults, pTheme) => {
|
|
7
|
+
module.exports = (pResults, pTheme, _, pShowMetrics) => {
|
|
8
8
|
return consolidateToFolder(pResults.modules)
|
|
9
9
|
.sort(compare.modules)
|
|
10
10
|
.map(moduleUtl.extractFirstTransgression)
|
|
11
|
-
.map(moduleUtl.folderify)
|
|
11
|
+
.map(moduleUtl.folderify(pShowMetrics))
|
|
12
12
|
.map(stripSelfTransitions)
|
|
13
13
|
.map(moduleUtl.applyTheme(pTheme))
|
|
14
14
|
.map(moduleUtl.addURL(_get(pResults, "summary.optionsUsed.prefix", "")));
|
package/src/report/metrics.js
CHANGED
|
@@ -1,62 +1,110 @@
|
|
|
1
|
-
const
|
|
1
|
+
const { EOL } = require("os");
|
|
2
2
|
const chalk = require("chalk");
|
|
3
3
|
|
|
4
4
|
const DECIMAL_BASE = 10;
|
|
5
5
|
const METRIC_WIDTH = 4;
|
|
6
6
|
const INSTABILITY_DECIMALS = 2;
|
|
7
7
|
const YADDUM = DECIMAL_BASE ** INSTABILITY_DECIMALS;
|
|
8
|
+
const COMPONENT_HEADER = "name";
|
|
8
9
|
|
|
9
|
-
function
|
|
10
|
+
function getHeader(pMaxNameWidth) {
|
|
11
|
+
return `${COMPONENT_HEADER.padEnd(pMaxNameWidth)} ${"N".padStart(
|
|
12
|
+
METRIC_WIDTH + 1
|
|
13
|
+
)} ${"Ca".padStart(METRIC_WIDTH + 1)} ${"Ce".padStart(
|
|
14
|
+
METRIC_WIDTH + 1
|
|
15
|
+
)} ${"I".padEnd(METRIC_WIDTH + 1)}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getDemarcationLine(pMaxNameWidth) {
|
|
19
|
+
return `${"-".repeat(pMaxNameWidth)} ${"-".repeat(
|
|
20
|
+
METRIC_WIDTH + 1
|
|
21
|
+
)} ${"-".repeat(METRIC_WIDTH + 1)} ${"-".repeat(
|
|
22
|
+
METRIC_WIDTH + 1
|
|
23
|
+
)} ${"-".repeat(METRIC_WIDTH + 1)}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function getMetricsTable(pMetrics, pMaxNameWidth) {
|
|
27
|
+
return pMetrics.map(
|
|
28
|
+
({
|
|
29
|
+
name,
|
|
30
|
+
moduleCount,
|
|
31
|
+
afferentCouplings,
|
|
32
|
+
efferentCouplings,
|
|
33
|
+
instability,
|
|
34
|
+
}) =>
|
|
35
|
+
`${name.padEnd(pMaxNameWidth, " ")} ${moduleCount
|
|
36
|
+
.toString(DECIMAL_BASE)
|
|
37
|
+
.padStart(METRIC_WIDTH)} ${afferentCouplings
|
|
38
|
+
.toString(DECIMAL_BASE)
|
|
39
|
+
.padStart(METRIC_WIDTH)} ${efferentCouplings
|
|
40
|
+
.toString(DECIMAL_BASE)
|
|
41
|
+
.padStart(METRIC_WIDTH)} ${(Math.round(YADDUM * instability) / YADDUM)
|
|
42
|
+
.toString(DECIMAL_BASE)
|
|
43
|
+
.padEnd(METRIC_WIDTH)}`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function metricifyModule({ source, dependents, dependencies, instability }) {
|
|
48
|
+
return {
|
|
49
|
+
name: source,
|
|
50
|
+
moduleCount: 1,
|
|
51
|
+
afferentCouplings: dependents.length,
|
|
52
|
+
efferentCouplings: dependencies.length,
|
|
53
|
+
instability,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function metricsAreCalculable(pModule) {
|
|
58
|
+
return Object.prototype.hasOwnProperty.call(pModule, "instability");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function orderByNumber(pAttributeName) {
|
|
62
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
63
|
+
return (pLeft, pRight) => pRight[pAttributeName] - pLeft[pAttributeName];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function orderByString(pAttributeName) {
|
|
67
|
+
return (pLeft, pRight) =>
|
|
68
|
+
// eslint-disable-next-line security/detect-object-injection
|
|
69
|
+
pLeft[pAttributeName].localeCompare(pRight[pAttributeName]);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function transformMetricsToTable(
|
|
73
|
+
{ modules, folders },
|
|
74
|
+
{ orderBy, hideFolders, hideModules }
|
|
75
|
+
) {
|
|
10
76
|
// TODO: should probably use a table module for this (i.e. text-table)
|
|
11
|
-
// to simplify this code
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
77
|
+
// to simplify this code
|
|
78
|
+
let lComponents = [];
|
|
79
|
+
lComponents = lComponents.concat(hideFolders ? [] : folders);
|
|
80
|
+
lComponents = lComponents.concat(
|
|
81
|
+
hideModules ? [] : modules.filter(metricsAreCalculable).map(metricifyModule)
|
|
82
|
+
);
|
|
83
|
+
const lMaxNameWidth = lComponents
|
|
84
|
+
.map(({ name }) => name.length)
|
|
85
|
+
.concat(COMPONENT_HEADER.length)
|
|
15
86
|
.sort((pLeft, pRight) => pLeft - pRight)
|
|
16
87
|
.pop();
|
|
17
88
|
|
|
18
|
-
return [
|
|
19
|
-
|
|
20
|
-
`${"folder".padEnd(lMaxNameWidth)} ${"N".padStart(
|
|
21
|
-
METRIC_WIDTH + 1
|
|
22
|
-
)} ${"Ca".padStart(METRIC_WIDTH + 1)} ${"Ce".padStart(
|
|
23
|
-
METRIC_WIDTH + 1
|
|
24
|
-
)} ${"I".padEnd(METRIC_WIDTH + 1)}`
|
|
25
|
-
),
|
|
26
|
-
]
|
|
27
|
-
.concat(
|
|
28
|
-
`${"-".repeat(lMaxNameWidth)} ${"-".repeat(
|
|
29
|
-
METRIC_WIDTH + 1
|
|
30
|
-
)} ${"-".repeat(METRIC_WIDTH + 1)} ${"-".repeat(
|
|
31
|
-
METRIC_WIDTH + 1
|
|
32
|
-
)} ${"-".repeat(METRIC_WIDTH + 1)}`
|
|
33
|
-
)
|
|
89
|
+
return [chalk.bold(getHeader(lMaxNameWidth))]
|
|
90
|
+
.concat(getDemarcationLine(lMaxNameWidth))
|
|
34
91
|
.concat(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
.padStart(METRIC_WIDTH)} ${pMetric.afferentCouplings
|
|
42
|
-
.toString(DECIMAL_BASE)
|
|
43
|
-
.padStart(METRIC_WIDTH)} ${pMetric.efferentCouplings
|
|
44
|
-
.toString(DECIMAL_BASE)
|
|
45
|
-
.padStart(METRIC_WIDTH)} ${(
|
|
46
|
-
Math.round(YADDUM * pMetric.instability) / YADDUM
|
|
47
|
-
)
|
|
48
|
-
.toString(DECIMAL_BASE)
|
|
49
|
-
.padEnd(METRIC_WIDTH)}`;
|
|
50
|
-
})
|
|
92
|
+
getMetricsTable(
|
|
93
|
+
lComponents
|
|
94
|
+
.sort(orderByString("name"))
|
|
95
|
+
.sort(orderByNumber(orderBy || "instability")),
|
|
96
|
+
lMaxNameWidth
|
|
97
|
+
)
|
|
51
98
|
)
|
|
52
|
-
.join(
|
|
53
|
-
.concat(
|
|
99
|
+
.join(EOL)
|
|
100
|
+
.concat(EOL);
|
|
54
101
|
}
|
|
55
102
|
|
|
56
103
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
104
|
+
* returns stability metrics of modules & folders in an ascii table
|
|
105
|
+
*
|
|
106
|
+
* Potential future features:
|
|
107
|
+
* - additional output formats (csv?, html?)
|
|
60
108
|
*
|
|
61
109
|
* @param {import('../../types/dependency-cruiser').ICruiseResult} pCruiseResult -
|
|
62
110
|
* the output of a dependency-cruise adhering to dependency-cruiser's
|
|
@@ -65,17 +113,18 @@ function transformMetricsToTable(pMetrics) {
|
|
|
65
113
|
* output: some metrics on folders and dependencies
|
|
66
114
|
* exitCode: 0
|
|
67
115
|
*/
|
|
68
|
-
module.exports = (pCruiseResult) => {
|
|
116
|
+
module.exports = (pCruiseResult, pReporterOptions) => {
|
|
117
|
+
const lReporterOptions = pReporterOptions || {};
|
|
69
118
|
if (pCruiseResult.folders) {
|
|
70
119
|
return {
|
|
71
|
-
output: transformMetricsToTable(pCruiseResult
|
|
120
|
+
output: transformMetricsToTable(pCruiseResult, lReporterOptions),
|
|
72
121
|
exitCode: 0,
|
|
73
122
|
};
|
|
74
123
|
} else {
|
|
75
124
|
return {
|
|
76
125
|
output:
|
|
77
|
-
`${
|
|
78
|
-
` the '--metrics' command line option should fix that.${
|
|
126
|
+
`${EOL}ERROR: The cruise result didn't contain any metrics - re-running the cruise with${EOL}` +
|
|
127
|
+
` the '--metrics' command line option should fix that.${EOL}${EOL}`,
|
|
79
128
|
exitCode: 1,
|
|
80
129
|
};
|
|
81
130
|
}
|
|
@@ -168,6 +168,7 @@ module.exports = {
|
|
|
168
168
|
licenseNot: { $ref: "#/definitions/REAsStringsType" },
|
|
169
169
|
via: { $ref: "#/definitions/REAsStringsType" },
|
|
170
170
|
viaNot: { $ref: "#/definitions/REAsStringsType" },
|
|
171
|
+
moreUnstable: { type: "boolean" },
|
|
171
172
|
},
|
|
172
173
|
},
|
|
173
174
|
DependentsModuleRestrictionType: {
|
|
@@ -348,6 +349,7 @@ module.exports = {
|
|
|
348
349
|
},
|
|
349
350
|
},
|
|
350
351
|
},
|
|
352
|
+
metrics: { type: "boolean" },
|
|
351
353
|
baseDir: { type: "string" },
|
|
352
354
|
},
|
|
353
355
|
},
|
|
@@ -394,6 +396,7 @@ module.exports = {
|
|
|
394
396
|
dot: { $ref: "#/definitions/DotReporterOptionsType" },
|
|
395
397
|
ddot: { $ref: "#/definitions/DotReporterOptionsType" },
|
|
396
398
|
flat: { $ref: "#/definitions/DotReporterOptionsType" },
|
|
399
|
+
metrics: { $ref: "#/definitions/MetricsReporterOptionsType" },
|
|
397
400
|
},
|
|
398
401
|
},
|
|
399
402
|
AnonReporterOptionsType: {
|
|
@@ -401,12 +404,31 @@ module.exports = {
|
|
|
401
404
|
additionalProperties: false,
|
|
402
405
|
properties: { wordlist: { type: "array", items: { type: "string" } } },
|
|
403
406
|
},
|
|
407
|
+
MetricsReporterOptionsType: {
|
|
408
|
+
type: "object",
|
|
409
|
+
additionalProperties: false,
|
|
410
|
+
properties: {
|
|
411
|
+
orderBy: {
|
|
412
|
+
type: "string",
|
|
413
|
+
enum: [
|
|
414
|
+
"instability",
|
|
415
|
+
"moduleCount",
|
|
416
|
+
"afferentCouplings",
|
|
417
|
+
"efferentCouplings",
|
|
418
|
+
"name",
|
|
419
|
+
],
|
|
420
|
+
},
|
|
421
|
+
hideModules: { type: "boolean" },
|
|
422
|
+
hideFolders: { type: "boolean" },
|
|
423
|
+
},
|
|
424
|
+
},
|
|
404
425
|
DotReporterOptionsType: {
|
|
405
426
|
type: "object",
|
|
406
427
|
additionalProperties: false,
|
|
407
428
|
properties: {
|
|
408
429
|
collapsePattern: { $ref: "#/definitions/REAsStringsType" },
|
|
409
430
|
filters: { $ref: "#/definitions/ReporterFiltersType" },
|
|
431
|
+
showMetrics: { type: "boolean" },
|
|
410
432
|
theme: { $ref: "#/definitions/DotThemeType" },
|
|
411
433
|
},
|
|
412
434
|
},
|
|
@@ -47,6 +47,7 @@ module.exports = {
|
|
|
47
47
|
items: { $ref: "#/definitions/RuleSummaryType" },
|
|
48
48
|
},
|
|
49
49
|
consolidated: { type: "boolean" },
|
|
50
|
+
instability: { type: "number" },
|
|
50
51
|
},
|
|
51
52
|
},
|
|
52
53
|
ReachableType: {
|
|
@@ -126,6 +127,7 @@ module.exports = {
|
|
|
126
127
|
type: "array",
|
|
127
128
|
items: { $ref: "#/definitions/RuleSummaryType" },
|
|
128
129
|
},
|
|
130
|
+
instability: { type: "number" },
|
|
129
131
|
},
|
|
130
132
|
},
|
|
131
133
|
DependencyTypeType: {
|
|
@@ -356,6 +358,7 @@ module.exports = {
|
|
|
356
358
|
licenseNot: { $ref: "#/definitions/REAsStringsType" },
|
|
357
359
|
via: { $ref: "#/definitions/REAsStringsType" },
|
|
358
360
|
viaNot: { $ref: "#/definitions/REAsStringsType" },
|
|
361
|
+
moreUnstable: { type: "boolean" },
|
|
359
362
|
},
|
|
360
363
|
},
|
|
361
364
|
DependentsModuleRestrictionType: {
|
|
@@ -495,6 +498,7 @@ module.exports = {
|
|
|
495
498
|
},
|
|
496
499
|
},
|
|
497
500
|
},
|
|
501
|
+
metrics: { type: "boolean" },
|
|
498
502
|
baseDir: { type: "string" },
|
|
499
503
|
args: { type: "string" },
|
|
500
504
|
rulesFile: { type: "string" },
|
|
@@ -570,6 +574,7 @@ module.exports = {
|
|
|
570
574
|
dot: { $ref: "#/definitions/DotReporterOptionsType" },
|
|
571
575
|
ddot: { $ref: "#/definitions/DotReporterOptionsType" },
|
|
572
576
|
flat: { $ref: "#/definitions/DotReporterOptionsType" },
|
|
577
|
+
metrics: { $ref: "#/definitions/MetricsReporterOptionsType" },
|
|
573
578
|
},
|
|
574
579
|
},
|
|
575
580
|
AnonReporterOptionsType: {
|
|
@@ -577,12 +582,31 @@ module.exports = {
|
|
|
577
582
|
additionalProperties: false,
|
|
578
583
|
properties: { wordlist: { type: "array", items: { type: "string" } } },
|
|
579
584
|
},
|
|
585
|
+
MetricsReporterOptionsType: {
|
|
586
|
+
type: "object",
|
|
587
|
+
additionalProperties: false,
|
|
588
|
+
properties: {
|
|
589
|
+
orderBy: {
|
|
590
|
+
type: "string",
|
|
591
|
+
enum: [
|
|
592
|
+
"instability",
|
|
593
|
+
"moduleCount",
|
|
594
|
+
"afferentCouplings",
|
|
595
|
+
"efferentCouplings",
|
|
596
|
+
"name",
|
|
597
|
+
],
|
|
598
|
+
},
|
|
599
|
+
hideModules: { type: "boolean" },
|
|
600
|
+
hideFolders: { type: "boolean" },
|
|
601
|
+
},
|
|
602
|
+
},
|
|
580
603
|
DotReporterOptionsType: {
|
|
581
604
|
type: "object",
|
|
582
605
|
additionalProperties: false,
|
|
583
606
|
properties: {
|
|
584
607
|
collapsePattern: { $ref: "#/definitions/REAsStringsType" },
|
|
585
608
|
filters: { $ref: "#/definitions/ReporterFiltersType" },
|
|
609
|
+
showMetrics: { type: "boolean" },
|
|
586
610
|
theme: { $ref: "#/definitions/DotThemeType" },
|
|
587
611
|
},
|
|
588
612
|
},
|
|
@@ -40,6 +40,7 @@ function match(pFrom, pTo) {
|
|
|
40
40
|
matchers.toExoticRequireNot(pRule, pTo) &&
|
|
41
41
|
matchers.toVia(pRule, pTo) &&
|
|
42
42
|
matchers.toViaNot(pRule, pTo) &&
|
|
43
|
+
matchers.toIsMoreUnstable(pRule, pFrom, pTo) &&
|
|
43
44
|
// preCompilationOnly is not a mandatory attribute, but if the attribute
|
|
44
45
|
// is in the rule but not in the dependency there won't be a match
|
|
45
46
|
// anyway, so we can use the default propertyEquals method regardless
|
package/src/validate/matchers.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const _has = require("lodash/has");
|
|
1
2
|
const { intersects } = require("../utl/array-util");
|
|
2
3
|
|
|
3
4
|
function fromPath(pRule, pModule) {
|
|
@@ -123,6 +124,16 @@ function toVia(pRule, pDependency) {
|
|
|
123
124
|
);
|
|
124
125
|
}
|
|
125
126
|
|
|
127
|
+
function toIsMoreUnstable(pRule, pFrom, pTo) {
|
|
128
|
+
if (_has(pRule, "to.moreUnstable")) {
|
|
129
|
+
return (
|
|
130
|
+
(pRule.to.moreUnstable && pFrom.instability < pTo.instability) ||
|
|
131
|
+
(!pRule.to.moreUnstable && pFrom.instability >= pTo.instability)
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
126
137
|
module.exports = {
|
|
127
138
|
_replaceGroupPlaceholders,
|
|
128
139
|
fromPath,
|
|
@@ -141,4 +152,5 @@ module.exports = {
|
|
|
141
152
|
toExoticRequireNot,
|
|
142
153
|
toVia,
|
|
143
154
|
toViaNot,
|
|
155
|
+
toIsMoreUnstable,
|
|
144
156
|
};
|
package/types/cruise-result.d.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { ICruiseOptions } from "./options";
|
|
2
2
|
import { IFlattenedRuleSet } from "./rule-set";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
SeverityType,
|
|
7
|
-
ProtocolType,
|
|
8
|
-
} from "./shared-types";
|
|
3
|
+
import { DependencyType, ModuleSystemType, ProtocolType } from "./shared-types";
|
|
4
|
+
import { IViolation } from "./violations";
|
|
5
|
+
import { IRuleSummary } from "./rule-summary";
|
|
9
6
|
|
|
10
7
|
export interface ICruiseResult {
|
|
11
8
|
/**
|
|
@@ -106,6 +103,15 @@ export interface IModule {
|
|
|
106
103
|
* purposes - it will not be present after a regular cruise.
|
|
107
104
|
*/
|
|
108
105
|
consolidated?: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* "number of dependents/ (number of dependents + number of dependencies)
|
|
108
|
+
* A measure for how stable the module is; ranging between 0 (completely
|
|
109
|
+
* stable module) to 1 (completely instable module). Derived from Uncle
|
|
110
|
+
* Bob's instability metric - but applied to a single module instead of
|
|
111
|
+
* to a group of them. This attribute is only present when dependency-cruiser
|
|
112
|
+
* was asked to calculate metrics.
|
|
113
|
+
*/
|
|
114
|
+
instability?: number;
|
|
109
115
|
}
|
|
110
116
|
|
|
111
117
|
export interface IDependency {
|
|
@@ -215,27 +221,11 @@ export interface IDependency {
|
|
|
215
221
|
* will be in the 'rule' object at the same level.
|
|
216
222
|
*/
|
|
217
223
|
valid: boolean;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* If there was a rule violation (valid === false), this object contains the name of the
|
|
222
|
-
* rule and severity of violating it.
|
|
223
|
-
*/
|
|
224
|
-
export interface IRuleSummary {
|
|
225
224
|
/**
|
|
226
|
-
*
|
|
227
|
-
* '
|
|
225
|
+
* the (de-normalized) instability of the dependency - also available in
|
|
226
|
+
* the module on the 'to' side of this dependency
|
|
228
227
|
*/
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* How severe a violation of a rule is. The 'error' severity will make some reporters return
|
|
232
|
-
* a non-zero exit code, so if you want e.g. a build to stop when there's a rule violated:
|
|
233
|
-
* use that. The absence of the 'ignore' severity here is by design; ignored rules don't
|
|
234
|
-
* show up in the output.
|
|
235
|
-
*
|
|
236
|
-
* Severity to use when a dependency is not in the 'allowed' set of rules. Defaults to 'warn'
|
|
237
|
-
*/
|
|
238
|
-
severity: SeverityType;
|
|
228
|
+
instability: number;
|
|
239
229
|
}
|
|
240
230
|
|
|
241
231
|
export interface IReachable {
|
|
@@ -380,29 +370,6 @@ export interface IWebpackConfig {
|
|
|
380
370
|
*/
|
|
381
371
|
export type WebpackEnvType = { [key: string]: any } | string;
|
|
382
372
|
|
|
383
|
-
export interface IViolation {
|
|
384
|
-
/**
|
|
385
|
-
* The violated rule
|
|
386
|
-
*/
|
|
387
|
-
rule: IRuleSummary;
|
|
388
|
-
/**
|
|
389
|
-
* The from part of the dependency this violation is about
|
|
390
|
-
*/
|
|
391
|
-
from: string;
|
|
392
|
-
/**
|
|
393
|
-
* The to part of the dependency this violation is about
|
|
394
|
-
*/
|
|
395
|
-
to: string;
|
|
396
|
-
/**
|
|
397
|
-
* The circular path if the violation is about circularity
|
|
398
|
-
*/
|
|
399
|
-
cycle?: string[];
|
|
400
|
-
/**
|
|
401
|
-
* The path from the from to the to if the violation is transitive
|
|
402
|
-
*/
|
|
403
|
-
via?: string[];
|
|
404
|
-
}
|
|
405
|
-
|
|
406
373
|
export interface IFolder {
|
|
407
374
|
/**
|
|
408
375
|
* The name of the folder. FOlder names are normalized to posix (so
|
|
@@ -446,3 +413,6 @@ export interface IFolder {
|
|
|
446
413
|
*/
|
|
447
414
|
instability?: number;
|
|
448
415
|
}
|
|
416
|
+
|
|
417
|
+
export * from "./violations";
|
|
418
|
+
export * from "./rule-summary";
|
package/types/options.d.ts
CHANGED
|
@@ -239,4 +239,9 @@ export interface ICruiseOptions {
|
|
|
239
239
|
*/
|
|
240
240
|
type: ProgressType;
|
|
241
241
|
};
|
|
242
|
+
/**
|
|
243
|
+
* When this flag is set to true, dependency-cruiser will calculate (stability) metrics
|
|
244
|
+
* for all modules and folders. Defaults to false.
|
|
245
|
+
*/
|
|
246
|
+
metrics?: boolean;
|
|
242
247
|
}
|
|
@@ -21,6 +21,10 @@ export interface IReporterOptions {
|
|
|
21
21
|
* Options to tweak the output of the flat /fdot reporter
|
|
22
22
|
*/
|
|
23
23
|
flat?: IDotReporterOptions;
|
|
24
|
+
/**
|
|
25
|
+
* Options to tweak the output of the metrics reporter
|
|
26
|
+
*/
|
|
27
|
+
metrics?: IMetricsReporterOptions;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
export interface IReporterFiltersType {
|
|
@@ -54,6 +58,12 @@ export interface IDotReporterOptions {
|
|
|
54
58
|
* goal of the report)
|
|
55
59
|
*/
|
|
56
60
|
filters?: IReporterFiltersType;
|
|
61
|
+
/**
|
|
62
|
+
* When passed the value 'true', shows instability metrics in the
|
|
63
|
+
* output if dependency-cruiser calculated them. Doesn't show them
|
|
64
|
+
* in all other cases. Defaults to false",
|
|
65
|
+
*/
|
|
66
|
+
showMetrics?: boolean;
|
|
57
67
|
/**
|
|
58
68
|
* A bunch of criteria to (conditionally) theme the dot output
|
|
59
69
|
*/
|
|
@@ -96,3 +106,16 @@ export interface IDotThemeEntry {
|
|
|
96
106
|
criteria: any;
|
|
97
107
|
attributes: any;
|
|
98
108
|
}
|
|
109
|
+
|
|
110
|
+
export interface IMetricsReporterOptions {
|
|
111
|
+
hideModules?: boolean;
|
|
112
|
+
hideFolders?: boolean;
|
|
113
|
+
oderBy?: MetricsOrderByType;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export type MetricsOrderByType =
|
|
117
|
+
| "instability"
|
|
118
|
+
| "moduleCount"
|
|
119
|
+
| "afferentCouplings"
|
|
120
|
+
| "efferentCouplings"
|
|
121
|
+
| "name";
|
package/types/restrictions.d.ts
CHANGED
|
@@ -92,6 +92,18 @@ export interface IToRestriction extends IBaseRestrictionType {
|
|
|
92
92
|
* licenses. E.g. to flag everyting non MIT use "MIT" here
|
|
93
93
|
*/
|
|
94
94
|
licenseNot?: string | string[];
|
|
95
|
+
/**
|
|
96
|
+
* When set to true moreUnstable matches for any dependency that has a higher
|
|
97
|
+
* Instability than the module that depends on it. When set to false it matches
|
|
98
|
+
* when the opposite is true; the dependency has an equal or lower Instability.
|
|
99
|
+
*
|
|
100
|
+
* This attribute is useful when you want to check against Robert C. Martin's
|
|
101
|
+
* stable dependency * principle. See online documentation for examples and
|
|
102
|
+
* details.
|
|
103
|
+
*
|
|
104
|
+
* Leave this out when you don't care either way.
|
|
105
|
+
*/
|
|
106
|
+
moreUnstable?: boolean;
|
|
95
107
|
}
|
|
96
108
|
|
|
97
109
|
export interface IReachabilityToRestrictionType extends IBaseRestrictionType {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SeverityType } from "./shared-types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* If there was a rule violation (valid === false), this object contains the name of the
|
|
5
|
+
* rule and severity of violating it.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface IRuleSummary {
|
|
9
|
+
/**
|
|
10
|
+
* The (short, eslint style) name of the violated rule. Typically something like
|
|
11
|
+
* 'no-core-punycode' or 'no-outside-deps'.
|
|
12
|
+
*/
|
|
13
|
+
name: string;
|
|
14
|
+
/**
|
|
15
|
+
* How severe a violation of a rule is. The 'error' severity will make some reporters return
|
|
16
|
+
* a non-zero exit code, so if you want e.g. a build to stop when there's a rule violated:
|
|
17
|
+
* use that. The absence of the 'ignore' severity here is by design; ignored rules don't
|
|
18
|
+
* show up in the output.
|
|
19
|
+
*
|
|
20
|
+
* Severity to use when a dependency is not in the 'allowed' set of rules. Defaults to 'warn'
|
|
21
|
+
*/
|
|
22
|
+
severity: SeverityType;
|
|
23
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { IRuleSummary } from "./rule-summary";
|
|
2
|
+
|
|
3
|
+
export interface IViolation {
|
|
4
|
+
/**
|
|
5
|
+
* The violated rule
|
|
6
|
+
*/
|
|
7
|
+
rule: IRuleSummary;
|
|
8
|
+
/**
|
|
9
|
+
* The from part of the dependency this violation is about
|
|
10
|
+
*/
|
|
11
|
+
from: string;
|
|
12
|
+
/**
|
|
13
|
+
* The to part of the dependency this violation is about
|
|
14
|
+
*/
|
|
15
|
+
to: string;
|
|
16
|
+
/**
|
|
17
|
+
* The circular path if the violation is about circularity
|
|
18
|
+
*/
|
|
19
|
+
cycle?: string[];
|
|
20
|
+
/**
|
|
21
|
+
* The path from the from to the to if the violation is transitive
|
|
22
|
+
*/
|
|
23
|
+
via?: string[];
|
|
24
|
+
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
const getStabilityMetrics = require("./get-stability-metrics");
|
|
2
|
-
|
|
3
|
-
function shouldDeriveFolders(pOptions) {
|
|
4
|
-
return pOptions.metrics || pOptions.outputType === "metrics";
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
module.exports = function deriveFolders(pModules, pOptions) {
|
|
8
|
-
if (shouldDeriveFolders(pOptions)) {
|
|
9
|
-
return { folders: getStabilityMetrics(pModules) };
|
|
10
|
-
}
|
|
11
|
-
return {};
|
|
12
|
-
};
|