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 +1 -1
- package/src/cache/cache.mjs +35 -10
- package/src/cache/content-strategy.mjs +29 -22
- package/src/cache/find-content-changes.mjs +2 -2
- package/src/cache/helpers.mjs +12 -5
- package/src/cache/metadata-strategy.mjs +14 -6
- package/src/cache/options-compatible.mjs +11 -7
- package/src/config-utl/extract-known-violations.mjs +43 -3
- package/src/enrich/derive/circular.mjs +5 -9
- package/src/enrich/derive/metrics/get-module-metrics.mjs +1 -1
- package/src/enrich/summarize/is-same-violation.mjs +9 -3
- package/src/extract/resolve/module-classifiers.mjs +10 -1
- package/src/graph-utl/indexed-module-graph.mjs +135 -47
- package/src/meta.js +1 -1
- package/src/report/anon/anonymize-path.mjs +1 -1
- package/src/report/anon/index.mjs +15 -4
- package/src/report/anon/random-string.mjs +1 -1
- package/src/report/azure-devops.mjs +3 -1
- package/src/report/error-html/utl.mjs +1 -1
- package/src/report/error.mjs +10 -1
- package/src/report/teamcity.mjs +11 -9
- package/src/schema/baseline-violations.schema.mjs +1 -1
- package/src/schema/configuration.schema.mjs +1 -1
- package/src/schema/cruise-result.schema.mjs +1 -1
- package/src/validate/matchers.mjs +23 -13
- package/types/cruise-result.d.mts +9 -2
- package/types/shared-types.d.mts +12 -0
- package/types/violations.d.mts +2 -2
package/package.json
CHANGED
package/src/cache/cache.mjs
CHANGED
|
@@ -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 {
|
|
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 {
|
|
47
|
-
* @param {
|
|
48
|
-
* @param {
|
|
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<
|
|
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 {
|
|
137
|
-
* @param {
|
|
161
|
+
* @param {ICruiseResult} pCruiseResult
|
|
162
|
+
* @param {IRevisionData=} pRevisionData
|
|
138
163
|
*/
|
|
139
164
|
async write(pCacheFolder, pCruiseResult, pRevisionData) {
|
|
140
|
-
|
|
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 {(
|
|
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 {
|
|
30
|
-
* @param {
|
|
31
|
-
* @returns {
|
|
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 {
|
|
48
|
-
* @param {
|
|
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 {
|
|
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
|
-
|
|
67
|
-
lOptions.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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 {
|
|
79
|
-
* @param {
|
|
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 {
|
|
99
|
-
* @param {
|
|
100
|
-
* @returns {
|
|
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(
|
package/src/cache/helpers.mjs
CHANGED
|
@@ -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
|
-
* @
|
|
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 {
|
|
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 {
|
|
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<
|
|
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 {
|
|
67
|
-
* @param {
|
|
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 {
|
|
85
|
-
* @param {
|
|
86
|
-
* @returns {
|
|
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 {
|
|
92
|
-
* @param {
|
|
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
|
-
|
|
8
|
-
|
|
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.
|
|
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
|
|
9
|
-
|
|
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
|
-
|
|
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
|