eslint-plugin-absolute 0.2.5 → 0.2.7
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/.absolutejs/eslint.cache.json +4 -4
- package/.absolutejs/prettier.cache.json +2 -2
- package/.absolutejs/tsconfig.tsbuildinfo +1 -1
- package/dist/index.js +164 -112
- package/package.json +1 -1
- package/src/rules/sort-keys-fixable.ts +264 -169
|
@@ -52,6 +52,23 @@ type TopLevelBinding =
|
|
|
52
52
|
|
|
53
53
|
const SORT_BEFORE = -1;
|
|
54
54
|
const PURE_CONSTRUCTORS = new Set(["Date"]);
|
|
55
|
+
const PURE_GLOBAL_IDENTIFIERS = new Set([
|
|
56
|
+
"Array",
|
|
57
|
+
"BigInt",
|
|
58
|
+
"Boolean",
|
|
59
|
+
"Date",
|
|
60
|
+
"Function",
|
|
61
|
+
"Map",
|
|
62
|
+
"Number",
|
|
63
|
+
"Object",
|
|
64
|
+
"Promise",
|
|
65
|
+
"RegExp",
|
|
66
|
+
"Set",
|
|
67
|
+
"String",
|
|
68
|
+
"Symbol",
|
|
69
|
+
"URL",
|
|
70
|
+
"undefined"
|
|
71
|
+
]);
|
|
55
72
|
const PURE_GLOBAL_FUNCTIONS = new Set(["Boolean", "Number", "String"]);
|
|
56
73
|
const PURE_MEMBER_METHODS = new Set([
|
|
57
74
|
"getDay",
|
|
@@ -105,59 +122,6 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
105
122
|
? option.variablesBeforeFunctions
|
|
106
123
|
: false;
|
|
107
124
|
|
|
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
|
-
|
|
161
125
|
/**
|
|
162
126
|
* Compare two key strings based on the provided options.
|
|
163
127
|
* This function mimics the behavior of the built-in rule.
|
|
@@ -180,50 +144,176 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
180
144
|
return left.localeCompare(right);
|
|
181
145
|
};
|
|
182
146
|
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
147
|
+
const addImportBindings = (statement: TSESTree.ImportDeclaration) => {
|
|
148
|
+
if (statement.specifiers.length === 0) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
for (const specifier of statement.specifiers) {
|
|
153
|
+
topLevelBindings.set(specifier.local.name, {
|
|
154
|
+
kind: "import"
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const addVariableBinding = (
|
|
160
|
+
declaration: TSESTree.VariableDeclarator
|
|
186
161
|
) => {
|
|
187
|
-
if (!
|
|
162
|
+
if (declaration.id.type !== "Identifier" || !declaration.init) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (
|
|
167
|
+
declaration.init.type === "ArrowFunctionExpression" ||
|
|
168
|
+
declaration.init.type === "FunctionExpression"
|
|
169
|
+
) {
|
|
170
|
+
topLevelBindings.set(declaration.id.name, {
|
|
171
|
+
kind: "function",
|
|
172
|
+
node: declaration.init
|
|
173
|
+
});
|
|
188
174
|
return;
|
|
189
175
|
}
|
|
190
176
|
|
|
191
|
-
|
|
192
|
-
|
|
177
|
+
topLevelBindings.set(declaration.id.name, {
|
|
178
|
+
kind: "value",
|
|
179
|
+
node: declaration.init
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const addTopLevelBindings = (statement: TSESTree.ProgramStatement) => {
|
|
184
|
+
if (statement.type === "ImportDeclaration") {
|
|
185
|
+
addImportBindings(statement);
|
|
193
186
|
return;
|
|
194
187
|
}
|
|
195
188
|
|
|
196
|
-
if (
|
|
197
|
-
|
|
189
|
+
if (statement.type === "FunctionDeclaration" && statement.id) {
|
|
190
|
+
topLevelBindings.set(statement.id.name, {
|
|
191
|
+
kind: "function",
|
|
192
|
+
node: statement
|
|
193
|
+
});
|
|
198
194
|
return;
|
|
199
195
|
}
|
|
200
196
|
|
|
201
|
-
if (
|
|
202
|
-
|
|
197
|
+
if (
|
|
198
|
+
statement.type !== "VariableDeclaration" ||
|
|
199
|
+
statement.kind !== "const"
|
|
200
|
+
) {
|
|
203
201
|
return;
|
|
204
202
|
}
|
|
205
203
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
204
|
+
for (const declaration of statement.declarations) {
|
|
205
|
+
addVariableBinding(declaration);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
211
208
|
|
|
212
|
-
|
|
213
|
-
|
|
209
|
+
for (const statement of sourceCode.ast.body) {
|
|
210
|
+
addTopLevelBindings(statement);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const addBoundIdentifiers = (
|
|
214
|
+
node: TSESTree.Node | null,
|
|
215
|
+
stableLocals: Set<string>
|
|
216
|
+
) => {
|
|
217
|
+
if (!node) {
|
|
214
218
|
return;
|
|
215
219
|
}
|
|
216
220
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
221
|
+
switch (node.type) {
|
|
222
|
+
case "Identifier":
|
|
223
|
+
stableLocals.add(node.name);
|
|
224
|
+
return;
|
|
225
|
+
case "AssignmentPattern":
|
|
226
|
+
addBoundIdentifiers(node.left, stableLocals);
|
|
227
|
+
return;
|
|
228
|
+
case "RestElement":
|
|
229
|
+
addBoundIdentifiers(node.argument, stableLocals);
|
|
230
|
+
return;
|
|
231
|
+
case "ArrayPattern":
|
|
232
|
+
for (const element of node.elements.filter(Boolean)) {
|
|
233
|
+
addBoundIdentifiers(element, stableLocals);
|
|
222
234
|
}
|
|
235
|
+
break;
|
|
236
|
+
case "ObjectPattern":
|
|
237
|
+
for (const property of node.properties) {
|
|
238
|
+
const bindingNode =
|
|
239
|
+
property.type === "RestElement"
|
|
240
|
+
? property.argument
|
|
241
|
+
: property.value;
|
|
242
|
+
addBoundIdentifiers(bindingNode, stableLocals);
|
|
243
|
+
}
|
|
244
|
+
break;
|
|
245
|
+
default:
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const addFunctionParamBindings = (
|
|
251
|
+
functionNode:
|
|
252
|
+
| TSESTree.ArrowFunctionExpression
|
|
253
|
+
| TSESTree.FunctionDeclaration
|
|
254
|
+
| TSESTree.FunctionExpression,
|
|
255
|
+
stableLocals: Set<string>
|
|
256
|
+
) => {
|
|
257
|
+
for (const parameter of functionNode.params) {
|
|
258
|
+
addBoundIdentifiers(parameter, stableLocals);
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const addAncestorConstBindings = (
|
|
263
|
+
ancestor: TSESTree.BlockStatement | TSESTree.Program,
|
|
264
|
+
node: TSESTree.Node,
|
|
265
|
+
stableLocals: Set<string>
|
|
266
|
+
) => {
|
|
267
|
+
const addDeclarationBindings = (statement: TSESTree.Statement) => {
|
|
268
|
+
if (
|
|
269
|
+
statement.type !== "VariableDeclaration" ||
|
|
270
|
+
statement.kind !== "const"
|
|
271
|
+
) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
for (const declaration of statement.declarations) {
|
|
276
|
+
addBoundIdentifiers(declaration.id, stableLocals);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
223
279
|
|
|
224
|
-
|
|
280
|
+
for (const statement of ancestor.body) {
|
|
281
|
+
if (statement.range[0] >= node.range[0]) {
|
|
282
|
+
return;
|
|
225
283
|
}
|
|
284
|
+
|
|
285
|
+
addDeclarationBindings(statement);
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const addAncestorBindingsForNode = (
|
|
290
|
+
ancestor: TSESTree.Node,
|
|
291
|
+
node: TSESTree.Node,
|
|
292
|
+
stableLocals: Set<string>
|
|
293
|
+
) => {
|
|
294
|
+
if (
|
|
295
|
+
ancestor.type !== "Program" &&
|
|
296
|
+
ancestor.type !== "BlockStatement"
|
|
297
|
+
) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
addAncestorConstBindings(ancestor, node, stableLocals);
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const addFunctionBindingsForAncestor = (
|
|
305
|
+
ancestor: TSESTree.Node,
|
|
306
|
+
stableLocals: Set<string>
|
|
307
|
+
) => {
|
|
308
|
+
if (
|
|
309
|
+
ancestor.type !== "FunctionDeclaration" &&
|
|
310
|
+
ancestor.type !== "FunctionExpression" &&
|
|
311
|
+
ancestor.type !== "ArrowFunctionExpression"
|
|
312
|
+
) {
|
|
313
|
+
return;
|
|
226
314
|
}
|
|
315
|
+
|
|
316
|
+
addFunctionParamBindings(ancestor, stableLocals);
|
|
227
317
|
};
|
|
228
318
|
|
|
229
319
|
const getStableLocalsForNode = (node: TSESTree.Node) => {
|
|
@@ -231,38 +321,11 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
231
321
|
const ancestors = sourceCode.getAncestors(node);
|
|
232
322
|
|
|
233
323
|
for (const ancestor of ancestors) {
|
|
234
|
-
|
|
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
|
-
}
|
|
324
|
+
addFunctionBindingsForAncestor(ancestor, stableLocals);
|
|
243
325
|
}
|
|
244
326
|
|
|
245
327
|
for (const ancestor of ancestors) {
|
|
246
|
-
|
|
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
|
-
}
|
|
328
|
+
addAncestorBindingsForNode(ancestor, node, stableLocals);
|
|
266
329
|
}
|
|
267
330
|
|
|
268
331
|
return stableLocals;
|
|
@@ -293,6 +356,10 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
293
356
|
name: string,
|
|
294
357
|
stableLocals: ReadonlySet<string>
|
|
295
358
|
) => {
|
|
359
|
+
if (PURE_GLOBAL_IDENTIFIERS.has(name)) {
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
|
|
296
363
|
if (stableLocals.has(name)) {
|
|
297
364
|
return true;
|
|
298
365
|
}
|
|
@@ -313,12 +380,77 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
313
380
|
return false;
|
|
314
381
|
};
|
|
315
382
|
|
|
383
|
+
const isPureConstStatement = (
|
|
384
|
+
statement: TSESTree.VariableDeclaration,
|
|
385
|
+
stableLocals: Set<string>,
|
|
386
|
+
checkExpression: (expression: TSESTree.Expression) => boolean
|
|
387
|
+
) => {
|
|
388
|
+
if (statement.kind !== "const") {
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
for (const declaration of statement.declarations) {
|
|
393
|
+
if (declaration.id.type !== "Identifier" || !declaration.init) {
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (!checkExpression(declaration.init)) {
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
stableLocals.add(declaration.id.name);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return true;
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
const isPureFunctionStatement = (
|
|
408
|
+
statement: TSESTree.Statement,
|
|
409
|
+
stableLocals: Set<string>,
|
|
410
|
+
checkExpression: (expression: TSESTree.Expression) => boolean
|
|
411
|
+
) => {
|
|
412
|
+
if (statement.type === "ReturnStatement") {
|
|
413
|
+
return (
|
|
414
|
+
!statement.argument || checkExpression(statement.argument)
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (statement.type === "VariableDeclaration") {
|
|
419
|
+
return isPureConstStatement(
|
|
420
|
+
statement,
|
|
421
|
+
stableLocals,
|
|
422
|
+
checkExpression
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return false;
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
const isPureFunctionBody = (
|
|
430
|
+
body: TSESTree.BlockStatement,
|
|
431
|
+
stableLocals: Set<string>,
|
|
432
|
+
checkExpression: (expression: TSESTree.Expression) => boolean
|
|
433
|
+
) => {
|
|
434
|
+
for (const statement of body.body) {
|
|
435
|
+
const statementIsPure = isPureFunctionStatement(
|
|
436
|
+
statement,
|
|
437
|
+
stableLocals,
|
|
438
|
+
checkExpression
|
|
439
|
+
);
|
|
440
|
+
if (!statementIsPure) {
|
|
441
|
+
return false;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return true;
|
|
446
|
+
};
|
|
447
|
+
|
|
316
448
|
const isPureTopLevelFunction = (
|
|
317
449
|
functionNode:
|
|
318
450
|
| TSESTree.ArrowFunctionExpression
|
|
319
451
|
| TSESTree.FunctionDeclaration
|
|
320
452
|
| TSESTree.FunctionExpression
|
|
321
|
-
)
|
|
453
|
+
) => {
|
|
322
454
|
const cached = pureFunctionCache.get(functionNode);
|
|
323
455
|
if (cached !== undefined) {
|
|
324
456
|
return cached;
|
|
@@ -331,63 +463,38 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
331
463
|
pureFunctionInProgress.add(functionNode);
|
|
332
464
|
|
|
333
465
|
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;
|
|
466
|
+
addFunctionParamBindings(functionNode, stableLocals);
|
|
342
467
|
const checkExpression = (expression: TSESTree.Expression) =>
|
|
343
468
|
isPureRuntimeExpression(expression, stableLocals);
|
|
469
|
+
const isPure =
|
|
470
|
+
functionNode.body.type === "BlockStatement"
|
|
471
|
+
? isPureFunctionBody(
|
|
472
|
+
functionNode.body,
|
|
473
|
+
stableLocals,
|
|
474
|
+
checkExpression
|
|
475
|
+
)
|
|
476
|
+
: checkExpression(functionNode.body);
|
|
344
477
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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
|
-
}
|
|
478
|
+
pureFunctionInProgress.delete(functionNode);
|
|
479
|
+
pureFunctionCache.set(functionNode, isPure);
|
|
480
|
+
return isPure;
|
|
481
|
+
};
|
|
377
482
|
|
|
378
|
-
|
|
379
|
-
|
|
483
|
+
const isPureIdentifierCall = (
|
|
484
|
+
callExpression: TSESTree.CallExpression
|
|
485
|
+
) => {
|
|
486
|
+
if (callExpression.callee.type !== "Identifier") {
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
380
489
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
}
|
|
384
|
-
} else {
|
|
385
|
-
isPure = checkExpression(functionNode.body);
|
|
490
|
+
if (PURE_GLOBAL_FUNCTIONS.has(callExpression.callee.name)) {
|
|
491
|
+
return true;
|
|
386
492
|
}
|
|
387
493
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
494
|
+
const binding = topLevelBindings.get(callExpression.callee.name);
|
|
495
|
+
return binding?.kind === "function"
|
|
496
|
+
? isPureTopLevelFunction(binding.node)
|
|
497
|
+
: false;
|
|
391
498
|
};
|
|
392
499
|
|
|
393
500
|
const isPureRuntimeExpression: (
|
|
@@ -501,15 +608,7 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
501
608
|
}
|
|
502
609
|
|
|
503
610
|
if (node.callee.type === "Identifier") {
|
|
504
|
-
|
|
505
|
-
return true;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
const binding = topLevelBindings.get(node.callee.name);
|
|
509
|
-
return (
|
|
510
|
-
binding?.kind === "function" &&
|
|
511
|
-
isPureTopLevelFunction(binding.node)
|
|
512
|
-
);
|
|
611
|
+
return isPureIdentifierCall(node);
|
|
513
612
|
}
|
|
514
613
|
|
|
515
614
|
if (node.callee.type !== "MemberExpression") {
|
|
@@ -531,10 +630,6 @@ export const sortKeysFixable: TSESLint.RuleModule<MessageIds, Options> = {
|
|
|
531
630
|
}
|
|
532
631
|
};
|
|
533
632
|
|
|
534
|
-
const isSafeToReorderExpression: (
|
|
535
|
-
node: TSESTree.Node | null
|
|
536
|
-
) => boolean = (node) => isPureRuntimeExpression(node, new Set());
|
|
537
|
-
|
|
538
633
|
const isSafeJSXAttributeValue = (
|
|
539
634
|
value: TSESTree.JSXAttribute["value"],
|
|
540
635
|
scopeNode: TSESTree.Node
|