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.d.ts +0 -46
- package/dist/index.js +271 -871
- package/package.json +1 -1
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
|
|
21073
|
+
const findReExportSourcesForName = (programRoot, exportedName) => {
|
|
21076
21074
|
if (!isNodeOfType(programRoot, "Program")) return [];
|
|
21077
|
-
const
|
|
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)
|
|
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")
|
|
21100
|
-
importedName: exportedName,
|
|
21101
|
-
source: sourceValue
|
|
21102
|
-
});
|
|
21088
|
+
if (typeof sourceValue === "string") exportAllSources.push(sourceValue);
|
|
21103
21089
|
}
|
|
21104
21090
|
}
|
|
21105
|
-
return
|
|
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
|
|
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
|
|
21503
|
-
|
|
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,
|
|
21507
|
-
if (resolved)
|
|
21651
|
+
const resolved = resolveFunctionExportInFile(nextFilePath, exportedName, visitedFilePaths);
|
|
21652
|
+
if (resolved) return resolved;
|
|
21508
21653
|
}
|
|
21509
|
-
|
|
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
|
|
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 (
|
|
23284
|
-
|
|
23285
|
-
|
|
23286
|
-
|
|
23287
|
-
|
|
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
|
|
23296
|
-
if (
|
|
23297
|
-
|
|
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
|
|
23318
|
-
|
|
23319
|
-
|
|
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
|
|
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
|
|
23412
|
-
const frames = collectBoundedEffectExecutionFrames(analysis, effectNode
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
34116
|
-
|
|
34117
|
-
|
|
34118
|
-
|
|
34119
|
-
|
|
34120
|
-
|
|
34121
|
-
|
|
34122
|
-
|
|
34123
|
-
|
|
34124
|
-
const
|
|
34125
|
-
if (!
|
|
34126
|
-
|
|
34127
|
-
if (!effectFn)
|
|
34128
|
-
|
|
34129
|
-
|
|
34130
|
-
|
|
34131
|
-
|
|
34132
|
-
if (
|
|
34133
|
-
|
|
34134
|
-
|
|
34135
|
-
|
|
34136
|
-
|
|
34137
|
-
|
|
34138
|
-
|
|
34139
|
-
|
|
34140
|
-
|
|
34141
|
-
|
|
34142
|
-
|
|
34143
|
-
|
|
34144
|
-
|
|
34145
|
-
|
|
34146
|
-
|
|
34147
|
-
|
|
34148
|
-
|
|
34149
|
-
|
|
34150
|
-
|
|
34151
|
-
|
|
34152
|
-
|
|
34153
|
-
|
|
34154
|
-
|
|
34155
|
-
|
|
34156
|
-
|
|
34157
|
-
|
|
34158
|
-
|
|
34159
|
-
|
|
34160
|
-
|
|
34161
|
-
|
|
34162
|
-
|
|
34163
|
-
|
|
34164
|
-
|
|
34165
|
-
if (
|
|
34166
|
-
if (
|
|
34167
|
-
|
|
34168
|
-
|
|
34169
|
-
|
|
34170
|
-
|
|
34171
|
-
|
|
34172
|
-
|
|
34173
|
-
|
|
34174
|
-
|
|
34175
|
-
|
|
34176
|
-
|
|
34177
|
-
|
|
34178
|
-
|
|
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],
|