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.
Files changed (49) hide show
  1. package/.absolutejs/eslint.cache.json +49 -0
  2. package/.absolutejs/prettier.cache.json +49 -0
  3. package/.absolutejs/tsconfig.tsbuildinfo +1 -0
  4. package/.claude/settings.local.json +10 -0
  5. package/dist/index.js +1787 -1457
  6. package/eslint.config.mjs +107 -0
  7. package/package.json +15 -12
  8. package/src/index.ts +45 -0
  9. package/src/rules/explicit-object-types.ts +75 -0
  10. package/src/rules/inline-style-limit.ts +88 -0
  11. package/src/rules/localize-react-props.ts +454 -0
  12. package/src/rules/max-depth-extended.ts +153 -0
  13. package/src/rules/{max-jsx-nesting.js → max-jsx-nesting.ts} +37 -38
  14. package/src/rules/min-var-length.ts +360 -0
  15. package/src/rules/no-button-navigation.ts +270 -0
  16. package/src/rules/no-explicit-return-types.ts +83 -0
  17. package/src/rules/no-inline-prop-types.ts +68 -0
  18. package/src/rules/no-multi-style-objects.ts +80 -0
  19. package/src/rules/no-nested-jsx-return.ts +205 -0
  20. package/src/rules/no-or-none-component.ts +63 -0
  21. package/src/rules/no-transition-cssproperties.ts +131 -0
  22. package/src/rules/no-unnecessary-div.ts +65 -0
  23. package/src/rules/no-unnecessary-key.ts +111 -0
  24. package/src/rules/no-useless-function.ts +56 -0
  25. package/src/rules/seperate-style-files.ts +79 -0
  26. package/src/rules/sort-exports.ts +424 -0
  27. package/src/rules/sort-keys-fixable.ts +647 -0
  28. package/src/rules/spring-naming-convention.ts +160 -0
  29. package/tsconfig.json +4 -1
  30. package/src/index.js +0 -45
  31. package/src/rules/explicit-object-types.js +0 -54
  32. package/src/rules/inline-style-limit.js +0 -77
  33. package/src/rules/localize-react-props.js +0 -418
  34. package/src/rules/max-depth-extended.js +0 -124
  35. package/src/rules/min-var-length.js +0 -300
  36. package/src/rules/no-button-navigation.js +0 -232
  37. package/src/rules/no-explicit-return-types.js +0 -64
  38. package/src/rules/no-inline-prop-types.js +0 -55
  39. package/src/rules/no-multi-style-objects.js +0 -70
  40. package/src/rules/no-nested-jsx-return.js +0 -154
  41. package/src/rules/no-or-none-component.js +0 -50
  42. package/src/rules/no-transition-cssproperties.js +0 -102
  43. package/src/rules/no-unnecessary-div.js +0 -40
  44. package/src/rules/no-unnecessary-key.js +0 -128
  45. package/src/rules/no-useless-function.js +0 -43
  46. package/src/rules/seperate-style-files.js +0 -62
  47. package/src/rules/sort-exports.js +0 -397
  48. package/src/rules/sort-keys-fixable.js +0 -459
  49. 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", // "noUncheckedIndexedAccess": true,
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
- };