dependency-cruiser 10.8.0 → 11.2.0
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/README.md +1 -1
- package/package.json +22 -16
- package/src/enrich/clear-caches.js +1 -1
- package/src/enrich/derive/circular/get-cycle.js +1 -1
- package/src/enrich/derive/dependents/get-dependents.js +1 -1
- package/src/enrich/derive/{metrics/get-stability-metrics.js → folders/aggregate-to-folders.js} +39 -30
- package/src/enrich/derive/folders/index.js +9 -0
- package/src/enrich/derive/folders/utl.js +44 -0
- package/src/enrich/derive/metrics/get-module-metrics.js +39 -0
- package/src/enrich/derive/metrics/index.js +14 -0
- package/src/enrich/derive/{utl.js → module-utl.js} +28 -0
- package/src/enrich/derive/orphan/is-orphan.js +1 -1
- package/src/enrich/derive/reachable/get-path.js +1 -1
- package/src/enrich/derive/reachable/index.js +1 -1
- package/src/enrich/enrich-modules.js +2 -2
- package/src/enrich/index.js +2 -2
- package/src/enrich/summarize/summarize-modules.js +24 -5
- package/src/extract/get-dependencies.js +11 -6
- package/src/extract/parse/to-typescript-ast.js +24 -8
- package/src/extract/utl/get-extension.js +9 -5
- package/src/main/options/normalize.js +28 -0
- package/src/meta.js +1 -1
- package/src/report/dot/dot.template.js +1 -1
- package/src/report/dot/index.js +7 -2
- package/src/report/dot/module-utl.js +47 -19
- package/src/report/dot/prepare-custom-level.js +2 -2
- package/src/report/dot/prepare-flat-level.js +2 -2
- package/src/report/dot/prepare-folder-level.js +2 -2
- package/src/report/error-html/error-html.template.js +1 -1
- package/src/report/error-html/utl.js +50 -7
- package/src/report/error.js +55 -21
- package/src/report/teamcity.js +37 -11
- package/src/report/utl/index.js +16 -0
- package/src/schema/baseline-violations.schema.js +1 -35
- package/src/schema/configuration.schema.js +1 -493
- package/src/schema/cruise-result.schema.js +1 -642
- package/src/utl/regex-util.js +57 -0
- package/src/validate/match-dependency-rule.js +19 -32
- package/src/validate/match-module-rule.js +1 -1
- package/src/validate/matchers.js +58 -48
- package/src/validate/violates-required-rule.js +1 -1
- package/types/baseline-violations.d.ts +1 -1
- package/types/cruise-result.d.ts +11 -50
- package/types/options.d.ts +5 -0
- package/types/reporter-options.d.ts +6 -0
- package/types/restrictions.d.ts +12 -0
- package/types/rule-summary.d.ts +23 -0
- package/types/shared-types.d.ts +7 -0
- package/types/violations.d.ts +43 -0
- package/src/enrich/derive/metrics/folder.js +0 -9
- package/src/enrich/derive/metrics/module-utl.js +0 -27
- package/src/enrich/derive/metrics/module.js +0 -29
- package/src/enrich/derive/metrics/utl.js +0 -28
- package/src/validate/utl.js +0 -31
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* If there is at least one group expression in the given pRulePath
|
|
3
|
+
* return the first matched one.
|
|
4
|
+
*
|
|
5
|
+
* Return null in all other cases.
|
|
6
|
+
*
|
|
7
|
+
* This fills our current need. Later we can expand it to return all group
|
|
8
|
+
* matches.
|
|
9
|
+
*
|
|
10
|
+
* @param {import("../../types/rule-set").IFromRestriction} pFromRestriction
|
|
11
|
+
* @param {string} pActualPath
|
|
12
|
+
* @returns {string[]|null}
|
|
13
|
+
*/
|
|
14
|
+
function extractGroups(pFromRestriction, pActualPath) {
|
|
15
|
+
let lReturnValue = [];
|
|
16
|
+
|
|
17
|
+
if (Boolean(pFromRestriction.path)) {
|
|
18
|
+
let lMatchResult = pActualPath.match(pFromRestriction.path);
|
|
19
|
+
|
|
20
|
+
if (Boolean(lMatchResult) && lMatchResult.length > 1) {
|
|
21
|
+
lReturnValue = lMatchResult.filter(
|
|
22
|
+
(pResult) => typeof pResult === "string"
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return lReturnValue;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
*
|
|
31
|
+
* Examples:
|
|
32
|
+
* replaceGroupPlaceholders("./src/components/$1", ["wholematch", "lala-component"]) =>
|
|
33
|
+
* './src/components/lala-component'
|
|
34
|
+
*
|
|
35
|
+
* replaceGroupPlaceholders("./test/components/$1/$2.spec.js$", ["wholematch", "lala-component", "things"]) =>
|
|
36
|
+
* './test/components/lala-component/things.spec.js$'
|
|
37
|
+
*
|
|
38
|
+
* @param {string} pString
|
|
39
|
+
* @param {string[]} pExtractedGroups - note that when using the result of a
|
|
40
|
+
* regex match, the 0th index contains the whole matched string and indices
|
|
41
|
+
* > 1 contain matched groups
|
|
42
|
+
* @returns {string} pString with the matching groups replaced with the
|
|
43
|
+
* groups from pExtractedgroups
|
|
44
|
+
*/
|
|
45
|
+
function replaceGroupPlaceholders(pString, pExtractedGroups) {
|
|
46
|
+
return pExtractedGroups.reduce(
|
|
47
|
+
(pAll, pThis, pIndex) =>
|
|
48
|
+
// eslint-disable-next-line security/detect-non-literal-regexp
|
|
49
|
+
pAll.replace(new RegExp(`\\$${pIndex}`, "g"), pThis),
|
|
50
|
+
pString
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = {
|
|
55
|
+
extractGroups,
|
|
56
|
+
replaceGroupPlaceholders,
|
|
57
|
+
};
|
|
@@ -1,25 +1,6 @@
|
|
|
1
|
-
const
|
|
1
|
+
const { extractGroups } = require("../utl/regex-util");
|
|
2
2
|
const isModuleOnlyRule = require("./is-module-only-rule");
|
|
3
3
|
const matchers = require("./matchers");
|
|
4
|
-
const { extractGroups } = require("./utl");
|
|
5
|
-
|
|
6
|
-
function propertyEquals(pTo, pRule, pProperty) {
|
|
7
|
-
// ignore security/detect-object-injection because:
|
|
8
|
-
// - we only use it from within the module with two fixed values
|
|
9
|
-
// - the propertyEquals function is not exposed externaly
|
|
10
|
-
return _has(pRule.to, pProperty)
|
|
11
|
-
? // eslint-disable-next-line security/detect-object-injection
|
|
12
|
-
pTo[pProperty] === pRule.to[pProperty]
|
|
13
|
-
: true;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function matchesMoreThanOneDependencyType(pRuleTo, pTo) {
|
|
17
|
-
if (_has(pRuleTo, "moreThanOneDependencyType")) {
|
|
18
|
-
return pRuleTo.moreThanOneDependencyType === pTo.dependencyTypes.length > 1;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
4
|
|
|
24
5
|
function match(pFrom, pTo) {
|
|
25
6
|
// eslint-disable-next-line complexity
|
|
@@ -33,22 +14,28 @@ function match(pFrom, pTo) {
|
|
|
33
14
|
matchers.toPathNot(pRule, pTo, lGroups) &&
|
|
34
15
|
matchers.toDependencyTypes(pRule, pTo) &&
|
|
35
16
|
matchers.toDependencyTypesNot(pRule, pTo) &&
|
|
36
|
-
matchesMoreThanOneDependencyType(pRule
|
|
37
|
-
matchers.toLicense(pRule, pTo) &&
|
|
38
|
-
matchers.toLicenseNot(pRule, pTo) &&
|
|
39
|
-
matchers.toExoticRequire(pRule, pTo) &&
|
|
40
|
-
matchers.toExoticRequireNot(pRule, pTo) &&
|
|
41
|
-
matchers.toVia(pRule, pTo) &&
|
|
42
|
-
matchers.toViaNot(pRule, pTo) &&
|
|
17
|
+
matchers.matchesMoreThanOneDependencyType(pRule, pTo) &&
|
|
43
18
|
// preCompilationOnly is not a mandatory attribute, but if the attribute
|
|
44
19
|
// is in the rule but not in the dependency there won't be a match
|
|
45
20
|
// anyway, so we can use the default propertyEquals method regardless
|
|
46
|
-
propertyEquals(
|
|
21
|
+
matchers.propertyEquals(pRule, pTo, "preCompilationOnly") &&
|
|
47
22
|
// couldNotResolve, circular, dynamic and exoticallyRequired _are_ mandatory
|
|
48
|
-
propertyEquals(
|
|
49
|
-
propertyEquals(
|
|
50
|
-
propertyEquals(
|
|
51
|
-
propertyEquals(
|
|
23
|
+
matchers.propertyEquals(pRule, pTo, "couldNotResolve") &&
|
|
24
|
+
matchers.propertyEquals(pRule, pTo, "circular") &&
|
|
25
|
+
matchers.propertyEquals(pRule, pTo, "dynamic") &&
|
|
26
|
+
matchers.propertyEquals(pRule, pTo, "exoticallyRequired") &&
|
|
27
|
+
matchers.propertyMatches(pRule, pTo, "license", "license") &&
|
|
28
|
+
matchers.propertyMatchesNot(pRule, pTo, "licenseNot", "license") &&
|
|
29
|
+
matchers.propertyMatches(pRule, pTo, "exoticRequire", "exoticRequire") &&
|
|
30
|
+
matchers.propertyMatchesNot(
|
|
31
|
+
pRule,
|
|
32
|
+
pTo,
|
|
33
|
+
"exoticRequireNot",
|
|
34
|
+
"exoticRequire"
|
|
35
|
+
) &&
|
|
36
|
+
matchers.toVia(pRule, pTo) &&
|
|
37
|
+
matchers.toViaNot(pRule, pTo) &&
|
|
38
|
+
matchers.toIsMoreUnstable(pRule, pFrom, pTo)
|
|
52
39
|
);
|
|
53
40
|
};
|
|
54
41
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const _has = require("lodash/has");
|
|
2
|
+
const { extractGroups } = require("../utl/regex-util");
|
|
2
3
|
const isModuleOnlyRule = require("./is-module-only-rule");
|
|
3
4
|
const matchers = require("./matchers");
|
|
4
|
-
const { extractGroups } = require("./utl");
|
|
5
5
|
|
|
6
6
|
function matchesOrphanRule(pRule, pModule) {
|
|
7
7
|
return (
|
package/src/validate/matchers.js
CHANGED
|
@@ -1,4 +1,31 @@
|
|
|
1
|
+
/* eslint-disable security/detect-object-injection */
|
|
2
|
+
const _has = require("lodash/has");
|
|
1
3
|
const { intersects } = require("../utl/array-util");
|
|
4
|
+
const { replaceGroupPlaceholders } = require("../utl/regex-util");
|
|
5
|
+
|
|
6
|
+
function propertyEquals(pRule, pDependency, pProperty) {
|
|
7
|
+
// The properties can be booleans, so we can't use !pRule.to[pProperty]
|
|
8
|
+
if (_has(pRule.to, pProperty)) {
|
|
9
|
+
return pDependency[pProperty] === pRule.to[pProperty];
|
|
10
|
+
}
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function propertyMatches(pRule, pDependency, pRuleProperty, pProperty) {
|
|
15
|
+
return Boolean(
|
|
16
|
+
!pRule.to[pRuleProperty] ||
|
|
17
|
+
(pDependency[pProperty] &&
|
|
18
|
+
pDependency[pProperty].match(pRule.to[pRuleProperty]))
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function propertyMatchesNot(pRule, pDependency, pRuleProperty, pProperty) {
|
|
23
|
+
return Boolean(
|
|
24
|
+
!pRule.to[pRuleProperty] ||
|
|
25
|
+
(pDependency[pProperty] &&
|
|
26
|
+
!pDependency[pProperty].match(pRule.to[pRuleProperty]))
|
|
27
|
+
);
|
|
28
|
+
}
|
|
2
29
|
|
|
3
30
|
function fromPath(pRule, pModule) {
|
|
4
31
|
return Boolean(!pRule.from.path || pModule.source.match(pRule.from.path));
|
|
@@ -20,20 +47,11 @@ function modulePathNot(pRule, pModule) {
|
|
|
20
47
|
);
|
|
21
48
|
}
|
|
22
49
|
|
|
23
|
-
function _replaceGroupPlaceholders(pString, pExtractedGroups) {
|
|
24
|
-
return pExtractedGroups.reduce(
|
|
25
|
-
(pAll, pThis, pIndex) =>
|
|
26
|
-
// eslint-disable-next-line security/detect-non-literal-regexp
|
|
27
|
-
pAll.replace(new RegExp(`\\$${pIndex}`, "g"), pThis),
|
|
28
|
-
pString
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
50
|
function _toPath(pRule, pString, pGroups = []) {
|
|
33
51
|
return Boolean(
|
|
34
52
|
!pRule.to.path ||
|
|
35
53
|
(pGroups.length > 0
|
|
36
|
-
? pString.match(
|
|
54
|
+
? pString.match(replaceGroupPlaceholders(pRule.to.path, pGroups))
|
|
37
55
|
: pString.match(pRule.to.path))
|
|
38
56
|
);
|
|
39
57
|
}
|
|
@@ -50,7 +68,7 @@ function _toPathNot(pRule, pString, pGroups = []) {
|
|
|
50
68
|
return (
|
|
51
69
|
!Boolean(pRule.to.pathNot) ||
|
|
52
70
|
!(pGroups.length > 0
|
|
53
|
-
? pString.match(
|
|
71
|
+
? pString.match(replaceGroupPlaceholders(pRule.to.pathNot, pGroups))
|
|
54
72
|
: pString.match(pRule.to.pathNot))
|
|
55
73
|
);
|
|
56
74
|
}
|
|
@@ -77,33 +95,11 @@ function toDependencyTypesNot(pRule, pDependency) {
|
|
|
77
95
|
);
|
|
78
96
|
}
|
|
79
97
|
|
|
80
|
-
function
|
|
81
|
-
return Boolean(
|
|
82
|
-
!pRule.to.license ||
|
|
83
|
-
(pDependency.license && pDependency.license.match(pRule.to.license))
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function toLicenseNot(pRule, pDependency) {
|
|
88
|
-
return Boolean(
|
|
89
|
-
!pRule.to.licenseNot ||
|
|
90
|
-
(pDependency.license && !pDependency.license.match(pRule.to.licenseNot))
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function toExoticRequire(pRule, pDependency) {
|
|
95
|
-
return Boolean(
|
|
96
|
-
!pRule.to.exoticRequire ||
|
|
97
|
-
(pDependency.exoticRequire &&
|
|
98
|
-
pDependency.exoticRequire.match(pRule.to.exoticRequire))
|
|
99
|
-
);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function toExoticRequireNot(pRule, pDependency) {
|
|
98
|
+
function toVia(pRule, pDependency) {
|
|
103
99
|
return Boolean(
|
|
104
|
-
!pRule.to.
|
|
105
|
-
(pDependency.
|
|
106
|
-
|
|
100
|
+
!pRule.to.via ||
|
|
101
|
+
(pDependency.cycle &&
|
|
102
|
+
pDependency.cycle.some((pVia) => pVia.match(pRule.to.via)))
|
|
107
103
|
);
|
|
108
104
|
}
|
|
109
105
|
|
|
@@ -115,16 +111,32 @@ function toViaNot(pRule, pDependency) {
|
|
|
115
111
|
);
|
|
116
112
|
}
|
|
117
113
|
|
|
118
|
-
function
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
(
|
|
122
|
-
|
|
123
|
-
|
|
114
|
+
function toIsMoreUnstable(pRule, pModule, pDependency) {
|
|
115
|
+
if (_has(pRule, "to.moreUnstable")) {
|
|
116
|
+
return (
|
|
117
|
+
(pRule.to.moreUnstable &&
|
|
118
|
+
pModule.instability < pDependency.instability) ||
|
|
119
|
+
(!pRule.to.moreUnstable && pModule.instability >= pDependency.instability)
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function matchesMoreThanOneDependencyType(pRule, pDependency) {
|
|
126
|
+
if (_has(pRule.to, "moreThanOneDependencyType")) {
|
|
127
|
+
return (
|
|
128
|
+
pRule.to.moreThanOneDependencyType ===
|
|
129
|
+
pDependency.dependencyTypes.length > 1
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
return true;
|
|
124
133
|
}
|
|
125
134
|
|
|
126
135
|
module.exports = {
|
|
127
|
-
|
|
136
|
+
replaceGroupPlaceholders,
|
|
137
|
+
propertyEquals,
|
|
138
|
+
propertyMatches,
|
|
139
|
+
propertyMatchesNot,
|
|
128
140
|
fromPath,
|
|
129
141
|
fromPathNot,
|
|
130
142
|
toPath,
|
|
@@ -135,10 +147,8 @@ module.exports = {
|
|
|
135
147
|
toModulePathNot,
|
|
136
148
|
toDependencyTypes,
|
|
137
149
|
toDependencyTypesNot,
|
|
138
|
-
toLicense,
|
|
139
|
-
toLicenseNot,
|
|
140
|
-
toExoticRequire,
|
|
141
|
-
toExoticRequireNot,
|
|
142
150
|
toVia,
|
|
143
151
|
toViaNot,
|
|
152
|
+
toIsMoreUnstable,
|
|
153
|
+
matchesMoreThanOneDependencyType,
|
|
144
154
|
};
|
package/types/cruise-result.d.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { ICruiseOptions } from "./options";
|
|
2
2
|
import { IFlattenedRuleSet } from "./rule-set";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
SeverityType,
|
|
7
|
-
ProtocolType,
|
|
8
|
-
} from "./shared-types";
|
|
3
|
+
import { DependencyType, ModuleSystemType, ProtocolType } from "./shared-types";
|
|
4
|
+
import { IViolation } from "./violations";
|
|
5
|
+
import { IRuleSummary } from "./rule-summary";
|
|
9
6
|
|
|
10
7
|
export interface ICruiseResult {
|
|
11
8
|
/**
|
|
@@ -224,27 +221,11 @@ export interface IDependency {
|
|
|
224
221
|
* will be in the 'rule' object at the same level.
|
|
225
222
|
*/
|
|
226
223
|
valid: boolean;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* If there was a rule violation (valid === false), this object contains the name of the
|
|
231
|
-
* rule and severity of violating it.
|
|
232
|
-
*/
|
|
233
|
-
export interface IRuleSummary {
|
|
234
224
|
/**
|
|
235
|
-
*
|
|
236
|
-
* '
|
|
225
|
+
* the (de-normalized) instability of the dependency - also available in
|
|
226
|
+
* the module on the 'to' side of this dependency
|
|
237
227
|
*/
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* How severe a violation of a rule is. The 'error' severity will make some reporters return
|
|
241
|
-
* a non-zero exit code, so if you want e.g. a build to stop when there's a rule violated:
|
|
242
|
-
* use that. The absence of the 'ignore' severity here is by design; ignored rules don't
|
|
243
|
-
* show up in the output.
|
|
244
|
-
*
|
|
245
|
-
* Severity to use when a dependency is not in the 'allowed' set of rules. Defaults to 'warn'
|
|
246
|
-
*/
|
|
247
|
-
severity: SeverityType;
|
|
228
|
+
instability: number;
|
|
248
229
|
}
|
|
249
230
|
|
|
250
231
|
export interface IReachable {
|
|
@@ -389,29 +370,6 @@ export interface IWebpackConfig {
|
|
|
389
370
|
*/
|
|
390
371
|
export type WebpackEnvType = { [key: string]: any } | string;
|
|
391
372
|
|
|
392
|
-
export interface IViolation {
|
|
393
|
-
/**
|
|
394
|
-
* The violated rule
|
|
395
|
-
*/
|
|
396
|
-
rule: IRuleSummary;
|
|
397
|
-
/**
|
|
398
|
-
* The from part of the dependency this violation is about
|
|
399
|
-
*/
|
|
400
|
-
from: string;
|
|
401
|
-
/**
|
|
402
|
-
* The to part of the dependency this violation is about
|
|
403
|
-
*/
|
|
404
|
-
to: string;
|
|
405
|
-
/**
|
|
406
|
-
* The circular path if the violation is about circularity
|
|
407
|
-
*/
|
|
408
|
-
cycle?: string[];
|
|
409
|
-
/**
|
|
410
|
-
* The path from the from to the to if the violation is transitive
|
|
411
|
-
*/
|
|
412
|
-
via?: string[];
|
|
413
|
-
}
|
|
414
|
-
|
|
415
373
|
export interface IFolder {
|
|
416
374
|
/**
|
|
417
375
|
* The name of the folder. FOlder names are normalized to posix (so
|
|
@@ -421,11 +379,11 @@ export interface IFolder {
|
|
|
421
379
|
/**
|
|
422
380
|
* List of folders depending on this folder
|
|
423
381
|
*/
|
|
424
|
-
dependents?: string[];
|
|
382
|
+
dependents?: { name: string }[];
|
|
425
383
|
/**
|
|
426
384
|
* List of folders this folder depends upon
|
|
427
385
|
*/
|
|
428
|
-
dependencies?: string[];
|
|
386
|
+
dependencies?: { name: string; instability: number }[];
|
|
429
387
|
/**
|
|
430
388
|
* The total number of modules detected in this folder and its sub-folders
|
|
431
389
|
*/
|
|
@@ -455,3 +413,6 @@ export interface IFolder {
|
|
|
455
413
|
*/
|
|
456
414
|
instability?: number;
|
|
457
415
|
}
|
|
416
|
+
|
|
417
|
+
export * from "./violations";
|
|
418
|
+
export * from "./rule-summary";
|
package/types/options.d.ts
CHANGED
|
@@ -239,4 +239,9 @@ export interface ICruiseOptions {
|
|
|
239
239
|
*/
|
|
240
240
|
type: ProgressType;
|
|
241
241
|
};
|
|
242
|
+
/**
|
|
243
|
+
* When this flag is set to true, dependency-cruiser will calculate (stability) metrics
|
|
244
|
+
* for all modules and folders. Defaults to false.
|
|
245
|
+
*/
|
|
246
|
+
metrics?: boolean;
|
|
242
247
|
}
|
|
@@ -58,6 +58,12 @@ export interface IDotReporterOptions {
|
|
|
58
58
|
* goal of the report)
|
|
59
59
|
*/
|
|
60
60
|
filters?: IReporterFiltersType;
|
|
61
|
+
/**
|
|
62
|
+
* When passed the value 'true', shows instability metrics in the
|
|
63
|
+
* output if dependency-cruiser calculated them. Doesn't show them
|
|
64
|
+
* in all other cases. Defaults to false",
|
|
65
|
+
*/
|
|
66
|
+
showMetrics?: boolean;
|
|
61
67
|
/**
|
|
62
68
|
* A bunch of criteria to (conditionally) theme the dot output
|
|
63
69
|
*/
|
package/types/restrictions.d.ts
CHANGED
|
@@ -92,6 +92,18 @@ export interface IToRestriction extends IBaseRestrictionType {
|
|
|
92
92
|
* licenses. E.g. to flag everyting non MIT use "MIT" here
|
|
93
93
|
*/
|
|
94
94
|
licenseNot?: string | string[];
|
|
95
|
+
/**
|
|
96
|
+
* When set to true moreUnstable matches for any dependency that has a higher
|
|
97
|
+
* Instability than the module that depends on it. When set to false it matches
|
|
98
|
+
* when the opposite is true; the dependency has an equal or lower Instability.
|
|
99
|
+
*
|
|
100
|
+
* This attribute is useful when you want to check against Robert C. Martin's
|
|
101
|
+
* stable dependency * principle. See online documentation for examples and
|
|
102
|
+
* details.
|
|
103
|
+
*
|
|
104
|
+
* Leave this out when you don't care either way.
|
|
105
|
+
*/
|
|
106
|
+
moreUnstable?: boolean;
|
|
95
107
|
}
|
|
96
108
|
|
|
97
109
|
export interface IReachabilityToRestrictionType extends IBaseRestrictionType {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SeverityType } from "./shared-types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* If there was a rule violation (valid === false), this object contains the name of the
|
|
5
|
+
* rule and severity of violating it.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface IRuleSummary {
|
|
9
|
+
/**
|
|
10
|
+
* The (short, eslint style) name of the violated rule. Typically something like
|
|
11
|
+
* 'no-core-punycode' or 'no-outside-deps'.
|
|
12
|
+
*/
|
|
13
|
+
name: string;
|
|
14
|
+
/**
|
|
15
|
+
* How severe a violation of a rule is. The 'error' severity will make some reporters return
|
|
16
|
+
* a non-zero exit code, so if you want e.g. a build to stop when there's a rule violated:
|
|
17
|
+
* use that. The absence of the 'ignore' severity here is by design; ignored rules don't
|
|
18
|
+
* show up in the output.
|
|
19
|
+
*
|
|
20
|
+
* Severity to use when a dependency is not in the 'allowed' set of rules. Defaults to 'warn'
|
|
21
|
+
*/
|
|
22
|
+
severity: SeverityType;
|
|
23
|
+
}
|
package/types/shared-types.d.ts
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { IRuleSummary } from "./rule-summary";
|
|
2
|
+
import { ViolationType } from "./shared-types";
|
|
3
|
+
|
|
4
|
+
export interface IMetricsSummary {
|
|
5
|
+
from: {
|
|
6
|
+
instability: number;
|
|
7
|
+
};
|
|
8
|
+
to: {
|
|
9
|
+
instability: number;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface IViolation {
|
|
14
|
+
/**
|
|
15
|
+
* Type of violation. When left out assumed to be of type 'dependency'
|
|
16
|
+
*/
|
|
17
|
+
type?: ViolationType;
|
|
18
|
+
/**
|
|
19
|
+
* The violated rule
|
|
20
|
+
*/
|
|
21
|
+
rule: IRuleSummary;
|
|
22
|
+
/**
|
|
23
|
+
* The from part of the dependency this violation is about
|
|
24
|
+
*/
|
|
25
|
+
from: string;
|
|
26
|
+
/**
|
|
27
|
+
* The to part of the dependency this violation is about
|
|
28
|
+
*/
|
|
29
|
+
to: string;
|
|
30
|
+
/**
|
|
31
|
+
* The circular path if the violation is about circularity
|
|
32
|
+
*/
|
|
33
|
+
cycle?: string[];
|
|
34
|
+
/**
|
|
35
|
+
* The path from the from to the to if the violation is transitive
|
|
36
|
+
*/
|
|
37
|
+
via?: string[];
|
|
38
|
+
/**
|
|
39
|
+
* metrics - when the violation pertains to a violation of a metrics
|
|
40
|
+
* principle
|
|
41
|
+
*/
|
|
42
|
+
metrics?: IMetricsSummary;
|
|
43
|
+
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
const getStabilityMetrics = require("./get-stability-metrics");
|
|
2
|
-
const { shouldDeriveMetrics } = require("./utl");
|
|
3
|
-
|
|
4
|
-
module.exports = function deriveMetrics(pModules, pOptions) {
|
|
5
|
-
if (shouldDeriveMetrics(pOptions)) {
|
|
6
|
-
return { folders: getStabilityMetrics(pModules) };
|
|
7
|
-
}
|
|
8
|
-
return {};
|
|
9
|
-
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
const path = require("path").posix;
|
|
2
|
-
|
|
3
|
-
function getAfferentCouplings(pModule, pDirname) {
|
|
4
|
-
return pModule.dependents.filter(
|
|
5
|
-
(pDependent) => !pDependent.startsWith(pDirname.concat(path.sep))
|
|
6
|
-
);
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function getEfferentCouplings(pModule, pDirname) {
|
|
10
|
-
return pModule.dependencies.filter(
|
|
11
|
-
(pDependency) => !pDependency.resolved.startsWith(pDirname.concat(path.sep))
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function metricsAreCalculable(pModule) {
|
|
16
|
-
return (
|
|
17
|
-
!pModule.coreModule &&
|
|
18
|
-
!pModule.couldNotResolve &&
|
|
19
|
-
!pModule.matchesDoNotFollow
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
module.exports = {
|
|
24
|
-
getAfferentCouplings,
|
|
25
|
-
getEfferentCouplings,
|
|
26
|
-
metricsAreCalculable,
|
|
27
|
-
};
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
// const { findModuleByName, clearCache } = require("../utl");
|
|
2
|
-
const { metricsAreCalculable } = require("./module-utl");
|
|
3
|
-
const { shouldDeriveMetrics } = require("./utl");
|
|
4
|
-
|
|
5
|
-
module.exports = function deriveModuleMetrics(pModules, pOptions) {
|
|
6
|
-
if (shouldDeriveMetrics(pOptions)) {
|
|
7
|
-
return pModules.map((pModule) => ({
|
|
8
|
-
...pModule,
|
|
9
|
-
...(metricsAreCalculable(pModule)
|
|
10
|
-
? {
|
|
11
|
-
instability:
|
|
12
|
-
pModule.dependencies.length /
|
|
13
|
-
(pModule.dependents.length + pModule.dependencies.length) || 0,
|
|
14
|
-
}
|
|
15
|
-
: {}),
|
|
16
|
-
}));
|
|
17
|
-
// clearCache();
|
|
18
|
-
// return lModules.map((pModule) => ({
|
|
19
|
-
// ...pModule,
|
|
20
|
-
// dependencies: pModule.dependencies.map((pDependency) => ({
|
|
21
|
-
// ...pDependency,
|
|
22
|
-
// instability:
|
|
23
|
-
// (findModuleByName(lModules, pDependency.resolved) || {})
|
|
24
|
-
// .instability || 0,
|
|
25
|
-
// })),
|
|
26
|
-
// }));
|
|
27
|
-
}
|
|
28
|
-
return pModules;
|
|
29
|
-
};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/* eslint-disable security/detect-object-injection */
|
|
2
|
-
function getParentFolders(pPath) {
|
|
3
|
-
let lFragments = pPath.split("/");
|
|
4
|
-
let lReturnValue = [];
|
|
5
|
-
|
|
6
|
-
while (lFragments.length > 0) {
|
|
7
|
-
lReturnValue.push(lFragments.join("/"));
|
|
8
|
-
lFragments.pop();
|
|
9
|
-
}
|
|
10
|
-
return lReturnValue.reverse();
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function foldersObject2folderArray(pObject) {
|
|
14
|
-
return Object.keys(pObject).map((pKey) => ({
|
|
15
|
-
name: pKey,
|
|
16
|
-
...pObject[pKey],
|
|
17
|
-
}));
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function shouldDeriveMetrics(pOptions) {
|
|
21
|
-
return pOptions.metrics || pOptions.outputType === "metrics";
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
module.exports = {
|
|
25
|
-
getParentFolders,
|
|
26
|
-
foldersObject2folderArray,
|
|
27
|
-
shouldDeriveMetrics,
|
|
28
|
-
};
|
package/src/validate/utl.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* If there is at least one group expression in the given pRulePath
|
|
3
|
-
* return the first matched one.
|
|
4
|
-
*
|
|
5
|
-
* Return null in all other cases.
|
|
6
|
-
*
|
|
7
|
-
* This fills our current need. Later we can expand it to return all group
|
|
8
|
-
* matches.
|
|
9
|
-
*
|
|
10
|
-
* @param {import("../../types/rule-set").IFromRestriction} pRestriction
|
|
11
|
-
* @param {string} pActualPath
|
|
12
|
-
* @returns {string[]|null}
|
|
13
|
-
*/
|
|
14
|
-
function extractGroups(pRestriction, pActualPath) {
|
|
15
|
-
let lReturnValue = [];
|
|
16
|
-
|
|
17
|
-
if (Boolean(pRestriction.path)) {
|
|
18
|
-
let lMatchResult = pActualPath.match(pRestriction.path);
|
|
19
|
-
|
|
20
|
-
if (Boolean(lMatchResult) && lMatchResult.length > 1) {
|
|
21
|
-
lReturnValue = lMatchResult.filter(
|
|
22
|
-
(pResult) => typeof pResult === "string"
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return lReturnValue;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
module.exports = {
|
|
30
|
-
extractGroups,
|
|
31
|
-
};
|