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
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import pluginJs from "@eslint/js";
|
|
2
|
+
import tsParser from "@typescript-eslint/parser";
|
|
3
|
+
import { defineConfig } from "eslint/config";
|
|
4
|
+
import tseslint from "typescript-eslint";
|
|
5
|
+
|
|
6
|
+
// Import ourselves from dist — this IS the plugin (run `bun run build` first)
|
|
7
|
+
import absolutePlugin from "./dist/index.js";
|
|
8
|
+
|
|
9
|
+
export default defineConfig([
|
|
10
|
+
{ ignores: ["dist/**", "node_modules/**", ".absolutejs/**"] },
|
|
11
|
+
|
|
12
|
+
pluginJs.configs.recommended,
|
|
13
|
+
|
|
14
|
+
...tseslint.configs.recommended,
|
|
15
|
+
|
|
16
|
+
{
|
|
17
|
+
files: ["**/*.ts"],
|
|
18
|
+
languageOptions: {
|
|
19
|
+
parser: tsParser,
|
|
20
|
+
parserOptions: {
|
|
21
|
+
project: "./tsconfig.json"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
rules: {
|
|
25
|
+
"@typescript-eslint/consistent-type-assertions": [
|
|
26
|
+
"error",
|
|
27
|
+
{ assertionStyle: "never" }
|
|
28
|
+
],
|
|
29
|
+
"@typescript-eslint/no-unnecessary-type-assertion": "error"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
files: ["**/*.{ts,js,mjs}"],
|
|
34
|
+
plugins: {
|
|
35
|
+
absolute: absolutePlugin
|
|
36
|
+
},
|
|
37
|
+
rules: {
|
|
38
|
+
"absolute/explicit-object-types": "error",
|
|
39
|
+
"absolute/max-depth-extended": ["error", 1],
|
|
40
|
+
"absolute/min-var-length": [
|
|
41
|
+
"error",
|
|
42
|
+
{ allowedVars: ["_", "id"], minLength: 3 }
|
|
43
|
+
],
|
|
44
|
+
"absolute/no-explicit-return-type": "error",
|
|
45
|
+
"absolute/no-useless-function": "error",
|
|
46
|
+
"absolute/sort-exports": [
|
|
47
|
+
"error",
|
|
48
|
+
{ caseSensitive: true, natural: true, order: "asc" }
|
|
49
|
+
],
|
|
50
|
+
"absolute/sort-keys-fixable": [
|
|
51
|
+
"error",
|
|
52
|
+
{ caseSensitive: true, natural: true, order: "asc" }
|
|
53
|
+
],
|
|
54
|
+
"arrow-body-style": ["error", "as-needed"],
|
|
55
|
+
"consistent-return": "error",
|
|
56
|
+
eqeqeq: "error",
|
|
57
|
+
"func-style": [
|
|
58
|
+
"error",
|
|
59
|
+
"expression",
|
|
60
|
+
{ allowArrowFunctions: true }
|
|
61
|
+
],
|
|
62
|
+
"no-await-in-loop": "error",
|
|
63
|
+
"no-duplicate-imports": "error",
|
|
64
|
+
"no-else-return": "error",
|
|
65
|
+
"no-empty-function": "error",
|
|
66
|
+
"no-floating-decimal": "error",
|
|
67
|
+
"no-implicit-coercion": "error",
|
|
68
|
+
"no-implicit-globals": "error",
|
|
69
|
+
"no-loop-func": "error",
|
|
70
|
+
"no-magic-numbers": [
|
|
71
|
+
"warn",
|
|
72
|
+
{ detectObjects: false, enforceConst: true, ignore: [0, 1, 2] }
|
|
73
|
+
],
|
|
74
|
+
"no-nested-ternary": "error",
|
|
75
|
+
"no-new-wrappers": "error",
|
|
76
|
+
"no-param-reassign": "error",
|
|
77
|
+
"no-return-await": "error",
|
|
78
|
+
"no-shadow": "error",
|
|
79
|
+
"no-unneeded-ternary": "error",
|
|
80
|
+
"no-useless-assignment": "error",
|
|
81
|
+
"no-useless-concat": "error",
|
|
82
|
+
"no-useless-return": "error",
|
|
83
|
+
"no-var": "error",
|
|
84
|
+
"prefer-arrow-callback": "error",
|
|
85
|
+
"prefer-const": "error",
|
|
86
|
+
"prefer-destructuring": [
|
|
87
|
+
"error",
|
|
88
|
+
{ array: true, object: true },
|
|
89
|
+
{ enforceForRenamedProperties: false }
|
|
90
|
+
],
|
|
91
|
+
"prefer-template": "error"
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
files: ["eslint.config.mjs"],
|
|
96
|
+
rules: {
|
|
97
|
+
"@typescript-eslint/no-unused-expressions": "off"
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
files: ["**/*.test.ts"],
|
|
102
|
+
rules: {
|
|
103
|
+
"absolute/min-var-length": "off",
|
|
104
|
+
"no-magic-numbers": "off"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-absolute",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "ESLint plugin for AbsoluteJS",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -13,20 +13,22 @@
|
|
|
13
13
|
"scripts": {
|
|
14
14
|
"test": "bun test tests/",
|
|
15
15
|
"build": "rm -rf dist && bun build src/index.ts --outdir dist --splitting --target=bun --external eslint --external @typescript-eslint/utils",
|
|
16
|
-
"format": "prettier --write
|
|
16
|
+
"format": "absolutejs prettier --write",
|
|
17
17
|
"release": "bun run format && bun run build && bun publish",
|
|
18
18
|
"prune": "ts-prune --error",
|
|
19
|
+
"lint": "bun run build && bun run absolutejs eslint",
|
|
19
20
|
"typecheck": "bun run tsc --noEmit"
|
|
20
21
|
},
|
|
21
22
|
"devDependencies": {
|
|
22
|
-
"@types/bun": "
|
|
23
|
-
"@
|
|
24
|
-
"@
|
|
25
|
-
"@typescript-eslint/
|
|
23
|
+
"@types/bun": "1.3.3",
|
|
24
|
+
"@absolutejs/absolute": "0.16.10",
|
|
25
|
+
"@types/react": "19.2.14",
|
|
26
|
+
"@typescript-eslint/rule-tester": "8.56.0",
|
|
27
|
+
"@typescript-eslint/utils": "8.56.0",
|
|
26
28
|
"eslint": "10",
|
|
27
|
-
"prettier": "
|
|
29
|
+
"prettier": "3.8.1",
|
|
28
30
|
"ts-prune": "0.10.3",
|
|
29
31
|
"typescript": "5.9.3",
|
|
30
|
-
"typescript-eslint": "
|
|
32
|
+
"typescript-eslint": "8.56.0"
|
|
31
33
|
}
|
|
32
34
|
}
|
package/src/index.ts
CHANGED
|
@@ -21,25 +21,25 @@ import { noUnnecessaryDiv } from "./rules/no-unnecessary-div";
|
|
|
21
21
|
|
|
22
22
|
export default {
|
|
23
23
|
rules: {
|
|
24
|
-
"no-nested-jsx-return": noNestedJSXReturn,
|
|
25
24
|
"explicit-object-types": explicitObjectTypes,
|
|
26
|
-
"
|
|
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,
|
|
25
|
+
"inline-style-limit": inlineStyleLimit,
|
|
33
26
|
"localize-react-props": localizeReactProps,
|
|
34
|
-
"
|
|
27
|
+
"max-depth-extended": maxDepthExtended,
|
|
28
|
+
"max-jsxnesting": maxJSXNesting,
|
|
29
|
+
"min-var-length": minVarLength,
|
|
35
30
|
"no-button-navigation": noButtonNavigation,
|
|
31
|
+
"no-explicit-return-type": noExplicitReturnTypes,
|
|
32
|
+
"no-inline-prop-types": noInlinePropTypes,
|
|
36
33
|
"no-multi-style-objects": noMultiStyleObjects,
|
|
34
|
+
"no-nested-jsx-return": noNestedJSXReturn,
|
|
35
|
+
"no-or-none-component": noOrNoneComponent,
|
|
36
|
+
"no-transition-cssproperties": noTransitionCSSProperties,
|
|
37
|
+
"no-unnecessary-div": noUnnecessaryDiv,
|
|
38
|
+
"no-unnecessary-key": noUnnecessaryKey,
|
|
37
39
|
"no-useless-function": noUselessFunction,
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"no-inline-prop-types": noInlinePropTypes,
|
|
43
|
-
"no-unnecessary-div": noUnnecessaryDiv
|
|
40
|
+
"seperate-style-files": seperateStyleFiles,
|
|
41
|
+
"sort-exports": sortExports,
|
|
42
|
+
"sort-keys-fixable": sortKeysFixable,
|
|
43
|
+
"spring-naming-convention": springNamingConvention
|
|
44
44
|
}
|
|
45
45
|
};
|
|
@@ -4,31 +4,17 @@ type Options = [];
|
|
|
4
4
|
type MessageIds = "objectLiteralNeedsType" | "arrayOfObjectLiteralsNeedsType";
|
|
5
5
|
|
|
6
6
|
export const explicitObjectTypes: TSESLint.RuleModule<MessageIds, Options> = {
|
|
7
|
-
meta: {
|
|
8
|
-
type: "problem",
|
|
9
|
-
docs: {
|
|
10
|
-
description:
|
|
11
|
-
"Require explicit type annotations for object literals and arrays of object literals"
|
|
12
|
-
},
|
|
13
|
-
schema: [],
|
|
14
|
-
messages: {
|
|
15
|
-
objectLiteralNeedsType:
|
|
16
|
-
"Object literal must have an explicit type annotation.",
|
|
17
|
-
arrayOfObjectLiteralsNeedsType:
|
|
18
|
-
"Array of object literals must have an explicit type annotation."
|
|
19
|
-
}
|
|
20
|
-
},
|
|
21
|
-
defaultOptions: [],
|
|
22
7
|
create(context) {
|
|
23
8
|
/**
|
|
24
9
|
* Returns true if the node is an object literal.
|
|
25
10
|
* @param {ASTNode} node The AST node to check.
|
|
26
11
|
*/
|
|
27
|
-
|
|
12
|
+
const isObjectLiteral = (
|
|
28
13
|
node: TSESTree.Node | null | undefined
|
|
29
|
-
): node is TSESTree.ObjectExpression
|
|
30
|
-
|
|
31
|
-
|
|
14
|
+
): node is TSESTree.ObjectExpression =>
|
|
15
|
+
node !== null &&
|
|
16
|
+
node !== undefined &&
|
|
17
|
+
node.type === "ObjectExpression";
|
|
32
18
|
|
|
33
19
|
return {
|
|
34
20
|
VariableDeclarator(node: TSESTree.VariableDeclarator) {
|
|
@@ -40,34 +26,50 @@ export const explicitObjectTypes: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
40
26
|
return;
|
|
41
27
|
|
|
42
28
|
// Check if the initializer is an object literal.
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
29
|
+
if (
|
|
30
|
+
isObjectLiteral(node.init) &&
|
|
31
|
+
node.id.type === "Identifier"
|
|
32
|
+
) {
|
|
33
|
+
context.report({
|
|
34
|
+
messageId: "objectLiteralNeedsType",
|
|
35
|
+
node: node.id
|
|
36
|
+
});
|
|
50
37
|
return;
|
|
51
38
|
}
|
|
52
39
|
|
|
53
40
|
// Check if the initializer is an array literal containing any object literals.
|
|
54
|
-
if (node.init.type
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (!element || element.type === "SpreadElement")
|
|
58
|
-
return false;
|
|
59
|
-
return isObjectLiteral(element);
|
|
60
|
-
}
|
|
61
|
-
);
|
|
41
|
+
if (node.init.type !== "ArrayExpression") {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
62
44
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
45
|
+
const hasObjectLiteral = node.init.elements.some((element) => {
|
|
46
|
+
if (!element || element.type === "SpreadElement")
|
|
47
|
+
return false;
|
|
48
|
+
return isObjectLiteral(element);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (hasObjectLiteral && node.id.type === "Identifier") {
|
|
52
|
+
context.report({
|
|
53
|
+
messageId: "arrayOfObjectLiteralsNeedsType",
|
|
54
|
+
node: node.id
|
|
55
|
+
});
|
|
69
56
|
}
|
|
70
57
|
}
|
|
71
58
|
};
|
|
59
|
+
},
|
|
60
|
+
defaultOptions: [],
|
|
61
|
+
meta: {
|
|
62
|
+
docs: {
|
|
63
|
+
description:
|
|
64
|
+
"Require explicit type annotations for object literals and arrays of object literals"
|
|
65
|
+
},
|
|
66
|
+
messages: {
|
|
67
|
+
arrayOfObjectLiteralsNeedsType:
|
|
68
|
+
"Array of object literals must have an explicit type annotation.",
|
|
69
|
+
objectLiteralNeedsType:
|
|
70
|
+
"Object literal must have an explicit type annotation."
|
|
71
|
+
},
|
|
72
|
+
schema: [],
|
|
73
|
+
type: "problem"
|
|
72
74
|
}
|
|
73
75
|
};
|
|
@@ -1,50 +1,18 @@
|
|
|
1
1
|
import { TSESLint, TSESTree } from "@typescript-eslint/utils";
|
|
2
2
|
|
|
3
|
+
const DEFAULT_MAX_KEYS = 3;
|
|
4
|
+
|
|
3
5
|
type Options = [number | { maxKeys?: number }];
|
|
4
6
|
type MessageIds = "extractStyle";
|
|
5
7
|
|
|
6
8
|
export const inlineStyleLimit: TSESLint.RuleModule<MessageIds, Options> = {
|
|
7
|
-
meta: {
|
|
8
|
-
type: "suggestion",
|
|
9
|
-
docs: {
|
|
10
|
-
description:
|
|
11
|
-
"Disallow inline style objects with too many keys and encourage extracting them"
|
|
12
|
-
},
|
|
13
|
-
schema: [
|
|
14
|
-
{
|
|
15
|
-
anyOf: [
|
|
16
|
-
{
|
|
17
|
-
type: "number"
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
type: "object",
|
|
21
|
-
properties: {
|
|
22
|
-
maxKeys: {
|
|
23
|
-
type: "number",
|
|
24
|
-
description:
|
|
25
|
-
"Maximum number of keys allowed in an inline style object before it must be extracted."
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
additionalProperties: false
|
|
29
|
-
}
|
|
30
|
-
]
|
|
31
|
-
}
|
|
32
|
-
],
|
|
33
|
-
messages: {
|
|
34
|
-
extractStyle:
|
|
35
|
-
"Inline style objects should be extracted into a separate object or file when containing more than {{max}} keys."
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
defaultOptions: [3],
|
|
40
|
-
|
|
41
9
|
create(context) {
|
|
42
|
-
const option = context.options
|
|
10
|
+
const [option] = context.options;
|
|
43
11
|
// If a number is passed directly, use it as maxKeys; otherwise, extract maxKeys from the object (default to 3)
|
|
44
12
|
const maxKeys =
|
|
45
13
|
typeof option === "number"
|
|
46
14
|
? option
|
|
47
|
-
: (option && option.maxKeys) ||
|
|
15
|
+
: (option && option.maxKeys) || DEFAULT_MAX_KEYS;
|
|
48
16
|
|
|
49
17
|
return {
|
|
50
18
|
JSXAttribute(node: TSESTree.JSXAttribute) {
|
|
@@ -58,29 +26,63 @@ export const inlineStyleLimit: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
58
26
|
|
|
59
27
|
// Ensure the value is a JSX expression container with an object literal
|
|
60
28
|
if (
|
|
61
|
-
node.value
|
|
62
|
-
node.value.type
|
|
63
|
-
node.value.expression
|
|
64
|
-
node.value.expression.type
|
|
29
|
+
!node.value ||
|
|
30
|
+
node.value.type !== "JSXExpressionContainer" ||
|
|
31
|
+
!node.value.expression ||
|
|
32
|
+
node.value.expression.type !== "ObjectExpression"
|
|
65
33
|
) {
|
|
66
|
-
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
67
36
|
|
|
68
|
-
|
|
69
|
-
const keyCount = styleObject.properties.filter(
|
|
70
|
-
(prop): prop is TSESTree.Property =>
|
|
71
|
-
prop.type === "Property"
|
|
72
|
-
).length;
|
|
37
|
+
const styleObject = node.value.expression;
|
|
73
38
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
39
|
+
// Count only "Property" nodes (ignoring spread elements or others)
|
|
40
|
+
const keyCount = styleObject.properties.filter(
|
|
41
|
+
(prop): prop is TSESTree.Property =>
|
|
42
|
+
prop.type === "Property"
|
|
43
|
+
).length;
|
|
44
|
+
|
|
45
|
+
// Report only if the number of keys exceeds the allowed maximum
|
|
46
|
+
if (keyCount > maxKeys) {
|
|
47
|
+
context.report({
|
|
48
|
+
data: { max: maxKeys },
|
|
49
|
+
messageId: "extractStyle",
|
|
50
|
+
node
|
|
51
|
+
});
|
|
82
52
|
}
|
|
83
53
|
}
|
|
84
54
|
};
|
|
55
|
+
},
|
|
56
|
+
defaultOptions: [DEFAULT_MAX_KEYS],
|
|
57
|
+
meta: {
|
|
58
|
+
docs: {
|
|
59
|
+
description:
|
|
60
|
+
"Disallow inline style objects with too many keys and encourage extracting them"
|
|
61
|
+
},
|
|
62
|
+
messages: {
|
|
63
|
+
extractStyle:
|
|
64
|
+
"Inline style objects should be extracted into a separate object or file when containing more than {{max}} keys."
|
|
65
|
+
},
|
|
66
|
+
schema: [
|
|
67
|
+
{
|
|
68
|
+
anyOf: [
|
|
69
|
+
{
|
|
70
|
+
type: "number"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
additionalProperties: false,
|
|
74
|
+
properties: {
|
|
75
|
+
maxKeys: {
|
|
76
|
+
description:
|
|
77
|
+
"Maximum number of keys allowed in an inline style object before it must be extracted.",
|
|
78
|
+
type: "number"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
type: "object"
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
type: "suggestion"
|
|
85
87
|
}
|
|
86
88
|
};
|