eslint-plugin-absolute 0.2.0 → 0.2.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/.absolutejs/eslint.cache.json +49 -0
- package/.absolutejs/prettier.cache.json +49 -0
- package/.absolutejs/tsconfig.tsbuildinfo +1 -0
- package/.claude/settings.local.json +8 -3
- package/dist/index.js +1321 -1419
- package/eslint.config.mjs +107 -0
- package/package.json +10 -8
- package/src/index.ts +15 -15
- package/src/rules/explicit-object-types.ts +42 -40
- package/src/rules/inline-style-limit.ts +56 -54
- package/src/rules/localize-react-props.ts +261 -266
- package/src/rules/max-depth-extended.ts +55 -66
- package/src/rules/max-jsx-nesting.ts +28 -36
- package/src/rules/min-var-length.ts +238 -208
- package/src/rules/no-button-navigation.ts +114 -156
- package/src/rules/no-explicit-return-types.ts +32 -36
- package/src/rules/no-inline-prop-types.ts +30 -30
- package/src/rules/no-multi-style-objects.ts +35 -42
- package/src/rules/no-nested-jsx-return.ts +100 -105
- package/src/rules/no-or-none-component.ts +17 -19
- package/src/rules/no-transition-cssproperties.ts +76 -70
- package/src/rules/no-unnecessary-div.ts +26 -34
- package/src/rules/no-unnecessary-key.ts +41 -75
- package/src/rules/no-useless-function.ts +18 -20
- package/src/rules/seperate-style-files.ts +19 -21
- package/src/rules/sort-exports.ts +252 -248
- package/src/rules/sort-keys-fixable.ts +354 -328
- package/src/rules/spring-naming-convention.ts +104 -89
- package/tsconfig.json +2 -0
|
@@ -4,11 +4,6 @@
|
|
|
4
4
|
* on a JSX element when it is not part of a mapping, except when the element is
|
|
5
5
|
* returned from a helper render function.
|
|
6
6
|
*
|
|
7
|
-
* The rule walks up the ancestors of the JSX element to check if one of them
|
|
8
|
-
* is a CallExpression where the callee is a MemberExpression with the property "map".
|
|
9
|
-
* If not—and if the JSX element is not directly returned from a helper function—
|
|
10
|
-
* then a key prop is considered unnecessary and an error is reported.
|
|
11
|
-
*
|
|
12
7
|
* Note: This rule does not auto-fix.
|
|
13
8
|
*/
|
|
14
9
|
|
|
@@ -17,25 +12,25 @@ import { TSESLint, TSESTree } from "@typescript-eslint/utils";
|
|
|
17
12
|
type Options = [];
|
|
18
13
|
type MessageIds = "unnecessaryKey";
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
type
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
schema: [],
|
|
28
|
-
messages: {
|
|
29
|
-
unnecessaryKey:
|
|
30
|
-
"The key prop should only be used on elements that are directly rendered as part of an array mapping."
|
|
31
|
-
}
|
|
32
|
-
},
|
|
15
|
+
const isMapCallExpression = (node: TSESTree.Node) => {
|
|
16
|
+
if (
|
|
17
|
+
node.type !== "CallExpression" ||
|
|
18
|
+
node.callee.type !== "MemberExpression"
|
|
19
|
+
) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
33
22
|
|
|
34
|
-
|
|
23
|
+
const { property } = node.callee;
|
|
24
|
+
return (
|
|
25
|
+
(property.type === "Identifier" && property.name === "map") ||
|
|
26
|
+
(property.type === "Literal" && property.value === "map")
|
|
27
|
+
);
|
|
28
|
+
};
|
|
35
29
|
|
|
30
|
+
export const noUnnecessaryKey: TSESLint.RuleModule<MessageIds, Options> = {
|
|
36
31
|
create(context) {
|
|
37
32
|
// Polyfill for context.getAncestors if it's not available.
|
|
38
|
-
|
|
33
|
+
const getAncestors = (node: TSESTree.Node) => {
|
|
39
34
|
const ancestors: TSESTree.Node[] = [];
|
|
40
35
|
let current: TSESTree.Node | null | undefined = node.parent;
|
|
41
36
|
while (current) {
|
|
@@ -43,64 +38,27 @@ export const noUnnecessaryKey: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
43
38
|
current = current.parent;
|
|
44
39
|
}
|
|
45
40
|
return ancestors;
|
|
46
|
-
}
|
|
41
|
+
};
|
|
47
42
|
|
|
48
43
|
/**
|
|
49
44
|
* Checks if any of the ancestors is a CallExpression
|
|
50
|
-
* representing an array mapping
|
|
51
|
-
* whose property is an identifier or literal named "map").
|
|
52
|
-
*
|
|
53
|
-
* @param {ASTNode[]} ancestors - The array of ancestor nodes.
|
|
54
|
-
* @returns {boolean} True if a mapping is detected; otherwise, false.
|
|
45
|
+
* representing an array mapping.
|
|
55
46
|
*/
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (
|
|
59
|
-
node.type === "CallExpression" &&
|
|
60
|
-
node.callee.type === "MemberExpression"
|
|
61
|
-
) {
|
|
62
|
-
const property = node.callee.property;
|
|
63
|
-
if (
|
|
64
|
-
property.type === "Identifier" &&
|
|
65
|
-
property.name === "map"
|
|
66
|
-
) {
|
|
67
|
-
return true;
|
|
68
|
-
}
|
|
69
|
-
if (
|
|
70
|
-
property.type === "Literal" &&
|
|
71
|
-
property.value === "map"
|
|
72
|
-
) {
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
47
|
+
const isInsideMapCall = (ancestors: TSESTree.Node[]) =>
|
|
48
|
+
ancestors.some(isMapCallExpression);
|
|
79
49
|
|
|
80
50
|
/**
|
|
81
51
|
* Checks whether the JSX element is being returned from a helper render
|
|
82
|
-
* function.
|
|
83
|
-
* is eventually invoked from a mapping.
|
|
84
|
-
*
|
|
85
|
-
* @param {ASTNode[]} ancestors - The array of ancestor nodes.
|
|
86
|
-
* @returns {boolean} True if the element is inside a helper render function.
|
|
52
|
+
* function.
|
|
87
53
|
*/
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
if (node.type === "ReturnStatement") {
|
|
91
|
-
return true;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
54
|
+
const isReturnedFromFunction = (ancestors: TSESTree.Node[]) =>
|
|
55
|
+
ancestors.some((ancestor) => ancestor.type === "ReturnStatement");
|
|
96
56
|
|
|
97
57
|
/**
|
|
98
58
|
* Reports a JSX element if it has a key prop and is not rendered as part
|
|
99
59
|
* of an inline mapping (and not simply returned from a render helper function).
|
|
100
|
-
*
|
|
101
|
-
* @param {ASTNode} node - The JSXOpeningElement node.
|
|
102
60
|
*/
|
|
103
|
-
|
|
61
|
+
const checkJSXOpeningElement = (node: TSESTree.JSXOpeningElement) => {
|
|
104
62
|
// Find a key attribute.
|
|
105
63
|
const keyAttribute = node.attributes.find(
|
|
106
64
|
(attr) =>
|
|
@@ -113,13 +71,8 @@ export const noUnnecessaryKey: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
113
71
|
return;
|
|
114
72
|
}
|
|
115
73
|
|
|
116
|
-
// Retrieve ancestors
|
|
117
|
-
|
|
118
|
-
if (typeof context.getAncestors === "function") {
|
|
119
|
-
ancestors = context.getAncestors();
|
|
120
|
-
} else {
|
|
121
|
-
ancestors = getAncestors(node);
|
|
122
|
-
}
|
|
74
|
+
// Retrieve ancestors.
|
|
75
|
+
const ancestors = getAncestors(node);
|
|
123
76
|
|
|
124
77
|
// If the element is (directly or indirectly) part of a map call, allow it.
|
|
125
78
|
if (isInsideMapCall(ancestors)) {
|
|
@@ -133,13 +86,26 @@ export const noUnnecessaryKey: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
133
86
|
|
|
134
87
|
// Otherwise, report the key prop as unnecessary.
|
|
135
88
|
context.report({
|
|
136
|
-
|
|
137
|
-
|
|
89
|
+
messageId: "unnecessaryKey",
|
|
90
|
+
node: keyAttribute
|
|
138
91
|
});
|
|
139
|
-
}
|
|
92
|
+
};
|
|
140
93
|
|
|
141
94
|
return {
|
|
142
95
|
JSXOpeningElement: checkJSXOpeningElement
|
|
143
96
|
};
|
|
97
|
+
},
|
|
98
|
+
defaultOptions: [],
|
|
99
|
+
meta: {
|
|
100
|
+
docs: {
|
|
101
|
+
description:
|
|
102
|
+
"enforce that the key prop is only used on components rendered as part of a mapping"
|
|
103
|
+
},
|
|
104
|
+
messages: {
|
|
105
|
+
unnecessaryKey:
|
|
106
|
+
"The key prop should only be used on elements that are directly rendered as part of an array mapping."
|
|
107
|
+
},
|
|
108
|
+
schema: [],
|
|
109
|
+
type: "problem"
|
|
144
110
|
}
|
|
145
111
|
};
|
|
@@ -4,24 +4,9 @@ type Options = [];
|
|
|
4
4
|
type MessageIds = "uselessFunction";
|
|
5
5
|
|
|
6
6
|
export const noUselessFunction: TSESLint.RuleModule<MessageIds, Options> = {
|
|
7
|
-
meta: {
|
|
8
|
-
type: "suggestion",
|
|
9
|
-
docs: {
|
|
10
|
-
description:
|
|
11
|
-
"Disallow functions that have no parameters and just return an object literal; consider exporting the object directly, unless the function is used as a callback (e.g., in react-spring)."
|
|
12
|
-
},
|
|
13
|
-
schema: [],
|
|
14
|
-
messages: {
|
|
15
|
-
uselessFunction:
|
|
16
|
-
"This function has no parameters and simply returns an object. Consider exporting the object directly instead of wrapping it in a function."
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
|
|
20
|
-
defaultOptions: [],
|
|
21
|
-
|
|
22
7
|
create(context) {
|
|
23
|
-
|
|
24
|
-
const parent = node
|
|
8
|
+
const isCallbackFunction = (node: TSESTree.ArrowFunctionExpression) => {
|
|
9
|
+
const { parent } = node;
|
|
25
10
|
if (!parent || parent.type !== "CallExpression") {
|
|
26
11
|
return false;
|
|
27
12
|
}
|
|
@@ -33,7 +18,7 @@ export const noUselessFunction: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
33
18
|
}
|
|
34
19
|
|
|
35
20
|
return false;
|
|
36
|
-
}
|
|
21
|
+
};
|
|
37
22
|
|
|
38
23
|
return {
|
|
39
24
|
ArrowFunctionExpression(node: TSESTree.ArrowFunctionExpression) {
|
|
@@ -48,11 +33,24 @@ export const noUselessFunction: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
48
33
|
return;
|
|
49
34
|
}
|
|
50
35
|
context.report({
|
|
51
|
-
|
|
52
|
-
|
|
36
|
+
messageId: "uselessFunction",
|
|
37
|
+
node
|
|
53
38
|
});
|
|
54
39
|
}
|
|
55
40
|
}
|
|
56
41
|
};
|
|
42
|
+
},
|
|
43
|
+
defaultOptions: [],
|
|
44
|
+
meta: {
|
|
45
|
+
docs: {
|
|
46
|
+
description:
|
|
47
|
+
"Disallow functions that have no parameters and just return an object literal; consider exporting the object directly, unless the function is used as a callback (e.g., in react-spring)."
|
|
48
|
+
},
|
|
49
|
+
messages: {
|
|
50
|
+
uselessFunction:
|
|
51
|
+
"This function has no parameters and simply returns an object. Consider exporting the object directly instead of wrapping it in a function."
|
|
52
|
+
},
|
|
53
|
+
schema: [],
|
|
54
|
+
type: "suggestion"
|
|
57
55
|
}
|
|
58
56
|
};
|
|
@@ -4,25 +4,9 @@ type Options = [];
|
|
|
4
4
|
type MessageIds = "moveToFile";
|
|
5
5
|
|
|
6
6
|
export const seperateStyleFiles: TSESLint.RuleModule<MessageIds, Options> = {
|
|
7
|
-
meta: {
|
|
8
|
-
type: "suggestion",
|
|
9
|
-
docs: {
|
|
10
|
-
description:
|
|
11
|
-
"Warn when a component file (.jsx or .tsx) contains a style object typed as CSSProperties. " +
|
|
12
|
-
"Style objects should be moved to their own file under the style folder."
|
|
13
|
-
},
|
|
14
|
-
schema: [],
|
|
15
|
-
messages: {
|
|
16
|
-
moveToFile:
|
|
17
|
-
'Style object "{{name}}" is typed as {{typeName}}. Move it to its own file under the style folder.'
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
defaultOptions: [],
|
|
22
|
-
|
|
23
7
|
create(context) {
|
|
24
8
|
// Only run this rule on .tsx or .jsx files.
|
|
25
|
-
const filename = context
|
|
9
|
+
const { filename } = context;
|
|
26
10
|
if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) {
|
|
27
11
|
return {};
|
|
28
12
|
}
|
|
@@ -60,22 +44,36 @@ export const seperateStyleFiles: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
60
44
|
}
|
|
61
45
|
// When typeName is a TSQualifiedName, e.g., React.CSSProperties.
|
|
62
46
|
else if (typeNameNode.type === "TSQualifiedName") {
|
|
63
|
-
const right = typeNameNode
|
|
47
|
+
const { right } = typeNameNode;
|
|
64
48
|
typeName = right.name;
|
|
65
49
|
}
|
|
66
50
|
|
|
67
51
|
// Report if the type name is CSSProperties.
|
|
68
52
|
if (typeName === "CSSProperties") {
|
|
69
53
|
context.report({
|
|
70
|
-
node,
|
|
71
|
-
messageId: "moveToFile",
|
|
72
54
|
data: {
|
|
73
55
|
name: identifier.name,
|
|
74
56
|
typeName
|
|
75
|
-
}
|
|
57
|
+
},
|
|
58
|
+
messageId: "moveToFile",
|
|
59
|
+
node
|
|
76
60
|
});
|
|
77
61
|
}
|
|
78
62
|
}
|
|
79
63
|
};
|
|
64
|
+
},
|
|
65
|
+
defaultOptions: [],
|
|
66
|
+
meta: {
|
|
67
|
+
docs: {
|
|
68
|
+
description:
|
|
69
|
+
"Warn when a component file (.jsx or .tsx) contains a style object typed as CSSProperties. " +
|
|
70
|
+
"Style objects should be moved to their own file under the style folder."
|
|
71
|
+
},
|
|
72
|
+
messages: {
|
|
73
|
+
moveToFile:
|
|
74
|
+
'Style object "{{name}}" is typed as {{typeName}}. Move it to its own file under the style folder.'
|
|
75
|
+
},
|
|
76
|
+
schema: [],
|
|
77
|
+
type: "suggestion"
|
|
80
78
|
}
|
|
81
79
|
};
|