eslint-plugin-absolute 0.2.1 → 0.2.3
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 +26 -26
- package/.absolutejs/prettier.cache.json +25 -25
- package/.absolutejs/tsconfig.tsbuildinfo +1 -1
- package/.codex +0 -0
- package/dist/index.js +215 -14
- package/eslint.config.mjs +2 -2
- package/package.json +21 -21
- package/src/rules/sort-exports.ts +180 -23
- package/src/rules/sort-keys-fixable.ts +138 -0
- package/tsconfig.json +2 -2
|
@@ -36,6 +36,101 @@ type KeyInfo = {
|
|
|
36
36
|
|
|
37
37
|
const SORT_BEFORE = -1;
|
|
38
38
|
|
|
39
|
+
const hasDuplicateNames = (names: Array<string | null>) => {
|
|
40
|
+
const seen = new Set<string>();
|
|
41
|
+
const nonNullNames = names.flatMap((name) => (name === null ? [] : [name]));
|
|
42
|
+
|
|
43
|
+
for (const name of nonNullNames) {
|
|
44
|
+
if (seen.has(name)) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
seen.add(name);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return false;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const isSafeStaticTemplate = (node: TSESTree.TemplateLiteral) =>
|
|
54
|
+
node.expressions.length === 0;
|
|
55
|
+
|
|
56
|
+
const isSafeArrayElement: (node: TSESTree.Node | null) => boolean = (node) => {
|
|
57
|
+
if (!node || node.type === "SpreadElement") {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return isSafeToReorderExpression(node);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const isSafeObjectProperty: (
|
|
65
|
+
property: TSESTree.ObjectExpression["properties"][number]
|
|
66
|
+
) => boolean = (property) => {
|
|
67
|
+
if (
|
|
68
|
+
property.type !== "Property" ||
|
|
69
|
+
property.computed ||
|
|
70
|
+
property.kind !== "init"
|
|
71
|
+
) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (property.key.type !== "Identifier" && property.key.type !== "Literal") {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (property.method) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return isSafeToReorderExpression(property.value);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const isSafeToReorderExpression: (node: TSESTree.Node | null) => boolean = (
|
|
87
|
+
node
|
|
88
|
+
) => {
|
|
89
|
+
if (!node || node.type === "PrivateIdentifier") {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
switch (node.type) {
|
|
94
|
+
case "Identifier":
|
|
95
|
+
case "Literal":
|
|
96
|
+
case "ThisExpression":
|
|
97
|
+
case "FunctionExpression":
|
|
98
|
+
case "ArrowFunctionExpression":
|
|
99
|
+
case "ClassExpression":
|
|
100
|
+
return true;
|
|
101
|
+
case "TemplateLiteral":
|
|
102
|
+
return isSafeStaticTemplate(node);
|
|
103
|
+
case "UnaryExpression":
|
|
104
|
+
return isSafeToReorderExpression(node.argument);
|
|
105
|
+
case "ArrayExpression":
|
|
106
|
+
return node.elements.every(isSafeArrayElement);
|
|
107
|
+
case "ObjectExpression":
|
|
108
|
+
return node.properties.every(isSafeObjectProperty);
|
|
109
|
+
default:
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const isSafeJSXAttributeValue = (value: TSESTree.JSXAttribute["value"]) => {
|
|
115
|
+
if (value === null) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (value.type === "Literal") {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (value.type !== "JSXExpressionContainer") {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (value.expression.type === "JSXEmptyExpression") {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return isSafeToReorderExpression(value.expression);
|
|
132
|
+
};
|
|
133
|
+
|
|
39
134
|
export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
40
135
|
create(context) {
|
|
41
136
|
const { sourceCode } = context;
|
|
@@ -367,6 +462,21 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
367
462
|
};
|
|
368
463
|
});
|
|
369
464
|
|
|
465
|
+
if (hasDuplicateNames(keys.map((key) => key.keyName))) {
|
|
466
|
+
autoFixable = false;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
if (
|
|
470
|
+
autoFixable &&
|
|
471
|
+
keys.some(
|
|
472
|
+
(key) =>
|
|
473
|
+
key.node.type === "Property" &&
|
|
474
|
+
!isSafeToReorderExpression(key.node.value)
|
|
475
|
+
)
|
|
476
|
+
) {
|
|
477
|
+
autoFixable = false;
|
|
478
|
+
}
|
|
479
|
+
|
|
370
480
|
let fixProvided = false;
|
|
371
481
|
|
|
372
482
|
const createReportWithFix = (curr: KeyInfo, shouldFix: boolean) => {
|
|
@@ -538,6 +648,34 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
538
648
|
return;
|
|
539
649
|
}
|
|
540
650
|
|
|
651
|
+
if (hasDuplicateNames(names)) {
|
|
652
|
+
context.report({
|
|
653
|
+
messageId: "unsorted",
|
|
654
|
+
node:
|
|
655
|
+
attrs[0]!.type === "JSXAttribute"
|
|
656
|
+
? attrs[0]!.name
|
|
657
|
+
: attrs[0]!
|
|
658
|
+
});
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if (
|
|
663
|
+
attrs.some(
|
|
664
|
+
(attr) =>
|
|
665
|
+
attr.type === "JSXAttribute" &&
|
|
666
|
+
!isSafeJSXAttributeValue(attr.value)
|
|
667
|
+
)
|
|
668
|
+
) {
|
|
669
|
+
context.report({
|
|
670
|
+
messageId: "unsorted",
|
|
671
|
+
node:
|
|
672
|
+
attrs[0]!.type === "JSXAttribute"
|
|
673
|
+
? attrs[0]!.name
|
|
674
|
+
: attrs[0]!
|
|
675
|
+
});
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
|
|
541
679
|
// Be conservative: only fix if there are no JSX comments/braces between attributes.
|
|
542
680
|
const braceConflict = attrs.find((currAttr, idx) => {
|
|
543
681
|
if (idx === 0) {
|
package/tsconfig.json
CHANGED
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
"compilerOptions": {
|
|
3
3
|
"esModuleInterop": true,
|
|
4
4
|
"forceConsistentCasingInFileNames": true,
|
|
5
|
+
"incremental": true,
|
|
5
6
|
"jsx": "react-jsx",
|
|
6
7
|
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
|
7
8
|
"module": "ESNext",
|
|
8
9
|
"moduleResolution": "bundler",
|
|
10
|
+
"noUncheckedIndexedAccess": true,
|
|
9
11
|
"skipLibCheck": true,
|
|
10
12
|
"strict": true,
|
|
11
13
|
"target": "ESNext",
|
|
12
|
-
"noUncheckedIndexedAccess": true,
|
|
13
|
-
"incremental": true,
|
|
14
14
|
"tsBuildInfoFile": ".absolutejs/tsconfig.tsbuildinfo",
|
|
15
15
|
"types": ["bun-types"]
|
|
16
16
|
}
|