dependency-cruiser 17.3.4 → 17.3.5
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/package.json +1 -1
- package/src/cache/cache.mjs +1 -1
- package/src/cache/find-content-changes.mjs +4 -4
- package/src/cache/metadata-strategy.mjs +4 -3
- package/src/cli/listeners/ndjson.mjs +1 -1
- package/src/cli/listeners/performance-log/format-helpers.mjs +2 -2
- package/src/cli/listeners/performance-log/handlers.mjs +1 -1
- package/src/cli/listeners/performance-log/index.mjs +1 -1
- package/src/enrich/derive/circular.mjs +14 -9
- package/src/enrich/derive/dependents.mjs +15 -13
- package/src/enrich/derive/folders/aggregate-to-folders.mjs +1 -2
- package/src/enrich/derive/metrics/get-module-metrics.mjs +4 -7
- package/src/enrich/derive/metrics/index.mjs +7 -3
- package/src/enrich/derive/module-utl.mjs +0 -7
- package/src/enrich/derive/orphan/index.mjs +12 -1
- package/src/enrich/derive/orphan/is-orphan.mjs +9 -4
- package/src/enrich/enrich-modules.mjs +2 -3
- package/src/extract/index.mjs +2 -2
- package/src/extract/resolve/resolve.mjs +14 -2
- package/src/graph-utl/module-graph-with-dependency-set.mjs +41 -0
- package/src/main/cruise.mjs +6 -6
- package/src/main/report-wrap.mjs +3 -0
- package/src/main/rule-set/assert-validity.mjs +4 -4
- package/src/main/rule-set/normalize.mjs +1 -1
- package/src/meta.cjs +1 -1
- package/src/utl/bus.mjs +4 -0
- package/types/resolve-options.d.mts +0 -1
package/package.json
CHANGED
package/src/cache/cache.mjs
CHANGED
|
@@ -87,7 +87,7 @@ export default class Cache {
|
|
|
87
87
|
},
|
|
88
88
|
));
|
|
89
89
|
this.#revisionData.cacheFormatVersion = CACHE_FORMAT_VERSION;
|
|
90
|
-
bus.debug("cache:
|
|
90
|
+
bus.debug("cache: compare");
|
|
91
91
|
return (
|
|
92
92
|
this.cacheFormatVersionCompatible(pCachedCruiseResult) &&
|
|
93
93
|
this.#cacheStrategy.revisionDataEqual(
|
|
@@ -83,7 +83,7 @@ export default function findContentChanges(
|
|
|
83
83
|
pCachedCruiseResult,
|
|
84
84
|
pOptions,
|
|
85
85
|
) {
|
|
86
|
-
bus.debug("cache:
|
|
86
|
+
bus.debug("cache: content: get revision data");
|
|
87
87
|
const lFileSet = new Set(
|
|
88
88
|
findAllFiles(pDirectory, {
|
|
89
89
|
baseDir: pOptions.baseDir,
|
|
@@ -92,12 +92,12 @@ export default function findContentChanges(
|
|
|
92
92
|
}).filter(hasInterestingExtension(pOptions.extensions)),
|
|
93
93
|
);
|
|
94
94
|
|
|
95
|
-
bus.debug("cache:
|
|
95
|
+
bus.debug("cache: content: get (cached - new)");
|
|
96
96
|
const lDiffCachedVsNew = pCachedCruiseResult.modules.map(
|
|
97
97
|
diffCachedModuleAgainstFileSet(lFileSet, pOptions.baseDir),
|
|
98
98
|
);
|
|
99
99
|
|
|
100
|
-
bus.debug("cache:
|
|
100
|
+
bus.debug("cache: content: get (new - cached)");
|
|
101
101
|
// eslint-disable-next-line budapestian/local-variable-pattern
|
|
102
102
|
for (const { name } of lDiffCachedVsNew) {
|
|
103
103
|
lFileSet.delete(name);
|
|
@@ -112,6 +112,6 @@ export default function findContentChanges(
|
|
|
112
112
|
});
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
bus.debug("cache:
|
|
115
|
+
bus.debug("cache: content: return revision data");
|
|
116
116
|
return lDiffCachedVsNew.concat(lDiffNewVsCached);
|
|
117
117
|
}
|
|
@@ -21,6 +21,7 @@ export default class MetaDataStrategy {
|
|
|
21
21
|
/**
|
|
22
22
|
* @param {string} _pDirectory
|
|
23
23
|
* @param {ICruiseResult} _pCachedCruiseResult
|
|
24
|
+
* @param {IStrictCruiseOptions} pCruiseOptions
|
|
24
25
|
* @param {Object} pOptions
|
|
25
26
|
* @param {Set<string>} pOptions.extensions
|
|
26
27
|
* @param {Set<changeType>=} pOptions.interestingChangeTypes
|
|
@@ -42,9 +43,9 @@ export default class MetaDataStrategy {
|
|
|
42
43
|
...pOptions,
|
|
43
44
|
};
|
|
44
45
|
try {
|
|
45
|
-
bus.debug("cache:
|
|
46
|
+
bus.debug("cache: metadata: get sha");
|
|
46
47
|
const lSHA = await lOptions.shaRetrievalFn();
|
|
47
|
-
bus.debug("cache:
|
|
48
|
+
bus.debug("cache: metadata: get diff");
|
|
48
49
|
const lDiff = /** @type {IChange[]} */ (
|
|
49
50
|
await lOptions.diffListFn({ oldRevision: lSHA })
|
|
50
51
|
);
|
|
@@ -55,7 +56,7 @@ export default class MetaDataStrategy {
|
|
|
55
56
|
)
|
|
56
57
|
.filter(changeHasInterestingExtension(lOptions.extensions))
|
|
57
58
|
.filter(isInterestingChangeType(lOptions.interestingChangeTypes));
|
|
58
|
-
bus.debug("cache:
|
|
59
|
+
bus.debug("cache: metadata: sha-sum diff");
|
|
59
60
|
return {
|
|
60
61
|
SHA1: lSHA,
|
|
61
62
|
changes: lChanges.map(lOptions.checksumFn),
|
|
@@ -76,7 +76,7 @@ export default function setUpNDJSONListener(
|
|
|
76
76
|
) {
|
|
77
77
|
let lState = {
|
|
78
78
|
runStartTime: new Date(Date.now()).toISOString(),
|
|
79
|
-
previousMessage: "nodejs
|
|
79
|
+
previousMessage: "startup: nodejs loading",
|
|
80
80
|
previousTime: 0,
|
|
81
81
|
previousUserUsage: 0,
|
|
82
82
|
previousSystemUsage: 0,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { styleText } from "node:util";
|
|
2
|
-
import { INFO } from "#utl/bus.mjs";
|
|
2
|
+
import { INFO, SUMMARY } from "#utl/bus.mjs";
|
|
3
3
|
|
|
4
4
|
const MS_PER_SECOND = 1000;
|
|
5
5
|
const MS_PER_MICRO_SECOND = 0.001;
|
|
@@ -64,7 +64,7 @@ function formatMessage(pMessage, pLevel) {
|
|
|
64
64
|
export function formatTime(
|
|
65
65
|
pNumber,
|
|
66
66
|
pConversionMultiplier = MS_PER_SECOND,
|
|
67
|
-
pLevel =
|
|
67
|
+
pLevel = SUMMARY,
|
|
68
68
|
) {
|
|
69
69
|
return formatMessage(
|
|
70
70
|
gTimeFormat(pConversionMultiplier * pNumber)
|
|
@@ -50,7 +50,7 @@ export function getEndText(pState, pLevel, pMaxLevel) {
|
|
|
50
50
|
const lTime = process.uptime();
|
|
51
51
|
const { user, system } = process.cpuUsage();
|
|
52
52
|
const { heapUsed, heapTotal, external, rss } = process.memoryUsage();
|
|
53
|
-
pState.previousMessage = "really done";
|
|
53
|
+
pState.previousMessage = "end: really done";
|
|
54
54
|
|
|
55
55
|
return (
|
|
56
56
|
getProgressLine("", pState, pLevel, pMaxLevel) +
|
|
@@ -35,7 +35,7 @@ export default function setUpPerformanceLogListener(
|
|
|
35
35
|
pStream = process.stderr,
|
|
36
36
|
) {
|
|
37
37
|
let lState = {
|
|
38
|
-
previousMessage: "nodejs
|
|
38
|
+
previousMessage: "startup: nodejs loading",
|
|
39
39
|
previousTime: 0,
|
|
40
40
|
previousUserUsage: 0,
|
|
41
41
|
previousSystemUsage: 0,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* eslint-disable security/detect-object-injection */
|
|
2
|
+
import IndexedModuleGraph from "#graph-utl/indexed-module-graph.mjs";
|
|
2
3
|
|
|
3
4
|
/** @import { IFlattenedRuleSet } from "../../../types/rule-set.mjs" */
|
|
4
5
|
|
|
@@ -44,29 +45,33 @@ function addCircularityCheckToDependency(
|
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
/**
|
|
47
|
-
* Runs through all dependencies of all
|
|
48
|
+
* Runs through all dependencies of all pModulesOrFolders, for each of them determines
|
|
48
49
|
* whether it's (part of a) circular (relationship) and returns the
|
|
49
50
|
* dependencies with that added.
|
|
50
51
|
*/
|
|
51
52
|
export default function detectAndAddCycles(
|
|
52
|
-
|
|
53
|
-
pIndexedNodes,
|
|
53
|
+
pModulesOrFolders,
|
|
54
54
|
{ pSourceAttribute, pDependencyName, pSkipAnalysisNotInRules, pRuleSet },
|
|
55
55
|
) {
|
|
56
56
|
if (!pSkipAnalysisNotInRules || hasCycleRule(pRuleSet)) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
const lIndexedGraph = new IndexedModuleGraph(
|
|
58
|
+
pModulesOrFolders,
|
|
59
|
+
pSourceAttribute,
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
return pModulesOrFolders.map((pModuleOrFolder) => ({
|
|
63
|
+
...pModuleOrFolder,
|
|
64
|
+
dependencies: pModuleOrFolder.dependencies.map((pToDep) =>
|
|
60
65
|
addCircularityCheckToDependency(
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
lIndexedGraph,
|
|
67
|
+
pModuleOrFolder[pSourceAttribute],
|
|
63
68
|
pToDep,
|
|
64
69
|
pDependencyName,
|
|
65
70
|
),
|
|
66
71
|
),
|
|
67
72
|
}));
|
|
68
73
|
}
|
|
69
|
-
return
|
|
74
|
+
return pModulesOrFolders.map((pModule) => ({
|
|
70
75
|
...pModule,
|
|
71
76
|
dependencies: pModule.dependencies.map((pToDep) => ({
|
|
72
77
|
...pToDep,
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { isDependent } from "./module-utl.mjs";
|
|
2
|
-
|
|
3
1
|
/** @import { IFlattenedRuleSet } from "../../../types/rule-set.mjs" */
|
|
4
2
|
|
|
3
|
+
import ModuleGraphWithDependencySet from "#graph-utl/module-graph-with-dependency-set.mjs";
|
|
4
|
+
|
|
5
5
|
function isDependentsRule(pRule) {
|
|
6
6
|
// used in folder rules and when moreUnstable is in the 'to' => governed by
|
|
7
|
-
// the 'metrics' flag in options,
|
|
7
|
+
// the 'metrics' flag in options, so not going to repeat that here
|
|
8
8
|
|
|
9
|
-
// dependents are used in the orphans
|
|
10
|
-
// where it does its own analysis
|
|
9
|
+
// dependents are used in the orphans analysis. However, there is a fall back
|
|
10
|
+
// where it does its own analysis which is faster on itself, so not going
|
|
11
|
+
// to repeat that check here either.
|
|
11
12
|
return (
|
|
12
13
|
/* c8 ignore start */
|
|
13
14
|
Object.hasOwn(pRule?.module ?? {}, "numberOfDependentsLessThan") ||
|
|
@@ -16,13 +17,6 @@ function isDependentsRule(pRule) {
|
|
|
16
17
|
);
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
export function getDependents(pModule, pModules) {
|
|
20
|
-
// perf between O(n) in an unconnected graph and O(n^2) in a fully connected one
|
|
21
|
-
return pModules
|
|
22
|
-
.filter(isDependent(pModule.source))
|
|
23
|
-
.map((pDependentModule) => pDependentModule.source);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
20
|
/**
|
|
27
21
|
* @param {IFlattenedRuleSet} pRuleSet
|
|
28
22
|
* @returns {boolean}
|
|
@@ -39,9 +33,17 @@ export default function addDependents(
|
|
|
39
33
|
{ skipAnalysisNotInRules, metrics, ruleSet },
|
|
40
34
|
) {
|
|
41
35
|
if (!skipAnalysisNotInRules || metrics || hasDependentsRule(ruleSet)) {
|
|
36
|
+
// creating this optimized structure here might seem overkill, but on
|
|
37
|
+
// large graphs it pays off significantly - creation is a few centiseconds
|
|
38
|
+
// but it cuts analysis in half on large graphs (and even on smaller
|
|
39
|
+
// graphs it's a win, even though just a few milliseconds)
|
|
40
|
+
const lModulesWithDependencySet = new ModuleGraphWithDependencySet(
|
|
41
|
+
pModules,
|
|
42
|
+
);
|
|
43
|
+
|
|
42
44
|
return pModules.map((pModule) => ({
|
|
43
45
|
...pModule,
|
|
44
|
-
dependents: getDependents(pModule
|
|
46
|
+
dependents: lModulesWithDependencySet.getDependents(pModule),
|
|
45
47
|
}));
|
|
46
48
|
}
|
|
47
49
|
return pModules;
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
getParentFolders,
|
|
10
10
|
object2Array,
|
|
11
11
|
} from "./utl.mjs";
|
|
12
|
-
import IndexedModuleGraph from "#graph-utl/indexed-module-graph.mjs";
|
|
13
12
|
import { uniq } from "#utl/array-util.mjs";
|
|
14
13
|
|
|
15
14
|
function upsertCouplings(pAllDependents, pNewDependents) {
|
|
@@ -148,7 +147,7 @@ export default function aggregateToFolders(pModules, pOptions = {}) {
|
|
|
148
147
|
.map(deNormalizeInstability);
|
|
149
148
|
lFolders = lFolders.concat(getSinks(lFolders));
|
|
150
149
|
|
|
151
|
-
return detectCycles(lFolders,
|
|
150
|
+
return detectCycles(lFolders, {
|
|
152
151
|
pSourceAttribute: "name",
|
|
153
152
|
pDependencyName: "name",
|
|
154
153
|
pSkipAnalysisNotInRules: pOptions.skipAnalysisNotInRules,
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { calculateInstability, metricsAreCalculable } from "../module-utl.mjs";
|
|
2
|
-
import IndexedModuleGraph from "#graph-utl/indexed-module-graph.mjs";
|
|
3
2
|
|
|
4
3
|
export function addInstabilityMetric(pModule) {
|
|
5
4
|
return {
|
|
@@ -15,25 +14,23 @@ export function addInstabilityMetric(pModule) {
|
|
|
15
14
|
};
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
function addInstabilityToDependency(
|
|
19
|
-
const lIndexedModules = new IndexedModuleGraph(pAllModules);
|
|
17
|
+
function addInstabilityToDependency(pIndexedModules) {
|
|
20
18
|
return (pDependency) => ({
|
|
21
19
|
...pDependency,
|
|
22
20
|
instability:
|
|
23
|
-
(
|
|
21
|
+
(pIndexedModules.findVertexByName(pDependency.resolved) || {})
|
|
24
22
|
.instability || 0,
|
|
25
23
|
});
|
|
26
24
|
}
|
|
27
25
|
|
|
28
26
|
export function deNormalizeInstabilityMetricsToDependencies(
|
|
29
27
|
pModule,
|
|
30
|
-
|
|
31
|
-
pAllModules,
|
|
28
|
+
pIndexedModules,
|
|
32
29
|
) {
|
|
33
30
|
return {
|
|
34
31
|
...pModule,
|
|
35
32
|
dependencies: pModule.dependencies.map(
|
|
36
|
-
addInstabilityToDependency(
|
|
33
|
+
addInstabilityToDependency(pIndexedModules),
|
|
37
34
|
),
|
|
38
35
|
};
|
|
39
36
|
}
|
|
@@ -2,12 +2,16 @@ import {
|
|
|
2
2
|
addInstabilityMetric,
|
|
3
3
|
deNormalizeInstabilityMetricsToDependencies,
|
|
4
4
|
} from "./get-module-metrics.mjs";
|
|
5
|
+
import IndexedModuleGraph from "#graph-utl/indexed-module-graph.mjs";
|
|
5
6
|
|
|
6
7
|
export default function deriveModulesMetrics(pModules, pOptions) {
|
|
7
8
|
if (pOptions.metrics) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
const lModules = pModules.map(addInstabilityMetric);
|
|
10
|
+
const lIndexedModules = new IndexedModuleGraph(lModules);
|
|
11
|
+
|
|
12
|
+
return lModules.map((pModule) =>
|
|
13
|
+
deNormalizeInstabilityMetricsToDependencies(pModule, lIndexedModules),
|
|
14
|
+
);
|
|
11
15
|
}
|
|
12
16
|
return pModules;
|
|
13
17
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import isOrphan from "./is-orphan.mjs";
|
|
2
|
+
import ModuleGraphWithDependencySet from "#graph-utl/module-graph-with-dependency-set.mjs";
|
|
2
3
|
|
|
3
4
|
/** @import { IFlattenedRuleSet } from "../../../../types/rule-set.mjs" */
|
|
4
5
|
|
|
@@ -25,9 +26,19 @@ export default function deriveOrphans(
|
|
|
25
26
|
{ skipAnalysisNotInRules, ruleSet },
|
|
26
27
|
) {
|
|
27
28
|
if (!skipAnalysisNotInRules || hasOrphanRule(ruleSet)) {
|
|
29
|
+
// just like the dependents derivation, creating this data structure
|
|
30
|
+
// might seem overkill, but it can save effort here as well, even though
|
|
31
|
+
// isOrphan has early exits the dependents derivation doesn't:
|
|
32
|
+
// - when the module already has dependencies (quick length check)
|
|
33
|
+
// - when dependents are already calculated (quick length check as well). If
|
|
34
|
+
// they are, the lModulesWithDependencySet is created for naught.
|
|
35
|
+
const lModulesWithDependencySet = new ModuleGraphWithDependencySet(
|
|
36
|
+
pModules,
|
|
37
|
+
);
|
|
38
|
+
|
|
28
39
|
return pModules.map((pModule) => ({
|
|
29
40
|
...pModule,
|
|
30
|
-
orphan: isOrphan(pModule,
|
|
41
|
+
orphan: isOrphan(pModule, lModulesWithDependencySet),
|
|
31
42
|
}));
|
|
32
43
|
}
|
|
33
44
|
return pModules;
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* returns true if the given module has no dependencies and no dependents
|
|
3
|
+
*
|
|
4
|
+
* @param {import("../../../../types/dependency-cruiser.mjs").IModule} pModule
|
|
5
|
+
* @param {import("#graph-utl/module-graph-with-dependency-set.mjs").default} pModulesWithDependencySet
|
|
6
|
+
* @returns {boolean}
|
|
7
|
+
*/
|
|
8
|
+
export default function isOrphan(pModule, pModulesWithDependencySet) {
|
|
4
9
|
if (pModule.dependencies.length > 0) {
|
|
5
10
|
return false;
|
|
6
11
|
}
|
|
@@ -10,5 +15,5 @@ export default function isOrphan(pModule, pGraph) {
|
|
|
10
15
|
return pModule.dependents.length === 0;
|
|
11
16
|
}
|
|
12
17
|
// ... otherwise calculate them
|
|
13
|
-
return !
|
|
18
|
+
return !pModulesWithDependencySet.moduleHasDependents(pModule);
|
|
14
19
|
}
|
|
@@ -5,7 +5,6 @@ import deriveReachable from "./derive/reachable.mjs";
|
|
|
5
5
|
import addValidations from "./add-validations.mjs";
|
|
6
6
|
import softenKnownViolations from "./soften-known-violations.mjs";
|
|
7
7
|
import deriveModuleMetrics from "./derive/metrics/index.mjs";
|
|
8
|
-
import IndexedModuleGraph from "#graph-utl/indexed-module-graph.mjs";
|
|
9
8
|
import addFocus from "#graph-utl/add-focus.mjs";
|
|
10
9
|
import { bus } from "#utl/bus.mjs";
|
|
11
10
|
|
|
@@ -18,8 +17,8 @@ import { bus } from "#utl/bus.mjs";
|
|
|
18
17
|
*/
|
|
19
18
|
export default function enrichModules(pModules, pOptions) {
|
|
20
19
|
bus.info("analyze: cycles");
|
|
21
|
-
|
|
22
|
-
let lModules = deriveCycles(pModules,
|
|
20
|
+
|
|
21
|
+
let lModules = deriveCycles(pModules, {
|
|
23
22
|
pSourceAttribute: "source",
|
|
24
23
|
pDependencyName: "resolved",
|
|
25
24
|
pSkipAnalysisNotInRules: pOptions.skipAnalysisNotInRules,
|
package/src/extract/index.mjs
CHANGED
|
@@ -70,13 +70,13 @@ function extractFileDirectoryArray(
|
|
|
70
70
|
) {
|
|
71
71
|
let lVisited = new Set();
|
|
72
72
|
|
|
73
|
-
bus.
|
|
73
|
+
bus.debug("extract: gather initial sources");
|
|
74
74
|
const lInitialSources = gatherInitialSources(
|
|
75
75
|
pFileDirectoryArray,
|
|
76
76
|
pCruiseOptions,
|
|
77
77
|
);
|
|
78
78
|
|
|
79
|
-
bus.
|
|
79
|
+
bus.debug("extract: visit dependencies");
|
|
80
80
|
const lResult = [];
|
|
81
81
|
for (const lFilename of lInitialSources) {
|
|
82
82
|
if (!lVisited.has(lFilename)) {
|
|
@@ -2,9 +2,21 @@ import enhancedResolve from "enhanced-resolve";
|
|
|
2
2
|
import { stripQueryParameters } from "../helpers.mjs";
|
|
3
3
|
import pathToPosix from "#utl/path-to-posix.mjs";
|
|
4
4
|
|
|
5
|
+
/** @import {IResolveOptions} from "../../../types/resolve-options.mjs" */
|
|
6
|
+
|
|
7
|
+
/** @type {Map<string, enhancedResolve.Resolver>} */
|
|
5
8
|
let gResolvers = new Map();
|
|
9
|
+
/** @type {Map<string, boolean>} */
|
|
6
10
|
let gInitialized = new Map();
|
|
7
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Initializes a resolver for the given caching context if not already done
|
|
14
|
+
*
|
|
15
|
+
* @param {IResolveOptions} pEHResolveOptions Options to pass to enhanced resolve
|
|
16
|
+
* @param {string} pCachingContext - caching
|
|
17
|
+
*
|
|
18
|
+
* @returns {void}
|
|
19
|
+
*/
|
|
8
20
|
function init(pEHResolveOptions, pCachingContext) {
|
|
9
21
|
if (!gInitialized.get(pCachingContext) || pEHResolveOptions.bustTheCache) {
|
|
10
22
|
// assuming the cached file system here
|
|
@@ -23,8 +35,8 @@ function init(pEHResolveOptions, pCachingContext) {
|
|
|
23
35
|
*
|
|
24
36
|
* @param {string} pModuleName The module name to resolve (e.g. 'slodash', './myModule')
|
|
25
37
|
* @param {string} pFileDirectory The directory from which to resolve the module
|
|
26
|
-
* @param {
|
|
27
|
-
* @param {
|
|
38
|
+
* @param {IResolveOptions} pResolveOptions Options to pass to enhanced resolve
|
|
39
|
+
* @param {string} pCachingContext - caching
|
|
28
40
|
*
|
|
29
41
|
* @returns {string} path to the resolved file on disk
|
|
30
42
|
*/
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export default class ModuleGraphWithDependencySet {
|
|
2
|
+
/** @type {Array<{source: string, dependencies: Set<string>}>} */
|
|
3
|
+
#modulesWithDependencySet;
|
|
4
|
+
/**
|
|
5
|
+
* Creates a module graph optimized for querying dependents
|
|
6
|
+
* @constructor
|
|
7
|
+
* @param {import("../../types/dependency-cruiser.mjs").IModule[]} pModules
|
|
8
|
+
*/
|
|
9
|
+
constructor(pModules) {
|
|
10
|
+
this.#modulesWithDependencySet = pModules.map((pModule) => ({
|
|
11
|
+
source: pModule.source,
|
|
12
|
+
dependencies: new Set(
|
|
13
|
+
pModule.dependencies.map((pDependency) => pDependency.resolved),
|
|
14
|
+
),
|
|
15
|
+
}));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Returns true if the given module has dependents in the graph, false otherwise
|
|
20
|
+
*
|
|
21
|
+
* @param {import("../../types/dependency-cruiser.mjs").IModule} pModule
|
|
22
|
+
* @returns {boolean}
|
|
23
|
+
*/
|
|
24
|
+
moduleHasDependents(pModule) {
|
|
25
|
+
return this.#modulesWithDependencySet.some(({ dependencies }) =>
|
|
26
|
+
dependencies.has(pModule.source),
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Returns an array of module source paths that depend on the given module
|
|
32
|
+
*
|
|
33
|
+
* @param {import("../../types/dependency-cruiser.mjs").IModule} pModule
|
|
34
|
+
* @returns {Array<string>}
|
|
35
|
+
*/
|
|
36
|
+
getDependents(pModule) {
|
|
37
|
+
return this.#modulesWithDependencySet
|
|
38
|
+
.filter(({ dependencies }) => dependencies.has(pModule.source))
|
|
39
|
+
.map(({ source }) => source);
|
|
40
|
+
}
|
|
41
|
+
}
|
package/src/main/cruise.mjs
CHANGED
|
@@ -19,7 +19,7 @@ export default async function cruise(
|
|
|
19
19
|
pResolveOptions,
|
|
20
20
|
pTranspileOptions,
|
|
21
21
|
) {
|
|
22
|
-
bus.summary("parse options", c(1));
|
|
22
|
+
bus.summary("startup: parse options", c(1));
|
|
23
23
|
const lCruiseOptionsValid = assertCruiseOptionsValid(pCruiseOptions);
|
|
24
24
|
/** @type {import("../../types/strict-options.js").IStrictCruiseOptions} */
|
|
25
25
|
let lCruiseOptions = normalizeCruiseOptions(
|
|
@@ -47,7 +47,7 @@ export default async function cruise(
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
bus.summary("import analytical modules", c(3));
|
|
50
|
+
bus.summary("startup: import analytical modules", c(3));
|
|
51
51
|
const [
|
|
52
52
|
{ default: normalizeRuleSet },
|
|
53
53
|
{ default: assertRuleSetValid },
|
|
@@ -67,7 +67,7 @@ export default async function cruise(
|
|
|
67
67
|
]);
|
|
68
68
|
|
|
69
69
|
if (lCruiseOptions.ruleSet) {
|
|
70
|
-
bus.summary("parse rule set", c(4));
|
|
70
|
+
bus.summary("startup: parse rule set", c(4));
|
|
71
71
|
lCruiseOptions.ruleSet = normalizeRuleSet(
|
|
72
72
|
assertRuleSetValid(lCruiseOptions.ruleSet),
|
|
73
73
|
);
|
|
@@ -77,14 +77,14 @@ export default async function cruise(
|
|
|
77
77
|
pFileAndDirectoryArray,
|
|
78
78
|
);
|
|
79
79
|
|
|
80
|
-
bus.summary("
|
|
80
|
+
bus.summary("startup: get resolve options", c(5));
|
|
81
81
|
const lNormalizedResolveOptions = await normalizeResolveOptions(
|
|
82
82
|
pResolveOptions,
|
|
83
83
|
lCruiseOptions,
|
|
84
84
|
pTranspileOptions?.tsConfig,
|
|
85
85
|
);
|
|
86
86
|
|
|
87
|
-
bus.summary("
|
|
87
|
+
bus.summary("extract", c(6));
|
|
88
88
|
const lExtractionResult = extract(
|
|
89
89
|
lNormalizedFileAndDirectoryArray,
|
|
90
90
|
lCruiseOptions,
|
|
@@ -107,7 +107,7 @@ export default async function cruise(
|
|
|
107
107
|
bus.summary("report", c(9));
|
|
108
108
|
const lResult = await reportWrap(lCruiseResult, lCruiseOptions);
|
|
109
109
|
|
|
110
|
-
bus.debug("clear regex cache");
|
|
110
|
+
bus.debug("end: clear regex cache");
|
|
111
111
|
clearRegExpCache();
|
|
112
112
|
|
|
113
113
|
return lResult;
|
package/src/main/report-wrap.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { applyFilters } from "#graph-utl/filter-bank.mjs";
|
|
|
4
4
|
import consolidateToPattern from "#graph-utl/consolidate-to-pattern.mjs";
|
|
5
5
|
import { compareModules } from "#graph-utl/compare.mjs";
|
|
6
6
|
import stripSelfTransitions from "#graph-utl/strip-self-transitions.mjs";
|
|
7
|
+
import { bus } from "#utl/bus.mjs";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* @import { ICruiseResult } from "../../types/cruise-result.mjs";
|
|
@@ -54,12 +55,14 @@ function getReporterSection(pOutputType) {
|
|
|
54
55
|
* @returns {IReporterOutput}
|
|
55
56
|
*/
|
|
56
57
|
export default async function reportWrap(pResult, pFormatOptions) {
|
|
58
|
+
bus.debug("report: get");
|
|
57
59
|
const lReportFunction = await getReporter(pFormatOptions.outputType);
|
|
58
60
|
const lReportOptions =
|
|
59
61
|
pResult.summary.optionsUsed?.reporterOptions?.[
|
|
60
62
|
getReporterSection(pFormatOptions.outputType)
|
|
61
63
|
] ?? {};
|
|
62
64
|
|
|
65
|
+
bus.debug("report: execute");
|
|
63
66
|
return lReportFunction(
|
|
64
67
|
reSummarizeResults(pResult, pFormatOptions),
|
|
65
68
|
// passing format options here so reporters that read collapse patterns
|
|
@@ -80,16 +80,16 @@ function assertRuleSafety(pRule) {
|
|
|
80
80
|
* @throws {Error} An error with the reason for the error as a message
|
|
81
81
|
*/
|
|
82
82
|
export default function assertRuleSetValid(pConfiguration) {
|
|
83
|
-
bus.
|
|
83
|
+
bus.debug("startup: parse rule set: validate");
|
|
84
84
|
|
|
85
|
-
bus.
|
|
85
|
+
bus.trace("startup: parse rule set: validate: schema");
|
|
86
86
|
assertSchemaCompliance(pConfiguration);
|
|
87
87
|
|
|
88
|
-
bus.
|
|
88
|
+
bus.trace("startup: parse rule set: validate: rule safety");
|
|
89
89
|
(pConfiguration.allowed || []).forEach(assertRuleSafety);
|
|
90
90
|
(pConfiguration.forbidden || []).forEach(assertRuleSafety);
|
|
91
91
|
|
|
92
|
-
bus.
|
|
92
|
+
bus.trace("startup: parse rule set: validate: options");
|
|
93
93
|
if (pConfiguration?.options) {
|
|
94
94
|
assertCruiseOptionsValid(pConfiguration.options);
|
|
95
95
|
}
|
|
@@ -114,7 +114,7 @@ function normalizeRule(pRule) {
|
|
|
114
114
|
* @return {IStrictRuleSet}
|
|
115
115
|
*/
|
|
116
116
|
export default function normalizeRuleSet(pRuleSet) {
|
|
117
|
-
bus.
|
|
117
|
+
bus.debug("startup: parse rule set: normalize");
|
|
118
118
|
const lRuleSet = structuredClone(pRuleSet);
|
|
119
119
|
|
|
120
120
|
if (lRuleSet?.allowed) {
|
package/src/meta.cjs
CHANGED
package/src/utl/bus.mjs
CHANGED
|
@@ -24,6 +24,10 @@ class Bus extends EventEmitter {
|
|
|
24
24
|
debug(pMessage, pOptions, ...pArguments) {
|
|
25
25
|
this.progress(pMessage, { ...pOptions, level: DEBUG }, pArguments);
|
|
26
26
|
}
|
|
27
|
+
|
|
28
|
+
trace(pMessage, pOptions, ...pArguments) {
|
|
29
|
+
this.progress(pMessage, { ...pOptions, level: TRACE }, pArguments);
|
|
30
|
+
}
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
export const bus = new Bus();
|
|
@@ -8,7 +8,6 @@ import type { ResolveOptions, CachedInputFileSystem } from "enhanced-resolve";
|
|
|
8
8
|
*/
|
|
9
9
|
interface IResolveOptions extends ResolveOptions {
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
11
|
* Without bustTheCache (or with the value `false`) the resolver
|
|
13
12
|
* is initialized only once per session. If the attribute
|
|
14
13
|
* equals `true` the resolver is initialized on each call
|