eslint-plugin-power-esrules 0.1.10 → 0.1.12
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/INTEGRATION_GUIDE.md +11 -0
- package/index.js +3 -1
- package/lib/rules/props-destructuring-sort.js +391 -0
- package/package.json +5 -2
- package/tests/props-destructuring-sort.test.js +157 -0
package/INTEGRATION_GUIDE.md
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
- `use-state-naming.js` - проверка именования useState хуков
|
|
13
13
|
- `class-to-functional.js` - определение классовых компонентов для конвертации в функциональные
|
|
14
14
|
- `import-sorting.js` - сортировка импортов
|
|
15
|
+
- `props-destructuring-sort.js` - сортировка деструктурированных props
|
|
15
16
|
|
|
16
17
|
### Правила плагина
|
|
17
18
|
|
|
@@ -66,6 +67,16 @@
|
|
|
66
67
|
#### 7. `import-sorting`
|
|
67
68
|
**Назначение:** Сортирует импорты согласно правилам установленным в команде разработки.
|
|
68
69
|
|
|
70
|
+
#### 8. `props-destructuring-sort`
|
|
71
|
+
**Назначение:** Сортирует деструктурированные props React-компонентов по алфавиту.
|
|
72
|
+
|
|
73
|
+
**Логика:**
|
|
74
|
+
- `const { ... } = props` — только внутри функционального React-компонента
|
|
75
|
+
- `const { ... } = this.props` — только внутри классового React-компонента (`Component` / `PureComponent`)
|
|
76
|
+
- Первый параметр `({ ... })` — только у React-компонента (имя с заглавной буквы, JSX в теле, или обёртка `memo` / `forwardRef` / `lazy`)
|
|
77
|
+
- Rest-элемент (`...rest`) остаётся последним
|
|
78
|
+
- Автоисправление при сохранении (eslint --fix), как у `import-sorting`
|
|
79
|
+
|
|
69
80
|
## Что нужно для интеграции
|
|
70
81
|
|
|
71
82
|
### 1. npm install eslint-plugin-power-esrules
|
package/index.js
CHANGED
|
@@ -7,6 +7,7 @@ module.exports = {
|
|
|
7
7
|
"use-state-naming": require("./lib/rules/use-state-naming"),
|
|
8
8
|
"class-to-functional": require("./lib/rules/class-to-functional"),
|
|
9
9
|
"import-sorting": require("./lib/rules/import-sorting"),
|
|
10
|
+
"props-destructuring-sort": require("./lib/rules/props-destructuring-sort"),
|
|
10
11
|
"require-data-testid": require("./lib/rules/require-data-testid"),
|
|
11
12
|
},
|
|
12
13
|
configs: {
|
|
@@ -20,7 +21,8 @@ module.exports = {
|
|
|
20
21
|
"power-esrules/use-state-naming": "warn",
|
|
21
22
|
"power-esrules/class-to-functional": "error",
|
|
22
23
|
"power-esrules/import-sorting": "error",
|
|
23
|
-
"power-esrules/
|
|
24
|
+
"power-esrules/props-destructuring-sort": "error",
|
|
25
|
+
"power-esrules/require-data-testid": "warn",
|
|
24
26
|
},
|
|
25
27
|
},
|
|
26
28
|
},
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule: props-destructuring-sort
|
|
3
|
+
*
|
|
4
|
+
* Сортирует деструктурированные props React-компонентов по алфавиту:
|
|
5
|
+
* - const { ... } = props (внутри функционального React-компонента)
|
|
6
|
+
* - const { ... } = this.props (внутри классового React-компонента)
|
|
7
|
+
* - function Component({ ... }) и аналогичные стрелочные/function expression
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const REACT_WRAPPER_CALLEES = new Set(["memo", "forwardRef", "lazy"]);
|
|
11
|
+
|
|
12
|
+
const NESTED_FUNCTION_TYPES = new Set([
|
|
13
|
+
"FunctionDeclaration",
|
|
14
|
+
"FunctionExpression",
|
|
15
|
+
"ArrowFunctionExpression",
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
function isPropsInitializer(node) {
|
|
19
|
+
if (!node) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (node.type === "Identifier" && node.name === "props") {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
if (
|
|
26
|
+
node.type === "MemberExpression" &&
|
|
27
|
+
!node.computed &&
|
|
28
|
+
node.object.type === "ThisExpression" &&
|
|
29
|
+
node.property.type === "Identifier" &&
|
|
30
|
+
node.property.name === "props"
|
|
31
|
+
) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isReactComponentName(name) {
|
|
38
|
+
if (!name || typeof name !== "string") {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return /^[A-Z]/.test(name);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isReactComponentClass(node) {
|
|
45
|
+
if (
|
|
46
|
+
!node ||
|
|
47
|
+
(node.type !== "ClassDeclaration" && node.type !== "ClassExpression")
|
|
48
|
+
) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (!node.superClass) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const { superClass } = node;
|
|
55
|
+
if (superClass.type === "Identifier") {
|
|
56
|
+
return (
|
|
57
|
+
superClass.name === "Component" || superClass.name === "PureComponent"
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
if (
|
|
61
|
+
superClass.type === "MemberExpression" &&
|
|
62
|
+
superClass.object &&
|
|
63
|
+
superClass.object.type === "Identifier" &&
|
|
64
|
+
superClass.object.name === "React" &&
|
|
65
|
+
superClass.property &&
|
|
66
|
+
superClass.property.type === "Identifier" &&
|
|
67
|
+
(superClass.property.name === "Component" ||
|
|
68
|
+
superClass.property.name === "PureComponent")
|
|
69
|
+
) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function getCalleeName(callee) {
|
|
76
|
+
if (!callee) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
if (callee.type === "Identifier") {
|
|
80
|
+
return callee.name;
|
|
81
|
+
}
|
|
82
|
+
if (
|
|
83
|
+
callee.type === "MemberExpression" &&
|
|
84
|
+
!callee.computed &&
|
|
85
|
+
callee.property.type === "Identifier"
|
|
86
|
+
) {
|
|
87
|
+
return callee.property.name;
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function isWrappedByReactWrapper(funcNode) {
|
|
93
|
+
let current = funcNode.parent;
|
|
94
|
+
while (current) {
|
|
95
|
+
if (current.type === "CallExpression") {
|
|
96
|
+
const calleeName = getCalleeName(current.callee);
|
|
97
|
+
if (calleeName && REACT_WRAPPER_CALLEES.has(calleeName)) {
|
|
98
|
+
return current.arguments[0] === funcNode;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
current = current.parent;
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function getReactComponentName(funcNode) {
|
|
107
|
+
if (funcNode.type === "FunctionDeclaration" && funcNode.id) {
|
|
108
|
+
return funcNode.id.name;
|
|
109
|
+
}
|
|
110
|
+
const { parent } = funcNode;
|
|
111
|
+
if (parent?.type === "VariableDeclarator" && parent.id?.type === "Identifier") {
|
|
112
|
+
return parent.id.name;
|
|
113
|
+
}
|
|
114
|
+
if (parent?.type === "AssignmentExpression" && parent.left?.type === "Identifier") {
|
|
115
|
+
return parent.left.name;
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function nodeContainsJSX(node) {
|
|
121
|
+
if (!node || typeof node !== "object") {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
if (node.type === "JSXElement" || node.type === "JSXFragment") {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
for (const key of Object.keys(node)) {
|
|
128
|
+
if (key === "parent") {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const value = node[key];
|
|
132
|
+
if (!value) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (Array.isArray(value)) {
|
|
136
|
+
for (const item of value) {
|
|
137
|
+
if (!item || typeof item !== "object" || !item.type) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (NESTED_FUNCTION_TYPES.has(item.type)) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (nodeContainsJSX(item)) {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
} else if (typeof value === "object" && value.type) {
|
|
148
|
+
if (NESTED_FUNCTION_TYPES.has(value.type)) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (nodeContainsJSX(value)) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function functionBodyContainsJSX(funcNode) {
|
|
160
|
+
const { body } = funcNode;
|
|
161
|
+
if (!body) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
if (body.type === "JSXElement" || body.type === "JSXFragment") {
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
return nodeContainsJSX(body);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function isReactComponentFunction(funcNode) {
|
|
171
|
+
if (!funcNode || !NESTED_FUNCTION_TYPES.has(funcNode.type)) {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
if (isWrappedByReactWrapper(funcNode)) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
const componentName = getReactComponentName(funcNode);
|
|
178
|
+
if (isReactComponentName(componentName)) {
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
return functionBodyContainsJSX(funcNode);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function getEnclosingFunction(node) {
|
|
185
|
+
let current = node.parent;
|
|
186
|
+
while (current) {
|
|
187
|
+
if (NESTED_FUNCTION_TYPES.has(current.type)) {
|
|
188
|
+
return current;
|
|
189
|
+
}
|
|
190
|
+
current = current.parent;
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function isInsideReactClass(node) {
|
|
196
|
+
let current = node.parent;
|
|
197
|
+
while (current) {
|
|
198
|
+
if (current.type === "ClassDeclaration" || current.type === "ClassExpression") {
|
|
199
|
+
return isReactComponentClass(current);
|
|
200
|
+
}
|
|
201
|
+
current = current.parent;
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function isInsideReactComponentFunction(node) {
|
|
207
|
+
const enclosingFunction = getEnclosingFunction(node);
|
|
208
|
+
if (!enclosingFunction) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
return isReactComponentFunction(enclosingFunction);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function getPropertySortKey(prop) {
|
|
215
|
+
if (prop.type === "RestElement") {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
if (prop.type === "Property") {
|
|
219
|
+
if (prop.key.type === "Identifier" && !prop.computed) {
|
|
220
|
+
return prop.key.name;
|
|
221
|
+
}
|
|
222
|
+
if (prop.key.type === "Literal") {
|
|
223
|
+
return String(prop.key.value);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return "";
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function partitionProperties(properties) {
|
|
230
|
+
const regular = [];
|
|
231
|
+
const rest = [];
|
|
232
|
+
for (const prop of properties) {
|
|
233
|
+
if (prop.type === "RestElement") {
|
|
234
|
+
rest.push(prop);
|
|
235
|
+
} else {
|
|
236
|
+
regular.push(prop);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return { regular, rest };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function isAlphabeticallySorted(regular) {
|
|
243
|
+
for (let i = 1; i < regular.length; i += 1) {
|
|
244
|
+
const prevKey = getPropertySortKey(regular[i - 1]);
|
|
245
|
+
const currentKey = getPropertySortKey(regular[i]);
|
|
246
|
+
if (prevKey.localeCompare(currentKey) > 0) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function sortObjectPatternProperties(properties) {
|
|
254
|
+
const { regular, rest } = partitionProperties(properties);
|
|
255
|
+
const sortedRegular = [...regular].sort((a, b) =>
|
|
256
|
+
getPropertySortKey(a).localeCompare(getPropertySortKey(b)),
|
|
257
|
+
);
|
|
258
|
+
return [...sortedRegular, ...rest];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function getPropertiesSeparator(sourceCode, properties) {
|
|
262
|
+
if (properties.length < 2) {
|
|
263
|
+
return ", ";
|
|
264
|
+
}
|
|
265
|
+
const comma = sourceCode.getTokenAfter(properties[0], {
|
|
266
|
+
filter: (token) => token.value === ",",
|
|
267
|
+
});
|
|
268
|
+
if (!comma) {
|
|
269
|
+
return ", ";
|
|
270
|
+
}
|
|
271
|
+
return sourceCode.text.slice(comma.range[0], properties[1].range[0]);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function buildSortedObjectPatternText(patternNode, sortedProperties, sourceCode) {
|
|
275
|
+
const openBrace = sourceCode.getFirstToken(patternNode);
|
|
276
|
+
const closeBrace = sourceCode.getLastToken(patternNode);
|
|
277
|
+
const originalProperties = patternNode.properties;
|
|
278
|
+
const separator = getPropertiesSeparator(sourceCode, originalProperties);
|
|
279
|
+
|
|
280
|
+
let innerText = "";
|
|
281
|
+
for (let i = 0; i < sortedProperties.length; i += 1) {
|
|
282
|
+
innerText += sourceCode.getText(sortedProperties[i]);
|
|
283
|
+
if (i < sortedProperties.length - 1) {
|
|
284
|
+
innerText += separator;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const afterOpenBrace = sourceCode.text.slice(
|
|
289
|
+
openBrace.range[1],
|
|
290
|
+
originalProperties[0].range[0],
|
|
291
|
+
);
|
|
292
|
+
const lastOriginal = originalProperties[originalProperties.length - 1];
|
|
293
|
+
const beforeCloseBrace = sourceCode.text.slice(
|
|
294
|
+
lastOriginal.range[1],
|
|
295
|
+
closeBrace.range[0],
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
return `{${afterOpenBrace}${innerText}${beforeCloseBrace}}`;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function checkObjectPattern(patternNode, context) {
|
|
302
|
+
const { properties } = patternNode;
|
|
303
|
+
if (!properties || properties.length < 2) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const { regular } = partitionProperties(properties);
|
|
308
|
+
if (regular.length < 2 || isAlphabeticallySorted(regular)) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const sourceCode = context.getSourceCode();
|
|
313
|
+
context.report({
|
|
314
|
+
node: patternNode,
|
|
315
|
+
messageId: "incorrectOrder",
|
|
316
|
+
fix(fixer) {
|
|
317
|
+
const sortedProperties = sortObjectPatternProperties(properties);
|
|
318
|
+
const sortedText = buildSortedObjectPatternText(
|
|
319
|
+
patternNode,
|
|
320
|
+
sortedProperties,
|
|
321
|
+
sourceCode,
|
|
322
|
+
);
|
|
323
|
+
return fixer.replaceText(patternNode, sortedText);
|
|
324
|
+
},
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function checkFunctionParams(funcNode, context) {
|
|
329
|
+
if (!isReactComponentFunction(funcNode)) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
const { params } = funcNode;
|
|
333
|
+
if (!params || params.length === 0) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const firstParam = params[0];
|
|
337
|
+
if (firstParam.type === "ObjectPattern") {
|
|
338
|
+
checkObjectPattern(firstParam, context);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
module.exports = {
|
|
343
|
+
meta: {
|
|
344
|
+
type: "layout",
|
|
345
|
+
docs: {
|
|
346
|
+
description:
|
|
347
|
+
"Сортирует деструктурированные props React-компонентов по алфавиту",
|
|
348
|
+
category: "Stylistic Issues",
|
|
349
|
+
recommended: false,
|
|
350
|
+
},
|
|
351
|
+
fixable: "code",
|
|
352
|
+
schema: [],
|
|
353
|
+
messages: {
|
|
354
|
+
incorrectOrder:
|
|
355
|
+
"Деструктурированные props должны быть отсортированы по алфавиту",
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
|
|
359
|
+
create(context) {
|
|
360
|
+
return {
|
|
361
|
+
VariableDeclarator(node) {
|
|
362
|
+
if (node.id.type !== "ObjectPattern" || !isPropsInitializer(node.init)) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const isThisProps =
|
|
366
|
+
node.init.type === "MemberExpression" &&
|
|
367
|
+
node.init.object.type === "ThisExpression";
|
|
368
|
+
if (isThisProps) {
|
|
369
|
+
if (!isInsideReactClass(node)) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
} else if (!isInsideReactComponentFunction(node)) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
checkObjectPattern(node.id, context);
|
|
376
|
+
},
|
|
377
|
+
|
|
378
|
+
FunctionDeclaration(node) {
|
|
379
|
+
checkFunctionParams(node, context);
|
|
380
|
+
},
|
|
381
|
+
|
|
382
|
+
FunctionExpression(node) {
|
|
383
|
+
checkFunctionParams(node, context);
|
|
384
|
+
},
|
|
385
|
+
|
|
386
|
+
ArrowFunctionExpression(node) {
|
|
387
|
+
checkFunctionParams(node, context);
|
|
388
|
+
},
|
|
389
|
+
};
|
|
390
|
+
},
|
|
391
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-power-esrules",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "custom ESLint rules",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"peerDependencies": {
|
|
@@ -27,5 +27,8 @@
|
|
|
27
27
|
"bugs": {
|
|
28
28
|
"url": "https://github.com/Vollmond148259/eslint-plugin-power-esrules/issues"
|
|
29
29
|
},
|
|
30
|
-
"homepage": "https://github.com/Vollmond148259/eslint-plugin-power-esrules#readme"
|
|
30
|
+
"homepage": "https://github.com/Vollmond148259/eslint-plugin-power-esrules#readme",
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"eslint": "^8.57.1"
|
|
33
|
+
}
|
|
31
34
|
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
const { RuleTester } = require("eslint");
|
|
2
|
+
const rule = require("../lib/rules/props-destructuring-sort");
|
|
3
|
+
|
|
4
|
+
const ruleTester = new RuleTester({
|
|
5
|
+
parserOptions: {
|
|
6
|
+
ecmaVersion: 2022,
|
|
7
|
+
sourceType: "module",
|
|
8
|
+
ecmaFeatures: { jsx: true },
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
ruleTester.run("props-destructuring-sort", rule, {
|
|
13
|
+
valid: [
|
|
14
|
+
{
|
|
15
|
+
name: "sorted destructuring from props in functional component",
|
|
16
|
+
code: `
|
|
17
|
+
function MyComponent(props) {
|
|
18
|
+
const { alpha, beta } = props;
|
|
19
|
+
return <div>{alpha}{beta}</div>;
|
|
20
|
+
}
|
|
21
|
+
`,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "single property from props",
|
|
25
|
+
code: `
|
|
26
|
+
function MyComponent(props) {
|
|
27
|
+
const { only } = props;
|
|
28
|
+
return <div>{only}</div>;
|
|
29
|
+
}
|
|
30
|
+
`,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "unsorted props outside React component",
|
|
34
|
+
code: `
|
|
35
|
+
function util(props) {
|
|
36
|
+
const { z, a } = props;
|
|
37
|
+
return z + a;
|
|
38
|
+
}
|
|
39
|
+
`,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "sorted params in arrow component",
|
|
43
|
+
code: `
|
|
44
|
+
const MyComponent = ({ alpha, beta }) => <div>{alpha}{beta}</div>;
|
|
45
|
+
`,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "sorted this.props in class component",
|
|
49
|
+
code: `
|
|
50
|
+
class MyComponent extends React.Component {
|
|
51
|
+
render() {
|
|
52
|
+
const { alpha, beta } = this.props;
|
|
53
|
+
return <div>{alpha}{beta}</div>;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
`,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "rest element last when already sorted",
|
|
60
|
+
code: `
|
|
61
|
+
function MyComponent(props) {
|
|
62
|
+
const { alpha, beta, ...rest } = props;
|
|
63
|
+
return <div>{alpha}</div>;
|
|
64
|
+
}
|
|
65
|
+
`,
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
|
|
69
|
+
invalid: [
|
|
70
|
+
{
|
|
71
|
+
name: "const from props in functional component",
|
|
72
|
+
code: `
|
|
73
|
+
function MyComponent(props) {
|
|
74
|
+
const { zebra, alpha } = props;
|
|
75
|
+
return <div>{zebra}{alpha}</div>;
|
|
76
|
+
}
|
|
77
|
+
`,
|
|
78
|
+
output: `
|
|
79
|
+
function MyComponent(props) {
|
|
80
|
+
const { alpha, zebra } = props;
|
|
81
|
+
return <div>{zebra}{alpha}</div>;
|
|
82
|
+
}
|
|
83
|
+
`,
|
|
84
|
+
errors: [{ messageId: "incorrectOrder" }],
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: "destructured params in function declaration",
|
|
88
|
+
code: `
|
|
89
|
+
function MyComponent({ zebra, alpha }) {
|
|
90
|
+
return <div>{zebra}{alpha}</div>;
|
|
91
|
+
}
|
|
92
|
+
`,
|
|
93
|
+
output: `
|
|
94
|
+
function MyComponent({ alpha, zebra }) {
|
|
95
|
+
return <div>{zebra}{alpha}</div>;
|
|
96
|
+
}
|
|
97
|
+
`,
|
|
98
|
+
errors: [{ messageId: "incorrectOrder" }],
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: "destructured params in arrow component with JSX",
|
|
102
|
+
code: `
|
|
103
|
+
const MyComponent = ({ zebra, alpha }) => <div>{zebra}{alpha}</div>;
|
|
104
|
+
`,
|
|
105
|
+
output: `
|
|
106
|
+
const MyComponent = ({ alpha, zebra }) => <div>{zebra}{alpha}</div>;
|
|
107
|
+
`,
|
|
108
|
+
errors: [{ messageId: "incorrectOrder" }],
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: "this.props in class component",
|
|
112
|
+
code: `
|
|
113
|
+
class MyComponent extends Component {
|
|
114
|
+
render() {
|
|
115
|
+
const { zebra, alpha } = this.props;
|
|
116
|
+
return <div>{zebra}{alpha}</div>;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
`,
|
|
120
|
+
output: `
|
|
121
|
+
class MyComponent extends Component {
|
|
122
|
+
render() {
|
|
123
|
+
const { alpha, zebra } = this.props;
|
|
124
|
+
return <div>{zebra}{alpha}</div>;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
`,
|
|
128
|
+
errors: [{ messageId: "incorrectOrder" }],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: "rest stays last after sort",
|
|
132
|
+
code: `
|
|
133
|
+
function MyComponent(props) {
|
|
134
|
+
const { zebra, alpha, ...rest } = props;
|
|
135
|
+
return <div>{zebra}</div>;
|
|
136
|
+
}
|
|
137
|
+
`,
|
|
138
|
+
output: `
|
|
139
|
+
function MyComponent(props) {
|
|
140
|
+
const { alpha, zebra, ...rest } = props;
|
|
141
|
+
return <div>{zebra}</div>;
|
|
142
|
+
}
|
|
143
|
+
`,
|
|
144
|
+
errors: [{ messageId: "incorrectOrder" }],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: "memo-wrapped component params",
|
|
148
|
+
code: `
|
|
149
|
+
const MyComponent = memo(({ zebra, alpha }) => <div>{zebra}</div>);
|
|
150
|
+
`,
|
|
151
|
+
output: `
|
|
152
|
+
const MyComponent = memo(({ alpha, zebra }) => <div>{zebra}</div>);
|
|
153
|
+
`,
|
|
154
|
+
errors: [{ messageId: "incorrectOrder" }],
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
});
|