oxlint-plugin-react-doctor 0.2.4 → 0.2.6

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
@@ -529,26 +529,6 @@ declare const REACT_DOCTOR_RULES: readonly [{
529
529
  readonly recommendation?: string;
530
530
  readonly create: (context: RuleContext) => RuleVisitors;
531
531
  };
532
- }, {
533
- readonly key: "react-doctor/design-no-bold-heading";
534
- readonly id: "design-no-bold-heading";
535
- readonly source: "react-doctor";
536
- readonly originallyExternal: false;
537
- readonly framework: "global";
538
- readonly category: "Architecture";
539
- readonly severity: "warn";
540
- readonly rule: {
541
- readonly framework: "global";
542
- readonly category: "Architecture";
543
- readonly id: string;
544
- readonly severity: RuleSeverity;
545
- readonly requires?: ReadonlyArray<string>;
546
- readonly disabledBy?: ReadonlyArray<string>;
547
- readonly tags?: ReadonlyArray<string>;
548
- readonly defaultEnabled?: boolean;
549
- readonly recommendation?: string;
550
- readonly create: (context: RuleContext) => RuleVisitors;
551
- };
552
532
  }, {
553
533
  readonly key: "react-doctor/design-no-em-dash-in-jsx-text";
554
534
  readonly id: "design-no-em-dash-in-jsx-text";
@@ -6315,26 +6295,6 @@ declare const RULES: readonly [{
6315
6295
  readonly recommendation?: string;
6316
6296
  readonly create: (context: RuleContext) => RuleVisitors;
6317
6297
  };
6318
- }, {
6319
- readonly key: "react-doctor/design-no-bold-heading";
6320
- readonly id: "design-no-bold-heading";
6321
- readonly source: "react-doctor";
6322
- readonly originallyExternal: false;
6323
- readonly framework: "global";
6324
- readonly category: "Architecture";
6325
- readonly severity: "warn";
6326
- readonly rule: {
6327
- readonly framework: "global";
6328
- readonly category: "Architecture";
6329
- readonly id: string;
6330
- readonly severity: RuleSeverity;
6331
- readonly requires?: ReadonlyArray<string>;
6332
- readonly disabledBy?: ReadonlyArray<string>;
6333
- readonly tags?: ReadonlyArray<string>;
6334
- readonly defaultEnabled?: boolean;
6335
- readonly recommendation?: string;
6336
- readonly create: (context: RuleContext) => RuleVisitors;
6337
- };
6338
6298
  }, {
6339
6299
  readonly key: "react-doctor/design-no-em-dash-in-jsx-text";
6340
6300
  readonly id: "design-no-em-dash-in-jsx-text";
package/dist/index.js CHANGED
@@ -943,6 +943,15 @@ const anchorHasContent = defineRule({
943
943
  } })
944
944
  });
945
945
  //#endregion
946
+ //#region src/plugin/utils/get-static-template-literal-value.ts
947
+ const getStaticTemplateLiteralValue = (templateLiteral) => {
948
+ const expressions = templateLiteral.expressions ?? [];
949
+ const quasis = templateLiteral.quasis ?? [];
950
+ if (expressions.length !== 0 || quasis.length !== 1) return null;
951
+ const value = quasis[0]?.value;
952
+ return value?.cooked ?? value?.raw ?? null;
953
+ };
954
+ //#endregion
946
955
  //#region src/plugin/rules/a11y/anchor-is-valid.ts
947
956
  const MESSAGE_MISSING_HREF = "`<a>` element is missing an `href` — anchors without `href` aren't keyboard-focusable; use `<button>` for actions.";
948
957
  const MESSAGE_INCORRECT_HREF = "`<a>` element has invalid `href` (`#`, `javascript:`, or empty) — provide a real destination.";
@@ -971,8 +980,8 @@ const checkValueIsEmptyOrInvalid = (value, validHrefs) => {
971
980
  if (typeof expression.value === "string") return isInvalidHref(expression.value, validHrefs);
972
981
  }
973
982
  if (isNodeOfType(expression, "TemplateLiteral")) {
974
- if (expression.expressions.length > 0) return false;
975
- return isInvalidHref(expression.quasis[0]?.value.cooked ?? "", validHrefs);
983
+ const staticValue = getStaticTemplateLiteralValue(expression);
984
+ return staticValue === null ? false : isInvalidHref(staticValue, validHrefs);
976
985
  }
