dependency-cruiser 16.0.0-beta-3 → 16.0.0-beta-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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dependency-cruiser",
3
- "version": "16.0.0-beta-3",
3
+ "version": "16.0.0-beta-5",
4
4
  "description": "Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.",
5
5
  "keywords": [
6
6
  "static analysis",
@@ -14,6 +14,13 @@ import { scannableExtensions } from "#extract/transpile/meta.mjs";
14
14
  // @ts-expect-error ts(2307) - the ts compiler is not privy to the existence of #imports in package.json
15
15
  import { bus } from "#utl/bus.mjs";
16
16
 
17
+ /**
18
+ * @typedef {import("../../types/dependency-cruiser.mjs").IRevisionData} IRevisionData
19
+ * @typedef {import("../../types/strict-options.mjs").IStrictCruiseOptions} IStrictCruiseOptions
20
+ * @typedef {import("../../types/dependency-cruiser.mjs").ICruiseResult} ICruiseResult
21
+ * @typedef {import("../../types/cache-options.mjs").cacheStrategyType} cacheStrategyType
22
+ */
23
+
17
24
  const CACHE_FILE_NAME = "cache.json";
18
25
  const EMPTY_CACHE = {
19
26
  modules: [],
@@ -27,10 +34,21 @@ const EMPTY_CACHE = {
27
34
  optionsUsed: {},
28
35
  },
29
36
  };
37
+ // Bump this to the current major.minor version when the cache format changes in
38
+ // a way that's not backwards compatible.
39
+ // e.g. version 3.0.0 => 3
40
+ // version 3.1.0 => 3.1
41
+ // version 3.1.1 => 3.1
42
+ // version 3.11.0 => 3.11
43
+ // This means we assume breaking cache format versions won't occur
44
+ // in patch releases. If worst case scenario it _is_ necessary we could
45
+ // add the patch version divided by 1000_000 e.g.:
46
+ // version 3.14.16 => 3.14 + 16/1000_000 = 3.140016
47
+ const CACHE_FORMAT_VERSION = 16;
30
48
 
31
49
  export default class Cache {
32
50
  /**
33
- * @param {import("../../types/cache-options.js").cacheStrategyType=} pCacheStrategy
51
+ * @param {cacheStrategyType=} pCacheStrategy
34
52
  * @param {boolean=} pCompress
35
53
  */
36
54
  constructor(pCacheStrategy, pCompress) {
@@ -42,10 +60,17 @@ export default class Cache {
42
60
  this.compress = pCompress ?? false;
43
61
  }
44
62
 
63
+ cacheFormatVersionCompatible(pCachedCruiseResult) {
64
+ return (
65
+ (pCachedCruiseResult?.revisionData?.cacheFormatVersion ?? 1) >=
66
+ CACHE_FORMAT_VERSION
67
+ );
68
+ }
69
+
45
70
  /**
46
- * @param {import("../../types/strict-options.js").IStrictCruiseOptions} pCruiseOptions
47
- * @param {import("../../types/dependency-cruiser.js").ICruiseResult} pCachedCruiseResult
48
- * @param {import("../../types/dependency-cruiser.js").IRevisionData=} pRevisionData
71
+ * @param {IStrictCruiseOptions} pCruiseOptions
72
+ * @param {ICruiseResult} pCachedCruiseResult
73
+ * @param {IRevisionData=} pRevisionData
49
74
  * @returns {Promise<boolean>}
50
75
  */
51
76
  async canServeFromCache(pCruiseOptions, pCachedCruiseResult, pRevisionData) {
@@ -61,15 +86,15 @@ export default class Cache {
61
86
  ),
62
87
  },
63
88
  ));
89
+ this.revisionData.cacheFormatVersion = CACHE_FORMAT_VERSION;
64
90
  bus.debug("cache: - comparing");
65
91
  return (
92
+ this.cacheFormatVersionCompatible(pCachedCruiseResult) &&
66
93
  this.cacheStrategy.revisionDataEqual(
67
94
  pCachedCruiseResult.revisionData,
68
95
  this.revisionData,
69
96
  ) &&
70
97
  optionsAreCompatible(
71
- // @ts-expect-error ts(2345) - it's indeed not strict cruise options,
72
- // but it will do for now (_it works_)
73
98
  pCachedCruiseResult.summary.optionsUsed,
74
99
  pCruiseOptions,
75
100
  )
@@ -78,7 +103,7 @@ export default class Cache {
78
103
 
79
104
  /**
80
105
  * @param {string} pCacheFolder
81
- * @returns {Promise<import("../../types/dependency-cruiser.js").ICruiseResult>}
106
+ * @returns {Promise<ICruiseResult>}
82
107
  */
83
108
  async read(pCacheFolder) {
84
109
  try {
@@ -133,11 +158,11 @@ export default class Cache {
133
158
 
134
159
  /**
135
160
  * @param {string} pCacheFolder
136
- * @param {import("../../types/dependency-cruiser.js").ICruiseResult} pCruiseResult
137
- * @param {import("../../types/dependency-cruiser.js").IRevisionData=} pRevisionData
161
+ * @param {ICruiseResult} pCruiseResult
162
+ * @param {IRevisionData=} pRevisionData
138
163
  */
139
164
  async write(pCacheFolder, pCruiseResult, pRevisionData) {
140
- const lRevisionData = pRevisionData ?? this.revisionData;
165
+ let lRevisionData = pRevisionData ?? this.revisionData;
141
166
 
142
167
  await mkdir(pCacheFolder, { recursive: true });
143
168
  const lUncompressedPayload = JSON.stringify(
@@ -9,9 +9,17 @@ import {
9
9
  moduleIsInterestingForDiff,
10
10
  } from "./helpers.mjs";
11
11
 
12
+ /**
13
+ * @typedef {import("../../types/dependency-cruiser.mjs").IModule} IModule
14
+ * @typedef {import("../../types/dependency-cruiser.mjs").IRevisionChange} IRevisionChange
15
+ * @typedef {import("../../types/dependency-cruiser.mjs").IRevisionData} IRevisionData
16
+ * @typedef {import("../../types/dependency-cruiser.mjs").ICruiseResult} ICruiseResult
17
+ * @typedef {import("../../types/strict-options.mjs").IStrictCruiseOptions} IStrictCruiseOptions
18
+ */
19
+
12
20
  /**
13
21
  * @param {string} pBaseDirectory
14
- * @returns {(pModule: import("../../types/dependency-cruiser.js").IModule) => import("../../types/dependency-cruiser.js").IModule}
22
+ * @returns {(IModule) => IModule}
15
23
  */
16
24
  function addCheckSumToModule(pBaseDirectory) {
17
25
  return (pModule) => {
@@ -26,9 +34,9 @@ function addCheckSumToModule(pBaseDirectory) {
26
34
  }
27
35
 
28
36
  /**
29
- * @param {import("../../types/dependency-cruiser.js").IRevisionChange[]} pChanges
30
- * @param {import("../../types/dependency-cruiser.js").IModule[]} pModules
31
- * @returns {import("../../types/dependency-cruiser.js").IRevisionChange[]}
37
+ * @param {IRevisionChange[]} pChanges
38
+ * @param {IModule[]} pModules
39
+ * @returns {IRevisionChange[]}
32
40
  */
33
41
  function refreshChanges(pChanges, pModules) {
34
42
  return pChanges.filter(
@@ -44,15 +52,15 @@ function refreshChanges(pChanges, pModules) {
44
52
  export default class ContentStrategy {
45
53
  /**
46
54
  * @param {string} pDirectory
47
- * @param {import("../../types/dependency-cruiser.js").ICruiseResult} pCachedCruiseResult
48
- * @param {import("../../types/strict-options.js").IStrictCruiseOptions} pCruiseOptions
55
+ * @param {ICruiseResult} pCachedCruiseResult
56
+ * @param {IStrictCruiseOptions} pCruiseOptions
49
57
  * @param {Object} pOptions
50
58
  * @param {Set<string>} pOptions.extensions
51
59
  * @param {Set<import("watskeburt").changeTypeType>=} pOptions.interestingChangeTypes?
52
60
  * @param {string=} pOptions.baseDir
53
61
  * @param {typeof findContentChanges=} pOptions.diffListFn
54
62
  * @param {typeof import('watskeburt').getSHA=} pOptions.checksumFn
55
- * @returns {import("../../types/dependency-cruiser.js").IRevisionData}
63
+ * @returns {IRevisionData}
56
64
  */
57
65
  getRevisionData(pDirectory, pCachedCruiseResult, pCruiseOptions, pOptions) {
58
66
  const lOptions = {
@@ -62,21 +70,20 @@ export default class ContentStrategy {
62
70
  };
63
71
  return {
64
72
  SHA1: "unknown-in-content-cache-strategy",
65
- changes:
66
- /** @type {import("../../types/dependency-cruiser.js").IRevisionChange[]} */ (
67
- lOptions.diffListFn(pDirectory, pCachedCruiseResult, {
68
- baseDir: lOptions.baseDir,
69
- extensions: lOptions.extensions,
70
- includeOnly: pCruiseOptions.includeOnly,
71
- exclude: pCruiseOptions.exclude,
72
- })
73
- ).filter(isInterestingChangeType(lOptions.interestingChangeTypes)),
73
+ changes: /** @type {IRevisionChange[]} */ (
74
+ lOptions.diffListFn(pDirectory, pCachedCruiseResult, {
75
+ baseDir: lOptions.baseDir,
76
+ extensions: lOptions.extensions,
77
+ includeOnly: pCruiseOptions.includeOnly,
78
+ exclude: pCruiseOptions.exclude,
79
+ })
80
+ ).filter(isInterestingChangeType(lOptions.interestingChangeTypes)),
74
81
  };
75
82
  }
76
83
 
77
84
  /**
78
- * @param {import("../../types/dependency-cruiser.js").IRevisionData=} pExistingRevisionData
79
- * @param {import("../../types/dependency-cruiser.js").IRevisionData=} pNewRevisionData
85
+ * @param {IRevisionData=} pExistingRevisionData
86
+ * @param {IRevisionData=} pNewRevisionData
80
87
  * @returns {boolean}
81
88
  */
82
89
  revisionDataEqual(pExistingRevisionData, pNewRevisionData) {
@@ -95,13 +102,13 @@ export default class ContentStrategy {
95
102
  }
96
103
 
97
104
  /**
98
- * @param {import("../../types/dependency-cruiser.js").ICruiseResult} pCruiseResult
99
- * @param {import("../../types/dependency-cruiser.js").IRevisionData=} pRevisionData
100
- * @returns {import("../../types/dependency-cruiser.js").ICruiseResult}
105
+ * @param {ICruiseResult} pCruiseResult
106
+ * @param {IRevisionData=} pRevisionData
107
+ * @returns {ICruiseResult}
101
108
  */
102
109
  prepareRevisionDataForSaving(pCruiseResult, pRevisionData) {
103
110
  const lModulesWithCheckSum = pCruiseResult.modules.map(
104
- addCheckSumToModule(pCruiseResult.summary.optionsUsed.baseDir),
111
+ addCheckSumToModule(pCruiseResult.summary.optionsUsed.baseDir || "."),
105
112
  );
106
113
  const lRevisionData = {
107
114
  ...pRevisionData,
@@ -69,8 +69,8 @@ function diffCachedModuleAgainstFileSet(
69
69
  * @param {Object} pOptions
70
70
  * @param {Set<string>} pOptions.extensions
71
71
  * @param {string} pOptions.baseDir
72
- * @param {import("../../types/strict-filter-types").IStrictExcludeType} pOptions.exclude
73
- * @param {import("../../types/strict-filter-types").IStrictIncludeOnlyType=} pOptions.includeOnly
72
+ * @param {import("../../types/strict-filter-types.mjs").IStrictExcludeType} pOptions.exclude
73
+ * @param {import("../../types/strict-filter-types.mjs").IStrictIncludeOnlyType=} pOptions.includeOnly
74
74
  * @returns {import("../..").IRevisionChange[]}
75
75
  */
76
76
  export default function findContentChanges(
@@ -6,6 +6,13 @@ import memoize from "lodash/memoize.js";
6
6
  // @ts-expect-error ts(2307) - the ts compiler is not privy to the existence of #imports in package.json
7
7
  import { filenameMatchesPattern } from "#graph-utl/match-facade.mjs";
8
8
 
9
+ /**
10
+ * @typedef {import("../..").IModule} IModule
11
+ * @typedef {import("../../types/dependency-cruiser.mjs").IRevisionChange} IRevisionChange
12
+ * @typedef {import("../../types/filter-types.mjs").IExcludeType} IExcludeType
13
+ * @typedef {import("../../types/strict-filter-types.mjs").IStrictIncludeOnlyType} IStrictIncludeOnlyType
14
+ */
15
+
9
16
  /**
10
17
  * @param {string} pString
11
18
  * @returns {string}
@@ -30,7 +37,7 @@ export const getFileHashSync = memoize(_getFileHashSync);
30
37
 
31
38
  /**
32
39
  * @param {import("watskeburt").IChange} pChange
33
- * @param {import("../../types/dependency-cruiser.js").IRevisionChange}
40
+ * @return {IRevisionChange}
34
41
  */
35
42
  export function addCheckSumToChangeSync(pChange) {
36
43
  return {
@@ -40,8 +47,7 @@ export function addCheckSumToChangeSync(pChange) {
40
47
  }
41
48
 
42
49
  /**
43
- *
44
- * @param {import("../../types/filter-types.js").IExcludeType} pExcludeOption
50
+ * @param {IExcludeType} pExcludeOption
45
51
  * @returns {(pFileName: string) => boolean}
46
52
  */
47
53
  export function excludeFilter(pExcludeOption) {
@@ -54,7 +60,7 @@ export function excludeFilter(pExcludeOption) {
54
60
  }
55
61
 
56
62
  /**
57
- * @param {import("../../types/strict-filter-types.js").IStrictIncludeOnlyType=} pIncludeOnlyFilter
63
+ * @param {IStrictIncludeOnlyType=} pIncludeOnlyFilter
58
64
  * @returns {(pFileName: string) => boolean}
59
65
  */
60
66
  export function includeOnlyFilter(pIncludeOnlyFilter) {
@@ -112,7 +118,8 @@ export function isInterestingChangeType(pInterestingChangeTypes) {
112
118
  }
113
119
 
114
120
  /**
115
- * @param {import("../..").IModule} pModule
121
+ * @param {IModule} pModule
122
+ * @returns {boolean}
116
123
  */
117
124
  export function moduleIsInterestingForDiff(pModule) {
118
125
  return (
@@ -12,6 +12,14 @@ import {
12
12
  // @ts-expect-error ts(2307) - the ts compiler is not privy to the existence of #imports in package.json
13
13
  import { bus } from "#utl/bus.mjs";
14
14
 
15
+ /**
16
+ * @typedef {import("../../types/dependency-cruiser.mjs").IModule} IModule
17
+ * @typedef {import("../../types/dependency-cruiser.mjs").IRevisionChange} IRevisionChange
18
+ * @typedef {import("../../types/dependency-cruiser.mjs").IRevisionData} IRevisionData
19
+ * @typedef {import("../../types/dependency-cruiser.mjs").ICruiseResult} ICruiseResult
20
+ * @typedef {import("../../types/strict-options.mjs").IStrictCruiseOptions} IStrictCruiseOptions
21
+ */
22
+
15
23
  export default class MetaDataStrategy {
16
24
  /**
17
25
  * @param {string} _pDirectory
@@ -22,7 +30,7 @@ export default class MetaDataStrategy {
22
30
  * @param {typeof getSHA=} pOptions.shaRetrievalFn
23
31
  * @param {typeof list=} pOptions.diffListFn
24
32
  * @param {typeof addCheckSumToChangeSync=} pOptions.checksumFn
25
- * @returns {Promise<import("../../types/dependency-cruiser.js").IRevisionData>}
33
+ * @returns {Promise<IRevisionData>}
26
34
  */
27
35
  async getRevisionData(
28
36
  _pDirectory,
@@ -63,8 +71,8 @@ export default class MetaDataStrategy {
63
71
  }
64
72
 
65
73
  /**
66
- * @param {import("../../types/dependency-cruiser.js").IRevisionData=} pExistingRevisionData
67
- * @param {import("../../types/dependency-cruiser.js").IRevisionData=} pNewRevisionData
74
+ * @param {IRevisionData=} pExistingRevisionData
75
+ * @param {IRevisionData=} pNewRevisionData
68
76
  * @returns {boolean}
69
77
  */
70
78
  revisionDataEqual(pExistingRevisionData, pNewRevisionData) {
@@ -81,9 +89,9 @@ export default class MetaDataStrategy {
81
89
  }
82
90
 
83
91
  /**
84
- * @param {import("../../types/dependency-cruiser.js").ICruiseResult} pCruiseResult
85
- * @param {import("../../types/dependency-cruiser.js").IRevisionData=} pRevisionData
86
- * @returns {import("../../types/dependency-cruiser.js").ICruiseResult}
92
+ * @param {ICruiseResult} pCruiseResult
93
+ * @param {IRevisionData=} pRevisionData
94
+ * @returns {ICruiseResult}
87
95
  */
88
96
  prepareRevisionDataForSaving(pCruiseResult, pRevisionData) {
89
97
  return pRevisionData
@@ -1,6 +1,10 @@
1
1
  // @ts-check
2
2
  import { isDeepStrictEqual } from "node:util";
3
3
 
4
+ /**
5
+ * @typedef {import("../../types/strict-options.mjs").IStrictCruiseOptions} IStrictCruiseOptions
6
+ */
7
+
4
8
  /*
5
9
  # command line options
6
10
  ## No influence on cache
@@ -88,8 +92,8 @@ export function cacheOptionIsCompatible(pExistingCacheOption, pNewCacheOption) {
88
92
 
89
93
  /**
90
94
  *
91
- * @param {import("../../types/strict-options").IStrictCruiseOptions} pOldOptions
92
- * @param {import("../../types/strict-options").IStrictCruiseOptions} pNewOptions
95
+ * @param {IStrictCruiseOptions} pOldOptions
96
+ * @param {IStrictCruiseOptions} pNewOptions
93
97
  * @returns {boolean}
94
98
  */
95
99
  // eslint-disable-next-line complexity
@@ -105,11 +109,11 @@ export function optionsAreCompatible(pOldOptions, pNewOptions) {
105
109
  includeOnlyIsCompatible(pOldOptions.includeOnly, pNewOptions.includeOnly) &&
106
110
  filterOptionIsCompatible(
107
111
  pOldOptions.doNotFollow,
108
- pNewOptions.doNotFollow
112
+ pNewOptions.doNotFollow,
109
113
  ) &&
110
114
  filterOptionIsCompatible(
111
115
  pOldOptions.moduleSystems,
112
- pNewOptions.moduleSystems
116
+ pNewOptions.moduleSystems,
113
117
  ) &&
114
118
  filterOptionIsCompatible(pOldOptions.exclude, pNewOptions.exclude) &&
115
119
  filterOptionIsCompatible(pOldOptions.focus, pNewOptions.focus) &&
@@ -119,15 +123,15 @@ export function optionsAreCompatible(pOldOptions, pNewOptions) {
119
123
  limitIsCompatible(pOldOptions.maxDepth, pNewOptions.maxDepth) &&
120
124
  optionIsCompatible(
121
125
  pOldOptions.knownViolations || [],
122
- pNewOptions.knownViolations || []
126
+ pNewOptions.knownViolations || [],
123
127
  ) &&
124
128
  optionIsCompatible(
125
129
  pOldOptions.enhancedResolveOptions,
126
- pNewOptions.enhancedResolveOptions
130
+ pNewOptions.enhancedResolveOptions,
127
131
  ) &&
128
132
  optionIsCompatible(
129
133
  pOldOptions.exoticRequireStrings,
130
- pNewOptions.exoticRequireStrings
134
+ pNewOptions.exoticRequireStrings,
131
135
  ) &&
132
136
  cacheOptionIsCompatible(pOldOptions.cache, pNewOptions.cache)
133
137
  );
@@ -2,15 +2,55 @@ import { readFile } from "node:fs/promises";
2
2
  import json5 from "json5";
3
3
  import makeAbsolute from "./make-absolute.mjs";
4
4
 
5
+ /**
6
+ * @typedef {import("../../types/violations.d.mjs").IViolation} IViolation
7
+ * @typedef {import("../../types/shared-types.d.mts").DependencyType} DependencyType
8
+ *
9
+ * @typedef {IViolation & {cycle: Array<{name: string, dependencyTypes: Array<DependencyType>}>} | {cycle: Array<DependencyType>}} IMaybeOldFormatViolation
10
+ */
11
+
12
+ /**
13
+ * cycles went from Array<string> to Array<{name: string, dependencyTypes: Array<DependencyType>}>
14
+ * in version 16.0.0. Known violations would typically be still in the old
15
+ * format. To prevent customers from bumping into error messages and having
16
+ * to update or regenerate their known violations, we'll make the old format
17
+ * forward compatible with this.
18
+ *
19
+ * @param {IMaybeOldFormatViolation} pKnownViolation
20
+ * @returns {IViolation}
21
+ */
22
+ function makeForwardCompatible(pKnownViolation) {
23
+ if (Boolean(pKnownViolation.cycle)) {
24
+ return {
25
+ ...pKnownViolation,
26
+ cycle: pKnownViolation.cycle.map((pModule) => {
27
+ if (Boolean(pModule.name)) {
28
+ return pModule;
29
+ }
30
+ return {
31
+ name: pModule,
32
+ dependencyTypes: [],
33
+ };
34
+ }),
35
+ };
36
+ }
37
+ return pKnownViolation;
38
+ }
39
+
5
40
  export default async function extractKnownViolations(pKnownViolationsFileName) {
6
41
  try {
7
- return json5.parse(
8
- await readFile(makeAbsolute(pKnownViolationsFileName), "utf8")
42
+ const lFileContents = await readFile(
43
+ makeAbsolute(pKnownViolationsFileName),
44
+ "utf8",
9
45
  );
46
+ const lKnownViolations = json5.parse(lFileContents);
47
+ const lForwardCompatible = lKnownViolations.map(makeForwardCompatible);
48
+
49
+ return lForwardCompatible;
10
50
  } catch (pError) {
11
51
  if (pError instanceof SyntaxError) {
12
52
  throw new SyntaxError(
13
- `'${pKnownViolationsFileName}' should be valid json\n ${pError}`
53
+ `'${pKnownViolationsFileName}' should be valid json\n ${pError}`,
14
54
  );
15
55
  }
16
56
  throw pError;
@@ -4,17 +4,13 @@ function addCircularityCheckToDependency(
4
4
  pIndexedGraph,
5
5
  pFrom,
6
6
  pToDep,
7
- pDependencyName
7
+ pDependencyName,
8
8
  ) {
9
9
  let lReturnValue = {
10
10
  ...pToDep,
11
11
  circular: false,
12
12
  };
13
- const lCycle = pIndexedGraph.getCycle(
14
- pFrom,
15
- pToDep[pDependencyName],
16
- pDependencyName
17
- );
13
+ const lCycle = pIndexedGraph.getCycle(pFrom, pToDep[pDependencyName]);
18
14
 
19
15
  if (lCycle.length > 0) {
20
16
  lReturnValue = {
@@ -35,7 +31,7 @@ function addCircularityCheckToDependency(
35
31
  export default function detectAndAddCycles(
36
32
  pNodes,
37
33
  pIndexedNodes,
38
- { pSourceAttribute, pDependencyName }
34
+ { pSourceAttribute, pDependencyName },
39
35
  ) {
40
36
  return pNodes.map((pModule) => ({
41
37
  ...pModule,
@@ -44,8 +40,8 @@ export default function detectAndAddCycles(
44
40
  pIndexedNodes,
45
41
  pModule[pSourceAttribute],
46
42
  pToDep,
47
- pDependencyName
48
- )
43
+ pDependencyName,
44
+ ),
49
45
  ),
50
46
  }));
51
47
  }
@@ -20,7 +20,7 @@ function addInstabilityToDependency(pAllModules) {
20
20
  return (pDependency) => ({
21
21
  ...pDependency,
22
22
  instability:
23
- (lIndexedModules.findModuleByName(pDependency.resolved) || {})
23
+ (lIndexedModules.findVertexByName(pDependency.resolved) || {})
24
24
  .instability || 0,
25
25
  });
26
26
  }
@@ -1,3 +1,7 @@
1
+ function pluckName({ name }) {
2
+ return name;
3
+ }
4
+
1
5
  export default function isSameViolation(pLeftViolation, pRightViolation) {
2
6
  let lReturnValue = false;
3
7
 
@@ -5,9 +9,11 @@ export default function isSameViolation(pLeftViolation, pRightViolation) {
5
9
  if (pRightViolation.cycle && pLeftViolation.cycle) {
6
10
  lReturnValue =
7
11
  pLeftViolation.cycle.length === pRightViolation.cycle.length &&
8
- pLeftViolation.cycle.every((pModule) =>
9
- pRightViolation.cycle.includes(pModule)
10
- );
12
+ pLeftViolation.cycle
13
+ .map(pluckName)
14
+ .every((pModule) =>
15
+ pRightViolation.cycle.map(pluckName).includes(pModule),
16
+ );
11
17
  } else {
12
18
  lReturnValue =
13
19
  pLeftViolation.from === pRightViolation.from &&
@@ -1,3 +1,4 @@
1
+ /* eslint-disable max-lines */
1
2
  import { isAbsolute, resolve as path_resolve } from "node:path";
2
3
  import { join as posix_join } from "node:path/posix";
3
4
  import { isMatch } from "picomatch";
@@ -219,6 +220,8 @@ function stripIndex(pModulePath) {
219
220
  *
220
221
  * https://www.typescriptlang.org/docs/handbook/modules/reference.html#baseurl
221
222
  *
223
+ * Assumes the pModuleName is not relative.
224
+ *
222
225
  * @param {string} pModuleName
223
226
  * @param {string} pResolvedModuleName
224
227
  * @param {string} pTSConfigBaseURL
@@ -228,7 +231,13 @@ function matchesTSConfigBaseURL(
228
231
  pResolvedModuleName,
229
232
  pTSConfigBaseURL,
230
233
  ) {
231
- if (!pTSConfigBaseURL) {
234
+ // the pModuleName === pResolvedModuleName check is there to prevent
235
+ // false positives for core modules ('fs' resolved === 'fs') and modules that
236
+ // we couldn't resolve at all (e.g. 'this/does/not/exist' => 'this/does/not/exist')
237
+ //
238
+ // we could also check whether the moduleName is relative, but that's
239
+ // not efficient as that was already done before this function was called.
240
+ if (!pTSConfigBaseURL || pModuleName === pResolvedModuleName) {
232
241
  return false;
233
242
  }
234
243
  // "If baseUrl is set, TypeScript will resolve non-relative module names