eslint-plugin-stratified-design 0.6.1 → 0.7.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.
|
@@ -66,7 +66,7 @@ If you want to register the level of a layer by 'number,' set the option `useLev
|
|
|
66
66
|
|
|
67
67
|
The options `structure` and `useLevelNumber` can be used together.
|
|
68
68
|
|
|
69
|
-
An `index.xxx` file can be the highest level layer when the option `isIndexHighest` is set to `true`:
|
|
69
|
+
An `index.xxx` file can be the highest level layer of sibling files when the option `isIndexHighest` is set to `true`:
|
|
70
70
|
|
|
71
71
|
```json
|
|
72
72
|
"lower-level-imports": ["error", { "isIndexHighest": true }]
|
|
@@ -95,6 +95,7 @@ If a file structure is as follows:
|
|
|
95
95
|
src/
|
|
96
96
|
┣ layer1.js
|
|
97
97
|
┣ layer2/
|
|
98
|
+
┃ ┣ index.js
|
|
98
99
|
┃ ┣ file.js
|
|
99
100
|
┃ ┣ otherFile.js
|
|
100
101
|
┃ ┗ subFolder/
|
|
@@ -189,6 +190,6 @@ import { func } from "../layer3/entry";
|
|
|
189
190
|
|
|
190
191
|
```js
|
|
191
192
|
/* "lower-level-imports": ["error", { "isIndexHighest": true }] */
|
|
192
|
-
// ./src/
|
|
193
|
-
import { func } from "./
|
|
193
|
+
// ./src/layer2/index.js
|
|
194
|
+
import { func } from "./file";
|
|
194
195
|
```
|
|
@@ -36,6 +36,18 @@ function func3(...) {
|
|
|
36
36
|
}
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
```js
|
|
40
|
+
// @level 1
|
|
41
|
+
const funcA = (...) => { ... }
|
|
42
|
+
|
|
43
|
+
// @level 2
|
|
44
|
+
const funcB = (...) => {
|
|
45
|
+
...
|
|
46
|
+
funcA(...)
|
|
47
|
+
...
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
39
51
|
Examples of **correct** code for this rule:
|
|
40
52
|
|
|
41
53
|
```js
|
|
@@ -53,3 +65,15 @@ function func1(...) {
|
|
|
53
65
|
func2(...);
|
|
54
66
|
}
|
|
55
67
|
```
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
// @level 2
|
|
71
|
+
const funcA = (...) => { ... }
|
|
72
|
+
|
|
73
|
+
// @level 1
|
|
74
|
+
const funcB = (...) => {
|
|
75
|
+
...
|
|
76
|
+
funcA(...)
|
|
77
|
+
...
|
|
78
|
+
}
|
|
79
|
+
```
|
|
@@ -11,6 +11,43 @@ const path = require("path");
|
|
|
11
11
|
// Rule Definition
|
|
12
12
|
//------------------------------------------------------------------------------
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {import('eslint').Rule.Node} Node
|
|
16
|
+
* @typedef {import('eslint').AST.Token} Token
|
|
17
|
+
* @typedef {import('eslint').SourceCode} SourceCode
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {SourceCode} sourceCode
|
|
22
|
+
* @param {Node | Token} nodeOrToken
|
|
23
|
+
*
|
|
24
|
+
*/
|
|
25
|
+
const deriveLevel = (sourceCode, nodeOrToken) => {
|
|
26
|
+
const comments = sourceCode.getCommentsBefore(nodeOrToken);
|
|
27
|
+
for (const { value } of comments) {
|
|
28
|
+
const levelInStr = value.replace(/^[^]*@level\s+?([0-9]+)[^0-9]*$/, "$1");
|
|
29
|
+
const levelInNum = Number(levelInStr);
|
|
30
|
+
if (levelInStr && !Number.isNaN(levelInNum)) {
|
|
31
|
+
return levelInNum;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
*
|
|
39
|
+
* @param {Node} node
|
|
40
|
+
*/
|
|
41
|
+
const traceAncestor = (node) => {
|
|
42
|
+
let parent = node;
|
|
43
|
+
let nextParent = node.parent;
|
|
44
|
+
while (nextParent.type !== "Program") {
|
|
45
|
+
parent = parent.parent;
|
|
46
|
+
nextParent = parent.parent;
|
|
47
|
+
}
|
|
48
|
+
return parent;
|
|
49
|
+
};
|
|
50
|
+
|
|
14
51
|
/** @type {import('eslint').Rule.RuleModule} */
|
|
15
52
|
module.exports = {
|
|
16
53
|
meta: {
|
|
@@ -36,10 +73,9 @@ module.exports = {
|
|
|
36
73
|
additionalItems: false,
|
|
37
74
|
},
|
|
38
75
|
messages: {
|
|
39
|
-
"no-same-level-funcs": "
|
|
76
|
+
"no-same-level-funcs": "{{func}} is NOT lower level",
|
|
40
77
|
},
|
|
41
78
|
},
|
|
42
|
-
|
|
43
79
|
create(context) {
|
|
44
80
|
const options = {
|
|
45
81
|
exclude: ["**/*.{test,spec}.{js,ts,jsx,tsx}"],
|
|
@@ -59,31 +95,47 @@ module.exports = {
|
|
|
59
95
|
|
|
60
96
|
if (isExcludedFile) return {};
|
|
61
97
|
|
|
62
|
-
|
|
98
|
+
/**
|
|
99
|
+
* @type {({[name: string]: number | null})}
|
|
100
|
+
*/
|
|
101
|
+
const levels = {};
|
|
102
|
+
|
|
103
|
+
const sourceCode = context.getSourceCode();
|
|
63
104
|
|
|
64
105
|
return {
|
|
65
106
|
Program(node) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
107
|
+
node.body.forEach((token) => {
|
|
108
|
+
const isFuncDeclaration = token.type === "FunctionDeclaration";
|
|
109
|
+
const isVarDeclaration =
|
|
110
|
+
token.type === "VariableDeclaration" &&
|
|
111
|
+
[
|
|
112
|
+
"ArrowFunctionExpression",
|
|
113
|
+
"FunctionExpression",
|
|
114
|
+
"CallExpression",
|
|
115
|
+
].includes(token.declarations[0].init.type);
|
|
116
|
+
|
|
117
|
+
if (isFuncDeclaration || isVarDeclaration) {
|
|
118
|
+
const level = deriveLevel(sourceCode, token);
|
|
119
|
+
const name = isFuncDeclaration
|
|
120
|
+
? token.id.name
|
|
121
|
+
: token.declarations[0].id.name;
|
|
122
|
+
levels[name] = level;
|
|
75
123
|
}
|
|
76
|
-
|
|
77
|
-
}, []);
|
|
124
|
+
});
|
|
78
125
|
},
|
|
79
126
|
CallExpression(node) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
127
|
+
const calleeLevel = levels[node.callee.name];
|
|
128
|
+
if (calleeLevel === undefined) return;
|
|
129
|
+
if (calleeLevel !== null) {
|
|
130
|
+
const ancestor = traceAncestor(node);
|
|
131
|
+
const ancestorLevel = deriveLevel(sourceCode, ancestor);
|
|
132
|
+
if (ancestorLevel !== null && ancestorLevel < calleeLevel) return;
|
|
86
133
|
}
|
|
134
|
+
context.report({
|
|
135
|
+
node,
|
|
136
|
+
messageId: "no-same-level-funcs",
|
|
137
|
+
data: { func: node.callee.name },
|
|
138
|
+
});
|
|
87
139
|
},
|
|
88
140
|
};
|
|
89
141
|
},
|
package/package.json
CHANGED
|
@@ -44,6 +44,30 @@ ruleTester.run("no-same-level-funcs", rule, {
|
|
|
44
44
|
filename: "./src/foo.js",
|
|
45
45
|
options: [{ include: ["**/src/**/*.*"], exclude: ["**/foo.js"] }],
|
|
46
46
|
},
|
|
47
|
+
{
|
|
48
|
+
code: "// @level 2\nfunction func2(){};\n// @level 1\nfunction func1(){ func2(); }",
|
|
49
|
+
filename: "./src/foo.js",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
code: "// @level 2\nconst func2 = () => {};\n// @level 1\nfunction func1(){ func2(); }",
|
|
53
|
+
filename: "./src/foo.js",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
code: "// @level 2\nconst func2 = () => {};\n// @level 1\nconst func1 = () => func2();",
|
|
57
|
+
filename: "./src/foo.js",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
code: "/*@level 2*/\nconst func2 = () => {};\n/*@level 1*/\nconst func1 = () => func2();",
|
|
61
|
+
filename: "./src/foo.js",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
code: "// @level 2 something\nconst func2 = () => {};\n// @level 1 something\nconst func1 = () => func2();",
|
|
65
|
+
filename: "./src/foo.js",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
code: "/*\n@level 2\nsomething\n*/\nconst func2 = () => {};\n/*something\n@level 1\n*/\nconst func1 = () => func2();",
|
|
69
|
+
filename: "./src/foo.js",
|
|
70
|
+
},
|
|
47
71
|
],
|
|
48
72
|
invalid: [
|
|
49
73
|
{
|
|
@@ -71,5 +95,35 @@ ruleTester.run("no-same-level-funcs", rule, {
|
|
|
71
95
|
filename: "./src/foo.js",
|
|
72
96
|
errors: [{ messageId: "no-same-level-funcs", data: { func: "func1" } }],
|
|
73
97
|
},
|
|
98
|
+
{
|
|
99
|
+
code: "const fn = () => 1; const value = fn()",
|
|
100
|
+
filename: "./src/foo.js",
|
|
101
|
+
errors: [{ messageId: "no-same-level-funcs", data: { func: "fn" } }],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
code: "const fnByHof = hof(() => 1); const value = fnByHof()",
|
|
105
|
+
filename: "./src/foo.js",
|
|
106
|
+
errors: [{ messageId: "no-same-level-funcs", data: { func: "fnByHof" } }],
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
code: "const fnByHof = hof(() => 1); const fn = fnByHof(() => 1)",
|
|
110
|
+
filename: "./src/foo.js",
|
|
111
|
+
errors: [{ messageId: "no-same-level-funcs", data: { func: "fnByHof" } }],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
code: "// @level 1\nfunction func1(){};\n// @level 2\nfunction func2(){ func1(); }",
|
|
115
|
+
filename: "./src/foo.js",
|
|
116
|
+
errors: [{ messageId: "no-same-level-funcs", data: { func: "func1" } }],
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
code: "// @level 2\nfunction func2(){};\nfunction func1(){ func2(); }",
|
|
120
|
+
filename: "./src/foo.js",
|
|
121
|
+
errors: [{ messageId: "no-same-level-funcs", data: { func: "func2" } }],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
code: "function func2(){};\n// @level 1\nfunction func1(){ func2(); }",
|
|
125
|
+
filename: "./src/foo.js",
|
|
126
|
+
errors: [{ messageId: "no-same-level-funcs", data: { func: "func2" } }],
|
|
127
|
+
},
|
|
74
128
|
],
|
|
75
129
|
});
|