eslint-plugin-absolute 0.5.0 → 0.7.0

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.
Files changed (2) hide show
  1. package/dist/index.js +299 -57
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -253,6 +253,8 @@ var PURE_MEMBER_METHODS = new Set([
253
253
  "trim",
254
254
  "trimEnd",
255
255
  "trimStart",
256
+ "exec",
257
+ "test",
256
258
  "toExponential",
257
259
  "toFixed",
258
260
  "toPrecision",
@@ -328,6 +330,18 @@ var PURE_MEMBER_METHODS = new Set([
328
330
  "values",
329
331
  "concat",
330
332
  "join",
333
+ "every",
334
+ "filter",
335
+ "find",
336
+ "findIndex",
337
+ "findLast",
338
+ "findLastIndex",
339
+ "flatMap",
340
+ "map",
341
+ "reduce",
342
+ "reduceRight",
343
+ "some",
344
+ "sort",
331
345
  "parse",
332
346
  "stringify"
333
347
  ]);
@@ -433,21 +447,27 @@ var sortKeysFixable = {
433
447
  });
434
448
  };
435
449
  const addTopLevelBindings = (statement) => {
436
- if (statement.type === "ImportDeclaration") {
437
- addImportBindings(statement);
450
+ let inner = statement;
451
+ if (inner.type === "ExportNamedDeclaration" && inner.declaration) {
452
+ inner = inner.declaration;
453
+ } else if (inner.type === "ExportDefaultDeclaration" && (inner.declaration.type === "FunctionDeclaration" || inner.declaration.type === "ClassDeclaration")) {
454
+ inner = inner.declaration;
455
+ }
456
+ if (inner.type === "ImportDeclaration") {
457
+ addImportBindings(inner);
438
458
  return;
439
459
  }
440
- if (statement.type === "FunctionDeclaration" && statement.id) {
441
- topLevelBindings.set(statement.id.name, {
460
+ if (inner.type === "FunctionDeclaration" && inner.id) {
461
+ topLevelBindings.set(inner.id.name, {
442
462
  kind: "function",
443
- node: statement
463
+ node: inner
444
464
  });
445
465
  return;
446
466
  }
447
- if (statement.type !== "VariableDeclaration" || statement.kind !== "const") {
467
+ if (inner.type !== "VariableDeclaration" || inner.kind !== "const") {
448
468
  return;
449
469
  }
450
- for (const declaration of statement.declarations) {
470
+ for (const declaration of inner.declarations) {
451
471
  addVariableBinding(declaration);
452
472
  }
453
473
  };
@@ -490,10 +510,14 @@ var sortKeysFixable = {
490
510
  };
491
511
  const addAncestorConstBindings = (ancestor, node, stableLocals) => {
492
512
  const addDeclarationBindings = (statement) => {
493
- if (statement.type !== "VariableDeclaration") {
513
+ let inner = statement;
514
+ if (inner.type === "ExportNamedDeclaration" && inner.declaration) {
515
+ inner = inner.declaration;
516
+ }
517
+ if (inner.type !== "VariableDeclaration") {
494
518
  return;
495
519
  }
496
- for (const declaration of statement.declarations) {
520
+ for (const declaration of inner.declarations) {
497
521
  addBoundIdentifiers(declaration.id, stableLocals);
498
522
  }
499
523
  };
@@ -516,11 +540,33 @@ var sortKeysFixable = {
516
540
  }
517
541
  addFunctionParamBindings(ancestor, stableLocals);
518
542
  };
543
+ const addForStatementBindings = (ancestor, stableLocals) => {
544
+ if (ancestor.type !== "ForOfStatement" && ancestor.type !== "ForInStatement" && ancestor.type !== "ForStatement") {
545
+ return;
546
+ }
547
+ const left = ancestor.type === "ForStatement" ? ancestor.init : ancestor.left;
548
+ if (!left)
549
+ return;
550
+ if (left.type === "VariableDeclaration") {
551
+ for (const declaration of left.declarations) {
552
+ addBoundIdentifiers(declaration.id, stableLocals);
553
+ }
554
+ } else {
555
+ addBoundIdentifiers(left, stableLocals);
556
+ }
557
+ };
558
+ const addCatchClauseBindings = (ancestor, stableLocals) => {
559
+ if (ancestor.type !== "CatchClause" || !ancestor.param)
560
+ return;
561
+ addBoundIdentifiers(ancestor.param, stableLocals);
562
+ };
519
563
  const getStableLocalsForNode = (node) => {
520
564
  const stableLocals = new Set;
521
565
  const ancestors = sourceCode.getAncestors(node);
522
566
  for (const ancestor of ancestors) {
523
567
  addFunctionBindingsForAncestor(ancestor, stableLocals);
568
+ addForStatementBindings(ancestor, stableLocals);
569
+ addCatchClauseBindings(ancestor, stableLocals);
524
570
  }
525
571
  for (const ancestor of ancestors) {
526
572
  addAncestorBindingsForNode(ancestor, node, stableLocals);
@@ -556,34 +602,60 @@ var sortKeysFixable = {
556
602
  }
557
603
  return false;
558
604
  };
559
- const isPureConstStatement = (statement, stableLocals, checkExpression) => {
560
- if (statement.kind !== "const") {
561
- return false;
562
- }
605
+ const isPureLocalVariableStatement = (statement, stableLocals) => {
563
606
  for (const declaration of statement.declarations) {
564
- if (declaration.id.type !== "Identifier" || !declaration.init) {
565
- return false;
566
- }
567
- if (!checkExpression(declaration.init)) {
568
- return false;
607
+ if (declaration.init) {
608
+ if (!isPureRuntimeExpression(declaration.init, stableLocals)) {
609
+ return false;
610
+ }
569
611
  }
570
- stableLocals.add(declaration.id.name);
612
+ addBoundIdentifiers(declaration.id, stableLocals);
571
613
  }
572
614
  return true;
573
615
  };
574
- const isPureFunctionStatement = (statement, stableLocals, checkExpression) => {
616
+ const isPureLocalAssignment = (expression, stableLocals) => {
617
+ if (expression.type !== "AssignmentExpression")
618
+ return false;
619
+ if (expression.operator !== "=") {}
620
+ if (expression.left.type !== "Identifier")
621
+ return false;
622
+ if (!stableLocals.has(expression.left.name))
623
+ return false;
624
+ return isPureRuntimeExpression(expression.right, stableLocals);
625
+ };
626
+ const isPureFunctionStatement = (statement, stableLocals) => {
575
627
  if (statement.type === "ReturnStatement") {
576
- return !statement.argument || checkExpression(statement.argument);
628
+ return !statement.argument || isPureRuntimeExpression(statement.argument, stableLocals);
577
629
  }
578
630
  if (statement.type === "VariableDeclaration") {
579
- return isPureConstStatement(statement, stableLocals, checkExpression);
631
+ return isPureLocalVariableStatement(statement, stableLocals);
632
+ }
633
+ if (statement.type === "ExpressionStatement") {
634
+ return isPureLocalAssignment(statement.expression, stableLocals);
635
+ }
636
+ if (statement.type === "IfStatement") {
637
+ if (!isPureRuntimeExpression(statement.test, stableLocals)) {
638
+ return false;
639
+ }
640
+ if (!isPureFunctionBranch(statement.consequent, new Set(stableLocals))) {
641
+ return false;
642
+ }
643
+ return !statement.alternate || isPureFunctionBranch(statement.alternate, new Set(stableLocals));
644
+ }
645
+ if (statement.type === "BlockStatement") {
646
+ return isPureFunctionBody(statement, new Set(stableLocals));
580
647
  }
581
648
  return false;
582
649
  };
583
- const isPureFunctionBody = (body, stableLocals, checkExpression) => {
650
+ const isPureFunctionBranch = (statement, stableLocals) => {
651
+ if (statement.type === "BlockStatement") {
652
+ return isPureFunctionBody(statement, stableLocals);
653
+ }
654
+ return isPureFunctionStatement(statement, stableLocals);
655
+ };
656
+ const isPureFunctionBody = (body, stableLocals) => {
584
657
  for (const statement of body.body) {
585
- const statementIsPure = isPureFunctionStatement(statement, stableLocals, checkExpression);
586
- if (!statementIsPure) {
658
+ if (!isPureFunctionStatement(statement, stableLocals)) {
587
659
  return false;
588
660
  }
589
661
  }
@@ -600,8 +672,7 @@ var sortKeysFixable = {
600
672
  pureFunctionInProgress.add(functionNode);
601
673
  const stableLocals = new Set;
602
674
  addFunctionParamBindings(functionNode, stableLocals);
603
- const checkExpression = (expression) => isPureRuntimeExpression(expression, stableLocals);
604
- const isPure = functionNode.body.type === "BlockStatement" ? isPureFunctionBody(functionNode.body, stableLocals, checkExpression) : checkExpression(functionNode.body);
675
+ const isPure = functionNode.body.type === "BlockStatement" ? isPureFunctionBody(functionNode.body, stableLocals) : isPureRuntimeExpression(functionNode.body, stableLocals);
605
676
  pureFunctionInProgress.delete(functionNode);
606
677
  pureFunctionCache.set(functionNode, isPure);
607
678
  return isPure;
@@ -702,35 +773,69 @@ var sortKeysFixable = {
702
773
  if (ts.isVariableDeclaration(declaration) && declaration.initializer && declaration.parent && ts.isVariableDeclarationList(declaration.parent) && declaration.parent.flags & ts.NodeFlags.Const) {
703
774
  return isPureTsExpression(declaration.initializer, new Set);
704
775
  }
705
- if (ts.isFunctionDeclaration(declaration) || ts.isClassDeclaration(declaration)) {
776
+ if (ts.isVariableDeclaration(declaration) || ts.isFunctionDeclaration(declaration) || ts.isClassDeclaration(declaration) || ts.isParameter(declaration) || ts.isBindingElement(declaration)) {
706
777
  return true;
707
778
  }
708
779
  return false;
709
780
  };
710
- const isPureTsBlock = (block, stableLocals) => {
711
- for (const statement of block.statements) {
712
- if (ts.isReturnStatement(statement)) {
713
- if (statement.expression && !isPureTsExpression(statement.expression, stableLocals)) {
714
- return false;
715
- }
716
- continue;
717
- }
718
- if (ts.isVariableStatement(statement)) {
719
- if (!(statement.declarationList.flags & ts.NodeFlags.Const)) {
720
- return false;
721
- }
722
- for (const declaration of statement.declarationList.declarations) {
723
- if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
724
- return false;
725
- }
781
+ const isPureTsLocalAssignment = (expression, stableLocals) => {
782
+ if (!ts.isBinaryExpression(expression))
783
+ return false;
784
+ if (!ASSIGNMENT_OPERATOR_KINDS.has(expression.operatorToken.kind))
785
+ return false;
786
+ if (!ts.isIdentifier(expression.left))
787
+ return false;
788
+ if (!stableLocals.has(expression.left.text))
789
+ return false;
790
+ return isPureTsExpression(expression.right, stableLocals);
791
+ };
792
+ const isPureTsStatement = (statement, stableLocals) => {
793
+ if (ts.isReturnStatement(statement)) {
794
+ return !statement.expression || isPureTsExpression(statement.expression, stableLocals);
795
+ }
796
+ if (ts.isVariableStatement(statement)) {
797
+ for (const declaration of statement.declarationList.declarations) {
798
+ if (declaration.initializer) {
726
799
  if (!isPureTsExpression(declaration.initializer, stableLocals)) {
727
800
  return false;
728
801
  }
729
- stableLocals.add(declaration.name.text);
730
802
  }
731
- continue;
803
+ addTsBoundIdentifiers(declaration.name, stableLocals);
804
+ }
805
+ return true;
806
+ }
807
+ if (ts.isExpressionStatement(statement)) {
808
+ return isPureTsLocalAssignment(statement.expression, stableLocals);
809
+ }
810
+ if (ts.isIfStatement(statement)) {
811
+ if (!isPureTsExpression(statement.expression, stableLocals)) {
812
+ return false;
813
+ }
814
+ const branchScope = new Set(stableLocals);
815
+ if (!isPureTsStatementOrBlock(statement.thenStatement, branchScope)) {
816
+ return false;
817
+ }
818
+ if (statement.elseStatement && !isPureTsStatementOrBlock(statement.elseStatement, new Set(stableLocals))) {
819
+ return false;
820
+ }
821
+ return true;
822
+ }
823
+ if (ts.isBlock(statement)) {
824
+ return isPureTsBlock(statement, new Set(stableLocals));
825
+ }
826
+ return false;
827
+ };
828
+ const isPureTsStatementOrBlock = (statement, stableLocals) => {
829
+ if (ts.isBlock(statement)) {
830
+ return isPureTsBlock(statement, stableLocals);
831
+ }
832
+ return isPureTsStatement(statement, stableLocals);
833
+ };
834
+ const isPureTsBlock = (block, stableLocals) => {
835
+ for (const statement of block.statements) {
836
+ if (!isPureTsStatement(statement, stableLocals)) {
837
+ return false;
732
838
  }
733
- return false;
734
839
  }
735
840
  return true;
736
841
  };
@@ -758,7 +863,7 @@ var sortKeysFixable = {
758
863
  const isPureTsCallExpression = (node, stableLocals) => {
759
864
  const argsArePure = node.arguments.every((argument) => {
760
865
  if (ts.isSpreadElement(argument)) {
761
- return false;
866
+ return isPureTsExpression(argument.expression, stableLocals);
762
867
  }
763
868
  return isPureTsExpression(argument, stableLocals);
764
869
  });
@@ -769,6 +874,12 @@ var sortKeysFixable = {
769
874
  if (calleePath !== null && pureImports.has(calleePath)) {
770
875
  return true;
771
876
  }
877
+ if (ts.isPropertyAccessExpression(node.expression)) {
878
+ const memberName = node.expression.name.text;
879
+ if (PURE_MEMBER_METHODS.has(memberName)) {
880
+ return isPureTsExpression(node.expression.expression, stableLocals);
881
+ }
882
+ }
772
883
  const calleeId = getCalleeIdentifier(node.expression);
773
884
  if (!calleeId) {
774
885
  return false;
@@ -822,6 +933,9 @@ var sortKeysFixable = {
822
933
  if (ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) {
823
934
  return isPureTsExpression(node.operand, stableLocals);
824
935
  }
936
+ if (ts.isTypeOfExpression(node) || ts.isVoidExpression(node)) {
937
+ return isPureTsExpression(node.expression, stableLocals);
938
+ }
825
939
  if (ts.isBinaryExpression(node)) {
826
940
  if (ASSIGNMENT_OPERATOR_KINDS.has(node.operatorToken.kind) || node.operatorToken.kind === ts.SyntaxKind.CommaToken) {
827
941
  return false;
@@ -834,7 +948,7 @@ var sortKeysFixable = {
834
948
  if (ts.isArrayLiteralExpression(node)) {
835
949
  return node.elements.every((element) => {
836
950
  if (ts.isSpreadElement(element)) {
837
- return false;
951
+ return isPureTsExpression(element.expression, stableLocals);
838
952
  }
839
953
  if (element.kind === ts.SyntaxKind.OmittedExpression) {
840
954
  return false;
@@ -954,14 +1068,28 @@ var sortKeysFixable = {
954
1068
  const type = tsChecker.getTypeAtLocation(tsNode);
955
1069
  return type.symbol ?? type.aliasSymbol;
956
1070
  };
1071
+ const isObjectLikeType = (type) => {
1072
+ if (type.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined | ts.TypeFlags.Void)) {
1073
+ return false;
1074
+ }
1075
+ if (type.flags & ts.TypeFlags.Union) {
1076
+ return type.types.every((member) => {
1077
+ if (member.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined | ts.TypeFlags.Void)) {
1078
+ return true;
1079
+ }
1080
+ return isObjectLikeType(member);
1081
+ });
1082
+ }
1083
+ return Boolean(type.flags & (ts.TypeFlags.Object | ts.TypeFlags.Intersection));
1084
+ };
957
1085
  const callReturnsNominalInstance = (node) => {
958
- const symbol = getCallReturnTypeSymbol(node);
959
- if (!symbol)
1086
+ if (!tsChecker || !esTreeNodeToTSNodeMap)
960
1087
  return false;
961
- const declarations = symbol.declarations;
962
- if (!declarations)
1088
+ const tsNode = esTreeNodeToTSNodeMap.get(node);
1089
+ if (!tsNode)
963
1090
  return false;
964
- return declarations.some((declaration) => ts.isClassDeclaration(declaration) || ts.isClassExpression(declaration) || ts.isInterfaceDeclaration(declaration));
1091
+ const type = tsChecker.getTypeAtLocation(tsNode);
1092
+ return isObjectLikeType(type);
965
1093
  };
966
1094
  const isEncapsulatedFreshExpression = (node, stableLocals) => {
967
1095
  if (!node)
@@ -1051,13 +1179,20 @@ var sortKeysFixable = {
1051
1179
  });
1052
1180
  case "MemberExpression":
1053
1181
  return isPureRuntimeExpression(node.object, stableLocals) && (!node.computed || isPureRuntimeExpression(node.property, stableLocals));
1054
- case "NewExpression":
1055
- return node.callee.type === "Identifier" && PURE_CONSTRUCTORS.has(node.callee.name) && node.arguments.every((argument) => {
1182
+ case "NewExpression": {
1183
+ const argsArePure = node.arguments.every((argument) => {
1056
1184
  if (argument.type === "SpreadElement") {
1057
- return false;
1185
+ return isPureRuntimeExpression(argument.argument, stableLocals);
1058
1186
  }
1059
1187
  return isPureRuntimeExpression(argument, stableLocals);
1060
1188
  });
1189
+ if (!argsArePure)
1190
+ return false;
1191
+ if (node.callee.type === "Identifier" && PURE_CONSTRUCTORS.has(node.callee.name)) {
1192
+ return true;
1193
+ }
1194
+ return true;
1195
+ }
1061
1196
  case "CallExpression": {
1062
1197
  const argsArePure = node.arguments.every((argument) => {
1063
1198
  if (argument.type === "SpreadElement") {
@@ -3390,6 +3525,112 @@ var noUnnecessaryDiv = {
3390
3525
  }
3391
3526
  };
3392
3527
 
3528
+ // src/rules/prefer-inline-exports.ts
3529
+ var isLocalDeclaration = (node) => node.type === "VariableDeclaration" || node.type === "FunctionDeclaration" || node.type === "ClassDeclaration" || node.type === "TSTypeAliasDeclaration" || node.type === "TSInterfaceDeclaration" || node.type === "TSEnumDeclaration";
3530
+ var declarationName = (decl) => {
3531
+ if (decl.type === "VariableDeclaration") {
3532
+ if (decl.declarations.length !== 1)
3533
+ return null;
3534
+ const [first] = decl.declarations;
3535
+ if (!first || first.id.type !== "Identifier")
3536
+ return null;
3537
+ return first.id.name;
3538
+ }
3539
+ if (!decl.id || decl.id.type !== "Identifier")
3540
+ return null;
3541
+ return decl.id.name;
3542
+ };
3543
+ var findOwnDeclaration = (program, name) => {
3544
+ for (const stmt of program.body) {
3545
+ if (stmt.type === "ExportNamedDeclaration" && stmt.declaration && isLocalDeclaration(stmt.declaration) && declarationName(stmt.declaration) === name) {
3546
+ return { alreadyExported: true, decl: stmt.declaration };
3547
+ }
3548
+ if (stmt.type === "ExportDefaultDeclaration" && stmt.declaration.type !== "Identifier" && isLocalDeclaration(stmt.declaration) && declarationName(stmt.declaration) === name) {
3549
+ return {
3550
+ alreadyExported: true,
3551
+ decl: stmt.declaration
3552
+ };
3553
+ }
3554
+ if (isLocalDeclaration(stmt) && declarationName(stmt) === name) {
3555
+ return { alreadyExported: false, decl: stmt };
3556
+ }
3557
+ }
3558
+ return null;
3559
+ };
3560
+ var preferInlineExports = {
3561
+ create(context) {
3562
+ const { sourceCode } = context;
3563
+ const program = sourceCode.ast;
3564
+ return {
3565
+ ExportNamedDeclaration(node) {
3566
+ if (node.source)
3567
+ return;
3568
+ if (node.declaration)
3569
+ return;
3570
+ if (node.specifiers.length === 0)
3571
+ return;
3572
+ if (node.exportKind === "type")
3573
+ return;
3574
+ const fixable = [];
3575
+ for (const spec of node.specifiers) {
3576
+ if (spec.type !== "ExportSpecifier")
3577
+ continue;
3578
+ if (spec.local.type !== "Identifier")
3579
+ continue;
3580
+ if (spec.exported.type !== "Identifier")
3581
+ continue;
3582
+ if (spec.local.name !== spec.exported.name)
3583
+ continue;
3584
+ if (spec.exportKind === "type")
3585
+ continue;
3586
+ const found = findOwnDeclaration(program, spec.local.name);
3587
+ if (!found)
3588
+ continue;
3589
+ if (found.alreadyExported)
3590
+ continue;
3591
+ fixable.push({ decl: found.decl, spec });
3592
+ }
3593
+ if (fixable.length === 0)
3594
+ return;
3595
+ const allSpecsAreFixable = fixable.length === node.specifiers.length;
3596
+ const names = fixable.map(({ spec }) => spec.local.type === "Identifier" ? spec.local.name : "").filter((name) => name.length > 0);
3597
+ context.report({
3598
+ data: { names: names.join(", ") },
3599
+ fix(fixer) {
3600
+ const fixes = [];
3601
+ for (const { decl } of fixable) {
3602
+ const [declStart] = decl.range;
3603
+ fixes.push(fixer.insertTextBeforeRange([declStart, declStart], "export "));
3604
+ }
3605
+ if (allSpecsAreFixable) {
3606
+ fixes.push(fixer.remove(node));
3607
+ } else {
3608
+ const survivors = node.specifiers.filter((spec) => !fixable.some((entry) => entry.spec === spec));
3609
+ const replacement = `export { ${survivors.map((spec) => sourceCode.getText(spec)).join(", ")} };`;
3610
+ fixes.push(fixer.replaceText(node, replacement));
3611
+ }
3612
+ return fixes;
3613
+ },
3614
+ messageId: "preferInline",
3615
+ node
3616
+ });
3617
+ }
3618
+ };
3619
+ },
3620
+ defaultOptions: [],
3621
+ meta: {
3622
+ docs: {
3623
+ description: "Prefer inlining `export` at a declaration site over a trailing `export { name }` statement when the name is a local declaration."
3624
+ },
3625
+ fixable: "code",
3626
+ messages: {
3627
+ preferInline: "Inline `export` at the declaration of `{{names}}` instead of re-exporting at the bottom of the file."
3628
+ },
3629
+ schema: [],
3630
+ type: "suggestion"
3631
+ }
3632
+ };
3633
+
3393
3634
  // src/index.ts
3394
3635
  var src_default = {
3395
3636
  rules: {
@@ -3410,6 +3651,7 @@ var src_default = {
3410
3651
  "no-unnecessary-div": noUnnecessaryDiv,
3411
3652
  "no-unnecessary-key": noUnnecessaryKey,
3412
3653
  "no-useless-function": noUselessFunction,
3654
+ "prefer-inline-exports": preferInlineExports,
3413
3655
  "seperate-style-files": seperateStyleFiles,
3414
3656
  "sort-exports": sortExports,
3415
3657
  "sort-keys-fixable": sortKeysFixable,
package/package.json CHANGED
@@ -40,5 +40,5 @@
40
40
  "typecheck": "bun run tsc --noEmit"
41
41
  },
42
42
  "type": "module",
43
- "version": "0.5.0"
43
+ "version": "0.7.0"
44
44
  }