react-doctor 0.7.1 → 0.7.2-dev.08b768b
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 +1219 -689
- package/dist/index.d.ts +21 -2
- package/dist/index.js +1212 -680
- package/dist/lsp.js +1170 -667
- package/package.json +6 -6
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!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]="
|
|
2
|
+
!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]="9aed4e70-dd94-51e8-9810-f8e8945d80a3")}catch(e){}}();
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import * as NodeChildProcess from "node:child_process";
|
|
5
5
|
import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
@@ -9,7 +9,7 @@ import * as NFS from "node:fs";
|
|
|
9
9
|
import fs, { mkdtempSync, rmSync } from "node:fs";
|
|
10
10
|
import process$1 from "node:process";
|
|
11
11
|
import * as ts from "typescript";
|
|
12
|
-
import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
|
|
12
|
+
import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
|
|
13
13
|
import * as OS from "node:os";
|
|
14
14
|
import os, { tmpdir } from "node:os";
|
|
15
15
|
import { parseJSON5 } from "confbox";
|
|
@@ -35959,6 +35959,25 @@ var AmbiguousProjectError = class extends Error {
|
|
|
35959
35959
|
}
|
|
35960
35960
|
};
|
|
35961
35961
|
const isProjectDiscoveryError = (value) => value instanceof ProjectNotFoundError || value instanceof NoReactDependencyError || value instanceof PackageJsonNotFoundError || value instanceof NotADirectoryError || value instanceof AmbiguousProjectError;
|
|
35962
|
+
const isErrnoException = (error) => error instanceof Error && "code" in error;
|
|
35963
|
+
const IGNORABLE_READDIR_ERROR_CODES = new Set([
|
|
35964
|
+
"EACCES",
|
|
35965
|
+
"EPERM",
|
|
35966
|
+
"ENOENT",
|
|
35967
|
+
"ENOTDIR",
|
|
35968
|
+
"EINVAL",
|
|
35969
|
+
"ELOOP",
|
|
35970
|
+
"ENAMETOOLONG"
|
|
35971
|
+
]);
|
|
35972
|
+
const isIgnorableReaddirError = (error) => isErrnoException(error) && typeof error.code === "string" && IGNORABLE_READDIR_ERROR_CODES.has(error.code);
|
|
35973
|
+
const readDirectoryEntries = (directoryPath) => {
|
|
35974
|
+
try {
|
|
35975
|
+
return NFS.readdirSync(directoryPath, { withFileTypes: true });
|
|
35976
|
+
} catch (error) {
|
|
35977
|
+
if (isIgnorableReaddirError(error)) return [];
|
|
35978
|
+
throw error;
|
|
35979
|
+
}
|
|
35980
|
+
};
|
|
35962
35981
|
const isFile = (filePath) => {
|
|
35963
35982
|
try {
|
|
35964
35983
|
return NFS.statSync(filePath).isFile();
|
|
@@ -35966,10 +35985,24 @@ const isFile = (filePath) => {
|
|
|
35966
35985
|
return false;
|
|
35967
35986
|
}
|
|
35968
35987
|
};
|
|
35988
|
+
const isDirectory = (directoryPath) => {
|
|
35989
|
+
try {
|
|
35990
|
+
return NFS.statSync(directoryPath).isDirectory();
|
|
35991
|
+
} catch {
|
|
35992
|
+
return false;
|
|
35993
|
+
}
|
|
35994
|
+
};
|
|
35995
|
+
const isPlainObject = (value) => {
|
|
35996
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
35997
|
+
const prototype = Object.getPrototypeOf(value);
|
|
35998
|
+
return prototype === null || prototype === Object.prototype;
|
|
35999
|
+
};
|
|
35969
36000
|
const SOURCE_FILE_PATTERN = /\.(tsx?|jsx?|mts|mjs)$/;
|
|
35970
36001
|
const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
|
|
36002
|
+
const GENERATED_SOURCE_DIRECTORY_PATTERN = /(?:^|\/)__generated__\//;
|
|
35971
36003
|
const MINIFIED_SNIFF_BYTES = 65536;
|
|
35972
36004
|
const GIT_LS_FILES_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
|
|
36005
|
+
const SCANNED_DOT_DIRECTORIES = new Set([".dumi", ".storybook"]);
|
|
35973
36006
|
const IGNORED_DIRECTORIES = new Set([
|
|
35974
36007
|
".angular",
|
|
35975
36008
|
".astro",
|
|
@@ -36098,6 +36131,8 @@ const OXLINT_SPAWN_TIMEOUT_MS = 6e4;
|
|
|
36098
36131
|
const NODE_COMPILE_CACHE_DIR_NAME = "node-compile-cache";
|
|
36099
36132
|
const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
|
|
36100
36133
|
const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
|
|
36134
|
+
const ABORT_EXIT_CODES = new Set([134, 3221226505]);
|
|
36135
|
+
const OXLINT_OOM_RESCUE_BUDGET_MS = 6e4;
|
|
36101
36136
|
const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
|
|
36102
36137
|
const LINT_PHASE_TIMEOUT_MS = 3e5;
|
|
36103
36138
|
const SCAN_TOTAL_DEADLINE_MS = 9e5;
|
|
@@ -36144,7 +36179,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
36144
36179
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
36145
36180
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
36146
36181
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
36147
|
-
const CORE_PACKAGE_VERSION = "0.7.
|
|
36182
|
+
const CORE_PACKAGE_VERSION = "0.7.2";
|
|
36148
36183
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
36149
36184
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
36150
36185
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -36154,8 +36189,9 @@ const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
|
|
|
36154
36189
|
const SUPPLY_CHAIN_CACHE_TTL_MS = 864e5;
|
|
36155
36190
|
const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
|
|
36156
36191
|
const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
|
|
36157
|
-
const
|
|
36158
|
-
const
|
|
36192
|
+
const isIgnoredDirectoryName = (directoryName) => IGNORED_DIRECTORIES.has(directoryName) || directoryName.startsWith(".") && !SCANNED_DOT_DIRECTORIES.has(directoryName);
|
|
36193
|
+
const hasIgnoredPathSegment = (relativePath) => relativePath.split(/[/\\]/).slice(0, -1).some((segment) => isIgnoredDirectoryName(segment));
|
|
36194
|
+
const isLintableSourceFile = (filePath) => SOURCE_FILE_PATTERN.test(filePath) && !GENERATED_BUNDLE_FILE_PATTERN.test(filePath) && !GENERATED_SOURCE_DIRECTORY_PATTERN.test(filePath);
|
|
36159
36195
|
const isMinifiedSource = (absolutePath) => {
|
|
36160
36196
|
let fileDescriptor;
|
|
36161
36197
|
try {
|
|
@@ -36189,25 +36225,6 @@ const isLargeMinifiedFile = (absolutePath, knownSizeBytes) => {
|
|
|
36189
36225
|
cachedIsLargeMinifiedByPath.set(absolutePath, result);
|
|
36190
36226
|
return result;
|
|
36191
36227
|
};
|
|
36192
|
-
const isErrnoException = (error) => error instanceof Error && "code" in error;
|
|
36193
|
-
const IGNORABLE_READDIR_ERROR_CODES = new Set([
|
|
36194
|
-
"EACCES",
|
|
36195
|
-
"EPERM",
|
|
36196
|
-
"ENOENT",
|
|
36197
|
-
"ENOTDIR",
|
|
36198
|
-
"EINVAL",
|
|
36199
|
-
"ELOOP",
|
|
36200
|
-
"ENAMETOOLONG"
|
|
36201
|
-
]);
|
|
36202
|
-
const isIgnorableReaddirError = (error) => isErrnoException(error) && typeof error.code === "string" && IGNORABLE_READDIR_ERROR_CODES.has(error.code);
|
|
36203
|
-
const readDirectoryEntries = (directoryPath) => {
|
|
36204
|
-
try {
|
|
36205
|
-
return NFS.readdirSync(directoryPath, { withFileTypes: true });
|
|
36206
|
-
} catch (error) {
|
|
36207
|
-
if (isIgnorableReaddirError(error)) return [];
|
|
36208
|
-
throw error;
|
|
36209
|
-
}
|
|
36210
|
-
};
|
|
36211
36228
|
function* walkSourceTreeFiles(rootDirectory) {
|
|
36212
36229
|
const stack = [rootDirectory];
|
|
36213
36230
|
while (stack.length > 0) {
|
|
@@ -36216,7 +36233,7 @@ function* walkSourceTreeFiles(rootDirectory) {
|
|
|
36216
36233
|
for (const entry of readDirectoryEntries(currentDirectory)) {
|
|
36217
36234
|
const absolutePath = Path.join(currentDirectory, entry.name);
|
|
36218
36235
|
if (entry.isDirectory()) {
|
|
36219
|
-
if (!
|
|
36236
|
+
if (!isIgnoredDirectoryName(entry.name)) stack.push(absolutePath);
|
|
36220
36237
|
continue;
|
|
36221
36238
|
}
|
|
36222
36239
|
if (entry.isFile()) yield {
|
|
@@ -36310,6 +36327,146 @@ const findMonorepoRoot = (startDirectory) => {
|
|
|
36310
36327
|
* byte-equivalent copy.
|
|
36311
36328
|
*/
|
|
36312
36329
|
const isProjectBoundary = (directory) => NFS.existsSync(Path.join(directory, ".git")) || isMonorepoRoot(directory);
|
|
36330
|
+
const TSCONFIG_FILENAME = "tsconfig.json";
|
|
36331
|
+
const isRelativeExtendsValue = (extendsValue) => extendsValue.startsWith("./") || extendsValue.startsWith("../") || Path.isAbsolute(extendsValue);
|
|
36332
|
+
const ensureJsonExtension = (filePath) => Path.extname(filePath) === "" ? `${filePath}.json` : filePath;
|
|
36333
|
+
const resolvePackageExtendsPath = (extendsValue, fromConfigDirectory) => {
|
|
36334
|
+
const requireFromConfig = createRequire(Path.join(fromConfigDirectory, "tsconfig.json"));
|
|
36335
|
+
const candidates = [
|
|
36336
|
+
extendsValue,
|
|
36337
|
+
ensureJsonExtension(extendsValue),
|
|
36338
|
+
`${extendsValue.replace(/\/$/, "")}/tsconfig.json`
|
|
36339
|
+
];
|
|
36340
|
+
for (const candidate of candidates) try {
|
|
36341
|
+
return requireFromConfig.resolve(candidate);
|
|
36342
|
+
} catch {
|
|
36343
|
+
continue;
|
|
36344
|
+
}
|
|
36345
|
+
return null;
|
|
36346
|
+
};
|
|
36347
|
+
const resolveExtendsPath = (extendsValue, fromConfigDirectory) => {
|
|
36348
|
+
if (isRelativeExtendsValue(extendsValue)) return ensureJsonExtension(Path.resolve(fromConfigDirectory, extendsValue));
|
|
36349
|
+
return resolvePackageExtendsPath(extendsValue, fromConfigDirectory);
|
|
36350
|
+
};
|
|
36351
|
+
const normalizeCompilerOptions = (compilerOptions) => {
|
|
36352
|
+
if (!isPlainObject(compilerOptions)) return { hasExplicitLib: false };
|
|
36353
|
+
const target = typeof compilerOptions.target === "string" ? compilerOptions.target : void 0;
|
|
36354
|
+
const hasExplicitLib = Object.hasOwn(compilerOptions, "lib");
|
|
36355
|
+
return {
|
|
36356
|
+
target,
|
|
36357
|
+
lib: Array.isArray(compilerOptions.lib) ? compilerOptions.lib.filter((entry) => typeof entry === "string") : void 0,
|
|
36358
|
+
hasExplicitLib
|
|
36359
|
+
};
|
|
36360
|
+
};
|
|
36361
|
+
const readTsConfig = (filePath) => {
|
|
36362
|
+
let content;
|
|
36363
|
+
try {
|
|
36364
|
+
content = NFS.readFileSync(filePath, "utf-8");
|
|
36365
|
+
} catch {
|
|
36366
|
+
return null;
|
|
36367
|
+
}
|
|
36368
|
+
const parsed = ts.parseConfigFileTextToJson(filePath, content);
|
|
36369
|
+
if (!isPlainObject(parsed.config)) return null;
|
|
36370
|
+
return {
|
|
36371
|
+
extends: typeof parsed.config.extends === "string" ? parsed.config.extends : void 0,
|
|
36372
|
+
referencePaths: normalizeReferencePaths(parsed.config.references),
|
|
36373
|
+
compilerOptions: normalizeCompilerOptions(parsed.config.compilerOptions)
|
|
36374
|
+
};
|
|
36375
|
+
};
|
|
36376
|
+
const normalizeReferencePaths = (references) => {
|
|
36377
|
+
if (!Array.isArray(references)) return [];
|
|
36378
|
+
return references.map((reference) => isPlainObject(reference) && typeof reference.path === "string" ? reference.path : null).filter((referencePath) => referencePath !== null);
|
|
36379
|
+
};
|
|
36380
|
+
const mergeCompilerOptions = (inherited, current) => {
|
|
36381
|
+
const target = current.target ?? inherited?.target;
|
|
36382
|
+
const hasExplicitLib = current.hasExplicitLib || Boolean(inherited?.hasExplicitLib);
|
|
36383
|
+
return {
|
|
36384
|
+
target,
|
|
36385
|
+
lib: current.hasExplicitLib ? current.lib : inherited?.lib,
|
|
36386
|
+
hasExplicitLib
|
|
36387
|
+
};
|
|
36388
|
+
};
|
|
36389
|
+
const readResolvedCompilerOptions = (tsConfigPath, extendsDepth, visitedPaths) => {
|
|
36390
|
+
const realPath = NFS.realpathSync.native(tsConfigPath);
|
|
36391
|
+
if (visitedPaths.has(realPath)) return null;
|
|
36392
|
+
const tsConfig = readTsConfig(realPath);
|
|
36393
|
+
if (!tsConfig) return null;
|
|
36394
|
+
const nextVisitedPaths = new Set(visitedPaths);
|
|
36395
|
+
nextVisitedPaths.add(realPath);
|
|
36396
|
+
if (tsConfig.extends && extendsDepth < 8) {
|
|
36397
|
+
const parentPath = resolveExtendsPath(tsConfig.extends, Path.dirname(realPath));
|
|
36398
|
+
if (parentPath && isFile(parentPath)) return mergeCompilerOptions(readResolvedCompilerOptions(parentPath, extendsDepth + 1, nextVisitedPaths), tsConfig.compilerOptions);
|
|
36399
|
+
}
|
|
36400
|
+
return tsConfig.compilerOptions;
|
|
36401
|
+
};
|
|
36402
|
+
const targetYearIsPreES2023 = (target) => {
|
|
36403
|
+
const year = ES_TARGET_YEAR_BY_NAME[target.toLowerCase()];
|
|
36404
|
+
return year !== void 0 && year < 2023;
|
|
36405
|
+
};
|
|
36406
|
+
const libEntryIncludesES2023Array = (entry) => {
|
|
36407
|
+
const normalizedEntry = entry.toLowerCase();
|
|
36408
|
+
if (normalizedEntry === "esnext" || normalizedEntry === "esnext.array") return true;
|
|
36409
|
+
const esYearMatch = /^es(\d{4})(?:\.(.+))?$/.exec(normalizedEntry);
|
|
36410
|
+
if (!esYearMatch) return false;
|
|
36411
|
+
if (Number(esYearMatch[1]) < 2023) return false;
|
|
36412
|
+
const component = esYearMatch[2];
|
|
36413
|
+
return component === void 0 || component === "array";
|
|
36414
|
+
};
|
|
36415
|
+
const libIncludesES2023 = (lib) => lib.some(libEntryIncludesES2023Array);
|
|
36416
|
+
const compilerOptionsArePreES2023 = (compilerOptions) => {
|
|
36417
|
+
if (compilerOptions.target) return targetYearIsPreES2023(compilerOptions.target);
|
|
36418
|
+
if (compilerOptions.hasExplicitLib) return !libIncludesES2023(compilerOptions.lib ?? []);
|
|
36419
|
+
return false;
|
|
36420
|
+
};
|
|
36421
|
+
const compilerOptionsDeclareTargetOrLib = (compilerOptions) => compilerOptions.hasExplicitLib || compilerOptions.target !== void 0;
|
|
36422
|
+
const detectPreES2023FromConfig = (tsConfigPath, visitedConfigPaths = /* @__PURE__ */ new Set()) => {
|
|
36423
|
+
if (visitedConfigPaths.has(tsConfigPath)) return false;
|
|
36424
|
+
const compilerOptions = readResolvedCompilerOptions(tsConfigPath, 0, /* @__PURE__ */ new Set());
|
|
36425
|
+
if (!compilerOptions) return false;
|
|
36426
|
+
if (!compilerOptionsDeclareTargetOrLib(compilerOptions)) {
|
|
36427
|
+
const tsConfig = readTsConfig(tsConfigPath);
|
|
36428
|
+
if (!tsConfig) return false;
|
|
36429
|
+
const nextVisitedConfigPaths = new Set(visitedConfigPaths);
|
|
36430
|
+
nextVisitedConfigPaths.add(tsConfigPath);
|
|
36431
|
+
const configDirectory = Path.dirname(tsConfigPath);
|
|
36432
|
+
return tsConfig.referencePaths.some((referencePath) => {
|
|
36433
|
+
const resolvedReferencePath = Path.resolve(configDirectory, referencePath);
|
|
36434
|
+
const referencedConfigPath = isFile(resolvedReferencePath) ? resolvedReferencePath : Path.join(resolvedReferencePath, TSCONFIG_FILENAME);
|
|
36435
|
+
return isFile(referencedConfigPath) && detectPreES2023FromConfig(referencedConfigPath, nextVisitedConfigPaths);
|
|
36436
|
+
});
|
|
36437
|
+
}
|
|
36438
|
+
return compilerOptionsArePreES2023(compilerOptions);
|
|
36439
|
+
};
|
|
36440
|
+
const detectPreES2023Target = (directory) => {
|
|
36441
|
+
const tsConfigPath = Path.join(directory, TSCONFIG_FILENAME);
|
|
36442
|
+
if (isFile(tsConfigPath)) return detectPreES2023FromConfig(tsConfigPath);
|
|
36443
|
+
for (const fallbackFilename of FALLBACK_TSCONFIG_FILENAMES) {
|
|
36444
|
+
const fallbackPath = Path.join(directory, fallbackFilename);
|
|
36445
|
+
if (isFile(fallbackPath)) return detectPreES2023FromConfig(fallbackPath);
|
|
36446
|
+
}
|
|
36447
|
+
return false;
|
|
36448
|
+
};
|
|
36449
|
+
const FALLBACK_TSCONFIG_FILENAMES = ["tsconfig.app.json", "tsconfig.build.json"];
|
|
36450
|
+
const FRAMEWORK_PACKAGES = {
|
|
36451
|
+
next: "nextjs",
|
|
36452
|
+
"@tanstack/react-start": "tanstack-start",
|
|
36453
|
+
vite: "vite",
|
|
36454
|
+
"react-scripts": "cra",
|
|
36455
|
+
"@remix-run/react": "remix",
|
|
36456
|
+
gatsby: "gatsby",
|
|
36457
|
+
expo: "expo",
|
|
36458
|
+
"react-native": "react-native"
|
|
36459
|
+
};
|
|
36460
|
+
const detectFramework = (dependencies) => {
|
|
36461
|
+
for (const [packageName, frameworkName] of Object.entries(FRAMEWORK_PACKAGES)) if (dependencies[packageName]) return frameworkName;
|
|
36462
|
+
if (dependencies.preact && !dependencies.react) return "preact";
|
|
36463
|
+
return "unknown";
|
|
36464
|
+
};
|
|
36465
|
+
const MOBILE_FRAMEWORKS = new Set(["expo", "react-native"]);
|
|
36466
|
+
const frameworkMergeRank = (framework) => {
|
|
36467
|
+
if (framework === "unknown") return 3;
|
|
36468
|
+
return MOBILE_FRAMEWORKS.has(framework) ? 2 : 1;
|
|
36469
|
+
};
|
|
36313
36470
|
const REACT_COMPILER_PACKAGES = new Set([
|
|
36314
36471
|
"babel-plugin-react-compiler",
|
|
36315
36472
|
"react-compiler-runtime",
|
|
@@ -36346,6 +36503,7 @@ const EXPO_APP_CONFIG_FILENAMES = [
|
|
|
36346
36503
|
];
|
|
36347
36504
|
const REACT_COMPILER_PACKAGE_REFERENCE_PATTERN = /babel-plugin-react-compiler|react-compiler-runtime|eslint-plugin-react-compiler|["']react-compiler["']/;
|
|
36348
36505
|
const REACT_COMPILER_ENABLED_FLAG_PATTERN = /["']?reactCompiler["']?\s*:\s*(?:true\b|\{)/;
|
|
36506
|
+
const STATIC_EXPORT_OUTPUT_PATTERN = /(?:^|[^.\w])["']?output["']?\s*:\s*["']export["']/m;
|
|
36349
36507
|
const hasCompilerPackage = (packageJson) => {
|
|
36350
36508
|
const allDependencies = {
|
|
36351
36509
|
...packageJson.peerDependencies,
|
|
@@ -36378,34 +36536,10 @@ const detectReactCompiler = (directory, packageJson) => {
|
|
|
36378
36536
|
}
|
|
36379
36537
|
return false;
|
|
36380
36538
|
};
|
|
36381
|
-
const
|
|
36382
|
-
|
|
36383
|
-
|
|
36384
|
-
|
|
36385
|
-
"react-scripts": "cra",
|
|
36386
|
-
"@remix-run/react": "remix",
|
|
36387
|
-
gatsby: "gatsby",
|
|
36388
|
-
expo: "expo",
|
|
36389
|
-
"react-native": "react-native"
|
|
36390
|
-
};
|
|
36391
|
-
const FRAMEWORK_DISPLAY_NAMES = {
|
|
36392
|
-
nextjs: "Next.js",
|
|
36393
|
-
"tanstack-start": "TanStack Start",
|
|
36394
|
-
vite: "Vite",
|
|
36395
|
-
cra: "Create React App",
|
|
36396
|
-
remix: "Remix",
|
|
36397
|
-
gatsby: "Gatsby",
|
|
36398
|
-
expo: "Expo",
|
|
36399
|
-
"react-native": "React Native",
|
|
36400
|
-
preact: "Preact",
|
|
36401
|
-
unknown: "React"
|
|
36402
|
-
};
|
|
36403
|
-
const formatFrameworkName = (framework) => FRAMEWORK_DISPLAY_NAMES[framework];
|
|
36404
|
-
const detectFramework = (dependencies) => {
|
|
36405
|
-
for (const [packageName, frameworkName] of Object.entries(FRAMEWORK_PACKAGES)) if (dependencies[packageName]) return frameworkName;
|
|
36406
|
-
if (dependencies.preact && !dependencies.react) return "preact";
|
|
36407
|
-
return "unknown";
|
|
36408
|
-
};
|
|
36539
|
+
const detectNextjsStaticExport = (directory) => NEXT_CONFIG_FILENAMES.some((filename) => {
|
|
36540
|
+
const filePath = Path.join(directory, filename);
|
|
36541
|
+
return isFile(filePath) && STATIC_EXPORT_OUTPUT_PATTERN.test(NFS.readFileSync(filePath, "utf-8"));
|
|
36542
|
+
});
|
|
36409
36543
|
const UNRESOLVABLE_PROTOCOL_VERSION = /^(?:file|git|github|https?|link|patch|portal|workspace|npm):/i;
|
|
36410
36544
|
const DIST_TAG_VERSION = /^[a-z][a-z0-9._-]*$/i;
|
|
36411
36545
|
const WILDCARD_VERSION = /^[*xX](?:\.[*xX])*$/;
|
|
@@ -36543,10 +36677,76 @@ const isConcreteDependencyVersion = (version) => {
|
|
|
36543
36677
|
const normalizedVersion = normalizeDependencyVersion(version);
|
|
36544
36678
|
return normalizedVersion !== null && /\d/.test(normalizedVersion);
|
|
36545
36679
|
};
|
|
36546
|
-
const
|
|
36547
|
-
if (
|
|
36548
|
-
|
|
36549
|
-
return
|
|
36680
|
+
const isMajorMinorAtLeast = (detected, required) => {
|
|
36681
|
+
if (detected === null) return true;
|
|
36682
|
+
if (detected.major !== required.major) return detected.major > required.major;
|
|
36683
|
+
return detected.minor >= required.minor;
|
|
36684
|
+
};
|
|
36685
|
+
const parseReactMajor = (reactVersion) => {
|
|
36686
|
+
if (typeof reactVersion !== "string") return null;
|
|
36687
|
+
return getLowestDependencyMajor(reactVersion);
|
|
36688
|
+
};
|
|
36689
|
+
const MAJOR_MINOR_PATTERN = /(\d{1,4})\.(\d{1,4})/;
|
|
36690
|
+
const MAJOR_ONLY_PATTERN = /(\d{1,4})/;
|
|
36691
|
+
const UPPER_BOUND_COMPARATOR_PATTERN = /<=?\s{0,8}\d{1,4}(?:\.\d{1,4}){0,2}(?:-[^\s,|]+)?/g;
|
|
36692
|
+
const parseReactMajorMinor = (reactVersion) => {
|
|
36693
|
+
if (typeof reactVersion !== "string") return null;
|
|
36694
|
+
const trimmed = reactVersion.trim();
|
|
36695
|
+
if (trimmed.length === 0) return null;
|
|
36696
|
+
const lowerBoundsOnly = trimmed.replace(UPPER_BOUND_COMPARATOR_PATTERN, " ").trim();
|
|
36697
|
+
if (lowerBoundsOnly.length === 0) return null;
|
|
36698
|
+
const majorMinorMatch = lowerBoundsOnly.match(MAJOR_MINOR_PATTERN);
|
|
36699
|
+
if (majorMinorMatch) {
|
|
36700
|
+
const major = Number.parseInt(majorMinorMatch[1], 10);
|
|
36701
|
+
const minor = Number.parseInt(majorMinorMatch[2], 10);
|
|
36702
|
+
if (!Number.isFinite(major) || major <= 0) return null;
|
|
36703
|
+
if (!Number.isFinite(minor) || minor < 0) return null;
|
|
36704
|
+
return {
|
|
36705
|
+
major,
|
|
36706
|
+
minor
|
|
36707
|
+
};
|
|
36708
|
+
}
|
|
36709
|
+
const majorOnlyMatch = lowerBoundsOnly.match(MAJOR_ONLY_PATTERN);
|
|
36710
|
+
if (!majorOnlyMatch) return null;
|
|
36711
|
+
const major = Number.parseInt(majorOnlyMatch[1], 10);
|
|
36712
|
+
if (!Number.isFinite(major) || major <= 0) return null;
|
|
36713
|
+
return {
|
|
36714
|
+
major,
|
|
36715
|
+
minor: 0
|
|
36716
|
+
};
|
|
36717
|
+
};
|
|
36718
|
+
const parseLowerBoundVersion = (versionSpec) => import_semver.validRange(versionSpec) !== null ? import_semver.minVersion(versionSpec) : import_semver.coerce(versionSpec);
|
|
36719
|
+
const parseTailwindMajorMinor = (tailwindVersion) => {
|
|
36720
|
+
if (typeof tailwindVersion !== "string") return null;
|
|
36721
|
+
const trimmed = tailwindVersion.trim();
|
|
36722
|
+
if (trimmed.length === 0) return null;
|
|
36723
|
+
const lowerBound = parseLowerBoundVersion(trimmed);
|
|
36724
|
+
if (lowerBound === null || lowerBound.major <= 0) return null;
|
|
36725
|
+
return {
|
|
36726
|
+
major: lowerBound.major,
|
|
36727
|
+
minor: lowerBound.minor
|
|
36728
|
+
};
|
|
36729
|
+
};
|
|
36730
|
+
const hasUpperBoundOnlyPeerRange = (range) => {
|
|
36731
|
+
if (typeof range !== "string") return false;
|
|
36732
|
+
const normalizedRange = normalizeDependencyVersion(range);
|
|
36733
|
+
if (normalizedRange === null) return false;
|
|
36734
|
+
return splitDependencyVersionBranches(normalizedRange).some((branch) => {
|
|
36735
|
+
const normalizedBranch = normalizeDependencyVersion(branch);
|
|
36736
|
+
return normalizedBranch !== null && getBranchLowestMajor(normalizedBranch) === null && hasUpperBoundComparator(normalizedBranch);
|
|
36737
|
+
});
|
|
36738
|
+
};
|
|
36739
|
+
const peerRangeMinMajor = (range) => {
|
|
36740
|
+
if (typeof range !== "string") return null;
|
|
36741
|
+
return getLowestDependencyMajor(range);
|
|
36742
|
+
};
|
|
36743
|
+
const resolveEffectiveReactMajor = (reactVersion, packageJson) => {
|
|
36744
|
+
const installedReactMajor = parseReactMajor(reactVersion);
|
|
36745
|
+
const peerReactRange = packageJson.peerDependencies?.react;
|
|
36746
|
+
if (typeof peerReactRange !== "string") return installedReactMajor;
|
|
36747
|
+
const peerFloor = peerRangeMinMajor(peerReactRange);
|
|
36748
|
+
if (peerFloor === null) return hasUpperBoundOnlyPeerRange(peerReactRange) ? null : installedReactMajor;
|
|
36749
|
+
return installedReactMajor !== null ? Math.min(installedReactMajor, peerFloor) : peerFloor;
|
|
36550
36750
|
};
|
|
36551
36751
|
const isCatalogReference = (version) => version.startsWith("catalog:");
|
|
36552
36752
|
const extractCatalogName = (version) => {
|
|
@@ -36688,6 +36888,16 @@ const resolveCatalogVersion = (packageJson, packageName, rootDirectory, explicit
|
|
|
36688
36888
|
}
|
|
36689
36889
|
return null;
|
|
36690
36890
|
};
|
|
36891
|
+
const REACT_SECTIONS = [
|
|
36892
|
+
"dependencies",
|
|
36893
|
+
"peerDependencies",
|
|
36894
|
+
"devDependencies"
|
|
36895
|
+
];
|
|
36896
|
+
const TAILWIND_ZOD_SECTIONS = [
|
|
36897
|
+
"dependencies",
|
|
36898
|
+
"devDependencies",
|
|
36899
|
+
"peerDependencies"
|
|
36900
|
+
];
|
|
36691
36901
|
const EMPTY_DEPENDENCY_INFO = {
|
|
36692
36902
|
reactVersion: null,
|
|
36693
36903
|
tailwindVersion: null,
|
|
@@ -36728,6 +36938,10 @@ const extractDependencyInfo = (packageJson) => {
|
|
|
36728
36938
|
framework: detectFramework(allDependencies)
|
|
36729
36939
|
};
|
|
36730
36940
|
};
|
|
36941
|
+
const getDependencySpec = (packageJson, packageName) => {
|
|
36942
|
+
const spec = packageJson.dependencies?.[packageName] ?? packageJson.devDependencies?.[packageName] ?? packageJson.peerDependencies?.[packageName] ?? packageJson.optionalDependencies?.[packageName];
|
|
36943
|
+
return typeof spec === "string" ? spec : null;
|
|
36944
|
+
};
|
|
36731
36945
|
const getDependencyDeclaration = ({ packageJson, packageName, sections }) => {
|
|
36732
36946
|
for (const section of sections) {
|
|
36733
36947
|
const version = packageJson[section]?.[packageName];
|
|
@@ -36744,222 +36958,91 @@ const getDependencyDeclaration = ({ packageJson, packageName, sections }) => {
|
|
|
36744
36958
|
version: null
|
|
36745
36959
|
};
|
|
36746
36960
|
};
|
|
36747
|
-
const
|
|
36748
|
-
|
|
36749
|
-
|
|
36750
|
-
|
|
36751
|
-
|
|
36752
|
-
|
|
36753
|
-
|
|
36754
|
-
const
|
|
36755
|
-
|
|
36756
|
-
|
|
36757
|
-
|
|
36758
|
-
|
|
36759
|
-
|
|
36760
|
-
if (!isFile(Path.join(rootDirectory, "nx.json"))) return [];
|
|
36761
|
-
const collected = [];
|
|
36762
|
-
for (const candidate of NX_PROJECT_DISCOVERY_DIRS) {
|
|
36763
|
-
const candidatePath = Path.join(rootDirectory, candidate);
|
|
36764
|
-
if (!isDirectory(candidatePath)) continue;
|
|
36765
|
-
for (const entry of readDirectoryEntries(candidatePath)) {
|
|
36766
|
-
if (!entry.isDirectory()) continue;
|
|
36767
|
-
const projectDirectory = Path.join(candidatePath, entry.name);
|
|
36768
|
-
if (isFile(Path.join(projectDirectory, "project.json")) || isFile(Path.join(projectDirectory, "package.json"))) collected.push(`${candidate}/${entry.name}`);
|
|
36769
|
-
}
|
|
36770
|
-
}
|
|
36771
|
-
return collected;
|
|
36772
|
-
};
|
|
36773
|
-
const parsePnpmWorkspacePatterns = (rootDirectory) => {
|
|
36774
|
-
const workspacePath = Path.join(rootDirectory, "pnpm-workspace.yaml");
|
|
36775
|
-
if (!isFile(workspacePath)) return [];
|
|
36776
|
-
const content = NFS.readFileSync(workspacePath, "utf-8");
|
|
36777
|
-
const patterns = [];
|
|
36778
|
-
let isInsidePackagesBlock = false;
|
|
36779
|
-
for (const line of content.split("\n")) {
|
|
36780
|
-
const trimmed = line.trim();
|
|
36781
|
-
if (trimmed === "packages:") {
|
|
36782
|
-
isInsidePackagesBlock = true;
|
|
36783
|
-
continue;
|
|
36784
|
-
}
|
|
36785
|
-
if (isInsidePackagesBlock && trimmed.startsWith("-")) patterns.push(trimmed.replace(/^-\s*/, "").replace(/["']/g, ""));
|
|
36786
|
-
else if (isInsidePackagesBlock && trimmed.length > 0 && !trimmed.startsWith("#")) isInsidePackagesBlock = false;
|
|
36787
|
-
}
|
|
36788
|
-
return patterns;
|
|
36789
|
-
};
|
|
36790
|
-
const getWorkspacePatterns = (rootDirectory, packageJson) => {
|
|
36791
|
-
const pnpmPatterns = parsePnpmWorkspacePatterns(rootDirectory);
|
|
36792
|
-
if (pnpmPatterns.length > 0) return pnpmPatterns;
|
|
36793
|
-
if (Array.isArray(packageJson.workspaces)) return packageJson.workspaces;
|
|
36794
|
-
if (packageJson.workspaces?.packages) return packageJson.workspaces.packages;
|
|
36795
|
-
const nxPatterns = getNxWorkspaceDirectories(rootDirectory);
|
|
36796
|
-
if (nxPatterns.length > 0) return nxPatterns;
|
|
36797
|
-
return [];
|
|
36798
|
-
};
|
|
36799
|
-
const parseReactMajor = (reactVersion) => {
|
|
36800
|
-
if (typeof reactVersion !== "string") return null;
|
|
36801
|
-
return getLowestDependencyMajor(reactVersion);
|
|
36961
|
+
const REACT_DEPENDENCY_NAMES = new Set([
|
|
36962
|
+
"react",
|
|
36963
|
+
"react-native",
|
|
36964
|
+
"next",
|
|
36965
|
+
"preact"
|
|
36966
|
+
]);
|
|
36967
|
+
const hasReactDependency = (packageJson) => {
|
|
36968
|
+
const allDependencies = {
|
|
36969
|
+
...packageJson.peerDependencies,
|
|
36970
|
+
...packageJson.dependencies,
|
|
36971
|
+
...packageJson.devDependencies
|
|
36972
|
+
};
|
|
36973
|
+
return Object.keys(allDependencies).some((packageName) => REACT_DEPENDENCY_NAMES.has(packageName));
|
|
36802
36974
|
};
|
|
36803
|
-
const
|
|
36804
|
-
|
|
36805
|
-
|
|
36806
|
-
|
|
36807
|
-
|
|
36808
|
-
|
|
36809
|
-
}
|
|
36810
|
-
const wildcardIndex = cleanPattern.indexOf("*");
|
|
36811
|
-
const baseDirectory = Path.join(rootDirectory, cleanPattern.slice(0, wildcardIndex));
|
|
36812
|
-
const suffixAfterWildcard = cleanPattern.slice(wildcardIndex + 1);
|
|
36813
|
-
if (!isDirectory(baseDirectory)) return [];
|
|
36814
|
-
const resolved = [];
|
|
36815
|
-
for (const entry of readDirectoryEntries(baseDirectory)) {
|
|
36816
|
-
const entryPath = Path.join(baseDirectory, entry.name, suffixAfterWildcard);
|
|
36817
|
-
if (isDirectory(entryPath) && isFile(Path.join(entryPath, "package.json"))) resolved.push(entryPath);
|
|
36818
|
-
}
|
|
36819
|
-
return resolved;
|
|
36975
|
+
const getPreactVersion = (packageJson) => {
|
|
36976
|
+
return {
|
|
36977
|
+
...packageJson.peerDependencies,
|
|
36978
|
+
...packageJson.dependencies,
|
|
36979
|
+
...packageJson.devDependencies
|
|
36980
|
+
}.preact ?? null;
|
|
36820
36981
|
};
|
|
36821
|
-
const
|
|
36822
|
-
|
|
36823
|
-
|
|
36824
|
-
|
|
36825
|
-
|
|
36826
|
-
|
|
36827
|
-
|
|
36828
|
-
|
|
36982
|
+
const TANSTACK_QUERY_PACKAGES = new Set([
|
|
36983
|
+
"@tanstack/react-query",
|
|
36984
|
+
"@tanstack/query-core",
|
|
36985
|
+
"react-query"
|
|
36986
|
+
]);
|
|
36987
|
+
const hasTanStackQuery = (packageJson) => {
|
|
36988
|
+
const allDependencies = {
|
|
36989
|
+
...packageJson.peerDependencies,
|
|
36990
|
+
...packageJson.dependencies,
|
|
36991
|
+
...packageJson.devDependencies
|
|
36992
|
+
};
|
|
36993
|
+
return Object.keys(allDependencies).some((packageName) => TANSTACK_QUERY_PACKAGES.has(packageName));
|
|
36829
36994
|
};
|
|
36830
|
-
const
|
|
36831
|
-
if (!
|
|
36832
|
-
const
|
|
36833
|
-
const
|
|
36834
|
-
if (
|
|
36835
|
-
|
|
36836
|
-
|
|
36995
|
+
const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, version }) => {
|
|
36996
|
+
if (version === null || !isCatalogReference(version)) return version;
|
|
36997
|
+
const catalogName = extractCatalogName(version);
|
|
36998
|
+
const resolvedLocalVersion = resolveCatalogVersion(rootPackageJson, packageName, rootDirectory, catalogName);
|
|
36999
|
+
if (resolvedLocalVersion) return resolvedLocalVersion;
|
|
37000
|
+
const monorepoRoot = findMonorepoRoot(rootDirectory);
|
|
37001
|
+
if (!monorepoRoot) return version;
|
|
37002
|
+
const monorepoPackageJsonPath = Path.join(monorepoRoot, "package.json");
|
|
37003
|
+
if (!isFile(monorepoPackageJsonPath)) return version;
|
|
37004
|
+
return resolveCatalogVersion(readPackageJson$1(monorepoPackageJsonPath), packageName, monorepoRoot, catalogName) ?? version;
|
|
36837
37005
|
};
|
|
36838
|
-
|
|
36839
|
-
|
|
36840
|
-
|
|
36841
|
-
|
|
36842
|
-
|
|
36843
|
-
|
|
36844
|
-
|
|
36845
|
-
|
|
36846
|
-
|
|
36847
|
-
|
|
36848
|
-
|
|
36849
|
-
|
|
36850
|
-
|
|
36851
|
-
|
|
36852
|
-
|
|
36853
|
-
"peerDependencies",
|
|
36854
|
-
"devDependencies"
|
|
36855
|
-
],
|
|
36856
|
-
workspaceDirectory,
|
|
36857
|
-
workspacePackageJson
|
|
36858
|
-
});
|
|
36859
|
-
const tailwindVersion = resolveWorkspaceDependencyVersion({
|
|
36860
|
-
concreteVersion: info.tailwindVersion,
|
|
36861
|
-
packageName: "tailwindcss",
|
|
36862
|
-
rootDirectory,
|
|
36863
|
-
rootPackageJson: packageJson,
|
|
36864
|
-
sections: [
|
|
36865
|
-
"dependencies",
|
|
36866
|
-
"devDependencies",
|
|
36867
|
-
"peerDependencies"
|
|
36868
|
-
],
|
|
36869
|
-
workspaceDirectory,
|
|
36870
|
-
workspacePackageJson
|
|
36871
|
-
});
|
|
36872
|
-
const zodVersion = resolveWorkspaceDependencyVersion({
|
|
36873
|
-
concreteVersion: info.zodVersion,
|
|
36874
|
-
packageName: "zod",
|
|
36875
|
-
rootDirectory,
|
|
36876
|
-
rootPackageJson: packageJson,
|
|
36877
|
-
sections: [
|
|
36878
|
-
"dependencies",
|
|
36879
|
-
"devDependencies",
|
|
36880
|
-
"peerDependencies"
|
|
36881
|
-
],
|
|
36882
|
-
workspaceDirectory,
|
|
36883
|
-
workspacePackageJson
|
|
36884
|
-
});
|
|
36885
|
-
if (reactVersion && shouldReplaceReactVersion(result.reactVersion, reactVersion)) result.reactVersion = reactVersion;
|
|
36886
|
-
if (tailwindVersion && !result.tailwindVersion) result.tailwindVersion = tailwindVersion;
|
|
36887
|
-
if (zodVersion && !result.zodVersion) result.zodVersion = zodVersion;
|
|
36888
|
-
if (info.framework !== "unknown" && result.framework === "unknown") result.framework = info.framework;
|
|
36889
|
-
const resultReactMajor = parseReactMajor(result.reactVersion);
|
|
36890
|
-
if (result.reactVersion && result.tailwindVersion && result.framework !== "unknown" && resultReactMajor !== null && resultReactMajor <= 17) return result;
|
|
36891
|
-
}
|
|
37006
|
+
/**
|
|
37007
|
+
* The chain of ancestor directories of `startDirectory`, nearest first, up to
|
|
37008
|
+
* and including the filesystem root. With `includeStart`, the chain begins at
|
|
37009
|
+
* `startDirectory` itself; otherwise at its parent. The one place the "walk up
|
|
37010
|
+
* the tree" traversal is expressed, so each caller adds only its own match /
|
|
37011
|
+
* stop policy instead of re-deriving the `path.dirname` loop.
|
|
37012
|
+
*/
|
|
37013
|
+
const ancestorDirectories = (startDirectory, { includeStart }) => {
|
|
37014
|
+
const directories = [];
|
|
37015
|
+
let currentDirectory = includeStart ? startDirectory : Path.dirname(startDirectory);
|
|
37016
|
+
while (true) {
|
|
37017
|
+
directories.push(currentDirectory);
|
|
37018
|
+
const parentDirectory = Path.dirname(currentDirectory);
|
|
37019
|
+
if (parentDirectory === currentDirectory) return directories;
|
|
37020
|
+
currentDirectory = parentDirectory;
|
|
36892
37021
|
}
|
|
36893
|
-
return result;
|
|
36894
37022
|
};
|
|
36895
|
-
|
|
36896
|
-
|
|
36897
|
-
|
|
36898
|
-
|
|
36899
|
-
|
|
36900
|
-
|
|
36901
|
-
|
|
36902
|
-
|
|
36903
|
-
|
|
36904
|
-
|
|
36905
|
-
|
|
36906
|
-
|
|
36907
|
-
|
|
36908
|
-
|
|
36909
|
-
|
|
36910
|
-
|
|
36911
|
-
|
|
36912
|
-
})
|
|
36913
|
-
|
|
36914
|
-
|
|
36915
|
-
packageName: "tailwindcss",
|
|
36916
|
-
sections: [
|
|
36917
|
-
"dependencies",
|
|
36918
|
-
"devDependencies",
|
|
36919
|
-
"peerDependencies"
|
|
36920
|
-
]
|
|
36921
|
-
}) : null;
|
|
36922
|
-
const leafZodDeclaration = leafPackageJson ? getDependencyDeclaration({
|
|
36923
|
-
packageJson: leafPackageJson,
|
|
36924
|
-
packageName: "zod",
|
|
36925
|
-
sections: [
|
|
36926
|
-
"dependencies",
|
|
36927
|
-
"devDependencies",
|
|
36928
|
-
"peerDependencies"
|
|
36929
|
-
]
|
|
36930
|
-
}) : null;
|
|
36931
|
-
const shouldUseReactFallback = !leafReactDeclaration?.hasDeclaration;
|
|
36932
|
-
const shouldUseTailwindFallback = leafTailwindDeclaration?.hasDeclaration ?? true;
|
|
36933
|
-
const shouldUseZodFallback = leafZodDeclaration?.hasDeclaration ?? true;
|
|
36934
|
-
const reactCatalogVersion = shouldUseReactFallback ? resolveCatalogVersion(rootPackageJson, "react", monorepoRoot, leafReactDeclaration?.catalogReference) : null;
|
|
36935
|
-
const tailwindCatalogVersion = shouldUseTailwindFallback ? resolveCatalogVersion(rootPackageJson, "tailwindcss", monorepoRoot, leafTailwindDeclaration?.catalogReference) : null;
|
|
36936
|
-
const zodCatalogVersion = shouldUseZodFallback ? resolveCatalogVersion(rootPackageJson, "zod", monorepoRoot, leafZodDeclaration?.catalogReference) : null;
|
|
36937
|
-
const workspaceInfo = findReactInWorkspaces(monorepoRoot, rootPackageJson);
|
|
36938
|
-
return {
|
|
36939
|
-
reactVersion: shouldUseReactFallback ? reactCatalogVersion ?? rootInfo.reactVersion ?? workspaceInfo.reactVersion : rootInfo.reactVersion ?? workspaceInfo.reactVersion,
|
|
36940
|
-
tailwindVersion: shouldUseTailwindFallback ? tailwindCatalogVersion ?? rootInfo.tailwindVersion ?? workspaceInfo.tailwindVersion : null,
|
|
36941
|
-
zodVersion: shouldUseZodFallback ? zodCatalogVersion ?? rootInfo.zodVersion ?? workspaceInfo.zodVersion : null,
|
|
36942
|
-
framework: rootInfo.framework !== "unknown" ? rootInfo.framework : workspaceInfo.framework
|
|
36943
|
-
};
|
|
36944
|
-
};
|
|
36945
|
-
const findInWorkspacePackageJsons = (rootDirectory, rootPackageJson, select) => {
|
|
36946
|
-
const rootValue = select(rootPackageJson);
|
|
36947
|
-
if (rootValue !== null) return rootValue;
|
|
36948
|
-
const patterns = getWorkspacePatterns(rootDirectory, rootPackageJson);
|
|
36949
|
-
if (patterns.length === 0) return null;
|
|
36950
|
-
const visitedDirectories = /* @__PURE__ */ new Set();
|
|
36951
|
-
for (const pattern of patterns) {
|
|
36952
|
-
const directories = [...resolveWorkspaceDirectories(rootDirectory, pattern)].sort();
|
|
36953
|
-
for (const workspaceDirectory of directories) {
|
|
36954
|
-
if (visitedDirectories.has(workspaceDirectory)) continue;
|
|
36955
|
-
visitedDirectories.add(workspaceDirectory);
|
|
36956
|
-
const value = select(readPackageJson$1(Path.join(workspaceDirectory, "package.json")));
|
|
36957
|
-
if (value !== null) return value;
|
|
36958
|
-
}
|
|
37023
|
+
/**
|
|
37024
|
+
* Walk up from `startDirectory` to the nearest ancestor that owns a
|
|
37025
|
+
* `package.json`, stopping at (and including) the enclosing project boundary
|
|
37026
|
+
* — the working tree's git root or a monorepo root. Lets a scan of a package
|
|
37027
|
+
* subfolder that has no `package.json` of its own adopt the nearest enclosing
|
|
37028
|
+
* package as its project root, so it inherits that package's dependency +
|
|
37029
|
+
* framework detection instead of synthesizing an empty (React-blind) project.
|
|
37030
|
+
*
|
|
37031
|
+
* Generalizes the older monorepo-root-only lookup: the nearest ancestor is the
|
|
37032
|
+
* most specific owning package (a leaf workspace, not just the repo root), so
|
|
37033
|
+
* a subfolder of one workspace no longer borrows a sibling's capabilities.
|
|
37034
|
+
*
|
|
37035
|
+
* Returns `null` when no ancestor `package.json` exists inside the project
|
|
37036
|
+
* boundary (a loose tree of files, or a subfolder of a package-less repo).
|
|
37037
|
+
*/
|
|
37038
|
+
const findNearestAncestorPackageJson = (startDirectory) => {
|
|
37039
|
+
if (isProjectBoundary(startDirectory)) return null;
|
|
37040
|
+
for (const directory of ancestorDirectories(startDirectory, { includeStart: false })) {
|
|
37041
|
+
if (isFile(Path.join(directory, "package.json"))) return directory;
|
|
37042
|
+
if (isProjectBoundary(directory)) return null;
|
|
36959
37043
|
}
|
|
36960
37044
|
return null;
|
|
36961
37045
|
};
|
|
36962
|
-
const someWorkspacePackageJson = (rootDirectory, rootPackageJson, predicate) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => predicate(packageJson) ? true : null) !== null;
|
|
36963
37046
|
const NAMES = new Set([
|
|
36964
37047
|
"react-native",
|
|
36965
37048
|
"react-native-tvos",
|
|
@@ -36992,47 +37075,7 @@ const isPackageJsonReactNativeAware = (packageJson) => {
|
|
|
36992
37075
|
if (containsAnyReactNativeDependency(packageJson.optionalDependencies)) return true;
|
|
36993
37076
|
return false;
|
|
36994
37077
|
};
|
|
36995
|
-
const
|
|
36996
|
-
const getDependencySpec = (packageJson, packageName) => {
|
|
36997
|
-
const spec = packageJson.dependencies?.[packageName] ?? packageJson.devDependencies?.[packageName] ?? packageJson.peerDependencies?.[packageName] ?? packageJson.optionalDependencies?.[packageName];
|
|
36998
|
-
return typeof spec === "string" ? spec : null;
|
|
36999
|
-
};
|
|
37000
|
-
const findExpoVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, "expo"));
|
|
37001
|
-
const SHOPIFY_FLASH_LIST_PACKAGE_NAME = "@shopify/flash-list";
|
|
37002
|
-
const findShopifyFlashListVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, SHOPIFY_FLASH_LIST_PACKAGE_NAME));
|
|
37003
|
-
const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, version }) => {
|
|
37004
|
-
if (version === null || !isCatalogReference(version)) return version;
|
|
37005
|
-
const catalogName = extractCatalogName(version);
|
|
37006
|
-
const resolvedLocalVersion = resolveCatalogVersion(rootPackageJson, packageName, rootDirectory, catalogName);
|
|
37007
|
-
if (resolvedLocalVersion) return resolvedLocalVersion;
|
|
37008
|
-
const monorepoRoot = findMonorepoRoot(rootDirectory);
|
|
37009
|
-
if (!monorepoRoot) return version;
|
|
37010
|
-
const monorepoPackageJsonPath = path.join(monorepoRoot, "package.json");
|
|
37011
|
-
if (!isFile(monorepoPackageJsonPath)) return version;
|
|
37012
|
-
return resolveCatalogVersion(readPackageJson$1(monorepoPackageJsonPath), packageName, monorepoRoot, catalogName) ?? version;
|
|
37013
|
-
};
|
|
37014
|
-
const findNextjsVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, "next"));
|
|
37015
|
-
const getPreactVersion = (packageJson) => {
|
|
37016
|
-
return {
|
|
37017
|
-
...packageJson.peerDependencies,
|
|
37018
|
-
...packageJson.dependencies,
|
|
37019
|
-
...packageJson.devDependencies
|
|
37020
|
-
}.preact ?? null;
|
|
37021
|
-
};
|
|
37022
|
-
const TANSTACK_QUERY_PACKAGES = new Set([
|
|
37023
|
-
"@tanstack/react-query",
|
|
37024
|
-
"@tanstack/query-core",
|
|
37025
|
-
"react-query"
|
|
37026
|
-
]);
|
|
37027
|
-
const hasTanStackQuery = (packageJson) => {
|
|
37028
|
-
const allDependencies = {
|
|
37029
|
-
...packageJson.peerDependencies,
|
|
37030
|
-
...packageJson.dependencies,
|
|
37031
|
-
...packageJson.devDependencies
|
|
37032
|
-
};
|
|
37033
|
-
return Object.keys(allDependencies).some((packageName) => TANSTACK_QUERY_PACKAGES.has(packageName));
|
|
37034
|
-
};
|
|
37035
|
-
const REANIMATED_DEPENDENCY_NAME = "react-native-reanimated";
|
|
37078
|
+
const REANIMATED_DEPENDENCY_NAME$1 = "react-native-reanimated";
|
|
37036
37079
|
const isPackageJsonReanimatedAware = (packageJson) => {
|
|
37037
37080
|
const allDependencies = {
|
|
37038
37081
|
...packageJson.peerDependencies,
|
|
@@ -37040,144 +37083,70 @@ const isPackageJsonReanimatedAware = (packageJson) => {
|
|
|
37040
37083
|
...packageJson.devDependencies,
|
|
37041
37084
|
...packageJson.optionalDependencies
|
|
37042
37085
|
};
|
|
37043
|
-
return Object.hasOwn(allDependencies, REANIMATED_DEPENDENCY_NAME);
|
|
37086
|
+
return Object.hasOwn(allDependencies, REANIMATED_DEPENDENCY_NAME$1);
|
|
37044
37087
|
};
|
|
37045
|
-
const
|
|
37046
|
-
|
|
37047
|
-
|
|
37048
|
-
|
|
37049
|
-
|
|
37050
|
-
|
|
37051
|
-
|
|
37052
|
-
|
|
37053
|
-
return splitDependencyVersionBranches(normalizedRange).some((branch) => {
|
|
37054
|
-
const normalizedBranch = normalizeDependencyVersion(branch);
|
|
37055
|
-
return normalizedBranch !== null && getBranchLowestMajor(normalizedBranch) === null && hasUpperBoundComparator(normalizedBranch);
|
|
37056
|
-
});
|
|
37057
|
-
};
|
|
37058
|
-
const peerRangeMinMajor = (range) => {
|
|
37059
|
-
if (typeof range !== "string") return null;
|
|
37060
|
-
return getLowestDependencyMajor(range);
|
|
37061
|
-
};
|
|
37062
|
-
const resolveEffectiveReactMajor = (reactVersion, packageJson) => {
|
|
37063
|
-
const installedReactMajor = parseReactMajor(reactVersion);
|
|
37064
|
-
const peerReactRange = packageJson.peerDependencies?.react;
|
|
37065
|
-
if (typeof peerReactRange !== "string") return installedReactMajor;
|
|
37066
|
-
const peerFloor = peerRangeMinMajor(peerReactRange);
|
|
37067
|
-
if (peerFloor === null) return hasUpperBoundOnlyPeerRange(peerReactRange) ? null : installedReactMajor;
|
|
37068
|
-
return installedReactMajor !== null ? Math.min(installedReactMajor, peerFloor) : peerFloor;
|
|
37088
|
+
const getWorkspacePatterns = (rootDirectory, packageJson) => {
|
|
37089
|
+
const pnpmPatterns = parsePnpmWorkspacePatterns(rootDirectory);
|
|
37090
|
+
if (pnpmPatterns.length > 0) return pnpmPatterns;
|
|
37091
|
+
if (Array.isArray(packageJson.workspaces)) return packageJson.workspaces;
|
|
37092
|
+
if (packageJson.workspaces?.packages) return packageJson.workspaces.packages;
|
|
37093
|
+
const nxPatterns = getNxWorkspaceDirectories(rootDirectory);
|
|
37094
|
+
if (nxPatterns.length > 0) return nxPatterns;
|
|
37095
|
+
return [];
|
|
37069
37096
|
};
|
|
37070
|
-
const
|
|
37071
|
-
const
|
|
37072
|
-
|
|
37073
|
-
const
|
|
37074
|
-
const
|
|
37075
|
-
|
|
37076
|
-
|
|
37077
|
-
|
|
37078
|
-
|
|
37079
|
-
|
|
37080
|
-
|
|
37081
|
-
|
|
37082
|
-
|
|
37083
|
-
|
|
37097
|
+
const parsePnpmWorkspacePatterns = (rootDirectory) => {
|
|
37098
|
+
const workspacePath = Path.join(rootDirectory, "pnpm-workspace.yaml");
|
|
37099
|
+
if (!isFile(workspacePath)) return [];
|
|
37100
|
+
const content = NFS.readFileSync(workspacePath, "utf-8");
|
|
37101
|
+
const patterns = [];
|
|
37102
|
+
let isInsidePackagesBlock = false;
|
|
37103
|
+
for (const line of content.split("\n")) {
|
|
37104
|
+
const trimmed = line.trim();
|
|
37105
|
+
if (trimmed === "packages:") {
|
|
37106
|
+
isInsidePackagesBlock = true;
|
|
37107
|
+
continue;
|
|
37108
|
+
}
|
|
37109
|
+
if (isInsidePackagesBlock && trimmed.startsWith("-")) patterns.push(trimmed.replace(/^-\s*/, "").replace(/["']/g, ""));
|
|
37110
|
+
else if (isInsidePackagesBlock && trimmed.length > 0 && !trimmed.startsWith("#")) isInsidePackagesBlock = false;
|
|
37084
37111
|
}
|
|
37085
|
-
return
|
|
37086
|
-
};
|
|
37087
|
-
const resolveExtendsPath = (extendsValue, fromConfigDirectory) => {
|
|
37088
|
-
if (isRelativeExtendsValue(extendsValue)) return ensureJsonExtension(Path.resolve(fromConfigDirectory, extendsValue));
|
|
37089
|
-
return resolvePackageExtendsPath(extendsValue, fromConfigDirectory);
|
|
37090
|
-
};
|
|
37091
|
-
const normalizeCompilerOptions = (compilerOptions) => {
|
|
37092
|
-
if (!isPlainObject(compilerOptions)) return { hasExplicitLib: false };
|
|
37093
|
-
const target = typeof compilerOptions.target === "string" ? compilerOptions.target : void 0;
|
|
37094
|
-
const hasExplicitLib = Object.hasOwn(compilerOptions, "lib");
|
|
37095
|
-
return {
|
|
37096
|
-
target,
|
|
37097
|
-
lib: Array.isArray(compilerOptions.lib) ? compilerOptions.lib.filter((entry) => typeof entry === "string") : void 0,
|
|
37098
|
-
hasExplicitLib
|
|
37099
|
-
};
|
|
37112
|
+
return patterns;
|
|
37100
37113
|
};
|
|
37101
|
-
const
|
|
37102
|
-
|
|
37103
|
-
|
|
37104
|
-
|
|
37105
|
-
|
|
37106
|
-
|
|
37114
|
+
const NX_PROJECT_DISCOVERY_DIRS = [
|
|
37115
|
+
"apps",
|
|
37116
|
+
"libs",
|
|
37117
|
+
"packages"
|
|
37118
|
+
];
|
|
37119
|
+
const getNxWorkspaceDirectories = (rootDirectory) => {
|
|
37120
|
+
if (!isFile(Path.join(rootDirectory, "nx.json"))) return [];
|
|
37121
|
+
const collected = [];
|
|
37122
|
+
for (const candidate of NX_PROJECT_DISCOVERY_DIRS) {
|
|
37123
|
+
const candidatePath = Path.join(rootDirectory, candidate);
|
|
37124
|
+
if (!isDirectory(candidatePath)) continue;
|
|
37125
|
+
for (const entry of readDirectoryEntries(candidatePath)) {
|
|
37126
|
+
if (!entry.isDirectory()) continue;
|
|
37127
|
+
const projectDirectory = Path.join(candidatePath, entry.name);
|
|
37128
|
+
if (isFile(Path.join(projectDirectory, "project.json")) || isFile(Path.join(projectDirectory, "package.json"))) collected.push(`${candidate}/${entry.name}`);
|
|
37129
|
+
}
|
|
37107
37130
|
}
|
|
37108
|
-
|
|
37109
|
-
if (!isPlainObject(parsed.config)) return null;
|
|
37110
|
-
return {
|
|
37111
|
-
extends: typeof parsed.config.extends === "string" ? parsed.config.extends : void 0,
|
|
37112
|
-
compilerOptions: normalizeCompilerOptions(parsed.config.compilerOptions)
|
|
37113
|
-
};
|
|
37114
|
-
};
|
|
37115
|
-
const mergeCompilerOptions = (inherited, current) => {
|
|
37116
|
-
const target = current.target ?? inherited?.target;
|
|
37117
|
-
const hasExplicitLib = current.hasExplicitLib || Boolean(inherited?.hasExplicitLib);
|
|
37118
|
-
return {
|
|
37119
|
-
target,
|
|
37120
|
-
lib: current.hasExplicitLib ? current.lib : inherited?.lib,
|
|
37121
|
-
hasExplicitLib
|
|
37122
|
-
};
|
|
37131
|
+
return collected;
|
|
37123
37132
|
};
|
|
37124
|
-
const
|
|
37125
|
-
const
|
|
37126
|
-
if (
|
|
37127
|
-
|
|
37128
|
-
|
|
37129
|
-
|
|
37130
|
-
nextVisitedPaths.add(realPath);
|
|
37131
|
-
if (tsConfig.extends && extendsDepth < 8) {
|
|
37132
|
-
const parentPath = resolveExtendsPath(tsConfig.extends, Path.dirname(realPath));
|
|
37133
|
-
if (parentPath && isFile(parentPath)) return mergeCompilerOptions(readResolvedCompilerOptions(parentPath, extendsDepth + 1, nextVisitedPaths), tsConfig.compilerOptions);
|
|
37133
|
+
const resolveWorkspaceDirectories = (rootDirectory, pattern) => {
|
|
37134
|
+
const cleanPattern = pattern.replace(/["']/g, "").replace(/\/\*\*$/, "/*");
|
|
37135
|
+
if (!cleanPattern.includes("*")) {
|
|
37136
|
+
const directoryPath = Path.join(rootDirectory, cleanPattern);
|
|
37137
|
+
if (isDirectory(directoryPath) && isFile(Path.join(directoryPath, "package.json"))) return [directoryPath];
|
|
37138
|
+
return [];
|
|
37134
37139
|
}
|
|
37135
|
-
|
|
37136
|
-
|
|
37137
|
-
const
|
|
37138
|
-
|
|
37139
|
-
|
|
37140
|
-
|
|
37141
|
-
const
|
|
37142
|
-
|
|
37143
|
-
|
|
37144
|
-
|
|
37145
|
-
if (!esYearMatch) return false;
|
|
37146
|
-
if (Number(esYearMatch[1]) < 2023) return false;
|
|
37147
|
-
const component = esYearMatch[2];
|
|
37148
|
-
return component === void 0 || component === "array";
|
|
37149
|
-
};
|
|
37150
|
-
const libIncludesES2023 = (lib) => lib.some(libEntryIncludesES2023Array);
|
|
37151
|
-
const compilerOptionsArePreES2023 = (compilerOptions) => {
|
|
37152
|
-
if (compilerOptions.hasExplicitLib) return !libIncludesES2023(compilerOptions.lib ?? []);
|
|
37153
|
-
if (compilerOptions.target) return targetYearIsPreES2023(compilerOptions.target);
|
|
37154
|
-
return false;
|
|
37155
|
-
};
|
|
37156
|
-
const compilerOptionsDeclareTargetOrLib = (compilerOptions) => compilerOptions.hasExplicitLib || compilerOptions.target !== void 0;
|
|
37157
|
-
const detectPreES2023FromConfig = (tsConfigPath) => {
|
|
37158
|
-
const compilerOptions = readResolvedCompilerOptions(tsConfigPath, 0, /* @__PURE__ */ new Set());
|
|
37159
|
-
if (!compilerOptions) return false;
|
|
37160
|
-
if (!compilerOptionsDeclareTargetOrLib(compilerOptions)) return false;
|
|
37161
|
-
return compilerOptionsArePreES2023(compilerOptions);
|
|
37162
|
-
};
|
|
37163
|
-
const detectPreES2023Target = (directory) => {
|
|
37164
|
-
const tsConfigPath = Path.join(directory, TSCONFIG_FILENAME);
|
|
37165
|
-
if (isFile(tsConfigPath)) return detectPreES2023FromConfig(tsConfigPath);
|
|
37166
|
-
return false;
|
|
37167
|
-
};
|
|
37168
|
-
const REACT_DEPENDENCY_NAMES = new Set([
|
|
37169
|
-
"react",
|
|
37170
|
-
"react-native",
|
|
37171
|
-
"next",
|
|
37172
|
-
"preact"
|
|
37173
|
-
]);
|
|
37174
|
-
const hasReactDependency = (packageJson) => {
|
|
37175
|
-
const allDependencies = {
|
|
37176
|
-
...packageJson.peerDependencies,
|
|
37177
|
-
...packageJson.dependencies,
|
|
37178
|
-
...packageJson.devDependencies
|
|
37179
|
-
};
|
|
37180
|
-
return Object.keys(allDependencies).some((packageName) => REACT_DEPENDENCY_NAMES.has(packageName));
|
|
37140
|
+
const wildcardIndex = cleanPattern.indexOf("*");
|
|
37141
|
+
const baseDirectory = Path.join(rootDirectory, cleanPattern.slice(0, wildcardIndex));
|
|
37142
|
+
const suffixAfterWildcard = cleanPattern.slice(wildcardIndex + 1);
|
|
37143
|
+
if (!isDirectory(baseDirectory)) return [];
|
|
37144
|
+
const resolved = [];
|
|
37145
|
+
for (const entry of readDirectoryEntries(baseDirectory)) {
|
|
37146
|
+
const entryPath = Path.join(baseDirectory, entry.name, suffixAfterWildcard);
|
|
37147
|
+
if (isDirectory(entryPath) && isFile(Path.join(entryPath, "package.json"))) resolved.push(entryPath);
|
|
37148
|
+
}
|
|
37149
|
+
return resolved;
|
|
37181
37150
|
};
|
|
37182
37151
|
const listWorkspacePackages = (rootDirectory) => {
|
|
37183
37152
|
const packageJsonPath = Path.join(rootDirectory, "package.json");
|
|
@@ -37209,6 +37178,208 @@ const listWorkspacePackages = (rootDirectory) => {
|
|
|
37209
37178
|
}
|
|
37210
37179
|
return packages;
|
|
37211
37180
|
};
|
|
37181
|
+
const REANIMATED_DEPENDENCY_NAME = "react-native-reanimated";
|
|
37182
|
+
const SHOPIFY_FLASH_LIST_PACKAGE_NAME = "@shopify/flash-list";
|
|
37183
|
+
const resolveWorkspaceDependencyVersion = ({ concreteVersion, packageName, rootDirectory, rootPackageJson, sections, workspaceDirectory, workspacePackageJson }) => {
|
|
37184
|
+
const dependencyDeclaration = getDependencyDeclaration({
|
|
37185
|
+
packageJson: workspacePackageJson,
|
|
37186
|
+
packageName,
|
|
37187
|
+
sections
|
|
37188
|
+
});
|
|
37189
|
+
if (!dependencyDeclaration.hasDeclaration) return null;
|
|
37190
|
+
return concreteVersion ?? resolveCatalogVersion(workspacePackageJson, packageName, workspaceDirectory, dependencyDeclaration.catalogReference) ?? resolveCatalogVersion(rootPackageJson, packageName, rootDirectory, dependencyDeclaration.catalogReference);
|
|
37191
|
+
};
|
|
37192
|
+
const shouldReplaceReactVersion = (currentVersion, nextVersion) => {
|
|
37193
|
+
if (!currentVersion) return true;
|
|
37194
|
+
const currentMajor = parseReactMajor(currentVersion);
|
|
37195
|
+
const nextMajor = parseReactMajor(nextVersion);
|
|
37196
|
+
if (currentMajor === null) return nextMajor !== null;
|
|
37197
|
+
if (nextMajor === null) return false;
|
|
37198
|
+
return nextMajor < currentMajor;
|
|
37199
|
+
};
|
|
37200
|
+
const evaluateManifestFacts = (facts, packageJson, directory) => {
|
|
37201
|
+
if (facts.expo.version === null) {
|
|
37202
|
+
const spec = getDependencySpec(packageJson, "expo");
|
|
37203
|
+
if (spec !== null) facts.expo = {
|
|
37204
|
+
version: spec,
|
|
37205
|
+
sourceDirectory: directory
|
|
37206
|
+
};
|
|
37207
|
+
}
|
|
37208
|
+
if (facts.next.version === null) {
|
|
37209
|
+
const spec = getDependencySpec(packageJson, "next");
|
|
37210
|
+
if (spec !== null) facts.next = {
|
|
37211
|
+
version: spec,
|
|
37212
|
+
sourceDirectory: directory
|
|
37213
|
+
};
|
|
37214
|
+
}
|
|
37215
|
+
if (facts.shopifyFlashList.version === null) {
|
|
37216
|
+
const spec = getDependencySpec(packageJson, SHOPIFY_FLASH_LIST_PACKAGE_NAME);
|
|
37217
|
+
if (spec !== null) facts.shopifyFlashList = {
|
|
37218
|
+
version: spec,
|
|
37219
|
+
sourceDirectory: directory
|
|
37220
|
+
};
|
|
37221
|
+
}
|
|
37222
|
+
if (facts.reanimatedVersion === null) {
|
|
37223
|
+
const spec = getDependencySpec(packageJson, REANIMATED_DEPENDENCY_NAME);
|
|
37224
|
+
if (spec !== null) facts.reanimatedVersion = spec;
|
|
37225
|
+
}
|
|
37226
|
+
facts.hasReactNativeAwarePackage = facts.hasReactNativeAwarePackage || isPackageJsonReactNativeAware(packageJson);
|
|
37227
|
+
facts.hasReanimatedAwarePackage = facts.hasReanimatedAwarePackage || isPackageJsonReanimatedAware(packageJson);
|
|
37228
|
+
};
|
|
37229
|
+
const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGroup }) => {
|
|
37230
|
+
const facts = {
|
|
37231
|
+
reactVersion: null,
|
|
37232
|
+
tailwindVersion: null,
|
|
37233
|
+
zodVersion: null,
|
|
37234
|
+
framework: "unknown",
|
|
37235
|
+
expo: {
|
|
37236
|
+
version: null,
|
|
37237
|
+
sourceDirectory: null
|
|
37238
|
+
},
|
|
37239
|
+
next: {
|
|
37240
|
+
version: null,
|
|
37241
|
+
sourceDirectory: null
|
|
37242
|
+
},
|
|
37243
|
+
shopifyFlashList: {
|
|
37244
|
+
version: null,
|
|
37245
|
+
sourceDirectory: null
|
|
37246
|
+
},
|
|
37247
|
+
hasReactNativeAwarePackage: false,
|
|
37248
|
+
hasReanimatedAwarePackage: false,
|
|
37249
|
+
reanimatedVersion: null
|
|
37250
|
+
};
|
|
37251
|
+
evaluateManifestFacts(facts, rootPackageJson, rootDirectory);
|
|
37252
|
+
let isReactGroupSettled = !collectReactGroup;
|
|
37253
|
+
const visitedDirectories = /* @__PURE__ */ new Set();
|
|
37254
|
+
for (const pattern of getWorkspacePatterns(rootDirectory, rootPackageJson)) {
|
|
37255
|
+
const directories = [...resolveWorkspaceDirectories(rootDirectory, pattern)].sort();
|
|
37256
|
+
for (const workspaceDirectory of directories) {
|
|
37257
|
+
if (visitedDirectories.has(workspaceDirectory)) continue;
|
|
37258
|
+
visitedDirectories.add(workspaceDirectory);
|
|
37259
|
+
const workspacePackageJson = readPackageJson$1(Path.join(workspaceDirectory, "package.json"));
|
|
37260
|
+
evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory);
|
|
37261
|
+
const info = extractDependencyInfo(workspacePackageJson);
|
|
37262
|
+
if (info.framework !== "unknown" && frameworkMergeRank(info.framework) < frameworkMergeRank(facts.framework)) facts.framework = info.framework;
|
|
37263
|
+
if (isReactGroupSettled) continue;
|
|
37264
|
+
const reactVersion = resolveWorkspaceDependencyVersion({
|
|
37265
|
+
concreteVersion: info.reactVersion,
|
|
37266
|
+
packageName: "react",
|
|
37267
|
+
rootDirectory,
|
|
37268
|
+
rootPackageJson,
|
|
37269
|
+
sections: REACT_SECTIONS,
|
|
37270
|
+
workspaceDirectory,
|
|
37271
|
+
workspacePackageJson
|
|
37272
|
+
});
|
|
37273
|
+
const tailwindVersion = resolveWorkspaceDependencyVersion({
|
|
37274
|
+
concreteVersion: info.tailwindVersion,
|
|
37275
|
+
packageName: "tailwindcss",
|
|
37276
|
+
rootDirectory,
|
|
37277
|
+
rootPackageJson,
|
|
37278
|
+
sections: TAILWIND_ZOD_SECTIONS,
|
|
37279
|
+
workspaceDirectory,
|
|
37280
|
+
workspacePackageJson
|
|
37281
|
+
});
|
|
37282
|
+
const zodVersion = resolveWorkspaceDependencyVersion({
|
|
37283
|
+
concreteVersion: info.zodVersion,
|
|
37284
|
+
packageName: "zod",
|
|
37285
|
+
rootDirectory,
|
|
37286
|
+
rootPackageJson,
|
|
37287
|
+
sections: TAILWIND_ZOD_SECTIONS,
|
|
37288
|
+
workspaceDirectory,
|
|
37289
|
+
workspacePackageJson
|
|
37290
|
+
});
|
|
37291
|
+
if (reactVersion && shouldReplaceReactVersion(facts.reactVersion, reactVersion)) facts.reactVersion = reactVersion;
|
|
37292
|
+
if (tailwindVersion && !facts.tailwindVersion) facts.tailwindVersion = tailwindVersion;
|
|
37293
|
+
if (zodVersion && !facts.zodVersion) facts.zodVersion = zodVersion;
|
|
37294
|
+
const settledReactMajor = parseReactMajor(facts.reactVersion);
|
|
37295
|
+
isReactGroupSettled = Boolean(facts.reactVersion && facts.tailwindVersion && facts.framework !== "unknown" && settledReactMajor !== null && settledReactMajor <= 17);
|
|
37296
|
+
}
|
|
37297
|
+
}
|
|
37298
|
+
return facts;
|
|
37299
|
+
};
|
|
37300
|
+
const findDependencyInfoFromMonorepoRoot = (directory) => {
|
|
37301
|
+
const monorepoRoot = findMonorepoRoot(directory);
|
|
37302
|
+
if (!monorepoRoot) return EMPTY_DEPENDENCY_INFO;
|
|
37303
|
+
const monorepoPackageJsonPath = Path.join(monorepoRoot, "package.json");
|
|
37304
|
+
if (!isFile(monorepoPackageJsonPath)) return EMPTY_DEPENDENCY_INFO;
|
|
37305
|
+
const rootPackageJson = readPackageJson$1(monorepoPackageJsonPath);
|
|
37306
|
+
const rootInfo = extractDependencyInfo(rootPackageJson);
|
|
37307
|
+
const leafPackageJsonPath = Path.join(directory, "package.json");
|
|
37308
|
+
const leafPackageJson = isFile(leafPackageJsonPath) ? readPackageJson$1(leafPackageJsonPath) : null;
|
|
37309
|
+
const leafReactDeclaration = leafPackageJson ? getDependencyDeclaration({
|
|
37310
|
+
packageJson: leafPackageJson,
|
|
37311
|
+
packageName: "react",
|
|
37312
|
+
sections: REACT_SECTIONS
|
|
37313
|
+
}) : null;
|
|
37314
|
+
const leafTailwindDeclaration = leafPackageJson ? getDependencyDeclaration({
|
|
37315
|
+
packageJson: leafPackageJson,
|
|
37316
|
+
packageName: "tailwindcss",
|
|
37317
|
+
sections: TAILWIND_ZOD_SECTIONS
|
|
37318
|
+
}) : null;
|
|
37319
|
+
const leafZodDeclaration = leafPackageJson ? getDependencyDeclaration({
|
|
37320
|
+
packageJson: leafPackageJson,
|
|
37321
|
+
packageName: "zod",
|
|
37322
|
+
sections: TAILWIND_ZOD_SECTIONS
|
|
37323
|
+
}) : null;
|
|
37324
|
+
const shouldUseReactFallback = !leafReactDeclaration?.hasDeclaration;
|
|
37325
|
+
const shouldUseTailwindFallback = leafTailwindDeclaration?.hasDeclaration ?? true;
|
|
37326
|
+
const shouldUseZodFallback = leafZodDeclaration?.hasDeclaration ?? true;
|
|
37327
|
+
const reactCatalogVersion = shouldUseReactFallback ? resolveCatalogVersion(rootPackageJson, "react", monorepoRoot, leafReactDeclaration?.catalogReference) : null;
|
|
37328
|
+
const tailwindCatalogVersion = shouldUseTailwindFallback ? resolveCatalogVersion(rootPackageJson, "tailwindcss", monorepoRoot, leafTailwindDeclaration?.catalogReference) : null;
|
|
37329
|
+
const zodCatalogVersion = shouldUseZodFallback ? resolveCatalogVersion(rootPackageJson, "zod", monorepoRoot, leafZodDeclaration?.catalogReference) : null;
|
|
37330
|
+
const workspaceFacts = collectWorkspaceFacts(monorepoRoot, rootPackageJson, { collectReactGroup: true });
|
|
37331
|
+
return {
|
|
37332
|
+
reactVersion: shouldUseReactFallback ? reactCatalogVersion ?? rootInfo.reactVersion ?? workspaceFacts.reactVersion : rootInfo.reactVersion ?? workspaceFacts.reactVersion,
|
|
37333
|
+
tailwindVersion: shouldUseTailwindFallback ? tailwindCatalogVersion ?? rootInfo.tailwindVersion ?? workspaceFacts.tailwindVersion : null,
|
|
37334
|
+
zodVersion: shouldUseZodFallback ? zodCatalogVersion ?? rootInfo.zodVersion ?? workspaceFacts.zodVersion : null,
|
|
37335
|
+
framework: rootInfo.framework !== "unknown" ? rootInfo.framework : workspaceFacts.framework
|
|
37336
|
+
};
|
|
37337
|
+
};
|
|
37338
|
+
/**
|
|
37339
|
+
* Last-resort React detection: locate React the way Node's `node_modules`
|
|
37340
|
+
* resolution would from `directory` — check each `node_modules/react` up the
|
|
37341
|
+
* tree — but bounded to the enclosing repo so a globally installed React can't
|
|
37342
|
+
* masquerade as the project's version. Stops at (and including) the nearest
|
|
37343
|
+
* project boundary — the git root or a monorepo root — and returns the first
|
|
37344
|
+
* installed `version`.
|
|
37345
|
+
*
|
|
37346
|
+
* Makes "React is installed and importable" ⇒ "React is detected" an invariant
|
|
37347
|
+
* for packages whose only React declaration is a version-less spec
|
|
37348
|
+
* (`workspace:*`, `*`, a dist-tag) or where React lives solely in a hoisted
|
|
37349
|
+
* `node_modules` the declaration walks never reach — the profile of a component
|
|
37350
|
+
* package inside a monorepo. Matching the `node_modules/react` entry rather than
|
|
37351
|
+
* its realpath means a project whose `node_modules` is symlinked elsewhere (a
|
|
37352
|
+
* Docker volume, a shared store) still counts, while a global install outside
|
|
37353
|
+
* the repo does not.
|
|
37354
|
+
*
|
|
37355
|
+
* Returns `null` when React isn't installed within the repo or its package.json
|
|
37356
|
+
* carries no version string. A tree with no boundary marker at all bounds the
|
|
37357
|
+
* search to `directory`, so a dependency hoisted above the scanned package there
|
|
37358
|
+
* is conservatively not adopted.
|
|
37359
|
+
*/
|
|
37360
|
+
/**
|
|
37361
|
+
* The nearest enclosing project boundary (git root or monorepo root), or
|
|
37362
|
+
* `directory` itself when the scan target sits outside any repo. Bounds the
|
|
37363
|
+
* node_modules walk so a React hoisted anywhere inside the repo counts, but a
|
|
37364
|
+
* React in an ancestor above the repo — a home-directory or global
|
|
37365
|
+
* `node_modules` — can't leak in.
|
|
37366
|
+
*/
|
|
37367
|
+
const findContainmentRoot = (directory) => {
|
|
37368
|
+
for (const ancestorDirectory of ancestorDirectories(directory, { includeStart: true })) if (isProjectBoundary(ancestorDirectory)) return ancestorDirectory;
|
|
37369
|
+
return directory;
|
|
37370
|
+
};
|
|
37371
|
+
const resolveInstalledReactVersion = (directory) => {
|
|
37372
|
+
const containmentRoot = findContainmentRoot(directory);
|
|
37373
|
+
for (const ancestorDirectory of ancestorDirectories(directory, { includeStart: true })) {
|
|
37374
|
+
const reactPackageJsonPath = Path.join(ancestorDirectory, "node_modules", "react", "package.json");
|
|
37375
|
+
if (isFile(reactPackageJsonPath)) {
|
|
37376
|
+
const installedVersion = readPackageJson$1(reactPackageJsonPath).version;
|
|
37377
|
+
return typeof installedVersion === "string" ? installedVersion : null;
|
|
37378
|
+
}
|
|
37379
|
+
if (ancestorDirectory === containmentRoot) return null;
|
|
37380
|
+
}
|
|
37381
|
+
return null;
|
|
37382
|
+
};
|
|
37212
37383
|
const toReactWorkspacePackages = (directories) => {
|
|
37213
37384
|
const packages = [];
|
|
37214
37385
|
for (const directory of directories) {
|
|
@@ -37289,8 +37460,8 @@ const cachedProjectInfos = /* @__PURE__ */ new Map();
|
|
|
37289
37460
|
const discoverProjectWithoutPackageJson = (directory) => {
|
|
37290
37461
|
const sourceFileCount = countSourceFiles(directory);
|
|
37291
37462
|
const hasOwnTsConfig = NFS.existsSync(Path.join(directory, "tsconfig.json"));
|
|
37292
|
-
const
|
|
37293
|
-
const enclosingProject =
|
|
37463
|
+
const enclosingProjectRoot = findNearestAncestorPackageJson(directory);
|
|
37464
|
+
const enclosingProject = enclosingProjectRoot !== null ? discoverProject(enclosingProjectRoot) : null;
|
|
37294
37465
|
if (enclosingProject !== null) return {
|
|
37295
37466
|
...enclosingProject,
|
|
37296
37467
|
rootDirectory: directory,
|
|
@@ -37320,7 +37491,9 @@ const discoverProjectWithoutPackageJson = (directory) => {
|
|
|
37320
37491
|
shopifyFlashListVersion: null,
|
|
37321
37492
|
shopifyFlashListMajorVersion: null,
|
|
37322
37493
|
hasReanimated: false,
|
|
37494
|
+
reanimatedVersion: null,
|
|
37323
37495
|
isPreES2023Target: hasOwnTsConfig && detectPreES2023Target(directory),
|
|
37496
|
+
isStaticExport: false,
|
|
37324
37497
|
sourceFileCount
|
|
37325
37498
|
};
|
|
37326
37499
|
};
|
|
@@ -37334,88 +37507,85 @@ const discoverProject = (directory) => {
|
|
|
37334
37507
|
return synthesized;
|
|
37335
37508
|
}
|
|
37336
37509
|
const packageJson = readPackageJson$1(packageJsonPath);
|
|
37337
|
-
|
|
37338
|
-
|
|
37339
|
-
|
|
37340
|
-
|
|
37341
|
-
|
|
37342
|
-
|
|
37343
|
-
|
|
37344
|
-
|
|
37345
|
-
|
|
37346
|
-
|
|
37347
|
-
|
|
37348
|
-
|
|
37349
|
-
|
|
37350
|
-
|
|
37351
|
-
|
|
37352
|
-
|
|
37353
|
-
|
|
37354
|
-
]
|
|
37355
|
-
});
|
|
37356
|
-
const zodDeclaration = getDependencyDeclaration({
|
|
37510
|
+
const rootInfo = extractDependencyInfo(packageJson);
|
|
37511
|
+
let framework = rootInfo.framework;
|
|
37512
|
+
const tracked = {
|
|
37513
|
+
react: {
|
|
37514
|
+
version: rootInfo.reactVersion,
|
|
37515
|
+
sections: REACT_SECTIONS
|
|
37516
|
+
},
|
|
37517
|
+
tailwindcss: {
|
|
37518
|
+
version: rootInfo.tailwindVersion,
|
|
37519
|
+
sections: TAILWIND_ZOD_SECTIONS
|
|
37520
|
+
},
|
|
37521
|
+
zod: {
|
|
37522
|
+
version: rootInfo.zodVersion,
|
|
37523
|
+
sections: TAILWIND_ZOD_SECTIONS
|
|
37524
|
+
}
|
|
37525
|
+
};
|
|
37526
|
+
const declarations = Object.fromEntries(Object.entries(tracked).map(([packageName, entry]) => [packageName, getDependencyDeclaration({
|
|
37357
37527
|
packageJson,
|
|
37358
|
-
packageName
|
|
37359
|
-
sections:
|
|
37360
|
-
|
|
37361
|
-
|
|
37362
|
-
|
|
37363
|
-
|
|
37364
|
-
|
|
37365
|
-
if (!
|
|
37366
|
-
if (!tailwindVersion && tailwindDeclaration.hasDeclaration) tailwindVersion = resolveCatalogVersion(packageJson, "tailwindcss", directory, tailwindDeclaration.catalogReference);
|
|
37367
|
-
if (!zodVersion && zodDeclaration.hasDeclaration) zodVersion = resolveCatalogVersion(packageJson, "zod", directory, zodDeclaration.catalogReference);
|
|
37368
|
-
if (!reactVersion || !tailwindVersion || !zodVersion) {
|
|
37528
|
+
packageName,
|
|
37529
|
+
sections: entry.sections
|
|
37530
|
+
})]));
|
|
37531
|
+
const fillFromCatalogs = (source, sourceDirectory) => {
|
|
37532
|
+
for (const [packageName, entry] of Object.entries(tracked)) if (!entry.version && declarations[packageName].hasDeclaration) entry.version = resolveCatalogVersion(source, packageName, sourceDirectory, declarations[packageName].catalogReference);
|
|
37533
|
+
};
|
|
37534
|
+
fillFromCatalogs(packageJson, directory);
|
|
37535
|
+
if (!tracked.react.version || !tracked.tailwindcss.version || !tracked.zod.version) {
|
|
37369
37536
|
const monorepoRoot = findMonorepoRoot(directory);
|
|
37370
37537
|
if (monorepoRoot) {
|
|
37371
37538
|
const monorepoPackageJsonPath = Path.join(monorepoRoot, "package.json");
|
|
37372
|
-
if (isFile(monorepoPackageJsonPath))
|
|
37373
|
-
const rootPackageJson = readPackageJson$1(monorepoPackageJsonPath);
|
|
37374
|
-
if (!reactVersion && reactDeclaration.hasDeclaration) reactVersion = resolveCatalogVersion(rootPackageJson, "react", monorepoRoot, reactDeclaration.catalogReference);
|
|
37375
|
-
if (!tailwindVersion && tailwindDeclaration.hasDeclaration) tailwindVersion = resolveCatalogVersion(rootPackageJson, "tailwindcss", monorepoRoot, tailwindDeclaration.catalogReference);
|
|
37376
|
-
if (!zodVersion && zodDeclaration.hasDeclaration) zodVersion = resolveCatalogVersion(rootPackageJson, "zod", monorepoRoot, zodDeclaration.catalogReference);
|
|
37377
|
-
}
|
|
37539
|
+
if (isFile(monorepoPackageJsonPath)) fillFromCatalogs(readPackageJson$1(monorepoPackageJsonPath), monorepoRoot);
|
|
37378
37540
|
}
|
|
37379
37541
|
}
|
|
37380
|
-
|
|
37381
|
-
|
|
37382
|
-
|
|
37383
|
-
|
|
37384
|
-
|
|
37385
|
-
|
|
37542
|
+
const shouldCollectReactGroup = !tracked.react.version || framework === "unknown";
|
|
37543
|
+
const workspaceFacts = collectWorkspaceFacts(directory, packageJson, { collectReactGroup: shouldCollectReactGroup });
|
|
37544
|
+
if (shouldCollectReactGroup) {
|
|
37545
|
+
tracked.react.version ||= workspaceFacts.reactVersion;
|
|
37546
|
+
tracked.tailwindcss.version ||= workspaceFacts.tailwindVersion;
|
|
37547
|
+
tracked.zod.version ||= workspaceFacts.zodVersion;
|
|
37548
|
+
if (framework === "unknown" && workspaceFacts.framework !== "unknown") framework = workspaceFacts.framework;
|
|
37386
37549
|
}
|
|
37387
|
-
if ((!
|
|
37550
|
+
if ((!tracked.react.version || framework === "unknown") && !isMonorepoRoot(directory)) {
|
|
37388
37551
|
const monorepoInfo = findDependencyInfoFromMonorepoRoot(directory);
|
|
37389
|
-
|
|
37390
|
-
|
|
37391
|
-
|
|
37552
|
+
tracked.react.version ||= monorepoInfo.reactVersion;
|
|
37553
|
+
tracked.tailwindcss.version ||= monorepoInfo.tailwindVersion;
|
|
37554
|
+
tracked.zod.version ||= monorepoInfo.zodVersion;
|
|
37392
37555
|
if (framework === "unknown") framework = monorepoInfo.framework;
|
|
37393
37556
|
}
|
|
37394
|
-
|
|
37395
|
-
|
|
37396
|
-
|
|
37557
|
+
for (const [packageName, entry] of Object.entries(tracked)) {
|
|
37558
|
+
const declaredVersion = declarations[packageName].version;
|
|
37559
|
+
if (!entry.version && declaredVersion && !isCatalogReference(declaredVersion)) entry.version = declaredVersion;
|
|
37560
|
+
}
|
|
37561
|
+
const { react, tailwindcss, zod } = tracked;
|
|
37562
|
+
let reactVersion = react.version;
|
|
37563
|
+
if (!reactVersion || parseReactMajor(reactVersion) === null) reactVersion = resolveInstalledReactVersion(directory) ?? reactVersion;
|
|
37564
|
+
const tailwindVersion = tailwindcss.version;
|
|
37565
|
+
const zodVersion = zod.version;
|
|
37397
37566
|
const projectName = packageJson.name ?? Path.basename(directory);
|
|
37398
37567
|
const hasTypeScript = NFS.existsSync(Path.join(directory, "tsconfig.json"));
|
|
37399
37568
|
const sourceFileCount = countSourceFiles(directory);
|
|
37400
|
-
const hasReactNativeWorkspace = framework === "expo" || framework === "react-native" ||
|
|
37569
|
+
const hasReactNativeWorkspace = framework === "expo" || framework === "react-native" || workspaceFacts.hasReactNativeAwarePackage;
|
|
37401
37570
|
const expoVersion = hasReactNativeWorkspace ? resolveCatalogBackedDependencyVersion({
|
|
37402
37571
|
rootDirectory: directory,
|
|
37403
37572
|
rootPackageJson: packageJson,
|
|
37404
37573
|
packageName: "expo",
|
|
37405
|
-
version:
|
|
37574
|
+
version: workspaceFacts.expo.version
|
|
37406
37575
|
}) : null;
|
|
37407
37576
|
const shopifyFlashListVersion = hasReactNativeWorkspace ? resolveCatalogBackedDependencyVersion({
|
|
37408
37577
|
rootDirectory: directory,
|
|
37409
37578
|
rootPackageJson: packageJson,
|
|
37410
37579
|
packageName: SHOPIFY_FLASH_LIST_PACKAGE_NAME,
|
|
37411
|
-
version:
|
|
37580
|
+
version: workspaceFacts.shopifyFlashList.version
|
|
37412
37581
|
}) : null;
|
|
37413
|
-
const hasReanimated = hasReactNativeWorkspace &&
|
|
37582
|
+
const hasReanimated = hasReactNativeWorkspace && workspaceFacts.hasReanimatedAwarePackage;
|
|
37583
|
+
const reanimatedVersion = hasReanimated ? workspaceFacts.reanimatedVersion : null;
|
|
37414
37584
|
const nextjsVersion = framework === "nextjs" ? resolveCatalogBackedDependencyVersion({
|
|
37415
37585
|
rootDirectory: directory,
|
|
37416
37586
|
rootPackageJson: packageJson,
|
|
37417
37587
|
packageName: "next",
|
|
37418
|
-
version:
|
|
37588
|
+
version: workspaceFacts.next.version
|
|
37419
37589
|
}) : null;
|
|
37420
37590
|
const preactVersion = getPreactVersion(packageJson);
|
|
37421
37591
|
const isPreES2023Target = hasTypeScript && detectPreES2023Target(directory);
|
|
@@ -37426,7 +37596,7 @@ const discoverProject = (directory) => {
|
|
|
37426
37596
|
reactMajorVersion: resolveEffectiveReactMajor(reactVersion, packageJson),
|
|
37427
37597
|
tailwindVersion,
|
|
37428
37598
|
zodVersion,
|
|
37429
|
-
zodMajorVersion:
|
|
37599
|
+
zodMajorVersion: zodVersion === null ? null : getLowestDependencyMajor(zodVersion),
|
|
37430
37600
|
framework,
|
|
37431
37601
|
hasTypeScript,
|
|
37432
37602
|
hasReactCompiler: detectReactCompiler(directory, packageJson),
|
|
@@ -37440,7 +37610,9 @@ const discoverProject = (directory) => {
|
|
|
37440
37610
|
shopifyFlashListVersion,
|
|
37441
37611
|
shopifyFlashListMajorVersion: shopifyFlashListVersion === null ? null : getLowestDependencyMajor(shopifyFlashListVersion),
|
|
37442
37612
|
hasReanimated,
|
|
37613
|
+
reanimatedVersion,
|
|
37443
37614
|
isPreES2023Target,
|
|
37615
|
+
isStaticExport: framework === "nextjs" && detectNextjsStaticExport(workspaceFacts.next.sourceDirectory ?? directory),
|
|
37444
37616
|
sourceFileCount
|
|
37445
37617
|
};
|
|
37446
37618
|
cachedProjectInfos.set(directory, projectInfo);
|
|
@@ -37456,57 +37628,6 @@ const discoverProject = (directory) => {
|
|
|
37456
37628
|
* single source file has nothing to analyze.
|
|
37457
37629
|
*/
|
|
37458
37630
|
const isAnalyzableProject = (project) => project.reactVersion !== null || project.preactVersion !== null || project.sourceFileCount > 0;
|
|
37459
|
-
const MAJOR_MINOR_PATTERN = /(\d{1,4})\.(\d{1,4})/;
|
|
37460
|
-
const MAJOR_ONLY_PATTERN = /(\d{1,4})/;
|
|
37461
|
-
const UPPER_BOUND_COMPARATOR_PATTERN = /<=?\s{0,8}\d{1,4}(?:\.\d{1,4}){0,2}(?:-[^\s,|]+)?/g;
|
|
37462
|
-
const parseReactMajorMinor = (reactVersion) => {
|
|
37463
|
-
if (typeof reactVersion !== "string") return null;
|
|
37464
|
-
const trimmed = reactVersion.trim();
|
|
37465
|
-
if (trimmed.length === 0) return null;
|
|
37466
|
-
const lowerBoundsOnly = trimmed.replace(UPPER_BOUND_COMPARATOR_PATTERN, " ").trim();
|
|
37467
|
-
if (lowerBoundsOnly.length === 0) return null;
|
|
37468
|
-
const majorMinorMatch = lowerBoundsOnly.match(MAJOR_MINOR_PATTERN);
|
|
37469
|
-
if (majorMinorMatch) {
|
|
37470
|
-
const major = Number.parseInt(majorMinorMatch[1], 10);
|
|
37471
|
-
const minor = Number.parseInt(majorMinorMatch[2], 10);
|
|
37472
|
-
if (!Number.isFinite(major) || major <= 0) return null;
|
|
37473
|
-
if (!Number.isFinite(minor) || minor < 0) return null;
|
|
37474
|
-
return {
|
|
37475
|
-
major,
|
|
37476
|
-
minor
|
|
37477
|
-
};
|
|
37478
|
-
}
|
|
37479
|
-
const majorOnlyMatch = lowerBoundsOnly.match(MAJOR_ONLY_PATTERN);
|
|
37480
|
-
if (!majorOnlyMatch) return null;
|
|
37481
|
-
const major = Number.parseInt(majorOnlyMatch[1], 10);
|
|
37482
|
-
if (!Number.isFinite(major) || major <= 0) return null;
|
|
37483
|
-
return {
|
|
37484
|
-
major,
|
|
37485
|
-
minor: 0
|
|
37486
|
-
};
|
|
37487
|
-
};
|
|
37488
|
-
const isReactAtLeast = (detected, required) => {
|
|
37489
|
-
if (detected === null) return true;
|
|
37490
|
-
if (detected.major !== required.major) return detected.major > required.major;
|
|
37491
|
-
return detected.minor >= required.minor;
|
|
37492
|
-
};
|
|
37493
|
-
const parseLowerBoundVersion = (versionSpec) => import_semver.validRange(versionSpec) !== null ? import_semver.minVersion(versionSpec) : import_semver.coerce(versionSpec);
|
|
37494
|
-
const parseTailwindMajorMinor = (tailwindVersion) => {
|
|
37495
|
-
if (typeof tailwindVersion !== "string") return null;
|
|
37496
|
-
const trimmed = tailwindVersion.trim();
|
|
37497
|
-
if (trimmed.length === 0) return null;
|
|
37498
|
-
const lowerBound = parseLowerBoundVersion(trimmed);
|
|
37499
|
-
if (lowerBound === null || lowerBound.major <= 0) return null;
|
|
37500
|
-
return {
|
|
37501
|
-
major: lowerBound.major,
|
|
37502
|
-
minor: lowerBound.minor
|
|
37503
|
-
};
|
|
37504
|
-
};
|
|
37505
|
-
const isTailwindAtLeast = (detected, required) => {
|
|
37506
|
-
if (detected === null) return true;
|
|
37507
|
-
if (detected.major !== required.major) return detected.major > required.major;
|
|
37508
|
-
return detected.minor >= required.minor;
|
|
37509
|
-
};
|
|
37510
37631
|
const messageFromUnknown = (error) => error instanceof Error ? error.message : String(error);
|
|
37511
37632
|
var InvalidGlobPatternError = class extends Error {
|
|
37512
37633
|
pattern;
|
|
@@ -37632,6 +37753,9 @@ const buildRuleSeverityControls = (config) => {
|
|
|
37632
37753
|
...config.buckets !== void 0 ? { buckets: config.buckets } : {}
|
|
37633
37754
|
};
|
|
37634
37755
|
};
|
|
37756
|
+
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
37757
|
+
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
37758
|
+
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
37635
37759
|
const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
37636
37760
|
"effect/no-adjust-state-on-prop-change": "react-doctor/no-adjust-state-on-prop-change",
|
|
37637
37761
|
"effect/no-chain-state-updates": "react-doctor/no-chain-state-updates",
|
|
@@ -37709,6 +37833,7 @@ const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
|
37709
37833
|
"react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
|
|
37710
37834
|
"react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
|
|
37711
37835
|
"react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
|
|
37836
|
+
"react/jsx-no-target-blank": "react-doctor/jsx-no-target-blank",
|
|
37712
37837
|
"react/jsx-no-undef": "react-doctor/jsx-no-undef",
|
|
37713
37838
|
"react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
|
|
37714
37839
|
"react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
|
|
@@ -37793,9 +37918,6 @@ const tokenizeRuleList = (ruleList) => {
|
|
|
37793
37918
|
if (!ruleSection) return [];
|
|
37794
37919
|
return ruleSection.split(/[,\s]+/).map((token) => token.trim()).filter(Boolean);
|
|
37795
37920
|
};
|
|
37796
|
-
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
37797
|
-
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
37798
|
-
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
37799
37921
|
const buildHint = (tool, token, ruleId) => `oxlint matches plugin rules only by their full name, so \`${token}\` in your ${tool}-disable comment does not silence \`${ruleId}\` — add \`${ruleId}\` to the comment (e.g. \`${token}, ${ruleId}\`).`;
|
|
37800
37922
|
const tokenMisnamesRule = (token, ruleId) => token !== ruleId && isSameRuleKey(token, ruleId);
|
|
37801
37923
|
const detectInlineNearMiss = (lines, diagnosticLineIndex, ruleId) => {
|
|
@@ -37849,6 +37971,53 @@ const detectForeignDisableNearMiss = (lines, diagnosticLineIndex, ruleId) => {
|
|
|
37849
37971
|
if (!ruleId.startsWith("react-doctor/")) return null;
|
|
37850
37972
|
return detectInlineNearMiss(lines, diagnosticLineIndex, ruleId) ?? detectBlockNearMiss(lines, diagnosticLineIndex, ruleId);
|
|
37851
37973
|
};
|
|
37974
|
+
const ESLINT_REACT_HOOKS_PLUGIN_PREFIX = "react-hooks/";
|
|
37975
|
+
const buildAcceptedTokens = (ruleId) => {
|
|
37976
|
+
const shortRuleId = ruleId.slice(15);
|
|
37977
|
+
return new Set([ruleId, `${ESLINT_REACT_HOOKS_PLUGIN_PREFIX}${shortRuleId}`]);
|
|
37978
|
+
};
|
|
37979
|
+
const listNamesRule = (ruleList, acceptedTokens) => tokenizeRuleList(ruleList).some((token) => acceptedTokens.has(token));
|
|
37980
|
+
const isInlineDisabled = (lines, diagnosticLineIndex, acceptedTokens) => {
|
|
37981
|
+
const candidates = [{
|
|
37982
|
+
line: lines[diagnosticLineIndex],
|
|
37983
|
+
requiredScope: "line"
|
|
37984
|
+
}, {
|
|
37985
|
+
line: lines[diagnosticLineIndex - 1],
|
|
37986
|
+
requiredScope: "next-line"
|
|
37987
|
+
}];
|
|
37988
|
+
for (const { line, requiredScope } of candidates) {
|
|
37989
|
+
const match = line?.match(FOREIGN_INLINE_DISABLE_PATTERN);
|
|
37990
|
+
if (!match) continue;
|
|
37991
|
+
const [, , scope, ruleList] = match;
|
|
37992
|
+
if (scope !== requiredScope) continue;
|
|
37993
|
+
if (listNamesRule(ruleList, acceptedTokens)) return true;
|
|
37994
|
+
}
|
|
37995
|
+
return false;
|
|
37996
|
+
};
|
|
37997
|
+
const isBlockDisabled = (lines, diagnosticLineIndex, acceptedTokens) => {
|
|
37998
|
+
let isRangeOpen = false;
|
|
37999
|
+
const lastLineIndex = Math.min(diagnosticLineIndex, lines.length - 1);
|
|
38000
|
+
for (let lineIndex = 0; lineIndex <= lastLineIndex; lineIndex++) {
|
|
38001
|
+
const line = lines[lineIndex];
|
|
38002
|
+
if (line === void 0 || !line.includes("-disable") && !line.includes("-enable")) continue;
|
|
38003
|
+
const disableMatch = line.match(FOREIGN_BLOCK_DISABLE_PATTERN);
|
|
38004
|
+
if (disableMatch && listNamesRule(disableMatch[2], acceptedTokens)) {
|
|
38005
|
+
isRangeOpen = true;
|
|
38006
|
+
continue;
|
|
38007
|
+
}
|
|
38008
|
+
const enableMatch = line.match(FOREIGN_BLOCK_ENABLE_PATTERN);
|
|
38009
|
+
if (enableMatch) {
|
|
38010
|
+
const enabledRules = tokenizeRuleList(enableMatch[1]);
|
|
38011
|
+
if (enabledRules.length === 0 || enabledRules.some((rule) => acceptedTokens.has(rule))) isRangeOpen = false;
|
|
38012
|
+
}
|
|
38013
|
+
}
|
|
38014
|
+
return isRangeOpen;
|
|
38015
|
+
};
|
|
38016
|
+
const isCompilerRuleForeignDisabled = (lines, diagnosticLineIndex, ruleId) => {
|
|
38017
|
+
if (!ruleId.startsWith("react-hooks-js/")) return false;
|
|
38018
|
+
const acceptedTokens = buildAcceptedTokens(ruleId);
|
|
38019
|
+
return isInlineDisabled(lines, diagnosticLineIndex, acceptedTokens) || isBlockDisabled(lines, diagnosticLineIndex, acceptedTokens);
|
|
38020
|
+
};
|
|
37852
38021
|
const JSX_OPENER_TAG_PATTERN = /<[A-Za-z][\w.]*/g;
|
|
37853
38022
|
const JSX_TAG_NAME_FOLLOW = /[A-Za-z]/;
|
|
37854
38023
|
const isOpenerMatchInsideLineComment = (line, openerCharIndex) => {
|
|
@@ -37992,6 +38161,11 @@ const evaluateSuppression = (lines, diagnosticLineIndex, ruleId) => {
|
|
|
37992
38161
|
isSuppressed: true,
|
|
37993
38162
|
nearMissHint: null
|
|
37994
38163
|
};
|
|
38164
|
+
if (isCompilerRuleForeignDisabled(lines, diagnosticLineIndex, ruleId)) return {
|
|
38165
|
+
isSuppressed: true,
|
|
38166
|
+
nearMissHint: null,
|
|
38167
|
+
isForeignDirective: true
|
|
38168
|
+
};
|
|
37995
38169
|
const directComments = findStackedDisableCommentsAbove(lines, diagnosticLineIndex);
|
|
37996
38170
|
if (hasChainSuppressor(directComments, ruleId)) return {
|
|
37997
38171
|
isSuppressed: true,
|
|
@@ -38273,12 +38447,18 @@ const collectStringSet = (values) => {
|
|
|
38273
38447
|
return new Set(values.filter((value) => typeof value === "string"));
|
|
38274
38448
|
};
|
|
38275
38449
|
const JSX_NO_UNDEF_IDENTIFIER_PATTERN = /^`([^`]+)`/;
|
|
38450
|
+
const NODE_CLI_PATH_SEGMENTS = new Set([
|
|
38451
|
+
"scripts",
|
|
38452
|
+
"bin",
|
|
38453
|
+
"tools"
|
|
38454
|
+
]);
|
|
38455
|
+
const hasNodeCliPathSegment = (filePath) => filePath.split(/[\\/]/).slice(0, -1).some((segment) => NODE_CLI_PATH_SEGMENTS.has(segment));
|
|
38276
38456
|
/**
|
|
38277
38457
|
* Pre-compiles every stateful filter and returns a single
|
|
38278
38458
|
* `apply(diagnostic)` closure that runs (in order):
|
|
38279
38459
|
*
|
|
38280
38460
|
* 1. auto-suppress (test-noise rules in test files; `migration-hint`
|
|
38281
|
-
* wins over `test-noise`)
|
|
38461
|
+
* wins over `test-noise`; `no-console` on Node CLI scripts)
|
|
38282
38462
|
* 2. severity overrides (top-level `rules` / `categories`, with
|
|
38283
38463
|
* `"off"` dropping)
|
|
38284
38464
|
* 3. warning suppression (only when `showWarnings` is false: drops every
|
|
@@ -38376,6 +38556,12 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
38376
38556
|
if (hasRawTextWrappers && isInsideStringOnlyWrapper(lines, diagnostic.line, diagnostic.column, rawTextWrapperComponentNames)) return true;
|
|
38377
38557
|
return false;
|
|
38378
38558
|
};
|
|
38559
|
+
const isNoConsoleOnNodeCliScript = (diagnostic) => {
|
|
38560
|
+
if (diagnostic.rule !== "no-console") return false;
|
|
38561
|
+
if (hasNodeCliPathSegment(diagnostic.filePath)) return true;
|
|
38562
|
+
const lines = getFileLines(diagnostic.filePath);
|
|
38563
|
+
return lines !== null && Boolean(lines[0]?.startsWith("#!"));
|
|
38564
|
+
};
|
|
38379
38565
|
const isJsxNoUndefSuppressedByConfig = (diagnostic) => {
|
|
38380
38566
|
if (!hasRuntimeGlobals) return false;
|
|
38381
38567
|
if (diagnostic.rule !== "jsx-no-undef") return false;
|
|
@@ -38385,6 +38571,7 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
38385
38571
|
return {
|
|
38386
38572
|
apply: (diagnostic) => {
|
|
38387
38573
|
if (shouldAutoSuppress(diagnostic)) return null;
|
|
38574
|
+
if (isNoConsoleOnNodeCliScript(diagnostic)) return null;
|
|
38388
38575
|
let current = diagnostic;
|
|
38389
38576
|
let explicitSeverityOverride;
|
|
38390
38577
|
let explicitRuleOverride;
|
|
@@ -38414,7 +38601,7 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
38414
38601
|
if (lines) {
|
|
38415
38602
|
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
38416
38603
|
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
38417
|
-
if (evaluation.isSuppressed) return suppress(current, "inline");
|
|
38604
|
+
if (evaluation.isSuppressed) return suppress(current, evaluation.isForeignDirective ? "foreign-inline" : "inline");
|
|
38418
38605
|
if (evaluation.nearMissHint) current = {
|
|
38419
38606
|
...current,
|
|
38420
38607
|
suppressionHint: evaluation.nearMissHint
|
|
@@ -40251,64 +40438,64 @@ function* collectSecurityScanFiles(rootDirectory) {
|
|
|
40251
40438
|
}
|
|
40252
40439
|
}
|
|
40253
40440
|
}
|
|
40254
|
-
|
|
40255
|
-
|
|
40256
|
-
|
|
40257
|
-
|
|
40258
|
-
|
|
40259
|
-
|
|
40260
|
-
|
|
40441
|
+
const CLIENT_ONLY_FRAMEWORKS = new Set([
|
|
40442
|
+
"vite",
|
|
40443
|
+
"cra",
|
|
40444
|
+
"gatsby",
|
|
40445
|
+
"react-native",
|
|
40446
|
+
"expo"
|
|
40447
|
+
]);
|
|
40448
|
+
const addMajorLadder = (capabilities, name, major, earliest, latest) => {
|
|
40449
|
+
if (major === null) return;
|
|
40450
|
+
const cappedMajor = Math.min(major, latest);
|
|
40451
|
+
for (let candidate = earliest; candidate <= cappedMajor; candidate += 1) capabilities.add(`${name}:${candidate}`);
|
|
40452
|
+
};
|
|
40261
40453
|
const buildCapabilities = (project) => {
|
|
40262
40454
|
const capabilities = /* @__PURE__ */ new Set();
|
|
40263
40455
|
capabilities.add(project.framework);
|
|
40264
|
-
if (
|
|
40456
|
+
if (project.reactVersion !== null || project.preactVersion !== null) capabilities.add("react");
|
|
40265
40457
|
if (project.framework === "expo" || project.framework === "react-native" || project.hasReactNativeWorkspace) capabilities.add("react-native");
|
|
40266
40458
|
if (project.expoVersion !== null) capabilities.add("expo");
|
|
40459
|
+
if ((project.framework === "nextjs" || project.framework === "tanstack-start" || project.framework === "remix") && !project.isStaticExport) capabilities.add("server-actions");
|
|
40460
|
+
if (CLIENT_ONLY_FRAMEWORKS.has(project.framework)) capabilities.add("client-only");
|
|
40461
|
+
if (project.isStaticExport) capabilities.add("nextjs:static-export");
|
|
40267
40462
|
if (project.nextjsMajorVersion !== null && project.nextjsMajorVersion >= 15) capabilities.add("nextjs:15");
|
|
40268
|
-
|
|
40269
|
-
if (
|
|
40270
|
-
|
|
40271
|
-
|
|
40272
|
-
|
|
40273
|
-
|
|
40274
|
-
|
|
40275
|
-
|
|
40276
|
-
|
|
40277
|
-
|
|
40278
|
-
|
|
40279
|
-
if (project.
|
|
40280
|
-
capabilities.add("tailwind");
|
|
40281
|
-
if (isTailwindAtLeast(parseTailwindMajorMinor(project.tailwindVersion), {
|
|
40282
|
-
major: 3,
|
|
40283
|
-
minor: 4
|
|
40284
|
-
})) capabilities.add("tailwind:3.4");
|
|
40285
|
-
}
|
|
40286
|
-
if (project.zodVersion !== null) {
|
|
40287
|
-
capabilities.add("zod");
|
|
40288
|
-
if (project.zodMajorVersion !== null && project.zodMajorVersion >= 4) capabilities.add("zod:4");
|
|
40289
|
-
}
|
|
40463
|
+
addMajorLadder(capabilities, "react", project.reactMajorVersion, 17, 30);
|
|
40464
|
+
if (project.reactMajorVersion !== null && project.reactMajorVersion >= 19 && isMajorMinorAtLeast(parseReactMajorMinor(project.reactVersion), {
|
|
40465
|
+
major: 19,
|
|
40466
|
+
minor: 2
|
|
40467
|
+
})) capabilities.add("react:19.2");
|
|
40468
|
+
if (project.tailwindVersion !== null) capabilities.add("tailwind");
|
|
40469
|
+
if (project.tailwindVersion !== null && isMajorMinorAtLeast(parseTailwindMajorMinor(project.tailwindVersion), {
|
|
40470
|
+
major: 3,
|
|
40471
|
+
minor: 4
|
|
40472
|
+
})) capabilities.add("tailwind:3.4");
|
|
40473
|
+
if (project.zodVersion !== null) capabilities.add("zod");
|
|
40474
|
+
if (project.zodMajorVersion !== null && project.zodMajorVersion >= 4) capabilities.add("zod:4");
|
|
40290
40475
|
if (project.isPreES2023Target) capabilities.add("pre-es2023");
|
|
40291
40476
|
if (project.hasReactCompiler) capabilities.add("react-compiler");
|
|
40292
40477
|
if (project.hasTanStackQuery) capabilities.add("tanstack-query");
|
|
40293
40478
|
if (project.hasTypeScript) capabilities.add("typescript");
|
|
40294
|
-
if (project.preactVersion !== null)
|
|
40295
|
-
|
|
40296
|
-
|
|
40297
|
-
if (preactMajor !== null) {
|
|
40298
|
-
const cappedPreactMajor = Math.min(preactMajor, 20);
|
|
40299
|
-
for (let major = 10; major <= cappedPreactMajor; major++) capabilities.add(`preact:${major}`);
|
|
40300
|
-
}
|
|
40301
|
-
if (project.reactVersion === null) capabilities.add("pure-preact");
|
|
40302
|
-
}
|
|
40479
|
+
if (project.preactVersion !== null) capabilities.add("preact");
|
|
40480
|
+
addMajorLadder(capabilities, "preact", project.preactMajorVersion, 10, 20);
|
|
40481
|
+
if (project.preactVersion !== null && project.reactVersion === null) capabilities.add("pure-preact");
|
|
40303
40482
|
return capabilities;
|
|
40304
40483
|
};
|
|
40305
|
-
const
|
|
40484
|
+
const capabilitiesByProject = /* @__PURE__ */ new WeakMap();
|
|
40485
|
+
const getCapabilities = (project) => {
|
|
40486
|
+
const cached = capabilitiesByProject.get(project);
|
|
40487
|
+
if (cached !== void 0) return cached;
|
|
40488
|
+
const capabilities = buildCapabilities(project);
|
|
40489
|
+
capabilitiesByProject.set(project, capabilities);
|
|
40490
|
+
return capabilities;
|
|
40491
|
+
};
|
|
40492
|
+
const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledWhen) => {
|
|
40306
40493
|
if (requires) {
|
|
40307
40494
|
for (const capability of requires) if (!capabilities.has(capability)) return false;
|
|
40308
40495
|
}
|
|
40309
40496
|
if (tags?.includes("react-jsx-only") && !capabilities.has("react")) return false;
|
|
40310
|
-
if (
|
|
40311
|
-
for (const capability of
|
|
40497
|
+
if (disabledWhen) {
|
|
40498
|
+
for (const capability of disabledWhen) if (capabilities.has(capability)) return false;
|
|
40312
40499
|
}
|
|
40313
40500
|
if (tags) {
|
|
40314
40501
|
for (const tag of tags) if (ignoredTags.has(tag)) return false;
|
|
@@ -40319,14 +40506,14 @@ const yieldToEventLoop = () => new Promise((resolve) => {
|
|
|
40319
40506
|
setImmediate(resolve);
|
|
40320
40507
|
});
|
|
40321
40508
|
const createSecurityScanSession = (rootDirectory, options) => {
|
|
40322
|
-
const capabilities = options.project ?
|
|
40509
|
+
const capabilities = options.project ? getCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
40323
40510
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
40324
40511
|
const enabledScanRules = REACT_DOCTOR_RULES.flatMap((entry) => {
|
|
40325
40512
|
const rule = entry.rule;
|
|
40326
40513
|
const scan = rule.scan;
|
|
40327
40514
|
if (typeof scan !== "function") return [];
|
|
40328
40515
|
if (rule.defaultEnabled === false) return [];
|
|
40329
|
-
if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.
|
|
40516
|
+
if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledWhen)) return [];
|
|
40330
40517
|
return [{
|
|
40331
40518
|
entry,
|
|
40332
40519
|
scan,
|
|
@@ -40346,6 +40533,7 @@ const createSecurityScanSession = (rootDirectory, options) => {
|
|
|
40346
40533
|
return status === true;
|
|
40347
40534
|
};
|
|
40348
40535
|
const scanFileByRule = function* (file) {
|
|
40536
|
+
if (file === null) return;
|
|
40349
40537
|
for (const { entry, scan, committedFilesOnly } of enabledScanRules) {
|
|
40350
40538
|
for (const finding of scan(file)) {
|
|
40351
40539
|
if (committedFilesOnly && isFileGitIgnored(file)) continue;
|
|
@@ -41807,7 +41995,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
41807
41995
|
})),
|
|
41808
41996
|
showStagedContent: (directory, relativePath, options) => runCommand({
|
|
41809
41997
|
command: "git",
|
|
41810
|
-
args: ["show",
|
|
41998
|
+
args: ["show", `:./${relativePath}`],
|
|
41811
41999
|
directory,
|
|
41812
42000
|
maxStdoutBytes: options?.maxBufferBytes
|
|
41813
42001
|
}).pipe(map$3((result) => result.status === 0 ? result.stdout : null)),
|
|
@@ -42242,7 +42430,7 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
42242
42430
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
42243
42431
|
const jsPlugins = [];
|
|
42244
42432
|
if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
|
|
42245
|
-
const capabilities =
|
|
42433
|
+
const capabilities = getCapabilities(project);
|
|
42246
42434
|
const enabledReactDoctorRules = {};
|
|
42247
42435
|
for (const registryEntry of REACT_DOCTOR_RULES) {
|
|
42248
42436
|
const rule = reactDoctorPlugin.rules[registryEntry.id];
|
|
@@ -42253,7 +42441,7 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
42253
42441
|
if (rule.scan !== void 0) continue;
|
|
42254
42442
|
if (customRulesOnly && registryEntry.originallyExternal) continue;
|
|
42255
42443
|
if (rule.framework !== "global" && !rule.requires) continue;
|
|
42256
|
-
if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.
|
|
42444
|
+
if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledWhen)) continue;
|
|
42257
42445
|
const explicitRuleOverride = resolveRuleSeverityOverride({ ruleKey: registryEntry.key }, severityControls);
|
|
42258
42446
|
if (rule.defaultEnabled === false && explicitRuleOverride === void 0) continue;
|
|
42259
42447
|
const severity = resolveRuleSeverityOverride({
|
|
@@ -42284,6 +42472,7 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
42284
42472
|
settings: { "react-doctor": {
|
|
42285
42473
|
framework: project.framework,
|
|
42286
42474
|
rootDirectory: resolveSettingsRootDirectory(project.rootDirectory),
|
|
42475
|
+
capabilities: [...capabilities].sort(),
|
|
42287
42476
|
...project.shopifyFlashListMajorVersion !== null ? { shopifyFlashListMajorVersion: project.shopifyFlashListMajorVersion } : {},
|
|
42288
42477
|
...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {}
|
|
42289
42478
|
} },
|
|
@@ -42574,25 +42763,18 @@ const mapWithConcurrency = async (items, concurrency, task) => {
|
|
|
42574
42763
|
if (errors.length > 0) throw errors[0];
|
|
42575
42764
|
return results;
|
|
42576
42765
|
};
|
|
42577
|
-
const getPublicEnvPrefix = (framework) => {
|
|
42578
|
-
switch (framework) {
|
|
42579
|
-
case "nextjs": return "NEXT_PUBLIC_*";
|
|
42580
|
-
case "vite":
|
|
42581
|
-
case "tanstack-start": return "VITE_*";
|
|
42582
|
-
case "cra": return "REACT_APP_*";
|
|
42583
|
-
case "gatsby": return "GATSBY_*";
|
|
42584
|
-
default: return null;
|
|
42585
|
-
}
|
|
42586
|
-
};
|
|
42587
|
-
const buildNoSecretsRecommendation = (project, fallbackRecommendation) => {
|
|
42588
|
-
const publicEnvPrefix = getPublicEnvPrefix(project.framework);
|
|
42589
|
-
if (!publicEnvPrefix) return fallbackRecommendation;
|
|
42590
|
-
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`;
|
|
42591
|
-
};
|
|
42592
42766
|
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";
|
|
42767
|
+
const ACCESSORS_MIN_VERSION = "3.15.0";
|
|
42768
|
+
const supportsAccessors = (reanimatedVersion) => {
|
|
42769
|
+
if (reanimatedVersion === null) return true;
|
|
42770
|
+
const lowerBound = import_semver.validRange(reanimatedVersion) !== null ? import_semver.minVersion(reanimatedVersion) : import_semver.coerce(reanimatedVersion);
|
|
42771
|
+
if (lowerBound === null) return true;
|
|
42772
|
+
return import_semver.gte(lowerBound, ACCESSORS_MIN_VERSION);
|
|
42773
|
+
};
|
|
42593
42774
|
const appendReanimatedSharedValueHint = (help, rule, project) => {
|
|
42594
42775
|
if (rule !== "immutability") return help;
|
|
42595
42776
|
if (!project.hasReanimated) return help;
|
|
42777
|
+
if (!supportsAccessors(project.reanimatedVersion)) return help;
|
|
42596
42778
|
if (!help) return REANIMATED_SHARED_VALUE_HINT;
|
|
42597
42779
|
return `${help}\n\n${REANIMATED_SHARED_VALUE_HINT}`;
|
|
42598
42780
|
};
|
|
@@ -42754,13 +42936,13 @@ const REACT_USE_BINDING_RESOLUTION = {
|
|
|
42754
42936
|
isReactUseBinding: true,
|
|
42755
42937
|
isReactNamespaceBinding: false
|
|
42756
42938
|
};
|
|
42757
|
-
const getScriptKind = (filename) => {
|
|
42939
|
+
const getScriptKind$2 = (filename) => {
|
|
42758
42940
|
if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
42759
42941
|
if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
42760
42942
|
if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
42761
42943
|
return ts.ScriptKind.JS;
|
|
42762
42944
|
};
|
|
42763
|
-
const getUtf16Offset = (sourceText, utf8Offset) => Buffer.from(sourceText).subarray(0, utf8Offset).toString("utf8").length;
|
|
42945
|
+
const getUtf16Offset$1 = (sourceText, utf8Offset) => Buffer.from(sourceText).subarray(0, utf8Offset).toString("utf8").length;
|
|
42764
42946
|
const unwrapExpression = (expression) => {
|
|
42765
42947
|
let currentExpression = expression;
|
|
42766
42948
|
while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression)) currentExpression = currentExpression.expression;
|
|
@@ -43009,8 +43191,8 @@ const findUseCallIdentifier = (sourceFile, useOffset) => {
|
|
|
43009
43191
|
return matchedIdentifier;
|
|
43010
43192
|
};
|
|
43011
43193
|
const resolveUseCallBinding = (sourceText, filename, utf8Offset) => {
|
|
43012
|
-
const sourceFile = ts.createSourceFile(filename, sourceText, ts.ScriptTarget.Latest, true, getScriptKind(filename));
|
|
43013
|
-
const useIdentifier = findUseCallIdentifier(sourceFile, getUtf16Offset(sourceText, utf8Offset));
|
|
43194
|
+
const sourceFile = ts.createSourceFile(filename, sourceText, ts.ScriptTarget.Latest, true, getScriptKind$2(filename));
|
|
43195
|
+
const useIdentifier = findUseCallIdentifier(sourceFile, getUtf16Offset$1(sourceText, utf8Offset));
|
|
43014
43196
|
if (!useIdentifier) return null;
|
|
43015
43197
|
return resolveIdentifierBinding(useIdentifier, collectReactImportBindings(sourceFile), sourceFile);
|
|
43016
43198
|
};
|
|
@@ -43031,6 +43213,201 @@ const shouldSuppressLocalUseHookDiagnostic = (diagnostic, rootDirectory) => {
|
|
|
43031
43213
|
const bindingResolution = resolveUseCallBinding(sourceText, absolutePath, primaryLabel.span.offset);
|
|
43032
43214
|
return bindingResolution !== null && !bindingResolution.isReactUseBinding;
|
|
43033
43215
|
};
|
|
43216
|
+
const SUPPRESSED_COMPILER_CODES = new Set(["react-hooks-js(immutability)", "react-hooks-js(refs)"]);
|
|
43217
|
+
const WORKLET_DIRECTIVE = "worklet";
|
|
43218
|
+
const WORKLET_ACCEPTING_CALLEES = new Set([
|
|
43219
|
+
"useAnimatedStyle",
|
|
43220
|
+
"useAnimatedProps",
|
|
43221
|
+
"useDerivedValue",
|
|
43222
|
+
"useAnimatedReaction",
|
|
43223
|
+
"useAnimatedScrollHandler",
|
|
43224
|
+
"useAnimatedGestureHandler",
|
|
43225
|
+
"useFrameCallback",
|
|
43226
|
+
"runOnUI",
|
|
43227
|
+
"executeOnUIRuntimeSync"
|
|
43228
|
+
]);
|
|
43229
|
+
const GESTURE_ROOT_IDENTIFIER = "Gesture";
|
|
43230
|
+
const getScriptKind$1 = (filename) => {
|
|
43231
|
+
if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
43232
|
+
if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
43233
|
+
if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
43234
|
+
return ts.ScriptKind.JS;
|
|
43235
|
+
};
|
|
43236
|
+
const getUtf16Offset = (sourceText, utf8Offset) => Buffer.from(sourceText).subarray(0, utf8Offset).toString("utf8").length;
|
|
43237
|
+
const hasWorkletDirective = (node) => {
|
|
43238
|
+
if (!ts.isFunctionDeclaration(node) && !ts.isFunctionExpression(node) && !ts.isArrowFunction(node) && !ts.isMethodDeclaration(node)) return false;
|
|
43239
|
+
const body = node.body;
|
|
43240
|
+
if (!body || !ts.isBlock(body)) return false;
|
|
43241
|
+
for (const statement of body.statements) {
|
|
43242
|
+
if (!ts.isExpressionStatement(statement)) return false;
|
|
43243
|
+
if (!ts.isStringLiteral(statement.expression)) return false;
|
|
43244
|
+
if (statement.expression.text === WORKLET_DIRECTIVE) return true;
|
|
43245
|
+
}
|
|
43246
|
+
return false;
|
|
43247
|
+
};
|
|
43248
|
+
const rootIdentifierOfCallChain = (expression) => {
|
|
43249
|
+
let currentExpression = expression;
|
|
43250
|
+
while (true) {
|
|
43251
|
+
if (ts.isIdentifier(currentExpression)) return currentExpression.text;
|
|
43252
|
+
if (ts.isPropertyAccessExpression(currentExpression)) {
|
|
43253
|
+
currentExpression = currentExpression.expression;
|
|
43254
|
+
continue;
|
|
43255
|
+
}
|
|
43256
|
+
if (ts.isCallExpression(currentExpression)) {
|
|
43257
|
+
currentExpression = currentExpression.expression;
|
|
43258
|
+
continue;
|
|
43259
|
+
}
|
|
43260
|
+
return null;
|
|
43261
|
+
}
|
|
43262
|
+
};
|
|
43263
|
+
const isWorkletAcceptingCall = (callExpression) => {
|
|
43264
|
+
const callee = callExpression.expression;
|
|
43265
|
+
if (ts.isIdentifier(callee)) return WORKLET_ACCEPTING_CALLEES.has(callee.text);
|
|
43266
|
+
if (ts.isPropertyAccessExpression(callee)) {
|
|
43267
|
+
if (WORKLET_ACCEPTING_CALLEES.has(callee.name.text)) return true;
|
|
43268
|
+
return rootIdentifierOfCallChain(callee) === GESTURE_ROOT_IDENTIFIER;
|
|
43269
|
+
}
|
|
43270
|
+
return false;
|
|
43271
|
+
};
|
|
43272
|
+
const isFunctionWorklet = (functionNode) => {
|
|
43273
|
+
if (hasWorkletDirective(functionNode)) return true;
|
|
43274
|
+
const parent = functionNode.parent;
|
|
43275
|
+
return ts.isCallExpression(parent) && parent.arguments.some((argument) => argument === functionNode) && isWorkletAcceptingCall(parent);
|
|
43276
|
+
};
|
|
43277
|
+
const findNodeAtOffset = (sourceFile, targetOffset) => {
|
|
43278
|
+
let matchedNode = null;
|
|
43279
|
+
const visit = (node) => {
|
|
43280
|
+
if (node.getStart(sourceFile) > targetOffset || node.getEnd() <= targetOffset) return;
|
|
43281
|
+
matchedNode = node;
|
|
43282
|
+
ts.forEachChild(node, visit);
|
|
43283
|
+
};
|
|
43284
|
+
visit(sourceFile);
|
|
43285
|
+
return matchedNode;
|
|
43286
|
+
};
|
|
43287
|
+
const isOffsetInsideWorklet = (sourceFile, targetOffset) => {
|
|
43288
|
+
let currentNode = findNodeAtOffset(sourceFile, targetOffset);
|
|
43289
|
+
while (currentNode) {
|
|
43290
|
+
if (ts.isFunctionLike(currentNode) && isFunctionWorklet(currentNode)) return true;
|
|
43291
|
+
currentNode = currentNode.parent;
|
|
43292
|
+
}
|
|
43293
|
+
return false;
|
|
43294
|
+
};
|
|
43295
|
+
const shouldSuppressCompilerFindingInWorklet = (diagnostic, project, rootDirectory) => {
|
|
43296
|
+
if (!SUPPRESSED_COMPILER_CODES.has(diagnostic.code)) return false;
|
|
43297
|
+
if (!project.hasReanimated) return false;
|
|
43298
|
+
const primaryLabel = diagnostic.labels[0];
|
|
43299
|
+
if (!primaryLabel) return false;
|
|
43300
|
+
const absolutePath = Path.isAbsolute(diagnostic.filename) ? diagnostic.filename : Path.join(rootDirectory, diagnostic.filename);
|
|
43301
|
+
let sourceText;
|
|
43302
|
+
try {
|
|
43303
|
+
sourceText = NFS.readFileSync(absolutePath, "utf-8");
|
|
43304
|
+
} catch {
|
|
43305
|
+
return false;
|
|
43306
|
+
}
|
|
43307
|
+
return isOffsetInsideWorklet(ts.createSourceFile(absolutePath, sourceText, ts.ScriptTarget.Latest, true, getScriptKind$1(absolutePath)), getUtf16Offset(sourceText, primaryLabel.span.offset));
|
|
43308
|
+
};
|
|
43309
|
+
const MANUAL_MEMOIZATION_PLUGIN = "react-doctor";
|
|
43310
|
+
const MANUAL_MEMOIZATION_RULE = "react-compiler-no-manual-memoization";
|
|
43311
|
+
const REACT_COMPILER_PLUGIN = "react-hooks-js";
|
|
43312
|
+
const getScriptKind = (filename) => {
|
|
43313
|
+
if (filename.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
43314
|
+
if (filename.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
43315
|
+
if (filename.endsWith(".ts")) return ts.ScriptKind.TS;
|
|
43316
|
+
return ts.ScriptKind.JS;
|
|
43317
|
+
};
|
|
43318
|
+
const isManualMemoizationDiagnostic = (diagnostic) => diagnostic.plugin === MANUAL_MEMOIZATION_PLUGIN && diagnostic.rule === MANUAL_MEMOIZATION_RULE;
|
|
43319
|
+
const nodeLineRange = (sourceFile, node) => ({
|
|
43320
|
+
startLine: sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1,
|
|
43321
|
+
endLine: sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line + 1
|
|
43322
|
+
});
|
|
43323
|
+
const findOutermostEnclosingFunctionRange = (sourceFile, targetLine) => {
|
|
43324
|
+
let foundRange = null;
|
|
43325
|
+
const visit = (node) => {
|
|
43326
|
+
if (foundRange) return;
|
|
43327
|
+
if (ts.isFunctionLike(node)) {
|
|
43328
|
+
const range = nodeLineRange(sourceFile, node);
|
|
43329
|
+
if (range.startLine <= targetLine && targetLine <= range.endLine) foundRange = range;
|
|
43330
|
+
return;
|
|
43331
|
+
}
|
|
43332
|
+
ts.forEachChild(node, visit);
|
|
43333
|
+
};
|
|
43334
|
+
visit(sourceFile);
|
|
43335
|
+
return foundRange;
|
|
43336
|
+
};
|
|
43337
|
+
const NEWLINE_BYTE = 10;
|
|
43338
|
+
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
43339
|
+
let lineNumber = 1;
|
|
43340
|
+
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
43341
|
+
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === NEWLINE_BYTE) lineNumber++;
|
|
43342
|
+
return lineNumber;
|
|
43343
|
+
};
|
|
43344
|
+
/**
|
|
43345
|
+
* Drops `react-doctor/react-compiler-no-manual-memoization` diagnostics
|
|
43346
|
+
* whose enclosing function also carries a `react-hooks-js` (React
|
|
43347
|
+
* Compiler) diagnostic. Every react-hooks-js finding marks a function the
|
|
43348
|
+
* compiler could NOT compile (a bail-out / skip / preserve-manual-
|
|
43349
|
+
* memoization failure), so "React Compiler already caches every value
|
|
43350
|
+
* here" is false for that function and the manual memoization is
|
|
43351
|
+
* load-bearing, not dead weight.
|
|
43352
|
+
*
|
|
43353
|
+
* Matching is per enclosing function, not per file: a bailed-out
|
|
43354
|
+
* component must not silence redundant-memo findings in a sibling
|
|
43355
|
+
* component the compiler did optimize.
|
|
43356
|
+
*
|
|
43357
|
+
* Module-scope `memo(() => …)` wrappers have no enclosing function; for
|
|
43358
|
+
* those the memo call's own span is the containment range, so a bail-out
|
|
43359
|
+
* inside the inline-wrapped component still suppresses. Every failure
|
|
43360
|
+
* path (unreadable file, missing span) fails open and keeps the
|
|
43361
|
+
* diagnostic.
|
|
43362
|
+
*/
|
|
43363
|
+
const suppressMemoizationInBailedOutFunctions = (diagnostics, rootDirectory) => {
|
|
43364
|
+
const compilerDiagnosticLinesByFile = /* @__PURE__ */ new Map();
|
|
43365
|
+
let hasManualMemoizationDiagnostic = false;
|
|
43366
|
+
for (const diagnostic of diagnostics) {
|
|
43367
|
+
if (isManualMemoizationDiagnostic(diagnostic)) {
|
|
43368
|
+
hasManualMemoizationDiagnostic = true;
|
|
43369
|
+
continue;
|
|
43370
|
+
}
|
|
43371
|
+
if (diagnostic.plugin !== REACT_COMPILER_PLUGIN || diagnostic.line <= 0) continue;
|
|
43372
|
+
const lines = compilerDiagnosticLinesByFile.get(diagnostic.filePath);
|
|
43373
|
+
if (lines) lines.push(diagnostic.line);
|
|
43374
|
+
else compilerDiagnosticLinesByFile.set(diagnostic.filePath, [diagnostic.line]);
|
|
43375
|
+
}
|
|
43376
|
+
if (!hasManualMemoizationDiagnostic || compilerDiagnosticLinesByFile.size === 0) return diagnostics;
|
|
43377
|
+
const parsedFileCache = /* @__PURE__ */ new Map();
|
|
43378
|
+
const readAndParse = (filePath) => {
|
|
43379
|
+
const cached = parsedFileCache.get(filePath);
|
|
43380
|
+
if (cached !== void 0) return cached;
|
|
43381
|
+
const absolutePath = Path.isAbsolute(filePath) ? filePath : Path.resolve(rootDirectory || ".", filePath);
|
|
43382
|
+
let parsed;
|
|
43383
|
+
try {
|
|
43384
|
+
const buffer = NFS.readFileSync(absolutePath);
|
|
43385
|
+
parsed = {
|
|
43386
|
+
sourceFile: ts.createSourceFile(absolutePath, buffer.toString("utf8"), ts.ScriptTarget.Latest, true, getScriptKind(absolutePath)),
|
|
43387
|
+
buffer
|
|
43388
|
+
};
|
|
43389
|
+
} catch {
|
|
43390
|
+
parsed = null;
|
|
43391
|
+
}
|
|
43392
|
+
parsedFileCache.set(filePath, parsed);
|
|
43393
|
+
return parsed;
|
|
43394
|
+
};
|
|
43395
|
+
const isInsideBailedOutFunction = (diagnostic) => {
|
|
43396
|
+
const compilerLines = compilerDiagnosticLinesByFile.get(diagnostic.filePath);
|
|
43397
|
+
if (!compilerLines || diagnostic.line <= 0) return false;
|
|
43398
|
+
const parsed = readAndParse(diagnostic.filePath);
|
|
43399
|
+
if (!parsed) return false;
|
|
43400
|
+
let containmentRange = findOutermostEnclosingFunctionRange(parsed.sourceFile, diagnostic.line);
|
|
43401
|
+
if (!containmentRange && diagnostic.offset !== void 0 && diagnostic.length !== void 0) containmentRange = {
|
|
43402
|
+
startLine: diagnostic.line,
|
|
43403
|
+
endLine: lineOfUtf8Offset(parsed.buffer, diagnostic.offset + diagnostic.length)
|
|
43404
|
+
};
|
|
43405
|
+
if (!containmentRange) return false;
|
|
43406
|
+
const { startLine, endLine } = containmentRange;
|
|
43407
|
+
return compilerLines.some((compilerLine) => startLine <= compilerLine && compilerLine <= endLine);
|
|
43408
|
+
};
|
|
43409
|
+
return diagnostics.filter((diagnostic) => !isManualMemoizationDiagnostic(diagnostic) || !isInsideBailedOutFunction(diagnostic));
|
|
43410
|
+
};
|
|
43034
43411
|
const FILEPATH_WITH_LOCATION_PATTERN = /\S+\.\w+:\d+:\d+[\s\S]*$/;
|
|
43035
43412
|
const LEADING_SEVERITY_LABEL_PATTERN = /^(?:Error|Warning):\s*/;
|
|
43036
43413
|
const TRAILING_PERIOD_PATTERN = /\.$/;
|
|
@@ -43066,8 +43443,14 @@ const PLUGIN_CATEGORY_MAP = {
|
|
|
43066
43443
|
};
|
|
43067
43444
|
const lookupOwnString = (record, key) => Object.hasOwn(record, key) ? record[key] : void 0;
|
|
43068
43445
|
const getRuleRecommendation = (ruleName, project) => {
|
|
43069
|
-
|
|
43070
|
-
return
|
|
43446
|
+
const rule = reactDoctorPlugin.rules[ruleName];
|
|
43447
|
+
if (!rule) return void 0;
|
|
43448
|
+
if (rule.recommendationFor) {
|
|
43449
|
+
const capabilities = getCapabilities(project);
|
|
43450
|
+
const conditionalRecommendation = rule.recommendationFor((capability) => capabilities.has(capability));
|
|
43451
|
+
if (conditionalRecommendation !== void 0) return conditionalRecommendation;
|
|
43452
|
+
}
|
|
43453
|
+
return rule.recommendation;
|
|
43071
43454
|
};
|
|
43072
43455
|
const getRuleCategory = (ruleName) => reactDoctorPlugin.rules[ruleName]?.category;
|
|
43073
43456
|
const getRuleTitle = (ruleName) => reactDoctorPlugin.rules[ruleName]?.title;
|
|
@@ -43170,7 +43553,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
43170
43553
|
minifiedFileCache.set(absolutePath, minified);
|
|
43171
43554
|
return minified;
|
|
43172
43555
|
};
|
|
43173
|
-
return parsed.diagnostics.filter((diagnostic) => diagnostic.code && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory)).map((diagnostic) => {
|
|
43556
|
+
return suppressMemoizationInBailedOutFunctions(parsed.diagnostics.filter((diagnostic) => diagnostic.code && isLintableSourceFile(diagnostic.filename) && !isMinifiedDiagnosticFile(diagnostic.filename) && !shouldSuppressLocalUseHookDiagnostic(diagnostic, rootDirectory) && !shouldSuppressCompilerFindingInWorklet(diagnostic, project, rootDirectory)).map((diagnostic) => {
|
|
43174
43557
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
43175
43558
|
const primaryLabel = diagnostic.labels[0];
|
|
43176
43559
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
@@ -43197,7 +43580,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
43197
43580
|
...resolveMatchByOccurrence(rule, category) ? { matchByOccurrence: true } : {},
|
|
43198
43581
|
...relatedLocations.length > 0 ? { relatedLocations } : {}
|
|
43199
43582
|
};
|
|
43200
|
-
});
|
|
43583
|
+
}), rootDirectory);
|
|
43201
43584
|
};
|
|
43202
43585
|
const buildOxlintChildEnv = (sourceEnv) => {
|
|
43203
43586
|
const childEnv = {};
|
|
@@ -43221,7 +43604,9 @@ const SANITIZED_ENV = buildOxlintChildEnv(process.env);
|
|
|
43221
43604
|
* - `OxlintBatchExceeded { kind: "output-too-large" }` — stdout+stderr
|
|
43222
43605
|
* crossed `OXLINT_OUTPUT_MAX_BYTES`.
|
|
43223
43606
|
* - `OxlintBatchExceeded { kind: "oom" | "killed" }` — child exited
|
|
43224
|
-
* on a signal (SIGABRT → OOM, others → generic kill)
|
|
43607
|
+
* on a signal (SIGABRT → OOM, others → generic kill), or with one
|
|
43608
|
+
* of the abort exit codes Windows reports instead of a signal
|
|
43609
|
+
* (`ABORT_EXIT_CODES` → OOM).
|
|
43225
43610
|
* - `OxlintSpawnFailed { cause }` — `spawn` itself errored, or the
|
|
43226
43611
|
* child exited successfully but printed only stderr.
|
|
43227
43612
|
*
|
|
@@ -43287,7 +43672,7 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
|
|
|
43287
43672
|
clearAbortListener();
|
|
43288
43673
|
reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: error }) }));
|
|
43289
43674
|
});
|
|
43290
|
-
child.on("close", (
|
|
43675
|
+
child.on("close", (code, signal) => {
|
|
43291
43676
|
clearTimeout(timeoutHandle);
|
|
43292
43677
|
clearAbortListener();
|
|
43293
43678
|
if (didKillForSize) {
|
|
@@ -43297,10 +43682,11 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
|
|
|
43297
43682
|
}) }));
|
|
43298
43683
|
return;
|
|
43299
43684
|
}
|
|
43300
|
-
|
|
43685
|
+
const isAbortExitCode = code !== null && ABORT_EXIT_CODES.has(code);
|
|
43686
|
+
if (signal || isAbortExitCode) {
|
|
43301
43687
|
const stderrOutput = Buffer.concat(stderrBuffers).toString("utf-8").trim();
|
|
43302
|
-
const isOom = signal === "SIGABRT";
|
|
43303
|
-
const detailParts = [`killed by ${signal}`];
|
|
43688
|
+
const isOom = signal === "SIGABRT" || isAbortExitCode;
|
|
43689
|
+
const detailParts = [signal ? `killed by ${signal}` : `aborted with exit code ${code}`];
|
|
43304
43690
|
if (isOom) detailParts.push("try scanning fewer files with --diff");
|
|
43305
43691
|
if (stderrOutput) detailParts.push(stderrOutput);
|
|
43306
43692
|
reject(new ReactDoctorError({ reason: new OxlintBatchExceeded({
|
|
@@ -43343,12 +43729,19 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
43343
43729
|
* (surfaced via `onPartialFailure`) so JSON-mode consumers see WHICH
|
|
43344
43730
|
* files were skipped instead of silently losing them.
|
|
43345
43731
|
*
|
|
43346
|
-
* Parallel runs (concurrency > 1) get
|
|
43347
|
-
* fails with a resource-exhaustion error that's exclusive
|
|
43348
|
-
* many oxlint subprocesses at once (EAGAIN / EMFILE /
|
|
43349
|
-
* see `isParallelismRelatedSpawnError`), the whole
|
|
43350
|
-
* with a single worker. That's the only failure a
|
|
43351
|
-
* clear, so every other error class is left to
|
|
43732
|
+
* Parallel runs (concurrency > 1) get two extra safety nets:
|
|
43733
|
+
* - If the pass fails with a resource-exhaustion error that's exclusive
|
|
43734
|
+
* to running many oxlint subprocesses at once (EAGAIN / EMFILE /
|
|
43735
|
+
* ENFILE / ENOMEM — see `isParallelismRelatedSpawnError`), the whole
|
|
43736
|
+
* pass replays once with a single worker. That's the only failure a
|
|
43737
|
+
* serial replay can clear, so every other error class is left to
|
|
43738
|
+
* propagate.
|
|
43739
|
+
* - Files dropped because oxlint's native binding SIGABRT'd under
|
|
43740
|
+
* memory pressure (`OxlintBatchExceeded { kind: "oom" }`) get one
|
|
43741
|
+
* serial single-file-batch rescue pass: the OOM is usually a function
|
|
43742
|
+
* of N concurrent oxlint allocator arenas, not the file itself, so a
|
|
43743
|
+
* lone process with the machine to itself typically clears it —
|
|
43744
|
+
* turning a partial scan into a complete (if slower) one.
|
|
43352
43745
|
*
|
|
43353
43746
|
* Errors that aren't splittable and aren't parallelism-related (oxlint
|
|
43354
43747
|
* config crash, JS plugin resolution failure, etc.) propagate to the
|
|
@@ -43358,13 +43751,16 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
43358
43751
|
const spawnLintBatches = async (input) => {
|
|
43359
43752
|
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal, deadlineEpochMs } = input;
|
|
43360
43753
|
const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
|
|
43361
|
-
|
|
43362
|
-
const
|
|
43754
|
+
let rescueDeadlineEpochMs;
|
|
43755
|
+
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0 || rescueDeadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(rescueDeadlineEpochMs) === 0;
|
|
43756
|
+
const runBatchPass = async (concurrency, passBatches, passOnFileProgress) => {
|
|
43757
|
+
const totalFileCount = passBatches.reduce((sum, batch) => sum + batch.length, 0);
|
|
43363
43758
|
const allDiagnostics = [];
|
|
43364
43759
|
const droppedFiles = [];
|
|
43760
|
+
const oomDroppedFiles = [];
|
|
43365
43761
|
const deadlineSkippedFiles = [];
|
|
43366
|
-
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0;
|
|
43367
43762
|
let firstDropReason = null;
|
|
43763
|
+
let firstNonOomDropReason = null;
|
|
43368
43764
|
const spawnLintBatch = async (batch, depth, batchState) => {
|
|
43369
43765
|
if (isPastDeadline()) {
|
|
43370
43766
|
deadlineSkippedFiles.push(...batch);
|
|
@@ -43386,12 +43782,14 @@ const spawnLintBatches = async (input) => {
|
|
|
43386
43782
|
const isDepthCapReached = depth >= splitMaxDepth;
|
|
43387
43783
|
if (batch.length <= 1 || isBudgetElapsed || isDepthCapReached) {
|
|
43388
43784
|
droppedFiles.push(...batch);
|
|
43389
|
-
|
|
43390
|
-
|
|
43391
|
-
|
|
43392
|
-
|
|
43393
|
-
|
|
43394
|
-
}
|
|
43785
|
+
const isOomDrop = error.reason._tag === "OxlintBatchExceeded" && error.reason.kind === "oom";
|
|
43786
|
+
if (isOomDrop) oomDroppedFiles.push(...batch);
|
|
43787
|
+
let limitHint = "";
|
|
43788
|
+
if (isDepthCapReached) limitHint = ` (split depth cap of ${splitMaxDepth} levels reached)`;
|
|
43789
|
+
else if (isBudgetElapsed) limitHint = ` (split budget of ${splitTotalBudgetMs / MILLISECONDS_PER_SECOND}s exhausted at depth ${depth})`;
|
|
43790
|
+
const dropReason = batch.length > 1 ? `${error.message}${limitHint}` : error.message;
|
|
43791
|
+
firstDropReason ??= dropReason;
|
|
43792
|
+
if (!isOomDrop) firstNonOomDropReason ??= dropReason;
|
|
43395
43793
|
return [];
|
|
43396
43794
|
}
|
|
43397
43795
|
const splitIndex = Math.ceil(batch.length / 2);
|
|
@@ -43401,16 +43799,16 @@ const spawnLintBatches = async (input) => {
|
|
|
43401
43799
|
let startedFileCount = 0;
|
|
43402
43800
|
let scannedFileCount = 0;
|
|
43403
43801
|
let displayedFileCount = 0;
|
|
43404
|
-
const progressTimer =
|
|
43802
|
+
const progressTimer = passOnFileProgress && totalFileCount > 1 ? setInterval(() => {
|
|
43405
43803
|
const ceiling = Math.min(startedFileCount, totalFileCount - 1);
|
|
43406
43804
|
if (displayedFileCount < ceiling) {
|
|
43407
43805
|
displayedFileCount += 1;
|
|
43408
|
-
|
|
43806
|
+
passOnFileProgress(displayedFileCount, totalFileCount);
|
|
43409
43807
|
}
|
|
43410
43808
|
}, 50) : null;
|
|
43411
43809
|
progressTimer?.unref?.();
|
|
43412
43810
|
try {
|
|
43413
|
-
const batchResults = await mapWithConcurrency(
|
|
43811
|
+
const batchResults = await mapWithConcurrency(passBatches, concurrency, async (batch) => {
|
|
43414
43812
|
if (isPastDeadline()) {
|
|
43415
43813
|
deadlineSkippedFiles.push(...batch);
|
|
43416
43814
|
return [];
|
|
@@ -43422,9 +43820,9 @@ const spawnLintBatches = async (input) => {
|
|
|
43422
43820
|
};
|
|
43423
43821
|
const batchDiagnostics = await spawnLintBatch(batch, 0, batchState);
|
|
43424
43822
|
scannedFileCount += batch.length - batchState.deadlineSkippedFileCount;
|
|
43425
|
-
if (
|
|
43823
|
+
if (passOnFileProgress) {
|
|
43426
43824
|
displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
|
|
43427
|
-
|
|
43825
|
+
passOnFileProgress(displayedFileCount, totalFileCount);
|
|
43428
43826
|
}
|
|
43429
43827
|
return batchDiagnostics;
|
|
43430
43828
|
});
|
|
@@ -43432,22 +43830,53 @@ const spawnLintBatches = async (input) => {
|
|
|
43432
43830
|
} finally {
|
|
43433
43831
|
if (progressTimer !== null) clearInterval(progressTimer);
|
|
43434
43832
|
}
|
|
43435
|
-
|
|
43436
|
-
|
|
43437
|
-
|
|
43833
|
+
return {
|
|
43834
|
+
diagnostics: allDiagnostics,
|
|
43835
|
+
droppedFiles,
|
|
43836
|
+
oomDroppedFiles,
|
|
43837
|
+
deadlineSkippedFiles,
|
|
43838
|
+
firstDropReason,
|
|
43839
|
+
firstNonOomDropReason
|
|
43438
43840
|
};
|
|
43439
|
-
const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
|
|
43440
|
-
reportSkippedFiles(droppedFiles, (fileListPreview) => `${droppedFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
|
|
43441
|
-
reportSkippedFiles(deadlineSkippedFiles, (fileListPreview) => `${deadlineSkippedFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
|
|
43442
|
-
return allDiagnostics;
|
|
43443
43841
|
};
|
|
43444
|
-
let
|
|
43842
|
+
let outcome;
|
|
43843
|
+
let effectiveConcurrency = requestedConcurrency;
|
|
43445
43844
|
try {
|
|
43446
|
-
|
|
43845
|
+
outcome = await runBatchPass(requestedConcurrency, fileBatches, onFileProgress);
|
|
43447
43846
|
} catch (error) {
|
|
43448
43847
|
if (requestedConcurrency <= 1 || !isParallelismRelatedSpawnError(error)) throw error;
|
|
43449
|
-
|
|
43848
|
+
effectiveConcurrency = 1;
|
|
43849
|
+
outcome = await runBatchPass(1, fileBatches, onFileProgress);
|
|
43850
|
+
}
|
|
43851
|
+
let { droppedFiles, firstDropReason } = outcome;
|
|
43852
|
+
const diagnostics = outcome.diagnostics;
|
|
43853
|
+
if (outcome.oomDroppedFiles.length > 0 && effectiveConcurrency > 1 && !isPastDeadline()) {
|
|
43854
|
+
rescueDeadlineEpochMs = Date.now() + OXLINT_OOM_RESCUE_BUDGET_MS;
|
|
43855
|
+
const rescuedFiles = /* @__PURE__ */ new Set();
|
|
43856
|
+
let firstRescueFailureReason = null;
|
|
43857
|
+
for (const oomDroppedFile of outcome.oomDroppedFiles) {
|
|
43858
|
+
if (isPastDeadline()) break;
|
|
43859
|
+
try {
|
|
43860
|
+
const rescueOutcome = await runBatchPass(1, [[oomDroppedFile]], void 0);
|
|
43861
|
+
diagnostics.push(...rescueOutcome.diagnostics);
|
|
43862
|
+
if (rescueOutcome.droppedFiles.length > 0 || rescueOutcome.deadlineSkippedFiles.length > 0) firstRescueFailureReason ??= rescueOutcome.firstDropReason;
|
|
43863
|
+
else rescuedFiles.add(oomDroppedFile);
|
|
43864
|
+
} catch (error) {
|
|
43865
|
+
firstRescueFailureReason ??= error instanceof Error ? error.message : String(error);
|
|
43866
|
+
}
|
|
43867
|
+
}
|
|
43868
|
+
rescueDeadlineEpochMs = void 0;
|
|
43869
|
+
droppedFiles = droppedFiles.filter((filePath) => !rescuedFiles.has(filePath));
|
|
43870
|
+
if (droppedFiles.length === 0) firstDropReason = null;
|
|
43871
|
+
else firstDropReason = firstRescueFailureReason ?? outcome.firstNonOomDropReason ?? firstDropReason;
|
|
43450
43872
|
}
|
|
43873
|
+
const reportSkippedFiles = (skippedFiles, buildMessage) => {
|
|
43874
|
+
if (skippedFiles.length === 0 || !onPartialFailure) return;
|
|
43875
|
+
onPartialFailure(buildMessage(`${skippedFiles.slice(0, 3).join(", ")}${skippedFiles.length > 3 ? `, +${skippedFiles.length - 3} more` : ""}`));
|
|
43876
|
+
};
|
|
43877
|
+
const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
|
|
43878
|
+
reportSkippedFiles(droppedFiles, (fileListPreview) => `${droppedFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
|
|
43879
|
+
reportSkippedFiles(outcome.deadlineSkippedFiles, (fileListPreview) => `${outcome.deadlineSkippedFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
|
|
43451
43880
|
return dedupeDiagnostics(diagnostics);
|
|
43452
43881
|
};
|
|
43453
43882
|
let didValidate = false;
|
|
@@ -43708,6 +44137,7 @@ const runOxlint = async (options) => {
|
|
|
43708
44137
|
const serverAuthFunctionNames = Array.isArray(userConfig?.serverAuthFunctionNames) ? userConfig.serverAuthFunctionNames.filter((entry) => typeof entry === "string" && entry.length > 0) : void 0;
|
|
43709
44138
|
const severityControls = buildRuleSeverityControls(userConfig);
|
|
43710
44139
|
validateRuleRegistration();
|
|
44140
|
+
resetManifestCaches();
|
|
43711
44141
|
if (includePaths !== void 0 && includePaths.length === 0) return [];
|
|
43712
44142
|
const pluginPath = resolvePluginPath();
|
|
43713
44143
|
const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
|
|
@@ -44370,9 +44800,10 @@ const CONTEXT_AXES = [
|
|
|
44370
44800
|
}
|
|
44371
44801
|
];
|
|
44372
44802
|
const SCORE_AXES = [...GATED_AXES, ...CONTEXT_AXES];
|
|
44373
|
-
const
|
|
44374
|
-
|
|
44375
|
-
|
|
44803
|
+
const gatedAxesForSection = (section) => section === "devDependencies" ? GATED_AXES.filter((axis) => axis.key !== "vulnerability") : GATED_AXES;
|
|
44804
|
+
const worstGatedAxis = (score, gatedAxes) => {
|
|
44805
|
+
let worst = gatedAxes[0];
|
|
44806
|
+
for (const axis of gatedAxes) if (score[axis.key] < score[worst.key]) worst = axis;
|
|
44376
44807
|
return worst;
|
|
44377
44808
|
};
|
|
44378
44809
|
const clampScore = (value) => {
|
|
@@ -44396,6 +44827,69 @@ const resolveConcreteVersion = (spec) => {
|
|
|
44396
44827
|
if (range === null || range === "*") return null;
|
|
44397
44828
|
return import_semver.minVersion(trimmed)?.version ?? null;
|
|
44398
44829
|
};
|
|
44830
|
+
const readTextFileOrNull = (filePath) => {
|
|
44831
|
+
try {
|
|
44832
|
+
return NFS.readFileSync(filePath, "utf-8");
|
|
44833
|
+
} catch {
|
|
44834
|
+
return null;
|
|
44835
|
+
}
|
|
44836
|
+
};
|
|
44837
|
+
const buildNpmLockLookup = (lockText) => {
|
|
44838
|
+
let parsed;
|
|
44839
|
+
try {
|
|
44840
|
+
parsed = JSON.parse(lockText);
|
|
44841
|
+
} catch {
|
|
44842
|
+
return null;
|
|
44843
|
+
}
|
|
44844
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
44845
|
+
const lock = parsed;
|
|
44846
|
+
return (name) => {
|
|
44847
|
+
const entry = lock.packages?.[`node_modules/${name}`] ?? lock.dependencies?.[name];
|
|
44848
|
+
return typeof entry?.version === "string" ? entry.version : null;
|
|
44849
|
+
};
|
|
44850
|
+
};
|
|
44851
|
+
const buildYarnLockLookup = (lockText) => {
|
|
44852
|
+
const lines = lockText.split(/\r?\n/);
|
|
44853
|
+
return (name, spec) => {
|
|
44854
|
+
const descriptor = `${name}@${spec}`;
|
|
44855
|
+
const berryDescriptor = `${name}@npm:${spec}`;
|
|
44856
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
44857
|
+
const line = lines[lineIndex];
|
|
44858
|
+
if (line.startsWith(" ") || line.startsWith("#") || !line.includes(":")) continue;
|
|
44859
|
+
const descriptors = line.replace(/:\s*$/, "").split(",").map((part) => part.trim().replace(/^"|"$/g, ""));
|
|
44860
|
+
if (!descriptors.includes(descriptor) && !descriptors.includes(berryDescriptor)) continue;
|
|
44861
|
+
for (let bodyIndex = lineIndex + 1; bodyIndex < lines.length && (lines[bodyIndex].startsWith(" ") || lines[bodyIndex] === ""); bodyIndex += 1) {
|
|
44862
|
+
const versionMatch = /^\s+version:?\s+"?([^"\s]+)"?/.exec(lines[bodyIndex]);
|
|
44863
|
+
if (versionMatch) return versionMatch[1];
|
|
44864
|
+
}
|
|
44865
|
+
return null;
|
|
44866
|
+
}
|
|
44867
|
+
return null;
|
|
44868
|
+
};
|
|
44869
|
+
};
|
|
44870
|
+
const buildPnpmLockLookup = (lockText) => {
|
|
44871
|
+
return (name) => {
|
|
44872
|
+
const entryPattern = new RegExp(`^ {4,8}['"]?${name.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&")}['"]?:\\s*$`);
|
|
44873
|
+
const lines = lockText.split(/\r?\n/);
|
|
44874
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
44875
|
+
if (!entryPattern.test(lines[lineIndex])) continue;
|
|
44876
|
+
for (let bodyIndex = lineIndex + 1; bodyIndex < lines.length && /^ {5,}\S/.test(lines[bodyIndex]); bodyIndex += 1) {
|
|
44877
|
+
const versionMatch = /^\s+version:\s+['"]?([^'"\s(]+)/.exec(lines[bodyIndex]);
|
|
44878
|
+
if (versionMatch) return versionMatch[1];
|
|
44879
|
+
}
|
|
44880
|
+
}
|
|
44881
|
+
return null;
|
|
44882
|
+
};
|
|
44883
|
+
};
|
|
44884
|
+
const buildLockedVersionLookup = (rootDirectory) => {
|
|
44885
|
+
const npmLockText = readTextFileOrNull(Path.join(rootDirectory, "package-lock.json"));
|
|
44886
|
+
if (npmLockText !== null) return buildNpmLockLookup(npmLockText);
|
|
44887
|
+
const yarnLockText = readTextFileOrNull(Path.join(rootDirectory, "yarn.lock"));
|
|
44888
|
+
if (yarnLockText !== null) return buildYarnLockLookup(yarnLockText);
|
|
44889
|
+
const pnpmLockText = readTextFileOrNull(Path.join(rootDirectory, "pnpm-lock.yaml"));
|
|
44890
|
+
if (pnpmLockText !== null) return buildPnpmLockLookup(pnpmLockText);
|
|
44891
|
+
return null;
|
|
44892
|
+
};
|
|
44399
44893
|
const locateDependencyKey = (packageJsonText, section, name) => {
|
|
44400
44894
|
const needle = `"${name}"`;
|
|
44401
44895
|
const sectionHeader = new RegExp(`"${section}"\\s*:\\s*\\{`);
|
|
@@ -44422,7 +44916,7 @@ const locateDependencyKey = (packageJsonText, section, name) => {
|
|
|
44422
44916
|
}
|
|
44423
44917
|
return null;
|
|
44424
44918
|
};
|
|
44425
|
-
const collectDependenciesToScore = (packageJson, packageJsonText, includeDevDependencies) => {
|
|
44919
|
+
const collectDependenciesToScore = (packageJson, packageJsonText, includeDevDependencies, lockedVersionLookup) => {
|
|
44426
44920
|
const sectionByName = /* @__PURE__ */ new Map();
|
|
44427
44921
|
for (const name of Object.keys(packageJson.dependencies ?? {})) sectionByName.set(name, "dependencies");
|
|
44428
44922
|
if (includeDevDependencies) {
|
|
@@ -44432,13 +44926,18 @@ const collectDependenciesToScore = (packageJson, packageJsonText, includeDevDepe
|
|
|
44432
44926
|
for (const [name, section] of sectionByName) {
|
|
44433
44927
|
if (SUPPLY_CHAIN_IGNORED_PACKAGES.has(name)) continue;
|
|
44434
44928
|
const spec = (packageJson[section] ?? {})[name] ?? "";
|
|
44435
|
-
const
|
|
44436
|
-
if (
|
|
44929
|
+
const floorVersion = resolveConcreteVersion(spec);
|
|
44930
|
+
if (floorVersion === null) continue;
|
|
44931
|
+
const lockedVersion = lockedVersionLookup?.(name, spec) ?? null;
|
|
44932
|
+
const lockSatisfiesSpec = lockedVersion !== null && import_semver.valid(lockedVersion) !== null && import_semver.satisfies(lockedVersion, spec);
|
|
44933
|
+
const isExactPin = import_semver.valid(spec.trim()) !== null;
|
|
44437
44934
|
const location = locateDependencyKey(packageJsonText, section, name);
|
|
44438
44935
|
dependencies.push({
|
|
44439
44936
|
name,
|
|
44440
|
-
version,
|
|
44937
|
+
version: lockSatisfiesSpec ? lockedVersion : floorVersion,
|
|
44441
44938
|
spec,
|
|
44939
|
+
section,
|
|
44940
|
+
resolution: isExactPin ? "pin" : lockSatisfiesSpec ? "locked" : "floor",
|
|
44442
44941
|
line: location?.line ?? 0,
|
|
44443
44942
|
column: location?.column ?? 0
|
|
44444
44943
|
});
|
|
@@ -44589,7 +45088,11 @@ const formatAlertReason = (topAlerts, totalCount) => {
|
|
|
44589
45088
|
}
|
|
44590
45089
|
return `Socket flagged ${totalCount} alerts (${topAlerts.map((alert) => friendlyAlertType(alert.type)).join(", ")}${totalCount > topAlerts.length ? ` (+${totalCount - topAlerts.length} more)` : ""}); most severe: ${displaySeverity(topAlerts[0].severity)}.`;
|
|
44591
45090
|
};
|
|
44592
|
-
const formatDependencyIdentity = (dependency) =>
|
|
45091
|
+
const formatDependencyIdentity = (dependency) => {
|
|
45092
|
+
if (dependency.resolution === "pin") return `${dependency.name}@${dependency.version}`;
|
|
45093
|
+
if (dependency.resolution === "locked") return `${dependency.name}@${dependency.version} (lockfile resolution of "${dependency.spec}")`;
|
|
45094
|
+
return `${dependency.name}@${dependency.version} (lowest version "${dependency.spec}" allows)`;
|
|
45095
|
+
};
|
|
44593
45096
|
const buildSupplyChainHelp = (dependency, failingAxis, topAlerts, packagePageUrl, options) => {
|
|
44594
45097
|
const hasCriticalAlert = topAlerts.some((alert) => alert.severity.toLowerCase() === "critical");
|
|
44595
45098
|
const entry = `\`"${dependency.name}": "${dependency.spec}"\``;
|
|
@@ -44632,7 +45135,7 @@ const buildLowScoreDiagnostic = (dependency, artifact, failingAxis, options) =>
|
|
|
44632
45135
|
const checkSupplyChain = (input) => gen(function* () {
|
|
44633
45136
|
const options = resolveOptions(input.userConfig);
|
|
44634
45137
|
const packageJsonPath = Path.join(input.rootDirectory, "package.json");
|
|
44635
|
-
const dependencies = collectDependenciesToScore(readPackageJson$1(packageJsonPath), readPackageJsonText(packageJsonPath), options.includeDevDependencies);
|
|
45138
|
+
const dependencies = collectDependenciesToScore(readPackageJson$1(packageJsonPath), readPackageJsonText(packageJsonPath), options.includeDevDependencies, buildLockedVersionLookup(input.rootDirectory));
|
|
44636
45139
|
if (dependencies.length === 0) return [];
|
|
44637
45140
|
const cacheDirectory = isSupplyChainCacheDisabled() ? null : resolveReactDoctorCacheDir(input.rootDirectory);
|
|
44638
45141
|
if (cacheDirectory !== null) pruneExpiredSocketCache(cacheDirectory);
|
|
@@ -44641,7 +45144,7 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
44641
45144
|
for (let index = 0; index < dependencies.length; index += 1) {
|
|
44642
45145
|
const artifact = artifacts[index];
|
|
44643
45146
|
if (!artifact) continue;
|
|
44644
|
-
const worstAxis = worstGatedAxis(artifact.score);
|
|
45147
|
+
const worstAxis = worstGatedAxis(artifact.score, gatedAxesForSection(dependencies[index].section));
|
|
44645
45148
|
if (toHundred(artifact.score[worstAxis.key]) >= options.minScore) continue;
|
|
44646
45149
|
diagnostics.push(buildLowScoreDiagnostic(dependencies[index], artifact, worstAxis, options));
|
|
44647
45150
|
}
|
|
@@ -45342,6 +45845,13 @@ const summarizeDiagnostics = (diagnostics, worstScore = null, worstScoreLabel =
|
|
|
45342
45845
|
scoreLabel: worstScoreLabel
|
|
45343
45846
|
};
|
|
45344
45847
|
};
|
|
45848
|
+
/**
|
|
45849
|
+
* Whether project discovery resolved a React-compatible runtime (React or
|
|
45850
|
+
* Preact, which ships the same hooks + JSX model). This is the same
|
|
45851
|
+
* predicate that grants the `react` capability, so `false` means every
|
|
45852
|
+
* React-runtime rule family was gated off for the project.
|
|
45853
|
+
*/
|
|
45854
|
+
const hasReactRuntime = (project) => project.reactVersion !== null || project.preactVersion !== null;
|
|
45345
45855
|
const toJsonDiff = (diff) => {
|
|
45346
45856
|
if (!diff) return null;
|
|
45347
45857
|
return {
|
|
@@ -45924,7 +46434,7 @@ const makeNoopConsole = () => ({
|
|
|
45924
46434
|
});
|
|
45925
46435
|
//#endregion
|
|
45926
46436
|
//#region src/cli/utils/version.ts
|
|
45927
|
-
const VERSION = "0.7.
|
|
46437
|
+
const VERSION = "0.7.2-dev.08b768b";
|
|
45928
46438
|
//#endregion
|
|
45929
46439
|
//#region src/cli/utils/json-mode.ts
|
|
45930
46440
|
let context = null;
|
|
@@ -46293,13 +46803,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
46293
46803
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
46294
46804
|
* standard `SENTRY_RELEASE` override.
|
|
46295
46805
|
*/
|
|
46296
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.
|
|
46806
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.2-dev.08b768b`;
|
|
46297
46807
|
/**
|
|
46298
46808
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
46299
46809
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
46300
46810
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
46301
46811
|
*/
|
|
46302
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.
|
|
46812
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.2-dev.08b768b") ? "development" : "production");
|
|
46303
46813
|
/**
|
|
46304
46814
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
46305
46815
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -58143,7 +58653,7 @@ const buildRuntimeLayers = (input) => {
|
|
|
58143
58653
|
const linterLayer = input.shouldSkipLint ? Linter.layerOf([]) : Linter.layerOxlint;
|
|
58144
58654
|
const deadCodeLayer = input.shouldRunDeadCode ? DeadCode.layerNode : DeadCode.layerOf([]);
|
|
58145
58655
|
const scoreLayer = input.shouldComputeScore ? Score.layerHttp : Score.layerOf(null);
|
|
58146
|
-
const supplyChainLayer = input.
|
|
58656
|
+
const supplyChainLayer = input.shouldRunSupplyChain ? SupplyChain.layerNode : SupplyChain.layerOf([]);
|
|
58147
58657
|
const projectLayer = input.projectInfoOverride === void 0 ? Project.layerNode : Project.layerOf(input.projectInfoOverride);
|
|
58148
58658
|
const progressLayer = input.shouldShowProgressSpinners ? Progress.layerOra(buildSpinnerProgressHandle) : Progress.layerNoop;
|
|
58149
58659
|
const baseLayers = mergeAll$1(projectLayer, input.hasConfigOverride ? Config.layerOf({
|
|
@@ -58499,21 +59009,22 @@ const resolveDotenvFingerprint = (projectDirectory) => {
|
|
|
58499
59009
|
return [];
|
|
58500
59010
|
}
|
|
58501
59011
|
};
|
|
58502
|
-
const
|
|
59012
|
+
const isGitIdentityTrustworthy = (projectDirectory) => {
|
|
58503
59013
|
const output = runGit(projectDirectory, ["ls-files", "-v"]);
|
|
58504
|
-
if (output === null) return
|
|
58505
|
-
|
|
59014
|
+
if (output === null) return false;
|
|
59015
|
+
const entryLines = output.split("\n").filter((line) => line.length > 0);
|
|
59016
|
+
return entryLines.length > 0 && entryLines.every((line) => line[0] === "H");
|
|
58506
59017
|
};
|
|
58507
59018
|
const resolveCacheFilePath = (projectDirectory) => path.join(resolveReactDoctorCacheDir(projectDirectory), SCAN_RESULT_CACHE_FILENAME);
|
|
58508
59019
|
const readPersistedCache = (cacheFilePath) => {
|
|
58509
59020
|
try {
|
|
58510
59021
|
const parsed = JSON.parse(fs.readFileSync(cacheFilePath, "utf8"));
|
|
58511
|
-
if (!isRecord(parsed) || parsed.version !==
|
|
58512
|
-
version:
|
|
59022
|
+
if (!isRecord(parsed) || parsed.version !== 4) return {
|
|
59023
|
+
version: 4,
|
|
58513
59024
|
entries: []
|
|
58514
59025
|
};
|
|
58515
59026
|
if (!Array.isArray(parsed.entries)) return {
|
|
58516
|
-
version:
|
|
59027
|
+
version: 4,
|
|
58517
59028
|
entries: []
|
|
58518
59029
|
};
|
|
58519
59030
|
const entries = [];
|
|
@@ -58523,12 +59034,12 @@ const readPersistedCache = (cacheFilePath) => {
|
|
|
58523
59034
|
entries.push(entry);
|
|
58524
59035
|
}
|
|
58525
59036
|
return {
|
|
58526
|
-
version:
|
|
59037
|
+
version: 4,
|
|
58527
59038
|
entries
|
|
58528
59039
|
};
|
|
58529
59040
|
} catch {
|
|
58530
59041
|
return {
|
|
58531
|
-
version:
|
|
59042
|
+
version: 4,
|
|
58532
59043
|
entries: []
|
|
58533
59044
|
};
|
|
58534
59045
|
}
|
|
@@ -58560,6 +59071,7 @@ const resolveProjectIdentity = (projectDirectory) => {
|
|
|
58560
59071
|
return path.resolve(projectDirectory);
|
|
58561
59072
|
}
|
|
58562
59073
|
};
|
|
59074
|
+
const resolveManifestContentHash = (projectDirectory) => hashFileContents(getPackageJsonPath(projectDirectory));
|
|
58563
59075
|
const fileFingerprint = (filePath) => {
|
|
58564
59076
|
try {
|
|
58565
59077
|
const stat = fs.statSync(filePath);
|
|
@@ -58582,14 +59094,14 @@ const resolveToolchainFingerprint = (nodeBinaryPath) => {
|
|
|
58582
59094
|
};
|
|
58583
59095
|
const buildScanResultCacheKey = (input) => {
|
|
58584
59096
|
if (isCacheGloballyDisabled()) return null;
|
|
58585
|
-
if (
|
|
59097
|
+
if (!isGitIdentityTrustworthy(input.projectDirectory)) return null;
|
|
58586
59098
|
const worktreeFingerprint = buildWorktreeFingerprint(input.projectDirectory);
|
|
58587
59099
|
if (worktreeFingerprint === null) return null;
|
|
58588
59100
|
const headSha = readHeadSha(input.projectDirectory);
|
|
58589
59101
|
if (headSha === null) return null;
|
|
58590
59102
|
if (stringifyStableJson(input.userConfig) === null) return null;
|
|
58591
59103
|
const cacheKeyJson = stringifyStableJson({
|
|
58592
|
-
schemaVersion:
|
|
59104
|
+
schemaVersion: 4,
|
|
58593
59105
|
projectIdentity: resolveProjectIdentity(input.projectDirectory),
|
|
58594
59106
|
headSha,
|
|
58595
59107
|
worktreeFingerprint,
|
|
@@ -58604,6 +59116,7 @@ const buildScanResultCacheKey = (input) => {
|
|
|
58604
59116
|
engineOptions: {
|
|
58605
59117
|
lint: input.options.lint,
|
|
58606
59118
|
deadCode: input.options.deadCode,
|
|
59119
|
+
supplyChain: input.options.supplyChain,
|
|
58607
59120
|
includePaths: [...input.options.includePaths].sort(),
|
|
58608
59121
|
customRulesOnly: input.options.customRulesOnly,
|
|
58609
59122
|
respectInlineDisables: input.options.respectInlineDisables,
|
|
@@ -58629,17 +59142,24 @@ const createScanResultCache = (projectDirectory) => {
|
|
|
58629
59142
|
for (const entry of persistedCache.entries) entries.set(entry.key, entry);
|
|
58630
59143
|
const persist = () => {
|
|
58631
59144
|
writePersistedCache(cacheFilePath, {
|
|
58632
|
-
version:
|
|
59145
|
+
version: 4,
|
|
58633
59146
|
entries: [...entries.values()].sort((firstEntry, secondEntry) => secondEntry.createdAtMs - firstEntry.createdAtMs).slice(0, 20)
|
|
58634
59147
|
});
|
|
58635
59148
|
};
|
|
58636
59149
|
return {
|
|
58637
|
-
lookup: (key) =>
|
|
59150
|
+
lookup: (key) => {
|
|
59151
|
+
const payload = entries.get(key)?.payload;
|
|
59152
|
+
if (payload === void 0) return null;
|
|
59153
|
+
return resolveProjectIdentity(payload.directory) === resolveProjectIdentity(projectDirectory) && (payload.manifestContentHash ?? null) === resolveManifestContentHash(projectDirectory) ? payload : null;
|
|
59154
|
+
},
|
|
58638
59155
|
store: (key, payload) => {
|
|
58639
59156
|
entries.set(key, {
|
|
58640
59157
|
key,
|
|
58641
59158
|
createdAtMs: Date.now(),
|
|
58642
|
-
payload
|
|
59159
|
+
payload: {
|
|
59160
|
+
...payload,
|
|
59161
|
+
manifestContentHash: resolveManifestContentHash(projectDirectory)
|
|
59162
|
+
}
|
|
58643
59163
|
});
|
|
58644
59164
|
persist();
|
|
58645
59165
|
}
|
|
@@ -58806,13 +59326,15 @@ const buildOutcomeAttributes = (input) => {
|
|
|
58806
59326
|
const countBySource = {
|
|
58807
59327
|
config: 0,
|
|
58808
59328
|
override: 0,
|
|
58809
|
-
inline: 0
|
|
59329
|
+
inline: 0,
|
|
59330
|
+
"foreign-inline": 0
|
|
58810
59331
|
};
|
|
58811
59332
|
for (const suppression of input.suppressedRuleCounts) countBySource[suppression.source] += suppression.count;
|
|
58812
|
-
suppressionRollup.suppressed = countBySource.config + countBySource.override + countBySource.inline;
|
|
59333
|
+
suppressionRollup.suppressed = countBySource.config + countBySource.override + countBySource.inline + countBySource["foreign-inline"];
|
|
58813
59334
|
suppressionRollup.suppressedConfig = countBySource.config;
|
|
58814
59335
|
suppressionRollup.suppressedOverride = countBySource.override;
|
|
58815
59336
|
suppressionRollup.suppressedInline = countBySource.inline;
|
|
59337
|
+
suppressionRollup.suppressedForeignInline = countBySource["foreign-inline"];
|
|
58816
59338
|
}
|
|
58817
59339
|
const attributes = {
|
|
58818
59340
|
...withNamespace("outcome", {
|
|
@@ -58904,6 +59426,7 @@ const buildScanAttributes = (input) => {
|
|
|
58904
59426
|
maxDurationMs: input.maxDurationMs,
|
|
58905
59427
|
lint: input.lint,
|
|
58906
59428
|
deadCode: input.deadCode,
|
|
59429
|
+
supplyChain: input.supplyChain,
|
|
58907
59430
|
scoreOnly: input.scoreOnly,
|
|
58908
59431
|
noScore: input.noScore,
|
|
58909
59432
|
respectInlineDisables: input.respectInlineDisables,
|
|
@@ -60178,6 +60701,7 @@ const buildIgnoredTags = (userConfig) => {
|
|
|
60178
60701
|
const mergeInspectOptions = (inputOptions, userConfig) => ({
|
|
60179
60702
|
lint: inputOptions.lint ?? userConfig?.lint ?? true,
|
|
60180
60703
|
deadCode: inputOptions.deadCode ?? userConfig?.deadCode ?? true,
|
|
60704
|
+
supplyChain: inputOptions.supplyChain ?? userConfig?.supplyChain?.enabled ?? true,
|
|
60181
60705
|
verbose: inputOptions.verbose ?? userConfig?.verbose ?? false,
|
|
60182
60706
|
outputDirectory: inputOptions.outputDirectory || null,
|
|
60183
60707
|
scoreOnly: inputOptions.scoreOnly ?? false,
|
|
@@ -60217,6 +60741,7 @@ const buildRunEventConfig = (options, userConfig, hasCustomConfig, resolvedWorke
|
|
|
60217
60741
|
maxDurationMs: options.maxDurationMs,
|
|
60218
60742
|
lint: options.lint,
|
|
60219
60743
|
deadCode: options.deadCode,
|
|
60744
|
+
supplyChain: options.supplyChain,
|
|
60220
60745
|
scoreOnly: options.scoreOnly,
|
|
60221
60746
|
noScore: options.noScore,
|
|
60222
60747
|
respectInlineDisables: options.respectInlineDisables,
|
|
@@ -60300,6 +60825,7 @@ const runBaselineComparison = async (params) => {
|
|
|
60300
60825
|
projectInfoOverride: params.headProjectInfo,
|
|
60301
60826
|
shouldSkipLint: !params.options.lint || !params.resolvedNodeBinaryPath,
|
|
60302
60827
|
shouldRunDeadCode: false,
|
|
60828
|
+
shouldRunSupplyChain: params.options.supplyChain,
|
|
60303
60829
|
shouldComputeScore: false,
|
|
60304
60830
|
shouldShowProgressSpinners: false,
|
|
60305
60831
|
oxlintConcurrency: params.options.concurrency
|
|
@@ -60388,6 +60914,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
60388
60914
|
configSourceDirectory,
|
|
60389
60915
|
shouldSkipLint: !options.lint || lintBindingMissing,
|
|
60390
60916
|
shouldRunDeadCode: options.deadCode,
|
|
60917
|
+
shouldRunSupplyChain: options.supplyChain,
|
|
60391
60918
|
shouldComputeScore: !options.noScore,
|
|
60392
60919
|
shouldShowProgressSpinners,
|
|
60393
60920
|
oxlintConcurrency: options.concurrency
|
|
@@ -62673,8 +63200,8 @@ const resolveParallelFlag = (parallel) => parallel === false ? 1 : void 0;
|
|
|
62673
63200
|
* accepts. Flag-derived fields only (`scoreOnly`, `noScore`, `silent`,
|
|
62674
63201
|
* `isCi`): every `userConfig` fallback — including `noScore` — lives in
|
|
62675
63202
|
* `inspect()`'s merge layer, which sees each project's module-merged
|
|
62676
|
-
* config. The plain boolean knobs (`lint`, `deadCode`, `
|
|
62677
|
-
* through unchanged for the same reason. This resolver reads
|
|
63203
|
+
* config. The plain boolean knobs (`lint`, `deadCode`, `supplyChain`,
|
|
63204
|
+
* `verbose`) pass through unchanged for the same reason. This resolver reads
|
|
62678
63205
|
* `userConfig` only to decide the `--blocking warning` gate.
|
|
62679
63206
|
*/
|
|
62680
63207
|
const resolveCliInspectOptions = (flags, userConfig) => {
|
|
@@ -62682,6 +63209,7 @@ const resolveCliInspectOptions = (flags, userConfig) => {
|
|
|
62682
63209
|
return {
|
|
62683
63210
|
lint: flags.lint,
|
|
62684
63211
|
deadCode: flags.deadCode,
|
|
63212
|
+
supplyChain: flags.supplyChain,
|
|
62685
63213
|
verbose: flags.verbose,
|
|
62686
63214
|
outputDirectory: flags.outputDir,
|
|
62687
63215
|
respectInlineDisables: flags.respectInlineDisables === false ? false : void 0,
|
|
@@ -63232,7 +63760,7 @@ const inspectAction = async (directory, flags) => {
|
|
|
63232
63760
|
const scanDirectory = projectScanTarget.resolvedDirectory;
|
|
63233
63761
|
const projectConfig = projectDirectory === resolvedDirectory ? userConfig : mergeReactDoctorConfigs(userConfig, projectScanTarget.userConfig ?? void 0);
|
|
63234
63762
|
const projectConfigSourceDirectory = projectScanTarget.userConfig?.plugins === void 0 ? scanTarget.configSourceDirectory : projectScanTarget.configSourceDirectory;
|
|
63235
|
-
const supplyChainEnabled = projectConfig?.supplyChain?.enabled !== false;
|
|
63763
|
+
const supplyChainEnabled = flags.supplyChain ?? projectConfig?.supplyChain?.enabled !== false;
|
|
63236
63764
|
let includePaths;
|
|
63237
63765
|
let supplyChainManifestChanged = false;
|
|
63238
63766
|
if (isDiffMode) {
|
|
@@ -64152,10 +64680,12 @@ const ROOT_FLAG_SPEC = {
|
|
|
64152
64680
|
"--no-parallel",
|
|
64153
64681
|
"--no-respect-inline-disables",
|
|
64154
64682
|
"--no-score",
|
|
64683
|
+
"--no-supply-chain",
|
|
64155
64684
|
"--no-telemetry",
|
|
64156
64685
|
"--no-warnings",
|
|
64157
64686
|
"--score",
|
|
64158
64687
|
"--staged",
|
|
64688
|
+
"--supply-chain",
|
|
64159
64689
|
"--verbose",
|
|
64160
64690
|
"--version",
|
|
64161
64691
|
"--warnings",
|
|
@@ -64404,7 +64934,7 @@ ${highlighter.dim("Learn more:")}
|
|
|
64404
64934
|
${highlighter.info(CI_URL)}
|
|
64405
64935
|
`;
|
|
64406
64936
|
const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
|
|
64407
|
-
const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much to scan/report: full (default), files, changed (only new issues vs base), or lines (only changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks").option("--max-duration <seconds>", "scan time budget for the whole run, shared across workspace projects: past it, remaining lint batches and dead-code are skipped and partial results are reported (skipped files are listed in the JSON report)").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
|
|
64937
|
+
const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much to scan/report: full (default), files, changed (only new issues vs base), or lines (only changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks").option("--max-duration <seconds>", "scan time budget for the whole run, shared across workspace projects: past it, remaining lint batches and dead-code are skipped and partial results are reported (skipped files are listed in the JSON report)").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
|
|
64408
64938
|
program.action(inspectAction);
|
|
64409
64939
|
program.command("why <location>").description("Explain why a rule fired (or why a suppression didn't apply) at a file:line").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple)").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").action((location, options) => whyAction(location, options));
|
|
64410
64940
|
program.command("install").alias("setup").description("Install the react-doctor skill into your coding agents and optional git hook").option("-y, --yes", "skip prompts, install for all detected agents").option("--dry-run", "show what would be installed without writing files").option("--agent-hooks", "install native non-blocking agent hooks for Claude Code and Cursor").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderInstallHelpEpilog).action(installAction);
|
|
@@ -64454,4 +64984,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
64454
64984
|
export {};
|
|
64455
64985
|
|
|
64456
64986
|
//# sourceMappingURL=cli.js.map
|
|
64457
|
-
//# debugId=
|
|
64987
|
+
//# debugId=9aed4e70-dd94-51e8-9810-f8e8945d80a3
|