eslint-plugin-boundaries 3.2.0 → 3.4.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 +36 -1
- package/package.json +13 -10
- package/src/constants/plugin.js +2 -0
- package/src/constants/settings.js +6 -1
- package/src/core/dependencyInfo.js +4 -4
- package/src/core/elementsInfo.js +8 -8
- package/src/helpers/debug.js +2 -8
- package/src/helpers/messages.js +35 -10
- package/src/helpers/rules.js +42 -14
- package/src/helpers/settings.js +25 -1
- 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
|
@@ -228,6 +228,37 @@ Files or dependencies matching these [`micromatch` patterns](https://github.com/
|
|
|
228
228
|
|
|
229
229
|
> Note: The `boundaries/include` option has preference over `boundaries/ignore`. If you define `boundaries/include`, use `boundaries/ignore` to ignore subsets of included files.
|
|
230
230
|
|
|
231
|
+
#### __`boundaries/root-path`__
|
|
232
|
+
|
|
233
|
+
Use this setting only if you are facing issues with the plugin when executing the lint command from a different path than the project root.
|
|
234
|
+
|
|
235
|
+
<details>
|
|
236
|
+
<summary>How to define the root path of the project</summary>
|
|
237
|
+
|
|
238
|
+
By default, the plugin uses the current working directory (`process.cwd()`) as root path of the project. This path is used as the base path when resolving file matchers from rules and `boundaries/elements` settings. This is specially important when using the `basePattern` option or the `full` mode in the `boundaries/elements` setting. This may produce unexpected results [when the lint command is executed from a different path than the project root](https://github.com/javierbrea/eslint-plugin-boundaries/issues/296). To fix this, you can define a different root path using this option.
|
|
239
|
+
|
|
240
|
+
For example, supposing that the `.eslintrc.js` file is located in the project root, you could define the root path as in:
|
|
241
|
+
|
|
242
|
+
```js
|
|
243
|
+
{
|
|
244
|
+
settings: {
|
|
245
|
+
"boundaries/root-path": path.resolve(__dirname)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Note that the path should be absolute and resolved before passing it to the plugin. Otherwise, it will be resolved using the current working directory, and the problem will persist. In case you are defining the configuration in a `.eslintrc.(yml|json)` file, and you don't want to hardcode an absolute path, you can use the next environment variable to define the root path when executing the lint command:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
ESLINT_PLUGIN_BOUNDARIES_ROOT_PATH=../../project-root npm run lint
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
You can also provide an absolute path in the environment variable, but it may be more useful to use a relative path to the project root. Remember that it will be resolved from the path where the lint command is executed.
|
|
257
|
+
|
|
258
|
+
</details>
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
|
|
231
262
|
### Predefined configurations
|
|
232
263
|
|
|
233
264
|
This plugin is distributed with two different predefined configurations: "recommended" and "strict".
|
|
@@ -261,7 +292,7 @@ Some rules require extra configuration, and it has to be defined in each specifi
|
|
|
261
292
|
|
|
262
293
|
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
294
|
|
|
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
|
|
295
|
+
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
296
|
|
|
266
297
|
```jsonc
|
|
267
298
|
{
|
|
@@ -277,6 +308,8 @@ Options set an `allow` or `disallow` value by default, and provide an array of r
|
|
|
277
308
|
"from": ["helpers"],
|
|
278
309
|
// ...disallow importing this type of elements
|
|
279
310
|
"disallow": ["modules", "components"],
|
|
311
|
+
// ..for this kind of imports (applies only when using TypeScript)
|
|
312
|
+
"importKind": "value",
|
|
280
313
|
// ...and return this custom error message
|
|
281
314
|
"message": "Helpers must not import other thing than helpers"
|
|
282
315
|
},
|
|
@@ -300,6 +333,7 @@ Remember that:
|
|
|
300
333
|
|
|
301
334
|
* __`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
335
|
* __`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.
|
|
336
|
+
* __`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
337
|
* __`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
338
|
|
|
305
339
|
> Tip: Properties `from/target` and `disallow/allow` can receive a single matcher, or an array of matchers.
|
|
@@ -340,6 +374,7 @@ Available properties in error templates both from `file` or `dependency` are:
|
|
|
340
374
|
* `internalPath`: File path being analyzed or imported. Relative to the element's root path.
|
|
341
375
|
* `source`: Available only for `dependency`. The source of the `import` statement as it is in the code.
|
|
342
376
|
* `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.
|
|
377
|
+
* `importKind`: Available only for `dependency` when using TypeScript. It contains the kind of import being analyzed. Possible values are `"value"`, `"type"` or `"typeof"`.
|
|
343
378
|
* ...All captured properties are also available
|
|
344
379
|
|
|
345
380
|
> 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.4.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",
|
package/src/constants/plugin.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { PLUGIN_NAME } = require("./plugin");
|
|
1
|
+
const { PLUGIN_NAME, PLUGIN_ENV_VARS_PREFIX } = require("./plugin");
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
4
|
ELEMENT_TYPES,
|
|
@@ -15,6 +15,11 @@ module.exports = {
|
|
|
15
15
|
ELEMENTS: `${PLUGIN_NAME}/elements`,
|
|
16
16
|
IGNORE: `${PLUGIN_NAME}/ignore`,
|
|
17
17
|
INCLUDE: `${PLUGIN_NAME}/include`,
|
|
18
|
+
ROOT_PATH: `${PLUGIN_NAME}/root-path`,
|
|
19
|
+
|
|
20
|
+
// env vars
|
|
21
|
+
DEBUG: `${PLUGIN_ENV_VARS_PREFIX}_DEBUG`,
|
|
22
|
+
ENV_ROOT_PATH: `${PLUGIN_ENV_VARS_PREFIX}_ROOT_PATH`,
|
|
18
23
|
|
|
19
24
|
// rules
|
|
20
25
|
RULE_ELEMENT_TYPES: `${PLUGIN_NAME}/${ELEMENT_TYPES}`,
|
|
@@ -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
|
@@ -3,7 +3,7 @@ const micromatch = require("micromatch");
|
|
|
3
3
|
const resolve = require("eslint-module-utils/resolve").default;
|
|
4
4
|
|
|
5
5
|
const { IGNORE, INCLUDE, VALID_MODES } = require("../constants/settings");
|
|
6
|
-
const { getElements } = require("../helpers/settings");
|
|
6
|
+
const { getElements, getRootPath } = require("../helpers/settings");
|
|
7
7
|
const { debugFileInfo } = require("../helpers/debug");
|
|
8
8
|
const { isArray } = require("../helpers/utils");
|
|
9
9
|
|
|
@@ -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 {
|
|
@@ -184,9 +184,9 @@ function replacePathSlashes(absolutePath) {
|
|
|
184
184
|
return absolutePath.replace(/\\/g, "/");
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
function projectPath(absolutePath) {
|
|
187
|
+
function projectPath(absolutePath, rootPath) {
|
|
188
188
|
if (absolutePath) {
|
|
189
|
-
return replacePathSlashes(absolutePath).replace(`${replacePathSlashes(
|
|
189
|
+
return replacePathSlashes(absolutePath).replace(`${replacePathSlashes(rootPath)}/`, "");
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
|
|
@@ -195,7 +195,7 @@ function externalModulePath(source, baseModuleValue) {
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
function importInfo(source, context) {
|
|
198
|
-
const path = projectPath(resolve(source, context));
|
|
198
|
+
const path = projectPath(resolve(source, context), getRootPath(context.settings));
|
|
199
199
|
const isExternalModule = isExternal(source, path);
|
|
200
200
|
const resultCache = importsCache.load(isExternalModule ? source : path, context.settings);
|
|
201
201
|
let elementCache;
|
|
@@ -237,7 +237,7 @@ function importInfo(source, context) {
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
function fileInfo(context) {
|
|
240
|
-
const path = projectPath(context.getFilename());
|
|
240
|
+
const path = projectPath(context.getFilename(), getRootPath(context.settings));
|
|
241
241
|
const resultCache = filesCache.load(path, context.settings);
|
|
242
242
|
let elementCache;
|
|
243
243
|
let result;
|
package/src/helpers/debug.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const chalk = require("chalk");
|
|
2
2
|
|
|
3
3
|
const { PLUGIN_NAME } = require("../constants/plugin");
|
|
4
|
+
const { isDebugModeEnabled } = require("./settings");
|
|
4
5
|
|
|
5
6
|
const warns = [];
|
|
6
7
|
const debuggedFiles = [];
|
|
@@ -13,12 +14,6 @@ function warn(message) {
|
|
|
13
14
|
trace(message, "yellow");
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
function debug(message) {
|
|
17
|
-
if (process.env.ESLINT_PLUGIN_BOUNDARIES_DEBUG) {
|
|
18
|
-
trace(message, "grey");
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
17
|
function success(message) {
|
|
23
18
|
trace(message, "green");
|
|
24
19
|
}
|
|
@@ -32,7 +27,7 @@ function warnOnce(message) {
|
|
|
32
27
|
|
|
33
28
|
function debugFileInfo(fileInfo) {
|
|
34
29
|
const fileInfoKey = fileInfo.path || fileInfo.source;
|
|
35
|
-
if (
|
|
30
|
+
if (isDebugModeEnabled() && !debuggedFiles.includes(fileInfoKey)) {
|
|
36
31
|
debuggedFiles.push(fileInfoKey);
|
|
37
32
|
if (fileInfo.type) {
|
|
38
33
|
success(`'${fileInfoKey}' is of type '${fileInfo.type}'`);
|
|
@@ -44,7 +39,6 @@ function debugFileInfo(fileInfo) {
|
|
|
44
39
|
}
|
|
45
40
|
|
|
46
41
|
module.exports = {
|
|
47
|
-
debug,
|
|
48
42
|
success,
|
|
49
43
|
debugFileInfo,
|
|
50
44
|
warnOnce,
|
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/settings.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
TYPES,
|
|
3
|
+
ELEMENTS,
|
|
4
|
+
VALID_MODES,
|
|
5
|
+
ROOT_PATH,
|
|
6
|
+
ENV_ROOT_PATH,
|
|
7
|
+
DEBUG,
|
|
8
|
+
} = require("../constants/settings");
|
|
2
9
|
const { isString } = require("./utils");
|
|
10
|
+
const { isAbsolute, resolve } = require("path");
|
|
3
11
|
|
|
4
12
|
function isLegacyType(type) {
|
|
5
13
|
return isString(type);
|
|
@@ -34,8 +42,24 @@ function getElementsTypeNames(settings) {
|
|
|
34
42
|
return getElements(settings).map((element) => element.type);
|
|
35
43
|
}
|
|
36
44
|
|
|
45
|
+
function getRootPath(settings) {
|
|
46
|
+
const rootPathUserSetting = process.env[ENV_ROOT_PATH] || settings[ROOT_PATH];
|
|
47
|
+
if (rootPathUserSetting) {
|
|
48
|
+
return isAbsolute(rootPathUserSetting)
|
|
49
|
+
? rootPathUserSetting
|
|
50
|
+
: resolve(process.cwd(), rootPathUserSetting);
|
|
51
|
+
}
|
|
52
|
+
return process.cwd();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isDebugModeEnabled() {
|
|
56
|
+
return process.env[DEBUG];
|
|
57
|
+
}
|
|
58
|
+
|
|
37
59
|
module.exports = {
|
|
38
60
|
isLegacyType,
|
|
39
61
|
getElements,
|
|
40
62
|
getElementsTypeNames,
|
|
63
|
+
isDebugModeEnabled,
|
|
64
|
+
getRootPath,
|
|
41
65
|
};
|
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
|
},
|