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.
package/dist/index.js CHANGED
@@ -186,6 +186,16 @@ var explicitObjectTypes = {
186
186
 
187
187
  // src/rules/sort-keys-fixable.ts
188
188
  var SORT_BEFORE = -1;
189
+ var PURE_CONSTRUCTORS = new Set(["Date"]);
190
+ var PURE_GLOBAL_FUNCTIONS = new Set(["Boolean", "Number", "String"]);
191
+ var PURE_MEMBER_METHODS = new Set([
192
+ "getDay",
193
+ "getHours",
194
+ "getMilliseconds",
195
+ "getMinutes",
196
+ "getSeconds",
197
+ "padStart"
198
+ ]);
189
199
  var hasDuplicateNames = (names) => {
190
200
  const seen = new Set;
191
201
  const nonNullNames = names.flatMap((name) => name === null ? [] : [name]);
@@ -197,73 +207,54 @@ var hasDuplicateNames = (names) => {
197
207
  }
198
208
  return false;
199
209
  };
200
- var isSafeStaticTemplate = (node) => node.expressions.length === 0;
201
- var isSafeArrayElement = (node) => {
202
- if (!node || node.type === "SpreadElement") {
203
- return false;
204
- }
205
- return isSafeToReorderExpression(node);
206
- };
207
- var isSafeObjectProperty = (property) => {
208
- if (property.type !== "Property" || property.computed || property.kind !== "init") {
209
- return false;
210
- }
211
- if (property.key.type !== "Identifier" && property.key.type !== "Literal") {
212
- return false;
213
- }
214
- if (property.method) {
215
- return true;
216
- }
217
- return isSafeToReorderExpression(property.value);
218
- };
219
- var isSafeToReorderExpression = (node) => {
220
- if (!node || node.type === "PrivateIdentifier") {
221
- return false;
222
- }
223
- switch (node.type) {
224
- case "Identifier":
225
- case "Literal":
226
- case "ThisExpression":
227
- case "FunctionExpression":
228
- case "ArrowFunctionExpression":
229
- case "ClassExpression":
230
- return true;
231
- case "TemplateLiteral":
232
- return isSafeStaticTemplate(node);
233
- case "UnaryExpression":
234
- return isSafeToReorderExpression(node.argument);
235
- case "ArrayExpression":
236
- return node.elements.every(isSafeArrayElement);
237
- case "ObjectExpression":
238
- return node.properties.every(isSafeObjectProperty);
239
- default:
240
- return false;
241
- }
242
- };
243
- var isSafeJSXAttributeValue = (value) => {
244
- if (value === null) {
245
- return true;
246
- }
247
- if (value.type === "Literal") {
248
- return true;
249
- }
250
- if (value.type !== "JSXExpressionContainer") {
251
- return false;
252
- }
253
- if (value.expression.type === "JSXEmptyExpression") {
254
- return false;
255
- }
256
- return isSafeToReorderExpression(value.expression);
257
- };
258
210
  var sortKeysFixable = {
259
211
  create(context) {
260
212
  const { sourceCode } = context;
261
213
  const [option] = context.options;
214
+ const topLevelBindings = new Map;
215
+ const pureFunctionCache = new Map;
216
+ const pureFunctionInProgress = new Set;
262
217
  const order = option && option.order ? option.order : "asc";
263
218
  const caseSensitive = option && typeof option.caseSensitive === "boolean" ? option.caseSensitive : false;
264
219
  const natural = option && typeof option.natural === "boolean" ? option.natural : false;
265
220
  const minKeys = option && typeof option.minKeys === "number" ? option.minKeys : 2;
266
221
  const variablesBeforeFunctions = option && typeof option.variablesBeforeFunctions === "boolean" ? option.variablesBeforeFunctions : false;
222
+ for (const statement of sourceCode.ast.body) {
223
+ if (statement.type === "ImportDeclaration" && statement.specifiers.length > 0) {
224
+ for (const specifier of statement.specifiers) {
225
+ topLevelBindings.set(specifier.local.name, {
226
+ kind: "import"
227
+ });
228
+ }
229
+ continue;
230
+ }
231
+ if (statement.type === "FunctionDeclaration" && statement.id) {
232
+ topLevelBindings.set(statement.id.name, {
233
+ kind: "function",
234
+ node: statement
235
+ });
236
+ continue;
237
+ }
238
+ if (statement.type !== "VariableDeclaration" || statement.kind !== "const") {
239
+ continue;
240
+ }
241
+ for (const declaration of statement.declarations) {
242
+ if (declaration.id.type !== "Identifier" || !declaration.init) {
243
+ continue;
244
+ }
245
+ if (declaration.init.type === "ArrowFunctionExpression" || declaration.init.type === "FunctionExpression") {
246
+ topLevelBindings.set(declaration.id.name, {
247
+ kind: "function",
248
+ node: declaration.init
249
+ });
250
+ continue;
251
+ }
252
+ topLevelBindings.set(declaration.id.name, {
253
+ kind: "value",
254
+ node: declaration.init
255
+ });
256
+ }
257
+ }
267
258
  const compareKeys = (keyLeft, keyRight) => {
268
259
  let left = keyLeft;
269
260
  let right = keyRight;
@@ -278,6 +269,237 @@ var sortKeysFixable = {
278
269
  }
279
270
  return left.localeCompare(right);
280
271
  };
272
+ const addBoundIdentifiers = (node, stableLocals) => {
273
+ if (!node) {
274
+ return;
275
+ }
276
+ if (node.type === "Identifier") {
277
+ stableLocals.add(node.name);
278
+ return;
279
+ }
280
+ if (node.type === "AssignmentPattern") {
281
+ addBoundIdentifiers(node.left, stableLocals);
282
+ return;
283
+ }
284
+ if (node.type === "RestElement") {
285
+ addBoundIdentifiers(node.argument, stableLocals);
286
+ return;
287
+ }
288
+ if (node.type === "ArrayPattern") {
289
+ for (const element of node.elements) {
290
+ if (!element) {
291
+ continue;
292
+ }
293
+ addBoundIdentifiers(element, stableLocals);
294
+ }
295
+ return;
296
+ }
297
+ if (node.type === "ObjectPattern") {
298
+ for (const property of node.properties) {
299
+ if (property.type === "RestElement") {
300
+ addBoundIdentifiers(property.argument, stableLocals);
301
+ continue;
302
+ }
303
+ addBoundIdentifiers(property.value, stableLocals);
304
+ }
305
+ }
306
+ };
307
+ const getStableLocalsForNode = (node) => {
308
+ const stableLocals = new Set;
309
+ const ancestors = sourceCode.getAncestors(node);
310
+ for (const ancestor of ancestors) {
311
+ if (ancestor.type === "FunctionDeclaration" || ancestor.type === "FunctionExpression" || ancestor.type === "ArrowFunctionExpression") {
312
+ for (const parameter of ancestor.params) {
313
+ addBoundIdentifiers(parameter, stableLocals);
314
+ }
315
+ }
316
+ }
317
+ for (const ancestor of ancestors) {
318
+ if (ancestor.type !== "Program" && ancestor.type !== "BlockStatement") {
319
+ continue;
320
+ }
321
+ for (const statement of ancestor.body) {
322
+ if (statement.range[0] >= node.range[0] || statement.type !== "VariableDeclaration" || statement.kind !== "const") {
323
+ break;
324
+ }
325
+ for (const declaration of statement.declarations) {
326
+ addBoundIdentifiers(declaration.id, stableLocals);
327
+ }
328
+ }
329
+ }
330
+ return stableLocals;
331
+ };
332
+ const getStaticMemberName = (memberExpression) => {
333
+ if (!memberExpression.computed && memberExpression.property.type === "Identifier") {
334
+ return memberExpression.property.name;
335
+ }
336
+ if (memberExpression.computed && memberExpression.property.type === "Literal" && typeof memberExpression.property.value === "string") {
337
+ return memberExpression.property.value;
338
+ }
339
+ return null;
340
+ };
341
+ const isStableIdentifier = (name, stableLocals) => {
342
+ if (stableLocals.has(name)) {
343
+ return true;
344
+ }
345
+ const binding = topLevelBindings.get(name);
346
+ if (!binding) {
347
+ return false;
348
+ }
349
+ if (binding.kind === "import") {
350
+ return true;
351
+ }
352
+ if (binding.kind === "value") {
353
+ return isPureRuntimeExpression(binding.node, stableLocals);
354
+ }
355
+ return false;
356
+ };
357
+ const isPureTopLevelFunction = (functionNode) => {
358
+ const cached = pureFunctionCache.get(functionNode);
359
+ if (cached !== undefined) {
360
+ return cached;
361
+ }
362
+ if (pureFunctionInProgress.has(functionNode)) {
363
+ return false;
364
+ }
365
+ pureFunctionInProgress.add(functionNode);
366
+ const stableLocals = new Set;
367
+ for (const parameter of functionNode.params) {
368
+ if (parameter.type === "Identifier") {
369
+ stableLocals.add(parameter.name);
370
+ }
371
+ }
372
+ let isPure = true;
373
+ const checkExpression = (expression) => isPureRuntimeExpression(expression, stableLocals);
374
+ if (functionNode.body.type === "BlockStatement") {
375
+ for (const statement of functionNode.body.body) {
376
+ if (statement.type === "ReturnStatement") {
377
+ if (statement.argument && !checkExpression(statement.argument)) {
378
+ isPure = false;
379
+ }
380
+ continue;
381
+ }
382
+ if (statement.type === "VariableDeclaration" && statement.kind === "const") {
383
+ for (const declaration of statement.declarations) {
384
+ if (declaration.id.type !== "Identifier" || !declaration.init || !checkExpression(declaration.init)) {
385
+ isPure = false;
386
+ break;
387
+ }
388
+ stableLocals.add(declaration.id.name);
389
+ }
390
+ if (!isPure) {
391
+ break;
392
+ }
393
+ continue;
394
+ }
395
+ isPure = false;
396
+ break;
397
+ }
398
+ } else {
399
+ isPure = checkExpression(functionNode.body);
400
+ }
401
+ pureFunctionInProgress.delete(functionNode);
402
+ pureFunctionCache.set(functionNode, isPure);
403
+ return isPure;
404
+ };
405
+ const isPureRuntimeExpression = (node, stableLocals) => {
406
+ if (!node || node.type === "PrivateIdentifier") {
407
+ return false;
408
+ }
409
+ switch (node.type) {
410
+ case "Identifier":
411
+ return isStableIdentifier(node.name, stableLocals);
412
+ case "Literal":
413
+ case "FunctionExpression":
414
+ case "ArrowFunctionExpression":
415
+ case "ClassExpression":
416
+ return true;
417
+ case "ThisExpression":
418
+ return stableLocals.has("this");
419
+ case "TemplateLiteral":
420
+ return node.expressions.every((expression) => isPureRuntimeExpression(expression, stableLocals));
421
+ case "UnaryExpression":
422
+ return isPureRuntimeExpression(node.argument, stableLocals);
423
+ case "BinaryExpression":
424
+ case "LogicalExpression":
425
+ return isPureRuntimeExpression(node.left, stableLocals) && isPureRuntimeExpression(node.right, stableLocals);
426
+ case "ConditionalExpression":
427
+ return isPureRuntimeExpression(node.test, stableLocals) && isPureRuntimeExpression(node.consequent, stableLocals) && isPureRuntimeExpression(node.alternate, stableLocals);
428
+ case "ArrayExpression":
429
+ return node.elements.every((element) => {
430
+ if (!element || element.type === "SpreadElement") {
431
+ return false;
432
+ }
433
+ return isPureRuntimeExpression(element, stableLocals);
434
+ });
435
+ case "ObjectExpression":
436
+ return node.properties.every((property) => {
437
+ if (property.type !== "Property" || property.computed || property.kind !== "init") {
438
+ return false;
439
+ }
440
+ if (property.key.type !== "Identifier" && property.key.type !== "Literal") {
441
+ return false;
442
+ }
443
+ if (property.method) {
444
+ return true;
445
+ }
446
+ return isPureRuntimeExpression(property.value, stableLocals);
447
+ });
448
+ case "MemberExpression":
449
+ return isPureRuntimeExpression(node.object, stableLocals) && (!node.computed || isPureRuntimeExpression(node.property, stableLocals));
450
+ case "NewExpression":
451
+ return node.callee.type === "Identifier" && PURE_CONSTRUCTORS.has(node.callee.name) && node.arguments.every((argument) => {
452
+ if (argument.type === "SpreadElement") {
453
+ return false;
454
+ }
455
+ return isPureRuntimeExpression(argument, stableLocals);
456
+ });
457
+ case "CallExpression": {
458
+ const argsArePure = node.arguments.every((argument) => {
459
+ if (argument.type === "SpreadElement") {
460
+ return false;
461
+ }
462
+ return isPureRuntimeExpression(argument, stableLocals);
463
+ });
464
+ if (!argsArePure) {
465
+ return false;
466
+ }
467
+ if (node.callee.type === "Identifier") {
468
+ if (PURE_GLOBAL_FUNCTIONS.has(node.callee.name)) {
469
+ return true;
470
+ }
471
+ const binding = topLevelBindings.get(node.callee.name);
472
+ return binding?.kind === "function" && isPureTopLevelFunction(binding.node);
473
+ }
474
+ if (node.callee.type !== "MemberExpression") {
475
+ return false;
476
+ }
477
+ const memberName = getStaticMemberName(node.callee);
478
+ if (!memberName || !PURE_MEMBER_METHODS.has(memberName)) {
479
+ return false;
480
+ }
481
+ return isPureRuntimeExpression(node.callee.object, stableLocals);
482
+ }
483
+ default:
484
+ return false;
485
+ }
486
+ };
487
+ const isSafeToReorderExpression = (node) => isPureRuntimeExpression(node, new Set);
488
+ const isSafeJSXAttributeValue = (value, scopeNode) => {
489
+ if (value === null) {
490
+ return true;
491
+ }
492
+ if (value.type === "Literal") {
493
+ return true;
494
+ }
495
+ if (value.type !== "JSXExpressionContainer") {
496
+ return false;
497
+ }
498
+ if (value.expression.type === "JSXEmptyExpression") {
499
+ return false;
500
+ }
501
+ return isPureRuntimeExpression(value.expression, getStableLocalsForNode(scopeNode));
502
+ };
281
503
  const isFunctionProperty = (prop) => {
282
504
  const { value } = prop;
283
505
  return Boolean(value) && (value.type === "FunctionExpression" || value.type === "ArrowFunctionExpression" || prop.method === true);
@@ -424,7 +646,7 @@ ${indent}`;
424
646
  if (hasDuplicateNames(keys.map((key) => key.keyName))) {
425
647
  autoFixable = false;
426
648
  }
427
- if (autoFixable && keys.some((key) => key.node.type === "Property" && !isSafeToReorderExpression(key.node.value))) {
649
+ if (autoFixable && keys.some((key) => key.node.type === "Property" && !isPureRuntimeExpression(key.node.value, getStableLocalsForNode(key.node)))) {
428
650
  autoFixable = false;
429
651
  }
430
652
  let fixProvided = false;
@@ -523,7 +745,7 @@ ${indent}`;
523
745
  });
524
746
  return;
525
747
  }
526
- if (attrs.some((attr) => attr.type === "JSXAttribute" && !isSafeJSXAttributeValue(attr.value))) {
748
+ if (attrs.some((attr) => attr.type === "JSXAttribute" && !isSafeJSXAttributeValue(attr.value, attr))) {
527
749
  context.report({
528
750
  messageId: "unsorted",
529
751
  node: attrs[0].type === "JSXAttribute" ? attrs[0].name : attrs[0]
@@ -1140,13 +1362,9 @@ var sortExports = {
1140
1362
  }
1141
1363
  const dependencies = getImmediateDependencyNames(item.node);
1142
1364
  for (const dependency of dependencies) {
1143
- if (!exportNames.has(dependency)) {
1144
- continue;
1145
- }
1146
- const dependencyIndex = sortedIndices.get(dependency);
1147
- if (dependencyIndex !== undefined && itemIndex < dependencyIndex) {
1365
+ const dependencyIndex = exportNames.has(dependency) ? sortedIndices.get(dependency) : undefined;
1366
+ if (dependencyIndex !== undefined && itemIndex < dependencyIndex)
1148
1367
  return true;
1149
- }
1150
1368
  }
1151
1369
  return false;
1152
1370
  });
package/package.json CHANGED
@@ -30,5 +30,5 @@
30
30
  "typecheck": "bun run tsc --noEmit"
31
31
  },
32
32
  "type": "module",
33
- "version": "0.2.3"
33
+ "version": "0.2.5"
34
34
  }
@@ -423,14 +423,14 @@ export const sortExports: TSESLint.RuleModule<MessageIds, Options> = {
423
423
 
424
424
  const dependencies = getImmediateDependencyNames(item.node);
425
425
  for (const dependency of dependencies) {
426
- if (!exportNames.has(dependency)) {
427
- continue;
428
- }
429
-
430
- const dependencyIndex = sortedIndices.get(dependency);
431
- if (dependencyIndex !== undefined && itemIndex < dependencyIndex) {
426
+ const dependencyIndex = exportNames.has(dependency)
427
+ ? sortedIndices.get(dependency)
428
+ : undefined;
429
+ if (
430
+ dependencyIndex !== undefined &&
431
+ itemIndex < dependencyIndex
432
+ )
432
433
  return true;
433
- }
434
434
  }
435
435
 
436
436
  return false;