dependency-cruiser 11.0.0 → 11.3.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/LICENSE +1 -1
- package/README.md +1 -1
- package/package.json +42 -32
- package/src/cli/format.js +0 -1
- package/src/enrich/clear-caches.js +1 -1
- package/src/enrich/derive/circular/get-cycle.js +1 -1
- package/src/enrich/derive/dependents/get-dependents.js +1 -1
- package/src/enrich/derive/{metrics/get-stability-metrics.js → folders/aggregate-to-folders.js} +39 -30
- package/src/enrich/derive/folders/index.js +46 -0
- package/src/enrich/derive/folders/utl.js +44 -0
- package/src/enrich/derive/metrics/get-module-metrics.js +39 -0
- package/src/enrich/derive/metrics/index.js +14 -0
- package/src/enrich/derive/{utl.js → module-utl.js} +28 -0
- package/src/enrich/derive/orphan/is-orphan.js +1 -1
- package/src/enrich/derive/reachable/get-path.js +1 -1
- package/src/enrich/derive/reachable/index.js +1 -1
- package/src/enrich/enrich-modules.js +2 -2
- package/src/enrich/index.js +12 -4
- package/src/enrich/summarize/get-stats.js +31 -0
- package/src/enrich/summarize/index.js +25 -6
- package/src/enrich/summarize/summarize-folders.js +38 -0
- package/src/enrich/summarize/summarize-modules.js +25 -32
- package/src/enrich/summarize/summarize-options.js +13 -9
- package/src/extract/ast-extractors/swc-dependency-visitor.js +29 -5
- package/src/extract/transpile/vue-template-wrap.js +13 -1
- package/src/main/options/normalize.js +28 -0
- package/src/main/report-wrap.js +17 -3
- 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/error-html/error-html.template.js +1 -1
- package/src/report/error-html/utl.js +50 -7
- package/src/report/error.js +55 -21
- package/src/report/teamcity.js +37 -11
- package/src/report/utl/index.js +16 -0
- package/src/schema/baseline-violations.schema.js +1 -35
- package/src/schema/configuration.schema.js +1 -493
- package/src/schema/cruise-result.schema.js +1 -642
- package/src/utl/regex-util.js +57 -0
- package/src/validate/index.js +25 -22
- package/src/validate/match-dependency-rule.js +22 -34
- package/src/validate/match-folder-dependency-rule.js +19 -0
- package/src/validate/match-module-rule.js +4 -3
- package/src/validate/matchers.js +58 -48
- package/src/validate/rule-classifiers.js +21 -0
- package/src/validate/violates-required-rule.js +1 -1
- package/types/cruise-result.d.ts +7 -2
- package/types/dependency-cruiser.d.ts +2 -3
- package/types/options.d.ts +37 -0
- package/types/reporter-options.d.ts +6 -0
- package/types/restrictions.d.ts +12 -0
- package/types/rule-set.d.ts +12 -1
- package/types/shared-types.d.ts +9 -0
- package/types/violations.d.ts +19 -0
- package/src/enrich/derive/metrics/folder.js +0 -9
- package/src/enrich/derive/metrics/module-utl.js +0 -27
- package/src/enrich/derive/metrics/module.js +0 -29
- package/src/enrich/derive/metrics/utl.js +0 -28
- package/src/validate/is-module-only-rule.js +0 -16
- package/src/validate/utl.js +0 -31
package/LICENSE
CHANGED
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": "11.
|
|
3
|
+
"version": "11.3.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/",
|
|
@@ -112,8 +113,11 @@
|
|
|
112
113
|
"scm:push:gitlab-mirror:tags": "git push --tags gitlab-mirror",
|
|
113
114
|
"scm:stage": "git add .",
|
|
114
115
|
"test": "mocha --timeout 4000 \"test/**/*.spec.{js,mjs,cjs}\"",
|
|
116
|
+
"test:i": "mocha --timeout 4000 \"test/**/*.spec.{js,mjs,cjs}\" -g \"^\\[[I]\\]\"",
|
|
117
|
+
"test:u": "mocha --timeout 4000 \"test/**/*.spec.{js,mjs,cjs}\" --grep \"^\\[[U]\\]\"",
|
|
118
|
+
"test:e": "mocha --timeout 4000 \"test/**/*.spec.{js,mjs,cjs}\" --grep \"^\\[[E]\\]\"",
|
|
115
119
|
"test:cover": "c8 --check-coverage --statements 99.9 --branches 99.7 --functions 100 --lines 99.9 --exclude \"{bin,configs,doc,docs,coverage,test,tools,webpack.conf.js,tmp*,src/**/*.template.js,src/cli/tools/svg-in-html-snippets/script.snippet.js,src/cli/init-config/get-user-input.js,src/cli/listeners/*/index.js}\" --reporter text-summary --reporter html --reporter json-summary npm test",
|
|
116
|
-
"test:glob": "set -f && test \"`bin/dependency-cruise.js test/extract/
|
|
120
|
+
"test:glob": "set -f && test \"`bin/dependency-cruise.js test/extract/__mocks__/gather-globbing/packages/**/src/**/*.js | grep \"no dependency violations found\"`\" = \"✔ no dependency violations found (6 modules, 0 dependencies cruised)\"",
|
|
117
121
|
"test:yarn-pnp": "npm-run-all test:yarn-pnp:cleanup test:yarn-pnp:pack test:yarn-pnp:copy test:yarn-pnp:install test:yarn-pnp:version test:yarn-pnp:run test:yarn-pnp:test test:yarn-pnp:cleanup",
|
|
118
122
|
"test:yarn-pnp:pack": "npm pack",
|
|
119
123
|
"test:yarn-pnp:copy": "shx cp -r test/integration/yarn-pnp.template test/integration/yarn-pnp.testing-ground",
|
|
@@ -131,12 +135,12 @@
|
|
|
131
135
|
"version": "npm-run-all build depcruise:graph:doc scm:stage"
|
|
132
136
|
},
|
|
133
137
|
"dependencies": {
|
|
134
|
-
"acorn": "8.
|
|
138
|
+
"acorn": "8.7.0",
|
|
135
139
|
"acorn-jsx": "5.3.2",
|
|
136
140
|
"acorn-jsx-walk": "2.0.0",
|
|
137
|
-
"acorn-loose": "8.
|
|
141
|
+
"acorn-loose": "8.3.0",
|
|
138
142
|
"acorn-walk": "8.2.0",
|
|
139
|
-
"ajv": "8.
|
|
143
|
+
"ajv": "8.9.0",
|
|
140
144
|
"chalk": "^4.1.2",
|
|
141
145
|
"commander": "8.3.0",
|
|
142
146
|
"enhanced-resolve": "5.8.3",
|
|
@@ -151,19 +155,19 @@
|
|
|
151
155
|
"safe-regex": "2.1.1",
|
|
152
156
|
"semver": "^7.3.5",
|
|
153
157
|
"semver-try-require": "^5.0.1",
|
|
154
|
-
"teamcity-service-messages": "0.1.
|
|
158
|
+
"teamcity-service-messages": "0.1.12",
|
|
155
159
|
"tsconfig-paths-webpack-plugin": "3.5.2",
|
|
156
160
|
"wrap-ansi": "^7.0.0"
|
|
157
161
|
},
|
|
158
162
|
"devDependencies": {
|
|
159
|
-
"@babel/core": "7.16.
|
|
160
|
-
"@babel/plugin-transform-modules-commonjs": "7.16.
|
|
161
|
-
"@babel/preset-typescript": "7.16.
|
|
162
|
-
"@swc/core": "1.2.
|
|
163
|
-
"@typescript-eslint/eslint-plugin": "5.
|
|
164
|
-
"@typescript-eslint/parser": "5.
|
|
165
|
-
"@vue/compiler-sfc": "3.2.
|
|
166
|
-
"c8": "7.
|
|
163
|
+
"@babel/core": "7.16.12",
|
|
164
|
+
"@babel/plugin-transform-modules-commonjs": "7.16.8",
|
|
165
|
+
"@babel/preset-typescript": "7.16.7",
|
|
166
|
+
"@swc/core": "1.2.133",
|
|
167
|
+
"@typescript-eslint/eslint-plugin": "5.10.0",
|
|
168
|
+
"@typescript-eslint/parser": "5.10.0",
|
|
169
|
+
"@vue/compiler-sfc": "3.2.28",
|
|
170
|
+
"c8": "7.11.0",
|
|
167
171
|
"chai": "4.3.4",
|
|
168
172
|
"chai-json-schema": "1.5.1",
|
|
169
173
|
"coffeescript": "2.6.1",
|
|
@@ -171,23 +175,23 @@
|
|
|
171
175
|
"eslint-config-moving-meadow": "2.0.9",
|
|
172
176
|
"eslint-config-prettier": "8.3.0",
|
|
173
177
|
"eslint-plugin-budapestian": "3.0.1",
|
|
174
|
-
"eslint-plugin-import": "2.25.
|
|
175
|
-
"eslint-plugin-mocha": "9.0.0",
|
|
178
|
+
"eslint-plugin-import": "2.25.4",
|
|
179
|
+
"eslint-plugin-mocha": "^9.0.0",
|
|
176
180
|
"eslint-plugin-node": "11.1.0",
|
|
177
181
|
"eslint-plugin-security": "1.4.0",
|
|
178
|
-
"eslint-plugin-unicorn": "
|
|
182
|
+
"eslint-plugin-unicorn": "40.1.0",
|
|
179
183
|
"husky": "^4.3.8",
|
|
180
184
|
"intercept-stdout": "0.1.2",
|
|
181
|
-
"lint-staged": "12.
|
|
182
|
-
"mocha": "9.1.
|
|
185
|
+
"lint-staged": "12.3.0",
|
|
186
|
+
"mocha": "9.1.4",
|
|
183
187
|
"normalize-newline": "^3.0.0",
|
|
184
188
|
"npm-run-all": "4.1.5",
|
|
185
|
-
"prettier": "2.5.
|
|
189
|
+
"prettier": "2.5.1",
|
|
186
190
|
"proxyquire": "2.1.3",
|
|
187
|
-
"shx": "0.3.
|
|
188
|
-
"svelte": "3.
|
|
191
|
+
"shx": "0.3.4",
|
|
192
|
+
"svelte": "3.46.2",
|
|
189
193
|
"symlink-dir": "5.0.1",
|
|
190
|
-
"typescript": "4.5.
|
|
194
|
+
"typescript": "4.5.5",
|
|
191
195
|
"upem": "^7.0.0",
|
|
192
196
|
"vue-template-compiler": "2.6.14",
|
|
193
197
|
"yarn": "1.22.17"
|
|
@@ -219,6 +223,11 @@
|
|
|
219
223
|
"policy": "wanted",
|
|
220
224
|
"because": "version 5 only exports ejs - and we use cjs and don't transpile"
|
|
221
225
|
},
|
|
226
|
+
{
|
|
227
|
+
"package": "eslint-plugin-mocha",
|
|
228
|
+
"policy": "wanted",
|
|
229
|
+
"because": "version 10 dropped support for node 12, which we still do support"
|
|
230
|
+
},
|
|
222
231
|
{
|
|
223
232
|
"package": "normalize-newline",
|
|
224
233
|
"policy": "wanted",
|
|
@@ -241,10 +250,11 @@
|
|
|
241
250
|
"src/**/*.template.js",
|
|
242
251
|
"src/cli/tools/svg-in-html-snippets/script.snippet.js",
|
|
243
252
|
"test/integration/**",
|
|
244
|
-
"test/*/
|
|
245
|
-
"test/*/*/
|
|
246
|
-
"test
|
|
247
|
-
"test
|
|
253
|
+
"test/*/__fixtures__/**",
|
|
254
|
+
"test/*/*/__fixtures__/**",
|
|
255
|
+
"test/*/*/*/__fixtures__/**",
|
|
256
|
+
"test/*/__mocks__/**",
|
|
257
|
+
"test/*/*/__mocks__/**",
|
|
248
258
|
"types/**"
|
|
249
259
|
],
|
|
250
260
|
"engines": {
|
|
@@ -277,13 +287,13 @@
|
|
|
277
287
|
"eslint --fix",
|
|
278
288
|
"prettier --write"
|
|
279
289
|
],
|
|
280
|
-
"test/**/*.{utl,spec}.{mjs,
|
|
290
|
+
"test/**/*.{utl,spec}.{mjs,cjs}": [
|
|
281
291
|
"eslint --fix",
|
|
282
292
|
"prettier --write"
|
|
283
293
|
],
|
|
284
|
-
"
|
|
294
|
+
"types/**/*.d.ts": [
|
|
285
295
|
"eslint --config types/.eslintrc.json --fix",
|
|
286
|
-
"prettier --write
|
|
296
|
+
"prettier --write"
|
|
287
297
|
]
|
|
288
298
|
}
|
|
289
299
|
}
|
package/src/cli/format.js
CHANGED
package/src/enrich/derive/{metrics/get-stability-metrics.js → folders/aggregate-to-folders.js}
RENAMED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/* eslint-disable security/detect-object-injection */
|
|
2
2
|
const path = require("path").posix;
|
|
3
|
-
const {
|
|
3
|
+
const { calculateInstability, metricsAreCalculable } = require("../module-utl");
|
|
4
4
|
const {
|
|
5
5
|
getAfferentCouplings,
|
|
6
6
|
getEfferentCouplings,
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
getParentFolders,
|
|
8
|
+
object2Array,
|
|
9
|
+
} = require("./utl");
|
|
9
10
|
|
|
10
11
|
function upsertCouplings(pAllDependents, pNewDependents) {
|
|
11
12
|
pNewDependents.forEach((pNewDependent) => {
|
|
@@ -34,16 +35,14 @@ function upsertFolderAttributes(pAllMetrics, pModule, pDirname) {
|
|
|
34
35
|
)
|
|
35
36
|
);
|
|
36
37
|
pAllMetrics[pDirname].moduleCount += 1;
|
|
37
|
-
|
|
38
38
|
return pAllMetrics;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return pRightMetric.instability - pLeftMetric.instability;
|
|
41
|
+
function aggregateToFolder(pAllFolders, pModule) {
|
|
42
|
+
getParentFolders(path.dirname(pModule.source)).forEach((pParentDirectory) =>
|
|
43
|
+
upsertFolderAttributes(pAllFolders, pModule, pParentDirectory)
|
|
44
|
+
);
|
|
45
|
+
return pAllFolders;
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
function sumCounts(pAll, pCurrent) {
|
|
@@ -59,12 +58,15 @@ function getFolderLevelCouplings(pCouplingArray) {
|
|
|
59
58
|
: path.dirname(pCoupling.name)
|
|
60
59
|
)
|
|
61
60
|
)
|
|
62
|
-
);
|
|
61
|
+
).map((pCoupling) => ({ name: pCoupling }));
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
function calculateFolderMetrics(pFolder) {
|
|
66
|
-
const lModuleDependents =
|
|
67
|
-
const lModuleDependencies =
|
|
65
|
+
const lModuleDependents = object2Array(pFolder.dependents);
|
|
66
|
+
const lModuleDependencies = object2Array(pFolder.dependencies);
|
|
67
|
+
// this calculation might look superfluous (why not just .length the dependents
|
|
68
|
+
// and dependencies?), but it isn't because there can be > 1 relation between
|
|
69
|
+
// two folders
|
|
68
70
|
const lAfferentCouplings = lModuleDependents.reduce(sumCounts, 0);
|
|
69
71
|
const lEfferentCouplings = lModuleDependencies.reduce(sumCounts, 0);
|
|
70
72
|
|
|
@@ -72,26 +74,33 @@ function calculateFolderMetrics(pFolder) {
|
|
|
72
74
|
...pFolder,
|
|
73
75
|
afferentCouplings: lAfferentCouplings,
|
|
74
76
|
efferentCouplings: lEfferentCouplings,
|
|
75
|
-
|
|
76
|
-
// yield NaN. Judging Bob Martin's intention, a component with no outgoing
|
|
77
|
-
// dependencies is maximum stable (0)
|
|
78
|
-
instability:
|
|
79
|
-
lEfferentCouplings / (lEfferentCouplings + lAfferentCouplings) || 0,
|
|
77
|
+
instability: calculateInstability(lEfferentCouplings, lAfferentCouplings),
|
|
80
78
|
dependents: getFolderLevelCouplings(lModuleDependents),
|
|
81
79
|
dependencies: getFolderLevelCouplings(lModuleDependencies),
|
|
82
80
|
};
|
|
83
81
|
}
|
|
84
82
|
|
|
85
|
-
|
|
86
|
-
return
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
83
|
+
function findFolderByName(pAllFolders, pName) {
|
|
84
|
+
return pAllFolders.find((pFolder) => pFolder.name === pName);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function denormalizeInstability(pFolder, _, pAllFolders) {
|
|
88
|
+
return {
|
|
89
|
+
...pFolder,
|
|
90
|
+
dependencies: pFolder.dependencies.map((pDependency) => {
|
|
91
|
+
const lFolder = findFolderByName(pAllFolders, pDependency.name) || {};
|
|
92
|
+
return {
|
|
93
|
+
...pDependency,
|
|
94
|
+
instability: lFolder.instability >= 0 ? lFolder.instability : 0,
|
|
95
|
+
};
|
|
96
|
+
}),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = function aggregateToFolders(pModules) {
|
|
101
|
+
const lFolders = object2Array(
|
|
102
|
+
pModules.filter(metricsAreCalculable).reduce(aggregateToFolder, {})
|
|
103
|
+
).map(calculateFolderMetrics);
|
|
104
|
+
|
|
105
|
+
return lFolders.map(denormalizeInstability);
|
|
97
106
|
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const validate = require("../../../validate");
|
|
2
|
+
const aggregateToFolders = require("./aggregate-to-folders");
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param {*} pFolder
|
|
7
|
+
* @param {import('../../../../types/dependency-cruiser').IOptions} pOptions
|
|
8
|
+
* @returns
|
|
9
|
+
*/
|
|
10
|
+
function validateFolderDependency(pFolder, pOptions) {
|
|
11
|
+
return (pDependency) => {
|
|
12
|
+
return {
|
|
13
|
+
...pDependency,
|
|
14
|
+
...validate.folder(pOptions.ruleSet || {}, pFolder, pDependency),
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function addFolderDependencyViolations(pOptions) {
|
|
20
|
+
return (pFolder) => {
|
|
21
|
+
return {
|
|
22
|
+
...pFolder,
|
|
23
|
+
dependencies: pFolder.dependencies.map(
|
|
24
|
+
validateFolderDependency(pFolder, pOptions)
|
|
25
|
+
),
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
*
|
|
32
|
+
* @param {import('../../../../types/dependency-cruiser').IModule[]} pModules
|
|
33
|
+
* @param {import('../../../../types/dependency-cruiser').IOptions} pOptions
|
|
34
|
+
* @returns {any}
|
|
35
|
+
*/
|
|
36
|
+
module.exports = function deriveFolderMetrics(pModules, pOptions) {
|
|
37
|
+
let lReturnValue = {};
|
|
38
|
+
if (pOptions.metrics) {
|
|
39
|
+
lReturnValue = {
|
|
40
|
+
folders: aggregateToFolders(pModules).map(
|
|
41
|
+
addFolderDependencyViolations(pOptions)
|
|
42
|
+
),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return lReturnValue;
|
|
46
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/* eslint-disable security/detect-object-injection */
|
|
2
|
+
const path = require("path").posix;
|
|
3
|
+
|
|
4
|
+
function getAfferentCouplings(pModule, pDirname) {
|
|
5
|
+
return pModule.dependents.filter(
|
|
6
|
+
(pDependent) => !pDependent.startsWith(pDirname.concat(path.sep))
|
|
7
|
+
);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function getEfferentCouplings(pModule, pDirname) {
|
|
11
|
+
return pModule.dependencies.filter(
|
|
12
|
+
(pDependency) => !pDependency.resolved.startsWith(pDirname.concat(path.sep))
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
*
|
|
18
|
+
* @param {string} pPath
|
|
19
|
+
* @returns string[]
|
|
20
|
+
*/
|
|
21
|
+
function getParentFolders(pPath) {
|
|
22
|
+
let lFragments = pPath.split("/");
|
|
23
|
+
let lReturnValue = [];
|
|
24
|
+
|
|
25
|
+
while (lFragments.length > 0) {
|
|
26
|
+
lReturnValue.push(lFragments.join("/"));
|
|
27
|
+
lFragments.pop();
|
|
28
|
+
}
|
|
29
|
+
return lReturnValue.reverse();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function object2Array(pObject) {
|
|
33
|
+
return Object.keys(pObject).map((pKey) => ({
|
|
34
|
+
name: pKey,
|
|
35
|
+
...pObject[pKey],
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
getAfferentCouplings,
|
|
41
|
+
getEfferentCouplings,
|
|
42
|
+
getParentFolders,
|
|
43
|
+
object2Array,
|
|
44
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const { findModuleByName } = require("../module-utl");
|
|
2
|
+
const { calculateInstability, metricsAreCalculable } = require("../module-utl");
|
|
3
|
+
|
|
4
|
+
function addInstabilityMetric(pModule) {
|
|
5
|
+
return {
|
|
6
|
+
...pModule,
|
|
7
|
+
...(metricsAreCalculable(pModule)
|
|
8
|
+
? {
|
|
9
|
+
instability: calculateInstability(
|
|
10
|
+
pModule.dependencies.length,
|
|
11
|
+
pModule.dependents.length
|
|
12
|
+
),
|
|
13
|
+
}
|
|
14
|
+
: {}),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function addInstabilityToDependency(pAllModules) {
|
|
19
|
+
return (pDependency) => ({
|
|
20
|
+
...pDependency,
|
|
21
|
+
instability:
|
|
22
|
+
(findModuleByName(pAllModules, pDependency.resolved) || {}).instability ||
|
|
23
|
+
0,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function deNormalizeInstabilityMetricsToDependencies(pModule, _, pAllModules) {
|
|
28
|
+
return {
|
|
29
|
+
...pModule,
|
|
30
|
+
dependencies: pModule.dependencies.map(
|
|
31
|
+
addInstabilityToDependency(pAllModules)
|
|
32
|
+
),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
37
|
+
addInstabilityMetric,
|
|
38
|
+
deNormalizeInstabilityMetricsToDependencies,
|
|
39
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const { clearCache } = require("../module-utl");
|
|
2
|
+
const {
|
|
3
|
+
addInstabilityMetric,
|
|
4
|
+
deNormalizeInstabilityMetricsToDependencies,
|
|
5
|
+
} = require("./get-module-metrics");
|
|
6
|
+
|
|
7
|
+
module.exports = function deriveModulesMetrics(pModules, pOptions) {
|
|
8
|
+
if (pOptions.metrics) {
|
|
9
|
+
const lModules = pModules.map(addInstabilityMetric);
|
|
10
|
+
clearCache();
|
|
11
|
+
return lModules.map(deNormalizeInstabilityMetricsToDependencies);
|
|
12
|
+
}
|
|
13
|
+
return pModules;
|
|
14
|
+
};
|
|
@@ -28,6 +28,32 @@ function isDependent(pResolvedName) {
|
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
function metricsAreCalculable(pModule) {
|
|
32
|
+
return (
|
|
33
|
+
!pModule.coreModule &&
|
|
34
|
+
!pModule.couldNotResolve &&
|
|
35
|
+
!pModule.matchesDoNotFollow
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* returns the Instability of a component given the number of incoming (afferent)
|
|
41
|
+
* and outgoign (efferent) connections ('couplings')
|
|
42
|
+
*
|
|
43
|
+
* @param {number} pEfferentCouplingCount
|
|
44
|
+
* @param {number} pAfferentCouplingCount
|
|
45
|
+
* @returns number
|
|
46
|
+
*/
|
|
47
|
+
function calculateInstability(pEfferentCouplingCount, pAfferentCouplingCount) {
|
|
48
|
+
// when both afferentCouplings and efferentCouplings equal 0 instability will
|
|
49
|
+
// yield NaN. Judging Bob Martin's intention, a component with no outgoing
|
|
50
|
+
// dependencies is maximum stable (0)
|
|
51
|
+
return (
|
|
52
|
+
pEfferentCouplingCount /
|
|
53
|
+
(pEfferentCouplingCount + pAfferentCouplingCount) || 0
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
31
57
|
function clearCache() {
|
|
32
58
|
gIndexedGraph = null;
|
|
33
59
|
}
|
|
@@ -36,4 +62,6 @@ module.exports = {
|
|
|
36
62
|
findModuleByName,
|
|
37
63
|
clearCache,
|
|
38
64
|
isDependent,
|
|
65
|
+
metricsAreCalculable,
|
|
66
|
+
calculateInstability,
|
|
39
67
|
};
|
|
@@ -4,7 +4,7 @@ const _clone = require("lodash/clone");
|
|
|
4
4
|
const _get = require("lodash/get");
|
|
5
5
|
const _has = require("lodash/has");
|
|
6
6
|
const matchers = require("../../../validate/matchers");
|
|
7
|
-
const { extractGroups } = require("../../../
|
|
7
|
+
const { extractGroups } = require("../../../utl/regex-util");
|
|
8
8
|
const getPath = require("./get-path");
|
|
9
9
|
|
|
10
10
|
function getReachableRules(pRuleSet) {
|
|
@@ -8,7 +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
|
|
11
|
+
const deriveModuleMetrics = require("./derive/metrics");
|
|
12
12
|
|
|
13
13
|
module.exports = function enrichModules(pModules, pOptions) {
|
|
14
14
|
bus.emit("progress", "analyzing: cycles", { level: busLogLevels.INFO });
|
|
@@ -19,7 +19,7 @@ module.exports = function enrichModules(pModules, pOptions) {
|
|
|
19
19
|
lModules = deriveOrphans(lModules);
|
|
20
20
|
bus.emit("progress", "analyzing: reachables", { level: busLogLevels.INFO });
|
|
21
21
|
lModules = deriveReachable(lModules, pOptions.ruleSet);
|
|
22
|
-
bus.emit("progress", "analyzing:
|
|
22
|
+
bus.emit("progress", "analyzing: module metrics", {
|
|
23
23
|
level: busLogLevels.INFO,
|
|
24
24
|
});
|
|
25
25
|
lModules = deriveModuleMetrics(lModules, pOptions);
|
package/src/enrich/index.js
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
const enrichModules = require("./enrich-modules");
|
|
2
|
-
const
|
|
2
|
+
const aggregateToFolders = require("./derive/folders");
|
|
3
3
|
const summarize = require("./summarize");
|
|
4
4
|
const clearCaches = require("./clear-caches");
|
|
5
5
|
|
|
6
6
|
module.exports = function enrich(pModules, pOptions, pFileAndDirectoryArray) {
|
|
7
7
|
clearCaches();
|
|
8
8
|
const lModules = enrichModules(pModules, pOptions);
|
|
9
|
+
const lFolders = aggregateToFolders(lModules, pOptions);
|
|
9
10
|
|
|
10
11
|
clearCaches();
|
|
11
|
-
|
|
12
|
+
const lReturnValue = {
|
|
12
13
|
modules: lModules,
|
|
13
|
-
...
|
|
14
|
-
summary: summarize(
|
|
14
|
+
...lFolders,
|
|
15
|
+
summary: summarize(
|
|
16
|
+
lModules,
|
|
17
|
+
pOptions,
|
|
18
|
+
pFileAndDirectoryArray,
|
|
19
|
+
lFolders.folders
|
|
20
|
+
),
|
|
15
21
|
};
|
|
22
|
+
|
|
23
|
+
return lReturnValue;
|
|
16
24
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
function getViolationStats(pViolations) {
|
|
2
|
+
return pViolations.reduce(
|
|
3
|
+
(pAll, pThis) => {
|
|
4
|
+
pAll[pThis.rule.severity] += 1;
|
|
5
|
+
return pAll;
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
error: 0,
|
|
9
|
+
warn: 0,
|
|
10
|
+
info: 0,
|
|
11
|
+
ignore: 0,
|
|
12
|
+
}
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function getModulesCruised(pModules) {
|
|
17
|
+
return pModules.length;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getDependenciesCruised(pModules) {
|
|
21
|
+
return pModules.reduce(
|
|
22
|
+
(pAll, pModule) => pAll + pModule.dependencies.length,
|
|
23
|
+
0
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
getViolationStats,
|
|
29
|
+
getModulesCruised,
|
|
30
|
+
getDependenciesCruised,
|
|
31
|
+
};
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
const addRuleSetUsed = require("./add-rule-set-used");
|
|
2
2
|
const summarizeModules = require("./summarize-modules");
|
|
3
|
+
const summarizeFolders = require("./summarize-folders");
|
|
3
4
|
const summarizeOptions = require("./summarize-options");
|
|
5
|
+
const {
|
|
6
|
+
getViolationStats,
|
|
7
|
+
getModulesCruised,
|
|
8
|
+
getDependenciesCruised,
|
|
9
|
+
} = require("./get-stats");
|
|
4
10
|
|
|
5
11
|
/**
|
|
6
12
|
*
|
|
@@ -8,17 +14,30 @@ const summarizeOptions = require("./summarize-options");
|
|
|
8
14
|
* cruised modules that have been enriched with mandatory attributes &
|
|
9
15
|
* have been validated against rules as passed in the options
|
|
10
16
|
* @param {import("../../../types/options").ICruiseOptions} pOptions -
|
|
11
|
-
*
|
|
12
17
|
* @param {string[]} pFileDirectoryArray -
|
|
13
18
|
* the files/ directories originally passed to be cruised
|
|
19
|
+
* @param {import("../../../types/dependency-cruiser").IFolder[]} pFolders -
|
|
20
|
+
* the pModules collapsed to folders, with their own metrics & deps
|
|
14
21
|
*
|
|
15
22
|
* @returns {import("../../../types/cruise-result").ISummary} -
|
|
16
23
|
* a summary of the found modules, dependencies and any violations
|
|
17
24
|
*/
|
|
18
|
-
module.exports = function
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
module.exports = function summarize(
|
|
26
|
+
pModules,
|
|
27
|
+
pOptions,
|
|
28
|
+
pFileDirectoryArray,
|
|
29
|
+
pFolders
|
|
30
|
+
) {
|
|
31
|
+
const lViolations = summarizeModules(pModules, pOptions.ruleSet).concat(
|
|
32
|
+
summarizeFolders(pFolders || [], pOptions.ruleSet)
|
|
23
33
|
);
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
violations: lViolations,
|
|
37
|
+
...getViolationStats(lViolations),
|
|
38
|
+
totalCruised: getModulesCruised(pModules),
|
|
39
|
+
totalDependenciesCruised: getDependenciesCruised(pModules),
|
|
40
|
+
...summarizeOptions(pFileDirectoryArray, pOptions),
|
|
41
|
+
...(pOptions.ruleSet ? { ruleSetUsed: addRuleSetUsed(pOptions) } : {}),
|
|
42
|
+
};
|
|
24
43
|
};
|