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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dependency-cruiser",
3
- "version": "17.3.3-beta-2",
3
+ "version": "17.3.3-beta-4",
4
4
  "description": "Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.",
5
5
  "keywords": [
6
6
  "static analysis",
@@ -98,7 +98,10 @@ export default function findContentChanges(
98
98
  );
99
99
 
100
100
  bus.debug("cache: - get (new - cached)");
101
- lDiffCachedVsNew.forEach(({ name }) => lFileSet.delete(name));
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.replace(
49
- /{{testLocationRE}}/g,
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
- .replace(
215
- /{{sourceLocationRE}}/g,
214
+ .replaceAll(
215
+ "{{sourceLocationRE}}",
216
216
  folderNameArrayToRE(pInitOptions.sourceLocation),
217
217
  )
218
- .replace(
219
- /{{resolutionExtensionsAsString}}/g,
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.replace(/\\/g, "\\\\").replace(/\./g, "\\."))
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
- const lRuleFrom = pRule.from ?? pRule.module;
23
- if (lRuleFrom) {
24
- return (
25
- (!lRuleFrom.path || pModule.source.match(lRuleFrom.path)) &&
26
- (!lRuleFrom.pathNot || !pModule.source.match(lRuleFrom.pathNot))
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 false;
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 lToModules = pGraph.filter((pToModule) =>
122
- isModuleInRuleTo(pReachableRule, pToModule, pModule),
123
- );
124
-
125
- for (const lToModule of lToModules) {
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.replace(
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.replace(
229
- // eslint-disable-next-line no-control-regex
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.replace(/\*/g, ".+");
112
- // eslint-disable-next-line security/detect-non-literal-regexp
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
- // eslint-disable-next-line security/detect-non-literal-regexp
231
- const lMatchRE = new RegExp(`^${pPathLHS.replace(/\*/g, ".+")}$`);
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 = pDependency.resolved.match(pCollapsePattern);
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 = pModule.source.match(pCollapsePattern);
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
- const lDirectUnvisitedDependencies = lFromNode.dependencies
169
- .filter((pDependency) => !pVisited.has(pDependency.name))
170
- .map(this.#geldEdge);
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 RegExp(pPattern, "g").test(pFullPathToFile);
4
+ return getCachedRegExp(pPattern).test(pFullPathToFile);
3
5
  }
4
6
 
5
7
  export function moduleMatchesFilter(pModule, pFilter) {
@@ -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
- return await reportWrap(lCruiseResult, lCruiseOptions);
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" || pCollapse.match(lSingleDigitRe)) {
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,6 +1,6 @@
1
1
  /* generated - don't edit */
2
2
  module.exports = {
3
- version: "17.3.3-beta-2",
3
+ version: "17.3.3-beta-4",
4
4
  engines: {
5
5
  node: "^20.12||^22||>=24",
6
6
  },
@@ -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.replace(/[^a-zA-Z-]/g, "_"))
152
- .filter((pString) => pString.match(/^[a-zA-Z-_]+$/g))
153
- .filter((pString) => !pString.match(WHITELIST_RE));
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
- return {
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
  }
@@ -106,10 +106,10 @@ function focusHighlights(pModules, pNamesHashMap) {
106
106
 
107
107
  const hashToReadableNodeName = (pNode) =>
108
108
  pNode
109
- .replace(ACORN_DUMMY_VALUE, "__unknown__")
110
- .replace(/^\.$|^\.\//g, "__currentPath__")
111
- .replace(/^\.{2}$|^\.{2}\//g, "__prevPath__")
112
- .replace(/[[\]/.@~-]/g, "_");
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
@@ -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 || "").match(lPluginPatternRE);
46
+ const lPluginMatch = lPluginPatternRE.exec(pOutputType || "");
47
47
 
48
48
  if (lPluginMatch?.groups) {
49
49
  return getPluginReporter(lPluginMatch.groups.pluginName);
@@ -19,18 +19,18 @@ function escape(pMessageString) {
19
19
  return (
20
20
  pMessageString
21
21
  .toString()
22
- .replace(/\|/g, "||")
23
- .replace(/\n/g, "|n")
24
- .replace(/\r/g, "|r")
25
- .replace(/\[/g, "|[")
26
- .replace(/\]/g, "|]")
22
+ .replaceAll("|", "||")
23
+ .replaceAll("\n", "|n")
24
+ .replaceAll("\r", "|r")
25
+ .replaceAll("[", "|[")
26
+ .replaceAll("]", "|]")
27
27
  // next line
28
- .replace(/\u0085/g, "|x")
28
+ .replaceAll("\u0085", "|x")
29
29
  // line separator
30
- .replace(/\u2028/g, "|l")
30
+ .replaceAll("\u2028", "|l")
31
31
  // paragraph separator
32
- .replace(/\u2029/g, "|p")
33
- .replace(/'/g, "|'")
32
+ .replaceAll("\u2029", "|p")
33
+ .replaceAll("'", "|'")
34
34
  );
35
35
  }
36
36
 
@@ -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 = pSource.match(lRE);
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?.some((pType) => pType === "core")) {
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 = pFileName.match(EXTENSION_RE);
19
+ const lMatchResult = EXTENSION_RE.exec(pFileName);
20
20
 
21
21
  return lMatchResult?.groups?.extension ?? extname(pFileName);
22
22
  }
@@ -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 = pActualPath.match(pFromRestriction.path);
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
- // eslint-disable-next-line security/detect-non-literal-regexp
53
- pAll.replace(new RegExp(`\\$${pIndex}`, "g"), pThis),
74
+ pAll.replaceAll(new RegExp(`\\$${pIndex}`, "g"), pThis),
54
75
  pString,
55
76
  );
56
77
  }
@@ -1,9 +1,7 @@
1
1
  const DEFAULT_INDENT = 4;
2
2
 
3
3
  function indentString(pString, pCount) {
4
- const lRegex = /^(?!\s*$)/gm;
5
-
6
- return pString.replace(lRegex, " ".repeat(pCount));
4
+ return pString.replaceAll(/^(?!\s*$)/gm, " ".repeat(pCount));
7
5
  }
8
6
 
9
7
  /**
@@ -1,28 +1,39 @@
1
1
  import { isModuleOnlyRule, isFolderScope } from "./rule-classifiers.mjs";
2
2
  import { propertyEquals, matchesToIsMoreUnstable } from "./matchers.mjs";
3
- import { extractGroups, replaceGroupPlaceholders } from "#utl/regex-util.mjs";
3
+ import {
4
+ getCachedRegExp,
5
+ extractGroups,
6
+ replaceGroupPlaceholders,
7
+ } from "#utl/regex-util.mjs";
4
8
 
5
9
  function fromFolderPath(pRule, pFromFolder) {
6
- return Boolean(!pRule.from.path || pFromFolder.name.match(pRule.from.path));
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 Boolean(
11
- !pRule.from.pathNot || !pFromFolder.name.match(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 Boolean(
23
+ return (
17
24
  !pRule.to.path ||
18
- pToFolder.name.match(replaceGroupPlaceholders(pRule.to.path, pGroups)),
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 Boolean(
32
+ return (
24
33
  !pRule.to.pathNot ||
25
- !pToFolder.name.match(replaceGroupPlaceholders(pRule.to.pathNot, pGroups)),
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
- Boolean(!pRule.from.path || pDependent.match(pRule.from.path)) &&
102
- Boolean(!pRule.from.pathNot || !pDependent.match(pRule.from.pathNot)),
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 Boolean(
32
+ return (
33
33
  !pRule.to[pRuleProperty] ||
34
34
  (pDependency[pProperty] &&
35
- pDependency[pProperty].match(pRule.to[pRuleProperty])),
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 Boolean(
45
+ return (
46
46
  !pRule.to[pRuleProperty] ||
47
47
  (pDependency[pProperty] &&
48
- !pDependency[pProperty].match(pRule.to[pRuleProperty])),
48
+ !getCachedRegExp(pRule.to[pRuleProperty]).test(pDependency[pProperty]))
49
49
  );
50
50
  }
51
51
 
52
52
  export function matchesFromPath(pRule, pModule) {
53
- return Boolean(!pRule.from.path || pModule.source.match(pRule.from.path));
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 Boolean(
58
- !pRule.from.pathNot || !pModule.source.match(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 Boolean(!pRule.module.path || pModule.source.match(pRule.module.path));
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 Boolean(
68
- !pRule.module.pathNot || !pModule.source.match(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 Boolean(
80
+ return (
74
81
  !pRule.to.path ||
75
- pString.match(replaceGroupPlaceholders(pRule.to.path, pGroups)),
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
- !pString.match(replaceGroupPlaceholders(pRule.to.pathNot, pGroups))
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 Boolean(
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 Boolean(
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
- name.match(replaceGroupPlaceholders(pRule.to.via.path, pGroups)),
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
- name.match(replaceGroupPlaceholders(pRule.to.via.pathNot, pGroups)),
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
- name.match(replaceGroupPlaceholders(pRule.to.viaOnly.path, pGroups)),
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
- name.match(replaceGroupPlaceholders(pRule.to.viaOnly.pathNot, pGroups)),
174
+ getCachedRegExp(
175
+ replaceGroupPlaceholders(pRule.to.viaOnly.pathNot, pGroups),
176
+ ).test(name),
158
177
  );
159
178
  }
160
179
  if (pRule.to.viaOnly.dependencyTypes) {