977
986
  }
978
987
  if (isNodeOfType(value, "JSXFragment")) return true;
@@ -1303,9 +1312,10 @@ const parseJsxValue = (value) => {
1303
1312
  }
1304
1313
  }
1305
1314
  if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "-" && isNodeOfType(expression.argument, "Literal") && typeof expression.argument.value === "number") return -expression.argument.value;
1306
- if (isNodeOfType(expression, "TemplateLiteral") && expression.expressions.length === 0 && expression.quasis.length === 1) {
1307
- const cooked = expression.quasis[0].value.cooked ?? "";
1308
- const parsed = Number(cooked);
1315
+ if (isNodeOfType(expression, "TemplateLiteral")) {
1316
+ const staticValue = getStaticTemplateLiteralValue(expression);
1317
+ if (staticValue === null) return null;
1318
+ const parsed = Number(staticValue);
1309
1319
  return Number.isFinite(parsed) ? parsed : null;
1310
1320
  }
1311
1321
  if (isNodeOfType(expression, "ConditionalExpression")) {
@@ -1670,8 +1680,8 @@ const expressionToBoolean = (expression) => {
1670
1680
  return null;
1671
1681
  }
1672
1682
  if (isNodeOfType(expression, "TemplateLiteral")) {
1673
- if (expression.expressions.length === 0 && expression.quasis.length === 1) return (expression.quasis[0]?.value.cooked ?? "").length > 0;
1674
- return null;
1683
+ const staticValue = getStaticTemplateLiteralValue(expression);
1684
+ return staticValue === null ? null : staticValue.length > 0;
1675
1685
  }
1676
1686
  if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "!") {
1677
1687
  const inner = expressionToBoolean(expression.argument);
@@ -1702,10 +1712,7 @@ const parseAriaValueAsString = (value, booleanAsString) => {
1702
1712
  if (typeof expression.value === "boolean") return booleanAsString ? String(expression.value) : null;
1703
1713
  return null;
1704
1714
  }
1705
- if (isNodeOfType(expression, "TemplateLiteral")) {
1706
- if (expression.expressions.length === 0 && expression.quasis.length === 1) return (expression.quasis[0]?.value.cooked ?? "").toLowerCase();
1707
- return null;
1708
- }
1715
+ if (isNodeOfType(expression, "TemplateLiteral")) return getStaticTemplateLiteralValue(expression)?.toLowerCase() ?? null;
1709
1716
  if (booleanAsString && isNodeOfType(expression, "UnaryExpression") && expression.operator === "!") {
1710
1717
  const inner = expressionToBoolean(expression.argument);
1711
1718
  if (inner === null) return null;
@@ -1718,7 +1725,7 @@ const isMultiQuasiTemplate = (value) => {
1718
1725
  if (!isNodeOfType(value, "JSXExpressionContainer")) return false;
1719
1726
  const expression = value.expression;
1720
1727
  if (!isNodeOfType(expression, "TemplateLiteral")) return false;
1721
- return expression.expressions.length > 0;
1728
+ return (expression.expressions ?? []).length > 0;
1722
1729
  };
1723
1730
  const isValidValueForType = (propType, value) => {
1724
1731
  if (!isTargetLiteralValue(value)) return true;
@@ -3102,9 +3109,9 @@ const isValidTypeValue = (rawValue, settings) => {
3102
3109
  };
3103
3110
  const isProvenValidExpression = (expression, settings) => {
3104
3111
  if (isNodeOfType(expression, "Literal") && typeof expression.value === "string") return isValidTypeValue(expression.value, settings);
3105
- if (isNodeOfType(expression, "TemplateLiteral") && expression.expressions.length === 0 && expression.quasis.length === 1) {
3106
- const cookedValue = expression.quasis[0].value.cooked;
3107
- if (typeof cookedValue === "string") return isValidTypeValue(cookedValue, settings);
3112
+ if (isNodeOfType(expression, "TemplateLiteral")) {
3113
+ const staticValue = getStaticTemplateLiteralValue(expression);
3114
+ if (staticValue !== null) return isValidTypeValue(staticValue, settings);
3108
3115
  }
3109
3116
  if (isNodeOfType(expression, "ConditionalExpression")) return isProvenValidExpression(expression.consequent, settings) && isProvenValidExpression(expression.alternate, settings);
3110
3117
  return false;
@@ -3520,19 +3527,6 @@ const controlHasAssociatedLabel = defineRule({
3520
3527
  }
3521
3528
  });
3522
3529
  const LONG_TRANSITION_DURATION_THRESHOLD_MS = 1e3;
3523
- const HEADING_TAG_NAMES = new Set([
3524
- "h1",
3525
- "h2",
3526
- "h3",
3527
- "h4",
3528
- "h5",
3529
- "h6"
3530
- ]);
3531
- const HEAVY_HEADING_TAILWIND_WEIGHTS = new Set([
3532
- "font-bold",
3533
- "font-extrabold",
3534
- "font-black"
3535
- ]);
3536
3530
  const VAGUE_BUTTON_LABELS = new Set([
3537
3531
  "continue",
3538
3532
  "submit",
@@ -3602,85 +3596,6 @@ const getOpeningElementTagName = (openingElement) => {
3602
3596
  return null;
3603
3597
  };
3604
3598
  //#endregion
3605
- //#region src/plugin/rules/react-ui/utils/get-class-name-literal.ts
3606
- const getClassNameLiteral = (classAttribute) => {
3607
- if (!isNodeOfType(classAttribute, "JSXAttribute")) return null;
3608
- if (!classAttribute.value) return null;
3609
- if (isNodeOfType(classAttribute.value, "Literal") && typeof classAttribute.value.value === "string") return classAttribute.value.value;
3610
- if (isNodeOfType(classAttribute.value, "JSXExpressionContainer")) {
3611
- const expression = classAttribute.value.expression;
3612
- if (isNodeOfType(expression, "Literal") && typeof expression.value === "string") return expression.value;
3613
- if (isNodeOfType(expression, "TemplateLiteral") && expression.quasis?.length === 1) return expression.quasis[0].value?.raw ?? null;
3614
- }
3615
- return null;
3616
- };
3617
- //#endregion
3618
- //#region src/plugin/rules/react-ui/no-bold-heading.ts
3619
- const getInlineStyleObjectExpression = (jsxAttribute) => {
3620
- if (!isNodeOfType(jsxAttribute, "JSXAttribute")) return null;
3621
- if (!isNodeOfType(jsxAttribute.name, "JSXIdentifier") || jsxAttribute.name.name !== "style") return null;
3622
- if (!isNodeOfType(jsxAttribute.value, "JSXExpressionContainer")) return null;
3623
- const expression = jsxAttribute.value.expression;
3624
- if (!isNodeOfType(expression, "ObjectExpression")) return null;
3625
- return expression;
3626
- };
3627
- const getStylePropertyKeyName = (objectProperty) => {
3628
- if (!isNodeOfType(objectProperty, "Property")) return null;
3629
- if (isNodeOfType(objectProperty.key, "Identifier")) return objectProperty.key.name;
3630
- if (isNodeOfType(objectProperty.key, "Literal") && typeof objectProperty.key.value === "string") return objectProperty.key.value;
3631
- return null;
3632
- };
3633
- const getStylePropertyNumericValue = (objectProperty) => {
3634
- if (!isNodeOfType(objectProperty, "Property")) return null;
3635
- const valueNode = objectProperty.value;
3636
- if (!valueNode) return null;
3637
- if (isNodeOfType(valueNode, "Literal") && typeof valueNode.value === "number") return valueNode.value;
3638
- if (isNodeOfType(valueNode, "Literal") && typeof valueNode.value === "string") {
3639
- const parsed = parseFloat(valueNode.value);
3640
- return Number.isFinite(parsed) ? parsed : null;
3641
- }
3642
- return null;
3643
- };
3644
- const noBoldHeading = defineRule({
3645
- id: "design-no-bold-heading",
3646
- tags: ["design", "test-noise"],
3647
- severity: "warn",
3648
- category: "Architecture",
3649
- recommendation: "Use `font-semibold` (600) or `font-medium` (500) on headings — 700+ crushes letter counter shapes at display sizes",
3650
- create: (context) => ({ JSXOpeningElement(openingNode) {
3651
- const tagName = getOpeningElementTagName(openingNode);
3652
- if (!tagName || !HEADING_TAG_NAMES.has(tagName)) return;
3653
- const classAttribute = findJsxAttribute(openingNode.attributes ?? [], "className");
3654
- if (classAttribute) {
3655
- const classNameLiteral = getClassNameLiteral(classAttribute);
3656
- if (classNameLiteral) {
3657
- for (const tailwindWeightToken of HEAVY_HEADING_TAILWIND_WEIGHTS) if (new RegExp(`(?:^|\\s)${tailwindWeightToken}(?:$|\\s|:)`).test(classNameLiteral)) {
3658
- context.report({
3659
- node: classAttribute,
3660
- message: `${tailwindWeightToken} on <${tagName}> crushes counter shapes at display sizes — use font-semibold (600) or font-medium (500)`
3661
- });
3662
- return;
3663
- }
3664
- }
3665
- }
3666
- const styleAttribute = findJsxAttribute(openingNode.attributes ?? [], "style");
3667
- if (!styleAttribute) return;
3668
- const styleObject = getInlineStyleObjectExpression(styleAttribute);
3669
- if (!styleObject) return;
3670
- for (const objectProperty of styleObject.properties ?? []) {
3671
- if (getStylePropertyKeyName(objectProperty) !== "fontWeight") continue;
3672
- const numericWeight = getStylePropertyNumericValue(objectProperty);
3673
- if (numericWeight !== null && numericWeight >= 700) {
3674
- context.report({
3675
- node: objectProperty,
3676
- message: `fontWeight: ${numericWeight} on <${tagName}> crushes counter shapes at display sizes — use 500 or 600`
3677
- });
3678
- return;
3679
- }
3680
- }
3681
- } })
3682
- });
3683
- //#endregion
3684
3599
  //#region src/plugin/rules/react-ui/utils/is-inside-excluded-typography-ancestor.ts
3685
3600
  const isInsideExcludedTypographyAncestor = (jsxTextNode) => {
3686
3601
  let cursor = jsxTextNode.parent;
@@ -3714,6 +3629,19 @@ const noEmDashInJsxText = defineRule({
3714
3629
  } })
3715
3630
  });
3716
3631
  //#endregion
3632
+ //#region src/plugin/rules/react-ui/utils/get-class-name-literal.ts
3633
+ const getClassNameLiteral = (classAttribute) => {
3634
+ if (!isNodeOfType(classAttribute, "JSXAttribute")) return null;
3635
+ if (!classAttribute.value) return null;
3636
+ if (isNodeOfType(classAttribute.value, "Literal") && typeof classAttribute.value.value === "string") return classAttribute.value.value;
3637
+ if (isNodeOfType(classAttribute.value, "JSXExpressionContainer")) {
3638
+ const expression = classAttribute.value.expression;
3639
+ if (isNodeOfType(expression, "Literal") && typeof expression.value === "string") return expression.value;
3640
+ if (isNodeOfType(expression, "TemplateLiteral") && expression.quasis?.length === 1) return expression.quasis[0].value?.raw ?? null;
3641
+ }
3642
+ return null;
3643
+ };
3644
+ //#endregion
3717
3645
  //#region src/plugin/rules/react-ui/utils/collect-axis-shorthand-pairs.ts
3718
3646
  const collectAxisShorthandPairs = (classNameValue, horizontalPattern, verticalPattern) => {
3719
3647
  const horizontalValues = /* @__PURE__ */ new Set();
@@ -3845,10 +3773,10 @@ const collectJsxLabelText = (jsxElementNode) => {
3845
3773
  collectedFragments.push(expression.value);
3846
3774
  continue;
3847
3775
  }
3848
- if (isNodeOfType(expression, "TemplateLiteral") && expression.quasis?.length === 1) {
3849
- const rawTemplate = expression.quasis[0].value?.raw;
3850
- if (typeof rawTemplate === "string" && expression.expressions.length === 0) {
3851
- collectedFragments.push(rawTemplate);
3776
+ if (isNodeOfType(expression, "TemplateLiteral")) {
3777
+ const staticValue = getStaticTemplateLiteralValue(expression);
3778
+ if (staticValue !== null) {
3779
+ collectedFragments.push(staticValue);
3852
3780
  continue;
3853
3781
  }
3854
3782
  }
@@ -5031,7 +4959,7 @@ const symbolHasStableHookOrigin = (symbol) => {
5031
4959
  const initializer = unwrapExpression(initializerRaw);
5032
4960
  if (symbol.kind === "const") {
5033
4961
  if (isNodeOfType(initializer, "Literal") && (initializer.value === null || typeof initializer.value === "number" || typeof initializer.value === "string" || typeof initializer.value === "boolean")) return true;
5034
- if (isNodeOfType(initializer, "TemplateLiteral") && initializer.expressions.length === 0) return true;
4962
+ if (isNodeOfType(initializer, "TemplateLiteral") && getStaticTemplateLiteralValue(initializer) !== null) return true;
5035
4963
  }
5036
4964
  if (!isNodeOfType(initializer, "CallExpression")) return false;
5037
4965
  const initializerHookName = getHookName(initializer.callee);
@@ -5251,7 +5179,7 @@ const isOutsideAllFunctions = (symbol) => {
5251
5179
  }
5252
5180
  return true;
5253
5181
  };
5254
- const isLiteralOrEmptyTemplate = (node) => isNodeOfType(node, "Literal") || isNodeOfType(node, "TemplateLiteral") && node.expressions.length === 0;
5182
+ const isLiteralOrEmptyTemplate = (node) => isNodeOfType(node, "Literal") || isNodeOfType(node, "TemplateLiteral") && getStaticTemplateLiteralValue(node) !== null;
5255
5183
  const isNonStringLiteral = (node) => isNodeOfType(node, "Literal") && typeof node.value !== "string";
5256
5184
  const isMatchingDepOrPrefix = (declaredKey, captureKey) => captureKey === declaredKey || captureKey.startsWith(`${declaredKey}.`);
5257
5185
  const hasBroaderDeclaredDependency = (declaredKey, declaredKeys) => {
@@ -6169,11 +6097,8 @@ const evaluateLang = (attributeValue) => {
6169
6097
  return "ok";
6170
6098
  }
6171
6099
  if (isNodeOfType(expression, "TemplateLiteral")) {
6172
- if (expression.expressions.length === 0 && expression.quasis.length === 1) {
6173
- const cooked = expression.quasis[0].value.cooked;
6174
- return cooked && cooked.length > 0 ? "ok" : "empty";
6175
- }
6176
- return "ok";
6100
+ const staticValue = getStaticTemplateLiteralValue(expression);
6101
+ return staticValue === null ? "ok" : staticValue.length > 0 ? "ok" : "empty";
6177
6102
  }
6178
6103
  return "ok";
6179
6104
  }
@@ -6235,11 +6160,8 @@ const evaluateTitleValue = (value) => {
6235
6160
  return "dynamic-ok";
6236
6161
  }
6237
6162
  if (isNodeOfType(expression, "TemplateLiteral")) {
6238
- if (expression.expressions.length === 0 && expression.quasis.length === 1) {
6239
- const cooked = expression.quasis[0].value.cooked;
6240
- return cooked && cooked.length > 0 ? "ok" : "empty";
6241
- }
6242
- return "dynamic-ok";
6163
+ const staticValue = getStaticTemplateLiteralValue(expression);
6164
+ return staticValue === null ? "dynamic-ok" : staticValue.length > 0 ? "ok" : "empty";
6243
6165
  }
6244
6166
  return "dynamic-ok";
6245
6167
  }
@@ -7513,10 +7435,9 @@ const checkExpressionContainer = (container, parentIsAttribute, context, setting
7513
7435
  return;
7514
7436
  }
7515
7437
  if (isNodeOfType(expression, "TemplateLiteral") && allowed === "never") {
7516
- if (expression.expressions.length !== 0 || expression.quasis.length !== 1) return;
7517
- const quasi = expression.quasis[0];
7518
- const rawSource = quasi.value.raw ?? "";
7519
- const cooked = quasi.value.cooked ?? "";
7438
+ const cooked = getStaticTemplateLiteralValue(expression);
7439
+ if (cooked === null) return;
7440
+ const rawSource = expression.quasis?.[0]?.value.raw ?? "";
7520
7441
  if (!parentIsAttribute && containsAnyQuote(cooked)) return;
7521
7442
  if (isAllowedStringLikeInContainer(rawSource, container, parentIsAttribute)) return;
7522
7443
  context.report({
@@ -7883,8 +7804,7 @@ const resolveSettings$31 = (settings) => {
7883
7804
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.jsxKey ?? {} : {};
7884
7805
  return {
7885
7806
  checkKeyMustBeforeSpread: ruleSettings.checkKeyMustBeforeSpread ?? true,
7886
- warnOnDuplicates: ruleSettings.warnOnDuplicates ?? false,
7887
- checkFragmentShorthand: ruleSettings.checkFragmentShorthand ?? true
7807
+ warnOnDuplicates: ruleSettings.warnOnDuplicates ?? false
7888
7808
  };
7889
7809
  };
7890
7810
  const findEnclosingIteratorContext = (jsxNode) => {
@@ -7999,10 +7919,10 @@ const getKeyAttributeValueString = (openingElement) => {
7999
7919
  };
8000
7920
  return null;
8001
7921
  }
8002
- if (isNodeOfType(expression, "TemplateLiteral") && expression.expressions.length === 0 && expression.quasis.length === 1) {
8003
- const cookedValue = expression.quasis[0].value.cooked;
8004
- if (typeof cookedValue === "string") return {
8005
- keyValue: cookedValue,
7922
+ if (isNodeOfType(expression, "TemplateLiteral")) {
7923
+ const staticValue = getStaticTemplateLiteralValue(expression);
7924
+ if (staticValue !== null) return {
7925
+ keyValue: staticValue,
8006
7926
  node: attribute
8007
7927
  };
8008
7928
  }
@@ -8042,16 +7962,6 @@ const jsxKey = defineRule({
8042
7962
  message: enclosingContext.kind === "array" ? MISSING_KEY_ARRAY : MISSING_KEY_ITERATOR
8043
7963
  });
8044
7964
  },
8045
- JSXFragment(node) {
8046
- if (!settings.checkFragmentShorthand) return;
8047
- const enclosingContext = findEnclosingIteratorContext(node);
8048
- if (!enclosingContext) return;
8049
- if (isWithinChildrenToArray(node)) return;
8050
- context.report({
8051
- node,
8052
- message: enclosingContext.kind === "array" ? MISSING_KEY_ARRAY : MISSING_KEY_ITERATOR
8053
- });
8054
- },
8055
7965
  ArrayExpression(node) {
8056
7966
  if (!settings.warnOnDuplicates) return;
8057
7967
  const seenKeys = /* @__PURE__ */ new Set();
@@ -13054,7 +12964,7 @@ const isFalseAttributeValue = (value) => {
13054
12964
  if (typeof expression.value === "string") return expression.value === "false";
13055
12965
  return false;
13056
12966
  }
13057
- if (isNodeOfType(expression, "TemplateLiteral")) return expression.expressions.length === 0 && expression.quasis.length === 1 && expression.quasis[0].value.cooked === "false";
12967
+ if (isNodeOfType(expression, "TemplateLiteral")) return getStaticTemplateLiteralValue(expression) === "false";
13058
12968
  }
13059
12969
  return false;
13060
12970
  };
@@ -24078,7 +23988,7 @@ const isPlatformSelectCallee = (callee) => {
24078
23988
  const isStringLiteralEqualTo = (node, expected) => {
24079
23989
  if (!node) return false;
24080
23990
  if (isNodeOfType(node, "Literal") && node.value === expected) return true;
24081
- if (isNodeOfType(node, "TemplateLiteral") && node.quasis.length === 1) return node.quasis[0]?.value?.cooked === expected;
23991
+ if (isNodeOfType(node, "TemplateLiteral")) return getStaticTemplateLiteralValue(node) === expected;
24082
23992
  return false;
24083
23993
  };
24084
23994
  const readStaticPropertyKeyName = (property) => {
@@ -29892,20 +29802,6 @@ const reactDoctorRules = [
29892
29802
  category: "Accessibility"
29893
29803
  }
29894
29804
  },
29895
- {
29896
- key: "react-doctor/design-no-bold-heading",
29897
- id: "design-no-bold-heading",
29898
- source: "react-doctor",
29899
- originallyExternal: false,
29900
- framework: "global",
29901
- category: "Architecture",
29902
- severity: "warn",
29903
- rule: {
29904
- ...noBoldHeading,
29905
- framework: "global",
29906
- category: "Architecture"
29907
- }
29908
- },
29909
29805
  {
29910
29806
  key: "react-doctor/design-no-em-dash-in-jsx-text",
29911
29807
  id: "design-no-em-dash-in-jsx-text",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-react-doctor",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
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",
@@ -50,7 +50,7 @@
50
50
  "oxc-parser": "^0.132.0"
51
51
  },
52
52
  "engines": {
53
- "node": ">=22.13.0"
53
+ "node": "^20.19.0 || >=22.12.0"
54
54
  },
55
55
  "scripts": {
56
56
  "dev": "vp pack --watch",