@blumintinc/eslint-plugin-blumint 1.20.197 → 1.20.199

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 (78) hide show
  1. package/lib/index.js +1 -1
  2. package/lib/rules/array-methods-this-context.js +1 -1
  3. package/lib/rules/consistent-callback-naming.js +1 -1
  4. package/lib/rules/enforce-assert-safe-object-key.js +10 -8
  5. package/lib/rules/enforce-callback-memo.js +1 -1
  6. package/lib/rules/enforce-centralized-mock-firestore.js +1 -1
  7. package/lib/rules/enforce-date-ttime.js +1 -2
  8. package/lib/rules/enforce-early-destructuring.js +2 -4
  9. package/lib/rules/enforce-empty-object-check.js +92 -14
  10. package/lib/rules/enforce-exported-function-types.js +5 -3
  11. package/lib/rules/enforce-f-extension-for-entry-points.js +1 -1
  12. package/lib/rules/enforce-firestore-doc-ref-generic.js +4 -2
  13. package/lib/rules/enforce-firestore-facade.js +1 -1
  14. package/lib/rules/enforce-firestore-set-merge.js +1 -1
  15. package/lib/rules/enforce-global-constants.js +1 -1
  16. package/lib/rules/enforce-memoize-async.js +3 -3
  17. package/lib/rules/enforce-microdiff.js +1 -1
  18. package/lib/rules/enforce-object-literal-as-const.js +1 -1
  19. package/lib/rules/enforce-props-argument-name.js +1 -1
  20. package/lib/rules/enforce-safe-stringify.js +3 -2
  21. package/lib/rules/enforce-stable-hash-spread-props.js +1 -2
  22. package/lib/rules/enforce-typescript-markdown-code-blocks.js +1 -1
  23. package/lib/rules/enforce-verb-noun-naming.js +10 -4
  24. package/lib/rules/ensure-pointer-events-none.js +1 -1
  25. package/lib/rules/fast-deep-equal-over-microdiff.js +1 -1
  26. package/lib/rules/global-const-style.js +1 -1
  27. package/lib/rules/key-only-outermost-element.js +1 -1
  28. package/lib/rules/logical-top-to-bottom-grouping.js +1 -3
  29. package/lib/rules/memo-nested-react-components.js +1 -1
  30. package/lib/rules/no-async-foreach.js +2 -12
  31. package/lib/rules/no-curly-brackets-around-commented-properties.js +1 -1
  32. package/lib/rules/no-entire-object-hook-deps.js +55 -13
  33. package/lib/rules/no-firestore-object-arrays.js +1 -1
  34. package/lib/rules/no-inline-component-prop.js +1 -1
  35. package/lib/rules/no-jsx-whitespace-literal.js +1 -2
  36. package/lib/rules/no-margin-properties.js +1 -1
  37. package/lib/rules/no-object-values-on-strings.js +1 -1
  38. package/lib/rules/no-passthrough-getters.js +1 -1
  39. package/lib/rules/no-redundant-annotation-assertion.js +1 -2
  40. package/lib/rules/no-redundant-usecallback-wrapper.js +1 -1
  41. package/lib/rules/no-stablehash-react-nodes.js +1 -2
  42. package/lib/rules/no-stale-state-across-await.js +1 -1
  43. package/lib/rules/no-unnecessary-verb-suffix.js +3 -3
  44. package/lib/rules/no-unused-usestate.js +1 -1
  45. package/lib/rules/no-useless-fragment.js +1 -1
  46. package/lib/rules/no-useless-usememo-primitives.js +1 -1
  47. package/lib/rules/no-uuidv4-base62-as-key.js +1 -1
  48. package/lib/rules/optimize-object-boolean-conditions.js +1 -1
  49. package/lib/rules/parallelize-async-operations.js +1 -1
  50. package/lib/rules/prefer-block-comments-for-declarations.js +1 -1
  51. package/lib/rules/prefer-clone-deep.js +1 -1
  52. package/lib/rules/prefer-fragment-component.js +1 -1
  53. package/lib/rules/prefer-global-router-state-key.js +1 -1
  54. package/lib/rules/prefer-next-dynamic.js +1 -1
  55. package/lib/rules/prefer-params-over-parent-id.js +1 -1
  56. package/lib/rules/prefer-settings-object.js +1 -1
  57. package/lib/rules/prefer-type-over-interface.js +1 -1
  58. package/lib/rules/prefer-url-tostring-over-tojson.js +1 -1
  59. package/lib/rules/prefer-use-deep-compare-memo.js +27 -6
  60. package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +1 -1
  61. package/lib/rules/prefer-utility-function-over-private-static.js +1 -1
  62. package/lib/rules/prevent-children-clobber.js +1 -2
  63. package/lib/rules/react-usememo-should-be-component.js +2 -2
  64. package/lib/rules/require-hooks-default-params.js +1 -1
  65. package/lib/rules/require-https-error-cause.js +1 -1
  66. package/lib/rules/require-memo.js +4 -4
  67. package/lib/rules/require-migration-script-metadata.js +1 -1
  68. package/lib/rules/use-latest-callback.js +1 -1
  69. package/lib/rules/warn-https-error-message-user-friendly.js +1 -1
  70. package/lib/utils/ASTHelpers.d.ts +5 -2
  71. package/lib/utils/ASTHelpers.js +7 -4
  72. package/lib/utils/composedFixConfig.d.ts +31 -0
  73. package/lib/utils/composedFixConfig.js +33 -1
  74. package/lib/utils/disableDirectives.js +1 -1
  75. package/lib/utils/syntheticRuleOptions.d.ts +124 -0
  76. package/lib/utils/syntheticRuleOptions.js +287 -0
  77. package/package.json +1 -1
  78. package/release-manifest.json +36 -0
