oxlint-plugin-react-doctor 0.7.5-dev.8fc5848 → 0.7.5-dev.d9676e2

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 +116 -231
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5934,21 +5934,6 @@ const checkedRequiresOnchangeOrReadonly = defineRule({
5934
5934
  }
5935
5935
  });
5936
5936
  //#endregion
5937
- //#region src/plugin/utils/get-static-property-key-name.ts
5938
- const getStaticPropertyKeyName = (node, options = {}) => {
5939
- if (!isNodeOfType(node, "Property")) return null;
5940
- if (node.computed) {
5941
- if (options.allowComputedString && isNodeOfType(node.key, "Literal") && typeof node.key.value === "string") return node.key.value;
5942
- return null;
5943
- }
5944
- if (isNodeOfType(node.key, "Identifier")) return node.key.name;
5945
- if (isNodeOfType(node.key, "Literal")) {
5946
- if (typeof node.key.value === "string") return node.key.value;
5947
- if (options.stringifyNonStringLiterals) return String(node.key.value);
5948
- }
5949
- return null;
5950
- };
5951
- //#endregion
5952
5937
  //#region src/plugin/utils/is-presentation-role.ts
5953
5938
  const isPresentationRole = (openingElement) => {
5954
5939
  const roleAttribute = hasJsxPropIgnoreCase(openingElement.attributes, "role");
@@ -5993,21 +5978,6 @@ const isPureEventBlockerHandler = (attribute) => {
5993
5978
  return false;
5994
5979
  };
5995
5980
  //#endregion
5996
- //#region src/plugin/utils/resolve-const-identifier-alias.ts
5997
- const resolveConstIdentifierAlias = (identifier, scopes) => {
5998
- if (!isNodeOfType(identifier, "Identifier")) return null;
5999
- const visitedSymbolIds = /* @__PURE__ */ new Set();
6000
- let symbol = scopes.symbolFor(identifier);
6001
- while (symbol?.kind === "const") {
6002
- if (visitedSymbolIds.has(symbol.id) || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
6003
- visitedSymbolIds.add(symbol.id);
6004
- const initializer = stripParenExpression(symbol.initializer);
6005
- if (!isNodeOfType(initializer, "Identifier")) return symbol;
6006
- symbol = scopes.symbolFor(initializer);
6007
- }
6008
- return symbol;
6009
- };
6010
- //#endregion
6011
5981
  //#region src/plugin/rules/a11y/click-events-have-key-events.ts
6012
5982
  const MESSAGE$57 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
6013
5983
  const KEY_HANDLERS = [
@@ -6018,63 +5988,6 @@ const KEY_HANDLERS = [
6018
5988
  "onKeyDownCapture",
6019
5989
  "onKeyPressCapture"
6020
5990
  ];
6021
- const CLICK_HANDLERS = ["onClick", "onClickCapture"];
6022
- const TRANSPARENT_SPREAD_EVENT_NAMES = new Set([...CLICK_HANDLERS, ...KEY_HANDLERS].map((eventName) => eventName.toLowerCase()));
6023
- const CONSERVATIVE_SPREAD_PROP_NAMES = new Set([
6024
- "aria-hidden",
6025
- "onmouseenter",
6026
- "onmouseover",
6027
- "role"
6028
- ]);
6029
- const resolveSpreadObjectExpression = (expression, scopes) => {
6030
- const innerExpression = stripParenExpression(expression);
6031
- if (isNodeOfType(innerExpression, "ObjectExpression")) return innerExpression;
6032
- if (!isNodeOfType(innerExpression, "Identifier")) return null;
6033
- const symbol = resolveConstIdentifierAlias(innerExpression, scopes);
6034
- if (symbol?.kind !== "const" || !symbol.initializer) return null;
6035
- const initializer = stripParenExpression(symbol.initializer);
6036
- return isNodeOfType(initializer, "ObjectExpression") ? initializer : null;
6037
- };
6038
- const collectTransparentSpreadEventNames = (expression, scopes, eventValues, visitedObjectExpressions) => {
6039
- const objectExpression = resolveSpreadObjectExpression(expression, scopes);
6040
- if (!objectExpression || visitedObjectExpressions.has(objectExpression)) return false;
6041
- visitedObjectExpressions.add(objectExpression);
6042
- let isTransparent = true;
6043
- for (const property of objectExpression.properties) {
6044
- if (isNodeOfType(property, "SpreadElement")) {
6045
- if (!collectTransparentSpreadEventNames(property.argument, scopes, eventValues, visitedObjectExpressions)) {
6046
- isTransparent = false;
6047
- break;
6048
- }
6049
- continue;
6050
- }
6051
- if (!isNodeOfType(property, "Property")) {
6052
- isTransparent = false;
6053
- break;
6054
- }
6055
- const propertyName = getStaticPropertyKeyName(property, { allowComputedString: true });
6056
- if (!propertyName) {
6057
- isTransparent = false;
6058
- break;
6059
- }
6060
- const normalizedPropertyName = propertyName.toLowerCase();
6061
- if (CONSERVATIVE_SPREAD_PROP_NAMES.has(normalizedPropertyName)) {
6062
- isTransparent = false;
6063
- break;
6064
- }
6065
- if (TRANSPARENT_SPREAD_EVENT_NAMES.has(normalizedPropertyName)) eventValues.set(normalizedPropertyName, property.value);
6066
- }
6067
- visitedObjectExpressions.delete(objectExpression);
6068
- return isTransparent;
6069
- };
6070
- const getTransparentSpreadEventValues = (attributes, scopes) => {
6071
- const eventValues = /* @__PURE__ */ new Map();
6072
- for (const attribute of attributes) {
6073
- if (!isNodeOfType(attribute, "JSXSpreadAttribute")) continue;
6074
- if (!collectTransparentSpreadEventNames(attribute.argument, scopes, eventValues, /* @__PURE__ */ new Set())) return null;
6075
- }
6076
- return eventValues;
6077
- };
6078
5991
  const FOCUSLESS_CONTAINER_TAGS = new Set([
6079
5992
  "tr",
6080
5993
  "td",
@@ -6122,10 +6035,7 @@ const isFocusForwardingFunctionBody = (body) => {
6122
6035
  };
6123
6036
  const resolveHandlerFunction = (attribute) => {
6124
6037
  if (!attribute.value || !isNodeOfType(attribute.value, "JSXExpressionContainer")) return null;
6125
- return resolveHandlerFunctionExpression(attribute.value.expression);
6126
- };
6127
- const resolveHandlerFunctionExpression = (handlerExpression) => {
6128
- let expression = handlerExpression;
6038
+ let expression = attribute.value.expression;
6129
6039
  if (isNodeOfType(expression, "Identifier")) {
6130
6040
  const binding = findVariableInitializer(expression, expression.name);
6131
6041
  if (!binding?.initializer) return null;
@@ -6224,22 +6134,18 @@ const clickEventsHaveKeyEvents = defineRule({
6224
6134
  if (!HTML_TAGS.has(tag)) return;
6225
6135
  if (tag === "label") return;
6226
6136
  if (!FOCUSLESS_CONTAINER_TAGS.has(tag) && isInteractiveElement(tag, node)) return;
6227
- const spreadEventValues = getTransparentSpreadEventValues(node.attributes, context.scopes);
6228
- if (!spreadEventValues) return;
6229
6137
  const onClick = hasJsxPropIgnoreCase(node.attributes, "onClick") ?? hasJsxPropIgnoreCase(node.attributes, "onClickCapture");
6230
- const spreadOnClickExpression = CLICK_HANDLERS.map((name) => spreadEventValues.get(name.toLowerCase())).find((expression) => expression !== void 0);
6231
- if (!onClick && !spreadOnClickExpression) return;
6232
- if (onClick && isPureEventBlockerHandler(onClick)) return;
6233
- if (onClick && isFocusForwardingHandler(onClick)) return;
6234
- const spreadHandlerFunction = spreadOnClickExpression ? resolveHandlerFunctionExpression(spreadOnClickExpression) : null;
6235
- if (spreadHandlerFunction && (isFocusForwardingFunctionBody(spreadHandlerFunction.body ?? null) || containsBackdropDismissComparison(spreadHandlerFunction.body ?? null))) return;
6138
+ if (!onClick) return;
6139
+ if (isPureEventBlockerHandler(onClick)) return;
6140
+ if (isFocusForwardingHandler(onClick)) return;
6141
+ if (hasJsxSpreadAttribute$1(node.attributes)) return;
6236
6142
  if (hasCompositeItemRole(node)) return;
6237
6143
  if (isHoverSelectionListItem(tag, node)) return;
6238
- if (onClick && isBackdropDismissHandler(onClick)) return;
6144
+ if (isBackdropDismissHandler(onClick)) return;
6239
6145
  if (containsKeyboardActivatableDescendant(node.parent)) return;
6240
6146
  if (isHiddenFromScreenReader(node, context.settings)) return;
6241
6147
  if (isPresentationRole(node)) return;
6242
- if (KEY_HANDLERS.some((handler) => hasJsxPropIgnoreCase(node.attributes, handler) || spreadEventValues.has(handler.toLowerCase()))) return;
6148
+ if (KEY_HANDLERS.some((handler) => hasJsxPropIgnoreCase(node.attributes, handler))) return;
6243
6149
  context.report({
6244
6150
  node: node.name,
6245
6151
  message: MESSAGE$57
@@ -7712,7 +7618,7 @@ const isCreateClassLikeCall = (node) => {
7712
7618
  if (isNodeOfType(callee, "MemberExpression")) return getStaticMemberName$2(callee) === "createClass";
7713
7619
  return false;
7714
7620
  };
7715
- const isCreateContextCall$1 = (node) => {
7621
+ const isCreateContextCall = (node) => {
7716
7622
  if (!isNodeOfType(node, "CallExpression")) return false;
7717
7623
  const callee = node.callee;
7718
7624
  if (isNodeOfType(callee, "Identifier")) return callee.name === "createContext";
@@ -7889,7 +7795,7 @@ const displayName = defineRule({
7889
7795
  }
7890
7796
  },
7891
7797
  CallExpression(node) {
7892
- if (settings.checkContextObjects && isReactVersionAtLeast$1(settings.reactVersion, 16, 3) && isCreateContextCall$1(node)) {
7798
+ if (settings.checkContextObjects && isReactVersionAtLeast$1(settings.reactVersion, 16, 3) && isCreateContextCall(node)) {
7893
7799
  const assignedName = getAssignedName(node);
7894
7800
  if (assignedName) {
7895
7801
  const programRoot = findProgramRoot(node);
@@ -7936,6 +7842,21 @@ const displayName = defineRule({
7936
7842
  }
7937
7843
  });
7938
7844
  //#endregion
7845
+ //#region src/plugin/utils/get-static-property-key-name.ts
7846
+ const getStaticPropertyKeyName = (node, options = {}) => {
7847
+ if (!isNodeOfType(node, "Property")) return null;
7848
+ if (node.computed) {
7849
+ if (options.allowComputedString && isNodeOfType(node.key, "Literal") && typeof node.key.value === "string") return node.key.value;
7850
+ return null;
7851
+ }
7852
+ if (isNodeOfType(node.key, "Identifier")) return node.key.name;
7853
+ if (isNodeOfType(node.key, "Literal")) {
7854
+ if (typeof node.key.value === "string") return node.key.value;
7855
+ if (options.stringifyNonStringLiterals) return String(node.key.value);
7856
+ }
7857
+ return null;
7858
+ };
7859
+ //#endregion
7939
7860
  //#region src/plugin/utils/read-static-boolean.ts
7940
7861
  const readStaticBoolean = (node) => {
7941
7862
  if (!node) return null;
@@ -7944,6 +7865,21 @@ const readStaticBoolean = (node) => {
7944
7865
  return unwrappedNode.value;
7945
7866
  };
7946
7867
  //#endregion
7868
+ //#region src/plugin/utils/resolve-const-identifier-alias.ts
7869
+ const resolveConstIdentifierAlias = (identifier, scopes) => {
7870
+ if (!isNodeOfType(identifier, "Identifier")) return null;
7871
+ const visitedSymbolIds = /* @__PURE__ */ new Set();
7872
+ let symbol = scopes.symbolFor(identifier);
7873
+ while (symbol?.kind === "const") {
7874
+ if (visitedSymbolIds.has(symbol.id) || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
7875
+ visitedSymbolIds.add(symbol.id);
7876
+ const initializer = stripParenExpression(symbol.initializer);
7877
+ if (!isNodeOfType(initializer, "Identifier")) return symbol;
7878
+ symbol = scopes.symbolFor(initializer);
7879
+ }
7880
+ return symbol;
7881
+ };
7882
+ //#endregion
7947
7883
  //#region src/plugin/utils/is-react-api-call.ts
7948
7884
  const includesApiName = (apiNames, apiName) => typeof apiNames === "string" ? apiNames === apiName : apiNames.has(apiName);
7949
7885
  const isImportedFromReact = (symbol) => {
@@ -7951,9 +7887,9 @@ const isImportedFromReact = (symbol) => {
7951
7887
  const importDeclaration = symbol.declarationNode.parent;
7952
7888
  return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && typeof importDeclaration.source.value === "string" && REACT_RUNTIME_MODULE_SOURCES.has(importDeclaration.source.value));
7953
7889
  };
7954
- const isNamedReactApiImport = (identifier, apiNames, scopes, resolveAliases) => {
7890
+ const isNamedReactApiImport = (identifier, apiNames, scopes) => {
7955
7891
  if (!isNodeOfType(identifier, "Identifier")) return false;
7956
- const symbol = resolveAliases ? resolveConstIdentifierAlias(identifier, scopes) : scopes.symbolFor(identifier);
7892
+ const symbol = scopes.symbolFor(identifier);
7957
7893
  if (!symbol || !isImportedFromReact(symbol)) return false;
7958
7894
  const importedName = getImportedName(symbol.declarationNode);
7959
7895
  return Boolean(importedName && includesApiName(apiNames, importedName));
@@ -7967,7 +7903,7 @@ const isReactApiCall = (node, apiNames, scopes, options = {}) => {
7967
7903
  if (!isNodeOfType(node, "CallExpression")) return false;
7968
7904
  const callee = stripParenExpression(node.callee);
7969
7905
  if (isNodeOfType(callee, "Identifier")) {
7970
- if (isNamedReactApiImport(callee, apiNames, scopes, Boolean(options.resolveNamedAliases))) return true;
7906
+ if (isNamedReactApiImport(callee, apiNames, scopes)) return true;
7971
7907
  return Boolean(options.allowUnboundBareCalls && includesApiName(apiNames, callee.name) && scopes.isGlobalReference(callee));
7972
7908
  }
7973
7909
  if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || !includesApiName(apiNames, callee.property.name)) return false;
@@ -11176,11 +11112,6 @@ If the missing value is recreated every render, move it inside the hook or stabi
11176
11112
  return { CallExpression(node) {
11177
11113
  const hookName = getHookName(node.callee, context.scopes);
11178
11114
  if (!hookName || !isHookOfInterest(hookName, node.callee)) return;
11179
- if (HOOKS_REQUIRING_DEPS_MATCH.has(hookName) && !isReactApiCall(node, HOOKS_REQUIRING_DEPS_MATCH, context.scopes, {
11180
- allowGlobalReactNamespace: true,
11181
- allowUnboundBareCalls: true,
11182
- resolveNamedAliases: true
11183
- })) return;
11184
11115
  const callbackArgumentIndex = getCallbackArgumentIndex(hookName);
11185
11116
  const depsArgumentIndex = getDepsArgumentIndex(hookName);
11186
11117
  const callbackArgument = node.arguments[callbackArgumentIndex];
@@ -23108,73 +23039,61 @@ const PURE_GLOBAL_CALLEE_NAMES = new Set([
23108
23039
  "Array",
23109
23040
  "BigInt",
23110
23041
  "Boolean",
23111
- "encodeURIComponent",
23112
23042
  "Number",
23113
23043
  "Object",
23114
23044
  "String",
23115
23045
  "parseFloat",
23116
- "parseInt",
23117
- "structuredClone"
23118
- ]);
23119
- const PURE_GLOBAL_CONSTRUCTOR_NAMES = new Set(["Date", "Set"]);
23120
- const PURE_HELPER_NAMESPACE_MEMBER_NAMES = new Map([
23121
- ["Array", new Set(["from"])],
23122
- ["JSON", new Set([
23123
- "isRawJSON",
23124
- "parse",
23125
- "rawJSON",
23126
- "stringify"
23127
- ])],
23128
- ["Math", new Set([
23129
- "abs",
23130
- "acos",
23131
- "acosh",
23132
- "asin",
23133
- "asinh",
23134
- "atan",
23135
- "atan2",
23136
- "atanh",
23137
- "cbrt",
23138
- "ceil",
23139
- "clz32",
23140
- "cos",
23141
- "cosh",
23142
- "exp",
23143
- "floor",
23144
- "fround",
23145
- "hypot",
23146
- "imul",
23147
- "log",
23148
- "log10",
23149
- "log1p",
23150
- "log2",
23151
- "max",
23152
- "min",
23153
- "pow",
23154
- "round",
23155
- "sign",
23156
- "sin",
23157
- "sinh",
23158
- "sqrt",
23159
- "tan",
23160
- "tanh",
23161
- "trunc"
23162
- ])],
23163
- ["Object", new Set(["assign"])]
23046
+ "parseInt"
23164
23047
  ]);
23048
+ const PURE_GLOBAL_NAMESPACE_NAMES = new Set(["JSON", "Math"]);
23049
+ const PURE_HELPER_NAMESPACE_MEMBER_NAMES = new Map([["JSON", new Set([
23050
+ "isRawJSON",
23051
+ "parse",
23052
+ "rawJSON",
23053
+ "stringify"
23054
+ ])], ["Math", new Set([
23055
+ "abs",
23056
+ "acos",
23057
+ "acosh",
23058
+ "asin",
23059
+ "asinh",
23060
+ "atan",
23061
+ "atan2",
23062
+ "atanh",
23063
+ "cbrt",
23064
+ "ceil",
23065
+ "clz32",
23066
+ "cos",
23067
+ "cosh",
23068
+ "exp",
23069
+ "floor",
23070
+ "fround",
23071
+ "hypot",
23072
+ "imul",
23073
+ "log",
23074
+ "log10",
23075
+ "log1p",
23076
+ "log2",
23077
+ "max",
23078
+ "min",
23079
+ "pow",
23080
+ "round",
23081
+ "sign",
23082
+ "sin",
23083
+ "sinh",
23084
+ "sqrt",
23085
+ "tan",
23086
+ "tanh",
23087
+ "trunc"
23088
+ ])]]);
23165
23089
  const PURE_MEMBER_TRANSFORM_NAMES = new Set([
23166
23090
  "concat",
23167
23091
  "filter",
23168
- "flatMap",
23169
23092
  "join",
23170
23093
  "map",
23171
- "reduce",
23172
- "replace",
23173
- "slice",
23174
23094
  "split",
23175
23095
  "toLowerCase",
23176
23096
  "toString",
23177
- "toSorted",
23178
23097
  "toUpperCase",
23179
23098
  "trim"
23180
23099
  ]);
@@ -23556,11 +23475,6 @@ const analyzeHelperExpression = (expression, environment, usedParameterIndices)
23556
23475
  const callbackSummary = analyzeHelperStatements(node.body.body ?? [], callbackEnvironment, usedParameterIndices, analyzeHelperExpression);
23557
23476
  return callbackSummary.isValid && !callbackSummary.canContinue;
23558
23477
  }
23559
- if (isNodeOfType(node, "NewExpression")) {
23560
- const callee = stripParenExpression(node.callee);
23561
- if (!isNodeOfType(callee, "Identifier") || !PURE_GLOBAL_CONSTRUCTOR_NAMES.has(callee.name) || environment.parameterIndices.has(callee.name) || environment.recursiveNames.has(callee.name) || environment.shadowedGlobalNames.has(callee.name)) return false;
23562
- return (node.arguments ?? []).every((argument) => analyzeHelperExpression(argument, environment, usedParameterIndices));
23563
- }
23564
23478
  if (isNodeOfType(node, "CallExpression")) {
23565
23479
  const callee = stripParenExpression(node.callee);
23566
23480
  const calleeRoot = getMemberRoot(callee);
@@ -23763,7 +23677,7 @@ const collectValueEvidence = (analysis, expression, frame, remainingCallFrames,
23763
23677
  }
23764
23678
  const callee = stripParenExpression(node.callee);
23765
23679
  const calleeRoot = getMemberRoot(callee);
23766
- const isPureGlobalCall = isNodeOfType(callee, "Identifier") && PURE_GLOBAL_CALLEE_NAMES.has(callee.name) && getIdentifierBindingIdentity(analysis, callee) === null || isNodeOfType(callee, "MemberExpression") && isNodeOfType(calleeRoot, "Identifier") && getIdentifierBindingIdentity(analysis, calleeRoot) === null && PURE_HELPER_NAMESPACE_MEMBER_NAMES.get(calleeRoot.name)?.has(getStaticMemberName$1(callee) ?? "") === true;
23680
+ const isPureGlobalCall = isNodeOfType(callee, "Identifier") && PURE_GLOBAL_CALLEE_NAMES.has(callee.name) && getIdentifierBindingIdentity(analysis, callee) === null || isNodeOfType(calleeRoot, "Identifier") && PURE_GLOBAL_NAMESPACE_NAMES.has(calleeRoot.name) && getIdentifierBindingIdentity(analysis, calleeRoot) === null;
23767
23681
  const isPureMemberTransform = isNodeOfType(callee, "MemberExpression") && PURE_MEMBER_TRANSFORM_NAMES.has(getStaticMemberName$1(callee) ?? "");
23768
23682
  if (isPureGlobalCall || isPureMemberTransform) {
23769
23683
  if (isPureMemberTransform && isNodeOfType(callee, "MemberExpression")) mergeEvidence(evidence, collectValueEvidence(analysis, callee.object, frame, remainingCallFrames, new Set(visitedBindings)));
@@ -23816,15 +23730,6 @@ const collectValueEvidence = (analysis, expression, frame, remainingCallFrames,
23816
23730
  }
23817
23731
  return evidence;
23818
23732
  }
23819
- if (isNodeOfType(node, "NewExpression")) {
23820
- const callee = stripParenExpression(node.callee);
23821
- if (!isNodeOfType(callee, "Identifier") || !PURE_GLOBAL_CONSTRUCTOR_NAMES.has(callee.name) || getIdentifierBindingIdentity(analysis, callee) !== null) {
23822
- evidence.hasUnknownSource = true;
23823
- return evidence;
23824
- }
23825
- for (const argument of node.arguments ?? []) mergeEvidence(evidence, collectValueEvidence(analysis, argument, frame, remainingCallFrames, new Set(visitedBindings)));
23826
- return evidence;
23827
- }
23828
23733
  if (isFunctionLike$1(node) || isNodeOfType(node, "AwaitExpression")) {
23829
23734
  evidence.hasUnknownSource = true;
23830
23735
  return evidence;
@@ -25165,11 +25070,7 @@ const noAsyncEffectCallback = defineRule({
25165
25070
  severity: "warn",
25166
25071
  recommendation: "Don't make the effect callback `async`. Define an async function inside the effect and call it, then return a real cleanup function if you need one.",
25167
25072
  create: (context) => ({ CallExpression(node) {
25168
- if (!isReactApiCall(node, EFFECT_HOOK_NAMES$1, context.scopes, {
25169
- allowGlobalReactNamespace: true,
25170
- allowUnboundBareCalls: true,
25171
- resolveNamedAliases: true
25172
- })) return;
25073
+ if (!isHookCall$2(node, EFFECT_HOOK_NAMES$1)) return;
25173
25074
  const callback = getEffectCallback(node);
25174
25075
  if (!callback) return;
25175
25076
  if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return;
@@ -26367,19 +26268,6 @@ const noCloneElement = defineRule({
26367
26268
  } })
26368
26269
  });
26369
26270
  //#endregion
26370
- //#region src/plugin/utils/get-call-method-name.ts
26371
- /**
26372
- * Returns the static method name of a call's callee when it's a
26373
- * non-computed MemberExpression (`obj.method` → `"method"`), or
26374
- * `null` otherwise. Used by `no-pass-data-to-parent` and
26375
- * `no-pass-live-state-to-parent` to match the receiver-bound
26376
- * method-call shape against an allow/block list.
26377
- */
26378
- const getCallMethodName = (callee) => {
26379
- if (isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier")) return callee.property.name;
26380
- return null;
26381
- };
26382
- //#endregion
26383
26271
  //#region src/plugin/rules/state-and-effects/no-create-context-in-render.ts
26384
26272
  const MESSAGE$31 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
26385
26273
  const CONTEXT_MODULES = [
@@ -26387,35 +26275,23 @@ const CONTEXT_MODULES = [
26387
26275
  "use-context-selector",
26388
26276
  "react-tracked"
26389
26277
  ];
26390
- const getSupportedContextImportSource = (symbol) => {
26391
- if (symbol?.kind !== "import") return null;
26392
- const importDeclaration = symbol.declarationNode.parent;
26393
- if (!importDeclaration || !isNodeOfType(importDeclaration, "ImportDeclaration") || typeof importDeclaration.source.value !== "string" || !CONTEXT_MODULES.includes(importDeclaration.source.value)) return null;
26394
- return importDeclaration.source.value;
26395
- };
26396
- const isSupportedNamespaceSymbol = (symbol) => getSupportedContextImportSource(symbol) !== null && Boolean(symbol && (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")));
26397
- const isDestructuredCreateContextBinding = (identifier, scopes) => {
26398
- if (!isNodeOfType(identifier, "Identifier")) return false;
26399
- const symbol = scopes.symbolFor(identifier);
26400
- if (symbol?.kind !== "const" || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || !isNodeOfType(symbol.declarationNode.id, "ObjectPattern")) return false;
26401
- const property = symbol.bindingIdentifier.parent;
26402
- if (!property || !isNodeOfType(property, "Property") || getStaticPropertyKeyName(property, { allowComputedString: true }) !== "createContext") return false;
26403
- const initializer = stripParenExpression(symbol.initializer);
26404
- return isNodeOfType(initializer, "Identifier") && isSupportedNamespaceSymbol(resolveConstIdentifierAlias(initializer, scopes));
26405
- };
26406
- const isCreateContextCall = (node, scopes) => {
26407
- const callee = stripParenExpression(node.callee);
26278
+ const isCreateContextCallee = (callee) => {
26408
26279
  if (isNodeOfType(callee, "Identifier")) {
26409
- if (isDestructuredCreateContextBinding(callee, scopes)) return true;
26410
- const symbol = resolveConstIdentifierAlias(callee, scopes);
26411
- return getSupportedContextImportSource(symbol) !== null && Boolean(symbol && getImportedName(symbol.declarationNode) === "createContext");
26280
+ const binding = getImportBindingForName(callee, callee.name);
26281
+ return binding !== null && binding.exportedName === "createContext" && CONTEXT_MODULES.includes(binding.source);
26412
26282
  }
26413
- if (!isNodeOfType(callee, "MemberExpression")) return false;
26414
- if ((getCallMethodName(callee) ?? (callee.computed && isNodeOfType(callee.property, "Literal") && typeof callee.property.value === "string" ? callee.property.value : null)) !== "createContext") return false;
26415
- const receiver = stripParenExpression(callee.object);
26416
- if (!isNodeOfType(receiver, "Identifier")) return false;
26417
- if (receiver.name === "React" && scopes.isGlobalReference(receiver)) return true;
26418
- return isSupportedNamespaceSymbol(resolveConstIdentifierAlias(receiver, scopes));
26283
+ if (isNodeOfType(callee, "MemberExpression") && !callee.computed) {
26284
+ const namespaceIdentifier = callee.object;
26285
+ const propertyIdentifier = callee.property;
26286
+ if (!isNodeOfType(namespaceIdentifier, "Identifier")) return false;
26287
+ if (!isNodeOfType(propertyIdentifier, "Identifier")) return false;
26288
+ if (propertyIdentifier.name !== "createContext") return false;
26289
+ const namespaceName = namespaceIdentifier.name;
26290
+ if (isCanonicalReactNamespaceName(namespaceName)) return true;
26291
+ const importSource = getImportSourceForName(namespaceIdentifier, namespaceName);
26292
+ return importSource !== null && CONTEXT_MODULES.includes(importSource);
26293
+ }
26294
+ return false;
26419
26295
  };
26420
26296
  const noCreateContextInRender = defineRule({
26421
26297
  id: "no-create-context-in-render",
@@ -26424,7 +26300,7 @@ const noCreateContextInRender = defineRule({
26424
26300
  category: "Correctness",
26425
26301
  recommendation: "Move `createContext(...)` outside the component, to the top level of the file, so it stays the same on every render.",
26426
26302
  create: (context) => ({ CallExpression(node) {
26427
- if (!isCreateContextCall(node, context.scopes)) return;
26303
+ if (!isCreateContextCallee(node.callee)) return;
26428
26304
  const componentOrHookName = enclosingComponentOrHookName(node);
26429
26305
  if (!componentOrHookName) return;
26430
26306
  context.report({
@@ -30475,11 +30351,7 @@ const noFetchInEffect = defineRule({
30475
30351
  severity: "warn",
30476
30352
  recommendation: "Use a data-fetching layer or Server Component so fetches do not race, double-fire, or leak from `useEffect`.",
30477
30353
  create: (context) => ({ CallExpression(node) {
30478
- if (!isReactApiCall(node, EFFECT_HOOK_NAMES$1, context.scopes, {
30479
- allowGlobalReactNamespace: true,
30480
- allowUnboundBareCalls: true,
30481
- resolveNamedAliases: true
30482
- })) return;
30354
+ if (!isHookCall$2(node, EFFECT_HOOK_NAMES$1)) return;
30483
30355
  const callback = getEffectCallback(node);
30484
30356
  if (!callback) return;
30485
30357
  const analysisFunctions = collectEffectAnalysisFunctions(callback, context);
@@ -34689,6 +34561,19 @@ const DATA_SINK_METHOD_NAMES = new Set([
34689
34561
  "deserialize"
34690
34562
  ]);
34691
34563
  //#endregion
34564
+ //#region src/plugin/utils/get-call-method-name.ts
34565
+ /**
34566
+ * Returns the static method name of a call's callee when it's a
34567
+ * non-computed MemberExpression (`obj.method` → `"method"`), or
34568
+ * `null` otherwise. Used by `no-pass-data-to-parent` and
34569
+ * `no-pass-live-state-to-parent` to match the receiver-bound
34570
+ * method-call shape against an allow/block list.
34571
+ */
34572
+ const getCallMethodName = (callee) => {
34573
+ if (isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier")) return callee.property.name;
34574
+ return null;
34575
+ };
34576
+ //#endregion
34692
34577
  //#region src/plugin/rules/state-and-effects/no-pass-data-to-parent.ts
34693
34578
  const isUseStateIdentifier = (identifier) => {
34694
34579
  if (!isNodeOfType(identifier, "Identifier")) return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-react-doctor",
3
- "version": "0.7.5-dev.8fc5848",
3
+ "version": "0.7.5-dev.d9676e2",
4
4
  "description": "React Doctor rules for oxlint.",
5
5
  "keywords": [
6
6
  "accessibility",