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.
Files changed (40) hide show
  1. package/.claude/settings.local.json +5 -0
  2. package/dist/index.js +1226 -798
  3. package/package.json +12 -11
  4. package/src/index.ts +45 -0
  5. package/src/rules/explicit-object-types.ts +73 -0
  6. package/src/rules/{inline-style-limit.js → inline-style-limit.ts} +16 -7
  7. package/src/rules/localize-react-props.ts +459 -0
  8. package/src/rules/max-depth-extended.ts +164 -0
  9. package/src/rules/{max-jsx-nesting.js → max-jsx-nesting.ts} +15 -8
  10. package/src/rules/{min-var-length.js → min-var-length.ts} +75 -45
  11. package/src/rules/no-button-navigation.ts +312 -0
  12. package/src/rules/no-explicit-return-types.ts +87 -0
  13. package/src/rules/{no-inline-prop-types.js → no-inline-prop-types.ts} +22 -9
  14. package/src/rules/no-multi-style-objects.ts +87 -0
  15. package/src/rules/no-nested-jsx-return.ts +210 -0
  16. package/src/rules/no-or-none-component.ts +65 -0
  17. package/src/rules/{no-transition-cssproperties.js → no-transition-cssproperties.ts} +50 -27
  18. package/src/rules/no-unnecessary-div.ts +73 -0
  19. package/src/rules/{no-unnecessary-key.js → no-unnecessary-key.ts} +43 -26
  20. package/src/rules/no-useless-function.ts +58 -0
  21. package/src/rules/seperate-style-files.ts +81 -0
  22. package/src/rules/sort-exports.ts +420 -0
  23. package/src/rules/sort-keys-fixable.ts +621 -0
  24. package/src/rules/spring-naming-convention.ts +145 -0
  25. package/tsconfig.json +2 -1
  26. package/src/index.js +0 -45
  27. package/src/rules/explicit-object-types.js +0 -54
  28. package/src/rules/localize-react-props.js +0 -418
  29. package/src/rules/max-depth-extended.js +0 -124
  30. package/src/rules/no-button-navigation.js +0 -232
  31. package/src/rules/no-explicit-return-types.js +0 -64
  32. package/src/rules/no-multi-style-objects.js +0 -70
  33. package/src/rules/no-nested-jsx-return.js +0 -154
  34. package/src/rules/no-or-none-component.js +0 -50
  35. package/src/rules/no-unnecessary-div.js +0 -40
  36. package/src/rules/no-useless-function.js +0 -43
  37. package/src/rules/seperate-style-files.js +0 -62
  38. package/src/rules/sort-exports.js +0 -397
  39. package/src/rules/sort-keys-fixable.js +0 -459
  40. package/src/rules/spring-naming-convention.js +0 -111
