eslint-plugin-absolute 0.2.8 → 0.3.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 +469 -11
  2. package/package.json +7 -2
package/dist/index.js CHANGED
@@ -185,6 +185,7 @@ var explicitObjectTypes = {
185
185
  };
186
186
 
187
187
  // src/rules/sort-keys-fixable.ts
188
+ import * as ts from "typescript";
188
189
  var SORT_BEFORE = -1;
189
190
  var PURE_CONSTRUCTORS = new Set(["Date"]);
190
191
  var PURE_GLOBAL_IDENTIFIERS = new Set([
@@ -236,6 +237,13 @@ var sortKeysFixable = {
236
237
  const natural = option && typeof option.natural === "boolean" ? option.natural : false;
237
238
  const minKeys = option && typeof option.minKeys === "number" ? option.minKeys : 2;
238
239
  const variablesBeforeFunctions = option && typeof option.variablesBeforeFunctions === "boolean" ? option.variablesBeforeFunctions : false;
240
+ const pureImports = new Set(option && Array.isArray(option.pureImports) ? option.pureImports : []);
241
+ const parserServices = sourceCode.parserServices ?? null;
242
+ const tsProgram = parserServices && "program" in parserServices ? parserServices.program : null;
243
+ const tsChecker = tsProgram ? tsProgram.getTypeChecker() : null;
244
+ const esTreeNodeToTSNodeMap = parserServices && "esTreeNodeToTSNodeMap" in parserServices ? parserServices.esTreeNodeToTSNodeMap : null;
245
+ const importedCallPurityCache = new Map;
246
+ const importedCallPurityInProgress = new Set;
239
247
  const compareKeys = (keyLeft, keyRight) => {
240
248
  let left = keyLeft;
241
249
  let right = keyRight;
@@ -449,6 +457,329 @@ var sortKeysFixable = {
449
457
  pureFunctionCache.set(functionNode, isPure);
450
458
  return isPure;
451
459
  };
460
+ const ASSIGNMENT_OPERATOR_KINDS = new Set([
461
+ ts.SyntaxKind.EqualsToken,
462
+ ts.SyntaxKind.PlusEqualsToken,
463
+ ts.SyntaxKind.MinusEqualsToken,
464
+ ts.SyntaxKind.AsteriskEqualsToken,
465
+ ts.SyntaxKind.AsteriskAsteriskEqualsToken,
466
+ ts.SyntaxKind.SlashEqualsToken,
467
+ ts.SyntaxKind.PercentEqualsToken,
468
+ ts.SyntaxKind.AmpersandEqualsToken,
469
+ ts.SyntaxKind.BarEqualsToken,
470
+ ts.SyntaxKind.CaretEqualsToken,
471
+ ts.SyntaxKind.LessThanLessThanEqualsToken,
472
+ ts.SyntaxKind.GreaterThanGreaterThanEqualsToken,
473
+ ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken,
474
+ ts.SyntaxKind.AmpersandAmpersandEqualsToken,
475
+ ts.SyntaxKind.BarBarEqualsToken,
476
+ ts.SyntaxKind.QuestionQuestionEqualsToken
477
+ ]);
478
+ const addTsBoundIdentifiers = (node, stableLocals) => {
479
+ if (node.kind === ts.SyntaxKind.OmittedExpression) {
480
+ return;
481
+ }
482
+ if (ts.isIdentifier(node)) {
483
+ stableLocals.add(node.text);
484
+ return;
485
+ }
486
+ if (ts.isBindingElement(node)) {
487
+ addTsBoundIdentifiers(node.name, stableLocals);
488
+ return;
489
+ }
490
+ if (ts.isObjectBindingPattern(node) || ts.isArrayBindingPattern(node)) {
491
+ for (const element of node.elements) {
492
+ addTsBoundIdentifiers(element, stableLocals);
493
+ }
494
+ }
495
+ };
496
+ const getCalleeIdentifier = (callee) => {
497
+ if (ts.isIdentifier(callee)) {
498
+ return callee;
499
+ }
500
+ if (ts.isPropertyAccessExpression(callee) && ts.isIdentifier(callee.name)) {
501
+ return callee.name;
502
+ }
503
+ return null;
504
+ };
505
+ const getTsCalleePath = (callee) => {
506
+ if (ts.isParenthesizedExpression(callee)) {
507
+ return getTsCalleePath(callee.expression);
508
+ }
509
+ if (ts.isIdentifier(callee)) {
510
+ return callee.text;
511
+ }
512
+ if (ts.isPropertyAccessExpression(callee) && ts.isIdentifier(callee.name)) {
513
+ const objectPath = getTsCalleePath(callee.expression);
514
+ return objectPath === null ? null : `${objectPath}.${callee.name.text}`;
515
+ }
516
+ return null;
517
+ };
518
+ const getFunctionLikeFromDeclaration = (declaration) => {
519
+ if (ts.isFunctionDeclaration(declaration)) {
520
+ return declaration;
521
+ }
522
+ if (ts.isVariableDeclaration(declaration) && declaration.initializer) {
523
+ const init = declaration.initializer;
524
+ if (ts.isArrowFunction(init) || ts.isFunctionExpression(init)) {
525
+ return init;
526
+ }
527
+ }
528
+ return null;
529
+ };
530
+ const isPureTsIdentifier = (identifier, stableLocals) => {
531
+ const name = identifier.text;
532
+ if (PURE_GLOBAL_IDENTIFIERS.has(name)) {
533
+ return true;
534
+ }
535
+ if (stableLocals.has(name)) {
536
+ return true;
537
+ }
538
+ if (!tsChecker) {
539
+ return false;
540
+ }
541
+ const symbol = tsChecker.getSymbolAtLocation(identifier);
542
+ if (!symbol) {
543
+ return false;
544
+ }
545
+ const target = symbol.flags & ts.SymbolFlags.Alias ? tsChecker.getAliasedSymbol(symbol) : symbol;
546
+ const declaration = target.declarations?.[0];
547
+ if (!declaration) {
548
+ return false;
549
+ }
550
+ if (declaration.getSourceFile().isDeclarationFile) {
551
+ return pureImports.has(name);
552
+ }
553
+ if (ts.isVariableDeclaration(declaration) && declaration.initializer && declaration.parent && ts.isVariableDeclarationList(declaration.parent) && declaration.parent.flags & ts.NodeFlags.Const) {
554
+ return isPureTsExpression(declaration.initializer, new Set);
555
+ }
556
+ if (ts.isFunctionDeclaration(declaration) || ts.isClassDeclaration(declaration)) {
557
+ return true;
558
+ }
559
+ return false;
560
+ };
561
+ const isPureTsBlock = (block, stableLocals) => {
562
+ for (const statement of block.statements) {
563
+ if (ts.isReturnStatement(statement)) {
564
+ if (statement.expression && !isPureTsExpression(statement.expression, stableLocals)) {
565
+ return false;
566
+ }
567
+ continue;
568
+ }
569
+ if (ts.isVariableStatement(statement)) {
570
+ if (!(statement.declarationList.flags & ts.NodeFlags.Const)) {
571
+ return false;
572
+ }
573
+ for (const declaration of statement.declarationList.declarations) {
574
+ if (!ts.isIdentifier(declaration.name) || !declaration.initializer) {
575
+ return false;
576
+ }
577
+ if (!isPureTsExpression(declaration.initializer, stableLocals)) {
578
+ return false;
579
+ }
580
+ stableLocals.add(declaration.name.text);
581
+ }
582
+ continue;
583
+ }
584
+ return false;
585
+ }
586
+ return true;
587
+ };
588
+ const isPureTsFunction = (func) => {
589
+ const cached = importedCallPurityCache.get(func);
590
+ if (cached !== undefined) {
591
+ return cached;
592
+ }
593
+ if (importedCallPurityInProgress.has(func)) {
594
+ return false;
595
+ }
596
+ importedCallPurityInProgress.add(func);
597
+ const stableLocals = new Set;
598
+ for (const parameter of func.parameters) {
599
+ addTsBoundIdentifiers(parameter.name, stableLocals);
600
+ }
601
+ let pure = false;
602
+ if (func.body) {
603
+ pure = ts.isBlock(func.body) ? isPureTsBlock(func.body, stableLocals) : isPureTsExpression(func.body, stableLocals);
604
+ }
605
+ importedCallPurityInProgress.delete(func);
606
+ importedCallPurityCache.set(func, pure);
607
+ return pure;
608
+ };
609
+ const isPureTsCallExpression = (node, stableLocals) => {
610
+ const argsArePure = node.arguments.every((argument) => {
611
+ if (ts.isSpreadElement(argument)) {
612
+ return false;
613
+ }
614
+ return isPureTsExpression(argument, stableLocals);
615
+ });
616
+ if (!argsArePure) {
617
+ return false;
618
+ }
619
+ const calleePath = getTsCalleePath(node.expression);
620
+ if (calleePath !== null && pureImports.has(calleePath)) {
621
+ return true;
622
+ }
623
+ const calleeId = getCalleeIdentifier(node.expression);
624
+ if (!calleeId) {
625
+ return false;
626
+ }
627
+ if (PURE_GLOBAL_FUNCTIONS.has(calleeId.text)) {
628
+ return true;
629
+ }
630
+ if (!tsChecker) {
631
+ return false;
632
+ }
633
+ const symbol = tsChecker.getSymbolAtLocation(calleeId);
634
+ if (!symbol) {
635
+ return false;
636
+ }
637
+ const target = symbol.flags & ts.SymbolFlags.Alias ? tsChecker.getAliasedSymbol(symbol) : symbol;
638
+ const declaration = target.declarations?.[0];
639
+ if (!declaration) {
640
+ return false;
641
+ }
642
+ if (declaration.getSourceFile().isDeclarationFile) {
643
+ return false;
644
+ }
645
+ const funcNode = getFunctionLikeFromDeclaration(declaration);
646
+ if (!funcNode) {
647
+ return false;
648
+ }
649
+ return isPureTsFunction(funcNode);
650
+ };
651
+ const isPureTsExpression = (node, stableLocals) => {
652
+ if (!node) {
653
+ return false;
654
+ }
655
+ if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isTypeAssertionExpression(node) || ts.isNonNullExpression(node) || ts.isSatisfiesExpression(node)) {
656
+ return isPureTsExpression(node.expression, stableLocals);
657
+ }
658
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node) || ts.isNumericLiteral(node) || ts.isBigIntLiteral(node) || node.kind === ts.SyntaxKind.TrueKeyword || node.kind === ts.SyntaxKind.FalseKeyword || node.kind === ts.SyntaxKind.NullKeyword) {
659
+ return true;
660
+ }
661
+ if (ts.isFunctionExpression(node) || ts.isArrowFunction(node) || ts.isClassExpression(node)) {
662
+ return true;
663
+ }
664
+ if (node.kind === ts.SyntaxKind.ThisKeyword) {
665
+ return stableLocals.has("this");
666
+ }
667
+ if (ts.isIdentifier(node)) {
668
+ return isPureTsIdentifier(node, stableLocals);
669
+ }
670
+ if (ts.isTemplateExpression(node)) {
671
+ return node.templateSpans.every((span) => isPureTsExpression(span.expression, stableLocals));
672
+ }
673
+ if (ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) {
674
+ return isPureTsExpression(node.operand, stableLocals);
675
+ }
676
+ if (ts.isBinaryExpression(node)) {
677
+ if (ASSIGNMENT_OPERATOR_KINDS.has(node.operatorToken.kind) || node.operatorToken.kind === ts.SyntaxKind.CommaToken) {
678
+ return false;
679
+ }
680
+ return isPureTsExpression(node.left, stableLocals) && isPureTsExpression(node.right, stableLocals);
681
+ }
682
+ if (ts.isConditionalExpression(node)) {
683
+ return isPureTsExpression(node.condition, stableLocals) && isPureTsExpression(node.whenTrue, stableLocals) && isPureTsExpression(node.whenFalse, stableLocals);
684
+ }
685
+ if (ts.isArrayLiteralExpression(node)) {
686
+ return node.elements.every((element) => {
687
+ if (ts.isSpreadElement(element)) {
688
+ return false;
689
+ }
690
+ if (element.kind === ts.SyntaxKind.OmittedExpression) {
691
+ return false;
692
+ }
693
+ return isPureTsExpression(element, stableLocals);
694
+ });
695
+ }
696
+ if (ts.isObjectLiteralExpression(node)) {
697
+ return node.properties.every((property) => {
698
+ if (ts.isShorthandPropertyAssignment(property)) {
699
+ return isPureTsIdentifier(property.name, stableLocals);
700
+ }
701
+ if (ts.isPropertyAssignment(property)) {
702
+ if (ts.isComputedPropertyName(property.name)) {
703
+ return false;
704
+ }
705
+ return isPureTsExpression(property.initializer, stableLocals);
706
+ }
707
+ if (ts.isMethodDeclaration(property)) {
708
+ return !ts.isComputedPropertyName(property.name);
709
+ }
710
+ return false;
711
+ });
712
+ }
713
+ if (ts.isPropertyAccessExpression(node)) {
714
+ return isPureTsExpression(node.expression, stableLocals);
715
+ }
716
+ if (ts.isElementAccessExpression(node)) {
717
+ return isPureTsExpression(node.expression, stableLocals) && isPureTsExpression(node.argumentExpression, stableLocals);
718
+ }
719
+ if (ts.isNewExpression(node)) {
720
+ if (!ts.isIdentifier(node.expression) || !PURE_CONSTRUCTORS.has(node.expression.text)) {
721
+ return false;
722
+ }
723
+ return node.arguments?.every((argument) => {
724
+ if (ts.isSpreadElement(argument)) {
725
+ return false;
726
+ }
727
+ return isPureTsExpression(argument, stableLocals);
728
+ }) ?? true;
729
+ }
730
+ if (ts.isCallExpression(node)) {
731
+ return isPureTsCallExpression(node, stableLocals);
732
+ }
733
+ return false;
734
+ };
735
+ const getCalleePath = (node) => {
736
+ if (node.type === "Identifier") {
737
+ return node.name;
738
+ }
739
+ if (node.type === "ChainExpression") {
740
+ return getCalleePath(node.expression);
741
+ }
742
+ if (node.type === "MemberExpression" && !node.computed && node.property.type === "Identifier") {
743
+ const objectPath = getCalleePath(node.object);
744
+ return objectPath === null ? null : `${objectPath}.${node.property.name}`;
745
+ }
746
+ return null;
747
+ };
748
+ const isPureImportedCallExpression = (callExpression) => {
749
+ if (!tsChecker || !esTreeNodeToTSNodeMap) {
750
+ return false;
751
+ }
752
+ let calleeEsNode = null;
753
+ if (callExpression.callee.type === "Identifier") {
754
+ calleeEsNode = callExpression.callee;
755
+ } else if (callExpression.callee.type === "MemberExpression" && !callExpression.callee.computed && callExpression.callee.property.type === "Identifier") {
756
+ calleeEsNode = callExpression.callee.property;
757
+ }
758
+ if (!calleeEsNode) {
759
+ return false;
760
+ }
761
+ const tsCallee = esTreeNodeToTSNodeMap.get(calleeEsNode);
762
+ if (!tsCallee || !ts.isIdentifier(tsCallee)) {
763
+ return false;
764
+ }
765
+ const symbol = tsChecker.getSymbolAtLocation(tsCallee);
766
+ if (!symbol) {
767
+ return false;
768
+ }
769
+ const target = symbol.flags & ts.SymbolFlags.Alias ? tsChecker.getAliasedSymbol(symbol) : symbol;
770
+ const declaration = target.declarations?.[0];
771
+ if (!declaration) {
772
+ return false;
773
+ }
774
+ if (declaration.getSourceFile().isDeclarationFile) {
775
+ return false;
776
+ }
777
+ const funcNode = getFunctionLikeFromDeclaration(declaration);
778
+ if (!funcNode) {
779
+ return false;
780
+ }
781
+ return isPureTsFunction(funcNode);
782
+ };
452
783
  const isPureIdentifierCall = (callExpression) => {
453
784
  if (callExpression.callee.type !== "Identifier") {
454
785
  return false;
@@ -456,8 +787,14 @@ var sortKeysFixable = {
456
787
  if (PURE_GLOBAL_FUNCTIONS.has(callExpression.callee.name)) {
457
788
  return true;
458
789
  }
790
+ if (pureImports.has(callExpression.callee.name)) {
791
+ return true;
792
+ }
459
793
  const binding = topLevelBindings.get(callExpression.callee.name);
460
- return binding?.kind === "function" ? isPureTopLevelFunction(binding.node) : false;
794
+ if (binding?.kind === "function") {
795
+ return isPureTopLevelFunction(binding.node);
796
+ }
797
+ return isPureImportedCallExpression(callExpression);
461
798
  };
462
799
  const isPureRuntimeExpression = (node, stableLocals) => {
463
800
  if (!node || node.type === "PrivateIdentifier") {
@@ -473,6 +810,8 @@ var sortKeysFixable = {
473
810
  return true;
474
811
  case "ThisExpression":
475
812
  return stableLocals.has("this");
813
+ case "ChainExpression":
814
+ return isPureRuntimeExpression(node.expression, stableLocals);
476
815
  case "TemplateLiteral":
477
816
  return node.expressions.every((expression) => isPureRuntimeExpression(expression, stableLocals));
478
817
  case "UnaryExpression":
@@ -521,6 +860,10 @@ var sortKeysFixable = {
521
860
  if (!argsArePure) {
522
861
  return false;
523
862
  }
863
+ const calleePath = getCalleePath(node.callee);
864
+ if (calleePath !== null && pureImports.has(calleePath)) {
865
+ return true;
866
+ }
524
867
  if (node.callee.type === "Identifier") {
525
868
  return isPureIdentifierCall(node);
526
869
  }
@@ -528,10 +871,10 @@ var sortKeysFixable = {
528
871
  return false;
529
872
  }
530
873
  const memberName = getStaticMemberName(node.callee);
531
- if (!memberName || !PURE_MEMBER_METHODS.has(memberName)) {
532
- return false;
874
+ if (memberName && PURE_MEMBER_METHODS.has(memberName)) {
875
+ return isPureRuntimeExpression(node.callee.object, stableLocals);
533
876
  }
534
- return isPureRuntimeExpression(node.callee.object, stableLocals);
877
+ return isPureImportedCallExpression(node);
535
878
  }
536
879
  default:
537
880
  return false;
@@ -698,8 +1041,11 @@ ${indent}`;
698
1041
  if (hasDuplicateNames(keys.map((key) => key.keyName))) {
699
1042
  autoFixable = false;
700
1043
  }
701
- if (autoFixable && keys.some((key) => key.node.type === "Property" && !isPureRuntimeExpression(key.node.value, getStableLocalsForNode(key.node)))) {
702
- autoFixable = false;
1044
+ if (autoFixable) {
1045
+ const impureCount = keys.filter((key) => key.node.type === "Property" && !isPureRuntimeExpression(key.node.value, getStableLocalsForNode(key.node))).length;
1046
+ if (impureCount > 1) {
1047
+ autoFixable = false;
1048
+ }
703
1049
  }
704
1050
  let fixProvided = false;
705
1051
  const createReportWithFix = (curr, shouldFix) => {
@@ -797,7 +1143,8 @@ ${indent}`;
797
1143
  });
798
1144
  return;
799
1145
  }
800
- if (attrs.some((attr) => attr.type === "JSXAttribute" && !isSafeJSXAttributeValue(attr.value, attr))) {
1146
+ const impureAttrCount = attrs.filter((attr) => attr.type === "JSXAttribute" && !isSafeJSXAttributeValue(attr.value, attr)).length;
1147
+ if (impureAttrCount > 1) {
801
1148
  context.report({
802
1149
  messageId: "unsorted",
803
1150
  node: attrs[0].type === "JSXAttribute" ? attrs[0].name : attrs[0]
@@ -872,6 +1219,11 @@ ${indent}`;
872
1219
  enum: ["asc", "desc"],
873
1220
  type: "string"
874
1221
  },
1222
+ pureImports: {
1223
+ items: { type: "string" },
1224
+ type: "array",
1225
+ uniqueItems: true
1226
+ },
875
1227
  variablesBeforeFunctions: {
876
1228
  type: "boolean"
877
1229
  }
@@ -2666,16 +3018,121 @@ var noInlinePropTypes = {
2666
3018
  }
2667
3019
  };
2668
3020
 
2669
- // src/rules/no-unnecessary-div.ts
3021
+ // src/rules/no-nondeterministic-render.ts
2670
3022
  import { AST_NODE_TYPES as AST_NODE_TYPES3 } from "@typescript-eslint/utils";
3023
+ var BANNED_TEMPLATE_PATTERN = /\bMath\.random\s*\(|\bDate\.now\s*\(|\bnew\s+Date\s*\(\s*\)|\bcrypto\.randomUUID\s*\(|\bperformance\.now\s*\(/;
3024
+ var isIdentifier2 = (node, name) => node?.type === AST_NODE_TYPES3.Identifier && node.name === name;
3025
+ var isStaticMemberCall = (node, objectName, propertyName) => node.callee.type === AST_NODE_TYPES3.MemberExpression && !node.callee.computed && isIdentifier2(node.callee.object, objectName) && isIdentifier2(node.callee.property, propertyName);
3026
+ var getPropertyName2 = (node) => {
3027
+ const { key } = node;
3028
+ if (key.type === AST_NODE_TYPES3.Identifier)
3029
+ return key.name;
3030
+ if (key.type === AST_NODE_TYPES3.Literal && typeof key.value === "string") {
3031
+ return key.value;
3032
+ }
3033
+ return null;
3034
+ };
3035
+ var isComponentDecorator = (decorator) => {
3036
+ const { expression } = decorator;
3037
+ return expression.type === AST_NODE_TYPES3.CallExpression && isIdentifier2(expression.callee, "Component");
3038
+ };
3039
+ var isAngularComponentClass = (node) => node.type === AST_NODE_TYPES3.ClassDeclaration && (node.decorators ?? []).some(isComponentDecorator);
3040
+ var getTemplateText = (node) => {
3041
+ if (node.type === AST_NODE_TYPES3.Literal && typeof node.value === "string") {
3042
+ return node.value;
3043
+ }
3044
+ if (node.type === AST_NODE_TYPES3.TemplateLiteral) {
3045
+ return node.quasis.map((quasi) => quasi.value.cooked ?? "").join("");
3046
+ }
3047
+ return null;
3048
+ };
3049
+ var getEnclosingAngularComponentClass = (node) => {
3050
+ let current = node.parent;
3051
+ while (current) {
3052
+ if (isAngularComponentClass(current))
3053
+ return current;
3054
+ current = current.parent;
3055
+ }
3056
+ return null;
3057
+ };
3058
+ var getEnclosingPropertyDefinition = (node) => {
3059
+ let current = node.parent;
3060
+ while (current) {
3061
+ if (current.type === AST_NODE_TYPES3.PropertyDefinition) {
3062
+ return current;
3063
+ }
3064
+ if (current.type === AST_NODE_TYPES3.MethodDefinition || current.type === AST_NODE_TYPES3.FunctionDeclaration || current.type === AST_NODE_TYPES3.FunctionExpression || current.type === AST_NODE_TYPES3.ArrowFunctionExpression) {
3065
+ return null;
3066
+ }
3067
+ current = current.parent;
3068
+ }
3069
+ return null;
3070
+ };
3071
+ var isInAngularFieldInitializer = (node) => {
3072
+ const propertyDefinition = getEnclosingPropertyDefinition(node);
3073
+ if (!propertyDefinition || propertyDefinition.value === null)
3074
+ return false;
3075
+ return getEnclosingAngularComponentClass(propertyDefinition) !== null;
3076
+ };
3077
+ var isBannedCall = (node) => isStaticMemberCall(node, "Math", "random") || isStaticMemberCall(node, "Date", "now") || isStaticMemberCall(node, "crypto", "randomUUID") || isStaticMemberCall(node, "performance", "now");
3078
+ var noNondeterministicRender = {
3079
+ create(context) {
3080
+ const reportField = (node) => {
3081
+ if (!isInAngularFieldInitializer(node))
3082
+ return;
3083
+ context.report({
3084
+ messageId: "nondeterministicField",
3085
+ node
3086
+ });
3087
+ };
3088
+ return {
3089
+ CallExpression(node) {
3090
+ if (isBannedCall(node))
3091
+ reportField(node);
3092
+ },
3093
+ "ClassDeclaration > Decorator CallExpression > ObjectExpression > Property"(node) {
3094
+ if (getPropertyName2(node) !== "template")
3095
+ return;
3096
+ const templateText = getTemplateText(node.value);
3097
+ if (templateText === null || !BANNED_TEMPLATE_PATTERN.test(templateText)) {
3098
+ return;
3099
+ }
3100
+ context.report({
3101
+ messageId: "nondeterministicTemplate",
3102
+ node: node.value
3103
+ });
3104
+ },
3105
+ NewExpression(node) {
3106
+ if (isIdentifier2(node.callee, "Date") && node.arguments.length === 0) {
3107
+ reportField(node);
3108
+ }
3109
+ }
3110
+ };
3111
+ },
3112
+ defaultOptions: [],
3113
+ meta: {
3114
+ docs: {
3115
+ description: "Disallow nondeterministic values in Angular render paths that can cause SSR hydration mismatches."
3116
+ },
3117
+ messages: {
3118
+ nondeterministicField: "Do not use nondeterministic values in Angular component field initializers. Inject AbsoluteJS deterministic tokens instead.",
3119
+ nondeterministicTemplate: "Do not use nondeterministic values in Angular templates. Compute a deterministic value before render instead."
3120
+ },
3121
+ schema: [],
3122
+ type: "problem"
3123
+ }
3124
+ };
3125
+
3126
+ // src/rules/no-unnecessary-div.ts
3127
+ import { AST_NODE_TYPES as AST_NODE_TYPES4 } from "@typescript-eslint/utils";
2671
3128
  var noUnnecessaryDiv = {
2672
3129
  create(context) {
2673
3130
  const isDivElement = (node) => {
2674
3131
  const nameNode = node.openingElement.name;
2675
- return nameNode.type === AST_NODE_TYPES3.JSXIdentifier && nameNode.name === "div";
3132
+ return nameNode.type === AST_NODE_TYPES4.JSXIdentifier && nameNode.name === "div";
2676
3133
  };
2677
3134
  const isMeaningfulChild = (child) => {
2678
- if (child.type === AST_NODE_TYPES3.JSXText) {
3135
+ if (child.type === AST_NODE_TYPES4.JSXText) {
2679
3136
  return child.value.trim() !== "";
2680
3137
  }
2681
3138
  return true;
@@ -2694,7 +3151,7 @@ var noUnnecessaryDiv = {
2694
3151
  if (!onlyChild) {
2695
3152
  return;
2696
3153
  }
2697
- if (onlyChild.type === AST_NODE_TYPES3.JSXElement) {
3154
+ if (onlyChild.type === AST_NODE_TYPES4.JSXElement) {
2698
3155
  context.report({
2699
3156
  messageId: "unnecessaryDivWrapper",
2700
3157
  node
@@ -2730,6 +3187,7 @@ var src_default = {
2730
3187
  "no-inline-prop-types": noInlinePropTypes,
2731
3188
  "no-multi-style-objects": noMultiStyleObjects,
2732
3189
  "no-nested-jsx-return": noNestedJSXReturn,
3190
+ "no-nondeterministic-render": noNondeterministicRender,
2733
3191
  "no-or-none-component": noOrNoneComponent,
2734
3192
  "no-transition-cssproperties": noTransitionCSSProperties,
2735
3193
  "no-unnecessary-div": noUnnecessaryDiv,
package/package.json CHANGED
@@ -21,12 +21,17 @@
21
21
  "license": "CC BY-NC 4.0",
22
22
  "main": "./dist/index.js",
23
23
  "name": "eslint-plugin-absolute",
24
+ "peerDependencies": {
25
+ "@typescript-eslint/utils": "^8.0.0",
26
+ "eslint": ">=9",
27
+ "typescript": ">=5.0.0"
28
+ },
24
29
  "repository": {
25
30
  "type": "git",
26
31
  "url": "https://github.com/absolutejs/eslint-plugin-absolute.git"
27
32
  },
28
33
  "scripts": {
29
- "build": "rm -rf dist && bun build src/index.ts --outdir dist --splitting --target=bun --external eslint --external @typescript-eslint/utils",
34
+ "build": "rm -rf dist && bun build src/index.ts --outdir dist --splitting --target=bun --external eslint --external @typescript-eslint/utils --external typescript",
30
35
  "format": "absolutejs prettier --write",
31
36
  "lint": "bun run build && bun run absolutejs eslint",
32
37
  "prune": "ts-prune --error",
@@ -35,5 +40,5 @@
35
40
  "typecheck": "bun run tsc --noEmit"
36
41
  },
37
42
  "type": "module",
38
- "version": "0.2.8"
43
+ "version": "0.3.0"
39
44
  }