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
- for (const statement of sourceCode.ast.body) {
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
- statement.type === "ImportDeclaration" &&
111
- statement.specifiers.length > 0
150
+ declaration.init.type === "ArrowFunctionExpression" ||
151
+ declaration.init.type === "FunctionExpression"
112
152
  ) {
113
- for (const specifier of statement.specifiers) {
114
- topLevelBindings.set(specifier.local.name, {
115
- kind: "import"
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
- continue;
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
- continue;
184
+ return;
136
185
  }
137
186
 
138
187
  for (const declaration of statement.declarations) {
139
- if (declaration.id.type !== "Identifier" || !declaration.init) {
140
- continue;
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
- declaration.init.type === "ArrowFunctionExpression" ||
145
- declaration.init.type === "FunctionExpression"
252
+ statement.type !== "VariableDeclaration" ||
253
+ statement.kind !== "const"
146
254
  ) {
147
- topLevelBindings.set(declaration.id.name, {
148
- kind: "function",
149
- node: declaration.init
150
- });
151
- continue;
255
+ return;
152
256
  }
153
257
 
154
- topLevelBindings.set(declaration.id.name, {
155
- kind: "value",
156
- node: declaration.init
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
- * Compare two key strings based on the provided options.
163
- * This function mimics the behavior of the built-in rule.
164
- */
165
- const compareKeys = (keyLeft: string, keyRight: string) => {
166
- let left = keyLeft;
167
- let right = keyRight;
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
- if (!caseSensitive) {
170
- left = left.toLowerCase();
171
- right = right.toLowerCase();
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
- if (natural) {
175
- return left.localeCompare(right, undefined, {
176
- numeric: true
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
- return left.localeCompare(right);
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
- ): boolean => {
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
- if (functionNode.body.type === "BlockStatement") {
258
- for (const statement of functionNode.body.body) {
259
- if (statement.type === "ReturnStatement") {
260
- if (
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
- continue;
291
- }
462
+ const isPureIdentifierCall = (
463
+ callExpression: TSESTree.CallExpression
464
+ ) => {
465
+ if (callExpression.callee.type !== "Identifier") {
466
+ return false;
467
+ }
292
468
 
293
- isPure = false;
294
- break;
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
- pureFunctionInProgress.delete(functionNode);
301
- pureFunctionCache.set(functionNode, isPure);
302
- return isPure;
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
- if (PURE_GLOBAL_FUNCTIONS.has(node.callee.name)) {
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 isSafeToReorderExpression(value.expression);
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
- !isSafeToReorderExpression(key.node.value)
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({