eslint-plugin-boundaries 3.4.1 → 4.0.1

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 CHANGED
@@ -8,7 +8,9 @@
8
8
 
9
9
  In words of Robert C. Martin, _"Software architecture is the art of drawing lines that I call boundaries. Those boundaries separate software elements from one another, and restrict those on one side from knowing about those on the other."_ _([\*acknowledgements](#acknowledgements))_
10
10
 
11
- __This plugin ensures that your architecture boundaries are respected by the elements in your project__ checking the folders and files structure and the `import` statements (_Read the [main rules overview chapter](#main-rules-overview) for better comprehension._). __It is not a replacement for [eslint-plugin-import](https://www.npmjs.com/package/eslint-plugin-import), on the contrary, the combination of both plugins is recommended.__
11
+ __This plugin ensures that your architecture boundaries are respected by the elements in your project__ checking the folders and files structure and the dependencies between them. __It is not a replacement for [eslint-plugin-import](https://www.npmjs.com/package/eslint-plugin-import), on the contrary, the combination of both plugins is recommended.__
12
+
13
+ By default, __the plugin works by checking `import` statements, but it is also able to analyze exports, dynamic imports, and can be configured to check any other [AST nodes](https://eslint.org/docs/latest/extend/selectors)__. (_Read the [main rules overview](#main-rules-overview) and [configuration](#configuration) chapters for better comprehension_)
12
14
 
13
15
  ## Table of Contents
14
16
 
@@ -33,6 +35,7 @@ __This plugin ensures that your architecture boundaries are respected by the ele
33
35
  * [Advanced example](#advanced-example)
34
36
  - [Resolvers](#resolvers)
35
37
  - [Usage with TypeScript](#usage-with-typescript)
38
+ - [Migration guides](#migration-guides)
36
39
  - [Debug mode](#debug-mode)
37
40
  - [Acknowledgements](#acknowledgements)
38
41
  - [Contributing](#contributing)
@@ -59,15 +62,11 @@ Activate the plugin and one of the canned configs in your `.eslintrc.(yml|json|j
59
62
  }
60
63
  ```
61
64
 
62
- ## Migrating from v1.x
63
-
64
- New v2.0.0 release has introduced many breaking changes. If you were using v1.x, you should [read the "how to migrate from v1 to v2" guide](./docs/guides/how-to-migrate-from-v1-to-v2.md).
65
-
66
65
  ## Overview
67
66
 
68
- All of the plugin rules need to be able to identify the elements in the project, so, first of all you have to define your project elements using the `boundaries/elements` setting.
67
+ All of the plugin rules need to be able to identify the elements in the project, so, first of all you have to define your project element types by using the `boundaries/elements` setting.
69
68
 
70
- The plugin will use the provided patterns to identify each file or local `import` statement as one of the element types.
69
+ The plugin will use the provided patterns to identify each file as one of the element types. It will also assign a type to each dependency detected in the [dependency nodes (`import` or other statements)](#boundariesdependency-nodes), and it will check if the relationship between the dependent element and the dependency is allowed or not.
71
70
 
72
71
  ```json
73
72
  {
@@ -92,7 +91,7 @@ The plugin will use the provided patterns to identify each file or local `import
92
91
 
93
92
  This is only a basic example of configuration. The plugin can be configured to identify elements being a file, or elements being a folder containing files. It also supports capturing path fragments to be used afterwards on each rule options, etc. __Read the [configuration chapter](#configuration) for further info, as configuring it properly is crucial__ to take advantage of all of the plugin features.
94
93
 
95
- Once your project elements are defined, you can use them to configure each rule using its own options. For example, you could define which elements can be dependencies of other ones configuring the `element-types` rule as in:
94
+ Once your project element types are defined, you can use them to configure each rule using its own options. For example, you could define which elements can be dependencies of other ones by configuring the `element-types` rule as in:
96
95
 
97
96
  ```json
98
97
  {
@@ -114,7 +113,7 @@ Once your project elements are defined, you can use them to configure each rule
114
113
  }
115
114
  ```
116
115
 
117
- > The plugin won't apply rules to a file or `import` when it does not recognize its element type, but you can force all files in your project to belong to an element type enabling the [boundaries/no-unknown-files](docs/rules/no-unknown-files.md) rule.
116
+ > The plugin won't apply rules to a file or dependency when it does not recognize its element type, but you can force all files in your project to belong to an element type by enabling the [boundaries/no-unknown-files](docs/rules/no-unknown-files.md) rule.
118
117
 
119
118
  ## Main rules overview
120
119
 
@@ -202,6 +201,52 @@ Define patterns to recognize each file in the project as one of this element typ
202
201
 
203
202
  > Tip: You can enable the [debug mode](#debug-mode) when configuring the plugin, and you will get information about the type assigned to each file in the project, as well as captured properties and values.
204
203
 
204
+ #### __`boundaries/dependency-nodes`__
205
+
206
+ This setting allows to modify built-in default dependency nodes. By default, the plugin will analyze only the `import` statements. All the rules defined for the plugin will be applicable to the nodes defined in this setting.
207
+
208
+ The setting should be an array of the following strings:
209
+
210
+ * `'import'`: analyze `import` statements.
211
+ * `'export'`: analyze `export` statements.
212
+ * `'dynamic-import'`: analyze [dynamic import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) statements.
213
+
214
+ If you want to define custom dependency nodes, such as `jest.mock(...)`, use [additional-dependency-nodes](#boundariesadditional-dependency-nodes) setting.
215
+
216
+ For example, if you want to analyze the `import` and `dynamic-import` statements, you should use the following value:
217
+
218
+ ```jsonc
219
+ "boundaries/dependency-nodes": ["import", "dynamic-import"],
220
+ ```
221
+
222
+ #### __`boundaries/additional-dependency-nodes`__
223
+
224
+ This setting allows to define custom dependency nodes to analyze. All the rules defined for the plugin will be applicable to the nodes defined in this setting.
225
+
226
+ The setting should be an array of objects with the following structure:
227
+
228
+ * __`selector`__: The [esquery selector](https://github.com/estools/esquery) for the `Literal` node in which dependency source are defined. For example, to analyze `jest.mock(...)` calls you could use this [AST selector](https://eslint.org/docs/latest/extend/selectors): `CallExpression[callee.object.name=jest][callee.property.name=mock] > Literal:first-child`.
229
+ * __`kind`__: The kind of dependency, possible values are: `"value"` or `"type"`. It is available only when using TypeScript.
230
+
231
+ Example of usage:
232
+
233
+ ```jsonc
234
+ {
235
+ "boundaries/additional-dependency-nodes": [
236
+ // jest.requireActual('source')
237
+ {
238
+ "selector": "CallExpression[callee.object.name=jest][callee.property.name=requireActual] > Literal",
239
+ "kind": "value",
240
+ },
241
+ // jest.mock('source', ...)
242
+ {
243
+ "selector": "CallExpression[callee.object.name=jest][callee.property.name=mock] > Literal:first-child",
244
+ "kind": "value",
245
+ },
246
+ ],
247
+ }
248
+ ```
249
+
205
250
  #### __`boundaries/include`__
206
251
 
207
252
  Files or dependencies not matching these [`micromatch` patterns](https://github.com/micromatch/micromatch) will be ignored by the plugin. If this option is not provided, all files will be included.
@@ -235,7 +280,7 @@ Use this setting only if you are facing issues with the plugin when executing th
235
280
  <details>
236
281
  <summary>How to define the root path of the project</summary>
237
282
 
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.
283
+ 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 by using this option.
239
284
 
240
285
  For example, supposing that the `.eslintrc.js` file is located in the project root, you could define the root path as in:
241
286
 
@@ -257,12 +302,9 @@ You can also provide an absolute path in the environment variable, but it may be
257
302
 
258
303
  </details>
259
304
 
260
-
261
-
262
305
  ### Predefined configurations
263
306
 
264
- This plugin is distributed with two different predefined configurations: "recommended" and "strict".
265
-
307
+ The plugin is distributed with two different predefined configurations: "recommended" and "strict".
266
308
 
267
309
  #### Recommended
268
310
 
@@ -333,7 +375,7 @@ Remember that:
333
375
 
334
376
  * __`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`).
335
377
  * __`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.
378
+ * __`importKind`__: `<string>` _Optional_. It is useful only when using TypeScript, because 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.
337
379
  * __`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.
338
380
 
339
381
  > Tip: Properties `from/target` and `disallow/allow` can receive a single matcher, or an array of matchers.
@@ -511,6 +553,16 @@ module.exports = {
511
553
 
512
554
  In case you face any issue configuring it, you can also [use this repository as a guide](https://github.com/javierbrea/epb-ts-example). It contains a fully working and tested example.
513
555
 
556
+ ## Migration guides
557
+
558
+ ### Migrating from v3.x
559
+
560
+ New v4.0.0 release has introduced breaking changes. If you were using v3.x, you should [read the "how to migrate from v3 to v4" guide](./docs/guides/how-to-migrate-from-v3-to-v4.md).
561
+
562
+ ### Migrating from v1.x
563
+
564
+ New v2.0.0 release has introduced many breaking changes. If you were using v1.x, you should [read the "how to migrate from v1 to v2" guide](./docs/guides/how-to-migrate-from-v1-to-v2.md).
565
+
514
566
  ## Debug mode
515
567
 
516
568
  In order to help during the configuration process, the plugin can trace information about the files and imports being analyzed. The information includes the file path, the assigned element type, the captured values, etc. So, it can help you to check that your `elements` setting works as expected. You can enable it using the `ESLINT_PLUGIN_BOUNDARIES_DEBUG` environment variable.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-boundaries",
3
- "version": "3.4.1",
3
+ "version": "4.0.1",
4
4
  "description": "Eslint plugin checking architecture boundaries between elements",
5
5
  "keywords": [
6
6
  "eslint",
@@ -16,6 +16,8 @@ module.exports = {
16
16
  IGNORE: `${PLUGIN_NAME}/ignore`,
17
17
  INCLUDE: `${PLUGIN_NAME}/include`,
18
18
  ROOT_PATH: `${PLUGIN_NAME}/root-path`,
19
+ DEPENDENCY_NODES: `${PLUGIN_NAME}/dependency-nodes`,
20
+ ADDITIONAL_DEPENDENCY_NODES: `${PLUGIN_NAME}/additional-dependency-nodes`,
19
21
 
20
22
  // env vars
21
23
  DEBUG: `${PLUGIN_ENV_VARS_PREFIX}_DEBUG`,
@@ -36,4 +38,28 @@ module.exports = {
36
38
 
37
39
  // elements settings properties,
38
40
  VALID_MODES: ["folder", "file", "full"],
41
+
42
+ VALID_DEPENDENCY_NODE_KINDS: ["value", "type"],
43
+ DEFAULT_DEPENDENCY_NODES: {
44
+ import: [
45
+ // Note: detects "import x from 'source'"
46
+ { selector: "ImportDeclaration:not([importKind=type]) > Literal", kind: "value" },
47
+ // Note: detects "import type x from 'source'"
48
+ { selector: "ImportDeclaration[importKind=type] > Literal", kind: "type" },
49
+ ],
50
+ "dynamic-import": [
51
+ // Note: detects "import('source')"
52
+ { selector: "ImportExpression > Literal", kind: "value" },
53
+ ],
54
+ export: [
55
+ // Note: detects "export * from 'source'";
56
+ { selector: "ExportAllDeclaration:not([exportKind=type]) > Literal", kind: "value" },
57
+ // Note: detects "export type * from 'source'";
58
+ { selector: "ExportAllDeclaration[exportKind=type] > Literal", kind: "type" },
59
+ // Note: detects "export { x } from 'source'";
60
+ { selector: "ExportNamedDeclaration:not([exportKind=type]) > Literal", kind: "value" },
61
+ // Note: detects "export type { x } from 'source'";
62
+ { selector: "ExportNamedDeclaration[exportKind=type] > Literal", kind: "type" },
63
+ ],
64
+ },
39
65
  };
@@ -28,23 +28,6 @@ function meta({ description, schema = [], ruleName }) {
28
28
  };
29
29
  }
30
30
 
31
- function dependencyLocation(node, context) {
32
- const columnStart = context.getSourceCode().getText(node).indexOf(node.source.value) - 1;
33
- const columnEnd = columnStart + node.source.value.length + 2;
34
- return {
35
- loc: {
36
- start: {
37
- line: node.loc.start.line,
38
- column: columnStart,
39
- },
40
- end: {
41
- line: node.loc.end.line,
42
- column: columnEnd,
43
- },
44
- },
45
- };
46
- }
47
-
48
31
  function micromatchPatternReplacingObjectsValues(pattern, object) {
49
32
  let patternToReplace = pattern;
50
33
  // Backward compatibility
@@ -62,7 +45,7 @@ function micromatchPatternReplacingObjectsValues(pattern, object) {
62
45
  function isObjectMatch(objectWithMatchers, object, objectsWithValuesToReplace) {
63
46
  return Object.keys(objectWithMatchers).reduce((isMatch, key) => {
64
47
  if (isMatch) {
65
- if (!object) {
48
+ if (!object || !object[key]) {
66
49
  return false;
67
50
  }
68
51
  const micromatchPattern = micromatchPatternReplacingObjectsValues(
@@ -237,7 +220,6 @@ function elementRulesAllowDependency({
237
220
 
238
221
  module.exports = {
239
222
  meta,
240
- dependencyLocation,
241
223
  isObjectMatch,
242
224
  isMatchElementKey,
243
225
  isMatchElementType,
@@ -6,6 +6,14 @@ function isArray(object) {
6
6
  return Array.isArray(object);
7
7
  }
8
8
 
9
+ function isObject(object) {
10
+ return typeof object === "object" && object !== null && !isArray(object);
11
+ }
12
+
13
+ function getArrayOrNull(value) {
14
+ return isArray(value) ? value : null;
15
+ }
16
+
9
17
  function replaceObjectValueInTemplate(template, key, value, namespace) {
10
18
  const keyToReplace = namespace ? `${namespace}.${key}` : key;
11
19
  const regexp = new RegExp(`\\$\\{${keyToReplace}\\}`, "g");
@@ -27,5 +35,7 @@ function replaceObjectValuesInTemplates(strings, object, namespace) {
27
35
  module.exports = {
28
36
  isString,
29
37
  isArray,
38
+ isObject,
39
+ getArrayOrNull,
30
40
  replaceObjectValuesInTemplates,
31
41
  };
@@ -1,11 +1,20 @@
1
1
  const micromatch = require("micromatch");
2
2
 
3
- const { TYPES, ALIAS, ELEMENTS, VALID_MODES } = require("../constants/settings");
3
+ const {
4
+ TYPES,
5
+ ALIAS,
6
+ ELEMENTS,
7
+ VALID_MODES,
8
+ DEPENDENCY_NODES,
9
+ ADDITIONAL_DEPENDENCY_NODES,
10
+ VALID_DEPENDENCY_NODE_KINDS,
11
+ DEFAULT_DEPENDENCY_NODES,
12
+ } = require("../constants/settings");
4
13
 
5
14
  const { getElementsTypeNames, isLegacyType } = require("./settings");
6
15
  const { rulesMainKey } = require("./rules");
7
16
  const { warnOnce } = require("./debug");
8
- const { isArray, isString } = require("./utils");
17
+ const { isArray, isString, isObject } = require("./utils");
9
18
 
10
19
  const invalidMatchers = [];
11
20
 
@@ -147,6 +156,58 @@ function validateElements(elements) {
147
156
  });
148
157
  }
149
158
 
159
+ function validateDependencyNodes(dependencyNodes) {
160
+ if (!dependencyNodes) {
161
+ return;
162
+ }
163
+
164
+ const defaultNodesNames = Object.keys(DEFAULT_DEPENDENCY_NODES);
165
+ const invalidFormatMessage = [
166
+ `Please provide a valid value in ${DEPENDENCY_NODES} setting.`,
167
+ `The value should be an array of the following strings:`,
168
+ ` "${defaultNodesNames.join('", "')}".`,
169
+ ].join(" ");
170
+
171
+ if (!isArray(dependencyNodes)) {
172
+ warnOnce(invalidFormatMessage);
173
+ return;
174
+ }
175
+
176
+ dependencyNodes.forEach((dependencyNode) => {
177
+ if (!isString(dependencyNode) || !defaultNodesNames.includes(dependencyNode)) {
178
+ warnOnce(invalidFormatMessage);
179
+ }
180
+ });
181
+ }
182
+
183
+ function validateAdditionalDependencyNodes(additionalDependencyNodes) {
184
+ if (!additionalDependencyNodes) {
185
+ return;
186
+ }
187
+
188
+ const invalidFormatMessage = [
189
+ `Please provide a valid value in ${ADDITIONAL_DEPENDENCY_NODES} setting.`,
190
+ "The value should be an array composed of the following objects:",
191
+ '{ selector: "<esquery selector>", kind: "value" | "type" }.',
192
+ ].join(" ");
193
+
194
+ if (!isArray(additionalDependencyNodes)) {
195
+ warnOnce(invalidFormatMessage);
196
+ return;
197
+ }
198
+
199
+ additionalDependencyNodes.forEach((dependencyNode) => {
200
+ const isValidObject =
201
+ isObject(dependencyNode) &&
202
+ isString(dependencyNode.selector) &&
203
+ (!dependencyNode.kind || VALID_DEPENDENCY_NODE_KINDS.includes(dependencyNode.kind));
204
+
205
+ if (!isValidObject) {
206
+ warnOnce(invalidFormatMessage);
207
+ }
208
+ });
209
+ }
210
+
150
211
  function deprecateAlias(aliases) {
151
212
  if (aliases) {
152
213
  warnOnce(
@@ -165,6 +226,8 @@ function validateSettings(settings) {
165
226
  deprecateTypes(settings[TYPES]);
166
227
  deprecateAlias(settings[ALIAS]);
167
228
  validateElements(settings[ELEMENTS] || settings[TYPES]);
229
+ validateDependencyNodes(settings[DEPENDENCY_NODES]);
230
+ validateAdditionalDependencyNodes(settings[ADDITIONAL_DEPENDENCY_NODES]);
168
231
  }
169
232
 
170
233
  function validateRules(settings, rules = [], options = {}) {
@@ -3,11 +3,7 @@ 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 {
7
- dependencyLocation,
8
- isMatchElementType,
9
- elementRulesAllowDependency,
10
- } = require("../helpers/rules");
6
+ const { isMatchElementType, elementRulesAllowDependency } = require("../helpers/rules");
11
7
  const {
12
8
  customErrorMessage,
13
9
  ruleElementMessage,
@@ -59,7 +55,6 @@ module.exports = dependencyRule(
59
55
  context.report({
60
56
  message: errorMessage(ruleData, file, dependency),
61
57
  node: node,
62
- ...dependencyLocation(node, context),
63
58
  });
64
59
  }
65
60
  }
@@ -4,7 +4,6 @@ const dependencyRule = require("../rules-factories/dependency-rule");
4
4
 
5
5
  const { rulesOptionsSchema } = require("../helpers/validations");
6
6
  const {
7
- dependencyLocation,
8
7
  isMatchElementKey,
9
8
  elementRulesAllowDependency,
10
9
  isMatchImportKind,
@@ -73,7 +72,6 @@ module.exports = dependencyRule(
73
72
  context.report({
74
73
  message: errorMessage(ruleData, file, dependency),
75
74
  node: node,
76
- ...dependencyLocation(node, context),
77
75
  });
78
76
  }
79
77
  }
@@ -6,7 +6,6 @@ const dependencyRule = require("../rules-factories/dependency-rule");
6
6
 
7
7
  const { rulesOptionsSchema } = require("../helpers/validations");
8
8
  const {
9
- dependencyLocation,
10
9
  elementRulesAllowDependency,
11
10
  micromatchPatternReplacingObjectsValues,
12
11
  isMatchImportKind,
@@ -19,18 +18,29 @@ const {
19
18
  } = require("../helpers/messages");
20
19
  const { isArray } = require("../helpers/utils");
21
20
 
21
+ function getSpecifiers(node) {
22
+ if (node.parent.type === "ImportDeclaration") {
23
+ return node.parent.specifiers
24
+ .filter((specifier) => specifier.type === "ImportSpecifier" && specifier.imported.name)
25
+ .map((specifier) => specifier.imported.name);
26
+ }
27
+
28
+ if (node.parent.type === "ExportNamedDeclaration") {
29
+ return node.parent.specifiers
30
+ .filter((specifier) => specifier.type === "ExportSpecifier" && specifier.exported.name)
31
+ .map((specifier) => specifier.exported.name);
32
+ }
33
+
34
+ return [];
35
+ }
36
+
22
37
  function specifiersMatch(specifiers, specifierOptions, elementsCapturedValues) {
23
- const importedSpecifiersNames = specifiers
24
- .filter((specifier) => {
25
- return specifier.type === "ImportSpecifier" && specifier.imported.name;
26
- })
27
- .map((specifier) => specifier.imported.name);
28
38
  return specifierOptions.reduce((found, option) => {
29
39
  const matcherWithTemplateReplaced = micromatchPatternReplacingObjectsValues(
30
40
  option,
31
41
  elementsCapturedValues,
32
42
  );
33
- if (micromatch.some(importedSpecifiersNames, matcherWithTemplateReplaced)) {
43
+ if (micromatch.some(specifiers, matcherWithTemplateReplaced)) {
34
44
  found.push(option);
35
45
  }
36
46
  return found;
@@ -182,14 +192,13 @@ module.exports = dependencyRule(
182
192
  if (dependency.isExternal) {
183
193
  const ruleData = elementRulesAllowExternalDependency(
184
194
  file,
185
- { ...dependency, specifiers: node.source.parent.specifiers },
195
+ { ...dependency, specifiers: getSpecifiers(node) },
186
196
  options,
187
197
  );
188
198
  if (!ruleData.result) {
189
199
  context.report({
190
200
  message: errorMessage(ruleData, file, dependency),
191
201
  node: node,
192
- ...dependencyLocation(node, context),
193
202
  });
194
203
  }
195
204
  }
@@ -2,8 +2,6 @@ const { RULE_NO_IGNORED } = require("../constants/settings");
2
2
 
3
3
  const dependencyRule = require("../rules-factories/dependency-rule");
4
4
 
5
- const { dependencyLocation } = require("../helpers/rules");
6
-
7
5
  module.exports = dependencyRule(
8
6
  {
9
7
  ruleName: RULE_NO_IGNORED,
@@ -14,7 +12,6 @@ module.exports = dependencyRule(
14
12
  context.report({
15
13
  message: `Importing ignored files is not allowed`,
16
14
  node: node,
17
- ...dependencyLocation(node, context),
18
15
  });
19
16
  }
20
17
  },
@@ -2,7 +2,6 @@ const { RULE_NO_PRIVATE } = require("../constants/settings");
2
2
 
3
3
  const dependencyRule = require("../rules-factories/dependency-rule");
4
4
 
5
- const { dependencyLocation } = require("../helpers/rules");
6
5
  const { customErrorMessage, elementMessage } = require("../helpers/messages");
7
6
 
8
7
  function errorMessage(file, dependency, options) {
@@ -45,7 +44,6 @@ module.exports = dependencyRule(
45
44
  context.report({
46
45
  message: errorMessage(file, dependency, options),
47
46
  node: node,
48
- ...dependencyLocation(node, context),
49
47
  });
50
48
  }
51
49
  },
@@ -2,8 +2,6 @@ const { RULE_NO_UNKNOWN } = require("../constants/settings");
2
2
 
3
3
  const dependencyRule = require("../rules-factories/dependency-rule");
4
4
 
5
- const { dependencyLocation } = require("../helpers/rules");
6
-
7
5
  module.exports = dependencyRule(
8
6
  {
9
7
  ruleName: RULE_NO_UNKNOWN,
@@ -14,7 +12,6 @@ module.exports = dependencyRule(
14
12
  context.report({
15
13
  message: `Importing unknown elements is not allowed`,
16
14
  node: node,
17
- ...dependencyLocation(node, context),
18
15
  });
19
16
  }
20
17
  },
@@ -1,3 +1,9 @@
1
+ const {
2
+ DEPENDENCY_NODES,
3
+ DEFAULT_DEPENDENCY_NODES,
4
+ ADDITIONAL_DEPENDENCY_NODES,
5
+ } = require("../constants/settings");
6
+ const { getArrayOrNull } = require("../helpers/utils");
1
7
  const { fileInfo } = require("../core/elementsInfo");
2
8
  const { dependencyInfo } = require("../core/dependencyInfo");
3
9
 
@@ -19,13 +25,28 @@ module.exports = function (ruleMeta, rule, ruleOptions = {}) {
19
25
  validateRules(context.settings, options.rules, ruleOptions.validateRules);
20
26
  }
21
27
 
22
- return {
23
- ImportDeclaration: (node) => {
24
- const dependency = dependencyInfo(node.source.value, node.importKind, context);
28
+ const dependencyNodesSetting = getArrayOrNull(context.settings[DEPENDENCY_NODES]);
29
+ const additionalDependencyNodesSetting = getArrayOrNull(
30
+ context.settings[ADDITIONAL_DEPENDENCY_NODES],
31
+ );
32
+ const dependencyNodes = (dependencyNodesSetting || ["import"])
33
+ .map((dependencyNode) => DEFAULT_DEPENDENCY_NODES[dependencyNode])
34
+ .flat()
35
+ .filter(Boolean);
36
+ const additionalDependencyNodes = additionalDependencyNodesSetting || [];
25
37
 
26
- rule({ file, dependency, options, node, context });
38
+ return [...dependencyNodes, ...additionalDependencyNodes].reduce(
39
+ (visitors, { selector, kind }) => {
40
+ visitors[selector] = (node) => {
41
+ const dependency = dependencyInfo(node.value, kind, context);
42
+
43
+ rule({ file, dependency, options, node, context });
44
+ };
45
+
46
+ return visitors;
27
47
  },
28
- };
48
+ {},
49
+ );
29
50
  },
30
51
  };
31
52
  };