eslint-plugin-boundaries 3.2.0 → 3.3.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 +5 -1
- package/package.json +13 -10
- package/src/core/dependencyInfo.js +4 -4
- package/src/core/elementsInfo.js +3 -3
- package/src/helpers/messages.js +35 -10
- package/src/helpers/rules.js +42 -14
- package/src/helpers/utils.js +2 -1
- package/src/helpers/validations.js +17 -4
- package/src/index.js +5 -0
- package/src/rules/element-types.js +14 -6
- package/src/rules/entry-point.js +23 -5
- package/src/rules/external.js +33 -14
- package/src/rules/no-ignored.js +1 -1
- package/src/rules/no-private.js +2 -2
- package/src/rules/no-unknown.js +1 -1
- package/src/rules-factories/dependency-rule.js +1 -1
package/README.md
CHANGED
|
@@ -261,7 +261,7 @@ Some rules require extra configuration, and it has to be defined in each specifi
|
|
|
261
261
|
|
|
262
262
|
The docs of each rule contains an specification of their own options, but __the main rules share the format in which the options have to be defined__. The format described here is valid for options of [`element-types`](docs/rules/element-types.md), [`external`](docs/rules/external.md) and [`entry-point`](docs/rules/entry-point.md) rules.
|
|
263
263
|
|
|
264
|
-
Options set an `allow` or `disallow` value by default, and provide an array of rules. Each matching rule will override the default value and the value returned by previous matching rules. So, the final result of the options, once processed for each case, will be `allow` or `disallow`, and this value will be applied by the plugin rule in the
|
|
264
|
+
Options set an `allow` or `disallow` value by default, and provide an array of rules. Each matching rule will override the default value and the value returned by previous matching rules. So, the final result of the options, once processed for each case, will be `allow` or `disallow`, and this value will be applied by the plugin rule in the correspondent way, making it to produce an eslint error or not.
|
|
265
265
|
|
|
266
266
|
```jsonc
|
|
267
267
|
{
|
|
@@ -277,6 +277,8 @@ Options set an `allow` or `disallow` value by default, and provide an array of r
|
|
|
277
277
|
"from": ["helpers"],
|
|
278
278
|
// ...disallow importing this type of elements
|
|
279
279
|
"disallow": ["modules", "components"],
|
|
280
|
+
// ..for this kind of imports (applies only when using TypeScript)
|
|
281
|
+
"importKind": "value",
|
|
280
282
|
// ...and return this custom error message
|
|
281
283
|
"message": "Helpers must not import other thing than helpers"
|
|
282
284
|
},
|
|
@@ -300,6 +302,7 @@ Remember that:
|
|
|
300
302
|
|
|
301
303
|
* __`from/target`__: `<element matchers>` Depending of the rule to which the options are for, the rule will be applied only if the file being analyzed matches with this element matcher (`from`), or the dependency being imported matches with this element matcher (`target`).
|
|
302
304
|
* __`disallow/allow`__: `<value matchers>` If the plugin rule target matches with this, then the result of the rule will be "disallow/allow". Each rule will require a type of value here depending of what it is checking. In the case of the `element-types` rule, for example, another `<element matcher>` has to be provided in order to check the type of the local dependency.
|
|
305
|
+
* __`importKind`__: `<string>` _Optional_. It is useful only when using TypeScript, as it allows to define if the rule applies when the dependency is being imported as a value or as a type. It can be also defined as an array of strings, or a micromatch pattern. Note that possible values to match with are `"value"`, `"type"` or `"typeof"`. For example, you could define that "components" can import "helpers" as a value, but not as a type. So, `import { helper } from "helpers/helper-a"` would be allowed, but `import type { Helper } from "helpers/helper-a"` would be disallowed.
|
|
303
306
|
* __`message`__: `<string>` Optional. If the rule results in an error, the plugin will return this message instead of the default one. Read [error messages](#error-messages) for further info.
|
|
304
307
|
|
|
305
308
|
> Tip: Properties `from/target` and `disallow/allow` can receive a single matcher, or an array of matchers.
|
|
@@ -340,6 +343,7 @@ Available properties in error templates both from `file` or `dependency` are:
|
|
|
340
343
|
* `internalPath`: File path being analyzed or imported. Relative to the element's root path.
|
|
341
344
|
* `source`: Available only for `dependency`. The source of the `import` statement as it is in the code.
|
|
342
345
|
* `parent`: If the element is child of another element, it is also available in this property, which contains correspondent `type`, `internalPath` and captured properties as well.
|
|
346
|
+
* `importKind`: Available only for `dependency` when using TypeScript. It contains the kind of import being analyzed. Possible values are `"value"`, `"type"` or `"typeof"`.
|
|
343
347
|
* ...All captured properties are also available
|
|
344
348
|
|
|
345
349
|
> Tip: Read ["Global settings"](#global-settings) for further info about how to capture values from elements.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-boundaries",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Eslint plugin checking architecture boundaries between elements",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"eslint",
|
|
@@ -32,21 +32,24 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"chalk": "4.1.2",
|
|
35
|
-
"eslint-import-resolver-node": "0.3.
|
|
36
|
-
"eslint-module-utils": "2.
|
|
37
|
-
"is-core-module": "2.
|
|
35
|
+
"eslint-import-resolver-node": "0.3.9",
|
|
36
|
+
"eslint-module-utils": "2.8.0",
|
|
37
|
+
"is-core-module": "2.13.0",
|
|
38
38
|
"micromatch": "4.0.5"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
+
"@typescript-eslint/eslint-plugin": "6.4.0",
|
|
42
|
+
"@typescript-eslint/parser": "6.4.0",
|
|
41
43
|
"cross-env": "7.0.3",
|
|
42
|
-
"eslint": "8.
|
|
43
|
-
"eslint-config-prettier": "
|
|
44
|
-
"eslint-
|
|
44
|
+
"eslint": "8.47.0",
|
|
45
|
+
"eslint-config-prettier": "9.0.0",
|
|
46
|
+
"eslint-import-resolver-typescript": "3.6.0",
|
|
47
|
+
"eslint-plugin-prettier": "5.0.0",
|
|
45
48
|
"husky": "8.0.3",
|
|
46
49
|
"is-ci": "3.0.1",
|
|
47
|
-
"jest": "29.
|
|
48
|
-
"lint-staged": "
|
|
49
|
-
"prettier": "
|
|
50
|
+
"jest": "29.6.2",
|
|
51
|
+
"lint-staged": "14.0.0",
|
|
52
|
+
"prettier": "3.0.1"
|
|
50
53
|
},
|
|
51
54
|
"lint-staged": {
|
|
52
55
|
"test/**/*.js": "eslint",
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
const { fileInfo, importInfo } = require("./elementsInfo");
|
|
2
2
|
|
|
3
3
|
function getParent(elementInfo) {
|
|
4
|
-
|
|
5
|
-
return parent && parent.elementPath;
|
|
4
|
+
return elementInfo.parents?.[0]?.elementPath;
|
|
6
5
|
}
|
|
7
6
|
|
|
8
7
|
function getCommonAncestor(elementInfoA, elementInfoB) {
|
|
@@ -11,7 +10,7 @@ function getCommonAncestor(elementInfoA, elementInfoB) {
|
|
|
11
10
|
return elementParentA.elementPath === elementParentB.elementPath;
|
|
12
11
|
});
|
|
13
12
|
});
|
|
14
|
-
return commonAncestor
|
|
13
|
+
return commonAncestor?.elementPath;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
function isUncle(elementA, elementB) {
|
|
@@ -65,12 +64,13 @@ function dependencyRelationship(dependency, element) {
|
|
|
65
64
|
return null;
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
function dependencyInfo(source, context) {
|
|
67
|
+
function dependencyInfo(source, importKind, context) {
|
|
69
68
|
const elementInfo = fileInfo(context);
|
|
70
69
|
const dependency = importInfo(source, context);
|
|
71
70
|
|
|
72
71
|
return {
|
|
73
72
|
...dependency,
|
|
73
|
+
importKind: importKind || "value",
|
|
74
74
|
relationship: dependencyRelationship(dependency, elementInfo),
|
|
75
75
|
isInternal: isInternal(dependency, elementInfo),
|
|
76
76
|
};
|
package/src/core/elementsInfo.js
CHANGED
|
@@ -126,12 +126,12 @@ function elementTypeAndParents(path, settings) {
|
|
|
126
126
|
path
|
|
127
127
|
.split("/")
|
|
128
128
|
.slice(0, path.split("/").length - lastSegmentMatching)
|
|
129
|
-
.join("/")
|
|
129
|
+
.join("/"),
|
|
130
130
|
);
|
|
131
131
|
}
|
|
132
132
|
const capture = micromatch.capture(
|
|
133
133
|
pattern,
|
|
134
|
-
useFullPathMatch ? path : accumulator.join("/")
|
|
134
|
+
useFullPathMatch ? path : accumulator.join("/"),
|
|
135
135
|
);
|
|
136
136
|
|
|
137
137
|
if (capture && basePatternCapture) {
|
|
@@ -171,7 +171,7 @@ function elementTypeAndParents(path, settings) {
|
|
|
171
171
|
});
|
|
172
172
|
return { accumulator, lastSegmentMatching };
|
|
173
173
|
},
|
|
174
|
-
{ accumulator: [], lastSegmentMatching: 0 }
|
|
174
|
+
{ accumulator: [], lastSegmentMatching: 0 },
|
|
175
175
|
);
|
|
176
176
|
|
|
177
177
|
return {
|
package/src/helpers/messages.js
CHANGED
|
@@ -22,7 +22,7 @@ function propertiesConcater(properties, index) {
|
|
|
22
22
|
function micromatchPatternMessage(micromatchPatterns, elementCapturedValues) {
|
|
23
23
|
const micromatchPatternsWithValues = micromatchPatternReplacingObjectsValues(
|
|
24
24
|
micromatchPatterns,
|
|
25
|
-
{ from: elementCapturedValues }
|
|
25
|
+
{ from: elementCapturedValues },
|
|
26
26
|
);
|
|
27
27
|
if (isArray(micromatchPatternsWithValues)) {
|
|
28
28
|
if (micromatchPatternsWithValues.length === 1) {
|
|
@@ -60,7 +60,7 @@ function elementMatcherMessage(elementMatcher, elementCapturedValues) {
|
|
|
60
60
|
}
|
|
61
61
|
return `${typeMessage(elementMatcher[0])}${capturedValuesMatcherMessage(
|
|
62
62
|
elementMatcher[1],
|
|
63
|
-
elementCapturedValues
|
|
63
|
+
elementCapturedValues,
|
|
64
64
|
)}`;
|
|
65
65
|
}
|
|
66
66
|
|
|
@@ -85,6 +85,7 @@ function elementPropertiesToReplaceInTemplate(element) {
|
|
|
85
85
|
type: element.type,
|
|
86
86
|
internalPath: element.internalPath,
|
|
87
87
|
source: element.source,
|
|
88
|
+
importKind: element.importKind,
|
|
88
89
|
};
|
|
89
90
|
}
|
|
90
91
|
|
|
@@ -92,39 +93,39 @@ function customErrorMessage(message, file, dependency, report = {}) {
|
|
|
92
93
|
let replacedMessage = replaceObjectValuesInTemplates(
|
|
93
94
|
replaceObjectValuesInTemplates(message, elementPropertiesToReplaceInTemplate(file), "file"),
|
|
94
95
|
elementPropertiesToReplaceInTemplate(dependency),
|
|
95
|
-
"dependency"
|
|
96
|
+
"dependency",
|
|
96
97
|
);
|
|
97
98
|
replacedMessage = replaceObjectValuesInTemplates(
|
|
98
99
|
replaceObjectValuesInTemplates(
|
|
99
100
|
replacedMessage,
|
|
100
101
|
elementPropertiesToReplaceInTemplate(file),
|
|
101
|
-
"from"
|
|
102
|
+
"from",
|
|
102
103
|
),
|
|
103
104
|
elementPropertiesToReplaceInTemplate(dependency),
|
|
104
|
-
"target"
|
|
105
|
+
"target",
|
|
105
106
|
);
|
|
106
107
|
if (file.parents[0]) {
|
|
107
108
|
replacedMessage = replaceObjectValuesInTemplates(
|
|
108
109
|
replacedMessage,
|
|
109
110
|
elementPropertiesToReplaceInTemplate(file.parents[0]),
|
|
110
|
-
"file.parent"
|
|
111
|
+
"file.parent",
|
|
111
112
|
);
|
|
112
113
|
replacedMessage = replaceObjectValuesInTemplates(
|
|
113
114
|
replacedMessage,
|
|
114
115
|
elementPropertiesToReplaceInTemplate(file.parents[0]),
|
|
115
|
-
"from.parent"
|
|
116
|
+
"from.parent",
|
|
116
117
|
);
|
|
117
118
|
}
|
|
118
119
|
if (dependency.parents[0]) {
|
|
119
120
|
replacedMessage = replaceObjectValuesInTemplates(
|
|
120
121
|
replacedMessage,
|
|
121
122
|
elementPropertiesToReplaceInTemplate(dependency.parents[0]),
|
|
122
|
-
"dependency.parent"
|
|
123
|
+
"dependency.parent",
|
|
123
124
|
);
|
|
124
125
|
replacedMessage = replaceObjectValuesInTemplates(
|
|
125
126
|
replacedMessage,
|
|
126
127
|
elementPropertiesToReplaceInTemplate(dependency.parents[0]),
|
|
127
|
-
"target.parent"
|
|
128
|
+
"target.parent",
|
|
128
129
|
);
|
|
129
130
|
}
|
|
130
131
|
return replaceObjectValuesInTemplates(replacedMessage, report, "report");
|
|
@@ -148,13 +149,37 @@ function elementCapturedValuesMessage(capturedValues) {
|
|
|
148
149
|
|
|
149
150
|
function elementMessage(elementInfo) {
|
|
150
151
|
return `of type ${quote(elementInfo.type)}${elementCapturedValuesMessage(
|
|
151
|
-
elementInfo.capturedValues
|
|
152
|
+
elementInfo.capturedValues,
|
|
152
153
|
)}`;
|
|
153
154
|
}
|
|
154
155
|
|
|
156
|
+
function hasToPrintKindMessage(ruleImportKind, dependencyInfo) {
|
|
157
|
+
return ruleImportKind && dependencyInfo.importKind;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function dependencyImportKindMessage(ruleImportKind, dependencyInfo) {
|
|
161
|
+
if (hasToPrintKindMessage(ruleImportKind, dependencyInfo)) {
|
|
162
|
+
return `kind ${quote(dependencyInfo.importKind)} from `;
|
|
163
|
+
}
|
|
164
|
+
return "";
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function dependencyUsageKindMessage(
|
|
168
|
+
ruleImportKind,
|
|
169
|
+
dependencyInfo,
|
|
170
|
+
{ suffix = " ", prefix = "" } = {},
|
|
171
|
+
) {
|
|
172
|
+
if (hasToPrintKindMessage(ruleImportKind, dependencyInfo)) {
|
|
173
|
+
return `${prefix}${dependencyInfo.importKind}${suffix}`;
|
|
174
|
+
}
|
|
175
|
+
return "";
|
|
176
|
+
}
|
|
177
|
+
|
|
155
178
|
module.exports = {
|
|
156
179
|
quote,
|
|
157
180
|
ruleElementMessage,
|
|
158
181
|
customErrorMessage,
|
|
159
182
|
elementMessage,
|
|
183
|
+
dependencyImportKindMessage,
|
|
184
|
+
dependencyUsageKindMessage,
|
|
160
185
|
};
|
package/src/helpers/rules.js
CHANGED
|
@@ -62,9 +62,12 @@ function micromatchPatternReplacingObjectsValues(pattern, object) {
|
|
|
62
62
|
function isObjectMatch(objectWithMatchers, object, objectsWithValuesToReplace) {
|
|
63
63
|
return Object.keys(objectWithMatchers).reduce((isMatch, key) => {
|
|
64
64
|
if (isMatch) {
|
|
65
|
+
if (!object) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
65
68
|
const micromatchPattern = micromatchPatternReplacingObjectsValues(
|
|
66
69
|
objectWithMatchers[key],
|
|
67
|
-
objectsWithValuesToReplace
|
|
70
|
+
objectsWithValuesToReplace,
|
|
68
71
|
);
|
|
69
72
|
return micromatch.isMatch(object[key], micromatchPattern);
|
|
70
73
|
}
|
|
@@ -76,17 +79,23 @@ function rulesMainKey(key) {
|
|
|
76
79
|
return key || FROM;
|
|
77
80
|
}
|
|
78
81
|
|
|
79
|
-
function ruleMatch(ruleMatchers, targetElement, isMatch, fromElement) {
|
|
82
|
+
function ruleMatch(ruleMatchers, targetElement, isMatch, fromElement, importKind) {
|
|
80
83
|
let match = { result: false, report: null };
|
|
81
84
|
const matchers = !isArray(ruleMatchers) ? [ruleMatchers] : ruleMatchers;
|
|
82
85
|
matchers.forEach((matcher) => {
|
|
83
86
|
if (!match.result) {
|
|
84
87
|
if (isArray(matcher)) {
|
|
85
88
|
const [value, captures] = matcher;
|
|
86
|
-
match = isMatch(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
89
|
+
match = isMatch(
|
|
90
|
+
targetElement,
|
|
91
|
+
value,
|
|
92
|
+
captures,
|
|
93
|
+
{
|
|
94
|
+
from: fromElement.capturedValues,
|
|
95
|
+
target: targetElement.capturedValues,
|
|
96
|
+
},
|
|
97
|
+
importKind,
|
|
98
|
+
);
|
|
90
99
|
} else {
|
|
91
100
|
match = isMatch(
|
|
92
101
|
targetElement,
|
|
@@ -95,7 +104,8 @@ function ruleMatch(ruleMatchers, targetElement, isMatch, fromElement) {
|
|
|
95
104
|
{
|
|
96
105
|
from: fromElement.capturedValues,
|
|
97
106
|
target: targetElement.capturedValues,
|
|
98
|
-
}
|
|
107
|
+
},
|
|
108
|
+
importKind,
|
|
99
109
|
);
|
|
100
110
|
}
|
|
101
111
|
}
|
|
@@ -108,11 +118,11 @@ function isMatchElementKey(
|
|
|
108
118
|
matcher,
|
|
109
119
|
options,
|
|
110
120
|
elementKey,
|
|
111
|
-
elementsToCompareCapturedValues
|
|
121
|
+
elementsToCompareCapturedValues,
|
|
112
122
|
) {
|
|
113
123
|
const isMatch = micromatch.isMatch(
|
|
114
124
|
elementInfo[elementKey],
|
|
115
|
-
micromatchPatternReplacingObjectsValues(matcher, elementsToCompareCapturedValues)
|
|
125
|
+
micromatchPatternReplacingObjectsValues(matcher, elementsToCompareCapturedValues),
|
|
116
126
|
);
|
|
117
127
|
if (isMatch && options) {
|
|
118
128
|
return {
|
|
@@ -124,7 +134,23 @@ function isMatchElementKey(
|
|
|
124
134
|
};
|
|
125
135
|
}
|
|
126
136
|
|
|
127
|
-
function
|
|
137
|
+
function isMatchImportKind(elementInfo, importKind) {
|
|
138
|
+
if (!elementInfo.importKind || !importKind) {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
return micromatch.isMatch(elementInfo.importKind, importKind);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function isMatchElementType(
|
|
145
|
+
elementInfo,
|
|
146
|
+
matcher,
|
|
147
|
+
options,
|
|
148
|
+
elementsToCompareCapturedValues,
|
|
149
|
+
importKind,
|
|
150
|
+
) {
|
|
151
|
+
if (!isMatchImportKind(elementInfo, importKind)) {
|
|
152
|
+
return { result: false };
|
|
153
|
+
}
|
|
128
154
|
return isMatchElementKey(elementInfo, matcher, options, "type", elementsToCompareCapturedValues);
|
|
129
155
|
}
|
|
130
156
|
|
|
@@ -166,11 +192,11 @@ function elementRulesAllowDependency({
|
|
|
166
192
|
const [result, report, ruleReport] = getElementRules(
|
|
167
193
|
elementToGetRulesFrom(element, dependency, mainKey),
|
|
168
194
|
options,
|
|
169
|
-
mainKey
|
|
195
|
+
mainKey,
|
|
170
196
|
).reduce(
|
|
171
197
|
(allowed, rule) => {
|
|
172
198
|
if (rule.disallow) {
|
|
173
|
-
const match = ruleMatch(rule.disallow, dependency, isMatch, element);
|
|
199
|
+
const match = ruleMatch(rule.disallow, dependency, isMatch, element, rule.importKind);
|
|
174
200
|
if (match.result) {
|
|
175
201
|
return [
|
|
176
202
|
false,
|
|
@@ -180,12 +206,13 @@ function elementRulesAllowDependency({
|
|
|
180
206
|
disallow: rule.disallow,
|
|
181
207
|
index: rule.index,
|
|
182
208
|
message: rule.message || options.message,
|
|
209
|
+
importKind: rule.importKind,
|
|
183
210
|
},
|
|
184
211
|
];
|
|
185
212
|
}
|
|
186
213
|
}
|
|
187
214
|
if (rule.allow) {
|
|
188
|
-
const match = ruleMatch(rule.allow, dependency, isMatch, element);
|
|
215
|
+
const match = ruleMatch(rule.allow, dependency, isMatch, element, rule.importKind);
|
|
189
216
|
if (match.result) {
|
|
190
217
|
return [true, match.report];
|
|
191
218
|
}
|
|
@@ -199,7 +226,7 @@ function elementRulesAllowDependency({
|
|
|
199
226
|
isDefault: true,
|
|
200
227
|
message: options.message,
|
|
201
228
|
},
|
|
202
|
-
]
|
|
229
|
+
],
|
|
203
230
|
);
|
|
204
231
|
return {
|
|
205
232
|
result,
|
|
@@ -218,4 +245,5 @@ module.exports = {
|
|
|
218
245
|
getElementRules,
|
|
219
246
|
rulesMainKey,
|
|
220
247
|
micromatchPatternReplacingObjectsValues,
|
|
248
|
+
isMatchImportKind,
|
|
221
249
|
};
|
package/src/helpers/utils.js
CHANGED
|
@@ -8,7 +8,8 @@ function isArray(object) {
|
|
|
8
8
|
|
|
9
9
|
function replaceObjectValueInTemplate(template, key, value, namespace) {
|
|
10
10
|
const keyToReplace = namespace ? `${namespace}.${key}` : key;
|
|
11
|
-
|
|
11
|
+
const regexp = new RegExp(`\\$\\{${keyToReplace}\\}`, "g");
|
|
12
|
+
return template.replace(regexp, value);
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
function replaceObjectValuesInTemplates(strings, object, namespace) {
|
|
@@ -63,6 +63,19 @@ function rulesOptionsSchema(options = {}) {
|
|
|
63
63
|
[mainKey]: elementsMatcherSchema(),
|
|
64
64
|
allow: elementsMatcherSchema(options.targetMatcherOptions),
|
|
65
65
|
disallow: elementsMatcherSchema(options.targetMatcherOptions),
|
|
66
|
+
importKind: {
|
|
67
|
+
oneOf: [
|
|
68
|
+
{
|
|
69
|
+
type: "string",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
type: "array",
|
|
73
|
+
items: {
|
|
74
|
+
type: "string",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
},
|
|
66
79
|
message: {
|
|
67
80
|
type: "string",
|
|
68
81
|
},
|
|
@@ -109,7 +122,7 @@ function validateElements(elements) {
|
|
|
109
122
|
// TODO, remove in next major version
|
|
110
123
|
if (isLegacyType(element)) {
|
|
111
124
|
warnOnce(
|
|
112
|
-
`Defining elements as strings in settings is deprecated. Will be automatically converted, but this feature will be removed in next major versions
|
|
125
|
+
`Defining elements as strings in settings is deprecated. Will be automatically converted, but this feature will be removed in next major versions`,
|
|
113
126
|
);
|
|
114
127
|
} else {
|
|
115
128
|
Object.keys(element).forEach(() => {
|
|
@@ -119,8 +132,8 @@ function validateElements(elements) {
|
|
|
119
132
|
if (element.mode && !VALID_MODES.includes(element.mode)) {
|
|
120
133
|
warnOnce(
|
|
121
134
|
`Invalid mode property in '${ELEMENTS}' setting. Should be one of ${VALID_MODES.join(
|
|
122
|
-
","
|
|
123
|
-
)}. Default value "${VALID_MODES[0]}" will be used instead
|
|
135
|
+
",",
|
|
136
|
+
)}. Default value "${VALID_MODES[0]}" will be used instead`,
|
|
124
137
|
);
|
|
125
138
|
}
|
|
126
139
|
if (!element.pattern || !(isString(element.pattern) || isArray(element.pattern))) {
|
|
@@ -137,7 +150,7 @@ function validateElements(elements) {
|
|
|
137
150
|
function deprecateAlias(aliases) {
|
|
138
151
|
if (aliases) {
|
|
139
152
|
warnOnce(
|
|
140
|
-
`Defining aliases in '${ALIAS}' setting is deprecated. Please use 'import/resolver' setting
|
|
153
|
+
`Defining aliases in '${ALIAS}' setting is deprecated. Please use 'import/resolver' setting`,
|
|
141
154
|
);
|
|
142
155
|
}
|
|
143
156
|
}
|
package/src/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const packageJson = require("../package.json");
|
|
1
2
|
const rules = require("./constants/rules");
|
|
2
3
|
const recommendedConfig = require("./configs/recommended");
|
|
3
4
|
const strictConfig = require("./configs/strict");
|
|
@@ -17,6 +18,10 @@ const importRules = (ruleNames) => {
|
|
|
17
18
|
// export all configs
|
|
18
19
|
|
|
19
20
|
module.exports = {
|
|
21
|
+
meta: {
|
|
22
|
+
name: packageJson.name,
|
|
23
|
+
version: packageJson.version,
|
|
24
|
+
},
|
|
20
25
|
rules: importRules(rules),
|
|
21
26
|
configs: {
|
|
22
27
|
recommended: recommendedConfig,
|
|
@@ -8,7 +8,12 @@ const {
|
|
|
8
8
|
isMatchElementType,
|
|
9
9
|
elementRulesAllowDependency,
|
|
10
10
|
} = require("../helpers/rules");
|
|
11
|
-
const {
|
|
11
|
+
const {
|
|
12
|
+
customErrorMessage,
|
|
13
|
+
ruleElementMessage,
|
|
14
|
+
elementMessage,
|
|
15
|
+
dependencyImportKindMessage,
|
|
16
|
+
} = require("../helpers/messages");
|
|
12
17
|
|
|
13
18
|
function elementRulesAllowDependencyType(element, dependency, options) {
|
|
14
19
|
return elementRulesAllowDependency({
|
|
@@ -26,15 +31,18 @@ function errorMessage(ruleData, file, dependency) {
|
|
|
26
31
|
}
|
|
27
32
|
if (ruleReport.isDefault) {
|
|
28
33
|
return `No rule allowing this dependency was found. File is ${elementMessage(
|
|
29
|
-
file
|
|
34
|
+
file,
|
|
30
35
|
)}. Dependency is ${elementMessage(dependency)}`;
|
|
31
36
|
}
|
|
32
|
-
return `Importing ${
|
|
37
|
+
return `Importing ${dependencyImportKindMessage(
|
|
38
|
+
ruleReport.importKind,
|
|
39
|
+
dependency,
|
|
40
|
+
)}${ruleElementMessage(
|
|
33
41
|
ruleReport.disallow,
|
|
34
|
-
file.capturedValues
|
|
42
|
+
file.capturedValues,
|
|
35
43
|
)} is not allowed in ${ruleElementMessage(
|
|
36
44
|
ruleReport.element,
|
|
37
|
-
file.capturedValues
|
|
45
|
+
file.capturedValues,
|
|
38
46
|
)}. Disallowed in rule ${ruleReport.index + 1}`;
|
|
39
47
|
}
|
|
40
48
|
|
|
@@ -55,5 +63,5 @@ module.exports = dependencyRule(
|
|
|
55
63
|
});
|
|
56
64
|
}
|
|
57
65
|
}
|
|
58
|
-
}
|
|
66
|
+
},
|
|
59
67
|
);
|
package/src/rules/entry-point.js
CHANGED
|
@@ -7,10 +7,25 @@ const {
|
|
|
7
7
|
dependencyLocation,
|
|
8
8
|
isMatchElementKey,
|
|
9
9
|
elementRulesAllowDependency,
|
|
10
|
+
isMatchImportKind,
|
|
10
11
|
} = require("../helpers/rules");
|
|
11
|
-
const {
|
|
12
|
+
const {
|
|
13
|
+
customErrorMessage,
|
|
14
|
+
ruleElementMessage,
|
|
15
|
+
elementMessage,
|
|
16
|
+
dependencyUsageKindMessage,
|
|
17
|
+
} = require("../helpers/messages");
|
|
12
18
|
|
|
13
|
-
function isMatchElementInternalPath(
|
|
19
|
+
function isMatchElementInternalPath(
|
|
20
|
+
elementInfo,
|
|
21
|
+
matcher,
|
|
22
|
+
options,
|
|
23
|
+
elementsCapturedValues,
|
|
24
|
+
importKind,
|
|
25
|
+
) {
|
|
26
|
+
if (!isMatchImportKind(elementInfo, importKind)) {
|
|
27
|
+
return { result: false };
|
|
28
|
+
}
|
|
14
29
|
return isMatchElementKey(elementInfo, matcher, options, "internalPath", elementsCapturedValues);
|
|
15
30
|
}
|
|
16
31
|
|
|
@@ -36,8 +51,11 @@ function errorMessage(ruleData, file, dependency) {
|
|
|
36
51
|
}
|
|
37
52
|
return `The entry point '${dependency.internalPath}' is not allowed in ${ruleElementMessage(
|
|
38
53
|
ruleReport.element,
|
|
39
|
-
dependency.capturedValues
|
|
40
|
-
)}
|
|
54
|
+
dependency.capturedValues,
|
|
55
|
+
)}${dependencyUsageKindMessage(ruleReport.importKind, dependency, {
|
|
56
|
+
prefix: " when importing ",
|
|
57
|
+
suffix: "",
|
|
58
|
+
})}. Disallowed in rule ${ruleReport.index + 1}`;
|
|
41
59
|
}
|
|
42
60
|
|
|
43
61
|
module.exports = dependencyRule(
|
|
@@ -62,5 +80,5 @@ module.exports = dependencyRule(
|
|
|
62
80
|
},
|
|
63
81
|
{
|
|
64
82
|
validateRules: { onlyMainKey: true, mainKey: "target" },
|
|
65
|
-
}
|
|
83
|
+
},
|
|
66
84
|
);
|
package/src/rules/external.js
CHANGED
|
@@ -9,8 +9,14 @@ const {
|
|
|
9
9
|
dependencyLocation,
|
|
10
10
|
elementRulesAllowDependency,
|
|
11
11
|
micromatchPatternReplacingObjectsValues,
|
|
12
|
+
isMatchImportKind,
|
|
12
13
|
} = require("../helpers/rules");
|
|
13
|
-
const {
|
|
14
|
+
const {
|
|
15
|
+
customErrorMessage,
|
|
16
|
+
ruleElementMessage,
|
|
17
|
+
elementMessage,
|
|
18
|
+
dependencyUsageKindMessage,
|
|
19
|
+
} = require("../helpers/messages");
|
|
14
20
|
const { isArray } = require("../helpers/utils");
|
|
15
21
|
|
|
16
22
|
function specifiersMatch(specifiers, specifierOptions, elementsCapturedValues) {
|
|
@@ -22,7 +28,7 @@ function specifiersMatch(specifiers, specifierOptions, elementsCapturedValues) {
|
|
|
22
28
|
return specifierOptions.reduce((found, option) => {
|
|
23
29
|
const matcherWithTemplateReplaced = micromatchPatternReplacingObjectsValues(
|
|
24
30
|
option,
|
|
25
|
-
elementsCapturedValues
|
|
31
|
+
elementsCapturedValues,
|
|
26
32
|
);
|
|
27
33
|
if (micromatch.some(importedSpecifiersNames, matcherWithTemplateReplaced)) {
|
|
28
34
|
found.push(option);
|
|
@@ -39,7 +45,7 @@ function pathMatch(path, pathOptions, elementsCapturedValues) {
|
|
|
39
45
|
}
|
|
40
46
|
const matcherWithTemplateReplaced = micromatchPatternReplacingObjectsValues(
|
|
41
47
|
option,
|
|
42
|
-
elementsCapturedValues
|
|
48
|
+
elementsCapturedValues,
|
|
43
49
|
);
|
|
44
50
|
if (micromatch.some(path, matcherWithTemplateReplaced)) {
|
|
45
51
|
isMatch = true;
|
|
@@ -48,11 +54,20 @@ function pathMatch(path, pathOptions, elementsCapturedValues) {
|
|
|
48
54
|
}, false);
|
|
49
55
|
}
|
|
50
56
|
|
|
51
|
-
function isMatchExternalDependency(
|
|
57
|
+
function isMatchExternalDependency(
|
|
58
|
+
dependency,
|
|
59
|
+
matcher,
|
|
60
|
+
options,
|
|
61
|
+
elementsCapturedValues,
|
|
62
|
+
importKind,
|
|
63
|
+
) {
|
|
52
64
|
const matcherWithTemplatesReplaced = micromatchPatternReplacingObjectsValues(
|
|
53
65
|
matcher,
|
|
54
|
-
elementsCapturedValues
|
|
66
|
+
elementsCapturedValues,
|
|
55
67
|
);
|
|
68
|
+
if (!isMatchImportKind(dependency, importKind)) {
|
|
69
|
+
return { result: false };
|
|
70
|
+
}
|
|
56
71
|
const isMatch = micromatch.isMatch(dependency.baseModule, matcherWithTemplatesReplaced);
|
|
57
72
|
if (isMatch && options && Object.keys(options).length) {
|
|
58
73
|
const isPathMatch = options.path
|
|
@@ -62,7 +77,7 @@ function isMatchExternalDependency(dependency, matcher, options, elementsCapture
|
|
|
62
77
|
const specifiersResult = specifiersMatch(
|
|
63
78
|
dependency.specifiers,
|
|
64
79
|
options.specifiers,
|
|
65
|
-
elementsCapturedValues
|
|
80
|
+
elementsCapturedValues,
|
|
66
81
|
);
|
|
67
82
|
return {
|
|
68
83
|
result: specifiersResult.length > 0,
|
|
@@ -103,9 +118,8 @@ function errorMessage(ruleData, file, dependency) {
|
|
|
103
118
|
const ruleReport = ruleData.ruleReport;
|
|
104
119
|
if (ruleReport.message) {
|
|
105
120
|
return customErrorMessage(ruleReport.message, file, dependency, {
|
|
106
|
-
specifiers:
|
|
107
|
-
|
|
108
|
-
path: ruleData.report && ruleData.report.path,
|
|
121
|
+
specifiers: ruleData.report?.specifiers?.join(", "),
|
|
122
|
+
path: ruleData.report?.path,
|
|
109
123
|
});
|
|
110
124
|
}
|
|
111
125
|
if (ruleReport.isDefault) {
|
|
@@ -116,15 +130,20 @@ function errorMessage(ruleData, file, dependency) {
|
|
|
116
130
|
|
|
117
131
|
const fileReport = `is not allowed in ${ruleElementMessage(
|
|
118
132
|
ruleReport.element,
|
|
119
|
-
file.capturedValues
|
|
133
|
+
file.capturedValues,
|
|
120
134
|
)}. Disallowed in rule ${ruleReport.index + 1}`;
|
|
121
135
|
|
|
122
136
|
if (ruleData.report) {
|
|
123
|
-
return `Usage of
|
|
137
|
+
return `Usage of ${dependencyUsageKindMessage(
|
|
138
|
+
ruleReport.importKind,
|
|
139
|
+
dependency,
|
|
140
|
+
)}'${getErrorReportMessage(ruleData.report)}' from external module '${
|
|
124
141
|
dependency.baseModule
|
|
125
142
|
}' ${fileReport}`;
|
|
126
143
|
}
|
|
127
|
-
return `Usage of
|
|
144
|
+
return `Usage of ${dependencyUsageKindMessage(ruleReport.importKind, dependency, {
|
|
145
|
+
suffix: " from ",
|
|
146
|
+
})}external module '${dependency.baseModule}' ${fileReport}`;
|
|
128
147
|
}
|
|
129
148
|
|
|
130
149
|
module.exports = dependencyRule(
|
|
@@ -164,7 +183,7 @@ module.exports = dependencyRule(
|
|
|
164
183
|
const ruleData = elementRulesAllowExternalDependency(
|
|
165
184
|
file,
|
|
166
185
|
{ ...dependency, specifiers: node.source.parent.specifiers },
|
|
167
|
-
options
|
|
186
|
+
options,
|
|
168
187
|
);
|
|
169
188
|
if (!ruleData.result) {
|
|
170
189
|
context.report({
|
|
@@ -177,5 +196,5 @@ module.exports = dependencyRule(
|
|
|
177
196
|
},
|
|
178
197
|
{
|
|
179
198
|
validateRules: { onlyMainKey: true },
|
|
180
|
-
}
|
|
199
|
+
},
|
|
181
200
|
);
|
package/src/rules/no-ignored.js
CHANGED
package/src/rules/no-private.js
CHANGED
|
@@ -40,7 +40,7 @@ module.exports = dependencyRule(
|
|
|
40
40
|
dependency.relationship !== "internal" &&
|
|
41
41
|
dependency.relationship !== "child" &&
|
|
42
42
|
dependency.relationship !== "brother" &&
|
|
43
|
-
(!options
|
|
43
|
+
(!options?.allowUncles || dependency.relationship !== "uncle")
|
|
44
44
|
) {
|
|
45
45
|
context.report({
|
|
46
46
|
message: errorMessage(file, dependency, options),
|
|
@@ -51,5 +51,5 @@ module.exports = dependencyRule(
|
|
|
51
51
|
},
|
|
52
52
|
{
|
|
53
53
|
validate: false,
|
|
54
|
-
}
|
|
54
|
+
},
|
|
55
55
|
);
|
package/src/rules/no-unknown.js
CHANGED
|
@@ -21,7 +21,7 @@ module.exports = function (ruleMeta, rule, ruleOptions = {}) {
|
|
|
21
21
|
|
|
22
22
|
return {
|
|
23
23
|
ImportDeclaration: (node) => {
|
|
24
|
-
const dependency = dependencyInfo(node.source.value, context);
|
|
24
|
+
const dependency = dependencyInfo(node.source.value, node.importKind, context);
|
|
25
25
|
|
|
26
26
|
rule({ file, dependency, options, node, context });
|
|
27
27
|
},
|