dependency-cruiser 12.5.1 → 12.6.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/bin/dependency-cruise.js +5 -0
- package/package.json +1 -1
- package/src/cli/listeners/performance-log/format-helpers.js +10 -7
- package/src/cli/listeners/performance-log/handlers.js +19 -7
- package/src/enrich/derive/{circular/index.js → circular.js} +15 -16
- package/src/enrich/derive/folders/aggregate-to-folders.js +6 -5
- package/src/enrich/derive/folders/index.js +1 -2
- package/src/enrich/derive/metrics/get-module-metrics.js +4 -3
- package/src/enrich/derive/metrics/index.js +3 -4
- package/src/enrich/derive/module-utl.js +1 -33
- package/src/enrich/derive/{reachable/index.js → reachable.js} +22 -13
- package/src/enrich/enrich-modules.js +3 -3
- package/src/enrich/index.js +0 -3
- package/src/extract/gather-initial-sources.js +13 -13
- package/src/graph-utl/indexed-module-graph.js +80 -6
- package/src/meta.js +1 -1
- package/src/utl/find-all-files.js +4 -6
- package/src/enrich/clear-caches.js +0 -5
- package/src/enrich/derive/circular/get-cycle.js +0 -70
- package/src/enrich/derive/reachable/get-path.js +0 -29
package/bin/dependency-cruise.js
CHANGED
|
@@ -111,6 +111,11 @@ try {
|
|
|
111
111
|
"(experimental) strategy to use for detecting changed files in the cache. " +
|
|
112
112
|
"Possible values: metadata (= use git, the default), content"
|
|
113
113
|
)
|
|
114
|
+
.option(
|
|
115
|
+
"--no-cache",
|
|
116
|
+
"switch off caching. Overrides the 'cache' key in .dependency-cruiser.js " +
|
|
117
|
+
"and --cache options set earlier on the command line"
|
|
118
|
+
)
|
|
114
119
|
.option("--preserve-symlinks", `leave symlinks unchanged (off by default)`)
|
|
115
120
|
.option("-v, --validate [file]", `alias for --config`)
|
|
116
121
|
.option(
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@ const chalk = require("chalk");
|
|
|
2
2
|
|
|
3
3
|
const MS_PER_SECOND = 1000;
|
|
4
4
|
const MS_PER_MICRO_SECOND = 0.001;
|
|
5
|
-
const
|
|
5
|
+
const MAX_EXPECTED_LENGTH = 13;
|
|
6
6
|
const NUMBER_OF_COLUMNS = 8;
|
|
7
7
|
const K = 1024;
|
|
8
8
|
/*
|
|
@@ -28,7 +28,11 @@ const gSizeFormat = new Intl.NumberFormat(LOCALE, {
|
|
|
28
28
|
maximumFractionDigits: 0,
|
|
29
29
|
}).format;
|
|
30
30
|
|
|
31
|
-
const pad = (pString) => pString.padStart(
|
|
31
|
+
const pad = (pString) => pString.padStart(MAX_EXPECTED_LENGTH).concat(" ");
|
|
32
|
+
|
|
33
|
+
function formatDividerLine() {
|
|
34
|
+
return `${`${"-".repeat(MAX_EXPECTED_LENGTH)} `.repeat(NUMBER_OF_COLUMNS)}\n`;
|
|
35
|
+
}
|
|
32
36
|
|
|
33
37
|
function formatHeader() {
|
|
34
38
|
return chalk
|
|
@@ -43,19 +47,17 @@ function formatHeader() {
|
|
|
43
47
|
pad("∆ external")
|
|
44
48
|
}after step...\n`
|
|
45
49
|
)
|
|
46
|
-
.concat(
|
|
47
|
-
`${`${"-".repeat(MAX_LENGTH_EXPECTED)} `.repeat(NUMBER_OF_COLUMNS)}\n`
|
|
48
|
-
);
|
|
50
|
+
.concat(formatDividerLine());
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
function formatTime(pNumber, pConversionMultiplier = MS_PER_SECOND) {
|
|
52
54
|
return gTimeFormat(pConversionMultiplier * pNumber)
|
|
53
|
-
.padStart(
|
|
55
|
+
.padStart(MAX_EXPECTED_LENGTH)
|
|
54
56
|
.concat(" ");
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
function formatMemory(pBytes) {
|
|
58
|
-
const lReturnValue = gSizeFormat(pBytes / K).padStart(
|
|
60
|
+
const lReturnValue = gSizeFormat(pBytes / K).padStart(MAX_EXPECTED_LENGTH);
|
|
59
61
|
|
|
60
62
|
return (pBytes < 0 ? chalk.blue(lReturnValue) : lReturnValue).concat(" ");
|
|
61
63
|
}
|
|
@@ -87,4 +89,5 @@ module.exports = {
|
|
|
87
89
|
formatMemory,
|
|
88
90
|
formatPerfLine,
|
|
89
91
|
formatHeader,
|
|
92
|
+
formatDividerLine,
|
|
90
93
|
};
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
const {
|
|
2
|
-
formatTime,
|
|
3
2
|
formatPerfLine,
|
|
4
3
|
formatHeader,
|
|
5
|
-
|
|
4
|
+
formatDividerLine,
|
|
6
5
|
} = require("./format-helpers");
|
|
7
6
|
|
|
8
7
|
function getHeader(pLevel, pMaxLevel) {
|
|
@@ -47,11 +46,24 @@ function getProgressLine(pMessage, pState, pLevel, pMaxLevel) {
|
|
|
47
46
|
function getEndText(pState, pLevel, pMaxLevel) {
|
|
48
47
|
if (pLevel <= pMaxLevel) {
|
|
49
48
|
const lTime = process.uptime();
|
|
50
|
-
const {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return
|
|
49
|
+
const { user, system } = process.cpuUsage();
|
|
50
|
+
const { heapUsed, heapTotal, external, rss } = process.memoryUsage();
|
|
51
|
+
pState.previousMessage = "really done";
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
getProgressLine("", pState, pLevel, pMaxLevel) +
|
|
55
|
+
formatDividerLine() +
|
|
56
|
+
formatPerfLine({
|
|
57
|
+
elapsedTime: lTime,
|
|
58
|
+
elapsedUser: user,
|
|
59
|
+
elapsedSystem: system,
|
|
60
|
+
deltaRss: rss,
|
|
61
|
+
deltaHeapUsed: heapUsed,
|
|
62
|
+
deltaHeapTotal: heapTotal,
|
|
63
|
+
deltaExternal: external,
|
|
64
|
+
message: "",
|
|
65
|
+
})
|
|
66
|
+
);
|
|
55
67
|
}
|
|
56
68
|
return "";
|
|
57
69
|
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
/* eslint-disable security/detect-object-injection */
|
|
2
|
-
const getCycle = require("./get-cycle");
|
|
3
2
|
|
|
4
3
|
function addCircularityCheckToDependency(
|
|
5
|
-
|
|
4
|
+
pIndexedGraph,
|
|
6
5
|
pFrom,
|
|
7
6
|
pToDep,
|
|
8
|
-
|
|
7
|
+
pDependencyName
|
|
9
8
|
) {
|
|
10
9
|
let lReturnValue = {
|
|
11
10
|
...pToDep,
|
|
12
11
|
circular: false,
|
|
13
12
|
};
|
|
14
|
-
const lCycle = getCycle(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
const lCycle = pIndexedGraph.getCycle(
|
|
14
|
+
pFrom,
|
|
15
|
+
pToDep[pDependencyName],
|
|
16
|
+
pDependencyName
|
|
17
|
+
);
|
|
18
18
|
|
|
19
19
|
if (lCycle.length > 0) {
|
|
20
20
|
lReturnValue = {
|
|
@@ -32,21 +32,20 @@ function addCircularityCheckToDependency(
|
|
|
32
32
|
* whether it's (part of a) circular (relationship) and returns the
|
|
33
33
|
* dependencies with that added.
|
|
34
34
|
*/
|
|
35
|
-
module.exports = (
|
|
35
|
+
module.exports = function detectAndAddCycles(
|
|
36
36
|
pNodes,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
pIndexedNodes,
|
|
38
|
+
{ pSourceAttribute, pDependencyName }
|
|
39
|
+
) {
|
|
40
|
+
return pNodes.map((pModule) => ({
|
|
40
41
|
...pModule,
|
|
41
42
|
dependencies: pModule.dependencies.map((pToDep) =>
|
|
42
43
|
addCircularityCheckToDependency(
|
|
43
|
-
|
|
44
|
+
pIndexedNodes,
|
|
44
45
|
pModule[pSourceAttribute],
|
|
45
46
|
pToDep,
|
|
46
|
-
|
|
47
|
-
pDependencyName,
|
|
48
|
-
pFindNodeByName,
|
|
49
|
-
}
|
|
47
|
+
pDependencyName
|
|
50
48
|
)
|
|
51
49
|
),
|
|
52
50
|
}));
|
|
51
|
+
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
const path = require("path").posix;
|
|
3
3
|
const { calculateInstability, metricsAreCalculable } = require("../module-utl");
|
|
4
4
|
const detectCycles = require("../circular");
|
|
5
|
+
const IndexedModuleGraph = require("../../../graph-utl/indexed-module-graph");
|
|
5
6
|
const {
|
|
6
7
|
findFolderByName,
|
|
7
8
|
getAfferentCouplings,
|
|
@@ -82,7 +83,7 @@ function calculateFolderMetrics(pFolder) {
|
|
|
82
83
|
};
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
function
|
|
86
|
+
function deNormalizeInstability(pFolder, _, pAllFolders) {
|
|
86
87
|
return {
|
|
87
88
|
...pFolder,
|
|
88
89
|
dependencies: pFolder.dependencies.map((pDependency) => {
|
|
@@ -114,15 +115,15 @@ function getSinks(pFolders) {
|
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
module.exports = function aggregateToFolders(pModules) {
|
|
117
|
-
|
|
118
|
+
let lFolders = object2Array(
|
|
118
119
|
pModules.filter(metricsAreCalculable).reduce(aggregateToFolder, {})
|
|
119
120
|
)
|
|
120
121
|
.map(calculateFolderMetrics)
|
|
121
|
-
.map(
|
|
122
|
+
.map(deNormalizeInstability);
|
|
123
|
+
lFolders = lFolders.concat(getSinks(lFolders));
|
|
122
124
|
|
|
123
|
-
return detectCycles(lFolders
|
|
125
|
+
return detectCycles(lFolders, new IndexedModuleGraph(lFolders, "name"), {
|
|
124
126
|
pSourceAttribute: "name",
|
|
125
127
|
pDependencyName: "name",
|
|
126
|
-
pFindNodeByName: findFolderByName,
|
|
127
128
|
});
|
|
128
129
|
};
|
|
@@ -2,8 +2,7 @@ const validate = require("../../../validate");
|
|
|
2
2
|
const aggregateToFolders = require("./aggregate-to-folders");
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
* @param {*} pFolder
|
|
5
|
+
* @param {import("../../../..").IFolder} pFolder
|
|
7
6
|
* @param {import('../../../..').IOptions} pOptions
|
|
8
7
|
* @returns
|
|
9
8
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const IndexedModuleGraph = require("../../../graph-utl/indexed-module-graph");
|
|
2
2
|
const { calculateInstability, metricsAreCalculable } = require("../module-utl");
|
|
3
3
|
|
|
4
4
|
function addInstabilityMetric(pModule) {
|
|
@@ -16,11 +16,12 @@ function addInstabilityMetric(pModule) {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function addInstabilityToDependency(pAllModules) {
|
|
19
|
+
const lIndexedModules = new IndexedModuleGraph(pAllModules);
|
|
19
20
|
return (pDependency) => ({
|
|
20
21
|
...pDependency,
|
|
21
22
|
instability:
|
|
22
|
-
(findModuleByName(
|
|
23
|
-
|
|
23
|
+
(lIndexedModules.findModuleByName(pDependency.resolved) || {})
|
|
24
|
+
.instability || 0,
|
|
24
25
|
});
|
|
25
26
|
}
|
|
26
27
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const { clearCache } = require("../module-utl");
|
|
2
1
|
const {
|
|
3
2
|
addInstabilityMetric,
|
|
4
3
|
deNormalizeInstabilityMetricsToDependencies,
|
|
@@ -6,9 +5,9 @@ const {
|
|
|
6
5
|
|
|
7
6
|
module.exports = function deriveModulesMetrics(pModules, pOptions) {
|
|
8
7
|
if (pOptions.metrics) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
return pModules
|
|
9
|
+
.map(addInstabilityMetric)
|
|
10
|
+
.map(deNormalizeInstabilityMetricsToDependencies);
|
|
12
11
|
}
|
|
13
12
|
return pModules;
|
|
14
13
|
};
|
|
@@ -1,29 +1,3 @@
|
|
|
1
|
-
/** @type {Map<string,import('../../../types/cruise-result').IModule>} */
|
|
2
|
-
let gIndexedGraph = null;
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Returns the module with attribute pSource, when it exists in the pModuleGraph.
|
|
6
|
-
* Returns undefined when it doesn't.
|
|
7
|
-
*
|
|
8
|
-
* This function uses an indexed cache for efficiency reasons. If you need to
|
|
9
|
-
* call this function consecutively for different module graphs, you can clear
|
|
10
|
-
* this cache with the clearCache function from this module.
|
|
11
|
-
*
|
|
12
|
-
* TODO: use IndexedModuleGraph class from src/graph-utl in stead
|
|
13
|
-
*
|
|
14
|
-
* @param {import('../../../types/cruise-result').IModule[]} pModuleGraph
|
|
15
|
-
* @param {string} pSource
|
|
16
|
-
* @returns {import('../../../types/cruise-result').IModule | undefined}
|
|
17
|
-
*/
|
|
18
|
-
function findModuleByName(pModuleGraph, pSource) {
|
|
19
|
-
if (!gIndexedGraph) {
|
|
20
|
-
gIndexedGraph = new Map(
|
|
21
|
-
pModuleGraph.map((pModule) => [pModule.source, pModule])
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
return gIndexedGraph.get(pSource);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
1
|
function isDependent(pResolvedName) {
|
|
28
2
|
return (pModule) =>
|
|
29
3
|
pModule.dependencies.some(
|
|
@@ -41,7 +15,7 @@ function metricsAreCalculable(pModule) {
|
|
|
41
15
|
|
|
42
16
|
/**
|
|
43
17
|
* returns the Instability of a component given the number of incoming (afferent)
|
|
44
|
-
* and
|
|
18
|
+
* and outgoing (efferent) connections ('couplings')
|
|
45
19
|
*
|
|
46
20
|
* @param {number} pEfferentCouplingCount
|
|
47
21
|
* @param {number} pAfferentCouplingCount
|
|
@@ -57,13 +31,7 @@ function calculateInstability(pEfferentCouplingCount, pAfferentCouplingCount) {
|
|
|
57
31
|
);
|
|
58
32
|
}
|
|
59
33
|
|
|
60
|
-
function clearCache() {
|
|
61
|
-
gIndexedGraph = null;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
34
|
module.exports = {
|
|
65
|
-
findModuleByName,
|
|
66
|
-
clearCache,
|
|
67
35
|
isDependent,
|
|
68
36
|
metricsAreCalculable,
|
|
69
37
|
calculateInstability,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/* eslint-disable security/detect-object-injection, no-inline-comments */
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const cloneDeep = require("lodash/cloneDeep");
|
|
4
4
|
const has = require("lodash/has");
|
|
5
|
-
const matchers = require("
|
|
6
|
-
const { extractGroups } = require("
|
|
7
|
-
const
|
|
5
|
+
const matchers = require("../../validate/matchers");
|
|
6
|
+
const { extractGroups } = require("../../utl/regex-util");
|
|
7
|
+
const IndexedModuleGraph = require("../../graph-utl/indexed-module-graph");
|
|
8
8
|
|
|
9
9
|
function getReachableRules(pRuleSet) {
|
|
10
10
|
return (pRuleSet?.forbidden ?? [])
|
|
@@ -105,14 +105,14 @@ function shouldAddReachable(pRule, pModuleTo, pGraph) {
|
|
|
105
105
|
return lReturnValue;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
function addReachesToModule(pModule, pGraph, pReachableRule) {
|
|
108
|
+
function addReachesToModule(pModule, pGraph, pIndexedGraph, pReachableRule) {
|
|
109
109
|
const lToModules = pGraph.filter((pToModule) =>
|
|
110
110
|
isModuleInRuleTo(pReachableRule, pToModule, pModule)
|
|
111
111
|
);
|
|
112
112
|
|
|
113
113
|
for (let lToModule of lToModules) {
|
|
114
114
|
if (pModule.source !== lToModule.source) {
|
|
115
|
-
const lPath = getPath(
|
|
115
|
+
const lPath = pIndexedGraph.getPath(pModule.source, lToModule.source);
|
|
116
116
|
|
|
117
117
|
if (lPath.length > 0) {
|
|
118
118
|
pModule.reaches = mergeReachesProperties(
|
|
@@ -127,7 +127,7 @@ function addReachesToModule(pModule, pGraph, pReachableRule) {
|
|
|
127
127
|
return pModule;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
function addReachableToModule(pModule, pGraph, pReachableRule) {
|
|
130
|
+
function addReachableToModule(pModule, pGraph, pIndexedGraph, pReachableRule) {
|
|
131
131
|
const lFromModules = pGraph.filter(isModuleInRuleFrom(pReachableRule));
|
|
132
132
|
let lFound = false;
|
|
133
133
|
|
|
@@ -137,7 +137,7 @@ function addReachableToModule(pModule, pGraph, pReachableRule) {
|
|
|
137
137
|
pModule.source !== lFromModule.source &&
|
|
138
138
|
isModuleInRuleTo(pReachableRule, pModule, lFromModule)
|
|
139
139
|
) {
|
|
140
|
-
const lPath = getPath(
|
|
140
|
+
const lPath = pIndexedGraph.getPath(lFromModule.source, pModule.source);
|
|
141
141
|
|
|
142
142
|
lFound = lPath.length > 0;
|
|
143
143
|
pModule.reachable = mergeReachableProperties(
|
|
@@ -151,17 +151,23 @@ function addReachableToModule(pModule, pGraph, pReachableRule) {
|
|
|
151
151
|
return pModule;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
function addReachabilityToGraph(pGraph, pReachableRule) {
|
|
154
|
+
function addReachabilityToGraph(pGraph, pIndexedGraph, pReachableRule) {
|
|
155
155
|
return pGraph.map((pModule) => {
|
|
156
|
-
let lClonedModule =
|
|
156
|
+
let lClonedModule = cloneDeep(pModule);
|
|
157
157
|
|
|
158
158
|
if (shouldAddReaches(pReachableRule, lClonedModule)) {
|
|
159
|
-
lClonedModule = addReachesToModule(
|
|
159
|
+
lClonedModule = addReachesToModule(
|
|
160
|
+
lClonedModule,
|
|
161
|
+
pGraph,
|
|
162
|
+
pIndexedGraph,
|
|
163
|
+
pReachableRule
|
|
164
|
+
);
|
|
160
165
|
}
|
|
161
166
|
if (shouldAddReachable(pReachableRule, lClonedModule, pGraph)) {
|
|
162
167
|
lClonedModule = addReachableToModule(
|
|
163
168
|
lClonedModule,
|
|
164
169
|
pGraph,
|
|
170
|
+
pIndexedGraph,
|
|
165
171
|
pReachableRule
|
|
166
172
|
);
|
|
167
173
|
}
|
|
@@ -171,9 +177,12 @@ function addReachabilityToGraph(pGraph, pReachableRule) {
|
|
|
171
177
|
|
|
172
178
|
module.exports = function deriveReachables(pGraph, pRuleSet) {
|
|
173
179
|
const lReachableRules = pRuleSet ? getReachableRules(pRuleSet) : [];
|
|
180
|
+
const lIndexedGraph =
|
|
181
|
+
lReachableRules.length > 0 ? new IndexedModuleGraph(pGraph) : {};
|
|
174
182
|
|
|
175
183
|
return lReachableRules.reduce(
|
|
176
|
-
(pReturnGraph, pRule) =>
|
|
177
|
-
|
|
184
|
+
(pReturnGraph, pRule) =>
|
|
185
|
+
addReachabilityToGraph(pReturnGraph, lIndexedGraph, pRule),
|
|
186
|
+
cloneDeep(pGraph)
|
|
178
187
|
);
|
|
179
188
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const bus = require("../utl/bus");
|
|
2
2
|
const busLogLevels = require("../utl/bus-log-levels");
|
|
3
3
|
const addFocus = require("../../src/graph-utl/add-focus");
|
|
4
|
+
const IndexedModuleGraph = require("../graph-utl/indexed-module-graph");
|
|
4
5
|
const deriveCycles = require("./derive/circular");
|
|
5
6
|
const deriveOrphans = require("./derive/orphan");
|
|
6
7
|
const addDependents = require("./derive/dependents");
|
|
@@ -8,14 +9,13 @@ const deriveReachable = require("./derive/reachable");
|
|
|
8
9
|
const addValidations = require("./add-validations");
|
|
9
10
|
const softenKnownViolations = require("./soften-known-violations");
|
|
10
11
|
const deriveModuleMetrics = require("./derive/metrics");
|
|
11
|
-
const { findModuleByName } = require("./derive/module-utl");
|
|
12
12
|
|
|
13
13
|
module.exports = function enrichModules(pModules, pOptions) {
|
|
14
14
|
bus.emit("progress", "analyzing: cycles", { level: busLogLevels.INFO });
|
|
15
|
-
|
|
15
|
+
const lIndexedModules = new IndexedModuleGraph(pModules);
|
|
16
|
+
let lModules = deriveCycles(pModules, lIndexedModules, {
|
|
16
17
|
pSourceAttribute: "source",
|
|
17
18
|
pDependencyName: "resolved",
|
|
18
|
-
pFindNodeByName: findModuleByName,
|
|
19
19
|
});
|
|
20
20
|
bus.emit("progress", "analyzing: dependents", { level: busLogLevels.INFO });
|
|
21
21
|
lModules = addDependents(lModules, pOptions);
|
package/src/enrich/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const enrichModules = require("./enrich-modules");
|
|
2
2
|
const aggregateToFolders = require("./derive/folders");
|
|
3
3
|
const summarize = require("./summarize");
|
|
4
|
-
const clearCaches = require("./clear-caches");
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Enriches the passed modules with things like cycle detection, validations,
|
|
@@ -17,11 +16,9 @@ const clearCaches = require("./clear-caches");
|
|
|
17
16
|
* @returns {import("../..").ICruiseResult}
|
|
18
17
|
*/
|
|
19
18
|
module.exports = function enrich(pModules, pOptions, pFileAndDirectoryArray) {
|
|
20
|
-
clearCaches();
|
|
21
19
|
const lModules = enrichModules(pModules, pOptions);
|
|
22
20
|
const lFolders = aggregateToFolders(lModules, pOptions);
|
|
23
21
|
|
|
24
|
-
clearCaches();
|
|
25
22
|
const lReturnValue = {
|
|
26
23
|
modules: lModules,
|
|
27
24
|
...lFolders,
|
|
@@ -53,19 +53,19 @@ function gatherScannableFilesFromDirectory(pDirectoryName, pOptions) {
|
|
|
53
53
|
shouldNotBeExcluded(pathToPosix(pFullPathToFile), pOptions)
|
|
54
54
|
)
|
|
55
55
|
.reduce((pSum, pFullPathToFile) => {
|
|
56
|
-
let lStat = {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
56
|
+
let lStat = fs.statSync(path.join(pOptions.baseDir, pFullPathToFile), {
|
|
57
|
+
throwIfNoEntry: false,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (lStat) {
|
|
61
|
+
if (lStat.isDirectory()) {
|
|
62
|
+
return pSum.concat(
|
|
63
|
+
gatherScannableFilesFromDirectory(pFullPathToFile, pOptions)
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
if (fileIsScannable(pOptions, pFullPathToFile)) {
|
|
67
|
+
return pSum.concat(pFullPathToFile);
|
|
68
|
+
}
|
|
69
69
|
}
|
|
70
70
|
return pSum;
|
|
71
71
|
}, [])
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
+
/* eslint-disable security/detect-object-injection */
|
|
1
2
|
module.exports = class IndexedModuleGraph {
|
|
2
|
-
init(pModules) {
|
|
3
|
+
init(pModules, pIndexAttribute) {
|
|
3
4
|
this.indexedGraph = new Map(
|
|
4
|
-
pModules.map((pModule) => [pModule
|
|
5
|
+
pModules.map((pModule) => [pModule[pIndexAttribute], pModule])
|
|
5
6
|
);
|
|
6
7
|
}
|
|
7
8
|
|
|
8
|
-
constructor(pModules) {
|
|
9
|
-
this.init(pModules);
|
|
9
|
+
constructor(pModules, pIndexAttribute = "source") {
|
|
10
|
+
this.init(pModules, pIndexAttribute);
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
|
-
*
|
|
14
14
|
* @param {string} pName
|
|
15
|
-
* @return {import("
|
|
15
|
+
* @return {import("..").IModule}
|
|
16
16
|
*/
|
|
17
17
|
findModuleByName(pName) {
|
|
18
18
|
return this.indexedGraph.get(pName);
|
|
@@ -102,4 +102,78 @@ module.exports = class IndexedModuleGraph {
|
|
|
102
102
|
}
|
|
103
103
|
return lReturnValue;
|
|
104
104
|
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {string} pFrom
|
|
108
|
+
* @param {string} pTo
|
|
109
|
+
* @param {Set<string>} pVisited
|
|
110
|
+
* @returns {string[]}
|
|
111
|
+
*/
|
|
112
|
+
getPath(pFrom, pTo, pVisited = new Set()) {
|
|
113
|
+
let lReturnValue = [];
|
|
114
|
+
const lFromNode = this.findModuleByName(pFrom);
|
|
115
|
+
|
|
116
|
+
pVisited.add(pFrom);
|
|
117
|
+
|
|
118
|
+
if (lFromNode) {
|
|
119
|
+
const lDirectUnvisitedDependencies = lFromNode.dependencies
|
|
120
|
+
.filter((pDependency) => !pVisited.has(pDependency.resolved))
|
|
121
|
+
.map((pDependency) => pDependency.resolved);
|
|
122
|
+
if (lDirectUnvisitedDependencies.includes(pTo)) {
|
|
123
|
+
lReturnValue = [pFrom, pTo];
|
|
124
|
+
} else {
|
|
125
|
+
for (const lFrom of lDirectUnvisitedDependencies) {
|
|
126
|
+
let lCandidatePath = this.getPath(lFrom, pTo, pVisited);
|
|
127
|
+
// eslint-disable-next-line max-depth
|
|
128
|
+
if (lCandidatePath.length > 0) {
|
|
129
|
+
lReturnValue = [pFrom].concat(lCandidatePath);
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return lReturnValue;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Returns the first non-zero length path from pInitialSource to pInitialSource
|
|
140
|
+
* Returns the empty array if there is no such path
|
|
141
|
+
*
|
|
142
|
+
* @param {string} pInitialSource The 'source' attribute of the node to be tested
|
|
143
|
+
* (source uniquely identifying a node)
|
|
144
|
+
* @param {string} pCurrentSource The 'source' attribute of the 'to' node to
|
|
145
|
+
* be traversed
|
|
146
|
+
* @param {string} pDependencyName The attribute name of the dependency to use.
|
|
147
|
+
* defaults to "resolved" (which is in use for
|
|
148
|
+
* modules). For folders pass "name"
|
|
149
|
+
* @return {string[]} see description above
|
|
150
|
+
*/
|
|
151
|
+
getCycle(pInitialSource, pCurrentSource, pDependencyName, pVisited) {
|
|
152
|
+
let lVisited = pVisited || new Set();
|
|
153
|
+
const lCurrentNode = this.findModuleByName(pCurrentSource);
|
|
154
|
+
const lDependencies = lCurrentNode.dependencies.filter(
|
|
155
|
+
(pDependency) => !lVisited.has(pDependency[pDependencyName])
|
|
156
|
+
);
|
|
157
|
+
const lMatch = lDependencies.find(
|
|
158
|
+
(pDependency) => pDependency[pDependencyName] === pInitialSource
|
|
159
|
+
);
|
|
160
|
+
if (lMatch) {
|
|
161
|
+
return [pCurrentSource, lMatch[pDependencyName]];
|
|
162
|
+
}
|
|
163
|
+
return lDependencies.reduce((pAll, pDependency) => {
|
|
164
|
+
if (!pAll.includes(pCurrentSource)) {
|
|
165
|
+
const lCycle = this.getCycle(
|
|
166
|
+
pInitialSource,
|
|
167
|
+
pDependency[pDependencyName],
|
|
168
|
+
pDependencyName,
|
|
169
|
+
lVisited.add(pDependency[pDependencyName])
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
if (lCycle.length > 0 && !lCycle.includes(pCurrentSource)) {
|
|
173
|
+
return pAll.concat(pCurrentSource).concat(lCycle);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return pAll;
|
|
177
|
+
}, []);
|
|
178
|
+
}
|
|
105
179
|
};
|
package/src/meta.js
CHANGED
|
@@ -13,12 +13,10 @@ const pathToPosix = require("./path-to-posix");
|
|
|
13
13
|
* @returns {boolean}
|
|
14
14
|
*/
|
|
15
15
|
function fileIsDirectory(pFullPathToFile, pBaseDirectory) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
16
|
+
const lStat = statSync(join(pBaseDirectory, pFullPathToFile), {
|
|
17
|
+
throwIfNoEntry: false,
|
|
18
|
+
});
|
|
19
|
+
return lStat?.isDirectory() ?? false;
|
|
22
20
|
}
|
|
23
21
|
|
|
24
22
|
/**
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/* eslint-disable security/detect-object-injection */
|
|
2
|
-
/* about the absence of checks whether attributes/ objects actually
|
|
3
|
-
* exist:
|
|
4
|
-
* - it saves CPU cycles to the effect of being ~30% faster than with the
|
|
5
|
-
* checks
|
|
6
|
-
* - .dependencies is a mandatory attribute (as per json schema)
|
|
7
|
-
* - .resolved is a mandatory attribute (as per json schema)
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Returns the first non-zero length path from pInitialSource to pInitialSource
|
|
12
|
-
* Returns the empty array if there is no such path
|
|
13
|
-
*
|
|
14
|
-
* @param {any} pGraph The graph in which to test this condition
|
|
15
|
-
* @param {string} pInitialSource The 'source' attribute of the node to be tested
|
|
16
|
-
* (source uniquely identifying a node)
|
|
17
|
-
* @param {string} pCurrentSource The 'source' attribute of the 'to' node to
|
|
18
|
-
* be traversed
|
|
19
|
-
* @param {string} pDependencyName The attribute name of the dependency to use.
|
|
20
|
-
* defaults to "resolved" (which is in use for
|
|
21
|
-
* modules). For folders pass "name"
|
|
22
|
-
* @param {function} pFindNodeByName Function taking a Graph and a pCurrentSource string
|
|
23
|
-
* that returns the Node in that graph going by
|
|
24
|
-
* that name. Defaults to findModuleByName (which
|
|
25
|
-
* is in use for modules). For folders pass
|
|
26
|
-
* findFolderByName
|
|
27
|
-
* @param {Set} pVisited The set of nodes visited hithereto (optional
|
|
28
|
-
* attribute - there's no need to pass it when
|
|
29
|
-
* calling it; it's used by the algorithm to
|
|
30
|
-
* determine the stop condition)
|
|
31
|
-
* @return {string[]} see description above
|
|
32
|
-
*/
|
|
33
|
-
// eslint-disable-next-line max-params
|
|
34
|
-
function getCycle(
|
|
35
|
-
pGraph,
|
|
36
|
-
pInitialSource,
|
|
37
|
-
pCurrentSource,
|
|
38
|
-
{ pDependencyName, pFindNodeByName },
|
|
39
|
-
pVisited
|
|
40
|
-
) {
|
|
41
|
-
let lVisited = pVisited || new Set();
|
|
42
|
-
const lCurrentNode = pFindNodeByName(pGraph, pCurrentSource);
|
|
43
|
-
const lDependencies = lCurrentNode.dependencies.filter(
|
|
44
|
-
(pDependency) => !lVisited.has(pDependency[pDependencyName])
|
|
45
|
-
);
|
|
46
|
-
const lMatch = lDependencies.find(
|
|
47
|
-
(pDependency) => pDependency[pDependencyName] === pInitialSource
|
|
48
|
-
);
|
|
49
|
-
if (lMatch) {
|
|
50
|
-
return [pCurrentSource, lMatch[pDependencyName]];
|
|
51
|
-
}
|
|
52
|
-
return lDependencies.reduce((pAll, pDependency) => {
|
|
53
|
-
if (!pAll.includes(pCurrentSource)) {
|
|
54
|
-
const lCycle = getCycle(
|
|
55
|
-
pGraph,
|
|
56
|
-
pInitialSource,
|
|
57
|
-
pDependency[pDependencyName],
|
|
58
|
-
{ pDependencyName, pFindNodeByName },
|
|
59
|
-
lVisited.add(pDependency[pDependencyName])
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
if (lCycle.length > 0 && !lCycle.includes(pCurrentSource)) {
|
|
63
|
-
return pAll.concat(pCurrentSource).concat(lCycle);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return pAll;
|
|
67
|
-
}, []);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
module.exports = getCycle;
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
const { findModuleByName } = require("../module-utl");
|
|
2
|
-
|
|
3
|
-
function getPath(pGraph, pFrom, pTo, pVisited = new Set()) {
|
|
4
|
-
let lReturnValue = [];
|
|
5
|
-
const lFromNode = findModuleByName(pGraph, pFrom);
|
|
6
|
-
|
|
7
|
-
pVisited.add(pFrom);
|
|
8
|
-
|
|
9
|
-
if (lFromNode) {
|
|
10
|
-
const lDirectUnvisitedDependencies = lFromNode.dependencies
|
|
11
|
-
.filter((pDependency) => !pVisited.has(pDependency.resolved))
|
|
12
|
-
.map((pDependency) => pDependency.resolved);
|
|
13
|
-
if (lDirectUnvisitedDependencies.includes(pTo)) {
|
|
14
|
-
lReturnValue = [pFrom, pTo];
|
|
15
|
-
} else {
|
|
16
|
-
for (const lFrom of lDirectUnvisitedDependencies) {
|
|
17
|
-
let lCandidatePath = getPath(pGraph, lFrom, pTo, pVisited);
|
|
18
|
-
// eslint-disable-next-line max-depth
|
|
19
|
-
if (lCandidatePath.length > 0) {
|
|
20
|
-
lReturnValue = [pFrom].concat(lCandidatePath);
|
|
21
|
-
break;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return lReturnValue;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
module.exports = getPath;
|