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.
- package/.claude/settings.local.json +5 -0
- package/dist/index.js +1226 -798
- package/package.json +12 -11
- package/src/index.ts +45 -0
- package/src/rules/explicit-object-types.ts +73 -0
- package/src/rules/{inline-style-limit.js → inline-style-limit.ts} +16 -7
- package/src/rules/localize-react-props.ts +459 -0
- package/src/rules/max-depth-extended.ts +164 -0
- package/src/rules/{max-jsx-nesting.js → max-jsx-nesting.ts} +15 -8
- package/src/rules/{min-var-length.js → min-var-length.ts} +75 -45
- package/src/rules/no-button-navigation.ts +312 -0
- package/src/rules/no-explicit-return-types.ts +87 -0
- package/src/rules/{no-inline-prop-types.js → no-inline-prop-types.ts} +22 -9
- package/src/rules/no-multi-style-objects.ts +87 -0
- package/src/rules/no-nested-jsx-return.ts +210 -0
- package/src/rules/no-or-none-component.ts +65 -0
- package/src/rules/{no-transition-cssproperties.js → no-transition-cssproperties.ts} +50 -27
- package/src/rules/no-unnecessary-div.ts +73 -0
- package/src/rules/{no-unnecessary-key.js → no-unnecessary-key.ts} +43 -26
- package/src/rules/no-useless-function.ts +58 -0
- package/src/rules/seperate-style-files.ts +81 -0
- package/src/rules/sort-exports.ts +420 -0
- package/src/rules/sort-keys-fixable.ts +621 -0
- package/src/rules/spring-naming-convention.ts +145 -0
- package/tsconfig.json +2 -1
- package/src/index.js +0 -45
- package/src/rules/explicit-object-types.js +0 -54
- package/src/rules/localize-react-props.js +0 -418
- package/src/rules/max-depth-extended.js +0 -124
- package/src/rules/no-button-navigation.js +0 -232
- package/src/rules/no-explicit-return-types.js +0 -64
- 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-unnecessary-div.js +0 -40
- 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
package/dist/index.js
CHANGED
|
@@ -1,62 +1,76 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// src/rules/no-nested-jsx-return.
|
|
3
|
-
|
|
2
|
+
// src/rules/no-nested-jsx-return.ts
|
|
3
|
+
import { AST_NODE_TYPES } from "@typescript-eslint/utils";
|
|
4
|
+
var noNestedJSXReturn = {
|
|
4
5
|
meta: {
|
|
5
6
|
type: "problem",
|
|
6
7
|
docs: {
|
|
7
|
-
description: "Disallow nested functions that return non-component, non-singular JSX to enforce one component per file"
|
|
8
|
-
recommended: false
|
|
8
|
+
description: "Disallow nested functions that return non-component, non-singular JSX to enforce one component per file"
|
|
9
9
|
},
|
|
10
|
-
schema: []
|
|
10
|
+
schema: [],
|
|
11
|
+
messages: {
|
|
12
|
+
nestedFunctionJSX: "Nested function returning non-component, non-singular JSX detected. Extract it into its own component.",
|
|
13
|
+
nestedArrowJSX: "Nested arrow function returning non-component, non-singular JSX detected. Extract it into its own component.",
|
|
14
|
+
nestedArrowFragment: "Nested arrow function returning a non-singular JSX fragment detected. Extract it into its own component."
|
|
15
|
+
}
|
|
11
16
|
},
|
|
17
|
+
defaultOptions: [],
|
|
12
18
|
create(context) {
|
|
13
19
|
function isJSX(node) {
|
|
14
|
-
return node && (node.type ===
|
|
20
|
+
return !!node && (node.type === AST_NODE_TYPES.JSXElement || node.type === AST_NODE_TYPES.JSXFragment);
|
|
21
|
+
}
|
|
22
|
+
function getLeftmostJSXIdentifier(name) {
|
|
23
|
+
let current = name;
|
|
24
|
+
while (current.type === AST_NODE_TYPES.JSXMemberExpression) {
|
|
25
|
+
current = current.object;
|
|
26
|
+
}
|
|
27
|
+
if (current.type === AST_NODE_TYPES.JSXIdentifier) {
|
|
28
|
+
return current;
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
15
31
|
}
|
|
16
32
|
function isJSXComponentElement(node) {
|
|
17
|
-
if (node
|
|
18
|
-
const opening = node.openingElement;
|
|
19
|
-
if (opening && opening.name) {
|
|
20
|
-
if (opening.name.type === "JSXIdentifier") {
|
|
21
|
-
return /^[A-Z]/.test(opening.name.name);
|
|
22
|
-
}
|
|
23
|
-
if (opening.name.type === "JSXMemberExpression") {
|
|
24
|
-
let current = opening.name;
|
|
25
|
-
while (current && current.type === "JSXMemberExpression") {
|
|
26
|
-
current = current.object;
|
|
27
|
-
}
|
|
28
|
-
return current && current.type === "JSXIdentifier" && /^[A-Z]/.test(current.name);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
33
|
+
if (!node || node.type !== AST_NODE_TYPES.JSXElement) {
|
|
31
34
|
return false;
|
|
32
35
|
}
|
|
33
|
-
|
|
36
|
+
const opening = node.openingElement;
|
|
37
|
+
const nameNode = opening.name;
|
|
38
|
+
if (nameNode.type === AST_NODE_TYPES.JSXIdentifier) {
|
|
39
|
+
return /^[A-Z]/.test(nameNode.name);
|
|
40
|
+
}
|
|
41
|
+
const leftmost = getLeftmostJSXIdentifier(nameNode);
|
|
42
|
+
if (!leftmost) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
return /^[A-Z]/.test(leftmost.name);
|
|
34
46
|
}
|
|
35
47
|
function isSingularJSXReturn(node) {
|
|
36
48
|
if (!isJSX(node))
|
|
37
49
|
return false;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
const children = node.children.filter((child) => {
|
|
51
|
+
if (child.type === AST_NODE_TYPES.JSXText) {
|
|
52
|
+
return child.value.trim() !== "";
|
|
53
|
+
}
|
|
54
|
+
return true;
|
|
55
|
+
});
|
|
56
|
+
if (children.length === 0) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
if (children.length === 1) {
|
|
60
|
+
const child = children[0];
|
|
61
|
+
if (!child) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
if (child.type === AST_NODE_TYPES.JSXElement || child.type === AST_NODE_TYPES.JSXFragment) {
|
|
65
|
+
const innerChildren = child.children.filter((innerChild) => {
|
|
66
|
+
if (innerChild.type === AST_NODE_TYPES.JSXText) {
|
|
67
|
+
return innerChild.value.trim() !== "";
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
});
|
|
71
|
+
return innerChildren.length === 0;
|
|
58
72
|
}
|
|
59
|
-
return
|
|
73
|
+
return true;
|
|
60
74
|
}
|
|
61
75
|
return false;
|
|
62
76
|
}
|
|
@@ -71,36 +85,49 @@ var no_nested_jsx_return_default = {
|
|
|
71
85
|
"FunctionDeclaration, FunctionExpression, ArrowFunctionExpression"(node) {
|
|
72
86
|
pushFunction(node);
|
|
73
87
|
},
|
|
74
|
-
"FunctionDeclaration:exit"(
|
|
88
|
+
"FunctionDeclaration:exit"(_node) {
|
|
75
89
|
popFunction();
|
|
76
90
|
},
|
|
77
|
-
"FunctionExpression:exit"(
|
|
91
|
+
"FunctionExpression:exit"(_node) {
|
|
78
92
|
popFunction();
|
|
79
93
|
},
|
|
80
|
-
"ArrowFunctionExpression:exit"(
|
|
94
|
+
"ArrowFunctionExpression:exit"(_node) {
|
|
81
95
|
popFunction();
|
|
82
96
|
},
|
|
83
97
|
ReturnStatement(node) {
|
|
84
|
-
if (functionStack.length
|
|
98
|
+
if (functionStack.length <= 1) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const argument = node.argument;
|
|
102
|
+
if (!isJSX(argument)) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (!isJSXComponentElement(argument) && !isSingularJSXReturn(argument)) {
|
|
85
106
|
context.report({
|
|
86
107
|
node,
|
|
87
|
-
|
|
108
|
+
messageId: "nestedFunctionJSX"
|
|
88
109
|
});
|
|
89
110
|
}
|
|
90
111
|
},
|
|
91
112
|
"ArrowFunctionExpression > JSXElement"(node) {
|
|
92
|
-
if (functionStack.length
|
|
113
|
+
if (functionStack.length <= 1) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (!isJSXComponentElement(node) && !isSingularJSXReturn(node)) {
|
|
93
117
|
context.report({
|
|
94
118
|
node,
|
|
95
|
-
|
|
119
|
+
messageId: "nestedArrowJSX"
|
|
96
120
|
});
|
|
97
121
|
}
|
|
98
122
|
},
|
|
99
123
|
"ArrowFunctionExpression > JSXFragment"(node) {
|
|
100
|
-
if (functionStack.length
|
|
124
|
+
if (functionStack.length <= 1) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (!isSingularJSXReturn(node)) {
|
|
101
128
|
context.report({
|
|
102
129
|
node,
|
|
103
|
-
|
|
130
|
+
messageId: "nestedArrowFragment"
|
|
104
131
|
});
|
|
105
132
|
}
|
|
106
133
|
}
|
|
@@ -108,39 +135,49 @@ var no_nested_jsx_return_default = {
|
|
|
108
135
|
}
|
|
109
136
|
};
|
|
110
137
|
|
|
111
|
-
// src/rules/explicit-object-types.
|
|
112
|
-
var
|
|
138
|
+
// src/rules/explicit-object-types.ts
|
|
139
|
+
var explicitObjectTypes = {
|
|
113
140
|
meta: {
|
|
114
141
|
type: "problem",
|
|
115
142
|
docs: {
|
|
116
|
-
description: "Require explicit type annotations for object literals and arrays of object literals"
|
|
117
|
-
recommended: false
|
|
143
|
+
description: "Require explicit type annotations for object literals and arrays of object literals"
|
|
118
144
|
},
|
|
119
|
-
schema: []
|
|
145
|
+
schema: [],
|
|
146
|
+
messages: {
|
|
147
|
+
objectLiteralNeedsType: "Object literal must have an explicit type annotation.",
|
|
148
|
+
arrayOfObjectLiteralsNeedsType: "Array of object literals must have an explicit type annotation."
|
|
149
|
+
}
|
|
120
150
|
},
|
|
151
|
+
defaultOptions: [],
|
|
121
152
|
create(context) {
|
|
122
153
|
function isObjectLiteral(node) {
|
|
123
|
-
return node && node.type === "ObjectExpression";
|
|
154
|
+
return !!node && node.type === "ObjectExpression";
|
|
124
155
|
}
|
|
125
156
|
return {
|
|
126
157
|
VariableDeclarator(node) {
|
|
127
158
|
if (!node.init)
|
|
128
159
|
return;
|
|
129
|
-
if (node.id && node.id.typeAnnotation)
|
|
160
|
+
if (node.id.type === "Identifier" && node.id.typeAnnotation)
|
|
130
161
|
return;
|
|
131
162
|
if (isObjectLiteral(node.init)) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
163
|
+
if (node.id.type === "Identifier") {
|
|
164
|
+
context.report({
|
|
165
|
+
node: node.id,
|
|
166
|
+
messageId: "objectLiteralNeedsType"
|
|
167
|
+
});
|
|
168
|
+
}
|
|
136
169
|
return;
|
|
137
170
|
}
|
|
138
171
|
if (node.init.type === "ArrayExpression") {
|
|
139
|
-
const hasObjectLiteral = node.init.elements.some((element) =>
|
|
140
|
-
|
|
172
|
+
const hasObjectLiteral = node.init.elements.some((element) => {
|
|
173
|
+
if (!element || element.type === "SpreadElement")
|
|
174
|
+
return false;
|
|
175
|
+
return isObjectLiteral(element);
|
|
176
|
+
});
|
|
177
|
+
if (hasObjectLiteral && node.id.type === "Identifier") {
|
|
141
178
|
context.report({
|
|
142
179
|
node: node.id,
|
|
143
|
-
|
|
180
|
+
messageId: "arrayOfObjectLiteralsNeedsType"
|
|
144
181
|
});
|
|
145
182
|
}
|
|
146
183
|
}
|
|
@@ -149,13 +186,12 @@ var explicit_object_types_default = {
|
|
|
149
186
|
}
|
|
150
187
|
};
|
|
151
188
|
|
|
152
|
-
// src/rules/sort-keys-fixable.
|
|
153
|
-
var
|
|
189
|
+
// src/rules/sort-keys-fixable.ts
|
|
190
|
+
var sortKeysFixable = {
|
|
154
191
|
meta: {
|
|
155
192
|
type: "suggestion",
|
|
156
193
|
docs: {
|
|
157
|
-
description: "enforce sorted keys in object literals with auto-fix (limited to simple cases, preserving comments)"
|
|
158
|
-
recommended: false
|
|
194
|
+
description: "enforce sorted keys in object literals with auto-fix (limited to simple cases, preserving comments)"
|
|
159
195
|
},
|
|
160
196
|
fixable: "code",
|
|
161
197
|
schema: [
|
|
@@ -163,6 +199,7 @@ var sort_keys_fixable_default = {
|
|
|
163
199
|
type: "object",
|
|
164
200
|
properties: {
|
|
165
201
|
order: {
|
|
202
|
+
type: "string",
|
|
166
203
|
enum: ["asc", "desc"]
|
|
167
204
|
},
|
|
168
205
|
caseSensitive: {
|
|
@@ -186,14 +223,15 @@ var sort_keys_fixable_default = {
|
|
|
186
223
|
unsorted: "Object keys are not sorted."
|
|
187
224
|
}
|
|
188
225
|
},
|
|
226
|
+
defaultOptions: [{}],
|
|
189
227
|
create(context) {
|
|
190
|
-
const sourceCode = context.
|
|
191
|
-
const
|
|
192
|
-
const order =
|
|
193
|
-
const caseSensitive =
|
|
194
|
-
const natural =
|
|
195
|
-
const minKeys =
|
|
196
|
-
const variablesBeforeFunctions =
|
|
228
|
+
const sourceCode = context.sourceCode;
|
|
229
|
+
const option = context.options[0];
|
|
230
|
+
const order = option && option.order ? option.order : "asc";
|
|
231
|
+
const caseSensitive = option && typeof option.caseSensitive === "boolean" ? option.caseSensitive : false;
|
|
232
|
+
const natural = option && typeof option.natural === "boolean" ? option.natural : false;
|
|
233
|
+
const minKeys = option && typeof option.minKeys === "number" ? option.minKeys : 2;
|
|
234
|
+
const variablesBeforeFunctions = option && typeof option.variablesBeforeFunctions === "boolean" ? option.variablesBeforeFunctions : false;
|
|
197
235
|
function compareKeys(a, b) {
|
|
198
236
|
let keyA = a;
|
|
199
237
|
let keyB = b;
|
|
@@ -202,41 +240,116 @@ var sort_keys_fixable_default = {
|
|
|
202
240
|
keyB = keyB.toLowerCase();
|
|
203
241
|
}
|
|
204
242
|
if (natural) {
|
|
205
|
-
return keyA.localeCompare(keyB, undefined, {
|
|
243
|
+
return keyA.localeCompare(keyB, undefined, {
|
|
244
|
+
numeric: true
|
|
245
|
+
});
|
|
206
246
|
}
|
|
207
247
|
return keyA.localeCompare(keyB);
|
|
208
248
|
}
|
|
209
249
|
function isFunctionProperty(prop) {
|
|
210
|
-
|
|
250
|
+
const value = prop.value;
|
|
251
|
+
return !!value && (value.type === "FunctionExpression" || value.type === "ArrowFunctionExpression" || prop.method === true);
|
|
252
|
+
}
|
|
253
|
+
function getPropertyKeyName(prop) {
|
|
254
|
+
const key = prop.key;
|
|
255
|
+
if (key.type === "Identifier") {
|
|
256
|
+
return key.name;
|
|
257
|
+
}
|
|
258
|
+
if (key.type === "Literal") {
|
|
259
|
+
const value = key.value;
|
|
260
|
+
if (typeof value === "string") {
|
|
261
|
+
return value;
|
|
262
|
+
}
|
|
263
|
+
return String(value);
|
|
264
|
+
}
|
|
265
|
+
return "";
|
|
266
|
+
}
|
|
267
|
+
function getLeadingComments(prop, prevProp) {
|
|
268
|
+
const comments = sourceCode.getCommentsBefore(prop);
|
|
269
|
+
if (!prevProp || comments.length === 0) {
|
|
270
|
+
return comments;
|
|
271
|
+
}
|
|
272
|
+
return comments.filter((c) => c.loc.start.line !== prevProp.loc.end.line);
|
|
211
273
|
}
|
|
212
|
-
function
|
|
213
|
-
const
|
|
274
|
+
function getTrailingComments(prop, nextProp) {
|
|
275
|
+
const after = sourceCode.getCommentsAfter(prop).filter((c) => c.loc.start.line === prop.loc.end.line);
|
|
276
|
+
if (nextProp) {
|
|
277
|
+
const beforeNext = sourceCode.getCommentsBefore(nextProp);
|
|
278
|
+
const trailingOfPrev = beforeNext.filter((c) => c.loc.start.line === prop.loc.end.line);
|
|
279
|
+
for (const c of trailingOfPrev) {
|
|
280
|
+
if (!after.some((a) => a.range[0] === c.range[0])) {
|
|
281
|
+
after.push(c);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return after;
|
|
286
|
+
}
|
|
287
|
+
function buildSortedText(fixableProps, rangeStart) {
|
|
288
|
+
const chunks = [];
|
|
289
|
+
for (let i = 0;i < fixableProps.length; i++) {
|
|
290
|
+
const prop = fixableProps[i];
|
|
291
|
+
const prevProp = i > 0 ? fixableProps[i - 1] : null;
|
|
292
|
+
const nextProp = i < fixableProps.length - 1 ? fixableProps[i + 1] : null;
|
|
293
|
+
const leading = getLeadingComments(prop, prevProp);
|
|
294
|
+
const trailing = getTrailingComments(prop, nextProp);
|
|
295
|
+
const fullStart = leading.length > 0 ? leading[0].range[0] : prop.range[0];
|
|
296
|
+
const fullEnd = trailing.length > 0 ? trailing[trailing.length - 1].range[1] : prop.range[1];
|
|
297
|
+
let chunkStart;
|
|
298
|
+
if (i === 0) {
|
|
299
|
+
chunkStart = rangeStart;
|
|
300
|
+
} else {
|
|
301
|
+
const prevTrailing = getTrailingComments(prevProp, prop);
|
|
302
|
+
const prevEnd = prevTrailing.length > 0 ? prevTrailing[prevTrailing.length - 1].range[1] : prevProp.range[1];
|
|
303
|
+
const tokenAfterPrev = sourceCode.getTokenAfter({
|
|
304
|
+
range: [prevEnd, prevEnd]
|
|
305
|
+
}, { includeComments: false });
|
|
306
|
+
if (tokenAfterPrev && tokenAfterPrev.value === "," && tokenAfterPrev.range[1] <= fullStart) {
|
|
307
|
+
chunkStart = tokenAfterPrev.range[1];
|
|
308
|
+
} else {
|
|
309
|
+
chunkStart = prevEnd;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
const text = sourceCode.text.slice(chunkStart, fullEnd);
|
|
313
|
+
chunks.push({ prop, text });
|
|
314
|
+
}
|
|
315
|
+
const sorted = chunks.slice().sort((a, b) => {
|
|
214
316
|
if (variablesBeforeFunctions) {
|
|
215
|
-
const aIsFunc = isFunctionProperty(a);
|
|
216
|
-
const bIsFunc = isFunctionProperty(b);
|
|
317
|
+
const aIsFunc = isFunctionProperty(a.prop);
|
|
318
|
+
const bIsFunc = isFunctionProperty(b.prop);
|
|
217
319
|
if (aIsFunc !== bIsFunc) {
|
|
218
320
|
return aIsFunc ? 1 : -1;
|
|
219
321
|
}
|
|
220
322
|
}
|
|
221
|
-
const
|
|
222
|
-
const
|
|
223
|
-
let res = compareKeys(
|
|
323
|
+
const aKey = getPropertyKeyName(a.prop);
|
|
324
|
+
const bKey = getPropertyKeyName(b.prop);
|
|
325
|
+
let res = compareKeys(aKey, bKey);
|
|
224
326
|
if (order === "desc") {
|
|
225
327
|
res = -res;
|
|
226
328
|
}
|
|
227
329
|
return res;
|
|
228
330
|
});
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
const
|
|
236
|
-
|
|
237
|
-
`
|
|
238
|
-
|
|
239
|
-
|
|
331
|
+
const firstPropLine = fixableProps[0].loc.start.line;
|
|
332
|
+
const lastPropLine = fixableProps[fixableProps.length - 1].loc.start.line;
|
|
333
|
+
const isMultiline = firstPropLine !== lastPropLine;
|
|
334
|
+
let separator;
|
|
335
|
+
if (isMultiline) {
|
|
336
|
+
const col = fixableProps[0].loc.start.column;
|
|
337
|
+
const indent = sourceCode.text.slice(fixableProps[0].range[0] - col, fixableProps[0].range[0]);
|
|
338
|
+
separator = `,
|
|
339
|
+
` + indent;
|
|
340
|
+
} else {
|
|
341
|
+
separator = ", ";
|
|
342
|
+
}
|
|
343
|
+
return sorted.map((chunk, i) => {
|
|
344
|
+
if (i === 0) {
|
|
345
|
+
const originalFirstChunk = chunks[0];
|
|
346
|
+
const originalLeadingWs = originalFirstChunk.text.match(/^(\s*)/)?.[1] ?? "";
|
|
347
|
+
const stripped2 = chunk.text.replace(/^\s*/, "");
|
|
348
|
+
return originalLeadingWs + stripped2;
|
|
349
|
+
}
|
|
350
|
+
const stripped = chunk.text.replace(/^\s*/, "");
|
|
351
|
+
return separator + stripped;
|
|
352
|
+
}).join("");
|
|
240
353
|
}
|
|
241
354
|
function checkObjectExpression(node) {
|
|
242
355
|
if (node.properties.length < minKeys) {
|
|
@@ -253,140 +366,171 @@ var sort_keys_fixable_default = {
|
|
|
253
366
|
if (prop.key.type === "Identifier") {
|
|
254
367
|
keyName = prop.key.name;
|
|
255
368
|
} else if (prop.key.type === "Literal") {
|
|
256
|
-
|
|
369
|
+
const value = prop.key.value;
|
|
370
|
+
keyName = typeof value === "string" ? value : String(value);
|
|
257
371
|
} else {
|
|
258
372
|
autoFixable = false;
|
|
259
373
|
}
|
|
260
|
-
if (prop
|
|
261
|
-
|
|
262
|
-
isFunc = true;
|
|
263
|
-
}
|
|
374
|
+
if (isFunctionProperty(prop)) {
|
|
375
|
+
isFunc = true;
|
|
264
376
|
}
|
|
265
377
|
} else {
|
|
266
378
|
autoFixable = false;
|
|
267
379
|
}
|
|
268
|
-
return {
|
|
380
|
+
return {
|
|
381
|
+
keyName,
|
|
382
|
+
node: prop,
|
|
383
|
+
isFunction: isFunc
|
|
384
|
+
};
|
|
269
385
|
});
|
|
386
|
+
const getFixableProps = () => {
|
|
387
|
+
const props = [];
|
|
388
|
+
for (const prop of node.properties) {
|
|
389
|
+
if (prop.type !== "Property") {
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
if (prop.computed) {
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
if (prop.key.type !== "Identifier" && prop.key.type !== "Literal") {
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
props.push(prop);
|
|
399
|
+
}
|
|
400
|
+
return props;
|
|
401
|
+
};
|
|
270
402
|
let fixProvided = false;
|
|
271
403
|
for (let i = 1;i < keys.length; i++) {
|
|
272
404
|
const prev = keys[i - 1];
|
|
273
405
|
const curr = keys[i];
|
|
406
|
+
if (!prev || !curr) {
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
274
409
|
if (prev.keyName === null || curr.keyName === null) {
|
|
275
410
|
continue;
|
|
276
411
|
}
|
|
412
|
+
const shouldFix = !fixProvided && autoFixable;
|
|
413
|
+
const reportWithFix = () => {
|
|
414
|
+
context.report({
|
|
415
|
+
node: curr.node.type === "Property" ? curr.node.key : curr.node,
|
|
416
|
+
messageId: "unsorted",
|
|
417
|
+
fix: shouldFix ? (fixer) => {
|
|
418
|
+
const fixableProps = getFixableProps();
|
|
419
|
+
if (fixableProps.length < minKeys) {
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
const firstProp = fixableProps[0];
|
|
423
|
+
const lastProp = fixableProps[fixableProps.length - 1];
|
|
424
|
+
if (!firstProp || !lastProp) {
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
const firstLeading = getLeadingComments(firstProp, null);
|
|
428
|
+
const rangeStart = firstLeading.length > 0 ? firstLeading[0].range[0] : firstProp.range[0];
|
|
429
|
+
const lastTrailing = getTrailingComments(lastProp, null);
|
|
430
|
+
const rangeEnd = lastTrailing.length > 0 ? lastTrailing[lastTrailing.length - 1].range[1] : lastProp.range[1];
|
|
431
|
+
const sortedText = buildSortedText(fixableProps, rangeStart);
|
|
432
|
+
return fixer.replaceTextRange([rangeStart, rangeEnd], sortedText);
|
|
433
|
+
} : null
|
|
434
|
+
});
|
|
435
|
+
fixProvided = true;
|
|
436
|
+
};
|
|
277
437
|
if (variablesBeforeFunctions) {
|
|
278
438
|
if (prev.isFunction && !curr.isFunction) {
|
|
279
|
-
|
|
280
|
-
node: curr.node.key,
|
|
281
|
-
messageId: "unsorted",
|
|
282
|
-
fix: !fixProvided && autoFixable ? (fixer) => {
|
|
283
|
-
const fixableProps = node.properties.filter((prop) => prop.type === "Property" && !prop.computed && (prop.key.type === "Identifier" || prop.key.type === "Literal") || false);
|
|
284
|
-
if (fixableProps.length < minKeys) {
|
|
285
|
-
return null;
|
|
286
|
-
}
|
|
287
|
-
const sortedText = buildSortedText(fixableProps);
|
|
288
|
-
const firstProp = fixableProps[0];
|
|
289
|
-
const lastProp = fixableProps[fixableProps.length - 1];
|
|
290
|
-
return fixer.replaceTextRange([
|
|
291
|
-
firstProp.range[0],
|
|
292
|
-
lastProp.range[1]
|
|
293
|
-
], sortedText);
|
|
294
|
-
} : null
|
|
295
|
-
});
|
|
296
|
-
fixProvided = true;
|
|
439
|
+
reportWithFix();
|
|
297
440
|
continue;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
node: curr.node.key,
|
|
302
|
-
messageId: "unsorted",
|
|
303
|
-
fix: !fixProvided && autoFixable ? (fixer) => {
|
|
304
|
-
const fixableProps = node.properties.filter((prop) => prop.type === "Property" && !prop.computed && (prop.key.type === "Identifier" || prop.key.type === "Literal") || false);
|
|
305
|
-
if (fixableProps.length < minKeys) {
|
|
306
|
-
return null;
|
|
307
|
-
}
|
|
308
|
-
const sortedText = buildSortedText(fixableProps);
|
|
309
|
-
const firstProp = fixableProps[0];
|
|
310
|
-
const lastProp = fixableProps[fixableProps.length - 1];
|
|
311
|
-
return fixer.replaceTextRange([
|
|
312
|
-
firstProp.range[0],
|
|
313
|
-
lastProp.range[1]
|
|
314
|
-
], sortedText);
|
|
315
|
-
} : null
|
|
316
|
-
});
|
|
317
|
-
fixProvided = true;
|
|
318
|
-
}
|
|
441
|
+
}
|
|
442
|
+
if (prev.isFunction === curr.isFunction && compareKeys(prev.keyName, curr.keyName) > 0) {
|
|
443
|
+
reportWithFix();
|
|
319
444
|
}
|
|
320
445
|
} else {
|
|
321
446
|
if (compareKeys(prev.keyName, curr.keyName) > 0) {
|
|
322
|
-
|
|
323
|
-
node: curr.node.key,
|
|
324
|
-
messageId: "unsorted",
|
|
325
|
-
fix: !fixProvided && autoFixable ? (fixer) => {
|
|
326
|
-
const fixableProps = node.properties.filter((prop) => prop.type === "Property" && !prop.computed && (prop.key.type === "Identifier" || prop.key.type === "Literal") || false);
|
|
327
|
-
if (fixableProps.length < minKeys) {
|
|
328
|
-
return null;
|
|
329
|
-
}
|
|
330
|
-
const sortedText = buildSortedText(fixableProps);
|
|
331
|
-
const firstProp = fixableProps[0];
|
|
332
|
-
const lastProp = fixableProps[fixableProps.length - 1];
|
|
333
|
-
return fixer.replaceTextRange([
|
|
334
|
-
firstProp.range[0],
|
|
335
|
-
lastProp.range[1]
|
|
336
|
-
], sortedText);
|
|
337
|
-
} : null
|
|
338
|
-
});
|
|
339
|
-
fixProvided = true;
|
|
447
|
+
reportWithFix();
|
|
340
448
|
}
|
|
341
449
|
}
|
|
342
450
|
}
|
|
343
451
|
}
|
|
344
452
|
function checkJSXAttributeObject(attr) {
|
|
345
|
-
|
|
346
|
-
|
|
453
|
+
const value = attr.value;
|
|
454
|
+
if (value && value.type === "JSXExpressionContainer") {
|
|
455
|
+
const expr = value.expression;
|
|
456
|
+
if (expr && expr.type === "ObjectExpression") {
|
|
457
|
+
checkObjectExpression(expr);
|
|
458
|
+
}
|
|
347
459
|
}
|
|
348
460
|
}
|
|
349
461
|
function checkJSXOpeningElement(node) {
|
|
350
462
|
const attrs = node.attributes;
|
|
351
|
-
if (attrs.length < minKeys)
|
|
463
|
+
if (attrs.length < minKeys) {
|
|
352
464
|
return;
|
|
353
|
-
|
|
465
|
+
}
|
|
466
|
+
if (attrs.some((a) => a.type !== "JSXAttribute")) {
|
|
354
467
|
return;
|
|
355
|
-
|
|
468
|
+
}
|
|
469
|
+
if (attrs.some((a) => a.type === "JSXAttribute" && a.name.type !== "JSXIdentifier")) {
|
|
356
470
|
return;
|
|
357
|
-
|
|
471
|
+
}
|
|
472
|
+
const names = attrs.map((a) => {
|
|
473
|
+
if (a.type !== "JSXAttribute") {
|
|
474
|
+
return "";
|
|
475
|
+
}
|
|
476
|
+
if (a.name.type !== "JSXIdentifier") {
|
|
477
|
+
return "";
|
|
478
|
+
}
|
|
479
|
+
return a.name.name;
|
|
480
|
+
});
|
|
358
481
|
const cmp = (a, b) => {
|
|
359
482
|
let res = compareKeys(a, b);
|
|
360
|
-
|
|
483
|
+
if (order === "desc") {
|
|
484
|
+
res = -res;
|
|
485
|
+
}
|
|
486
|
+
return res;
|
|
361
487
|
};
|
|
362
488
|
let outOfOrder = false;
|
|
363
489
|
for (let i = 1;i < names.length; i++) {
|
|
364
|
-
|
|
490
|
+
const prevName = names[i - 1];
|
|
491
|
+
const currName = names[i];
|
|
492
|
+
if (!prevName || !currName) {
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
if (cmp(prevName, currName) > 0) {
|
|
365
496
|
outOfOrder = true;
|
|
366
497
|
break;
|
|
367
498
|
}
|
|
368
499
|
}
|
|
369
|
-
if (!outOfOrder)
|
|
500
|
+
if (!outOfOrder) {
|
|
370
501
|
return;
|
|
502
|
+
}
|
|
371
503
|
for (let i = 1;i < attrs.length; i++) {
|
|
372
|
-
const
|
|
504
|
+
const prevAttr = attrs[i - 1];
|
|
505
|
+
const currAttr = attrs[i];
|
|
506
|
+
if (!prevAttr || !currAttr) {
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
const between = sourceCode.text.slice(prevAttr.range[1], currAttr.range[0]);
|
|
373
510
|
if (between.includes("{")) {
|
|
374
511
|
context.report({
|
|
375
|
-
node:
|
|
512
|
+
node: currAttr.type === "JSXAttribute" ? currAttr.name : currAttr,
|
|
376
513
|
messageId: "unsorted"
|
|
377
514
|
});
|
|
378
515
|
return;
|
|
379
516
|
}
|
|
380
517
|
}
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
518
|
+
const sortedAttrs = attrs.slice().sort((a, b) => {
|
|
519
|
+
const aName = a.type === "JSXAttribute" && a.name.type === "JSXIdentifier" ? a.name.name : "";
|
|
520
|
+
const bName = b.type === "JSXAttribute" && b.name.type === "JSXIdentifier" ? b.name.name : "";
|
|
521
|
+
return cmp(aName, bName);
|
|
522
|
+
});
|
|
523
|
+
const firstAttr = attrs[0];
|
|
524
|
+
const lastAttr = attrs[attrs.length - 1];
|
|
525
|
+
if (!firstAttr || !lastAttr) {
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
const replacement = sortedAttrs.map((a) => sourceCode.getText(a)).join(" ");
|
|
385
529
|
context.report({
|
|
386
|
-
node:
|
|
530
|
+
node: firstAttr.type === "JSXAttribute" ? firstAttr.name : firstAttr,
|
|
387
531
|
messageId: "unsorted",
|
|
388
532
|
fix(fixer) {
|
|
389
|
-
return fixer.replaceTextRange([
|
|
533
|
+
return fixer.replaceTextRange([firstAttr.range[0], lastAttr.range[1]], replacement);
|
|
390
534
|
}
|
|
391
535
|
});
|
|
392
536
|
}
|
|
@@ -400,21 +544,21 @@ var sort_keys_fixable_default = {
|
|
|
400
544
|
}
|
|
401
545
|
};
|
|
402
546
|
|
|
403
|
-
// src/rules/no-transition-cssproperties.
|
|
404
|
-
var
|
|
547
|
+
// src/rules/no-transition-cssproperties.ts
|
|
548
|
+
var noTransitionCSSProperties = {
|
|
405
549
|
meta: {
|
|
406
550
|
type: "problem",
|
|
407
551
|
docs: {
|
|
408
|
-
description: "Objects typed as CSSProperties must not include a 'transition' property as it conflicts with react-spring."
|
|
409
|
-
recommended: false
|
|
552
|
+
description: "Objects typed as CSSProperties must not include a 'transition' property as it conflicts with react-spring."
|
|
410
553
|
},
|
|
411
554
|
schema: [],
|
|
412
555
|
messages: {
|
|
413
556
|
forbiddenTransition: "Objects typed as CSSProperties must not include a 'transition' property as it conflicts with react-spring."
|
|
414
557
|
}
|
|
415
558
|
},
|
|
559
|
+
defaultOptions: [],
|
|
416
560
|
create(context) {
|
|
417
|
-
const sourceCode = context.
|
|
561
|
+
const sourceCode = context.sourceCode;
|
|
418
562
|
return {
|
|
419
563
|
VariableDeclarator(node) {
|
|
420
564
|
if (!node.id || node.id.type !== "Identifier" || !node.id.typeAnnotation) {
|
|
@@ -423,10 +567,10 @@ var no_transition_cssproperties_default = {
|
|
|
423
567
|
let isStyleType = false;
|
|
424
568
|
const typeAnnotation = node.id.typeAnnotation.typeAnnotation;
|
|
425
569
|
if (typeAnnotation && typeAnnotation.type === "TSTypeReference") {
|
|
426
|
-
const
|
|
570
|
+
const typeName = typeAnnotation.typeName;
|
|
427
571
|
if (typeName.type === "Identifier" && typeName.name === "CSSProperties") {
|
|
428
572
|
isStyleType = true;
|
|
429
|
-
} else if (typeName.type === "TSQualifiedName" && typeName.right && typeName.right.name === "CSSProperties") {
|
|
573
|
+
} else if (typeName.type === "TSQualifiedName" && typeName.right && typeName.right.type === "Identifier" && typeName.right.name === "CSSProperties") {
|
|
430
574
|
isStyleType = true;
|
|
431
575
|
}
|
|
432
576
|
}
|
|
@@ -439,80 +583,100 @@ var no_transition_cssproperties_default = {
|
|
|
439
583
|
if (!isStyleType) {
|
|
440
584
|
return;
|
|
441
585
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
586
|
+
const init = node.init;
|
|
587
|
+
if (!init || init.type !== "ObjectExpression") {
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
for (const prop of init.properties) {
|
|
591
|
+
if (prop.type !== "Property") {
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
if (prop.computed) {
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
let keyName = null;
|
|
598
|
+
if (prop.key.type === "Identifier") {
|
|
599
|
+
keyName = prop.key.name;
|
|
600
|
+
} else if (prop.key.type === "Literal") {
|
|
601
|
+
if (typeof prop.key.value === "string") {
|
|
602
|
+
keyName = prop.key.value;
|
|
603
|
+
} else {
|
|
454
604
|
keyName = String(prop.key.value);
|
|
455
605
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
}
|
|
462
|
-
}
|
|
606
|
+
}
|
|
607
|
+
if (keyName === "transition") {
|
|
608
|
+
context.report({
|
|
609
|
+
node: prop,
|
|
610
|
+
messageId: "forbiddenTransition"
|
|
611
|
+
});
|
|
612
|
+
}
|
|
463
613
|
}
|
|
464
614
|
}
|
|
465
615
|
};
|
|
466
616
|
}
|
|
467
617
|
};
|
|
468
618
|
|
|
469
|
-
// src/rules/no-explicit-return-types.
|
|
470
|
-
var
|
|
619
|
+
// src/rules/no-explicit-return-types.ts
|
|
620
|
+
var noExplicitReturnTypes = {
|
|
471
621
|
meta: {
|
|
472
622
|
type: "suggestion",
|
|
473
623
|
docs: {
|
|
474
|
-
description: "Disallow explicit return type annotations on functions, except when using type predicates for type guards or inline object literal returns (e.g., style objects)."
|
|
475
|
-
recommended: false
|
|
624
|
+
description: "Disallow explicit return type annotations on functions, except when using type predicates for type guards or inline object literal returns (e.g., style objects)."
|
|
476
625
|
},
|
|
477
626
|
schema: [],
|
|
478
627
|
messages: {
|
|
479
628
|
noExplicitReturnType: "Explicit return types are disallowed; rely on TypeScript's inference instead."
|
|
480
629
|
}
|
|
481
630
|
},
|
|
631
|
+
defaultOptions: [],
|
|
482
632
|
create(context) {
|
|
633
|
+
function hasSingleObjectReturn(body) {
|
|
634
|
+
let returnCount = 0;
|
|
635
|
+
let returnedObject = null;
|
|
636
|
+
for (const stmt of body.body) {
|
|
637
|
+
if (stmt.type === "ReturnStatement") {
|
|
638
|
+
returnCount++;
|
|
639
|
+
const arg = stmt.argument;
|
|
640
|
+
if (arg && arg.type === "ObjectExpression") {
|
|
641
|
+
returnedObject = arg;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
return returnCount === 1 && returnedObject !== null;
|
|
646
|
+
}
|
|
483
647
|
return {
|
|
484
648
|
"FunctionDeclaration, FunctionExpression, ArrowFunctionExpression"(node) {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
649
|
+
const returnType = node.returnType;
|
|
650
|
+
if (!returnType) {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
const typeAnnotation = returnType.typeAnnotation;
|
|
654
|
+
if (typeAnnotation && typeAnnotation.type === "TSTypePredicate") {
|
|
655
|
+
return;
|
|
656
|
+
}
|
|
657
|
+
if (node.type === "ArrowFunctionExpression" && node.expression === true && node.body.type === "ObjectExpression") {
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
if (node.body && node.body.type === "BlockStatement") {
|
|
661
|
+
if (hasSingleObjectReturn(node.body)) {
|
|
491
662
|
return;
|
|
492
663
|
}
|
|
493
|
-
if (node.body && node.body.type === "BlockStatement") {
|
|
494
|
-
const returns = node.body.body.filter((stmt) => stmt.type === "ReturnStatement");
|
|
495
|
-
if (returns.length === 1 && returns[0].argument && returns[0].argument.type === "ObjectExpression") {
|
|
496
|
-
return;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
context.report({
|
|
500
|
-
node: node.returnType,
|
|
501
|
-
messageId: "noExplicitReturnType"
|
|
502
|
-
});
|
|
503
664
|
}
|
|
665
|
+
context.report({
|
|
666
|
+
node: returnType,
|
|
667
|
+
messageId: "noExplicitReturnType"
|
|
668
|
+
});
|
|
504
669
|
}
|
|
505
670
|
};
|
|
506
671
|
}
|
|
507
672
|
};
|
|
508
673
|
|
|
509
|
-
// src/rules/max-jsx-nesting.
|
|
510
|
-
var
|
|
674
|
+
// src/rules/max-jsx-nesting.ts
|
|
675
|
+
var maxJSXNesting = {
|
|
511
676
|
meta: {
|
|
512
677
|
type: "suggestion",
|
|
513
678
|
docs: {
|
|
514
|
-
description: "Warn when JSX elements are nested too deeply, suggesting refactoring into a separate component."
|
|
515
|
-
recommended: false
|
|
679
|
+
description: "Warn when JSX elements are nested too deeply, suggesting refactoring into a separate component."
|
|
516
680
|
},
|
|
517
681
|
schema: [
|
|
518
682
|
{
|
|
@@ -524,8 +688,10 @@ var max_jsx_nesting_default = {
|
|
|
524
688
|
tooDeeplyNested: "JSX element is nested too deeply ({{level}} levels, allowed is {{maxAllowed}} levels). Consider refactoring into a separate component."
|
|
525
689
|
}
|
|
526
690
|
},
|
|
691
|
+
defaultOptions: [1],
|
|
527
692
|
create(context) {
|
|
528
|
-
const
|
|
693
|
+
const option = context.options[0];
|
|
694
|
+
const maxAllowed = typeof option === "number" ? option : 1;
|
|
529
695
|
function getJSXNestingLevel(node) {
|
|
530
696
|
let level = 1;
|
|
531
697
|
let current = node.parent;
|
|
@@ -552,68 +718,74 @@ var max_jsx_nesting_default = {
|
|
|
552
718
|
}
|
|
553
719
|
};
|
|
554
720
|
|
|
555
|
-
// src/rules/seperate-style-files.
|
|
556
|
-
var
|
|
721
|
+
// src/rules/seperate-style-files.ts
|
|
722
|
+
var seperateStyleFiles = {
|
|
557
723
|
meta: {
|
|
558
724
|
type: "suggestion",
|
|
559
725
|
docs: {
|
|
560
|
-
description: "Warn when a component file (.jsx or .tsx) contains a style object typed as CSSProperties. " + "Style objects should be moved to their own file under the style folder."
|
|
561
|
-
recommended: false
|
|
726
|
+
description: "Warn when a component file (.jsx or .tsx) contains a style object typed as CSSProperties. " + "Style objects should be moved to their own file under the style folder."
|
|
562
727
|
},
|
|
563
728
|
schema: [],
|
|
564
729
|
messages: {
|
|
565
730
|
moveToFile: 'Style object "{{name}}" is typed as {{typeName}}. Move it to its own file under the style folder.'
|
|
566
731
|
}
|
|
567
732
|
},
|
|
733
|
+
defaultOptions: [],
|
|
568
734
|
create(context) {
|
|
569
|
-
const filename = context.
|
|
735
|
+
const filename = context.filename;
|
|
570
736
|
if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) {
|
|
571
737
|
return {};
|
|
572
738
|
}
|
|
573
739
|
return {
|
|
574
740
|
VariableDeclarator(node) {
|
|
575
|
-
if (!node.id || node.id.type !== "Identifier")
|
|
741
|
+
if (!node.id || node.id.type !== "Identifier") {
|
|
576
742
|
return;
|
|
743
|
+
}
|
|
577
744
|
const identifier = node.id;
|
|
578
|
-
|
|
745
|
+
const idTypeAnnotation = identifier.typeAnnotation;
|
|
746
|
+
if (!idTypeAnnotation || idTypeAnnotation.type !== "TSTypeAnnotation") {
|
|
579
747
|
return;
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
748
|
+
}
|
|
749
|
+
const typeNode = idTypeAnnotation.typeAnnotation;
|
|
750
|
+
if (!typeNode || typeNode.type !== "TSTypeReference") {
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
const typeNameNode = typeNode.typeName;
|
|
754
|
+
let typeName = null;
|
|
755
|
+
if (typeNameNode.type === "Identifier") {
|
|
756
|
+
typeName = typeNameNode.name;
|
|
757
|
+
} else if (typeNameNode.type === "TSQualifiedName") {
|
|
758
|
+
const right = typeNameNode.right;
|
|
759
|
+
typeName = right.name;
|
|
760
|
+
}
|
|
761
|
+
if (typeName === "CSSProperties") {
|
|
762
|
+
context.report({
|
|
763
|
+
node,
|
|
764
|
+
messageId: "moveToFile",
|
|
765
|
+
data: {
|
|
766
|
+
name: identifier.name,
|
|
767
|
+
typeName
|
|
768
|
+
}
|
|
769
|
+
});
|
|
598
770
|
}
|
|
599
771
|
}
|
|
600
772
|
};
|
|
601
773
|
}
|
|
602
774
|
};
|
|
603
775
|
|
|
604
|
-
// src/rules/no-unnecessary-key.
|
|
605
|
-
var
|
|
776
|
+
// src/rules/no-unnecessary-key.ts
|
|
777
|
+
var noUnnecessaryKey = {
|
|
606
778
|
meta: {
|
|
607
779
|
type: "problem",
|
|
608
780
|
docs: {
|
|
609
|
-
description: "enforce that the key prop is only used on components rendered as part of a mapping"
|
|
610
|
-
recommended: false
|
|
781
|
+
description: "enforce that the key prop is only used on components rendered as part of a mapping"
|
|
611
782
|
},
|
|
612
783
|
schema: [],
|
|
613
784
|
messages: {
|
|
614
785
|
unnecessaryKey: "The key prop should only be used on elements that are directly rendered as part of an array mapping."
|
|
615
786
|
}
|
|
616
787
|
},
|
|
788
|
+
defaultOptions: [],
|
|
617
789
|
create(context) {
|
|
618
790
|
function getAncestors(node) {
|
|
619
791
|
const ancestors = [];
|
|
@@ -625,23 +797,38 @@ var no_unnecessary_key_default = {
|
|
|
625
797
|
return ancestors;
|
|
626
798
|
}
|
|
627
799
|
function isInsideMapCall(ancestors) {
|
|
628
|
-
|
|
629
|
-
if (node.type === "CallExpression" && node.callee
|
|
800
|
+
for (const node of ancestors) {
|
|
801
|
+
if (node.type === "CallExpression" && node.callee.type === "MemberExpression") {
|
|
630
802
|
const property = node.callee.property;
|
|
631
|
-
|
|
803
|
+
if (property.type === "Identifier" && property.name === "map") {
|
|
804
|
+
return true;
|
|
805
|
+
}
|
|
806
|
+
if (property.type === "Literal" && property.value === "map") {
|
|
807
|
+
return true;
|
|
808
|
+
}
|
|
632
809
|
}
|
|
633
|
-
|
|
634
|
-
|
|
810
|
+
}
|
|
811
|
+
return false;
|
|
635
812
|
}
|
|
636
813
|
function isReturnedFromFunction(ancestors) {
|
|
637
|
-
|
|
814
|
+
for (const node of ancestors) {
|
|
815
|
+
if (node.type === "ReturnStatement") {
|
|
816
|
+
return true;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
return false;
|
|
638
820
|
}
|
|
639
821
|
function checkJSXOpeningElement(node) {
|
|
640
|
-
const keyAttribute = node.attributes.find((attr) => attr.type === "JSXAttribute" && attr.name && attr.name.name === "key");
|
|
822
|
+
const keyAttribute = node.attributes.find((attr) => attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && attr.name.name === "key");
|
|
641
823
|
if (!keyAttribute) {
|
|
642
824
|
return;
|
|
643
825
|
}
|
|
644
|
-
|
|
826
|
+
let ancestors;
|
|
827
|
+
if (typeof context.getAncestors === "function") {
|
|
828
|
+
ancestors = context.getAncestors();
|
|
829
|
+
} else {
|
|
830
|
+
ancestors = getAncestors(node);
|
|
831
|
+
}
|
|
645
832
|
if (isInsideMapCall(ancestors)) {
|
|
646
833
|
return;
|
|
647
834
|
}
|
|
@@ -659,14 +846,12 @@ var no_unnecessary_key_default = {
|
|
|
659
846
|
}
|
|
660
847
|
};
|
|
661
848
|
|
|
662
|
-
// src/rules/sort-exports.
|
|
663
|
-
var
|
|
849
|
+
// src/rules/sort-exports.ts
|
|
850
|
+
var sortExports = {
|
|
664
851
|
meta: {
|
|
665
852
|
type: "suggestion",
|
|
666
853
|
docs: {
|
|
667
|
-
description: "Enforce that top-level export declarations are sorted by exported name and, optionally, that variable exports come before function exports"
|
|
668
|
-
category: "Stylistic Issues",
|
|
669
|
-
recommended: false
|
|
854
|
+
description: "Enforce that top-level export declarations are sorted by exported name and, optionally, that variable exports come before function exports"
|
|
670
855
|
},
|
|
671
856
|
fixable: "code",
|
|
672
857
|
schema: [
|
|
@@ -674,6 +859,7 @@ var sort_exports_default = {
|
|
|
674
859
|
type: "object",
|
|
675
860
|
properties: {
|
|
676
861
|
order: {
|
|
862
|
+
type: "string",
|
|
677
863
|
enum: ["asc", "desc"]
|
|
678
864
|
},
|
|
679
865
|
caseSensitive: {
|
|
@@ -698,14 +884,15 @@ var sort_exports_default = {
|
|
|
698
884
|
variablesBeforeFunctions: "Non-function exports should come before function exports."
|
|
699
885
|
}
|
|
700
886
|
},
|
|
887
|
+
defaultOptions: [{}],
|
|
701
888
|
create(context) {
|
|
702
|
-
const sourceCode = context.
|
|
703
|
-
const
|
|
704
|
-
const order =
|
|
705
|
-
const caseSensitive =
|
|
706
|
-
const natural =
|
|
707
|
-
const minKeys =
|
|
708
|
-
const variablesBeforeFunctions =
|
|
889
|
+
const sourceCode = context.sourceCode;
|
|
890
|
+
const option = context.options[0];
|
|
891
|
+
const order = option && option.order ? option.order : "asc";
|
|
892
|
+
const caseSensitive = option && typeof option.caseSensitive === "boolean" ? option.caseSensitive : false;
|
|
893
|
+
const natural = option && typeof option.natural === "boolean" ? option.natural : false;
|
|
894
|
+
const minKeys = option && typeof option.minKeys === "number" ? option.minKeys : 2;
|
|
895
|
+
const variablesBeforeFunctions = option && typeof option.variablesBeforeFunctions === "boolean" ? option.variablesBeforeFunctions : false;
|
|
709
896
|
function generateExportText(node) {
|
|
710
897
|
return sourceCode.getText(node).trim().replace(/\s*;?\s*$/, ";");
|
|
711
898
|
}
|
|
@@ -716,48 +903,66 @@ var sort_exports_default = {
|
|
|
716
903
|
strA = strA.toLowerCase();
|
|
717
904
|
strB = strB.toLowerCase();
|
|
718
905
|
}
|
|
719
|
-
|
|
906
|
+
const cmp = natural ? strA.localeCompare(strB, undefined, { numeric: true }) : strA.localeCompare(strB);
|
|
720
907
|
return order === "asc" ? cmp : -cmp;
|
|
721
908
|
}
|
|
722
909
|
function getExportName(node) {
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
if (
|
|
726
|
-
if (
|
|
727
|
-
const
|
|
728
|
-
if (id.type === "Identifier") {
|
|
729
|
-
return id.name;
|
|
910
|
+
const declaration = node.declaration;
|
|
911
|
+
if (declaration) {
|
|
912
|
+
if (declaration.type === "VariableDeclaration") {
|
|
913
|
+
if (declaration.declarations.length === 1) {
|
|
914
|
+
const firstDeclarator = declaration.declarations[0];
|
|
915
|
+
if (firstDeclarator && firstDeclarator.id.type === "Identifier") {
|
|
916
|
+
return firstDeclarator.id.name;
|
|
730
917
|
}
|
|
731
918
|
}
|
|
732
|
-
} else if (
|
|
733
|
-
|
|
734
|
-
|
|
919
|
+
} else if (declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") {
|
|
920
|
+
const id = declaration.id;
|
|
921
|
+
if (id && id.type === "Identifier") {
|
|
922
|
+
return id.name;
|
|
735
923
|
}
|
|
736
924
|
}
|
|
737
|
-
} else if (node.specifiers
|
|
925
|
+
} else if (node.specifiers.length === 1) {
|
|
738
926
|
const spec = node.specifiers[0];
|
|
739
|
-
|
|
927
|
+
if (!spec) {
|
|
928
|
+
return null;
|
|
929
|
+
}
|
|
930
|
+
if (spec.exported.type === "Identifier") {
|
|
931
|
+
return spec.exported.name;
|
|
932
|
+
}
|
|
933
|
+
if (spec.exported.type === "Literal" && typeof spec.exported.value === "string") {
|
|
934
|
+
return spec.exported.value;
|
|
935
|
+
}
|
|
740
936
|
}
|
|
741
937
|
return null;
|
|
742
938
|
}
|
|
743
939
|
function isFunctionExport(node) {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
940
|
+
const declaration = node.declaration;
|
|
941
|
+
if (!declaration) {
|
|
942
|
+
return false;
|
|
943
|
+
}
|
|
944
|
+
if (declaration.type === "VariableDeclaration") {
|
|
945
|
+
if (declaration.declarations.length === 1) {
|
|
946
|
+
const firstDeclarator = declaration.declarations[0];
|
|
947
|
+
if (!firstDeclarator) {
|
|
948
|
+
return false;
|
|
949
|
+
}
|
|
950
|
+
const init = firstDeclarator.init;
|
|
951
|
+
if (!init) {
|
|
952
|
+
return false;
|
|
953
|
+
}
|
|
954
|
+
return init.type === "FunctionExpression" || init.type === "ArrowFunctionExpression";
|
|
753
955
|
}
|
|
754
956
|
return false;
|
|
755
957
|
}
|
|
958
|
+
if (declaration.type === "FunctionDeclaration") {
|
|
959
|
+
return true;
|
|
960
|
+
}
|
|
756
961
|
return false;
|
|
757
962
|
}
|
|
758
963
|
function sortComparator(a, b) {
|
|
759
|
-
const kindA = a.node.exportKind
|
|
760
|
-
const kindB = b.node.exportKind
|
|
964
|
+
const kindA = a.node.exportKind ?? "value";
|
|
965
|
+
const kindB = b.node.exportKind ?? "value";
|
|
761
966
|
if (kindA !== kindB) {
|
|
762
967
|
return kindA === "type" ? -1 : 1;
|
|
763
968
|
}
|
|
@@ -768,33 +973,11 @@ var sort_exports_default = {
|
|
|
768
973
|
}
|
|
769
974
|
return compareStrings(a.name, b.name);
|
|
770
975
|
}
|
|
771
|
-
function hasForwardDependency(node, laterNames
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
return false;
|
|
777
|
-
}
|
|
778
|
-
visited.add(node);
|
|
779
|
-
if (node.type === "Identifier" && laterNames.has(node.name)) {
|
|
780
|
-
return true;
|
|
781
|
-
}
|
|
782
|
-
for (const key in node) {
|
|
783
|
-
if (Object.prototype.hasOwnProperty.call(node, key)) {
|
|
784
|
-
const value = node[key];
|
|
785
|
-
if (Array.isArray(value)) {
|
|
786
|
-
for (const element of value) {
|
|
787
|
-
if (element && typeof element === "object") {
|
|
788
|
-
if (hasForwardDependency(element, laterNames, visited)) {
|
|
789
|
-
return true;
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
} else if (value && typeof value === "object") {
|
|
794
|
-
if (hasForwardDependency(value, laterNames, visited)) {
|
|
795
|
-
return true;
|
|
796
|
-
}
|
|
797
|
-
}
|
|
976
|
+
function hasForwardDependency(node, laterNames) {
|
|
977
|
+
const text = sourceCode.getText(node);
|
|
978
|
+
for (const name of laterNames) {
|
|
979
|
+
if (text.includes(name)) {
|
|
980
|
+
return true;
|
|
798
981
|
}
|
|
799
982
|
}
|
|
800
983
|
return false;
|
|
@@ -803,18 +986,19 @@ var sort_exports_default = {
|
|
|
803
986
|
if (block.length < minKeys) {
|
|
804
987
|
return;
|
|
805
988
|
}
|
|
806
|
-
const items =
|
|
989
|
+
const items = [];
|
|
990
|
+
for (const node of block) {
|
|
807
991
|
const name = getExportName(node);
|
|
808
|
-
if (name
|
|
809
|
-
|
|
992
|
+
if (!name) {
|
|
993
|
+
continue;
|
|
810
994
|
}
|
|
811
|
-
|
|
995
|
+
items.push({
|
|
812
996
|
name,
|
|
813
997
|
node,
|
|
814
998
|
isFunction: isFunctionExport(node),
|
|
815
999
|
text: sourceCode.getText(node)
|
|
816
|
-
};
|
|
817
|
-
}
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
818
1002
|
if (items.length < minKeys) {
|
|
819
1003
|
return;
|
|
820
1004
|
}
|
|
@@ -822,9 +1006,14 @@ var sort_exports_default = {
|
|
|
822
1006
|
let reportNeeded = false;
|
|
823
1007
|
let messageId = "alphabetical";
|
|
824
1008
|
for (let i = 1;i < items.length; i++) {
|
|
825
|
-
|
|
1009
|
+
const prev = items[i - 1];
|
|
1010
|
+
const current = items[i];
|
|
1011
|
+
if (!prev || !current) {
|
|
1012
|
+
continue;
|
|
1013
|
+
}
|
|
1014
|
+
if (sortComparator(prev, current) > 0) {
|
|
826
1015
|
reportNeeded = true;
|
|
827
|
-
if (variablesBeforeFunctions &&
|
|
1016
|
+
if (variablesBeforeFunctions && prev.isFunction && !current.isFunction) {
|
|
828
1017
|
messageId = "variablesBeforeFunctions";
|
|
829
1018
|
}
|
|
830
1019
|
break;
|
|
@@ -835,66 +1024,85 @@ var sort_exports_default = {
|
|
|
835
1024
|
}
|
|
836
1025
|
const exportNames = items.map((item) => item.name);
|
|
837
1026
|
for (let i = 0;i < items.length; i++) {
|
|
1027
|
+
const item = items[i];
|
|
1028
|
+
if (!item) {
|
|
1029
|
+
continue;
|
|
1030
|
+
}
|
|
838
1031
|
const laterNames = new Set(exportNames.slice(i + 1));
|
|
839
|
-
const nodeToCheck =
|
|
1032
|
+
const nodeToCheck = item.node.declaration ?? item.node;
|
|
840
1033
|
if (hasForwardDependency(nodeToCheck, laterNames)) {
|
|
841
1034
|
return;
|
|
842
1035
|
}
|
|
843
1036
|
}
|
|
844
1037
|
const expectedOrder = sortedItems.map((item) => item.name).join(", ");
|
|
1038
|
+
const firstNode = block[0];
|
|
1039
|
+
const lastNode = block[block.length - 1];
|
|
1040
|
+
if (!firstNode || !lastNode) {
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
845
1043
|
context.report({
|
|
846
|
-
node:
|
|
1044
|
+
node: firstNode,
|
|
847
1045
|
messageId,
|
|
848
1046
|
data: {
|
|
849
1047
|
expectedOrder
|
|
850
1048
|
},
|
|
851
1049
|
fix(fixer) {
|
|
852
|
-
const fixableNodes =
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
1050
|
+
const fixableNodes = [];
|
|
1051
|
+
for (const n of block) {
|
|
1052
|
+
const declaration = n.declaration;
|
|
1053
|
+
if (declaration) {
|
|
1054
|
+
if (declaration.type === "VariableDeclaration" && declaration.declarations.length === 1) {
|
|
1055
|
+
const firstDecl = declaration.declarations[0];
|
|
1056
|
+
if (firstDecl && firstDecl.id.type === "Identifier") {
|
|
1057
|
+
fixableNodes.push(n);
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
856
1060
|
}
|
|
857
|
-
if ((
|
|
858
|
-
|
|
1061
|
+
if ((declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") && declaration.id && declaration.id.type === "Identifier") {
|
|
1062
|
+
fixableNodes.push(n);
|
|
1063
|
+
continue;
|
|
859
1064
|
}
|
|
860
|
-
|
|
1065
|
+
continue;
|
|
861
1066
|
}
|
|
862
|
-
if (n.specifiers
|
|
863
|
-
|
|
1067
|
+
if (n.specifiers.length === 1) {
|
|
1068
|
+
fixableNodes.push(n);
|
|
864
1069
|
}
|
|
865
|
-
|
|
866
|
-
});
|
|
1070
|
+
}
|
|
867
1071
|
if (fixableNodes.length < minKeys) {
|
|
868
1072
|
return null;
|
|
869
1073
|
}
|
|
870
1074
|
const sortedText = sortedItems.map((item) => generateExportText(item.node)).join(`
|
|
871
1075
|
`);
|
|
872
|
-
const
|
|
873
|
-
const
|
|
874
|
-
const
|
|
1076
|
+
const rangeStart = firstNode.range[0];
|
|
1077
|
+
const rangeEnd = lastNode.range[1];
|
|
1078
|
+
const fullText = sourceCode.getText();
|
|
1079
|
+
const originalText = fullText.slice(rangeStart, rangeEnd);
|
|
875
1080
|
if (originalText === sortedText) {
|
|
876
1081
|
return null;
|
|
877
1082
|
}
|
|
878
|
-
return fixer.replaceTextRange([
|
|
1083
|
+
return fixer.replaceTextRange([rangeStart, rangeEnd], sortedText);
|
|
879
1084
|
}
|
|
880
1085
|
});
|
|
881
1086
|
}
|
|
882
1087
|
return {
|
|
883
1088
|
"Program:exit"(node) {
|
|
884
1089
|
const body = node.body;
|
|
885
|
-
|
|
1090
|
+
const block = [];
|
|
886
1091
|
for (let i = 0;i < body.length; i++) {
|
|
887
|
-
const
|
|
888
|
-
if (
|
|
889
|
-
|
|
1092
|
+
const stmt = body[i];
|
|
1093
|
+
if (!stmt) {
|
|
1094
|
+
continue;
|
|
1095
|
+
}
|
|
1096
|
+
if (stmt.type === "ExportNamedDeclaration" && !stmt.source && getExportName(stmt) !== null) {
|
|
1097
|
+
block.push(stmt);
|
|
890
1098
|
} else {
|
|
891
|
-
if (block.length) {
|
|
1099
|
+
if (block.length > 0) {
|
|
892
1100
|
processExportBlock(block);
|
|
893
|
-
block =
|
|
1101
|
+
block.length = 0;
|
|
894
1102
|
}
|
|
895
1103
|
}
|
|
896
1104
|
}
|
|
897
|
-
if (block.length) {
|
|
1105
|
+
if (block.length > 0) {
|
|
898
1106
|
processExportBlock(block);
|
|
899
1107
|
}
|
|
900
1108
|
}
|
|
@@ -902,47 +1110,73 @@ var sort_exports_default = {
|
|
|
902
1110
|
}
|
|
903
1111
|
};
|
|
904
1112
|
|
|
905
|
-
// src/rules/localize-react-props.
|
|
906
|
-
|
|
1113
|
+
// src/rules/localize-react-props.ts
|
|
1114
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES2 } from "@typescript-eslint/utils";
|
|
1115
|
+
var localizeReactProps = {
|
|
907
1116
|
meta: {
|
|
908
1117
|
type: "suggestion",
|
|
909
1118
|
docs: {
|
|
910
|
-
description: "Disallow variables that are only passed to a single custom child component. For useState, only report if both the state and its setter are exclusively passed to a single custom child. For general variables, only report if a given child receives exactly one such candidate \u2013 if two or more are passed to the same component type, they\u2019re assumed to be settings that belong on the parent."
|
|
911
|
-
|
|
912
|
-
|
|
1119
|
+
description: "Disallow variables that are only passed to a single custom child component. For useState, only report if both the state and its setter are exclusively passed to a single custom child. For general variables, only report if a given child receives exactly one such candidate \u2013 if two or more are passed to the same component type, they\u2019re assumed to be settings that belong on the parent."
|
|
1120
|
+
},
|
|
1121
|
+
schema: [],
|
|
1122
|
+
messages: {
|
|
1123
|
+
stateAndSetterToChild: "State variable '{{stateVarName}}' and its setter '{{setterVarName}}' are only passed to a single custom child component. Consider moving the state into that component.",
|
|
1124
|
+
variableToChild: "Variable '{{varName}}' is only passed to a single custom child component. Consider moving it to that component."
|
|
913
1125
|
}
|
|
914
1126
|
},
|
|
1127
|
+
defaultOptions: [],
|
|
915
1128
|
create(context) {
|
|
916
1129
|
const candidateVariables = [];
|
|
917
|
-
function
|
|
918
|
-
|
|
919
|
-
return
|
|
1130
|
+
function getSingleSetElement(set) {
|
|
1131
|
+
for (const value of set) {
|
|
1132
|
+
return value;
|
|
920
1133
|
}
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
1134
|
+
return null;
|
|
1135
|
+
}
|
|
1136
|
+
function getRightmostJSXIdentifier(name) {
|
|
1137
|
+
let current = name;
|
|
1138
|
+
while (current.type === AST_NODE_TYPES2.JSXMemberExpression) {
|
|
1139
|
+
current = current.property;
|
|
924
1140
|
}
|
|
925
|
-
if (
|
|
926
|
-
|
|
927
|
-
while (current.property) {
|
|
928
|
-
current = current.property;
|
|
929
|
-
}
|
|
930
|
-
if (current && current.type === "JSXIdentifier") {
|
|
931
|
-
return current.name;
|
|
932
|
-
}
|
|
1141
|
+
if (current.type === AST_NODE_TYPES2.JSXIdentifier) {
|
|
1142
|
+
return current;
|
|
933
1143
|
}
|
|
934
|
-
return
|
|
1144
|
+
return null;
|
|
1145
|
+
}
|
|
1146
|
+
function getLeftmostJSXIdentifier(name) {
|
|
1147
|
+
let current = name;
|
|
1148
|
+
while (current.type === AST_NODE_TYPES2.JSXMemberExpression) {
|
|
1149
|
+
current = current.object;
|
|
1150
|
+
}
|
|
1151
|
+
if (current.type === AST_NODE_TYPES2.JSXIdentifier) {
|
|
1152
|
+
return current;
|
|
1153
|
+
}
|
|
1154
|
+
return null;
|
|
1155
|
+
}
|
|
1156
|
+
function getJSXElementName(jsxElement) {
|
|
1157
|
+
if (!jsxElement || !jsxElement.openingElement || !jsxElement.openingElement.name) {
|
|
1158
|
+
return "";
|
|
1159
|
+
}
|
|
1160
|
+
const nameNode = jsxElement.openingElement.name;
|
|
1161
|
+
if (nameNode.type === AST_NODE_TYPES2.JSXIdentifier) {
|
|
1162
|
+
return nameNode.name;
|
|
1163
|
+
}
|
|
1164
|
+
const rightmost = getRightmostJSXIdentifier(nameNode);
|
|
1165
|
+
if (rightmost) {
|
|
1166
|
+
return rightmost.name;
|
|
1167
|
+
}
|
|
1168
|
+
return "";
|
|
935
1169
|
}
|
|
936
1170
|
function isUseStateCall(node) {
|
|
937
|
-
return node && node.type ===
|
|
1171
|
+
return node !== null && node.type === AST_NODE_TYPES2.CallExpression && node.callee !== null && (node.callee.type === AST_NODE_TYPES2.Identifier && node.callee.name === "useState" || node.callee.type === AST_NODE_TYPES2.MemberExpression && node.callee.property !== null && node.callee.property.type === AST_NODE_TYPES2.Identifier && node.callee.property.name === "useState");
|
|
938
1172
|
}
|
|
939
1173
|
function isHookCall(node) {
|
|
940
|
-
return node && node.type ===
|
|
1174
|
+
return node !== null && node.type === AST_NODE_TYPES2.CallExpression && node.callee !== null && node.callee.type === AST_NODE_TYPES2.Identifier && /^use[A-Z]/.test(node.callee.name) && node.callee.name !== "useState";
|
|
941
1175
|
}
|
|
942
1176
|
function getJSXAncestor(node) {
|
|
943
1177
|
let current = node.parent;
|
|
944
1178
|
while (current) {
|
|
945
|
-
if (current.type ===
|
|
1179
|
+
if (current.type === AST_NODE_TYPES2.JSXElement) {
|
|
946
1180
|
return current;
|
|
947
1181
|
}
|
|
948
1182
|
current = current.parent;
|
|
@@ -952,21 +1186,18 @@ var localize_react_props_default = {
|
|
|
952
1186
|
function isContextProviderValueProp(node) {
|
|
953
1187
|
let current = node.parent;
|
|
954
1188
|
while (current) {
|
|
955
|
-
if (current.type ===
|
|
956
|
-
if (current.parent && current.parent.type ===
|
|
1189
|
+
if (current.type === AST_NODE_TYPES2.JSXAttribute && current.name && current.name.type === AST_NODE_TYPES2.JSXIdentifier && current.name.name === "value") {
|
|
1190
|
+
if (current.parent && current.parent.type === AST_NODE_TYPES2.JSXOpeningElement) {
|
|
957
1191
|
const nameNode = current.parent.name;
|
|
958
|
-
if (nameNode.type ===
|
|
1192
|
+
if (nameNode.type === AST_NODE_TYPES2.JSXIdentifier) {
|
|
959
1193
|
const tagName = nameNode.name;
|
|
960
1194
|
if (tagName.endsWith("Provider") || tagName.endsWith("Context")) {
|
|
961
1195
|
return true;
|
|
962
1196
|
}
|
|
963
|
-
} else
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
}
|
|
968
|
-
if (currentMember && currentMember.type === "JSXIdentifier") {
|
|
969
|
-
if (currentMember.name.endsWith("Provider") || currentMember.name.endsWith("Context")) {
|
|
1197
|
+
} else {
|
|
1198
|
+
const rightmost = getRightmostJSXIdentifier(nameNode);
|
|
1199
|
+
if (rightmost) {
|
|
1200
|
+
if (rightmost.name.endsWith("Provider") || rightmost.name.endsWith("Context")) {
|
|
970
1201
|
return true;
|
|
971
1202
|
}
|
|
972
1203
|
}
|
|
@@ -982,120 +1213,105 @@ var localize_react_props_default = {
|
|
|
982
1213
|
return false;
|
|
983
1214
|
}
|
|
984
1215
|
const nameNode = jsxElement.openingElement.name;
|
|
985
|
-
if (nameNode.type ===
|
|
1216
|
+
if (nameNode.type === AST_NODE_TYPES2.JSXIdentifier) {
|
|
986
1217
|
return /^[A-Z]/.test(nameNode.name);
|
|
987
1218
|
}
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
current = current.object;
|
|
992
|
-
}
|
|
993
|
-
return current.type === "JSXIdentifier" && /^[A-Z]/.test(current.name);
|
|
1219
|
+
const leftmost = getLeftmostJSXIdentifier(nameNode);
|
|
1220
|
+
if (leftmost && /^[A-Z]/.test(leftmost.name)) {
|
|
1221
|
+
return true;
|
|
994
1222
|
}
|
|
995
1223
|
return false;
|
|
996
1224
|
}
|
|
997
1225
|
function getComponentFunction(node) {
|
|
998
1226
|
let current = node;
|
|
999
1227
|
while (current) {
|
|
1000
|
-
if (current.type ===
|
|
1228
|
+
if (current.type === AST_NODE_TYPES2.FunctionDeclaration || current.type === AST_NODE_TYPES2.FunctionExpression || current.type === AST_NODE_TYPES2.ArrowFunctionExpression) {
|
|
1001
1229
|
return current;
|
|
1002
1230
|
}
|
|
1003
1231
|
current = current.parent;
|
|
1004
1232
|
}
|
|
1005
1233
|
return null;
|
|
1006
1234
|
}
|
|
1007
|
-
function
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1235
|
+
function findVariableForIdentifier(id) {
|
|
1236
|
+
let scope = context.sourceCode.getScope(id);
|
|
1237
|
+
while (scope) {
|
|
1238
|
+
for (const variable of scope.variables) {
|
|
1239
|
+
for (const def of variable.defs) {
|
|
1240
|
+
if (def.name === id) {
|
|
1241
|
+
return variable;
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1015
1244
|
}
|
|
1016
|
-
|
|
1017
|
-
stack.push(componentFunction.body);
|
|
1245
|
+
scope = scope.upper ?? null;
|
|
1018
1246
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1247
|
+
return null;
|
|
1248
|
+
}
|
|
1249
|
+
function analyzeVariableUsage(declarationId) {
|
|
1250
|
+
const variable = findVariableForIdentifier(declarationId);
|
|
1251
|
+
if (!variable) {
|
|
1252
|
+
return {
|
|
1253
|
+
jsxUsageSet: new Set,
|
|
1254
|
+
hasOutsideUsage: false
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
const jsxUsageSet = new Set;
|
|
1258
|
+
let hasOutsideUsage = false;
|
|
1259
|
+
for (const reference of variable.references) {
|
|
1260
|
+
const identifier = reference.identifier;
|
|
1261
|
+
if (identifier === declarationId) {
|
|
1022
1262
|
continue;
|
|
1023
|
-
if (currentNode.type === "Identifier" && currentNode.name === varName && currentNode !== declarationNode) {
|
|
1024
|
-
if (isContextProviderValueProp(currentNode)) {} else {
|
|
1025
|
-
const jsxAncestor = getJSXAncestor(currentNode);
|
|
1026
|
-
if (jsxAncestor && isCustomJSXElement(jsxAncestor)) {
|
|
1027
|
-
usage.jsxUsageSet.add(jsxAncestor);
|
|
1028
|
-
} else {
|
|
1029
|
-
usage.hasOutsideUsage = true;
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
1263
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
let shadows = false;
|
|
1036
|
-
if (currentNode.params && currentNode.params.length > 0) {
|
|
1037
|
-
for (let i = 0;i < currentNode.params.length; i++) {
|
|
1038
|
-
const param = currentNode.params[i];
|
|
1039
|
-
if (param.type === "Identifier" && param.name === varName) {
|
|
1040
|
-
shadows = true;
|
|
1041
|
-
break;
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1045
|
-
if (shadows)
|
|
1046
|
-
continue;
|
|
1264
|
+
if (isContextProviderValueProp(identifier)) {
|
|
1265
|
+
continue;
|
|
1047
1266
|
}
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
for (let j = 0;j < child.length; j++) {
|
|
1054
|
-
if (child[j] && typeof child[j].type === "string") {
|
|
1055
|
-
stack.push(child[j]);
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
} else if (child && typeof child.type === "string") {
|
|
1059
|
-
stack.push(child);
|
|
1060
|
-
}
|
|
1267
|
+
const jsxAncestor = getJSXAncestor(identifier);
|
|
1268
|
+
if (jsxAncestor && isCustomJSXElement(jsxAncestor)) {
|
|
1269
|
+
jsxUsageSet.add(jsxAncestor);
|
|
1270
|
+
} else {
|
|
1271
|
+
hasOutsideUsage = true;
|
|
1061
1272
|
}
|
|
1062
1273
|
}
|
|
1063
|
-
return
|
|
1274
|
+
return {
|
|
1275
|
+
jsxUsageSet,
|
|
1276
|
+
hasOutsideUsage
|
|
1277
|
+
};
|
|
1064
1278
|
}
|
|
1065
1279
|
const componentHookVars = new WeakMap;
|
|
1066
1280
|
function getHookSet(componentFunction) {
|
|
1067
|
-
|
|
1068
|
-
|
|
1281
|
+
let hookSet = componentHookVars.get(componentFunction);
|
|
1282
|
+
if (!hookSet) {
|
|
1283
|
+
hookSet = new Set;
|
|
1284
|
+
componentHookVars.set(componentFunction, hookSet);
|
|
1069
1285
|
}
|
|
1070
|
-
return
|
|
1286
|
+
return hookSet;
|
|
1071
1287
|
}
|
|
1072
1288
|
function hasHookDependency(node, hookSet) {
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1289
|
+
if (!node.range) {
|
|
1290
|
+
return false;
|
|
1291
|
+
}
|
|
1292
|
+
const nodeRange = node.range;
|
|
1293
|
+
const nodeStart = nodeRange[0];
|
|
1294
|
+
const nodeEnd = nodeRange[1];
|
|
1295
|
+
let scope = context.sourceCode.getScope(node);
|
|
1296
|
+
while (scope) {
|
|
1297
|
+
for (const variable of scope.variables) {
|
|
1298
|
+
if (!hookSet.has(variable.name)) {
|
|
1299
|
+
continue;
|
|
1083
1300
|
}
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1301
|
+
for (const reference of variable.references) {
|
|
1302
|
+
const identifier = reference.identifier;
|
|
1303
|
+
if (!identifier.range) {
|
|
1304
|
+
continue;
|
|
1305
|
+
}
|
|
1306
|
+
const refRange = identifier.range;
|
|
1307
|
+
const refStart = refRange[0];
|
|
1308
|
+
const refEnd = refRange[1];
|
|
1309
|
+
if (refStart >= nodeStart && refEnd <= nodeEnd) {
|
|
1310
|
+
return true;
|
|
1094
1311
|
}
|
|
1095
|
-
} else if (child && typeof child.type === "string") {
|
|
1096
|
-
stack.push(child);
|
|
1097
1312
|
}
|
|
1098
1313
|
}
|
|
1314
|
+
scope = scope.upper ?? null;
|
|
1099
1315
|
}
|
|
1100
1316
|
return false;
|
|
1101
1317
|
}
|
|
@@ -1104,30 +1320,34 @@ var localize_react_props_default = {
|
|
|
1104
1320
|
const componentFunction = getComponentFunction(node);
|
|
1105
1321
|
if (!componentFunction || !componentFunction.body)
|
|
1106
1322
|
return;
|
|
1107
|
-
if (node.init && node.id && node.id.type ===
|
|
1323
|
+
if (node.init && node.id && node.id.type === AST_NODE_TYPES2.Identifier && node.init.type === AST_NODE_TYPES2.CallExpression && isHookCall(node.init)) {
|
|
1108
1324
|
const hookSet = getHookSet(componentFunction);
|
|
1109
1325
|
hookSet.add(node.id.name);
|
|
1110
1326
|
}
|
|
1111
|
-
if (node.init && isUseStateCall(node.init) && node.id.type ===
|
|
1327
|
+
if (node.init && isUseStateCall(node.init) && node.id.type === AST_NODE_TYPES2.ArrayPattern && node.id.elements.length >= 2) {
|
|
1112
1328
|
const stateElem = node.id.elements[0];
|
|
1113
1329
|
const setterElem = node.id.elements[1];
|
|
1114
|
-
if (!stateElem || stateElem.type !==
|
|
1330
|
+
if (!stateElem || stateElem.type !== AST_NODE_TYPES2.Identifier || !setterElem || setterElem.type !== AST_NODE_TYPES2.Identifier) {
|
|
1115
1331
|
return;
|
|
1116
1332
|
}
|
|
1117
1333
|
const stateVarName = stateElem.name;
|
|
1118
1334
|
const setterVarName = setterElem.name;
|
|
1119
|
-
const stateUsage = analyzeVariableUsage(stateElem
|
|
1120
|
-
const setterUsage = analyzeVariableUsage(setterElem
|
|
1335
|
+
const stateUsage = analyzeVariableUsage(stateElem);
|
|
1336
|
+
const setterUsage = analyzeVariableUsage(setterElem);
|
|
1121
1337
|
const stateExclusivelySingleJSX = !stateUsage.hasOutsideUsage && stateUsage.jsxUsageSet.size === 1;
|
|
1122
1338
|
const setterExclusivelySingleJSX = !setterUsage.hasOutsideUsage && setterUsage.jsxUsageSet.size === 1;
|
|
1123
|
-
if (stateExclusivelySingleJSX && setterExclusivelySingleJSX
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1339
|
+
if (stateExclusivelySingleJSX && setterExclusivelySingleJSX) {
|
|
1340
|
+
const stateTarget = getSingleSetElement(stateUsage.jsxUsageSet);
|
|
1341
|
+
const setterTarget = getSingleSetElement(setterUsage.jsxUsageSet);
|
|
1342
|
+
if (stateTarget && stateTarget === setterTarget) {
|
|
1343
|
+
context.report({
|
|
1344
|
+
node,
|
|
1345
|
+
messageId: "stateAndSetterToChild",
|
|
1346
|
+
data: { stateVarName, setterVarName }
|
|
1347
|
+
});
|
|
1348
|
+
}
|
|
1129
1349
|
}
|
|
1130
|
-
} else if (node.id && node.id.type ===
|
|
1350
|
+
} else if (node.id && node.id.type === AST_NODE_TYPES2.Identifier) {
|
|
1131
1351
|
const varName = node.id.name;
|
|
1132
1352
|
if (node.init) {
|
|
1133
1353
|
const hookSet = getHookSet(componentFunction);
|
|
@@ -1135,9 +1355,9 @@ var localize_react_props_default = {
|
|
|
1135
1355
|
return;
|
|
1136
1356
|
}
|
|
1137
1357
|
}
|
|
1138
|
-
const usage = analyzeVariableUsage(node.id
|
|
1358
|
+
const usage = analyzeVariableUsage(node.id);
|
|
1139
1359
|
if (!usage.hasOutsideUsage && usage.jsxUsageSet.size === 1) {
|
|
1140
|
-
const target =
|
|
1360
|
+
const target = getSingleSetElement(usage.jsxUsageSet);
|
|
1141
1361
|
const componentName = getJSXElementName(target);
|
|
1142
1362
|
candidateVariables.push({
|
|
1143
1363
|
node,
|
|
@@ -1149,244 +1369,367 @@ var localize_react_props_default = {
|
|
|
1149
1369
|
},
|
|
1150
1370
|
"Program:exit"() {
|
|
1151
1371
|
const groups = new Map;
|
|
1152
|
-
|
|
1372
|
+
for (const candidate of candidateVariables) {
|
|
1153
1373
|
const key = candidate.componentName;
|
|
1154
|
-
|
|
1155
|
-
|
|
1374
|
+
const existing = groups.get(key);
|
|
1375
|
+
if (existing) {
|
|
1376
|
+
existing.push(candidate);
|
|
1377
|
+
} else {
|
|
1378
|
+
groups.set(key, [candidate]);
|
|
1156
1379
|
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
groups.forEach((candidates) => {
|
|
1380
|
+
}
|
|
1381
|
+
for (const candidates of groups.values()) {
|
|
1160
1382
|
if (candidates.length === 1) {
|
|
1161
1383
|
const candidate = candidates[0];
|
|
1384
|
+
if (!candidate) {
|
|
1385
|
+
continue;
|
|
1386
|
+
}
|
|
1162
1387
|
context.report({
|
|
1163
1388
|
node: candidate.node,
|
|
1164
|
-
|
|
1389
|
+
messageId: "variableToChild",
|
|
1165
1390
|
data: { varName: candidate.varName }
|
|
1166
1391
|
});
|
|
1167
1392
|
}
|
|
1168
|
-
}
|
|
1393
|
+
}
|
|
1169
1394
|
}
|
|
1170
1395
|
};
|
|
1171
1396
|
}
|
|
1172
1397
|
};
|
|
1173
1398
|
|
|
1174
|
-
// src/rules/no-or-none-component.
|
|
1175
|
-
var
|
|
1399
|
+
// src/rules/no-or-none-component.ts
|
|
1400
|
+
var noOrNoneComponent = {
|
|
1176
1401
|
meta: {
|
|
1177
1402
|
type: "suggestion",
|
|
1178
1403
|
docs: {
|
|
1179
|
-
description: "Prefer using logical && operator over ternary with null/undefined for conditional JSX rendering."
|
|
1180
|
-
recommended: false
|
|
1404
|
+
description: "Prefer using logical && operator over ternary with null/undefined for conditional JSX rendering."
|
|
1181
1405
|
},
|
|
1406
|
+
schema: [],
|
|
1182
1407
|
messages: {
|
|
1183
1408
|
useLogicalAnd: "Prefer using the logical '&&' operator instead of a ternary with null/undefined for conditional rendering."
|
|
1184
1409
|
}
|
|
1185
1410
|
},
|
|
1411
|
+
defaultOptions: [],
|
|
1186
1412
|
create(context) {
|
|
1187
1413
|
return {
|
|
1188
1414
|
ConditionalExpression(node) {
|
|
1189
1415
|
const alternate = node.alternate;
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1416
|
+
const isNullAlternate = alternate && alternate.type === "Literal" && alternate.value === null;
|
|
1417
|
+
const isUndefinedAlternate = alternate && alternate.type === "Identifier" && alternate.name === "undefined";
|
|
1418
|
+
if (!isNullAlternate && !isUndefinedAlternate) {
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
const parent = node.parent;
|
|
1422
|
+
if (!parent || parent.type !== "JSXExpressionContainer") {
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
const containerParent = parent.parent;
|
|
1426
|
+
if (containerParent && containerParent.type !== "JSXAttribute") {
|
|
1427
|
+
context.report({
|
|
1428
|
+
node,
|
|
1429
|
+
messageId: "useLogicalAnd"
|
|
1430
|
+
});
|
|
1200
1431
|
}
|
|
1201
1432
|
}
|
|
1202
1433
|
};
|
|
1203
1434
|
}
|
|
1204
1435
|
};
|
|
1205
1436
|
|
|
1206
|
-
// src/rules/no-button-navigation.
|
|
1207
|
-
var
|
|
1437
|
+
// src/rules/no-button-navigation.ts
|
|
1438
|
+
var noButtonNavigation = {
|
|
1208
1439
|
meta: {
|
|
1209
1440
|
type: "suggestion",
|
|
1210
1441
|
docs: {
|
|
1211
|
-
description: "Enforce using anchor tags for navigation instead of buttons whose onClick handlers change the path. Allow only query/hash updates via window.location.search or history.replaceState(window.location.pathname + \u2026)."
|
|
1212
|
-
category: "Best Practices",
|
|
1213
|
-
recommended: false
|
|
1442
|
+
description: "Enforce using anchor tags for navigation instead of buttons whose onClick handlers change the path. Allow only query/hash updates via window.location.search or history.replaceState(window.location.pathname + \u2026)."
|
|
1214
1443
|
},
|
|
1215
|
-
schema: []
|
|
1444
|
+
schema: [],
|
|
1445
|
+
messages: {
|
|
1446
|
+
noButtonNavigation: "Use an anchor tag for navigation instead of a button whose onClick handler changes the path. Detected: {{reason}}. Only query/hash updates (reading window.location.search, .pathname, or .hash) are allowed."
|
|
1447
|
+
}
|
|
1216
1448
|
},
|
|
1449
|
+
defaultOptions: [],
|
|
1217
1450
|
create(context) {
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
const
|
|
1221
|
-
|
|
1222
|
-
|
|
1451
|
+
const handlerStack = [];
|
|
1452
|
+
function getCurrentHandler() {
|
|
1453
|
+
const state = handlerStack[handlerStack.length - 1];
|
|
1454
|
+
if (!state) {
|
|
1455
|
+
return null;
|
|
1456
|
+
}
|
|
1457
|
+
return state;
|
|
1458
|
+
}
|
|
1459
|
+
function isOnClickButtonHandler(node) {
|
|
1460
|
+
const parent = node.parent;
|
|
1461
|
+
if (!parent || parent.type !== "JSXExpressionContainer") {
|
|
1462
|
+
return null;
|
|
1463
|
+
}
|
|
1464
|
+
const attributeCandidate = parent.parent;
|
|
1465
|
+
if (!attributeCandidate || attributeCandidate.type !== "JSXAttribute") {
|
|
1466
|
+
return null;
|
|
1467
|
+
}
|
|
1468
|
+
const attr = attributeCandidate;
|
|
1469
|
+
if (!attr.name || attr.name.type !== "JSXIdentifier" || attr.name.name !== "onClick") {
|
|
1470
|
+
return null;
|
|
1471
|
+
}
|
|
1472
|
+
const openingElementCandidate = attr.parent;
|
|
1473
|
+
if (!openingElementCandidate || openingElementCandidate.type !== "JSXOpeningElement") {
|
|
1474
|
+
return null;
|
|
1475
|
+
}
|
|
1476
|
+
const openingElement = openingElementCandidate;
|
|
1477
|
+
const tagNameNode = openingElement.name;
|
|
1478
|
+
if (tagNameNode.type !== "JSXIdentifier" || tagNameNode.name !== "button") {
|
|
1479
|
+
return null;
|
|
1480
|
+
}
|
|
1481
|
+
return attr;
|
|
1482
|
+
}
|
|
1483
|
+
function isWindowLocationMember(member) {
|
|
1484
|
+
const object = member.object;
|
|
1485
|
+
if (object.type !== "MemberExpression") {
|
|
1486
|
+
return false;
|
|
1487
|
+
}
|
|
1488
|
+
const outerObject = object.object;
|
|
1489
|
+
const outerProperty = object.property;
|
|
1490
|
+
if (outerObject.type === "Identifier" && outerObject.name === "window" && outerProperty.type === "Identifier" && outerProperty.name === "location") {
|
|
1491
|
+
return true;
|
|
1492
|
+
}
|
|
1493
|
+
return false;
|
|
1494
|
+
}
|
|
1495
|
+
function isWindowHistoryMember(member) {
|
|
1496
|
+
const object = member.object;
|
|
1497
|
+
if (object.type !== "MemberExpression") {
|
|
1498
|
+
return false;
|
|
1499
|
+
}
|
|
1500
|
+
const outerObject = object.object;
|
|
1501
|
+
const outerProperty = object.property;
|
|
1502
|
+
if (outerObject.type === "Identifier" && outerObject.name === "window" && outerProperty.type === "Identifier" && outerProperty.name === "history") {
|
|
1503
|
+
return true;
|
|
1504
|
+
}
|
|
1505
|
+
return false;
|
|
1506
|
+
}
|
|
1507
|
+
return {
|
|
1508
|
+
ArrowFunctionExpression(node) {
|
|
1509
|
+
const attr = isOnClickButtonHandler(node);
|
|
1510
|
+
if (!attr) {
|
|
1223
1511
|
return;
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1512
|
+
}
|
|
1513
|
+
handlerStack.push({
|
|
1514
|
+
attribute: attr,
|
|
1515
|
+
reason: null,
|
|
1516
|
+
sawReplaceCall: false,
|
|
1517
|
+
sawAllowedLocationRead: false
|
|
1518
|
+
});
|
|
1519
|
+
},
|
|
1520
|
+
"ArrowFunctionExpression:exit"(node) {
|
|
1521
|
+
const attr = isOnClickButtonHandler(node);
|
|
1522
|
+
if (!attr) {
|
|
1227
1523
|
return;
|
|
1228
1524
|
}
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
const child = n[key];
|
|
1233
|
-
if (Array.isArray(child)) {
|
|
1234
|
-
child.forEach((c) => check(c));
|
|
1235
|
-
} else {
|
|
1236
|
-
check(child);
|
|
1237
|
-
}
|
|
1525
|
+
const state = handlerStack.pop();
|
|
1526
|
+
if (!state) {
|
|
1527
|
+
return;
|
|
1238
1528
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
function inspect(n, parent) {
|
|
1249
|
-
if (reason || !n || typeof n !== "object" || visited.has(n))
|
|
1529
|
+
const reason = state.reason;
|
|
1530
|
+
const sawReplaceCall = state.sawReplaceCall;
|
|
1531
|
+
const sawAllowedLocationRead = state.sawAllowedLocationRead;
|
|
1532
|
+
if (reason) {
|
|
1533
|
+
context.report({
|
|
1534
|
+
node: state.attribute,
|
|
1535
|
+
messageId: "noButtonNavigation",
|
|
1536
|
+
data: { reason }
|
|
1537
|
+
});
|
|
1250
1538
|
return;
|
|
1251
|
-
|
|
1252
|
-
if (
|
|
1253
|
-
|
|
1539
|
+
}
|
|
1540
|
+
if (sawReplaceCall && !sawAllowedLocationRead) {
|
|
1541
|
+
context.report({
|
|
1542
|
+
node: state.attribute,
|
|
1543
|
+
messageId: "noButtonNavigation",
|
|
1544
|
+
data: {
|
|
1545
|
+
reason: "history.replaceState/pushState without reading window.location"
|
|
1546
|
+
}
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
},
|
|
1550
|
+
FunctionExpression(node) {
|
|
1551
|
+
const attr = isOnClickButtonHandler(node);
|
|
1552
|
+
if (!attr) {
|
|
1254
1553
|
return;
|
|
1255
1554
|
}
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1555
|
+
handlerStack.push({
|
|
1556
|
+
attribute: attr,
|
|
1557
|
+
reason: null,
|
|
1558
|
+
sawReplaceCall: false,
|
|
1559
|
+
sawAllowedLocationRead: false
|
|
1560
|
+
});
|
|
1561
|
+
},
|
|
1562
|
+
"FunctionExpression:exit"(node) {
|
|
1563
|
+
const attr = isOnClickButtonHandler(node);
|
|
1564
|
+
if (!attr) {
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1567
|
+
const state = handlerStack.pop();
|
|
1568
|
+
if (!state) {
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
const reason = state.reason;
|
|
1572
|
+
const sawReplaceCall = state.sawReplaceCall;
|
|
1573
|
+
const sawAllowedLocationRead = state.sawAllowedLocationRead;
|
|
1574
|
+
if (reason) {
|
|
1575
|
+
context.report({
|
|
1576
|
+
node: state.attribute,
|
|
1577
|
+
messageId: "noButtonNavigation",
|
|
1578
|
+
data: { reason }
|
|
1579
|
+
});
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1582
|
+
if (sawReplaceCall && !sawAllowedLocationRead) {
|
|
1583
|
+
context.report({
|
|
1584
|
+
node: state.attribute,
|
|
1585
|
+
messageId: "noButtonNavigation",
|
|
1586
|
+
data: {
|
|
1587
|
+
reason: "history.replaceState/pushState without reading window.location"
|
|
1588
|
+
}
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
},
|
|
1592
|
+
MemberExpression(node) {
|
|
1593
|
+
const state = getCurrentHandler();
|
|
1594
|
+
if (!state) {
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
if (node.object.type === "Identifier" && node.object.name === "window" && node.property.type === "Identifier" && node.property.name === "open") {
|
|
1598
|
+
if (!state.reason) {
|
|
1599
|
+
state.reason = "window.open";
|
|
1261
1600
|
}
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1601
|
+
}
|
|
1602
|
+
if (isWindowLocationMember(node) && node.property.type === "Identifier" && (node.property.name === "search" || node.property.name === "pathname" || node.property.name === "hash")) {
|
|
1603
|
+
state.sawAllowedLocationRead = true;
|
|
1604
|
+
}
|
|
1605
|
+
},
|
|
1606
|
+
AssignmentExpression(node) {
|
|
1607
|
+
const state = getCurrentHandler();
|
|
1608
|
+
if (!state) {
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
if (node.left.type !== "MemberExpression") {
|
|
1612
|
+
return;
|
|
1613
|
+
}
|
|
1614
|
+
const left = node.left;
|
|
1615
|
+
if (left.object.type === "Identifier" && left.object.name === "window" && left.property.type === "Identifier" && left.property.name === "location") {
|
|
1616
|
+
if (!state.reason) {
|
|
1617
|
+
state.reason = "assignment to window.location";
|
|
1265
1618
|
}
|
|
1619
|
+
return;
|
|
1266
1620
|
}
|
|
1267
|
-
if (
|
|
1268
|
-
if (
|
|
1269
|
-
reason = "window.location
|
|
1270
|
-
return;
|
|
1621
|
+
if (isWindowLocationMember(left)) {
|
|
1622
|
+
if (!state.reason) {
|
|
1623
|
+
state.reason = "assignment to window.location sub-property";
|
|
1271
1624
|
}
|
|
1272
1625
|
}
|
|
1273
|
-
|
|
1274
|
-
|
|
1626
|
+
},
|
|
1627
|
+
CallExpression(node) {
|
|
1628
|
+
const state = getCurrentHandler();
|
|
1629
|
+
if (!state) {
|
|
1630
|
+
return;
|
|
1275
1631
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1632
|
+
const callee = node.callee;
|
|
1633
|
+
if (callee.type !== "MemberExpression") {
|
|
1634
|
+
return;
|
|
1278
1635
|
}
|
|
1279
|
-
|
|
1280
|
-
if (
|
|
1281
|
-
|
|
1282
|
-
const child = n[key];
|
|
1283
|
-
if (Array.isArray(child)) {
|
|
1284
|
-
child.forEach((c) => inspect(c, n));
|
|
1285
|
-
} else {
|
|
1286
|
-
inspect(child, n);
|
|
1636
|
+
if (isWindowLocationMember(callee) && callee.property.type === "Identifier" && callee.property.name === "replace") {
|
|
1637
|
+
if (!state.reason) {
|
|
1638
|
+
state.reason = "window.location.replace";
|
|
1287
1639
|
}
|
|
1288
|
-
|
|
1289
|
-
return;
|
|
1640
|
+
return;
|
|
1290
1641
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
if (reason) {
|
|
1294
|
-
return { shouldReport: true, reason };
|
|
1295
|
-
}
|
|
1296
|
-
if (sawReplaceCall && !sawAllowedLocationRead) {
|
|
1297
|
-
return {
|
|
1298
|
-
shouldReport: true,
|
|
1299
|
-
reason: "history.replace/pushState without reading window.location"
|
|
1300
|
-
};
|
|
1301
|
-
}
|
|
1302
|
-
return { shouldReport: false, reason: null };
|
|
1303
|
-
}
|
|
1304
|
-
return {
|
|
1305
|
-
JSXElement(node) {
|
|
1306
|
-
const { openingElement } = node;
|
|
1307
|
-
if (openingElement.name.type === "JSXIdentifier" && openingElement.name.name === "button") {
|
|
1308
|
-
for (const attr of openingElement.attributes) {
|
|
1309
|
-
if (attr.type === "JSXAttribute" && attr.name.name === "onClick" && attr.value?.type === "JSXExpressionContainer") {
|
|
1310
|
-
const expr = attr.value.expression;
|
|
1311
|
-
if (expr.type === "ArrowFunctionExpression" || expr.type === "FunctionExpression") {
|
|
1312
|
-
const { shouldReport, reason } = containsWindowNavigation(expr);
|
|
1313
|
-
if (shouldReport) {
|
|
1314
|
-
context.report({
|
|
1315
|
-
node: attr,
|
|
1316
|
-
message: `Use an anchor tag for navigation instead of a button whose onClick handler changes the path. ` + `Detected: ${reason}. Only query/hash updates (reading window.location.search, .pathname, or .hash) are allowed.`
|
|
1317
|
-
});
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1642
|
+
if (isWindowHistoryMember(callee) && callee.property.type === "Identifier" && (callee.property.name === "pushState" || callee.property.name === "replaceState")) {
|
|
1643
|
+
state.sawReplaceCall = true;
|
|
1322
1644
|
}
|
|
1323
1645
|
}
|
|
1324
1646
|
};
|
|
1325
1647
|
}
|
|
1326
1648
|
};
|
|
1327
1649
|
|
|
1328
|
-
// src/rules/no-multi-style-objects.
|
|
1329
|
-
var
|
|
1650
|
+
// src/rules/no-multi-style-objects.ts
|
|
1651
|
+
var noMultiStyleObjects = {
|
|
1330
1652
|
meta: {
|
|
1331
1653
|
type: "problem",
|
|
1332
1654
|
docs: {
|
|
1333
|
-
description: "Disallow grouping CSS style objects in a single export; export each style separately."
|
|
1334
|
-
category: "Best Practices",
|
|
1335
|
-
recommended: false
|
|
1655
|
+
description: "Disallow grouping CSS style objects in a single export; export each style separately."
|
|
1336
1656
|
},
|
|
1337
|
-
schema: []
|
|
1657
|
+
schema: [],
|
|
1658
|
+
messages: {
|
|
1659
|
+
noMultiStyleObjects: "Do not group CSS style objects in a single export; export each style separately."
|
|
1660
|
+
}
|
|
1338
1661
|
},
|
|
1662
|
+
defaultOptions: [],
|
|
1339
1663
|
create(context) {
|
|
1340
1664
|
function checkObjectExpression(node) {
|
|
1341
|
-
if (node.properties
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
if (
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1665
|
+
if (!node.properties.length) {
|
|
1666
|
+
return;
|
|
1667
|
+
}
|
|
1668
|
+
const cssStyleProperties = [];
|
|
1669
|
+
for (const prop of node.properties) {
|
|
1670
|
+
if (prop.type !== "Property") {
|
|
1671
|
+
continue;
|
|
1672
|
+
}
|
|
1673
|
+
const key = prop.key;
|
|
1674
|
+
let name = null;
|
|
1675
|
+
if (key.type === "Identifier") {
|
|
1676
|
+
name = key.name;
|
|
1677
|
+
} else if (key.type === "Literal" && typeof key.value === "string") {
|
|
1678
|
+
name = key.value;
|
|
1679
|
+
}
|
|
1680
|
+
if (name && name.endsWith("Style")) {
|
|
1681
|
+
cssStyleProperties.push(prop);
|
|
1358
1682
|
}
|
|
1359
1683
|
}
|
|
1684
|
+
if (cssStyleProperties.length > 1) {
|
|
1685
|
+
context.report({
|
|
1686
|
+
node,
|
|
1687
|
+
messageId: "noMultiStyleObjects"
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1360
1690
|
}
|
|
1361
1691
|
return {
|
|
1362
1692
|
ExportDefaultDeclaration(node) {
|
|
1363
|
-
|
|
1364
|
-
|
|
1693
|
+
const declaration = node.declaration;
|
|
1694
|
+
if (declaration && declaration.type === "ObjectExpression") {
|
|
1695
|
+
checkObjectExpression(declaration);
|
|
1365
1696
|
}
|
|
1366
1697
|
},
|
|
1367
1698
|
ReturnStatement(node) {
|
|
1368
|
-
|
|
1369
|
-
|
|
1699
|
+
const argument = node.argument;
|
|
1700
|
+
if (argument && argument.type === "ObjectExpression") {
|
|
1701
|
+
checkObjectExpression(argument);
|
|
1370
1702
|
}
|
|
1371
1703
|
}
|
|
1372
1704
|
};
|
|
1373
1705
|
}
|
|
1374
1706
|
};
|
|
1375
1707
|
|
|
1376
|
-
// src/rules/no-useless-function.
|
|
1377
|
-
var
|
|
1708
|
+
// src/rules/no-useless-function.ts
|
|
1709
|
+
var noUselessFunction = {
|
|
1378
1710
|
meta: {
|
|
1379
1711
|
type: "suggestion",
|
|
1380
1712
|
docs: {
|
|
1381
|
-
description: "Disallow functions that have no parameters and just return an object literal; consider exporting the object directly, unless the function is used as a callback (e.g., in react-spring)."
|
|
1382
|
-
category: "Best Practices",
|
|
1383
|
-
recommended: false
|
|
1713
|
+
description: "Disallow functions that have no parameters and just return an object literal; consider exporting the object directly, unless the function is used as a callback (e.g., in react-spring)."
|
|
1384
1714
|
},
|
|
1385
|
-
|
|
1715
|
+
schema: [],
|
|
1716
|
+
messages: {
|
|
1717
|
+
uselessFunction: "This function has no parameters and simply returns an object. Consider exporting the object directly instead of wrapping it in a function."
|
|
1718
|
+
}
|
|
1386
1719
|
},
|
|
1720
|
+
defaultOptions: [],
|
|
1387
1721
|
create(context) {
|
|
1388
1722
|
function isCallbackFunction(node) {
|
|
1389
|
-
|
|
1723
|
+
const parent = node.parent;
|
|
1724
|
+
if (!parent || parent.type !== "CallExpression") {
|
|
1725
|
+
return false;
|
|
1726
|
+
}
|
|
1727
|
+
for (const arg of parent.arguments) {
|
|
1728
|
+
if (arg === node) {
|
|
1729
|
+
return true;
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
return false;
|
|
1390
1733
|
}
|
|
1391
1734
|
return {
|
|
1392
1735
|
ArrowFunctionExpression(node) {
|
|
@@ -1396,7 +1739,7 @@ var no_useless_function_default = {
|
|
|
1396
1739
|
}
|
|
1397
1740
|
context.report({
|
|
1398
1741
|
node,
|
|
1399
|
-
|
|
1742
|
+
messageId: "uselessFunction"
|
|
1400
1743
|
});
|
|
1401
1744
|
}
|
|
1402
1745
|
}
|
|
@@ -1404,13 +1747,12 @@ var no_useless_function_default = {
|
|
|
1404
1747
|
}
|
|
1405
1748
|
};
|
|
1406
1749
|
|
|
1407
|
-
// src/rules/min-var-length.
|
|
1408
|
-
var
|
|
1750
|
+
// src/rules/min-var-length.ts
|
|
1751
|
+
var minVarLength = {
|
|
1409
1752
|
meta: {
|
|
1410
1753
|
type: "problem",
|
|
1411
1754
|
docs: {
|
|
1412
|
-
description: "Disallow variable names shorter than the configured minimum length unless an outer variable with a longer name starting with the same characters exists. You can exempt specific variable names using the allowedVars option."
|
|
1413
|
-
recommended: false
|
|
1755
|
+
description: "Disallow variable names shorter than the configured minimum length unless an outer variable with a longer name starting with the same characters exists. You can exempt specific variable names using the allowedVars option."
|
|
1414
1756
|
},
|
|
1415
1757
|
schema: [
|
|
1416
1758
|
{
|
|
@@ -1436,11 +1778,14 @@ var min_var_length_default = {
|
|
|
1436
1778
|
variableNameTooShort: "Variable '{{name}}' is too short. Minimum allowed length is {{minLength}} characters unless an outer variable with a longer name starting with '{{name}}' exists."
|
|
1437
1779
|
}
|
|
1438
1780
|
},
|
|
1781
|
+
defaultOptions: [{}],
|
|
1439
1782
|
create(context) {
|
|
1440
|
-
const sourceCode = context.
|
|
1441
|
-
const options = context.options[0]
|
|
1442
|
-
const
|
|
1443
|
-
const
|
|
1783
|
+
const sourceCode = context.sourceCode;
|
|
1784
|
+
const options = context.options[0];
|
|
1785
|
+
const configuredMinLength = options && typeof options.minLength === "number" ? options.minLength : 1;
|
|
1786
|
+
const configuredAllowedVars = options && Array.isArray(options.allowedVars) ? options.allowedVars : [];
|
|
1787
|
+
const minLength = configuredMinLength;
|
|
1788
|
+
const allowedVars = configuredAllowedVars;
|
|
1444
1789
|
function getAncestors(node) {
|
|
1445
1790
|
const ancestors = [];
|
|
1446
1791
|
let current = node.parent;
|
|
@@ -1451,7 +1796,15 @@ var min_var_length_default = {
|
|
|
1451
1796
|
return ancestors;
|
|
1452
1797
|
}
|
|
1453
1798
|
function getScope(node) {
|
|
1454
|
-
|
|
1799
|
+
const scopeManager = sourceCode.scopeManager;
|
|
1800
|
+
if (!scopeManager) {
|
|
1801
|
+
return null;
|
|
1802
|
+
}
|
|
1803
|
+
const acquired = scopeManager.acquire(node);
|
|
1804
|
+
if (acquired) {
|
|
1805
|
+
return acquired;
|
|
1806
|
+
}
|
|
1807
|
+
return scopeManager.globalScope ?? null;
|
|
1455
1808
|
}
|
|
1456
1809
|
function getVariablesInNearestBlock(node) {
|
|
1457
1810
|
let current = node.parent;
|
|
@@ -1459,7 +1812,7 @@ var min_var_length_default = {
|
|
|
1459
1812
|
current = current.parent;
|
|
1460
1813
|
}
|
|
1461
1814
|
const names = [];
|
|
1462
|
-
if (current && Array.isArray(current.body)) {
|
|
1815
|
+
if (current && current.type === "BlockStatement" && Array.isArray(current.body)) {
|
|
1463
1816
|
for (const stmt of current.body) {
|
|
1464
1817
|
if (stmt.type === "VariableDeclaration") {
|
|
1465
1818
|
for (const decl of stmt.declarations) {
|
|
@@ -1490,8 +1843,9 @@ var min_var_length_default = {
|
|
|
1490
1843
|
break;
|
|
1491
1844
|
case "ArrayPattern":
|
|
1492
1845
|
for (const element of pattern.elements) {
|
|
1493
|
-
if (element)
|
|
1846
|
+
if (element) {
|
|
1494
1847
|
extractIdentifiersFromPattern(element, identifiers);
|
|
1848
|
+
}
|
|
1495
1849
|
}
|
|
1496
1850
|
break;
|
|
1497
1851
|
case "AssignmentPattern":
|
|
@@ -1503,8 +1857,8 @@ var min_var_length_default = {
|
|
|
1503
1857
|
return identifiers;
|
|
1504
1858
|
}
|
|
1505
1859
|
function hasOuterCorrespondingIdentifier(shortName, node) {
|
|
1506
|
-
|
|
1507
|
-
let outer =
|
|
1860
|
+
const startingScope = getScope(node);
|
|
1861
|
+
let outer = startingScope && startingScope.upper ? startingScope.upper : null;
|
|
1508
1862
|
while (outer) {
|
|
1509
1863
|
for (const variable of outer.variables) {
|
|
1510
1864
|
if (variable.name.length >= minLength && variable.name.length > shortName.length && variable.name.startsWith(shortName)) {
|
|
@@ -1550,7 +1904,7 @@ var min_var_length_default = {
|
|
|
1550
1904
|
}
|
|
1551
1905
|
function checkIdentifier(node) {
|
|
1552
1906
|
const name = node.name;
|
|
1553
|
-
if (
|
|
1907
|
+
if (name.length < minLength) {
|
|
1554
1908
|
if (allowedVars.includes(name)) {
|
|
1555
1909
|
return;
|
|
1556
1910
|
}
|
|
@@ -1581,8 +1935,9 @@ var min_var_length_default = {
|
|
|
1581
1935
|
break;
|
|
1582
1936
|
case "ArrayPattern":
|
|
1583
1937
|
for (const element of pattern.elements) {
|
|
1584
|
-
if (element)
|
|
1938
|
+
if (element) {
|
|
1585
1939
|
checkPattern(element);
|
|
1940
|
+
}
|
|
1586
1941
|
}
|
|
1587
1942
|
break;
|
|
1588
1943
|
case "AssignmentPattern":
|
|
@@ -1612,23 +1967,26 @@ var min_var_length_default = {
|
|
|
1612
1967
|
}
|
|
1613
1968
|
};
|
|
1614
1969
|
|
|
1615
|
-
// src/rules/max-depth-extended.
|
|
1616
|
-
var
|
|
1970
|
+
// src/rules/max-depth-extended.ts
|
|
1971
|
+
var maxDepthExtended = {
|
|
1617
1972
|
meta: {
|
|
1618
1973
|
type: "suggestion",
|
|
1619
1974
|
docs: {
|
|
1620
|
-
description: "disallow too many nested blocks except when the block only contains an early exit (return or throw)"
|
|
1621
|
-
category: "Best Practices",
|
|
1622
|
-
recommended: false
|
|
1975
|
+
description: "disallow too many nested blocks except when the block only contains an early exit (return or throw)"
|
|
1623
1976
|
},
|
|
1624
1977
|
schema: [
|
|
1625
1978
|
{
|
|
1626
1979
|
type: "number"
|
|
1627
1980
|
}
|
|
1628
|
-
]
|
|
1981
|
+
],
|
|
1982
|
+
messages: {
|
|
1983
|
+
tooDeep: "Blocks are nested too deeply ({{depth}}). Maximum allowed is {{maxDepth}} or an early exit."
|
|
1984
|
+
}
|
|
1629
1985
|
},
|
|
1986
|
+
defaultOptions: [1],
|
|
1630
1987
|
create(context) {
|
|
1631
|
-
const
|
|
1988
|
+
const option = context.options[0];
|
|
1989
|
+
const maxDepth = typeof option === "number" ? option : 1;
|
|
1632
1990
|
const functionStack = [];
|
|
1633
1991
|
function getAncestors(node) {
|
|
1634
1992
|
const ancestors = [];
|
|
@@ -1640,13 +1998,44 @@ var max_depth_extended_default = {
|
|
|
1640
1998
|
return ancestors;
|
|
1641
1999
|
}
|
|
1642
2000
|
function isEarlyExitBlock(node) {
|
|
1643
|
-
|
|
2001
|
+
if (node.body.length !== 1) {
|
|
2002
|
+
return false;
|
|
2003
|
+
}
|
|
2004
|
+
const first = node.body[0];
|
|
2005
|
+
if (!first) {
|
|
2006
|
+
return false;
|
|
2007
|
+
}
|
|
2008
|
+
return first.type === "ReturnStatement" || first.type === "ThrowStatement";
|
|
2009
|
+
}
|
|
2010
|
+
function incrementCurrentDepth() {
|
|
2011
|
+
if (functionStack.length === 0) {
|
|
2012
|
+
return null;
|
|
2013
|
+
}
|
|
2014
|
+
const index = functionStack.length - 1;
|
|
2015
|
+
const currentDepth = functionStack[index];
|
|
2016
|
+
if (typeof currentDepth !== "number") {
|
|
2017
|
+
return null;
|
|
2018
|
+
}
|
|
2019
|
+
const nextDepth = currentDepth + 1;
|
|
2020
|
+
functionStack[index] = nextDepth;
|
|
2021
|
+
return nextDepth;
|
|
2022
|
+
}
|
|
2023
|
+
function decrementCurrentDepth() {
|
|
2024
|
+
if (functionStack.length === 0) {
|
|
2025
|
+
return;
|
|
2026
|
+
}
|
|
2027
|
+
const index = functionStack.length - 1;
|
|
2028
|
+
const currentDepth = functionStack[index];
|
|
2029
|
+
if (typeof currentDepth !== "number") {
|
|
2030
|
+
return;
|
|
2031
|
+
}
|
|
2032
|
+
functionStack[index] = currentDepth - 1;
|
|
1644
2033
|
}
|
|
1645
2034
|
function checkDepth(node, depth) {
|
|
1646
2035
|
if (depth > maxDepth) {
|
|
1647
2036
|
context.report({
|
|
1648
2037
|
node,
|
|
1649
|
-
|
|
2038
|
+
messageId: "tooDeep",
|
|
1650
2039
|
data: { depth, maxDepth }
|
|
1651
2040
|
});
|
|
1652
2041
|
}
|
|
@@ -1663,30 +2052,28 @@ var max_depth_extended_default = {
|
|
|
1663
2052
|
},
|
|
1664
2053
|
BlockStatement(node) {
|
|
1665
2054
|
const ancestors = getAncestors(node);
|
|
1666
|
-
const parent = ancestors[0];
|
|
2055
|
+
const parent = ancestors.length > 0 ? ancestors[0] : undefined;
|
|
1667
2056
|
if (parent && (parent.type === "FunctionDeclaration" || parent.type === "FunctionExpression" || parent.type === "ArrowFunctionExpression") && node === parent.body) {
|
|
1668
2057
|
return;
|
|
1669
2058
|
}
|
|
1670
2059
|
if (isEarlyExitBlock(node)) {
|
|
1671
2060
|
return;
|
|
1672
2061
|
}
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
checkDepth(node,
|
|
2062
|
+
const depth = incrementCurrentDepth();
|
|
2063
|
+
if (depth !== null) {
|
|
2064
|
+
checkDepth(node, depth);
|
|
1676
2065
|
}
|
|
1677
2066
|
},
|
|
1678
2067
|
"BlockStatement:exit"(node) {
|
|
1679
2068
|
const ancestors = getAncestors(node);
|
|
1680
|
-
const parent = ancestors[0];
|
|
2069
|
+
const parent = ancestors.length > 0 ? ancestors[0] : undefined;
|
|
1681
2070
|
if (parent && (parent.type === "FunctionDeclaration" || parent.type === "FunctionExpression" || parent.type === "ArrowFunctionExpression") && node === parent.body) {
|
|
1682
2071
|
return;
|
|
1683
2072
|
}
|
|
1684
2073
|
if (isEarlyExitBlock(node)) {
|
|
1685
2074
|
return;
|
|
1686
2075
|
}
|
|
1687
|
-
|
|
1688
|
-
functionStack[functionStack.length - 1]--;
|
|
1689
|
-
}
|
|
2076
|
+
decrementCurrentDepth();
|
|
1690
2077
|
},
|
|
1691
2078
|
"FunctionDeclaration:exit"() {
|
|
1692
2079
|
functionStack.pop();
|
|
@@ -1701,88 +2088,103 @@ var max_depth_extended_default = {
|
|
|
1701
2088
|
}
|
|
1702
2089
|
};
|
|
1703
2090
|
|
|
1704
|
-
// src/rules/spring-naming-convention.
|
|
1705
|
-
var
|
|
2091
|
+
// src/rules/spring-naming-convention.ts
|
|
2092
|
+
var springNamingConvention = {
|
|
1706
2093
|
meta: {
|
|
1707
2094
|
type: "problem",
|
|
1708
2095
|
docs: {
|
|
1709
|
-
description: "Enforce correct naming for useSpring and useSprings hook destructuring"
|
|
1710
|
-
category: "Stylistic Issues",
|
|
1711
|
-
recommended: false
|
|
2096
|
+
description: "Enforce correct naming for useSpring and useSprings hook destructuring"
|
|
1712
2097
|
},
|
|
1713
|
-
schema: []
|
|
2098
|
+
schema: [],
|
|
2099
|
+
messages: {
|
|
2100
|
+
firstMustEndWithSprings: "The first variable must end with 'Springs'.",
|
|
2101
|
+
firstMustHaveBase: "The first variable must have a non-empty name before 'Springs'.",
|
|
2102
|
+
secondMustMatch: "The second variable must be named '{{expected}}'.",
|
|
2103
|
+
pluralRequired: "The first variable for useSprings should be plural (ending with 's') before 'Springs'."
|
|
2104
|
+
}
|
|
1714
2105
|
},
|
|
2106
|
+
defaultOptions: [],
|
|
1715
2107
|
create(context) {
|
|
1716
2108
|
return {
|
|
1717
2109
|
VariableDeclarator(node) {
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
2110
|
+
const init = node.init;
|
|
2111
|
+
if (!init || init.type !== "CallExpression" || init.callee.type !== "Identifier") {
|
|
2112
|
+
return;
|
|
2113
|
+
}
|
|
2114
|
+
const hookName = init.callee.name;
|
|
2115
|
+
if (hookName !== "useSpring" && hookName !== "useSprings") {
|
|
2116
|
+
return;
|
|
2117
|
+
}
|
|
2118
|
+
if (node.id.type !== "ArrayPattern") {
|
|
2119
|
+
return;
|
|
2120
|
+
}
|
|
2121
|
+
const elements = node.id.elements;
|
|
2122
|
+
if (elements.length < 2) {
|
|
2123
|
+
return;
|
|
2124
|
+
}
|
|
2125
|
+
const firstElem = elements[0];
|
|
2126
|
+
const secondElem = elements[1];
|
|
2127
|
+
if (!firstElem || firstElem.type !== "Identifier" || !secondElem || secondElem.type !== "Identifier") {
|
|
2128
|
+
return;
|
|
2129
|
+
}
|
|
2130
|
+
const firstName = firstElem.name;
|
|
2131
|
+
const secondName = secondElem.name;
|
|
2132
|
+
if (hookName === "useSpring") {
|
|
2133
|
+
if (!firstName.endsWith("Springs")) {
|
|
2134
|
+
context.report({
|
|
2135
|
+
node: firstElem,
|
|
2136
|
+
messageId: "firstMustEndWithSprings"
|
|
2137
|
+
});
|
|
2138
|
+
return;
|
|
2139
|
+
}
|
|
2140
|
+
const base = firstName.slice(0, -"Springs".length);
|
|
2141
|
+
if (!base) {
|
|
2142
|
+
context.report({
|
|
2143
|
+
node: firstElem,
|
|
2144
|
+
messageId: "firstMustHaveBase"
|
|
2145
|
+
});
|
|
2146
|
+
return;
|
|
2147
|
+
}
|
|
2148
|
+
const expectedSecond = `${base}Api`;
|
|
2149
|
+
if (secondName !== expectedSecond) {
|
|
2150
|
+
context.report({
|
|
2151
|
+
node: secondElem,
|
|
2152
|
+
messageId: "secondMustMatch",
|
|
2153
|
+
data: { expected: expectedSecond }
|
|
2154
|
+
});
|
|
2155
|
+
}
|
|
2156
|
+
return;
|
|
2157
|
+
}
|
|
2158
|
+
if (hookName === "useSprings") {
|
|
2159
|
+
if (!firstName.endsWith("Springs")) {
|
|
2160
|
+
context.report({
|
|
2161
|
+
node: firstElem,
|
|
2162
|
+
messageId: "firstMustEndWithSprings"
|
|
2163
|
+
});
|
|
2164
|
+
return;
|
|
2165
|
+
}
|
|
2166
|
+
const basePlural = firstName.slice(0, -"Springs".length);
|
|
2167
|
+
if (!basePlural) {
|
|
2168
|
+
context.report({
|
|
2169
|
+
node: firstElem,
|
|
2170
|
+
messageId: "firstMustHaveBase"
|
|
2171
|
+
});
|
|
2172
|
+
return;
|
|
2173
|
+
}
|
|
2174
|
+
if (!basePlural.endsWith("s")) {
|
|
2175
|
+
context.report({
|
|
2176
|
+
node: firstElem,
|
|
2177
|
+
messageId: "pluralRequired"
|
|
2178
|
+
});
|
|
2179
|
+
return;
|
|
2180
|
+
}
|
|
2181
|
+
const expectedSecond = `${basePlural}Api`;
|
|
2182
|
+
if (secondName !== expectedSecond) {
|
|
2183
|
+
context.report({
|
|
2184
|
+
node: secondElem,
|
|
2185
|
+
messageId: "secondMustMatch",
|
|
2186
|
+
data: { expected: expectedSecond }
|
|
2187
|
+
});
|
|
1786
2188
|
}
|
|
1787
2189
|
}
|
|
1788
2190
|
}
|
|
@@ -1790,14 +2192,12 @@ var spring_naming_convention_default = {
|
|
|
1790
2192
|
}
|
|
1791
2193
|
};
|
|
1792
2194
|
|
|
1793
|
-
// src/rules/inline-style-limit.
|
|
1794
|
-
var
|
|
2195
|
+
// src/rules/inline-style-limit.ts
|
|
2196
|
+
var inlineStyleLimit = {
|
|
1795
2197
|
meta: {
|
|
1796
2198
|
type: "suggestion",
|
|
1797
2199
|
docs: {
|
|
1798
|
-
description: "Disallow inline style objects with too many keys and encourage extracting them"
|
|
1799
|
-
category: "Best Practices",
|
|
1800
|
-
recommended: false
|
|
2200
|
+
description: "Disallow inline style objects with too many keys and encourage extracting them"
|
|
1801
2201
|
},
|
|
1802
2202
|
schema: [
|
|
1803
2203
|
{
|
|
@@ -1822,12 +2222,13 @@ var inline_style_limit_default = {
|
|
|
1822
2222
|
extractStyle: "Inline style objects should be extracted into a separate object or file when containing more than {{max}} keys."
|
|
1823
2223
|
}
|
|
1824
2224
|
},
|
|
2225
|
+
defaultOptions: [3],
|
|
1825
2226
|
create(context) {
|
|
1826
2227
|
const option = context.options[0];
|
|
1827
2228
|
const maxKeys = typeof option === "number" ? option : option && option.maxKeys || 3;
|
|
1828
2229
|
return {
|
|
1829
2230
|
JSXAttribute(node) {
|
|
1830
|
-
if (node.name.name !== "style") {
|
|
2231
|
+
if (node.name.type !== "JSXIdentifier" || node.name.name !== "style") {
|
|
1831
2232
|
return;
|
|
1832
2233
|
}
|
|
1833
2234
|
if (node.value && node.value.type === "JSXExpressionContainer" && node.value.expression && node.value.expression.type === "ObjectExpression") {
|
|
@@ -1846,23 +2247,22 @@ var inline_style_limit_default = {
|
|
|
1846
2247
|
}
|
|
1847
2248
|
};
|
|
1848
2249
|
|
|
1849
|
-
// src/rules/no-inline-prop-types.
|
|
1850
|
-
var
|
|
2250
|
+
// src/rules/no-inline-prop-types.ts
|
|
2251
|
+
var noInlinePropTypes = {
|
|
1851
2252
|
meta: {
|
|
1852
2253
|
type: "suggestion",
|
|
1853
2254
|
docs: {
|
|
1854
|
-
description: "Enforce that component prop types are not defined inline (using an object literal) but rather use a named type or interface."
|
|
1855
|
-
category: "Best Practices",
|
|
1856
|
-
recommended: false
|
|
2255
|
+
description: "Enforce that component prop types are not defined inline (using an object literal) but rather use a named type or interface."
|
|
1857
2256
|
},
|
|
1858
2257
|
schema: [],
|
|
1859
2258
|
messages: {
|
|
1860
2259
|
noInlinePropTypes: "Inline prop type definitions are not allowed. Use a named type alias or interface instead of an inline object type."
|
|
1861
2260
|
}
|
|
1862
2261
|
},
|
|
2262
|
+
defaultOptions: [],
|
|
1863
2263
|
create(context) {
|
|
1864
2264
|
function checkParameter(param) {
|
|
1865
|
-
if (param
|
|
2265
|
+
if (param.type === "ObjectPattern" && param.typeAnnotation && param.typeAnnotation.type === "TSTypeAnnotation") {
|
|
1866
2266
|
const annotation = param.typeAnnotation.typeAnnotation;
|
|
1867
2267
|
if (annotation.type === "TSTypeLiteral") {
|
|
1868
2268
|
context.report({
|
|
@@ -1874,70 +2274,98 @@ var no_inline_prop_types_default = {
|
|
|
1874
2274
|
}
|
|
1875
2275
|
return {
|
|
1876
2276
|
"FunctionDeclaration, ArrowFunctionExpression, FunctionExpression"(node) {
|
|
2277
|
+
if (node.params.length === 0) {
|
|
2278
|
+
return;
|
|
2279
|
+
}
|
|
1877
2280
|
const firstParam = node.params[0];
|
|
1878
|
-
if (firstParam) {
|
|
1879
|
-
|
|
2281
|
+
if (!firstParam) {
|
|
2282
|
+
return;
|
|
1880
2283
|
}
|
|
2284
|
+
checkParameter(firstParam);
|
|
1881
2285
|
}
|
|
1882
2286
|
};
|
|
1883
2287
|
}
|
|
1884
2288
|
};
|
|
1885
2289
|
|
|
1886
|
-
// src/rules/no-unnecessary-div.
|
|
1887
|
-
|
|
2290
|
+
// src/rules/no-unnecessary-div.ts
|
|
2291
|
+
import { AST_NODE_TYPES as AST_NODE_TYPES3 } from "@typescript-eslint/utils";
|
|
2292
|
+
var noUnnecessaryDiv = {
|
|
1888
2293
|
meta: {
|
|
1889
2294
|
type: "suggestion",
|
|
1890
2295
|
docs: {
|
|
1891
|
-
description: "Flag unnecessary <div> wrappers that enclose a single JSX element. Remove the wrapper if it doesn't add semantic or functional value, or replace it with a semantic element if wrapping is needed."
|
|
1892
|
-
|
|
1893
|
-
|
|
2296
|
+
description: "Flag unnecessary <div> wrappers that enclose a single JSX element. Remove the wrapper if it doesn't add semantic or functional value, or replace it with a semantic element if wrapping is needed."
|
|
2297
|
+
},
|
|
2298
|
+
schema: [],
|
|
2299
|
+
messages: {
|
|
2300
|
+
unnecessaryDivWrapper: "Unnecessary <div> wrapper detected. Remove it if not needed, or replace with a semantic element that reflects its purpose."
|
|
1894
2301
|
}
|
|
1895
2302
|
},
|
|
2303
|
+
defaultOptions: [],
|
|
1896
2304
|
create(context) {
|
|
2305
|
+
function isDivElement(node) {
|
|
2306
|
+
const nameNode = node.openingElement.name;
|
|
2307
|
+
return nameNode.type === AST_NODE_TYPES3.JSXIdentifier && nameNode.name === "div";
|
|
2308
|
+
}
|
|
2309
|
+
function getMeaningfulChildren(node) {
|
|
2310
|
+
const result = [];
|
|
2311
|
+
for (const child of node.children) {
|
|
2312
|
+
if (child.type === AST_NODE_TYPES3.JSXText) {
|
|
2313
|
+
if (child.value.trim() !== "") {
|
|
2314
|
+
result.push(child);
|
|
2315
|
+
}
|
|
2316
|
+
} else {
|
|
2317
|
+
result.push(child);
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
return result;
|
|
2321
|
+
}
|
|
1897
2322
|
return {
|
|
1898
2323
|
JSXElement(node) {
|
|
1899
|
-
if (node
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
2324
|
+
if (!isDivElement(node)) {
|
|
2325
|
+
return;
|
|
2326
|
+
}
|
|
2327
|
+
const meaningfulChildren = getMeaningfulChildren(node);
|
|
2328
|
+
if (meaningfulChildren.length !== 1) {
|
|
2329
|
+
return;
|
|
2330
|
+
}
|
|
2331
|
+
const onlyChild = meaningfulChildren[0];
|
|
2332
|
+
if (!onlyChild) {
|
|
2333
|
+
return;
|
|
2334
|
+
}
|
|
2335
|
+
if (onlyChild.type === AST_NODE_TYPES3.JSXElement) {
|
|
2336
|
+
context.report({
|
|
2337
|
+
node,
|
|
2338
|
+
messageId: "unnecessaryDivWrapper"
|
|
1905
2339
|
});
|
|
1906
|
-
if (meaningfulChildren.length === 1 && meaningfulChildren[0].type === "JSXElement") {
|
|
1907
|
-
context.report({
|
|
1908
|
-
node,
|
|
1909
|
-
message: "Unnecessary <div> wrapper detected. Remove it if not needed, or replace with a semantic element that reflects its purpose."
|
|
1910
|
-
});
|
|
1911
|
-
}
|
|
1912
2340
|
}
|
|
1913
2341
|
}
|
|
1914
2342
|
};
|
|
1915
2343
|
}
|
|
1916
2344
|
};
|
|
1917
2345
|
|
|
1918
|
-
// src/index.
|
|
2346
|
+
// src/index.ts
|
|
1919
2347
|
var src_default = {
|
|
1920
2348
|
rules: {
|
|
1921
|
-
"no-nested-jsx-return":
|
|
1922
|
-
"explicit-object-types":
|
|
1923
|
-
"sort-keys-fixable":
|
|
1924
|
-
"no-transition-cssproperties":
|
|
1925
|
-
"no-explicit-return-type":
|
|
1926
|
-
"max-jsxnesting":
|
|
1927
|
-
"seperate-style-files":
|
|
1928
|
-
"no-unnecessary-key":
|
|
1929
|
-
"sort-exports":
|
|
1930
|
-
"localize-react-props":
|
|
1931
|
-
"no-or-none-component":
|
|
1932
|
-
"no-button-navigation":
|
|
1933
|
-
"no-multi-style-objects":
|
|
1934
|
-
"no-useless-function":
|
|
1935
|
-
"min-var-length":
|
|
1936
|
-
"max-depth-extended":
|
|
1937
|
-
"spring-naming-convention":
|
|
1938
|
-
"inline-style-limit":
|
|
1939
|
-
"no-inline-prop-types":
|
|
1940
|
-
"no-unnecessary-div":
|
|
2349
|
+
"no-nested-jsx-return": noNestedJSXReturn,
|
|
2350
|
+
"explicit-object-types": explicitObjectTypes,
|
|
2351
|
+
"sort-keys-fixable": sortKeysFixable,
|
|
2352
|
+
"no-transition-cssproperties": noTransitionCSSProperties,
|
|
2353
|
+
"no-explicit-return-type": noExplicitReturnTypes,
|
|
2354
|
+
"max-jsxnesting": maxJSXNesting,
|
|
2355
|
+
"seperate-style-files": seperateStyleFiles,
|
|
2356
|
+
"no-unnecessary-key": noUnnecessaryKey,
|
|
2357
|
+
"sort-exports": sortExports,
|
|
2358
|
+
"localize-react-props": localizeReactProps,
|
|
2359
|
+
"no-or-none-component": noOrNoneComponent,
|
|
2360
|
+
"no-button-navigation": noButtonNavigation,
|
|
2361
|
+
"no-multi-style-objects": noMultiStyleObjects,
|
|
2362
|
+
"no-useless-function": noUselessFunction,
|
|
2363
|
+
"min-var-length": minVarLength,
|
|
2364
|
+
"max-depth-extended": maxDepthExtended,
|
|
2365
|
+
"spring-naming-convention": springNamingConvention,
|
|
2366
|
+
"inline-style-limit": inlineStyleLimit,
|
|
2367
|
+
"no-inline-prop-types": noInlinePropTypes,
|
|
2368
|
+
"no-unnecessary-div": noUnnecessaryDiv
|
|
1941
2369
|
}
|
|
1942
2370
|
};
|
|
1943
2371
|
export {
|