eslint-plugin-absolute 0.2.3 → 0.2.5
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.
|
@@ -34,7 +34,33 @@ type KeyInfo = {
|
|
|
34
34
|
isFunction: boolean;
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
+
type TopLevelBinding =
|
|
38
|
+
| {
|
|
39
|
+
kind: "function";
|
|
40
|
+
node:
|
|
41
|
+
| TSESTree.ArrowFunctionExpression
|
|
42
|
+
| TSESTree.FunctionDeclaration
|
|
43
|
+
| TSESTree.FunctionExpression;
|
|
44
|
+
}
|
|
45
|
+
| {
|
|
46
|
+
kind: "import";
|
|
47
|
+
}
|
|
48
|
+
| {
|
|
49
|
+
kind: "value";
|
|
50
|
+
node: TSESTree.Expression;
|
|
51
|
+
};
|
|
52
|
+
|
|
37
53
|
const SORT_BEFORE = -1;
|
|
54
|
+
const PURE_CONSTRUCTORS = new Set(["Date"]);
|
|
55
|
+
const PURE_GLOBAL_FUNCTIONS = new Set(["Boolean", "Number", "String"]);
|
|
56
|
+
const PURE_MEMBER_METHODS = new Set([
|
|
57
|
+
"getDay",
|
|
58
|
+
"getHours",
|
|
59
|
+
"getMilliseconds",
|
|
60
|
+
"getMinutes",
|
|
61
|
+
"getSeconds",
|
|
62
|
+
"padStart"
|
|
63
|
+
]);
|
|
38
64
|
|
|
39
65
|
const hasDuplicateNames = (names: Array<string | null>) => {
|
|
40
66
|
const seen = new Set<string>();
|
|
@@ -50,91 +76,13 @@ const hasDuplicateNames = (names: Array<string | null>) => {
|
|
|
50
76
|
return false;
|
|
51
77
|
};
|
|
52
78
|
|
|
53
|
-
const isSafeStaticTemplate = (node: TSESTree.TemplateLiteral) =>
|
|
54
|
-
node.expressions.length === 0;
|
|
55
|
-
|
|
56
|
-
const isSafeArrayElement: (node: TSESTree.Node | null) => boolean = (node) => {
|
|
57
|
-
if (!node || node.type === "SpreadElement") {
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return isSafeToReorderExpression(node);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const isSafeObjectProperty: (
|
|
65
|
-
property: TSESTree.ObjectExpression["properties"][number]
|
|
66
|
-
) => boolean = (property) => {
|
|
67
|
-
if (
|
|
68
|
-
property.type !== "Property" ||
|
|
69
|
-
property.computed ||
|
|
70
|
-
property.kind !== "init"
|
|
71
|
-
) {
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (property.key.type !== "Identifier" && property.key.type !== "Literal") {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (property.method) {
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return isSafeToReorderExpression(property.value);
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
const isSafeToReorderExpression: (node: TSESTree.Node | null) => boolean = (
|
|
87
|
-
node
|
|
88
|
-
) => {
|
|
89
|
-
if (!node || node.type === "PrivateIdentifier") {
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
switch (node.type) {
|
|
94
|
-
case "Identifier":
|
|
95
|
-
case "Literal":
|
|
96
|
-
case "ThisExpression":
|
|
97
|
-
case "FunctionExpression":
|
|
98
|
-
case "ArrowFunctionExpression":
|
|
99
|
-
case "ClassExpression":
|
|
100
|
-
return true;
|
|
101
|
-
case "TemplateLiteral":
|
|
102
|
-
return isSafeStaticTemplate(node);
|
|
103
|
-
case "UnaryExpression":
|
|
104
|
-
return isSafeToReorderExpression(node.argument);
|
|
105
|
-
case "ArrayExpression":
|
|
106
|
-
return node.elements.every(isSafeArrayElement);
|
|
107
|
-
case "ObjectExpression":
|
|
108
|
-
return node.properties.every(isSafeObjectProperty);
|
|
109
|
-
default:
|
|
110
|
-
return false;
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const isSafeJSXAttributeValue = (value: TSESTree.JSXAttribute["value"]) => {
|
|
115
|
-
if (value === null) {
|
|
116
|
-
return true;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (value.type === "Literal") {
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (value.type !== "JSXExpressionContainer") {
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if (value.expression.type === "JSXEmptyExpression") {
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return isSafeToReorderExpression(value.expression);
|
|
132
|
-
};
|
|
133
|
-
|
|
134
79
|
export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
135
80
|
create(context) {
|
|
136
81
|
const { sourceCode } = context;
|
|
137
82
|
const [option] = context.options;
|
|
83
|
+
const topLevelBindings = new Map<string, TopLevelBinding>();
|
|
84
|
+
const pureFunctionCache = new Map<TSESTree.Node, boolean>();
|
|
85
|
+
const pureFunctionInProgress = new Set<TSESTree.Node>();
|
|
138
86
|
|
|
139
87
|
const order: "asc" | "desc" =
|
|
140
88
|
option && option.order ? option.order : "asc";
|
|
@@ -157,6 +105,59 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
157
105
|
? option.variablesBeforeFunctions
|
|
158
106
|
: false;
|
|
159
107
|
|
|
108
|
+
for (const statement of sourceCode.ast.body) {
|
|
109
|
+
if (
|
|
110
|
+
statement.type === "ImportDeclaration" &&
|
|
111
|
+
statement.specifiers.length > 0
|
|
112
|
+
) {
|
|
113
|
+
for (const specifier of statement.specifiers) {
|
|
114
|
+
topLevelBindings.set(specifier.local.name, {
|
|
115
|
+
kind: "import"
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (statement.type === "FunctionDeclaration" && statement.id) {
|
|
123
|
+
topLevelBindings.set(statement.id.name, {
|
|
124
|
+
kind: "function",
|
|
125
|
+
node: statement
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (
|
|
132
|
+
statement.type !== "VariableDeclaration" ||
|
|
133
|
+
statement.kind !== "const"
|
|
134
|
+
) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
for (const declaration of statement.declarations) {
|
|
139
|
+
if (declaration.id.type !== "Identifier" || !declaration.init) {
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (
|
|
144
|
+
declaration.init.type === "ArrowFunctionExpression" ||
|
|
145
|
+
declaration.init.type === "FunctionExpression"
|
|
146
|
+
) {
|
|
147
|
+
topLevelBindings.set(declaration.id.name, {
|
|
148
|
+
kind: "function",
|
|
149
|
+
node: declaration.init
|
|
150
|
+
});
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
topLevelBindings.set(declaration.id.name, {
|
|
155
|
+
kind: "value",
|
|
156
|
+
node: declaration.init
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
160
161
|
/**
|
|
161
162
|
* Compare two key strings based on the provided options.
|
|
162
163
|
* This function mimics the behavior of the built-in rule.
|
|
@@ -179,6 +180,387 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
179
180
|
return left.localeCompare(right);
|
|
180
181
|
};
|
|
181
182
|
|
|
183
|
+
const addBoundIdentifiers = (
|
|
184
|
+
node: TSESTree.Node | null,
|
|
185
|
+
stableLocals: Set<string>
|
|
186
|
+
) => {
|
|
187
|
+
if (!node) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (node.type === "Identifier") {
|
|
192
|
+
stableLocals.add(node.name);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (node.type === "AssignmentPattern") {
|
|
197
|
+
addBoundIdentifiers(node.left, stableLocals);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (node.type === "RestElement") {
|
|
202
|
+
addBoundIdentifiers(node.argument, stableLocals);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (node.type === "ArrayPattern") {
|
|
207
|
+
for (const element of node.elements) {
|
|
208
|
+
if (!element) {
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
addBoundIdentifiers(element, stableLocals);
|
|
213
|
+
}
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (node.type === "ObjectPattern") {
|
|
218
|
+
for (const property of node.properties) {
|
|
219
|
+
if (property.type === "RestElement") {
|
|
220
|
+
addBoundIdentifiers(property.argument, stableLocals);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
addBoundIdentifiers(property.value, stableLocals);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const getStableLocalsForNode = (node: TSESTree.Node) => {
|
|
230
|
+
const stableLocals = new Set<string>();
|
|
231
|
+
const ancestors = sourceCode.getAncestors(node);
|
|
232
|
+
|
|
233
|
+
for (const ancestor of ancestors) {
|
|
234
|
+
if (
|
|
235
|
+
ancestor.type === "FunctionDeclaration" ||
|
|
236
|
+
ancestor.type === "FunctionExpression" ||
|
|
237
|
+
ancestor.type === "ArrowFunctionExpression"
|
|
238
|
+
) {
|
|
239
|
+
for (const parameter of ancestor.params) {
|
|
240
|
+
addBoundIdentifiers(parameter, stableLocals);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
for (const ancestor of ancestors) {
|
|
246
|
+
if (
|
|
247
|
+
ancestor.type !== "Program" &&
|
|
248
|
+
ancestor.type !== "BlockStatement"
|
|
249
|
+
) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
for (const statement of ancestor.body) {
|
|
254
|
+
if (
|
|
255
|
+
statement.range[0] >= node.range[0] ||
|
|
256
|
+
statement.type !== "VariableDeclaration" ||
|
|
257
|
+
statement.kind !== "const"
|
|
258
|
+
) {
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
for (const declaration of statement.declarations) {
|
|
263
|
+
addBoundIdentifiers(declaration.id, stableLocals);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return stableLocals;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const getStaticMemberName = (
|
|
272
|
+
memberExpression: TSESTree.MemberExpression
|
|
273
|
+
) => {
|
|
274
|
+
if (
|
|
275
|
+
!memberExpression.computed &&
|
|
276
|
+
memberExpression.property.type === "Identifier"
|
|
277
|
+
) {
|
|
278
|
+
return memberExpression.property.name;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (
|
|
282
|
+
memberExpression.computed &&
|
|
283
|
+
memberExpression.property.type === "Literal" &&
|
|
284
|
+
typeof memberExpression.property.value === "string"
|
|
285
|
+
) {
|
|
286
|
+
return memberExpression.property.value;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return null;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const isStableIdentifier = (
|
|
293
|
+
name: string,
|
|
294
|
+
stableLocals: ReadonlySet<string>
|
|
295
|
+
) => {
|
|
296
|
+
if (stableLocals.has(name)) {
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const binding = topLevelBindings.get(name);
|
|
301
|
+
if (!binding) {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (binding.kind === "import") {
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (binding.kind === "value") {
|
|
310
|
+
return isPureRuntimeExpression(binding.node, stableLocals);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return false;
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
const isPureTopLevelFunction = (
|
|
317
|
+
functionNode:
|
|
318
|
+
| TSESTree.ArrowFunctionExpression
|
|
319
|
+
| TSESTree.FunctionDeclaration
|
|
320
|
+
| TSESTree.FunctionExpression
|
|
321
|
+
): boolean => {
|
|
322
|
+
const cached = pureFunctionCache.get(functionNode);
|
|
323
|
+
if (cached !== undefined) {
|
|
324
|
+
return cached;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (pureFunctionInProgress.has(functionNode)) {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
pureFunctionInProgress.add(functionNode);
|
|
332
|
+
|
|
333
|
+
const stableLocals = new Set<string>();
|
|
334
|
+
|
|
335
|
+
for (const parameter of functionNode.params) {
|
|
336
|
+
if (parameter.type === "Identifier") {
|
|
337
|
+
stableLocals.add(parameter.name);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
let isPure = true;
|
|
342
|
+
const checkExpression = (expression: TSESTree.Expression) =>
|
|
343
|
+
isPureRuntimeExpression(expression, stableLocals);
|
|
344
|
+
|
|
345
|
+
if (functionNode.body.type === "BlockStatement") {
|
|
346
|
+
for (const statement of functionNode.body.body) {
|
|
347
|
+
if (statement.type === "ReturnStatement") {
|
|
348
|
+
if (
|
|
349
|
+
statement.argument &&
|
|
350
|
+
!checkExpression(statement.argument)
|
|
351
|
+
) {
|
|
352
|
+
isPure = false;
|
|
353
|
+
}
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (
|
|
358
|
+
statement.type === "VariableDeclaration" &&
|
|
359
|
+
statement.kind === "const"
|
|
360
|
+
) {
|
|
361
|
+
for (const declaration of statement.declarations) {
|
|
362
|
+
if (
|
|
363
|
+
declaration.id.type !== "Identifier" ||
|
|
364
|
+
!declaration.init ||
|
|
365
|
+
!checkExpression(declaration.init)
|
|
366
|
+
) {
|
|
367
|
+
isPure = false;
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
stableLocals.add(declaration.id.name);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (!isPure) {
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
isPure = false;
|
|
382
|
+
break;
|
|
383
|
+
}
|
|
384
|
+
} else {
|
|
385
|
+
isPure = checkExpression(functionNode.body);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
pureFunctionInProgress.delete(functionNode);
|
|
389
|
+
pureFunctionCache.set(functionNode, isPure);
|
|
390
|
+
return isPure;
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const isPureRuntimeExpression: (
|
|
394
|
+
node: TSESTree.Node | null,
|
|
395
|
+
stableLocals: ReadonlySet<string>
|
|
396
|
+
) => boolean = (node, stableLocals) => {
|
|
397
|
+
if (!node || node.type === "PrivateIdentifier") {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
switch (node.type) {
|
|
402
|
+
case "Identifier":
|
|
403
|
+
return isStableIdentifier(node.name, stableLocals);
|
|
404
|
+
case "Literal":
|
|
405
|
+
case "FunctionExpression":
|
|
406
|
+
case "ArrowFunctionExpression":
|
|
407
|
+
case "ClassExpression":
|
|
408
|
+
return true;
|
|
409
|
+
case "ThisExpression":
|
|
410
|
+
return stableLocals.has("this");
|
|
411
|
+
case "TemplateLiteral":
|
|
412
|
+
return node.expressions.every((expression) =>
|
|
413
|
+
isPureRuntimeExpression(expression, stableLocals)
|
|
414
|
+
);
|
|
415
|
+
case "UnaryExpression":
|
|
416
|
+
return isPureRuntimeExpression(node.argument, stableLocals);
|
|
417
|
+
case "BinaryExpression":
|
|
418
|
+
case "LogicalExpression":
|
|
419
|
+
return (
|
|
420
|
+
isPureRuntimeExpression(node.left, stableLocals) &&
|
|
421
|
+
isPureRuntimeExpression(node.right, stableLocals)
|
|
422
|
+
);
|
|
423
|
+
case "ConditionalExpression":
|
|
424
|
+
return (
|
|
425
|
+
isPureRuntimeExpression(node.test, stableLocals) &&
|
|
426
|
+
isPureRuntimeExpression(
|
|
427
|
+
node.consequent,
|
|
428
|
+
stableLocals
|
|
429
|
+
) &&
|
|
430
|
+
isPureRuntimeExpression(node.alternate, stableLocals)
|
|
431
|
+
);
|
|
432
|
+
case "ArrayExpression":
|
|
433
|
+
return node.elements.every((element) => {
|
|
434
|
+
if (!element || element.type === "SpreadElement") {
|
|
435
|
+
return false;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return isPureRuntimeExpression(element, stableLocals);
|
|
439
|
+
});
|
|
440
|
+
case "ObjectExpression":
|
|
441
|
+
return node.properties.every((property) => {
|
|
442
|
+
if (
|
|
443
|
+
property.type !== "Property" ||
|
|
444
|
+
property.computed ||
|
|
445
|
+
property.kind !== "init"
|
|
446
|
+
) {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (
|
|
451
|
+
property.key.type !== "Identifier" &&
|
|
452
|
+
property.key.type !== "Literal"
|
|
453
|
+
) {
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (property.method) {
|
|
458
|
+
return true;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return isPureRuntimeExpression(
|
|
462
|
+
property.value,
|
|
463
|
+
stableLocals
|
|
464
|
+
);
|
|
465
|
+
});
|
|
466
|
+
case "MemberExpression":
|
|
467
|
+
return (
|
|
468
|
+
isPureRuntimeExpression(node.object, stableLocals) &&
|
|
469
|
+
(!node.computed ||
|
|
470
|
+
isPureRuntimeExpression(
|
|
471
|
+
node.property,
|
|
472
|
+
stableLocals
|
|
473
|
+
))
|
|
474
|
+
);
|
|
475
|
+
case "NewExpression":
|
|
476
|
+
return (
|
|
477
|
+
node.callee.type === "Identifier" &&
|
|
478
|
+
PURE_CONSTRUCTORS.has(node.callee.name) &&
|
|
479
|
+
node.arguments.every((argument) => {
|
|
480
|
+
if (argument.type === "SpreadElement") {
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return isPureRuntimeExpression(
|
|
485
|
+
argument,
|
|
486
|
+
stableLocals
|
|
487
|
+
);
|
|
488
|
+
})
|
|
489
|
+
);
|
|
490
|
+
case "CallExpression": {
|
|
491
|
+
const argsArePure = node.arguments.every((argument) => {
|
|
492
|
+
if (argument.type === "SpreadElement") {
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return isPureRuntimeExpression(argument, stableLocals);
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
if (!argsArePure) {
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (node.callee.type === "Identifier") {
|
|
504
|
+
if (PURE_GLOBAL_FUNCTIONS.has(node.callee.name)) {
|
|
505
|
+
return true;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const binding = topLevelBindings.get(node.callee.name);
|
|
509
|
+
return (
|
|
510
|
+
binding?.kind === "function" &&
|
|
511
|
+
isPureTopLevelFunction(binding.node)
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (node.callee.type !== "MemberExpression") {
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const memberName = getStaticMemberName(node.callee);
|
|
520
|
+
if (!memberName || !PURE_MEMBER_METHODS.has(memberName)) {
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return isPureRuntimeExpression(
|
|
525
|
+
node.callee.object,
|
|
526
|
+
stableLocals
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
default:
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
const isSafeToReorderExpression: (
|
|
535
|
+
node: TSESTree.Node | null
|
|
536
|
+
) => boolean = (node) => isPureRuntimeExpression(node, new Set());
|
|
537
|
+
|
|
538
|
+
const isSafeJSXAttributeValue = (
|
|
539
|
+
value: TSESTree.JSXAttribute["value"],
|
|
540
|
+
scopeNode: TSESTree.Node
|
|
541
|
+
) => {
|
|
542
|
+
if (value === null) {
|
|
543
|
+
return true;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (value.type === "Literal") {
|
|
547
|
+
return true;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (value.type !== "JSXExpressionContainer") {
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (value.expression.type === "JSXEmptyExpression") {
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
return isPureRuntimeExpression(
|
|
559
|
+
value.expression,
|
|
560
|
+
getStableLocalsForNode(scopeNode)
|
|
561
|
+
);
|
|
562
|
+
};
|
|
563
|
+
|
|
182
564
|
/**
|
|
183
565
|
* Determines if a property is a function property.
|
|
184
566
|
*/
|
|
@@ -471,7 +853,10 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
471
853
|
keys.some(
|
|
472
854
|
(key) =>
|
|
473
855
|
key.node.type === "Property" &&
|
|
474
|
-
!
|
|
856
|
+
!isPureRuntimeExpression(
|
|
857
|
+
key.node.value,
|
|
858
|
+
getStableLocalsForNode(key.node)
|
|
859
|
+
)
|
|
475
860
|
)
|
|
476
861
|
) {
|
|
477
862
|
autoFixable = false;
|
|
@@ -663,7 +1048,7 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
663
1048
|
attrs.some(
|
|
664
1049
|
(attr) =>
|
|
665
1050
|
attr.type === "JSXAttribute" &&
|
|
666
|
-
!isSafeJSXAttributeValue(attr.value)
|
|
1051
|
+
!isSafeJSXAttributeValue(attr.value, attr)
|
|
667
1052
|
)
|
|
668
1053
|
) {
|
|
669
1054
|
context.report({
|