oxlint-plugin-react-doctor 0.2.10 → 0.2.11

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.d.ts CHANGED
@@ -82,10 +82,17 @@ interface ScopeAnalysis {
82
82
  //#region src/plugin/utils/rule-context.d.ts
83
83
  interface BaseRuleContext {
84
84
  report: (descriptor: ReportDescriptor) => void;
85
- getFilename?: () => string;
85
+ readonly filename?: string;
86
+ /**
87
+ * @deprecated Rules use `context.filename`. Read only as a fallback by
88
+ * `wrapWithSemanticContext`; ESLint implements it as a `this`-bound class
89
+ * method, so it must be called on the host context, never a detached
90
+ * reference.
91
+ */
92
+ getFilename?: () => string | undefined;
86
93
  readonly settings?: Readonly<Record<string, unknown>>;
87
94
  }
88
- interface RuleContext extends BaseRuleContext {
95
+ interface RuleContext extends Omit<BaseRuleContext, "getFilename"> {
89
96
  readonly scopes: ScopeAnalysis;
90
97
  readonly cfg: ControlFlowAnalysis;
91
98
  }
package/dist/index.js CHANGED
@@ -223,7 +223,7 @@ const jsxAttributeIsNonReactDialectMarker = (openingNode) => {
223
223
  //#endregion
224
224
  //#region src/plugin/utils/define-rule.ts
225
225
  const wrapCreateForTestNoise = (create) => ((context) => {
226
- if (isTestlikeFilename(context.getFilename?.())) return {};
226
+ if (isTestlikeFilename(context.filename)) return {};
227
227
  return create(context);
228
228
  });
229
229
  const VISITOR_NODE_NAME_PATTERN = /^[A-Z]/;
@@ -3127,7 +3127,7 @@ const asyncParallel = defineRule({
3127
3127
  severity: "warn",
3128
3128
  recommendation: "Use `const [a, b] = await Promise.all([fetchA(), fetchB()])` to run independent operations concurrently",
3129
3129
  create: (context) => {
3130
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
3130
+ const filename = normalizeFilename$1(context.filename ?? "");
3131
3131
  const isBrowserTestFile = BROWSER_TEST_FILE_PATTERN.test(filename);
3132
3132
  let hasTestLibraryImport = false;
3133
3133
  const shouldSkipFile = () => isBrowserTestFile || hasTestLibraryImport;
@@ -3330,7 +3330,7 @@ const buttonHasType = defineRule({
3330
3330
  recommendation: "Set `type=\"button\"` (or `\"submit\"` / `\"reset\"`) explicitly on every `<button>`.",
3331
3331
  create: (context) => {
3332
3332
  const settings = resolveSettings$48(context.settings);
3333
- const isTestlikeFile = isTestlikeFilename(context.getFilename?.());
3333
+ const isTestlikeFile = isTestlikeFilename(context.filename);
3334
3334
  return {
3335
3335
  JSXOpeningElement(node) {
3336
3336
  if (isTestlikeFile) return;
@@ -3530,7 +3530,7 @@ const clickEventsHaveKeyEvents = defineRule({
3530
3530
  recommendation: "Pair `onClick` with `onKeyUp` / `onKeyDown` / `onKeyPress` for keyboard users.",
3531
3531
  category: "Accessibility",
3532
3532
  create: (context) => {
3533
- const isTestlikeFile = isTestlikeFilename(context.getFilename?.());
3533
+ const isTestlikeFile = isTestlikeFilename(context.filename);
3534
3534
  return { JSXOpeningElement(node) {
3535
3535
  if (isTestlikeFile) return;
3536
3536
  const tag = getElementType(node, context.settings);
@@ -3791,7 +3791,7 @@ const controlHasAssociatedLabel = defineRule({
3791
3791
  category: "Accessibility",
3792
3792
  create: (context) => {
3793
3793
  const settings = resolveSettings$46(context.settings);
3794
- const isTestlikeFile = isTestlikeFilename(context.getFilename?.());
3794
+ const isTestlikeFile = isTestlikeFilename(context.filename);
3795
3795
  return { JSXElement(node) {
3796
3796
  if (isTestlikeFile) return;
3797
3797
  const opening = node.openingElement;
@@ -8423,6 +8423,7 @@ const jsTosortedImmutable = defineRule({
8423
8423
  id: "js-tosorted-immutable",
8424
8424
  tags: ["test-noise"],
8425
8425
  severity: "warn",
8426
+ disabledBy: ["react-native"],
8426
8427
  recommendation: "Use `array.toSorted()` (ES2023) instead of `[...array].sort()` for immutable sorting without the spread allocation",
8427
8428
  create: (context) => ({ CallExpression(node) {
8428
8429
  if (!isMemberProperty(node.callee, "sort")) return;
@@ -8711,7 +8712,7 @@ const jsxFilenameExtension = defineRule({
8711
8712
  const settings = resolveSettings$34(context.settings);
8712
8713
  const allowedExtensions = normalizeExtensions(settings.extensions);
8713
8714
  const allowedList = [...allowedExtensions].map((extension) => `.${extension}`).join(", ");
8714
- const filename = context.getFilename ? normalizeFilename$1(context.getFilename()) : "fixture.tsx";
8715
+ const filename = normalizeFilename$1(context.filename ?? "fixture.tsx");
8715
8716
  const extensionOnly = path.extname(filename).slice(1);
8716
8717
  const fileHasAllowedExtension = allowedExtensions.has(extensionOnly);
8717
8718
  let didReportMismatch = false;
@@ -9495,7 +9496,7 @@ const jsxNoConstructedContextValues = defineRule({
9495
9496
  recommendation: "Memoize the context value (`useMemo`) or hoist it outside the render.",
9496
9497
  category: "Performance",
9497
9498
  create: (context) => {
9498
- const isTestlikeFile = isTestlikeFilename(context.getFilename?.());
9499
+ const isTestlikeFile = isTestlikeFilename(context.filename);
9499
9500
  return { JSXOpeningElement(node) {
9500
9501
  if (isTestlikeFile) return;
9501
9502
  if (!isProviderName(node.name)) return;
@@ -9841,7 +9842,7 @@ const jsxNoJsxAsProp = defineRule({
9841
9842
  recommendation: "Hoist the inner JSX outside the render or memoize via `useMemo`.",
9842
9843
  category: "Performance",
9843
9844
  create: (context) => {
9844
- const isTestlikeFile = isTestlikeFilename(context.getFilename?.());
9845
+ const isTestlikeFile = isTestlikeFilename(context.filename);
9845
9846
  let memoRegistry = null;
9846
9847
  return {
9847
9848
  Program(node) {
@@ -10212,7 +10213,7 @@ const jsxNoNewArrayAsProp = defineRule({
10212
10213
  recommendation: "Memoize the array (`useMemo`) or hoist it outside the component.",
10213
10214
  category: "Performance",
10214
10215
  create: (context) => {
10215
- const isTestlikeFile = isTestlikeFilename(context.getFilename?.());
10216
+ const isTestlikeFile = isTestlikeFilename(context.filename);
10216
10217
  let memoRegistry = null;
10217
10218
  return {
10218
10219
  Program(node) {
@@ -10674,7 +10675,7 @@ const jsxNoNewFunctionAsProp = defineRule({
10674
10675
  recommendation: "Memoize the callback (`useCallback`) or hoist it outside the component.",
10675
10676
  category: "Performance",
10676
10677
  create: (context) => {
10677
- const isTestlikeFile = isTestlikeFilename(context.getFilename?.());
10678
+ const isTestlikeFile = isTestlikeFilename(context.filename);
10678
10679
  let memoRegistry = null;
10679
10680
  return {
10680
10681
  Program(node) {
@@ -10980,7 +10981,7 @@ const jsxNoNewObjectAsProp = defineRule({
10980
10981
  recommendation: "Memoize the object (`useMemo`) or hoist it outside the component.",
10981
10982
  category: "Performance",
10982
10983
  create: (context) => {
10983
- const isTestlikeFile = isTestlikeFilename(context.getFilename?.());
10984
+ const isTestlikeFile = isTestlikeFilename(context.filename);
10984
10985
  let memoRegistry = null;
10985
10986
  return {
10986
10987
  Program(node) {
@@ -11785,7 +11786,7 @@ const labelHasAssociatedControl = defineRule({
11785
11786
  category: "Accessibility",
11786
11787
  create: (context) => {
11787
11788
  const settings = resolveSettings$24(context.settings);
11788
- const isTestlikeFile = isTestlikeFilename(context.getFilename?.());
11789
+ const isTestlikeFile = isTestlikeFilename(context.filename);
11789
11790
  return { JSXElement(node) {
11790
11791
  if (isTestlikeFile) return;
11791
11792
  const opening = node.openingElement;
@@ -12323,7 +12324,7 @@ const nextjsMissingMetadata = defineRule({
12323
12324
  severity: "warn",
12324
12325
  recommendation: "Add `export const metadata = { title: '...', description: '...' }` or `export async function generateMetadata()`",
12325
12326
  create: (context) => ({ Program(programNode) {
12326
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
12327
+ const filename = normalizeFilename$1(context.filename ?? "");
12327
12328
  if (!PAGE_FILE_PATTERN.test(filename)) return;
12328
12329
  if (INTERNAL_PAGE_PATH_PATTERN.test(filename)) return;
12329
12330
  if (!programNode.body?.some((statement) => {
@@ -12388,7 +12389,7 @@ const nextjsNoClientFetchForServerData = defineRule({
12388
12389
  if (!fileHasUseClient || !isHookCall$1(node, EFFECT_HOOK_NAMES$1)) return;
12389
12390
  const callback = getEffectCallback(node);
12390
12391
  if (!callback || !containsFetchCall(callback)) return;
12391
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
12392
+ const filename = normalizeFilename$1(context.filename ?? "");
12392
12393
  if (PAGE_OR_LAYOUT_FILE_PATTERN.test(filename) || PAGES_DIRECTORY_PATTERN.test(filename)) context.report({
12393
12394
  node,
12394
12395
  message: "useEffect + fetch in a page/layout — fetch data server-side with a server component instead"
@@ -12421,7 +12422,7 @@ const nextjsNoClientSideRedirect = defineRule({
12421
12422
  severity: "warn",
12422
12423
  recommendation: "Avoid redirects inside useEffect. Use an event handler, middleware, or server-side redirect (App Router: redirect() from next/navigation; Pages Router: getServerSideProps redirect)",
12423
12424
  create: (context) => {
12424
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
12425
+ const filename = normalizeFilename$1(context.filename ?? "");
12425
12426
  const isPagesRouterFile = PAGES_DIRECTORY_PATTERN.test(filename);
12426
12427
  return { CallExpression(node) {
12427
12428
  if (!isHookCall$1(node, EFFECT_HOOK_NAMES$1)) return;
@@ -12490,7 +12491,7 @@ const nextjsNoHeadImport = defineRule({
12490
12491
  recommendation: "Use the Metadata API instead: `export const metadata = { title: '...' }` or `export async function generateMetadata()`",
12491
12492
  create: (context) => ({ ImportDeclaration(node) {
12492
12493
  if (node.source?.value !== "next/head") return;
12493
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
12494
+ const filename = normalizeFilename$1(context.filename ?? "");
12494
12495
  if (!APP_DIRECTORY_PATTERN.test(filename)) return;
12495
12496
  context.report({
12496
12497
  node,
@@ -12507,7 +12508,7 @@ const nextjsNoImgElement = defineRule({
12507
12508
  severity: "warn",
12508
12509
  recommendation: "`import Image from 'next/image'` — provides automatic WebP/AVIF, lazy loading, and responsive srcset",
12509
12510
  create: (context) => {
12510
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
12511
+ const filename = normalizeFilename$1(context.filename ?? "");
12511
12512
  const isOgRoute = OG_ROUTE_PATTERN.test(filename);
12512
12513
  return { JSXOpeningElement(node) {
12513
12514
  if (isOgRoute) return;
@@ -12861,7 +12862,7 @@ const nextjsNoSideEffectInGetHandler = defineRule({
12861
12862
  resolveBinding = buildProgramBindingLookup(node);
12862
12863
  },
12863
12864
  ExportNamedDeclaration(node) {
12864
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
12865
+ const filename = normalizeFilename$1(context.filename ?? "");
12865
12866
  if (!ROUTE_HANDLER_FILE_PATTERN.test(filename)) return;
12866
12867
  if (CRON_ROUTE_PATTERN.test(filename)) return;
12867
12868
  if (!isExportedGetHandler(node)) return;
@@ -14093,7 +14094,7 @@ const noAutofocus = defineRule({
14093
14094
  category: "Accessibility",
14094
14095
  create: (context) => {
14095
14096
  const settings = resolveSettings$21(context.settings);
14096
- const isTestlikeFile = isTestlikeFilename(context.getFilename?.());
14097
+ const isTestlikeFile = isTestlikeFilename(context.filename);
14097
14098
  return { JSXOpeningElement(node) {
14098
14099
  if (isTestlikeFile) return;
14099
14100
  const autoFocusAttribute = node.attributes.find((attribute) => {
@@ -14467,7 +14468,7 @@ const noBarrelImport = defineRule({
14467
14468
  if (didReportForFile) return;
14468
14469
  const source = node.source?.value;
14469
14470
  if (typeof source !== "string" || !source.startsWith(".")) return;
14470
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
14471
+ const filename = normalizeFilename$1(context.filename ?? "");
14471
14472
  if (!filename) return;
14472
14473
  const importRequests = getRuntimeImportRequests(node);
14473
14474
  if (importRequests.length === 0) return;
@@ -18246,7 +18247,7 @@ const noMultiComp = defineRule({
18246
18247
  category: "Architecture",
18247
18248
  create: (context) => {
18248
18249
  const settings = resolveSettings$16(context.settings);
18249
- const isTestlikeFile = isTestlikeFilename(context.getFilename?.());
18250
+ const isTestlikeFile = isTestlikeFilename(context.filename);
18250
18251
  return { Program(node) {
18251
18252
  if (isTestlikeFile) return;
18252
18253
  const visitContext = {
@@ -20193,7 +20194,7 @@ const noSecretsInClientCode = defineRule({
20193
20194
  severity: "warn",
20194
20195
  recommendation: "Move secrets to server-only code. Public client environment variables are bundled into browser code and must not contain secrets",
20195
20196
  create: (context) => {
20196
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
20197
+ const filename = normalizeFilename$1(context.filename ?? "");
20197
20198
  const framework = getReactDoctorStringSetting(context.settings, "framework");
20198
20199
  const rootDirectory = getReactDoctorStringSetting(context.settings, "rootDirectory");
20199
20200
  let shouldUseVariableNameHeuristic = classifySecretFileExposure(filename, {
@@ -20448,7 +20449,7 @@ const noStaticElementInteractions = defineRule({
20448
20449
  category: "Accessibility",
20449
20450
  create: (context) => {
20450
20451
  const settings = resolveSettings$12(context.settings);
20451
- const isTestlikeFile = isTestlikeFilename(context.getFilename?.());
20452
+ const isTestlikeFile = isTestlikeFilename(context.filename);
20452
20453
  return { JSXOpeningElement(node) {
20453
20454
  if (isTestlikeFile) return;
20454
20455
  let hasNonBlockerHandler = false;
@@ -20525,7 +20526,7 @@ const noStringRefs = defineRule({
20525
20526
  recommendation: "Use a callback ref (`ref={(node) => { this.foo = node }}`) or `useRef` instead of string refs.",
20526
20527
  create: (context) => {
20527
20528
  const { noTemplateLiterals = false } = resolveSettings$11(context.settings);
20528
- const isTestlikeFile = isTestlikeFilename(context.getFilename?.());
20529
+ const isTestlikeFile = isTestlikeFilename(context.filename);
20529
20530
  return {
20530
20531
  JSXAttribute(node) {
20531
20532
  if (isTestlikeFile) return;
@@ -22671,7 +22672,7 @@ const onlyExportComponents = defineRule({
22671
22672
  allowConstantExport: settings.allowConstantExport
22672
22673
  };
22673
22674
  return { Program(node) {
22674
- if (!isFileNameAllowed(context.getFilename ? normalizeFilename$1(context.getFilename()) : void 0, settings.checkJS)) return;
22675
+ if (!isFileNameAllowed(normalizeFilename$1(context.filename ?? ""), settings.checkJS)) return;
22675
22676
  const allNodes = collectAllNodes(node);
22676
22677
  const exports = [];
22677
22678
  let hasReactExport = false;
@@ -24192,7 +24193,7 @@ const renderingSvgPrecision = defineRule({
24192
24193
  category: "Performance",
24193
24194
  recommendation: "Truncate path/points/transform decimals to 1–2 digits — sub-pixel precision adds bytes with no visible difference",
24194
24195
  create: (context) => {
24195
- const filename = context.getFilename?.();
24196
+ const filename = context.filename;
24196
24197
  const isAutoGenerated = isAutoGeneratedSvgFile(filename ? normalizeFilename$1(filename) : void 0);
24197
24198
  return { JSXAttribute(node) {
24198
24199
  if (isAutoGenerated) return;
@@ -25524,7 +25525,8 @@ const classifyPackagePlatform = (filename) => {
25524
25525
  //#endregion
25525
25526
  //#region src/plugin/utils/is-expo-managed-file.ts
25526
25527
  const isExpoManagedFileActive = (context) => {
25527
- const filename = context.getFilename?.() ? normalizeFilename$1(context.getFilename()) : void 0;
25528
+ const rawFilename = context.filename;
25529
+ const filename = rawFilename ? normalizeFilename$1(rawFilename) : void 0;
25528
25530
  if (filename) {
25529
25531
  const packagePlatform = classifyPackagePlatform(filename);
25530
25532
  if (packagePlatform === "expo") return true;
@@ -30692,7 +30694,7 @@ const serverFetchWithoutRevalidate = defineRule({
30692
30694
  let isServerSideFile = false;
30693
30695
  return {
30694
30696
  Program(node) {
30695
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
30697
+ const filename = normalizeFilename$1(context.filename ?? "");
30696
30698
  if (!APP_ROUTER_FILE_PATTERN.test(filename)) {
30697
30699
  isServerSideFile = false;
30698
30700
  return;
@@ -30805,7 +30807,7 @@ const serverHoistStaticIo = defineRule({
30805
30807
  inspectHandlerBody(context, declaration.body, `${handlerName} route handler`, collectIdentifierParams(declaration.params ?? []));
30806
30808
  },
30807
30809
  ExportDefaultDeclaration(node) {
30808
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
30810
+ const filename = normalizeFilename$1(context.filename ?? "");
30809
30811
  if (!PAGES_ROUTER_API_PATH_PATTERN.test(filename)) return;
30810
30812
  const declaration = node.declaration;
30811
30813
  if (!declaration || !isNodeOfType(declaration, "FunctionDeclaration") && !isNodeOfType(declaration, "FunctionExpression") && !isNodeOfType(declaration, "ArrowFunctionExpression")) return;
@@ -31356,7 +31358,7 @@ const tanstackStartMissingHeadContent = defineRule({
31356
31358
  };
31357
31359
  return {
31358
31360
  Program(node) {
31359
- const filename = context.getFilename?.() ?? "";
31361
+ const filename = context.filename ?? "";
31360
31362
  if (!TANSTACK_ROOT_ROUTE_FILE_PATTERN.test(filename)) return;
31361
31363
  const statements = node.body ?? [];
31362
31364
  for (const statement of statements) collectImportBindings(statement);
@@ -31366,17 +31368,17 @@ const tanstackStartMissingHeadContent = defineRule({
31366
31368
  }
31367
31369
  },
31368
31370
  ImportDeclaration(node) {
31369
- const filename = context.getFilename?.() ?? "";
31371
+ const filename = context.filename ?? "";
31370
31372
  if (!TANSTACK_ROOT_ROUTE_FILE_PATTERN.test(filename)) return;
31371
31373
  collectImportBindings(node);
31372
31374
  },
31373
31375
  VariableDeclarator(node) {
31374
- const filename = context.getFilename?.() ?? "";
31376
+ const filename = context.filename ?? "";
31375
31377
  if (!TANSTACK_ROOT_ROUTE_FILE_PATTERN.test(filename)) return;
31376
31378
  collectVariableAlias(node);
31377
31379
  },
31378
31380
  JSXOpeningElement(node) {
31379
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
31381
+ const filename = normalizeFilename$1(context.filename ?? "");
31380
31382
  if (!TANSTACK_ROOT_ROUTE_FILE_PATTERN.test(filename)) return;
31381
31383
  if (isNodeOfType(node.name, "JSXIdentifier")) {
31382
31384
  if (node.name.name === DOCUMENT_HEAD_ELEMENT_NAME) hasDocumentHeadElement = true;
@@ -31391,7 +31393,7 @@ const tanstackStartMissingHeadContent = defineRule({
31391
31393
  if (isInsideDocumentHeadElement(node) && isCustomJsxElementName(node.name)) hasCustomHeadChildElement = true;
31392
31394
  },
31393
31395
  "Program:exit"(programNode) {
31394
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
31396
+ const filename = normalizeFilename$1(context.filename ?? "");
31395
31397
  if (!TANSTACK_ROOT_ROUTE_FILE_PATTERN.test(filename)) return;
31396
31398
  if (hasDocumentHeadElement && !hasHeadContentElement && !hasCustomHeadChildElement) context.report({
31397
31399
  node: programNode,
@@ -31410,7 +31412,7 @@ const tanstackStartNoAnchorElement = defineRule({
31410
31412
  severity: "warn",
31411
31413
  recommendation: "`import { Link } from '@tanstack/react-router'` — enables type-safe routes, preloading via `preload=\"intent\"`, and client-side navigation",
31412
31414
  create: (context) => ({ JSXOpeningElement(node) {
31413
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
31415
+ const filename = normalizeFilename$1(context.filename ?? "");
31414
31416
  if (!TANSTACK_ROUTE_FILE_PATTERN.test(filename)) return;
31415
31417
  if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "a") return;
31416
31418
  const hrefAttribute = (node.attributes ?? []).find((attribute) => isNodeOfType(attribute, "JSXAttribute") && isNodeOfType(attribute.name, "JSXIdentifier") && attribute.name.name === "href");
@@ -31484,7 +31486,7 @@ const tanstackStartNoNavigateInRender = defineRule({
31484
31486
  const isEventHandlerAttribute = (node) => isNodeOfType(node, "JSXAttribute") && isNodeOfType(node.name, "JSXIdentifier") && typeof node.name.name === "string" && node.name.name.startsWith("on") && UPPERCASE_PATTERN.test(node.name.name.charAt(2));
31485
31487
  return {
31486
31488
  CallExpression(node) {
31487
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
31489
+ const filename = normalizeFilename$1(context.filename ?? "");
31488
31490
  if (!TANSTACK_ROUTE_FILE_PATTERN.test(filename)) return;
31489
31491
  if (isDeferredHookCall(node)) deferredCallbackDepth++;
31490
31492
  if (deferredCallbackDepth > 0 || eventHandlerDepth > 0) return;
@@ -31494,17 +31496,17 @@ const tanstackStartNoNavigateInRender = defineRule({
31494
31496
  });
31495
31497
  },
31496
31498
  "CallExpression:exit"(node) {
31497
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
31499
+ const filename = normalizeFilename$1(context.filename ?? "");
31498
31500
  if (!TANSTACK_ROUTE_FILE_PATTERN.test(filename)) return;
31499
31501
  if (isDeferredHookCall(node)) deferredCallbackDepth = Math.max(0, deferredCallbackDepth - 1);
31500
31502
  },
31501
31503
  JSXAttribute(node) {
31502
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
31504
+ const filename = normalizeFilename$1(context.filename ?? "");
31503
31505
  if (!TANSTACK_ROUTE_FILE_PATTERN.test(filename)) return;
31504
31506
  if (isEventHandlerAttribute(node)) eventHandlerDepth++;
31505
31507
  },
31506
31508
  "JSXAttribute:exit"(node) {
31507
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
31509
+ const filename = normalizeFilename$1(context.filename ?? "");
31508
31510
  if (!TANSTACK_ROUTE_FILE_PATTERN.test(filename)) return;
31509
31511
  if (isEventHandlerAttribute(node)) eventHandlerDepth = Math.max(0, eventHandlerDepth - 1);
31510
31512
  }
@@ -31585,7 +31587,7 @@ const tanstackStartNoUseEffectFetch = defineRule({
31585
31587
  severity: "warn",
31586
31588
  recommendation: "Fetch data in the route `loader` instead — the router coordinates loading before rendering to avoid waterfalls",
31587
31589
  create: (context) => ({ CallExpression(node) {
31588
- const filename = normalizeFilename$1(context.getFilename?.() ?? "");
31590
+ const filename = normalizeFilename$1(context.filename ?? "");
31589
31591
  if (!TANSTACK_ROUTE_FILE_PATTERN.test(filename)) return;
31590
31592
  if (!isHookCall$1(node, EFFECT_HOOK_NAMES$1)) return;
31591
31593
  const callback = node.arguments?.[0];
@@ -35254,7 +35256,7 @@ const ruleRegistry = Object.fromEntries(reactDoctorRules.map((rule) => [rule.id,
35254
35256
  const WEB_FILE_EXTENSION_PATTERN = /\.web\.[cm]?[jt]sx?$/;
35255
35257
  const NATIVE_FILE_EXTENSION_PATTERN = /\.(?:ios|android|native)\.[cm]?[jt]sx?$/;
35256
35258
  const isReactNativeFileActive = (context) => {
35257
- const rawFilename = context.getFilename?.();
35259
+ const rawFilename = context.filename;
35258
35260
  if (!rawFilename) return true;
35259
35261
  const filename = normalizeFilename$1(rawFilename);
35260
35262
  if (NATIVE_FILE_EXTENSION_PATTERN.test(filename)) return true;
@@ -35742,7 +35744,9 @@ const wrapWithSemanticContext = (rule) => ({
35742
35744
  };
35743
35745
  const enrichedContext = {
35744
35746
  report: baseContext.report,
35745
- getFilename: baseContext.getFilename,
35747
+ get filename() {
35748
+ return baseContext.filename ?? baseContext.getFilename?.();
35749
+ },
35746
35750
  settings: baseContext.settings,
35747
35751
  get scopes() {
35748
35752
  return getScopes();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-react-doctor",
3
- "version": "0.2.10",
3
+ "version": "0.2.11",
4
4
  "description": "oxlint plugin for React Doctor: diagnose React codebases for security, performance, correctness, accessibility, bundle-size, and architecture issues",
5
5
  "keywords": [
6
6
  "accessibility",