oxlint-plugin-react-doctor 0.6.2-dev.d8628d7 → 0.6.2-dev.da3b19c

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 +78 -45
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4843,7 +4843,8 @@ const FORM_CONTROL_TAGS = new Set([
4843
4843
  ]);
4844
4844
  const resolveSettings$48 = (settings) => {
4845
4845
  const reactDoctor = settings?.["react-doctor"];
4846
- return { inputComponents: (typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.autocompleteValid ?? {} : {}).inputComponents ?? [] };
4846
+ const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.autocompleteValid ?? {} : {};
4847
+ return { inputComponents: new Set(ruleSettings.inputComponents ?? []) };
4847
4848
  };
4848
4849
  const autocompleteValid = defineRule({
4849
4850
  id: "autocomplete-valid",
@@ -4856,7 +4857,7 @@ const autocompleteValid = defineRule({
4856
4857
  const settings = resolveSettings$48(context.settings);
4857
4858
  return { JSXOpeningElement: (node) => {
4858
4859
  const tag = getElementType(node, context.settings);
4859
- if (!FORM_CONTROL_TAGS.has(tag) && !settings.inputComponents.includes(tag)) return;
4860
+ if (!FORM_CONTROL_TAGS.has(tag) && !settings.inputComponents.has(tag)) return;
4860
4861
  const attribute = hasJsxPropIgnoreCase(node.attributes, "autoComplete");
4861
4862
  if (!attribute) return;
4862
4863
  const value = getJsxPropStringValue(attribute);
@@ -8636,7 +8637,7 @@ const DEFAULT_HEADING_TAGS = [
8636
8637
  const resolveSettings$40 = (settings) => {
8637
8638
  const reactDoctor = settings?.["react-doctor"];
8638
8639
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.headingHasContent ?? {} : {};
8639
- return { headingTags: [...DEFAULT_HEADING_TAGS, ...ruleSettings.components ?? []] };
8640
+ return { headingTags: new Set([...DEFAULT_HEADING_TAGS, ...ruleSettings.components ?? []]) };
8640
8641
  };
8641
8642
  const headingHasContent = defineRule({
8642
8643
  id: "heading-has-content",
@@ -8649,7 +8650,7 @@ const headingHasContent = defineRule({
8649
8650
  const settings = resolveSettings$40(context.settings);
8650
8651
  return { JSXOpeningElement(node) {
8651
8652
  const elementType = getElementType(node, context.settings);
8652
- if (!settings.headingTags.includes(elementType)) return;
8653
+ if (!settings.headingTags.has(elementType)) return;
8653
8654
  const parent = node.parent;
8654
8655
  if (parent && isNodeOfType(parent, "JSXElement")) {
8655
8656
  if (objectHasAccessibleChild(parent, context.settings)) return;
@@ -9555,6 +9556,7 @@ const KEYBOARD_EVENT_HANDLERS = [
9555
9556
  "onKeyUp"
9556
9557
  ];
9557
9558
  const ALL_EVENT_HANDLERS = [...MOUSE_EVENT_HANDLERS, ...KEYBOARD_EVENT_HANDLERS];
9559
+ const ALL_EVENT_HANDLERS_LOWER = new Set(ALL_EVENT_HANDLERS.map((handlerName) => handlerName.toLowerCase()));
9558
9560
  //#endregion
9559
9561
  //#region src/plugin/utils/is-disabled-element.ts
9560
9562
  const isDisabledElement = (openingElement) => {
@@ -9614,7 +9616,16 @@ const interactiveSupportsFocus = defineRule({
9614
9616
  const roleAttribute = hasJsxPropIgnoreCase(node.attributes, "role");
9615
9617
  const role = roleAttribute ? getJsxPropStringValue(roleAttribute) : null;
9616
9618
  if (!role) return;
9617
- if (!ALL_EVENT_HANDLERS.some((handler) => Boolean(hasJsxPropIgnoreCase(node.attributes, handler)))) return;
9619
+ let hasInteractiveHandler = false;
9620
+ for (const attribute of node.attributes) {
9621
+ if (!isNodeOfType(attribute, "JSXAttribute")) continue;
9622
+ const attributeName = getJsxAttributeName(attribute.name);
9623
+ if (attributeName && ALL_EVENT_HANDLERS_LOWER.has(attributeName.toLowerCase())) {
9624
+ hasInteractiveHandler = true;
9625
+ break;
9626
+ }
9627
+ }
9628
+ if (!hasInteractiveHandler) return;
9618
9629
  const elementType = getElementType(node, context.settings);
9619
9630
  if (!HTML_TAGS.has(elementType)) return;
9620
9631
  if (isDisabledElement(node) || isHiddenFromScreenReader(node, context.settings) || isPresentationRole(node)) return;
@@ -14463,14 +14474,14 @@ const keyLifecycleRisk = defineRule({
14463
14474
  //#region src/plugin/rules/a11y/label-has-associated-control.ts
14464
14475
  const MESSAGE_NO_LABEL = "Blind users can't identify this field because screen readers find no label text, so add visible text, `aria-label`, or `aria-labelledby`.";
14465
14476
  const MESSAGE_NO_CONTROL = "Screen reader users can't tell which input this label names because it's tied to none, so add `htmlFor` or wrap the input inside it.";
14466
- const DEFAULT_CONTROL_COMPONENTS = [
14477
+ const DEFAULT_CONTROL_COMPONENTS = new Set([
14467
14478
  "input",
14468
14479
  "meter",
14469
14480
  "output",
14470
14481
  "progress",
14471
14482
  "select",
14472
14483
  "textarea"
14473
- ];
14484
+ ]);
14474
14485
  const DEFAULT_LABEL_ATTRIBUTES = [
14475
14486
  "alt",
14476
14487
  "aria-label",
@@ -14482,8 +14493,8 @@ const resolveSettings$24 = (settings) => {
14482
14493
  const jsxA11y = settings?.["jsx-a11y"];
14483
14494
  const forAttributes = (typeof jsxA11y === "object" && jsxA11y !== null ? jsxA11y : {}).attributes?.for ?? ["htmlFor"];
14484
14495
  return {
14485
- labelComponents: ["label", ...ruleSettings.labelComponents ?? []].sort(),
14486
- labelAttributes: [...new Set([...DEFAULT_LABEL_ATTRIBUTES, ...ruleSettings.labelAttributes ?? []])].sort(),
14496
+ labelComponents: new Set(["label", ...ruleSettings.labelComponents ?? []]),
14497
+ labelAttributes: new Set([...DEFAULT_LABEL_ATTRIBUTES, ...ruleSettings.labelAttributes ?? []]),
14487
14498
  controlComponents: ruleSettings.controlComponents ?? [],
14488
14499
  assert: ruleSettings.assert ?? "either",
14489
14500
  depth: Math.min(ruleSettings.depth ?? 5, 25),
@@ -14491,7 +14502,7 @@ const resolveSettings$24 = (settings) => {
14491
14502
  };
14492
14503
  };
14493
14504
  const isControlComponent = (tagName, controlComponents) => {
14494
- if (DEFAULT_CONTROL_COMPONENTS.includes(tagName)) return true;
14505
+ if (DEFAULT_CONTROL_COMPONENTS.has(tagName)) return true;
14495
14506
  return controlComponents.some((pattern) => compileGlob(pattern).test(tagName));
14496
14507
  };
14497
14508
  const searchForNestedControl = (child, currentDepth, searchContext) => {
@@ -14517,7 +14528,7 @@ const searchForAccessibleLabel = (child, currentDepth, searchContext) => {
14517
14528
  const attributeName = attribute.name;
14518
14529
  if (!isNodeOfType(attributeName, "JSXIdentifier")) continue;
14519
14530
  const propName = getJsxAttributeName(attributeName);
14520
- if (!propName || !searchContext.labelAttributes.includes(propName)) continue;
14531
+ if (!propName || !searchContext.labelAttributes.has(propName)) continue;
14521
14532
  const attributeValue = attribute.value;
14522
14533
  if (!attributeValue) continue;
14523
14534
  if (isNodeOfType(attributeValue, "Literal") && typeof attributeValue.value === "string") {
@@ -14539,7 +14550,7 @@ const hasAccessibleLabel = (element, searchContext) => {
14539
14550
  const attributeName = attribute.name;
14540
14551
  if (!isNodeOfType(attributeName, "JSXIdentifier")) continue;
14541
14552
  const propName = getJsxAttributeName(attributeName);
14542
- if (propName && searchContext.labelAttributes.includes(propName)) return true;
14553
+ if (propName && searchContext.labelAttributes.has(propName)) return true;
14543
14554
  }
14544
14555
  for (const child of element.children) if (searchForAccessibleLabel(child, 1, searchContext)) return true;
14545
14556
  return false;
@@ -14562,7 +14573,7 @@ const labelHasAssociatedControl = defineRule({
14562
14573
  if (isTestlikeFile) return;
14563
14574
  const opening = node.openingElement;
14564
14575
  const tagName = getElementType(opening, context.settings);
14565
- if (!settings.labelComponents.includes(tagName)) return;
14576
+ if (!settings.labelComponents.has(tagName)) return;
14566
14577
  const hasHtmlFor = settings.forAttributes.some((attributeName) => Boolean(hasJsxPropIgnoreCase(opening.attributes, attributeName)));
14567
14578
  const searchContext = {
14568
14579
  depth: settings.depth,
@@ -15151,9 +15162,9 @@ const resolveSettings$23 = (settings) => {
15151
15162
  const reactDoctor = settings?.["react-doctor"];
15152
15163
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.mediaHasCaption ?? {} : {};
15153
15164
  return {
15154
- audio: [...DEFAULT_AUDIO, ...ruleSettings.audio ?? []],
15155
- video: [...DEFAULT_VIDEO, ...ruleSettings.video ?? []],
15156
- track: [...DEFAULT_TRACK, ...ruleSettings.track ?? []]
15165
+ audio: new Set([...DEFAULT_AUDIO, ...ruleSettings.audio ?? []]),
15166
+ video: new Set([...DEFAULT_VIDEO, ...ruleSettings.video ?? []]),
15167
+ track: new Set([...DEFAULT_TRACK, ...ruleSettings.track ?? []])
15157
15168
  };
15158
15169
  };
15159
15170
  const evaluateMuted = (attribute) => {
@@ -15182,7 +15193,7 @@ const childMayRenderTrack = (child, trackTags, settings) => {
15182
15193
  let rendersCaptionTrack = false;
15183
15194
  walkAst(expression, (inner) => {
15184
15195
  if (rendersCaptionTrack) return false;
15185
- if (isNodeOfType(inner, "JSXElement") && trackTags.includes(getElementType(inner.openingElement, settings)) && trackKindMightBeCaptions(inner.openingElement)) {
15196
+ if (isNodeOfType(inner, "JSXElement") && trackTags.has(getElementType(inner.openingElement, settings)) && trackKindMightBeCaptions(inner.openingElement)) {
15186
15197
  rendersCaptionTrack = true;
15187
15198
  return false;
15188
15199
  }
@@ -15200,7 +15211,7 @@ const mediaHasCaption = defineRule({
15200
15211
  const settings = resolveSettings$23(context.settings);
15201
15212
  return { JSXOpeningElement(node) {
15202
15213
  const tag = getElementType(node, context.settings);
15203
- if (!(settings.audio.includes(tag) || settings.video.includes(tag))) return;
15214
+ if (!(settings.audio.has(tag) || settings.video.has(tag))) return;
15204
15215
  if (evaluateMuted(hasJsxPropIgnoreCase(node.attributes, "muted")) === true) return;
15205
15216
  const parent = node.parent;
15206
15217
  if (!parent || !isNodeOfType(parent, "JSXElement")) {
@@ -15215,7 +15226,7 @@ const mediaHasCaption = defineRule({
15215
15226
  if (!isNodeOfType(child, "JSXElement")) return false;
15216
15227
  const opening = child.openingElement;
15217
15228
  const childTag = getElementType(opening, context.settings);
15218
- if (!settings.track.includes(childTag)) return false;
15229
+ if (!settings.track.has(childTag)) return false;
15219
15230
  const kindAttribute = hasJsxPropIgnoreCase(opening.attributes, "kind");
15220
15231
  if (!kindAttribute) return false;
15221
15232
  let kindValue = kindAttribute.value;
@@ -24630,14 +24641,14 @@ const collectRoleBranches = (expression, out) => {
24630
24641
  out.hasOpaqueBranch = true;
24631
24642
  };
24632
24643
  const buildMessage$12 = (tag) => `Keyboard & screen reader users can't trigger this \`<${tag}>\` because it isn't interactive, so use a button or link or add an interactive role.`;
24633
- const INTERACTIVE_HANDLERS = [
24644
+ const INTERACTIVE_HANDLERS_LOWER = new Set([
24634
24645
  "onClick",
24635
24646
  "onMouseDown",
24636
24647
  "onMouseUp",
24637
24648
  "onKeyDown",
24638
24649
  "onKeyPress",
24639
24650
  "onKeyUp"
24640
- ];
24651
+ ].map((handlerName) => handlerName.toLowerCase()));
24641
24652
  const noNoninteractiveElementInteractions = defineRule({
24642
24653
  id: "no-noninteractive-element-interactions",
24643
24654
  title: "Handler on non-interactive element",
@@ -24652,7 +24663,16 @@ const noNoninteractiveElementInteractions = defineRule({
24652
24663
  const tag = getElementType(node, context.settings);
24653
24664
  if (!NON_INTERACTIVE_ELEMENTS.has(tag)) return;
24654
24665
  if (tag === "label") return;
24655
- if (!INTERACTIVE_HANDLERS.some((handler) => hasJsxPropIgnoreCase(node.attributes, handler))) return;
24666
+ let hasHandler = false;
24667
+ for (const attribute of node.attributes) {
24668
+ if (!isNodeOfType(attribute, "JSXAttribute")) continue;
24669
+ const attributeName = getJsxAttributeName(attribute.name);
24670
+ if (attributeName && INTERACTIVE_HANDLERS_LOWER.has(attributeName.toLowerCase())) {
24671
+ hasHandler = true;
24672
+ break;
24673
+ }
24674
+ }
24675
+ if (!hasHandler) return;
24656
24676
  if (isHiddenFromScreenReader(node, context.settings)) return;
24657
24677
  const roleAttr = hasJsxPropIgnoreCase(node.attributes, "role");
24658
24678
  if (roleAttr) {
@@ -25965,16 +25985,22 @@ const ELEMENT_ROLE_PAIRS = [
25965
25985
  ["tr", "row"],
25966
25986
  ["ul", "list"]
25967
25987
  ];
25968
- const getElementImplicitRoles = (tag) => {
25969
- const out = [];
25970
- for (const [element, role] of ELEMENT_ROLE_PAIRS) if (element === tag && !out.includes(role)) out.push(role);
25971
- return out;
25972
- };
25973
- const getTagsForRole = (role) => {
25974
- const out = [];
25975
- for (const [element, r] of ELEMENT_ROLE_PAIRS) if (r === role && !out.includes(element)) out.push(element);
25976
- return out;
25988
+ const EMPTY_ROLE_LIST = [];
25989
+ const buildLookup = (pairs, keyIndex) => {
25990
+ const lookup = /* @__PURE__ */ new Map();
25991
+ for (const pair of pairs) {
25992
+ const key = pair[keyIndex];
25993
+ const value = pair[keyIndex === 0 ? 1 : 0];
25994
+ const values = lookup.get(key);
25995
+ if (!values) lookup.set(key, [value]);
25996
+ else if (!values.includes(value)) values.push(value);
25997
+ }
25998
+ return lookup;
25977
25999
  };
26000
+ const IMPLICIT_ROLES_BY_TAG = buildLookup(ELEMENT_ROLE_PAIRS, 0);
26001
+ const TAGS_BY_ROLE = buildLookup(ELEMENT_ROLE_PAIRS, 1);
26002
+ const getElementImplicitRoles = (tag) => IMPLICIT_ROLES_BY_TAG.get(tag) ?? EMPTY_ROLE_LIST;
26003
+ const getTagsForRole = (role) => TAGS_BY_ROLE.get(role) ?? EMPTY_ROLE_LIST;
25978
26004
  //#endregion
25979
26005
  //#region src/plugin/utils/get-implicit-role.ts
25980
26006
  const getImplicitRole = (node, elementType) => {
@@ -26474,6 +26500,7 @@ const TANSTACK_ROUTE_PROPERTY_ORDER = [
26474
26500
  "headers",
26475
26501
  "remountDeps"
26476
26502
  ];
26503
+ const TANSTACK_ROUTE_PROPERTY_INDEX = new Map(TANSTACK_ROUTE_PROPERTY_ORDER.map((propertyName, orderIndex) => [propertyName, orderIndex]));
26477
26504
  const TANSTACK_ROUTE_CREATION_FUNCTIONS = new Set([
26478
26505
  "createFileRoute",
26479
26506
  "createRoute",
@@ -26489,6 +26516,7 @@ const TANSTACK_MIDDLEWARE_METHOD_ORDER = [
26489
26516
  "server",
26490
26517
  "handler"
26491
26518
  ];
26519
+ const TANSTACK_MIDDLEWARE_METHOD_INDEX = new Map(TANSTACK_MIDDLEWARE_METHOD_ORDER.map((methodName, orderIndex) => [methodName, orderIndex]));
26492
26520
  const TANSTACK_REDIRECT_FUNCTIONS = new Set(["redirect", "notFound"]);
26493
26521
  const TANSTACK_SERVER_FN_FILE_PATTERN = /\.functions(\.[jt]sx?)?$/;
26494
26522
  const TANSTACK_QUERY_HOOKS = new Set([
@@ -27193,14 +27221,21 @@ const noStaticElementInteractions = defineRule({
27193
27221
  category: "Accessibility",
27194
27222
  create: (context) => {
27195
27223
  const settings = resolveSettings$12(context.settings);
27224
+ const handlersLower = new Set(settings.handlers.map((handlerName) => handlerName.toLowerCase()));
27196
27225
  const isTestlikeFile = isTestlikeFilename(context.filename);
27197
27226
  return { JSXOpeningElement(node) {
27198
27227
  if (isTestlikeFile) return;
27199
27228
  let hasNonBlockerHandler = false;
27200
27229
  let hasAnyHandler = false;
27201
- for (const handler of settings.handlers) {
27202
- const attribute = hasJsxPropIgnoreCase(node.attributes, handler);
27203
- if (!attribute) continue;
27230
+ let seenHandlerNames = null;
27231
+ for (const attribute of node.attributes) {
27232
+ if (!isNodeOfType(attribute, "JSXAttribute")) continue;
27233
+ const attributeName = getJsxAttributeName(attribute.name);
27234
+ if (!attributeName) continue;
27235
+ const handlerNameLower = attributeName.toLowerCase();
27236
+ if (!handlersLower.has(handlerNameLower)) continue;
27237
+ if (seenHandlerNames?.has(handlerNameLower)) continue;
27238
+ (seenHandlerNames ??= /* @__PURE__ */ new Set()).add(handlerNameLower);
27204
27239
  if (isNullValue(attribute)) continue;
27205
27240
  hasAnyHandler = true;
27206
27241
  if (!isPureEventBlockerHandler(attribute)) {
@@ -28381,13 +28416,13 @@ const DOM_ATTRIBUTES_TO_CAMEL = new Map([
28381
28416
  ["xml:lang", "xmlLang"],
28382
28417
  ["xml:space", "xmlSpace"]
28383
28418
  ]);
28384
- const DOM_PROPERTIES_IGNORE_CASE = [
28419
+ const DOM_PROPERTIES_IGNORE_CASE_BY_LOWER = new Map([
28385
28420
  "charset",
28386
28421
  "allowFullScreen",
28387
28422
  "webkitAllowFullScreen",
28388
28423
  "mozAllowFullScreen",
28389
28424
  "webkitDirectory"
28390
- ];
28425
+ ].map((name) => [name.toLowerCase(), name]));
28391
28426
  //#endregion
28392
28427
  //#region src/plugin/constants/dom-property-tags.ts
28393
28428
  const DOM_PROPERTY_TO_ALLOWED_TAGS = new Map([
@@ -28591,10 +28626,7 @@ const matchesHtmlTagConventions = (tagName) => {
28591
28626
  if (!(firstCharacter >= 97 && firstCharacter <= 122)) return false;
28592
28627
  return !tagName.includes("-");
28593
28628
  };
28594
- const normalizeAttributeCase = (name) => {
28595
- for (const ignoreCaseName of DOM_PROPERTIES_IGNORE_CASE) if (ignoreCaseName.toLowerCase() === name.toLowerCase()) return ignoreCaseName;
28596
- return name;
28597
- };
28629
+ const normalizeAttributeCase = (name) => DOM_PROPERTIES_IGNORE_CASE_BY_LOWER.get(name.toLowerCase()) ?? name;
28598
28630
  const hasUppercaseChar = (input) => /[A-Z]/.test(input);
28599
28631
  const INVALID_PROP_ON_TAG = (propName, allowedTags) => `React ignores \`${propName}\` here because it only works on these tags: ${allowedTags}.`;
28600
28632
  const DATA_LOWERCASE_REQUIRED = () => `React drops this \`data-*\` prop because of its capital letters.`;
@@ -34791,9 +34823,10 @@ const resolveTextBoundaryName = (openingElement) => {
34791
34823
  if (isNodeOfType(openingElement.name, "JSXNamespacedName")) return openingElement.name.namespace.name;
34792
34824
  return resolveJsxElementName(openingElement);
34793
34825
  };
34826
+ const TEXT_COMPONENT_KEYWORDS = [...REACT_NATIVE_TEXT_COMPONENT_KEYWORDS];
34794
34827
  const isTextHandlingComponent = (elementName) => {
34795
34828
  if (REACT_NATIVE_TEXT_COMPONENTS.has(elementName)) return true;
34796
- return [...REACT_NATIVE_TEXT_COMPONENT_KEYWORDS].some((keyword) => elementName.includes(keyword));
34829
+ return TEXT_COMPONENT_KEYWORDS.some((keyword) => elementName.includes(keyword));
34797
34830
  };
34798
34831
  const isTransparentTextWrapper = (elementName) => elementName !== null && REACT_NATIVE_TEXT_TRANSPARENT_COMPONENTS.has(elementName);
34799
34832
  const isInsideTextHandlingComponent = (node) => {
@@ -41297,10 +41330,10 @@ const tanstackStartRoutePropertyOrder = defineRule({
41297
41330
  const propertyName = getPropertyKeyName(property);
41298
41331
  if (propertyName !== null) orderedPropertyNames.push(propertyName);
41299
41332
  }
41300
- const sensitiveProperties = orderedPropertyNames.filter((propertyName) => TANSTACK_ROUTE_PROPERTY_ORDER.includes(propertyName));
41333
+ const sensitiveProperties = orderedPropertyNames.filter((propertyName) => TANSTACK_ROUTE_PROPERTY_INDEX.has(propertyName));
41301
41334
  let lastIndex = -1;
41302
41335
  for (const propertyName of sensitiveProperties) {
41303
- const currentIndex = TANSTACK_ROUTE_PROPERTY_ORDER.indexOf(propertyName);
41336
+ const currentIndex = TANSTACK_ROUTE_PROPERTY_INDEX.get(propertyName) ?? -1;
41304
41337
  if (currentIndex < lastIndex) {
41305
41338
  const expectedBefore = TANSTACK_ROUTE_PROPERTY_ORDER[lastIndex];
41306
41339
  context.report({
@@ -41337,10 +41370,10 @@ const tanstackStartServerFnMethodOrder = defineRule({
41337
41370
  } else return;
41338
41371
  const ownMethodName = isNodeOfType(node.callee.property, "Identifier") ? node.callee.property.name : null;
41339
41372
  if (methodNames[methodNames.length - 1] !== ownMethodName) return;
41340
- const orderSensitiveMethods = methodNames.filter((name) => TANSTACK_MIDDLEWARE_METHOD_ORDER.includes(toMethodOrderToken(name)));
41373
+ const orderSensitiveMethods = methodNames.filter((name) => TANSTACK_MIDDLEWARE_METHOD_INDEX.has(toMethodOrderToken(name)));
41341
41374
  let lastIndex = -1;
41342
41375
  for (const methodName of orderSensitiveMethods) {
41343
- const currentIndex = TANSTACK_MIDDLEWARE_METHOD_ORDER.indexOf(toMethodOrderToken(methodName));
41376
+ const currentIndex = TANSTACK_MIDDLEWARE_METHOD_INDEX.get(toMethodOrderToken(methodName)) ?? -1;
41344
41377
  if (currentIndex < lastIndex) {
41345
41378
  const expectedBefore = TANSTACK_MIDDLEWARE_METHOD_ORDER[lastIndex];
41346
41379
  context.report({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-react-doctor",
3
- "version": "0.6.2-dev.d8628d7",
3
+ "version": "0.6.2-dev.da3b19c",
4
4
  "description": "React Doctor rules for oxlint.",
5
5
  "keywords": [
6
6
  "accessibility",