@@ -1,459 +0,0 @@
1
- /**
2
- * @fileoverview Enforce sorted keys in object literals (like ESLint’s built-in sort-keys)
3
- * with an auto-fix for simple cases that preserves comments.
4
- *
5
- * Note: This rule reports errors just like the original sort-keys rule.
6
- * However, the auto-fix only applies if all properties are “fixable” – i.e.:
7
- * - They are of type Property (not SpreadElement, etc.)
8
- * - They are not computed (e.g. [foo])
9
- * - Their key is an Identifier or a Literal.
10
- *
11
- * Comments attached to the properties are preserved in the auto-fix.
12
- *
13
- * Use this rule with a grain of salt. I did not test every edge case and there’s
14
- * a reason the original rule doesn’t have auto-fix. Computed keys, spread elements,
15
- * comments, and formatting are not handled perfectly.
16
- */
17
-
18
- export default {
19
- meta: {
20
- type: "suggestion",
21
- docs: {
22
- description:
23
- "enforce sorted keys in object literals with auto-fix (limited to simple cases, preserving comments)",
24
- recommended: false
25
- },
26
- fixable: "code",
27
- // The schema supports the same options as the built-in sort-keys rule plus:
28
- // variablesBeforeFunctions: boolean (when true, non-function properties come before function properties)
29
- schema: [
30
- {
31
- type: "object",
32
- properties: {
33
- order: {
34
- enum: ["asc", "desc"]
35
- },
36
- caseSensitive: {
37
- type: "boolean"
38
- },
39
- natural: {
40
- type: "boolean"
41
- },
42
- minKeys: {
43
- type: "integer",
44
- minimum: 2
45
- },
46
- variablesBeforeFunctions: {
47
- type: "boolean"
48
- }
49
- },
50
- additionalProperties: false
51
- }
52
- ],
53
- messages: {
54
- unsorted: "Object keys are not sorted."
55
- }
56
- },
57
-
58
- create(context) {
59
- const sourceCode = context.getSourceCode();
60
- const options = context.options[0] || {};
61
- const order = options.order || "asc";
62
- const caseSensitive =
63
- options.caseSensitive !== undefined ? options.caseSensitive : false;
64
- const natural = options.natural !== undefined ? options.natural : false;
65
- const minKeys = options.minKeys !== undefined ? options.minKeys : 2;
66
- const variablesBeforeFunctions =
67
- options.variablesBeforeFunctions !== undefined
68
- ? options.variablesBeforeFunctions
69
- : false;
70
-
71
- /**
72
- * Compare two key strings based on the provided options.
73
- * This function mimics the behavior of the built-in rule.
74
- *
75
- * @param {string} a First key.
76
- * @param {string} b Second key.
77
- * @returns {number} Negative if a comes before b, positive if after, 0 if equal.
78
- */
79
- function compareKeys(a, b) {
80
- let keyA = a;
81
- let keyB = b;
82
- if (!caseSensitive) {
83
- keyA = keyA.toLowerCase();
84
- keyB = keyB.toLowerCase();
85
- }
86
- if (natural) {
87
- return keyA.localeCompare(keyB, undefined, { numeric: true });
88
- }
89
- return keyA.localeCompare(keyB);
90
- }
91
-
92
- /**
93
- * Determines if a property is a function property.
94
- *
95
- * @param {ASTNode} prop The property node.
96
- * @returns {boolean} True if the property's value is a function.
97
- */
98
- function isFunctionProperty(prop) {
99
- return (
100
- prop.value &&
101
- (prop.value.type === "FunctionExpression" ||
102
- prop.value.type === "ArrowFunctionExpression" ||
103
- prop.method === true)
104
- );
105
- }
106
-
107
- /**
108
- * Build the sorted text from the fixable properties while preserving comments.
109
- *
110
- * @param {ASTNode[]} fixableProps Array of fixable property nodes.
111
- * @returns {string} The concatenated text representing the sorted properties.
112
- */
113
- function buildSortedText(fixableProps) {
114
- // Create a sorted copy of the properties.
115
- const sorted = fixableProps.slice().sort((a, b) => {
116
- if (variablesBeforeFunctions) {
117
- const aIsFunc = isFunctionProperty(a);
118
- const bIsFunc = isFunctionProperty(b);
119
- if (aIsFunc !== bIsFunc) {
120
- return aIsFunc ? 1 : -1;
121
- }
122
- }
123
- const keyA =
124
- a.key.type === "Identifier"
125
- ? a.key.name
126
- : String(a.key.value);
127
- const keyB =
128
- b.key.type === "Identifier"
129
- ? b.key.name
130
- : String(b.key.value);
131
- let res = compareKeys(keyA, keyB);
132
- if (order === "desc") {
133
- res = -res;
134
- }
135
- return res;
136
- });
137
-
138
- // Reconstruct each property along with its comments.
139
- return sorted
140
- .map((prop) => {
141
- // Retrieve leading and trailing comments.
142
- const leadingComments =
143
- sourceCode.getCommentsBefore(prop) || [];
144
- const trailingComments =
145
- sourceCode.getCommentsAfter(prop) || [];
146
- // Assemble the comment text.
147
- const leadingText =
148
- leadingComments.length > 0
149
- ? leadingComments
150
- .map((comment) =>
151
- sourceCode.getText(comment)
152
- )
153
- .join("\n") + "\n"
154
- : "";
155
- const trailingText =
156
- trailingComments.length > 0
157
- ? "\n" +
158
- trailingComments
159
- .map((comment) =>
160
- sourceCode.getText(comment)
161
- )
162
- .join("\n")
163
- : "";
164
- // Return the property text with its comments.
165
- return (
166
- leadingText + sourceCode.getText(prop) + trailingText
167
- );
168
- })
169
- .join(", ");
170
- }
171
-
172
- /**
173
- * Checks an ObjectExpression node for unsorted keys.
174
- * Reports an error on each out-of-order key.
175
- *
176
- * For auto-fix purposes, only simple properties are considered fixable.
177
- * (Computed keys, spread elements, or non-Identifier/Literal keys disable the fix.)
178
- *
179
- * @param {ASTNode} node The ObjectExpression node.
180
- */
181
- function checkObjectExpression(node) {
182
- if (node.properties.length < minKeys) {
183
- return;
184
- }
185
-
186
- // Build an array of objects: { keyName, node, isFunction }.
187
- let autoFixable = true;
188
- const keys = node.properties.map((prop) => {
189
- let keyName = null;
190
- let isFunc = false;
191
- if (prop.type === "Property") {
192
- if (prop.computed) {
193
- // Computed keys are not handled in the fixer.
194
- autoFixable = false;
195
- }
196
- if (prop.key.type === "Identifier") {
197
- keyName = prop.key.name;
198
- } else if (prop.key.type === "Literal") {
199
- keyName = String(prop.key.value);
200
- } else {
201
- autoFixable = false;
202
- }
203
- // Determine if the property is a function.
204
- if (prop.value) {
205
- if (
206
- prop.value.type === "FunctionExpression" ||
207
- prop.value.type === "ArrowFunctionExpression" ||
208
- prop.method === true
209
- ) {
210
- isFunc = true;
211
- }
212
- }
213
- } else {
214
- // Spread elements or other non-Property nodes.
215
- autoFixable = false;
216
- }
217
- return { keyName, node: prop, isFunction: isFunc };
218
- });
219
-
220
- // Report an error for each adjacent pair of keys that is out of order.
221
- // For auto-fix, only the first reported error for a given object gets a fix function.
222
- let fixProvided = false;
223
- for (let i = 1; i < keys.length; i++) {
224
- const prev = keys[i - 1];
225
- const curr = keys[i];
226
-
227
- // Skip comparison if keyName is null.
228
- if (prev.keyName === null || curr.keyName === null) {
229
- continue;
230
- }
231
-
232
- if (variablesBeforeFunctions) {
233
- // When sorting variables before functions, if the previous property is a function and the current one is not,
234
- // then they are out of order.
235
- if (prev.isFunction && !curr.isFunction) {
236
- context.report({
237
- node: curr.node.key,
238
- messageId: "unsorted",
239
- fix:
240
- !fixProvided && autoFixable
241
- ? (fixer) => {
242
- const fixableProps =
243
- node.properties.filter(
244
- (prop) =>
245
- (prop.type ===
246
- "Property" &&
247
- !prop.computed &&
248
- (prop.key.type ===
249
- "Identifier" ||
250
- prop.key
251
- .type ===
252
- "Literal")) ||
253
- false
254
- );
255
- if (fixableProps.length < minKeys) {
256
- return null;
257
- }
258
- const sortedText =
259
- buildSortedText(fixableProps);
260
- const firstProp = fixableProps[0];
261
- const lastProp =
262
- fixableProps[
263
- fixableProps.length - 1
264
- ];
265
- return fixer.replaceTextRange(
266
- [
267
- firstProp.range[0],
268
- lastProp.range[1]
269
- ],
270
- sortedText
271
- );
272
- }
273
- : null
274
- });
275
- fixProvided = true;
276
- continue;
277
- } else if (prev.isFunction === curr.isFunction) {
278
- // If both properties are of the same type, compare keys alphabetically.
279
- if (compareKeys(prev.keyName, curr.keyName) > 0) {
280
- context.report({
281
- node: curr.node.key,
282
- messageId: "unsorted",
283
- fix:
284
- !fixProvided && autoFixable
285
- ? (fixer) => {
286
- const fixableProps =
287
- node.properties.filter(
288
- (prop) =>
289
- (prop.type ===
290
- "Property" &&
291
- !prop.computed &&
292
- (prop.key
293
- .type ===
294
- "Identifier" ||
295
- prop.key
296
- .type ===
297
- "Literal")) ||
298
- false
299
- );
300
- if (
301
- fixableProps.length <
302
- minKeys
303
- ) {
304
- return null;
305
- }
306
- const sortedText =
307
- buildSortedText(
308
- fixableProps
309
- );
310
- const firstProp =
311
- fixableProps[0];
312
- const lastProp =
313
- fixableProps[
314
- fixableProps.length - 1
315
- ];
316
- return fixer.replaceTextRange(
317
- [
318
- firstProp.range[0],
319
- lastProp.range[1]
320
- ],
321
- sortedText
322
- );
323
- }
324
- : null
325
- });
326
- fixProvided = true;
327
- }
328
- }
329
- } else {
330
- // Original behavior: compare keys alphabetically.
331
- if (compareKeys(prev.keyName, curr.keyName) > 0) {
332
- context.report({
333
- node: curr.node.key,
334
- messageId: "unsorted",
335
- fix:
336
- !fixProvided && autoFixable
337
- ? (fixer) => {
338
- const fixableProps =
339
- node.properties.filter(
340
- (prop) =>
341
- (prop.type ===
342
- "Property" &&
343
- !prop.computed &&
344
- (prop.key.type ===
345
- "Identifier" ||
346
- prop.key
347
- .type ===
348
- "Literal")) ||
349
- false
350
- );
351
- if (fixableProps.length < minKeys) {
352
- return null;
353
- }
354
- const sortedText =
355
- buildSortedText(fixableProps);
356
- const firstProp = fixableProps[0];
357
- const lastProp =
358
- fixableProps[
359
- fixableProps.length - 1
360
- ];
361
- return fixer.replaceTextRange(
362
- [
363
- firstProp.range[0],
364
- lastProp.range[1]
365
- ],
366
- sortedText
367
- );
368
- }
369
- : null
370
- });
371
- fixProvided = true;
372
- }
373
- }
374
- }
375
- }
376
-
377
- // Also check object literals inside JSX prop expressions
378
- function checkJSXAttributeObject(attr) {
379
- if (
380
- attr.value &&
381
- attr.value.type === "JSXExpressionContainer" &&
382
- attr.value.expression &&
383
- attr.value.expression.type === "ObjectExpression"
384
- ) {
385
- checkObjectExpression(attr.value.expression);
386
- }
387
- }
388
-
389
- // Also sort JSX attributes on elements
390
- function checkJSXOpeningElement(node) {
391
- const attrs = node.attributes;
392
- if (attrs.length < minKeys) return;
393
-
394
- // Only fix when all are simple JSXAttribute with JSXIdentifier names and no spreads.
395
- if (attrs.some((a) => a.type !== "JSXAttribute")) return;
396
- if (attrs.some((a) => a.name.type !== "JSXIdentifier")) return;
397
-
398
- const names = attrs.map((a) => a.name.name);
399
- const cmp = (a, b) => {
400
- let res = compareKeys(a, b);
401
- return order === "desc" ? -res : res;
402
- };
403
-
404
- let outOfOrder = false;
405
- for (let i = 1; i < names.length; i++) {
406
- if (cmp(names[i - 1], names[i]) > 0) {
407
- outOfOrder = true;
408
- break;
409
- }
410
- }
411
- if (!outOfOrder) return;
412
-
413
- // Be conservative: only fix if there are no JSX comments/braces between attributes.
414
- for (let i = 1; i < attrs.length; i++) {
415
- const between = sourceCode.text.slice(
416
- attrs[i - 1].range[1],
417
- attrs[i].range[0]
418
- );
419
- if (between.includes("{")) {
420
- // Likely contains a JSX comment or expression gap; report without fix.
421
- context.report({
422
- node: attrs[i].name,
423
- messageId: "unsorted"
424
- });
425
- return;
426
- }
427
- }
428
-
429
- const sorted = attrs
430
- .slice()
431
- .sort((a, b) => cmp(a.name.name, b.name.name));
432
-
433
- const first = attrs[0];
434
- const last = attrs[attrs.length - 1];
435
- const replacement = sorted
436
- .map((a) => sourceCode.getText(a))
437
- .join(" ");
438
-
439
- context.report({
440
- node: first.name,
441
- messageId: "unsorted",
442
- fix(fixer) {
443
- return fixer.replaceTextRange(
444
- [first.range[0], last.range[1]],
445
- replacement
446
- );
447
- }
448
- });
449
- }
450
-
451
- return {
452
- ObjectExpression: checkObjectExpression,
453
- JSXAttribute(node) {
454
- checkJSXAttributeObject(node);
455
- },
456
- JSXOpeningElement: checkJSXOpeningElement
457
- };
458
- }
459
- };
@@ -1,111 +0,0 @@
1
- export default {
2
- meta: {
3
- type: "problem",
4
- docs: {
5
- description:
6
- "Enforce correct naming for useSpring and useSprings hook destructuring",
7
- category: "Stylistic Issues",
8
- recommended: false
9
- },
10
- schema: []
11
- },
12
- create(context) {
13
- return {
14
- VariableDeclarator(node) {
15
- if (
16
- node.init &&
17
- node.init.type === "CallExpression" &&
18
- node.init.callee &&
19
- node.init.callee.type === "Identifier" &&
20
- (node.init.callee.name === "useSpring" ||
21
- node.init.callee.name === "useSprings")
22
- ) {
23
- const hookName = node.init.callee.name;
24
- if (node.id && node.id.type === "ArrayPattern") {
25
- const elements = node.id.elements;
26
- if (elements.length < 2) {
27
- return;
28
- }
29
- const firstElem = elements[0];
30
- const secondElem = elements[1];
31
- if (
32
- !firstElem ||
33
- firstElem.type !== "Identifier" ||
34
- !secondElem ||
35
- secondElem.type !== "Identifier"
36
- ) {
37
- return;
38
- }
39
- const firstName = firstElem.name;
40
- const secondName = secondElem.name;
41
- if (hookName === "useSpring") {
42
- if (!firstName.endsWith("Springs")) {
43
- context.report({
44
- node: firstElem,
45
- message:
46
- "The first variable from useSpring must end with 'Springs'."
47
- });
48
- } else {
49
- const base = firstName.slice(
50
- 0,
51
- -"Springs".length
52
- );
53
- if (!base) {
54
- context.report({
55
- node: firstElem,
56
- message:
57
- "The first variable must have a non-empty name before 'Springs'."
58
- });
59
- return;
60
- }
61
- const expectedSecond = base + "Api";
62
- if (secondName !== expectedSecond) {
63
- context.report({
64
- node: secondElem,
65
- message: `The second variable from useSpring must be named '${expectedSecond}'.`
66
- });
67
- }
68
- }
69
- } else if (hookName === "useSprings") {
70
- if (!firstName.endsWith("Springs")) {
71
- context.report({
72
- node: firstElem,
73
- message:
74
- "The first variable from useSprings must end with 'Springs'."
75
- });
76
- } else {
77
- const basePlural = firstName.slice(
78
- 0,
79
- -"Springs".length
80
- );
81
- if (!basePlural) {
82
- context.report({
83
- node: firstElem,
84
- message:
85
- "The first variable must have a non-empty name before 'Springs'."
86
- });
87
- return;
88
- }
89
- if (!basePlural.endsWith("s")) {
90
- context.report({
91
- node: firstElem,
92
- message:
93
- "The first variable for useSprings should be a plural name (ending with an 's') before 'Springs'."
94
- });
95
- } else {
96
- const expectedSecond = basePlural + "Api";
97
- if (secondName !== expectedSecond) {
98
- context.report({
99
- node: secondElem,
100
- message: `The second variable from useSprings must be named '${expectedSecond}'.`
101
- });
102
- }
103
- }
104
- }
105
- }
106
- }
107
- }
108
- }
109
- };
110
- }
111
- };