package/lib/index.js CHANGED
@@ -223,7 +223,7 @@ function noFrontendImportsFromFunctionsPatterns(pattern) {
223
223
  module.exports = {
224
224
  meta: {
225
225
  name: '@blumintinc/eslint-plugin-blumint',
226
- version: '1.20.197',
226
+ version: '1.20.199',
227
227
  },
228
228
  parseOptions: {
229
229
  ecmaVersion: 2020,
@@ -12,7 +12,7 @@ exports.ARRAY_METHODS = [
12
12
  ];
13
13
  exports.arrayMethodsThisContext = (0, createRule_1.createRule)({
14
14
  create(context) {
15
- const sourceCode = context.sourceCode;
15
+ const sourceCode = context.getSourceCode();
16
16
  return {
17
17
  CallExpression(node) {
18
18
  // Array method called with a class method reference
@@ -883,7 +883,7 @@ module.exports = (0, createRule_1.createRule)({
883
883
  }
884
884
  }
885
885
  // Get references from global scope
886
- const sourceCode = context.sourceCode;
886
+ const sourceCode = context.getSourceCode();
887
887
  if (sourceCode.scopeManager?.globalScope) {
888
888
  const globalVar = sourceCode.scopeManager.globalScope.variables.find((v) => v.name === functionName);
889
889
  if (globalVar) {
@@ -994,7 +994,8 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
994
994
  */
995
995
  const addAssertSafeImport = (fixer) => {
996
996
  const importStatement = `import { assertSafe } from '${computeImportSpecifier()}';\n`;
997
- return (0, importInsertion_1.insertAtImportAnchor)(context.sourceCode, fixer, (0, importInsertion_1.importInsertionAnchor)(context.sourceCode), importStatement);
997
+ const sourceCode = context.getSourceCode();
998
+ return (0, importInsertion_1.insertAtImportAnchor)(sourceCode, fixer, (0, importInsertion_1.importInsertionAnchor)(sourceCode), importStatement);
998
999
  };
999
1000
  /**
1000
1001
  * The wrap of the key, emitted over the whole access the key belongs to
@@ -1022,7 +1023,7 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
1022
1023
  * grouping parentheses `dropsGroupingParens` settles.
1023
1024
  */
1024
1025
  const planWrap = (node, argText) => {
1025
- const { sourceCode } = context;
1026
+ const sourceCode = context.getSourceCode();
1026
1027
  const replacement = `${ASSERT_SAFE_NAME}(${argText})`;
1027
1028
  const parens = groupingParensAround(sourceCode, node);
1028
1029
  const outermost = parens[parens.length - 1];
@@ -1068,7 +1069,7 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
1068
1069
  * meaning.
1069
1070
  */
1070
1071
  const planArrowBreak = (node, wrap) => {
1071
- const { sourceCode } = context;
1072
+ const sourceCode = context.getSourceCode();
1072
1073
  const { line } = node.loc.start;
1073
1074
  const original = sourceCode.lines[line - 1] ?? '';
1074
1075
  const delta = wrap.text.length - (wrap.range[1] - wrap.range[0]);
@@ -1140,7 +1141,7 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
1140
1141
  * which is what shipped before the width was measured at all.
1141
1142
  */
1142
1143
  const planMemberBreak = (node, wrap, argText) => {
1143
- const { sourceCode } = context;
1144
+ const sourceCode = context.getSourceCode();
1144
1145
  const { text } = sourceCode;
1145
1146
  const { line } = node.loc.start;
1146
1147
  const original = sourceCode.lines[line - 1] ?? '';
@@ -1231,7 +1232,7 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
1231
1232
  */
1232
1233
  const createFixes = (fixer, node, argText) => {
1233
1234
  const fixes = [];
1234
- const carriesImport = !importClaimed && !importsAssertSafe(context.sourceCode.ast);
1235
+ const carriesImport = !importClaimed && !importsAssertSafe(context.getSourceCode().ast);
1235
1236
  if (carriesImport) {
1236
1237
  fixes.push(addAssertSafeImport(fixer));
1237
1238
  importClaimed = true;
@@ -1313,7 +1314,7 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
1313
1314
  * substitution behave like every other template: `` `${a}${b}` `` has
1314
1315
  * always been wrapped whole.
1315
1316
  */
1316
- const reportWrittenKey = (written) => reportUseAssertSafe(written, context.sourceCode.getText(written));
1317
+ const reportWrittenKey = (written) => reportUseAssertSafe(written, context.getSourceCode().getText(written));
1317
1318
  /**
1318
1319
  * Returns true when the identifier was initialized directly from an
1319
1320
  * assertSafe(...) call, e.g. `const safeKey = assertSafe(rawKey)`, with any
@@ -1521,12 +1522,13 @@ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
1521
1522
  */
1522
1523
  const recordKeyCovers = (keyAnnotation, recordKeyType, anchor) => {
1523
1524
  const namesNoPrototypeField = (domain) => ![...domain].some((value) => PROTOTYPE_SURFACE_NAMES.has(value));
1525
+ const sourceCode = context.getSourceCode();
1524
1526
  if (keyAnnotation.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
1525
1527
  recordKeyType.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
1526
1528
  !keyAnnotation.typeParameters &&
1527
1529
  !recordKeyType.typeParameters &&
1528
- context.sourceCode.getText(keyAnnotation.typeName) ===
1529
- context.sourceCode.getText(recordKeyType.typeName)) {
1530
+ sourceCode.getText(keyAnnotation.typeName) ===
1531
+ sourceCode.getText(recordKeyType.typeName)) {
1530
1532
  const domain = keyDomainOf(keyAnnotation, anchor, new Set());
1531
1533
  if (domain === 'open') {
1532
1534
  return false;
@@ -118,7 +118,7 @@ exports.default = (0, createRule_1.createRule)({
118
118
  return [];
119
119
  }
120
120
  function referencesParentScopeVariables(functionNode, parentParams) {
121
- const scopeManager = context.sourceCode.scopeManager;
121
+ const scopeManager = context.getSourceCode().scopeManager;
122
122
  if (!scopeManager)
123
123
  return false;
124
124
  const scope = scopeManager.acquire(functionNode);
@@ -359,7 +359,7 @@ exports.enforceCentralizedMockFirestore = (0, createRule_1.createRule)({
359
359
  },
360
360
  'Program:exit'() {
361
361
  if (mockFirestoreNodes.size > 0) {
362
- const sourceCode = context.sourceCode;
362
+ const sourceCode = context.getSourceCode();
363
363
  // Report only once for the entire file
364
364
  context.report({
365
365
  node: Array.from(mockFirestoreNodes)[0],
@@ -45,8 +45,7 @@ exports.enforceDateTTime = (0, createRule_1.createRule)({
45
45
  },
46
46
  defaultOptions: [],
47
47
  create(context) {
48
- const sourceCode = context
49
- .sourceCode ?? context.getSourceCode();
48
+ const sourceCode = context.getSourceCode();
50
49
  const parserServices = sourceCode?.parserServices ?? context.parserServices;
51
50
  const checker = parserServices?.program?.getTypeChecker();
52
51
  if (!parserServices || !checker) {
@@ -1207,8 +1207,7 @@ function validateHookForTransform(node, context) {
1207
1207
  : null;
1208
1208
  if (!depsArray)
1209
1209
  return null;
1210
- const sourceCode = context.sourceCode ??
1211
- context.getSourceCode();
1210
+ const sourceCode = context.getSourceCode();
1212
1211
  const depTexts = dependencyElements(depsArray, sourceCode);
1213
1212
  const depTextSet = new Set(depTexts);
1214
1213
  const scope = context.getScope();
@@ -1239,8 +1238,7 @@ exports.enforceEarlyDestructuring = (0, createRule_1.createRule)({
1239
1238
  },
1240
1239
  defaultOptions: [],
1241
1240
  create(context) {
1242
- const sourceCode = context.sourceCode ??
1243
- context.getSourceCode();
1241
+ const sourceCode = context.getSourceCode();
1244
1242
  const visitorKeys = sourceCode
1245
1243
  .visitorKeys ??
1246
1244
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -27,6 +27,7 @@ exports.enforceEmptyObjectCheck = void 0;
27
27
  const utils_1 = require("@typescript-eslint/utils");
28
28
  const ts = __importStar(require("typescript"));
29
29
  const createRule_1 = require("../utils/createRule");
30
+ const ASTHelpers_1 = require("../utils/ASTHelpers");
30
31
  const DEFAULT_OBJECT_SUFFIXES = [
31
32
  'Config',
32
33
  'Configs',
@@ -776,8 +777,8 @@ function readSegmentGap(text, paren) {
776
777
  * on. Splitting the span at the operator token keeps whatever the author wrote
777
778
  * around each operand.
778
779
  */
779
- function buildDoc(node, segmentStart, segmentEnd, context) {
780
- const { source } = context;
780
+ function buildDoc(node, segmentStart, segmentEnd, docContext) {
781
+ const { source } = docContext;
781
782
  const before = readSegmentGap(source.slice(segmentStart, node.range[0]), '(');
782
783
  const after = readSegmentGap(source.slice(node.range[1], segmentEnd), ')');
783
784
  if (!before || !after) {
@@ -793,7 +794,7 @@ function buildDoc(node, segmentStart, segmentEnd, context) {
793
794
  * the segment carried have just been accounted for, and handing them down
794
795
  * again makes the first child look unbalanced and rejects the whole tree.
795
796
  */
796
- const core = buildCoreDoc(node, context);
797
+ const core = buildCoreDoc(node, docContext);
797
798
  if (!core) {
798
799
  return null;
799
800
  }
@@ -810,11 +811,11 @@ function buildDoc(node, segmentStart, segmentEnd, context) {
810
811
  }
811
812
  return doc;
812
813
  }
813
- function buildCoreDoc(node, context) {
814
- const { sourceCode, source, targetRange } = context;
814
+ function buildCoreDoc(node, docContext) {
815
+ const { sourceCode, source, targetRange } = docContext;
815
816
  const [segmentStart, segmentEnd] = node.range;
816
817
  if (node.range[0] === targetRange[0] && node.range[1] === targetRange[1]) {
817
- return context.targetDoc;
818
+ return docContext.targetDoc;
818
819
  }
819
820
  if (node.type === utils_1.AST_NODE_TYPES.LogicalExpression) {
820
821
  const operatorToken = sourceCode.getTokenAfter(node.left, {
@@ -824,8 +825,8 @@ function buildCoreDoc(node, context) {
824
825
  if (!operatorToken) {
825
826
  return null;
826
827
  }
827
- const left = buildDoc(node.left, segmentStart, operatorToken.range[0], context);
828
- const right = buildDoc(node.right, operatorToken.range[1], segmentEnd, context);
828
+ const left = buildDoc(node.left, segmentStart, operatorToken.range[0], docContext);
829
+ const right = buildDoc(node.right, operatorToken.range[1], segmentEnd, docContext);
829
830
  if (!left || !right) {
830
831
  return null;
831
832
  }
@@ -841,9 +842,9 @@ function buildCoreDoc(node, context) {
841
842
  if (!questionToken || !colonToken) {
842
843
  return null;
843
844
  }
844
- const test = buildDoc(node.test, segmentStart, questionToken.range[0], context);
845
- const consequent = buildDoc(node.consequent, questionToken.range[1], colonToken.range[0], context);
846
- const alternate = buildDoc(node.alternate, colonToken.range[1], segmentEnd, context);
845
+ const test = buildDoc(node.test, segmentStart, questionToken.range[0], docContext);
846
+ const consequent = buildDoc(node.consequent, questionToken.range[1], colonToken.range[0], docContext);
847
+ const alternate = buildDoc(node.alternate, colonToken.range[1], segmentEnd, docContext);
847
848
  if (!test || !consequent || !alternate) {
848
849
  return null;
849
850
  }
@@ -949,13 +950,13 @@ function planWidenedFix(input) {
949
950
  { kind: 'leaf', text: `Object.keys(${identifierText}).length === 0` },
950
951
  ],
951
952
  };
952
- const context = {
953
+ const docContext = {
953
954
  sourceCode,
954
955
  source,
955
956
  targetRange: target.range,
956
957
  targetDoc: needsParentheses ? { kind: 'group', inner: guard } : guard,
957
958
  };
958
- const doc = buildDoc(root, root.range[0], root.range[1], context);
959
+ const doc = buildDoc(root, root.range[0], root.range[1], docContext);
959
960
  if (!doc) {
960
961
  return null;
961
962
  }
@@ -974,7 +975,7 @@ function planWidenedFix(input) {
974
975
  const spliced = source.slice(root.range[0], target.range[0]) +
975
976
  (needsParentheses
976
977
  ? `(${flattenDoc(guard)})`
977
- : flattenDoc(context.targetDoc)) +
978
+ : flattenDoc(docContext.targetDoc)) +
978
979
  source.slice(target.range[1], root.range[1]);
979
980
  const flat = flattenDoc(doc);
980
981
  if (flat.includes('\n') ||
@@ -1341,6 +1342,68 @@ exports.enforceEmptyObjectCheck = (0, createRule_1.createRule)({
1341
1342
  ]);
1342
1343
  const processedExpressions = new WeakSet();
1343
1344
  const processedNegations = new WeakSet();
1345
+ /** The binding a value expression ultimately reads from. */
1346
+ function rootIdentifierOf(node) {
1347
+ switch (node.type) {
1348
+ case utils_1.AST_NODE_TYPES.Identifier:
1349
+ return node;
1350
+ case utils_1.AST_NODE_TYPES.CallExpression:
1351
+ case utils_1.AST_NODE_TYPES.NewExpression:
1352
+ return rootIdentifierOf(node.callee);
1353
+ case utils_1.AST_NODE_TYPES.MemberExpression:
1354
+ return rootIdentifierOf(node.object);
1355
+ case utils_1.AST_NODE_TYPES.AwaitExpression:
1356
+ return rootIdentifierOf(node.argument);
1357
+ case utils_1.AST_NODE_TYPES.ChainExpression:
1358
+ return rootIdentifierOf(node.expression);
1359
+ case utils_1.AST_NODE_TYPES.TSNonNullExpression:
1360
+ case utils_1.AST_NODE_TYPES.TSAsExpression:
1361
+ return rootIdentifierOf(node.expression);
1362
+ default:
1363
+ return null;
1364
+ }
1365
+ }
1366
+ function variableFor(identifier) {
1367
+ let scope = ASTHelpers_1.ASTHelpers.getScope(context, identifier);
1368
+ while (scope) {
1369
+ const found = scope.variables.find((candidate) => candidate.name === identifier.name);
1370
+ if (found) {
1371
+ return found;
1372
+ }
1373
+ scope = scope.upper;
1374
+ }
1375
+ return null;
1376
+ }
1377
+ /**
1378
+ * Whether this value originates in an IMPORTED binding.
1379
+ *
1380
+ * Consulted ONLY once the checker has returned `unknown`, which is what
1381
+ * makes it safe: in a file whose imports resolve, an imported callee
1382
+ * yields a real type and this is never reached. Reaching it means the
1383
+ * symbol came back `any` because the module did not resolve — a
1384
+ * program-completeness failure rather than evidence the value can be
1385
+ * `{}` (#2252).
1386
+ */
1387
+ function tracesToImport(identifier, seen = new Set()) {
1388
+ const variable = variableFor(identifier);
1389
+ if (!variable || seen.has(variable)) {
1390
+ return false;
1391
+ }
1392
+ seen.add(variable);
1393
+ for (const def of variable.defs) {
1394
+ if (def.type === 'ImportBinding') {
1395
+ return true;
1396
+ }
1397
+ if (def.node.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
1398
+ def.node.init) {
1399
+ const root = rootIdentifierOf(def.node.init);
1400
+ if (root && tracesToImport(root, seen)) {
1401
+ return true;
1402
+ }
1403
+ }
1404
+ }
1405
+ return false;
1406
+ }
1344
1407
  function isLikelyObject(identifier) {
1345
1408
  if (checker && parserServices?.esTreeNodeToTSNodeMap) {
1346
1409
  try {
@@ -1353,6 +1416,21 @@ exports.enforceEmptyObjectCheck = (0, createRule_1.createRule)({
1353
1416
  if (analysis === 'non-object') {
1354
1417
  return false;
1355
1418
  }
1419
+ /**
1420
+ * The checker looked and could not tell. Falling straight through
1421
+ * to the naming heuristic is what made a file EXCLUDED from the
1422
+ * `parserOptions.project` program report on byte-identical code its
1423
+ * in-program twin is exempt from: the suffix says `config`, so
1424
+ * absence of evidence became evidence the value can be `{}`.
1425
+ *
1426
+ * The heuristic is still the right answer for a value with no
1427
+ * declaration to resolve — removing it outright silences 52 of this
1428
+ * rule's own 92 fixtures — so only the unresolved-IMPORT case is
1429
+ * carved out (#2252).
1430
+ */
1431
+ if (tracesToImport(identifier)) {
1432
+ return false;
1433
+ }
1356
1434
  }
1357
1435
  catch {
1358
1436
  // TypeScript parser services can throw when AST-to-TS node mapping fails; fall back to naming heuristic so linting does not crash.
@@ -484,7 +484,9 @@ exports.enforceExportedFunctionTypes = (0, createRule_1.createRule)({
484
484
  * error.
485
485
  */
486
486
  function findExportableTypeDeclaration(typeName) {
487
- const declarations = context.sourceCode.ast.body.filter((statement) => (statement.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration ||
487
+ const declarations = context
488
+ .getSourceCode()
489
+ .ast.body.filter((statement) => (statement.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration ||
488
490
  statement.type === utils_1.AST_NODE_TYPES.TSInterfaceDeclaration) &&
489
491
  statement.id.name === typeName);
490
492
  return declarations.length === 1 ? declarations[0] : undefined;
@@ -573,7 +575,7 @@ exports.enforceExportedFunctionTypes = (0, createRule_1.createRule)({
573
575
  * so an unresolved name leaves the component unchecked rather than guessing.
574
576
  */
575
577
  function findModuleScopeDeclaration(name) {
576
- for (const statement of context.sourceCode.ast.body) {
578
+ for (const statement of context.getSourceCode().ast.body) {
577
579
  const declaration = statement.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration &&
578
580
  statement.declaration
579
581
  ? statement.declaration
@@ -602,7 +604,7 @@ exports.enforceExportedFunctionTypes = (0, createRule_1.createRule)({
602
604
  if (isBuiltInType(typeName)) {
603
605
  return true;
604
606
  }
605
- const sourceCode = context.sourceCode;
607
+ const sourceCode = context.getSourceCode();
606
608
  const program = sourceCode.ast;
607
609
  // Check for imported types first - if found, return true immediately
608
610
  // since imported types are already available to consumers
@@ -81,7 +81,7 @@ function isNodeDefiningEntryPoint(node, entryPoints) {
81
81
  * for the target node.
82
82
  */
83
83
  function getScopeForNode(context, node) {
84
- const sourceCode = context.sourceCode ?? context.getSourceCode();
84
+ const sourceCode = context.getSourceCode();
85
85
  if (isEslint9OrLater(sourceCode)) {
86
86
  return getEslint9Scope(sourceCode, node);
87
87
  }
@@ -994,7 +994,9 @@ exports.enforceFirestoreDocRefGeneric = (0, createRule_1.createRule)({
994
994
  if (fileImportsProjectModule !== undefined) {
995
995
  return fileImportsProjectModule;
996
996
  }
997
- const found = context.sourceCode.ast.body.some((statement) => statement.type === utils_1.AST_NODE_TYPES.ImportDeclaration &&
997
+ const found = context
998
+ .getSourceCode()
999
+ .ast.body.some((statement) => statement.type === utils_1.AST_NODE_TYPES.ImportDeclaration &&
998
1000
  isProjectModuleSpecifier(statement.source.value));
999
1001
  fileImportsProjectModule = found;
1000
1002
  return found;
@@ -1011,7 +1013,7 @@ exports.enforceFirestoreDocRefGeneric = (0, createRule_1.createRule)({
1011
1013
  return rulesUnitTestingLocals;
1012
1014
  }
1013
1015
  const locals = new Set();
1014
- for (const statement of context.sourceCode.ast.body) {
1016
+ for (const statement of context.getSourceCode().ast.body) {
1015
1017
  if (statement.type === utils_1.AST_NODE_TYPES.ImportDeclaration &&
1016
1018
  statement.source.value === RULES_UNIT_TESTING_MODULE) {
1017
1019
  for (const specifier of statement.specifiers) {
@@ -159,7 +159,7 @@ exports.enforceFirestoreFacade = (0, createRule_1.createRule)({
159
159
  const nonClientImportedLocals = new Set();
160
160
  // Batches/transactions proven to originate from the client SDK.
161
161
  const clientFirestoreVariables = new Set();
162
- const sourceCode = context.sourceCode;
162
+ const sourceCode = context.getSourceCode();
163
163
  const clearFirestoreTrackingFor = (name) => {
164
164
  firestoreDocRefVariables.delete(name);
165
165
  firestoreCollectionVariables.delete(name);
@@ -336,7 +336,7 @@ exports.enforceFirestoreSetMerge = (0, createRule_1.createRule)({
336
336
  },
337
337
  defaultOptions: [],
338
338
  create(context) {
339
- const sourceCode = context.sourceCode;
339
+ const sourceCode = context.getSourceCode();
340
340
  const updateAliases = new Set();
341
341
  /**
342
342
  * The `setDoc` binding rides on one violation's fix, which makes that
@@ -211,7 +211,7 @@ exports.enforceGlobalConstants = (0, createRule_1.createRule)({
211
211
  },
212
212
  defaultOptions: [{}],
213
213
  create(context, [options]) {
214
- const sourceCode = context.sourceCode;
214
+ const sourceCode = context.getSourceCode();
215
215
  const printWidth = typeof options.printWidth === 'number' && options.printWidth > 0
216
216
  ? options.printWidth
217
217
  : DEFAULT_PRINT_WIDTH;
@@ -503,7 +503,7 @@ exports.enforceMemoizeAsync = (0, createRule_1.createRule)({
503
503
  const readMemoizeImports = () => {
504
504
  const aliases = new Map();
505
505
  const namespaces = new Map();
506
- for (const statement of context.sourceCode.ast.body) {
506
+ for (const statement of context.getSourceCode().ast.body) {
507
507
  if (statement.type !== utils_1.AST_NODE_TYPES.ImportDeclaration) {
508
508
  continue;
509
509
  }
@@ -542,7 +542,7 @@ exports.enforceMemoizeAsync = (0, createRule_1.createRule)({
542
542
  const transactionAliases = () => {
543
543
  if (!transactionAliasCache) {
544
544
  transactionAliasCache = new Set();
545
- for (const statement of context.sourceCode.ast.body) {
545
+ for (const statement of context.getSourceCode().ast.body) {
546
546
  if (statement.type !== utils_1.AST_NODE_TYPES.ImportDeclaration) {
547
547
  continue;
548
548
  }
@@ -731,7 +731,7 @@ exports.enforceMemoizeAsync = (0, createRule_1.createRule)({
731
731
  return null;
732
732
  }
733
733
  const fixes = [];
734
- const sourceCode = context.sourceCode;
734
+ const sourceCode = context.getSourceCode();
735
735
  // Determine which identifier to use for the decorator
736
736
  let decoratorIdent = 'Memoize';
737
737
  if (hasMemoizeImport) {
@@ -485,7 +485,7 @@ exports.enforceMicrodiff = (0, createRule_1.createRule)({
485
485
  },
486
486
  defaultOptions: [],
487
487
  create(context) {
488
- const sourceCode = context.sourceCode;
488
+ const sourceCode = context.getSourceCode();
489
489
  const importedDiffLibraries = new Map();
490
490
  // The diff specifiers this file imports, keyed by node so a call site can
491
491
  // ask whether the binding it resolves to is one of them.
@@ -253,7 +253,7 @@ exports.enforceObjectLiteralAsConst = (0, createRule_1.createRule)({
253
253
  return;
254
254
  }
255
255
  // Check if the return statement is inside a function
256
- const sourceCode = context.sourceCode;
256
+ const sourceCode = context.getSourceCode();
257
257
  // Use ASTHelpers.getAncestors for ESLint v8/v9 compatibility
258
258
  const ancestors = ASTHelpers_1.ASTHelpers.getAncestors(context, node);
259
259
  const isInFunction = ancestors.some((ancestor) => ancestor.type === 'FunctionDeclaration' ||
@@ -353,7 +353,7 @@ exports.enforcePropsArgumentName = (0, createRule_1.createRule)({
353
353
  // Returns null whenever the rename cannot be applied everywhere, so the
354
354
  // report stands on its own rather than corrupting the source.
355
355
  function buildParameterRenameFixes(fixer, owner, id, newName) {
356
- const sourceCode = context.sourceCode;
356
+ const sourceCode = context.getSourceCode();
357
357
  const declarationFix = renameIdentifierToken(fixer, sourceCode, id, newName);
358
358
  if (!declarationFix) {
359
359
  return null;
@@ -95,7 +95,8 @@ exports.enforceStableStringify = (0, createRule_1.createRule)({
95
95
  return null;
96
96
  }
97
97
  const fixes = [];
98
- const program = context.sourceCode.ast;
98
+ const sourceCode = context.getSourceCode();
99
+ const program = sourceCode.ast;
99
100
  // Add the import only when the file lacks it. Suggestions are
100
101
  // applied one at a time with a re-lint in between, so each is
101
102
  // computed independently against the current file; adding the
@@ -108,7 +109,7 @@ exports.enforceStableStringify = (0, createRule_1.createRule)({
108
109
  // `'use client'` directive demotes the directive to a plain
109
110
  // expression, and above a `#!` shebang leaves the file
110
111
  // unparseable.
111
- fixes.push((0, importInsertion_1.insertAtImportAnchor)(context.sourceCode, fixer, (0, importInsertion_1.importInsertionAnchor)(context.sourceCode), "import stringify from 'safe-stable-stringify';\n"));
112
+ fixes.push((0, importInsertion_1.insertAtImportAnchor)(sourceCode, fixer, (0, importInsertion_1.importInsertionAnchor)(sourceCode), "import stringify from 'safe-stable-stringify';\n"));
112
113
  }
113
114
  fixes.push(fixer.replaceText(node, STRINGIFY_NAME));
114
115
  return fixes;
@@ -490,8 +490,7 @@ exports.enforceStableHashSpreadProps = (0, createRule_1.createRule)({
490
490
  },
491
491
  defaultOptions: [{}],
492
492
  create(context) {
493
- const sourceCode = context.sourceCode ??
494
- context.getSourceCode();
493
+ const sourceCode = context.getSourceCode();
495
494
  const [options = {}] = context.options;
496
495
  const hashImport = {
497
496
  source: options.hashImport?.source ?? DEFAULT_HASH_IMPORT.source,
@@ -227,7 +227,7 @@ exports.enforceTypescriptMarkdownCodeBlocks = (0, createRule_1.createRule)({
227
227
  }
228
228
  return {
229
229
  Program() {
230
- const sourceCode = context.sourceCode;
230
+ const sourceCode = context.getSourceCode();
231
231
  const text = sourceCode.getText();
232
232
  const lines = splitLines(text);
233
233
  let index = 0;
@@ -4026,7 +4026,7 @@ exports.enforceVerbNounNaming = (0, createRule_1.createRule)({
4026
4026
  return deferredExportedNames;
4027
4027
  }
4028
4028
  const names = new Set();
4029
- for (const statement of context.sourceCode.ast.body) {
4029
+ for (const statement of context.getSourceCode().ast.body) {
4030
4030
  if (statement.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration &&
4031
4031
  !statement.source) {
4032
4032
  for (const specifier of statement.specifiers) {
@@ -4181,7 +4181,9 @@ exports.enforceVerbNounNaming = (0, createRule_1.createRule)({
4181
4181
  const id = parent.id;
4182
4182
  if (id.type === utils_1.AST_NODE_TYPES.Identifier &&
4183
4183
  id.typeAnnotation?.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation) {
4184
- const typeText = context.sourceCode.getText(id.typeAnnotation.typeAnnotation);
4184
+ const typeText = context
4185
+ .getSourceCode()
4186
+ .getText(id.typeAnnotation.typeAnnotation);
4185
4187
  if (/\bReact\.(FC|FunctionComponent)\b/.test(typeText) ||
4186
4188
  /\b(FC|FunctionComponent)\b/.test(typeText)) {
4187
4189
  return true;
@@ -4195,7 +4197,9 @@ exports.enforceVerbNounNaming = (0, createRule_1.createRule)({
4195
4197
  const memberParent = node.parent;
4196
4198
  if (memberParent?.type === utils_1.AST_NODE_TYPES.PropertyDefinition &&
4197
4199
  memberParent.typeAnnotation?.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation) {
4198
- const typeText = context.sourceCode.getText(memberParent.typeAnnotation.typeAnnotation);
4200
+ const typeText = context
4201
+ .getSourceCode()
4202
+ .getText(memberParent.typeAnnotation.typeAnnotation);
4199
4203
  if (/\bReact\.(FC|FunctionComponent)\b/.test(typeText) ||
4200
4204
  /\b(FC|FunctionComponent)\b/.test(typeText)) {
4201
4205
  return true;
@@ -4203,7 +4207,9 @@ exports.enforceVerbNounNaming = (0, createRule_1.createRule)({
4203
4207
  }
4204
4208
  // Handle FunctionDeclaration/FunctionExpression/ArrowFunction return type: function Foo(): React.JSX.Element { ... }
4205
4209
  if (node.returnType?.type === utils_1.AST_NODE_TYPES.TSTypeAnnotation) {
4206
- const typeText = context.sourceCode.getText(node.returnType.typeAnnotation);
4210
+ const typeText = context
4211
+ .getSourceCode()
4212
+ .getText(node.returnType.typeAnnotation);
4207
4213
  return (/\bReact\.(FC|FunctionComponent|JSX\.Element|ReactElement)\b/.test(typeText) ||
4208
4214
  /\b(FC|FunctionComponent|JSX\.Element|ReactElement)\b/.test(typeText));
4209
4215
  }
@@ -539,7 +539,7 @@ exports.ensurePointerEventsNone = (0, createRule_1.createRule)({
539
539
  // decides what the opaque value resolves to.
540
540
  if (stylesWithUnreadablePointerEvents.get(node))
541
541
  return null;
542
- return appendPointerEventsNone(fixer, context.sourceCode, node, printWidth);
542
+ return appendPointerEventsNone(fixer, context.getSourceCode(), node, printWidth);
543
543
  },
544
544
  });
545
545
  }
@@ -113,7 +113,7 @@ exports.fastDeepEqualOverMicrodiff = (0, createRule_1.createRule)({
113
113
  },
114
114
  defaultOptions: [],
115
115
  create(context) {
116
- const sourceCode = context.sourceCode;
116
+ const sourceCode = context.getSourceCode();
117
117
  let hasFastDeepEqualImport = false;
118
118
  let hasMicrodiffImport = false;
119
119
  let microdiffImportName = 'diff';
@@ -587,7 +587,7 @@ exports.default = (0, createRule_1.createRule)({
587
587
  if (!init || isDynamicValue(init) || isBindingAlias(init)) {
588
588
  return;
589
589
  }
590
- const sourceCode = context.sourceCode;
590
+ const sourceCode = context.getSourceCode();
591
591
  const initText = sourceCode.getText(init);
592
592
  const typeAnnotation = declaration.id.typeAnnotation;
593
593
  const typeText = typeAnnotation
@@ -137,7 +137,7 @@ exports.keyOnlyOutermostElement = (0, createRule_1.createRule)({
137
137
  },
138
138
  defaultOptions: [],
139
139
  create(context) {
140
- const sourceCode = context.sourceCode;
140
+ const sourceCode = context.getSourceCode();
141
141
  // Track JSXElements that are direct children of a map callback
142
142
  const mapCallbackElements = new Set();
143
143
  // Track JSXFragments that are direct children of a map callback
@@ -2087,9 +2087,7 @@ exports.logicalTopToBottomGrouping = (0, createRule_1.createRule)({
2087
2087
  },
2088
2088
  defaultOptions: [],
2089
2089
  create(context) {
2090
- // Prefer context.sourceCode when present to avoid deprecated getSourceCode() while remaining
2091
- // compatible with ESLint versions that omit the property.
2092
- const sourceCode = context.sourceCode ?? context.getSourceCode();
2090
+ const sourceCode = context.getSourceCode();
2093
2091
  const ruleContext = {
2094
2092
  context,
2095
2093
  sourceCode,
@@ -671,7 +671,7 @@ See: https://react.dev/learn/your-first-component#nesting-and-organizing-compone
671
671
  if (ignorePatterns.length && shouldIgnoreFile(filename, ignorePatterns)) {
672
672
  return {};
673
673
  }
674
- const sourceCode = context.sourceCode ?? context.getSourceCode();
674
+ const sourceCode = context.getSourceCode();
675
675
  const reactImports = collectReactImports(sourceCode);
676
676
  const reportNestedComponentViolation = (node, componentName, locationDescription) => {
677
677
  context.report({