eslint-plugin-absolute 0.2.4 → 0.2.6
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.
|
@@ -105,18 +105,68 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
105
105
|
? option.variablesBeforeFunctions
|
|
106
106
|
: false;
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
/**
|
|
109
|
+
* Compare two key strings based on the provided options.
|
|
110
|
+
* This function mimics the behavior of the built-in rule.
|
|
111
|
+
*/
|
|
112
|
+
const compareKeys = (keyLeft: string, keyRight: string) => {
|
|
113
|
+
let left = keyLeft;
|
|
114
|
+
let right = keyRight;
|
|
115
|
+
|
|
116
|
+
if (!caseSensitive) {
|
|
117
|
+
left = left.toLowerCase();
|
|
118
|
+
right = right.toLowerCase();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (natural) {
|
|
122
|
+
return left.localeCompare(right, undefined, {
|
|
123
|
+
numeric: true
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return left.localeCompare(right);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const addImportBindings = (statement: TSESTree.ImportDeclaration) => {
|
|
131
|
+
if (statement.specifiers.length === 0) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
for (const specifier of statement.specifiers) {
|
|
136
|
+
topLevelBindings.set(specifier.local.name, {
|
|
137
|
+
kind: "import"
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const addVariableBinding = (
|
|
143
|
+
declaration: TSESTree.VariableDeclarator
|
|
144
|
+
) => {
|
|
145
|
+
if (declaration.id.type !== "Identifier" || !declaration.init) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
109
149
|
if (
|
|
110
|
-
|
|
111
|
-
|
|
150
|
+
declaration.init.type === "ArrowFunctionExpression" ||
|
|
151
|
+
declaration.init.type === "FunctionExpression"
|
|
112
152
|
) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
153
|
+
topLevelBindings.set(declaration.id.name, {
|
|
154
|
+
kind: "function",
|
|
155
|
+
node: declaration.init
|
|
156
|
+
});
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
topLevelBindings.set(declaration.id.name, {
|
|
161
|
+
kind: "value",
|
|
162
|
+
node: declaration.init
|
|
163
|
+
});
|
|
164
|
+
};
|
|
118
165
|
|
|
119
|
-
|
|
166
|
+
const addTopLevelBindings = (statement: TSESTree.ProgramStatement) => {
|
|
167
|
+
if (statement.type === "ImportDeclaration") {
|
|
168
|
+
addImportBindings(statement);
|
|
169
|
+
return;
|
|
120
170
|
}
|
|
121
171
|
|
|
122
172
|
if (statement.type === "FunctionDeclaration" && statement.id) {
|
|
@@ -124,60 +174,144 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
124
174
|
kind: "function",
|
|
125
175
|
node: statement
|
|
126
176
|
});
|
|
127
|
-
|
|
128
|
-
continue;
|
|
177
|
+
return;
|
|
129
178
|
}
|
|
130
179
|
|
|
131
180
|
if (
|
|
132
181
|
statement.type !== "VariableDeclaration" ||
|
|
133
182
|
statement.kind !== "const"
|
|
134
183
|
) {
|
|
135
|
-
|
|
184
|
+
return;
|
|
136
185
|
}
|
|
137
186
|
|
|
138
187
|
for (const declaration of statement.declarations) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
188
|
+
addVariableBinding(declaration);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
for (const statement of sourceCode.ast.body) {
|
|
193
|
+
addTopLevelBindings(statement);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const addBoundIdentifiers = (
|
|
197
|
+
node: TSESTree.Node | null,
|
|
198
|
+
stableLocals: Set<string>
|
|
199
|
+
) => {
|
|
200
|
+
if (!node) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
switch (node.type) {
|
|
205
|
+
case "Identifier":
|
|
206
|
+
stableLocals.add(node.name);
|
|
207
|
+
return;
|
|
208
|
+
case "AssignmentPattern":
|
|
209
|
+
addBoundIdentifiers(node.left, stableLocals);
|
|
210
|
+
return;
|
|
211
|
+
case "RestElement":
|
|
212
|
+
addBoundIdentifiers(node.argument, stableLocals);
|
|
213
|
+
return;
|
|
214
|
+
case "ArrayPattern":
|
|
215
|
+
for (const element of node.elements.filter(Boolean)) {
|
|
216
|
+
addBoundIdentifiers(element, stableLocals);
|
|
217
|
+
}
|
|
218
|
+
break;
|
|
219
|
+
case "ObjectPattern":
|
|
220
|
+
for (const property of node.properties) {
|
|
221
|
+
const bindingNode =
|
|
222
|
+
property.type === "RestElement"
|
|
223
|
+
? property.argument
|
|
224
|
+
: property.value;
|
|
225
|
+
addBoundIdentifiers(bindingNode, stableLocals);
|
|
226
|
+
}
|
|
227
|
+
break;
|
|
228
|
+
default:
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
};
|
|
142
232
|
|
|
233
|
+
const addFunctionParamBindings = (
|
|
234
|
+
functionNode:
|
|
235
|
+
| TSESTree.ArrowFunctionExpression
|
|
236
|
+
| TSESTree.FunctionDeclaration
|
|
237
|
+
| TSESTree.FunctionExpression,
|
|
238
|
+
stableLocals: Set<string>
|
|
239
|
+
) => {
|
|
240
|
+
for (const parameter of functionNode.params) {
|
|
241
|
+
addBoundIdentifiers(parameter, stableLocals);
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const addAncestorConstBindings = (
|
|
246
|
+
ancestor: TSESTree.BlockStatement | TSESTree.Program,
|
|
247
|
+
node: TSESTree.Node,
|
|
248
|
+
stableLocals: Set<string>
|
|
249
|
+
) => {
|
|
250
|
+
const addDeclarationBindings = (statement: TSESTree.Statement) => {
|
|
143
251
|
if (
|
|
144
|
-
|
|
145
|
-
|
|
252
|
+
statement.type !== "VariableDeclaration" ||
|
|
253
|
+
statement.kind !== "const"
|
|
146
254
|
) {
|
|
147
|
-
|
|
148
|
-
kind: "function",
|
|
149
|
-
node: declaration.init
|
|
150
|
-
});
|
|
151
|
-
continue;
|
|
255
|
+
return;
|
|
152
256
|
}
|
|
153
257
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
258
|
+
for (const declaration of statement.declarations) {
|
|
259
|
+
addBoundIdentifiers(declaration.id, stableLocals);
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
for (const statement of ancestor.body) {
|
|
264
|
+
if (statement.range[0] >= node.range[0]) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
addDeclarationBindings(statement);
|
|
158
269
|
}
|
|
159
|
-
}
|
|
270
|
+
};
|
|
160
271
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
272
|
+
const addAncestorBindingsForNode = (
|
|
273
|
+
ancestor: TSESTree.Node,
|
|
274
|
+
node: TSESTree.Node,
|
|
275
|
+
stableLocals: Set<string>
|
|
276
|
+
) => {
|
|
277
|
+
if (
|
|
278
|
+
ancestor.type !== "Program" &&
|
|
279
|
+
ancestor.type !== "BlockStatement"
|
|
280
|
+
) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
168
283
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
284
|
+
addAncestorConstBindings(ancestor, node, stableLocals);
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const addFunctionBindingsForAncestor = (
|
|
288
|
+
ancestor: TSESTree.Node,
|
|
289
|
+
stableLocals: Set<string>
|
|
290
|
+
) => {
|
|
291
|
+
if (
|
|
292
|
+
ancestor.type !== "FunctionDeclaration" &&
|
|
293
|
+
ancestor.type !== "FunctionExpression" &&
|
|
294
|
+
ancestor.type !== "ArrowFunctionExpression"
|
|
295
|
+
) {
|
|
296
|
+
return;
|
|
172
297
|
}
|
|
173
298
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
299
|
+
addFunctionParamBindings(ancestor, stableLocals);
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const getStableLocalsForNode = (node: TSESTree.Node) => {
|
|
303
|
+
const stableLocals = new Set<string>();
|
|
304
|
+
const ancestors = sourceCode.getAncestors(node);
|
|
305
|
+
|
|
306
|
+
for (const ancestor of ancestors) {
|
|
307
|
+
addFunctionBindingsForAncestor(ancestor, stableLocals);
|
|
178
308
|
}
|
|
179
309
|
|
|
180
|
-
|
|
310
|
+
for (const ancestor of ancestors) {
|
|
311
|
+
addAncestorBindingsForNode(ancestor, node, stableLocals);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return stableLocals;
|
|
181
315
|
};
|
|
182
316
|
|
|
183
317
|
const getStaticMemberName = (
|
|
@@ -225,12 +359,77 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
225
359
|
return false;
|
|
226
360
|
};
|
|
227
361
|
|
|
362
|
+
const isPureConstStatement = (
|
|
363
|
+
statement: TSESTree.VariableDeclaration,
|
|
364
|
+
stableLocals: Set<string>,
|
|
365
|
+
checkExpression: (expression: TSESTree.Expression) => boolean
|
|
366
|
+
) => {
|
|
367
|
+
if (statement.kind !== "const") {
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
for (const declaration of statement.declarations) {
|
|
372
|
+
if (declaration.id.type !== "Identifier" || !declaration.init) {
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (!checkExpression(declaration.init)) {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
stableLocals.add(declaration.id.name);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return true;
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
const isPureFunctionStatement = (
|
|
387
|
+
statement: TSESTree.Statement,
|
|
388
|
+
stableLocals: Set<string>,
|
|
389
|
+
checkExpression: (expression: TSESTree.Expression) => boolean
|
|
390
|
+
) => {
|
|
391
|
+
if (statement.type === "ReturnStatement") {
|
|
392
|
+
return (
|
|
393
|
+
!statement.argument || checkExpression(statement.argument)
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (statement.type === "VariableDeclaration") {
|
|
398
|
+
return isPureConstStatement(
|
|
399
|
+
statement,
|
|
400
|
+
stableLocals,
|
|
401
|
+
checkExpression
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return false;
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const isPureFunctionBody = (
|
|
409
|
+
body: TSESTree.BlockStatement,
|
|
410
|
+
stableLocals: Set<string>,
|
|
411
|
+
checkExpression: (expression: TSESTree.Expression) => boolean
|
|
412
|
+
) => {
|
|
413
|
+
for (const statement of body.body) {
|
|
414
|
+
const statementIsPure = isPureFunctionStatement(
|
|
415
|
+
statement,
|
|
416
|
+
stableLocals,
|
|
417
|
+
checkExpression
|
|
418
|
+
);
|
|
419
|
+
if (!statementIsPure) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return true;
|
|
425
|
+
};
|
|
426
|
+
|
|
228
427
|
const isPureTopLevelFunction = (
|
|
229
428
|
functionNode:
|
|
230
429
|
| TSESTree.ArrowFunctionExpression
|
|
231
430
|
| TSESTree.FunctionDeclaration
|
|
232
431
|
| TSESTree.FunctionExpression
|
|
233
|
-
)
|
|
432
|
+
) => {
|
|
234
433
|
const cached = pureFunctionCache.get(functionNode);
|
|
235
434
|
if (cached !== undefined) {
|
|
236
435
|
return cached;
|
|
@@ -243,63 +442,38 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
243
442
|
pureFunctionInProgress.add(functionNode);
|
|
244
443
|
|
|
245
444
|
const stableLocals = new Set<string>();
|
|
246
|
-
|
|
247
|
-
for (const parameter of functionNode.params) {
|
|
248
|
-
if (parameter.type === "Identifier") {
|
|
249
|
-
stableLocals.add(parameter.name);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
let isPure = true;
|
|
445
|
+
addFunctionParamBindings(functionNode, stableLocals);
|
|
254
446
|
const checkExpression = (expression: TSESTree.Expression) =>
|
|
255
447
|
isPureRuntimeExpression(expression, stableLocals);
|
|
448
|
+
const isPure =
|
|
449
|
+
functionNode.body.type === "BlockStatement"
|
|
450
|
+
? isPureFunctionBody(
|
|
451
|
+
functionNode.body,
|
|
452
|
+
stableLocals,
|
|
453
|
+
checkExpression
|
|
454
|
+
)
|
|
455
|
+
: checkExpression(functionNode.body);
|
|
256
456
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
statement.argument &&
|
|
262
|
-
!checkExpression(statement.argument)
|
|
263
|
-
) {
|
|
264
|
-
isPure = false;
|
|
265
|
-
}
|
|
266
|
-
continue;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (
|
|
270
|
-
statement.type === "VariableDeclaration" &&
|
|
271
|
-
statement.kind === "const"
|
|
272
|
-
) {
|
|
273
|
-
for (const declaration of statement.declarations) {
|
|
274
|
-
if (
|
|
275
|
-
declaration.id.type !== "Identifier" ||
|
|
276
|
-
!declaration.init ||
|
|
277
|
-
!checkExpression(declaration.init)
|
|
278
|
-
) {
|
|
279
|
-
isPure = false;
|
|
280
|
-
break;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
stableLocals.add(declaration.id.name);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (!isPure) {
|
|
287
|
-
break;
|
|
288
|
-
}
|
|
457
|
+
pureFunctionInProgress.delete(functionNode);
|
|
458
|
+
pureFunctionCache.set(functionNode, isPure);
|
|
459
|
+
return isPure;
|
|
460
|
+
};
|
|
289
461
|
|
|
290
|
-
|
|
291
|
-
|
|
462
|
+
const isPureIdentifierCall = (
|
|
463
|
+
callExpression: TSESTree.CallExpression
|
|
464
|
+
) => {
|
|
465
|
+
if (callExpression.callee.type !== "Identifier") {
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
292
468
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
} else {
|
|
297
|
-
isPure = checkExpression(functionNode.body);
|
|
469
|
+
if (PURE_GLOBAL_FUNCTIONS.has(callExpression.callee.name)) {
|
|
470
|
+
return true;
|
|
298
471
|
}
|
|
299
472
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
473
|
+
const binding = topLevelBindings.get(callExpression.callee.name);
|
|
474
|
+
return binding?.kind === "function"
|
|
475
|
+
? isPureTopLevelFunction(binding.node)
|
|
476
|
+
: false;
|
|
303
477
|
};
|
|
304
478
|
|
|
305
479
|
const isPureRuntimeExpression: (
|
|
@@ -413,15 +587,7 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
413
587
|
}
|
|
414
588
|
|
|
415
589
|
if (node.callee.type === "Identifier") {
|
|
416
|
-
|
|
417
|
-
return true;
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
const binding = topLevelBindings.get(node.callee.name);
|
|
421
|
-
return (
|
|
422
|
-
binding?.kind === "function" &&
|
|
423
|
-
isPureTopLevelFunction(binding.node)
|
|
424
|
-
);
|
|
590
|
+
return isPureIdentifierCall(node);
|
|
425
591
|
}
|
|
426
592
|
|
|
427
593
|
if (node.callee.type !== "MemberExpression") {
|
|
@@ -443,12 +609,9 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
443
609
|
}
|
|
444
610
|
};
|
|
445
611
|
|
|
446
|
-
const isSafeToReorderExpression: (
|
|
447
|
-
node: TSESTree.Node | null
|
|
448
|
-
) => boolean = (node) => isPureRuntimeExpression(node, new Set());
|
|
449
|
-
|
|
450
612
|
const isSafeJSXAttributeValue = (
|
|
451
|
-
value: TSESTree.JSXAttribute["value"]
|
|
613
|
+
value: TSESTree.JSXAttribute["value"],
|
|
614
|
+
scopeNode: TSESTree.Node
|
|
452
615
|
) => {
|
|
453
616
|
if (value === null) {
|
|
454
617
|
return true;
|
|
@@ -466,7 +629,10 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
466
629
|
return false;
|
|
467
630
|
}
|
|
468
631
|
|
|
469
|
-
return
|
|
632
|
+
return isPureRuntimeExpression(
|
|
633
|
+
value.expression,
|
|
634
|
+
getStableLocalsForNode(scopeNode)
|
|
635
|
+
);
|
|
470
636
|
};
|
|
471
637
|
|
|
472
638
|
/**
|
|
@@ -761,7 +927,10 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
761
927
|
keys.some(
|
|
762
928
|
(key) =>
|
|
763
929
|
key.node.type === "Property" &&
|
|
764
|
-
!
|
|
930
|
+
!isPureRuntimeExpression(
|
|
931
|
+
key.node.value,
|
|
932
|
+
getStableLocalsForNode(key.node)
|
|
933
|
+
)
|
|
765
934
|
)
|
|
766
935
|
) {
|
|
767
936
|
autoFixable = false;
|
|
@@ -953,7 +1122,7 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
953
1122
|
attrs.some(
|
|
954
1123
|
(attr) =>
|
|
955
1124
|
attr.type === "JSXAttribute" &&
|
|
956
|
-
!isSafeJSXAttributeValue(attr.value)
|
|
1125
|
+
!isSafeJSXAttributeValue(attr.value, attr)
|
|
957
1126
|
)
|
|
958
1127
|
) {
|
|
959
1128
|
context.report({
|