eslint-plugin-boundaries 5.0.1 → 5.0.2
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 +2 -2
- package/package.json +13 -30
- package/resolver-legacy-alias/index.js +3 -1
- package/src/constants/settings.js +24 -6
- package/src/core/dependencyInfo.js +9 -2
- package/src/core/elementsInfo.js +41 -11
- package/src/helpers/debug.js +2 -1
- package/src/helpers/messages.js +29 -15
- package/src/helpers/rules.js +52 -9
- package/src/helpers/utils.js +12 -2
- package/src/helpers/validations.js +30 -10
- package/src/rules/element-types.js +15 -3
- package/src/rules/entry-point.js +7 -1
- package/src/rules/external.js +19 -6
- package/src/rules-factories/dependency-rule.js +13 -3
package/README.md
CHANGED
|
@@ -171,7 +171,7 @@ Read the [docs of the `boundaries/entry-point` rule](docs/rules/entry-point.md)
|
|
|
171
171
|
|
|
172
172
|
### Global settings
|
|
173
173
|
|
|
174
|
-
#### __`boundaries/
|
|
174
|
+
#### __`boundaries/elements`__
|
|
175
175
|
|
|
176
176
|
Define patterns to recognize each file in the project as one of this element types. All rules need this setting to be configured properly to work. The plugin tries to identify each file being analyzed or `import` statement in rules as one of the defined element types. The assigned element type will be that with the first matching pattern, in the same order that elements are defined in the array, so you __should sort them from the most accurate patterns to the less ones__. Properties of each `element`:
|
|
177
177
|
|
|
@@ -619,7 +619,7 @@ MIT, see [LICENSE](./LICENSE) for details.
|
|
|
619
619
|
|
|
620
620
|
[coveralls-image]: https://coveralls.io/repos/github/javierbrea/eslint-plugin-boundaries/badge.svg
|
|
621
621
|
[coveralls-url]: https://coveralls.io/github/javierbrea/eslint-plugin-boundaries
|
|
622
|
-
[build-image]: https://github.com/javierbrea/eslint-plugin-boundaries/workflows/build/badge.svg
|
|
622
|
+
[build-image]: https://github.com/javierbrea/eslint-plugin-boundaries/workflows/build/badge.svg
|
|
623
623
|
[build-url]: https://github.com/javierbrea/eslint-plugin-boundaries/actions?query=workflow%3Abuild+branch%3Amaster
|
|
624
624
|
[last-commit-image]: https://img.shields.io/github/last-commit/javierbrea/eslint-plugin-boundaries.svg
|
|
625
625
|
[last-commit-url]: https://github.com/javierbrea/eslint-plugin-boundaries/commits
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-boundaries",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.2",
|
|
4
4
|
"description": "Eslint plugin checking architecture boundaries between elements",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
@@ -20,43 +20,26 @@
|
|
|
20
20
|
"resolver-legacy-alias"
|
|
21
21
|
],
|
|
22
22
|
"main": "index.js",
|
|
23
|
-
"scripts": {
|
|
24
|
-
"eslint": "eslint",
|
|
25
|
-
"lint": "eslint .",
|
|
26
|
-
"lint-staged": "lint-staged",
|
|
27
|
-
"test": "jest",
|
|
28
|
-
"test:unit": "cross-env ESLINT_PLUGIN_BOUNDARIES_DEBUG=1 npm run test",
|
|
29
|
-
"prepare": "is-ci || husky install"
|
|
30
|
-
},
|
|
31
23
|
"peerDependencies": {
|
|
32
24
|
"eslint": ">=6.0.0"
|
|
33
25
|
},
|
|
34
26
|
"dependencies": {
|
|
35
27
|
"chalk": "4.1.2",
|
|
36
28
|
"eslint-import-resolver-node": "0.3.9",
|
|
37
|
-
"eslint-module-utils": "2.12.
|
|
29
|
+
"eslint-module-utils": "2.12.1",
|
|
38
30
|
"micromatch": "4.0.8"
|
|
39
31
|
},
|
|
40
|
-
"devDependencies": {
|
|
41
|
-
"@typescript-eslint/eslint-plugin": "8.13.0",
|
|
42
|
-
"@typescript-eslint/parser": "8.13.0",
|
|
43
|
-
"cross-env": "7.0.3",
|
|
44
|
-
"eslint": "9.14.0",
|
|
45
|
-
"eslint-config-prettier": "9.1.0",
|
|
46
|
-
"eslint-plugin-local-rules": "3.0.2",
|
|
47
|
-
"eslint-plugin-prettier": "5.2.1",
|
|
48
|
-
"husky": "9.1.6",
|
|
49
|
-
"is-ci": "3.0.1",
|
|
50
|
-
"jest": "29.7.0",
|
|
51
|
-
"lint-staged": "15.2.10",
|
|
52
|
-
"prettier": "3.3.3"
|
|
53
|
-
},
|
|
54
|
-
"lint-staged": {
|
|
55
|
-
"**/*.js": "eslint",
|
|
56
|
-
"**/*.ts": "eslint",
|
|
57
|
-
"**/*.mjs": "eslint"
|
|
58
|
-
},
|
|
59
32
|
"engines": {
|
|
60
33
|
"node": ">=18.18"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"eslint": "eslint",
|
|
37
|
+
"build": "cross-env node ./scripts/copy-readme.js",
|
|
38
|
+
"check:all": "echo 'All checks passed'",
|
|
39
|
+
"check:spell": "cspell --quiet .",
|
|
40
|
+
"lint": "eslint .",
|
|
41
|
+
"lint:fix": "eslint . --fix",
|
|
42
|
+
"test": "jest",
|
|
43
|
+
"test:unit": "cross-env ESLINT_PLUGIN_BOUNDARIES_DEBUG=1 pnpm run test"
|
|
61
44
|
}
|
|
62
|
-
}
|
|
45
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const resolve = require("resolve");
|
|
2
2
|
|
|
3
3
|
const getUsedAlias = (relativeFilePath, config) => {
|
|
4
|
-
return Object.keys(config).find(
|
|
4
|
+
return Object.keys(config).find(
|
|
5
|
+
(alias) => relativeFilePath.indexOf(alias) === 0,
|
|
6
|
+
);
|
|
5
7
|
};
|
|
6
8
|
|
|
7
9
|
const replaceAliases = (filePath, config) => {
|
|
@@ -50,9 +50,15 @@ module.exports = {
|
|
|
50
50
|
],
|
|
51
51
|
import: [
|
|
52
52
|
// Note: detects "import x from 'source'"
|
|
53
|
-
{
|
|
53
|
+
{
|
|
54
|
+
selector: "ImportDeclaration:not([importKind=type]) > Literal",
|
|
55
|
+
kind: "value",
|
|
56
|
+
},
|
|
54
57
|
// Note: detects "import type x from 'source'"
|
|
55
|
-
{
|
|
58
|
+
{
|
|
59
|
+
selector: "ImportDeclaration[importKind=type] > Literal",
|
|
60
|
+
kind: "type",
|
|
61
|
+
},
|
|
56
62
|
],
|
|
57
63
|
"dynamic-import": [
|
|
58
64
|
// Note: detects "import('source')"
|
|
@@ -60,13 +66,25 @@ module.exports = {
|
|
|
60
66
|
],
|
|
61
67
|
export: [
|
|
62
68
|
// Note: detects "export * from 'source'";
|
|
63
|
-
{
|
|
69
|
+
{
|
|
70
|
+
selector: "ExportAllDeclaration:not([exportKind=type]) > Literal",
|
|
71
|
+
kind: "value",
|
|
72
|
+
},
|
|
64
73
|
// Note: detects "export type * from 'source'";
|
|
65
|
-
{
|
|
74
|
+
{
|
|
75
|
+
selector: "ExportAllDeclaration[exportKind=type] > Literal",
|
|
76
|
+
kind: "type",
|
|
77
|
+
},
|
|
66
78
|
// Note: detects "export { x } from 'source'";
|
|
67
|
-
{
|
|
79
|
+
{
|
|
80
|
+
selector: "ExportNamedDeclaration:not([exportKind=type]) > Literal",
|
|
81
|
+
kind: "value",
|
|
82
|
+
},
|
|
68
83
|
// Note: detects "export type { x } from 'source'";
|
|
69
|
-
{
|
|
84
|
+
{
|
|
85
|
+
selector: "ExportNamedDeclaration[exportKind=type] > Literal",
|
|
86
|
+
kind: "type",
|
|
87
|
+
},
|
|
70
88
|
],
|
|
71
89
|
},
|
|
72
90
|
};
|
|
@@ -25,7 +25,9 @@ function isBrother(elementA, elementB) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
function isDescendant(elementA, elementB) {
|
|
28
|
-
return
|
|
28
|
+
return elementA.parents.some(
|
|
29
|
+
(parent) => parent.elementPath === elementB.elementPath,
|
|
30
|
+
);
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
function isChild(elementA, elementB) {
|
|
@@ -37,7 +39,12 @@ function isInternal(elementA, elementB) {
|
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
function dependencyRelationship(dependency, element) {
|
|
40
|
-
if (
|
|
42
|
+
if (
|
|
43
|
+
!dependency.isLocal ||
|
|
44
|
+
dependency.isIgnored ||
|
|
45
|
+
!element.type ||
|
|
46
|
+
!dependency.type
|
|
47
|
+
) {
|
|
41
48
|
return null;
|
|
42
49
|
}
|
|
43
50
|
if (isInternal(dependency, element)) {
|
package/src/core/elementsInfo.js
CHANGED
|
@@ -113,15 +113,25 @@ function elementTypeAndParents(path, settings) {
|
|
|
113
113
|
.split("/")
|
|
114
114
|
.reverse()
|
|
115
115
|
.reduce(
|
|
116
|
-
(
|
|
116
|
+
(
|
|
117
|
+
{ accumulator, lastSegmentMatching },
|
|
118
|
+
elementPathSegment,
|
|
119
|
+
segmentIndex,
|
|
120
|
+
elementPaths,
|
|
121
|
+
) => {
|
|
117
122
|
accumulator.unshift(elementPathSegment);
|
|
118
123
|
let elementFound = false;
|
|
119
124
|
getElements(settings).forEach((element) => {
|
|
120
|
-
const typeOfMatch = VALID_MODES.includes(element.mode)
|
|
121
|
-
|
|
125
|
+
const typeOfMatch = VALID_MODES.includes(element.mode)
|
|
126
|
+
? element.mode
|
|
127
|
+
: VALID_MODES[0];
|
|
128
|
+
const elementPatterns = isArray(element.pattern)
|
|
129
|
+
? element.pattern
|
|
130
|
+
: [element.pattern];
|
|
122
131
|
elementPatterns.forEach((elementPattern) => {
|
|
123
132
|
if (!elementFound) {
|
|
124
|
-
const useFullPathMatch =
|
|
133
|
+
const useFullPathMatch =
|
|
134
|
+
typeOfMatch === VALID_MODES[2] && !elementResult.type;
|
|
125
135
|
const pattern =
|
|
126
136
|
typeOfMatch === VALID_MODES[0] && !elementResult.type
|
|
127
137
|
? `${elementPattern}/**/*`
|
|
@@ -145,10 +155,16 @@ function elementTypeAndParents(path, settings) {
|
|
|
145
155
|
if (capture && basePatternCapture) {
|
|
146
156
|
elementFound = true;
|
|
147
157
|
lastSegmentMatching = segmentIndex + 1;
|
|
148
|
-
let capturedValues = elementCaptureValues(
|
|
158
|
+
let capturedValues = elementCaptureValues(
|
|
159
|
+
capture,
|
|
160
|
+
element.capture,
|
|
161
|
+
);
|
|
149
162
|
if (element.basePattern) {
|
|
150
163
|
capturedValues = {
|
|
151
|
-
...elementCaptureValues(
|
|
164
|
+
...elementCaptureValues(
|
|
165
|
+
basePatternCapture,
|
|
166
|
+
element.baseCapture,
|
|
167
|
+
),
|
|
152
168
|
...capturedValues,
|
|
153
169
|
};
|
|
154
170
|
}
|
|
@@ -194,7 +210,10 @@ function replacePathSlashes(absolutePath) {
|
|
|
194
210
|
|
|
195
211
|
function projectPath(absolutePath, rootPath) {
|
|
196
212
|
if (absolutePath) {
|
|
197
|
-
return replacePathSlashes(absolutePath).replace(
|
|
213
|
+
return replacePathSlashes(absolutePath).replace(
|
|
214
|
+
`${replacePathSlashes(rootPath)}/`,
|
|
215
|
+
"",
|
|
216
|
+
);
|
|
198
217
|
}
|
|
199
218
|
}
|
|
200
219
|
|
|
@@ -203,9 +222,15 @@ function externalModulePath(source, baseModuleValue) {
|
|
|
203
222
|
}
|
|
204
223
|
|
|
205
224
|
function importInfo(source, context) {
|
|
206
|
-
const path = projectPath(
|
|
225
|
+
const path = projectPath(
|
|
226
|
+
resolve(source, context),
|
|
227
|
+
getRootPath(context.settings),
|
|
228
|
+
);
|
|
207
229
|
const isExternalModule = isExternal(source, path);
|
|
208
|
-
const resultCache = importsCache.load(
|
|
230
|
+
const resultCache = importsCache.load(
|
|
231
|
+
isExternalModule ? source : path,
|
|
232
|
+
context.settings,
|
|
233
|
+
);
|
|
209
234
|
let elementCache;
|
|
210
235
|
let result;
|
|
211
236
|
let elementResult;
|
|
@@ -216,7 +241,9 @@ function importInfo(source, context) {
|
|
|
216
241
|
elementCache = elementsCache.load(path, context.settings);
|
|
217
242
|
const baseModuleValue = isExternalModule ? baseModule(source) : null;
|
|
218
243
|
const isBuiltInModule = isBuiltIn(source, path);
|
|
219
|
-
const pathToUse = isExternalModule
|
|
244
|
+
const pathToUse = isExternalModule
|
|
245
|
+
? externalModulePath(source, baseModuleValue)
|
|
246
|
+
: path;
|
|
220
247
|
if (elementCache) {
|
|
221
248
|
elementResult = elementCache;
|
|
222
249
|
} else {
|
|
@@ -245,7 +272,10 @@ function importInfo(source, context) {
|
|
|
245
272
|
}
|
|
246
273
|
|
|
247
274
|
function fileInfo(context) {
|
|
248
|
-
const path = projectPath(
|
|
275
|
+
const path = projectPath(
|
|
276
|
+
context.getFilename(),
|
|
277
|
+
getRootPath(context.settings),
|
|
278
|
+
);
|
|
249
279
|
const resultCache = filesCache.load(path, context.settings);
|
|
250
280
|
let elementCache;
|
|
251
281
|
let result;
|
package/src/helpers/debug.js
CHANGED
|
@@ -7,6 +7,7 @@ const warns = [];
|
|
|
7
7
|
const debuggedFiles = [];
|
|
8
8
|
|
|
9
9
|
function trace(message, color) {
|
|
10
|
+
// eslint-disable-next-line no-console
|
|
10
11
|
console.log(chalk[color](`[${PLUGIN_NAME}]: ${message}`));
|
|
11
12
|
}
|
|
12
13
|
|
|
@@ -34,7 +35,7 @@ function debugFileInfo(fileInfo) {
|
|
|
34
35
|
} else {
|
|
35
36
|
warn(`'${fileInfoKey}' is of unknown type`);
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
+
trace(`\n${JSON.stringify(fileInfo, null, 2)}`, "gray");
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
package/src/helpers/messages.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
isString,
|
|
3
|
+
isArray,
|
|
4
|
+
replaceObjectValuesInTemplates,
|
|
5
|
+
} = require("./utils");
|
|
2
6
|
const { micromatchPatternReplacingObjectsValues } = require("./rules");
|
|
3
7
|
|
|
4
8
|
function quote(str) {
|
|
@@ -9,7 +13,7 @@ function typeMessage(elementMatcher) {
|
|
|
9
13
|
return `elements of type ${quote(elementMatcher)}`;
|
|
10
14
|
}
|
|
11
15
|
|
|
12
|
-
function
|
|
16
|
+
function propertiesConcatenator(properties, index) {
|
|
13
17
|
if (properties.length > 1 && index === properties.length - 1) {
|
|
14
18
|
return " and";
|
|
15
19
|
}
|
|
@@ -28,27 +32,33 @@ function micromatchPatternMessage(micromatchPatterns, elementCapturedValues) {
|
|
|
28
32
|
if (micromatchPatternsWithValues.length === 1) {
|
|
29
33
|
return quote(micromatchPatternsWithValues[0]);
|
|
30
34
|
}
|
|
31
|
-
return micromatchPatternsWithValues.reduce(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
return micromatchPatternsWithValues.reduce(
|
|
36
|
+
(message, micromatchPattern, index) => {
|
|
37
|
+
if (index === 0) {
|
|
38
|
+
return quote(micromatchPattern);
|
|
39
|
+
}
|
|
40
|
+
if (index === micromatchPatternsWithValues.length - 1) {
|
|
41
|
+
return `${message} or ${quote(micromatchPattern)}`;
|
|
42
|
+
}
|
|
43
|
+
return `${message}, ${quote(micromatchPattern)}`;
|
|
44
|
+
},
|
|
45
|
+
"",
|
|
46
|
+
);
|
|
40
47
|
}
|
|
41
48
|
return quote(micromatchPatternsWithValues);
|
|
42
49
|
}
|
|
43
50
|
|
|
44
|
-
function capturedValuesMatcherMessage(
|
|
51
|
+
function capturedValuesMatcherMessage(
|
|
52
|
+
capturedValuesPattern,
|
|
53
|
+
elementCapturedValues,
|
|
54
|
+
) {
|
|
45
55
|
const capturedValuesPatternKeys = Object.keys(capturedValuesPattern);
|
|
46
56
|
return capturedValuesPatternKeys
|
|
47
57
|
.map((key) => {
|
|
48
58
|
return [key, capturedValuesPattern[key]];
|
|
49
59
|
})
|
|
50
60
|
.reduce((message, propertyNameAndMatcher, index) => {
|
|
51
|
-
return `${message}${
|
|
61
|
+
return `${message}${propertiesConcatenator(capturedValuesPatternKeys, index)} ${
|
|
52
62
|
propertyNameAndMatcher[0]
|
|
53
63
|
} ${micromatchPatternMessage(propertyNameAndMatcher[1], elementCapturedValues)}`;
|
|
54
64
|
}, "");
|
|
@@ -91,7 +101,11 @@ function elementPropertiesToReplaceInTemplate(element) {
|
|
|
91
101
|
|
|
92
102
|
function customErrorMessage(message, file, dependency, report = {}) {
|
|
93
103
|
let replacedMessage = replaceObjectValuesInTemplates(
|
|
94
|
-
replaceObjectValuesInTemplates(
|
|
104
|
+
replaceObjectValuesInTemplates(
|
|
105
|
+
message,
|
|
106
|
+
elementPropertiesToReplaceInTemplate(file),
|
|
107
|
+
"file",
|
|
108
|
+
),
|
|
95
109
|
elementPropertiesToReplaceInTemplate(dependency),
|
|
96
110
|
"dependency",
|
|
97
111
|
);
|
|
@@ -141,7 +155,7 @@ function elementCapturedValuesMessage(capturedValues) {
|
|
|
141
155
|
return [key, capturedValues[key]];
|
|
142
156
|
})
|
|
143
157
|
.reduce((message, propertyNameAndValue, index) => {
|
|
144
|
-
return `${message}${
|
|
158
|
+
return `${message}${propertiesConcatenator(capturedValuesKeys, index)} ${
|
|
145
159
|
propertyNameAndValue[0]
|
|
146
160
|
} ${quote(propertyNameAndValue[1])}`;
|
|
147
161
|
}, "");
|
package/src/helpers/rules.js
CHANGED
|
@@ -32,13 +32,20 @@ function micromatchPatternReplacingObjectsValues(pattern, object) {
|
|
|
32
32
|
let patternToReplace = pattern;
|
|
33
33
|
// Backward compatibility
|
|
34
34
|
if (object.from) {
|
|
35
|
-
patternToReplace = replaceObjectValuesInTemplates(
|
|
35
|
+
patternToReplace = replaceObjectValuesInTemplates(
|
|
36
|
+
patternToReplace,
|
|
37
|
+
object.from,
|
|
38
|
+
);
|
|
36
39
|
}
|
|
37
40
|
return Object.keys(object).reduce((replacedPattern, namespace) => {
|
|
38
41
|
if (!object[namespace]) {
|
|
39
42
|
return replacedPattern;
|
|
40
43
|
}
|
|
41
|
-
return replaceObjectValuesInTemplates(
|
|
44
|
+
return replaceObjectValuesInTemplates(
|
|
45
|
+
replacedPattern,
|
|
46
|
+
object[namespace],
|
|
47
|
+
namespace,
|
|
48
|
+
);
|
|
42
49
|
}, patternToReplace);
|
|
43
50
|
}
|
|
44
51
|
|
|
@@ -62,7 +69,13 @@ function rulesMainKey(key) {
|
|
|
62
69
|
return key || FROM;
|
|
63
70
|
}
|
|
64
71
|
|
|
65
|
-
function ruleMatch(
|
|
72
|
+
function ruleMatch(
|
|
73
|
+
ruleMatchers,
|
|
74
|
+
targetElement,
|
|
75
|
+
isMatch,
|
|
76
|
+
fromElement,
|
|
77
|
+
importKind,
|
|
78
|
+
) {
|
|
66
79
|
let match = { result: false, report: null };
|
|
67
80
|
const matchers = !isArray(ruleMatchers) ? [ruleMatchers] : ruleMatchers;
|
|
68
81
|
matchers.forEach((matcher) => {
|
|
@@ -105,11 +118,18 @@ function isMatchElementKey(
|
|
|
105
118
|
) {
|
|
106
119
|
const isMatch = micromatch.isMatch(
|
|
107
120
|
elementInfo[elementKey],
|
|
108
|
-
micromatchPatternReplacingObjectsValues(
|
|
121
|
+
micromatchPatternReplacingObjectsValues(
|
|
122
|
+
matcher,
|
|
123
|
+
elementsToCompareCapturedValues,
|
|
124
|
+
),
|
|
109
125
|
);
|
|
110
126
|
if (isMatch && options) {
|
|
111
127
|
return {
|
|
112
|
-
result: isObjectMatch(
|
|
128
|
+
result: isObjectMatch(
|
|
129
|
+
options,
|
|
130
|
+
elementInfo.capturedValues,
|
|
131
|
+
elementsToCompareCapturedValues,
|
|
132
|
+
),
|
|
113
133
|
};
|
|
114
134
|
}
|
|
115
135
|
return {
|
|
@@ -134,7 +154,13 @@ function isMatchElementType(
|
|
|
134
154
|
if (!isMatchImportKind(elementInfo, importKind)) {
|
|
135
155
|
return { result: false };
|
|
136
156
|
}
|
|
137
|
-
return isMatchElementKey(
|
|
157
|
+
return isMatchElementKey(
|
|
158
|
+
elementInfo,
|
|
159
|
+
matcher,
|
|
160
|
+
options,
|
|
161
|
+
"type",
|
|
162
|
+
elementsToCompareCapturedValues,
|
|
163
|
+
);
|
|
138
164
|
}
|
|
139
165
|
|
|
140
166
|
function getElementRules(targetElement, options, mainKey, fromElement) {
|
|
@@ -150,7 +176,12 @@ function getElementRules(targetElement, options, mainKey, fromElement) {
|
|
|
150
176
|
};
|
|
151
177
|
})
|
|
152
178
|
.filter((rule) => {
|
|
153
|
-
return ruleMatch(
|
|
179
|
+
return ruleMatch(
|
|
180
|
+
rule[key],
|
|
181
|
+
targetElement,
|
|
182
|
+
isMatchElementType,
|
|
183
|
+
fromElement,
|
|
184
|
+
).result;
|
|
154
185
|
});
|
|
155
186
|
}
|
|
156
187
|
|
|
@@ -181,7 +212,13 @@ function elementRulesAllowDependency({
|
|
|
181
212
|
).reduce(
|
|
182
213
|
(allowed, rule) => {
|
|
183
214
|
if (rule.disallow) {
|
|
184
|
-
const match = ruleMatch(
|
|
215
|
+
const match = ruleMatch(
|
|
216
|
+
rule.disallow,
|
|
217
|
+
dependency,
|
|
218
|
+
isMatch,
|
|
219
|
+
element,
|
|
220
|
+
rule.importKind,
|
|
221
|
+
);
|
|
185
222
|
if (match.result) {
|
|
186
223
|
return [
|
|
187
224
|
false,
|
|
@@ -197,7 +234,13 @@ function elementRulesAllowDependency({
|
|
|
197
234
|
}
|
|
198
235
|
}
|
|
199
236
|
if (rule.allow) {
|
|
200
|
-
const match = ruleMatch(
|
|
237
|
+
const match = ruleMatch(
|
|
238
|
+
rule.allow,
|
|
239
|
+
dependency,
|
|
240
|
+
isMatch,
|
|
241
|
+
element,
|
|
242
|
+
rule.importKind,
|
|
243
|
+
);
|
|
201
244
|
if (match.result) {
|
|
202
245
|
return [true, match.report];
|
|
203
246
|
}
|
package/src/helpers/utils.js
CHANGED
|
@@ -25,10 +25,20 @@ function replaceObjectValuesInTemplates(strings, object, namespace) {
|
|
|
25
25
|
// If template is an array, replace key by value in all patterns
|
|
26
26
|
if (isArray(result)) {
|
|
27
27
|
return result.map((resultEntry) => {
|
|
28
|
-
return replaceObjectValueInTemplate(
|
|
28
|
+
return replaceObjectValueInTemplate(
|
|
29
|
+
resultEntry,
|
|
30
|
+
objectKey,
|
|
31
|
+
object[objectKey],
|
|
32
|
+
namespace,
|
|
33
|
+
);
|
|
29
34
|
});
|
|
30
35
|
}
|
|
31
|
-
return replaceObjectValueInTemplate(
|
|
36
|
+
return replaceObjectValueInTemplate(
|
|
37
|
+
result,
|
|
38
|
+
objectKey,
|
|
39
|
+
object[objectKey],
|
|
40
|
+
namespace,
|
|
41
|
+
);
|
|
32
42
|
}, strings);
|
|
33
43
|
}
|
|
34
44
|
|
|
@@ -110,15 +110,24 @@ function rulesOptionsSchema(options = {}) {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
function isValidElementTypesMatcher(matcher, settings) {
|
|
113
|
-
const
|
|
114
|
-
return
|
|
113
|
+
const matcherToCheck = isArray(matcher) ? matcher[0] : matcher;
|
|
114
|
+
return (
|
|
115
|
+
!matcher || micromatch.some(getElementsTypeNames(settings), matcherToCheck)
|
|
116
|
+
);
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
function validateElementTypesMatcher(elementsMatcher, settings) {
|
|
118
|
-
const [matcher] = isArray(elementsMatcher)
|
|
119
|
-
|
|
120
|
+
const [matcher] = isArray(elementsMatcher)
|
|
121
|
+
? elementsMatcher
|
|
122
|
+
: [elementsMatcher];
|
|
123
|
+
if (
|
|
124
|
+
!invalidMatchers.includes(matcher) &&
|
|
125
|
+
!isValidElementTypesMatcher(matcher, settings)
|
|
126
|
+
) {
|
|
120
127
|
invalidMatchers.push(matcher);
|
|
121
|
-
warnOnce(
|
|
128
|
+
warnOnce(
|
|
129
|
+
`Option '${matcher}' does not match any element type from '${ELEMENTS}' setting`,
|
|
130
|
+
);
|
|
122
131
|
}
|
|
123
132
|
}
|
|
124
133
|
|
|
@@ -147,13 +156,18 @@ function validateElements(elements) {
|
|
|
147
156
|
)}. Default value "${VALID_MODES[0]}" will be used instead`,
|
|
148
157
|
);
|
|
149
158
|
}
|
|
150
|
-
if (
|
|
159
|
+
if (
|
|
160
|
+
!element.pattern ||
|
|
161
|
+
!(isString(element.pattern) || isArray(element.pattern))
|
|
162
|
+
) {
|
|
151
163
|
warnOnce(
|
|
152
164
|
`Please provide a valid pattern to type ${element.type} in '${ELEMENTS}' setting`,
|
|
153
165
|
);
|
|
154
166
|
}
|
|
155
167
|
if (element.capture && !isArray(element.capture)) {
|
|
156
|
-
warnOnce(
|
|
168
|
+
warnOnce(
|
|
169
|
+
`Invalid capture property of type ${element.type} in '${ELEMENTS}' setting`,
|
|
170
|
+
);
|
|
157
171
|
}
|
|
158
172
|
});
|
|
159
173
|
}
|
|
@@ -178,7 +192,10 @@ function validateDependencyNodes(dependencyNodes) {
|
|
|
178
192
|
}
|
|
179
193
|
|
|
180
194
|
dependencyNodes.forEach((dependencyNode) => {
|
|
181
|
-
if (
|
|
195
|
+
if (
|
|
196
|
+
!isString(dependencyNode) ||
|
|
197
|
+
!defaultNodesNames.includes(dependencyNode)
|
|
198
|
+
) {
|
|
182
199
|
warnOnce(invalidFormatMessage);
|
|
183
200
|
}
|
|
184
201
|
});
|
|
@@ -204,7 +221,8 @@ function validateAdditionalDependencyNodes(additionalDependencyNodes) {
|
|
|
204
221
|
const isValidObject =
|
|
205
222
|
isObject(dependencyNode) &&
|
|
206
223
|
isString(dependencyNode.selector) &&
|
|
207
|
-
(!dependencyNode.kind ||
|
|
224
|
+
(!dependencyNode.kind ||
|
|
225
|
+
VALID_DEPENDENCY_NODE_KINDS.includes(dependencyNode.kind));
|
|
208
226
|
|
|
209
227
|
if (!isValidObject) {
|
|
210
228
|
warnOnce(invalidFormatMessage);
|
|
@@ -222,7 +240,9 @@ function deprecateAlias(aliases) {
|
|
|
222
240
|
|
|
223
241
|
function deprecateTypes(types) {
|
|
224
242
|
if (types) {
|
|
225
|
-
warnOnce(
|
|
243
|
+
warnOnce(
|
|
244
|
+
`'${TYPES}' setting is deprecated. Please use '${ELEMENTS}' instead`,
|
|
245
|
+
);
|
|
226
246
|
}
|
|
227
247
|
}
|
|
228
248
|
|
|
@@ -3,7 +3,10 @@ const { RULE_ELEMENT_TYPES } = require("../constants/settings");
|
|
|
3
3
|
const dependencyRule = require("../rules-factories/dependency-rule");
|
|
4
4
|
|
|
5
5
|
const { rulesOptionsSchema } = require("../helpers/validations");
|
|
6
|
-
const {
|
|
6
|
+
const {
|
|
7
|
+
isMatchElementType,
|
|
8
|
+
elementRulesAllowDependency,
|
|
9
|
+
} = require("../helpers/rules");
|
|
7
10
|
const {
|
|
8
11
|
customErrorMessage,
|
|
9
12
|
ruleElementMessage,
|
|
@@ -49,8 +52,17 @@ module.exports = dependencyRule(
|
|
|
49
52
|
schema: rulesOptionsSchema(),
|
|
50
53
|
},
|
|
51
54
|
function ({ dependency, file, node, context, options }) {
|
|
52
|
-
if (
|
|
53
|
-
|
|
55
|
+
if (
|
|
56
|
+
dependency.isLocal &&
|
|
57
|
+
!dependency.isIgnored &&
|
|
58
|
+
dependency.type &&
|
|
59
|
+
!dependency.isInternal
|
|
60
|
+
) {
|
|
61
|
+
const ruleData = elementRulesAllowDependencyType(
|
|
62
|
+
file,
|
|
63
|
+
dependency,
|
|
64
|
+
options,
|
|
65
|
+
);
|
|
54
66
|
if (!ruleData.result) {
|
|
55
67
|
context.report({
|
|
56
68
|
message: errorMessage(ruleData, file, dependency),
|
package/src/rules/entry-point.js
CHANGED
|
@@ -25,7 +25,13 @@ function isMatchElementInternalPath(
|
|
|
25
25
|
if (!isMatchImportKind(elementInfo, importKind)) {
|
|
26
26
|
return { result: false };
|
|
27
27
|
}
|
|
28
|
-
return isMatchElementKey(
|
|
28
|
+
return isMatchElementKey(
|
|
29
|
+
elementInfo,
|
|
30
|
+
matcher,
|
|
31
|
+
options,
|
|
32
|
+
"internalPath",
|
|
33
|
+
elementsCapturedValues,
|
|
34
|
+
);
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
function elementRulesAllowEntryPoint(element, dependency, options) {
|
package/src/rules/external.js
CHANGED
|
@@ -21,13 +21,19 @@ const { isArray } = require("../helpers/utils");
|
|
|
21
21
|
function getSpecifiers(node) {
|
|
22
22
|
if (node.parent.type === "ImportDeclaration") {
|
|
23
23
|
return node.parent.specifiers
|
|
24
|
-
.filter(
|
|
24
|
+
.filter(
|
|
25
|
+
(specifier) =>
|
|
26
|
+
specifier.type === "ImportSpecifier" && specifier.imported.name,
|
|
27
|
+
)
|
|
25
28
|
.map((specifier) => specifier.imported.name);
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
if (node.parent.type === "ExportNamedDeclaration") {
|
|
29
32
|
return node.parent.specifiers
|
|
30
|
-
.filter(
|
|
33
|
+
.filter(
|
|
34
|
+
(specifier) =>
|
|
35
|
+
specifier.type === "ExportSpecifier" && specifier.exported.name,
|
|
36
|
+
)
|
|
31
37
|
.map((specifier) => specifier.exported.name);
|
|
32
38
|
}
|
|
33
39
|
|
|
@@ -78,7 +84,10 @@ function isMatchExternalDependency(
|
|
|
78
84
|
if (!isMatchImportKind(dependency, importKind)) {
|
|
79
85
|
return { result: false };
|
|
80
86
|
}
|
|
81
|
-
const isMatch = micromatch.isMatch(
|
|
87
|
+
const isMatch = micromatch.isMatch(
|
|
88
|
+
dependency.baseModule,
|
|
89
|
+
matcherWithTemplatesReplaced,
|
|
90
|
+
);
|
|
82
91
|
if (isMatch && options && Object.keys(options).length) {
|
|
83
92
|
const isPathMatch = options.path
|
|
84
93
|
? pathMatch(dependency.path, options.path, elementsCapturedValues)
|
|
@@ -151,9 +160,13 @@ function errorMessage(ruleData, file, dependency) {
|
|
|
151
160
|
dependency.baseModule
|
|
152
161
|
}' ${fileReport}`;
|
|
153
162
|
}
|
|
154
|
-
return `Usage of ${dependencyUsageKindMessage(
|
|
155
|
-
|
|
156
|
-
|
|
163
|
+
return `Usage of ${dependencyUsageKindMessage(
|
|
164
|
+
ruleReport.importKind,
|
|
165
|
+
dependency,
|
|
166
|
+
{
|
|
167
|
+
suffix: " from ",
|
|
168
|
+
},
|
|
169
|
+
)}external module '${dependency.baseModule}' ${fileReport}`;
|
|
157
170
|
}
|
|
158
171
|
|
|
159
172
|
module.exports = dependencyRule(
|
|
@@ -18,14 +18,24 @@ module.exports = function (ruleMeta, rule, ruleOptions = {}) {
|
|
|
18
18
|
const options = context.options[0];
|
|
19
19
|
validateSettings(context.settings);
|
|
20
20
|
const file = fileInfo(context);
|
|
21
|
-
if (
|
|
21
|
+
if (
|
|
22
|
+
(ruleOptions.validate !== false && !options) ||
|
|
23
|
+
file.isIgnored ||
|
|
24
|
+
!file.type
|
|
25
|
+
) {
|
|
22
26
|
return {};
|
|
23
27
|
}
|
|
24
28
|
if (ruleOptions.validate !== false) {
|
|
25
|
-
validateRules(
|
|
29
|
+
validateRules(
|
|
30
|
+
context.settings,
|
|
31
|
+
options.rules,
|
|
32
|
+
ruleOptions.validateRules,
|
|
33
|
+
);
|
|
26
34
|
}
|
|
27
35
|
|
|
28
|
-
const dependencyNodesSetting = getArrayOrNull(
|
|
36
|
+
const dependencyNodesSetting = getArrayOrNull(
|
|
37
|
+
context.settings[DEPENDENCY_NODES],
|
|
38
|
+
);
|
|
29
39
|
const additionalDependencyNodesSetting = getArrayOrNull(
|
|
30
40
|
context.settings[ADDITIONAL_DEPENDENCY_NODES],
|
|
31
41
|
);
|