eslint-plugin-stratified-design 0.7.0 → 0.8.0-beta.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 +1 -0
- package/docs/rules/lower-level-imports.md +58 -20
- package/docs/rules/no-same-level-funcs.md +6 -1
- package/docs/rules/stratified-imports.md +178 -0
- package/lib/helpers/{lowerLevelImports/4 layer.js → common.js} +36 -2
- package/lib/helpers/lowerLevelImports/1 layer.js +18 -11
- package/lib/helpers/lowerLevelImports/2 layer.js +4 -4
- package/lib/helpers/lowerLevelImports/3 layer.js +34 -9
- package/lib/helpers/stratifiedImports/1 layer.js +184 -0
- package/lib/helpers/stratifiedImports/2 layer.js +238 -0
- package/lib/helpers/stratifiedImports/index.js +41 -0
- package/lib/helpers/type.js +8 -0
- package/lib/rules/lower-level-imports.js +13 -3
- package/lib/rules/no-same-level-funcs.js +50 -23
- package/lib/rules/stratified-imports.js +184 -0
- package/mocked/stratified-imports/.stratified.json +12 -0
- package/mocked/stratified-imports/layerB/.stratified.json +1 -0
- package/mocked/stratified-imports/layerD/.stratified.json +1 -0
- package/mocked/stratified-imports/layerD/layerDB/.stratified.json +1 -0
- package/mocked/stratified-imports/layerJ/.stratified.json +1 -0
- package/package.json +1 -1
- package/tests/lib/helpers/lower-level-imports.js +76 -0
- package/tests/lib/helpers/stratified-imports.js +292 -0
- package/tests/lib/rules/no-same-level-funcs.js +22 -1
- package/tests/lib/rules/stratified-imports.js +278 -0
package/README.md
CHANGED
|
@@ -39,4 +39,5 @@ Then configure the rules you wish to use under the rules section:
|
|
|
39
39
|
## Supported Rules
|
|
40
40
|
|
|
41
41
|
- [lower-level-imports](https://github.com/anisotropy/eslint-plugin-stratified-design/blob/main/docs/rules/lower-level-imports.md): Requires lower-level modules to be imported.
|
|
42
|
+
- [lower-level-imports](https://github.com/anisotropy/eslint-plugin-stratified-design/blob/main/docs/rules/stratified-imports.md): Requires lower-level modules to be imported. The stratified structure is set by `.stratified.json`.
|
|
42
43
|
- [no-same-level-funcs](https://github.com/anisotropy/eslint-plugin-stratified-design/blob/main/docs/rules/no-same-level-funcs.md): Disallows calling functions in the same file.
|
|
@@ -15,9 +15,14 @@ This rule works correctly on POSIX systems, where the path segment separator is
|
|
|
15
15
|
The syntax to specify the level structure is as follows:
|
|
16
16
|
|
|
17
17
|
```json
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
18
|
+
{
|
|
19
|
+
"stratified-design/lower-level-imports": [
|
|
20
|
+
"error",
|
|
21
|
+
{
|
|
22
|
+
"structure": ["layer1", "layer2", "layer3"]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
21
26
|
```
|
|
22
27
|
|
|
23
28
|
In the folder array, the file or folder on the left is considered to be at a higher level than the one on the right.
|
|
@@ -25,9 +30,14 @@ In the folder array, the file or folder on the left is considered to be at a hig
|
|
|
25
30
|
To designate a layer as an abstract barrier, set `barrier` to `true`:
|
|
26
31
|
|
|
27
32
|
```json
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
|
|
33
|
+
{
|
|
34
|
+
"stratified-design/lower-level-imports": [
|
|
35
|
+
"error",
|
|
36
|
+
{
|
|
37
|
+
"structure": ["layer1", { "name": "layer2", "barrier": true }, "layer3"]
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
}
|
|
31
41
|
```
|
|
32
42
|
|
|
33
43
|
For the 'abstract barrier,' refer to "[Grokking Simplicity](https://grokkingsimplicity.com)."
|
|
@@ -35,33 +45,54 @@ For the 'abstract barrier,' refer to "[Grokking Simplicity](https://grokkingsimp
|
|
|
35
45
|
To locate a node module in the structure, set `nodeModule` to `true`:
|
|
36
46
|
|
|
37
47
|
```json
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
48
|
+
{
|
|
49
|
+
"stratified-design/lower-level-imports": [
|
|
50
|
+
"error",
|
|
51
|
+
{
|
|
52
|
+
"structure": [
|
|
53
|
+
"layer1",
|
|
54
|
+
{ "name": "nodeModule", "nodeModule": true },
|
|
55
|
+
"layer3"
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
|
41
60
|
```
|
|
42
61
|
|
|
43
62
|
The default root directory is the current working directory. To change the root directory, use the `root` option:
|
|
44
63
|
|
|
45
64
|
```json
|
|
46
|
-
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
-
|
|
65
|
+
{
|
|
66
|
+
"stratified-design/lower-level-imports": [
|
|
67
|
+
"error",
|
|
68
|
+
{
|
|
69
|
+
"structure": ["layer1", "layer2", "layer3"],
|
|
70
|
+
"root": "./src"
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
50
74
|
```
|
|
51
75
|
|
|
52
76
|
If the name of an imported module has an alias, register the alias using the `aliases` option:
|
|
53
77
|
|
|
54
78
|
```json
|
|
55
|
-
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
79
|
+
{
|
|
80
|
+
"stratified-design/lower-level-imports": [
|
|
81
|
+
"error",
|
|
82
|
+
{
|
|
83
|
+
"structure": ["layer1", "layer2", "layer3"],
|
|
84
|
+
"aliases": { "@": "./src" }
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
}
|
|
59
88
|
```
|
|
60
89
|
|
|
61
90
|
If you want to register the level of a layer by 'number,' set the option `useLevelNumber` to `true`:
|
|
62
91
|
|
|
63
92
|
```json
|
|
64
|
-
|
|
93
|
+
{
|
|
94
|
+
"stratified-design/lower-level-imports": ["error", { "useLevelNumber": true }]
|
|
95
|
+
}
|
|
65
96
|
```
|
|
66
97
|
|
|
67
98
|
The options `structure` and `useLevelNumber` can be used together.
|
|
@@ -69,13 +100,20 @@ The options `structure` and `useLevelNumber` can be used together.
|
|
|
69
100
|
An `index.xxx` file can be the highest level layer of sibling files when the option `isIndexHighest` is set to `true`:
|
|
70
101
|
|
|
71
102
|
```json
|
|
72
|
-
|
|
103
|
+
{
|
|
104
|
+
"stratified-design/lower-level-imports": ["error", { "isIndexHighest": true }]
|
|
105
|
+
}
|
|
73
106
|
```
|
|
74
107
|
|
|
75
108
|
You can register the files to apply the rule (`lower-level-imports`) using the `include` and `exclude` options:
|
|
76
109
|
|
|
77
110
|
```json
|
|
78
|
-
|
|
111
|
+
{
|
|
112
|
+
"stratified-design/lower-level-imports": [
|
|
113
|
+
"error",
|
|
114
|
+
{ "include": ["**/*.js"], "exclude": ["**/*.test.js"] }
|
|
115
|
+
]
|
|
116
|
+
}
|
|
79
117
|
```
|
|
80
118
|
|
|
81
119
|
The default is as follows:
|
|
@@ -7,7 +7,12 @@ This rule prohibits calling functions at the same level in the same file.
|
|
|
7
7
|
You can register the files to apply the rule (`no-same-level-funcs`) using the `include` and `exclude` options:
|
|
8
8
|
|
|
9
9
|
```json
|
|
10
|
-
|
|
10
|
+
{
|
|
11
|
+
"stratified-design/no-same-level-funcs": [
|
|
12
|
+
"error",
|
|
13
|
+
{ "include": ["**/*.js"], "exclude": ["**/*.test.js"] }
|
|
14
|
+
]
|
|
15
|
+
}
|
|
11
16
|
```
|
|
12
17
|
|
|
13
18
|
The default is as follows:
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Require that lower-level modules be imported (stratified-imports)
|
|
2
|
+
|
|
3
|
+
(Note: This rule works correctly on POSIX systems, where the path segment separator is `/`. It will be updated to work well on Windows systems in the future.)
|
|
4
|
+
|
|
5
|
+
This rule enforces the requirement for importing lower-level modules. The hierarchy should be set by `.stratified.json` in **each folder** as follows:
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
[
|
|
9
|
+
["layerA"],
|
|
10
|
+
[{ "name": "layerB", "barrier": true }],
|
|
11
|
+
[{ "name": "nodeModuleC", "nodeModule": true }],
|
|
12
|
+
["layerD", "layerE"]
|
|
13
|
+
]
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
And consider that the folder structure is as follows:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
┣ layerA
|
|
20
|
+
┣ layerB
|
|
21
|
+
┣ layerD
|
|
22
|
+
┣ layerE
|
|
23
|
+
┃ ┣ index.js
|
|
24
|
+
┃ ┣ entry
|
|
25
|
+
┃ ┣ layerEA
|
|
26
|
+
┃ ┗ .stratified.json
|
|
27
|
+
┗ .stratified.json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The above JSON file indicates the following:
|
|
31
|
+
|
|
32
|
+
- The `layerA` file/folder at the highest level.
|
|
33
|
+
- The `layerB` file/folder is a lower-level layer than `layerA` and serves as an abstract barrier. (For the concept of 'abstract barrier,' refer to '[Grokking Simplicity](https://grokkingsimplicity.com).')
|
|
34
|
+
- `nodeModuleC` is an **installed module** (node module) and is at a lower level than `layerB`. (Unregistered node modules are considered to be the lowest layers.)
|
|
35
|
+
- The `layerD` file/folder and the `layerE` file/folder are at the same level and represent the lowest level layers.
|
|
36
|
+
|
|
37
|
+
Consider that the `.stratified.json` in the `layerE` folder is as follows:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
[["index", "entry"], ["layerEA"]]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Higher-level layers than `layerE` can import `./layerE` and `./layer/entry` as follows:
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
import { func } from "./layerE";
|
|
47
|
+
import { func } from "./layerE/entry";
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
However, `./layer/layerEA` should not be imported.
|
|
51
|
+
|
|
52
|
+
## Options
|
|
53
|
+
|
|
54
|
+
If an imported module has an alias, register the alias using the `aliases` option:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"stratified-design/stratified-imports": [
|
|
59
|
+
"error",
|
|
60
|
+
{
|
|
61
|
+
"aliases": { "@/": "./src/" }
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
You can register the files to which the rule (`stratified-imports`) should apply using the `include` and `exclude` options:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"stratified-design/lower-level-imports": [
|
|
72
|
+
"error",
|
|
73
|
+
{ "include": ["**/*.js"], "exclude": ["**/*.test.js"] }
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The default configuration is as follows:
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"include": ["**/*.{js,ts,jsx,tsx}"],
|
|
83
|
+
"exclude": ["**/*.{spec,test}.{js,ts,jsx,tsx}"]
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Rule Details
|
|
88
|
+
|
|
89
|
+
Consider the following folder structure:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
src/
|
|
93
|
+
┣ layerA.js
|
|
94
|
+
┣ layerB.js
|
|
95
|
+
┣ layerD.js
|
|
96
|
+
┣ layerE/
|
|
97
|
+
┃ ┣ index.js
|
|
98
|
+
┃ ┣ entry
|
|
99
|
+
┃ ┣ layerEA.js
|
|
100
|
+
┃ ┗ .stratified.json
|
|
101
|
+
┗ .stratified.json
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
and the `.stratified.json` in `src/` is as follows:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
[
|
|
108
|
+
["layerA"],
|
|
109
|
+
[{ "name": "layerB", "barrier": true }],
|
|
110
|
+
[{ "name": "nodeModuleC", "nodeModule": true }],
|
|
111
|
+
["layerD", "layerE"]
|
|
112
|
+
]
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
and the `.stratified.json` in `layerE/` is as follows:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
[["index", "entry"], ["layerEA"]]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Examples of **incorrect** code for this rule:
|
|
122
|
+
|
|
123
|
+
```js
|
|
124
|
+
// ./layerB.js
|
|
125
|
+
import { func } from "./layerA";
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
// ./layerA.js
|
|
130
|
+
import { func } from "./layerD";
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
// ./layerD.js
|
|
135
|
+
import { func } from "nodeModuleC";
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
// ./layerD.js
|
|
140
|
+
import { func } from "layerE";
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
```js
|
|
144
|
+
// ./layerB.js
|
|
145
|
+
import { func } from "layerE/layerEA";
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Examples of **correct** code for this rule:
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
// ./layerA.js
|
|
152
|
+
import { func } from "./layerB";
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
```js
|
|
156
|
+
// ./layerB.js
|
|
157
|
+
import { func } from "nodeModuleC";
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
// ./layerD.js
|
|
162
|
+
import { func } from "some-node-module";
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```js
|
|
166
|
+
// ./layerB.js
|
|
167
|
+
import { func } from "./layerD";
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
// ./layerB.js
|
|
172
|
+
import { func } from "./layerE";
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
```js
|
|
176
|
+
// ./layerB.js
|
|
177
|
+
import { func } from "./layerE/entry";
|
|
178
|
+
```
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const p = require("path");
|
|
2
2
|
|
|
3
|
+
// @level 2
|
|
3
4
|
/**
|
|
4
5
|
* @param {string} from
|
|
5
6
|
* @param {string} to
|
|
@@ -10,27 +11,34 @@ const toRelative = (from, to) => {
|
|
|
10
11
|
return `${to}/`.startsWith(`${from}/`) ? `./${rel}` : rel;
|
|
11
12
|
};
|
|
12
13
|
|
|
14
|
+
// @level 2
|
|
13
15
|
/**
|
|
14
16
|
* @param {string} path
|
|
15
17
|
* @returns path to segments
|
|
16
18
|
*/
|
|
17
19
|
const toSegments = (path) => path.split("/");
|
|
18
20
|
|
|
21
|
+
// @level 2
|
|
19
22
|
/**
|
|
20
23
|
* @param {string[]} segments
|
|
21
24
|
* @returns segments to path
|
|
22
25
|
*/
|
|
23
26
|
const toPath = (segments) => segments.join("/");
|
|
24
27
|
|
|
28
|
+
// @level 2
|
|
25
29
|
const joinPath = p.join;
|
|
26
30
|
|
|
31
|
+
// @level 2
|
|
27
32
|
const resolvePath = p.resolve;
|
|
28
33
|
|
|
34
|
+
// @level 2
|
|
29
35
|
const parsePath = p.parse;
|
|
30
36
|
|
|
37
|
+
// @level 2
|
|
31
38
|
/**
|
|
32
|
-
* @
|
|
33
|
-
* @param {
|
|
39
|
+
* @template T
|
|
40
|
+
* @param {T[]} array
|
|
41
|
+
* @param {(item: T) => boolean} callback
|
|
34
42
|
* @returns {number}
|
|
35
43
|
*/
|
|
36
44
|
const findLastIndex = (array, callback) => {
|
|
@@ -41,6 +49,7 @@ const findLastIndex = (array, callback) => {
|
|
|
41
49
|
}, -1);
|
|
42
50
|
};
|
|
43
51
|
|
|
52
|
+
// @level 2
|
|
44
53
|
/**
|
|
45
54
|
* @param {any[]} array1
|
|
46
55
|
* @param {any[]} array2
|
|
@@ -49,6 +58,29 @@ const equal = (array1, array2) => {
|
|
|
49
58
|
return array1.every((item, index) => item === array2[index]);
|
|
50
59
|
};
|
|
51
60
|
|
|
61
|
+
// @level 2
|
|
62
|
+
/**
|
|
63
|
+
* @template T
|
|
64
|
+
* @param {T[]} array
|
|
65
|
+
* @param {number} index
|
|
66
|
+
* @returns {T}
|
|
67
|
+
*/
|
|
68
|
+
const readArray = (array, index) => {
|
|
69
|
+
return index >= 0 ? array[index] : array[array.length + index];
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// @level 1
|
|
73
|
+
/**
|
|
74
|
+
* @param {string} path
|
|
75
|
+
* @return {string[]}
|
|
76
|
+
*/
|
|
77
|
+
const reducedPaths = (path) => {
|
|
78
|
+
return toSegments(path).reduce((paths, _, index, segments) => {
|
|
79
|
+
paths.push(toPath(segments.slice(0, segments.length - index)));
|
|
80
|
+
return paths;
|
|
81
|
+
}, []);
|
|
82
|
+
};
|
|
83
|
+
|
|
52
84
|
module.exports = {
|
|
53
85
|
toRelative,
|
|
54
86
|
toSegments,
|
|
@@ -58,4 +90,6 @@ module.exports = {
|
|
|
58
90
|
parsePath,
|
|
59
91
|
findLastIndex,
|
|
60
92
|
equal,
|
|
93
|
+
readArray,
|
|
94
|
+
reducedPaths,
|
|
61
95
|
};
|
|
@@ -3,7 +3,7 @@ const { report: reportError } = require("./2 layer");
|
|
|
3
3
|
const {
|
|
4
4
|
isNodeModule,
|
|
5
5
|
findLevel: findLayerLevel,
|
|
6
|
-
|
|
6
|
+
hasBarrier: hasBarrierBetween,
|
|
7
7
|
removeAlias: removeAliasFromModuleSource,
|
|
8
8
|
} = require("./3 layer");
|
|
9
9
|
const {
|
|
@@ -12,14 +12,14 @@ const {
|
|
|
12
12
|
toPath,
|
|
13
13
|
resolvePath,
|
|
14
14
|
parsePath,
|
|
15
|
-
} = require("
|
|
15
|
+
} = require("../common");
|
|
16
16
|
|
|
17
17
|
const FINISHED = "finished";
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
*
|
|
21
21
|
* @param {string} cwd
|
|
22
|
-
* @param {
|
|
22
|
+
* @param {import("./3 layer").Options} options
|
|
23
23
|
* @returns
|
|
24
24
|
*/
|
|
25
25
|
const createRootDir = (cwd, options) => {
|
|
@@ -27,7 +27,7 @@ const createRootDir = (cwd, options) => {
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* @param {
|
|
30
|
+
* @param {import("./3 layer").Options} options
|
|
31
31
|
* @param {string} contextFileSource
|
|
32
32
|
*/
|
|
33
33
|
const parseFileSource = (options, contextFileSource) => {
|
|
@@ -50,10 +50,11 @@ const parseFileSource = (options, contextFileSource) => {
|
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* @param {string} cwd
|
|
53
|
+
* @param {string[]} excludeImports
|
|
53
54
|
* @param {string} fileDir
|
|
54
55
|
* @param {{alias: string, path: string}[]} aliases
|
|
55
56
|
*/
|
|
56
|
-
const createModulePath = (cwd, fileDir, aliases) => {
|
|
57
|
+
const createModulePath = (cwd, excludeImports, fileDir, aliases) => {
|
|
57
58
|
const removeAlias = removeAliasFromModuleSource(cwd, fileDir, aliases);
|
|
58
59
|
/**
|
|
59
60
|
* @param {string} moduleSourceWithAlias
|
|
@@ -61,12 +62,18 @@ const createModulePath = (cwd, fileDir, aliases) => {
|
|
|
61
62
|
return (moduleSourceWithAlias) => {
|
|
62
63
|
const moduleSource = removeAlias(moduleSourceWithAlias);
|
|
63
64
|
const isNodeModule = moduleSource.startsWith(".") === false;
|
|
64
|
-
|
|
65
|
+
const modulePath = isNodeModule
|
|
66
|
+
? moduleSource
|
|
67
|
+
: resolvePath(fileDir, moduleSource);
|
|
68
|
+
const isModuleExcluded = Boolean(
|
|
69
|
+
excludeImports.find((pattern) => minimatch(modulePath, pattern))
|
|
70
|
+
);
|
|
71
|
+
return { modulePath, isModuleExcluded };
|
|
65
72
|
};
|
|
66
73
|
};
|
|
67
74
|
|
|
68
75
|
/**
|
|
69
|
-
* @param {
|
|
76
|
+
* @param {import("./3 layer").Options} options
|
|
70
77
|
* @param {string} fileDor
|
|
71
78
|
* @param {string} modulePath
|
|
72
79
|
*/
|
|
@@ -82,7 +89,7 @@ const isFileIndexOfModule = (options, fileDir, filePath) => (modulePath) => {
|
|
|
82
89
|
/**
|
|
83
90
|
* Report error about using `options.useLevelNumber`
|
|
84
91
|
* @param {import('eslint').Rule.RuleContext} context
|
|
85
|
-
* @param {
|
|
92
|
+
* @param {import("./3 layer").Options} options
|
|
86
93
|
* @param {string} rootDir
|
|
87
94
|
* @param {string} filePath
|
|
88
95
|
*/
|
|
@@ -191,10 +198,10 @@ const reportHasProperLevel = (
|
|
|
191
198
|
filePath
|
|
192
199
|
) => {
|
|
193
200
|
const findLevel = findLayerLevel(structure);
|
|
194
|
-
const
|
|
201
|
+
const hasBarrier = hasBarrierBetween(structure, fileLevel);
|
|
195
202
|
|
|
196
203
|
/**
|
|
197
|
-
* @param {import('
|
|
204
|
+
* @param {import('../type').Node} node
|
|
198
205
|
* @param {string} modulePath
|
|
199
206
|
*/
|
|
200
207
|
return (node, modulePath) => {
|
|
@@ -222,7 +229,7 @@ const reportHasProperLevel = (
|
|
|
222
229
|
return FINISHED;
|
|
223
230
|
}
|
|
224
231
|
|
|
225
|
-
if (
|
|
232
|
+
if (hasBarrier(moduleLevel)) {
|
|
226
233
|
report("barrier");
|
|
227
234
|
return FINISHED;
|
|
228
235
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
const { isNodeModule } = require("./3 layer");
|
|
2
|
-
const { toRelative } = require("
|
|
2
|
+
const { toRelative } = require("../common");
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Report eslint error
|
|
6
|
-
* @param {import('
|
|
6
|
+
* @param {import('../type.js').Context} context
|
|
7
7
|
* @param {string} rootDir
|
|
8
|
-
*
|
|
8
|
+
* @param {string} filePath
|
|
9
9
|
*/
|
|
10
10
|
const report = (context, rootDir, filePath) => {
|
|
11
11
|
/**
|
|
12
|
-
* @param {import('
|
|
12
|
+
* @param {import('../type').Node} node
|
|
13
13
|
* @param {string} messageId
|
|
14
14
|
* @param {string} modulePath
|
|
15
15
|
*/
|
|
@@ -4,11 +4,36 @@ const {
|
|
|
4
4
|
joinPath,
|
|
5
5
|
resolvePath,
|
|
6
6
|
toRelative,
|
|
7
|
-
} = require("
|
|
7
|
+
} = require("../common");
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* @
|
|
10
|
+
* @typedef {{
|
|
11
|
+
* structure: Array<
|
|
12
|
+
* string | { name: string, barrier?: boolean, interface?: boolean, nodeModule?: boolean, isNodeModule?: boolean }
|
|
13
|
+
* >,
|
|
14
|
+
* root: string,
|
|
15
|
+
* aliases: Record<string, string>,
|
|
16
|
+
* exclude: string[],
|
|
17
|
+
* include: string[],
|
|
18
|
+
* useLevelNumber: boolean
|
|
19
|
+
* isIndexHighest: boolean
|
|
20
|
+
* }} Options
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {{
|
|
25
|
+
* name: string;
|
|
26
|
+
* barrier?: boolean | undefined;
|
|
27
|
+
* interface?: boolean | undefined;
|
|
28
|
+
* nodeModule?: boolean | undefined;
|
|
29
|
+
* isNodeModule?: boolean | undefined;
|
|
30
|
+
* }[]} Structure
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {Options} options
|
|
11
35
|
* @param {string} rootDir
|
|
36
|
+
* @returns {Structure}
|
|
12
37
|
*/
|
|
13
38
|
const createStructure = (options, rootDir) => {
|
|
14
39
|
return options.structure.map((layer) => {
|
|
@@ -20,7 +45,7 @@ const createStructure = (options, rootDir) => {
|
|
|
20
45
|
};
|
|
21
46
|
|
|
22
47
|
/**
|
|
23
|
-
* @param {
|
|
48
|
+
* @param {Options} options
|
|
24
49
|
*/
|
|
25
50
|
const createAliases = (options) => {
|
|
26
51
|
return Object.keys(options.aliases)
|
|
@@ -63,7 +88,7 @@ const isNodeModule = (rootDir) => {
|
|
|
63
88
|
|
|
64
89
|
/**
|
|
65
90
|
* Find the layer level for a module
|
|
66
|
-
* @param {
|
|
91
|
+
* @param {Structure} structure
|
|
67
92
|
* @returns the level of the module with `path`
|
|
68
93
|
*/
|
|
69
94
|
const findLevel = (structure) => {
|
|
@@ -83,25 +108,25 @@ const findLevel = (structure) => {
|
|
|
83
108
|
|
|
84
109
|
/**
|
|
85
110
|
* Check if there is an interface between file layer and module layer
|
|
86
|
-
* @param {
|
|
111
|
+
* @param {Structure} structure
|
|
87
112
|
* @param {number} fileLevel
|
|
88
113
|
*/
|
|
89
|
-
const
|
|
114
|
+
const hasBarrier = (structure, fileLevel) => {
|
|
90
115
|
/**
|
|
91
116
|
* @param {number} moduleLevel
|
|
92
117
|
*/
|
|
93
118
|
return (moduleLevel) => {
|
|
94
|
-
const
|
|
119
|
+
const layerBarrier = structure
|
|
95
120
|
.slice(fileLevel + 1, moduleLevel)
|
|
96
121
|
.find((layer) => layer.barrier || layer.interface);
|
|
97
|
-
return Boolean(
|
|
122
|
+
return Boolean(layerBarrier);
|
|
98
123
|
};
|
|
99
124
|
};
|
|
100
125
|
|
|
101
126
|
module.exports = {
|
|
102
127
|
isNodeModule,
|
|
103
128
|
findLevel,
|
|
104
|
-
|
|
129
|
+
hasBarrier,
|
|
105
130
|
createStructure,
|
|
106
131
|
createAliases,
|
|
107
132
|
removeAlias,
|