eslint-plugin-absolute 0.1.6 → 0.2.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/.claude/settings.local.json +5 -0
- package/dist/index.js +1226 -798
- package/package.json +12 -11
- package/src/index.ts +45 -0
- package/src/rules/explicit-object-types.ts +73 -0
- package/src/rules/{inline-style-limit.js → inline-style-limit.ts} +16 -7
- package/src/rules/localize-react-props.ts +459 -0
- package/src/rules/max-depth-extended.ts +164 -0
- package/src/rules/{max-jsx-nesting.js → max-jsx-nesting.ts} +15 -8
- package/src/rules/{min-var-length.js → min-var-length.ts} +75 -45
- package/src/rules/no-button-navigation.ts +312 -0
- package/src/rules/no-explicit-return-types.ts +87 -0
- package/src/rules/{no-inline-prop-types.js → no-inline-prop-types.ts} +22 -9
- package/src/rules/no-multi-style-objects.ts +87 -0
- package/src/rules/no-nested-jsx-return.ts +210 -0
- package/src/rules/no-or-none-component.ts +65 -0
- package/src/rules/{no-transition-cssproperties.js → no-transition-cssproperties.ts} +50 -27
- package/src/rules/no-unnecessary-div.ts +73 -0
- package/src/rules/{no-unnecessary-key.js → no-unnecessary-key.ts} +43 -26
- package/src/rules/no-useless-function.ts +58 -0
- package/src/rules/seperate-style-files.ts +81 -0
- package/src/rules/sort-exports.ts +420 -0
- package/src/rules/sort-keys-fixable.ts +621 -0
- package/src/rules/spring-naming-convention.ts +145 -0
- package/tsconfig.json +2 -1
- package/src/index.js +0 -45
- package/src/rules/explicit-object-types.js +0 -54
- package/src/rules/localize-react-props.js +0 -418
- package/src/rules/max-depth-extended.js +0 -124
- package/src/rules/no-button-navigation.js +0 -232
- package/src/rules/no-explicit-return-types.js +0 -64
- package/src/rules/no-multi-style-objects.js +0 -70
- package/src/rules/no-nested-jsx-return.js +0 -154
- package/src/rules/no-or-none-component.js +0 -50
- package/src/rules/no-unnecessary-div.js +0 -40
- package/src/rules/no-useless-function.js +0 -43
- package/src/rules/seperate-style-files.js +0 -62
- package/src/rules/sort-exports.js +0 -397
- package/src/rules/sort-keys-fixable.js +0 -459
- package/src/rules/spring-naming-convention.js +0 -111
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { TSESLint, TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
type Options = [number?];
|
|
4
|
+
type MessageIds = "tooDeep";
|
|
5
|
+
|
|
6
|
+
export const maxDepthExtended: TSESLint.RuleModule<MessageIds, Options> = {
|
|
7
|
+
meta: {
|
|
8
|
+
type: "suggestion",
|
|
9
|
+
docs: {
|
|
10
|
+
description:
|
|
11
|
+
"disallow too many nested blocks except when the block only contains an early exit (return or throw)"
|
|
12
|
+
},
|
|
13
|
+
schema: [
|
|
14
|
+
{
|
|
15
|
+
// Accepts a single number as the maximum allowed depth.
|
|
16
|
+
type: "number"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
messages: {
|
|
20
|
+
tooDeep:
|
|
21
|
+
"Blocks are nested too deeply ({{depth}}). Maximum allowed is {{maxDepth}} or an early exit."
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
defaultOptions: [1],
|
|
26
|
+
|
|
27
|
+
create(context) {
|
|
28
|
+
const option = context.options[0];
|
|
29
|
+
const maxDepth = typeof option === "number" ? option : 1;
|
|
30
|
+
const functionStack: number[] = [];
|
|
31
|
+
|
|
32
|
+
// Helper to get ancestors of a node by walking its parent chain.
|
|
33
|
+
function getAncestors(node: TSESTree.Node) {
|
|
34
|
+
const ancestors: TSESTree.Node[] = [];
|
|
35
|
+
let current: TSESTree.Node | null | undefined = node.parent;
|
|
36
|
+
while (current) {
|
|
37
|
+
ancestors.push(current);
|
|
38
|
+
current = current.parent;
|
|
39
|
+
}
|
|
40
|
+
return ancestors;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check if a block only contains a single early exit: return or throw.
|
|
44
|
+
function isEarlyExitBlock(node: TSESTree.BlockStatement) {
|
|
45
|
+
if (node.body.length !== 1) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const first = node.body[0];
|
|
49
|
+
if (!first) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return (
|
|
53
|
+
first.type === "ReturnStatement" ||
|
|
54
|
+
first.type === "ThrowStatement"
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function incrementCurrentDepth(): number | null {
|
|
59
|
+
if (functionStack.length === 0) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const index = functionStack.length - 1;
|
|
63
|
+
const currentDepth = functionStack[index];
|
|
64
|
+
if (typeof currentDepth !== "number") {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const nextDepth = currentDepth + 1;
|
|
68
|
+
functionStack[index] = nextDepth;
|
|
69
|
+
return nextDepth;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function decrementCurrentDepth(): void {
|
|
73
|
+
if (functionStack.length === 0) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const index = functionStack.length - 1;
|
|
77
|
+
const currentDepth = functionStack[index];
|
|
78
|
+
if (typeof currentDepth !== "number") {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
functionStack[index] = currentDepth - 1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Report if the current depth exceeds the allowed maximum.
|
|
85
|
+
function checkDepth(node: TSESTree.BlockStatement, depth: number) {
|
|
86
|
+
if (depth > maxDepth) {
|
|
87
|
+
context.report({
|
|
88
|
+
node,
|
|
89
|
+
messageId: "tooDeep",
|
|
90
|
+
data: { depth, maxDepth }
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
FunctionDeclaration() {
|
|
97
|
+
functionStack.push(0);
|
|
98
|
+
},
|
|
99
|
+
FunctionExpression() {
|
|
100
|
+
functionStack.push(0);
|
|
101
|
+
},
|
|
102
|
+
ArrowFunctionExpression() {
|
|
103
|
+
functionStack.push(0);
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
BlockStatement(node: TSESTree.BlockStatement) {
|
|
107
|
+
const ancestors = getAncestors(node);
|
|
108
|
+
const parent = ancestors.length > 0 ? ancestors[0] : undefined;
|
|
109
|
+
|
|
110
|
+
// Do not count if this block is the body of a function.
|
|
111
|
+
if (
|
|
112
|
+
parent &&
|
|
113
|
+
(parent.type === "FunctionDeclaration" ||
|
|
114
|
+
parent.type === "FunctionExpression" ||
|
|
115
|
+
parent.type === "ArrowFunctionExpression") &&
|
|
116
|
+
node === parent.body
|
|
117
|
+
) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Skip blocks that only have an early exit.
|
|
122
|
+
if (isEarlyExitBlock(node)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const depth = incrementCurrentDepth();
|
|
127
|
+
if (depth !== null) {
|
|
128
|
+
checkDepth(node, depth);
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
"BlockStatement:exit"(node: TSESTree.BlockStatement) {
|
|
133
|
+
const ancestors = getAncestors(node);
|
|
134
|
+
const parent = ancestors.length > 0 ? ancestors[0] : undefined;
|
|
135
|
+
|
|
136
|
+
if (
|
|
137
|
+
parent &&
|
|
138
|
+
(parent.type === "FunctionDeclaration" ||
|
|
139
|
+
parent.type === "FunctionExpression" ||
|
|
140
|
+
parent.type === "ArrowFunctionExpression") &&
|
|
141
|
+
node === parent.body
|
|
142
|
+
) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (isEarlyExitBlock(node)) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
decrementCurrentDepth();
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
"FunctionDeclaration:exit"() {
|
|
154
|
+
functionStack.pop();
|
|
155
|
+
},
|
|
156
|
+
"FunctionExpression:exit"() {
|
|
157
|
+
functionStack.pop();
|
|
158
|
+
},
|
|
159
|
+
"ArrowFunctionExpression:exit"() {
|
|
160
|
+
functionStack.pop();
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
};
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
import { TSESLint, TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
type Options = [number];
|
|
4
|
+
type MessageIds = "tooDeeplyNested";
|
|
5
|
+
|
|
6
|
+
export const maxJSXNesting: TSESLint.RuleModule<MessageIds, Options> = {
|
|
2
7
|
meta: {
|
|
3
8
|
type: "suggestion",
|
|
4
9
|
docs: {
|
|
5
10
|
description:
|
|
6
|
-
"Warn when JSX elements are nested too deeply, suggesting refactoring into a separate component."
|
|
7
|
-
recommended: false
|
|
11
|
+
"Warn when JSX elements are nested too deeply, suggesting refactoring into a separate component."
|
|
8
12
|
},
|
|
9
13
|
// The rule accepts a single numeric option (minimum 1)
|
|
10
14
|
schema: [
|
|
@@ -18,9 +22,12 @@ export default {
|
|
|
18
22
|
"JSX element is nested too deeply ({{level}} levels, allowed is {{maxAllowed}} levels). Consider refactoring into a separate component."
|
|
19
23
|
}
|
|
20
24
|
},
|
|
25
|
+
|
|
26
|
+
defaultOptions: [1],
|
|
27
|
+
|
|
21
28
|
create(context) {
|
|
22
|
-
|
|
23
|
-
const maxAllowed =
|
|
29
|
+
const option = context.options[0];
|
|
30
|
+
const maxAllowed = typeof option === "number" ? option : 1;
|
|
24
31
|
|
|
25
32
|
/**
|
|
26
33
|
* Calculates the JSX nesting level for the given node by traversing the node.parent chain.
|
|
@@ -29,9 +36,9 @@ export default {
|
|
|
29
36
|
* @param {ASTNode} node The JSX element node.
|
|
30
37
|
* @returns {number} The nesting level.
|
|
31
38
|
*/
|
|
32
|
-
function getJSXNestingLevel(node) {
|
|
39
|
+
function getJSXNestingLevel(node: TSESTree.Node): number {
|
|
33
40
|
let level = 1; // count the current node as level 1
|
|
34
|
-
let current = node.parent;
|
|
41
|
+
let current: TSESTree.Node | null | undefined = node.parent;
|
|
35
42
|
while (current) {
|
|
36
43
|
if (
|
|
37
44
|
current.type === "JSXElement" ||
|
|
@@ -45,7 +52,7 @@ export default {
|
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
return {
|
|
48
|
-
JSXElement(node) {
|
|
55
|
+
JSXElement(node: TSESTree.JSXElement) {
|
|
49
56
|
const level = getJSXNestingLevel(node);
|
|
50
57
|
if (level > maxAllowed) {
|
|
51
58
|
context.report({
|
|
@@ -1,23 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* SourceCode object) and a custom helper to get ancestors.
|
|
13
|
-
*/
|
|
14
|
-
export default {
|
|
1
|
+
import { TSESLint, TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
type MinVarLengthOption = {
|
|
4
|
+
minLength?: number;
|
|
5
|
+
allowedVars?: string[];
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
type Options = [MinVarLengthOption?];
|
|
9
|
+
type MessageIds = "variableNameTooShort";
|
|
10
|
+
|
|
11
|
+
export const minVarLength: TSESLint.RuleModule<MessageIds, Options> = {
|
|
15
12
|
meta: {
|
|
16
13
|
type: "problem",
|
|
17
14
|
docs: {
|
|
18
15
|
description:
|
|
19
|
-
"Disallow variable names shorter than the configured minimum length unless an outer variable with a longer name starting with the same characters exists. You can exempt specific variable names using the allowedVars option."
|
|
20
|
-
recommended: false
|
|
16
|
+
"Disallow variable names shorter than the configured minimum length unless an outer variable with a longer name starting with the same characters exists. You can exempt specific variable names using the allowedVars option."
|
|
21
17
|
},
|
|
22
18
|
schema: [
|
|
23
19
|
{
|
|
@@ -46,17 +42,27 @@ export default {
|
|
|
46
42
|
}
|
|
47
43
|
},
|
|
48
44
|
|
|
45
|
+
defaultOptions: [{}],
|
|
46
|
+
|
|
49
47
|
create(context) {
|
|
50
|
-
const sourceCode = context.
|
|
51
|
-
const options = context.options[0]
|
|
52
|
-
const
|
|
53
|
-
typeof options.minLength === "number"
|
|
54
|
-
|
|
48
|
+
const sourceCode = context.sourceCode;
|
|
49
|
+
const options = context.options[0];
|
|
50
|
+
const configuredMinLength =
|
|
51
|
+
options && typeof options.minLength === "number"
|
|
52
|
+
? options.minLength
|
|
53
|
+
: 1;
|
|
54
|
+
const configuredAllowedVars =
|
|
55
|
+
options && Array.isArray(options.allowedVars)
|
|
56
|
+
? options.allowedVars
|
|
57
|
+
: [];
|
|
58
|
+
|
|
59
|
+
const minLength = configuredMinLength;
|
|
60
|
+
const allowedVars = configuredAllowedVars;
|
|
55
61
|
|
|
56
62
|
// Helper: walk up the node.parent chain to get ancestors.
|
|
57
|
-
function getAncestors(node) {
|
|
58
|
-
const ancestors = [];
|
|
59
|
-
let current = node.parent;
|
|
63
|
+
function getAncestors(node: TSESTree.Node) {
|
|
64
|
+
const ancestors: TSESTree.Node[] = [];
|
|
65
|
+
let current: TSESTree.Node | null | undefined = node.parent;
|
|
60
66
|
while (current) {
|
|
61
67
|
ancestors.push(current);
|
|
62
68
|
current = current.parent;
|
|
@@ -65,21 +71,30 @@ export default {
|
|
|
65
71
|
}
|
|
66
72
|
|
|
67
73
|
// Helper: retrieve the scope for a given node using the scopeManager.
|
|
68
|
-
function getScope(node) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
function getScope(node: TSESTree.Node): TSESLint.Scope.Scope | null {
|
|
75
|
+
const scopeManager = sourceCode.scopeManager;
|
|
76
|
+
if (!scopeManager) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const acquired = scopeManager.acquire(node);
|
|
80
|
+
if (acquired) {
|
|
81
|
+
return acquired;
|
|
82
|
+
}
|
|
83
|
+
return scopeManager.globalScope ?? null;
|
|
73
84
|
}
|
|
74
85
|
|
|
75
86
|
// Fallback: get declared variable names in the nearest BlockStatement.
|
|
76
|
-
function getVariablesInNearestBlock(node) {
|
|
77
|
-
let current = node.parent;
|
|
87
|
+
function getVariablesInNearestBlock(node: TSESTree.Node) {
|
|
88
|
+
let current: TSESTree.Node | null | undefined = node.parent;
|
|
78
89
|
while (current && current.type !== "BlockStatement") {
|
|
79
90
|
current = current.parent;
|
|
80
91
|
}
|
|
81
|
-
const names = [];
|
|
82
|
-
if (
|
|
92
|
+
const names: string[] = [];
|
|
93
|
+
if (
|
|
94
|
+
current &&
|
|
95
|
+
current.type === "BlockStatement" &&
|
|
96
|
+
Array.isArray(current.body)
|
|
97
|
+
) {
|
|
83
98
|
for (const stmt of current.body) {
|
|
84
99
|
if (stmt.type === "VariableDeclaration") {
|
|
85
100
|
for (const decl of stmt.declarations) {
|
|
@@ -99,7 +114,10 @@ export default {
|
|
|
99
114
|
* @param {string[]} identifiers Array to accumulate names.
|
|
100
115
|
* @returns {string[]} Array of identifier names.
|
|
101
116
|
*/
|
|
102
|
-
function extractIdentifiersFromPattern(
|
|
117
|
+
function extractIdentifiersFromPattern(
|
|
118
|
+
pattern: TSESTree.Node | null,
|
|
119
|
+
identifiers: string[] = []
|
|
120
|
+
): string[] {
|
|
103
121
|
if (!pattern) return identifiers;
|
|
104
122
|
switch (pattern.type) {
|
|
105
123
|
case "Identifier":
|
|
@@ -122,8 +140,9 @@ export default {
|
|
|
122
140
|
break;
|
|
123
141
|
case "ArrayPattern":
|
|
124
142
|
for (const element of pattern.elements) {
|
|
125
|
-
if (element)
|
|
143
|
+
if (element) {
|
|
126
144
|
extractIdentifiersFromPattern(element, identifiers);
|
|
145
|
+
}
|
|
127
146
|
}
|
|
128
147
|
break;
|
|
129
148
|
case "AssignmentPattern":
|
|
@@ -144,10 +163,16 @@ export default {
|
|
|
144
163
|
* @param {ASTNode} node The current identifier node.
|
|
145
164
|
* @returns {boolean} True if an outer variable is found.
|
|
146
165
|
*/
|
|
147
|
-
function hasOuterCorrespondingIdentifier(
|
|
166
|
+
function hasOuterCorrespondingIdentifier(
|
|
167
|
+
shortName: string,
|
|
168
|
+
node: TSESTree.Identifier
|
|
169
|
+
) {
|
|
148
170
|
// First, try using the scope manager.
|
|
149
|
-
|
|
150
|
-
let outer =
|
|
171
|
+
const startingScope = getScope(node);
|
|
172
|
+
let outer =
|
|
173
|
+
startingScope && startingScope.upper
|
|
174
|
+
? startingScope.upper
|
|
175
|
+
: null;
|
|
151
176
|
while (outer) {
|
|
152
177
|
for (const variable of outer.variables) {
|
|
153
178
|
if (
|
|
@@ -228,9 +253,9 @@ export default {
|
|
|
228
253
|
* and no outer variable with a longer name starting with the short name is found, it reports an error.
|
|
229
254
|
* @param {ASTNode} node The Identifier node.
|
|
230
255
|
*/
|
|
231
|
-
function checkIdentifier(node) {
|
|
256
|
+
function checkIdentifier(node: TSESTree.Identifier) {
|
|
232
257
|
const name = node.name;
|
|
233
|
-
if (
|
|
258
|
+
if (name.length < minLength) {
|
|
234
259
|
// If the name is in the allowed list, skip.
|
|
235
260
|
if (allowedVars.includes(name)) {
|
|
236
261
|
return;
|
|
@@ -249,7 +274,7 @@ export default {
|
|
|
249
274
|
* Recursively checks a pattern node for identifiers.
|
|
250
275
|
* @param {ASTNode} pattern The pattern node.
|
|
251
276
|
*/
|
|
252
|
-
function checkPattern(pattern) {
|
|
277
|
+
function checkPattern(pattern: TSESTree.Node | null) {
|
|
253
278
|
if (!pattern) return;
|
|
254
279
|
switch (pattern.type) {
|
|
255
280
|
case "Identifier":
|
|
@@ -266,7 +291,9 @@ export default {
|
|
|
266
291
|
break;
|
|
267
292
|
case "ArrayPattern":
|
|
268
293
|
for (const element of pattern.elements) {
|
|
269
|
-
if (element)
|
|
294
|
+
if (element) {
|
|
295
|
+
checkPattern(element);
|
|
296
|
+
}
|
|
270
297
|
}
|
|
271
298
|
break;
|
|
272
299
|
case "AssignmentPattern":
|
|
@@ -278,19 +305,22 @@ export default {
|
|
|
278
305
|
}
|
|
279
306
|
|
|
280
307
|
return {
|
|
281
|
-
VariableDeclarator(node) {
|
|
308
|
+
VariableDeclarator(node: TSESTree.VariableDeclarator) {
|
|
282
309
|
if (node.id) {
|
|
283
310
|
checkPattern(node.id);
|
|
284
311
|
}
|
|
285
312
|
},
|
|
286
313
|
"FunctionDeclaration, FunctionExpression, ArrowFunctionExpression"(
|
|
287
|
-
node
|
|
314
|
+
node:
|
|
315
|
+
| TSESTree.FunctionDeclaration
|
|
316
|
+
| TSESTree.FunctionExpression
|
|
317
|
+
| TSESTree.ArrowFunctionExpression
|
|
288
318
|
) {
|
|
289
319
|
for (const param of node.params) {
|
|
290
320
|
checkPattern(param);
|
|
291
321
|
}
|
|
292
322
|
},
|
|
293
|
-
CatchClause(node) {
|
|
323
|
+
CatchClause(node: TSESTree.CatchClause) {
|
|
294
324
|
if (node.param) {
|
|
295
325
|
checkPattern(node.param);
|
|
296
326
|
}
|