oxlint-plugin-react-doctor 0.9.2-dev.3bc63ea → 0.9.2-dev.444e177

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 +31 -422
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -388,7 +388,7 @@ const SOURCE_FILE_PATTERN = /\.(?:[cm]?[jt]sx?)$/i;
388
388
  const SCRIPT_SOURCE_FILE_PATTERN = /\.(?:[cm]?[jt]sx?|py|php)$/i;
389
389
  const DATABASE_SOURCE_FILE_PATTERN = /\.(?:[cm]?[jt]sx?|py)$/i;
390
390
  const SERVER_CONTEXT_PATTERN = /(?:^|\/)(?:api|backend|server|servers|middleware|route|routes|functions|lambdas|workers)(?:\/|$)|(?:^|\/)[^/]+\.server\.[cm]?[jt]sx?$/i;
391
- const TEST_CONTEXT_PATTERN = /(?:^|\/)(?:__fixtures__|__mocks__|__tests__|__integration__|fixtures|mocks|test|tests|testdata|test-data|e2e|playwright|cypress|specs?)(?:\/|$)|^(?:test|spec)-[^/]+\.[cm]?[jt]sx?$|\.(?:test|spec|e2e|e2e-spec|integration-test|fixture|fixtures|stories|story)\.[cm]?[jt]sx?$|(?:^|\/)(?:playwright|cypress|vitest|jest|karma)[^/]*\.conf(?:ig)?\.[cm]?[jt]s$|(?:^|\/)(?:test_[^/]+|[^/]+_test|conftest)\.py$|\.env\.[^/]*(?:test|e2e)[^/]*$/i;
391
+ const TEST_CONTEXT_PATTERN = /(?:^|\/)(?:__fixtures__|__mocks__|__tests__|__integration__|fixtures|mocks|test|tests|testdata|test-data|e2e|playwright|cypress|specs?)(?:\/|$)|\.(?:test|spec|e2e|e2e-spec|integration-test|fixture|fixtures|stories|story)\.[cm]?[jt]sx?$|(?:^|\/)(?:playwright|cypress|vitest|jest|karma)[^/]*\.conf(?:ig)?\.[cm]?[jt]s$|(?:^|\/)(?:test_[^/]+|[^/]+_test|conftest)\.py$|\.env\.[^/]*(?:test|e2e)[^/]*$/i;
392
392
  const BUILD_CONFIG_FILE_PATTERN = /(?:^|\/)(?:vite|next|nuxt|astro|remix|webpack|rollup|rspack|rsbuild|esbuild|tsup|metro|expo|babel|tailwind|postcss|svelte|farm|parcel|snowpack)[^/]*\.config\.[cm]?[jt]sx?$/i;
393
393
  const BUILD_SCRIPT_CONTEXT_PATTERN = /(?:^|\/)scripts(?:\/|$)/i;
394
394
  const DEMO_CONTEXT_PATTERN = /(?:^|\/)(?:examples?|tutorials?|demos?|samples?|playgrounds?)(?:\/|$)/i;
