eslint-plugin-absolute 0.1.6 → 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 +10 -0
- package/dist/index.js +1787 -1457
- package/eslint.config.mjs +107 -0
- package/package.json +15 -12
- package/src/index.ts +45 -0
- package/src/rules/explicit-object-types.ts +75 -0
- package/src/rules/inline-style-limit.ts +88 -0
- package/src/rules/localize-react-props.ts +454 -0
- package/src/rules/max-depth-extended.ts +153 -0
- package/src/rules/{max-jsx-nesting.js → max-jsx-nesting.ts} +37 -38
- package/src/rules/min-var-length.ts +360 -0
- package/src/rules/no-button-navigation.ts +270 -0
- package/src/rules/no-explicit-return-types.ts +83 -0
- package/src/rules/no-inline-prop-types.ts +68 -0
- package/src/rules/no-multi-style-objects.ts +80 -0
- package/src/rules/no-nested-jsx-return.ts +205 -0
- package/src/rules/no-or-none-component.ts +63 -0
- package/src/rules/no-transition-cssproperties.ts +131 -0
- package/src/rules/no-unnecessary-div.ts +65 -0
- package/src/rules/no-unnecessary-key.ts +111 -0
- package/src/rules/no-useless-function.ts +56 -0
- package/src/rules/seperate-style-files.ts +79 -0
- package/src/rules/sort-exports.ts +424 -0
- package/src/rules/sort-keys-fixable.ts +647 -0
- package/src/rules/spring-naming-convention.ts +160 -0
- package/tsconfig.json +4 -1
- package/src/index.js +0 -45
- package/src/rules/explicit-object-types.js +0 -54
- package/src/rules/inline-style-limit.js +0 -77
- package/src/rules/localize-react-props.js +0 -418
- package/src/rules/max-depth-extended.js +0 -124
- package/src/rules/min-var-length.js +0 -300
- package/src/rules/no-button-navigation.js +0 -232
- package/src/rules/no-explicit-return-types.js +0 -64
- package/src/rules/no-inline-prop-types.js +0 -55
- 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-transition-cssproperties.js +0 -102
- package/src/rules/no-unnecessary-div.js +0 -40
- package/src/rules/no-unnecessary-key.js +0 -128
- 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,160 @@
|
|
|
1
|
+
import { TSESLint, TSESTree } from "@typescript-eslint/utils";
|
|
2
|
+
|
|
3
|
+
type Options = [];
|
|
4
|
+
type MessageIds =
|
|
5
|
+
| "firstMustEndWithSprings"
|
|
6
|
+
| "firstMustHaveBase"
|
|
7
|
+
| "secondMustMatch"
|
|
8
|
+
| "pluralRequired";
|
|
9
|
+
|
|
10
|
+
const SPRINGS_SUFFIX = "Springs";
|
|
11
|
+
|
|
12
|
+
const checkUseSpring = (
|
|
13
|
+
context: TSESLint.RuleContext<MessageIds, Options>,
|
|
14
|
+
firstElem: TSESTree.Identifier,
|
|
15
|
+
secondElem: TSESTree.Identifier
|
|
16
|
+
) => {
|
|
17
|
+
const firstName = firstElem.name;
|
|
18
|
+
const secondName = secondElem.name;
|
|
19
|
+
|
|
20
|
+
if (!firstName.endsWith(SPRINGS_SUFFIX)) {
|
|
21
|
+
context.report({
|
|
22
|
+
messageId: "firstMustEndWithSprings",
|
|
23
|
+
node: firstElem
|
|
24
|
+
});
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const base = firstName.slice(0, -SPRINGS_SUFFIX.length);
|
|
29
|
+
if (!base) {
|
|
30
|
+
context.report({
|
|
31
|
+
messageId: "firstMustHaveBase",
|
|
32
|
+
node: firstElem
|
|
33
|
+
});
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const expectedSecond = `${base}Api`;
|
|
38
|
+
if (secondName !== expectedSecond) {
|
|
39
|
+
context.report({
|
|
40
|
+
data: { expected: expectedSecond },
|
|
41
|
+
messageId: "secondMustMatch",
|
|
42
|
+
node: secondElem
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const checkUseSprings = (
|
|
48
|
+
context: TSESLint.RuleContext<MessageIds, Options>,
|
|
49
|
+
firstElem: TSESTree.Identifier,
|
|
50
|
+
secondElem: TSESTree.Identifier
|
|
51
|
+
) => {
|
|
52
|
+
const firstName = firstElem.name;
|
|
53
|
+
const secondName = secondElem.name;
|
|
54
|
+
|
|
55
|
+
if (!firstName.endsWith(SPRINGS_SUFFIX)) {
|
|
56
|
+
context.report({
|
|
57
|
+
messageId: "firstMustEndWithSprings",
|
|
58
|
+
node: firstElem
|
|
59
|
+
});
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const basePlural = firstName.slice(0, -SPRINGS_SUFFIX.length);
|
|
64
|
+
if (!basePlural) {
|
|
65
|
+
context.report({
|
|
66
|
+
messageId: "firstMustHaveBase",
|
|
67
|
+
node: firstElem
|
|
68
|
+
});
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!basePlural.endsWith("s")) {
|
|
73
|
+
context.report({
|
|
74
|
+
messageId: "pluralRequired",
|
|
75
|
+
node: firstElem
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const expectedSecond = `${basePlural}Api`;
|
|
81
|
+
if (secondName !== expectedSecond) {
|
|
82
|
+
context.report({
|
|
83
|
+
data: { expected: expectedSecond },
|
|
84
|
+
messageId: "secondMustMatch",
|
|
85
|
+
node: secondElem
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export const springNamingConvention: TSESLint.RuleModule<MessageIds, Options> =
|
|
91
|
+
{
|
|
92
|
+
create(context) {
|
|
93
|
+
return {
|
|
94
|
+
VariableDeclarator(node: TSESTree.VariableDeclarator) {
|
|
95
|
+
const { init } = node;
|
|
96
|
+
|
|
97
|
+
if (
|
|
98
|
+
!init ||
|
|
99
|
+
init.type !== "CallExpression" ||
|
|
100
|
+
init.callee.type !== "Identifier"
|
|
101
|
+
) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const hookName = init.callee.name;
|
|
106
|
+
if (hookName !== "useSpring" && hookName !== "useSprings") {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (node.id.type !== "ArrayPattern") {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const { elements } = node.id;
|
|
115
|
+
if (elements.length < 2) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const [firstElem, secondElem] = elements;
|
|
120
|
+
|
|
121
|
+
if (
|
|
122
|
+
!firstElem ||
|
|
123
|
+
firstElem.type !== "Identifier" ||
|
|
124
|
+
!secondElem ||
|
|
125
|
+
secondElem.type !== "Identifier"
|
|
126
|
+
) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (hookName === "useSpring") {
|
|
131
|
+
checkUseSpring(context, firstElem, secondElem);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (hookName === "useSprings") {
|
|
136
|
+
checkUseSprings(context, firstElem, secondElem);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
defaultOptions: [],
|
|
142
|
+
meta: {
|
|
143
|
+
docs: {
|
|
144
|
+
description:
|
|
145
|
+
"Enforce correct naming for useSpring and useSprings hook destructuring"
|
|
146
|
+
},
|
|
147
|
+
messages: {
|
|
148
|
+
firstMustEndWithSprings:
|
|
149
|
+
"The first variable must end with 'Springs'.",
|
|
150
|
+
firstMustHaveBase:
|
|
151
|
+
"The first variable must have a non-empty name before 'Springs'.",
|
|
152
|
+
pluralRequired:
|
|
153
|
+
"The first variable for useSprings should be plural (ending with 's') before 'Springs'.",
|
|
154
|
+
secondMustMatch:
|
|
155
|
+
"The second variable must be named '{{expected}}'."
|
|
156
|
+
},
|
|
157
|
+
schema: [],
|
|
158
|
+
type: "problem"
|
|
159
|
+
}
|
|
160
|
+
};
|
package/tsconfig.json
CHANGED
|
@@ -8,7 +8,10 @@
|
|
|
8
8
|
"moduleResolution": "bundler",
|
|
9
9
|
"skipLibCheck": true,
|
|
10
10
|
"strict": true,
|
|
11
|
-
"target": "ESNext",
|
|
11
|
+
"target": "ESNext",
|
|
12
|
+
"noUncheckedIndexedAccess": true,
|
|
13
|
+
"incremental": true,
|
|
14
|
+
"tsBuildInfoFile": ".absolutejs/tsconfig.tsbuildinfo",
|
|
12
15
|
"types": ["bun-types"]
|
|
13
16
|
}
|
|
14
17
|
}
|
package/src/index.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import noNestedJsxReturn from "./rules/no-nested-jsx-return.js";
|
|
2
|
-
import explicitObjectTypes from "./rules/explicit-object-types.js";
|
|
3
|
-
import sortKeysFixable from "./rules/sort-keys-fixable.js";
|
|
4
|
-
import noTransitionCssproperties from "./rules/no-transition-cssproperties.js";
|
|
5
|
-
import noExplicitReturnTypes from "./rules/no-explicit-return-types.js";
|
|
6
|
-
import maxJsxNesting from "./rules/max-jsx-nesting.js";
|
|
7
|
-
import seperateStyleFiles from "./rules/seperate-style-files.js";
|
|
8
|
-
import noUnnecessaryKey from "./rules/no-unnecessary-key.js";
|
|
9
|
-
import sortExports from "./rules/sort-exports.js";
|
|
10
|
-
import localizeReactProps from "./rules/localize-react-props.js";
|
|
11
|
-
import noOrNoneComponent from "./rules/no-or-none-component.js";
|
|
12
|
-
import noButtonNavigation from "./rules/no-button-navigation.js";
|
|
13
|
-
import noMultiStyleObjects from "./rules/no-multi-style-objects.js";
|
|
14
|
-
import noUselessFunction from "./rules/no-useless-function.js";
|
|
15
|
-
import minVarLength from "./rules/min-var-length.js";
|
|
16
|
-
import maxDepthExtended from "./rules/max-depth-extended.js";
|
|
17
|
-
import springNamingConvention from "./rules/spring-naming-convention.js";
|
|
18
|
-
import inlineStyleLimit from "./rules/inline-style-limit.js";
|
|
19
|
-
import noInlinePropTypes from "./rules/no-inline-prop-types.js";
|
|
20
|
-
import noUnnecessaryDiv from "./rules/no-unnecessary-div.js";
|
|
21
|
-
|
|
22
|
-
export default {
|
|
23
|
-
rules: {
|
|
24
|
-
"no-nested-jsx-return": noNestedJsxReturn,
|
|
25
|
-
"explicit-object-types": explicitObjectTypes,
|
|
26
|
-
"sort-keys-fixable": sortKeysFixable,
|
|
27
|
-
"no-transition-cssproperties": noTransitionCssproperties,
|
|
28
|
-
"no-explicit-return-type": noExplicitReturnTypes,
|
|
29
|
-
"max-jsxnesting": maxJsxNesting,
|
|
30
|
-
"seperate-style-files": seperateStyleFiles,
|
|
31
|
-
"no-unnecessary-key": noUnnecessaryKey,
|
|
32
|
-
"sort-exports": sortExports,
|
|
33
|
-
"localize-react-props": localizeReactProps,
|
|
34
|
-
"no-or-none-component": noOrNoneComponent,
|
|
35
|
-
"no-button-navigation": noButtonNavigation,
|
|
36
|
-
"no-multi-style-objects": noMultiStyleObjects,
|
|
37
|
-
"no-useless-function": noUselessFunction,
|
|
38
|
-
"min-var-length": minVarLength,
|
|
39
|
-
"max-depth-extended": maxDepthExtended,
|
|
40
|
-
"spring-naming-convention": springNamingConvention,
|
|
41
|
-
"inline-style-limit": inlineStyleLimit,
|
|
42
|
-
"no-inline-prop-types": noInlinePropTypes,
|
|
43
|
-
"no-unnecessary-div": noUnnecessaryDiv
|
|
44
|
-
}
|
|
45
|
-
};
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
export default {
|
|
2
|
-
meta: {
|
|
3
|
-
type: "problem",
|
|
4
|
-
docs: {
|
|
5
|
-
description:
|
|
6
|
-
"Require explicit type annotations for object literals and arrays of object literals",
|
|
7
|
-
recommended: false
|
|
8
|
-
},
|
|
9
|
-
schema: []
|
|
10
|
-
},
|
|
11
|
-
create(context) {
|
|
12
|
-
/**
|
|
13
|
-
* Returns true if the node is an object literal.
|
|
14
|
-
* @param {ASTNode} node The AST node to check.
|
|
15
|
-
*/
|
|
16
|
-
function isObjectLiteral(node) {
|
|
17
|
-
return node && node.type === "ObjectExpression";
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return {
|
|
21
|
-
VariableDeclarator(node) {
|
|
22
|
-
// Skip if there's no initializer.
|
|
23
|
-
if (!node.init) return;
|
|
24
|
-
|
|
25
|
-
// Skip if the variable already has a type annotation.
|
|
26
|
-
if (node.id && node.id.typeAnnotation) return;
|
|
27
|
-
|
|
28
|
-
// Check if the initializer is an object literal.
|
|
29
|
-
if (isObjectLiteral(node.init)) {
|
|
30
|
-
context.report({
|
|
31
|
-
node: node.id,
|
|
32
|
-
message:
|
|
33
|
-
"Object literal must have an explicit type annotation."
|
|
34
|
-
});
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Check if the initializer is an array literal containing any object literals.
|
|
39
|
-
if (node.init.type === "ArrayExpression") {
|
|
40
|
-
const hasObjectLiteral = node.init.elements.some(
|
|
41
|
-
(element) => element && isObjectLiteral(element)
|
|
42
|
-
);
|
|
43
|
-
if (hasObjectLiteral) {
|
|
44
|
-
context.report({
|
|
45
|
-
node: node.id,
|
|
46
|
-
message:
|
|
47
|
-
"Array of object literals must have an explicit type annotation."
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
};
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
export default {
|
|
2
|
-
meta: {
|
|
3
|
-
type: "suggestion",
|
|
4
|
-
docs: {
|
|
5
|
-
description:
|
|
6
|
-
"Disallow inline style objects with too many keys and encourage extracting them",
|
|
7
|
-
category: "Best Practices",
|
|
8
|
-
recommended: false
|
|
9
|
-
},
|
|
10
|
-
schema: [
|
|
11
|
-
{
|
|
12
|
-
anyOf: [
|
|
13
|
-
{
|
|
14
|
-
type: "number"
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
type: "object",
|
|
18
|
-
properties: {
|
|
19
|
-
maxKeys: {
|
|
20
|
-
type: "number",
|
|
21
|
-
description:
|
|
22
|
-
"Maximum number of keys allowed in an inline style object before it must be extracted."
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
additionalProperties: false
|
|
26
|
-
}
|
|
27
|
-
]
|
|
28
|
-
}
|
|
29
|
-
],
|
|
30
|
-
messages: {
|
|
31
|
-
extractStyle:
|
|
32
|
-
"Inline style objects should be extracted into a separate object or file when containing more than {{max}} keys."
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
create(context) {
|
|
37
|
-
const option = context.options[0];
|
|
38
|
-
// If a number is passed directly, use it as maxKeys; otherwise, extract maxKeys from the object (default to 3)
|
|
39
|
-
const maxKeys =
|
|
40
|
-
typeof option === "number"
|
|
41
|
-
? option
|
|
42
|
-
: (option && option.maxKeys) || 3;
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
JSXAttribute(node) {
|
|
46
|
-
// Check if the attribute name is 'style'
|
|
47
|
-
if (node.name.name !== "style") {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Ensure the value is a JSX expression container with an object literal
|
|
52
|
-
if (
|
|
53
|
-
node.value &&
|
|
54
|
-
node.value.type === "JSXExpressionContainer" &&
|
|
55
|
-
node.value.expression &&
|
|
56
|
-
node.value.expression.type === "ObjectExpression"
|
|
57
|
-
) {
|
|
58
|
-
const styleObject = node.value.expression;
|
|
59
|
-
|
|
60
|
-
// Count only "Property" nodes (ignoring spread elements or others)
|
|
61
|
-
const keyCount = styleObject.properties.filter(
|
|
62
|
-
(prop) => prop.type === "Property"
|
|
63
|
-
).length;
|
|
64
|
-
|
|
65
|
-
// Report only if the number of keys exceeds the allowed maximum
|
|
66
|
-
if (keyCount > maxKeys) {
|
|
67
|
-
context.report({
|
|
68
|
-
node,
|
|
69
|
-
messageId: "extractStyle",
|
|
70
|
-
data: { max: maxKeys }
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
};
|