oxlint-plugin-react-doctor 0.7.7-dev.287dff7 → 0.7.7-dev.30b7eeb

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 +234 -1148
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10558,7 +10558,7 @@ const getAssignedName = (node) => {
10558
10558
  }
10559
10559
  return null;
10560
10560
  };
10561
- const isModuleExportsAssignment$1 = (node) => {
10561
+ const isModuleExportsAssignment = (node) => {
10562
10562
  const parent = node.parent;
10563
10563
  if (!parent || !isNodeOfType(parent, "AssignmentExpression")) return false;
10564
10564
  const left = parent.left;
@@ -10717,7 +10717,7 @@ const displayName = defineRule({
10717
10717
  }
10718
10718
  const assignedName = getAssignedName(node);
10719
10719
  if (assignedName && isReactComponentName(assignedName) && ignoreNamed) return;
10720
- if (isModuleExportsAssignment$1(node) && !node.id) {
10720
+ if (isModuleExportsAssignment(node) && !node.id) {
10721
10721
  reportAt(node);
10722
10722
  return;
10723
10723
  }
@@ -10739,7 +10739,7 @@ const displayName = defineRule({
10739
10739
  reportAt(node);
10740
10740
  return;
10741
10741
  }
10742
- if (isModuleExportsAssignment$1(node)) {
10742
+ if (isModuleExportsAssignment(node)) {
10743
10743
  reportAt(node);
10744
10744
  return;
10745
10745
  }
@@ -14368,22 +14368,6 @@ const isReactHocCallbackArgument = (functionNode) => {
14368
14368
  return calleeName !== null && REACT_HOC_NAMES.has(calleeName);
14369
14369
  };
14370
14370
  //#endregion
14371
- //#region src/plugin/utils/is-outside-all-functions.ts
14372
- const FUNCTION_SCOPE_KINDS = new Set([
14373
- "function",
14374
- "arrow-function",
14375
- "method"
14376
- ]);
14377
- const isOutsideAllFunctions = (symbol) => {
14378
- let scope = symbol.scope;
14379
- while (scope) {
14380
- if (FUNCTION_SCOPE_KINDS.has(scope.kind)) return false;
14381
- if (scope.kind === "module") return true;
14382
- scope = scope.parent;
14383
- }
14384
- return true;
14385
- };
14386
- //#endregion
14387
14371
  //#region src/plugin/rules/react-builtins/exhaustive-deps-low-level.ts
14388
14372
  /**
14389
14373
  * Lowest-level helpers consumed by both the main `exhaustive-deps`
@@ -14436,6 +14420,25 @@ const getHookName = (callee, scopes) => {
14436
14420
  if (isNodeOfType(strippedCallee, "MemberExpression") && !strippedCallee.computed && isNodeOfType(strippedCallee.property, "Identifier")) return strippedCallee.property.name;
14437
14421
  return null;
14438
14422
  };
14423
+ const FUNCTION_SCOPE_KINDS = new Set([
14424
+ "function",
14425
+ "arrow-function",
14426
+ "method"
14427
+ ]);
14428
+ /**
14429
+ * True for symbols declared at module scope (outside any function
14430
+ * scope). Module-scope bindings don't change between renders so they
14431
+ * don't need to live in dependency arrays.
14432
+ */
14433
+ const isOutsideAllFunctions = (symbol) => {
14434
+ let scope = symbol.scope;
14435
+ while (scope) {
14436
+ if (FUNCTION_SCOPE_KINDS.has(scope.kind)) return false;
14437
+ if (scope.kind === "module") return true;
14438
+ scope = scope.parent ?? null;
14439
+ }
14440
+ return true;
14441
+ };
14439
14442
  //#endregion
14440
14443
  //#region src/plugin/rules/react-builtins/exhaustive-deps-messages.ts
14441
14444
  const buildMissingDepMessage = (hookName, depName) => `\`${hookName}\` can run with a stale \`${depName}\` & show your users old data.`;
@@ -29493,7 +29496,7 @@ const isNumericForLoopCounter = (attributeNode, indexName) => {
29493
29496
  if (forStatement.test && forLoopTestReadsDataLength(forStatement.test)) return false;
29494
29497
  return true;
29495
29498
  };
29496
- const EMPTY_NAME_SET$1 = /* @__PURE__ */ new Set();
29499
+ const EMPTY_NAME_SET = /* @__PURE__ */ new Set();
29497
29500
  const findIteratorItemNamesOfBinding = (binding) => {
29498
29501
  const names = /* @__PURE__ */ new Set();
29499
29502
  if (!binding.bindingFunction || !isFunctionLike$1(binding.bindingFunction)) return names;
@@ -29607,9 +29610,9 @@ const noArrayIndexAsKey = defineRule({
29607
29610
  const jsxElement = openingElement.parent;
29608
29611
  if (jsxElement && isNodeOfType(jsxElement, "JSXElement")) {
29609
29612
  const isInlineTextRun = INLINE_TEXT_LEAF_TAGS.has(elementName.name);
29610
- const primitiveItemNames = keyTemplate ? findBareItemNamesReferencedByTemplate(keyTemplate, itemNames) : EMPTY_NAME_SET$1;
29613
+ const primitiveItemNames = keyTemplate ? findBareItemNamesReferencedByTemplate(keyTemplate, itemNames) : EMPTY_NAME_SET;
29611
29614
  if (!containsStatefulDescendant(jsxElement, {
29612
- memberRootNames: isInlineTextRun ? itemNames : EMPTY_NAME_SET$1,
29615
+ memberRootNames: isInlineTextRun ? itemNames : EMPTY_NAME_SET,
29613
29616
  bareIdentifierNames: primitiveItemNames.size > 0 ? new Set([...derivedNames, ...primitiveItemNames]) : derivedNames
29614
29617
  })) return;
29615
29618
  }
@@ -30110,9 +30113,6 @@ const findNearestPackageDirectory = (filename) => {
30110
30113
  const readNearestPackageManifest = (filename) => {
30111
30114
  const packageDirectory = findNearestPackageDirectory(filename);
30112
30115
  if (!packageDirectory) return null;
30113
- return readPackageManifest(packageDirectory);
30114
- };
30115
- const readPackageManifest = (packageDirectory) => {
30116
30116
  const packageJsonPath = path.join(packageDirectory, "package.json");
30117
30117
  recordContentProbe(packageJsonPath);
30118
30118
  const cached = cachedManifestByPackageDirectory.get(packageDirectory);
@@ -30491,16 +30491,7 @@ const isRenderPreservingCallArgumentFunction = (node, scopes) => {
30491
30491
  return parent.arguments.some((argumentNode) => argumentNode === node) && isProvenArrayMapCall(parent, scopes);
30492
30492
  };
30493
30493
  const isNestedRenderEvidenceBoundary = (node, scopes) => NESTED_RENDER_EVIDENCE_BOUNDARY_TYPES.has(node.type) && !isRenderPreservingCallArgumentFunction(node, scopes);
30494
- const isRenderOutputExpression = (node, scopes) => node.type === "JSXElement" || node.type === "JSXFragment" || isReactApiCall(node, "createElement", scopes, REACT_CREATE_ELEMENT_OPTIONS) || isReactDomCreatePortalCall(node, scopes);
30495
- const isReactDomCreatePortalCall = (node, scopes) => {
30496
- if (!isNodeOfType(node, "CallExpression")) return false;
30497
- const callee = stripParenExpression(node.callee);
30498
- if (isNodeOfType(callee, "Identifier")) return scopes.symbolFor(callee)?.kind === "import" && getImportedNameFromModule(callee, callee.name, "react-dom") === "createPortal";
30499
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.object, "Identifier") || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "createPortal") return false;
30500
- const symbol = scopes.symbolFor(callee.object);
30501
- if (!symbol || symbol.kind !== "import") return false;
30502
- return isDefaultImportFromModule(callee.object, callee.object.name, "react-dom") || isNamespaceImportFromModule(callee.object, callee.object.name, "react-dom");
30503
- };
30494
+ const isRenderOutputExpression = (node, scopes) => node.type === "JSXElement" || node.type === "JSXFragment" || isReactApiCall(node, "createElement", scopes, REACT_CREATE_ELEMENT_OPTIONS);
30504
30495
  const containsRenderOutput$1 = (rootNode, scopes) => {
30505
30496
  let hasRenderOutput = false;
30506
30497
  walkAst(rootNode, (node) => {
@@ -41892,107 +41883,18 @@ const getNodeText$1 = (node) => {
41892
41883
  return value;
41893
41884
  });
41894
41885
  };
41895
- const normalizeAsBoolean = (identity) => ({
41896
- ...identity,
41897
- booleanNormalization: identity.booleanNormalization === "identity" ? "normalized" : identity.booleanNormalization
41898
- });
41899
- const negateBoolean = (identity) => ({
41900
- ...identity,
41901
- booleanNormalization: identity.booleanNormalization === "negated" ? "normalized" : "negated"
41902
- });
41903
- const getLivePropExpressionIdentity = (analysis, context, node, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
41904
- const expression = stripParenExpression(node);
41905
- if (isNodeOfType(expression, "Identifier")) {
41906
- const reference = getRef(analysis, expression);
41907
- const symbol = context.scopes.symbolFor(expression);
41908
- if (!reference || !symbol) return null;
41909
- if (isProp(analysis, reference)) return {
41910
- propSymbolId: symbol.id,
41911
- memberPath: [],
41912
- booleanNormalization: "identity"
41913
- };
41914
- if (visitedSymbolIds.has(symbol.id) || symbol.kind !== "const") return null;
41915
- visitedSymbolIds.add(symbol.id);
41916
- const initializer = getDirectConstInitializer(symbol);
41917
- if (initializer) return getLivePropExpressionIdentity(analysis, context, initializer, visitedSymbolIds);
41918
- return getUpstreamRefs(analysis, reference).some((upstreamReference) => isProp(analysis, upstreamReference)) ? {
41919
- propSymbolId: symbol.id,
41920
- memberPath: [],
41921
- booleanNormalization: "identity"
41922
- } : null;
41923
- }
41924
- if (isNodeOfType(expression, "MemberExpression")) {
41925
- const propertyName = getStaticMemberPropertyName(expression);
41926
- if (!propertyName) return null;
41927
- const objectIdentity = getLivePropExpressionIdentity(analysis, context, expression.object, visitedSymbolIds);
41928
- if (!objectIdentity) return null;
41929
- return {
41930
- ...objectIdentity,
41931
- memberPath: [...objectIdentity.memberPath, propertyName]
41932
- };
41933
- }
41934
- if (isNodeOfType(expression, "CallExpression") && isNodeOfType(expression.callee, "Identifier") && expression.callee.name === "Boolean" && context.scopes.isGlobalReference(expression.callee) && expression.arguments?.length === 1) {
41935
- const argument = expression.arguments[0];
41936
- const argumentIdentity = getLivePropExpressionIdentity(analysis, context, argument, visitedSymbolIds);
41937
- return argumentIdentity ? normalizeAsBoolean(argumentIdentity) : null;
41938
- }
41939
- if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "!") {
41940
- const argumentIdentity = getLivePropExpressionIdentity(analysis, context, expression.argument, visitedSymbolIds);
41941
- return argumentIdentity ? negateBoolean(argumentIdentity) : null;
41942
- }
41943
- return null;
41944
- };
41945
- const isMountSnapshotInitializer = (context, node) => {
41946
- if (!isReactApiCall(node, "useMemo", context.scopes, { resolveNamedAliases: true }) || !isNodeOfType(node, "CallExpression")) return false;
41947
- const dependencies = node.arguments?.[1];
41948
- return isNodeOfType(dependencies, "ArrayExpression") && dependencies.elements.length === 0;
41949
- };
41950
- const isMountSnapshotBinding = (context, identifier, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
41951
- const symbol = context.scopes.symbolFor(identifier);
41952
- if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
41953
- visitedSymbolIds.add(symbol.id);
41954
- const initializer = getDirectConstInitializer(symbol);
41955
- if (!initializer) return false;
41956
- if (isMountSnapshotInitializer(context, initializer)) return true;
41957
- const expression = stripParenExpression(initializer);
41958
- return isNodeOfType(expression, "Identifier") && isMountSnapshotBinding(context, expression, visitedSymbolIds);
41959
- };
41960
- const isConstantBooleanExpression = (context, node, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
41961
- const expression = stripParenExpression(node);
41962
- if (isNodeOfType(expression, "Literal")) return true;
41963
- if (isNodeOfType(expression, "Identifier")) {
41964
- const symbol = context.scopes.symbolFor(expression);
41965
- if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
41966
- visitedSymbolIds.add(symbol.id);
41967
- const initializer = getDirectConstInitializer(symbol);
41968
- return Boolean(initializer && isConstantBooleanExpression(context, initializer, visitedSymbolIds));
41969
- }
41970
- if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "!") return isConstantBooleanExpression(context, expression.argument, visitedSymbolIds);
41971
- if (isNodeOfType(expression, "CallExpression") && isNodeOfType(expression.callee, "Identifier") && expression.callee.name === "Boolean" && context.scopes.isGlobalReference(expression.callee) && expression.arguments?.length === 1) return isConstantBooleanExpression(context, expression.arguments[0], visitedSymbolIds);
41972
- return false;
41886
+ const isLiteralConstantIdentifier = (analysis, identifier) => {
41887
+ const definitions = getRef(analysis, identifier)?.resolved?.defs;
41888
+ if (!definitions || definitions.length !== 1) return false;
41889
+ const definition = definitions[0];
41890
+ if (definition.type !== "Variable") return false;
41891
+ const declarator = definition.node;
41892
+ if (!isNodeOfType(declarator, "VariableDeclarator")) return false;
41893
+ const declaration = declarator.parent;
41894
+ if (!isNodeOfType(declaration, "VariableDeclaration") || declaration.kind !== "const") return false;
41895
+ return isNodeOfType(declarator.init, "Literal");
41973
41896
  };
41974
- const isProvenLiveBinding = (analysis, context, identifier) => {
41975
- const reference = getRef(analysis, identifier);
41976
- const symbol = context.scopes.symbolFor(identifier);
41977
- if (!reference || !symbol || symbol.kind !== "const" || isOutsideAllFunctions(symbol)) return false;
41978
- const initializer = symbol.initializer;
41979
- if (!initializer || isMountSnapshotBinding(context, identifier)) return false;
41980
- if (getDownstreamRefs(analysis, initializer).some((initializerReference) => isRefCurrent(initializerReference))) return false;
41981
- return !isConstantBooleanExpression(context, initializer);
41982
- };
41983
- const areSameProvenLiveBinding = (analysis, context, left, right) => {
41984
- const leftExpression = stripParenExpression(left);
41985
- const rightExpression = stripParenExpression(right);
41986
- if (!isNodeOfType(leftExpression, "Identifier") || !isNodeOfType(rightExpression, "Identifier")) return false;
41987
- const leftSymbol = context.scopes.symbolFor(leftExpression);
41988
- const rightSymbol = context.scopes.symbolFor(rightExpression);
41989
- return Boolean(leftSymbol && leftSymbol === rightSymbol && isProvenLiveBinding(analysis, context, leftExpression));
41990
- };
41991
- const haveSameLivePropExpressionIdentity = (left, right) => {
41992
- if (left.propSymbolId !== right.propSymbolId || left.booleanNormalization !== right.booleanNormalization || left.memberPath.length !== right.memberPath.length) return false;
41993
- return left.memberPath.every((propertyName, index) => propertyName === right.memberPath[index]);
41994
- };
41995
- const isSetStateToInitialValue = (analysis, context, setterRef) => {
41897
+ const isSetStateToInitialValue = (analysis, setterRef) => {
41996
41898
  const callExpr = getCallExpr(setterRef);
41997
41899
  if (!callExpr || !isNodeOfType(callExpr, "CallExpression")) return false;
41998
41900
  const setStateToValue = callExpr.arguments?.[0];
@@ -42003,12 +41905,7 @@ const isSetStateToInitialValue = (analysis, context, setterRef) => {
42003
41905
  if (isUndefinedNode(setStateToValue) && isUndefinedNode(stateInitialValue)) return true;
42004
41906
  if (setStateToValue == null && stateInitialValue == null) return true;
42005
41907
  if (setStateToValue && !stateInitialValue || !setStateToValue && stateInitialValue) return false;
42006
- if (stateInitialValue && setStateToValue) {
42007
- const initialLivePropIdentity = getLivePropExpressionIdentity(analysis, context, stateInitialValue);
42008
- const nextLivePropIdentity = getLivePropExpressionIdentity(analysis, context, setStateToValue);
42009
- if (initialLivePropIdentity && nextLivePropIdentity && haveSameLivePropExpressionIdentity(initialLivePropIdentity, nextLivePropIdentity)) return false;
42010
- if (areSameProvenLiveBinding(analysis, context, stateInitialValue, setStateToValue)) return false;
42011
- }
41908
+ if (stateInitialValue && isNodeOfType(stateInitialValue, "Identifier") && stateInitialValue.name !== "undefined" && !isLiteralConstantIdentifier(analysis, stateInitialValue)) return false;
42012
41909
  return getNodeText$1(setStateToValue) === getNodeText$1(stateInitialValue);
42013
41910
  };
42014
41911
  const countUseStates = (analysis, componentNode) => {
@@ -42017,10 +41914,10 @@ const countUseStates = (analysis, componentNode) => {
42017
41914
  for (const ref of getDownstreamRefs(analysis, componentNode)) if (isState(analysis, ref)) stateVariables.add(ref.resolved);
42018
41915
  return stateVariables.size;
42019
41916
  };
42020
- const findPropUsedToResetAllState = (analysis, context, effectFnRefs, depsRefs, useEffectNode, effectFn) => {
41917
+ const findPropUsedToResetAllState = (analysis, effectFnRefs, depsRefs, useEffectNode, effectFn) => {
42021
41918
  const stateSetterRefs = effectFnRefs.filter((ref) => isSyncStateSetterCall(analysis, ref, effectFn));
42022
41919
  if (stateSetterRefs.length === 0) return null;
42023
- if (!stateSetterRefs.every((ref) => isSetStateToInitialValue(analysis, context, ref))) return null;
41920
+ if (!stateSetterRefs.every((ref) => isSetStateToInitialValue(analysis, ref))) return null;
42024
41921
  if (stateSetterRefs.every((setterRef) => effectFnRefs.some((otherRef) => otherRef !== setterRef && otherRef.resolved === setterRef.resolved && Boolean(getCallExpr(otherRef)) && !isSyncStateSetterCall(analysis, otherRef, effectFn)))) return null;
42025
41922
  const containing = findContainingNode(analysis, useEffectNode);
42026
41923
  if (new Set(stateSetterRefs.map((setterRef) => setterRef.resolved)).size !== countUseStates(analysis, containing)) return null;
@@ -42044,7 +41941,7 @@ const noResetAllStateOnPropChange = defineRule({
42044
41941
  if (containing && isCustomHook(containing)) return;
42045
41942
  const effectFn = getEffectFn(analysis, node);
42046
41943
  if (!effectFn) return;
42047
- if (!findPropUsedToResetAllState(analysis, context, effectFnRefs, depsRefs, node, effectFn)) return;
41944
+ if (!findPropUsedToResetAllState(analysis, effectFnRefs, depsRefs, node, effectFn)) return;
42048
41945
  context.report({
42049
41946
  node,
42050
41947
  message: `Your users briefly see stale state when a prop changes because this useEffect clears all state.`
@@ -45847,726 +45744,19 @@ const nosqlInjectionRisk = defineRule({
45847
45744
  })
45848
45745
  });
45849
45746
  //#endregion
45850
- //#region src/plugin/utils/export-all-adds-runtime-values.ts
45851
- const getExportedName = (node) => {
45852
- if (!node) return null;
45853
- if (isNodeOfType(node, "Identifier")) return node.name;
45854
- if (isNodeOfType(node, "Literal") && typeof node.value === "string") return node.value;
45855
- return null;
45856
- };
45857
- const programHasRuntimeNamedExports = (filePath, program, visitedFilePaths) => {
45858
- if (!isNodeOfType(program, "Program")) return true;
45859
- for (const statement of program.body) {
45860
- if (isNodeOfType(statement, "ExportDefaultDeclaration")) continue;
45861
- if (isNodeOfType(statement, "ExportAllDeclaration")) {
45862
- if (statement.exportKind === "type") continue;
45863
- if (statement.exported) return true;
45864
- if (typeof statement.source.value !== "string") return true;
45865
- if (exportAllAddsRuntimeValues(filePath, statement.source.value, visitedFilePaths)) return true;
45866
- continue;
45867
- }
45868
- if (!isNodeOfType(statement, "ExportNamedDeclaration")) continue;
45869
- if (statement.exportKind === "type") continue;
45870
- if (statement.declaration) {
45871
- if (statement.declaration.type !== "TSInterfaceDeclaration" && statement.declaration.type !== "TSTypeAliasDeclaration" && statement.declaration.type !== "TSDeclareFunction") return true;
45872
- continue;
45873
- }
45874
- if (isEverySpecifierInlineType(statement.specifiers, "ExportSpecifier", "exportKind")) continue;
45875
- for (const specifier of statement.specifiers) {
45876
- if (!isNodeOfType(specifier, "ExportSpecifier") || specifier.exportKind === "type") continue;
45877
- if (getExportedName(specifier.exported) !== "default") return true;
45878
- }
45879
- }
45880
- return false;
45881
- };
45882
- const exportAllAddsRuntimeValues = (importerFilePath, source, visitedFilePaths = /* @__PURE__ */ new Set()) => {
45883
- const resolvedFilePath = resolveRelativeImportPath(importerFilePath, source);
45884
- if (!resolvedFilePath) return true;
45885
- if (path.extname(resolvedFilePath) === ".cjs" || resolvedFilePath.endsWith(".cts")) return true;
45886
- if (visitedFilePaths.has(resolvedFilePath)) return true;
45887
- visitedFilePaths.add(resolvedFilePath);
45888
- const program = parseSourceFile(resolvedFilePath);
45889
- if (!program) return true;
45890
- return programHasRuntimeNamedExports(resolvedFilePath, program, visitedFilePaths);
45891
- };
45892
- //#endregion
45893
45747
  //#region src/plugin/utils/is-framework-route-or-special-filename.ts
45894
- const NEXT_APP_ROUTE_FILE_PATTERN = new RegExp(`^(page|layout|loading|error|not-found|template|default|global-error|route)\\.${NEXTJS_SOURCE_FILE_EXTENSION_GROUP}$`);
45895
- const NEXT_PAGES_ROUTE_FILE_PATTERN = new RegExp(`^(_app|_document|_error|_meta)\\.${NEXTJS_SOURCE_FILE_EXTENSION_GROUP}$`);
45896
- const EXPO_ROUTE_FILE_PATTERN = new RegExp(`^(_layout|\\+html|\\+not-found|\\+native-intent)\\.${NEXTJS_SOURCE_FILE_EXTENSION_GROUP}$`);
45897
- const TANSTACK_ROUTE_FILE_PATTERN = new RegExp(`(?:^__root|\\.lazy)\\.${NEXTJS_SOURCE_FILE_EXTENSION_GROUP}$`);
45898
- const REACT_ROUTER_FILE_PATTERN = new RegExp(`^(root|entry\\.client|entry\\.server)\\.${NEXTJS_SOURCE_FILE_EXTENSION_GROUP}$`);
45899
- const isInNextDirectory = (context, directoryPath) => {
45900
- if (isInProjectDirectory(context, directoryPath)) return true;
45901
- const filename = normalizeFilename(context.filename ?? "");
45902
- if (path.isAbsolute(filename)) return false;
45903
- return filename.startsWith(`${directoryPath}/`) || filename.includes(`/${directoryPath}/`);
45904
- };
45905
- const isInExpoRouteDirectory = (context) => {
45906
- const filename = normalizeFilename(context.filename ?? "");
45907
- const configuredRootDirectory = getReactDoctorStringSetting(context.settings, "rootDirectory");
45908
- const packageDirectory = path.isAbsolute(filename) ? findNearestPackageDirectory(filename) : null;
45909
- const projectRelativeFilename = getProjectRelativeFilename(filename, configuredRootDirectory ?? packageDirectory ?? void 0);
45910
- const relativeFilename = projectRelativeFilename === filename && path.isAbsolute(filename) ? filename.split("/").slice(2).join("/") : projectRelativeFilename;
45911
- return relativeFilename.startsWith("app/") || relativeFilename.startsWith("src/app/");
45912
- };
45913
- const isFrameworkRouteOrSpecialFilename = (context, runtime) => {
45914
- const rawFilename = context.filename;
45748
+ const sourceFileExtensionGroup = NEXTJS_SOURCE_FILE_EXTENSION_GROUP;
45749
+ const FRAMEWORK_ROUTE_FILE_PATTERNS = [
45750
+ new RegExp(`^(page|layout|loading|error|not-found|template|default|global-error|route|_app|_document|_error)\\.${sourceFileExtensionGroup}$`),
45751
+ new RegExp(`^(_layout|\\+html|\\+not-found|\\+native-intent)\\.${sourceFileExtensionGroup}$`),
45752
+ new RegExp(`(?:^__root|\\.lazy)\\.${sourceFileExtensionGroup}$`),
45753
+ new RegExp(`^(root|entry\\.client|entry\\.server)\\.${sourceFileExtensionGroup}$`)
45754
+ ];
45755
+ const isFrameworkRouteOrSpecialFilename = (rawFilename) => {
45915
45756
  if (!rawFilename) return false;
45757
+ if (isNextjsMetadataImageRouteFilename(rawFilename)) return true;
45916
45758
  const basename = path.basename(rawFilename);
45917
- if (runtime === "next") return isInNextDirectory(context, "app") && (isNextjsMetadataImageRouteFilename(rawFilename) || NEXT_APP_ROUTE_FILE_PATTERN.test(basename)) || isInNextDirectory(context, "pages") && NEXT_PAGES_ROUTE_FILE_PATTERN.test(basename);
45918
- if (runtime === "expo") return isInExpoRouteDirectory(context) && EXPO_ROUTE_FILE_PATTERN.test(basename);
45919
- if (runtime === "tanstack") return TANSTACK_ROUTE_FILE_PATTERN.test(basename);
45920
- if (runtime === "react-router" || runtime === "remix") return REACT_ROUTER_FILE_PATTERN.test(basename);
45921
- return false;
45922
- };
45923
- //#endregion
45924
- //#region src/plugin/utils/function-has-react-element-return-type.ts
45925
- const isReactNamespaceBinding = (node) => {
45926
- if (!isNodeOfType(node, "Identifier")) return false;
45927
- const binding = getImportBindingForName(node, node.name);
45928
- return Boolean(binding && binding.source === "react" && (binding.isNamespace || binding.exportedName === "default"));
45929
- };
45930
- const hasLocalJsxNamespace = (node) => {
45931
- const program = findProgramRoot(node);
45932
- if (!program || !isNodeOfType(program, "Program")) return false;
45933
- return program.body.some((statement) => isNodeOfType(statement, "TSModuleDeclaration") && isNodeOfType(statement.id, "Identifier") && statement.id.name === "JSX");
45934
- };
45935
- const isReactElementTypeReference = (node) => {
45936
- if (!isNodeOfType(node, "TSTypeReference")) return false;
45937
- const typeName = node.typeName;
45938
- if (isNodeOfType(typeName, "Identifier")) return getImportedNameFromModule(typeName, typeName.name, "react") === "ReactElement";
45939
- if (!isNodeOfType(typeName, "TSQualifiedName")) return false;
45940
- if (!isNodeOfType(typeName.right, "Identifier")) return false;
45941
- if (typeName.right.name === "ReactElement" && isReactNamespaceBinding(typeName.left)) return true;
45942
- if (typeName.right.name !== "Element") return false;
45943
- if (isNodeOfType(typeName.left, "Identifier")) {
45944
- if (typeName.left.name !== "JSX") return false;
45945
- const binding = getImportBindingForName(typeName.left, typeName.left.name);
45946
- if (!binding) return !hasLocalJsxNamespace(typeName.left);
45947
- return binding.source === "react" && binding.exportedName === "JSX";
45948
- }
45949
- return isNodeOfType(typeName.left, "TSQualifiedName") && isNodeOfType(typeName.left.right, "Identifier") && typeName.left.right.name === "JSX" && isReactNamespaceBinding(typeName.left.left);
45950
- };
45951
- const containsReactElementType = (node) => {
45952
- if (isNodeOfType(node, "TSUnionType")) return node.types.some((member) => containsReactElementType(member));
45953
- return isReactElementTypeReference(node);
45954
- };
45955
- const functionHasReactElementReturnType = (functionNode) => {
45956
- const returnType = Reflect.get(functionNode, "returnType");
45957
- if (!isNodeOfType(returnType, "TSTypeAnnotation")) return false;
45958
- return containsReactElementType(returnType.typeAnnotation);
45959
- };
45960
- //#endregion
45961
- //#region src/plugin/constants/fast-refresh.ts
45962
- const FAST_REFRESH_CONFIG_FILENAMES = [
45963
- "vite.config.ts",
45964
- "vite.config.mts",
45965
- "vite.config.cts",
45966
- "vite.config.js",
45967
- "vite.config.mjs",
45968
- "vite.config.cjs",
45969
- "webpack.config.ts",
45970
- "webpack.config.js",
45971
- "webpack.config.mjs",
45972
- "webpack.config.cjs",
45973
- "rsbuild.config.ts",
45974
- "rsbuild.config.js",
45975
- "rspack.config.ts",
45976
- "rspack.config.js"
45977
- ];
45978
- const MINIMUM_FAST_REFRESH_VERSIONS = {
45979
- dumi: {
45980
- major: 2,
45981
- minor: 0
45982
- },
45983
- expo: {
45984
- major: 36,
45985
- minor: 0
45986
- },
45987
- gatsby: {
45988
- major: 2,
45989
- minor: 31
45990
- },
45991
- next: {
45992
- major: 9,
45993
- minor: 4
45994
- },
45995
- parcel: {
45996
- major: 2,
45997
- minor: 0
45998
- },
45999
- reactForGatsbyTwo: {
46000
- major: 17,
46001
- minor: 0
46002
- },
46003
- reactNative: {
46004
- major: 0,
46005
- minor: 61
46006
- },
46007
- reactScripts: {
46008
- major: 4,
46009
- minor: 0
46010
- },
46011
- storybookReact: {
46012
- major: 6,
46013
- minor: 1
46014
- }
46015
- };
46016
- //#endregion
46017
- //#region src/plugin/utils/get-fast-refresh-file-status.ts
46018
- const INTEGRATION_IMPORTS = new Map([
46019
- ["@vitejs/plugin-react", {
46020
- importedNames: null,
46021
- requiresViteDevelopmentServer: true,
46022
- runtime: "generic"
46023
- }],
46024
- ["@vitejs/plugin-react-swc", {
46025
- importedNames: null,
46026
- requiresViteDevelopmentServer: true,
46027
- runtime: "generic"
46028
- }],
46029
- ["@pmmmwh/react-refresh-webpack-plugin", {
46030
- importedNames: null,
46031
- runtime: "generic"
46032
- }],
46033
- ["@react-router/dev/vite", {
46034
- importedNames: new Set(["reactRouter"]),
46035
- requiresViteDevelopmentServer: true,
46036
- runtime: "react-router"
46037
- }],
46038
- ["@remix-run/dev", {
46039
- importedNames: new Set(["vitePlugin"]),
46040
- requiresViteDevelopmentServer: true,
46041
- runtime: "remix"
46042
- }],
46043
- ["@rsbuild/plugin-react", {
46044
- importedNames: new Set(["pluginReact"]),
46045
- runtime: "generic"
46046
- }],
46047
- ["@rspack/plugin-react-refresh", {
46048
- importedNames: null,
46049
- runtime: "generic"
46050
- }],
46051
- ["@rozenite/vite-plugin", {
46052
- importedNames: new Set(["rozenitePlugin"]),
46053
- requiresViteDevelopmentServer: true,
46054
- runtime: "generic"
46055
- }],
46056
- ["@tanstack/react-start/plugin/vite", {
46057
- importedNames: new Set(["tanstackStart"]),
46058
- requiresViteDevelopmentServer: true,
46059
- runtime: "tanstack"
46060
- }]
46061
- ]);
46062
- const REGISTERED_INTEGRATION_RUNTIME_PRECEDENCE = [
46063
- "tanstack",
46064
- "remix",
46065
- "react-router",
46066
- "generic"
46067
- ];
46068
- const cachedLocalStatusByManifest = /* @__PURE__ */ new WeakMap();
46069
- const cachedWorkspaceIndexByManifest = /* @__PURE__ */ new WeakMap();
46070
- const INACTIVE_STATUS = {
46071
- isActive: false,
46072
- runtime: "generic"
46073
- };
46074
- const WORKSPACE_IGNORED_DIRECTORY_NAMES = new Set([
46075
- ".git",
46076
- ".next",
46077
- "build",
46078
- "coverage",
46079
- "dist",
46080
- "node_modules",
46081
- "out"
46082
- ]);
46083
- const parseVersion = (version) => {
46084
- if (typeof version !== "string") return null;
46085
- const match = version.match(/(?:^|[^\d])(\d+)(?:\.(\d+))?/);
46086
- if (!match) return null;
46087
- return {
46088
- major: Number(match[1]),
46089
- minor: Number(match[2] ?? 0)
46090
- };
46091
- };
46092
- const isVersionAtLeast = (version, minimum) => {
46093
- const parsed = parseVersion(version);
46094
- if (!parsed) return false;
46095
- return parsed.major > minimum.major || parsed.major === minimum.major && parsed.minor >= minimum.minor;
46096
- };
46097
- const getDependencyVersion = (manifest, dependencyName) => manifest.dependencies?.[dependencyName] ?? manifest.devDependencies?.[dependencyName] ?? manifest.peerDependencies?.[dependencyName] ?? manifest.optionalDependencies?.[dependencyName];
46098
- const getRuntimeDependencyVersion = (manifest, dependencyName) => manifest.dependencies?.[dependencyName] ?? manifest.optionalDependencies?.[dependencyName];
46099
- const getOwnedDependencyVersion = (manifest, dependencyName, developmentCommandPattern) => {
46100
- const runtimeVersion = getRuntimeDependencyVersion(manifest, dependencyName);
46101
- if (runtimeVersion !== void 0) return runtimeVersion;
46102
- return Object.values(manifest.scripts ?? {}).some((script) => typeof script === "string" && developmentCommandPattern.test(script)) ? manifest.devDependencies?.[dependencyName] : void 0;
46103
- };
46104
- const hasParcelBrowserEntry = (manifest) => {
46105
- if (typeof manifest.source === "string" && manifest.source.endsWith(".html")) return true;
46106
- return Object.values(manifest.scripts ?? {}).some((script) => typeof script === "string" && /(?:^|\s)parcel(?:\s+serve)?\s+[^\n]*\.html(?:\s|$)/.test(script));
46107
- };
46108
- const hasParcelDevelopmentCommand = (manifest) => Object.values(manifest.scripts ?? {}).some((script) => typeof script === "string" && /(?:^|[\s;&|'\\"])parcel(?=\s|$)(?!\s+(?:build|help|watch|--help|--version|-h|-V)(?:\s|$))/.test(script) && !/(?:^|\s)--no-hmr(?:\s|$)/.test(script));
46109
- const hasOwnedDevelopmentCommand = (manifest, commandPattern) => Object.values(manifest.scripts ?? {}).some((script) => typeof script === "string" && commandPattern.test(script));
46110
- const hasViteDevelopmentCommand = (manifest) => Object.values(manifest.scripts ?? {}).some((script) => {
46111
- if (typeof script !== "string") return false;
46112
- return [...script.matchAll(/(?:^|[\s;&|'\\"])vite(?:\s+(build|preview|test)\b)?/g)].some((match) => match[1] === void 0);
46113
- });
46114
- const hasViteBrowserEntry = (manifest, packageDirectory) => {
46115
- if (typeof manifest.source === "string" && manifest.source.endsWith(".html")) return true;
46116
- const browserEntryPath = path.join(packageDirectory, "index.html");
46117
- recordExistenceProbe(browserEntryPath);
46118
- return fs.existsSync(browserEntryPath);
46119
- };
46120
- const getBuiltInStatus = (manifest) => {
46121
- if (isVersionAtLeast(getOwnedDependencyVersion(manifest, "next", /(?:^|\s)next\s+dev(?:\s|$)/), MINIMUM_FAST_REFRESH_VERSIONS.next)) return {
46122
- isActive: true,
46123
- runtime: "next"
46124
- };
46125
- const gatsbyDependency = getOwnedDependencyVersion(manifest, "gatsby", /(?:^|\s)gatsby\s+develop(?:\s|$)/);
46126
- const gatsbyVersion = parseVersion(gatsbyDependency);
46127
- const hasGatsbyFastRefresh = gatsbyVersion !== null && (gatsbyVersion.major >= 3 || isVersionAtLeast(gatsbyDependency, MINIMUM_FAST_REFRESH_VERSIONS.gatsby) && isVersionAtLeast(getDependencyVersion(manifest, "react"), MINIMUM_FAST_REFRESH_VERSIONS.reactForGatsbyTwo));
46128
- if (isVersionAtLeast(getOwnedDependencyVersion(manifest, "react-scripts", /(?:^|\s)react-scripts\s+start(?:\s|$)/), MINIMUM_FAST_REFRESH_VERSIONS.reactScripts) || hasGatsbyFastRefresh || isVersionAtLeast(getDependencyVersion(manifest, "parcel"), MINIMUM_FAST_REFRESH_VERSIONS.parcel) && declaresDependency(manifest, "react") && hasParcelDevelopmentCommand(manifest) && hasParcelBrowserEntry(manifest)) return {
46129
- isActive: true,
46130
- runtime: "generic"
46131
- };
46132
- if (isVersionAtLeast(getOwnedDependencyVersion(manifest, "expo", /(?:^|\s)expo\s+start(?:\s|$)/), MINIMUM_FAST_REFRESH_VERSIONS.expo) || isVersionAtLeast(getOwnedDependencyVersion(manifest, "react-native", /(?:^|\s)react-native\s+start(?:\s|$)/), MINIMUM_FAST_REFRESH_VERSIONS.reactNative)) return {
46133
- isActive: true,
46134
- runtime: "expo"
46135
- };
46136
- if (isVersionAtLeast(getOwnedDependencyVersion(manifest, "dumi", /(?:^|\s)dumi\s+dev(?:\s|$)/), MINIMUM_FAST_REFRESH_VERSIONS.dumi)) return {
46137
- isActive: true,
46138
- runtime: "generic"
46139
- };
46140
- return null;
46141
- };
46142
- const getIntegrationLocalNames = (program, scopes) => {
46143
- const localBindings = /* @__PURE__ */ new Map();
46144
- const registerLocalName = (source, importedName, localIdentifier) => {
46145
- const integration = INTEGRATION_IMPORTS.get(source);
46146
- if (!integration) return;
46147
- if (integration.importedNames === null ? importedName === null || importedName === "default" : importedName !== null && integration.importedNames.has(importedName)) {
46148
- const symbol = scopes.symbolFor(localIdentifier);
46149
- if (symbol) localBindings.set(symbol, integration);
46150
- }
46151
- };
46152
- walkAst(program, (node) => {
46153
- if (isNodeOfType(node, "ImportDeclaration")) {
46154
- const source = node.source.value;
46155
- if (typeof source !== "string") return;
46156
- for (const specifier of node.specifiers) {
46157
- if (isNodeOfType(specifier, "ImportDefaultSpecifier") || isNodeOfType(specifier, "ImportNamespaceSpecifier")) {
46158
- registerLocalName(source, null, specifier.local);
46159
- continue;
46160
- }
46161
- registerLocalName(source, getImportedName(specifier) ?? null, specifier.local);
46162
- }
46163
- return;
46164
- }
46165
- if (!isNodeOfType(node, "VariableDeclarator") || !node.init) return;
46166
- const initializer = node.init;
46167
- if (!isNodeOfType(initializer, "CallExpression") || !isNodeOfType(initializer.callee, "Identifier") || initializer.callee.name !== "require") return;
46168
- const sourceArgument = initializer.arguments[0];
46169
- if (!sourceArgument || !isNodeOfType(sourceArgument, "Literal")) return;
46170
- const source = sourceArgument.value;
46171
- if (typeof source !== "string") return;
46172
- if (isNodeOfType(node.id, "Identifier")) {
46173
- registerLocalName(source, null, node.id);
46174
- return;
46175
- }
46176
- if (!isNodeOfType(node.id, "ObjectPattern")) return;
46177
- for (const property of node.id.properties) {
46178
- if (!isNodeOfType(property, "Property") || !isNodeOfType(property.value, "Identifier")) continue;
46179
- registerLocalName(source, isNodeOfType(property.key, "Identifier") ? property.key.name : isNodeOfType(property.key, "Literal") && typeof property.key.value === "string" ? property.key.value : null, property.value);
46180
- }
46181
- });
46182
- return localBindings;
46183
- };
46184
- const isModuleExportsAssignment = (node) => isNodeOfType(node, "AssignmentExpression") && isNodeOfType(node.left, "MemberExpression") && isNodeOfType(node.left.object, "Identifier") && node.left.object.name === "module" && isNodeOfType(node.left.property, "Identifier") && node.left.property.name === "exports";
46185
- const getExportedBindings = (program, scopes) => {
46186
- const exportedBindings = /* @__PURE__ */ new Set();
46187
- walkAst(program, (node) => {
46188
- let exportRoot = null;
46189
- if (isNodeOfType(node, "ExportDefaultDeclaration")) exportRoot = node.declaration;
46190
- else if (isNodeOfType(node, "AssignmentExpression") && isModuleExportsAssignment(node)) exportRoot = node.right;
46191
- if (!exportRoot) return;
46192
- if (isFunctionLike$1(exportRoot) || isNodeOfType(exportRoot, "ObjectExpression")) return;
46193
- walkAst(exportRoot, (exportNode) => {
46194
- if (isFunctionLike$1(exportNode) || isNodeOfType(exportNode, "ObjectExpression")) return false;
46195
- if (!isNodeOfType(exportNode, "Identifier")) return;
46196
- const symbol = scopes.symbolFor(exportNode);
46197
- if (symbol) exportedBindings.add(symbol);
46198
- });
46199
- });
46200
- return exportedBindings;
46201
- };
46202
- const isExportedConfigProperty = (property, exportedBindings, scopes) => {
46203
- let ancestor = property.parent;
46204
- let didFindContainingObject = false;
46205
- let didCrossNestedProperty = false;
46206
- let didCrossFunctionBoundary = false;
46207
- let containingFunction = null;
46208
- let containingReturn = null;
46209
- while (ancestor) {
46210
- if (isNodeOfType(ancestor, "ObjectExpression") && !didFindContainingObject) didFindContainingObject = true;
46211
- else if (didFindContainingObject && isNodeOfType(ancestor, "Property")) didCrossNestedProperty = true;
46212
- if (!containingReturn && isNodeOfType(ancestor, "ReturnStatement")) containingReturn = ancestor;
46213
- if (isFunctionLike$1(ancestor)) if (containingFunction) didCrossFunctionBoundary = true;
46214
- else containingFunction = ancestor;
46215
- if (isNodeOfType(ancestor, "ExportDefaultDeclaration") || isModuleExportsAssignment(ancestor)) return !didCrossNestedProperty && !didCrossFunctionBoundary && (!containingFunction || Boolean(containingReturn) || !isNodeOfType(containingFunction.body, "BlockStatement"));
46216
- if (isNodeOfType(ancestor, "VariableDeclarator") && isNodeOfType(ancestor.id, "Identifier")) {
46217
- const binding = scopes.symbolFor(ancestor.id);
46218
- if (binding && exportedBindings.has(binding)) return !didCrossNestedProperty && !containingFunction;
46219
- }
46220
- ancestor = ancestor.parent;
46221
- }
46222
- return false;
46223
- };
46224
- const getExplicitConfigPaths = (packageDirectory, manifest) => {
46225
- const configPaths = new Set(FAST_REFRESH_CONFIG_FILENAMES.map((configFilename) => path.join(packageDirectory, configFilename)));
46226
- for (const script of Object.values(manifest.scripts ?? {})) {
46227
- if (typeof script !== "string") continue;
46228
- for (const match of script.matchAll(/(?:^|\s)--config(?:=|\s+)(?:"([^"]+)"|'([^']+)'|([^\s;&|]+))/g)) {
46229
- const relativeConfigPath = match[1] ?? match[2] ?? match[3];
46230
- if (!relativeConfigPath) continue;
46231
- const configPath = path.resolve(packageDirectory, relativeConfigPath);
46232
- const relativePath = path.relative(packageDirectory, configPath);
46233
- if (relativePath.startsWith("..") || path.isAbsolute(relativePath)) continue;
46234
- configPaths.add(configPath);
46235
- }
46236
- }
46237
- return [...configPaths];
46238
- };
46239
- const getRegisteredIntegration = (packageDirectory, manifest) => {
46240
- for (const configPath of getExplicitConfigPaths(packageDirectory, manifest)) {
46241
- const program = parseSourceFile(configPath);
46242
- if (!program) continue;
46243
- const scopes = analyzeScopes(program);
46244
- const integrationLocalBindings = getIntegrationLocalNames(program, scopes);
46245
- if (integrationLocalBindings.size === 0) continue;
46246
- const exportedBindings = getExportedBindings(program, scopes);
46247
- const registeredIntegrations = [];
46248
- walkAst(program, (node) => {
46249
- if (!isNodeOfType(node, "Property")) return;
46250
- if (!(isNodeOfType(node.key, "Identifier") && node.key.name === "plugins" || isNodeOfType(node.key, "Literal") && node.key.value === "plugins")) return;
46251
- if (!isExportedConfigProperty(node, exportedBindings, scopes)) return;
46252
- const inspectedBindings = /* @__PURE__ */ new Set();
46253
- const inspectValue = (value) => {
46254
- walkAst(value, (valueNode) => {
46255
- if (isNodeOfType(valueNode, "Identifier")) {
46256
- const symbol = scopes.symbolFor(valueNode);
46257
- const initializer = symbol ? getDirectUnreassignedInitializer(symbol) : null;
46258
- if (symbol && initializer && !inspectedBindings.has(symbol.id)) {
46259
- inspectedBindings.add(symbol.id);
46260
- inspectValue(initializer);
46261
- }
46262
- }
46263
- if (!isNodeOfType(valueNode, "CallExpression") && !isNodeOfType(valueNode, "NewExpression")) return;
46264
- if (!isNodeOfType(valueNode.callee, "Identifier")) return;
46265
- const symbol = scopes.symbolFor(valueNode.callee);
46266
- const integration = symbol ? integrationLocalBindings.get(symbol) : null;
46267
- if (integration) registeredIntegrations.push(integration);
46268
- });
46269
- };
46270
- inspectValue(node.value);
46271
- return false;
46272
- });
46273
- const hasViteDevelopmentRuntime = hasViteDevelopmentCommand(manifest) || hasViteBrowserEntry(manifest, packageDirectory);
46274
- const registeredIntegration = REGISTERED_INTEGRATION_RUNTIME_PRECEDENCE.flatMap((runtime) => registeredIntegrations.filter((integration) => integration.runtime === runtime && (!integration.requiresViteDevelopmentServer || hasViteDevelopmentRuntime)))[0];
46275
- if (registeredIntegration) return {
46276
- isActive: true,
46277
- runtime: registeredIntegration.runtime
46278
- };
46279
- }
46280
- return null;
46281
- };
46282
- const hasStorybookReactViteConfig = (packageDirectory) => {
46283
- for (const configFilename of [
46284
- "main.ts",
46285
- "main.js",
46286
- "main.mjs",
46287
- "main.cjs"
46288
- ]) {
46289
- const program = parseSourceFile(path.join(packageDirectory, ".storybook", configFilename));
46290
- if (!program) continue;
46291
- const scopes = analyzeScopes(program);
46292
- const exportedBindings = getExportedBindings(program, scopes);
46293
- let hasReactViteFramework = false;
46294
- walkAst(program, (node) => {
46295
- if (!isNodeOfType(node, "Property")) return;
46296
- if (!(isNodeOfType(node.key, "Identifier") && node.key.name === "framework" || isNodeOfType(node.key, "Literal") && node.key.value === "framework")) return;
46297
- if (!isExportedConfigProperty(node, exportedBindings, scopes)) return;
46298
- walkAst(node.value, (valueNode) => {
46299
- if (isNodeOfType(valueNode, "Literal") && valueNode.value === "@storybook/react-vite") {
46300
- hasReactViteFramework = true;
46301
- return false;
46302
- }
46303
- });
46304
- if (hasReactViteFramework) return false;
46305
- });
46306
- if (hasReactViteFramework) return true;
46307
- }
46308
- return false;
46309
- };
46310
- const resolveDirectUnreassignedValue = (value, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
46311
- const unwrappedValue = stripParenExpression(value);
46312
- if (!isNodeOfType(unwrappedValue, "Identifier")) return unwrappedValue;
46313
- const symbol = scopes.symbolFor(unwrappedValue);
46314
- if (!symbol || visitedSymbolIds.has(symbol.id)) return unwrappedValue;
46315
- const initializer = getDirectUnreassignedInitializer(symbol);
46316
- if (!initializer) return unwrappedValue;
46317
- visitedSymbolIds.add(symbol.id);
46318
- return resolveDirectUnreassignedValue(initializer, scopes, visitedSymbolIds);
46319
- };
46320
- const isStaticPropertyNamed = (node, propertyName) => isNodeOfType(node, "Property") && (!node.computed && isNodeOfType(node.key, "Identifier") && node.key.name === propertyName || isNodeOfType(node.key, "Literal") && node.key.value === propertyName);
46321
- const readLastStaticBooleanProperty = (value, propertyName, scopes) => {
46322
- const resolvedValue = resolveDirectUnreassignedValue(value, scopes);
46323
- if (!isNodeOfType(resolvedValue, "ObjectExpression")) return null;
46324
- let propertyValue = null;
46325
- for (const property of resolvedValue.properties) {
46326
- if (isNodeOfType(property, "SpreadElement")) {
46327
- propertyValue = null;
46328
- continue;
46329
- }
46330
- if (!isStaticPropertyNamed(property, propertyName) || !isNodeOfType(property, "Property")) continue;
46331
- propertyValue = readStaticBoolean(resolveDirectUnreassignedValue(property.value, scopes));
46332
- }
46333
- return propertyValue;
46334
- };
46335
- const isLastStaticPropertyInObject = (property, propertyName) => {
46336
- if (!isNodeOfType(property.parent, "ObjectExpression")) return false;
46337
- const propertyIndex = property.parent.properties.findIndex((objectProperty) => objectProperty === property);
46338
- return property.parent.properties.slice(propertyIndex + 1).every((laterProperty) => {
46339
- if (isNodeOfType(laterProperty, "SpreadElement")) return false;
46340
- return !isStaticPropertyNamed(laterProperty, propertyName);
46341
- });
46342
- };
46343
- const hasStorybookReactWebpackFastRefreshConfig = (packageDirectory) => {
46344
- for (const configFilename of [
46345
- "main.ts",
46346
- "main.js",
46347
- "main.mjs",
46348
- "main.cjs"
46349
- ]) {
46350
- const program = parseSourceFile(path.join(packageDirectory, ".storybook", configFilename));
46351
- if (!program) continue;
46352
- const scopes = analyzeScopes(program);
46353
- const exportedBindings = getExportedBindings(program, scopes);
46354
- let hasFastRefresh = false;
46355
- walkAst(program, (node) => {
46356
- if (!isStaticPropertyNamed(node, "reactOptions") || !isNodeOfType(node, "Property")) return;
46357
- if (!isExportedConfigProperty(node, exportedBindings, scopes)) return;
46358
- if (!isLastStaticPropertyInObject(node, "reactOptions")) return;
46359
- if (readLastStaticBooleanProperty(node.value, "fastRefresh", scopes) === true) {
46360
- hasFastRefresh = true;
46361
- return false;
46362
- }
46363
- });
46364
- if (hasFastRefresh) return true;
46365
- }
46366
- return false;
46367
- };
46368
- const hasStorybookReactViteIntegration = (packageDirectory, manifest) => hasOwnedDevelopmentCommand(manifest, /(?:^|\s)(?:storybook\s+dev|start-storybook)(?:\s|$)/) && hasStorybookReactViteConfig(packageDirectory);
46369
- const hasStorybookReactWebpackFastRefreshIntegration = (packageDirectory, manifest) => {
46370
- const developmentCommandPattern = /(?:^|\s)(?:storybook\s+dev|start-storybook)(?:\s|$)/;
46371
- if (!hasOwnedDevelopmentCommand(manifest, developmentCommandPattern)) return false;
46372
- return isVersionAtLeast(getOwnedDependencyVersion(manifest, "@storybook/react", developmentCommandPattern) ?? getOwnedDependencyVersion(manifest, "@storybook/react-webpack5", developmentCommandPattern), MINIMUM_FAST_REFRESH_VERSIONS.storybookReact) && hasStorybookReactWebpackFastRefreshConfig(packageDirectory);
46373
- };
46374
- const hasNxStorybookReactViteIntegration = (packageDirectory) => {
46375
- if (!hasStorybookReactViteConfig(packageDirectory)) return false;
46376
- try {
46377
- const project = JSON.parse(fs.readFileSync(path.join(packageDirectory, "project.json"), "utf8"));
46378
- if (typeof project !== "object" || project === null) return false;
46379
- const targets = Reflect.get(project, "targets");
46380
- if (typeof targets !== "object" || targets === null) return false;
46381
- return Object.keys(targets).some((targetName) => targetName === "storybook:serve:dev");
46382
- } catch {
46383
- return false;
46384
- }
46385
- };
46386
- const getLocalFastRefreshStatus = (packageDirectory, manifest) => {
46387
- const cachedStatus = cachedLocalStatusByManifest.get(manifest);
46388
- if (cachedStatus) return cachedStatus;
46389
- const status = getBuiltInStatus(manifest) ?? (hasStorybookReactViteIntegration(packageDirectory, manifest) || hasNxStorybookReactViteIntegration(packageDirectory) || hasStorybookReactWebpackFastRefreshIntegration(packageDirectory, manifest) ? {
46390
- isActive: true,
46391
- runtime: "generic"
46392
- } : null) ?? getRegisteredIntegration(packageDirectory, manifest) ?? INACTIVE_STATUS;
46393
- cachedLocalStatusByManifest.set(manifest, status);
46394
- return status;
46395
- };
46396
- const isWorkspaceRoot = (directory, manifest) => {
46397
- if (manifest?.workspaces !== void 0) return true;
46398
- return [
46399
- "pnpm-workspace.yaml",
46400
- "pnpm-workspace.yml",
46401
- "nx.json"
46402
- ].some((filename) => fs.existsSync(path.join(directory, filename)));
46403
- };
46404
- const findWorkspaceRoot = (packageDirectory) => {
46405
- let currentDirectory = packageDirectory;
46406
- let workspaceRoot = null;
46407
- while (true) {
46408
- const manifest = readPackageManifest(currentDirectory);
46409
- if (isWorkspaceRoot(currentDirectory, manifest)) workspaceRoot = currentDirectory;
46410
- const parentDirectory = path.dirname(currentDirectory);
46411
- if (parentDirectory === currentDirectory) return workspaceRoot;
46412
- currentDirectory = parentDirectory;
46413
- }
46414
- };
46415
- const collectWorkspacePackages = (workspaceRoot) => {
46416
- const packages = [];
46417
- const pendingDirectories = [workspaceRoot];
46418
- while (pendingDirectories.length > 0) {
46419
- const directory = pendingDirectories.pop();
46420
- if (!directory) continue;
46421
- const manifest = readPackageManifest(directory);
46422
- if (manifest) packages.push({
46423
- directory,
46424
- manifest,
46425
- status: getLocalFastRefreshStatus(directory, manifest)
46426
- });
46427
- let entries;
46428
- try {
46429
- entries = fs.readdirSync(directory, { withFileTypes: true });
46430
- } catch {
46431
- continue;
46432
- }
46433
- for (const entry of entries) {
46434
- if (!entry.isDirectory()) continue;
46435
- if (entry.name.startsWith(".") || WORKSPACE_IGNORED_DIRECTORY_NAMES.has(entry.name)) continue;
46436
- pendingDirectories.push(path.join(directory, entry.name));
46437
- }
46438
- }
46439
- return packages;
46440
- };
46441
- const isPropertyNamed = (node, name) => isNodeOfType(node, "Property") && (isNodeOfType(node.key, "Identifier") && node.key.name === name || isNodeOfType(node.key, "Literal") && node.key.value === name);
46442
- const normalizeAliasRoot = (configDirectory, aliasPath) => {
46443
- if (!aliasPath.startsWith(".") && !path.isAbsolute(aliasPath)) return null;
46444
- const withoutPlaceholder = aliasPath.replace(/(?:\/)?(?:\*|\$\d+).*$/, "");
46445
- return path.resolve(configDirectory, withoutPlaceholder);
46446
- };
46447
- const collectAliasRootsFromValue = (value, configDirectory, aliasRoots) => {
46448
- walkAst(value, (node) => {
46449
- if (!isPropertyNamed(node, "alias")) return;
46450
- if (!isNodeOfType(node, "Property")) return;
46451
- walkAst(node.value, (aliasNode) => {
46452
- if (!isNodeOfType(aliasNode, "Literal") || typeof aliasNode.value !== "string") return;
46453
- const aliasRoot = normalizeAliasRoot(configDirectory, aliasNode.value);
46454
- if (aliasRoot) aliasRoots.add(aliasRoot);
46455
- });
46456
- return false;
46457
- });
46458
- };
46459
- const getActivePackageAliasRoots = (packageDirectory, manifest) => {
46460
- const aliasRoots = /* @__PURE__ */ new Set();
46461
- const configPaths = [...getExplicitConfigPaths(packageDirectory, manifest), ...[
46462
- "main.ts",
46463
- "main.js",
46464
- "main.mjs",
46465
- "main.cjs"
46466
- ].map((filename) => path.join(packageDirectory, ".storybook", filename))];
46467
- for (const configPath of configPaths) {
46468
- const program = parseSourceFile(configPath);
46469
- if (!program) continue;
46470
- const scopes = analyzeScopes(program);
46471
- const exportedBindings = getExportedBindings(program, scopes);
46472
- walkAst(program, (node) => {
46473
- if (!isPropertyNamed(node, "resolve") && !isPropertyNamed(node, "viteFinal")) return;
46474
- if (!isNodeOfType(node, "Property")) return;
46475
- if (!isExportedConfigProperty(node, exportedBindings, scopes)) return;
46476
- collectAliasRootsFromValue(node.value, path.dirname(configPath), aliasRoots);
46477
- return false;
46478
- });
46479
- }
46480
- return aliasRoots;
46481
- };
46482
- const collectStringValues = (value, values) => {
46483
- if (typeof value === "string") {
46484
- values.push(value);
46485
- return;
46486
- }
46487
- if (Array.isArray(value)) {
46488
- for (const entry of value) collectStringValues(entry, values);
46489
- return;
46490
- }
46491
- if (typeof value !== "object" || value === null) return;
46492
- for (const entry of Object.values(value)) collectStringValues(entry, values);
46493
- };
46494
- const hasSourceRuntimeEntry = (manifest) => {
46495
- const entryValues = [];
46496
- collectStringValues(manifest.exports, entryValues);
46497
- collectStringValues(manifest.main, entryValues);
46498
- collectStringValues(manifest.module, entryValues);
46499
- return entryValues.some((entry) => /^\.\/?src(?:\/|\.|$)/.test(entry));
46500
- };
46501
- const getWorkspaceDependencyVersion = (manifest, dependencyName) => manifest.dependencies?.[dependencyName] ?? manifest.optionalDependencies?.[dependencyName];
46502
- const buildWorkspaceFastRefreshIndex = (workspaceRoot, rootManifest) => {
46503
- const cached = cachedWorkspaceIndexByManifest.get(rootManifest);
46504
- if (cached) return cached;
46505
- const workspacePackages = collectWorkspacePackages(workspaceRoot);
46506
- const activePackages = workspacePackages.filter((workspacePackage) => workspacePackage.status.isActive);
46507
- const aliasOwners = [];
46508
- for (const activePackage of activePackages) for (const rootDirectory of getActivePackageAliasRoots(activePackage.directory, activePackage.manifest)) aliasOwners.push({
46509
- rootDirectory,
46510
- status: activePackage.status
46511
- });
46512
- const sourceEntryOwners = /* @__PURE__ */ new Map();
46513
- for (const producerPackage of workspacePackages) {
46514
- const packageName = producerPackage.manifest.name;
46515
- if (typeof packageName !== "string" || !hasSourceRuntimeEntry(producerPackage.manifest)) continue;
46516
- const owner = activePackages.find((activePackage) => {
46517
- const dependencyVersion = getWorkspaceDependencyVersion(activePackage.manifest, packageName);
46518
- return typeof dependencyVersion === "string" && dependencyVersion.startsWith("workspace:");
46519
- });
46520
- if (owner) sourceEntryOwners.set(producerPackage.directory, owner.status);
46521
- }
46522
- const index = {
46523
- aliasOwners,
46524
- sourceEntryOwners
46525
- };
46526
- cachedWorkspaceIndexByManifest.set(rootManifest, index);
46527
- return index;
46528
- };
46529
- const isPathInside = (filePath, directory) => {
46530
- const relativePath = path.relative(directory, filePath);
46531
- return relativePath === "" || !relativePath.startsWith(`..${path.sep}`) && relativePath !== ".." && !path.isAbsolute(relativePath);
46532
- };
46533
- const getWorkspaceOwnedStatus = (filename, packageDirectory) => {
46534
- const workspaceRoot = findWorkspaceRoot(packageDirectory);
46535
- if (!workspaceRoot) return null;
46536
- const rootManifest = readPackageManifest(workspaceRoot);
46537
- if (!rootManifest) return null;
46538
- const index = buildWorkspaceFastRefreshIndex(workspaceRoot, rootManifest);
46539
- const aliasOwner = index.aliasOwners.find((owner) => isPathInside(filename, owner.rootDirectory));
46540
- if (aliasOwner) return aliasOwner.status;
46541
- for (const [producerDirectory, status] of index.sourceEntryOwners) if (isPathInside(filename, producerDirectory)) return status;
46542
- return null;
46543
- };
46544
- const resolveFastRefreshFileStatus = (filename) => {
46545
- const manifest = readNearestPackageManifest(filename);
46546
- if (!manifest) return INACTIVE_STATUS;
46547
- const packageDirectory = findNearestPackageDirectory(filename);
46548
- if (!packageDirectory) return INACTIVE_STATUS;
46549
- const localStatus = getLocalFastRefreshStatus(packageDirectory, manifest);
46550
- if (localStatus.isActive) return localStatus;
46551
- return getWorkspaceOwnedStatus(filename, packageDirectory) ?? INACTIVE_STATUS;
46552
- };
46553
- const getConfiguredFallbackStatus = (context) => {
46554
- const framework = getReactDoctorStringSetting(context.settings, "framework");
46555
- return {
46556
- isActive: true,
46557
- runtime: framework === "nextjs" ? "next" : framework === "expo" || framework === "react-native" ? "expo" : framework === "remix" ? "remix" : framework === "tanstack-start" ? "tanstack" : "generic"
46558
- };
46559
- };
46560
- const getFastRefreshFileStatus = (context) => {
46561
- let filename = context.filename;
46562
- if (!filename) return getConfiguredFallbackStatus(context);
46563
- if (!path.isAbsolute(filename)) {
46564
- const absoluteFilename = path.resolve(filename);
46565
- if (!fs.existsSync(absoluteFilename)) return getConfiguredFallbackStatus(context);
46566
- filename = absoluteFilename;
46567
- }
46568
- if (!readNearestPackageManifest(filename)) return INACTIVE_STATUS;
46569
- return resolveFastRefreshFileStatus(filename);
45759
+ return FRAMEWORK_ROUTE_FILE_PATTERNS.some((pattern) => pattern.test(basename));
46570
45760
  };
46571
45761
  //#endregion
46572
45762
  //#region src/plugin/rules/react-builtins/only-export-components-tables.ts
@@ -46578,7 +45768,6 @@ const NOT_REACT_COMPONENT_EXPRESSION_TYPES = new Set([
46578
45768
  "ConditionalExpression",
46579
45769
  "Literal",
46580
45770
  "LogicalExpression",
46581
- "NewExpression",
46582
45771
  "ObjectExpression",
46583
45772
  "TemplateLiteral",
46584
45773
  "ThisExpression",
@@ -46597,25 +45786,124 @@ const NON_FAST_REFRESH_PATH_SEGMENTS = [
46597
45786
  "/cypress/",
46598
45787
  "/.storybook/",
46599
45788
  "/stories/",
46600
- "/__stories__/"
45789
+ "/__stories__/",
45790
+ "/playground/",
45791
+ "/playgrounds/",
45792
+ "/examples/",
45793
+ "/example/",
45794
+ "/demo/",
45795
+ "/demos/",
45796
+ "/sandbox/",
45797
+ "/sandboxes/"
46601
45798
  ];
46602
- const TANSTACK_ROUTE_FACTORY_CALLEE_NAMES = new Set([
45799
+ const ENTRY_POINT_BASENAMES = new Set([
45800
+ "main.tsx",
45801
+ "main.jsx",
45802
+ "main.js",
45803
+ "index.tsx",
45804
+ "index.jsx",
45805
+ "entry.tsx",
45806
+ "entry.jsx",
45807
+ "bootstrap.tsx",
45808
+ "bootstrap.jsx",
45809
+ "client.tsx",
45810
+ "client.jsx",
45811
+ "server.tsx",
45812
+ "server.jsx",
45813
+ "app.tsx",
45814
+ "app.jsx",
45815
+ "App.tsx",
45816
+ "App.jsx"
45817
+ ]);
45818
+ const UTILITY_FILE_BASENAMES = new Set([
45819
+ "utils.tsx",
45820
+ "utils.jsx",
45821
+ "util.tsx",
45822
+ "util.jsx",
45823
+ "helpers.tsx",
45824
+ "helpers.jsx",
45825
+ "helper.tsx",
45826
+ "helper.jsx",
45827
+ "shared.tsx",
45828
+ "shared.jsx",
45829
+ "common.tsx",
45830
+ "common.jsx",
45831
+ "lib.tsx",
45832
+ "lib.jsx",
45833
+ "nodeTypes.tsx",
45834
+ "nodeTypes.jsx",
45835
+ "node-types.tsx",
45836
+ "node-types.jsx",
45837
+ "edgeTypes.tsx",
45838
+ "edgeTypes.jsx",
45839
+ "edge-types.tsx",
45840
+ "edge-types.jsx",
45841
+ "cellTypes.tsx",
45842
+ "cellTypes.jsx",
45843
+ "columnTypes.tsx",
45844
+ "columnTypes.jsx",
45845
+ "columnDefs.tsx",
45846
+ "columnDefs.jsx",
45847
+ "columnRenderers.tsx",
45848
+ "columnRenderers.jsx",
45849
+ "columns.tsx",
45850
+ "columns.jsx",
45851
+ "mappings.tsx",
45852
+ "mappings.jsx",
45853
+ "mapping.tsx",
45854
+ "mapping.jsx",
45855
+ "lookups.tsx",
45856
+ "lookups.jsx",
45857
+ "lookup.tsx",
45858
+ "lookup.jsx",
45859
+ "registry.tsx",
45860
+ "registry.jsx",
45861
+ "toast.tsx",
45862
+ "toast.jsx",
45863
+ "toaster.tsx",
45864
+ "toaster.jsx",
45865
+ "theme.tsx",
45866
+ "theme.jsx",
45867
+ "tokens.tsx",
45868
+ "tokens.jsx",
45869
+ "palette.tsx",
45870
+ "palette.jsx",
45871
+ "colors.tsx",
45872
+ "colors.jsx",
45873
+ "colours.tsx",
45874
+ "colours.jsx",
45875
+ "constants.tsx",
45876
+ "constants.jsx",
45877
+ "enums.tsx",
45878
+ "enums.jsx",
45879
+ "types.tsx",
45880
+ "types.jsx",
45881
+ "schemas.tsx",
45882
+ "schemas.jsx",
45883
+ "schema.tsx",
45884
+ "schema.jsx",
45885
+ "definitions.tsx",
45886
+ "definitions.jsx",
45887
+ "config.tsx",
45888
+ "config.jsx",
45889
+ "defaults.tsx",
45890
+ "defaults.jsx"
45891
+ ]);
45892
+ const ROUTE_FACTORY_CALLEE_NAMES = new Set([
46603
45893
  ...TANSTACK_ROUTE_CREATION_FUNCTIONS,
46604
45894
  "createLazyFileRoute",
46605
45895
  "createLazyRoute",
46606
45896
  "createAPIFileRoute",
46607
45897
  "createServerFileRoute",
46608
45898
  "createServerRootRoute",
46609
- "createServerRoute"
46610
- ]);
46611
- const REACT_ROUTER_FACTORY_CALLEE_NAMES = new Set([
45899
+ "createServerRoute",
46612
45900
  "createBrowserRouter",
46613
45901
  "createHashRouter",
46614
45902
  "createMemoryRouter",
46615
45903
  "createStaticRouter",
46616
45904
  "createRouter"
46617
45905
  ]);
46618
- const REACT_ROUTER_ALLOWED_EXPORT_NAMES = new Set([
45906
+ const ROUTE_MODULE_ALLOWED_EXPORT_NAMES = new Set([
46619
45907
  "loader",
46620
45908
  "clientLoader",
46621
45909
  "action",
@@ -46626,9 +45914,7 @@ const REACT_ROUTER_ALLOWED_EXPORT_NAMES = new Set([
46626
45914
  "handle",
46627
45915
  "shouldRevalidate",
46628
45916
  "middleware",
46629
- "unstable_middleware"
46630
- ]);
46631
- const NEXT_ALLOWED_EXPORT_NAMES = new Set([
45917
+ "unstable_middleware",
46632
45918
  "getServerSideProps",
46633
45919
  "getStaticProps",
46634
45920
  "getStaticPaths",
@@ -46648,9 +45934,9 @@ const NEXT_ALLOWED_EXPORT_NAMES = new Set([
46648
45934
  "runtime",
46649
45935
  "preferredRegion",
46650
45936
  "maxDuration",
46651
- "experimental_ppr"
45937
+ "experimental_ppr",
45938
+ "unstable_settings"
46652
45939
  ]);
46653
- const EXPO_ALLOWED_EXPORT_NAMES = new Set(["unstable_settings"]);
46654
45940
  //#endregion
46655
45941
  //#region src/plugin/rules/react-builtins/only-export-components.ts
46656
45942
  const NAMED_EXPORT_MESSAGE = "This file exports non-components, so Fast Refresh can't safely preserve component state.";
@@ -46663,8 +45949,6 @@ const DEFAULT_REACT_HOCS = [
46663
45949
  "forwardRef",
46664
45950
  "lazy"
46665
45951
  ];
46666
- const EMPTY_NAME_SET = /* @__PURE__ */ new Set();
46667
- const TEST_SUPPORT_FILE_PATTERN = /(?:^|\/)(?:test|spec)(?:[-_.]?(?:utils?|helpers?|setup|fixtures?))?\.(?:jsx?|tsx?)$/i;
46668
45952
  const resolveSettings$6 = (settings) => {
46669
45953
  const reactDoctor = settings?.["react-doctor"];
46670
45954
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.onlyExportComponents ?? {} : {};
@@ -46688,12 +45972,13 @@ const isReactCreateContext = (initializer) => {
46688
45972
  if (isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && callee.property.name === "createContext") return true;
46689
45973
  return false;
46690
45974
  };
46691
- const isRouteFactoryCall = (expression, bindings) => {
45975
+ const isRouteFactoryName = (name) => ROUTE_FACTORY_CALLEE_NAMES.has(name);
45976
+ const isRouteFactoryCall = (expression) => {
46692
45977
  let currentCall = expression;
46693
45978
  while (isNodeOfType(currentCall, "CallExpression")) {
46694
45979
  const callee = currentCall.callee;
46695
- if (isNodeOfType(callee, "Identifier") && bindings.localNames.has(callee.name)) return true;
46696
- if (isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.object, "Identifier") && bindings.namespaceNames.has(callee.object.name) && isNodeOfType(callee.property, "Identifier") && bindings.memberNames.has(callee.property.name)) return true;
45980
+ if (isNodeOfType(callee, "Identifier") && isRouteFactoryName(callee.name)) return true;
45981
+ if (isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && isRouteFactoryName(callee.property.name)) return true;
46697
45982
  if (!isNodeOfType(callee, "CallExpression")) return false;
46698
45983
  currentCall = callee;
46699
45984
  }
@@ -46721,11 +46006,10 @@ const isHocCallee = (callee, state) => {
46721
46006
  const canBeReactFunctionComponent = (initializer, state) => {
46722
46007
  if (!initializer) return false;
46723
46008
  const expression = skipTsExpression(initializer);
46724
- if (isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression")) return functionHasReactRenderSemantics(expression, state);
46009
+ if (isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression")) return true;
46725
46010
  if (isNodeOfType(expression, "CallExpression")) return isHocCallee(expression.callee, state);
46726
46011
  return false;
46727
46012
  };
46728
- const functionHasReactRenderSemantics = (functionNode, state) => functionContainsReactRenderOutput(functionNode, state.scopes, state.controlFlow) || functionHasReactElementReturnType(functionNode) || functionReturnsOnlyNull(functionNode);
46729
46013
  const isReactComponentInitializer = (expression, state) => {
46730
46014
  const stripped = skipTsExpression(expression);
46731
46015
  if (isNodeOfType(stripped, "ArrowFunctionExpression")) return true;
@@ -46734,60 +46018,6 @@ const isReactComponentInitializer = (expression, state) => {
46734
46018
  if (isNodeOfType(stripped, "CallExpression") && isHocCallee(stripped.callee, state) && stripped.arguments.length > 0) return true;
46735
46019
  return false;
46736
46020
  };
46737
- const isNextDynamicCall = (expression, nextDynamicImportSymbolIds, scopes) => {
46738
- const stripped = skipTsExpression(expression);
46739
- if (!isNodeOfType(stripped, "CallExpression")) return false;
46740
- const callee = skipTsExpression(stripped.callee);
46741
- if (!isNodeOfType(callee, "Identifier")) return false;
46742
- const symbol = scopes.symbolFor(callee);
46743
- return symbol !== null && nextDynamicImportSymbolIds.has(symbol.id);
46744
- };
46745
- const functionReturnsNextDynamicComponent = (expression, nextDynamicImportSymbolIds, scopes) => {
46746
- const stripped = skipTsExpression(expression);
46747
- if (!isNodeOfType(stripped, "ArrowFunctionExpression") && !isNodeOfType(stripped, "FunctionExpression") && !isNodeOfType(stripped, "FunctionDeclaration")) return false;
46748
- const body = stripped.body;
46749
- if (!isNodeOfType(body, "BlockStatement")) return isNextDynamicCall(body, nextDynamicImportSymbolIds, scopes);
46750
- if (body.body.length !== 1) return false;
46751
- const statement = body.body[0];
46752
- return Boolean(statement) && isNodeOfType(statement, "ReturnStatement") && Boolean(statement.argument) && isNextDynamicCall(statement.argument, nextDynamicImportSymbolIds, scopes);
46753
- };
46754
- const isComponentFactoryCall = (expression, state) => {
46755
- const stripped = skipTsExpression(expression);
46756
- if (!isNodeOfType(stripped, "CallExpression")) return false;
46757
- const callee = skipTsExpression(stripped.callee);
46758
- if (!isNodeOfType(callee, "Identifier")) return false;
46759
- const symbol = state.scopes.symbolFor(callee);
46760
- return symbol !== null && state.componentFactorySymbolIds.has(symbol.id);
46761
- };
46762
- const isProvenComponentValue = (expression, state, inspectedSymbolIds = /* @__PURE__ */ new Set()) => {
46763
- const stripped = skipTsExpression(expression);
46764
- if (isNodeOfType(stripped, "Identifier")) {
46765
- const symbol = state.scopes.symbolFor(stripped);
46766
- if (!symbol) return false;
46767
- if (state.localComponentNames.has(stripped.name)) return symbol.references.every((reference) => reference.flag === "read");
46768
- if (isReactComponentName(stripped.name) && symbol.kind === "import") return true;
46769
- if (inspectedSymbolIds.has(symbol.id)) return false;
46770
- const initializer = getDirectUnreassignedInitializer(symbol);
46771
- if (!initializer) return false;
46772
- const strippedInitializer = skipTsExpression(initializer);
46773
- if (isNodeOfType(strippedInitializer, "ArrowFunctionExpression") || isNodeOfType(strippedInitializer, "FunctionExpression")) return false;
46774
- return isProvenComponentValue(initializer, state, new Set([...inspectedSymbolIds, symbol.id]));
46775
- }
46776
- if (isNodeOfType(stripped, "MemberExpression") && !stripped.computed && isNodeOfType(stripped.object, "Identifier") && isNodeOfType(stripped.property, "Identifier") && isReactComponentName(stripped.property.name)) {
46777
- const objectSymbol = state.scopes.symbolFor(stripped.object);
46778
- return objectSymbol !== null && state.importSymbolIds.has(objectSymbol.id);
46779
- }
46780
- if (isNodeOfType(stripped, "ArrowFunctionExpression") || isNodeOfType(stripped, "FunctionExpression")) return functionHasReactRenderSemantics(stripped, state);
46781
- if (!isNodeOfType(stripped, "CallExpression")) return false;
46782
- if (!isHocCallee(stripped.callee, state)) return false;
46783
- return stripped.arguments.some((argument) => isProvenComponentValue(argument, state));
46784
- };
46785
- const isDirectRefreshWrapperCall = (call, state) => {
46786
- const callee = skipTsExpression(call.callee);
46787
- if (isHocCallee(callee, state)) return call.arguments.length > 0;
46788
- if (!isNodeOfType(callee, "Identifier") && !isNodeOfType(callee, "MemberExpression")) return false;
46789
- return call.arguments.some((argument) => isProvenComponentValue(argument, state));
46790
- };
46791
46021
  const objectExpressionBundlesComponents = (objectExpression, state) => {
46792
46022
  for (const property of objectExpression.properties ?? []) {
46793
46023
  if (!isNodeOfType(property, "Property")) continue;
@@ -46797,22 +46027,20 @@ const objectExpressionBundlesComponents = (objectExpression, state) => {
46797
46027
  continue;
46798
46028
  }
46799
46029
  if (!(!property.computed && isNodeOfType(property.key, "Identifier") && isReactComponentName(property.key.name))) continue;
46800
- if ((isNodeOfType(value, "ArrowFunctionExpression") || isNodeOfType(value, "FunctionExpression")) && functionHasReactRenderSemantics(value, state)) return true;
46030
+ if ((isNodeOfType(value, "ArrowFunctionExpression") || isNodeOfType(value, "FunctionExpression")) && functionContainsReactRenderOutput(value, state.scopes, state.controlFlow)) return true;
46801
46031
  if (isNodeOfType(value, "CallExpression") && isHocCallee(value.callee, state) && value.arguments.length > 0) return true;
46802
46032
  }
46803
46033
  return false;
46804
46034
  };
46805
46035
  const classifyExport = (name, reportNode, isFunction, initializer, state) => {
46806
- if (isNodeOfType(reportNode, "Identifier") && isProvenComponentValue(reportNode, state)) return { kind: "react-component" };
46807
46036
  if (initializer) {
46808
46037
  const expression = skipTsExpression(initializer);
46809
- if (isNodeOfType(expression, "CallExpression") && isReactComponentName(name) && isComponentFactoryCall(expression, state)) return { kind: "react-component" };
46810
- if (isRouteFactoryCall(expression, state.routeFactoryBindings)) return { kind: "react-component" };
46038
+ if (isRouteFactoryCall(expression)) return { kind: "react-component" };
46811
46039
  if (isNodeOfType(expression, "CallExpression") && isHocCallee(expression.callee, state) && expression.arguments.length > 0 && isReactComponentName(name)) return { kind: "react-component" };
46812
46040
  if (isNodeOfType(expression, "ConditionalExpression") && isReactComponentName(name) && isReactComponentInitializer(expression.consequent, state) && isReactComponentInitializer(expression.alternate, state)) return { kind: "react-component" };
46813
46041
  }
46814
46042
  if (state.allowExportNames.has(name)) return { kind: "allowed" };
46815
- if (state.allowedRouteExportNames.has(name)) return { kind: "allowed" };
46043
+ if (ROUTE_MODULE_ALLOWED_EXPORT_NAMES.has(name)) return { kind: "allowed" };
46816
46044
  if (/^use[A-Z]/.test(name)) return { kind: "allowed" };
46817
46045
  if (state.allowConstantExport && initializer) {
46818
46046
  const expression = skipTsExpression(initializer);
@@ -46838,33 +46066,42 @@ const classifyExport = (name, reportNode, isFunction, initializer, state) => {
46838
46066
  kind: "namespace-object",
46839
46067
  reportNode
46840
46068
  };
46841
- if (isNodeOfType(stripped, "MemberExpression")) return isProvenComponentValue(stripped, state) ? { kind: "react-component" } : {
46842
- kind: "non-component",
46843
- reportNode
46844
- };
46845
- if (isNodeOfType(stripped, "Identifier")) return isProvenComponentValue(stripped, state) ? { kind: "react-component" } : {
46846
- kind: "non-component",
46847
- reportNode
46848
- };
46849
46069
  if (NOT_REACT_COMPONENT_EXPRESSION_TYPES.has(stripped.type)) return {
46850
46070
  kind: "non-component",
46851
46071
  reportNode
46852
46072
  };
46853
- if (isNodeOfType(stripped, "ArrowFunctionExpression") || isNodeOfType(stripped, "FunctionExpression")) return {
46854
- kind: "non-component",
46855
- reportNode
46856
- };
46857
46073
  }
46858
46074
  return isReactComponentName(name) ? { kind: "react-component" } : {
46859
46075
  kind: "non-component",
46860
46076
  reportNode
46861
46077
  };
46862
46078
  };
46079
+ const isEntryPointFile = (filename) => {
46080
+ const lastSlash = Math.max(filename.lastIndexOf("/"), filename.lastIndexOf("\\"));
46081
+ const basename = lastSlash === -1 ? filename : filename.slice(lastSlash + 1);
46082
+ return ENTRY_POINT_BASENAMES.has(basename);
46083
+ };
46084
+ const ASSET_FILE_BASENAME_PATTERN = /^([A-Za-z][\w-]*[-._])?(icons?|svgs?|svg[-_]?sprites?|sprites?|emojis?|flags?|logos?|lockups?|illustrations?|glyphs?|stickers?|emotes?|avatars?|backgrounds?|patterns?|assets?|gradients?|countryVectors?|paymentVectors?|brandVectors?|brandLogos?)\.(t|j)sx?$/;
46085
+ const UTILITY_BASENAME_SUFFIX_PATTERN = /^[A-Za-z][\w-]*(Utils|Util|Helpers|Helper|Shared|Constants|Constant|Types|Type|Mappings|Mapping|Lookups|Lookup|Registry|Renderers|Renderer|NodeTypes|EdgeTypes|CellTypes|ColumnDefs|ColumnTypes|ColumnRenderers|Schemas|Schema|Definitions|Definition|Config|Configuration|Defaults|Default|Tokens|Palette|Context|Provider|Providers|Logic|Scene|Page|Layout)\.(t|j)sx?$/;
46086
+ const HOOK_FILE_BASENAME_PATTERN = /^use[A-Z][\w-]*\.(t|j)sx?$/;
46087
+ const NODE_DEFINITION_BASENAME_PATTERN = /^[A-Z][\w-]*(NodeUtil|ShapeUtil|EdgeUtil|BindingUtil|InlineNode|BlockNode|NotebookNode)\.(t|j)sx?$/;
46088
+ const isAssetOrUtilityFile = (filename) => {
46089
+ const lastSlash = Math.max(filename.lastIndexOf("/"), filename.lastIndexOf("\\"));
46090
+ const basename = lastSlash === -1 ? filename : filename.slice(lastSlash + 1);
46091
+ if (ASSET_FILE_BASENAME_PATTERN.test(basename)) return true;
46092
+ if (UTILITY_FILE_BASENAMES.has(basename)) return true;
46093
+ if (UTILITY_BASENAME_SUFFIX_PATTERN.test(basename)) return true;
46094
+ if (HOOK_FILE_BASENAME_PATTERN.test(basename)) return true;
46095
+ if (NODE_DEFINITION_BASENAME_PATTERN.test(basename)) return true;
46096
+ return false;
46097
+ };
46863
46098
  const isFileNameAllowed = (filename, checkJS) => {
46864
46099
  if (!filename) return true;
46865
- if (TEST_SUPPORT_FILE_PATTERN.test(filename)) return false;
46866
46100
  if (filename.includes(".test.") || filename.includes(".spec.") || filename.includes(".cy.") || filename.includes(".stories.")) return false;
46867
46101
  for (const segment of NON_FAST_REFRESH_PATH_SEGMENTS) if (filename.includes(segment)) return false;
46102
+ if (isEntryPointFile(filename)) return false;
46103
+ if (isFrameworkRouteOrSpecialFilename(filename)) return false;
46104
+ if (isAssetOrUtilityFile(filename)) return false;
46868
46105
  if (filename.endsWith(".tsx") || filename.endsWith(".jsx")) return true;
46869
46106
  if (checkJS && filename.endsWith(".js")) return true;
46870
46107
  return false;
@@ -46877,100 +46114,16 @@ const onlyExportComponents = defineRule({
46877
46114
  category: "Architecture",
46878
46115
  create: (context) => {
46879
46116
  const settings = resolveSettings$6(context.settings);
46880
- const filename = normalizeFilename(context.filename ?? "");
46881
- const fastRefreshStatus = getFastRefreshFileStatus(context);
46882
- if (!fastRefreshStatus.isActive) return {};
46883
- if (isFrameworkRouteOrSpecialFilename(context, fastRefreshStatus.runtime)) return {};
46884
- if (!isFileNameAllowed(filename, settings.checkJS)) return {};
46885
- const allowedRouteExportNames = fastRefreshStatus.runtime === "next" ? NEXT_ALLOWED_EXPORT_NAMES : fastRefreshStatus.runtime === "expo" ? EXPO_ALLOWED_EXPORT_NAMES : fastRefreshStatus.runtime === "react-router" || fastRefreshStatus.runtime === "remix" ? REACT_ROUTER_ALLOWED_EXPORT_NAMES : EMPTY_NAME_SET;
46886
- const routeFactoryMemberNames = fastRefreshStatus.runtime === "tanstack" ? TANSTACK_ROUTE_FACTORY_CALLEE_NAMES : fastRefreshStatus.runtime === "react-router" || fastRefreshStatus.runtime === "remix" ? REACT_ROUTER_FACTORY_CALLEE_NAMES : EMPTY_NAME_SET;
46887
- const routeFactoryLocalNames = /* @__PURE__ */ new Set();
46888
- const routeFactoryNamespaceNames = /* @__PURE__ */ new Set();
46889
- const nextDynamicImportSymbolIds = /* @__PURE__ */ new Set();
46890
- const importSymbolIds = /* @__PURE__ */ new Set();
46891
- const routeFactoryBindings = {
46892
- localNames: routeFactoryLocalNames,
46893
- memberNames: routeFactoryMemberNames,
46894
- namespaceNames: routeFactoryNamespaceNames
46895
- };
46117
+ if (!isFileNameAllowed(normalizeFilename(context.filename ?? ""), settings.checkJS)) return {};
46896
46118
  const exportNodes = [];
46897
46119
  const componentCandidates = [];
46898
- const createRootNames = /* @__PURE__ */ new Set();
46899
- const hydrateRootNames = /* @__PURE__ */ new Set();
46900
- const legacyRenderNames = /* @__PURE__ */ new Set();
46901
- const reactDomNamespaceNames = /* @__PURE__ */ new Set();
46902
- const rootNames = /* @__PURE__ */ new Set();
46903
- let hasRootMount = false;
46904
- const isCreateRootCall = (node) => {
46905
- if (!isNodeOfType(node, "CallExpression")) return false;
46906
- if (isNodeOfType(node.callee, "Identifier")) return createRootNames.has(node.callee.name);
46907
- return isNodeOfType(node.callee, "MemberExpression") && isNodeOfType(node.callee.object, "Identifier") && reactDomNamespaceNames.has(node.callee.object.name) && isNodeOfType(node.callee.property, "Identifier") && node.callee.property.name === "createRoot";
46908
- };
46909
- const visitImportDeclaration = (node) => {
46910
- if (!isNodeOfType(node, "ImportDeclaration")) return;
46911
- const source = node.source.value;
46912
- for (const specifier of node.specifiers) {
46913
- const symbol = context.scopes.symbolFor(specifier.local);
46914
- if (symbol) importSymbolIds.add(symbol.id);
46915
- }
46916
- if (typeof source === "string" && (fastRefreshStatus.runtime === "tanstack" && (source.startsWith("@tanstack/react-router") || source.startsWith("@tanstack/react-start")) || (fastRefreshStatus.runtime === "react-router" || fastRefreshStatus.runtime === "remix") && (source === "react-router" || source === "react-router-dom" || source === "@remix-run/react"))) for (const specifier of node.specifiers) {
46917
- if (isNodeOfType(specifier, "ImportNamespaceSpecifier")) {
46918
- routeFactoryNamespaceNames.add(specifier.local.name);
46919
- continue;
46920
- }
46921
- const importedName = getImportedName(specifier);
46922
- if (importedName && routeFactoryMemberNames.has(importedName)) routeFactoryLocalNames.add(specifier.local.name);
46923
- }
46924
- if (source === "next/dynamic") for (const specifier of node.specifiers) {
46925
- if (!isNodeOfType(specifier, "ImportDefaultSpecifier")) continue;
46926
- const symbol = context.scopes.symbolFor(specifier.local);
46927
- if (symbol) nextDynamicImportSymbolIds.add(symbol.id);
46928
- }
46929
- if (source !== "react-dom" && source !== "react-dom/client") return;
46930
- for (const specifier of node.specifiers) {
46931
- if (isNodeOfType(specifier, "ImportDefaultSpecifier") || isNodeOfType(specifier, "ImportNamespaceSpecifier")) {
46932
- reactDomNamespaceNames.add(specifier.local.name);
46933
- continue;
46934
- }
46935
- const importedName = getImportedName(specifier);
46936
- if (importedName === "createRoot") createRootNames.add(specifier.local.name);
46937
- if (importedName === "hydrateRoot") hydrateRootNames.add(specifier.local.name);
46938
- if (source === "react-dom" && (importedName === "render" || importedName === "hydrate")) legacyRenderNames.add(specifier.local.name);
46939
- }
46940
- };
46941
- const visitCallExpression = (node) => {
46942
- if (!isNodeOfType(node, "CallExpression")) return;
46943
- if (isInsideFunctionScope(node)) return;
46944
- if (isNodeOfType(node.callee, "Identifier")) {
46945
- if (hydrateRootNames.has(node.callee.name) || legacyRenderNames.has(node.callee.name)) hasRootMount = true;
46946
- return;
46947
- }
46948
- if (!isNodeOfType(node.callee, "MemberExpression") || !isNodeOfType(node.callee.property, "Identifier")) return;
46949
- const methodName = node.callee.property.name;
46950
- if (isNodeOfType(node.callee.object, "Identifier") && reactDomNamespaceNames.has(node.callee.object.name) && (methodName === "render" || methodName === "hydrate" || methodName === "hydrateRoot")) {
46951
- hasRootMount = true;
46952
- return;
46953
- }
46954
- if (methodName !== "render") return;
46955
- if (isCreateRootCall(node.callee.object)) {
46956
- hasRootMount = true;
46957
- return;
46958
- }
46959
- if (isNodeOfType(node.callee.object, "Identifier") && rootNames.has(node.callee.object.name)) hasRootMount = true;
46960
- };
46961
46120
  const pushExportNode = (node) => {
46962
46121
  exportNodes.push(node);
46963
46122
  };
46964
46123
  const pushComponentCandidate = (node) => {
46965
46124
  componentCandidates.push(node);
46966
- if (isNodeOfType(node, "VariableDeclarator") && isNodeOfType(node.id, "Identifier") && node.init && isCreateRootCall(node.init) && !isInsideFunctionScope(node)) rootNames.add(node.id.name);
46967
46125
  };
46968
46126
  return {
46969
- ImportDeclaration: visitImportDeclaration,
46970
- CallExpression: visitCallExpression,
46971
- AssignmentExpression(node) {
46972
- if (isNodeOfType(node.left, "Identifier") && rootNames.has(node.left.name)) rootNames.delete(node.left.name);
46973
- },
46974
46127
  ExportAllDeclaration: pushExportNode,
46975
46128
  ExportDefaultDeclaration: pushExportNode,
46976
46129
  ExportNamedDeclaration: pushExportNode,
@@ -46978,53 +46131,31 @@ const onlyExportComponents = defineRule({
46978
46131
  VariableDeclarator: pushComponentCandidate,
46979
46132
  ClassDeclaration: pushComponentCandidate,
46980
46133
  "Program:exit"() {
46981
- if (hasRootMount) return;
46982
46134
  const localComponentNames = /* @__PURE__ */ new Set();
46983
- const componentFactorySymbolIds = /* @__PURE__ */ new Set();
46984
- for (const child of componentCandidates) {
46985
- if (isInsideFunctionScope(child)) continue;
46986
- if (isNodeOfType(child, "FunctionDeclaration") && child.id) {
46987
- if (functionReturnsNextDynamicComponent(child, nextDynamicImportSymbolIds, context.scopes)) {
46988
- const symbol = context.scopes.symbolFor(child.id);
46989
- if (symbol?.references.every((reference) => reference.flag === "read")) componentFactorySymbolIds.add(symbol.id);
46990
- }
46991
- continue;
46992
- }
46993
- if (isNodeOfType(child, "VariableDeclarator") && isNodeOfType(child.id, "Identifier") && child.init && functionReturnsNextDynamicComponent(child.init, nextDynamicImportSymbolIds, context.scopes)) {
46994
- const symbol = context.scopes.symbolFor(child.id);
46995
- if (symbol?.references.every((reference) => reference.flag === "read")) componentFactorySymbolIds.add(symbol.id);
46996
- }
46997
- }
46998
46135
  const state = {
46999
46136
  customHocs: new Set([...DEFAULT_REACT_HOCS, ...settings.customHOCs]),
47000
46137
  allowExportNames: new Set(settings.allowExportNames),
47001
46138
  allowConstantExport: settings.allowConstantExport,
47002
- allowedRouteExportNames,
47003
- routeFactoryBindings,
47004
- componentFactorySymbolIds,
47005
- importSymbolIds,
47006
46139
  localComponentNames,
47007
46140
  scopes: context.scopes,
47008
46141
  controlFlow: context.cfg
47009
46142
  };
47010
46143
  for (const child of componentCandidates) {
47011
46144
  if (isNodeOfType(child, "FunctionDeclaration") && child.id) {
47012
- if (isReactComponentName(child.id.name) && !isInsideFunctionScope(child) && functionHasReactRenderSemantics(child, state)) localComponentNames.add(child.id.name);
46145
+ if (isReactComponentName(child.id.name) && !isInsideFunctionScope(child) && functionContainsReactRenderOutput(child, context.scopes, context.cfg)) localComponentNames.add(child.id.name);
47013
46146
  }
47014
46147
  if (isNodeOfType(child, "ClassDeclaration") && child.id) {
47015
46148
  if (isReactComponentName(child.id.name) && isEs6Component(child) && !isInsideFunctionScope(child)) localComponentNames.add(child.id.name);
47016
46149
  }
47017
46150
  if (isNodeOfType(child, "VariableDeclarator") && isNodeOfType(child.id, "Identifier")) {
47018
46151
  const initializer = child.init;
47019
- const expression = initializer ? skipTsExpression(initializer) : null;
47020
- const isDirectFunction = expression !== null && (isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression"));
47021
- if (isReactComponentName(child.id.name) && (canBeReactFunctionComponent(initializer, state) || (expression ? isEs6Component(expression) : false)) && !isInsideFunctionScope(child)) {
47022
- if (!isDirectFunction || functionHasReactRenderSemantics(expression, state)) localComponentNames.add(child.id.name);
46152
+ if (isReactComponentName(child.id.name) && (canBeReactFunctionComponent(initializer, state) || (initializer ? isEs6Component(skipTsExpression(initializer)) : false)) && !isInsideFunctionScope(child)) {
46153
+ const expression = initializer ? skipTsExpression(initializer) : null;
46154
+ if (!(expression !== null && (isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression"))) || functionContainsReactRenderOutput(expression, context.scopes, context.cfg)) localComponentNames.add(child.id.name);
47023
46155
  }
47024
46156
  }
47025
46157
  }
47026
46158
  const exports = [];
47027
- const exportAllNodes = [];
47028
46159
  let hasReactExport = false;
47029
46160
  let hasAnyExports = false;
47030
46161
  const isExportedNodeIds = /* @__PURE__ */ new WeakSet();
@@ -47032,8 +46163,10 @@ const onlyExportComponents = defineRule({
47032
46163
  if (isNodeOfType(child, "ExportAllDeclaration")) {
47033
46164
  if (child.exportKind === "type") continue;
47034
46165
  hasAnyExports = true;
47035
- const source = child.source.value;
47036
- if (typeof source !== "string" || exportAllAddsRuntimeValues(filename, source)) exportAllNodes.push(child);
46166
+ context.report({
46167
+ node: child,
46168
+ message: EXPORT_ALL_MESSAGE
46169
+ });
47037
46170
  continue;
47038
46171
  }
47039
46172
  if (isNodeOfType(child, "ExportDefaultDeclaration")) {
@@ -47041,24 +46174,17 @@ const onlyExportComponents = defineRule({
47041
46174
  const declaration = child.declaration;
47042
46175
  const stripped = skipTsExpression(declaration);
47043
46176
  if (isNodeOfType(stripped, "FunctionDeclaration") || isNodeOfType(stripped, "FunctionExpression")) {
47044
- const hasRenderOutput = functionHasReactRenderSemantics(stripped, state);
47045
46177
  if (stripped.id) {
47046
46178
  const idNode = stripped.id;
47047
46179
  isExportedNodeIds.add(stripped);
47048
- exports.push(hasRenderOutput ? classifyExport(idNode.name, idNode, true, null, state) : {
47049
- kind: "non-component",
47050
- reportNode: idNode
47051
- });
47052
- } else if (hasRenderOutput) {
46180
+ exports.push(classifyExport(idNode.name, idNode, true, null, state));
46181
+ } else {
47053
46182
  context.report({
47054
46183
  node: stripped,
47055
46184
  message: ANONYMOUS_MESSAGE
47056
46185
  });
47057
46186
  hasReactExport = true;
47058
- } else exports.push({
47059
- kind: "non-component",
47060
- reportNode: stripped
47061
- });
46187
+ }
47062
46188
  continue;
47063
46189
  }
47064
46190
  if (isNodeOfType(stripped, "ClassDeclaration") || isNodeOfType(stripped, "ClassExpression")) {
@@ -47077,34 +46203,26 @@ const onlyExportComponents = defineRule({
47077
46203
  continue;
47078
46204
  }
47079
46205
  if (isNodeOfType(stripped, "Identifier")) {
47080
- exports.push(isProvenComponentValue(stripped, state) ? { kind: "react-component" } : {
47081
- kind: "non-component",
47082
- reportNode: stripped
47083
- });
47084
- continue;
47085
- }
47086
- if (isNodeOfType(stripped, "MemberExpression")) {
47087
- if (isProvenComponentValue(stripped, state)) hasReactExport = true;
47088
- else exports.push({
47089
- kind: "non-component",
47090
- reportNode: stripped
47091
- });
46206
+ exports.push(classifyExport(stripped.name, stripped, false, null, state));
47092
46207
  continue;
47093
46208
  }
47094
46209
  if (isNodeOfType(stripped, "CallExpression")) {
47095
- if (isRouteFactoryCall(stripped, state.routeFactoryBindings)) {
46210
+ if (isRouteFactoryCall(stripped)) {
47096
46211
  hasReactExport = true;
47097
46212
  continue;
47098
46213
  }
47099
- if (isReactCreateContext(stripped)) {
47100
- exports.push({
47101
- kind: "react-context",
47102
- reportNode: stripped
47103
- });
47104
- continue;
47105
- }
47106
- if (isDirectRefreshWrapperCall(stripped, state)) hasReactExport = true;
47107
- else if (isConfigOnlyFactoryCall(stripped)) exports.push({
46214
+ const isHoc = isHocCallee(stripped.callee, state);
46215
+ const firstArg = stripped.arguments[0];
46216
+ const firstArgIsValid = Boolean(firstArg) && (() => {
46217
+ if (!firstArg) return false;
46218
+ const expression = skipTsExpression(firstArg);
46219
+ if (isNodeOfType(expression, "Identifier")) return true;
46220
+ if (isNodeOfType(expression, "FunctionExpression") && expression.id) return true;
46221
+ if (isNodeOfType(expression, "CallExpression") && isHocCallee(expression.callee, state)) return true;
46222
+ return false;
46223
+ })();
46224
+ if (isHoc && firstArgIsValid) hasReactExport = true;
46225
+ else if (!isHoc && isConfigOnlyFactoryCall(stripped)) exports.push({
47108
46226
  kind: "non-component",
47109
46227
  reportNode: stripped
47110
46228
  });
@@ -47115,32 +46233,16 @@ const onlyExportComponents = defineRule({
47115
46233
  continue;
47116
46234
  }
47117
46235
  if (isNodeOfType(stripped, "ObjectExpression")) {
47118
- exports.push(objectExpressionBundlesComponents(stripped, state) ? {
47119
- kind: "namespace-object",
47120
- reportNode: stripped
47121
- } : {
47122
- kind: "non-component",
47123
- reportNode: stripped
47124
- });
47125
- continue;
47126
- }
47127
- if (isNodeOfType(stripped, "ArrowFunctionExpression")) {
47128
- if (functionHasReactRenderSemantics(stripped, state)) {
47129
- context.report({
47130
- node: stripped,
47131
- message: ANONYMOUS_MESSAGE
47132
- });
47133
- hasReactExport = true;
47134
- } else exports.push({
47135
- kind: "non-component",
47136
- reportNode: stripped
46236
+ context.report({
46237
+ node: stripped,
46238
+ message: objectExpressionBundlesComponents(stripped, state) ? NAMESPACE_OBJECT_MESSAGE : ANONYMOUS_MESSAGE
47137
46239
  });
47138
46240
  continue;
47139
46241
  }
47140
- if (isNodeOfType(stripped, "Literal") || isNodeOfType(stripped, "NewExpression")) {
47141
- exports.push({
47142
- kind: "non-component",
47143
- reportNode: stripped
46242
+ if (isNodeOfType(stripped, "ArrowFunctionExpression") || isNodeOfType(stripped, "Literal")) {
46243
+ context.report({
46244
+ node: stripped,
46245
+ message: ANONYMOUS_MESSAGE
47144
46246
  });
47145
46247
  continue;
47146
46248
  }
@@ -47157,10 +46259,7 @@ const onlyExportComponents = defineRule({
47157
46259
  const declaration = child.declaration;
47158
46260
  if (isNodeOfType(declaration, "FunctionDeclaration") && declaration.id) {
47159
46261
  isExportedNodeIds.add(declaration);
47160
- exports.push(functionHasReactRenderSemantics(declaration, state) || localComponentNames.has(declaration.id.name) ? classifyExport(declaration.id.name, declaration.id, true, null, state) : {
47161
- kind: "non-component",
47162
- reportNode: declaration.id
47163
- });
46262
+ exports.push(classifyExport(declaration.id.name, declaration.id, true, null, state));
47164
46263
  } else if (isNodeOfType(declaration, "ClassDeclaration") && declaration.id) {
47165
46264
  isExportedNodeIds.add(declaration);
47166
46265
  if (isReactComponentName(declaration.id.name) && isEs6Component(declaration)) exports.push({ kind: "react-component" });
@@ -47184,7 +46283,6 @@ const onlyExportComponents = defineRule({
47184
46283
  const isReExportFromSource = Boolean(child.source);
47185
46284
  for (const specifier of child.specifiers ?? []) {
47186
46285
  if (!isNodeOfType(specifier, "ExportSpecifier")) continue;
47187
- if (specifier.exportKind === "type") continue;
47188
46286
  const exported = specifier.exported;
47189
46287
  const local = specifier.local;
47190
46288
  let exportedName = null;
@@ -47192,13 +46290,8 @@ const onlyExportComponents = defineRule({
47192
46290
  const localName = local && isNodeOfType(local, "Identifier") ? local.name : null;
47193
46291
  const reportNode = specifier;
47194
46292
  let entry;
47195
- if (localName && localComponentNames.has(localName)) entry = { kind: "react-component" };
47196
- else if (!isReExportFromSource && localName === exportedName && localName !== null && isReactComponentName(localName)) entry = { kind: "react-component" };
47197
- else if (exportedName === "default" && localName && local) entry = isProvenComponentValue(local, state) ? { kind: "react-component" } : {
47198
- kind: "non-component",
47199
- reportNode
47200
- };
47201
- else if (exportedName) entry = classifyExport(exportedName, reportNode, false, isReExportFromSource ? null : local, state);
46293
+ if (exportedName === "default" && localName) entry = classifyExport(localName, reportNode, false, null, state);
46294
+ else if (exportedName) entry = classifyExport(exportedName, reportNode, false, null, state);
47202
46295
  else {
47203
46296
  entry = {
47204
46297
  kind: "non-component",
@@ -47216,21 +46309,15 @@ const onlyExportComponents = defineRule({
47216
46309
  node: entry.reportNode,
47217
46310
  message: NAMESPACE_OBJECT_MESSAGE
47218
46311
  });
47219
- if (hasAnyExports && hasReactExport) {
47220
- for (const exportAllNode of exportAllNodes) context.report({
47221
- node: exportAllNode,
47222
- message: EXPORT_ALL_MESSAGE
46312
+ if (hasAnyExports && hasReactExport) for (const entry of exports) {
46313
+ if (entry.kind === "non-component") context.report({
46314
+ node: entry.reportNode,
46315
+ message: NAMED_EXPORT_MESSAGE
46316
+ });
46317
+ if (entry.kind === "react-context") context.report({
46318
+ node: entry.reportNode,
46319
+ message: REACT_CONTEXT_MESSAGE
47223
46320
  });
47224
- for (const entry of exports) {
47225
- if (entry.kind === "non-component") context.report({
47226
- node: entry.reportNode,
47227
- message: NAMED_EXPORT_MESSAGE
47228
- });
47229
- if (entry.kind === "react-context") context.report({
47230
- node: entry.reportNode,
47231
- message: REACT_CONTEXT_MESSAGE
47232
- });
47233
- }
47234
46321
  }
47235
46322
  }
47236
46323
  };
@@ -67327,7 +66414,6 @@ const CROSS_FILE_RULE_IDS = new Set([
67327
66414
  "no-effect-with-fresh-deps",
67328
66415
  "no-initialize-state",
67329
66416
  "no-mutating-reducer-state",
67330
- "only-export-components",
67331
66417
  "no-unguarded-browser-global-in-render-or-hook-init",
67332
66418
  "prefer-dynamic-import",
67333
66419
  "rendering-hydration-mismatch-time",
@@ -67535,7 +66621,7 @@ const CROSS_FILE_DEPENDENCY_COLLECTORS = new Map([
67535
66621
  * `CROSS_FILE_DEPENDENCY_COLLECTORS` (the core guard test enforces the
67536
66622
  * partition), forcing a conscious classification.
67537
66623
  */
67538
- const UNBOUNDED_CROSS_FILE_RULE_IDS = new Set(["only-export-components"]);
66624
+ const UNBOUNDED_CROSS_FILE_RULE_IDS = /* @__PURE__ */ new Set();
67539
66625
  /**
67540
66626
  * Runs the collectors for `ruleIds` over one file and returns every
67541
66627
  * filesystem probe they made — the file's cross-file dependency set.