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.
@@ -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 addBoundIdentifiers = (
184
- node: TSESTree.Node | null,
185
- stableLocals: Set<string>
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 (!node) {
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
- if (node.type === "Identifier") {
192
- stableLocals.add(node.name);
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 (node.type === "AssignmentPattern") {
197
- addBoundIdentifiers(node.left, stableLocals);
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 (node.type === "RestElement") {
202
- addBoundIdentifiers(node.argument, stableLocals);
197
+ if (
198
+ statement.type !== "VariableDeclaration" ||
199
+ statement.kind !== "const"
200
+ ) {
203
201
  return;
204
202
  }
205
203
 
206
- if (node.type === "ArrayPattern") {
207
- for (const element of node.elements) {
208
- if (!element) {
209
- continue;
210
- }
204
+ for (const declaration of statement.declarations) {
205
+ addVariableBinding(declaration);
206
+ }
207
+ };
211
208
 
212
- addBoundIdentifiers(element, stableLocals);
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
- if (node.type === "ObjectPattern") {
218
- for (const property of node.properties) {
219
- if (property.type === "RestElement") {
220
- addBoundIdentifiers(property.argument, stableLocals);
221
- continue;
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
- addBoundIdentifiers(property.value, stableLocals);
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
- 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
- }
324
+ addFunctionBindingsForAncestor(ancestor, stableLocals);
243
325
  }
244
326
 
245
327
  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
- }
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
- ): boolean => {
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
- 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
- }
478
+ pureFunctionInProgress.delete(functionNode);
479
+ pureFunctionCache.set(functionNode, isPure);
480
+ return isPure;
481
+ };
377
482
 
378
- continue;
379
- }
483
+ const isPureIdentifierCall = (
484
+ callExpression: TSESTree.CallExpression
485
+ ) => {
486
+ if (callExpression.callee.type !== "Identifier") {
487
+ return false;
488
+ }
380
489
 
381
- isPure = false;
382
- break;
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
- pureFunctionInProgress.delete(functionNode);
389
- pureFunctionCache.set(functionNode, isPure);
390
- return isPure;
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
- 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
- );
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