react-doctor 0.7.2 → 0.7.3-dev.6b70b32
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/cli.js +741 -685
- package/dist/index.d.ts +14 -2
- package/dist/index.js +826 -748
- package/dist/lsp.js +862 -813
- package/package.json +6 -6
package/dist/lsp.js
CHANGED
|
@@ -32827,6 +32827,25 @@ var AmbiguousProjectError = class extends Error {
|
|
|
32827
32827
|
}
|
|
32828
32828
|
};
|
|
32829
32829
|
const isProjectDiscoveryError = (value) => value instanceof ProjectNotFoundError || value instanceof NoReactDependencyError || value instanceof PackageJsonNotFoundError || value instanceof NotADirectoryError || value instanceof AmbiguousProjectError;
|
|
32830
|
+
const isErrnoException = (error) => error instanceof Error && "code" in error;
|
|
32831
|
+
const IGNORABLE_READDIR_ERROR_CODES = new Set([
|
|
32832
|
+
"EACCES",
|
|
32833
|
+
"EPERM",
|
|
32834
|
+
"ENOENT",
|
|
32835
|
+
"ENOTDIR",
|
|
32836
|
+
"EINVAL",
|
|
32837
|
+
"ELOOP",
|
|
32838
|
+
"ENAMETOOLONG"
|
|
32839
|
+
]);
|
|
32840
|
+
const isIgnorableReaddirError = (error) => isErrnoException(error) && typeof error.code === "string" && IGNORABLE_READDIR_ERROR_CODES.has(error.code);
|
|
32841
|
+
const readDirectoryEntries = (directoryPath) => {
|
|
32842
|
+
try {
|
|
32843
|
+
return NFS.readdirSync(directoryPath, { withFileTypes: true });
|
|
32844
|
+
} catch (error) {
|
|
32845
|
+
if (isIgnorableReaddirError(error)) return [];
|
|
32846
|
+
throw error;
|
|
32847
|
+
}
|
|
32848
|
+
};
|
|
32830
32849
|
const isFile = (filePath) => {
|
|
32831
32850
|
try {
|
|
32832
32851
|
return NFS.statSync(filePath).isFile();
|
|
@@ -32834,6 +32853,18 @@ const isFile = (filePath) => {
|
|
|
32834
32853
|
return false;
|
|
32835
32854
|
}
|
|
32836
32855
|
};
|
|
32856
|
+
const isDirectory = (directoryPath) => {
|
|
32857
|
+
try {
|
|
32858
|
+
return NFS.statSync(directoryPath).isDirectory();
|
|
32859
|
+
} catch {
|
|
32860
|
+
return false;
|
|
32861
|
+
}
|
|
32862
|
+
};
|
|
32863
|
+
const isPlainObject = (value) => {
|
|
32864
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
32865
|
+
const prototype = Object.getPrototypeOf(value);
|
|
32866
|
+
return prototype === null || prototype === Object.prototype;
|
|
32867
|
+
};
|
|
32837
32868
|
const SOURCE_FILE_PATTERN = /\.(tsx?|jsx?|mts|mjs)$/;
|
|
32838
32869
|
const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
|
|
32839
32870
|
const GENERATED_SOURCE_DIRECTORY_PATTERN = /(?:^|\/)__generated__\//;
|
|
@@ -32958,6 +32989,8 @@ const OXLINT_SPAWN_TIMEOUT_MS = 6e4;
|
|
|
32958
32989
|
const NODE_COMPILE_CACHE_DIR_NAME = "node-compile-cache";
|
|
32959
32990
|
const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
|
|
32960
32991
|
const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
|
|
32992
|
+
const ABORT_EXIT_CODES = new Set([134, 3221226505]);
|
|
32993
|
+
const OXLINT_OOM_RESCUE_BUDGET_MS = 6e4;
|
|
32961
32994
|
const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
|
|
32962
32995
|
const LINT_PHASE_TIMEOUT_MS = 3e5;
|
|
32963
32996
|
const SCAN_TOTAL_DEADLINE_MS = 9e5;
|
|
@@ -33004,7 +33037,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
33004
33037
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
33005
33038
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
33006
33039
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
33007
|
-
const CORE_PACKAGE_VERSION = "0.7.
|
|
33040
|
+
const CORE_PACKAGE_VERSION = "0.7.3";
|
|
33008
33041
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
33009
33042
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
33010
33043
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -33053,25 +33086,6 @@ const isLargeMinifiedFile = (absolutePath, knownSizeBytes) => {
|
|
|
33053
33086
|
cachedIsLargeMinifiedByPath.set(absolutePath, result);
|
|
33054
33087
|
return result;
|
|
33055
33088
|
};
|
|
33056
|
-
const isErrnoException = (error) => error instanceof Error && "code" in error;
|
|
33057
|
-
const IGNORABLE_READDIR_ERROR_CODES = new Set([
|
|
33058
|
-
"EACCES",
|
|
33059
|
-
"EPERM",
|
|
33060
|
-
"ENOENT",
|
|
33061
|
-
"ENOTDIR",
|
|
33062
|
-
"EINVAL",
|
|
33063
|
-
"ELOOP",
|
|
33064
|
-
"ENAMETOOLONG"
|
|
33065
|
-
]);
|
|
33066
|
-
const isIgnorableReaddirError = (error) => isErrnoException(error) && typeof error.code === "string" && IGNORABLE_READDIR_ERROR_CODES.has(error.code);
|
|
33067
|
-
const readDirectoryEntries = (directoryPath) => {
|
|
33068
|
-
try {
|
|
33069
|
-
return NFS.readdirSync(directoryPath, { withFileTypes: true });
|
|
33070
|
-
} catch (error) {
|
|
33071
|
-
if (isIgnorableReaddirError(error)) return [];
|
|
33072
|
-
throw error;
|
|
33073
|
-
}
|
|
33074
|
-
};
|
|
33075
33089
|
function* walkSourceTreeFiles(rootDirectory) {
|
|
33076
33090
|
const stack = [rootDirectory];
|
|
33077
33091
|
while (stack.length > 0) {
|
|
@@ -33126,23 +33140,6 @@ const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
|
33126
33140
|
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort());
|
|
33127
33141
|
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
33128
33142
|
const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
|
|
33129
|
-
/**
|
|
33130
|
-
* The chain of ancestor directories of `startDirectory`, nearest first, up to
|
|
33131
|
-
* and including the filesystem root. With `includeStart`, the chain begins at
|
|
33132
|
-
* `startDirectory` itself; otherwise at its parent. The one place the "walk up
|
|
33133
|
-
* the tree" traversal is expressed, so each caller adds only its own match /
|
|
33134
|
-
* stop policy instead of re-deriving the `path.dirname` loop.
|
|
33135
|
-
*/
|
|
33136
|
-
const ancestorDirectories = (startDirectory, { includeStart }) => {
|
|
33137
|
-
const directories = [];
|
|
33138
|
-
let currentDirectory = includeStart ? startDirectory : Path.dirname(startDirectory);
|
|
33139
|
-
while (true) {
|
|
33140
|
-
directories.push(currentDirectory);
|
|
33141
|
-
const parentDirectory = Path.dirname(currentDirectory);
|
|
33142
|
-
if (parentDirectory === currentDirectory) return directories;
|
|
33143
|
-
currentDirectory = parentDirectory;
|
|
33144
|
-
}
|
|
33145
|
-
};
|
|
33146
33143
|
const cachedPackageJsons = /* @__PURE__ */ new Map();
|
|
33147
33144
|
const clearPackageJsonCache = () => {
|
|
33148
33145
|
cachedPackageJsons.clear();
|
|
@@ -33176,7 +33173,11 @@ const isMonorepoRoot = (directory) => {
|
|
|
33176
33173
|
return Array.isArray(packageJson.workspaces) || Boolean(packageJson.workspaces?.packages);
|
|
33177
33174
|
};
|
|
33178
33175
|
const findMonorepoRoot = (startDirectory) => {
|
|
33179
|
-
|
|
33176
|
+
let currentDirectory = Path.dirname(startDirectory);
|
|
33177
|
+
while (currentDirectory !== Path.dirname(currentDirectory)) {
|
|
33178
|
+
if (isMonorepoRoot(currentDirectory)) return currentDirectory;
|
|
33179
|
+
currentDirectory = Path.dirname(currentDirectory);
|
|
33180
|
+
}
|
|
33180
33181
|
return null;
|
|
33181
33182
|
};
|
|
33182
33183
|
/**
|
|
@@ -33190,6 +33191,146 @@ const findMonorepoRoot = (startDirectory) => {
|
|
|
33190
33191
|
* byte-equivalent copy.
|
|
33191
33192
|
*/
|
|
33192
33193
|
const isProjectBoundary = (directory) => NFS.existsSync(Path.join(directory, ".git")) || isMonorepoRoot(directory);
|
|
33194
|
+
const TSCONFIG_FILENAME = "tsconfig.json";
|
|
33195
|
+
const isRelativeExtendsValue = (extendsValue) => extendsValue.startsWith("./") || extendsValue.startsWith("../") || Path.isAbsolute(extendsValue);
|
|
33196
|
+
const ensureJsonExtension = (filePath) => Path.extname(filePath) === "" ? `${filePath}.json` : filePath;
|
|
33197
|
+
const resolvePackageExtendsPath = (extendsValue, fromConfigDirectory) => {
|
|
33198
|
+
const requireFromConfig = createRequire(Path.join(fromConfigDirectory, "tsconfig.json"));
|
|
33199
|
+
const candidates = [
|
|
33200
|
+
extendsValue,
|
|
33201
|
+
ensureJsonExtension(extendsValue),
|
|
33202
|
+
`${extendsValue.replace(/\/$/, "")}/tsconfig.json`
|
|
33203
|
+
];
|
|
33204
|
+
for (const candidate of candidates) try {
|
|
33205
|
+
return requireFromConfig.resolve(candidate);
|
|
33206
|
+
} catch {
|
|
33207
|
+
continue;
|
|
33208
|
+
}
|
|
33209
|
+
return null;
|
|
33210
|
+
};
|
|
33211
|
+
const resolveExtendsPath = (extendsValue, fromConfigDirectory) => {
|
|
33212
|
+
if (isRelativeExtendsValue(extendsValue)) return ensureJsonExtension(Path.resolve(fromConfigDirectory, extendsValue));
|
|
33213
|
+
return resolvePackageExtendsPath(extendsValue, fromConfigDirectory);
|
|
33214
|
+
};
|
|
33215
|
+
const normalizeCompilerOptions = (compilerOptions) => {
|
|
33216
|
+
if (!isPlainObject(compilerOptions)) return { hasExplicitLib: false };
|
|
33217
|
+
const target = typeof compilerOptions.target === "string" ? compilerOptions.target : void 0;
|
|
33218
|
+
const hasExplicitLib = Object.hasOwn(compilerOptions, "lib");
|
|
33219
|
+
return {
|
|
33220
|
+
target,
|
|
33221
|
+
lib: Array.isArray(compilerOptions.lib) ? compilerOptions.lib.filter((entry) => typeof entry === "string") : void 0,
|
|
33222
|
+
hasExplicitLib
|
|
33223
|
+
};
|
|
33224
|
+
};
|
|
33225
|
+
const readTsConfig = (filePath) => {
|
|
33226
|
+
let content;
|
|
33227
|
+
try {
|
|
33228
|
+
content = NFS.readFileSync(filePath, "utf-8");
|
|
33229
|
+
} catch {
|
|
33230
|
+
return null;
|
|
33231
|
+
}
|
|
33232
|
+
const parsed = ts.parseConfigFileTextToJson(filePath, content);
|
|
33233
|
+
if (!isPlainObject(parsed.config)) return null;
|
|
33234
|
+
return {
|
|
33235
|
+
extends: typeof parsed.config.extends === "string" ? parsed.config.extends : void 0,
|
|
33236
|
+
referencePaths: normalizeReferencePaths(parsed.config.references),
|
|
33237
|
+
compilerOptions: normalizeCompilerOptions(parsed.config.compilerOptions)
|
|
33238
|
+
};
|
|
33239
|
+
};
|
|
33240
|
+
const normalizeReferencePaths = (references) => {
|
|
33241
|
+
if (!Array.isArray(references)) return [];
|
|
33242
|
+
return references.map((reference) => isPlainObject(reference) && typeof reference.path === "string" ? reference.path : null).filter((referencePath) => referencePath !== null);
|
|
33243
|
+
};
|
|
33244
|
+
const mergeCompilerOptions = (inherited, current) => {
|
|
33245
|
+
const target = current.target ?? inherited?.target;
|
|
33246
|
+
const hasExplicitLib = current.hasExplicitLib || Boolean(inherited?.hasExplicitLib);
|
|
33247
|
+
return {
|
|
33248
|
+
target,
|
|
33249
|
+
lib: current.hasExplicitLib ? current.lib : inherited?.lib,
|
|
33250
|
+
hasExplicitLib
|
|
33251
|
+
};
|
|
33252
|
+
};
|
|
33253
|
+
const readResolvedCompilerOptions = (tsConfigPath, extendsDepth, visitedPaths) => {
|
|
33254
|
+
const realPath = NFS.realpathSync.native(tsConfigPath);
|
|
33255
|
+
if (visitedPaths.has(realPath)) return null;
|
|
33256
|
+
const tsConfig = readTsConfig(realPath);
|
|
33257
|
+
if (!tsConfig) return null;
|
|
33258
|
+
const nextVisitedPaths = new Set(visitedPaths);
|
|
33259
|
+
nextVisitedPaths.add(realPath);
|
|
33260
|
+
if (tsConfig.extends && extendsDepth < 8) {
|
|
33261
|
+
const parentPath = resolveExtendsPath(tsConfig.extends, Path.dirname(realPath));
|
|
33262
|
+
if (parentPath && isFile(parentPath)) return mergeCompilerOptions(readResolvedCompilerOptions(parentPath, extendsDepth + 1, nextVisitedPaths), tsConfig.compilerOptions);
|
|
33263
|
+
}
|
|
33264
|
+
return tsConfig.compilerOptions;
|
|
33265
|
+
};
|
|
33266
|
+
const targetYearIsPreES2023 = (target) => {
|
|
33267
|
+
const year = ES_TARGET_YEAR_BY_NAME[target.toLowerCase()];
|
|
33268
|
+
return year !== void 0 && year < 2023;
|
|
33269
|
+
};
|
|
33270
|
+
const libEntryIncludesES2023Array = (entry) => {
|
|
33271
|
+
const normalizedEntry = entry.toLowerCase();
|
|
33272
|
+
if (normalizedEntry === "esnext" || normalizedEntry === "esnext.array") return true;
|
|
33273
|
+
const esYearMatch = /^es(\d{4})(?:\.(.+))?$/.exec(normalizedEntry);
|
|
33274
|
+
if (!esYearMatch) return false;
|
|
33275
|
+
if (Number(esYearMatch[1]) < 2023) return false;
|
|
33276
|
+
const component = esYearMatch[2];
|
|
33277
|
+
return component === void 0 || component === "array";
|
|
33278
|
+
};
|
|
33279
|
+
const libIncludesES2023 = (lib) => lib.some(libEntryIncludesES2023Array);
|
|
33280
|
+
const compilerOptionsArePreES2023 = (compilerOptions) => {
|
|
33281
|
+
if (compilerOptions.target) return targetYearIsPreES2023(compilerOptions.target);
|
|
33282
|
+
if (compilerOptions.hasExplicitLib) return !libIncludesES2023(compilerOptions.lib ?? []);
|
|
33283
|
+
return false;
|
|
33284
|
+
};
|
|
33285
|
+
const compilerOptionsDeclareTargetOrLib = (compilerOptions) => compilerOptions.hasExplicitLib || compilerOptions.target !== void 0;
|
|
33286
|
+
const detectPreES2023FromConfig = (tsConfigPath, visitedConfigPaths = /* @__PURE__ */ new Set()) => {
|
|
33287
|
+
if (visitedConfigPaths.has(tsConfigPath)) return false;
|
|
33288
|
+
const compilerOptions = readResolvedCompilerOptions(tsConfigPath, 0, /* @__PURE__ */ new Set());
|
|
33289
|
+
if (!compilerOptions) return false;
|
|
33290
|
+
if (!compilerOptionsDeclareTargetOrLib(compilerOptions)) {
|
|
33291
|
+
const tsConfig = readTsConfig(tsConfigPath);
|
|
33292
|
+
if (!tsConfig) return false;
|
|
33293
|
+
const nextVisitedConfigPaths = new Set(visitedConfigPaths);
|
|
33294
|
+
nextVisitedConfigPaths.add(tsConfigPath);
|
|
33295
|
+
const configDirectory = Path.dirname(tsConfigPath);
|
|
33296
|
+
return tsConfig.referencePaths.some((referencePath) => {
|
|
33297
|
+
const resolvedReferencePath = Path.resolve(configDirectory, referencePath);
|
|
33298
|
+
const referencedConfigPath = isFile(resolvedReferencePath) ? resolvedReferencePath : Path.join(resolvedReferencePath, TSCONFIG_FILENAME);
|
|
33299
|
+
return isFile(referencedConfigPath) && detectPreES2023FromConfig(referencedConfigPath, nextVisitedConfigPaths);
|
|
33300
|
+
});
|
|
33301
|
+
}
|
|
33302
|
+
return compilerOptionsArePreES2023(compilerOptions);
|
|
33303
|
+
};
|
|
33304
|
+
const detectPreES2023Target = (directory) => {
|
|
33305
|
+
const tsConfigPath = Path.join(directory, TSCONFIG_FILENAME);
|
|
33306
|
+
if (isFile(tsConfigPath)) return detectPreES2023FromConfig(tsConfigPath);
|
|
33307
|
+
for (const fallbackFilename of FALLBACK_TSCONFIG_FILENAMES) {
|
|
33308
|
+
const fallbackPath = Path.join(directory, fallbackFilename);
|
|
33309
|
+
if (isFile(fallbackPath)) return detectPreES2023FromConfig(fallbackPath);
|
|
33310
|
+
}
|
|
33311
|
+
return false;
|
|
33312
|
+
};
|
|
33313
|
+
const FALLBACK_TSCONFIG_FILENAMES = ["tsconfig.app.json", "tsconfig.build.json"];
|
|
33314
|
+
const FRAMEWORK_PACKAGES = {
|
|
33315
|
+
next: "nextjs",
|
|
33316
|
+
"@tanstack/react-start": "tanstack-start",
|
|
33317
|
+
vite: "vite",
|
|
33318
|
+
"react-scripts": "cra",
|
|
33319
|
+
"@remix-run/react": "remix",
|
|
33320
|
+
gatsby: "gatsby",
|
|
33321
|
+
expo: "expo",
|
|
33322
|
+
"react-native": "react-native"
|
|
33323
|
+
};
|
|
33324
|
+
const detectFramework = (dependencies) => {
|
|
33325
|
+
for (const [packageName, frameworkName] of Object.entries(FRAMEWORK_PACKAGES)) if (dependencies[packageName]) return frameworkName;
|
|
33326
|
+
if (dependencies.preact && !dependencies.react) return "preact";
|
|
33327
|
+
return "unknown";
|
|
33328
|
+
};
|
|
33329
|
+
const MOBILE_FRAMEWORKS = new Set(["expo", "react-native"]);
|
|
33330
|
+
const frameworkMergeRank = (framework) => {
|
|
33331
|
+
if (framework === "unknown") return 3;
|
|
33332
|
+
return MOBILE_FRAMEWORKS.has(framework) ? 2 : 1;
|
|
33333
|
+
};
|
|
33193
33334
|
const REACT_COMPILER_PACKAGES = new Set([
|
|
33194
33335
|
"babel-plugin-react-compiler",
|
|
33195
33336
|
"react-compiler-runtime",
|
|
@@ -33226,6 +33367,7 @@ const EXPO_APP_CONFIG_FILENAMES = [
|
|
|
33226
33367
|
];
|
|
33227
33368
|
const REACT_COMPILER_PACKAGE_REFERENCE_PATTERN = /babel-plugin-react-compiler|react-compiler-runtime|eslint-plugin-react-compiler|["']react-compiler["']/;
|
|
33228
33369
|
const REACT_COMPILER_ENABLED_FLAG_PATTERN = /["']?reactCompiler["']?\s*:\s*(?:true\b|\{)/;
|
|
33370
|
+
const STATIC_EXPORT_OUTPUT_PATTERN = /(?:^|[^.\w])["']?output["']?\s*:\s*["']export["']/m;
|
|
33229
33371
|
const hasCompilerPackage = (packageJson) => {
|
|
33230
33372
|
const allDependencies = {
|
|
33231
33373
|
...packageJson.peerDependencies,
|
|
@@ -33258,34 +33400,10 @@ const detectReactCompiler = (directory, packageJson) => {
|
|
|
33258
33400
|
}
|
|
33259
33401
|
return false;
|
|
33260
33402
|
};
|
|
33261
|
-
const
|
|
33262
|
-
|
|
33263
|
-
|
|
33264
|
-
|
|
33265
|
-
"react-scripts": "cra",
|
|
33266
|
-
"@remix-run/react": "remix",
|
|
33267
|
-
gatsby: "gatsby",
|
|
33268
|
-
expo: "expo",
|
|
33269
|
-
"react-native": "react-native"
|
|
33270
|
-
};
|
|
33271
|
-
const FRAMEWORK_DISPLAY_NAMES = {
|
|
33272
|
-
nextjs: "Next.js",
|
|
33273
|
-
"tanstack-start": "TanStack Start",
|
|
33274
|
-
vite: "Vite",
|
|
33275
|
-
cra: "Create React App",
|
|
33276
|
-
remix: "Remix",
|
|
33277
|
-
gatsby: "Gatsby",
|
|
33278
|
-
expo: "Expo",
|
|
33279
|
-
"react-native": "React Native",
|
|
33280
|
-
preact: "Preact",
|
|
33281
|
-
unknown: "React"
|
|
33282
|
-
};
|
|
33283
|
-
const formatFrameworkName = (framework) => FRAMEWORK_DISPLAY_NAMES[framework];
|
|
33284
|
-
const detectFramework = (dependencies) => {
|
|
33285
|
-
for (const [packageName, frameworkName] of Object.entries(FRAMEWORK_PACKAGES)) if (dependencies[packageName]) return frameworkName;
|
|
33286
|
-
if (dependencies.preact && !dependencies.react) return "preact";
|
|
33287
|
-
return "unknown";
|
|
33288
|
-
};
|
|
33403
|
+
const detectNextjsStaticExport = (directory) => NEXT_CONFIG_FILENAMES.some((filename) => {
|
|
33404
|
+
const filePath = Path.join(directory, filename);
|
|
33405
|
+
return isFile(filePath) && STATIC_EXPORT_OUTPUT_PATTERN.test(NFS.readFileSync(filePath, "utf-8"));
|
|
33406
|
+
});
|
|
33289
33407
|
const UNRESOLVABLE_PROTOCOL_VERSION = /^(?:file|git|github|https?|link|patch|portal|workspace|npm):/i;
|
|
33290
33408
|
const DIST_TAG_VERSION = /^[a-z][a-z0-9._-]*$/i;
|
|
33291
33409
|
const WILDCARD_VERSION = /^[*xX](?:\.[*xX])*$/;
|
|
@@ -33423,39 +33541,105 @@ const isConcreteDependencyVersion = (version) => {
|
|
|
33423
33541
|
const normalizedVersion = normalizeDependencyVersion(version);
|
|
33424
33542
|
return normalizedVersion !== null && /\d/.test(normalizedVersion);
|
|
33425
33543
|
};
|
|
33426
|
-
const
|
|
33427
|
-
if (
|
|
33428
|
-
|
|
33429
|
-
return
|
|
33430
|
-
};
|
|
33431
|
-
const isCatalogReference = (version) => version.startsWith("catalog:");
|
|
33432
|
-
const extractCatalogName = (version) => {
|
|
33433
|
-
if (!isCatalogReference(version)) return null;
|
|
33434
|
-
const name = version.slice(8).trim();
|
|
33435
|
-
return name.length > 0 ? name : null;
|
|
33544
|
+
const isMajorMinorAtLeast = (detected, required) => {
|
|
33545
|
+
if (detected === null) return true;
|
|
33546
|
+
if (detected.major !== required.major) return detected.major > required.major;
|
|
33547
|
+
return detected.minor >= required.minor;
|
|
33436
33548
|
};
|
|
33437
|
-
const
|
|
33438
|
-
|
|
33439
|
-
|
|
33440
|
-
return null;
|
|
33549
|
+
const parseReactMajor = (reactVersion) => {
|
|
33550
|
+
if (typeof reactVersion !== "string") return null;
|
|
33551
|
+
return getLowestDependencyMajor(reactVersion);
|
|
33441
33552
|
};
|
|
33442
|
-
const
|
|
33443
|
-
|
|
33444
|
-
|
|
33445
|
-
|
|
33446
|
-
|
|
33447
|
-
|
|
33448
|
-
|
|
33449
|
-
const
|
|
33450
|
-
|
|
33451
|
-
|
|
33452
|
-
|
|
33453
|
-
|
|
33454
|
-
const
|
|
33455
|
-
if (
|
|
33456
|
-
|
|
33457
|
-
|
|
33458
|
-
|
|
33553
|
+
const MAJOR_MINOR_PATTERN = /(\d{1,4})\.(\d{1,4})/;
|
|
33554
|
+
const MAJOR_ONLY_PATTERN = /(\d{1,4})/;
|
|
33555
|
+
const UPPER_BOUND_COMPARATOR_PATTERN = /<=?\s{0,8}\d{1,4}(?:\.\d{1,4}){0,2}(?:-[^\s,|]+)?/g;
|
|
33556
|
+
const parseReactMajorMinor = (reactVersion) => {
|
|
33557
|
+
if (typeof reactVersion !== "string") return null;
|
|
33558
|
+
const trimmed = reactVersion.trim();
|
|
33559
|
+
if (trimmed.length === 0) return null;
|
|
33560
|
+
const lowerBoundsOnly = trimmed.replace(UPPER_BOUND_COMPARATOR_PATTERN, " ").trim();
|
|
33561
|
+
if (lowerBoundsOnly.length === 0) return null;
|
|
33562
|
+
const majorMinorMatch = lowerBoundsOnly.match(MAJOR_MINOR_PATTERN);
|
|
33563
|
+
if (majorMinorMatch) {
|
|
33564
|
+
const major = Number.parseInt(majorMinorMatch[1], 10);
|
|
33565
|
+
const minor = Number.parseInt(majorMinorMatch[2], 10);
|
|
33566
|
+
if (!Number.isFinite(major) || major <= 0) return null;
|
|
33567
|
+
if (!Number.isFinite(minor) || minor < 0) return null;
|
|
33568
|
+
return {
|
|
33569
|
+
major,
|
|
33570
|
+
minor
|
|
33571
|
+
};
|
|
33572
|
+
}
|
|
33573
|
+
const majorOnlyMatch = lowerBoundsOnly.match(MAJOR_ONLY_PATTERN);
|
|
33574
|
+
if (!majorOnlyMatch) return null;
|
|
33575
|
+
const major = Number.parseInt(majorOnlyMatch[1], 10);
|
|
33576
|
+
if (!Number.isFinite(major) || major <= 0) return null;
|
|
33577
|
+
return {
|
|
33578
|
+
major,
|
|
33579
|
+
minor: 0
|
|
33580
|
+
};
|
|
33581
|
+
};
|
|
33582
|
+
const parseLowerBoundVersion = (versionSpec) => import_semver.validRange(versionSpec) !== null ? import_semver.minVersion(versionSpec) : import_semver.coerce(versionSpec);
|
|
33583
|
+
const parseTailwindMajorMinor = (tailwindVersion) => {
|
|
33584
|
+
if (typeof tailwindVersion !== "string") return null;
|
|
33585
|
+
const trimmed = tailwindVersion.trim();
|
|
33586
|
+
if (trimmed.length === 0) return null;
|
|
33587
|
+
const lowerBound = parseLowerBoundVersion(trimmed);
|
|
33588
|
+
if (lowerBound === null || lowerBound.major <= 0) return null;
|
|
33589
|
+
return {
|
|
33590
|
+
major: lowerBound.major,
|
|
33591
|
+
minor: lowerBound.minor
|
|
33592
|
+
};
|
|
33593
|
+
};
|
|
33594
|
+
const hasUpperBoundOnlyPeerRange = (range) => {
|
|
33595
|
+
if (typeof range !== "string") return false;
|
|
33596
|
+
const normalizedRange = normalizeDependencyVersion(range);
|
|
33597
|
+
if (normalizedRange === null) return false;
|
|
33598
|
+
return splitDependencyVersionBranches(normalizedRange).some((branch) => {
|
|
33599
|
+
const normalizedBranch = normalizeDependencyVersion(branch);
|
|
33600
|
+
return normalizedBranch !== null && getBranchLowestMajor(normalizedBranch) === null && hasUpperBoundComparator(normalizedBranch);
|
|
33601
|
+
});
|
|
33602
|
+
};
|
|
33603
|
+
const peerRangeMinMajor = (range) => {
|
|
33604
|
+
if (typeof range !== "string") return null;
|
|
33605
|
+
return getLowestDependencyMajor(range);
|
|
33606
|
+
};
|
|
33607
|
+
const resolveEffectiveReactMajor = (reactVersion, packageJson) => {
|
|
33608
|
+
const installedReactMajor = parseReactMajor(reactVersion);
|
|
33609
|
+
const peerReactRange = packageJson.peerDependencies?.react;
|
|
33610
|
+
if (typeof peerReactRange !== "string") return installedReactMajor;
|
|
33611
|
+
const peerFloor = peerRangeMinMajor(peerReactRange);
|
|
33612
|
+
if (peerFloor === null) return hasUpperBoundOnlyPeerRange(peerReactRange) ? null : installedReactMajor;
|
|
33613
|
+
return installedReactMajor !== null ? Math.min(installedReactMajor, peerFloor) : peerFloor;
|
|
33614
|
+
};
|
|
33615
|
+
const isCatalogReference = (version) => version.startsWith("catalog:");
|
|
33616
|
+
const extractCatalogName = (version) => {
|
|
33617
|
+
if (!isCatalogReference(version)) return null;
|
|
33618
|
+
const name = version.slice(8).trim();
|
|
33619
|
+
return name.length > 0 ? name : null;
|
|
33620
|
+
};
|
|
33621
|
+
const resolveVersionFromCatalog = (catalog, packageName) => {
|
|
33622
|
+
const version = catalog[packageName];
|
|
33623
|
+
if (typeof version === "string" && !isCatalogReference(version)) return version;
|
|
33624
|
+
return null;
|
|
33625
|
+
};
|
|
33626
|
+
const parsePnpmWorkspaceCatalogs = (rootDirectory) => {
|
|
33627
|
+
const workspacePath = Path.join(rootDirectory, "pnpm-workspace.yaml");
|
|
33628
|
+
if (!isFile(workspacePath)) return {
|
|
33629
|
+
defaultCatalog: {},
|
|
33630
|
+
namedCatalogs: {}
|
|
33631
|
+
};
|
|
33632
|
+
const content = NFS.readFileSync(workspacePath, "utf-8");
|
|
33633
|
+
const defaultCatalog = {};
|
|
33634
|
+
const namedCatalogs = {};
|
|
33635
|
+
let currentSection = "none";
|
|
33636
|
+
let currentCatalogName = "";
|
|
33637
|
+
for (const line of content.split("\n")) {
|
|
33638
|
+
const trimmed = line.trim();
|
|
33639
|
+
if (trimmed.length === 0 || trimmed.startsWith("#")) continue;
|
|
33640
|
+
const indentLevel = line.search(/\S/);
|
|
33641
|
+
if (indentLevel === 0 && trimmed === "catalog:") {
|
|
33642
|
+
currentSection = "catalog";
|
|
33459
33643
|
continue;
|
|
33460
33644
|
}
|
|
33461
33645
|
if (indentLevel === 0 && trimmed === "catalogs:") {
|
|
@@ -33568,6 +33752,16 @@ const resolveCatalogVersion = (packageJson, packageName, rootDirectory, explicit
|
|
|
33568
33752
|
}
|
|
33569
33753
|
return null;
|
|
33570
33754
|
};
|
|
33755
|
+
const REACT_SECTIONS = [
|
|
33756
|
+
"dependencies",
|
|
33757
|
+
"peerDependencies",
|
|
33758
|
+
"devDependencies"
|
|
33759
|
+
];
|
|
33760
|
+
const TAILWIND_ZOD_SECTIONS = [
|
|
33761
|
+
"dependencies",
|
|
33762
|
+
"devDependencies",
|
|
33763
|
+
"peerDependencies"
|
|
33764
|
+
];
|
|
33571
33765
|
const EMPTY_DEPENDENCY_INFO = {
|
|
33572
33766
|
reactVersion: null,
|
|
33573
33767
|
tailwindVersion: null,
|
|
@@ -33608,6 +33802,10 @@ const extractDependencyInfo = (packageJson) => {
|
|
|
33608
33802
|
framework: detectFramework(allDependencies)
|
|
33609
33803
|
};
|
|
33610
33804
|
};
|
|
33805
|
+
const getDependencySpec = (packageJson, packageName) => {
|
|
33806
|
+
const spec = packageJson.dependencies?.[packageName] ?? packageJson.devDependencies?.[packageName] ?? packageJson.peerDependencies?.[packageName] ?? packageJson.optionalDependencies?.[packageName];
|
|
33807
|
+
return typeof spec === "string" ? spec : null;
|
|
33808
|
+
};
|
|
33611
33809
|
const getDependencyDeclaration = ({ packageJson, packageName, sections }) => {
|
|
33612
33810
|
for (const section of sections) {
|
|
33613
33811
|
const version = packageJson[section]?.[packageName];
|
|
@@ -33624,31 +33822,141 @@ const getDependencyDeclaration = ({ packageJson, packageName, sections }) => {
|
|
|
33624
33822
|
version: null
|
|
33625
33823
|
};
|
|
33626
33824
|
};
|
|
33627
|
-
const
|
|
33628
|
-
|
|
33629
|
-
|
|
33630
|
-
|
|
33631
|
-
|
|
33825
|
+
const REACT_DEPENDENCY_NAMES = new Set([
|
|
33826
|
+
"react",
|
|
33827
|
+
"react-native",
|
|
33828
|
+
"next",
|
|
33829
|
+
"preact"
|
|
33830
|
+
]);
|
|
33831
|
+
const hasReactDependency = (packageJson) => {
|
|
33832
|
+
const allDependencies = {
|
|
33833
|
+
...packageJson.peerDependencies,
|
|
33834
|
+
...packageJson.dependencies,
|
|
33835
|
+
...packageJson.devDependencies
|
|
33836
|
+
};
|
|
33837
|
+
return Object.keys(allDependencies).some((packageName) => REACT_DEPENDENCY_NAMES.has(packageName));
|
|
33838
|
+
};
|
|
33839
|
+
const getPreactVersion = (packageJson) => {
|
|
33840
|
+
return {
|
|
33841
|
+
...packageJson.peerDependencies,
|
|
33842
|
+
...packageJson.dependencies,
|
|
33843
|
+
...packageJson.devDependencies
|
|
33844
|
+
}.preact ?? null;
|
|
33845
|
+
};
|
|
33846
|
+
const TANSTACK_QUERY_PACKAGES = new Set([
|
|
33847
|
+
"@tanstack/react-query",
|
|
33848
|
+
"@tanstack/query-core",
|
|
33849
|
+
"react-query"
|
|
33850
|
+
]);
|
|
33851
|
+
const hasTanStackQuery = (packageJson) => {
|
|
33852
|
+
const allDependencies = {
|
|
33853
|
+
...packageJson.peerDependencies,
|
|
33854
|
+
...packageJson.dependencies,
|
|
33855
|
+
...packageJson.devDependencies
|
|
33856
|
+
};
|
|
33857
|
+
return Object.keys(allDependencies).some((packageName) => TANSTACK_QUERY_PACKAGES.has(packageName));
|
|
33858
|
+
};
|
|
33859
|
+
const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, version }) => {
|
|
33860
|
+
if (version === null || !isCatalogReference(version)) return version;
|
|
33861
|
+
const catalogName = extractCatalogName(version);
|
|
33862
|
+
const resolvedLocalVersion = resolveCatalogVersion(rootPackageJson, packageName, rootDirectory, catalogName);
|
|
33863
|
+
if (resolvedLocalVersion) return resolvedLocalVersion;
|
|
33864
|
+
const monorepoRoot = findMonorepoRoot(rootDirectory);
|
|
33865
|
+
if (!monorepoRoot) return version;
|
|
33866
|
+
const monorepoPackageJsonPath = Path.join(monorepoRoot, "package.json");
|
|
33867
|
+
if (!isFile(monorepoPackageJsonPath)) return version;
|
|
33868
|
+
return resolveCatalogVersion(readPackageJson(monorepoPackageJsonPath), packageName, monorepoRoot, catalogName) ?? version;
|
|
33869
|
+
};
|
|
33870
|
+
/**
|
|
33871
|
+
* The chain of ancestor directories of `startDirectory`, nearest first, up to
|
|
33872
|
+
* and including the filesystem root. With `includeStart`, the chain begins at
|
|
33873
|
+
* `startDirectory` itself; otherwise at its parent. The one place the "walk up
|
|
33874
|
+
* the tree" traversal is expressed, so each caller adds only its own match /
|
|
33875
|
+
* stop policy instead of re-deriving the `path.dirname` loop.
|
|
33876
|
+
*/
|
|
33877
|
+
const ancestorDirectories = (startDirectory, { includeStart }) => {
|
|
33878
|
+
const directories = [];
|
|
33879
|
+
let currentDirectory = includeStart ? startDirectory : Path.dirname(startDirectory);
|
|
33880
|
+
while (true) {
|
|
33881
|
+
directories.push(currentDirectory);
|
|
33882
|
+
const parentDirectory = Path.dirname(currentDirectory);
|
|
33883
|
+
if (parentDirectory === currentDirectory) return directories;
|
|
33884
|
+
currentDirectory = parentDirectory;
|
|
33632
33885
|
}
|
|
33633
33886
|
};
|
|
33634
|
-
|
|
33635
|
-
|
|
33636
|
-
|
|
33637
|
-
|
|
33638
|
-
|
|
33639
|
-
|
|
33640
|
-
|
|
33641
|
-
|
|
33642
|
-
|
|
33643
|
-
|
|
33644
|
-
|
|
33645
|
-
|
|
33646
|
-
|
|
33647
|
-
|
|
33648
|
-
|
|
33649
|
-
|
|
33887
|
+
/**
|
|
33888
|
+
* Walk up from `startDirectory` to the nearest ancestor that owns a
|
|
33889
|
+
* `package.json`, stopping at (and including) the enclosing project boundary
|
|
33890
|
+
* — the working tree's git root or a monorepo root. Lets a scan of a package
|
|
33891
|
+
* subfolder that has no `package.json` of its own adopt the nearest enclosing
|
|
33892
|
+
* package as its project root, so it inherits that package's dependency +
|
|
33893
|
+
* framework detection instead of synthesizing an empty (React-blind) project.
|
|
33894
|
+
*
|
|
33895
|
+
* Generalizes the older monorepo-root-only lookup: the nearest ancestor is the
|
|
33896
|
+
* most specific owning package (a leaf workspace, not just the repo root), so
|
|
33897
|
+
* a subfolder of one workspace no longer borrows a sibling's capabilities.
|
|
33898
|
+
*
|
|
33899
|
+
* Returns `null` when no ancestor `package.json` exists inside the project
|
|
33900
|
+
* boundary (a loose tree of files, or a subfolder of a package-less repo).
|
|
33901
|
+
*/
|
|
33902
|
+
const findNearestAncestorPackageJson = (startDirectory) => {
|
|
33903
|
+
if (isProjectBoundary(startDirectory)) return null;
|
|
33904
|
+
for (const directory of ancestorDirectories(startDirectory, { includeStart: false })) {
|
|
33905
|
+
if (isFile(Path.join(directory, "package.json"))) return directory;
|
|
33906
|
+
if (isProjectBoundary(directory)) return null;
|
|
33650
33907
|
}
|
|
33651
|
-
return
|
|
33908
|
+
return null;
|
|
33909
|
+
};
|
|
33910
|
+
const NAMES = new Set([
|
|
33911
|
+
"react-native",
|
|
33912
|
+
"react-native-tvos",
|
|
33913
|
+
...new Set([
|
|
33914
|
+
"expo",
|
|
33915
|
+
"expo-router",
|
|
33916
|
+
"@expo/cli",
|
|
33917
|
+
"@expo/metro-config",
|
|
33918
|
+
"@expo/metro-runtime"
|
|
33919
|
+
]),
|
|
33920
|
+
"react-native-windows",
|
|
33921
|
+
"react-native-macos"
|
|
33922
|
+
]);
|
|
33923
|
+
const PREFIXES = ["@react-native/", "@react-native-"];
|
|
33924
|
+
const isReactNativeDependencyName = (dependencyName) => {
|
|
33925
|
+
if (NAMES.has(dependencyName)) return true;
|
|
33926
|
+
for (const prefix of PREFIXES) if (dependencyName.startsWith(prefix)) return true;
|
|
33927
|
+
return false;
|
|
33928
|
+
};
|
|
33929
|
+
const containsAnyReactNativeDependency = (section) => {
|
|
33930
|
+
if (!section) return false;
|
|
33931
|
+
for (const dependencyName of Object.keys(section)) if (isReactNativeDependencyName(dependencyName)) return true;
|
|
33932
|
+
return false;
|
|
33933
|
+
};
|
|
33934
|
+
const isPackageJsonReactNativeAware = (packageJson) => {
|
|
33935
|
+
if (typeof packageJson["react-native"] === "string") return true;
|
|
33936
|
+
if (containsAnyReactNativeDependency(packageJson.dependencies)) return true;
|
|
33937
|
+
if (containsAnyReactNativeDependency(packageJson.devDependencies)) return true;
|
|
33938
|
+
if (containsAnyReactNativeDependency(packageJson.peerDependencies)) return true;
|
|
33939
|
+
if (containsAnyReactNativeDependency(packageJson.optionalDependencies)) return true;
|
|
33940
|
+
return false;
|
|
33941
|
+
};
|
|
33942
|
+
const REANIMATED_DEPENDENCY_NAME$1 = "react-native-reanimated";
|
|
33943
|
+
const isPackageJsonReanimatedAware = (packageJson) => {
|
|
33944
|
+
const allDependencies = {
|
|
33945
|
+
...packageJson.peerDependencies,
|
|
33946
|
+
...packageJson.dependencies,
|
|
33947
|
+
...packageJson.devDependencies,
|
|
33948
|
+
...packageJson.optionalDependencies
|
|
33949
|
+
};
|
|
33950
|
+
return Object.hasOwn(allDependencies, REANIMATED_DEPENDENCY_NAME$1);
|
|
33951
|
+
};
|
|
33952
|
+
const getWorkspacePatterns = (rootDirectory, packageJson) => {
|
|
33953
|
+
const pnpmPatterns = parsePnpmWorkspacePatterns(rootDirectory);
|
|
33954
|
+
if (pnpmPatterns.length > 0) return pnpmPatterns;
|
|
33955
|
+
if (Array.isArray(packageJson.workspaces)) return packageJson.workspaces;
|
|
33956
|
+
if (packageJson.workspaces?.packages) return packageJson.workspaces.packages;
|
|
33957
|
+
const nxPatterns = getNxWorkspaceDirectories(rootDirectory);
|
|
33958
|
+
if (nxPatterns.length > 0) return nxPatterns;
|
|
33959
|
+
return [];
|
|
33652
33960
|
};
|
|
33653
33961
|
const parsePnpmWorkspacePatterns = (rootDirectory) => {
|
|
33654
33962
|
const workspacePath = Path.join(rootDirectory, "pnpm-workspace.yaml");
|
|
@@ -33667,487 +33975,42 @@ const parsePnpmWorkspacePatterns = (rootDirectory) => {
|
|
|
33667
33975
|
}
|
|
33668
33976
|
return patterns;
|
|
33669
33977
|
};
|
|
33670
|
-
const
|
|
33671
|
-
|
|
33672
|
-
|
|
33673
|
-
|
|
33674
|
-
|
|
33675
|
-
|
|
33676
|
-
if (
|
|
33677
|
-
|
|
33678
|
-
|
|
33679
|
-
const
|
|
33680
|
-
|
|
33681
|
-
|
|
33978
|
+
const NX_PROJECT_DISCOVERY_DIRS = [
|
|
33979
|
+
"apps",
|
|
33980
|
+
"libs",
|
|
33981
|
+
"packages"
|
|
33982
|
+
];
|
|
33983
|
+
const getNxWorkspaceDirectories = (rootDirectory) => {
|
|
33984
|
+
if (!isFile(Path.join(rootDirectory, "nx.json"))) return [];
|
|
33985
|
+
const collected = [];
|
|
33986
|
+
for (const candidate of NX_PROJECT_DISCOVERY_DIRS) {
|
|
33987
|
+
const candidatePath = Path.join(rootDirectory, candidate);
|
|
33988
|
+
if (!isDirectory(candidatePath)) continue;
|
|
33989
|
+
for (const entry of readDirectoryEntries(candidatePath)) {
|
|
33990
|
+
if (!entry.isDirectory()) continue;
|
|
33991
|
+
const projectDirectory = Path.join(candidatePath, entry.name);
|
|
33992
|
+
if (isFile(Path.join(projectDirectory, "project.json")) || isFile(Path.join(projectDirectory, "package.json"))) collected.push(`${candidate}/${entry.name}`);
|
|
33993
|
+
}
|
|
33994
|
+
}
|
|
33995
|
+
return collected;
|
|
33682
33996
|
};
|
|
33683
33997
|
const resolveWorkspaceDirectories = (rootDirectory, pattern) => {
|
|
33684
33998
|
const cleanPattern = pattern.replace(/["']/g, "").replace(/\/\*\*$/, "/*");
|
|
33685
33999
|
if (!cleanPattern.includes("*")) {
|
|
33686
|
-
const directoryPath = Path.join(rootDirectory, cleanPattern);
|
|
33687
|
-
if (isDirectory(directoryPath) && isFile(Path.join(directoryPath, "package.json"))) return [directoryPath];
|
|
33688
|
-
return [];
|
|
33689
|
-
}
|
|
33690
|
-
const wildcardIndex = cleanPattern.indexOf("*");
|
|
33691
|
-
const baseDirectory = Path.join(rootDirectory, cleanPattern.slice(0, wildcardIndex));
|
|
33692
|
-
const suffixAfterWildcard = cleanPattern.slice(wildcardIndex + 1);
|
|
33693
|
-
if (!isDirectory(baseDirectory)) return [];
|
|
33694
|
-
const resolved = [];
|
|
33695
|
-
for (const entry of readDirectoryEntries(baseDirectory)) {
|
|
33696
|
-
const entryPath = Path.join(baseDirectory, entry.name, suffixAfterWildcard);
|
|
33697
|
-
if (isDirectory(entryPath) && isFile(Path.join(entryPath, "package.json"))) resolved.push(entryPath);
|
|
33698
|
-
}
|
|
33699
|
-
return resolved;
|
|
33700
|
-
};
|
|
33701
|
-
const resolveWorkspaceDependencyVersion = ({ concreteVersion, packageName, rootDirectory, rootPackageJson, sections, workspaceDirectory, workspacePackageJson }) => {
|
|
33702
|
-
const dependencyDeclaration = getDependencyDeclaration({
|
|
33703
|
-
packageJson: workspacePackageJson,
|
|
33704
|
-
packageName,
|
|
33705
|
-
sections
|
|
33706
|
-
});
|
|
33707
|
-
if (!dependencyDeclaration.hasDeclaration) return null;
|
|
33708
|
-
return concreteVersion ?? resolveCatalogVersion(workspacePackageJson, packageName, workspaceDirectory, dependencyDeclaration.catalogReference) ?? resolveCatalogVersion(rootPackageJson, packageName, rootDirectory, dependencyDeclaration.catalogReference);
|
|
33709
|
-
};
|
|
33710
|
-
const shouldReplaceReactVersion = (currentVersion, nextVersion) => {
|
|
33711
|
-
if (!currentVersion) return true;
|
|
33712
|
-
const currentMajor = parseReactMajor(currentVersion);
|
|
33713
|
-
const nextMajor = parseReactMajor(nextVersion);
|
|
33714
|
-
if (currentMajor === null) return nextMajor !== null;
|
|
33715
|
-
if (nextMajor === null) return false;
|
|
33716
|
-
return nextMajor < currentMajor;
|
|
33717
|
-
};
|
|
33718
|
-
const findReactInWorkspaces = (rootDirectory, packageJson) => {
|
|
33719
|
-
const patterns = getWorkspacePatterns(rootDirectory, packageJson);
|
|
33720
|
-
const result = { ...EMPTY_DEPENDENCY_INFO };
|
|
33721
|
-
for (const pattern of patterns) {
|
|
33722
|
-
const directories = resolveWorkspaceDirectories(rootDirectory, pattern);
|
|
33723
|
-
for (const workspaceDirectory of directories) {
|
|
33724
|
-
const workspacePackageJson = readPackageJson(Path.join(workspaceDirectory, "package.json"));
|
|
33725
|
-
const info = extractDependencyInfo(workspacePackageJson);
|
|
33726
|
-
const reactVersion = resolveWorkspaceDependencyVersion({
|
|
33727
|
-
concreteVersion: info.reactVersion,
|
|
33728
|
-
packageName: "react",
|
|
33729
|
-
rootDirectory,
|
|
33730
|
-
rootPackageJson: packageJson,
|
|
33731
|
-
sections: [
|
|
33732
|
-
"dependencies",
|
|
33733
|
-
"peerDependencies",
|
|
33734
|
-
"devDependencies"
|
|
33735
|
-
],
|
|
33736
|
-
workspaceDirectory,
|
|
33737
|
-
workspacePackageJson
|
|
33738
|
-
});
|
|
33739
|
-
const tailwindVersion = resolveWorkspaceDependencyVersion({
|
|
33740
|
-
concreteVersion: info.tailwindVersion,
|
|
33741
|
-
packageName: "tailwindcss",
|
|
33742
|
-
rootDirectory,
|
|
33743
|
-
rootPackageJson: packageJson,
|
|
33744
|
-
sections: [
|
|
33745
|
-
"dependencies",
|
|
33746
|
-
"devDependencies",
|
|
33747
|
-
"peerDependencies"
|
|
33748
|
-
],
|
|
33749
|
-
workspaceDirectory,
|
|
33750
|
-
workspacePackageJson
|
|
33751
|
-
});
|
|
33752
|
-
const zodVersion = resolveWorkspaceDependencyVersion({
|
|
33753
|
-
concreteVersion: info.zodVersion,
|
|
33754
|
-
packageName: "zod",
|
|
33755
|
-
rootDirectory,
|
|
33756
|
-
rootPackageJson: packageJson,
|
|
33757
|
-
sections: [
|
|
33758
|
-
"dependencies",
|
|
33759
|
-
"devDependencies",
|
|
33760
|
-
"peerDependencies"
|
|
33761
|
-
],
|
|
33762
|
-
workspaceDirectory,
|
|
33763
|
-
workspacePackageJson
|
|
33764
|
-
});
|
|
33765
|
-
if (reactVersion && shouldReplaceReactVersion(result.reactVersion, reactVersion)) result.reactVersion = reactVersion;
|
|
33766
|
-
if (tailwindVersion && !result.tailwindVersion) result.tailwindVersion = tailwindVersion;
|
|
33767
|
-
if (zodVersion && !result.zodVersion) result.zodVersion = zodVersion;
|
|
33768
|
-
if (info.framework !== "unknown" && result.framework === "unknown") result.framework = info.framework;
|
|
33769
|
-
const resultReactMajor = parseReactMajor(result.reactVersion);
|
|
33770
|
-
if (result.reactVersion && result.tailwindVersion && result.framework !== "unknown" && resultReactMajor !== null && resultReactMajor <= 17) return result;
|
|
33771
|
-
}
|
|
33772
|
-
}
|
|
33773
|
-
return result;
|
|
33774
|
-
};
|
|
33775
|
-
const findDependencyInfoFromMonorepoRoot = (directory) => {
|
|
33776
|
-
const monorepoRoot = findMonorepoRoot(directory);
|
|
33777
|
-
if (!monorepoRoot) return EMPTY_DEPENDENCY_INFO;
|
|
33778
|
-
const monorepoPackageJsonPath = Path.join(monorepoRoot, "package.json");
|
|
33779
|
-
if (!isFile(monorepoPackageJsonPath)) return EMPTY_DEPENDENCY_INFO;
|
|
33780
|
-
const rootPackageJson = readPackageJson(monorepoPackageJsonPath);
|
|
33781
|
-
const rootInfo = extractDependencyInfo(rootPackageJson);
|
|
33782
|
-
const leafPackageJsonPath = Path.join(directory, "package.json");
|
|
33783
|
-
const leafPackageJson = isFile(leafPackageJsonPath) ? readPackageJson(leafPackageJsonPath) : null;
|
|
33784
|
-
const leafReactDeclaration = leafPackageJson ? getDependencyDeclaration({
|
|
33785
|
-
packageJson: leafPackageJson,
|
|
33786
|
-
packageName: "react",
|
|
33787
|
-
sections: [
|
|
33788
|
-
"dependencies",
|
|
33789
|
-
"peerDependencies",
|
|
33790
|
-
"devDependencies"
|
|
33791
|
-
]
|
|
33792
|
-
}) : null;
|
|
33793
|
-
const leafTailwindDeclaration = leafPackageJson ? getDependencyDeclaration({
|
|
33794
|
-
packageJson: leafPackageJson,
|
|
33795
|
-
packageName: "tailwindcss",
|
|
33796
|
-
sections: [
|
|
33797
|
-
"dependencies",
|
|
33798
|
-
"devDependencies",
|
|
33799
|
-
"peerDependencies"
|
|
33800
|
-
]
|
|
33801
|
-
}) : null;
|
|
33802
|
-
const leafZodDeclaration = leafPackageJson ? getDependencyDeclaration({
|
|
33803
|
-
packageJson: leafPackageJson,
|
|
33804
|
-
packageName: "zod",
|
|
33805
|
-
sections: [
|
|
33806
|
-
"dependencies",
|
|
33807
|
-
"devDependencies",
|
|
33808
|
-
"peerDependencies"
|
|
33809
|
-
]
|
|
33810
|
-
}) : null;
|
|
33811
|
-
const shouldUseReactFallback = !leafReactDeclaration?.hasDeclaration;
|
|
33812
|
-
const shouldUseTailwindFallback = leafTailwindDeclaration?.hasDeclaration ?? true;
|
|
33813
|
-
const shouldUseZodFallback = leafZodDeclaration?.hasDeclaration ?? true;
|
|
33814
|
-
const reactCatalogVersion = shouldUseReactFallback ? resolveCatalogVersion(rootPackageJson, "react", monorepoRoot, leafReactDeclaration?.catalogReference) : null;
|
|
33815
|
-
const tailwindCatalogVersion = shouldUseTailwindFallback ? resolveCatalogVersion(rootPackageJson, "tailwindcss", monorepoRoot, leafTailwindDeclaration?.catalogReference) : null;
|
|
33816
|
-
const zodCatalogVersion = shouldUseZodFallback ? resolveCatalogVersion(rootPackageJson, "zod", monorepoRoot, leafZodDeclaration?.catalogReference) : null;
|
|
33817
|
-
const workspaceInfo = findReactInWorkspaces(monorepoRoot, rootPackageJson);
|
|
33818
|
-
return {
|
|
33819
|
-
reactVersion: shouldUseReactFallback ? reactCatalogVersion ?? rootInfo.reactVersion ?? workspaceInfo.reactVersion : rootInfo.reactVersion ?? workspaceInfo.reactVersion,
|
|
33820
|
-
tailwindVersion: shouldUseTailwindFallback ? tailwindCatalogVersion ?? rootInfo.tailwindVersion ?? workspaceInfo.tailwindVersion : null,
|
|
33821
|
-
zodVersion: shouldUseZodFallback ? zodCatalogVersion ?? rootInfo.zodVersion ?? workspaceInfo.zodVersion : null,
|
|
33822
|
-
framework: rootInfo.framework !== "unknown" ? rootInfo.framework : workspaceInfo.framework
|
|
33823
|
-
};
|
|
33824
|
-
};
|
|
33825
|
-
/**
|
|
33826
|
-
* Walk up from `startDirectory` to the nearest ancestor that owns a
|
|
33827
|
-
* `package.json`, stopping at (and including) the enclosing project boundary
|
|
33828
|
-
* — the working tree's git root or a monorepo root. Lets a scan of a package
|
|
33829
|
-
* subfolder that has no `package.json` of its own adopt the nearest enclosing
|
|
33830
|
-
* package as its project root, so it inherits that package's dependency +
|
|
33831
|
-
* framework detection instead of synthesizing an empty (React-blind) project.
|
|
33832
|
-
*
|
|
33833
|
-
* Generalizes the older monorepo-root-only lookup: the nearest ancestor is the
|
|
33834
|
-
* most specific owning package (a leaf workspace, not just the repo root), so
|
|
33835
|
-
* a subfolder of one workspace no longer borrows a sibling's capabilities.
|
|
33836
|
-
*
|
|
33837
|
-
* Returns `null` when no ancestor `package.json` exists inside the project
|
|
33838
|
-
* boundary (a loose tree of files, or a subfolder of a package-less repo).
|
|
33839
|
-
*/
|
|
33840
|
-
const findNearestAncestorPackageJson = (startDirectory) => {
|
|
33841
|
-
if (isProjectBoundary(startDirectory)) return null;
|
|
33842
|
-
for (const directory of ancestorDirectories(startDirectory, { includeStart: false })) {
|
|
33843
|
-
if (isFile(Path.join(directory, "package.json"))) return directory;
|
|
33844
|
-
if (isProjectBoundary(directory)) return null;
|
|
33845
|
-
}
|
|
33846
|
-
return null;
|
|
33847
|
-
};
|
|
33848
|
-
/**
|
|
33849
|
-
* Last-resort React detection: locate React the way Node's `node_modules`
|
|
33850
|
-
* resolution would from `directory` — check each `node_modules/react` up the
|
|
33851
|
-
* tree — but bounded to the enclosing repo so a globally installed React can't
|
|
33852
|
-
* masquerade as the project's version. Stops at (and including) the nearest
|
|
33853
|
-
* project boundary — the git root or a monorepo root — and returns the first
|
|
33854
|
-
* installed `version`.
|
|
33855
|
-
*
|
|
33856
|
-
* Makes "React is installed and importable" ⇒ "React is detected" an invariant
|
|
33857
|
-
* for packages whose only React declaration is a version-less spec
|
|
33858
|
-
* (`workspace:*`, `*`, a dist-tag) or where React lives solely in a hoisted
|
|
33859
|
-
* `node_modules` the declaration walks never reach — the profile of a component
|
|
33860
|
-
* package inside a monorepo. Matching the `node_modules/react` entry rather than
|
|
33861
|
-
* its realpath means a project whose `node_modules` is symlinked elsewhere (a
|
|
33862
|
-
* Docker volume, a shared store) still counts, while a global install outside
|
|
33863
|
-
* the repo does not.
|
|
33864
|
-
*
|
|
33865
|
-
* Returns `null` when React isn't installed within the repo or its package.json
|
|
33866
|
-
* carries no version string. A tree with no boundary marker at all bounds the
|
|
33867
|
-
* search to `directory`, so a dependency hoisted above the scanned package there
|
|
33868
|
-
* is conservatively not adopted.
|
|
33869
|
-
*/
|
|
33870
|
-
/**
|
|
33871
|
-
* The nearest enclosing project boundary (git root or monorepo root), or
|
|
33872
|
-
* `directory` itself when the scan target sits outside any repo. Bounds the
|
|
33873
|
-
* node_modules walk so a React hoisted anywhere inside the repo counts, but a
|
|
33874
|
-
* React in an ancestor above the repo — a home-directory or global
|
|
33875
|
-
* `node_modules` — can't leak in.
|
|
33876
|
-
*/
|
|
33877
|
-
const findContainmentRoot = (directory) => {
|
|
33878
|
-
for (const ancestorDirectory of ancestorDirectories(directory, { includeStart: true })) if (isProjectBoundary(ancestorDirectory)) return ancestorDirectory;
|
|
33879
|
-
return directory;
|
|
33880
|
-
};
|
|
33881
|
-
const resolveInstalledReactVersion = (directory) => {
|
|
33882
|
-
const containmentRoot = findContainmentRoot(directory);
|
|
33883
|
-
for (const ancestorDirectory of ancestorDirectories(directory, { includeStart: true })) {
|
|
33884
|
-
const reactPackageJsonPath = Path.join(ancestorDirectory, "node_modules", "react", "package.json");
|
|
33885
|
-
if (isFile(reactPackageJsonPath)) {
|
|
33886
|
-
const installedVersion = readPackageJson(reactPackageJsonPath).version;
|
|
33887
|
-
return typeof installedVersion === "string" ? installedVersion : null;
|
|
33888
|
-
}
|
|
33889
|
-
if (ancestorDirectory === containmentRoot) return null;
|
|
33890
|
-
}
|
|
33891
|
-
return null;
|
|
33892
|
-
};
|
|
33893
|
-
const findInWorkspacePackageJsons = (rootDirectory, rootPackageJson, select) => {
|
|
33894
|
-
const rootValue = select(rootPackageJson);
|
|
33895
|
-
if (rootValue !== null) return rootValue;
|
|
33896
|
-
const patterns = getWorkspacePatterns(rootDirectory, rootPackageJson);
|
|
33897
|
-
if (patterns.length === 0) return null;
|
|
33898
|
-
const visitedDirectories = /* @__PURE__ */ new Set();
|
|
33899
|
-
for (const pattern of patterns) {
|
|
33900
|
-
const directories = [...resolveWorkspaceDirectories(rootDirectory, pattern)].sort();
|
|
33901
|
-
for (const workspaceDirectory of directories) {
|
|
33902
|
-
if (visitedDirectories.has(workspaceDirectory)) continue;
|
|
33903
|
-
visitedDirectories.add(workspaceDirectory);
|
|
33904
|
-
const value = select(readPackageJson(Path.join(workspaceDirectory, "package.json")));
|
|
33905
|
-
if (value !== null) return value;
|
|
33906
|
-
}
|
|
33907
|
-
}
|
|
33908
|
-
return null;
|
|
33909
|
-
};
|
|
33910
|
-
const someWorkspacePackageJson = (rootDirectory, rootPackageJson, predicate) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => predicate(packageJson) ? true : null) !== null;
|
|
33911
|
-
const NAMES = new Set([
|
|
33912
|
-
"react-native",
|
|
33913
|
-
"react-native-tvos",
|
|
33914
|
-
...new Set([
|
|
33915
|
-
"expo",
|
|
33916
|
-
"expo-router",
|
|
33917
|
-
"@expo/cli",
|
|
33918
|
-
"@expo/metro-config",
|
|
33919
|
-
"@expo/metro-runtime"
|
|
33920
|
-
]),
|
|
33921
|
-
"react-native-windows",
|
|
33922
|
-
"react-native-macos"
|
|
33923
|
-
]);
|
|
33924
|
-
const PREFIXES = ["@react-native/", "@react-native-"];
|
|
33925
|
-
const isReactNativeDependencyName = (dependencyName) => {
|
|
33926
|
-
if (NAMES.has(dependencyName)) return true;
|
|
33927
|
-
for (const prefix of PREFIXES) if (dependencyName.startsWith(prefix)) return true;
|
|
33928
|
-
return false;
|
|
33929
|
-
};
|
|
33930
|
-
const containsAnyReactNativeDependency = (section) => {
|
|
33931
|
-
if (!section) return false;
|
|
33932
|
-
for (const dependencyName of Object.keys(section)) if (isReactNativeDependencyName(dependencyName)) return true;
|
|
33933
|
-
return false;
|
|
33934
|
-
};
|
|
33935
|
-
const isPackageJsonReactNativeAware = (packageJson) => {
|
|
33936
|
-
if (typeof packageJson["react-native"] === "string") return true;
|
|
33937
|
-
if (containsAnyReactNativeDependency(packageJson.dependencies)) return true;
|
|
33938
|
-
if (containsAnyReactNativeDependency(packageJson.devDependencies)) return true;
|
|
33939
|
-
if (containsAnyReactNativeDependency(packageJson.peerDependencies)) return true;
|
|
33940
|
-
if (containsAnyReactNativeDependency(packageJson.optionalDependencies)) return true;
|
|
33941
|
-
return false;
|
|
33942
|
-
};
|
|
33943
|
-
const hasReactNativeWorkspaceAnywhere = (rootDirectory, rootPackageJson) => someWorkspacePackageJson(rootDirectory, rootPackageJson, isPackageJsonReactNativeAware);
|
|
33944
|
-
const getDependencySpec = (packageJson, packageName) => {
|
|
33945
|
-
const spec = packageJson.dependencies?.[packageName] ?? packageJson.devDependencies?.[packageName] ?? packageJson.peerDependencies?.[packageName] ?? packageJson.optionalDependencies?.[packageName];
|
|
33946
|
-
return typeof spec === "string" ? spec : null;
|
|
33947
|
-
};
|
|
33948
|
-
const findExpoVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, "expo"));
|
|
33949
|
-
const SHOPIFY_FLASH_LIST_PACKAGE_NAME = "@shopify/flash-list";
|
|
33950
|
-
const findShopifyFlashListVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, SHOPIFY_FLASH_LIST_PACKAGE_NAME));
|
|
33951
|
-
const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, version }) => {
|
|
33952
|
-
if (version === null || !isCatalogReference(version)) return version;
|
|
33953
|
-
const catalogName = extractCatalogName(version);
|
|
33954
|
-
const resolvedLocalVersion = resolveCatalogVersion(rootPackageJson, packageName, rootDirectory, catalogName);
|
|
33955
|
-
if (resolvedLocalVersion) return resolvedLocalVersion;
|
|
33956
|
-
const monorepoRoot = findMonorepoRoot(rootDirectory);
|
|
33957
|
-
if (!monorepoRoot) return version;
|
|
33958
|
-
const monorepoPackageJsonPath = path.join(monorepoRoot, "package.json");
|
|
33959
|
-
if (!isFile(monorepoPackageJsonPath)) return version;
|
|
33960
|
-
return resolveCatalogVersion(readPackageJson(monorepoPackageJsonPath), packageName, monorepoRoot, catalogName) ?? version;
|
|
33961
|
-
};
|
|
33962
|
-
const findNextjsVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, "next"));
|
|
33963
|
-
const getPreactVersion = (packageJson) => {
|
|
33964
|
-
return {
|
|
33965
|
-
...packageJson.peerDependencies,
|
|
33966
|
-
...packageJson.dependencies,
|
|
33967
|
-
...packageJson.devDependencies
|
|
33968
|
-
}.preact ?? null;
|
|
33969
|
-
};
|
|
33970
|
-
const TANSTACK_QUERY_PACKAGES = new Set([
|
|
33971
|
-
"@tanstack/react-query",
|
|
33972
|
-
"@tanstack/query-core",
|
|
33973
|
-
"react-query"
|
|
33974
|
-
]);
|
|
33975
|
-
const hasTanStackQuery = (packageJson) => {
|
|
33976
|
-
const allDependencies = {
|
|
33977
|
-
...packageJson.peerDependencies,
|
|
33978
|
-
...packageJson.dependencies,
|
|
33979
|
-
...packageJson.devDependencies
|
|
33980
|
-
};
|
|
33981
|
-
return Object.keys(allDependencies).some((packageName) => TANSTACK_QUERY_PACKAGES.has(packageName));
|
|
33982
|
-
};
|
|
33983
|
-
const REANIMATED_DEPENDENCY_NAME = "react-native-reanimated";
|
|
33984
|
-
const isPackageJsonReanimatedAware = (packageJson) => {
|
|
33985
|
-
const allDependencies = {
|
|
33986
|
-
...packageJson.peerDependencies,
|
|
33987
|
-
...packageJson.dependencies,
|
|
33988
|
-
...packageJson.devDependencies,
|
|
33989
|
-
...packageJson.optionalDependencies
|
|
33990
|
-
};
|
|
33991
|
-
return Object.hasOwn(allDependencies, REANIMATED_DEPENDENCY_NAME);
|
|
33992
|
-
};
|
|
33993
|
-
const parseZodMajor = (zodVersion) => {
|
|
33994
|
-
if (typeof zodVersion !== "string") return null;
|
|
33995
|
-
return getLowestDependencyMajor(zodVersion);
|
|
33996
|
-
};
|
|
33997
|
-
const hasUpperBoundOnlyPeerRange = (range) => {
|
|
33998
|
-
if (typeof range !== "string") return false;
|
|
33999
|
-
const normalizedRange = normalizeDependencyVersion(range);
|
|
34000
|
-
if (normalizedRange === null) return false;
|
|
34001
|
-
return splitDependencyVersionBranches(normalizedRange).some((branch) => {
|
|
34002
|
-
const normalizedBranch = normalizeDependencyVersion(branch);
|
|
34003
|
-
return normalizedBranch !== null && getBranchLowestMajor(normalizedBranch) === null && hasUpperBoundComparator(normalizedBranch);
|
|
34004
|
-
});
|
|
34005
|
-
};
|
|
34006
|
-
const peerRangeMinMajor = (range) => {
|
|
34007
|
-
if (typeof range !== "string") return null;
|
|
34008
|
-
return getLowestDependencyMajor(range);
|
|
34009
|
-
};
|
|
34010
|
-
const resolveEffectiveReactMajor = (reactVersion, packageJson) => {
|
|
34011
|
-
const installedReactMajor = parseReactMajor(reactVersion);
|
|
34012
|
-
const peerReactRange = packageJson.peerDependencies?.react;
|
|
34013
|
-
if (typeof peerReactRange !== "string") return installedReactMajor;
|
|
34014
|
-
const peerFloor = peerRangeMinMajor(peerReactRange);
|
|
34015
|
-
if (peerFloor === null) return hasUpperBoundOnlyPeerRange(peerReactRange) ? null : installedReactMajor;
|
|
34016
|
-
return installedReactMajor !== null ? Math.min(installedReactMajor, peerFloor) : peerFloor;
|
|
34017
|
-
};
|
|
34018
|
-
const TSCONFIG_FILENAME = "tsconfig.json";
|
|
34019
|
-
const isRelativeExtendsValue = (extendsValue) => extendsValue.startsWith("./") || extendsValue.startsWith("../") || Path.isAbsolute(extendsValue);
|
|
34020
|
-
const ensureJsonExtension = (filePath) => Path.extname(filePath) === "" ? `${filePath}.json` : filePath;
|
|
34021
|
-
const resolvePackageExtendsPath = (extendsValue, fromConfigDirectory) => {
|
|
34022
|
-
const requireFromConfig = createRequire(Path.join(fromConfigDirectory, "tsconfig.json"));
|
|
34023
|
-
const candidates = [
|
|
34024
|
-
extendsValue,
|
|
34025
|
-
ensureJsonExtension(extendsValue),
|
|
34026
|
-
`${extendsValue.replace(/\/$/, "")}/tsconfig.json`
|
|
34027
|
-
];
|
|
34028
|
-
for (const candidate of candidates) try {
|
|
34029
|
-
return requireFromConfig.resolve(candidate);
|
|
34030
|
-
} catch {
|
|
34031
|
-
continue;
|
|
34032
|
-
}
|
|
34033
|
-
return null;
|
|
34034
|
-
};
|
|
34035
|
-
const resolveExtendsPath = (extendsValue, fromConfigDirectory) => {
|
|
34036
|
-
if (isRelativeExtendsValue(extendsValue)) return ensureJsonExtension(Path.resolve(fromConfigDirectory, extendsValue));
|
|
34037
|
-
return resolvePackageExtendsPath(extendsValue, fromConfigDirectory);
|
|
34038
|
-
};
|
|
34039
|
-
const normalizeCompilerOptions = (compilerOptions) => {
|
|
34040
|
-
if (!isPlainObject(compilerOptions)) return { hasExplicitLib: false };
|
|
34041
|
-
const target = typeof compilerOptions.target === "string" ? compilerOptions.target : void 0;
|
|
34042
|
-
const hasExplicitLib = Object.hasOwn(compilerOptions, "lib");
|
|
34043
|
-
return {
|
|
34044
|
-
target,
|
|
34045
|
-
lib: Array.isArray(compilerOptions.lib) ? compilerOptions.lib.filter((entry) => typeof entry === "string") : void 0,
|
|
34046
|
-
hasExplicitLib
|
|
34047
|
-
};
|
|
34048
|
-
};
|
|
34049
|
-
const normalizeReferencePaths = (references) => {
|
|
34050
|
-
if (!Array.isArray(references)) return [];
|
|
34051
|
-
return references.map((reference) => isPlainObject(reference) && typeof reference.path === "string" ? reference.path : null).filter((referencePath) => referencePath !== null);
|
|
34052
|
-
};
|
|
34053
|
-
const readTsConfig = (filePath) => {
|
|
34054
|
-
let content;
|
|
34055
|
-
try {
|
|
34056
|
-
content = NFS.readFileSync(filePath, "utf-8");
|
|
34057
|
-
} catch {
|
|
34058
|
-
return null;
|
|
34059
|
-
}
|
|
34060
|
-
const parsed = ts.parseConfigFileTextToJson(filePath, content);
|
|
34061
|
-
if (!isPlainObject(parsed.config)) return null;
|
|
34062
|
-
return {
|
|
34063
|
-
extends: typeof parsed.config.extends === "string" ? parsed.config.extends : void 0,
|
|
34064
|
-
referencePaths: normalizeReferencePaths(parsed.config.references),
|
|
34065
|
-
compilerOptions: normalizeCompilerOptions(parsed.config.compilerOptions)
|
|
34066
|
-
};
|
|
34067
|
-
};
|
|
34068
|
-
const mergeCompilerOptions = (inherited, current) => {
|
|
34069
|
-
const target = current.target ?? inherited?.target;
|
|
34070
|
-
const hasExplicitLib = current.hasExplicitLib || Boolean(inherited?.hasExplicitLib);
|
|
34071
|
-
return {
|
|
34072
|
-
target,
|
|
34073
|
-
lib: current.hasExplicitLib ? current.lib : inherited?.lib,
|
|
34074
|
-
hasExplicitLib
|
|
34075
|
-
};
|
|
34076
|
-
};
|
|
34077
|
-
const readResolvedCompilerOptions = (tsConfigPath, extendsDepth, visitedPaths) => {
|
|
34078
|
-
const realPath = NFS.realpathSync.native(tsConfigPath);
|
|
34079
|
-
if (visitedPaths.has(realPath)) return null;
|
|
34080
|
-
const tsConfig = readTsConfig(realPath);
|
|
34081
|
-
if (!tsConfig) return null;
|
|
34082
|
-
const nextVisitedPaths = new Set(visitedPaths);
|
|
34083
|
-
nextVisitedPaths.add(realPath);
|
|
34084
|
-
if (tsConfig.extends && extendsDepth < 8) {
|
|
34085
|
-
const parentPath = resolveExtendsPath(tsConfig.extends, Path.dirname(realPath));
|
|
34086
|
-
if (parentPath && isFile(parentPath)) return mergeCompilerOptions(readResolvedCompilerOptions(parentPath, extendsDepth + 1, nextVisitedPaths), tsConfig.compilerOptions);
|
|
34087
|
-
}
|
|
34088
|
-
return tsConfig.compilerOptions;
|
|
34089
|
-
};
|
|
34090
|
-
const targetYearIsPreES2023 = (target) => {
|
|
34091
|
-
const year = ES_TARGET_YEAR_BY_NAME[target.toLowerCase()];
|
|
34092
|
-
return year !== void 0 && year < 2023;
|
|
34093
|
-
};
|
|
34094
|
-
const libEntryIncludesES2023Array = (entry) => {
|
|
34095
|
-
const normalizedEntry = entry.toLowerCase();
|
|
34096
|
-
if (normalizedEntry === "esnext" || normalizedEntry === "esnext.array") return true;
|
|
34097
|
-
const esYearMatch = /^es(\d{4})(?:\.(.+))?$/.exec(normalizedEntry);
|
|
34098
|
-
if (!esYearMatch) return false;
|
|
34099
|
-
if (Number(esYearMatch[1]) < 2023) return false;
|
|
34100
|
-
const component = esYearMatch[2];
|
|
34101
|
-
return component === void 0 || component === "array";
|
|
34102
|
-
};
|
|
34103
|
-
const libIncludesES2023 = (lib) => lib.some(libEntryIncludesES2023Array);
|
|
34104
|
-
const compilerOptionsArePreES2023 = (compilerOptions) => {
|
|
34105
|
-
if (compilerOptions.target && targetYearIsPreES2023(compilerOptions.target)) return true;
|
|
34106
|
-
if (compilerOptions.hasExplicitLib) return !libIncludesES2023(compilerOptions.lib ?? []);
|
|
34107
|
-
return false;
|
|
34108
|
-
};
|
|
34109
|
-
const compilerOptionsDeclareTargetOrLib = (compilerOptions) => compilerOptions.hasExplicitLib || compilerOptions.target !== void 0;
|
|
34110
|
-
const detectPreES2023FromConfig = (tsConfigPath, visitedConfigPaths = /* @__PURE__ */ new Set()) => {
|
|
34111
|
-
if (visitedConfigPaths.has(tsConfigPath)) return false;
|
|
34112
|
-
const compilerOptions = readResolvedCompilerOptions(tsConfigPath, 0, /* @__PURE__ */ new Set());
|
|
34113
|
-
if (!compilerOptions) return false;
|
|
34114
|
-
if (!compilerOptionsDeclareTargetOrLib(compilerOptions)) {
|
|
34115
|
-
const tsConfig = readTsConfig(tsConfigPath);
|
|
34116
|
-
if (!tsConfig) return false;
|
|
34117
|
-
const nextVisitedConfigPaths = new Set(visitedConfigPaths);
|
|
34118
|
-
nextVisitedConfigPaths.add(tsConfigPath);
|
|
34119
|
-
const configDirectory = Path.dirname(tsConfigPath);
|
|
34120
|
-
return tsConfig.referencePaths.some((referencePath) => {
|
|
34121
|
-
const resolvedReferencePath = Path.resolve(configDirectory, referencePath);
|
|
34122
|
-
const referencedConfigPath = isFile(resolvedReferencePath) ? resolvedReferencePath : Path.join(resolvedReferencePath, TSCONFIG_FILENAME);
|
|
34123
|
-
return isFile(referencedConfigPath) && detectPreES2023FromConfig(referencedConfigPath, nextVisitedConfigPaths);
|
|
34124
|
-
});
|
|
34125
|
-
}
|
|
34126
|
-
return compilerOptionsArePreES2023(compilerOptions);
|
|
34127
|
-
};
|
|
34128
|
-
const FALLBACK_TSCONFIG_FILENAMES = ["tsconfig.app.json", "tsconfig.build.json"];
|
|
34129
|
-
const detectPreES2023Target = (directory) => {
|
|
34130
|
-
const tsConfigPath = Path.join(directory, TSCONFIG_FILENAME);
|
|
34131
|
-
if (isFile(tsConfigPath)) return detectPreES2023FromConfig(tsConfigPath);
|
|
34132
|
-
for (const fallbackFilename of FALLBACK_TSCONFIG_FILENAMES) {
|
|
34133
|
-
const fallbackPath = Path.join(directory, fallbackFilename);
|
|
34134
|
-
if (isFile(fallbackPath)) return detectPreES2023FromConfig(fallbackPath);
|
|
34135
|
-
}
|
|
34136
|
-
return false;
|
|
34137
|
-
};
|
|
34138
|
-
const REACT_DEPENDENCY_NAMES = new Set([
|
|
34139
|
-
"react",
|
|
34140
|
-
"react-native",
|
|
34141
|
-
"next",
|
|
34142
|
-
"preact"
|
|
34143
|
-
]);
|
|
34144
|
-
const hasReactDependency = (packageJson) => {
|
|
34145
|
-
const allDependencies = {
|
|
34146
|
-
...packageJson.peerDependencies,
|
|
34147
|
-
...packageJson.dependencies,
|
|
34148
|
-
...packageJson.devDependencies
|
|
34149
|
-
};
|
|
34150
|
-
return Object.keys(allDependencies).some((packageName) => REACT_DEPENDENCY_NAMES.has(packageName));
|
|
34000
|
+
const directoryPath = Path.join(rootDirectory, cleanPattern);
|
|
34001
|
+
if (isDirectory(directoryPath) && isFile(Path.join(directoryPath, "package.json"))) return [directoryPath];
|
|
34002
|
+
return [];
|
|
34003
|
+
}
|
|
34004
|
+
const wildcardIndex = cleanPattern.indexOf("*");
|
|
34005
|
+
const baseDirectory = Path.join(rootDirectory, cleanPattern.slice(0, wildcardIndex));
|
|
34006
|
+
const suffixAfterWildcard = cleanPattern.slice(wildcardIndex + 1);
|
|
34007
|
+
if (!isDirectory(baseDirectory)) return [];
|
|
34008
|
+
const resolved = [];
|
|
34009
|
+
for (const entry of readDirectoryEntries(baseDirectory)) {
|
|
34010
|
+
const entryPath = Path.join(baseDirectory, entry.name, suffixAfterWildcard);
|
|
34011
|
+
if (isDirectory(entryPath) && isFile(Path.join(entryPath, "package.json"))) resolved.push(entryPath);
|
|
34012
|
+
}
|
|
34013
|
+
return resolved;
|
|
34151
34014
|
};
|
|
34152
34015
|
const listWorkspacePackages = (rootDirectory) => {
|
|
34153
34016
|
const packageJsonPath = Path.join(rootDirectory, "package.json");
|
|
@@ -34179,6 +34042,208 @@ const listWorkspacePackages = (rootDirectory) => {
|
|
|
34179
34042
|
}
|
|
34180
34043
|
return packages;
|
|
34181
34044
|
};
|
|
34045
|
+
const REANIMATED_DEPENDENCY_NAME = "react-native-reanimated";
|
|
34046
|
+
const SHOPIFY_FLASH_LIST_PACKAGE_NAME = "@shopify/flash-list";
|
|
34047
|
+
const resolveWorkspaceDependencyVersion = ({ concreteVersion, packageName, rootDirectory, rootPackageJson, sections, workspaceDirectory, workspacePackageJson }) => {
|
|
34048
|
+
const dependencyDeclaration = getDependencyDeclaration({
|
|
34049
|
+
packageJson: workspacePackageJson,
|
|
34050
|
+
packageName,
|
|
34051
|
+
sections
|
|
34052
|
+
});
|
|
34053
|
+
if (!dependencyDeclaration.hasDeclaration) return null;
|
|
34054
|
+
return concreteVersion ?? resolveCatalogVersion(workspacePackageJson, packageName, workspaceDirectory, dependencyDeclaration.catalogReference) ?? resolveCatalogVersion(rootPackageJson, packageName, rootDirectory, dependencyDeclaration.catalogReference);
|
|
34055
|
+
};
|
|
34056
|
+
const shouldReplaceReactVersion = (currentVersion, nextVersion) => {
|
|
34057
|
+
if (!currentVersion) return true;
|
|
34058
|
+
const currentMajor = parseReactMajor(currentVersion);
|
|
34059
|
+
const nextMajor = parseReactMajor(nextVersion);
|
|
34060
|
+
if (currentMajor === null) return nextMajor !== null;
|
|
34061
|
+
if (nextMajor === null) return false;
|
|
34062
|
+
return nextMajor < currentMajor;
|
|
34063
|
+
};
|
|
34064
|
+
const evaluateManifestFacts = (facts, packageJson, directory) => {
|
|
34065
|
+
if (facts.expo.version === null) {
|
|
34066
|
+
const spec = getDependencySpec(packageJson, "expo");
|
|
34067
|
+
if (spec !== null) facts.expo = {
|
|
34068
|
+
version: spec,
|
|
34069
|
+
sourceDirectory: directory
|
|
34070
|
+
};
|
|
34071
|
+
}
|
|
34072
|
+
if (facts.next.version === null) {
|
|
34073
|
+
const spec = getDependencySpec(packageJson, "next");
|
|
34074
|
+
if (spec !== null) facts.next = {
|
|
34075
|
+
version: spec,
|
|
34076
|
+
sourceDirectory: directory
|
|
34077
|
+
};
|
|
34078
|
+
}
|
|
34079
|
+
if (facts.shopifyFlashList.version === null) {
|
|
34080
|
+
const spec = getDependencySpec(packageJson, SHOPIFY_FLASH_LIST_PACKAGE_NAME);
|
|
34081
|
+
if (spec !== null) facts.shopifyFlashList = {
|
|
34082
|
+
version: spec,
|
|
34083
|
+
sourceDirectory: directory
|
|
34084
|
+
};
|
|
34085
|
+
}
|
|
34086
|
+
if (facts.reanimatedVersion === null) {
|
|
34087
|
+
const spec = getDependencySpec(packageJson, REANIMATED_DEPENDENCY_NAME);
|
|
34088
|
+
if (spec !== null) facts.reanimatedVersion = spec;
|
|
34089
|
+
}
|
|
34090
|
+
facts.hasReactNativeAwarePackage = facts.hasReactNativeAwarePackage || isPackageJsonReactNativeAware(packageJson);
|
|
34091
|
+
facts.hasReanimatedAwarePackage = facts.hasReanimatedAwarePackage || isPackageJsonReanimatedAware(packageJson);
|
|
34092
|
+
};
|
|
34093
|
+
const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGroup }) => {
|
|
34094
|
+
const facts = {
|
|
34095
|
+
reactVersion: null,
|
|
34096
|
+
tailwindVersion: null,
|
|
34097
|
+
zodVersion: null,
|
|
34098
|
+
framework: "unknown",
|
|
34099
|
+
expo: {
|
|
34100
|
+
version: null,
|
|
34101
|
+
sourceDirectory: null
|
|
34102
|
+
},
|
|
34103
|
+
next: {
|
|
34104
|
+
version: null,
|
|
34105
|
+
sourceDirectory: null
|
|
34106
|
+
},
|
|
34107
|
+
shopifyFlashList: {
|
|
34108
|
+
version: null,
|
|
34109
|
+
sourceDirectory: null
|
|
34110
|
+
},
|
|
34111
|
+
hasReactNativeAwarePackage: false,
|
|
34112
|
+
hasReanimatedAwarePackage: false,
|
|
34113
|
+
reanimatedVersion: null
|
|
34114
|
+
};
|
|
34115
|
+
evaluateManifestFacts(facts, rootPackageJson, rootDirectory);
|
|
34116
|
+
let isReactGroupSettled = !collectReactGroup;
|
|
34117
|
+
const visitedDirectories = /* @__PURE__ */ new Set();
|
|
34118
|
+
for (const pattern of getWorkspacePatterns(rootDirectory, rootPackageJson)) {
|
|
34119
|
+
const directories = [...resolveWorkspaceDirectories(rootDirectory, pattern)].sort();
|
|
34120
|
+
for (const workspaceDirectory of directories) {
|
|
34121
|
+
if (visitedDirectories.has(workspaceDirectory)) continue;
|
|
34122
|
+
visitedDirectories.add(workspaceDirectory);
|
|
34123
|
+
const workspacePackageJson = readPackageJson(Path.join(workspaceDirectory, "package.json"));
|
|
34124
|
+
evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory);
|
|
34125
|
+
const info = extractDependencyInfo(workspacePackageJson);
|
|
34126
|
+
if (info.framework !== "unknown" && frameworkMergeRank(info.framework) < frameworkMergeRank(facts.framework)) facts.framework = info.framework;
|
|
34127
|
+
if (isReactGroupSettled) continue;
|
|
34128
|
+
const reactVersion = resolveWorkspaceDependencyVersion({
|
|
34129
|
+
concreteVersion: info.reactVersion,
|
|
34130
|
+
packageName: "react",
|
|
34131
|
+
rootDirectory,
|
|
34132
|
+
rootPackageJson,
|
|
34133
|
+
sections: REACT_SECTIONS,
|
|
34134
|
+
workspaceDirectory,
|
|
34135
|
+
workspacePackageJson
|
|
34136
|
+
});
|
|
34137
|
+
const tailwindVersion = resolveWorkspaceDependencyVersion({
|
|
34138
|
+
concreteVersion: info.tailwindVersion,
|
|
34139
|
+
packageName: "tailwindcss",
|
|
34140
|
+
rootDirectory,
|
|
34141
|
+
rootPackageJson,
|
|
34142
|
+
sections: TAILWIND_ZOD_SECTIONS,
|
|
34143
|
+
workspaceDirectory,
|
|
34144
|
+
workspacePackageJson
|
|
34145
|
+
});
|
|
34146
|
+
const zodVersion = resolveWorkspaceDependencyVersion({
|
|
34147
|
+
concreteVersion: info.zodVersion,
|
|
34148
|
+
packageName: "zod",
|
|
34149
|
+
rootDirectory,
|
|
34150
|
+
rootPackageJson,
|
|
34151
|
+
sections: TAILWIND_ZOD_SECTIONS,
|
|
34152
|
+
workspaceDirectory,
|
|
34153
|
+
workspacePackageJson
|
|
34154
|
+
});
|
|
34155
|
+
if (reactVersion && shouldReplaceReactVersion(facts.reactVersion, reactVersion)) facts.reactVersion = reactVersion;
|
|
34156
|
+
if (tailwindVersion && !facts.tailwindVersion) facts.tailwindVersion = tailwindVersion;
|
|
34157
|
+
if (zodVersion && !facts.zodVersion) facts.zodVersion = zodVersion;
|
|
34158
|
+
const settledReactMajor = parseReactMajor(facts.reactVersion);
|
|
34159
|
+
isReactGroupSettled = Boolean(facts.reactVersion && facts.tailwindVersion && facts.framework !== "unknown" && settledReactMajor !== null && settledReactMajor <= 17);
|
|
34160
|
+
}
|
|
34161
|
+
}
|
|
34162
|
+
return facts;
|
|
34163
|
+
};
|
|
34164
|
+
const findDependencyInfoFromMonorepoRoot = (directory) => {
|
|
34165
|
+
const monorepoRoot = findMonorepoRoot(directory);
|
|
34166
|
+
if (!monorepoRoot) return EMPTY_DEPENDENCY_INFO;
|
|
34167
|
+
const monorepoPackageJsonPath = Path.join(monorepoRoot, "package.json");
|
|
34168
|
+
if (!isFile(monorepoPackageJsonPath)) return EMPTY_DEPENDENCY_INFO;
|
|
34169
|
+
const rootPackageJson = readPackageJson(monorepoPackageJsonPath);
|
|
34170
|
+
const rootInfo = extractDependencyInfo(rootPackageJson);
|
|
34171
|
+
const leafPackageJsonPath = Path.join(directory, "package.json");
|
|
34172
|
+
const leafPackageJson = isFile(leafPackageJsonPath) ? readPackageJson(leafPackageJsonPath) : null;
|
|
34173
|
+
const leafReactDeclaration = leafPackageJson ? getDependencyDeclaration({
|
|
34174
|
+
packageJson: leafPackageJson,
|
|
34175
|
+
packageName: "react",
|
|
34176
|
+
sections: REACT_SECTIONS
|
|
34177
|
+
}) : null;
|
|
34178
|
+
const leafTailwindDeclaration = leafPackageJson ? getDependencyDeclaration({
|
|
34179
|
+
packageJson: leafPackageJson,
|
|
34180
|
+
packageName: "tailwindcss",
|
|
34181
|
+
sections: TAILWIND_ZOD_SECTIONS
|
|
34182
|
+
}) : null;
|
|
34183
|
+
const leafZodDeclaration = leafPackageJson ? getDependencyDeclaration({
|
|
34184
|
+
packageJson: leafPackageJson,
|
|
34185
|
+
packageName: "zod",
|
|
34186
|
+
sections: TAILWIND_ZOD_SECTIONS
|
|
34187
|
+
}) : null;
|
|
34188
|
+
const shouldUseReactFallback = !leafReactDeclaration?.hasDeclaration;
|
|
34189
|
+
const shouldUseTailwindFallback = leafTailwindDeclaration?.hasDeclaration ?? true;
|
|
34190
|
+
const shouldUseZodFallback = leafZodDeclaration?.hasDeclaration ?? true;
|
|
34191
|
+
const reactCatalogVersion = shouldUseReactFallback ? resolveCatalogVersion(rootPackageJson, "react", monorepoRoot, leafReactDeclaration?.catalogReference) : null;
|
|
34192
|
+
const tailwindCatalogVersion = shouldUseTailwindFallback ? resolveCatalogVersion(rootPackageJson, "tailwindcss", monorepoRoot, leafTailwindDeclaration?.catalogReference) : null;
|
|
34193
|
+
const zodCatalogVersion = shouldUseZodFallback ? resolveCatalogVersion(rootPackageJson, "zod", monorepoRoot, leafZodDeclaration?.catalogReference) : null;
|
|
34194
|
+
const workspaceFacts = collectWorkspaceFacts(monorepoRoot, rootPackageJson, { collectReactGroup: true });
|
|
34195
|
+
return {
|
|
34196
|
+
reactVersion: shouldUseReactFallback ? reactCatalogVersion ?? rootInfo.reactVersion ?? workspaceFacts.reactVersion : rootInfo.reactVersion ?? workspaceFacts.reactVersion,
|
|
34197
|
+
tailwindVersion: shouldUseTailwindFallback ? tailwindCatalogVersion ?? rootInfo.tailwindVersion ?? workspaceFacts.tailwindVersion : null,
|
|
34198
|
+
zodVersion: shouldUseZodFallback ? zodCatalogVersion ?? rootInfo.zodVersion ?? workspaceFacts.zodVersion : null,
|
|
34199
|
+
framework: rootInfo.framework !== "unknown" ? rootInfo.framework : workspaceFacts.framework
|
|
34200
|
+
};
|
|
34201
|
+
};
|
|
34202
|
+
/**
|
|
34203
|
+
* Last-resort React detection: locate React the way Node's `node_modules`
|
|
34204
|
+
* resolution would from `directory` — check each `node_modules/react` up the
|
|
34205
|
+
* tree — but bounded to the enclosing repo so a globally installed React can't
|
|
34206
|
+
* masquerade as the project's version. Stops at (and including) the nearest
|
|
34207
|
+
* project boundary — the git root or a monorepo root — and returns the first
|
|
34208
|
+
* installed `version`.
|
|
34209
|
+
*
|
|
34210
|
+
* Makes "React is installed and importable" ⇒ "React is detected" an invariant
|
|
34211
|
+
* for packages whose only React declaration is a version-less spec
|
|
34212
|
+
* (`workspace:*`, `*`, a dist-tag) or where React lives solely in a hoisted
|
|
34213
|
+
* `node_modules` the declaration walks never reach — the profile of a component
|
|
34214
|
+
* package inside a monorepo. Matching the `node_modules/react` entry rather than
|
|
34215
|
+
* its realpath means a project whose `node_modules` is symlinked elsewhere (a
|
|
34216
|
+
* Docker volume, a shared store) still counts, while a global install outside
|
|
34217
|
+
* the repo does not.
|
|
34218
|
+
*
|
|
34219
|
+
* Returns `null` when React isn't installed within the repo or its package.json
|
|
34220
|
+
* carries no version string. A tree with no boundary marker at all bounds the
|
|
34221
|
+
* search to `directory`, so a dependency hoisted above the scanned package there
|
|
34222
|
+
* is conservatively not adopted.
|
|
34223
|
+
*/
|
|
34224
|
+
/**
|
|
34225
|
+
* The nearest enclosing project boundary (git root or monorepo root), or
|
|
34226
|
+
* `directory` itself when the scan target sits outside any repo. Bounds the
|
|
34227
|
+
* node_modules walk so a React hoisted anywhere inside the repo counts, but a
|
|
34228
|
+
* React in an ancestor above the repo — a home-directory or global
|
|
34229
|
+
* `node_modules` — can't leak in.
|
|
34230
|
+
*/
|
|
34231
|
+
const findContainmentRoot = (directory) => {
|
|
34232
|
+
for (const ancestorDirectory of ancestorDirectories(directory, { includeStart: true })) if (isProjectBoundary(ancestorDirectory)) return ancestorDirectory;
|
|
34233
|
+
return directory;
|
|
34234
|
+
};
|
|
34235
|
+
const resolveInstalledReactVersion = (directory) => {
|
|
34236
|
+
const containmentRoot = findContainmentRoot(directory);
|
|
34237
|
+
for (const ancestorDirectory of ancestorDirectories(directory, { includeStart: true })) {
|
|
34238
|
+
const reactPackageJsonPath = Path.join(ancestorDirectory, "node_modules", "react", "package.json");
|
|
34239
|
+
if (isFile(reactPackageJsonPath)) {
|
|
34240
|
+
const installedVersion = readPackageJson(reactPackageJsonPath).version;
|
|
34241
|
+
return typeof installedVersion === "string" ? installedVersion : null;
|
|
34242
|
+
}
|
|
34243
|
+
if (ancestorDirectory === containmentRoot) return null;
|
|
34244
|
+
}
|
|
34245
|
+
return null;
|
|
34246
|
+
};
|
|
34182
34247
|
const toReactWorkspacePackages = (directories) => {
|
|
34183
34248
|
const packages = [];
|
|
34184
34249
|
for (const directory of directories) {
|
|
@@ -34251,14 +34316,13 @@ const clearProjectCache = () => {
|
|
|
34251
34316
|
};
|
|
34252
34317
|
/**
|
|
34253
34318
|
* Build a `ProjectInfo` for a directory that has no `package.json` of
|
|
34254
|
-
* its own — a
|
|
34255
|
-
*
|
|
34256
|
-
*
|
|
34257
|
-
*
|
|
34258
|
-
*
|
|
34259
|
-
*
|
|
34260
|
-
*
|
|
34261
|
-
* nothing to scan (no enclosing project and no source files of its own).
|
|
34319
|
+
* its own — a monorepo subfolder like `repo/packages`, or any loose tree
|
|
34320
|
+
* of TypeScript/JavaScript files. Dependency + framework detection is
|
|
34321
|
+
* inherited from the enclosing workspace root when there is one, so
|
|
34322
|
+
* scanning a subdirectory of a React monorepo still gets the React
|
|
34323
|
+
* capabilities; a standalone non-React directory simply scans with the
|
|
34324
|
+
* framework-agnostic rules. Throws only when the directory has nothing
|
|
34325
|
+
* to scan (no enclosing project and no source files of its own).
|
|
34262
34326
|
*/
|
|
34263
34327
|
const discoverProjectWithoutPackageJson = (directory) => {
|
|
34264
34328
|
const sourceFileCount = countSourceFiles(directory);
|
|
@@ -34296,6 +34360,7 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
34296
34360
|
hasReanimated: false,
|
|
34297
34361
|
reanimatedVersion: null,
|
|
34298
34362
|
isPreES2023Target: hasOwnTsConfig && detectPreES2023Target(directory),
|
|
34363
|
+
isStaticExport: false,
|
|
34299
34364
|
sourceFileCount
|
|
34300
34365
|
};
|
|
34301
34366
|
};
|
|
@@ -34309,90 +34374,85 @@ const discoverProject = (directory) => {
|
|
|
34309
34374
|
return synthesized;
|
|
34310
34375
|
}
|
|
34311
34376
|
const packageJson = readPackageJson(packageJsonPath);
|
|
34312
|
-
|
|
34313
|
-
|
|
34314
|
-
|
|
34315
|
-
|
|
34316
|
-
|
|
34317
|
-
|
|
34318
|
-
|
|
34319
|
-
|
|
34320
|
-
|
|
34321
|
-
|
|
34322
|
-
|
|
34323
|
-
|
|
34324
|
-
|
|
34325
|
-
|
|
34326
|
-
|
|
34327
|
-
|
|
34328
|
-
|
|
34329
|
-
]
|
|
34330
|
-
});
|
|
34331
|
-
const zodDeclaration = getDependencyDeclaration({
|
|
34377
|
+
const rootInfo = extractDependencyInfo(packageJson);
|
|
34378
|
+
let framework = rootInfo.framework;
|
|
34379
|
+
const tracked = {
|
|
34380
|
+
react: {
|
|
34381
|
+
version: rootInfo.reactVersion,
|
|
34382
|
+
sections: REACT_SECTIONS
|
|
34383
|
+
},
|
|
34384
|
+
tailwindcss: {
|
|
34385
|
+
version: rootInfo.tailwindVersion,
|
|
34386
|
+
sections: TAILWIND_ZOD_SECTIONS
|
|
34387
|
+
},
|
|
34388
|
+
zod: {
|
|
34389
|
+
version: rootInfo.zodVersion,
|
|
34390
|
+
sections: TAILWIND_ZOD_SECTIONS
|
|
34391
|
+
}
|
|
34392
|
+
};
|
|
34393
|
+
const declarations = Object.fromEntries(Object.entries(tracked).map(([packageName, entry]) => [packageName, getDependencyDeclaration({
|
|
34332
34394
|
packageJson,
|
|
34333
|
-
packageName
|
|
34334
|
-
sections:
|
|
34335
|
-
|
|
34336
|
-
|
|
34337
|
-
|
|
34338
|
-
|
|
34339
|
-
|
|
34340
|
-
if (!
|
|
34341
|
-
if (!tailwindVersion && tailwindDeclaration.hasDeclaration) tailwindVersion = resolveCatalogVersion(packageJson, "tailwindcss", directory, tailwindDeclaration.catalogReference);
|
|
34342
|
-
if (!zodVersion && zodDeclaration.hasDeclaration) zodVersion = resolveCatalogVersion(packageJson, "zod", directory, zodDeclaration.catalogReference);
|
|
34343
|
-
if (!reactVersion || !tailwindVersion || !zodVersion) {
|
|
34395
|
+
packageName,
|
|
34396
|
+
sections: entry.sections
|
|
34397
|
+
})]));
|
|
34398
|
+
const fillFromCatalogs = (source, sourceDirectory) => {
|
|
34399
|
+
for (const [packageName, entry] of Object.entries(tracked)) if (!entry.version && declarations[packageName].hasDeclaration) entry.version = resolveCatalogVersion(source, packageName, sourceDirectory, declarations[packageName].catalogReference);
|
|
34400
|
+
};
|
|
34401
|
+
fillFromCatalogs(packageJson, directory);
|
|
34402
|
+
if (!tracked.react.version || !tracked.tailwindcss.version || !tracked.zod.version) {
|
|
34344
34403
|
const monorepoRoot = findMonorepoRoot(directory);
|
|
34345
34404
|
if (monorepoRoot) {
|
|
34346
34405
|
const monorepoPackageJsonPath = Path.join(monorepoRoot, "package.json");
|
|
34347
|
-
if (isFile(monorepoPackageJsonPath))
|
|
34348
|
-
const rootPackageJson = readPackageJson(monorepoPackageJsonPath);
|
|
34349
|
-
if (!reactVersion && reactDeclaration.hasDeclaration) reactVersion = resolveCatalogVersion(rootPackageJson, "react", monorepoRoot, reactDeclaration.catalogReference);
|
|
34350
|
-
if (!tailwindVersion && tailwindDeclaration.hasDeclaration) tailwindVersion = resolveCatalogVersion(rootPackageJson, "tailwindcss", monorepoRoot, tailwindDeclaration.catalogReference);
|
|
34351
|
-
if (!zodVersion && zodDeclaration.hasDeclaration) zodVersion = resolveCatalogVersion(rootPackageJson, "zod", monorepoRoot, zodDeclaration.catalogReference);
|
|
34352
|
-
}
|
|
34406
|
+
if (isFile(monorepoPackageJsonPath)) fillFromCatalogs(readPackageJson(monorepoPackageJsonPath), monorepoRoot);
|
|
34353
34407
|
}
|
|
34354
34408
|
}
|
|
34355
|
-
|
|
34356
|
-
|
|
34357
|
-
|
|
34358
|
-
|
|
34359
|
-
|
|
34360
|
-
|
|
34409
|
+
const shouldCollectReactGroup = !tracked.react.version || framework === "unknown";
|
|
34410
|
+
const workspaceFacts = collectWorkspaceFacts(directory, packageJson, { collectReactGroup: shouldCollectReactGroup });
|
|
34411
|
+
if (shouldCollectReactGroup) {
|
|
34412
|
+
tracked.react.version ||= workspaceFacts.reactVersion;
|
|
34413
|
+
tracked.tailwindcss.version ||= workspaceFacts.tailwindVersion;
|
|
34414
|
+
tracked.zod.version ||= workspaceFacts.zodVersion;
|
|
34415
|
+
if (framework === "unknown" && workspaceFacts.framework !== "unknown") framework = workspaceFacts.framework;
|
|
34361
34416
|
}
|
|
34362
|
-
if ((!
|
|
34417
|
+
if ((!tracked.react.version || framework === "unknown") && !isMonorepoRoot(directory)) {
|
|
34363
34418
|
const monorepoInfo = findDependencyInfoFromMonorepoRoot(directory);
|
|
34364
|
-
|
|
34365
|
-
|
|
34366
|
-
|
|
34419
|
+
tracked.react.version ||= monorepoInfo.reactVersion;
|
|
34420
|
+
tracked.tailwindcss.version ||= monorepoInfo.tailwindVersion;
|
|
34421
|
+
tracked.zod.version ||= monorepoInfo.zodVersion;
|
|
34367
34422
|
if (framework === "unknown") framework = monorepoInfo.framework;
|
|
34368
34423
|
}
|
|
34369
|
-
|
|
34370
|
-
|
|
34371
|
-
|
|
34424
|
+
for (const [packageName, entry] of Object.entries(tracked)) {
|
|
34425
|
+
const declaredVersion = declarations[packageName].version;
|
|
34426
|
+
if (!entry.version && declaredVersion && !isCatalogReference(declaredVersion)) entry.version = declaredVersion;
|
|
34427
|
+
}
|
|
34428
|
+
const { react, tailwindcss, zod } = tracked;
|
|
34429
|
+
let reactVersion = react.version;
|
|
34372
34430
|
if (!reactVersion || parseReactMajor(reactVersion) === null) reactVersion = resolveInstalledReactVersion(directory) ?? reactVersion;
|
|
34431
|
+
const tailwindVersion = tailwindcss.version;
|
|
34432
|
+
const zodVersion = zod.version;
|
|
34373
34433
|
const projectName = packageJson.name ?? Path.basename(directory);
|
|
34374
34434
|
const hasTypeScript = NFS.existsSync(Path.join(directory, "tsconfig.json"));
|
|
34375
34435
|
const sourceFileCount = countSourceFiles(directory);
|
|
34376
|
-
const hasReactNativeWorkspace = framework === "expo" || framework === "react-native" ||
|
|
34436
|
+
const hasReactNativeWorkspace = framework === "expo" || framework === "react-native" || workspaceFacts.hasReactNativeAwarePackage;
|
|
34377
34437
|
const expoVersion = hasReactNativeWorkspace ? resolveCatalogBackedDependencyVersion({
|
|
34378
34438
|
rootDirectory: directory,
|
|
34379
34439
|
rootPackageJson: packageJson,
|
|
34380
34440
|
packageName: "expo",
|
|
34381
|
-
version:
|
|
34441
|
+
version: workspaceFacts.expo.version
|
|
34382
34442
|
}) : null;
|
|
34383
34443
|
const shopifyFlashListVersion = hasReactNativeWorkspace ? resolveCatalogBackedDependencyVersion({
|
|
34384
34444
|
rootDirectory: directory,
|
|
34385
34445
|
rootPackageJson: packageJson,
|
|
34386
34446
|
packageName: SHOPIFY_FLASH_LIST_PACKAGE_NAME,
|
|
34387
|
-
version:
|
|
34447
|
+
version: workspaceFacts.shopifyFlashList.version
|
|
34388
34448
|
}) : null;
|
|
34389
|
-
const hasReanimated = hasReactNativeWorkspace &&
|
|
34390
|
-
const reanimatedVersion = hasReanimated ?
|
|
34449
|
+
const hasReanimated = hasReactNativeWorkspace && workspaceFacts.hasReanimatedAwarePackage;
|
|
34450
|
+
const reanimatedVersion = hasReanimated ? workspaceFacts.reanimatedVersion : null;
|
|
34391
34451
|
const nextjsVersion = framework === "nextjs" ? resolveCatalogBackedDependencyVersion({
|
|
34392
34452
|
rootDirectory: directory,
|
|
34393
34453
|
rootPackageJson: packageJson,
|
|
34394
34454
|
packageName: "next",
|
|
34395
|
-
version:
|
|
34455
|
+
version: workspaceFacts.next.version
|
|
34396
34456
|
}) : null;
|
|
34397
34457
|
const preactVersion = getPreactVersion(packageJson);
|
|
34398
34458
|
const isPreES2023Target = hasTypeScript && detectPreES2023Target(directory);
|
|
@@ -34403,7 +34463,7 @@ const discoverProject = (directory) => {
|
|
|
34403
34463
|
reactMajorVersion: resolveEffectiveReactMajor(reactVersion, packageJson),
|
|
34404
34464
|
tailwindVersion,
|
|
34405
34465
|
zodVersion,
|
|
34406
|
-
zodMajorVersion:
|
|
34466
|
+
zodMajorVersion: zodVersion === null ? null : getLowestDependencyMajor(zodVersion),
|
|
34407
34467
|
framework,
|
|
34408
34468
|
hasTypeScript,
|
|
34409
34469
|
hasReactCompiler: detectReactCompiler(directory, packageJson),
|
|
@@ -34419,6 +34479,7 @@ const discoverProject = (directory) => {
|
|
|
34419
34479
|
hasReanimated,
|
|
34420
34480
|
reanimatedVersion,
|
|
34421
34481
|
isPreES2023Target,
|
|
34482
|
+
isStaticExport: framework === "nextjs" && detectNextjsStaticExport(workspaceFacts.next.sourceDirectory ?? directory),
|
|
34422
34483
|
sourceFileCount
|
|
34423
34484
|
};
|
|
34424
34485
|
cachedProjectInfos.set(directory, projectInfo);
|
|
@@ -34434,57 +34495,6 @@ const discoverProject = (directory) => {
|
|
|
34434
34495
|
* single source file has nothing to analyze.
|
|
34435
34496
|
*/
|
|
34436
34497
|
const isAnalyzableProject = (project) => project.reactVersion !== null || project.preactVersion !== null || project.sourceFileCount > 0;
|
|
34437
|
-
const MAJOR_MINOR_PATTERN = /(\d{1,4})\.(\d{1,4})/;
|
|
34438
|
-
const MAJOR_ONLY_PATTERN = /(\d{1,4})/;
|
|
34439
|
-
const UPPER_BOUND_COMPARATOR_PATTERN = /<=?\s{0,8}\d{1,4}(?:\.\d{1,4}){0,2}(?:-[^\s,|]+)?/g;
|
|
34440
|
-
const parseReactMajorMinor = (reactVersion) => {
|
|
34441
|
-
if (typeof reactVersion !== "string") return null;
|
|
34442
|
-
const trimmed = reactVersion.trim();
|
|
34443
|
-
if (trimmed.length === 0) return null;
|
|
34444
|
-
const lowerBoundsOnly = trimmed.replace(UPPER_BOUND_COMPARATOR_PATTERN, " ").trim();
|
|
34445
|
-
if (lowerBoundsOnly.length === 0) return null;
|
|
34446
|
-
const majorMinorMatch = lowerBoundsOnly.match(MAJOR_MINOR_PATTERN);
|
|
34447
|
-
if (majorMinorMatch) {
|
|
34448
|
-
const major = Number.parseInt(majorMinorMatch[1], 10);
|
|
34449
|
-
const minor = Number.parseInt(majorMinorMatch[2], 10);
|
|
34450
|
-
if (!Number.isFinite(major) || major <= 0) return null;
|
|
34451
|
-
if (!Number.isFinite(minor) || minor < 0) return null;
|
|
34452
|
-
return {
|
|
34453
|
-
major,
|
|
34454
|
-
minor
|
|
34455
|
-
};
|
|
34456
|
-
}
|
|
34457
|
-
const majorOnlyMatch = lowerBoundsOnly.match(MAJOR_ONLY_PATTERN);
|
|
34458
|
-
if (!majorOnlyMatch) return null;
|
|
34459
|
-
const major = Number.parseInt(majorOnlyMatch[1], 10);
|
|
34460
|
-
if (!Number.isFinite(major) || major <= 0) return null;
|
|
34461
|
-
return {
|
|
34462
|
-
major,
|
|
34463
|
-
minor: 0
|
|
34464
|
-
};
|
|
34465
|
-
};
|
|
34466
|
-
const isReactAtLeast = (detected, required) => {
|
|
34467
|
-
if (detected === null) return true;
|
|
34468
|
-
if (detected.major !== required.major) return detected.major > required.major;
|
|
34469
|
-
return detected.minor >= required.minor;
|
|
34470
|
-
};
|
|
34471
|
-
const parseLowerBoundVersion = (versionSpec) => import_semver.validRange(versionSpec) !== null ? import_semver.minVersion(versionSpec) : import_semver.coerce(versionSpec);
|
|
34472
|
-
const parseTailwindMajorMinor = (tailwindVersion) => {
|
|
34473
|
-
if (typeof tailwindVersion !== "string") return null;
|
|
34474
|
-
const trimmed = tailwindVersion.trim();
|
|
34475
|
-
if (trimmed.length === 0) return null;
|
|
34476
|
-
const lowerBound = parseLowerBoundVersion(trimmed);
|
|
34477
|
-
if (lowerBound === null || lowerBound.major <= 0) return null;
|
|
34478
|
-
return {
|
|
34479
|
-
major: lowerBound.major,
|
|
34480
|
-
minor: lowerBound.minor
|
|
34481
|
-
};
|
|
34482
|
-
};
|
|
34483
|
-
const isTailwindAtLeast = (detected, required) => {
|
|
34484
|
-
if (detected === null) return true;
|
|
34485
|
-
if (detected.major !== required.major) return detected.major > required.major;
|
|
34486
|
-
return detected.minor >= required.minor;
|
|
34487
|
-
};
|
|
34488
34498
|
const messageFromUnknown = (error) => error instanceof Error ? error.message : String(error);
|
|
34489
34499
|
var InvalidGlobPatternError = class extends Error {
|
|
34490
34500
|
pattern;
|
|
@@ -37186,64 +37196,64 @@ function* collectSecurityScanFiles(rootDirectory) {
|
|
|
37186
37196
|
}
|
|
37187
37197
|
}
|
|
37188
37198
|
}
|
|
37189
|
-
|
|
37190
|
-
|
|
37191
|
-
|
|
37192
|
-
|
|
37193
|
-
|
|
37194
|
-
|
|
37195
|
-
|
|
37199
|
+
const CLIENT_ONLY_FRAMEWORKS = new Set([
|
|
37200
|
+
"vite",
|
|
37201
|
+
"cra",
|
|
37202
|
+
"gatsby",
|
|
37203
|
+
"react-native",
|
|
37204
|
+
"expo"
|
|
37205
|
+
]);
|
|
37206
|
+
const addMajorLadder = (capabilities, name, major, earliest, latest) => {
|
|
37207
|
+
if (major === null) return;
|
|
37208
|
+
const cappedMajor = Math.min(major, latest);
|
|
37209
|
+
for (let candidate = earliest; candidate <= cappedMajor; candidate += 1) capabilities.add(`${name}:${candidate}`);
|
|
37210
|
+
};
|
|
37196
37211
|
const buildCapabilities = (project) => {
|
|
37197
37212
|
const capabilities = /* @__PURE__ */ new Set();
|
|
37198
37213
|
capabilities.add(project.framework);
|
|
37199
|
-
if (
|
|
37214
|
+
if (project.reactVersion !== null || project.preactVersion !== null) capabilities.add("react");
|
|
37200
37215
|
if (project.framework === "expo" || project.framework === "react-native" || project.hasReactNativeWorkspace) capabilities.add("react-native");
|
|
37201
37216
|
if (project.expoVersion !== null) capabilities.add("expo");
|
|
37217
|
+
if ((project.framework === "nextjs" || project.framework === "tanstack-start" || project.framework === "remix") && !project.isStaticExport) capabilities.add("server-actions");
|
|
37218
|
+
if (CLIENT_ONLY_FRAMEWORKS.has(project.framework)) capabilities.add("client-only");
|
|
37219
|
+
if (project.isStaticExport) capabilities.add("nextjs:static-export");
|
|
37202
37220
|
if (project.nextjsMajorVersion !== null && project.nextjsMajorVersion >= 15) capabilities.add("nextjs:15");
|
|
37203
|
-
|
|
37204
|
-
if (
|
|
37205
|
-
|
|
37206
|
-
|
|
37207
|
-
|
|
37208
|
-
|
|
37209
|
-
|
|
37210
|
-
|
|
37211
|
-
|
|
37212
|
-
|
|
37213
|
-
|
|
37214
|
-
if (project.
|
|
37215
|
-
capabilities.add("tailwind");
|
|
37216
|
-
if (isTailwindAtLeast(parseTailwindMajorMinor(project.tailwindVersion), {
|
|
37217
|
-
major: 3,
|
|
37218
|
-
minor: 4
|
|
37219
|
-
})) capabilities.add("tailwind:3.4");
|
|
37220
|
-
}
|
|
37221
|
-
if (project.zodVersion !== null) {
|
|
37222
|
-
capabilities.add("zod");
|
|
37223
|
-
if (project.zodMajorVersion !== null && project.zodMajorVersion >= 4) capabilities.add("zod:4");
|
|
37224
|
-
}
|
|
37221
|
+
addMajorLadder(capabilities, "react", project.reactMajorVersion, 17, 30);
|
|
37222
|
+
if (project.reactMajorVersion !== null && project.reactMajorVersion >= 19 && isMajorMinorAtLeast(parseReactMajorMinor(project.reactVersion), {
|
|
37223
|
+
major: 19,
|
|
37224
|
+
minor: 2
|
|
37225
|
+
})) capabilities.add("react:19.2");
|
|
37226
|
+
if (project.tailwindVersion !== null) capabilities.add("tailwind");
|
|
37227
|
+
if (project.tailwindVersion !== null && isMajorMinorAtLeast(parseTailwindMajorMinor(project.tailwindVersion), {
|
|
37228
|
+
major: 3,
|
|
37229
|
+
minor: 4
|
|
37230
|
+
})) capabilities.add("tailwind:3.4");
|
|
37231
|
+
if (project.zodVersion !== null) capabilities.add("zod");
|
|
37232
|
+
if (project.zodMajorVersion !== null && project.zodMajorVersion >= 4) capabilities.add("zod:4");
|
|
37225
37233
|
if (project.isPreES2023Target) capabilities.add("pre-es2023");
|
|
37226
37234
|
if (project.hasReactCompiler) capabilities.add("react-compiler");
|
|
37227
37235
|
if (project.hasTanStackQuery) capabilities.add("tanstack-query");
|
|
37228
37236
|
if (project.hasTypeScript) capabilities.add("typescript");
|
|
37229
|
-
if (project.preactVersion !== null)
|
|
37230
|
-
|
|
37231
|
-
|
|
37232
|
-
if (preactMajor !== null) {
|
|
37233
|
-
const cappedPreactMajor = Math.min(preactMajor, 20);
|
|
37234
|
-
for (let major = 10; major <= cappedPreactMajor; major++) capabilities.add(`preact:${major}`);
|
|
37235
|
-
}
|
|
37236
|
-
if (project.reactVersion === null) capabilities.add("pure-preact");
|
|
37237
|
-
}
|
|
37237
|
+
if (project.preactVersion !== null) capabilities.add("preact");
|
|
37238
|
+
addMajorLadder(capabilities, "preact", project.preactMajorVersion, 10, 20);
|
|
37239
|
+
if (project.preactVersion !== null && project.reactVersion === null) capabilities.add("pure-preact");
|
|
37238
37240
|
return capabilities;
|
|
37239
37241
|
};
|
|
37240
|
-
const
|
|
37242
|
+
const capabilitiesByProject = /* @__PURE__ */ new WeakMap();
|
|
37243
|
+
const getCapabilities = (project) => {
|
|
37244
|
+
const cached = capabilitiesByProject.get(project);
|
|
37245
|
+
if (cached !== void 0) return cached;
|
|
37246
|
+
const capabilities = buildCapabilities(project);
|
|
37247
|
+
capabilitiesByProject.set(project, capabilities);
|
|
37248
|
+
return capabilities;
|
|
37249
|
+
};
|
|
37250
|
+
const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledWhen) => {
|
|
37241
37251
|
if (requires) {
|
|
37242
37252
|
for (const capability of requires) if (!capabilities.has(capability)) return false;
|
|
37243
37253
|
}
|
|
37244
37254
|
if (tags?.includes("react-jsx-only") && !capabilities.has("react")) return false;
|
|
37245
|
-
if (
|
|
37246
|
-
for (const capability of
|
|
37255
|
+
if (disabledWhen) {
|
|
37256
|
+
for (const capability of disabledWhen) if (capabilities.has(capability)) return false;
|
|
37247
37257
|
}
|
|
37248
37258
|
if (tags) {
|
|
37249
37259
|
for (const tag of tags) if (ignoredTags.has(tag)) return false;
|
|
@@ -37254,14 +37264,14 @@ const yieldToEventLoop = () => new Promise((resolve) => {
|
|
|
37254
37264
|
setImmediate(resolve);
|
|
37255
37265
|
});
|
|
37256
37266
|
const createSecurityScanSession = (rootDirectory, options) => {
|
|
37257
|
-
const capabilities = options.project ?
|
|
37267
|
+
const capabilities = options.project ? getCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
37258
37268
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
37259
37269
|
const enabledScanRules = REACT_DOCTOR_RULES.flatMap((entry) => {
|
|
37260
37270
|
const rule = entry.rule;
|
|
37261
37271
|
const scan = rule.scan;
|
|
37262
37272
|
if (typeof scan !== "function") return [];
|
|
37263
37273
|
if (rule.defaultEnabled === false) return [];
|
|
37264
|
-
if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.
|
|
37274
|
+
if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledWhen)) return [];
|
|
37265
37275
|
return [{
|
|
37266
37276
|
entry,
|
|
37267
37277
|
scan,
|
|
@@ -37281,6 +37291,7 @@ const createSecurityScanSession = (rootDirectory, options) => {
|
|
|
37281
37291
|
return status === true;
|
|
37282
37292
|
};
|
|
37283
37293
|
const scanFileByRule = function* (file) {
|
|
37294
|
+
if (file === null) return;
|
|
37284
37295
|
for (const { entry, scan, committedFilesOnly } of enabledScanRules) {
|
|
37285
37296
|
for (const finding of scan(file)) {
|
|
37286
37297
|
if (committedFilesOnly && isFileGitIgnored(file)) continue;
|
|
@@ -39161,7 +39172,7 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
39161
39172
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
39162
39173
|
const jsPlugins = [];
|
|
39163
39174
|
if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
|
|
39164
|
-
const capabilities =
|
|
39175
|
+
const capabilities = getCapabilities(project);
|
|
39165
39176
|
const enabledReactDoctorRules = {};
|
|
39166
39177
|
for (const registryEntry of REACT_DOCTOR_RULES) {
|
|
39167
39178
|
const rule = reactDoctorPlugin.rules[registryEntry.id];
|
|
@@ -39172,7 +39183,7 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
39172
39183
|
if (rule.scan !== void 0) continue;
|
|
39173
39184
|
if (customRulesOnly && registryEntry.originallyExternal) continue;
|
|
39174
39185
|
if (rule.framework !== "global" && !rule.requires) continue;
|
|
39175
|
-
if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.
|
|
39186
|
+
if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledWhen)) continue;
|
|
39176
39187
|
const explicitRuleOverride = resolveRuleSeverityOverride({ ruleKey: registryEntry.key }, severityControls);
|
|
39177
39188
|
if (rule.defaultEnabled === false && explicitRuleOverride === void 0) continue;
|
|
39178
39189
|
const severity = resolveRuleSeverityOverride({
|
|
@@ -39203,6 +39214,7 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
39203
39214
|
settings: { "react-doctor": {
|
|
39204
39215
|
framework: project.framework,
|
|
39205
39216
|
rootDirectory: resolveSettingsRootDirectory(project.rootDirectory),
|
|
39217
|
+
capabilities: [...capabilities].sort(),
|
|
39206
39218
|
...project.shopifyFlashListMajorVersion !== null ? { shopifyFlashListMajorVersion: project.shopifyFlashListMajorVersion } : {},
|
|
39207
39219
|
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {}
|
|
39208
39220
|
} },
|
|
@@ -39493,21 +39505,6 @@ const mapWithConcurrency = async (items, concurrency, task) => {
|
|
|
39493
39505
|
if (errors.length > 0) throw errors[0];
|
|
39494
39506
|
return results;
|
|
39495
39507
|
};
|
|
39496
|
-
const getPublicEnvPrefix = (framework) => {
|
|
39497
|
-
switch (framework) {
|
|
39498
|
-
case "nextjs": return "NEXT_PUBLIC_*";
|
|
39499
|
-
case "vite":
|
|
39500
|
-
case "tanstack-start": return "VITE_*";
|
|
39501
|
-
case "cra": return "REACT_APP_*";
|
|
39502
|
-
case "gatsby": return "GATSBY_*";
|
|
39503
|
-
default: return null;
|
|
39504
|
-
}
|
|
39505
|
-
};
|
|
39506
|
-
const buildNoSecretsRecommendation = (project, fallbackRecommendation) => {
|
|
39507
|
-
const publicEnvPrefix = getPublicEnvPrefix(project.framework);
|
|
39508
|
-
if (!publicEnvPrefix) return fallbackRecommendation;
|
|
39509
|
-
return `Move secrets to server-only code. In ${formatFrameworkName(project.framework)}, only \`${publicEnvPrefix}\` env vars are exposed to the browser, and they must not contain secrets`;
|
|
39510
|
-
};
|
|
39511
39508
|
const REANIMATED_SHARED_VALUE_HINT = "If this is a Reanimated shared value, prefer its React Compiler-compatible `.get()` / `.set()` accessors over `.value` — https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue/#react-compiler-support";
|
|
39512
39509
|
const ACCESSORS_MIN_VERSION = "3.15.0";
|
|
39513
39510
|
const supportsAccessors = (reanimatedVersion) => {
|
|
@@ -40188,8 +40185,14 @@ const PLUGIN_CATEGORY_MAP = {
|
|
|
40188
40185
|
};
|
|
40189
40186
|
const lookupOwnString = (record, key) => Object.hasOwn(record, key) ? record[key] : void 0;
|
|
40190
40187
|
const getRuleRecommendation = (ruleName, project) => {
|
|
40191
|
-
|
|
40192
|
-
return
|
|
40188
|
+
const rule = reactDoctorPlugin.rules[ruleName];
|
|
40189
|
+
if (!rule) return void 0;
|
|
40190
|
+
if (rule.recommendationFor) {
|
|
40191
|
+
const capabilities = getCapabilities(project);
|
|
40192
|
+
const conditionalRecommendation = rule.recommendationFor((capability) => capabilities.has(capability));
|
|
40193
|
+
if (conditionalRecommendation !== void 0) return conditionalRecommendation;
|
|
40194
|
+
}
|
|
40195
|
+
return rule.recommendation;
|
|
40193
40196
|
};
|
|
40194
40197
|
const getRuleCategory = (ruleName) => reactDoctorPlugin.rules[ruleName]?.category;
|
|
40195
40198
|
const getRuleTitle = (ruleName) => reactDoctorPlugin.rules[ruleName]?.title;
|
|
@@ -40343,7 +40346,9 @@ const SANITIZED_ENV = buildOxlintChildEnv(process.env);
|
|
|
40343
40346
|
* - `OxlintBatchExceeded { kind: "output-too-large" }` — stdout+stderr
|
|
40344
40347
|
* crossed `OXLINT_OUTPUT_MAX_BYTES`.
|
|
40345
40348
|
* - `OxlintBatchExceeded { kind: "oom" | "killed" }` — child exited
|
|
40346
|
-
* on a signal (SIGABRT → OOM, others → generic kill)
|
|
40349
|
+
* on a signal (SIGABRT → OOM, others → generic kill), or with one
|
|
40350
|
+
* of the abort exit codes Windows reports instead of a signal
|
|
40351
|
+
* (`ABORT_EXIT_CODES` → OOM).
|
|
40347
40352
|
* - `OxlintSpawnFailed { cause }` — `spawn` itself errored, or the
|
|
40348
40353
|
* child exited successfully but printed only stderr.
|
|
40349
40354
|
*
|
|
@@ -40409,7 +40414,7 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
|
|
|
40409
40414
|
clearAbortListener();
|
|
40410
40415
|
reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: error }) }));
|
|
40411
40416
|
});
|
|
40412
|
-
child.on("close", (
|
|
40417
|
+
child.on("close", (code, signal) => {
|
|
40413
40418
|
clearTimeout(timeoutHandle);
|
|
40414
40419
|
clearAbortListener();
|
|
40415
40420
|
if (didKillForSize) {
|
|
@@ -40419,10 +40424,11 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
|
|
|
40419
40424
|
}) }));
|
|
40420
40425
|
return;
|
|
40421
40426
|
}
|
|
40422
|
-
|
|
40427
|
+
const isAbortExitCode = code !== null && ABORT_EXIT_CODES.has(code);
|
|
40428
|
+
if (signal || isAbortExitCode) {
|
|
40423
40429
|
const stderrOutput = Buffer.concat(stderrBuffers).toString("utf-8").trim();
|
|
40424
|
-
const isOom = signal === "SIGABRT";
|
|
40425
|
-
const detailParts = [`killed by ${signal}`];
|
|
40430
|
+
const isOom = signal === "SIGABRT" || isAbortExitCode;
|
|
40431
|
+
const detailParts = [signal ? `killed by ${signal}` : `aborted with exit code ${code}`];
|
|
40426
40432
|
if (isOom) detailParts.push("try scanning fewer files with --diff");
|
|
40427
40433
|
if (stderrOutput) detailParts.push(stderrOutput);
|
|
40428
40434
|
reject(new ReactDoctorError({ reason: new OxlintBatchExceeded({
|
|
@@ -40465,12 +40471,19 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
40465
40471
|
* (surfaced via `onPartialFailure`) so JSON-mode consumers see WHICH
|
|
40466
40472
|
* files were skipped instead of silently losing them.
|
|
40467
40473
|
*
|
|
40468
|
-
* Parallel runs (concurrency > 1) get
|
|
40469
|
-
* fails with a resource-exhaustion error that's exclusive
|
|
40470
|
-
* many oxlint subprocesses at once (EAGAIN / EMFILE /
|
|
40471
|
-
* see `isParallelismRelatedSpawnError`), the whole
|
|
40472
|
-
* with a single worker. That's the only failure a
|
|
40473
|
-
* clear, so every other error class is left to
|
|
40474
|
+
* Parallel runs (concurrency > 1) get two extra safety nets:
|
|
40475
|
+
* - If the pass fails with a resource-exhaustion error that's exclusive
|
|
40476
|
+
* to running many oxlint subprocesses at once (EAGAIN / EMFILE /
|
|
40477
|
+
* ENFILE / ENOMEM — see `isParallelismRelatedSpawnError`), the whole
|
|
40478
|
+
* pass replays once with a single worker. That's the only failure a
|
|
40479
|
+
* serial replay can clear, so every other error class is left to
|
|
40480
|
+
* propagate.
|
|
40481
|
+
* - Files dropped because oxlint's native binding SIGABRT'd under
|
|
40482
|
+
* memory pressure (`OxlintBatchExceeded { kind: "oom" }`) get one
|
|
40483
|
+
* serial single-file-batch rescue pass: the OOM is usually a function
|
|
40484
|
+
* of N concurrent oxlint allocator arenas, not the file itself, so a
|
|
40485
|
+
* lone process with the machine to itself typically clears it —
|
|
40486
|
+
* turning a partial scan into a complete (if slower) one.
|
|
40474
40487
|
*
|
|
40475
40488
|
* Errors that aren't splittable and aren't parallelism-related (oxlint
|
|
40476
40489
|
* config crash, JS plugin resolution failure, etc.) propagate to the
|
|
@@ -40480,13 +40493,16 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
40480
40493
|
const spawnLintBatches = async (input) => {
|
|
40481
40494
|
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal, deadlineEpochMs } = input;
|
|
40482
40495
|
const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
|
|
40483
|
-
|
|
40484
|
-
const
|
|
40496
|
+
let rescueDeadlineEpochMs;
|
|
40497
|
+
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0 || rescueDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(rescueDeadlineEpochMs) === 0;
|
|
40498
|
+
const runBatchPass = async (concurrency, passBatches, passOnFileProgress) => {
|
|
40499
|
+
const totalFileCount = passBatches.reduce((sum, batch) => sum + batch.length, 0);
|
|
40485
40500
|
const allDiagnostics = [];
|
|
40486
40501
|
const droppedFiles = [];
|
|
40502
|
+
const oomDroppedFiles = [];
|
|
40487
40503
|
const deadlineSkippedFiles = [];
|
|
40488
|
-
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0;
|
|
40489
40504
|
let firstDropReason = null;
|
|
40505
|
+
let firstNonOomDropReason = null;
|
|
40490
40506
|
const spawnLintBatch = async (batch, depth, batchState) => {
|
|
40491
40507
|
if (isPastDeadline()) {
|
|
40492
40508
|
deadlineSkippedFiles.push(...batch);
|
|
@@ -40508,12 +40524,14 @@ const spawnLintBatches = async (input) => {
|
|
|
40508
40524
|
const isDepthCapReached = depth >= splitMaxDepth;
|
|
40509
40525
|
if (batch.length <= 1 || isBudgetElapsed || isDepthCapReached) {
|
|
40510
40526
|
droppedFiles.push(...batch);
|
|
40511
|
-
|
|
40512
|
-
|
|
40513
|
-
|
|
40514
|
-
|
|
40515
|
-
|
|
40516
|
-
}
|
|
40527
|
+
const isOomDrop = error.reason._tag === "OxlintBatchExceeded" && error.reason.kind === "oom";
|
|
40528
|
+
if (isOomDrop) oomDroppedFiles.push(...batch);
|
|
40529
|
+
let limitHint = "";
|
|
40530
|
+
if (isDepthCapReached) limitHint = ` (split depth cap of ${splitMaxDepth} levels reached)`;
|
|
40531
|
+
else if (isBudgetElapsed) limitHint = ` (split budget of ${splitTotalBudgetMs / MILLISECONDS_PER_SECOND}s exhausted at depth ${depth})`;
|
|
40532
|
+
const dropReason = batch.length > 1 ? `${error.message}${limitHint}` : error.message;
|
|
40533
|
+
firstDropReason ??= dropReason;
|
|
40534
|
+
if (!isOomDrop) firstNonOomDropReason ??= dropReason;
|
|
40517
40535
|
return [];
|
|
40518
40536
|
}
|
|
40519
40537
|
const splitIndex = Math.ceil(batch.length / 2);
|
|
@@ -40523,16 +40541,16 @@ const spawnLintBatches = async (input) => {
|
|
|
40523
40541
|
let startedFileCount = 0;
|
|
40524
40542
|
let scannedFileCount = 0;
|
|
40525
40543
|
let displayedFileCount = 0;
|
|
40526
|
-
const progressTimer =
|
|
40544
|
+
const progressTimer = passOnFileProgress && totalFileCount > 1 ? setInterval(() => {
|
|
40527
40545
|
const ceiling = Math.min(startedFileCount, totalFileCount - 1);
|
|
40528
40546
|
if (displayedFileCount < ceiling) {
|
|
40529
40547
|
displayedFileCount += 1;
|
|
40530
|
-
|
|
40548
|
+
passOnFileProgress(displayedFileCount, totalFileCount);
|
|
40531
40549
|
}
|
|
40532
40550
|
}, 50) : null;
|
|
40533
40551
|
progressTimer?.unref?.();
|
|
40534
40552
|
try {
|
|
40535
|
-
const batchResults = await mapWithConcurrency(
|
|
40553
|
+
const batchResults = await mapWithConcurrency(passBatches, concurrency, async (batch) => {
|
|
40536
40554
|
if (isPastDeadline()) {
|
|
40537
40555
|
deadlineSkippedFiles.push(...batch);
|
|
40538
40556
|
return [];
|
|
@@ -40544,9 +40562,9 @@ const spawnLintBatches = async (input) => {
|
|
|
40544
40562
|
};
|
|
40545
40563
|
const batchDiagnostics = await spawnLintBatch(batch, 0, batchState);
|
|
40546
40564
|
scannedFileCount += batch.length - batchState.deadlineSkippedFileCount;
|
|
40547
|
-
if (
|
|
40565
|
+
if (passOnFileProgress) {
|
|
40548
40566
|
displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
|
|
40549
|
-
|
|
40567
|
+
passOnFileProgress(displayedFileCount, totalFileCount);
|
|
40550
40568
|
}
|
|
40551
40569
|
return batchDiagnostics;
|
|
40552
40570
|
});
|
|
@@ -40554,22 +40572,53 @@ const spawnLintBatches = async (input) => {
|
|
|
40554
40572
|
} finally {
|
|
40555
40573
|
if (progressTimer !== null) clearInterval(progressTimer);
|
|
40556
40574
|
}
|
|
40557
|
-
|
|
40558
|
-
|
|
40559
|
-
|
|
40575
|
+
return {
|
|
40576
|
+
diagnostics: allDiagnostics,
|
|
40577
|
+
droppedFiles,
|
|
40578
|
+
oomDroppedFiles,
|
|
40579
|
+
deadlineSkippedFiles,
|
|
40580
|
+
firstDropReason,
|
|
40581
|
+
firstNonOomDropReason
|
|
40560
40582
|
};
|
|
40561
|
-
const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
|
|
40562
|
-
reportSkippedFiles(droppedFiles, (fileListPreview) => `${droppedFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
|
|
40563
|
-
reportSkippedFiles(deadlineSkippedFiles, (fileListPreview) => `${deadlineSkippedFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
|
|
40564
|
-
return allDiagnostics;
|
|
40565
40583
|
};
|
|
40566
|
-
let
|
|
40584
|
+
let outcome;
|
|
40585
|
+
let effectiveConcurrency = requestedConcurrency;
|
|
40567
40586
|
try {
|
|
40568
|
-
|
|
40587
|
+
outcome = await runBatchPass(requestedConcurrency, fileBatches, onFileProgress);
|
|
40569
40588
|
} catch (error) {
|
|
40570
40589
|
if (requestedConcurrency <= 1 || !isParallelismRelatedSpawnError(error)) throw error;
|
|
40571
|
-
|
|
40590
|
+
effectiveConcurrency = 1;
|
|
40591
|
+
outcome = await runBatchPass(1, fileBatches, onFileProgress);
|
|
40592
|
+
}
|
|
40593
|
+
let { droppedFiles, firstDropReason } = outcome;
|
|
40594
|
+
const diagnostics = outcome.diagnostics;
|
|
40595
|
+
if (outcome.oomDroppedFiles.length > 0 && effectiveConcurrency > 1 && !isPastDeadline()) {
|
|
40596
|
+
rescueDeadlineEpochMs = Date.now() + OXLINT_OOM_RESCUE_BUDGET_MS;
|
|
40597
|
+
const rescuedFiles = /* @__PURE__ */ new Set();
|
|
40598
|
+
let firstRescueFailureReason = null;
|
|
40599
|
+
for (const oomDroppedFile of outcome.oomDroppedFiles) {
|
|
40600
|
+
if (isPastDeadline()) break;
|
|
40601
|
+
try {
|
|
40602
|
+
const rescueOutcome = await runBatchPass(1, [[oomDroppedFile]], void 0);
|
|
40603
|
+
diagnostics.push(...rescueOutcome.diagnostics);
|
|
40604
|
+
if (rescueOutcome.droppedFiles.length > 0 || rescueOutcome.deadlineSkippedFiles.length > 0) firstRescueFailureReason ??= rescueOutcome.firstDropReason;
|
|
40605
|
+
else rescuedFiles.add(oomDroppedFile);
|
|
40606
|
+
} catch (error) {
|
|
40607
|
+
firstRescueFailureReason ??= error instanceof Error ? error.message : String(error);
|
|
40608
|
+
}
|
|
40609
|
+
}
|
|
40610
|
+
rescueDeadlineEpochMs = void 0;
|
|
40611
|
+
droppedFiles = droppedFiles.filter((filePath) => !rescuedFiles.has(filePath));
|
|
40612
|
+
if (droppedFiles.length === 0) firstDropReason = null;
|
|
40613
|
+
else firstDropReason = firstRescueFailureReason ?? outcome.firstNonOomDropReason ?? firstDropReason;
|
|
40572
40614
|
}
|
|
40615
|
+
const reportSkippedFiles = (skippedFiles, buildMessage) => {
|
|
40616
|
+
if (skippedFiles.length === 0 || !onPartialFailure) return;
|
|
40617
|
+
onPartialFailure(buildMessage(`${skippedFiles.slice(0, 3).join(", ")}${skippedFiles.length > 3 ? `, +${skippedFiles.length - 3} more` : ""}`));
|
|
40618
|
+
};
|
|
40619
|
+
const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
|
|
40620
|
+
reportSkippedFiles(droppedFiles, (fileListPreview) => `${droppedFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
|
|
40621
|
+
reportSkippedFiles(outcome.deadlineSkippedFiles, (fileListPreview) => `${outcome.deadlineSkippedFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
|
|
40573
40622
|
return dedupeDiagnostics(diagnostics);
|
|
40574
40623
|
};
|
|
40575
40624
|
let didValidate = false;
|
|
@@ -42563,7 +42612,7 @@ const computeConfigFingerprint = (projectDirectory, version) => {
|
|
|
42563
42612
|
/** Display name used in client-facing messages and progress titles. */
|
|
42564
42613
|
const SERVER_DISPLAY_NAME = "React Doctor";
|
|
42565
42614
|
/** Server version reported in `serverInfo`; injected at build, `dev` from source. */
|
|
42566
|
-
const SERVER_VERSION = "0.7.
|
|
42615
|
+
const SERVER_VERSION = "0.7.3";
|
|
42567
42616
|
/** `Diagnostic.source` shown next to every published diagnostic. */
|
|
42568
42617
|
const DIAGNOSTIC_SOURCE = "react-doctor";
|
|
42569
42618
|
/**
|
|
@@ -44614,5 +44663,5 @@ const startLanguageServer = () => {
|
|
|
44614
44663
|
};
|
|
44615
44664
|
//#endregion
|
|
44616
44665
|
export { startLanguageServer };
|
|
44617
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
44618
|
-
//# debugId=
|
|
44666
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="6105fd41-b8bf-5c49-b0bd-8979e737f179")}catch(e){}}();
|
|
44667
|
+
//# debugId=6105fd41-b8bf-5c49-b0bd-8979e737f179
|