dependency-cruiser 17.3.3-beta-2 → 17.3.3-beta-4
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/find-content-changes.mjs +4 -1
- package/src/cli/init-config/build-config.mjs +6 -6
- package/src/cli/init-config/utl.mjs +1 -1
- package/src/enrich/derive/reachable.mjs +23 -14
- package/src/extract/resolve/index.mjs +3 -4
- package/src/extract/resolve/module-classifiers.mjs +6 -10
- package/src/extract/tsc/extract-typescript-deps.mjs +0 -1
- package/src/graph-utl/consolidate-to-pattern.mjs +7 -2
- package/src/graph-utl/indexed-module-graph.mjs +10 -16
- package/src/graph-utl/match-facade.mjs +3 -1
- package/src/main/cruise.mjs +7 -1
- package/src/main/options/normalize.mjs +1 -1
- package/src/meta.cjs +1 -1
- package/src/report/anon/index.mjs +9 -4
- package/src/report/dot-webpage/svg-in-html-snippets/script.cjs +1 -1
- package/src/report/mermaid.mjs +4 -4
- package/src/report/plugins.mjs +1 -1
- package/src/report/teamcity.mjs +9 -9
- package/src/report/utl/index.mjs +2 -2
- package/src/utl/get-extension.mjs +1 -1
- package/src/utl/regex-util.mjs +24 -3
- package/src/utl/wrap-and-indent.mjs +1 -3
- package/src/validate/match-folder-dependency-rule.mjs +19 -8
- package/src/validate/match-module-rule-helpers.mjs +4 -3
- package/src/validate/matchers.mjs +41 -22
package/package.json
CHANGED
|
@@ -98,7 +98,10 @@ export default function findContentChanges(
|
|
|
98
98
|
);
|
|
99
99
|
|
|
100
100
|
bus.debug("cache: - get (new - cached)");
|
|
101
|
-
|
|
101
|
+
// eslint-disable-next-line budapestian/local-variable-pattern
|
|
102
|
+
for (const { name } of lDiffCachedVsNew) {
|
|
103
|
+
lFileSet.delete(name);
|
|
104
|
+
}
|
|
102
105
|
|
|
103
106
|
const lDiffNewVsCached = [];
|
|
104
107
|
for (const lFileName of lFileSet) {
|
|
@@ -45,8 +45,8 @@ function buildNotToTestRule(pInitOptions) {
|
|
|
45
45
|
}
|
|
46
46
|
},`;
|
|
47
47
|
return pInitOptions.hasTestsOutsideSource
|
|
48
|
-
? lNotToTestRule.
|
|
49
|
-
|
|
48
|
+
? lNotToTestRule.replaceAll(
|
|
49
|
+
"{{testLocationRE}}",
|
|
50
50
|
folderNameArrayToRE(pInitOptions?.testLocation ?? []),
|
|
51
51
|
)
|
|
52
52
|
: "";
|
|
@@ -211,12 +211,12 @@ function buildBuiltInModulesAttribute(pInitOptions) {
|
|
|
211
211
|
*/
|
|
212
212
|
export default function buildConfig(pInitOptions) {
|
|
213
213
|
return configTemplate
|
|
214
|
-
.
|
|
215
|
-
|
|
214
|
+
.replaceAll(
|
|
215
|
+
"{{sourceLocationRE}}",
|
|
216
216
|
folderNameArrayToRE(pInitOptions.sourceLocation),
|
|
217
217
|
)
|
|
218
|
-
.
|
|
219
|
-
|
|
218
|
+
.replaceAll(
|
|
219
|
+
"{{resolutionExtensionsAsString}}",
|
|
220
220
|
extensionsToString(pInitOptions.resolutionExtensions),
|
|
221
221
|
)
|
|
222
222
|
.replace("{{notToTestRule}}", buildNotToTestRule(pInitOptions))
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export function folderNameArrayToRE(pArrayOfStrings) {
|
|
7
7
|
const lFoldersInARE = pArrayOfStrings
|
|
8
|
-
.map((pName) => pName.
|
|
8
|
+
.map((pName) => pName.replaceAll("\\", "\\\\").replaceAll(".", "\\."))
|
|
9
9
|
.join("|");
|
|
10
10
|
|
|
11
11
|
return `^(${lFoldersInARE})`;
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
matchToModulePathNot,
|
|
5
5
|
} from "#validate/matchers.mjs";
|
|
6
6
|
import IndexedModuleGraph from "#graph-utl/indexed-module-graph.mjs";
|
|
7
|
-
import { extractGroups } from "#utl/regex-util.mjs";
|
|
7
|
+
import { getCachedRegExp, extractGroups } from "#utl/regex-util.mjs";
|
|
8
8
|
|
|
9
9
|
function isReachableRule(pRule) {
|
|
10
10
|
return Object.hasOwn(pRule?.to ?? {}, "reachable");
|
|
@@ -18,15 +18,25 @@ function getReachableRules(pRuleSet) {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
function isModuleInRuleFrom(pRule) {
|
|
21
|
+
const lRuleFrom = pRule.from ?? pRule.module;
|
|
22
|
+
if (!lRuleFrom) {
|
|
23
|
+
return () => false;
|
|
24
|
+
}
|
|
25
|
+
const lRuleFromPathRE = lRuleFrom.path
|
|
26
|
+
? getCachedRegExp(lRuleFrom.path)
|
|
27
|
+
: null;
|
|
28
|
+
const lRuleFromPathNotRE = lRuleFrom.pathNot
|
|
29
|
+
? getCachedRegExp(lRuleFrom.pathNot)
|
|
30
|
+
: null;
|
|
31
|
+
|
|
21
32
|
return (pModule) => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
);
|
|
33
|
+
if (lRuleFromPathRE && !lRuleFromPathRE.test(pModule.source)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (lRuleFromPathNotRE && lRuleFromPathNotRE.test(pModule.source)) {
|
|
37
|
+
return false;
|
|
28
38
|
}
|
|
29
|
-
return
|
|
39
|
+
return true;
|
|
30
40
|
};
|
|
31
41
|
}
|
|
32
42
|
|
|
@@ -118,12 +128,11 @@ function shouldAddReachable(pRule, pModuleTo, pGraph) {
|
|
|
118
128
|
}
|
|
119
129
|
|
|
120
130
|
function addReachesToModule(pModule, pGraph, pIndexedGraph, pReachableRule) {
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (pModule.source !== lToModule.source) {
|
|
131
|
+
for (const lToModule of pGraph) {
|
|
132
|
+
if (
|
|
133
|
+
pModule.source !== lToModule.source &&
|
|
134
|
+
isModuleInRuleTo(pReachableRule, lToModule, pModule)
|
|
135
|
+
) {
|
|
127
136
|
const lPath = pIndexedGraph.getPath(pModule.source, lToModule.source);
|
|
128
137
|
|
|
129
138
|
if (lPath.length > 0) {
|
|
@@ -146,7 +146,7 @@ function resolveWithRetry(
|
|
|
146
146
|
lReturnValue.couldNotResolve &&
|
|
147
147
|
canBeResolvedToTsVariant(lStrippedModuleName)
|
|
148
148
|
) {
|
|
149
|
-
const lModuleWithoutExtension = lStrippedModuleName.
|
|
149
|
+
const lModuleWithoutExtension = lStrippedModuleName.replaceAll(
|
|
150
150
|
/\.(js|jsx|cjs|mjs)$/g,
|
|
151
151
|
"",
|
|
152
152
|
);
|
|
@@ -225,9 +225,8 @@ export default function resolve(
|
|
|
225
225
|
// enhanced-resolve inserts a NULL character in front of any `#` character.
|
|
226
226
|
// This wonky replace corrects that so the filename again corresponds
|
|
227
227
|
// with a real file on disk
|
|
228
|
-
const lResolvedEHRCorrected = lResolvedDependency.resolved.
|
|
229
|
-
|
|
230
|
-
/\u0000#/g,
|
|
228
|
+
const lResolvedEHRCorrected = lResolvedDependency.resolved.replaceAll(
|
|
229
|
+
"\u0000#",
|
|
231
230
|
"#",
|
|
232
231
|
);
|
|
233
232
|
const lResolvedYarnVirtual = resolveYarnVirtual(
|
|
@@ -3,9 +3,9 @@ import { isAbsolute, resolve as path_resolve } from "node:path";
|
|
|
3
3
|
import { join as posix_join } from "node:path/posix";
|
|
4
4
|
import picomatch from "picomatch";
|
|
5
5
|
import getExtension from "#utl/get-extension.mjs";
|
|
6
|
+
import { getCachedRegExp } from "#utl/regex-util.mjs";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
|
-
* @import { IResolveOptions } from "../../../types/resolve-options.mjs"
|
|
9
9
|
* @import { ITranspileOptions } from "../../../types/dependency-cruiser.mjs"
|
|
10
10
|
*/
|
|
11
11
|
|
|
@@ -108,10 +108,8 @@ function isSubpathImport(pModuleName, pManifest) {
|
|
|
108
108
|
// replacement only.
|
|
109
109
|
// Quoting https://nodejs.org/api/packages.html#subpath-imports :
|
|
110
110
|
// > "* maps expose nested subpaths as it is a string replacement syntax only"
|
|
111
|
-
const lMatchREasString = pImportLHS.
|
|
112
|
-
|
|
113
|
-
const lMatchRE = new RegExp(`^${lMatchREasString}$`);
|
|
114
|
-
return lMatchRE.test(pModuleName);
|
|
111
|
+
const lMatchREasString = pImportLHS.replaceAll("*", ".+");
|
|
112
|
+
return getCachedRegExp(`^${lMatchREasString}$`).test(pModuleName);
|
|
115
113
|
})
|
|
116
114
|
);
|
|
117
115
|
}
|
|
@@ -226,11 +224,9 @@ function matchesTSConfigPaths(pModuleName, pPaths) {
|
|
|
226
224
|
//
|
|
227
225
|
// TODO: 'any string' - does this include the empty string as well? Checks seem
|
|
228
226
|
// to indicate it doesn't, so we use `.+` instead of `.*`
|
|
229
|
-
return Object.keys(pPaths).some((pPathLHS) =>
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
return lMatchRE.test(pModuleName);
|
|
233
|
-
});
|
|
227
|
+
return Object.keys(pPaths).some((pPathLHS) =>
|
|
228
|
+
getCachedRegExp(`^${pPathLHS.replaceAll("*", ".+")}$`).test(pModuleName),
|
|
229
|
+
);
|
|
234
230
|
}
|
|
235
231
|
|
|
236
232
|
function stripExtension(pModulePath) {
|
|
@@ -560,7 +560,6 @@ export default function extractTypeScriptDependencies(
|
|
|
560
560
|
pDetectJSDocImports,
|
|
561
561
|
pDetectProcessBuiltinModuleCalls,
|
|
562
562
|
) {
|
|
563
|
-
// console.dir(pTypeScriptAST, { depth: 100 });
|
|
564
563
|
return typescript
|
|
565
564
|
? extractImports(pTypeScriptAST)
|
|
566
565
|
.concat(extractExports(pTypeScriptAST))
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import consolidateModules from "./consolidate-modules.mjs";
|
|
2
2
|
import consolidateModuleDependencies from "./consolidate-module-dependencies.mjs";
|
|
3
|
+
import { getCachedRegExp } from "#utl/regex-util.mjs";
|
|
3
4
|
|
|
4
5
|
function squashDependencyToPattern(pCollapsePattern) {
|
|
5
6
|
return (pDependency) => {
|
|
6
|
-
const lCollapseMatch =
|
|
7
|
+
const lCollapseMatch = getCachedRegExp(pCollapsePattern).exec(
|
|
8
|
+
pDependency.resolved,
|
|
9
|
+
);
|
|
7
10
|
|
|
8
11
|
return {
|
|
9
12
|
...pDependency,
|
|
@@ -27,7 +30,9 @@ function determineConsolidatedness(pConsolidated, pCollapseMatch, pSource) {
|
|
|
27
30
|
|
|
28
31
|
function squashModuleToPattern(pCollapsePattern) {
|
|
29
32
|
return (pModule) => {
|
|
30
|
-
const lCollapseMatch =
|
|
33
|
+
const lCollapseMatch = getCachedRegExp(pCollapsePattern).exec(
|
|
34
|
+
pModule.source,
|
|
35
|
+
);
|
|
31
36
|
|
|
32
37
|
return {
|
|
33
38
|
...pModule,
|
|
@@ -164,25 +164,19 @@ export default class IndexedModuleGraph {
|
|
|
164
164
|
if (!lFromNode) {
|
|
165
165
|
return [];
|
|
166
166
|
}
|
|
167
|
+
for (const lDependency of lFromNode.dependencies) {
|
|
168
|
+
if (!pVisited.has(lDependency.name)) {
|
|
169
|
+
if (lDependency.name === pTo) {
|
|
170
|
+
return [this.#geldEdge(lDependency)];
|
|
171
|
+
}
|
|
172
|
+
let lCandidatePath = this.getPath(lDependency.name, pTo, pVisited);
|
|
167
173
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const lFoundDependency = lDirectUnvisitedDependencies.find(
|
|
172
|
-
(pDependency) => pDependency.name === pTo,
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
if (lFoundDependency) {
|
|
176
|
-
return [lFoundDependency];
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
for (const lNewFrom of lDirectUnvisitedDependencies) {
|
|
180
|
-
let lCandidatePath = this.getPath(lNewFrom.name, pTo, pVisited);
|
|
181
|
-
|
|
182
|
-
if (lCandidatePath.length > 0) {
|
|
183
|
-
return [lNewFrom].concat(lCandidatePath);
|
|
174
|
+
if (lCandidatePath.length > 0) {
|
|
175
|
+
return [this.#geldEdge(lDependency)].concat(lCandidatePath);
|
|
176
|
+
}
|
|
184
177
|
}
|
|
185
178
|
}
|
|
179
|
+
|
|
186
180
|
return [];
|
|
187
181
|
}
|
|
188
182
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { getCachedRegExp } from "#utl/regex-util.mjs";
|
|
2
|
+
|
|
1
3
|
export function filenameMatchesPattern(pFullPathToFile, pPattern) {
|
|
2
|
-
return
|
|
4
|
+
return getCachedRegExp(pPattern).test(pFullPathToFile);
|
|
3
5
|
}
|
|
4
6
|
|
|
5
7
|
export function moduleMatchesFilter(pModule, pFilter) {
|
package/src/main/cruise.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { assertCruiseOptionsValid } from "./options/assert-validity.mjs";
|
|
|
3
3
|
import { normalizeCruiseOptions } from "./options/normalize.mjs";
|
|
4
4
|
import reportWrap from "./report-wrap.mjs";
|
|
5
5
|
import { bus } from "#utl/bus.mjs";
|
|
6
|
+
import { clearRegExpCache } from "#utl/regex-util.mjs";
|
|
6
7
|
|
|
7
8
|
const TOTAL_STEPS = 10;
|
|
8
9
|
|
|
@@ -104,5 +105,10 @@ export default async function cruise(
|
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
bus.summary("report", c(9));
|
|
107
|
-
|
|
108
|
+
const lResult = await reportWrap(lCruiseResult, lCruiseOptions);
|
|
109
|
+
|
|
110
|
+
bus.debug("clear regex cache");
|
|
111
|
+
clearRegExpCache();
|
|
112
|
+
|
|
113
|
+
return lResult;
|
|
108
114
|
}
|
|
@@ -64,7 +64,7 @@ function normalizeCollapse(pCollapse) {
|
|
|
64
64
|
const lFolderBelowNodeModules = `node_modules/${lOneOrMoreNonSlashes}`;
|
|
65
65
|
const lSingleDigitRe = /^\d$/;
|
|
66
66
|
|
|
67
|
-
if (typeof pCollapse === "number" ||
|
|
67
|
+
if (typeof pCollapse === "number" || lSingleDigitRe.test(pCollapse)) {
|
|
68
68
|
lReturnValue = `${lFolderBelowNodeModules}|^${lFolderPattern.repeat(
|
|
69
69
|
Number.parseInt(pCollapse, 10),
|
|
70
70
|
)}`;
|
package/src/meta.cjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clearCache } from "./anonymize-path-element.mjs";
|
|
1
2
|
import { anonymizePath, WHITELIST_RE } from "./anonymize-path.mjs";
|
|
2
3
|
|
|
3
4
|
const EOL = "\n";
|
|
@@ -148,9 +149,11 @@ function anonymize(pResults, pWordList) {
|
|
|
148
149
|
|
|
149
150
|
function sanitizeWordList(pWordList) {
|
|
150
151
|
return pWordList
|
|
151
|
-
.map((pString) => pString.
|
|
152
|
-
.filter(
|
|
153
|
-
|
|
152
|
+
.map((pString) => pString.replaceAll(/[^a-zA-Z-]/g, "_"))
|
|
153
|
+
.filter(
|
|
154
|
+
(pString) =>
|
|
155
|
+
/^[a-zA-Z-_]+$/g.test(pString) && !WHITELIST_RE.test(pString),
|
|
156
|
+
);
|
|
154
157
|
}
|
|
155
158
|
|
|
156
159
|
/**
|
|
@@ -182,7 +185,7 @@ export default function reportAnonymous(pResults, pAnonymousReporterOptions) {
|
|
|
182
185
|
lAnonymousReporterOptions.wordlist =
|
|
183
186
|
pResults?.summary?.optionsUsed?.reporterOptions?.anon?.wordlist ?? [];
|
|
184
187
|
}
|
|
185
|
-
|
|
188
|
+
const lReturnValue = {
|
|
186
189
|
output:
|
|
187
190
|
JSON.stringify(
|
|
188
191
|
anonymize(
|
|
@@ -194,4 +197,6 @@ export default function reportAnonymous(pResults, pAnonymousReporterOptions) {
|
|
|
194
197
|
) + EOL,
|
|
195
198
|
exitCode: 0,
|
|
196
199
|
};
|
|
200
|
+
clearCache();
|
|
201
|
+
return lReturnValue;
|
|
197
202
|
}
|
|
@@ -270,7 +270,7 @@ function skewLineABit(lDrawingInstructions) {
|
|
|
270
270
|
// Even this value is so small that it is not visible to the
|
|
271
271
|
// human eye (tested with the two I have at my disposal).
|
|
272
272
|
var lIncrement = 0.001;
|
|
273
|
-
var lNewLastValue = parseFloat(lLastValue) + lIncrement;
|
|
273
|
+
var lNewLastValue = Number.parseFloat(lLastValue) + lIncrement;
|
|
274
274
|
|
|
275
275
|
return lDrawingInstructions.replace(lLastValue, lNewLastValue);
|
|
276
276
|
}
|
package/src/report/mermaid.mjs
CHANGED
|
@@ -106,10 +106,10 @@ function focusHighlights(pModules, pNamesHashMap) {
|
|
|
106
106
|
|
|
107
107
|
const hashToReadableNodeName = (pNode) =>
|
|
108
108
|
pNode
|
|
109
|
-
.
|
|
110
|
-
.
|
|
111
|
-
.
|
|
112
|
-
.
|
|
109
|
+
.replaceAll(ACORN_DUMMY_VALUE, "__unknown__")
|
|
110
|
+
.replaceAll(/^\.$|^\.\//g, "__currentPath__")
|
|
111
|
+
.replaceAll(/^\.{2}$|^\.{2}\//g, "__prevPath__")
|
|
112
|
+
.replaceAll(/[[\]/.@~-]/g, "_");
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
115
|
* @param {import("../../types/cruise-result").IModule[]} pModules
|
package/src/report/plugins.mjs
CHANGED
|
@@ -43,7 +43,7 @@ async function getPluginReporter(pOutputType) {
|
|
|
43
43
|
|
|
44
44
|
export function getExternalPluginReporter(pOutputType) {
|
|
45
45
|
const lPluginPatternRE = /^plugin:(?<pluginName>.+)$/;
|
|
46
|
-
const lPluginMatch = (pOutputType || "")
|
|
46
|
+
const lPluginMatch = lPluginPatternRE.exec(pOutputType || "");
|
|
47
47
|
|
|
48
48
|
if (lPluginMatch?.groups) {
|
|
49
49
|
return getPluginReporter(lPluginMatch.groups.pluginName);
|
package/src/report/teamcity.mjs
CHANGED
|
@@ -19,18 +19,18 @@ function escape(pMessageString) {
|
|
|
19
19
|
return (
|
|
20
20
|
pMessageString
|
|
21
21
|
.toString()
|
|
22
|
-
.
|
|
23
|
-
.
|
|
24
|
-
.
|
|
25
|
-
.
|
|
26
|
-
.
|
|
22
|
+
.replaceAll("|", "||")
|
|
23
|
+
.replaceAll("\n", "|n")
|
|
24
|
+
.replaceAll("\r", "|r")
|
|
25
|
+
.replaceAll("[", "|[")
|
|
26
|
+
.replaceAll("]", "|]")
|
|
27
27
|
// next line
|
|
28
|
-
.
|
|
28
|
+
.replaceAll("\u0085", "|x")
|
|
29
29
|
// line separator
|
|
30
|
-
.
|
|
30
|
+
.replaceAll("\u2028", "|l")
|
|
31
31
|
// paragraph separator
|
|
32
|
-
.
|
|
33
|
-
.
|
|
32
|
+
.replaceAll("\u2029", "|p")
|
|
33
|
+
.replaceAll("'", "|'")
|
|
34
34
|
);
|
|
35
35
|
}
|
|
36
36
|
|
package/src/report/utl/index.mjs
CHANGED
|
@@ -48,7 +48,7 @@ function smartURIConcat(pPrefix, pSource) {
|
|
|
48
48
|
function deriveExternalPackageName(pSource) {
|
|
49
49
|
const lRE =
|
|
50
50
|
/node_modules\/(?<packageName>[^@][^/]+)|(?<atPackageName>@[^/]+\/[^/]+)/;
|
|
51
|
-
const lMatch =
|
|
51
|
+
const lMatch = lRE.exec(pSource);
|
|
52
52
|
if (lMatch) {
|
|
53
53
|
return lMatch.groups.packageName || lMatch.groups.atPackageName;
|
|
54
54
|
}
|
|
@@ -72,7 +72,7 @@ function deriveCorePackageName(pSource) {
|
|
|
72
72
|
*/
|
|
73
73
|
export function getURLForModule(pModule, pPrefix, pSuffix) {
|
|
74
74
|
// TODO: derive the URLs from configuration
|
|
75
|
-
if (pModule.dependencyTypes?.
|
|
75
|
+
if (pModule.dependencyTypes?.includes("core")) {
|
|
76
76
|
const lPackageName = deriveCorePackageName(pModule.source);
|
|
77
77
|
// Check if it's a Bun core module (starts with bun:)
|
|
78
78
|
if (lPackageName.startsWith("bun:")) {
|
|
@@ -16,7 +16,7 @@ const EXTENSION_RE = /(?<extension>(?:(?:\.d\.(?:[cm])?ts)|\.coffee\.md)$)/;
|
|
|
16
16
|
* @return {string} extension
|
|
17
17
|
*/
|
|
18
18
|
export default function getExtension(pFileName) {
|
|
19
|
-
const lMatchResult =
|
|
19
|
+
const lMatchResult = EXTENSION_RE.exec(pFileName);
|
|
20
20
|
|
|
21
21
|
return lMatchResult?.groups?.extension ?? extname(pFileName);
|
|
22
22
|
}
|
package/src/utl/regex-util.mjs
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
/* eslint-disable security/detect-non-literal-regexp */
|
|
2
|
+
const REGEXP_CACHE = new Map();
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns a cached RegExp instance for the given pattern.
|
|
6
|
+
*
|
|
7
|
+
* See ./regex-cache.md for design considerations
|
|
8
|
+
*
|
|
9
|
+
* @param {string} pPattern
|
|
10
|
+
* @returns {RegExp}
|
|
11
|
+
*/
|
|
12
|
+
export function getCachedRegExp(pPattern) {
|
|
13
|
+
if (!REGEXP_CACHE.has(pPattern)) {
|
|
14
|
+
REGEXP_CACHE.set(pPattern, new RegExp(pPattern));
|
|
15
|
+
}
|
|
16
|
+
return REGEXP_CACHE.get(pPattern);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function clearRegExpCache() {
|
|
20
|
+
REGEXP_CACHE.clear();
|
|
21
|
+
}
|
|
22
|
+
|
|
1
23
|
/**
|
|
2
24
|
* If there is at least one group expression in the given pRulePath
|
|
3
25
|
* return the first matched one.
|
|
@@ -19,7 +41,7 @@ export function extractGroups(pFromRestriction, pActualPath) {
|
|
|
19
41
|
// except before it enters here it has already been 'normalized' to a string
|
|
20
42
|
// so it can be safely passed to match. The right solution here (TODO)
|
|
21
43
|
// is to create a separate type for NormalizedFromRestriction
|
|
22
|
-
let lMatchResult =
|
|
44
|
+
let lMatchResult = getCachedRegExp(pFromRestriction.path).exec(pActualPath);
|
|
23
45
|
|
|
24
46
|
if (lMatchResult && lMatchResult.length > 1) {
|
|
25
47
|
lReturnValue = lMatchResult.filter(
|
|
@@ -49,8 +71,7 @@ export function extractGroups(pFromRestriction, pActualPath) {
|
|
|
49
71
|
export function replaceGroupPlaceholders(pString, pExtractedGroups) {
|
|
50
72
|
return pExtractedGroups.reduce(
|
|
51
73
|
(pAll, pThis, pIndex) =>
|
|
52
|
-
|
|
53
|
-
pAll.replace(new RegExp(`\\$${pIndex}`, "g"), pThis),
|
|
74
|
+
pAll.replaceAll(new RegExp(`\\$${pIndex}`, "g"), pThis),
|
|
54
75
|
pString,
|
|
55
76
|
);
|
|
56
77
|
}
|
|
@@ -1,28 +1,39 @@
|
|
|
1
1
|
import { isModuleOnlyRule, isFolderScope } from "./rule-classifiers.mjs";
|
|
2
2
|
import { propertyEquals, matchesToIsMoreUnstable } from "./matchers.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
getCachedRegExp,
|
|
5
|
+
extractGroups,
|
|
6
|
+
replaceGroupPlaceholders,
|
|
7
|
+
} from "#utl/regex-util.mjs";
|
|
4
8
|
|
|
5
9
|
function fromFolderPath(pRule, pFromFolder) {
|
|
6
|
-
return
|
|
10
|
+
return (
|
|
11
|
+
!pRule.from.path || getCachedRegExp(pRule.from.path).test(pFromFolder.name)
|
|
12
|
+
);
|
|
7
13
|
}
|
|
8
14
|
|
|
9
15
|
function fromFolderPathNot(pRule, pFromFolder) {
|
|
10
|
-
return
|
|
11
|
-
!pRule.from.pathNot ||
|
|
16
|
+
return (
|
|
17
|
+
!pRule.from.pathNot ||
|
|
18
|
+
!getCachedRegExp(pRule.from.pathNot).test(pFromFolder.name)
|
|
12
19
|
);
|
|
13
20
|
}
|
|
14
21
|
|
|
15
22
|
function toFolderPath(pRule, pToFolder, pGroups) {
|
|
16
|
-
return
|
|
23
|
+
return (
|
|
17
24
|
!pRule.to.path ||
|
|
18
|
-
|
|
25
|
+
getCachedRegExp(replaceGroupPlaceholders(pRule.to.path, pGroups)).test(
|
|
26
|
+
pToFolder.name,
|
|
27
|
+
)
|
|
19
28
|
);
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
function toFolderPathNot(pRule, pToFolder, pGroups) {
|
|
23
|
-
return
|
|
32
|
+
return (
|
|
24
33
|
!pRule.to.pathNot ||
|
|
25
|
-
!
|
|
34
|
+
!getCachedRegExp(replaceGroupPlaceholders(pRule.to.pathNot, pGroups)).test(
|
|
35
|
+
pToFolder.name,
|
|
36
|
+
)
|
|
26
37
|
);
|
|
27
38
|
}
|
|
28
39
|
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
matchesModulePath,
|
|
7
7
|
matchesModulePathNot,
|
|
8
8
|
} from "./matchers.mjs";
|
|
9
|
-
import { extractGroups } from "#utl/regex-util.mjs";
|
|
9
|
+
import { getCachedRegExp, extractGroups } from "#utl/regex-util.mjs";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* @import { IModule } from "../../types/cruise-result.mjs";
|
|
@@ -98,8 +98,9 @@ export function matchesReachesRule(pRule, pModule) {
|
|
|
98
98
|
function dependentsCountsMatch(pRule, pDependents) {
|
|
99
99
|
const lMatchingDependentsCount = pDependents.filter(
|
|
100
100
|
(pDependent) =>
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
(!pRule.from.path || getCachedRegExp(pRule.from.path).test(pDependent)) &&
|
|
102
|
+
(!pRule.from.pathNot ||
|
|
103
|
+
!getCachedRegExp(pRule.from.pathNot).test(pDependent)),
|
|
103
104
|
).length;
|
|
104
105
|
return (
|
|
105
106
|
(!pRule.module.numberOfDependentsLessThan ||
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable security/detect-object-injection */
|
|
2
2
|
import { dirname, resolve, sep } from "node:path/posix";
|
|
3
|
-
import { replaceGroupPlaceholders } from "#utl/regex-util.mjs";
|
|
3
|
+
import { getCachedRegExp, replaceGroupPlaceholders } from "#utl/regex-util.mjs";
|
|
4
4
|
import { intersects } from "#utl/array-util.mjs";
|
|
5
5
|
|
|
6
6
|
// by their nature some dependency types always occur alongside other
|
|
@@ -29,10 +29,10 @@ export function propertyEquals(pRule, pDependency, pProperty) {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export function propertyMatches(pRule, pDependency, pRuleProperty, pProperty) {
|
|
32
|
-
return
|
|
32
|
+
return (
|
|
33
33
|
!pRule.to[pRuleProperty] ||
|
|
34
34
|
(pDependency[pProperty] &&
|
|
35
|
-
|
|
35
|
+
getCachedRegExp(pRule.to[pRuleProperty]).test(pDependency[pProperty]))
|
|
36
36
|
);
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -42,37 +42,46 @@ export function propertyMatchesNot(
|
|
|
42
42
|
pRuleProperty,
|
|
43
43
|
pProperty,
|
|
44
44
|
) {
|
|
45
|
-
return
|
|
45
|
+
return (
|
|
46
46
|
!pRule.to[pRuleProperty] ||
|
|
47
47
|
(pDependency[pProperty] &&
|
|
48
|
-
!
|
|
48
|
+
!getCachedRegExp(pRule.to[pRuleProperty]).test(pDependency[pProperty]))
|
|
49
49
|
);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export function matchesFromPath(pRule, pModule) {
|
|
53
|
-
return
|
|
53
|
+
return (
|
|
54
|
+
!pRule.from.path || getCachedRegExp(pRule.from.path).test(pModule.source)
|
|
55
|
+
);
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
export function matchesFromPathNot(pRule, pModule) {
|
|
57
|
-
return
|
|
58
|
-
!pRule.from.pathNot ||
|
|
59
|
+
return (
|
|
60
|
+
!pRule.from.pathNot ||
|
|
61
|
+
!getCachedRegExp(pRule.from.pathNot).test(pModule.source)
|
|
59
62
|
);
|
|
60
63
|
}
|
|
61
64
|
|
|
62
65
|
export function matchesModulePath(pRule, pModule) {
|
|
63
|
-
return
|
|
66
|
+
return (
|
|
67
|
+
!pRule.module.path ||
|
|
68
|
+
getCachedRegExp(pRule.module.path).test(pModule.source)
|
|
69
|
+
);
|
|
64
70
|
}
|
|
65
71
|
|
|
66
72
|
export function matchesModulePathNot(pRule, pModule) {
|
|
67
|
-
return
|
|
68
|
-
!pRule.module.pathNot ||
|
|
73
|
+
return (
|
|
74
|
+
!pRule.module.pathNot ||
|
|
75
|
+
!getCachedRegExp(pRule.module.pathNot).test(pModule.source)
|
|
69
76
|
);
|
|
70
77
|
}
|
|
71
78
|
|
|
72
79
|
function _matchesToPath(pRule, pString, pGroups = []) {
|
|
73
|
-
return
|
|
80
|
+
return (
|
|
74
81
|
!pRule.to.path ||
|
|
75
|
-
|
|
82
|
+
getCachedRegExp(replaceGroupPlaceholders(pRule.to.path, pGroups)).test(
|
|
83
|
+
pString,
|
|
84
|
+
)
|
|
76
85
|
);
|
|
77
86
|
}
|
|
78
87
|
|
|
@@ -87,7 +96,9 @@ export function matchToModulePath(pRule, pModule, pGroups) {
|
|
|
87
96
|
function _matchesToPathNot(pRule, pString, pGroups = []) {
|
|
88
97
|
return (
|
|
89
98
|
!pRule.to.pathNot ||
|
|
90
|
-
!
|
|
99
|
+
!getCachedRegExp(replaceGroupPlaceholders(pRule.to.pathNot, pGroups)).test(
|
|
100
|
+
pString,
|
|
101
|
+
)
|
|
91
102
|
);
|
|
92
103
|
}
|
|
93
104
|
|
|
@@ -100,16 +111,16 @@ export function matchToModulePathNot(pRule, pModule, pGroups) {
|
|
|
100
111
|
}
|
|
101
112
|
|
|
102
113
|
export function matchesToDependencyTypes(pRule, pDependency) {
|
|
103
|
-
return
|
|
114
|
+
return (
|
|
104
115
|
!pRule.to.dependencyTypes ||
|
|
105
|
-
intersects(pDependency.dependencyTypes, pRule.to.dependencyTypes)
|
|
116
|
+
intersects(pDependency.dependencyTypes, pRule.to.dependencyTypes)
|
|
106
117
|
);
|
|
107
118
|
}
|
|
108
119
|
|
|
109
120
|
export function matchesToDependencyTypesNot(pRule, pDependency) {
|
|
110
|
-
return
|
|
121
|
+
return (
|
|
111
122
|
!pRule.to.dependencyTypesNot ||
|
|
112
|
-
!intersects(pDependency.dependencyTypes, pRule.to.dependencyTypesNot)
|
|
123
|
+
!intersects(pDependency.dependencyTypes, pRule.to.dependencyTypesNot)
|
|
113
124
|
);
|
|
114
125
|
}
|
|
115
126
|
|
|
@@ -118,12 +129,16 @@ export function matchesToVia(pRule, pDependency, pGroups) {
|
|
|
118
129
|
if (pRule.to.via && pDependency.cycle) {
|
|
119
130
|
if (pRule.to.via.path) {
|
|
120
131
|
lReturnValue = pDependency.cycle.some(({ name }) =>
|
|
121
|
-
|
|
132
|
+
getCachedRegExp(
|
|
133
|
+
replaceGroupPlaceholders(pRule.to.via.path, pGroups),
|
|
134
|
+
).test(name),
|
|
122
135
|
);
|
|
123
136
|
}
|
|
124
137
|
if (pRule.to.via.pathNot) {
|
|
125
138
|
lReturnValue = !pDependency.cycle.every(({ name }) =>
|
|
126
|
-
|
|
139
|
+
getCachedRegExp(
|
|
140
|
+
replaceGroupPlaceholders(pRule.to.via.pathNot, pGroups),
|
|
141
|
+
).test(name),
|
|
127
142
|
);
|
|
128
143
|
}
|
|
129
144
|
if (pRule.to.via.dependencyTypes) {
|
|
@@ -149,12 +164,16 @@ export function matchesToViaOnly(pRule, pDependency, pGroups) {
|
|
|
149
164
|
if (pRule.to.viaOnly && pDependency.cycle) {
|
|
150
165
|
if (pRule.to.viaOnly.path) {
|
|
151
166
|
lReturnValue = pDependency.cycle.every(({ name }) =>
|
|
152
|
-
|
|
167
|
+
getCachedRegExp(
|
|
168
|
+
replaceGroupPlaceholders(pRule.to.viaOnly.path, pGroups),
|
|
169
|
+
).test(name),
|
|
153
170
|
);
|
|
154
171
|
}
|
|
155
172
|
if (pRule.to.viaOnly.pathNot) {
|
|
156
173
|
lReturnValue = !pDependency.cycle.some(({ name }) =>
|
|
157
|
-
|
|
174
|
+
getCachedRegExp(
|
|
175
|
+
replaceGroupPlaceholders(pRule.to.viaOnly.pathNot, pGroups),
|
|
176
|
+
).test(name),
|
|
158
177
|
);
|
|
159
178
|
}
|
|
160
179
|
if (pRule.to.viaOnly.dependencyTypes) {
|