dependency-cruiser 10.8.0 → 11.2.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 +22 -16
- 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 +9 -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 +2 -2
- package/src/enrich/summarize/summarize-modules.js +24 -5
- package/src/extract/get-dependencies.js +11 -6
- package/src/extract/parse/to-typescript-ast.js +24 -8
- package/src/extract/utl/get-extension.js +9 -5
- package/src/main/options/normalize.js +28 -0
- 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/match-dependency-rule.js +19 -32
- package/src/validate/match-module-rule.js +1 -1
- package/src/validate/matchers.js +58 -48
- package/src/validate/violates-required-rule.js +1 -1
- package/types/baseline-violations.d.ts +1 -1
- package/types/cruise-result.d.ts +11 -50
- package/types/options.d.ts +5 -0
- package/types/reporter-options.d.ts +6 -0
- package/types/restrictions.d.ts +12 -0
- package/types/rule-summary.d.ts +23 -0
- package/types/shared-types.d.ts +7 -0
- package/types/violations.d.ts +43 -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/utl.js +0 -31
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": "
|
|
3
|
+
"version": "11.2.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/",
|
|
@@ -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
|
+
"teamcity-service-messages": "0.1.12",
|
|
155
156
|
"tsconfig-paths-webpack-plugin": "3.5.2",
|
|
156
157
|
"wrap-ansi": "^7.0.0"
|
|
157
158
|
},
|
|
158
159
|
"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.
|
|
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.123",
|
|
164
|
+
"@typescript-eslint/eslint-plugin": "5.8.0",
|
|
165
|
+
"@typescript-eslint/parser": "5.8.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",
|
|
@@ -172,22 +173,22 @@
|
|
|
172
173
|
"eslint-config-prettier": "8.3.0",
|
|
173
174
|
"eslint-plugin-budapestian": "3.0.1",
|
|
174
175
|
"eslint-plugin-import": "2.25.3",
|
|
175
|
-
"eslint-plugin-mocha": "9.0.0",
|
|
176
|
+
"eslint-plugin-mocha": "^9.0.0",
|
|
176
177
|
"eslint-plugin-node": "11.1.0",
|
|
177
178
|
"eslint-plugin-security": "1.4.0",
|
|
178
179
|
"eslint-plugin-unicorn": "39.0.0",
|
|
179
180
|
"husky": "^4.3.8",
|
|
180
181
|
"intercept-stdout": "0.1.2",
|
|
181
|
-
"lint-staged": "12.1.
|
|
182
|
+
"lint-staged": "12.1.4",
|
|
182
183
|
"mocha": "9.1.3",
|
|
183
184
|
"normalize-newline": "^3.0.0",
|
|
184
185
|
"npm-run-all": "4.1.5",
|
|
185
|
-
"prettier": "2.5.
|
|
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.5.
|
|
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"
|
|
@@ -219,6 +220,11 @@
|
|
|
219
220
|
"policy": "wanted",
|
|
220
221
|
"because": "version 5 only exports ejs - and we use cjs and don't transpile"
|
|
221
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
|
+
},
|
|
222
228
|
{
|
|
223
229
|
"package": "normalize-newline",
|
|
224
230
|
"policy": "wanted",
|
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(pAllFolders) {
|
|
88
|
+
return (pFolder) => ({
|
|
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(lFolders));
|
|
97
106
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const aggregateToFolders = require("./aggregate-to-folders");
|
|
2
|
+
|
|
3
|
+
module.exports = function deriveFolderMetrics(pModules, pOptions) {
|
|
4
|
+
let lReturnValue = {};
|
|
5
|
+
if (pOptions.metrics) {
|
|
6
|
+
lReturnValue = { folders: aggregateToFolders(pModules) };
|
|
7
|
+
}
|
|
8
|
+
return lReturnValue;
|
|
9
|
+
};
|
|
@@ -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,5 +1,5 @@
|
|
|
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
|
|
|
@@ -10,7 +10,7 @@ module.exports = function enrich(pModules, pOptions, pFileAndDirectoryArray) {
|
|
|
10
10
|
clearCaches();
|
|
11
11
|
return {
|
|
12
12
|
modules: lModules,
|
|
13
|
-
...
|
|
13
|
+
...aggregateToFolders(lModules, pOptions),
|
|
14
14
|
summary: summarize(lModules, pOptions, pFileAndDirectoryArray),
|
|
15
15
|
};
|
|
16
16
|
};
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
const _flattenDeep = require("lodash/flattenDeep");
|
|
2
2
|
const _get = require("lodash/get");
|
|
3
|
+
const _has = require("lodash/has");
|
|
3
4
|
const _uniqWith = require("lodash/uniqWith");
|
|
4
5
|
const { findRuleByName } = require("../../graph-utl/rule-set");
|
|
5
6
|
const compare = require("../../graph-utl/compare");
|
|
6
7
|
const isSameViolation = require("./is-same-violation");
|
|
7
8
|
|
|
8
|
-
function cutNonTransgressions(
|
|
9
|
+
function cutNonTransgressions(pModule) {
|
|
9
10
|
return {
|
|
10
|
-
|
|
11
|
-
dependencies:
|
|
11
|
+
...pModule,
|
|
12
|
+
dependencies: pModule.dependencies.filter(
|
|
12
13
|
(pDependency) => pDependency.valid === false
|
|
13
14
|
),
|
|
14
15
|
};
|
|
@@ -30,21 +31,38 @@ function extractMetaData(pViolations) {
|
|
|
30
31
|
}
|
|
31
32
|
function toDependencyViolationSummary(pRule, pModule, pDependency, pRuleSet) {
|
|
32
33
|
let lReturnValue = {
|
|
34
|
+
type: "dependency",
|
|
33
35
|
from: pModule.source,
|
|
34
36
|
to: pDependency.resolved,
|
|
35
37
|
rule: pRule,
|
|
36
38
|
};
|
|
37
39
|
|
|
38
40
|
if (
|
|
39
|
-
pDependency
|
|
41
|
+
_has(pDependency, "cycle") &&
|
|
40
42
|
_get(findRuleByName(pRuleSet, pRule.name), "to.circular")
|
|
41
43
|
) {
|
|
42
44
|
lReturnValue = {
|
|
43
45
|
...lReturnValue,
|
|
46
|
+
type: "cycle",
|
|
44
47
|
cycle: pDependency.cycle,
|
|
45
48
|
};
|
|
46
49
|
}
|
|
47
50
|
|
|
51
|
+
if (
|
|
52
|
+
_has(pModule, "instability") &&
|
|
53
|
+
_has(pDependency, "instability") &&
|
|
54
|
+
_has(findRuleByName(pRuleSet, pRule.name), "to.moreUnstable")
|
|
55
|
+
) {
|
|
56
|
+
lReturnValue = {
|
|
57
|
+
...lReturnValue,
|
|
58
|
+
type: "instability",
|
|
59
|
+
metrics: {
|
|
60
|
+
from: { instability: pModule.instability },
|
|
61
|
+
to: { instability: pDependency.instability },
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
48
66
|
return lReturnValue;
|
|
49
67
|
}
|
|
50
68
|
|
|
@@ -82,7 +100,7 @@ function extractDependencyViolations(pModules, pRuleSet) {
|
|
|
82
100
|
|
|
83
101
|
function toModuleViolationSummary(pRule, pModule, pRuleSet) {
|
|
84
102
|
let lReturnValue = [
|
|
85
|
-
{ from: pModule.source, to: pModule.source, rule: pRule },
|
|
103
|
+
{ type: "module", from: pModule.source, to: pModule.source, rule: pRule },
|
|
86
104
|
];
|
|
87
105
|
if (
|
|
88
106
|
pModule.reaches &&
|
|
@@ -101,6 +119,7 @@ function toModuleViolationSummary(pRule, pModule, pRuleSet) {
|
|
|
101
119
|
[]
|
|
102
120
|
)
|
|
103
121
|
.map((pToModule) => ({
|
|
122
|
+
type: "reachability",
|
|
104
123
|
from: pModule.source,
|
|
105
124
|
to: pToModule.to,
|
|
106
125
|
rule: pRule,
|
|
@@ -21,15 +21,18 @@ function extractFromSwcAST(pOptions, pFileName) {
|
|
|
21
21
|
);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
function extractFromTypeScriptAST(pOptions, pFileName) {
|
|
24
|
+
function extractFromTypeScriptAST(pOptions, pFileName, pTranspileOptions) {
|
|
25
25
|
return extractTypeScriptDeps(
|
|
26
|
-
toTypescriptAST.getASTCached(
|
|
26
|
+
toTypescriptAST.getASTCached(
|
|
27
|
+
path.join(pOptions.baseDir, pFileName),
|
|
28
|
+
pTranspileOptions
|
|
29
|
+
),
|
|
27
30
|
pOptions.exoticRequireStrings
|
|
28
31
|
);
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
function isTypeScriptCompatible(pFileName) {
|
|
32
|
-
return [".ts", ".tsx", ".js", ".mjs", ".cjs"].includes(
|
|
35
|
+
return [".ts", ".tsx", ".js", ".mjs", ".cjs", ".vue"].includes(
|
|
33
36
|
path.extname(pFileName)
|
|
34
37
|
);
|
|
35
38
|
}
|
|
@@ -88,9 +91,11 @@ function extractWithTsc(
|
|
|
88
91
|
pFileName,
|
|
89
92
|
pTranspileOptions
|
|
90
93
|
) {
|
|
91
|
-
pDependencies = extractFromTypeScriptAST(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
pDependencies = extractFromTypeScriptAST(
|
|
95
|
+
pCruiseOptions,
|
|
96
|
+
pFileName,
|
|
97
|
+
pTranspileOptions
|
|
98
|
+
).filter((pDep) => pCruiseOptions.moduleSystems.includes(pDep.moduleSystem));
|
|
94
99
|
|
|
95
100
|
if (pCruiseOptions.tsPreCompilationDeps === "specify") {
|
|
96
101
|
pDependencies = detectPreCompilationNess(
|
|
@@ -2,21 +2,28 @@ const fs = require("fs");
|
|
|
2
2
|
const tryRequire = require("semver-try-require");
|
|
3
3
|
const _memoize = require("lodash/memoize");
|
|
4
4
|
const { supportedTranspilers } = require("../../../src/meta.js");
|
|
5
|
+
const transpile = require("../transpile");
|
|
6
|
+
const getExtension = require("../utl/get-extension");
|
|
5
7
|
|
|
6
8
|
const typescript = tryRequire("typescript", supportedTranspilers.typescript);
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Compiles pTypescriptSource into a (typescript) AST
|
|
10
12
|
*
|
|
11
|
-
* @param {
|
|
12
|
-
* @param {
|
|
13
|
-
*
|
|
13
|
+
* @param {object} pFileRecord Record with source code, an extension and a filename
|
|
14
|
+
* @param {any} [pTranspileOptions] options for the transpiler(s) - a tsconfig or
|
|
15
|
+
* a babel config
|
|
14
16
|
* @return {object} - a (typescript) AST
|
|
15
17
|
*/
|
|
16
|
-
function getASTFromSource(
|
|
18
|
+
function getASTFromSource(pFileRecord, pTranspileOptions) {
|
|
19
|
+
let lSource = pFileRecord.source;
|
|
20
|
+
if (pFileRecord.extension === ".vue") {
|
|
21
|
+
lSource = transpile(pFileRecord, pTranspileOptions);
|
|
22
|
+
}
|
|
23
|
+
|
|
17
24
|
return typescript.createSourceFile(
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
pFileRecord.filename || "$internal-file-name",
|
|
26
|
+
lSource,
|
|
20
27
|
typescript.ScriptTarget.Latest,
|
|
21
28
|
false
|
|
22
29
|
);
|
|
@@ -27,10 +34,19 @@ function getASTFromSource(pTypescriptSource, pFileName) {
|
|
|
27
34
|
* AST and returns it
|
|
28
35
|
*
|
|
29
36
|
* @param {string} pFileName - the name of the file to compile
|
|
37
|
+
* @param {any} [pTranspileOptions] options for the transpiler(s) - a tsconfig or
|
|
38
|
+
* a babel config
|
|
30
39
|
* @return {object} - a (typescript) AST
|
|
31
40
|
*/
|
|
32
|
-
function getAST(pFileName) {
|
|
33
|
-
return getASTFromSource(
|
|
41
|
+
function getAST(pFileName, pTranspileOptions) {
|
|
42
|
+
return getASTFromSource(
|
|
43
|
+
{
|
|
44
|
+
source: fs.readFileSync(pFileName, "utf8"),
|
|
45
|
+
extension: getExtension(pFileName),
|
|
46
|
+
filename: pFileName,
|
|
47
|
+
},
|
|
48
|
+
pTranspileOptions
|
|
49
|
+
);
|
|
34
50
|
}
|
|
35
51
|
|
|
36
52
|
const getASTCached = _memoize(getAST);
|
|
@@ -5,16 +5,20 @@ const path = require("path");
|
|
|
5
5
|
*
|
|
6
6
|
* Just using path.extname would be fine for most cases,
|
|
7
7
|
* except for coffeescript, where a markdown extension can
|
|
8
|
-
* mean literate coffeescript
|
|
8
|
+
* mean literate coffeescript, and for typescript where
|
|
9
|
+
* .d.ts and .ts are slightly different beasts
|
|
9
10
|
*
|
|
10
11
|
* @param {string} pFileName path to the file to be parsed
|
|
11
12
|
* @return {string} extension
|
|
12
13
|
*/
|
|
13
14
|
module.exports = function getExtensions(pFileName) {
|
|
14
|
-
|
|
15
|
+
if (pFileName.endsWith(".d.ts")) {
|
|
16
|
+
return ".d.ts";
|
|
17
|
+
}
|
|
15
18
|
|
|
16
|
-
if (
|
|
17
|
-
return
|
|
19
|
+
if (pFileName.endsWith(".coffee.md")) {
|
|
20
|
+
return ".coffee.md";
|
|
18
21
|
}
|
|
19
|
-
|
|
22
|
+
|
|
23
|
+
return path.extname(pFileName);
|
|
20
24
|
};
|