@@ -5887,7 +5887,6 @@ const isInlineFunctionExpression = (node) => Boolean(node && (isNodeOfType(node,
5887
5887
  //#endregion
5888
5888
  //#region src/plugin/rules/js-performance/async-await-in-loop.ts
5889
5889
  const LOOP_STATEMENT_TYPES$1 = new Set(LOOP_TYPES);
5890
- const ORDERED_OUTPUT_INSERTION_METHOD_NAMES = new Set(["push", "unshift"]);
5891
5890
  const findFirstAwaitOutsideNestedFunctions = (block, skipNestedLoops = false) => {
5892
5891
  let firstAwait = null;
5893
5892
  walkAst(block, (child) => {
@@ -5954,151 +5953,7 @@ const isAwaitingManualPromiseWait = (awaitNode) => {
5954
5953
  });
5955
5954
  return isWaitLike;
5956
5955
  };
5957
- const getRootObjectIdentifierName = (node) => {
5958
- let current = node;
5959
- while (isNodeOfType(current, "MemberExpression")) current = current.object;
5960
- return isNodeOfType(current, "Identifier") ? current.name : null;
5961
- };
5962
- const isScopeWithinFunction = (candidateScope, functionScope) => {
5963
- let currentScope = candidateScope;
5964
- while (currentScope) {
5965
- if (currentScope === functionScope) return true;
5966
- currentScope = currentScope.parent;
5967
- }
5968
- return false;
5969
- };
5970
- const isSymbolDirectlyReturned = (symbol, callerFunction) => Boolean(callerFunction) && symbol.references.some((reference) => {
5971
- const expressionRoot = findTransparentExpressionRoot(reference.identifier);
5972
- const parent = expressionRoot.parent;
5973
- return isNodeOfType(parent, "ReturnStatement") && parent.argument === expressionRoot && findEnclosingFunction$1(parent) === callerFunction;
5974
- });
5975
- const collectPatternBindingSymbolIds = (pattern, scopes, target) => {
5976
- if (isNodeOfType(pattern, "Identifier")) {
5977
- const symbol = scopes.symbolFor(pattern);
5978
- if (symbol) target.add(symbol.id);
5979
- return;
5980
- }
5981
- if (isNodeOfType(pattern, "ObjectPattern")) {
5982
- for (const property of pattern.properties ?? []) if (isNodeOfType(property, "Property") && property.value) collectPatternBindingSymbolIds(property.value, scopes, target);
5983
- else if (isNodeOfType(property, "RestElement") && property.argument) collectPatternBindingSymbolIds(property.argument, scopes, target);
5984
- return;
5985
- }
5986
- if (isNodeOfType(pattern, "ArrayPattern")) {
5987
- for (const element of pattern.elements ?? []) if (element) collectPatternBindingSymbolIds(element, scopes, target);
5988
- return;
5989
- }
5990
- if (isNodeOfType(pattern, "AssignmentPattern") && pattern.left) collectPatternBindingSymbolIds(pattern.left, scopes, target);
5991
- };
5992
- const collectReferencedSymbolIds = (expression, scopes) => {
5993
- const referencedSymbolIds = /* @__PURE__ */ new Set();
5994
- walkAst(expression, (child) => {
5995
- if (child !== expression && isFunctionLike$1(child)) return false;
5996
- if (!isNodeOfType(child, "Identifier")) return;
5997
- const symbol = scopes.symbolFor(child);
5998
- if (symbol) referencedSymbolIds.add(symbol.id);
5999
- });
6000
- return referencedSymbolIds;
6001
- };
6002
- const collectAwaitDerivedSymbolIds = (block, scopes) => {
6003
- const awaitDerivedSymbolIds = /* @__PURE__ */ new Set();
6004
- const bindingDependencies = [];
6005
- walkAst(block, (child) => {
6006
- if (child !== block && isFunctionLike$1(child)) return false;
6007
- if (isNodeOfType(child, "VariableDeclarator") && child.id && child.init) {
6008
- const declaredSymbolIds = /* @__PURE__ */ new Set();
6009
- collectPatternBindingSymbolIds(child.id, scopes, declaredSymbolIds);
6010
- if (containsDirectAwait(child.init)) for (const symbolId of declaredSymbolIds) awaitDerivedSymbolIds.add(symbolId);
6011
- const referencedSymbolIds = collectReferencedSymbolIds(child.init, scopes);
6012
- for (const declaredSymbolId of declaredSymbolIds) bindingDependencies.push({
6013
- declaredSymbolId,
6014
- referencedSymbolIds
6015
- });
6016
- return;
6017
- }
6018
- if (isNodeOfType(child, "AssignmentExpression") && child.left) {
6019
- const assignedSymbolIds = /* @__PURE__ */ new Set();
6020
- collectPatternBindingSymbolIds(child.left, scopes, assignedSymbolIds);
6021
- if (containsDirectAwait(child.right)) for (const symbolId of assignedSymbolIds) awaitDerivedSymbolIds.add(symbolId);
6022
- const referencedSymbolIds = collectReferencedSymbolIds(child.right, scopes);
6023
- for (const assignedSymbolId of assignedSymbolIds) bindingDependencies.push({
6024
- declaredSymbolId: assignedSymbolId,
6025
- referencedSymbolIds
6026
- });
6027
- }
6028
- });
6029
- let didGrow = true;
6030
- while (didGrow) {
6031
- didGrow = false;
6032
- for (const { declaredSymbolId, referencedSymbolIds } of bindingDependencies) {
6033
- if (awaitDerivedSymbolIds.has(declaredSymbolId)) continue;
6034
- for (const referencedSymbolId of referencedSymbolIds) {
6035
- if (!awaitDerivedSymbolIds.has(referencedSymbolId)) continue;
6036
- awaitDerivedSymbolIds.add(declaredSymbolId);
6037
- didGrow = true;
6038
- break;
6039
- }
6040
- }
6041
- }
6042
- return awaitDerivedSymbolIds;
6043
- };
6044
- const getSimpleParameterIdentifier = (parameter) => {
6045
- if (isNodeOfType(parameter, "Identifier")) return parameter;
6046
- if (isNodeOfType(parameter, "AssignmentPattern") && isNodeOfType(parameter.left, "Identifier")) return parameter.left;
6047
- return null;
6048
- };
6049
- const doesAwaitedLocalCallInsertAwaitDerivedOutput = (awaitNode, context) => {
6050
- if (!isNodeOfType(awaitNode, "AwaitExpression")) return false;
6051
- const callExpression = awaitNode.argument;
6052
- if (!isNodeOfType(callExpression, "CallExpression")) return false;
6053
- const localFunction = resolveStaticLocalCallFunction(callExpression, context.scopes);
6054
- if (!isFunctionLike$1(localFunction)) return false;
6055
- const callerFunction = findEnclosingFunction$1(callExpression);
6056
- const functionScope = context.scopes.ownScopeFor(localFunction);
6057
- if (!functionScope) return false;
6058
- const awaitDerivedSymbolIds = collectAwaitDerivedSymbolIds(localFunction.body, context.scopes);
6059
- const externallyReachableParameterSymbolIds = /* @__PURE__ */ new Set();
6060
- for (const [parameterIndex, parameter] of localFunction.params.entries()) {
6061
- const parameterIdentifier = getSimpleParameterIdentifier(parameter);
6062
- if (!parameterIdentifier) continue;
6063
- const argument = callExpression.arguments[parameterIndex];
6064
- if (!isNodeOfType(argument, "Identifier")) continue;
6065
- const parameterSymbol = context.scopes.symbolFor(parameterIdentifier);
6066
- const argumentSymbol = context.scopes.symbolFor(argument);
6067
- if (parameterSymbol && argumentSymbol && (isSymbolDirectlyReturned(argumentSymbol, callerFunction) || argumentSymbol.id === parameterSymbol.id)) externallyReachableParameterSymbolIds.add(parameterSymbol.id);
6068
- }
6069
- let doesInsertAwaitDerivedOutput = false;
6070
- walkAst(localFunction.body, (child) => {
6071
- if (doesInsertAwaitDerivedOutput) return false;
6072
- if (child !== localFunction.body && isFunctionLike$1(child)) return false;
6073
- if (!isNodeOfType(child, "CallExpression")) return;
6074
- const callee = child.callee;
6075
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || !ORDERED_OUTPUT_INSERTION_METHOD_NAMES.has(callee.property.name)) return;
6076
- let doesMutationConsumeAwaitedValue = false;
6077
- for (const mutationArgument of child.arguments ?? []) {
6078
- if (containsDirectAwait(mutationArgument)) {
6079
- doesMutationConsumeAwaitedValue = true;
6080
- break;
6081
- }
6082
- const referencedSymbolIds = collectReferencedSymbolIds(mutationArgument, context.scopes);
6083
- for (const referencedSymbolId of referencedSymbolIds) if (awaitDerivedSymbolIds.has(referencedSymbolId)) {
6084
- doesMutationConsumeAwaitedValue = true;
6085
- break;
6086
- }
6087
- if (doesMutationConsumeAwaitedValue) break;
6088
- }
6089
- if (!doesMutationConsumeAwaitedValue) return;
6090
- let receiverIdentifier = callee.object;
6091
- while (isNodeOfType(receiverIdentifier, "MemberExpression")) receiverIdentifier = receiverIdentifier.object;
6092
- if (!isNodeOfType(receiverIdentifier, "Identifier")) return;
6093
- const receiverSymbol = context.scopes.symbolFor(receiverIdentifier);
6094
- if (receiverSymbol && (externallyReachableParameterSymbolIds.has(receiverSymbol.id) || !isScopeWithinFunction(receiverSymbol.scope, functionScope) && isSymbolDirectlyReturned(receiverSymbol, callerFunction))) {
6095
- doesInsertAwaitDerivedOutput = true;
6096
- return false;
6097
- }
6098
- });
6099
- return doesInsertAwaitDerivedOutput;
6100
- };
6101
- const isIntentionallySequentialAwait = (awaitNode, context) => isAwaitingPossiblyMutatedMemberCall(awaitNode, context) || isAwaitingSleepLikeCall(awaitNode, context) || isAwaitingPromiseConcurrencyCall(awaitNode) || isAwaitingManualPromiseWait(awaitNode) || doesAwaitedLocalCallInsertAwaitDerivedOutput(awaitNode, context);
5956
+ const isIntentionallySequentialAwait = (awaitNode, context) => isAwaitingPossiblyMutatedMemberCall(awaitNode, context) || isAwaitingSleepLikeCall(awaitNode, context) || isAwaitingPromiseConcurrencyCall(awaitNode) || isAwaitingManualPromiseWait(awaitNode);
6102
5957
  const collectPatternIdentifiers = (pattern, target) => {
6103
5958
  if (isNodeOfType(pattern, "Identifier")) target.add(pattern.name);
6104
5959
  else if (isNodeOfType(pattern, "ObjectPattern")) {
@@ -6244,6 +6099,11 @@ const loopBodyHasAwaitDependentEarlyExit = (block, loopLabelName) => {
6244
6099
  });
6245
6100
  return hasAwaitDependentExit;
6246
6101
  };
6102
+ const getRootObjectIdentifierName = (node) => {
6103
+ let current = node;
6104
+ while (isNodeOfType(current, "MemberExpression")) current = current.object;
6105
+ return isNodeOfType(current, "Identifier") ? current.name : null;
6106
+ };
6247
6107
  const MUTATING_ARRAY_METHOD_NAMES$2 = new Set([
6248
6108
  ...ARRAY_MUTATION_METHOD_NAMES,
6249
6109
  "pop",
@@ -30674,8 +30534,7 @@ const STRING_TYPED_PROPERTY_NAMES = new Set([
30674
30534
  "code",
30675
30535
  "label",
30676
30536
  "slug",
30677
- "prefix",
30678
- "__html"
30537
+ "prefix"
30679
30538
  ]);
30680
30539
  const STRING_TYPED_IDENTIFIER_SUFFIXES = [
30681
30540
  "Text",
@@ -30793,25 +30652,13 @@ const STRING_TYPED_IDENTIFIER_NAMES = new Set([
30793
30652
  "title"
30794
30653
  ]);
30795
30654
  const STRING_RETURNING_CALLEE_PREFIX_PATTERN = /^(?:normalize|format|stringify|serialize)/;
30796
- const FRESH_ARRAY_METHOD_NAMES$2 = new Set([
30797
- "concat",
30798
- "filter",
30799
- "flat",
30800
- "flatMap",
30801
- "map",
30802
- "slice",
30803
- "split"
30804
- ]);
30805
30655
  const isLikelyStringReceiver = (receiver) => {
30806
30656
  if (!receiver) return false;
30807
- const unwrappedReceiver = stripParenExpression(receiver);
30808
- if (unwrappedReceiver !== receiver) return isLikelyStringReceiver(unwrappedReceiver);
30809
30657
  if (isNodeOfType(receiver, "Literal") && typeof receiver.value === "string") return true;
30810
30658
  if (isNodeOfType(receiver, "TemplateLiteral")) return true;
30811
30659
  if (isNodeOfType(receiver, "CallExpression") && isNodeOfType(receiver.callee, "Identifier") && receiver.callee.name === "String") return true;
30812
30660
  if (isNodeOfType(receiver, "CallExpression") && isNodeOfType(receiver.callee, "MemberExpression") && isNodeOfType(receiver.callee.property, "Identifier") && STRING_RETURNING_METHODS.has(receiver.callee.property.name)) return true;
30813
30661
  if (isNodeOfType(receiver, "CallExpression") && isNodeOfType(receiver.callee, "Identifier") && STRING_RETURNING_CALLEE_PREFIX_PATTERN.test(receiver.callee.name)) return true;
30814
- if (isNodeOfType(receiver, "CallExpression") && isNodeOfType(receiver.callee, "MemberExpression") && isNodeOfType(receiver.callee.property, "Identifier") && (receiver.callee.property.name === "concat" || receiver.callee.property.name === "slice") && isLikelyStringReceiver(receiver.callee.object)) return true;
30815
30662
  if (isNodeOfType(receiver, "MemberExpression") && isNodeOfType(receiver.property, "Identifier")) {
30816
30663
  if (STRING_TYPED_PROPERTY_NAMES.has(receiver.property.name)) return true;
30817
30664
  }
@@ -30829,46 +30676,8 @@ const isLikelyStringReceiver = (receiver) => {
30829
30676
  }
30830
30677
  if (isNodeOfType(receiver, "BinaryExpression") && receiver.operator === "+") return isLikelyStringReceiver(receiver.left) || isLikelyStringReceiver(receiver.right);
30831
30678
  if (isNodeOfType(receiver, "ConditionalExpression")) return isLikelyStringReceiver(receiver.consequent) && isLikelyStringReceiver(receiver.alternate);
30832
- if (isNodeOfType(receiver, "LogicalExpression")) return isLikelyStringReceiver(receiver.left) && isLikelyStringReceiver(receiver.right);
30833
30679
  return false;
30834
30680
  };
30835
- const isFreshArrayReceiver = (receiver) => {
30836
- const unwrappedReceiver = stripParenExpression(receiver);
30837
- if (unwrappedReceiver !== receiver) return isFreshArrayReceiver(unwrappedReceiver);
30838
- if (!isNodeOfType(receiver, "CallExpression") || !isNodeOfType(receiver.callee, "MemberExpression") || !isNodeOfType(receiver.callee.property, "Identifier")) return false;
30839
- if (!FRESH_ARRAY_METHOD_NAMES$2.has(receiver.callee.property.name)) return false;
30840
- if (receiver.callee.property.name === "split") return isLikelyStringReceiver(receiver.callee.object);
30841
- const sourceReceiver = stripParenExpression(receiver.callee.object);
30842
- return isKnownNativeArrayReceiver(sourceReceiver) || isFreshArrayReceiver(sourceReceiver);
30843
- };
30844
- const isSmallRestHelperOmissionList = (node) => {
30845
- if (!node) return false;
30846
- const candidate = stripParenExpression(node);
30847
- if (!isNodeOfType(candidate, "ArrayExpression")) return false;
30848
- const elements = candidate.elements ?? [];
30849
- return elements.length <= 8 && elements.every((element) => element === null || !isNodeOfType(element, "SpreadElement"));
30850
- };
30851
- const isTypeScriptRestHelperLookup = (lookupCall, receiver, scopes) => {
30852
- if (!isNodeOfType(receiver, "Identifier")) return false;
30853
- const enclosingFunction = findEnclosingFunction$1(lookupCall);
30854
- if (!isNodeOfType(enclosingFunction, "FunctionExpression") || !isNodeOfType(enclosingFunction.params?.[1], "Identifier") || enclosingFunction.params[1].name !== receiver.name) return false;
30855
- let bindingIdentifier = null;
30856
- let ancestor = enclosingFunction.parent;
30857
- while (ancestor && !isFunctionLike$1(ancestor)) {
30858
- if (isNodeOfType(ancestor, "VariableDeclarator") && isNodeOfType(ancestor.id, "Identifier") && ancestor.id.name === "__rest") {
30859
- bindingIdentifier = ancestor.id;
30860
- break;
30861
- }
30862
- ancestor = ancestor.parent;
30863
- }
30864
- if (!bindingIdentifier) return false;
30865
- const helperSymbol = scopes.symbolFor(bindingIdentifier);
30866
- if (!helperSymbol || helperSymbol.references.length === 0) return false;
30867
- return helperSymbol.references.every((reference) => {
30868
- const callExpression = reference.identifier.parent;
30869
- return isNodeOfType(callExpression, "CallExpression") && callExpression.callee === reference.identifier && isSmallRestHelperOmissionList(callExpression.arguments?.[1]);
30870
- });
30871
- };
30872
30681
  const INDEX_LIKE_IDENTIFIER_NAMES = new Set([
30873
30682
  "i",
30874
30683
  "j",
@@ -31465,8 +31274,6 @@ const jsSetMapLookups = defineRule({
31465
31274
  const query = node.arguments[0];
31466
31275
  if (methodName === "indexOf" && !isKnownSafeIndexOfQuery(query) && (isKnownUnsafeIndexOfQuery(query, receiver) || isKnownUnsafeIndexOfReceiver(receiver))) return;
31467
31276
  if (isLikelyStringReceiver(receiver)) return;
31468
- if (isFreshArrayReceiver(receiver)) return;
31469
- if (isTypeScriptRestHelperLookup(node, receiver, context.scopes)) return;
31470
31277
  if (isSmallInlineLiteralArray(receiver)) return;
31471
31278
  if (isScreamingSnakeCaseConstantReceiver(receiver)) return;
31472
31279
  if (isSmallFixedListMember(receiver)) return;
@@ -46908,101 +46715,6 @@ const isLoopCounterDeclarator = (declarator, referenceNode, indexName) => {
46908
46715
  }
46909
46716
  return isBindingReassignedOrMutated(referenceNode, indexName);
46910
46717
  };
46911
- const findEnclosingWhileLoop = (node) => {
46912
- let current = node.parent;
46913
- while (current) {
46914
- if (isNodeOfType(current, "WhileStatement") || isNodeOfType(current, "DoWhileStatement")) return current;
46915
- if (isFunctionLike$1(current) || isNodeOfType(current, "Program")) return null;
46916
- current = current.parent;
46917
- }
46918
- return null;
46919
- };
46920
- const isStaticMemberChain = (expression) => {
46921
- const candidate = stripParenExpression(expression);
46922
- if (isNodeOfType(candidate, "Identifier") || isNodeOfType(candidate, "ThisExpression")) return true;
46923
- return Boolean(isNodeOfType(candidate, "MemberExpression") && !candidate.computed && isNodeOfType(candidate.property, "Identifier") && isStaticMemberChain(candidate.object));
46924
- };
46925
- const findLengthBoundCollections = (expression) => {
46926
- const collections = [];
46927
- walkAst(expression, (child) => {
46928
- if (isNodeOfType(child, "MemberExpression") && !child.computed && isStaticMemberChain(child.object) && isNodeOfType(child.property, "Identifier") && child.property.name === "length") collections.push(child.object);
46929
- });
46930
- return collections;
46931
- };
46932
- const areSameBoundStaticMemberChains = (first, second) => isStaticMemberChain(first) && isStaticMemberChain(second) && areExpressionsStructurallyEqual(first, second, { areIdentifiersEqual: (firstIdentifier, secondIdentifier) => {
46933
- if (!isNodeOfType(firstIdentifier, "Identifier") || !isNodeOfType(secondIdentifier, "Identifier") || firstIdentifier.name !== secondIdentifier.name) return false;
46934
- const firstBinding = findVariableInitializer(firstIdentifier, firstIdentifier.name);
46935
- const secondBinding = findVariableInitializer(secondIdentifier, secondIdentifier.name);
46936
- return firstBinding?.bindingIdentifier === secondBinding?.bindingIdentifier;
46937
- } });
46938
- const RELATIONAL_BOUND_OPERATORS = new Set([
46939
- "<",
46940
- "<=",
46941
- ">",
46942
- ">="
46943
- ]);
46944
- const loopTestBoundsCounterByLength = (expression, indexName, bindingIdentifier) => {
46945
- const readsCounter = (candidate) => {
46946
- let didReadCounter = false;
46947
- walkAst(candidate, (child) => {
46948
- if (didReadCounter) return false;
46949
- if (isNodeOfType(child, "Identifier") && child.name === indexName && findVariableInitializer(child, indexName)?.bindingIdentifier === bindingIdentifier) {
46950
- didReadCounter = true;
46951
- return false;
46952
- }
46953
- });
46954
- return didReadCounter;
46955
- };
46956
- const readsLength = (candidate) => {
46957
- let didReadLength = false;
46958
- walkAst(candidate, (child) => {
46959
- if (didReadLength) return false;
46960
- if (isNodeOfType(child, "MemberExpression") && !child.computed && isNodeOfType(child.property, "Identifier") && child.property.name === "length") {
46961
- didReadLength = true;
46962
- return false;
46963
- }
46964
- });
46965
- return didReadLength;
46966
- };
46967
- let didFindLengthBound = false;
46968
- walkAst(expression, (child) => {
46969
- if (didFindLengthBound) return false;
46970
- if (!isNodeOfType(child, "BinaryExpression") || !RELATIONAL_BOUND_OPERATORS.has(child.operator)) return;
46971
- if (readsCounter(child.left) && readsLength(child.right) || readsCounter(child.right) && readsLength(child.left)) {
46972
- didFindLengthBound = true;
46973
- return false;
46974
- }
46975
- });
46976
- return didFindLengthBound;
46977
- };
46978
- const isDataIndexedWhileLoopCounter = (referenceNode, bindingIdentifier, indexName) => {
46979
- if (!INDEX_PARAMETER_NAMES.has(indexName)) return false;
46980
- const loop = findEnclosingWhileLoop(referenceNode);
46981
- if (!loop) return false;
46982
- let doesTestReadCounter = false;
46983
- walkAst(loop.test, (child) => {
46984
- if (doesTestReadCounter) return false;
46985
- if (!isNodeOfType(child, "Identifier") || child.name !== indexName) return;
46986
- if (findVariableInitializer(child, indexName)?.bindingIdentifier === bindingIdentifier) {
46987
- doesTestReadCounter = true;
46988
- return false;
46989
- }
46990
- });
46991
- if (!doesTestReadCounter) return false;
46992
- const lengthBoundCollections = findLengthBoundCollections(loop.test);
46993
- if (lengthBoundCollections.length === 0) return false;
46994
- let didFindIndexedCollectionRead = false;
46995
- walkAst(loop.body, (child) => {
46996
- if (didFindIndexedCollectionRead) return false;
46997
- if (isFunctionLike$1(child)) return false;
46998
- if (!isNodeOfType(child, "MemberExpression") || !child.computed || !isNodeOfType(child.property, "Identifier") || child.property.name !== indexName) return;
46999
- if (findVariableInitializer(child.property, indexName)?.bindingIdentifier === bindingIdentifier && lengthBoundCollections.some((collection) => areSameBoundStaticMemberChains(collection, child.object))) {
47000
- didFindIndexedCollectionRead = true;
47001
- return false;
47002
- }
47003
- });
47004
- return didFindIndexedCollectionRead;
47005
- };
47006
46718
  /**
47007
46719
  * Resolves whether an identifier is PROVABLY the positional array
47008
46720
  * index, by classifying its binding. Per the official rule prompt,
@@ -47053,12 +46765,6 @@ const resolvePositionalIndexBinding = (identifierNode, depth) => {
47053
46765
  }
47054
46766
  const declarator = binding.bindingIdentifier.parent;
47055
46767
  if (declarator && isNodeOfType(declarator, "VariableDeclarator") && declarator.id === binding.bindingIdentifier && declarator.init) {
47056
- if (isDataIndexedWhileLoopCounter(identifierNode, binding.bindingIdentifier, identifierNode.name)) return {
47057
- iteratorCall: null,
47058
- bindingFunction: null,
47059
- indexParameterPosition: null,
47060
- isDataIndexedLoopCounter: true
47061
- };
47062
46768
  const initializer = stripParenExpression(declarator.init);
47063
46769
  if (isNodeOfType(initializer, "Literal") && typeof initializer.value === "number") {
47064
46770
  if (!INDEX_PARAMETER_NAMES.has(identifierNode.name)) return null;
@@ -47093,101 +46799,6 @@ const iteratorCallExemptsIndexKey = (iteratorCall) => {
47093
46799
  const receiver = iteratorCall.callee.object;
47094
46800
  return isStaticPlaceholderReceiver(receiver) || isFixedMemoReceiver(receiver) || isStaticDefaultLiteralReceiver(receiver) || isStringDerivedReceiver(receiver);
47095
46801
  };
47096
- const isReactNamespaceIdentifier = (node) => {
47097
- if (!isNodeOfType(node, "Identifier")) return false;
47098
- const importBinding = getImportBindingForName(node, node.name);
47099
- const visibleBinding = findVariableInitializer(node, node.name);
47100
- if (!importBinding) return node.name === "React" && !visibleBinding;
47101
- return visibleBinding?.initializer?.type.startsWith("Import") === true && importBinding.source === "react" && (importBinding.isNamespace || importBinding.exportedName === "default");
47102
- };
47103
- const isReactChildrenObject = (node) => {
47104
- const candidate = stripParenExpression(node);
47105
- if (isNodeOfType(candidate, "Identifier")) {
47106
- const importBinding = getImportBindingForName(candidate, candidate.name);
47107
- const visibleBinding = findVariableInitializer(candidate, candidate.name);
47108
- return Boolean(visibleBinding?.initializer?.type === "ImportSpecifier" && importBinding?.source === "react" && importBinding.exportedName === "Children");
47109
- }
47110
- return Boolean(isNodeOfType(candidate, "MemberExpression") && !candidate.computed && isReactNamespaceIdentifier(candidate.object) && isNodeOfType(candidate.property, "Identifier") && candidate.property.name === "Children");
47111
- };
47112
- const isReactChildrenToArrayCall = (node) => {
47113
- const candidate = stripParenExpression(node);
47114
- if (!isNodeOfType(candidate, "CallExpression") || !isNodeOfType(candidate.callee, "MemberExpression") || candidate.callee.computed || !isReactChildrenObject(candidate.callee.object) || !isNodeOfType(candidate.callee.property, "Identifier") || candidate.callee.property.name !== "toArray") return false;
47115
- const normalizedValueNode = candidate.arguments?.[0];
47116
- const normalizedValue = normalizedValueNode ? stripParenExpression(normalizedValueNode) : null;
47117
- if (!normalizedValue || !isNodeOfType(normalizedValue, "Identifier") || normalizedValue.name !== "children") return false;
47118
- const normalizedBinding = findVariableInitializer(normalizedValue, normalizedValue.name);
47119
- return Boolean(normalizedBinding && findEnclosingParameter(normalizedBinding.bindingIdentifier));
47120
- };
47121
- const isSameIdentifier = (first, second) => {
47122
- const firstIdentifier = stripParenExpression(first);
47123
- const secondIdentifier = stripParenExpression(second);
47124
- if (!isNodeOfType(firstIdentifier, "Identifier") || !isNodeOfType(secondIdentifier, "Identifier") || firstIdentifier.name !== secondIdentifier.name) return false;
47125
- const firstBinding = findVariableInitializer(firstIdentifier, firstIdentifier.name);
47126
- const secondBinding = findVariableInitializer(secondIdentifier, secondIdentifier.name);
47127
- return firstBinding?.bindingIdentifier === secondBinding?.bindingIdentifier;
47128
- };
47129
- const isReactChildrenArrayNormalization = (node) => {
47130
- const candidate = stripParenExpression(node);
47131
- if (!isNodeOfType(candidate, "ConditionalExpression")) return false;
47132
- const test = stripParenExpression(candidate.test);
47133
- if (!isNodeOfType(test, "CallExpression") || !isNodeOfType(test.callee, "MemberExpression") || test.callee.computed || !isNodeOfType(test.callee.object, "Identifier") || test.callee.object.name !== "Array" || findVariableInitializer(test.callee.object, "Array") || !isNodeOfType(test.callee.property, "Identifier") || test.callee.property.name !== "isArray") return false;
47134
- const testedValueNode = test.arguments?.[0];
47135
- if (!testedValueNode) return false;
47136
- const testedValue = stripParenExpression(testedValueNode);
47137
- if (!isNodeOfType(testedValue, "Identifier")) return false;
47138
- const testedBinding = findVariableInitializer(testedValue, testedValue.name);
47139
- if (testedValue.name !== "children" || !testedBinding || !findEnclosingParameter(testedBinding.bindingIdentifier)) return false;
47140
- const branchWrapsTestedValue = (branch) => {
47141
- const unwrappedBranch = stripParenExpression(branch);
47142
- return isNodeOfType(unwrappedBranch, "ArrayExpression") && unwrappedBranch.elements?.length === 1 && Boolean(unwrappedBranch.elements[0] && isSameIdentifier(unwrappedBranch.elements[0], testedValue));
47143
- };
47144
- return isSameIdentifier(candidate.consequent, testedValue) && branchWrapsTestedValue(candidate.alternate) || isSameIdentifier(candidate.alternate, testedValue) && branchWrapsTestedValue(candidate.consequent);
47145
- };
47146
- const isMutatedEmptyArrayBinding = (identifierNode, depth) => {
47147
- const binding = findVariableInitializer(identifierNode, identifierNode.name);
47148
- const initializer = binding?.initializer ? stripParenExpression(binding.initializer) : null;
47149
- if (!binding || !initializer || !isNodeOfType(initializer, "ArrayExpression") || initializer.elements?.length !== 0) return false;
47150
- const program = findProgramRoot(identifierNode);
47151
- if (!program) return false;
47152
- let didFindReactChildPush = false;
47153
- walkAst(program, (child) => {
47154
- if (didFindReactChildPush) return false;
47155
- if (!isNodeOfType(child, "CallExpression") || !isNodeOfType(child.callee, "MemberExpression") || child.callee.computed || !isNodeOfType(child.callee.object, "Identifier") || child.callee.object.name !== identifierNode.name || !isNodeOfType(child.callee.property, "Identifier") || child.callee.property.name !== "push") return;
47156
- if (findVariableInitializer(child.callee.object, identifierNode.name)?.bindingIdentifier === binding.bindingIdentifier && (child.arguments ?? []).some((argument) => {
47157
- const candidate = stripParenExpression(argument);
47158
- if (!(isNodeOfType(candidate, "Identifier") || isNodeOfType(candidate, "JSXElement") || isNodeOfType(candidate, "JSXFragment"))) return false;
47159
- let doesCarryReactChild = false;
47160
- walkAst(candidate, (argumentChild) => {
47161
- if (doesCarryReactChild) return false;
47162
- if (!isNodeOfType(argumentChild, "Identifier")) return;
47163
- const declarator = findVariableInitializer(argumentChild, argumentChild.name)?.bindingIdentifier.parent;
47164
- const declaration = declarator?.parent;
47165
- const forOfStatement = declaration?.parent;
47166
- if (declarator && isNodeOfType(declarator, "VariableDeclarator") && declaration && isNodeOfType(declaration, "VariableDeclaration") && forOfStatement && isNodeOfType(forOfStatement, "ForOfStatement") && forOfStatement.left === declaration && isDynamicReactChildrenExpression(forOfStatement.right, depth + 1)) {
47167
- doesCarryReactChild = true;
47168
- return false;
47169
- }
47170
- });
47171
- return doesCarryReactChild;
47172
- })) {
47173
- didFindReactChildPush = true;
47174
- return false;
47175
- }
47176
- });
47177
- return didFindReactChildPush;
47178
- };
47179
- const isDynamicReactChildrenExpression = (expression, depth) => {
47180
- if (depth > TYPE_RESOLUTION_DEPTH_LIMIT$2) return false;
47181
- const candidate = stripParenExpression(expression);
47182
- if (isReactChildrenToArrayCall(candidate) || isReactChildrenArrayNormalization(candidate)) return true;
47183
- if (isNodeOfType(candidate, "Identifier")) {
47184
- if (isMutatedEmptyArrayBinding(candidate, depth)) return true;
47185
- const binding = findVariableInitializer(candidate, candidate.name);
47186
- return Boolean(binding?.initializer && isDynamicReactChildrenExpression(binding.initializer, depth + 1));
47187
- }
47188
- if (isNodeOfType(candidate, "CallExpression") && isNodeOfType(candidate.callee, "MemberExpression") && !candidate.callee.computed && isNodeOfType(candidate.callee.property, "Identifier") && candidate.callee.property.name === "filter") return isDynamicReactChildrenExpression(candidate.callee.object, depth + 1);
47189
- return false;
47190
- };
47191
46802
  const resolveKeyTemplateLiteral = (expression) => {
47192
46803
  const node = stripParenExpression(expression);
47193
46804
  if (isNodeOfType(node, "TemplateLiteral")) return node;
@@ -47240,19 +46851,28 @@ const findBareItemNamesReferencedByTemplate = (template, itemNames) => {
47240
46851
  }
47241
46852
  return referencedItemNames;
47242
46853
  };
47243
- const isNumericPlaceholderLoopCounter = (attributeNode, indexName) => {
46854
+ const forLoopTestReadsDataLength = (test) => {
46855
+ let didFindLengthRead = false;
46856
+ walkAst(test, (child) => {
46857
+ if (didFindLengthRead) return false;
46858
+ if (isNodeOfType(child, "MemberExpression") && isNodeOfType(child.property, "Identifier") && child.property.name === "length") {
46859
+ didFindLengthRead = true;
46860
+ return false;
46861
+ }
46862
+ });
46863
+ return didFindLengthRead;
46864
+ };
46865
+ const isNumericForLoopCounter = (attributeNode, indexName) => {
47244
46866
  const binding = findVariableInitializer(attributeNode, indexName);
47245
46867
  if (!binding) return false;
47246
46868
  const declarator = binding.bindingIdentifier.parent;
47247
46869
  if (!declarator || !isNodeOfType(declarator, "VariableDeclarator")) return false;
47248
46870
  const declaration = declarator.parent;
47249
46871
  if (!declaration || !isNodeOfType(declaration, "VariableDeclaration")) return false;
47250
- if (!declarator.init || !isNodeOfType(declarator.init, "Literal") || typeof declarator.init.value !== "number") return false;
47251
46872
  const forStatement = declaration.parent;
47252
- if (forStatement && isNodeOfType(forStatement, "ForStatement") && forStatement.init === declaration) return !(forStatement.test && loopTestBoundsCounterByLength(forStatement.test, indexName, binding.bindingIdentifier));
47253
- const whileLoop = findEnclosingWhileLoop(attributeNode);
47254
- if (!whileLoop) return false;
47255
- return !loopTestBoundsCounterByLength(whileLoop.test, indexName, binding.bindingIdentifier);
46873
+ if (!forStatement || !isNodeOfType(forStatement, "ForStatement") || forStatement.init !== declaration || !declarator.init || !isNodeOfType(declarator.init, "Literal") || typeof declarator.init.value !== "number") return false;
46874
+ if (forStatement.test && forLoopTestReadsDataLength(forStatement.test)) return false;
46875
+ return true;
47256
46876
  };
47257
46877
  const EMPTY_NAME_SET$1 = /* @__PURE__ */ new Set();
47258
46878
  const findIteratorItemNamesOfBinding = (binding) => {
@@ -47286,11 +46906,11 @@ const collectDerivedRowContentNames = (bindingFunction, itemNames) => {
47286
46906
  * observable harm; anything stateful — form controls, media, custom
47287
46907
  * components, unknown calls — keeps the diagnostic.
47288
46908
  */
47289
- const fragmentHasStatefulChildren = (openingElement, itemNames, derivedNames, areBareItemsDynamicReactChildren) => {
46909
+ const fragmentHasStatefulChildren = (openingElement, itemNames, derivedNames) => {
47290
46910
  const jsxElement = openingElement.parent;
47291
46911
  if (!jsxElement || !isNodeOfType(jsxElement, "JSXElement")) return false;
47292
46912
  const children = jsxElement.children ?? [];
47293
- const bareIdentifierNames = children.some((child) => isNodeOfType(child, "JSXElement")) ? derivedNames : areBareItemsDynamicReactChildren ? derivedNames : new Set([...derivedNames, ...itemNames]);
46913
+ const bareIdentifierNames = children.some((child) => isNodeOfType(child, "JSXElement")) ? derivedNames : new Set([...derivedNames, ...itemNames]);
47294
46914
  return children.some((child) => containsStatefulDescendant(child, {
47295
46915
  memberRootNames: itemNames,
47296
46916
  allowAnyMemberRead: true,
@@ -47298,15 +46918,6 @@ const fragmentHasStatefulChildren = (openingElement, itemNames, derivedNames, ar
47298
46918
  callCalleeRootNames: itemNames
47299
46919
  }));
47300
46920
  };
47301
- const elementHasDirectItemChild = (openingElement, itemNames) => {
47302
- const jsxElement = openingElement.parent;
47303
- if (!jsxElement || !isNodeOfType(jsxElement, "JSXElement")) return false;
47304
- return (jsxElement.children ?? []).some((child) => {
47305
- if (!isNodeOfType(child, "JSXExpressionContainer")) return false;
47306
- const expression = stripParenExpression(child.expression);
47307
- return isNodeOfType(expression, "Identifier") && itemNames.has(expression.name);
47308
- });
47309
- };
47310
46921
  const callbackFiltersRows = (bindingFunction) => {
47311
46922
  if (!bindingFunction) return false;
47312
46923
  let didFindNullReturn = false;
@@ -47358,21 +46969,19 @@ const noArrayIndexAsKey = defineRule({
47358
46969
  const indexUse = findPositionalIndexUse(node.value.expression, 0);
47359
46970
  if (!indexUse) return;
47360
46971
  const indexName = indexUse.identifier.name;
47361
- if (isNumericPlaceholderLoopCounter(node, indexName)) return;
46972
+ if (isNumericForLoopCounter(node, indexName)) return;
47362
46973
  if (indexUse.binding.iteratorCall && iteratorCallExemptsIndexKey(indexUse.binding.iteratorCall)) return;
47363
46974
  const keyTemplate = resolveKeyTemplateLiteral(node.value.expression);
47364
46975
  if (keyTemplate && templateHasOuterMemberIdentity(keyTemplate, indexUse.binding.bindingFunction)) return;
47365
- if (hasAriaHiddenAncestor(node) && !indexUse.binding.isDataIndexedLoopCounter) return;
46976
+ if (hasAriaHiddenAncestor(node)) return;
47366
46977
  const itemNames = findIteratorItemNamesOfBinding(indexUse.binding);
47367
46978
  const derivedNames = collectDerivedRowContentNames(indexUse.binding.bindingFunction, itemNames);
47368
- const iteratorCallee = indexUse.binding.iteratorCall?.callee;
47369
- const hasDynamicReactChildren = Boolean(iteratorCallee && isNodeOfType(iteratorCallee, "MemberExpression") && isDynamicReactChildrenExpression(iteratorCallee.object, 0));
47370
46979
  const openingElement = node.parent;
47371
46980
  if (openingElement && isNodeOfType(openingElement, "JSXOpeningElement")) {
47372
46981
  const elementName = openingElement.name;
47373
46982
  if (isNodeOfType(elementName, "JSXIdentifier")) {
47374
46983
  if (elementName.name === "Fragment") {
47375
- if (!fragmentHasStatefulChildren(openingElement, itemNames, derivedNames, hasDynamicReactChildren)) return;
46984
+ if (!fragmentHasStatefulChildren(openingElement, itemNames, derivedNames)) return;
47376
46985
  } else if (PURE_SVG_PRIMITIVE_TAGS.has(elementName.name)) {
47377
46986
  if (!callbackFiltersRows(indexUse.binding.bindingFunction)) return;
47378
46987
  } else if (STATELESS_HTML_LEAF_TAGS.has(elementName.name)) {
@@ -47380,14 +46989,14 @@ const noArrayIndexAsKey = defineRule({
47380
46989
  if (jsxElement && isNodeOfType(jsxElement, "JSXElement")) {
47381
46990
  const isInlineTextRun = INLINE_TEXT_LEAF_TAGS.has(elementName.name);
47382
46991
  const primitiveItemNames = keyTemplate ? findBareItemNamesReferencedByTemplate(keyTemplate, itemNames) : EMPTY_NAME_SET$1;
47383
- if (!(hasDynamicReactChildren && elementHasDirectItemChild(openingElement, itemNames) || containsStatefulDescendant(jsxElement, {
46992
+ if (!containsStatefulDescendant(jsxElement, {
47384
46993
  memberRootNames: isInlineTextRun ? itemNames : EMPTY_NAME_SET$1,
47385
46994
  bareIdentifierNames: primitiveItemNames.size > 0 ? new Set([...derivedNames, ...primitiveItemNames]) : derivedNames
47386
- }))) return;
46995
+ })) return;
47387
46996
  }
47388
46997
  }
47389
46998
  }
47390
- if (isNodeOfType(elementName, "JSXMemberExpression") && isNodeOfType(elementName.object, "JSXIdentifier") && isNodeOfType(elementName.property, "JSXIdentifier") && elementName.object.name === "React" && elementName.property.name === "Fragment" && !fragmentHasStatefulChildren(openingElement, itemNames, derivedNames, hasDynamicReactChildren)) return;
46999
+ if (isNodeOfType(elementName, "JSXMemberExpression") && isNodeOfType(elementName.object, "JSXIdentifier") && isNodeOfType(elementName.property, "JSXIdentifier") && elementName.object.name === "React" && elementName.property.name === "Fragment" && !fragmentHasStatefulChildren(openingElement, itemNames, derivedNames)) return;
47391
47000
  }
47392
47001
  context.report({
47393
47002
  node,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-react-doctor",
3
- "version": "0.9.2-dev.3bc63ea",
3
+ "version": "0.9.2-dev.444e177",
4
4
  "description": "React Doctor rules for oxlint.",
5
5
  "keywords": [
6
6
  "accessibility",