oxlint-plugin-react-doctor 0.7.4-dev.98005f2 → 0.7.4-dev.a31f5e8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -21008,7 +21008,6 @@ const findExportedFunctionBody = (programRoot, exportedName) => {
21008
21008
  continue;
21009
21009
  }
21010
21010
  if (isNodeOfType(statement, "ExportNamedDeclaration")) {
21011
- if (statement.exportKind === "type") continue;
21012
21011
  const declaration = statement.declaration;
21013
21012
  if (declaration && isNodeOfType(declaration, "VariableDeclaration")) {
21014
21013
  recordVariableDeclaration(declaration);
@@ -21023,7 +21022,6 @@ const findExportedFunctionBody = (programRoot, exportedName) => {
21023
21022
  }
21024
21023
  for (const specifier of statement.specifiers ?? []) {
21025
21024
  if (!isNodeOfType(specifier, "ExportSpecifier")) continue;
21026
- if (specifier.exportKind === "type") continue;
21027
21025
  const local = specifier.local;
21028
21026
  const exported = specifier.exported;
21029
21027
  if (!isNodeOfType(local, "Identifier")) continue;
@@ -21072,37 +21070,25 @@ const resolveImportedExportName = (importSpecifier) => {
21072
21070
  if (isNodeOfType(importSpecifier, "ImportDefaultSpecifier")) return "default";
21073
21071
  return null;
21074
21072
  };
21075
- const findReExportTargetsForName = (programRoot, exportedName) => {
21073
+ const findReExportSourcesForName = (programRoot, exportedName) => {
21076
21074
  if (!isNodeOfType(programRoot, "Program")) return [];
21077
- const exportAllTargets = [];
21075
+ const exportAllSources = [];
21078
21076
  for (const statement of programRoot.body ?? []) {
21079
21077
  if (isNodeOfType(statement, "ExportNamedDeclaration") && statement.source) {
21080
- if (statement.exportKind === "type") continue;
21081
21078
  const sourceValue = statement.source.value;
21082
21079
  if (typeof sourceValue !== "string") continue;
21083
21080
  for (const specifier of statement.specifiers ?? []) {
21084
21081
  if (!isNodeOfType(specifier, "ExportSpecifier")) continue;
21085
- if (specifier.exportKind === "type") continue;
21086
21082
  const exported = specifier.exported;
21087
- if ((isNodeOfType(exported, "Identifier") ? exported.name : isNodeOfType(exported, "Literal") && typeof exported.value === "string" ? exported.value : null) !== exportedName) continue;
21088
- const local = specifier.local;
21089
- const importedName = isNodeOfType(local, "Identifier") ? local.name : isNodeOfType(local, "Literal") && typeof local.value === "string" ? local.value : null;
21090
- if (importedName) return [{
21091
- importedName,
21092
- source: sourceValue
21093
- }];
21083
+ if ((isNodeOfType(exported, "Identifier") ? exported.name : isNodeOfType(exported, "Literal") && typeof exported.value === "string" ? exported.value : null) === exportedName) return [sourceValue];
21094
21084
  }
21095
21085
  }
21096
21086
  if (isNodeOfType(statement, "ExportAllDeclaration") && statement.source) {
21097
- if (statement.exportKind === "type" || statement.exported) continue;
21098
21087
  const sourceValue = statement.source.value;
21099
- if (typeof sourceValue === "string") exportAllTargets.push({
21100
- importedName: exportedName,
21101
- source: sourceValue
21102
- });
21088
+ if (typeof sourceValue === "string") exportAllSources.push(sourceValue);
21103
21089
  }
21104
21090
  }
21105
- return exportAllTargets;
21091
+ return exportAllSources;
21106
21092
  };
21107
21093
  //#endregion
21108
21094
  //#region src/plugin/utils/attach-parent-references.ts
@@ -21191,6 +21177,139 @@ const parseSourceFile = (absoluteFilePath) => {
21191
21177
  return parsedProgram;
21192
21178
  };
21193
21179
  //#endregion
21180
+ //#region src/plugin/utils/is-barrel-index-module.ts
21181
+ const INDEX_MODULE_FILE_PATTERN = /^index\.(?:[cm]?[jt]sx?|mjs)$/;
21182
+ const BINDING_IMPORT_DECLARATION_PATTERN = /^\s*import\s+(type\s+)?(?!["'])([^;]*?)\s+from\s+["']([^"']+)["']\s*;?\s*(?:(?:\/\/[^\n]*)?\s*)/gm;
21183
+ const BARREL_REEXPORT_DECLARATION_PATTERN = /^\s*export\s+(type\s+)?(?:\*(?:\s+as\s+([\w$]+))?|\{([\s\S]*?)\})\s+from\s+["']([^"']+)["']\s*;?\s*(?:(?:\/\/[^\n]*)?\s*)/gm;
21184
+ const LOCAL_EXPORT_SPECIFIER_DECLARATION_PATTERN = /^\s*export\s+(type\s+)?\{([\s\S]*?)\}\s*;?\s*(?:(?:\/\/[^\n]*)?\s*)/gm;
21185
+ const barrelIndexModuleInfoCache = /* @__PURE__ */ new Map();
21186
+ const isIndexModuleFilePath = (filePath) => INDEX_MODULE_FILE_PATTERN.test(path.basename(filePath));
21187
+ const createNonBarrelInfo = () => ({
21188
+ isBarrel: false,
21189
+ exportsByName: /* @__PURE__ */ new Map(),
21190
+ starExportSources: []
21191
+ });
21192
+ const addImportedBinding = (importedBindings, binding) => {
21193
+ importedBindings.set(binding.localName, {
21194
+ ...binding,
21195
+ didExport: false
21196
+ });
21197
+ };
21198
+ const collectNamedImportBindings = (namedSpecifiersText, source, declarationIsTypeOnly, importedBindings) => {
21199
+ for (const specifier of parseExportSpecifiers(namedSpecifiersText, declarationIsTypeOnly)) addImportedBinding(importedBindings, {
21200
+ localName: specifier.exportedName,
21201
+ importedName: specifier.localName,
21202
+ source,
21203
+ isTypeOnly: specifier.isTypeOnly
21204
+ });
21205
+ };
21206
+ const collectImportBindings = (importClause, source, declarationIsTypeOnly, importedBindings) => {
21207
+ const trimmedImportClause = importClause.trim();
21208
+ const namespaceMatch = trimmedImportClause.match(/(?:^|,\s*)\*\s+as\s+([\w$]+)/);
21209
+ if (namespaceMatch?.[1]) addImportedBinding(importedBindings, {
21210
+ localName: namespaceMatch[1],
21211
+ importedName: "*",
21212
+ source,
21213
+ isTypeOnly: declarationIsTypeOnly
21214
+ });
21215
+ const namedImportMatch = trimmedImportClause.match(/\{([\s\S]*?)\}/);
21216
+ if (namedImportMatch?.[1]) collectNamedImportBindings(namedImportMatch[1], source, declarationIsTypeOnly, importedBindings);
21217
+ const defaultImportName = trimmedImportClause.split(",")[0]?.trim();
21218
+ if (defaultImportName && !defaultImportName.startsWith("{") && !defaultImportName.startsWith("*")) addImportedBinding(importedBindings, {
21219
+ localName: defaultImportName,
21220
+ importedName: "default",
21221
+ source,
21222
+ isTypeOnly: declarationIsTypeOnly
21223
+ });
21224
+ };
21225
+ const replaceKnownDeclarations = (sourceText, importedBindings, exportsByName, starExportSources) => {
21226
+ let withoutKnownDeclarations = sourceText.replace(BINDING_IMPORT_DECLARATION_PATTERN, (_match, typeKeyword, importClause, source) => {
21227
+ collectImportBindings(importClause, source, Boolean(typeKeyword), importedBindings);
21228
+ return "";
21229
+ });
21230
+ withoutKnownDeclarations = withoutKnownDeclarations.replace(BARREL_REEXPORT_DECLARATION_PATTERN, (_match, typeKeyword, namespaceExportName, specifiersText, source) => {
21231
+ const isTypeOnly = Boolean(typeKeyword);
21232
+ if (namespaceExportName) {
21233
+ exportsByName.set(namespaceExportName, {
21234
+ exportedName: namespaceExportName,
21235
+ importedName: "*",
21236
+ source,
21237
+ isTypeOnly
21238
+ });
21239
+ return "";
21240
+ }
21241
+ if (specifiersText) {
21242
+ for (const specifier of parseExportSpecifiers(specifiersText, isTypeOnly)) exportsByName.set(specifier.exportedName, {
21243
+ exportedName: specifier.exportedName,
21244
+ importedName: specifier.localName,
21245
+ source,
21246
+ isTypeOnly: specifier.isTypeOnly
21247
+ });
21248
+ return "";
21249
+ }
21250
+ starExportSources.push(source);
21251
+ return "";
21252
+ });
21253
+ withoutKnownDeclarations = withoutKnownDeclarations.replace(LOCAL_EXPORT_SPECIFIER_DECLARATION_PATTERN, (_match, typeKeyword, specifiersText) => {
21254
+ for (const specifier of parseExportSpecifiers(specifiersText, Boolean(typeKeyword))) {
21255
+ const importedBinding = importedBindings.get(specifier.localName);
21256
+ if (!importedBinding) return _match;
21257
+ importedBinding.didExport = true;
21258
+ exportsByName.set(specifier.exportedName, {
21259
+ exportedName: specifier.exportedName,
21260
+ importedName: importedBinding.importedName,
21261
+ source: importedBinding.source,
21262
+ isTypeOnly: specifier.isTypeOnly || importedBinding.isTypeOnly
21263
+ });
21264
+ }
21265
+ return "";
21266
+ });
21267
+ return withoutKnownDeclarations;
21268
+ };
21269
+ const hasUnexportedRuntimeImport = (importedBindings) => {
21270
+ for (const binding of importedBindings.values()) if (!binding.isTypeOnly && !binding.didExport) return true;
21271
+ return false;
21272
+ };
21273
+ const classifyBarrelModule = (sourceText) => {
21274
+ const strippedSource = stripJsComments(sourceText).trim();
21275
+ if (!strippedSource) return createNonBarrelInfo();
21276
+ const importedBindings = /* @__PURE__ */ new Map();
21277
+ const exportsByName = /* @__PURE__ */ new Map();
21278
+ const starExportSources = [];
21279
+ if (replaceKnownDeclarations(strippedSource, importedBindings, exportsByName, starExportSources).trim() || hasUnexportedRuntimeImport(importedBindings)) return createNonBarrelInfo();
21280
+ return {
21281
+ isBarrel: exportsByName.size > 0 || starExportSources.length > 0,
21282
+ exportsByName,
21283
+ starExportSources
21284
+ };
21285
+ };
21286
+ const getBarrelIndexModuleInfo = (filePath) => {
21287
+ if (!isIndexModuleFilePath(filePath)) return createNonBarrelInfo();
21288
+ recordContentProbe(filePath);
21289
+ let fileStat;
21290
+ try {
21291
+ fileStat = fs.statSync(filePath);
21292
+ } catch {
21293
+ fileStat = null;
21294
+ }
21295
+ const cachedResult = barrelIndexModuleInfoCache.get(filePath);
21296
+ if (cachedResult !== void 0 && fileStat !== null && cachedResult.mtimeMs === fileStat.mtimeMs && cachedResult.size === fileStat.size) return cachedResult.moduleInfo;
21297
+ if (fileStat === null) return createNonBarrelInfo();
21298
+ let moduleInfo = createNonBarrelInfo();
21299
+ try {
21300
+ moduleInfo = classifyBarrelModule(fs.readFileSync(filePath, "utf8"));
21301
+ } catch {
21302
+ moduleInfo = createNonBarrelInfo();
21303
+ }
21304
+ barrelIndexModuleInfoCache.set(filePath, {
21305
+ mtimeMs: fileStat.mtimeMs,
21306
+ size: fileStat.size,
21307
+ moduleInfo
21308
+ });
21309
+ return moduleInfo;
21310
+ };
21311
+ const isBarrelIndexModule = (filePath) => getBarrelIndexModuleInfo(filePath).isBarrel;
21312
+ //#endregion
21194
21313
  //#region src/plugin/utils/resolve-relative-import-path.ts
21195
21314
  const MODULE_FILE_EXTENSIONS = [
21196
21315
  ".ts",
@@ -21307,6 +21426,35 @@ const resolveModuleFileFromAbsolutePath = (importPath) => {
21307
21426
  };
21308
21427
  const resolveRelativeImportPath = (filename, source) => resolveModuleFileFromAbsolutePath(path.resolve(path.dirname(filename), source));
21309
21428
  //#endregion
21429
+ //#region src/plugin/utils/resolve-barrel-export-file-path.ts
21430
+ const getUniqueFilePath = (filePaths) => {
21431
+ const uniqueFilePaths = new Set(filePaths);
21432
+ if (uniqueFilePaths.size !== 1) return null;
21433
+ const [filePath] = uniqueFilePaths;
21434
+ return filePath ?? null;
21435
+ };
21436
+ const resolveStarExportFilePath = (barrelFilePath, exportedName, source, visitedFilePaths) => {
21437
+ const resolvedTargetPath = resolveRelativeImportPath(barrelFilePath, source);
21438
+ if (!resolvedTargetPath) return null;
21439
+ const nestedTargetPath = resolveBarrelExportFilePath(resolvedTargetPath, exportedName, new Set(visitedFilePaths));
21440
+ if (nestedTargetPath) return nestedTargetPath;
21441
+ return doesModuleExportName(resolvedTargetPath, exportedName) ? resolvedTargetPath : null;
21442
+ };
21443
+ const resolveBarrelExportFilePath = (barrelFilePath, exportedName, visitedFilePaths = /* @__PURE__ */ new Set()) => {
21444
+ if (visitedFilePaths.has(barrelFilePath)) return null;
21445
+ visitedFilePaths.add(barrelFilePath);
21446
+ const moduleInfo = getBarrelIndexModuleInfo(barrelFilePath);
21447
+ if (!moduleInfo.isBarrel) return null;
21448
+ const target = moduleInfo.exportsByName.get(exportedName);
21449
+ if (target) {
21450
+ const resolvedTargetPath = resolveRelativeImportPath(barrelFilePath, target.source);
21451
+ if (!resolvedTargetPath) return null;
21452
+ return resolveBarrelExportFilePath(resolvedTargetPath, target.importedName, visitedFilePaths) ?? resolvedTargetPath;
21453
+ }
21454
+ if (exportedName === "default") return null;
21455
+ return getUniqueFilePath(moduleInfo.starExportSources.map((source) => resolveStarExportFilePath(barrelFilePath, exportedName, source, visitedFilePaths)).filter((filePath) => Boolean(filePath)));
21456
+ };
21457
+ //#endregion
21310
21458
  //#region src/plugin/utils/resolve-tsconfig-alias.ts
21311
21459
  const TSCONFIG_FILE_NAMES = ["tsconfig.json", "jsconfig.json"];
21312
21460
  const isObjectRecord = (value) => typeof value === "object" && value !== null;
@@ -21485,29 +21633,25 @@ const resolveTsconfigAliasPath = (fromFilename, source) => {
21485
21633
  };
21486
21634
  //#endregion
21487
21635
  //#region src/plugin/utils/resolve-module-path.ts
21488
- const resolveModulePath = (fromFilename, source) => {
21489
- if (path.isAbsolute(source)) return null;
21490
- return source.startsWith(".") ? resolveRelativeImportPath(fromFilename, source) : resolveTsconfigAliasPath(fromFilename, source);
21491
- };
21636
+ const resolveModulePath = (fromFilename, source) => resolveRelativeImportPath(fromFilename, source) ?? resolveTsconfigAliasPath(fromFilename, source);
21492
21637
  //#endregion
21493
21638
  //#region src/plugin/utils/resolve-cross-file-function-export.ts
21494
21639
  const resolveFunctionExportInFile = (filePath, exportedName, visitedFilePaths) => {
21495
21640
  if (visitedFilePaths.size >= 4) return null;
21496
21641
  if (visitedFilePaths.has(filePath)) return null;
21497
21642
  visitedFilePaths.add(filePath);
21498
- const programRoot = parseSourceFile(filePath);
21643
+ const actualFilePath = resolveBarrelExportFilePath(filePath, exportedName) ?? filePath;
21644
+ const programRoot = parseSourceFile(actualFilePath);
21499
21645
  if (!programRoot) return null;
21500
21646
  const exported = findExportedFunctionBody(programRoot, exportedName);
21501
21647
  if (exported) return exported;
21502
- const resolvedCandidates = /* @__PURE__ */ new Set();
21503
- for (const target of findReExportTargetsForName(programRoot, exportedName)) {
21504
- const nextFilePath = resolveModulePath(filePath, target.source);
21648
+ for (const reExportSource of findReExportSourcesForName(programRoot, exportedName)) {
21649
+ const nextFilePath = resolveModulePath(actualFilePath, reExportSource);
21505
21650
  if (!nextFilePath) continue;
21506
- const resolved = resolveFunctionExportInFile(nextFilePath, target.importedName, new Set(visitedFilePaths));
21507
- if (resolved) resolvedCandidates.add(resolved);
21651
+ const resolved = resolveFunctionExportInFile(nextFilePath, exportedName, visitedFilePaths);
21652
+ if (resolved) return resolved;
21508
21653
  }
21509
- if (resolvedCandidates.size !== 1) return null;
21510
- return resolvedCandidates.values().next().value ?? null;
21654
+ return null;
21511
21655
  };
21512
21656
  const resolveCrossFileFunctionExport = (fromFilename, source, exportedName) => {
21513
21657
  const resolvedFilePath = resolveModulePath(fromFilename, source);
@@ -22690,46 +22834,6 @@ const PURE_GLOBAL_CALLEE_NAMES = new Set([
22690
22834
  "parseInt"
22691
22835
  ]);
22692
22836
  const PURE_GLOBAL_NAMESPACE_NAMES = new Set(["JSON", "Math"]);
22693
- const PURE_HELPER_NAMESPACE_MEMBER_NAMES = new Map([["JSON", new Set([
22694
- "isRawJSON",
22695
- "parse",
22696
- "rawJSON",
22697
- "stringify"
22698
- ])], ["Math", new Set([
22699
- "abs",
22700
- "acos",
22701
- "acosh",
22702
- "asin",
22703
- "asinh",
22704
- "atan",
22705
- "atan2",
22706
- "atanh",
22707
- "cbrt",
22708
- "ceil",
22709
- "clz32",
22710
- "cos",
22711
- "cosh",
22712
- "exp",
22713
- "floor",
22714
- "fround",
22715
- "hypot",
22716
- "imul",
22717
- "log",
22718
- "log10",
22719
- "log1p",
22720
- "log2",
22721
- "max",
22722
- "min",
22723
- "pow",
22724
- "round",
22725
- "sign",
22726
- "sin",
22727
- "sinh",
22728
- "sqrt",
22729
- "tan",
22730
- "tanh",
22731
- "trunc"
22732
- ])]]);
22733
22837
  const PURE_MEMBER_TRANSFORM_NAMES = new Set([
22734
22838
  "concat",
22735
22839
  "filter",
@@ -22776,42 +22880,6 @@ const getParameterBindingIdentity = (analysis, functionNode, parameter) => {
22776
22880
  }
22777
22881
  return parameter;
22778
22882
  };
22779
- const isAsyncOrGeneratorFunction = (functionNode) => Boolean(functionNode.async === true || functionNode.generator === true);
22780
- const isModuleFunction = (functionNode) => {
22781
- let ancestor = functionNode.parent;
22782
- while (ancestor) {
22783
- if (isFunctionLike$1(ancestor)) return false;
22784
- if (isNodeOfType(ancestor, "Program")) return true;
22785
- ancestor = ancestor.parent;
22786
- }
22787
- return false;
22788
- };
22789
- const getFunctionBindingNames = (functionNode) => {
22790
- const names = /* @__PURE__ */ new Set();
22791
- if ((isNodeOfType(functionNode, "FunctionDeclaration") || isNodeOfType(functionNode, "FunctionExpression")) && functionNode.id) names.add(functionNode.id.name);
22792
- const parent = functionNode.parent;
22793
- if (parent && isNodeOfType(parent, "VariableDeclarator") && isNodeOfType(parent.id, "Identifier")) names.add(parent.id.name);
22794
- return names;
22795
- };
22796
- const collectModuleBindingNames = (functionNode) => {
22797
- let program = functionNode.parent;
22798
- while (program && !isNodeOfType(program, "Program")) program = program.parent;
22799
- const bindingNames = /* @__PURE__ */ new Set();
22800
- if (!program || !isNodeOfType(program, "Program")) return bindingNames;
22801
- for (const statement of program.body ?? []) {
22802
- if (isNodeOfType(statement, "ImportDeclaration")) {
22803
- for (const specifier of statement.specifiers ?? []) if (isNodeOfType(specifier.local, "Identifier")) bindingNames.add(specifier.local.name);
22804
- continue;
22805
- }
22806
- const declaration = isNodeOfType(statement, "ExportNamedDeclaration") || isNodeOfType(statement, "ExportDefaultDeclaration") ? statement.declaration : statement;
22807
- if (isNodeOfType(declaration, "VariableDeclaration")) {
22808
- for (const declarator of declaration.declarations ?? []) collectPatternNames(declarator.id, bindingNames);
22809
- continue;
22810
- }
22811
- if ((isNodeOfType(declaration, "FunctionDeclaration") || isNodeOfType(declaration, "ClassDeclaration")) && isNodeOfType(declaration.id, "Identifier")) bindingNames.add(declaration.id.name);
22812
- }
22813
- return bindingNames;
22814
- };
22815
22883
  const buildSubstitutions = (analysis, functionNode, argumentExpressions, parentFrame) => {
22816
22884
  const substitutions = /* @__PURE__ */ new Map();
22817
22885
  const parameters = getFunctionParameters(functionNode);
@@ -22942,7 +23010,7 @@ const collectIntroducedBindings = (analysis, functionNode) => {
22942
23010
  for (const parameter of getFunctionParameters(functionNode)) if (isNodeOfType(parameter, "Identifier")) introducedBindings.add(getParameterBindingIdentity(analysis, functionNode, parameter));
22943
23011
  return introducedBindings;
22944
23012
  };
22945
- const collectBoundedEffectExecutionFrames = (analysis, effectNode, currentFilename) => {
23013
+ const collectBoundedEffectExecutionFrames = (analysis, effectNode) => {
22946
23014
  const effectFunction = getEffectFn(analysis, effectNode);
22947
23015
  if (!effectFunction || !isFunctionLike$1(effectFunction) || effectFunction.async === true) return [];
22948
23016
  const invokedFunctionEvidence = collectEffectInvokedFunctions(effectFunction);
@@ -22951,8 +23019,7 @@ const collectBoundedEffectExecutionFrames = (analysis, effectNode, currentFilena
22951
23019
  invocation: null,
22952
23020
  isDeferred: false,
22953
23021
  introducedBindings: /* @__PURE__ */ new Set(),
22954
- substitutions: /* @__PURE__ */ new Map(),
22955
- currentFilename
23022
+ substitutions: /* @__PURE__ */ new Map()
22956
23023
  };
22957
23024
  const frames = [rootFrame];
22958
23025
  walkAst(effectFunction, (child) => {
@@ -22973,8 +23040,7 @@ const collectBoundedEffectExecutionFrames = (analysis, effectNode, currentFilena
22973
23040
  invocation: child,
22974
23041
  isDeferred,
22975
23042
  introducedBindings,
22976
- substitutions: buildSubstitutions(analysis, callable, argumentsForCallable, rootFrame),
22977
- currentFilename
23043
+ substitutions: buildSubstitutions(analysis, callable, argumentsForCallable, rootFrame)
22978
23044
  });
22979
23045
  };
22980
23046
  if (isFunctionLike$1(callee)) {
@@ -23027,174 +23093,6 @@ const isOpaqueHookCall = (callExpression) => {
23027
23093
  const calleeName = getCallCalleeName$1(callExpression);
23028
23094
  return Boolean(calleeName && /^use[A-Z0-9]/.test(calleeName) && calleeName !== "useMemo" && calleeName !== "useCallback" && calleeName !== "useEffectEvent");
23029
23095
  };
23030
- const analyzeHelperStatements = (statements, environment, usedParameterIndices, analyzeExpression) => {
23031
- let canContinue = true;
23032
- for (const statement of statements) {
23033
- if (!canContinue) {
23034
- if (!isNodeOfType(statement, "EmptyStatement")) return {
23035
- canContinue: false,
23036
- isValid: false
23037
- };
23038
- continue;
23039
- }
23040
- if (isNodeOfType(statement, "EmptyStatement")) continue;
23041
- if (isNodeOfType(statement, "ReturnStatement")) {
23042
- if (!statement.argument || !analyzeExpression(statement.argument, environment, usedParameterIndices)) return {
23043
- canContinue: false,
23044
- isValid: false
23045
- };
23046
- canContinue = false;
23047
- continue;
23048
- }
23049
- if (isNodeOfType(statement, "BlockStatement")) {
23050
- const blockSummary = analyzeHelperStatements(statement.body ?? [], environment, usedParameterIndices, analyzeExpression);
23051
- if (!blockSummary.isValid) return blockSummary;
23052
- canContinue = blockSummary.canContinue;
23053
- continue;
23054
- }
23055
- if (isNodeOfType(statement, "IfStatement")) {
23056
- if (!analyzeExpression(statement.test, environment, usedParameterIndices)) return {
23057
- canContinue: false,
23058
- isValid: false
23059
- };
23060
- const consequentSummary = analyzeHelperStatements([statement.consequent], environment, usedParameterIndices, analyzeExpression);
23061
- if (!consequentSummary.isValid) return consequentSummary;
23062
- const alternateSummary = statement.alternate ? analyzeHelperStatements([statement.alternate], environment, usedParameterIndices, analyzeExpression) : {
23063
- canContinue: true,
23064
- isValid: true
23065
- };
23066
- if (!alternateSummary.isValid) return alternateSummary;
23067
- canContinue = consequentSummary.canContinue || alternateSummary.canContinue;
23068
- continue;
23069
- }
23070
- return {
23071
- canContinue: false,
23072
- isValid: false
23073
- };
23074
- }
23075
- return {
23076
- canContinue,
23077
- isValid: true
23078
- };
23079
- };
23080
- const analyzeHelperExpression = (expression, environment, usedParameterIndices) => {
23081
- const node = stripParenExpression(expression);
23082
- if (isNodeOfType(node, "Literal") || isNodeOfType(node, "TemplateElement")) return true;
23083
- if (isNodeOfType(node, "Identifier")) {
23084
- const parameterIndex = environment.parameterIndices.get(node.name);
23085
- if (parameterIndex !== void 0) {
23086
- if (parameterIndex !== null) usedParameterIndices.add(parameterIndex);
23087
- return true;
23088
- }
23089
- return node.name === "undefined" || node.name === "NaN" || node.name === "Infinity";
23090
- }
23091
- if (isNodeOfType(node, "ArrayExpression")) return (node.elements ?? []).every((element) => !element || analyzeHelperExpression(element, environment, usedParameterIndices));
23092
- if (isNodeOfType(node, "ObjectExpression")) return (node.properties ?? []).every((property) => {
23093
- if (isNodeOfType(property, "SpreadElement")) return analyzeHelperExpression(property.argument, environment, usedParameterIndices);
23094
- if (!isNodeOfType(property, "Property") || property.kind !== "init" || property.method === true) return false;
23095
- if (property.computed && !analyzeHelperExpression(property.key, environment, usedParameterIndices)) return false;
23096
- return analyzeHelperExpression(property.value, environment, usedParameterIndices);
23097
- });
23098
- if (isNodeOfType(node, "TemplateLiteral")) return (node.expressions ?? []).every((templateExpression) => analyzeHelperExpression(templateExpression, environment, usedParameterIndices));
23099
- if (isNodeOfType(node, "UnaryExpression")) return node.operator !== "delete" && analyzeHelperExpression(node.argument, environment, usedParameterIndices);
23100
- if (isNodeOfType(node, "BinaryExpression") || isNodeOfType(node, "LogicalExpression")) return analyzeHelperExpression(node.left, environment, usedParameterIndices) && analyzeHelperExpression(node.right, environment, usedParameterIndices);
23101
- if (isNodeOfType(node, "ConditionalExpression")) return analyzeHelperExpression(node.test, environment, usedParameterIndices) && analyzeHelperExpression(node.consequent, environment, usedParameterIndices) && analyzeHelperExpression(node.alternate, environment, usedParameterIndices);
23102
- if (isNodeOfType(node, "MemberExpression")) {
23103
- if (!analyzeHelperExpression(node.object, environment, usedParameterIndices)) return false;
23104
- return !node.computed || analyzeHelperExpression(node.property, environment, usedParameterIndices);
23105
- }
23106
- if (isFunctionLike$1(node)) {
23107
- if (isAsyncOrGeneratorFunction(node)) return false;
23108
- const callbackParameterIndices = new Map(environment.parameterIndices);
23109
- for (const parameter of getFunctionParameters(node)) {
23110
- if (!isNodeOfType(parameter, "Identifier")) return false;
23111
- callbackParameterIndices.set(parameter.name, null);
23112
- }
23113
- const callbackEnvironment = {
23114
- parameterIndices: callbackParameterIndices,
23115
- recursiveNames: environment.recursiveNames,
23116
- shadowedGlobalNames: environment.shadowedGlobalNames
23117
- };
23118
- if (!isNodeOfType(node.body, "BlockStatement")) return analyzeHelperExpression(node.body, callbackEnvironment, usedParameterIndices);
23119
- const callbackSummary = analyzeHelperStatements(node.body.body ?? [], callbackEnvironment, usedParameterIndices, analyzeHelperExpression);
23120
- return callbackSummary.isValid && !callbackSummary.canContinue;
23121
- }
23122
- if (isNodeOfType(node, "CallExpression")) {
23123
- const callee = stripParenExpression(node.callee);
23124
- const calleeRoot = getMemberRoot(callee);
23125
- const isPureGlobalCall = isNodeOfType(callee, "Identifier") && PURE_GLOBAL_CALLEE_NAMES.has(callee.name) && !environment.parameterIndices.has(callee.name) && !environment.recursiveNames.has(callee.name) && !environment.shadowedGlobalNames.has(callee.name);
23126
- const namespaceName = isNodeOfType(calleeRoot, "Identifier") && !environment.parameterIndices.has(calleeRoot.name) && !environment.recursiveNames.has(calleeRoot.name) && !environment.shadowedGlobalNames.has(calleeRoot.name) ? calleeRoot.name : null;
23127
- const namespaceMemberName = getStaticMemberName(callee);
23128
- const isPureNamespaceCall = isNodeOfType(callee, "MemberExpression") && namespaceName !== null && namespaceMemberName !== null && PURE_HELPER_NAMESPACE_MEMBER_NAMES.get(namespaceName)?.has(namespaceMemberName) === true;
23129
- const isPureMemberTransform = isNodeOfType(callee, "MemberExpression") && PURE_MEMBER_TRANSFORM_NAMES.has(getStaticMemberName(callee) ?? "");
23130
- if (!isPureGlobalCall && !isPureNamespaceCall && !isPureMemberTransform) return false;
23131
- if (isPureMemberTransform && isNodeOfType(callee, "MemberExpression") && !analyzeHelperExpression(callee.object, environment, usedParameterIndices)) return false;
23132
- return (node.arguments ?? []).every((argument) => analyzeHelperExpression(argument, environment, usedParameterIndices));
23133
- }
23134
- if (isNodeOfType(node, "SpreadElement")) return analyzeHelperExpression(node.argument, environment, usedParameterIndices);
23135
- return false;
23136
- };
23137
- const helperSummaryCache = /* @__PURE__ */ new WeakMap();
23138
- const summarizeHelperReturn = (functionNode) => {
23139
- if (!isFunctionLike$1(functionNode) || !isModuleFunction(functionNode)) return null;
23140
- if (helperSummaryCache.has(functionNode)) return helperSummaryCache.get(functionNode) ?? null;
23141
- if (isAsyncOrGeneratorFunction(functionNode)) {
23142
- helperSummaryCache.set(functionNode, null);
23143
- return null;
23144
- }
23145
- const parameterIndices = /* @__PURE__ */ new Map();
23146
- const parameters = getFunctionParameters(functionNode);
23147
- for (let parameterIndex = 0; parameterIndex < parameters.length; parameterIndex += 1) {
23148
- const parameter = parameters[parameterIndex];
23149
- if (!parameter || !isNodeOfType(parameter, "Identifier") || parameterIndices.has(parameter.name)) {
23150
- helperSummaryCache.set(functionNode, null);
23151
- return null;
23152
- }
23153
- parameterIndices.set(parameter.name, parameterIndex);
23154
- }
23155
- const environment = {
23156
- parameterIndices,
23157
- recursiveNames: getFunctionBindingNames(functionNode),
23158
- shadowedGlobalNames: collectModuleBindingNames(functionNode)
23159
- };
23160
- const usedParameterIndices = /* @__PURE__ */ new Set();
23161
- if (!isNodeOfType(functionNode.body, "BlockStatement")) {
23162
- if (!analyzeHelperExpression(functionNode.body, environment, usedParameterIndices)) {
23163
- helperSummaryCache.set(functionNode, null);
23164
- return null;
23165
- }
23166
- } else {
23167
- const controlFlowSummary = analyzeHelperStatements(functionNode.body.body ?? [], environment, usedParameterIndices, analyzeHelperExpression);
23168
- if (!controlFlowSummary.isValid || controlFlowSummary.canContinue) {
23169
- helperSummaryCache.set(functionNode, null);
23170
- return null;
23171
- }
23172
- }
23173
- const summary = { usedParameterIndices };
23174
- helperSummaryCache.set(functionNode, summary);
23175
- return summary;
23176
- };
23177
- const resolveValueHelperFunction = (analysis, callee, currentFilename) => {
23178
- if (!isNodeOfType(callee, "Identifier")) return null;
23179
- const reference = getRef(analysis, callee);
23180
- if (!reference?.resolved) return null;
23181
- const importDefinition = reference.resolved.defs.find((definition) => definition.type === "ImportBinding");
23182
- if (importDefinition) {
23183
- if (!currentFilename) return null;
23184
- const specifier = importDefinition.node;
23185
- if (!isNodeOfType(specifier, "ImportSpecifier") && !isNodeOfType(specifier, "ImportDefaultSpecifier")) return null;
23186
- const importDeclaration = specifier.parent;
23187
- if (!importDeclaration || !isNodeOfType(importDeclaration, "ImportDeclaration")) return null;
23188
- if (importDeclaration.importKind === "type" || isNodeOfType(specifier, "ImportSpecifier") && specifier.importKind === "type") return null;
23189
- const source = importDeclaration.source?.value;
23190
- if (typeof source !== "string") return null;
23191
- const exportedName = resolveImportedExportName(specifier);
23192
- if (!exportedName) return null;
23193
- return resolveCrossFileFunctionExport(currentFilename, source, exportedName);
23194
- }
23195
- const callable = resolveWrappedCallable(analysis, callee);
23196
- return callable && isModuleFunction(callable) ? callable : null;
23197
- };
23198
23096
  const isLocallyConstructedObjectMember = (reference, memberExpression) => isNodeOfType(memberExpression, "MemberExpression") && reference.resolved?.defs.some((definition) => {
23199
23097
  const definitionNode = definition.node;
23200
23098
  return isNodeOfType(definitionNode, "VariableDeclarator") && Boolean(definitionNode.init) && (isNodeOfType(definitionNode.init, "ObjectExpression") || isNodeOfType(definitionNode.init, "ArrayExpression"));
@@ -23280,55 +23178,38 @@ const collectValueEvidence = (analysis, expression, frame, remainingCallFrames,
23280
23178
  const calleeRoot = getMemberRoot(callee);
23281
23179
  const isPureGlobalCall = isNodeOfType(callee, "Identifier") && PURE_GLOBAL_CALLEE_NAMES.has(callee.name) && getIdentifierBindingIdentity(analysis, callee) === null || isNodeOfType(calleeRoot, "Identifier") && PURE_GLOBAL_NAMESPACE_NAMES.has(calleeRoot.name) && getIdentifierBindingIdentity(analysis, calleeRoot) === null;
23282
23180
  const isPureMemberTransform = isNodeOfType(callee, "MemberExpression") && PURE_MEMBER_TRANSFORM_NAMES.has(getStaticMemberName(callee) ?? "");
23283
- if (isPureGlobalCall || isPureMemberTransform) {
23284
- if (isPureMemberTransform && isNodeOfType(callee, "MemberExpression")) mergeEvidence(evidence, collectValueEvidence(analysis, callee.object, frame, remainingCallFrames, new Set(visitedBindings)));
23285
- for (const argument of node.arguments ?? []) {
23286
- if (isFunctionLike$1(argument)) continue;
23287
- mergeEvidence(evidence, collectValueEvidence(analysis, argument, frame, remainingCallFrames, new Set(visitedBindings)));
23288
- }
23181
+ if (isPureMemberTransform) mergeEvidence(evidence, collectValueEvidence(analysis, callee.object, frame, remainingCallFrames, new Set(visitedBindings)));
23182
+ for (const argument of node.arguments ?? []) {
23183
+ if (isFunctionLike$1(argument)) continue;
23184
+ mergeEvidence(evidence, collectValueEvidence(analysis, argument, frame, remainingCallFrames, new Set(visitedBindings)));
23185
+ }
23186
+ if (isPureGlobalCall || isPureMemberTransform) return evidence;
23187
+ if (isNodeOfType(callee, "MemberExpression")) {
23188
+ evidence.hasUnknownSource = true;
23289
23189
  return evidence;
23290
23190
  }
23291
23191
  if (remainingCallFrames <= 0) {
23292
23192
  evidence.hasUnknownSource = true;
23293
23193
  return evidence;
23294
23194
  }
23295
- const localHelperFunction = resolveWrappedCallable(analysis, callee);
23296
- if (localHelperFunction && !isModuleFunction(localHelperFunction)) {
23297
- if (isAsyncOrGeneratorFunction(localHelperFunction) || functionInvokesItself(analysis, localHelperFunction)) {
23298
- evidence.hasUnknownSource = true;
23299
- return evidence;
23300
- }
23301
- const localHelperFrame = {
23302
- functionNode: localHelperFunction,
23303
- invocation: node,
23304
- isDeferred: false,
23305
- introducedBindings: /* @__PURE__ */ new Set(),
23306
- substitutions: buildSubstitutions(analysis, localHelperFunction, node.arguments ?? [], frame),
23307
- currentFilename: frame.currentFilename
23308
- };
23309
- const returnedExpressions = getReturnedExpressions(localHelperFunction);
23310
- if (returnedExpressions.length === 0) {
23311
- evidence.hasUnknownSource = true;
23312
- return evidence;
23313
- }
23314
- for (const returnedExpression of returnedExpressions) mergeEvidence(evidence, collectValueEvidence(analysis, returnedExpression, localHelperFrame, remainingCallFrames - 1, new Set(visitedBindings)));
23195
+ const callable = resolveWrappedCallable(analysis, callee);
23196
+ if (!callable || callable.async === true || functionInvokesItself(analysis, callable)) {
23197
+ evidence.hasUnknownSource = true;
23315
23198
  return evidence;
23316
23199
  }
23317
- const helperFunction = resolveValueHelperFunction(analysis, callee, frame.currentFilename);
23318
- const helperSummary = helperFunction ? summarizeHelperReturn(helperFunction) : null;
23319
- if (!helperSummary) {
23200
+ const valueFrame = {
23201
+ functionNode: callable,
23202
+ invocation: node,
23203
+ isDeferred: false,
23204
+ introducedBindings: /* @__PURE__ */ new Set(),
23205
+ substitutions: buildSubstitutions(analysis, callable, node.arguments ?? [], frame)
23206
+ };
23207
+ const returnedExpressions = getReturnedExpressions(callable);
23208
+ if (returnedExpressions.length === 0) {
23320
23209
  evidence.hasUnknownSource = true;
23321
23210
  return evidence;
23322
23211
  }
23323
- const argumentsForHelper = node.arguments ?? [];
23324
- for (const parameterIndex of helperSummary.usedParameterIndices) {
23325
- const argument = argumentsForHelper[parameterIndex];
23326
- if (!argument) {
23327
- evidence.hasUnknownSource = true;
23328
- return evidence;
23329
- }
23330
- mergeEvidence(evidence, collectValueEvidence(analysis, argument, frame, remainingCallFrames - 1, new Set(visitedBindings)));
23331
- }
23212
+ for (const returnedExpression of returnedExpressions) mergeEvidence(evidence, collectValueEvidence(analysis, returnedExpression, valueFrame, remainingCallFrames - 1, new Set(visitedBindings)));
23332
23213
  return evidence;
23333
23214
  }
23334
23215
  if (isFunctionLike$1(node) || isNodeOfType(node, "AwaitExpression")) {
@@ -23408,8 +23289,8 @@ const areInMutuallyExclusiveBranches = (leftNode, rightNode) => {
23408
23289
  }
23409
23290
  return false;
23410
23291
  };
23411
- const collectEffectStateWriteFacts = (analysis, effectNode, currentFilename) => {
23412
- const frames = collectBoundedEffectExecutionFrames(analysis, effectNode, currentFilename);
23292
+ const collectEffectStateWriteFacts = (analysis, effectNode) => {
23293
+ const frames = collectBoundedEffectExecutionFrames(analysis, effectNode);
23413
23294
  if (frames.length === 0) return [];
23414
23295
  const effectHasCleanup = hasCleanup(analysis, effectNode);
23415
23296
  const cleanupManagedStateDeclarators = /* @__PURE__ */ new Set();
@@ -23429,8 +23310,7 @@ const collectEffectStateWriteFacts = (analysis, effectNode, currentFilename) =>
23429
23310
  invocation: callExpression,
23430
23311
  isDeferred: frame.isDeferred,
23431
23312
  introducedBindings: collectIntroducedBindings(analysis, writtenValue),
23432
- substitutions: /* @__PURE__ */ new Map(),
23433
- currentFilename
23313
+ substitutions: /* @__PURE__ */ new Map()
23434
23314
  };
23435
23315
  valueEvidence = emptyEvidence();
23436
23316
  const returnedExpressions = getReturnedExpressions(writtenValue);
@@ -23479,7 +23359,7 @@ const noAdjustStateOnPropChange = defineRule({
23479
23359
  const dependencyReferences = getEffectDepsRefs(analysis, node);
23480
23360
  if (!dependencyReferences) return;
23481
23361
  if (!dependencyReferences.flatMap((reference) => isState(analysis, reference) ? [] : getUpstreamRefs(analysis, reference)).some((reference) => isProp(analysis, reference))) return;
23482
- for (const fact of collectEffectStateWriteFacts(analysis, node, context.filename)) {
23362
+ for (const fact of collectEffectStateWriteFacts(analysis, node)) {
23483
23363
  if (!fact.isRenderKnownCopy || fact.resetsSourceState) continue;
23484
23364
  context.report({
23485
23365
  node: fact.callExpression,
@@ -24740,139 +24620,6 @@ const createRelativeImportSource = (filename, targetFilePath) => {
24740
24620
  return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
24741
24621
  };
24742
24622
  //#endregion
24743
- //#region src/plugin/utils/is-barrel-index-module.ts
24744
- const INDEX_MODULE_FILE_PATTERN = /^index\.(?:[cm]?[jt]sx?|mjs)$/;
24745
- const BINDING_IMPORT_DECLARATION_PATTERN = /^\s*import\s+(type\s+)?(?!["'])([^;]*?)\s+from\s+["']([^"']+)["']\s*;?\s*(?:(?:\/\/[^\n]*)?\s*)/gm;
24746
- const BARREL_REEXPORT_DECLARATION_PATTERN = /^\s*export\s+(type\s+)?(?:\*(?:\s+as\s+([\w$]+))?|\{([\s\S]*?)\})\s+from\s+["']([^"']+)["']\s*;?\s*(?:(?:\/\/[^\n]*)?\s*)/gm;
24747
- const LOCAL_EXPORT_SPECIFIER_DECLARATION_PATTERN = /^\s*export\s+(type\s+)?\{([\s\S]*?)\}\s*;?\s*(?:(?:\/\/[^\n]*)?\s*)/gm;
24748
- const barrelIndexModuleInfoCache = /* @__PURE__ */ new Map();
24749
- const isIndexModuleFilePath = (filePath) => INDEX_MODULE_FILE_PATTERN.test(path.basename(filePath));
24750
- const createNonBarrelInfo = () => ({
24751
- isBarrel: false,
24752
- exportsByName: /* @__PURE__ */ new Map(),
24753
- starExportSources: []
24754
- });
24755
- const addImportedBinding = (importedBindings, binding) => {
24756
- importedBindings.set(binding.localName, {
24757
- ...binding,
24758
- didExport: false
24759
- });
24760
- };
24761
- const collectNamedImportBindings = (namedSpecifiersText, source, declarationIsTypeOnly, importedBindings) => {
24762
- for (const specifier of parseExportSpecifiers(namedSpecifiersText, declarationIsTypeOnly)) addImportedBinding(importedBindings, {
24763
- localName: specifier.exportedName,
24764
- importedName: specifier.localName,
24765
- source,
24766
- isTypeOnly: specifier.isTypeOnly
24767
- });
24768
- };
24769
- const collectImportBindings = (importClause, source, declarationIsTypeOnly, importedBindings) => {
24770
- const trimmedImportClause = importClause.trim();
24771
- const namespaceMatch = trimmedImportClause.match(/(?:^|,\s*)\*\s+as\s+([\w$]+)/);
24772
- if (namespaceMatch?.[1]) addImportedBinding(importedBindings, {
24773
- localName: namespaceMatch[1],
24774
- importedName: "*",
24775
- source,
24776
- isTypeOnly: declarationIsTypeOnly
24777
- });
24778
- const namedImportMatch = trimmedImportClause.match(/\{([\s\S]*?)\}/);
24779
- if (namedImportMatch?.[1]) collectNamedImportBindings(namedImportMatch[1], source, declarationIsTypeOnly, importedBindings);
24780
- const defaultImportName = trimmedImportClause.split(",")[0]?.trim();
24781
- if (defaultImportName && !defaultImportName.startsWith("{") && !defaultImportName.startsWith("*")) addImportedBinding(importedBindings, {
24782
- localName: defaultImportName,
24783
- importedName: "default",
24784
- source,
24785
- isTypeOnly: declarationIsTypeOnly
24786
- });
24787
- };
24788
- const replaceKnownDeclarations = (sourceText, importedBindings, exportsByName, starExportSources) => {
24789
- let withoutKnownDeclarations = sourceText.replace(BINDING_IMPORT_DECLARATION_PATTERN, (_match, typeKeyword, importClause, source) => {
24790
- collectImportBindings(importClause, source, Boolean(typeKeyword), importedBindings);
24791
- return "";
24792
- });
24793
- withoutKnownDeclarations = withoutKnownDeclarations.replace(BARREL_REEXPORT_DECLARATION_PATTERN, (_match, typeKeyword, namespaceExportName, specifiersText, source) => {
24794
- const isTypeOnly = Boolean(typeKeyword);
24795
- if (namespaceExportName) {
24796
- exportsByName.set(namespaceExportName, {
24797
- exportedName: namespaceExportName,
24798
- importedName: "*",
24799
- source,
24800
- isTypeOnly
24801
- });
24802
- return "";
24803
- }
24804
- if (specifiersText) {
24805
- for (const specifier of parseExportSpecifiers(specifiersText, isTypeOnly)) exportsByName.set(specifier.exportedName, {
24806
- exportedName: specifier.exportedName,
24807
- importedName: specifier.localName,
24808
- source,
24809
- isTypeOnly: specifier.isTypeOnly
24810
- });
24811
- return "";
24812
- }
24813
- starExportSources.push(source);
24814
- return "";
24815
- });
24816
- withoutKnownDeclarations = withoutKnownDeclarations.replace(LOCAL_EXPORT_SPECIFIER_DECLARATION_PATTERN, (_match, typeKeyword, specifiersText) => {
24817
- for (const specifier of parseExportSpecifiers(specifiersText, Boolean(typeKeyword))) {
24818
- const importedBinding = importedBindings.get(specifier.localName);
24819
- if (!importedBinding) return _match;
24820
- importedBinding.didExport = true;
24821
- exportsByName.set(specifier.exportedName, {
24822
- exportedName: specifier.exportedName,
24823
- importedName: importedBinding.importedName,
24824
- source: importedBinding.source,
24825
- isTypeOnly: specifier.isTypeOnly || importedBinding.isTypeOnly
24826
- });
24827
- }
24828
- return "";
24829
- });
24830
- return withoutKnownDeclarations;
24831
- };
24832
- const hasUnexportedRuntimeImport = (importedBindings) => {
24833
- for (const binding of importedBindings.values()) if (!binding.isTypeOnly && !binding.didExport) return true;
24834
- return false;
24835
- };
24836
- const classifyBarrelModule = (sourceText) => {
24837
- const strippedSource = stripJsComments(sourceText).trim();
24838
- if (!strippedSource) return createNonBarrelInfo();
24839
- const importedBindings = /* @__PURE__ */ new Map();
24840
- const exportsByName = /* @__PURE__ */ new Map();
24841
- const starExportSources = [];
24842
- if (replaceKnownDeclarations(strippedSource, importedBindings, exportsByName, starExportSources).trim() || hasUnexportedRuntimeImport(importedBindings)) return createNonBarrelInfo();
24843
- return {
24844
- isBarrel: exportsByName.size > 0 || starExportSources.length > 0,
24845
- exportsByName,
24846
- starExportSources
24847
- };
24848
- };
24849
- const getBarrelIndexModuleInfo = (filePath) => {
24850
- if (!isIndexModuleFilePath(filePath)) return createNonBarrelInfo();
24851
- recordContentProbe(filePath);
24852
- let fileStat;
24853
- try {
24854
- fileStat = fs.statSync(filePath);
24855
- } catch {
24856
- fileStat = null;
24857
- }
24858
- const cachedResult = barrelIndexModuleInfoCache.get(filePath);
24859
- if (cachedResult !== void 0 && fileStat !== null && cachedResult.mtimeMs === fileStat.mtimeMs && cachedResult.size === fileStat.size) return cachedResult.moduleInfo;
24860
- if (fileStat === null) return createNonBarrelInfo();
24861
- let moduleInfo = createNonBarrelInfo();
24862
- try {
24863
- moduleInfo = classifyBarrelModule(fs.readFileSync(filePath, "utf8"));
24864
- } catch {
24865
- moduleInfo = createNonBarrelInfo();
24866
- }
24867
- barrelIndexModuleInfoCache.set(filePath, {
24868
- mtimeMs: fileStat.mtimeMs,
24869
- size: fileStat.size,
24870
- moduleInfo
24871
- });
24872
- return moduleInfo;
24873
- };
24874
- const isBarrelIndexModule = (filePath) => getBarrelIndexModuleInfo(filePath).isBarrel;
24875
- //#endregion
24876
24623
  //#region src/react-native-dependency-names.ts
24877
24624
  const EXPO_MANAGED_DEPENDENCY_NAMES = new Set([
24878
24625
  "expo",
@@ -25075,35 +24822,6 @@ const classifyReactNativeFileTarget = (context) => {
25075
24822
  };
25076
24823
  const isReactNativeFileActive = (context) => classifyReactNativeFileTarget(context) !== "web";
25077
24824
  //#endregion
25078
- //#region src/plugin/utils/resolve-barrel-export-file-path.ts
25079
- const getUniqueFilePath = (filePaths) => {
25080
- const uniqueFilePaths = new Set(filePaths);
25081
- if (uniqueFilePaths.size !== 1) return null;
25082
- const [filePath] = uniqueFilePaths;
25083
- return filePath ?? null;
25084
- };
25085
- const resolveStarExportFilePath = (barrelFilePath, exportedName, source, visitedFilePaths) => {
25086
- const resolvedTargetPath = resolveRelativeImportPath(barrelFilePath, source);
25087
- if (!resolvedTargetPath) return null;
25088
- const nestedTargetPath = resolveBarrelExportFilePath(resolvedTargetPath, exportedName, new Set(visitedFilePaths));
25089
- if (nestedTargetPath) return nestedTargetPath;
25090
- return doesModuleExportName(resolvedTargetPath, exportedName) ? resolvedTargetPath : null;
25091
- };
25092
- const resolveBarrelExportFilePath = (barrelFilePath, exportedName, visitedFilePaths = /* @__PURE__ */ new Set()) => {
25093
- if (visitedFilePaths.has(barrelFilePath)) return null;
25094
- visitedFilePaths.add(barrelFilePath);
25095
- const moduleInfo = getBarrelIndexModuleInfo(barrelFilePath);
25096
- if (!moduleInfo.isBarrel) return null;
25097
- const target = moduleInfo.exportsByName.get(exportedName);
25098
- if (target) {
25099
- const resolvedTargetPath = resolveRelativeImportPath(barrelFilePath, target.source);
25100
- if (!resolvedTargetPath) return null;
25101
- return resolveBarrelExportFilePath(resolvedTargetPath, target.importedName, visitedFilePaths) ?? resolvedTargetPath;
25102
- }
25103
- if (exportedName === "default") return null;
25104
- return getUniqueFilePath(moduleInfo.starExportSources.map((source) => resolveStarExportFilePath(barrelFilePath, exportedName, source, visitedFilePaths)).filter((filePath) => Boolean(filePath)));
25105
- };
25106
- //#endregion
25107
24825
  //#region src/plugin/rules/bundle-size/no-barrel-import.ts
25108
24826
  const TYPE_DECLARATION_FILE_PATTERN = /\.d\.[cm]?ts$/;
25109
24827
  const SERVER_ONLY_FILE_PATTERN = /\.server\.[cm]?[jt]sx?$/;
@@ -26419,7 +26137,7 @@ const noDerivedState = defineRule({
26419
26137
  if (!isUseEffect(node)) return;
26420
26138
  const analysis = getProgramAnalysis(node);
26421
26139
  if (!analysis) return;
26422
- for (const fact of collectEffectStateWriteFacts(analysis, node, context.filename)) {
26140
+ for (const fact of collectEffectStateWriteFacts(analysis, node)) {
26423
26141
  if (!fact.isRenderKnownCopy || fact.resetsSourceState) continue;
26424
26142
  const stateName = getStateName$1(fact.stateDeclarator);
26425
26143
  context.report({
@@ -26441,7 +26159,7 @@ const noDerivedStateEffect = defineRule({
26441
26159
  if (!isHookCall$2(node, EFFECT_HOOK_NAMES$1)) return;
26442
26160
  const analysis = getProgramAnalysis(node);
26443
26161
  if (!analysis) return;
26444
- if (!collectEffectStateWriteFacts(analysis, node, context.filename).find((fact) => fact.isRenderKnownCopy && !fact.resetsSourceState)) return;
26162
+ if (!collectEffectStateWriteFacts(analysis, node).find((fact) => fact.isRenderKnownCopy && !fact.resetsSourceState)) return;
26445
26163
  context.report({
26446
26164
  node,
26447
26165
  message: "You pay an extra render for state you can derive from other values."
@@ -30603,131 +30321,6 @@ const noImgLazyWithHighFetchpriority = defineRule({
30603
30321
  } })
30604
30322
  });
30605
30323
  //#endregion
30606
- //#region src/plugin/rules/state-and-effects/no-impure-state-updater.ts
30607
- const TIMER_FUNCTION_NAMES = new Set([
30608
- "cancelAnimationFrame",
30609
- "clearInterval",
30610
- "clearTimeout",
30611
- "queueMicrotask",
30612
- "requestAnimationFrame",
30613
- "setInterval",
30614
- "setTimeout"
30615
- ]);
30616
- const STORAGE_MUTATION_METHOD_NAMES = new Set([
30617
- "clear",
30618
- "removeItem",
30619
- "setItem"
30620
- ]);
30621
- const STORAGE_RECEIVER_NAMES = new Set(["localStorage", "sessionStorage"]);
30622
- const EXTERNAL_READ_METHOD_NAMES = new Set(["getBoundingClientRect", "getClientRects"]);
30623
- const NOTIFICATION_RECEIVER_NAMES = new Set([
30624
- "message",
30625
- "notification",
30626
- "toast"
30627
- ]);
30628
- const NOTIFICATION_METHOD_NAMES = new Set([
30629
- "error",
30630
- "info",
30631
- "loading",
30632
- "open",
30633
- "show",
30634
- "success",
30635
- "warning"
30636
- ]);
30637
- const getMemberCall = (node) => {
30638
- if (!isNodeOfType(node, "CallExpression")) return null;
30639
- if (!isNodeOfType(node.callee, "MemberExpression") || node.callee.computed) return null;
30640
- if (!isNodeOfType(node.callee.property, "Identifier")) return null;
30641
- return {
30642
- methodName: node.callee.property.name,
30643
- receiver: stripParenExpression(node.callee.object)
30644
- };
30645
- };
30646
- const isNotificationReceiver = (receiver, scopes) => {
30647
- if (!isNodeOfType(receiver, "Identifier")) return false;
30648
- if (!NOTIFICATION_RECEIVER_NAMES.has(receiver.name)) return false;
30649
- const symbol = scopes.symbolFor(receiver);
30650
- if (symbol?.kind === "import") return true;
30651
- if (!isNodeOfType(symbol?.initializer, "CallExpression")) return false;
30652
- const callee = symbol.initializer.callee;
30653
- return isNodeOfType(callee, "Identifier") && /^use(?:Message|Notification|Toast)$/.test(callee.name);
30654
- };
30655
- const getKnownImpureCall = (callExpression, scopes) => {
30656
- if (isNodeOfType(callExpression.callee, "Identifier") && TIMER_FUNCTION_NAMES.has(callExpression.callee.name) && scopes.isGlobalReference(callExpression.callee)) return `${callExpression.callee.name}()`;
30657
- const memberCall = getMemberCall(callExpression);
30658
- if (!memberCall) return null;
30659
- const { methodName, receiver } = memberCall;
30660
- if (STORAGE_MUTATION_METHOD_NAMES.has(methodName) && isNodeOfType(receiver, "Identifier") && STORAGE_RECEIVER_NAMES.has(receiver.name) && scopes.isGlobalReference(receiver)) return `${receiver.name}.${methodName}()`;
30661
- if (EXTERNAL_READ_METHOD_NAMES.has(methodName)) return `.${methodName}()`;
30662
- if (NOTIFICATION_METHOD_NAMES.has(methodName) && isNodeOfType(receiver, "Identifier") && isNotificationReceiver(receiver, scopes)) return `${receiver.name}.${methodName}()`;
30663
- return null;
30664
- };
30665
- const getExternalAssignmentDescription = (assignmentTarget, updater, scopes) => {
30666
- let rootIdentifier = null;
30667
- if (isNodeOfType(assignmentTarget, "Identifier")) rootIdentifier = assignmentTarget;
30668
- else if (isNodeOfType(assignmentTarget, "MemberExpression") && isNodeOfType(assignmentTarget.object, "Identifier")) rootIdentifier = assignmentTarget.object;
30669
- if (!rootIdentifier) return null;
30670
- const updaterScope = scopes.ownScopeFor(updater);
30671
- if (!updaterScope) return null;
30672
- const symbol = scopes.symbolFor(rootIdentifier);
30673
- if (!symbol) return `the external value "${rootIdentifier.name}"`;
30674
- if (symbol.kind === "parameter" && symbol.scope === updaterScope) return `the updater argument "${rootIdentifier.name}"`;
30675
- return isDescendantScope(symbol.scope, updaterScope) ? null : `the captured value "${rootIdentifier.name}"`;
30676
- };
30677
- const findImpureUpdaterOperation = (updater, scopes) => {
30678
- const analysis = getProgramAnalysis(updater);
30679
- let operation = null;
30680
- walkAst(updater, (child) => {
30681
- if (operation) return false;
30682
- if (child !== updater && isFunctionLike$1(child) && !executesDuringRender(child, scopes)) return false;
30683
- if (isNodeOfType(child, "CallExpression")) {
30684
- if (isNodeOfType(child.callee, "Identifier") && analysis) {
30685
- const calleeReference = getRef(analysis, child.callee);
30686
- if (calleeReference && isStateSetterCall(analysis, calleeReference)) {
30687
- operation = `the nested state update "${child.callee.name}()"`;
30688
- return false;
30689
- }
30690
- }
30691
- const impureCall = getKnownImpureCall(child, scopes);
30692
- if (impureCall) {
30693
- operation = impureCall;
30694
- return false;
30695
- }
30696
- }
30697
- if (isNodeOfType(child, "AssignmentExpression")) {
30698
- operation = getExternalAssignmentDescription(child.left, updater, scopes);
30699
- if (operation) return false;
30700
- }
30701
- if (isNodeOfType(child, "UpdateExpression")) {
30702
- operation = getExternalAssignmentDescription(child.argument, updater, scopes);
30703
- if (operation) return false;
30704
- }
30705
- });
30706
- return operation;
30707
- };
30708
- const noImpureStateUpdater = defineRule({
30709
- id: "no-impure-state-updater",
30710
- title: "State updater has side effects",
30711
- severity: "error",
30712
- recommendation: "Keep state updater callbacks pure and return only the next state. Move notifications, storage, timers, ref writes, and other external work into the event or effect that queues the update.",
30713
- create: (context) => ({ CallExpression(node) {
30714
- const updater = node.arguments?.[0];
30715
- if (!updater || !isFunctionLike$1(updater)) return;
30716
- const analysis = getProgramAnalysis(node);
30717
- if (!analysis || !isNodeOfType(node.callee, "Identifier")) return;
30718
- const calleeReference = getRef(analysis, node.callee);
30719
- if (!calleeReference || !isStateSetterCall(analysis, calleeReference)) return;
30720
- const stateDeclarator = getUseStateDecl(analysis, calleeReference);
30721
- if (!isNodeOfType(stateDeclarator, "VariableDeclarator") || !isNodeOfType(stateDeclarator.init, "CallExpression") || !isReactApiCall(stateDeclarator.init, "useState", context.scopes, { allowGlobalReactNamespace: true })) return;
30722
- const operation = findImpureUpdaterOperation(updater, context.scopes);
30723
- if (!operation) return;
30724
- context.report({
30725
- node: updater,
30726
- message: `This state updater performs ${operation}. React may run updater functions more than once, so side effects here can repeat or observe inconsistent external state.`
30727
- });
30728
- } })
30729
- });
30730
- //#endregion
30731
30324
  //#region src/plugin/rules/correctness/no-indeterminate-attribute.ts
30732
30325
  const MESSAGE$21 = "The `indeterminate` HTML attribute does not set a checkbox's visual state. Assign the `HTMLInputElement.indeterminate` DOM property instead.";
30733
30326
  const REACT_USE_REF_OPTIONS = {
@@ -30894,7 +30487,7 @@ const noInitializeState = defineRule({
30894
30487
  if (!dependencies || !isNodeOfType(dependencies, "ArrayExpression") || (dependencies.elements ?? []).length !== 0) return;
30895
30488
  const analysis = getProgramAnalysis(node);
30896
30489
  if (!analysis) return;
30897
- for (const fact of collectEffectStateWriteFacts(analysis, node, context.filename)) {
30490
+ for (const fact of collectEffectStateWriteFacts(analysis, node)) {
30898
30491
  if (!fact.isRenderKnownCopy || fact.matchesStateInitializer || fact.resetsSourceState) continue;
30899
30492
  const stateName = getStateName(fact.stateDeclarator);
30900
30493
  context.report({
@@ -33896,166 +33489,6 @@ const isDirectParentCallbackRef = (analysis, ref) => {
33896
33489
  return getDownstreamRefs(analysis, initializer).some((initializerRef) => getUpstreamRefs(analysis, initializerRef).some((upstreamRef) => isProp(analysis, upstreamRef)));
33897
33490
  }));
33898
33491
  };
33899
- const getDeclarationKind = (declarator) => {
33900
- const declaration = declarator.parent;
33901
- return declaration && isNodeOfType(declaration, "VariableDeclaration") ? declaration.kind : null;
33902
- };
33903
- const hasMutableBindingWrite = (reference) => Boolean(reference.resolved?.references.some((candidateReference) => candidateReference.isWrite() && !candidateReference.init));
33904
- const getDestructuredPropertyName = (bindingIdentifier) => {
33905
- const property = bindingIdentifier.parent;
33906
- if (!property || !isNodeOfType(property, "Property")) return null;
33907
- if (property.computed) return isNodeOfType(property.key, "Literal") && typeof property.key.value === "string" ? String(property.key.value) : null;
33908
- return isNodeOfType(property.key, "Identifier") ? property.key.name : null;
33909
- };
33910
- const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @__PURE__ */ new Set()) => {
33911
- const unwrappedExpression = stripParenExpression(expression);
33912
- if (isNodeOfType(unwrappedExpression, "Identifier")) {
33913
- const callbackReference = getRef(analysis, unwrappedExpression);
33914
- const callbackVariable = callbackReference?.resolved;
33915
- if (!callbackReference || !callbackVariable || visitedVariables.has(callbackVariable)) return null;
33916
- if (isProp(analysis, callbackReference)) {
33917
- if (isWholePropsObjectReference(analysis, callbackReference)) return null;
33918
- const bindingIdentifier = callbackVariable.defs.find((definition) => definition.type === "Parameter")?.name;
33919
- return (bindingIdentifier && getDestructuredPropertyName(bindingIdentifier)) ?? unwrappedExpression.name;
33920
- }
33921
- if (hasMutableBindingWrite(callbackReference)) return null;
33922
- const definitions = callbackVariable.defs.map((definition) => definition.node).filter((definitionNode) => isNodeOfType(definitionNode, "VariableDeclarator"));
33923
- if (definitions.length !== 1) return null;
33924
- const declarator = definitions[0];
33925
- if (!declarator || !isNodeOfType(declarator, "VariableDeclarator") || getDeclarationKind(declarator) !== "const" || !declarator.init) return null;
33926
- visitedVariables.add(callbackVariable);
33927
- if (isNodeOfType(declarator.id, "ObjectPattern")) {
33928
- const bindingIdentifier = callbackVariable.defs[0]?.name;
33929
- const propertyName = bindingIdentifier ? getDestructuredPropertyName(bindingIdentifier) : null;
33930
- const propsReference = getDownstreamRefs(analysis, declarator.init).find((candidateReference) => isWholePropsObjectReference(analysis, candidateReference));
33931
- return propertyName && propsReference ? propertyName : null;
33932
- }
33933
- if (!isNodeOfType(declarator.id, "Identifier")) return null;
33934
- return getParentCallbackPropName(analysis, declarator.init, visitedVariables);
33935
- }
33936
- if (!isNodeOfType(unwrappedExpression, "MemberExpression")) return null;
33937
- const callbackName = getStaticMemberPropertyName(unwrappedExpression);
33938
- if (!callbackName) return null;
33939
- const receiver = stripParenExpression(unwrappedExpression.object);
33940
- if (!isNodeOfType(receiver, "Identifier")) return null;
33941
- const receiverReference = getRef(analysis, receiver);
33942
- if (!receiverReference || !isWholePropsObjectReference(analysis, receiverReference)) return null;
33943
- return callbackName;
33944
- };
33945
- const isNullishRefInitializer = (initializer) => {
33946
- if (!initializer) return true;
33947
- const unwrappedInitializer = stripParenExpression(initializer);
33948
- if (isNodeOfType(unwrappedInitializer, "Literal")) return unwrappedInitializer.value === null;
33949
- if (!isNodeOfType(unwrappedInitializer, "Identifier")) return false;
33950
- return unwrappedInitializer.name === "undefined";
33951
- };
33952
- const getDirectComponentBodyStatement = (node, componentBody) => {
33953
- let current = node;
33954
- while (current?.parent && current.parent !== componentBody) current = current.parent;
33955
- return current?.parent === componentBody ? current : null;
33956
- };
33957
- const getVariableForDeclarator = (analysis, declarator) => {
33958
- for (const scope of analysis.scopeManager.scopes) {
33959
- const variable = scope.variables.find((candidateVariable) => candidateVariable.defs.some((definition) => definition.node === declarator));
33960
- if (variable) return variable;
33961
- }
33962
- return null;
33963
- };
33964
- const getRefMember = (identifier) => {
33965
- const receiver = findTransparentExpressionRoot(identifier);
33966
- const member = receiver.parent;
33967
- if (!member || !isNodeOfType(member, "MemberExpression") || member.object !== receiver) return null;
33968
- return member;
33969
- };
33970
- const getRefMemberAssignment = (identifier) => {
33971
- const member = getRefMember(identifier);
33972
- if (!member) return null;
33973
- const memberRoot = findTransparentExpressionRoot(member);
33974
- const assignment = memberRoot.parent;
33975
- if (!assignment || !isNodeOfType(assignment, "AssignmentExpression") || assignment.left !== memberRoot) return null;
33976
- return assignment;
33977
- };
33978
- const getRefAliasDeclarator = (identifier) => {
33979
- const initializer = findTransparentExpressionRoot(identifier);
33980
- const declarator = initializer.parent;
33981
- if (!declarator || !isNodeOfType(declarator, "VariableDeclarator") || declarator.init !== initializer || !isNodeOfType(declarator.id, "Identifier") || getDeclarationKind(declarator) !== "const") return null;
33982
- return declarator;
33983
- };
33984
- const getRefBindingProvenance = (analysis, receiver, isReactUseRefCall) => {
33985
- if (!isNodeOfType(receiver, "Identifier")) return null;
33986
- const receiverReference = getRef(analysis, receiver);
33987
- if (!receiverReference?.resolved || hasMutableBindingWrite(receiverReference)) return null;
33988
- const variables = /* @__PURE__ */ new Set();
33989
- let currentVariable = receiverReference.resolved;
33990
- let refCall = null;
33991
- while (currentVariable && !variables.has(currentVariable)) {
33992
- variables.add(currentVariable);
33993
- const definitions = currentVariable.defs.map((definition) => definition.node).filter((definitionNode) => isNodeOfType(definitionNode, "VariableDeclarator"));
33994
- if (definitions.length !== 1) return null;
33995
- const declarator = definitions[0];
33996
- if (!declarator || !isNodeOfType(declarator, "VariableDeclarator") || !isNodeOfType(declarator.id, "Identifier") || !declarator.init) return null;
33997
- if (isNodeOfType(declarator.init, "CallExpression") && isReactUseRefCall(declarator.init)) {
33998
- refCall = declarator.init;
33999
- break;
34000
- }
34001
- if (getDeclarationKind(declarator) !== "const" || !isNodeOfType(stripParenExpression(declarator.init), "Identifier")) return null;
34002
- const upstreamReference = getRef(analysis, stripParenExpression(declarator.init));
34003
- if (!upstreamReference?.resolved || hasMutableBindingWrite(upstreamReference)) return null;
34004
- currentVariable = upstreamReference.resolved;
34005
- }
34006
- if (!refCall) return null;
34007
- const pendingVariables = [...variables];
34008
- while (pendingVariables.length > 0) {
34009
- const variable = pendingVariables.pop();
34010
- if (!variable) continue;
34011
- for (const candidateReference of variable.references) {
34012
- const aliasDeclarator = getRefAliasDeclarator(candidateReference.identifier);
34013
- if (!aliasDeclarator) continue;
34014
- const aliasVariable = getVariableForDeclarator(analysis, aliasDeclarator);
34015
- if (!aliasVariable || variables.has(aliasVariable)) continue;
34016
- if (aliasVariable.references.some((aliasReference) => aliasReference.isWrite() && !aliasReference.init)) return null;
34017
- variables.add(aliasVariable);
34018
- pendingVariables.push(aliasVariable);
34019
- }
34020
- }
34021
- return {
34022
- refCall,
34023
- variables
34024
- };
34025
- };
34026
- const getCallbackRefProvenance = (analysis, effectCall, callExpression, isReactUseRefCall) => {
34027
- const callee = stripParenExpression(callExpression.callee);
34028
- if (!isNodeOfType(callee, "MemberExpression") || getStaticMemberPropertyName(callee) !== "current") return null;
34029
- const bindingProvenance = getRefBindingProvenance(analysis, stripParenExpression(callee.object), isReactUseRefCall);
34030
- if (!bindingProvenance) return null;
34031
- const { refCall, variables } = bindingProvenance;
34032
- const componentFunction = findEnclosingFunction(effectCall);
34033
- if (!componentFunction || !isFunctionLike$1(componentFunction) || !isComponentFunction$1(componentFunction) || !isNodeOfType(componentFunction.body, "BlockStatement")) return null;
34034
- if (!getDirectComponentBodyStatement(effectCall, componentFunction.body)) return null;
34035
- const callbackPropNames = /* @__PURE__ */ new Set();
34036
- const initializer = refCall.arguments?.[0];
34037
- const initializerCallbackName = initializer ? getParentCallbackPropName(analysis, initializer) : null;
34038
- if (initializerCallbackName) callbackPropNames.add(initializerCallbackName);
34039
- else if (!isNullishRefInitializer(initializer)) return null;
34040
- for (const variable of variables) for (const candidateReference of variable.references) {
34041
- if (candidateReference.init) continue;
34042
- if (candidateReference.isWrite()) return null;
34043
- const identifier = candidateReference.identifier;
34044
- if (getRefAliasDeclarator(identifier)) continue;
34045
- const member = getRefMember(identifier);
34046
- if (!member || getStaticMemberPropertyName(member) !== "current") return null;
34047
- const memberParent = findTransparentExpressionRoot(member).parent;
34048
- if (memberParent && (isNodeOfType(memberParent, "UpdateExpression") || isNodeOfType(memberParent, "UnaryExpression") && memberParent.operator === "delete")) return null;
34049
- const assignment = getRefMemberAssignment(identifier);
34050
- if (!assignment) continue;
34051
- const assignmentStatement = findTransparentExpressionRoot(assignment).parent;
34052
- if (assignment.operator !== "=" || !assignmentStatement || !isNodeOfType(assignmentStatement, "ExpressionStatement") || assignmentStatement.parent !== componentFunction.body) return null;
34053
- const assignedCallbackName = getParentCallbackPropName(analysis, assignment.right);
34054
- if (!assignedCallbackName) return null;
34055
- callbackPropNames.add(assignedCallbackName);
34056
- }
34057
- return callbackPropNames.size > 0 ? { callbackPropNames } : null;
34058
- };
34059
33492
  const isWrapperHookCallbackRef = (analysis, ref) => Boolean(ref.resolved?.defs.some((def) => {
34060
33493
  const node = def.node;
34061
33494
  if (!isNodeOfType(node, "VariableDeclarator") || !node.init) return false;
@@ -34111,79 +33544,71 @@ const noPassDataToParent = defineRule({
34111
33544
  severity: "warn",
34112
33545
  tags: ["test-noise"],
34113
33546
  recommendation: "Fetch the data in the parent and pass it down as a prop (or return it from the hook), instead of handing it back up through a prop callback in a useEffect. See https://react.dev/learn/you-might-not-need-an-effect#passing-data-to-the-parent",
34114
- create: (context) => {
34115
- const isReactUseRefCall = (node) => isReactApiCall(node, "useRef", context.scopes, {
34116
- allowGlobalReactNamespace: true,
34117
- allowUnboundBareCalls: true
34118
- });
34119
- return { CallExpression(node) {
34120
- if (!isUseEffect(node)) return;
34121
- const analysis = getProgramAnalysis(node);
34122
- if (!analysis) return;
34123
- if (hasCleanup(analysis, node)) return;
34124
- const effectFnRefs = getEffectFnRefs(analysis, node);
34125
- if (!effectFnRefs) return;
34126
- const effectFn = getEffectFn(analysis, node);
34127
- if (!effectFn) return;
34128
- for (const ref of effectFnRefs) {
34129
- const callExpr = getCallExpr(ref);
34130
- if (!callExpr || !isNodeOfType(callExpr, "CallExpression")) continue;
34131
- const callbackRefProvenance = getCallbackRefProvenance(analysis, node, callExpr, isReactUseRefCall);
34132
- if (isRefCall(analysis, ref) && !callbackRefProvenance) continue;
34133
- if (!isSynchronous(ref.identifier, effectFn)) continue;
34134
- const calleeNode = unwrapChainExpression$1(callExpr.callee);
34135
- const identifier = ref.identifier;
34136
- if (callbackRefProvenance) {
34137
- if ([...callbackRefProvenance.callbackPropNames].some((callbackPropName) => COMMAND_PROP_NAME_PATTERN.test(callbackPropName))) continue;
34138
- } else if (calleeNode === identifier) {
34139
- if (!isDirectParentCallbackRef(analysis, ref)) continue;
34140
- if (isNodeOfType(identifier, "Identifier") && COMMAND_PROP_NAME_PATTERN.test(identifier.name)) continue;
34141
- } else if (isNodeOfType(calleeNode, "MemberExpression") && stripParenExpression(calleeNode.object) === identifier) {
34142
- if (!isWholePropsObjectReference(analysis, ref)) continue;
34143
- if (isCustomHookParameter(ref)) continue;
34144
- } else continue;
34145
- const methodName = getCallMethodName(calleeNode);
34146
- const isPropCallbackNamedLikeStringRead = Boolean(methodName && STRING_READ_METHOD_NAMES.has(methodName) && isNodeOfType(calleeNode, "MemberExpression") && stripParenExpression(calleeNode.object) === ref.identifier && isWholePropsObjectReference(analysis, ref));
34147
- if (methodName && DATA_SINK_METHOD_NAMES.has(methodName) && !isPropCallbackNamedLikeStringRead) continue;
34148
- if (methodName && COMMAND_PROP_NAME_PATTERN.test(methodName)) continue;
34149
- if (!callbackRefProvenance && isNamespacedApiCallee(calleeNode)) continue;
34150
- const isSetterNamedCallee = callbackRefProvenance ? [...callbackRefProvenance.callbackPropNames].every((callbackPropName) => SETTER_NAMED_PROP_PATTERN.test(callbackPropName)) : Boolean((isNodeOfType(identifier, "Identifier") ? identifier.name : methodName) && SETTER_NAMED_PROP_PATTERN.test((isNodeOfType(identifier, "Identifier") ? identifier.name : methodName) ?? ""));
34151
- const isLeafRef = (argRef) => getUpstreamRefs(analysis, argRef).length === 1;
34152
- const argsUpstreamRefs = (callExpr.arguments ?? []).flatMap((argument) => {
34153
- if (isFunctionLike$1(argument)) {
34154
- if (!isSetterNamedCallee) return [];
34155
- return getFunctionalUpdaterDataRefs(analysis, argument);
34156
- }
34157
- if (isHandlerBagArgument(analysis, argument)) return [];
34158
- if (isParentWiredHookResultArgument(analysis, argument)) return [];
34159
- if (isNodeOfType(argument, "Identifier")) {
34160
- const argumentRef = getRef(analysis, argument);
34161
- if (argumentRef && resolveToFunction(argumentRef)) return [];
34162
- }
34163
- return getDownstreamRefs(analysis, argument);
34164
- }).flatMap((argumentRef) => getUpstreamRefs(analysis, argumentRef)).filter(isLeafRef);
34165
- if (calleeNode === identifier && isWrapperHookCallbackRef(analysis, ref)) argsUpstreamRefs.push(...getArgsUpstreamRefs(analysis, ref).filter(isLeafRef));
34166
- if (!argsUpstreamRefs.some((argRef) => {
34167
- if (isUseStateIdentifier(argRef.identifier)) return false;
34168
- if (isProp(analysis, argRef)) return false;
34169
- if (isUseRefIdentifier(argRef.identifier)) return false;
34170
- if (isRefCurrent(argRef)) return false;
34171
- if (isConstant(argRef)) return false;
34172
- if (isParentWiredHookResultRef(analysis, argRef)) return false;
34173
- if (isParentWiredHookCalleeRef(analysis, argRef)) return false;
34174
- if (resolvesToFunctionBinding(argRef)) return false;
34175
- const argIdentifier = argRef.identifier;
34176
- if (isImportBindingRef(argRef) && !isCalleePosition(argIdentifier)) return false;
34177
- if (isNodeOfType(argIdentifier, "Identifier") && argIdentifier.name === "undefined") return false;
34178
- return true;
34179
- })) continue;
34180
- context.report({
34181
- node: callExpr,
34182
- message: "Handing data back to a parent from a useEffect costs your users an extra render."
34183
- });
34184
- }
34185
- } };
34186
- }
33547
+ create: (context) => ({ CallExpression(node) {
33548
+ if (!isUseEffect(node)) return;
33549
+ const analysis = getProgramAnalysis(node);
33550
+ if (!analysis) return;
33551
+ if (hasCleanup(analysis, node)) return;
33552
+ const effectFnRefs = getEffectFnRefs(analysis, node);
33553
+ if (!effectFnRefs) return;
33554
+ const effectFn = getEffectFn(analysis, node);
33555
+ if (!effectFn) return;
33556
+ for (const ref of effectFnRefs) {
33557
+ const callExpr = getCallExpr(ref);
33558
+ if (!callExpr || !isNodeOfType(callExpr, "CallExpression")) continue;
33559
+ if (isRefCall(analysis, ref)) continue;
33560
+ if (!isSynchronous(ref.identifier, effectFn)) continue;
33561
+ const calleeNode = unwrapChainExpression$1(callExpr.callee);
33562
+ const identifier = ref.identifier;
33563
+ if (calleeNode === identifier) {
33564
+ if (!isDirectParentCallbackRef(analysis, ref)) continue;
33565
+ if (isNodeOfType(identifier, "Identifier") && COMMAND_PROP_NAME_PATTERN.test(identifier.name)) continue;
33566
+ } else if (isNodeOfType(calleeNode, "MemberExpression") && stripParenExpression(calleeNode.object) === identifier) {
33567
+ if (!isWholePropsObjectReference(analysis, ref)) continue;
33568
+ if (isCustomHookParameter(ref)) continue;
33569
+ } else continue;
33570
+ const methodName = getCallMethodName(calleeNode);
33571
+ const isPropCallbackNamedLikeStringRead = Boolean(methodName && STRING_READ_METHOD_NAMES.has(methodName) && isNodeOfType(calleeNode, "MemberExpression") && stripParenExpression(calleeNode.object) === ref.identifier && isWholePropsObjectReference(analysis, ref));
33572
+ if (methodName && DATA_SINK_METHOD_NAMES.has(methodName) && !isPropCallbackNamedLikeStringRead) continue;
33573
+ if (methodName && COMMAND_PROP_NAME_PATTERN.test(methodName)) continue;
33574
+ if (isNamespacedApiCallee(calleeNode)) continue;
33575
+ const calleeName = isNodeOfType(identifier, "Identifier") ? identifier.name : methodName;
33576
+ const isSetterNamedCallee = Boolean(calleeName && SETTER_NAMED_PROP_PATTERN.test(calleeName));
33577
+ const isLeafRef = (argRef) => getUpstreamRefs(analysis, argRef).length === 1;
33578
+ const argsUpstreamRefs = (callExpr.arguments ?? []).flatMap((argument) => {
33579
+ if (isFunctionLike$1(argument)) {
33580
+ if (!isSetterNamedCallee) return [];
33581
+ return getFunctionalUpdaterDataRefs(analysis, argument);
33582
+ }
33583
+ if (isHandlerBagArgument(analysis, argument)) return [];
33584
+ if (isParentWiredHookResultArgument(analysis, argument)) return [];
33585
+ if (isNodeOfType(argument, "Identifier")) {
33586
+ const argumentRef = getRef(analysis, argument);
33587
+ if (argumentRef && resolveToFunction(argumentRef)) return [];
33588
+ }
33589
+ return getDownstreamRefs(analysis, argument);
33590
+ }).flatMap((argumentRef) => getUpstreamRefs(analysis, argumentRef)).filter(isLeafRef);
33591
+ if (calleeNode === identifier && isWrapperHookCallbackRef(analysis, ref)) argsUpstreamRefs.push(...getArgsUpstreamRefs(analysis, ref).filter(isLeafRef));
33592
+ if (!argsUpstreamRefs.some((argRef) => {
33593
+ if (isUseStateIdentifier(argRef.identifier)) return false;
33594
+ if (isProp(analysis, argRef)) return false;
33595
+ if (isUseRefIdentifier(argRef.identifier)) return false;
33596
+ if (isRefCurrent(argRef)) return false;
33597
+ if (isConstant(argRef)) return false;
33598
+ if (isParentWiredHookResultRef(analysis, argRef)) return false;
33599
+ if (isParentWiredHookCalleeRef(analysis, argRef)) return false;
33600
+ if (resolvesToFunctionBinding(argRef)) return false;
33601
+ const argIdentifier = argRef.identifier;
33602
+ if (isImportBindingRef(argRef) && !isCalleePosition(argIdentifier)) return false;
33603
+ if (isNodeOfType(argIdentifier, "Identifier") && argIdentifier.name === "undefined") return false;
33604
+ return true;
33605
+ })) continue;
33606
+ context.report({
33607
+ node: callExpr,
33608
+ message: "Handing data back to a parent from a useEffect costs your users an extra render."
33609
+ });
33610
+ }
33611
+ } })
34187
33612
  });
34188
33613
  //#endregion
34189
33614
  //#region src/plugin/utils/is-call-result-consumed-as-argument.ts
@@ -56393,18 +55818,6 @@ const reactDoctorRules = [
56393
55818
  requires: [...new Set(["react", ...noImgLazyWithHighFetchpriority.requires ?? []])]
56394
55819
  }
56395
55820
  },
56396
- {
56397
- key: "react-doctor/no-impure-state-updater",
56398
- id: "no-impure-state-updater",
56399
- source: "react-doctor",
56400
- originallyExternal: false,
56401
- rule: {
56402
- ...noImpureStateUpdater,
56403
- framework: "global",
56404
- category: "Bugs",
56405
- requires: [...new Set(["react", ...noImpureStateUpdater.requires ?? []])]
56406
- }
56407
- },
56408
55821
  {
56409
55822
  key: "react-doctor/no-indeterminate-attribute",
56410
55823
  id: "no-indeterminate-attribute",
@@ -59504,11 +58917,6 @@ const CROSS_FILE_RULE_IDS = new Set([
59504
58917
  "no-indeterminate-attribute",
59505
58918
  "no-locale-format-in-render",
59506
58919
  "no-match-media-in-state-initializer",
59507
- "no-adjust-state-on-prop-change",
59508
- "no-derived-state",
59509
- "no-derived-state-effect",
59510
- "no-event-handler",
59511
- "no-initialize-state",
59512
58920
  "no-mutating-reducer-state",
59513
58921
  "no-unguarded-browser-global-in-render-or-hook-init",
59514
58922
  "prefer-dynamic-import",
@@ -59562,9 +58970,6 @@ const collectNextjsSearchParamsDependencies = ({ absoluteFilePath, staticImports
59562
58970
  if (hasAncestorSuspenseLayout(absoluteFilePath)) return;
59563
58971
  for (const entry of flattenImportEntries(staticImports)) resolveCrossFileFunctionExport(absoluteFilePath, entry.source, entry.exportedName);
59564
58972
  };
59565
- const collectEffectValueHelperDependencies = ({ absoluteFilePath, staticImports }) => {
59566
- for (const entry of flattenImportEntries(staticImports)) resolveCrossFileFunctionExport(absoluteFilePath, entry.source, entry.exportedName);
59567
- };
59568
58973
  const collectMutatingReducerDependencies = ({ absoluteFilePath, sourceText, staticImports, program }) => {
59569
58974
  const namedUseReducerLocals = /* @__PURE__ */ new Set();
59570
58975
  const reactObjectLocals = /* @__PURE__ */ new Set();
@@ -59651,11 +59056,6 @@ const CROSS_FILE_DEPENDENCY_COLLECTORS = new Map([
59651
59056
  ["no-indeterminate-attribute", collectNearestManifestDependencies],
59652
59057
  ["no-locale-format-in-render", collectNearestManifestDependencies],
59653
59058
  ["no-match-media-in-state-initializer", collectNearestManifestDependencies],
59654
- ["no-adjust-state-on-prop-change", collectEffectValueHelperDependencies],
59655
- ["no-derived-state", collectEffectValueHelperDependencies],
59656
- ["no-derived-state-effect", collectEffectValueHelperDependencies],
59657
- ["no-event-handler", collectEffectValueHelperDependencies],
59658
- ["no-initialize-state", collectEffectValueHelperDependencies],
59659
59059
  ["no-mutating-reducer-state", collectMutatingReducerDependencies],
59660
59060
  ["no-unguarded-browser-global-in-render-or-hook-init", collectNearestManifestDependencies],
59661
59061
  ["prefer-dynamic-import", collectNearestManifestDependencies],