eslint-plugin-absolute 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierignore +4 -0
- package/.prettierrc.json +8 -0
- package/LICENSE +23 -0
- package/README.md +3 -0
- package/dist/index.js +1882 -0
- package/package.json +29 -0
- package/src/index.js +47 -0
- package/src/rules/explicit-object-types.js +54 -0
- package/src/rules/inline-style-limit.js +77 -0
- package/src/rules/localize-react-props.js +418 -0
- package/src/rules/max-depth-extended.js +115 -0
- package/src/rules/max-jsx-nesting.js +60 -0
- package/src/rules/min-var-length.js +300 -0
- package/src/rules/no-button-navigation.js +114 -0
- package/src/rules/no-explicit-return-types.js +64 -0
- package/src/rules/no-inline-prop-types.js +55 -0
- package/src/rules/no-multi-style-objects.js +70 -0
- package/src/rules/no-nested-jsx-return.js +154 -0
- package/src/rules/no-or-none-component.js +50 -0
- package/src/rules/no-transition-cssproperties.js +102 -0
- package/src/rules/no-type-cast.js +50 -0
- package/src/rules/no-unnecessary-div.js +40 -0
- package/src/rules/no-unnecessary-key.js +128 -0
- package/src/rules/no-useless-function.js +43 -0
- package/src/rules/seperate-style-files.js +62 -0
- package/src/rules/sort-exports.js +397 -0
- package/src/rules/sort-keys-fixable.js +375 -0
- package/src/rules/spring-naming-convention.js +111 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1882 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/rules/no-nested-jsx-return.js
|
|
3
|
+
var no_nested_jsx_return_default = {
|
|
4
|
+
meta: {
|
|
5
|
+
type: "problem",
|
|
6
|
+
docs: {
|
|
7
|
+
description: "Disallow nested functions that return non-component, non-singular JSX to enforce one component per file",
|
|
8
|
+
recommended: false
|
|
9
|
+
},
|
|
10
|
+
schema: []
|
|
11
|
+
},
|
|
12
|
+
create(context) {
|
|
13
|
+
function isJSX(node) {
|
|
14
|
+
return node && (node.type === "JSXElement" || node.type === "JSXFragment");
|
|
15
|
+
}
|
|
16
|
+
function isJSXComponentElement(node) {
|
|
17
|
+
if (node && node.type === "JSXElement") {
|
|
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
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
function isSingularJSXReturn(node) {
|
|
36
|
+
if (!isJSX(node))
|
|
37
|
+
return false;
|
|
38
|
+
let children = [];
|
|
39
|
+
if (node.type === "JSXElement" || node.type === "JSXFragment") {
|
|
40
|
+
children = node.children.filter((child) => {
|
|
41
|
+
if (child.type === "JSXText") {
|
|
42
|
+
return child.value.trim() !== "";
|
|
43
|
+
}
|
|
44
|
+
return true;
|
|
45
|
+
});
|
|
46
|
+
if (children.length === 1) {
|
|
47
|
+
const child = children[0];
|
|
48
|
+
if (child.type === "JSXElement" || child.type === "JSXFragment") {
|
|
49
|
+
const innerChildren = child.children.filter((innerChild) => {
|
|
50
|
+
if (innerChild.type === "JSXText") {
|
|
51
|
+
return innerChild.value.trim() !== "";
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
});
|
|
55
|
+
return innerChildren.length === 0;
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return children.length === 0;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const functionStack = [];
|
|
64
|
+
function pushFunction(node) {
|
|
65
|
+
functionStack.push(node);
|
|
66
|
+
}
|
|
67
|
+
function popFunction() {
|
|
68
|
+
functionStack.pop();
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
"FunctionDeclaration, FunctionExpression, ArrowFunctionExpression"(node) {
|
|
72
|
+
pushFunction(node);
|
|
73
|
+
},
|
|
74
|
+
"FunctionDeclaration:exit"(node) {
|
|
75
|
+
popFunction();
|
|
76
|
+
},
|
|
77
|
+
"FunctionExpression:exit"(node) {
|
|
78
|
+
popFunction();
|
|
79
|
+
},
|
|
80
|
+
"ArrowFunctionExpression:exit"(node) {
|
|
81
|
+
popFunction();
|
|
82
|
+
},
|
|
83
|
+
ReturnStatement(node) {
|
|
84
|
+
if (functionStack.length > 1 && isJSX(node.argument) && !isJSXComponentElement(node.argument) && !isSingularJSXReturn(node.argument)) {
|
|
85
|
+
context.report({
|
|
86
|
+
node,
|
|
87
|
+
message: "Nested function returning non-component, non-singular JSX detected. Extract it into its own component."
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"ArrowFunctionExpression > JSXElement"(node) {
|
|
92
|
+
if (functionStack.length > 1 && !isJSXComponentElement(node) && !isSingularJSXReturn(node)) {
|
|
93
|
+
context.report({
|
|
94
|
+
node,
|
|
95
|
+
message: "Nested arrow function returning non-component, non-singular JSX detected. Extract it into its own component."
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"ArrowFunctionExpression > JSXFragment"(node) {
|
|
100
|
+
if (functionStack.length > 1 && !isSingularJSXReturn(node)) {
|
|
101
|
+
context.report({
|
|
102
|
+
node,
|
|
103
|
+
message: "Nested arrow function returning a non-singular JSX fragment detected. Extract it into its own component."
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// src/rules/explicit-object-types.js
|
|
112
|
+
var explicit_object_types_default = {
|
|
113
|
+
meta: {
|
|
114
|
+
type: "problem",
|
|
115
|
+
docs: {
|
|
116
|
+
description: "Require explicit type annotations for object literals and arrays of object literals",
|
|
117
|
+
recommended: false
|
|
118
|
+
},
|
|
119
|
+
schema: []
|
|
120
|
+
},
|
|
121
|
+
create(context) {
|
|
122
|
+
function isObjectLiteral(node) {
|
|
123
|
+
return node && node.type === "ObjectExpression";
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
VariableDeclarator(node) {
|
|
127
|
+
if (!node.init)
|
|
128
|
+
return;
|
|
129
|
+
if (node.id && node.id.typeAnnotation)
|
|
130
|
+
return;
|
|
131
|
+
if (isObjectLiteral(node.init)) {
|
|
132
|
+
context.report({
|
|
133
|
+
node: node.id,
|
|
134
|
+
message: "Object literal must have an explicit type annotation."
|
|
135
|
+
});
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (node.init.type === "ArrayExpression") {
|
|
139
|
+
const hasObjectLiteral = node.init.elements.some((element) => element && isObjectLiteral(element));
|
|
140
|
+
if (hasObjectLiteral) {
|
|
141
|
+
context.report({
|
|
142
|
+
node: node.id,
|
|
143
|
+
message: "Array of object literals must have an explicit type annotation."
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// src/rules/no-type-cast.js
|
|
153
|
+
var no_type_cast_default = {
|
|
154
|
+
meta: {
|
|
155
|
+
type: "problem",
|
|
156
|
+
docs: {
|
|
157
|
+
description: 'Disallow type assertions using "as", angle bracket syntax, or built-in conversion functions.',
|
|
158
|
+
recommended: false
|
|
159
|
+
},
|
|
160
|
+
schema: []
|
|
161
|
+
},
|
|
162
|
+
create(context) {
|
|
163
|
+
function isBuiltInConversion(node) {
|
|
164
|
+
return node && node.type === "Identifier" && ["Number", "String", "Boolean"].includes(node.name);
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
TSAsExpression(node) {
|
|
168
|
+
context.report({
|
|
169
|
+
node,
|
|
170
|
+
message: 'Type assertions using "as" syntax are not allowed.'
|
|
171
|
+
});
|
|
172
|
+
},
|
|
173
|
+
TSTypeAssertion(node) {
|
|
174
|
+
context.report({
|
|
175
|
+
node,
|
|
176
|
+
message: "Type assertions using angle bracket syntax are not allowed."
|
|
177
|
+
});
|
|
178
|
+
},
|
|
179
|
+
CallExpression(node) {
|
|
180
|
+
if (isBuiltInConversion(node.callee)) {
|
|
181
|
+
context.report({
|
|
182
|
+
node,
|
|
183
|
+
message: `Type assertions using "${node.callee.name}()" syntax are not allowed.`
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// src/rules/sort-keys-fixable.js
|
|
192
|
+
var sort_keys_fixable_default = {
|
|
193
|
+
meta: {
|
|
194
|
+
type: "suggestion",
|
|
195
|
+
docs: {
|
|
196
|
+
description: "enforce sorted keys in object literals with auto-fix (limited to simple cases, preserving comments)",
|
|
197
|
+
recommended: false
|
|
198
|
+
},
|
|
199
|
+
fixable: "code",
|
|
200
|
+
schema: [
|
|
201
|
+
{
|
|
202
|
+
type: "object",
|
|
203
|
+
properties: {
|
|
204
|
+
order: {
|
|
205
|
+
enum: ["asc", "desc"]
|
|
206
|
+
},
|
|
207
|
+
caseSensitive: {
|
|
208
|
+
type: "boolean"
|
|
209
|
+
},
|
|
210
|
+
natural: {
|
|
211
|
+
type: "boolean"
|
|
212
|
+
},
|
|
213
|
+
minKeys: {
|
|
214
|
+
type: "integer",
|
|
215
|
+
minimum: 2
|
|
216
|
+
},
|
|
217
|
+
variablesBeforeFunctions: {
|
|
218
|
+
type: "boolean"
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
additionalProperties: false
|
|
222
|
+
}
|
|
223
|
+
],
|
|
224
|
+
messages: {
|
|
225
|
+
unsorted: "Object keys are not sorted."
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
create(context) {
|
|
229
|
+
const sourceCode = context.getSourceCode();
|
|
230
|
+
const options = context.options[0] || {};
|
|
231
|
+
const order = options.order || "asc";
|
|
232
|
+
const caseSensitive = options.caseSensitive !== undefined ? options.caseSensitive : false;
|
|
233
|
+
const natural = options.natural !== undefined ? options.natural : false;
|
|
234
|
+
const minKeys = options.minKeys !== undefined ? options.minKeys : 2;
|
|
235
|
+
const variablesBeforeFunctions = options.variablesBeforeFunctions !== undefined ? options.variablesBeforeFunctions : false;
|
|
236
|
+
function compareKeys(a, b) {
|
|
237
|
+
let keyA = a;
|
|
238
|
+
let keyB = b;
|
|
239
|
+
if (!caseSensitive) {
|
|
240
|
+
keyA = keyA.toLowerCase();
|
|
241
|
+
keyB = keyB.toLowerCase();
|
|
242
|
+
}
|
|
243
|
+
if (natural) {
|
|
244
|
+
return keyA.localeCompare(keyB, undefined, { numeric: true });
|
|
245
|
+
}
|
|
246
|
+
return keyA.localeCompare(keyB);
|
|
247
|
+
}
|
|
248
|
+
function isFunctionProperty(prop) {
|
|
249
|
+
return prop.value && (prop.value.type === "FunctionExpression" || prop.value.type === "ArrowFunctionExpression" || prop.method === true);
|
|
250
|
+
}
|
|
251
|
+
function buildSortedText(fixableProps) {
|
|
252
|
+
const sorted = fixableProps.slice().sort((a, b) => {
|
|
253
|
+
if (variablesBeforeFunctions) {
|
|
254
|
+
const aIsFunc = isFunctionProperty(a);
|
|
255
|
+
const bIsFunc = isFunctionProperty(b);
|
|
256
|
+
if (aIsFunc !== bIsFunc) {
|
|
257
|
+
return aIsFunc ? 1 : -1;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
const keyA = a.key.type === "Identifier" ? a.key.name : String(a.key.value);
|
|
261
|
+
const keyB = b.key.type === "Identifier" ? b.key.name : String(b.key.value);
|
|
262
|
+
let res = compareKeys(keyA, keyB);
|
|
263
|
+
if (order === "desc") {
|
|
264
|
+
res = -res;
|
|
265
|
+
}
|
|
266
|
+
return res;
|
|
267
|
+
});
|
|
268
|
+
return sorted.map((prop) => {
|
|
269
|
+
const leadingComments = sourceCode.getCommentsBefore(prop) || [];
|
|
270
|
+
const trailingComments = sourceCode.getCommentsAfter(prop) || [];
|
|
271
|
+
const leadingText = leadingComments.length > 0 ? leadingComments.map((comment) => sourceCode.getText(comment)).join(`
|
|
272
|
+
`) + `
|
|
273
|
+
` : "";
|
|
274
|
+
const trailingText = trailingComments.length > 0 ? `
|
|
275
|
+
` + trailingComments.map((comment) => sourceCode.getText(comment)).join(`
|
|
276
|
+
`) : "";
|
|
277
|
+
return leadingText + sourceCode.getText(prop) + trailingText;
|
|
278
|
+
}).join(", ");
|
|
279
|
+
}
|
|
280
|
+
function checkObjectExpression(node) {
|
|
281
|
+
if (node.properties.length < minKeys) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
let autoFixable = true;
|
|
285
|
+
const keys = node.properties.map((prop) => {
|
|
286
|
+
let keyName = null;
|
|
287
|
+
let isFunc = false;
|
|
288
|
+
if (prop.type === "Property") {
|
|
289
|
+
if (prop.computed) {
|
|
290
|
+
autoFixable = false;
|
|
291
|
+
}
|
|
292
|
+
if (prop.key.type === "Identifier") {
|
|
293
|
+
keyName = prop.key.name;
|
|
294
|
+
} else if (prop.key.type === "Literal") {
|
|
295
|
+
keyName = String(prop.key.value);
|
|
296
|
+
} else {
|
|
297
|
+
autoFixable = false;
|
|
298
|
+
}
|
|
299
|
+
if (prop.value) {
|
|
300
|
+
if (prop.value.type === "FunctionExpression" || prop.value.type === "ArrowFunctionExpression" || prop.method === true) {
|
|
301
|
+
isFunc = true;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
} else {
|
|
305
|
+
autoFixable = false;
|
|
306
|
+
}
|
|
307
|
+
return { keyName, node: prop, isFunction: isFunc };
|
|
308
|
+
});
|
|
309
|
+
let fixProvided = false;
|
|
310
|
+
for (let i = 1;i < keys.length; i++) {
|
|
311
|
+
const prev = keys[i - 1];
|
|
312
|
+
const curr = keys[i];
|
|
313
|
+
if (prev.keyName === null || curr.keyName === null) {
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (variablesBeforeFunctions) {
|
|
317
|
+
if (prev.isFunction && !curr.isFunction) {
|
|
318
|
+
context.report({
|
|
319
|
+
node: curr.node.key,
|
|
320
|
+
messageId: "unsorted",
|
|
321
|
+
fix: !fixProvided && autoFixable ? (fixer) => {
|
|
322
|
+
const fixableProps = node.properties.filter((prop) => prop.type === "Property" && !prop.computed && (prop.key.type === "Identifier" || prop.key.type === "Literal"));
|
|
323
|
+
if (fixableProps.length < minKeys) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
const sortedText = buildSortedText(fixableProps);
|
|
327
|
+
const firstProp = fixableProps[0];
|
|
328
|
+
const lastProp = fixableProps[fixableProps.length - 1];
|
|
329
|
+
return fixer.replaceTextRange([
|
|
330
|
+
firstProp.range[0],
|
|
331
|
+
lastProp.range[1]
|
|
332
|
+
], sortedText);
|
|
333
|
+
} : null
|
|
334
|
+
});
|
|
335
|
+
fixProvided = true;
|
|
336
|
+
continue;
|
|
337
|
+
} else if (prev.isFunction === curr.isFunction) {
|
|
338
|
+
if (compareKeys(prev.keyName, curr.keyName) > 0) {
|
|
339
|
+
context.report({
|
|
340
|
+
node: curr.node.key,
|
|
341
|
+
messageId: "unsorted",
|
|
342
|
+
fix: !fixProvided && autoFixable ? (fixer) => {
|
|
343
|
+
const fixableProps = node.properties.filter((prop) => prop.type === "Property" && !prop.computed && (prop.key.type === "Identifier" || prop.key.type === "Literal"));
|
|
344
|
+
if (fixableProps.length < minKeys) {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
const sortedText = buildSortedText(fixableProps);
|
|
348
|
+
const firstProp = fixableProps[0];
|
|
349
|
+
const lastProp = fixableProps[fixableProps.length - 1];
|
|
350
|
+
return fixer.replaceTextRange([
|
|
351
|
+
firstProp.range[0],
|
|
352
|
+
lastProp.range[1]
|
|
353
|
+
], sortedText);
|
|
354
|
+
} : null
|
|
355
|
+
});
|
|
356
|
+
fixProvided = true;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
} else {
|
|
360
|
+
if (compareKeys(prev.keyName, curr.keyName) > 0) {
|
|
361
|
+
context.report({
|
|
362
|
+
node: curr.node.key,
|
|
363
|
+
messageId: "unsorted",
|
|
364
|
+
fix: !fixProvided && autoFixable ? (fixer) => {
|
|
365
|
+
const fixableProps = node.properties.filter((prop) => prop.type === "Property" && !prop.computed && (prop.key.type === "Identifier" || prop.key.type === "Literal"));
|
|
366
|
+
if (fixableProps.length < minKeys) {
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
const sortedText = buildSortedText(fixableProps);
|
|
370
|
+
const firstProp = fixableProps[0];
|
|
371
|
+
const lastProp = fixableProps[fixableProps.length - 1];
|
|
372
|
+
return fixer.replaceTextRange([
|
|
373
|
+
firstProp.range[0],
|
|
374
|
+
lastProp.range[1]
|
|
375
|
+
], sortedText);
|
|
376
|
+
} : null
|
|
377
|
+
});
|
|
378
|
+
fixProvided = true;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return {
|
|
384
|
+
ObjectExpression: checkObjectExpression
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
// src/rules/no-transition-cssproperties.js
|
|
390
|
+
var no_transition_cssproperties_default = {
|
|
391
|
+
meta: {
|
|
392
|
+
type: "problem",
|
|
393
|
+
docs: {
|
|
394
|
+
description: "Objects typed as CSSProperties must not include a 'transition' property as it conflicts with react-spring.",
|
|
395
|
+
recommended: false
|
|
396
|
+
},
|
|
397
|
+
schema: [],
|
|
398
|
+
messages: {
|
|
399
|
+
forbiddenTransition: "Objects typed as CSSProperties must not include a 'transition' property as it conflicts with react-spring."
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
create(context) {
|
|
403
|
+
const sourceCode = context.getSourceCode();
|
|
404
|
+
return {
|
|
405
|
+
VariableDeclarator(node) {
|
|
406
|
+
if (!node.id || node.id.type !== "Identifier" || !node.id.typeAnnotation) {
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
let isStyleType = false;
|
|
410
|
+
const typeAnnotation = node.id.typeAnnotation.typeAnnotation;
|
|
411
|
+
if (typeAnnotation && typeAnnotation.type === "TSTypeReference") {
|
|
412
|
+
const { typeName } = typeAnnotation;
|
|
413
|
+
if (typeName.type === "Identifier" && typeName.name === "CSSProperties") {
|
|
414
|
+
isStyleType = true;
|
|
415
|
+
} else if (typeName.type === "TSQualifiedName" && typeName.right && typeName.right.name === "CSSProperties") {
|
|
416
|
+
isStyleType = true;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (!isStyleType) {
|
|
420
|
+
const annotationText = sourceCode.getText(node.id.typeAnnotation);
|
|
421
|
+
if (annotationText.includes("CSSProperties")) {
|
|
422
|
+
isStyleType = true;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if (!isStyleType) {
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
if (node.init && node.init.type === "ObjectExpression") {
|
|
429
|
+
node.init.properties.forEach((prop) => {
|
|
430
|
+
if (prop.type !== "Property") {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
if (prop.computed) {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
let keyName = null;
|
|
437
|
+
if (prop.key.type === "Identifier") {
|
|
438
|
+
keyName = prop.key.name;
|
|
439
|
+
} else if (prop.key.type === "Literal") {
|
|
440
|
+
keyName = String(prop.key.value);
|
|
441
|
+
}
|
|
442
|
+
if (keyName === "transition") {
|
|
443
|
+
context.report({
|
|
444
|
+
node: prop,
|
|
445
|
+
messageId: "forbiddenTransition"
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
// src/rules/no-explicit-return-types.js
|
|
456
|
+
var no_explicit_return_types_default = {
|
|
457
|
+
meta: {
|
|
458
|
+
type: "suggestion",
|
|
459
|
+
docs: {
|
|
460
|
+
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).",
|
|
461
|
+
recommended: false
|
|
462
|
+
},
|
|
463
|
+
schema: [],
|
|
464
|
+
messages: {
|
|
465
|
+
noExplicitReturnType: "Explicit return types are disallowed; rely on TypeScript's inference instead."
|
|
466
|
+
}
|
|
467
|
+
},
|
|
468
|
+
create(context) {
|
|
469
|
+
return {
|
|
470
|
+
"FunctionDeclaration, FunctionExpression, ArrowFunctionExpression"(node) {
|
|
471
|
+
if (node.returnType) {
|
|
472
|
+
const typeAnnotation = node.returnType.typeAnnotation;
|
|
473
|
+
if (typeAnnotation && typeAnnotation.type === "TSTypePredicate") {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (node.type === "ArrowFunctionExpression" && node.expression && node.body && node.body.type === "ObjectExpression") {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
if (node.body && node.body.type === "BlockStatement") {
|
|
480
|
+
const returns = node.body.body.filter((stmt) => stmt.type === "ReturnStatement");
|
|
481
|
+
if (returns.length === 1 && returns[0].argument && returns[0].argument.type === "ObjectExpression") {
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
context.report({
|
|
486
|
+
node: node.returnType,
|
|
487
|
+
messageId: "noExplicitReturnType"
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
// src/rules/max-jsx-nesting.js
|
|
496
|
+
var max_jsx_nesting_default = {
|
|
497
|
+
meta: {
|
|
498
|
+
type: "suggestion",
|
|
499
|
+
docs: {
|
|
500
|
+
description: "Warn when JSX elements are nested too deeply, suggesting refactoring into a separate component.",
|
|
501
|
+
recommended: false
|
|
502
|
+
},
|
|
503
|
+
schema: [
|
|
504
|
+
{
|
|
505
|
+
type: "number",
|
|
506
|
+
minimum: 1
|
|
507
|
+
}
|
|
508
|
+
],
|
|
509
|
+
messages: {
|
|
510
|
+
tooDeeplyNested: "JSX element is nested too deeply ({{level}} levels, allowed is {{maxAllowed}} levels). Consider refactoring into a separate component."
|
|
511
|
+
}
|
|
512
|
+
},
|
|
513
|
+
create(context) {
|
|
514
|
+
const maxAllowed = context.options[0];
|
|
515
|
+
function getJSXNestingLevel(node) {
|
|
516
|
+
let level = 1;
|
|
517
|
+
let current = node.parent;
|
|
518
|
+
while (current) {
|
|
519
|
+
if (current.type === "JSXElement" || current.type === "JSXFragment") {
|
|
520
|
+
level++;
|
|
521
|
+
}
|
|
522
|
+
current = current.parent;
|
|
523
|
+
}
|
|
524
|
+
return level;
|
|
525
|
+
}
|
|
526
|
+
return {
|
|
527
|
+
JSXElement(node) {
|
|
528
|
+
const level = getJSXNestingLevel(node);
|
|
529
|
+
if (level > maxAllowed) {
|
|
530
|
+
context.report({
|
|
531
|
+
node,
|
|
532
|
+
messageId: "tooDeeplyNested",
|
|
533
|
+
data: { level, maxAllowed }
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
// src/rules/seperate-style-files.js
|
|
542
|
+
var seperate_style_files_default = {
|
|
543
|
+
meta: {
|
|
544
|
+
type: "suggestion",
|
|
545
|
+
docs: {
|
|
546
|
+
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.",
|
|
547
|
+
recommended: false
|
|
548
|
+
},
|
|
549
|
+
schema: [],
|
|
550
|
+
messages: {
|
|
551
|
+
moveToFile: 'Style object "{{name}}" is typed as {{typeName}}. Move it to its own file under the style folder.'
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
create(context) {
|
|
555
|
+
const filename = context.getFilename();
|
|
556
|
+
if (!filename.endsWith(".tsx") && !filename.endsWith(".jsx")) {
|
|
557
|
+
return {};
|
|
558
|
+
}
|
|
559
|
+
return {
|
|
560
|
+
VariableDeclarator(node) {
|
|
561
|
+
if (!node.id || node.id.type !== "Identifier")
|
|
562
|
+
return;
|
|
563
|
+
const identifier = node.id;
|
|
564
|
+
if (!identifier.typeAnnotation)
|
|
565
|
+
return;
|
|
566
|
+
const typeNode = identifier.typeAnnotation.typeAnnotation;
|
|
567
|
+
if (typeNode.type === "TSTypeReference" && typeNode.typeName) {
|
|
568
|
+
let typeName = null;
|
|
569
|
+
if (typeNode.typeName.type === "Identifier") {
|
|
570
|
+
typeName = typeNode.typeName.name;
|
|
571
|
+
} else if (typeNode.typeName.type === "TSQualifiedName") {
|
|
572
|
+
typeName = typeNode.typeName.right.name;
|
|
573
|
+
}
|
|
574
|
+
if (typeName === "CSSProperties") {
|
|
575
|
+
context.report({
|
|
576
|
+
node,
|
|
577
|
+
messageId: "moveToFile",
|
|
578
|
+
data: {
|
|
579
|
+
name: identifier.name,
|
|
580
|
+
typeName
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
// src/rules/no-unnecessary-key.js
|
|
591
|
+
var no_unnecessary_key_default = {
|
|
592
|
+
meta: {
|
|
593
|
+
type: "problem",
|
|
594
|
+
docs: {
|
|
595
|
+
description: "enforce that the key prop is only used on components rendered as part of a mapping",
|
|
596
|
+
recommended: false
|
|
597
|
+
},
|
|
598
|
+
schema: [],
|
|
599
|
+
messages: {
|
|
600
|
+
unnecessaryKey: "The key prop should only be used on elements that are directly rendered as part of an array mapping."
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
create(context) {
|
|
604
|
+
function getAncestors(node) {
|
|
605
|
+
const ancestors = [];
|
|
606
|
+
let current = node.parent;
|
|
607
|
+
while (current) {
|
|
608
|
+
ancestors.push(current);
|
|
609
|
+
current = current.parent;
|
|
610
|
+
}
|
|
611
|
+
return ancestors;
|
|
612
|
+
}
|
|
613
|
+
function isInsideMapCall(ancestors) {
|
|
614
|
+
return ancestors.some((node) => {
|
|
615
|
+
if (node.type === "CallExpression" && node.callee && node.callee.type === "MemberExpression") {
|
|
616
|
+
const property = node.callee.property;
|
|
617
|
+
return property.type === "Identifier" && property.name === "map" || property.type === "Literal" && property.value === "map";
|
|
618
|
+
}
|
|
619
|
+
return false;
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
function isReturnedFromFunction(ancestors) {
|
|
623
|
+
return ancestors.some((node) => node.type === "ReturnStatement");
|
|
624
|
+
}
|
|
625
|
+
function checkJSXOpeningElement(node) {
|
|
626
|
+
const keyAttribute = node.attributes.find((attr) => attr.type === "JSXAttribute" && attr.name && attr.name.name === "key");
|
|
627
|
+
if (!keyAttribute) {
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
const ancestors = typeof context.getAncestors === "function" ? context.getAncestors() : getAncestors(node);
|
|
631
|
+
if (isInsideMapCall(ancestors)) {
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
if (isReturnedFromFunction(ancestors)) {
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
context.report({
|
|
638
|
+
node: keyAttribute,
|
|
639
|
+
messageId: "unnecessaryKey"
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
return {
|
|
643
|
+
JSXOpeningElement: checkJSXOpeningElement
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
// src/rules/sort-exports.js
|
|
649
|
+
var sort_exports_default = {
|
|
650
|
+
meta: {
|
|
651
|
+
type: "suggestion",
|
|
652
|
+
docs: {
|
|
653
|
+
description: "Enforce that top-level export declarations are sorted by exported name and, optionally, that variable exports come before function exports",
|
|
654
|
+
category: "Stylistic Issues",
|
|
655
|
+
recommended: false
|
|
656
|
+
},
|
|
657
|
+
fixable: "code",
|
|
658
|
+
schema: [
|
|
659
|
+
{
|
|
660
|
+
type: "object",
|
|
661
|
+
properties: {
|
|
662
|
+
order: {
|
|
663
|
+
enum: ["asc", "desc"]
|
|
664
|
+
},
|
|
665
|
+
caseSensitive: {
|
|
666
|
+
type: "boolean"
|
|
667
|
+
},
|
|
668
|
+
natural: {
|
|
669
|
+
type: "boolean"
|
|
670
|
+
},
|
|
671
|
+
minKeys: {
|
|
672
|
+
type: "integer",
|
|
673
|
+
minimum: 2
|
|
674
|
+
},
|
|
675
|
+
variablesBeforeFunctions: {
|
|
676
|
+
type: "boolean"
|
|
677
|
+
}
|
|
678
|
+
},
|
|
679
|
+
additionalProperties: false
|
|
680
|
+
}
|
|
681
|
+
],
|
|
682
|
+
messages: {
|
|
683
|
+
alphabetical: "Export declarations are not sorted alphabetically. Expected order: {{expectedOrder}}.",
|
|
684
|
+
variablesBeforeFunctions: "Non-function exports should come before function exports."
|
|
685
|
+
}
|
|
686
|
+
},
|
|
687
|
+
create(context) {
|
|
688
|
+
const sourceCode = context.getSourceCode();
|
|
689
|
+
const options = context.options[0] || {};
|
|
690
|
+
const order = options.order || "asc";
|
|
691
|
+
const caseSensitive = options.caseSensitive !== undefined ? options.caseSensitive : false;
|
|
692
|
+
const natural = options.natural !== undefined ? options.natural : false;
|
|
693
|
+
const minKeys = options.minKeys !== undefined ? options.minKeys : 2;
|
|
694
|
+
const variablesBeforeFunctions = options.variablesBeforeFunctions !== undefined ? options.variablesBeforeFunctions : false;
|
|
695
|
+
function generateExportText(node) {
|
|
696
|
+
return sourceCode.getText(node).trim().replace(/\s*;?\s*$/, ";");
|
|
697
|
+
}
|
|
698
|
+
function compareStrings(a, b) {
|
|
699
|
+
let strA = a;
|
|
700
|
+
let strB = b;
|
|
701
|
+
if (!caseSensitive) {
|
|
702
|
+
strA = strA.toLowerCase();
|
|
703
|
+
strB = strB.toLowerCase();
|
|
704
|
+
}
|
|
705
|
+
let cmp = natural ? strA.localeCompare(strB, undefined, { numeric: true }) : strA.localeCompare(strB);
|
|
706
|
+
return order === "asc" ? cmp : -cmp;
|
|
707
|
+
}
|
|
708
|
+
function getExportName(node) {
|
|
709
|
+
if (node.declaration) {
|
|
710
|
+
const decl = node.declaration;
|
|
711
|
+
if (decl.type === "VariableDeclaration") {
|
|
712
|
+
if (decl.declarations.length === 1) {
|
|
713
|
+
const id = decl.declarations[0].id;
|
|
714
|
+
if (id.type === "Identifier") {
|
|
715
|
+
return id.name;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
} else if (decl.type === "FunctionDeclaration" || decl.type === "ClassDeclaration") {
|
|
719
|
+
if (decl.id && decl.id.type === "Identifier") {
|
|
720
|
+
return decl.id.name;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
} else if (node.specifiers && node.specifiers.length === 1) {
|
|
724
|
+
const spec = node.specifiers[0];
|
|
725
|
+
return spec.exported.name || spec.exported.value;
|
|
726
|
+
}
|
|
727
|
+
return null;
|
|
728
|
+
}
|
|
729
|
+
function isFunctionExport(node) {
|
|
730
|
+
if (node.declaration) {
|
|
731
|
+
const decl = node.declaration;
|
|
732
|
+
if (decl.type === "VariableDeclaration") {
|
|
733
|
+
if (decl.declarations.length === 1) {
|
|
734
|
+
const init = decl.declarations[0].init;
|
|
735
|
+
return init && (init.type === "FunctionExpression" || init.type === "ArrowFunctionExpression");
|
|
736
|
+
}
|
|
737
|
+
} else if (decl.type === "FunctionDeclaration") {
|
|
738
|
+
return true;
|
|
739
|
+
}
|
|
740
|
+
return false;
|
|
741
|
+
}
|
|
742
|
+
return false;
|
|
743
|
+
}
|
|
744
|
+
function sortComparator(a, b) {
|
|
745
|
+
const kindA = a.node.exportKind || "value";
|
|
746
|
+
const kindB = b.node.exportKind || "value";
|
|
747
|
+
if (kindA !== kindB) {
|
|
748
|
+
return kindA === "type" ? -1 : 1;
|
|
749
|
+
}
|
|
750
|
+
if (variablesBeforeFunctions) {
|
|
751
|
+
if (a.isFunction !== b.isFunction) {
|
|
752
|
+
return a.isFunction ? 1 : -1;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
return compareStrings(a.name, b.name);
|
|
756
|
+
}
|
|
757
|
+
function hasForwardDependency(node, laterNames, visited = new WeakSet) {
|
|
758
|
+
if (!node || typeof node !== "object") {
|
|
759
|
+
return false;
|
|
760
|
+
}
|
|
761
|
+
if (visited.has(node)) {
|
|
762
|
+
return false;
|
|
763
|
+
}
|
|
764
|
+
visited.add(node);
|
|
765
|
+
if (node.type === "Identifier" && laterNames.has(node.name)) {
|
|
766
|
+
return true;
|
|
767
|
+
}
|
|
768
|
+
for (const key in node) {
|
|
769
|
+
if (Object.prototype.hasOwnProperty.call(node, key)) {
|
|
770
|
+
const value = node[key];
|
|
771
|
+
if (Array.isArray(value)) {
|
|
772
|
+
for (const element of value) {
|
|
773
|
+
if (element && typeof element === "object") {
|
|
774
|
+
if (hasForwardDependency(element, laterNames, visited)) {
|
|
775
|
+
return true;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
} else if (value && typeof value === "object") {
|
|
780
|
+
if (hasForwardDependency(value, laterNames, visited)) {
|
|
781
|
+
return true;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
return false;
|
|
787
|
+
}
|
|
788
|
+
function processExportBlock(block) {
|
|
789
|
+
if (block.length < minKeys) {
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
const items = block.map((node) => {
|
|
793
|
+
const name = getExportName(node);
|
|
794
|
+
if (name === null) {
|
|
795
|
+
return null;
|
|
796
|
+
}
|
|
797
|
+
return {
|
|
798
|
+
name,
|
|
799
|
+
node,
|
|
800
|
+
isFunction: isFunctionExport(node),
|
|
801
|
+
text: sourceCode.getText(node)
|
|
802
|
+
};
|
|
803
|
+
}).filter(Boolean);
|
|
804
|
+
if (items.length < minKeys) {
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
const sortedItems = items.slice().sort(sortComparator);
|
|
808
|
+
let reportNeeded = false;
|
|
809
|
+
let messageId = "alphabetical";
|
|
810
|
+
for (let i = 1;i < items.length; i++) {
|
|
811
|
+
if (sortComparator(items[i - 1], items[i]) > 0) {
|
|
812
|
+
reportNeeded = true;
|
|
813
|
+
if (variablesBeforeFunctions && items[i - 1].isFunction && !items[i].isFunction) {
|
|
814
|
+
messageId = "variablesBeforeFunctions";
|
|
815
|
+
}
|
|
816
|
+
break;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
if (!reportNeeded) {
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
const exportNames = items.map((item) => item.name);
|
|
823
|
+
for (let i = 0;i < items.length; i++) {
|
|
824
|
+
const laterNames = new Set(exportNames.slice(i + 1));
|
|
825
|
+
const nodeToCheck = items[i].node.declaration || items[i].node;
|
|
826
|
+
if (hasForwardDependency(nodeToCheck, laterNames)) {
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
const expectedOrder = sortedItems.map((item) => item.name).join(", ");
|
|
831
|
+
context.report({
|
|
832
|
+
node: items[0].node,
|
|
833
|
+
messageId,
|
|
834
|
+
data: {
|
|
835
|
+
expectedOrder
|
|
836
|
+
},
|
|
837
|
+
fix(fixer) {
|
|
838
|
+
const fixableNodes = block.filter((n) => {
|
|
839
|
+
if (n.declaration) {
|
|
840
|
+
if (n.declaration.type === "VariableDeclaration" && n.declaration.declarations.length === 1 && n.declaration.declarations[0].id.type === "Identifier") {
|
|
841
|
+
return true;
|
|
842
|
+
}
|
|
843
|
+
if ((n.declaration.type === "FunctionDeclaration" || n.declaration.type === "ClassDeclaration") && n.declaration.id && n.declaration.id.type === "Identifier") {
|
|
844
|
+
return true;
|
|
845
|
+
}
|
|
846
|
+
return false;
|
|
847
|
+
}
|
|
848
|
+
if (n.specifiers && n.specifiers.length === 1) {
|
|
849
|
+
return true;
|
|
850
|
+
}
|
|
851
|
+
return false;
|
|
852
|
+
});
|
|
853
|
+
if (fixableNodes.length < minKeys) {
|
|
854
|
+
return null;
|
|
855
|
+
}
|
|
856
|
+
const sortedText = sortedItems.map((item) => generateExportText(item.node)).join(`
|
|
857
|
+
`);
|
|
858
|
+
const first = block[0].range[0];
|
|
859
|
+
const last = block[block.length - 1].range[1];
|
|
860
|
+
const originalText = sourceCode.getText().slice(first, last);
|
|
861
|
+
if (originalText === sortedText) {
|
|
862
|
+
return null;
|
|
863
|
+
}
|
|
864
|
+
return fixer.replaceTextRange([first, last], sortedText);
|
|
865
|
+
}
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
return {
|
|
869
|
+
"Program:exit"(node) {
|
|
870
|
+
const body = node.body;
|
|
871
|
+
let block = [];
|
|
872
|
+
for (let i = 0;i < body.length; i++) {
|
|
873
|
+
const n = body[i];
|
|
874
|
+
if (n.type === "ExportNamedDeclaration" && !n.source && getExportName(n) !== null) {
|
|
875
|
+
block.push(n);
|
|
876
|
+
} else {
|
|
877
|
+
if (block.length) {
|
|
878
|
+
processExportBlock(block);
|
|
879
|
+
block = [];
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
if (block.length) {
|
|
884
|
+
processExportBlock(block);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
};
|
|
890
|
+
|
|
891
|
+
// src/rules/localize-react-props.js
|
|
892
|
+
var localize_react_props_default = {
|
|
893
|
+
meta: {
|
|
894
|
+
type: "suggestion",
|
|
895
|
+
docs: {
|
|
896
|
+
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.",
|
|
897
|
+
category: "Best Practices",
|
|
898
|
+
recommended: false
|
|
899
|
+
}
|
|
900
|
+
},
|
|
901
|
+
create(context) {
|
|
902
|
+
const candidateVariables = [];
|
|
903
|
+
function getJSXElementName(jsxElement) {
|
|
904
|
+
if (!jsxElement || !jsxElement.openingElement || !jsxElement.openingElement.name) {
|
|
905
|
+
return "";
|
|
906
|
+
}
|
|
907
|
+
const nameNode = jsxElement.openingElement.name;
|
|
908
|
+
if (nameNode.type === "JSXIdentifier") {
|
|
909
|
+
return nameNode.name;
|
|
910
|
+
}
|
|
911
|
+
if (nameNode.type === "JSXMemberExpression") {
|
|
912
|
+
let current = nameNode;
|
|
913
|
+
while (current.property) {
|
|
914
|
+
current = current.property;
|
|
915
|
+
}
|
|
916
|
+
if (current && current.type === "JSXIdentifier") {
|
|
917
|
+
return current.name;
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
return "";
|
|
921
|
+
}
|
|
922
|
+
function isUseStateCall(node) {
|
|
923
|
+
return node && node.type === "CallExpression" && node.callee && (node.callee.type === "Identifier" && node.callee.name === "useState" || node.callee.type === "MemberExpression" && node.callee.property && node.callee.property.name === "useState");
|
|
924
|
+
}
|
|
925
|
+
function isHookCall(node) {
|
|
926
|
+
return node && node.type === "CallExpression" && node.callee && node.callee.type === "Identifier" && /^use[A-Z]/.test(node.callee.name) && node.callee.name !== "useState";
|
|
927
|
+
}
|
|
928
|
+
function getJSXAncestor(node) {
|
|
929
|
+
let current = node.parent;
|
|
930
|
+
while (current) {
|
|
931
|
+
if (current.type === "JSXElement") {
|
|
932
|
+
return current;
|
|
933
|
+
}
|
|
934
|
+
current = current.parent;
|
|
935
|
+
}
|
|
936
|
+
return null;
|
|
937
|
+
}
|
|
938
|
+
function isContextProviderValueProp(node) {
|
|
939
|
+
let current = node.parent;
|
|
940
|
+
while (current) {
|
|
941
|
+
if (current.type === "JSXAttribute" && current.name && current.name.name === "value") {
|
|
942
|
+
if (current.parent && current.parent.type === "JSXOpeningElement") {
|
|
943
|
+
const nameNode = current.parent.name;
|
|
944
|
+
if (nameNode.type === "JSXIdentifier") {
|
|
945
|
+
const tagName = nameNode.name;
|
|
946
|
+
if (tagName.endsWith("Provider") || tagName.endsWith("Context")) {
|
|
947
|
+
return true;
|
|
948
|
+
}
|
|
949
|
+
} else if (nameNode.type === "JSXMemberExpression") {
|
|
950
|
+
let currentMember = nameNode;
|
|
951
|
+
while (currentMember.type === "JSXMemberExpression") {
|
|
952
|
+
currentMember = currentMember.property;
|
|
953
|
+
}
|
|
954
|
+
if (currentMember && currentMember.type === "JSXIdentifier") {
|
|
955
|
+
if (currentMember.name.endsWith("Provider") || currentMember.name.endsWith("Context")) {
|
|
956
|
+
return true;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
current = current.parent;
|
|
963
|
+
}
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
function isCustomJSXElement(jsxElement) {
|
|
967
|
+
if (!jsxElement || !jsxElement.openingElement || !jsxElement.openingElement.name) {
|
|
968
|
+
return false;
|
|
969
|
+
}
|
|
970
|
+
const nameNode = jsxElement.openingElement.name;
|
|
971
|
+
if (nameNode.type === "JSXIdentifier") {
|
|
972
|
+
return /^[A-Z]/.test(nameNode.name);
|
|
973
|
+
}
|
|
974
|
+
if (nameNode.type === "JSXMemberExpression") {
|
|
975
|
+
let current = nameNode;
|
|
976
|
+
while (current.object) {
|
|
977
|
+
current = current.object;
|
|
978
|
+
}
|
|
979
|
+
return current.type === "JSXIdentifier" && /^[A-Z]/.test(current.name);
|
|
980
|
+
}
|
|
981
|
+
return false;
|
|
982
|
+
}
|
|
983
|
+
function getComponentFunction(node) {
|
|
984
|
+
let current = node;
|
|
985
|
+
while (current) {
|
|
986
|
+
if (current.type === "FunctionDeclaration" || current.type === "FunctionExpression" || current.type === "ArrowFunctionExpression") {
|
|
987
|
+
return current;
|
|
988
|
+
}
|
|
989
|
+
current = current.parent;
|
|
990
|
+
}
|
|
991
|
+
return null;
|
|
992
|
+
}
|
|
993
|
+
function analyzeVariableUsage(declarationNode, varName, componentFunction) {
|
|
994
|
+
const usage = { jsxUsageSet: new Set, hasOutsideUsage: false };
|
|
995
|
+
const sourceCode = context.getSourceCode();
|
|
996
|
+
const visitorKeys = sourceCode.visitorKeys || {};
|
|
997
|
+
const stack = [];
|
|
998
|
+
if (componentFunction.body.type === "BlockStatement") {
|
|
999
|
+
for (let i = 0;i < componentFunction.body.body.length; i++) {
|
|
1000
|
+
stack.push(componentFunction.body.body[i]);
|
|
1001
|
+
}
|
|
1002
|
+
} else {
|
|
1003
|
+
stack.push(componentFunction.body);
|
|
1004
|
+
}
|
|
1005
|
+
while (stack.length) {
|
|
1006
|
+
const currentNode = stack.pop();
|
|
1007
|
+
if (!currentNode)
|
|
1008
|
+
continue;
|
|
1009
|
+
if (currentNode.type === "Identifier" && currentNode.name === varName && currentNode !== declarationNode) {
|
|
1010
|
+
if (isContextProviderValueProp(currentNode)) {} else {
|
|
1011
|
+
const jsxAncestor = getJSXAncestor(currentNode);
|
|
1012
|
+
if (jsxAncestor && isCustomJSXElement(jsxAncestor)) {
|
|
1013
|
+
usage.jsxUsageSet.add(jsxAncestor);
|
|
1014
|
+
} else {
|
|
1015
|
+
usage.hasOutsideUsage = true;
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
const isFunction = currentNode.type === "FunctionDeclaration" || currentNode.type === "FunctionExpression" || currentNode.type === "ArrowFunctionExpression";
|
|
1020
|
+
if (isFunction && currentNode !== componentFunction) {
|
|
1021
|
+
let shadows = false;
|
|
1022
|
+
if (currentNode.params && currentNode.params.length > 0) {
|
|
1023
|
+
for (let i = 0;i < currentNode.params.length; i++) {
|
|
1024
|
+
const param = currentNode.params[i];
|
|
1025
|
+
if (param.type === "Identifier" && param.name === varName) {
|
|
1026
|
+
shadows = true;
|
|
1027
|
+
break;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
if (shadows)
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
const keys = visitorKeys[currentNode.type] || [];
|
|
1035
|
+
for (let i = 0;i < keys.length; i++) {
|
|
1036
|
+
const key = keys[i];
|
|
1037
|
+
const child = currentNode[key];
|
|
1038
|
+
if (Array.isArray(child)) {
|
|
1039
|
+
for (let j = 0;j < child.length; j++) {
|
|
1040
|
+
if (child[j] && typeof child[j].type === "string") {
|
|
1041
|
+
stack.push(child[j]);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
} else if (child && typeof child.type === "string") {
|
|
1045
|
+
stack.push(child);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
return usage;
|
|
1050
|
+
}
|
|
1051
|
+
const componentHookVars = new WeakMap;
|
|
1052
|
+
function getHookSet(componentFunction) {
|
|
1053
|
+
if (!componentHookVars.has(componentFunction)) {
|
|
1054
|
+
componentHookVars.set(componentFunction, new Set);
|
|
1055
|
+
}
|
|
1056
|
+
return componentHookVars.get(componentFunction);
|
|
1057
|
+
}
|
|
1058
|
+
function hasHookDependency(node, hookSet) {
|
|
1059
|
+
const sourceCode = context.getSourceCode();
|
|
1060
|
+
const visitorKeys = sourceCode.visitorKeys || {};
|
|
1061
|
+
const stack = [node];
|
|
1062
|
+
while (stack.length) {
|
|
1063
|
+
const currentNode = stack.pop();
|
|
1064
|
+
if (!currentNode)
|
|
1065
|
+
continue;
|
|
1066
|
+
if (currentNode.type === "Identifier") {
|
|
1067
|
+
if (hookSet.has(currentNode.name)) {
|
|
1068
|
+
return true;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
const keys = visitorKeys[currentNode.type] || [];
|
|
1072
|
+
for (let i = 0;i < keys.length; i++) {
|
|
1073
|
+
const key = keys[i];
|
|
1074
|
+
const child = currentNode[key];
|
|
1075
|
+
if (Array.isArray(child)) {
|
|
1076
|
+
for (let j = 0;j < child.length; j++) {
|
|
1077
|
+
if (child[j] && typeof child[j].type === "string") {
|
|
1078
|
+
stack.push(child[j]);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
} else if (child && typeof child.type === "string") {
|
|
1082
|
+
stack.push(child);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
return false;
|
|
1087
|
+
}
|
|
1088
|
+
return {
|
|
1089
|
+
VariableDeclarator(node) {
|
|
1090
|
+
const componentFunction = getComponentFunction(node);
|
|
1091
|
+
if (!componentFunction || !componentFunction.body)
|
|
1092
|
+
return;
|
|
1093
|
+
if (node.init && node.id && node.id.type === "Identifier" && node.init.type === "CallExpression" && isHookCall(node.init)) {
|
|
1094
|
+
const hookSet = getHookSet(componentFunction);
|
|
1095
|
+
hookSet.add(node.id.name);
|
|
1096
|
+
}
|
|
1097
|
+
if (node.init && isUseStateCall(node.init) && node.id.type === "ArrayPattern" && node.id.elements.length >= 2) {
|
|
1098
|
+
const stateElem = node.id.elements[0];
|
|
1099
|
+
const setterElem = node.id.elements[1];
|
|
1100
|
+
if (!stateElem || stateElem.type !== "Identifier" || !setterElem || setterElem.type !== "Identifier") {
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
const stateVarName = stateElem.name;
|
|
1104
|
+
const setterVarName = setterElem.name;
|
|
1105
|
+
const stateUsage = analyzeVariableUsage(stateElem, stateVarName, componentFunction);
|
|
1106
|
+
const setterUsage = analyzeVariableUsage(setterElem, setterVarName, componentFunction);
|
|
1107
|
+
const stateExclusivelySingleJSX = !stateUsage.hasOutsideUsage && stateUsage.jsxUsageSet.size === 1;
|
|
1108
|
+
const setterExclusivelySingleJSX = !setterUsage.hasOutsideUsage && setterUsage.jsxUsageSet.size === 1;
|
|
1109
|
+
if (stateExclusivelySingleJSX && setterExclusivelySingleJSX && [...stateUsage.jsxUsageSet][0] === [...setterUsage.jsxUsageSet][0]) {
|
|
1110
|
+
context.report({
|
|
1111
|
+
node,
|
|
1112
|
+
message: "State variable '{{stateVarName}}' and its setter '{{setterVarName}}' are only passed to a single custom child component. Consider moving the state into that component.",
|
|
1113
|
+
data: { stateVarName, setterVarName }
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
} else if (node.id && node.id.type === "Identifier") {
|
|
1117
|
+
const varName = node.id.name;
|
|
1118
|
+
if (node.init) {
|
|
1119
|
+
const hookSet = getHookSet(componentFunction);
|
|
1120
|
+
if (hasHookDependency(node.init, hookSet)) {
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
const usage = analyzeVariableUsage(node.id, varName, componentFunction);
|
|
1125
|
+
if (!usage.hasOutsideUsage && usage.jsxUsageSet.size === 1) {
|
|
1126
|
+
const target = [...usage.jsxUsageSet][0];
|
|
1127
|
+
const componentName = getJSXElementName(target);
|
|
1128
|
+
candidateVariables.push({
|
|
1129
|
+
node,
|
|
1130
|
+
varName,
|
|
1131
|
+
componentName
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
},
|
|
1136
|
+
"Program:exit"() {
|
|
1137
|
+
const groups = new Map;
|
|
1138
|
+
candidateVariables.forEach((candidate) => {
|
|
1139
|
+
const key = candidate.componentName;
|
|
1140
|
+
if (!groups.has(key)) {
|
|
1141
|
+
groups.set(key, []);
|
|
1142
|
+
}
|
|
1143
|
+
groups.get(key).push(candidate);
|
|
1144
|
+
});
|
|
1145
|
+
groups.forEach((candidates) => {
|
|
1146
|
+
if (candidates.length === 1) {
|
|
1147
|
+
const candidate = candidates[0];
|
|
1148
|
+
context.report({
|
|
1149
|
+
node: candidate.node,
|
|
1150
|
+
message: "Variable '{{varName}}' is only passed to a single custom child component. Consider moving it to that component.",
|
|
1151
|
+
data: { varName: candidate.varName }
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
|
|
1160
|
+
// src/rules/no-or-none-component.js
|
|
1161
|
+
var no_or_none_component_default = {
|
|
1162
|
+
meta: {
|
|
1163
|
+
type: "suggestion",
|
|
1164
|
+
docs: {
|
|
1165
|
+
description: "Prefer using logical && operator over ternary with null/undefined for conditional JSX rendering.",
|
|
1166
|
+
recommended: false
|
|
1167
|
+
},
|
|
1168
|
+
messages: {
|
|
1169
|
+
useLogicalAnd: "Prefer using the logical '&&' operator instead of a ternary with null/undefined for conditional rendering."
|
|
1170
|
+
}
|
|
1171
|
+
},
|
|
1172
|
+
create(context) {
|
|
1173
|
+
return {
|
|
1174
|
+
ConditionalExpression(node) {
|
|
1175
|
+
const alternate = node.alternate;
|
|
1176
|
+
if (alternate && (alternate.type === "Literal" && alternate.value === null || alternate.type === "Identifier" && alternate.name === "undefined")) {
|
|
1177
|
+
if (node.parent && node.parent.type === "JSXExpressionContainer") {
|
|
1178
|
+
const containerParent = node.parent.parent;
|
|
1179
|
+
if (containerParent && containerParent.type !== "JSXAttribute") {
|
|
1180
|
+
context.report({
|
|
1181
|
+
node,
|
|
1182
|
+
messageId: "useLogicalAnd"
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
};
|
|
1191
|
+
|
|
1192
|
+
// src/rules/no-button-navigation.js
|
|
1193
|
+
var no_button_navigation_default = {
|
|
1194
|
+
meta: {
|
|
1195
|
+
type: "suggestion",
|
|
1196
|
+
docs: {
|
|
1197
|
+
description: "Enforce using anchor tags for navigation instead of buttons with onClick handlers that use window navigation methods (e.g., window.location, window.open)",
|
|
1198
|
+
category: "Best Practices",
|
|
1199
|
+
recommended: false
|
|
1200
|
+
},
|
|
1201
|
+
schema: []
|
|
1202
|
+
},
|
|
1203
|
+
create(context) {
|
|
1204
|
+
function containsWindowNavigation(node) {
|
|
1205
|
+
let found = false;
|
|
1206
|
+
const visited = new WeakSet;
|
|
1207
|
+
function inspect(n) {
|
|
1208
|
+
if (!n || found)
|
|
1209
|
+
return;
|
|
1210
|
+
if (typeof n !== "object")
|
|
1211
|
+
return;
|
|
1212
|
+
if (visited.has(n))
|
|
1213
|
+
return;
|
|
1214
|
+
visited.add(n);
|
|
1215
|
+
if (n.type === "MemberExpression") {
|
|
1216
|
+
if (n.object && n.object.type === "Identifier" && n.object.name === "window" && n.property && (n.property.type === "Identifier" && (n.property.name === "location" || n.property.name === "open") || n.property.type === "Literal" && (n.property.value === "location" || n.property.value === "open"))) {
|
|
1217
|
+
found = true;
|
|
1218
|
+
return;
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
if (n.type === "CallExpression" && n.callee && n.callee.type === "MemberExpression") {
|
|
1222
|
+
const callee = n.callee;
|
|
1223
|
+
if (callee.object && callee.object.type === "Identifier" && callee.object.name === "window" && callee.property && callee.property.type === "Identifier" && callee.property.name === "open") {
|
|
1224
|
+
found = true;
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
for (const key in n) {
|
|
1229
|
+
if (Object.prototype.hasOwnProperty.call(n, key)) {
|
|
1230
|
+
const child = n[key];
|
|
1231
|
+
if (Array.isArray(child)) {
|
|
1232
|
+
child.forEach(inspect);
|
|
1233
|
+
} else if (child && typeof child === "object") {
|
|
1234
|
+
inspect(child);
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
inspect(node);
|
|
1240
|
+
return found;
|
|
1241
|
+
}
|
|
1242
|
+
return {
|
|
1243
|
+
JSXElement(node) {
|
|
1244
|
+
const openingElement = node.openingElement;
|
|
1245
|
+
if (openingElement.name && openingElement.name.type === "JSXIdentifier" && openingElement.name.name === "button") {
|
|
1246
|
+
const attributes = openingElement.attributes;
|
|
1247
|
+
for (const attr of attributes) {
|
|
1248
|
+
if (attr.type === "JSXAttribute" && attr.name && attr.name.name === "onClick" && attr.value && attr.value.type === "JSXExpressionContainer") {
|
|
1249
|
+
const expression = attr.value.expression;
|
|
1250
|
+
if (containsWindowNavigation(expression)) {
|
|
1251
|
+
context.report({
|
|
1252
|
+
node: attr,
|
|
1253
|
+
message: "Use an anchor tag for navigation instead of a button with an onClick handler that uses window navigation methods."
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
};
|
|
1263
|
+
|
|
1264
|
+
// src/rules/no-multi-style-objects.js
|
|
1265
|
+
var no_multi_style_objects_default = {
|
|
1266
|
+
meta: {
|
|
1267
|
+
type: "problem",
|
|
1268
|
+
docs: {
|
|
1269
|
+
description: "Disallow grouping CSS style objects in a single export; export each style separately.",
|
|
1270
|
+
category: "Best Practices",
|
|
1271
|
+
recommended: false
|
|
1272
|
+
},
|
|
1273
|
+
schema: []
|
|
1274
|
+
},
|
|
1275
|
+
create(context) {
|
|
1276
|
+
function checkObjectExpression(node) {
|
|
1277
|
+
if (node.properties && node.properties.length > 0) {
|
|
1278
|
+
const cssStyleProperties = node.properties.filter((prop) => {
|
|
1279
|
+
if (prop.key) {
|
|
1280
|
+
if (prop.key.type === "Identifier") {
|
|
1281
|
+
return prop.key.name.endsWith("Style");
|
|
1282
|
+
}
|
|
1283
|
+
if (prop.key.type === "Literal" && typeof prop.key.value === "string") {
|
|
1284
|
+
return prop.key.value.endsWith("Style");
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
return false;
|
|
1288
|
+
});
|
|
1289
|
+
if (cssStyleProperties.length > 1) {
|
|
1290
|
+
context.report({
|
|
1291
|
+
node,
|
|
1292
|
+
message: "Do not group CSS style objects in a single export; export each style separately."
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
return {
|
|
1298
|
+
ExportDefaultDeclaration(node) {
|
|
1299
|
+
if (node.declaration && node.declaration.type === "ObjectExpression") {
|
|
1300
|
+
checkObjectExpression(node.declaration);
|
|
1301
|
+
}
|
|
1302
|
+
},
|
|
1303
|
+
ReturnStatement(node) {
|
|
1304
|
+
if (node.argument && node.argument.type === "ObjectExpression") {
|
|
1305
|
+
checkObjectExpression(node.argument);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
|
|
1312
|
+
// src/rules/no-useless-function.js
|
|
1313
|
+
var no_useless_function_default = {
|
|
1314
|
+
meta: {
|
|
1315
|
+
type: "suggestion",
|
|
1316
|
+
docs: {
|
|
1317
|
+
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).",
|
|
1318
|
+
category: "Best Practices",
|
|
1319
|
+
recommended: false
|
|
1320
|
+
},
|
|
1321
|
+
fixable: null
|
|
1322
|
+
},
|
|
1323
|
+
create(context) {
|
|
1324
|
+
function isCallbackFunction(node) {
|
|
1325
|
+
return node.parent && node.parent.type === "CallExpression" && node.parent.arguments.includes(node);
|
|
1326
|
+
}
|
|
1327
|
+
return {
|
|
1328
|
+
ArrowFunctionExpression(node) {
|
|
1329
|
+
if (node.params.length === 0 && node.body && node.body.type === "ObjectExpression") {
|
|
1330
|
+
if (isCallbackFunction(node)) {
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1333
|
+
context.report({
|
|
1334
|
+
node,
|
|
1335
|
+
message: "This function has no parameters and simply returns an object. Consider exporting the object directly instead of wrapping it in a function."
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
1341
|
+
};
|
|
1342
|
+
|
|
1343
|
+
// src/rules/min-var-length.js
|
|
1344
|
+
var min_var_length_default = {
|
|
1345
|
+
meta: {
|
|
1346
|
+
type: "problem",
|
|
1347
|
+
docs: {
|
|
1348
|
+
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.",
|
|
1349
|
+
recommended: false
|
|
1350
|
+
},
|
|
1351
|
+
schema: [
|
|
1352
|
+
{
|
|
1353
|
+
type: "object",
|
|
1354
|
+
properties: {
|
|
1355
|
+
minLength: {
|
|
1356
|
+
type: "number",
|
|
1357
|
+
default: 1
|
|
1358
|
+
},
|
|
1359
|
+
allowedVars: {
|
|
1360
|
+
type: "array",
|
|
1361
|
+
items: {
|
|
1362
|
+
type: "string",
|
|
1363
|
+
minLength: 1
|
|
1364
|
+
},
|
|
1365
|
+
default: []
|
|
1366
|
+
}
|
|
1367
|
+
},
|
|
1368
|
+
additionalProperties: false
|
|
1369
|
+
}
|
|
1370
|
+
],
|
|
1371
|
+
messages: {
|
|
1372
|
+
variableNameTooShort: "Variable '{{name}}' is too short. Minimum allowed length is {{minLength}} characters unless an outer variable with a longer name starting with '{{name}}' exists."
|
|
1373
|
+
}
|
|
1374
|
+
},
|
|
1375
|
+
create(context) {
|
|
1376
|
+
const sourceCode = context.getSourceCode();
|
|
1377
|
+
const options = context.options[0] || {};
|
|
1378
|
+
const minLength = typeof options.minLength === "number" ? options.minLength : 1;
|
|
1379
|
+
const allowedVars = options.allowedVars || [];
|
|
1380
|
+
function getAncestors(node) {
|
|
1381
|
+
const ancestors = [];
|
|
1382
|
+
let current = node.parent;
|
|
1383
|
+
while (current) {
|
|
1384
|
+
ancestors.push(current);
|
|
1385
|
+
current = current.parent;
|
|
1386
|
+
}
|
|
1387
|
+
return ancestors;
|
|
1388
|
+
}
|
|
1389
|
+
function getScope(node) {
|
|
1390
|
+
return sourceCode.scopeManager.acquire(node) || sourceCode.scopeManager.globalScope;
|
|
1391
|
+
}
|
|
1392
|
+
function getVariablesInNearestBlock(node) {
|
|
1393
|
+
let current = node.parent;
|
|
1394
|
+
while (current && current.type !== "BlockStatement") {
|
|
1395
|
+
current = current.parent;
|
|
1396
|
+
}
|
|
1397
|
+
const names = [];
|
|
1398
|
+
if (current && Array.isArray(current.body)) {
|
|
1399
|
+
for (const stmt of current.body) {
|
|
1400
|
+
if (stmt.type === "VariableDeclaration") {
|
|
1401
|
+
for (const decl of stmt.declarations) {
|
|
1402
|
+
if (decl.id && decl.id.type === "Identifier") {
|
|
1403
|
+
names.push(decl.id.name);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
return names;
|
|
1410
|
+
}
|
|
1411
|
+
function extractIdentifiersFromPattern(pattern, identifiers = []) {
|
|
1412
|
+
if (!pattern)
|
|
1413
|
+
return identifiers;
|
|
1414
|
+
switch (pattern.type) {
|
|
1415
|
+
case "Identifier":
|
|
1416
|
+
identifiers.push(pattern.name);
|
|
1417
|
+
break;
|
|
1418
|
+
case "ObjectPattern":
|
|
1419
|
+
for (const prop of pattern.properties) {
|
|
1420
|
+
if (prop.type === "Property") {
|
|
1421
|
+
extractIdentifiersFromPattern(prop.value, identifiers);
|
|
1422
|
+
} else if (prop.type === "RestElement") {
|
|
1423
|
+
extractIdentifiersFromPattern(prop.argument, identifiers);
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
break;
|
|
1427
|
+
case "ArrayPattern":
|
|
1428
|
+
for (const element of pattern.elements) {
|
|
1429
|
+
if (element)
|
|
1430
|
+
extractIdentifiersFromPattern(element, identifiers);
|
|
1431
|
+
}
|
|
1432
|
+
break;
|
|
1433
|
+
case "AssignmentPattern":
|
|
1434
|
+
extractIdentifiersFromPattern(pattern.left, identifiers);
|
|
1435
|
+
break;
|
|
1436
|
+
default:
|
|
1437
|
+
break;
|
|
1438
|
+
}
|
|
1439
|
+
return identifiers;
|
|
1440
|
+
}
|
|
1441
|
+
function hasOuterCorrespondingIdentifier(shortName, node) {
|
|
1442
|
+
let currentScope = getScope(node);
|
|
1443
|
+
let outer = currentScope.upper;
|
|
1444
|
+
while (outer) {
|
|
1445
|
+
for (const variable of outer.variables) {
|
|
1446
|
+
if (variable.name.length >= minLength && variable.name.length > shortName.length && variable.name.startsWith(shortName)) {
|
|
1447
|
+
return true;
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
outer = outer.upper;
|
|
1451
|
+
}
|
|
1452
|
+
const blockVars = getVariablesInNearestBlock(node);
|
|
1453
|
+
for (const name of blockVars) {
|
|
1454
|
+
if (name.length >= minLength && name.length > shortName.length && name.startsWith(shortName)) {
|
|
1455
|
+
return true;
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
const ancestors = getAncestors(node);
|
|
1459
|
+
for (const anc of ancestors) {
|
|
1460
|
+
if (anc.type === "VariableDeclarator" && anc.id && anc.id.type === "Identifier") {
|
|
1461
|
+
const outerName = anc.id.name;
|
|
1462
|
+
if (outerName.length >= minLength && outerName.length > shortName.length && outerName.startsWith(shortName)) {
|
|
1463
|
+
return true;
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
if ((anc.type === "FunctionDeclaration" || anc.type === "FunctionExpression" || anc.type === "ArrowFunctionExpression") && Array.isArray(anc.params)) {
|
|
1467
|
+
for (const param of anc.params) {
|
|
1468
|
+
const names = extractIdentifiersFromPattern(param, []);
|
|
1469
|
+
for (const n of names) {
|
|
1470
|
+
if (n.length >= minLength && n.length > shortName.length && n.startsWith(shortName)) {
|
|
1471
|
+
return true;
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
if (anc.type === "CatchClause" && anc.param) {
|
|
1477
|
+
const names = extractIdentifiersFromPattern(anc.param, []);
|
|
1478
|
+
for (const n of names) {
|
|
1479
|
+
if (n.length >= minLength && n.length > shortName.length && n.startsWith(shortName)) {
|
|
1480
|
+
return true;
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
return false;
|
|
1486
|
+
}
|
|
1487
|
+
function checkIdentifier(node) {
|
|
1488
|
+
const name = node.name;
|
|
1489
|
+
if (typeof name === "string" && name.length < minLength) {
|
|
1490
|
+
if (allowedVars.includes(name)) {
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
if (!hasOuterCorrespondingIdentifier(name, node)) {
|
|
1494
|
+
context.report({
|
|
1495
|
+
node,
|
|
1496
|
+
messageId: "variableNameTooShort",
|
|
1497
|
+
data: { name, minLength }
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
function checkPattern(pattern) {
|
|
1503
|
+
if (!pattern)
|
|
1504
|
+
return;
|
|
1505
|
+
switch (pattern.type) {
|
|
1506
|
+
case "Identifier":
|
|
1507
|
+
checkIdentifier(pattern);
|
|
1508
|
+
break;
|
|
1509
|
+
case "ObjectPattern":
|
|
1510
|
+
for (const prop of pattern.properties) {
|
|
1511
|
+
if (prop.type === "Property") {
|
|
1512
|
+
checkPattern(prop.value);
|
|
1513
|
+
} else if (prop.type === "RestElement") {
|
|
1514
|
+
checkPattern(prop.argument);
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
break;
|
|
1518
|
+
case "ArrayPattern":
|
|
1519
|
+
for (const element of pattern.elements) {
|
|
1520
|
+
if (element)
|
|
1521
|
+
checkPattern(element);
|
|
1522
|
+
}
|
|
1523
|
+
break;
|
|
1524
|
+
case "AssignmentPattern":
|
|
1525
|
+
checkPattern(pattern.left);
|
|
1526
|
+
break;
|
|
1527
|
+
default:
|
|
1528
|
+
break;
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
return {
|
|
1532
|
+
VariableDeclarator(node) {
|
|
1533
|
+
if (node.id) {
|
|
1534
|
+
checkPattern(node.id);
|
|
1535
|
+
}
|
|
1536
|
+
},
|
|
1537
|
+
"FunctionDeclaration, FunctionExpression, ArrowFunctionExpression"(node) {
|
|
1538
|
+
for (const param of node.params) {
|
|
1539
|
+
checkPattern(param);
|
|
1540
|
+
}
|
|
1541
|
+
},
|
|
1542
|
+
CatchClause(node) {
|
|
1543
|
+
if (node.param) {
|
|
1544
|
+
checkPattern(node.param);
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
};
|
|
1550
|
+
|
|
1551
|
+
// src/rules/max-depth-extended.js
|
|
1552
|
+
var max_depth_extended_default = {
|
|
1553
|
+
meta: {
|
|
1554
|
+
type: "suggestion",
|
|
1555
|
+
docs: {
|
|
1556
|
+
description: "disallow too many nested blocks except when the block only contains an early return",
|
|
1557
|
+
category: "Best Practices",
|
|
1558
|
+
recommended: false
|
|
1559
|
+
},
|
|
1560
|
+
schema: [
|
|
1561
|
+
{
|
|
1562
|
+
type: "number"
|
|
1563
|
+
}
|
|
1564
|
+
]
|
|
1565
|
+
},
|
|
1566
|
+
create(context) {
|
|
1567
|
+
const maxDepth = typeof context.options[0] === "number" ? context.options[0] : 1;
|
|
1568
|
+
const functionStack = [];
|
|
1569
|
+
function getAncestors(node) {
|
|
1570
|
+
const ancestors = [];
|
|
1571
|
+
let current = node.parent;
|
|
1572
|
+
while (current) {
|
|
1573
|
+
ancestors.push(current);
|
|
1574
|
+
current = current.parent;
|
|
1575
|
+
}
|
|
1576
|
+
return ancestors;
|
|
1577
|
+
}
|
|
1578
|
+
function isEarlyReturnBlock(node) {
|
|
1579
|
+
return node.body.length === 1 && node.body[0].type === "ReturnStatement";
|
|
1580
|
+
}
|
|
1581
|
+
function checkDepth(node, depth) {
|
|
1582
|
+
if (depth > maxDepth) {
|
|
1583
|
+
context.report({
|
|
1584
|
+
node,
|
|
1585
|
+
message: "Blocks are nested too deeply ({{depth}}). Maximum allowed is {{maxDepth}} or an early return.",
|
|
1586
|
+
data: { depth, maxDepth }
|
|
1587
|
+
});
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
return {
|
|
1591
|
+
FunctionDeclaration() {
|
|
1592
|
+
functionStack.push(0);
|
|
1593
|
+
},
|
|
1594
|
+
FunctionExpression() {
|
|
1595
|
+
functionStack.push(0);
|
|
1596
|
+
},
|
|
1597
|
+
ArrowFunctionExpression() {
|
|
1598
|
+
functionStack.push(0);
|
|
1599
|
+
},
|
|
1600
|
+
BlockStatement(node) {
|
|
1601
|
+
const ancestors = getAncestors(node);
|
|
1602
|
+
const parent = ancestors[0];
|
|
1603
|
+
if (parent && (parent.type === "FunctionDeclaration" || parent.type === "FunctionExpression" || parent.type === "ArrowFunctionExpression") && node === parent.body) {
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
if (isEarlyReturnBlock(node)) {
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
if (functionStack.length > 0) {
|
|
1610
|
+
functionStack[functionStack.length - 1]++;
|
|
1611
|
+
checkDepth(node, functionStack[functionStack.length - 1]);
|
|
1612
|
+
}
|
|
1613
|
+
},
|
|
1614
|
+
"BlockStatement:exit"(node) {
|
|
1615
|
+
const ancestors = getAncestors(node);
|
|
1616
|
+
const parent = ancestors[0];
|
|
1617
|
+
if (parent && (parent.type === "FunctionDeclaration" || parent.type === "FunctionExpression" || parent.type === "ArrowFunctionExpression") && node === parent.body) {
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
if (isEarlyReturnBlock(node)) {
|
|
1621
|
+
return;
|
|
1622
|
+
}
|
|
1623
|
+
if (functionStack.length > 0) {
|
|
1624
|
+
functionStack[functionStack.length - 1]--;
|
|
1625
|
+
}
|
|
1626
|
+
},
|
|
1627
|
+
"FunctionDeclaration:exit"() {
|
|
1628
|
+
functionStack.pop();
|
|
1629
|
+
},
|
|
1630
|
+
"FunctionExpression:exit"() {
|
|
1631
|
+
functionStack.pop();
|
|
1632
|
+
},
|
|
1633
|
+
"ArrowFunctionExpression:exit"() {
|
|
1634
|
+
functionStack.pop();
|
|
1635
|
+
}
|
|
1636
|
+
};
|
|
1637
|
+
}
|
|
1638
|
+
};
|
|
1639
|
+
|
|
1640
|
+
// src/rules/spring-naming-convention.js
|
|
1641
|
+
var spring_naming_convention_default = {
|
|
1642
|
+
meta: {
|
|
1643
|
+
type: "problem",
|
|
1644
|
+
docs: {
|
|
1645
|
+
description: "Enforce correct naming for useSpring and useSprings hook destructuring",
|
|
1646
|
+
category: "Stylistic Issues",
|
|
1647
|
+
recommended: false
|
|
1648
|
+
},
|
|
1649
|
+
schema: []
|
|
1650
|
+
},
|
|
1651
|
+
create(context) {
|
|
1652
|
+
return {
|
|
1653
|
+
VariableDeclarator(node) {
|
|
1654
|
+
if (node.init && node.init.type === "CallExpression" && node.init.callee && node.init.callee.type === "Identifier" && (node.init.callee.name === "useSpring" || node.init.callee.name === "useSprings")) {
|
|
1655
|
+
const hookName = node.init.callee.name;
|
|
1656
|
+
if (node.id && node.id.type === "ArrayPattern") {
|
|
1657
|
+
const elements = node.id.elements;
|
|
1658
|
+
if (elements.length < 2) {
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1661
|
+
const firstElem = elements[0];
|
|
1662
|
+
const secondElem = elements[1];
|
|
1663
|
+
if (!firstElem || firstElem.type !== "Identifier" || !secondElem || secondElem.type !== "Identifier") {
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
const firstName = firstElem.name;
|
|
1667
|
+
const secondName = secondElem.name;
|
|
1668
|
+
if (hookName === "useSpring") {
|
|
1669
|
+
if (!firstName.endsWith("Springs")) {
|
|
1670
|
+
context.report({
|
|
1671
|
+
node: firstElem,
|
|
1672
|
+
message: "The first variable from useSpring must end with 'Springs'."
|
|
1673
|
+
});
|
|
1674
|
+
} else {
|
|
1675
|
+
const base = firstName.slice(0, -"Springs".length);
|
|
1676
|
+
if (!base) {
|
|
1677
|
+
context.report({
|
|
1678
|
+
node: firstElem,
|
|
1679
|
+
message: "The first variable must have a non-empty name before 'Springs'."
|
|
1680
|
+
});
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
const expectedSecond = base + "Api";
|
|
1684
|
+
if (secondName !== expectedSecond) {
|
|
1685
|
+
context.report({
|
|
1686
|
+
node: secondElem,
|
|
1687
|
+
message: `The second variable from useSpring must be named '${expectedSecond}'.`
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
} else if (hookName === "useSprings") {
|
|
1692
|
+
if (!firstName.endsWith("Springs")) {
|
|
1693
|
+
context.report({
|
|
1694
|
+
node: firstElem,
|
|
1695
|
+
message: "The first variable from useSprings must end with 'Springs'."
|
|
1696
|
+
});
|
|
1697
|
+
} else {
|
|
1698
|
+
const basePlural = firstName.slice(0, -"Springs".length);
|
|
1699
|
+
if (!basePlural) {
|
|
1700
|
+
context.report({
|
|
1701
|
+
node: firstElem,
|
|
1702
|
+
message: "The first variable must have a non-empty name before 'Springs'."
|
|
1703
|
+
});
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
if (!basePlural.endsWith("s")) {
|
|
1707
|
+
context.report({
|
|
1708
|
+
node: firstElem,
|
|
1709
|
+
message: "The first variable for useSprings should be a plural name (ending with an 's') before 'Springs'."
|
|
1710
|
+
});
|
|
1711
|
+
} else {
|
|
1712
|
+
const expectedSecond = basePlural + "Api";
|
|
1713
|
+
if (secondName !== expectedSecond) {
|
|
1714
|
+
context.report({
|
|
1715
|
+
node: secondElem,
|
|
1716
|
+
message: `The second variable from useSprings must be named '${expectedSecond}'.`
|
|
1717
|
+
});
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
};
|
|
1728
|
+
|
|
1729
|
+
// src/rules/inline-style-limit.js
|
|
1730
|
+
var inline_style_limit_default = {
|
|
1731
|
+
meta: {
|
|
1732
|
+
type: "suggestion",
|
|
1733
|
+
docs: {
|
|
1734
|
+
description: "Disallow inline style objects with too many keys and encourage extracting them",
|
|
1735
|
+
category: "Best Practices",
|
|
1736
|
+
recommended: false
|
|
1737
|
+
},
|
|
1738
|
+
schema: [
|
|
1739
|
+
{
|
|
1740
|
+
anyOf: [
|
|
1741
|
+
{
|
|
1742
|
+
type: "number"
|
|
1743
|
+
},
|
|
1744
|
+
{
|
|
1745
|
+
type: "object",
|
|
1746
|
+
properties: {
|
|
1747
|
+
maxKeys: {
|
|
1748
|
+
type: "number",
|
|
1749
|
+
description: "Maximum number of keys allowed in an inline style object before it must be extracted."
|
|
1750
|
+
}
|
|
1751
|
+
},
|
|
1752
|
+
additionalProperties: false
|
|
1753
|
+
}
|
|
1754
|
+
]
|
|
1755
|
+
}
|
|
1756
|
+
],
|
|
1757
|
+
messages: {
|
|
1758
|
+
extractStyle: "Inline style objects should be extracted into a separate object or file when containing more than {{max}} keys."
|
|
1759
|
+
}
|
|
1760
|
+
},
|
|
1761
|
+
create(context) {
|
|
1762
|
+
const option = context.options[0];
|
|
1763
|
+
const maxKeys = typeof option === "number" ? option : option && option.maxKeys || 3;
|
|
1764
|
+
return {
|
|
1765
|
+
JSXAttribute(node) {
|
|
1766
|
+
if (node.name.name !== "style") {
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1769
|
+
if (node.value && node.value.type === "JSXExpressionContainer" && node.value.expression && node.value.expression.type === "ObjectExpression") {
|
|
1770
|
+
const styleObject = node.value.expression;
|
|
1771
|
+
const keyCount = styleObject.properties.filter((prop) => prop.type === "Property").length;
|
|
1772
|
+
if (keyCount > maxKeys) {
|
|
1773
|
+
context.report({
|
|
1774
|
+
node,
|
|
1775
|
+
messageId: "extractStyle",
|
|
1776
|
+
data: { max: maxKeys }
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
};
|
|
1782
|
+
}
|
|
1783
|
+
};
|
|
1784
|
+
|
|
1785
|
+
// src/rules/no-inline-prop-types.js
|
|
1786
|
+
var no_inline_prop_types_default = {
|
|
1787
|
+
meta: {
|
|
1788
|
+
type: "suggestion",
|
|
1789
|
+
docs: {
|
|
1790
|
+
description: "Enforce that component prop types are not defined inline (using an object literal) but rather use a named type or interface.",
|
|
1791
|
+
category: "Best Practices",
|
|
1792
|
+
recommended: false
|
|
1793
|
+
},
|
|
1794
|
+
schema: [],
|
|
1795
|
+
messages: {
|
|
1796
|
+
noInlinePropTypes: "Inline prop type definitions are not allowed. Use a named type alias or interface instead of an inline object type."
|
|
1797
|
+
}
|
|
1798
|
+
},
|
|
1799
|
+
create(context) {
|
|
1800
|
+
function checkParameter(param) {
|
|
1801
|
+
if (param && param.type === "ObjectPattern" && param.typeAnnotation && param.typeAnnotation.type === "TSTypeAnnotation") {
|
|
1802
|
+
const annotation = param.typeAnnotation.typeAnnotation;
|
|
1803
|
+
if (annotation.type === "TSTypeLiteral") {
|
|
1804
|
+
context.report({
|
|
1805
|
+
node: param,
|
|
1806
|
+
messageId: "noInlinePropTypes"
|
|
1807
|
+
});
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
return {
|
|
1812
|
+
"FunctionDeclaration, ArrowFunctionExpression, FunctionExpression"(node) {
|
|
1813
|
+
const firstParam = node.params[0];
|
|
1814
|
+
if (firstParam) {
|
|
1815
|
+
checkParameter(firstParam);
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
};
|
|
1819
|
+
}
|
|
1820
|
+
};
|
|
1821
|
+
|
|
1822
|
+
// src/rules/no-unnecessary-div.js
|
|
1823
|
+
var no_unnecessary_div_default = {
|
|
1824
|
+
meta: {
|
|
1825
|
+
type: "suggestion",
|
|
1826
|
+
docs: {
|
|
1827
|
+
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.",
|
|
1828
|
+
category: "Best Practices",
|
|
1829
|
+
recommended: false
|
|
1830
|
+
}
|
|
1831
|
+
},
|
|
1832
|
+
create(context) {
|
|
1833
|
+
return {
|
|
1834
|
+
JSXElement(node) {
|
|
1835
|
+
if (node.openingElement.name && node.openingElement.name.name === "div") {
|
|
1836
|
+
const meaningfulChildren = node.children.filter((child) => {
|
|
1837
|
+
if (child.type === "JSXText") {
|
|
1838
|
+
return child.value.trim() !== "";
|
|
1839
|
+
}
|
|
1840
|
+
return true;
|
|
1841
|
+
});
|
|
1842
|
+
if (meaningfulChildren.length === 1 && meaningfulChildren[0].type === "JSXElement") {
|
|
1843
|
+
context.report({
|
|
1844
|
+
node,
|
|
1845
|
+
message: "Unnecessary <div> wrapper detected. Remove it if not needed, or replace with a semantic element that reflects its purpose."
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
};
|
|
1851
|
+
}
|
|
1852
|
+
};
|
|
1853
|
+
|
|
1854
|
+
// src/index.js
|
|
1855
|
+
var src_default = {
|
|
1856
|
+
rules: {
|
|
1857
|
+
"no-nested-jsx-return": no_nested_jsx_return_default,
|
|
1858
|
+
"explicit-object-types": explicit_object_types_default,
|
|
1859
|
+
"no-type-cast": no_type_cast_default,
|
|
1860
|
+
"sort-keys-fixable": sort_keys_fixable_default,
|
|
1861
|
+
"no-transition-cssproperties": no_transition_cssproperties_default,
|
|
1862
|
+
"no-explicit-return-type": no_explicit_return_types_default,
|
|
1863
|
+
"max-jsxnesting": max_jsx_nesting_default,
|
|
1864
|
+
"seperate-style-files": seperate_style_files_default,
|
|
1865
|
+
"no-unnecessary-key": no_unnecessary_key_default,
|
|
1866
|
+
"sort-exports": sort_exports_default,
|
|
1867
|
+
"localize-react-props": localize_react_props_default,
|
|
1868
|
+
"no-or-none-component": no_or_none_component_default,
|
|
1869
|
+
"no-button-navigation": no_button_navigation_default,
|
|
1870
|
+
"no-multi-style-objects": no_multi_style_objects_default,
|
|
1871
|
+
"no-useless-function": no_useless_function_default,
|
|
1872
|
+
"min-var-length": min_var_length_default,
|
|
1873
|
+
"max-depth-extended": max_depth_extended_default,
|
|
1874
|
+
"spring-naming-convention": spring_naming_convention_default,
|
|
1875
|
+
"inline-style-limit": inline_style_limit_default,
|
|
1876
|
+
"no-inline-prop-types": no_inline_prop_types_default,
|
|
1877
|
+
"no-unnecessary-div": no_unnecessary_div_default
|
|
1878
|
+
}
|
|
1879
|
+
};
|
|
1880
|
+
export {
|
|
1881
|
+
src_default as default
|
|
1882
|
+
};
|