react-doctor 0.7.2-dev.aa519e5 → 0.7.2-dev.cb8f726

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 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]="a8ebd975-0f3b-5218-ad38-b9c8095f6668")}catch(e){}}();
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]="91ba16f6-8b1a-5cd9-a5d2-fc0b6fce2508")}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";
@@ -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,6 +35985,18 @@ 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;
35971
36002
  const GENERATED_SOURCE_DIRECTORY_PATTERN = /(?:^|\/)__generated__\//;
@@ -36192,25 +36223,6 @@ const isLargeMinifiedFile = (absolutePath, knownSizeBytes) => {
36192
36223
  cachedIsLargeMinifiedByPath.set(absolutePath, result);
36193
36224
  return result;
36194
36225
  };
36195
- const isErrnoException = (error) => error instanceof Error && "code" in error;
36196
- const IGNORABLE_READDIR_ERROR_CODES = new Set([
36197
- "EACCES",
36198
- "EPERM",
36199
- "ENOENT",
36200
- "ENOTDIR",
36201
- "EINVAL",
36202
- "ELOOP",
36203
- "ENAMETOOLONG"
36204
- ]);
36205
- const isIgnorableReaddirError = (error) => isErrnoException(error) && typeof error.code === "string" && IGNORABLE_READDIR_ERROR_CODES.has(error.code);
36206
- const readDirectoryEntries = (directoryPath) => {
36207
- try {
36208
- return NFS.readdirSync(directoryPath, { withFileTypes: true });
36209
- } catch (error) {
36210
- if (isIgnorableReaddirError(error)) return [];
36211
- throw error;
36212
- }
36213
- };
36214
36226
  function* walkSourceTreeFiles(rootDirectory) {
36215
36227
  const stack = [rootDirectory];
36216
36228
  while (stack.length > 0) {
@@ -36265,23 +36277,6 @@ const listSourceFilesViaFilesystem = (rootDirectory) => {
36265
36277
  const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort());
36266
36278
  const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
36267
36279
  const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
36268
- /**
36269
- * The chain of ancestor directories of `startDirectory`, nearest first, up to
36270
- * and including the filesystem root. With `includeStart`, the chain begins at
36271
- * `startDirectory` itself; otherwise at its parent. The one place the "walk up
36272
- * the tree" traversal is expressed, so each caller adds only its own match /
36273
- * stop policy instead of re-deriving the `path.dirname` loop.
36274
- */
36275
- const ancestorDirectories = (startDirectory, { includeStart }) => {
36276
- const directories = [];
36277
- let currentDirectory = includeStart ? startDirectory : Path.dirname(startDirectory);
36278
- while (true) {
36279
- directories.push(currentDirectory);
36280
- const parentDirectory = Path.dirname(currentDirectory);
36281
- if (parentDirectory === currentDirectory) return directories;
36282
- currentDirectory = parentDirectory;
36283
- }
36284
- };
36285
36280
  const cachedPackageJsons = /* @__PURE__ */ new Map();
36286
36281
  const readPackageJsonUncached = (packageJsonPath) => {
36287
36282
  try {
@@ -36312,7 +36307,11 @@ const isMonorepoRoot = (directory) => {
36312
36307
  return Array.isArray(packageJson.workspaces) || Boolean(packageJson.workspaces?.packages);
36313
36308
  };
36314
36309
  const findMonorepoRoot = (startDirectory) => {
36315
- for (const directory of ancestorDirectories(startDirectory, { includeStart: false })) if (isMonorepoRoot(directory)) return directory;
36310
+ let currentDirectory = Path.dirname(startDirectory);
36311
+ while (currentDirectory !== Path.dirname(currentDirectory)) {
36312
+ if (isMonorepoRoot(currentDirectory)) return currentDirectory;
36313
+ currentDirectory = Path.dirname(currentDirectory);
36314
+ }
36316
36315
  return null;
36317
36316
  };
36318
36317
  /**
@@ -36326,6 +36325,146 @@ const findMonorepoRoot = (startDirectory) => {
36326
36325
  * byte-equivalent copy.
36327
36326
  */
36328
36327
  const isProjectBoundary = (directory) => NFS.existsSync(Path.join(directory, ".git")) || isMonorepoRoot(directory);
36328
+ const TSCONFIG_FILENAME = "tsconfig.json";
36329
+ const isRelativeExtendsValue = (extendsValue) => extendsValue.startsWith("./") || extendsValue.startsWith("../") || Path.isAbsolute(extendsValue);
36330
+ const ensureJsonExtension = (filePath) => Path.extname(filePath) === "" ? `${filePath}.json` : filePath;
36331
+ const resolvePackageExtendsPath = (extendsValue, fromConfigDirectory) => {
36332
+ const requireFromConfig = createRequire(Path.join(fromConfigDirectory, "tsconfig.json"));
36333
+ const candidates = [
36334
+ extendsValue,
36335
+ ensureJsonExtension(extendsValue),
36336
+ `${extendsValue.replace(/\/$/, "")}/tsconfig.json`
36337
+ ];
36338
+ for (const candidate of candidates) try {
36339
+ return requireFromConfig.resolve(candidate);
36340
+ } catch {
36341
+ continue;
36342
+ }
36343
+ return null;
36344
+ };
36345
+ const resolveExtendsPath = (extendsValue, fromConfigDirectory) => {
36346
+ if (isRelativeExtendsValue(extendsValue)) return ensureJsonExtension(Path.resolve(fromConfigDirectory, extendsValue));
36347
+ return resolvePackageExtendsPath(extendsValue, fromConfigDirectory);
36348
+ };
36349
+ const normalizeCompilerOptions = (compilerOptions) => {
36350
+ if (!isPlainObject(compilerOptions)) return { hasExplicitLib: false };
36351
+ const target = typeof compilerOptions.target === "string" ? compilerOptions.target : void 0;
36352
+ const hasExplicitLib = Object.hasOwn(compilerOptions, "lib");
36353
+ return {
36354
+ target,
36355
+ lib: Array.isArray(compilerOptions.lib) ? compilerOptions.lib.filter((entry) => typeof entry === "string") : void 0,
36356
+ hasExplicitLib
36357
+ };
36358
+ };
36359
+ const readTsConfig = (filePath) => {
36360
+ let content;
36361
+ try {
36362
+ content = NFS.readFileSync(filePath, "utf-8");
36363
+ } catch {
36364
+ return null;
36365
+ }
36366
+ const parsed = ts.parseConfigFileTextToJson(filePath, content);
36367
+ if (!isPlainObject(parsed.config)) return null;
36368
+ return {
36369
+ extends: typeof parsed.config.extends === "string" ? parsed.config.extends : void 0,
36370
+ referencePaths: normalizeReferencePaths(parsed.config.references),
36371
+ compilerOptions: normalizeCompilerOptions(parsed.config.compilerOptions)
36372
+ };
36373
+ };
36374
+ const normalizeReferencePaths = (references) => {
36375
+ if (!Array.isArray(references)) return [];
36376
+ return references.map((reference) => isPlainObject(reference) && typeof reference.path === "string" ? reference.path : null).filter((referencePath) => referencePath !== null);
36377
+ };
36378
+ const mergeCompilerOptions = (inherited, current) => {
36379
+ const target = current.target ?? inherited?.target;
36380
+ const hasExplicitLib = current.hasExplicitLib || Boolean(inherited?.hasExplicitLib);
36381
+ return {
36382
+ target,
36383
+ lib: current.hasExplicitLib ? current.lib : inherited?.lib,
36384
+ hasExplicitLib
36385
+ };
36386
+ };
36387
+ const readResolvedCompilerOptions = (tsConfigPath, extendsDepth, visitedPaths) => {
36388
+ const realPath = NFS.realpathSync.native(tsConfigPath);
36389
+ if (visitedPaths.has(realPath)) return null;
36390
+ const tsConfig = readTsConfig(realPath);
36391
+ if (!tsConfig) return null;
36392
+ const nextVisitedPaths = new Set(visitedPaths);
36393
+ nextVisitedPaths.add(realPath);
36394
+ if (tsConfig.extends && extendsDepth < 8) {
36395
+ const parentPath = resolveExtendsPath(tsConfig.extends, Path.dirname(realPath));
36396
+ if (parentPath && isFile(parentPath)) return mergeCompilerOptions(readResolvedCompilerOptions(parentPath, extendsDepth + 1, nextVisitedPaths), tsConfig.compilerOptions);
36397
+ }
36398
+ return tsConfig.compilerOptions;
36399
+ };
36400
+ const targetYearIsPreES2023 = (target) => {
36401
+ const year = ES_TARGET_YEAR_BY_NAME[target.toLowerCase()];
36402
+ return year !== void 0 && year < 2023;
36403
+ };
36404
+ const libEntryIncludesES2023Array = (entry) => {
36405
+ const normalizedEntry = entry.toLowerCase();
36406
+ if (normalizedEntry === "esnext" || normalizedEntry === "esnext.array") return true;
36407
+ const esYearMatch = /^es(\d{4})(?:\.(.+))?$/.exec(normalizedEntry);
36408
+ if (!esYearMatch) return false;
36409
+ if (Number(esYearMatch[1]) < 2023) return false;
36410
+ const component = esYearMatch[2];
36411
+ return component === void 0 || component === "array";
36412
+ };
36413
+ const libIncludesES2023 = (lib) => lib.some(libEntryIncludesES2023Array);
36414
+ const compilerOptionsArePreES2023 = (compilerOptions) => {
36415
+ if (compilerOptions.target) return targetYearIsPreES2023(compilerOptions.target);
36416
+ if (compilerOptions.hasExplicitLib) return !libIncludesES2023(compilerOptions.lib ?? []);
36417
+ return false;
36418
+ };
36419
+ const compilerOptionsDeclareTargetOrLib = (compilerOptions) => compilerOptions.hasExplicitLib || compilerOptions.target !== void 0;
36420
+ const detectPreES2023FromConfig = (tsConfigPath, visitedConfigPaths = /* @__PURE__ */ new Set()) => {
36421
+ if (visitedConfigPaths.has(tsConfigPath)) return false;
36422
+ const compilerOptions = readResolvedCompilerOptions(tsConfigPath, 0, /* @__PURE__ */ new Set());
36423
+ if (!compilerOptions) return false;
36424
+ if (!compilerOptionsDeclareTargetOrLib(compilerOptions)) {
36425
+ const tsConfig = readTsConfig(tsConfigPath);
36426
+ if (!tsConfig) return false;
36427
+ const nextVisitedConfigPaths = new Set(visitedConfigPaths);
36428
+ nextVisitedConfigPaths.add(tsConfigPath);
36429
+ const configDirectory = Path.dirname(tsConfigPath);
36430
+ return tsConfig.referencePaths.some((referencePath) => {
36431
+ const resolvedReferencePath = Path.resolve(configDirectory, referencePath);
36432
+ const referencedConfigPath = isFile(resolvedReferencePath) ? resolvedReferencePath : Path.join(resolvedReferencePath, TSCONFIG_FILENAME);
36433
+ return isFile(referencedConfigPath) && detectPreES2023FromConfig(referencedConfigPath, nextVisitedConfigPaths);
36434
+ });
36435
+ }
36436
+ return compilerOptionsArePreES2023(compilerOptions);
36437
+ };
36438
+ const detectPreES2023Target = (directory) => {
36439
+ const tsConfigPath = Path.join(directory, TSCONFIG_FILENAME);
36440
+ if (isFile(tsConfigPath)) return detectPreES2023FromConfig(tsConfigPath);
36441
+ for (const fallbackFilename of FALLBACK_TSCONFIG_FILENAMES) {
36442
+ const fallbackPath = Path.join(directory, fallbackFilename);
36443
+ if (isFile(fallbackPath)) return detectPreES2023FromConfig(fallbackPath);
36444
+ }
36445
+ return false;
36446
+ };
36447
+ const FALLBACK_TSCONFIG_FILENAMES = ["tsconfig.app.json", "tsconfig.build.json"];
36448
+ const FRAMEWORK_PACKAGES = {
36449
+ next: "nextjs",
36450
+ "@tanstack/react-start": "tanstack-start",
36451
+ vite: "vite",
36452
+ "react-scripts": "cra",
36453
+ "@remix-run/react": "remix",
36454
+ gatsby: "gatsby",
36455
+ expo: "expo",
36456
+ "react-native": "react-native"
36457
+ };
36458
+ const detectFramework = (dependencies) => {
36459
+ for (const [packageName, frameworkName] of Object.entries(FRAMEWORK_PACKAGES)) if (dependencies[packageName]) return frameworkName;
36460
+ if (dependencies.preact && !dependencies.react) return "preact";
36461
+ return "unknown";
36462
+ };
36463
+ const MOBILE_FRAMEWORKS = new Set(["expo", "react-native"]);
36464
+ const frameworkMergeRank = (framework) => {
36465
+ if (framework === "unknown") return 3;
36466
+ return MOBILE_FRAMEWORKS.has(framework) ? 2 : 1;
36467
+ };
36329
36468
  const REACT_COMPILER_PACKAGES = new Set([
36330
36469
  "babel-plugin-react-compiler",
36331
36470
  "react-compiler-runtime",
@@ -36362,6 +36501,7 @@ const EXPO_APP_CONFIG_FILENAMES = [
36362
36501
  ];
36363
36502
  const REACT_COMPILER_PACKAGE_REFERENCE_PATTERN = /babel-plugin-react-compiler|react-compiler-runtime|eslint-plugin-react-compiler|["']react-compiler["']/;
36364
36503
  const REACT_COMPILER_ENABLED_FLAG_PATTERN = /["']?reactCompiler["']?\s*:\s*(?:true\b|\{)/;
36504
+ const STATIC_EXPORT_OUTPUT_PATTERN = /(?:^|[^.\w])["']?output["']?\s*:\s*["']export["']/m;
36365
36505
  const hasCompilerPackage = (packageJson) => {
36366
36506
  const allDependencies = {
36367
36507
  ...packageJson.peerDependencies,
@@ -36394,34 +36534,10 @@ const detectReactCompiler = (directory, packageJson) => {
36394
36534
  }
36395
36535
  return false;
36396
36536
  };
36397
- const FRAMEWORK_PACKAGES = {
36398
- next: "nextjs",
36399
- "@tanstack/react-start": "tanstack-start",
36400
- vite: "vite",
36401
- "react-scripts": "cra",
36402
- "@remix-run/react": "remix",
36403
- gatsby: "gatsby",
36404
- expo: "expo",
36405
- "react-native": "react-native"
36406
- };
36407
- const FRAMEWORK_DISPLAY_NAMES = {
36408
- nextjs: "Next.js",
36409
- "tanstack-start": "TanStack Start",
36410
- vite: "Vite",
36411
- cra: "Create React App",
36412
- remix: "Remix",
36413
- gatsby: "Gatsby",
36414
- expo: "Expo",
36415
- "react-native": "React Native",
36416
- preact: "Preact",
36417
- unknown: "React"
36418
- };
36419
- const formatFrameworkName = (framework) => FRAMEWORK_DISPLAY_NAMES[framework];
36420
- const detectFramework = (dependencies) => {
36421
- for (const [packageName, frameworkName] of Object.entries(FRAMEWORK_PACKAGES)) if (dependencies[packageName]) return frameworkName;
36422
- if (dependencies.preact && !dependencies.react) return "preact";
36423
- return "unknown";
36424
- };
36537
+ const detectNextjsStaticExport = (directory) => NEXT_CONFIG_FILENAMES.some((filename) => {
36538
+ const filePath = Path.join(directory, filename);
36539
+ return isFile(filePath) && STATIC_EXPORT_OUTPUT_PATTERN.test(NFS.readFileSync(filePath, "utf-8"));
36540
+ });
36425
36541
  const UNRESOLVABLE_PROTOCOL_VERSION = /^(?:file|git|github|https?|link|patch|portal|workspace|npm):/i;
36426
36542
  const DIST_TAG_VERSION = /^[a-z][a-z0-9._-]*$/i;
36427
36543
  const WILDCARD_VERSION = /^[*xX](?:\.[*xX])*$/;
@@ -36559,10 +36675,76 @@ const isConcreteDependencyVersion = (version) => {
36559
36675
  const normalizedVersion = normalizeDependencyVersion(version);
36560
36676
  return normalizedVersion !== null && /\d/.test(normalizedVersion);
36561
36677
  };
36562
- const isPlainObject = (value) => {
36563
- if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
36564
- const prototype = Object.getPrototypeOf(value);
36565
- return prototype === null || prototype === Object.prototype;
36678
+ const isMajorMinorAtLeast = (detected, required) => {
36679
+ if (detected === null) return true;
36680
+ if (detected.major !== required.major) return detected.major > required.major;
36681
+ return detected.minor >= required.minor;
36682
+ };
36683
+ const parseReactMajor = (reactVersion) => {
36684
+ if (typeof reactVersion !== "string") return null;
36685
+ return getLowestDependencyMajor(reactVersion);
36686
+ };
36687
+ const MAJOR_MINOR_PATTERN = /(\d{1,4})\.(\d{1,4})/;
36688
+ const MAJOR_ONLY_PATTERN = /(\d{1,4})/;
36689
+ const UPPER_BOUND_COMPARATOR_PATTERN = /<=?\s{0,8}\d{1,4}(?:\.\d{1,4}){0,2}(?:-[^\s,|]+)?/g;
36690
+ const parseReactMajorMinor = (reactVersion) => {
36691
+ if (typeof reactVersion !== "string") return null;
36692
+ const trimmed = reactVersion.trim();
36693
+ if (trimmed.length === 0) return null;
36694
+ const lowerBoundsOnly = trimmed.replace(UPPER_BOUND_COMPARATOR_PATTERN, " ").trim();
36695
+ if (lowerBoundsOnly.length === 0) return null;
36696
+ const majorMinorMatch = lowerBoundsOnly.match(MAJOR_MINOR_PATTERN);
36697
+ if (majorMinorMatch) {
36698
+ const major = Number.parseInt(majorMinorMatch[1], 10);
36699
+ const minor = Number.parseInt(majorMinorMatch[2], 10);
36700
+ if (!Number.isFinite(major) || major <= 0) return null;
36701
+ if (!Number.isFinite(minor) || minor < 0) return null;
36702
+ return {
36703
+ major,
36704
+ minor
36705
+ };
36706
+ }
36707
+ const majorOnlyMatch = lowerBoundsOnly.match(MAJOR_ONLY_PATTERN);
36708
+ if (!majorOnlyMatch) return null;
36709
+ const major = Number.parseInt(majorOnlyMatch[1], 10);
36710
+ if (!Number.isFinite(major) || major <= 0) return null;
36711
+ return {
36712
+ major,
36713
+ minor: 0
36714
+ };
36715
+ };
36716
+ const parseLowerBoundVersion = (versionSpec) => import_semver.validRange(versionSpec) !== null ? import_semver.minVersion(versionSpec) : import_semver.coerce(versionSpec);
36717
+ const parseTailwindMajorMinor = (tailwindVersion) => {
36718
+ if (typeof tailwindVersion !== "string") return null;
36719
+ const trimmed = tailwindVersion.trim();
36720
+ if (trimmed.length === 0) return null;
36721
+ const lowerBound = parseLowerBoundVersion(trimmed);
36722
+ if (lowerBound === null || lowerBound.major <= 0) return null;
36723
+ return {
36724
+ major: lowerBound.major,
36725
+ minor: lowerBound.minor
36726
+ };
36727
+ };
36728
+ const hasUpperBoundOnlyPeerRange = (range) => {
36729
+ if (typeof range !== "string") return false;
36730
+ const normalizedRange = normalizeDependencyVersion(range);
36731
+ if (normalizedRange === null) return false;
36732
+ return splitDependencyVersionBranches(normalizedRange).some((branch) => {
36733
+ const normalizedBranch = normalizeDependencyVersion(branch);
36734
+ return normalizedBranch !== null && getBranchLowestMajor(normalizedBranch) === null && hasUpperBoundComparator(normalizedBranch);
36735
+ });
36736
+ };
36737
+ const peerRangeMinMajor = (range) => {
36738
+ if (typeof range !== "string") return null;
36739
+ return getLowestDependencyMajor(range);
36740
+ };
36741
+ const resolveEffectiveReactMajor = (reactVersion, packageJson) => {
36742
+ const installedReactMajor = parseReactMajor(reactVersion);
36743
+ const peerReactRange = packageJson.peerDependencies?.react;
36744
+ if (typeof peerReactRange !== "string") return installedReactMajor;
36745
+ const peerFloor = peerRangeMinMajor(peerReactRange);
36746
+ if (peerFloor === null) return hasUpperBoundOnlyPeerRange(peerReactRange) ? null : installedReactMajor;
36747
+ return installedReactMajor !== null ? Math.min(installedReactMajor, peerFloor) : peerFloor;
36566
36748
  };
36567
36749
  const isCatalogReference = (version) => version.startsWith("catalog:");
36568
36750
  const extractCatalogName = (version) => {
@@ -36704,6 +36886,16 @@ const resolveCatalogVersion = (packageJson, packageName, rootDirectory, explicit
36704
36886
  }
36705
36887
  return null;
36706
36888
  };
36889
+ const REACT_SECTIONS = [
36890
+ "dependencies",
36891
+ "peerDependencies",
36892
+ "devDependencies"
36893
+ ];
36894
+ const TAILWIND_ZOD_SECTIONS = [
36895
+ "dependencies",
36896
+ "devDependencies",
36897
+ "peerDependencies"
36898
+ ];
36707
36899
  const EMPTY_DEPENDENCY_INFO = {
36708
36900
  reactVersion: null,
36709
36901
  tailwindVersion: null,
@@ -36744,6 +36936,10 @@ const extractDependencyInfo = (packageJson) => {
36744
36936
  framework: detectFramework(allDependencies)
36745
36937
  };
36746
36938
  };
36939
+ const getDependencySpec = (packageJson, packageName) => {
36940
+ const spec = packageJson.dependencies?.[packageName] ?? packageJson.devDependencies?.[packageName] ?? packageJson.peerDependencies?.[packageName] ?? packageJson.optionalDependencies?.[packageName];
36941
+ return typeof spec === "string" ? spec : null;
36942
+ };
36747
36943
  const getDependencyDeclaration = ({ packageJson, packageName, sections }) => {
36748
36944
  for (const section of sections) {
36749
36945
  const version = packageJson[section]?.[packageName];
@@ -36760,31 +36956,141 @@ const getDependencyDeclaration = ({ packageJson, packageName, sections }) => {
36760
36956
  version: null
36761
36957
  };
36762
36958
  };
36763
- const isDirectory = (directoryPath) => {
36764
- try {
36765
- return NFS.statSync(directoryPath).isDirectory();
36766
- } catch {
36767
- return false;
36959
+ const REACT_DEPENDENCY_NAMES = new Set([
36960
+ "react",
36961
+ "react-native",
36962
+ "next",
36963
+ "preact"
36964
+ ]);
36965
+ const hasReactDependency = (packageJson) => {
36966
+ const allDependencies = {
36967
+ ...packageJson.peerDependencies,
36968
+ ...packageJson.dependencies,
36969
+ ...packageJson.devDependencies
36970
+ };
36971
+ return Object.keys(allDependencies).some((packageName) => REACT_DEPENDENCY_NAMES.has(packageName));
36972
+ };
36973
+ const getPreactVersion = (packageJson) => {
36974
+ return {
36975
+ ...packageJson.peerDependencies,
36976
+ ...packageJson.dependencies,
36977
+ ...packageJson.devDependencies
36978
+ }.preact ?? null;
36979
+ };
36980
+ const TANSTACK_QUERY_PACKAGES = new Set([
36981
+ "@tanstack/react-query",
36982
+ "@tanstack/query-core",
36983
+ "react-query"
36984
+ ]);
36985
+ const hasTanStackQuery = (packageJson) => {
36986
+ const allDependencies = {
36987
+ ...packageJson.peerDependencies,
36988
+ ...packageJson.dependencies,
36989
+ ...packageJson.devDependencies
36990
+ };
36991
+ return Object.keys(allDependencies).some((packageName) => TANSTACK_QUERY_PACKAGES.has(packageName));
36992
+ };
36993
+ const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, version }) => {
36994
+ if (version === null || !isCatalogReference(version)) return version;
36995
+ const catalogName = extractCatalogName(version);
36996
+ const resolvedLocalVersion = resolveCatalogVersion(rootPackageJson, packageName, rootDirectory, catalogName);
36997
+ if (resolvedLocalVersion) return resolvedLocalVersion;
36998
+ const monorepoRoot = findMonorepoRoot(rootDirectory);
36999
+ if (!monorepoRoot) return version;
37000
+ const monorepoPackageJsonPath = Path.join(monorepoRoot, "package.json");
37001
+ if (!isFile(monorepoPackageJsonPath)) return version;
37002
+ return resolveCatalogVersion(readPackageJson$1(monorepoPackageJsonPath), packageName, monorepoRoot, catalogName) ?? version;
37003
+ };
37004
+ /**
37005
+ * The chain of ancestor directories of `startDirectory`, nearest first, up to
37006
+ * and including the filesystem root. With `includeStart`, the chain begins at
37007
+ * `startDirectory` itself; otherwise at its parent. The one place the "walk up
37008
+ * the tree" traversal is expressed, so each caller adds only its own match /
37009
+ * stop policy instead of re-deriving the `path.dirname` loop.
37010
+ */
37011
+ const ancestorDirectories = (startDirectory, { includeStart }) => {
37012
+ const directories = [];
37013
+ let currentDirectory = includeStart ? startDirectory : Path.dirname(startDirectory);
37014
+ while (true) {
37015
+ directories.push(currentDirectory);
37016
+ const parentDirectory = Path.dirname(currentDirectory);
37017
+ if (parentDirectory === currentDirectory) return directories;
37018
+ currentDirectory = parentDirectory;
36768
37019
  }
36769
37020
  };
36770
- const NX_PROJECT_DISCOVERY_DIRS = [
36771
- "apps",
36772
- "libs",
36773
- "packages"
36774
- ];
36775
- const getNxWorkspaceDirectories = (rootDirectory) => {
36776
- if (!isFile(Path.join(rootDirectory, "nx.json"))) return [];
36777
- const collected = [];
36778
- for (const candidate of NX_PROJECT_DISCOVERY_DIRS) {
36779
- const candidatePath = Path.join(rootDirectory, candidate);
36780
- if (!isDirectory(candidatePath)) continue;
36781
- for (const entry of readDirectoryEntries(candidatePath)) {
36782
- if (!entry.isDirectory()) continue;
36783
- const projectDirectory = Path.join(candidatePath, entry.name);
36784
- if (isFile(Path.join(projectDirectory, "project.json")) || isFile(Path.join(projectDirectory, "package.json"))) collected.push(`${candidate}/${entry.name}`);
36785
- }
37021
+ /**
37022
+ * Walk up from `startDirectory` to the nearest ancestor that owns a
37023
+ * `package.json`, stopping at (and including) the enclosing project boundary
37024
+ * — the working tree's git root or a monorepo root. Lets a scan of a package
37025
+ * subfolder that has no `package.json` of its own adopt the nearest enclosing
37026
+ * package as its project root, so it inherits that package's dependency +
37027
+ * framework detection instead of synthesizing an empty (React-blind) project.
37028
+ *
37029
+ * Generalizes the older monorepo-root-only lookup: the nearest ancestor is the
37030
+ * most specific owning package (a leaf workspace, not just the repo root), so
37031
+ * a subfolder of one workspace no longer borrows a sibling's capabilities.
37032
+ *
37033
+ * Returns `null` when no ancestor `package.json` exists inside the project
37034
+ * boundary (a loose tree of files, or a subfolder of a package-less repo).
37035
+ */
37036
+ const findNearestAncestorPackageJson = (startDirectory) => {
37037
+ if (isProjectBoundary(startDirectory)) return null;
37038
+ for (const directory of ancestorDirectories(startDirectory, { includeStart: false })) {
37039
+ if (isFile(Path.join(directory, "package.json"))) return directory;
37040
+ if (isProjectBoundary(directory)) return null;
36786
37041
  }
36787
- return collected;
37042
+ return null;
37043
+ };
37044
+ const NAMES = new Set([
37045
+ "react-native",
37046
+ "react-native-tvos",
37047
+ ...new Set([
37048
+ "expo",
37049
+ "expo-router",
37050
+ "@expo/cli",
37051
+ "@expo/metro-config",
37052
+ "@expo/metro-runtime"
37053
+ ]),
37054
+ "react-native-windows",
37055
+ "react-native-macos"
37056
+ ]);
37057
+ const PREFIXES = ["@react-native/", "@react-native-"];
37058
+ const isReactNativeDependencyName = (dependencyName) => {
37059
+ if (NAMES.has(dependencyName)) return true;
37060
+ for (const prefix of PREFIXES) if (dependencyName.startsWith(prefix)) return true;
37061
+ return false;
37062
+ };
37063
+ const containsAnyReactNativeDependency = (section) => {
37064
+ if (!section) return false;
37065
+ for (const dependencyName of Object.keys(section)) if (isReactNativeDependencyName(dependencyName)) return true;
37066
+ return false;
37067
+ };
37068
+ const isPackageJsonReactNativeAware = (packageJson) => {
37069
+ if (typeof packageJson["react-native"] === "string") return true;
37070
+ if (containsAnyReactNativeDependency(packageJson.dependencies)) return true;
37071
+ if (containsAnyReactNativeDependency(packageJson.devDependencies)) return true;
37072
+ if (containsAnyReactNativeDependency(packageJson.peerDependencies)) return true;
37073
+ if (containsAnyReactNativeDependency(packageJson.optionalDependencies)) return true;
37074
+ return false;
37075
+ };
37076
+ const REANIMATED_DEPENDENCY_NAME$1 = "react-native-reanimated";
37077
+ const isPackageJsonReanimatedAware = (packageJson) => {
37078
+ const allDependencies = {
37079
+ ...packageJson.peerDependencies,
37080
+ ...packageJson.dependencies,
37081
+ ...packageJson.devDependencies,
37082
+ ...packageJson.optionalDependencies
37083
+ };
37084
+ return Object.hasOwn(allDependencies, REANIMATED_DEPENDENCY_NAME$1);
37085
+ };
37086
+ const getWorkspacePatterns = (rootDirectory, packageJson) => {
37087
+ const pnpmPatterns = parsePnpmWorkspacePatterns(rootDirectory);
37088
+ if (pnpmPatterns.length > 0) return pnpmPatterns;
37089
+ if (Array.isArray(packageJson.workspaces)) return packageJson.workspaces;
37090
+ if (packageJson.workspaces?.packages) return packageJson.workspaces.packages;
37091
+ const nxPatterns = getNxWorkspaceDirectories(rootDirectory);
37092
+ if (nxPatterns.length > 0) return nxPatterns;
37093
+ return [];
36788
37094
  };
36789
37095
  const parsePnpmWorkspacePatterns = (rootDirectory) => {
36790
37096
  const workspacePath = Path.join(rootDirectory, "pnpm-workspace.yaml");
@@ -36803,18 +37109,24 @@ const parsePnpmWorkspacePatterns = (rootDirectory) => {
36803
37109
  }
36804
37110
  return patterns;
36805
37111
  };
36806
- const getWorkspacePatterns = (rootDirectory, packageJson) => {
36807
- const pnpmPatterns = parsePnpmWorkspacePatterns(rootDirectory);
36808
- if (pnpmPatterns.length > 0) return pnpmPatterns;
36809
- if (Array.isArray(packageJson.workspaces)) return packageJson.workspaces;
36810
- if (packageJson.workspaces?.packages) return packageJson.workspaces.packages;
36811
- const nxPatterns = getNxWorkspaceDirectories(rootDirectory);
36812
- if (nxPatterns.length > 0) return nxPatterns;
36813
- return [];
36814
- };
36815
- const parseReactMajor = (reactVersion) => {
36816
- if (typeof reactVersion !== "string") return null;
36817
- return getLowestDependencyMajor(reactVersion);
37112
+ const NX_PROJECT_DISCOVERY_DIRS = [
37113
+ "apps",
37114
+ "libs",
37115
+ "packages"
37116
+ ];
37117
+ const getNxWorkspaceDirectories = (rootDirectory) => {
37118
+ if (!isFile(Path.join(rootDirectory, "nx.json"))) return [];
37119
+ const collected = [];
37120
+ for (const candidate of NX_PROJECT_DISCOVERY_DIRS) {
37121
+ const candidatePath = Path.join(rootDirectory, candidate);
37122
+ if (!isDirectory(candidatePath)) continue;
37123
+ for (const entry of readDirectoryEntries(candidatePath)) {
37124
+ if (!entry.isDirectory()) continue;
37125
+ const projectDirectory = Path.join(candidatePath, entry.name);
37126
+ if (isFile(Path.join(projectDirectory, "project.json")) || isFile(Path.join(projectDirectory, "package.json"))) collected.push(`${candidate}/${entry.name}`);
37127
+ }
37128
+ }
37129
+ return collected;
36818
37130
  };
36819
37131
  const resolveWorkspaceDirectories = (rootDirectory, pattern) => {
36820
37132
  const cleanPattern = pattern.replace(/["']/g, "").replace(/\/\*\*$/, "/*");
@@ -36834,6 +37146,38 @@ const resolveWorkspaceDirectories = (rootDirectory, pattern) => {
36834
37146
  }
36835
37147
  return resolved;
36836
37148
  };
37149
+ const listWorkspacePackages = (rootDirectory) => {
37150
+ const packageJsonPath = Path.join(rootDirectory, "package.json");
37151
+ if (!isFile(packageJsonPath)) return [];
37152
+ const packageJson = readPackageJson$1(packageJsonPath);
37153
+ const patterns = getWorkspacePatterns(rootDirectory, packageJson);
37154
+ if (patterns.length === 0) return [];
37155
+ const packages = [];
37156
+ const seenDirectories = /* @__PURE__ */ new Set();
37157
+ const pushIfNew = (workspacePackage) => {
37158
+ if (seenDirectories.has(workspacePackage.directory)) return;
37159
+ seenDirectories.add(workspacePackage.directory);
37160
+ packages.push(workspacePackage);
37161
+ };
37162
+ if (hasReactDependency(packageJson)) pushIfNew({
37163
+ name: packageJson.name ?? Path.basename(rootDirectory),
37164
+ directory: rootDirectory
37165
+ });
37166
+ for (const pattern of patterns) {
37167
+ const directories = resolveWorkspaceDirectories(rootDirectory, pattern);
37168
+ for (const workspaceDirectory of directories) {
37169
+ const workspacePackageJson = readPackageJson$1(Path.join(workspaceDirectory, "package.json"));
37170
+ if (!hasReactDependency(workspacePackageJson)) continue;
37171
+ pushIfNew({
37172
+ name: workspacePackageJson.name ?? Path.basename(workspaceDirectory),
37173
+ directory: workspaceDirectory
37174
+ });
37175
+ }
37176
+ }
37177
+ return packages;
37178
+ };
37179
+ const REANIMATED_DEPENDENCY_NAME = "react-native-reanimated";
37180
+ const SHOPIFY_FLASH_LIST_PACKAGE_NAME = "@shopify/flash-list";
36837
37181
  const resolveWorkspaceDependencyVersion = ({ concreteVersion, packageName, rootDirectory, rootPackageJson, sections, workspaceDirectory, workspacePackageJson }) => {
36838
37182
  const dependencyDeclaration = getDependencyDeclaration({
36839
37183
  packageJson: workspacePackageJson,
@@ -36851,24 +37195,76 @@ const shouldReplaceReactVersion = (currentVersion, nextVersion) => {
36851
37195
  if (nextMajor === null) return false;
36852
37196
  return nextMajor < currentMajor;
36853
37197
  };
36854
- const findReactInWorkspaces = (rootDirectory, packageJson) => {
36855
- const patterns = getWorkspacePatterns(rootDirectory, packageJson);
36856
- const result = { ...EMPTY_DEPENDENCY_INFO };
36857
- for (const pattern of patterns) {
36858
- const directories = resolveWorkspaceDirectories(rootDirectory, pattern);
37198
+ const evaluateManifestFacts = (facts, packageJson, directory) => {
37199
+ if (facts.expo.version === null) {
37200
+ const spec = getDependencySpec(packageJson, "expo");
37201
+ if (spec !== null) facts.expo = {
37202
+ version: spec,
37203
+ sourceDirectory: directory
37204
+ };
37205
+ }
37206
+ if (facts.next.version === null) {
37207
+ const spec = getDependencySpec(packageJson, "next");
37208
+ if (spec !== null) facts.next = {
37209
+ version: spec,
37210
+ sourceDirectory: directory
37211
+ };
37212
+ }
37213
+ if (facts.shopifyFlashList.version === null) {
37214
+ const spec = getDependencySpec(packageJson, SHOPIFY_FLASH_LIST_PACKAGE_NAME);
37215
+ if (spec !== null) facts.shopifyFlashList = {
37216
+ version: spec,
37217
+ sourceDirectory: directory
37218
+ };
37219
+ }
37220
+ if (facts.reanimatedVersion === null) {
37221
+ const spec = getDependencySpec(packageJson, REANIMATED_DEPENDENCY_NAME);
37222
+ if (spec !== null) facts.reanimatedVersion = spec;
37223
+ }
37224
+ facts.hasReactNativeAwarePackage = facts.hasReactNativeAwarePackage || isPackageJsonReactNativeAware(packageJson);
37225
+ facts.hasReanimatedAwarePackage = facts.hasReanimatedAwarePackage || isPackageJsonReanimatedAware(packageJson);
37226
+ };
37227
+ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGroup }) => {
37228
+ const facts = {
37229
+ reactVersion: null,
37230
+ tailwindVersion: null,
37231
+ zodVersion: null,
37232
+ framework: "unknown",
37233
+ expo: {
37234
+ version: null,
37235
+ sourceDirectory: null
37236
+ },
37237
+ next: {
37238
+ version: null,
37239
+ sourceDirectory: null
37240
+ },
37241
+ shopifyFlashList: {
37242
+ version: null,
37243
+ sourceDirectory: null
37244
+ },
37245
+ hasReactNativeAwarePackage: false,
37246
+ hasReanimatedAwarePackage: false,
37247
+ reanimatedVersion: null
37248
+ };
37249
+ evaluateManifestFacts(facts, rootPackageJson, rootDirectory);
37250
+ let isReactGroupSettled = !collectReactGroup;
37251
+ const visitedDirectories = /* @__PURE__ */ new Set();
37252
+ for (const pattern of getWorkspacePatterns(rootDirectory, rootPackageJson)) {
37253
+ const directories = [...resolveWorkspaceDirectories(rootDirectory, pattern)].sort();
36859
37254
  for (const workspaceDirectory of directories) {
37255
+ if (visitedDirectories.has(workspaceDirectory)) continue;
37256
+ visitedDirectories.add(workspaceDirectory);
36860
37257
  const workspacePackageJson = readPackageJson$1(Path.join(workspaceDirectory, "package.json"));
37258
+ evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory);
36861
37259
  const info = extractDependencyInfo(workspacePackageJson);
37260
+ if (info.framework !== "unknown" && frameworkMergeRank(info.framework) < frameworkMergeRank(facts.framework)) facts.framework = info.framework;
37261
+ if (isReactGroupSettled) continue;
36862
37262
  const reactVersion = resolveWorkspaceDependencyVersion({
36863
37263
  concreteVersion: info.reactVersion,
36864
37264
  packageName: "react",
36865
37265
  rootDirectory,
36866
- rootPackageJson: packageJson,
36867
- sections: [
36868
- "dependencies",
36869
- "peerDependencies",
36870
- "devDependencies"
36871
- ],
37266
+ rootPackageJson,
37267
+ sections: REACT_SECTIONS,
36872
37268
  workspaceDirectory,
36873
37269
  workspacePackageJson
36874
37270
  });
@@ -36876,12 +37272,8 @@ const findReactInWorkspaces = (rootDirectory, packageJson) => {
36876
37272
  concreteVersion: info.tailwindVersion,
36877
37273
  packageName: "tailwindcss",
36878
37274
  rootDirectory,
36879
- rootPackageJson: packageJson,
36880
- sections: [
36881
- "dependencies",
36882
- "devDependencies",
36883
- "peerDependencies"
36884
- ],
37275
+ rootPackageJson,
37276
+ sections: TAILWIND_ZOD_SECTIONS,
36885
37277
  workspaceDirectory,
36886
37278
  workspacePackageJson
36887
37279
  });
@@ -36889,24 +37281,19 @@ const findReactInWorkspaces = (rootDirectory, packageJson) => {
36889
37281
  concreteVersion: info.zodVersion,
36890
37282
  packageName: "zod",
36891
37283
  rootDirectory,
36892
- rootPackageJson: packageJson,
36893
- sections: [
36894
- "dependencies",
36895
- "devDependencies",
36896
- "peerDependencies"
36897
- ],
37284
+ rootPackageJson,
37285
+ sections: TAILWIND_ZOD_SECTIONS,
36898
37286
  workspaceDirectory,
36899
37287
  workspacePackageJson
36900
37288
  });
36901
- if (reactVersion && shouldReplaceReactVersion(result.reactVersion, reactVersion)) result.reactVersion = reactVersion;
36902
- if (tailwindVersion && !result.tailwindVersion) result.tailwindVersion = tailwindVersion;
36903
- if (zodVersion && !result.zodVersion) result.zodVersion = zodVersion;
36904
- if (info.framework !== "unknown" && result.framework === "unknown") result.framework = info.framework;
36905
- const resultReactMajor = parseReactMajor(result.reactVersion);
36906
- if (result.reactVersion && result.tailwindVersion && result.framework !== "unknown" && resultReactMajor !== null && resultReactMajor <= 17) return result;
37289
+ if (reactVersion && shouldReplaceReactVersion(facts.reactVersion, reactVersion)) facts.reactVersion = reactVersion;
37290
+ if (tailwindVersion && !facts.tailwindVersion) facts.tailwindVersion = tailwindVersion;
37291
+ if (zodVersion && !facts.zodVersion) facts.zodVersion = zodVersion;
37292
+ const settledReactMajor = parseReactMajor(facts.reactVersion);
37293
+ isReactGroupSettled = Boolean(facts.reactVersion && facts.tailwindVersion && facts.framework !== "unknown" && settledReactMajor !== null && settledReactMajor <= 17);
36907
37294
  }
36908
37295
  }
36909
- return result;
37296
+ return facts;
36910
37297
  };
36911
37298
  const findDependencyInfoFromMonorepoRoot = (directory) => {
36912
37299
  const monorepoRoot = findMonorepoRoot(directory);
@@ -36920,29 +37307,17 @@ const findDependencyInfoFromMonorepoRoot = (directory) => {
36920
37307
  const leafReactDeclaration = leafPackageJson ? getDependencyDeclaration({
36921
37308
  packageJson: leafPackageJson,
36922
37309
  packageName: "react",
36923
- sections: [
36924
- "dependencies",
36925
- "peerDependencies",
36926
- "devDependencies"
36927
- ]
37310
+ sections: REACT_SECTIONS
36928
37311
  }) : null;
36929
37312
  const leafTailwindDeclaration = leafPackageJson ? getDependencyDeclaration({
36930
37313
  packageJson: leafPackageJson,
36931
37314
  packageName: "tailwindcss",
36932
- sections: [
36933
- "dependencies",
36934
- "devDependencies",
36935
- "peerDependencies"
36936
- ]
37315
+ sections: TAILWIND_ZOD_SECTIONS
36937
37316
  }) : null;
36938
37317
  const leafZodDeclaration = leafPackageJson ? getDependencyDeclaration({
36939
37318
  packageJson: leafPackageJson,
36940
37319
  packageName: "zod",
36941
- sections: [
36942
- "dependencies",
36943
- "devDependencies",
36944
- "peerDependencies"
36945
- ]
37320
+ sections: TAILWIND_ZOD_SECTIONS
36946
37321
  }) : null;
36947
37322
  const shouldUseReactFallback = !leafReactDeclaration?.hasDeclaration;
36948
37323
  const shouldUseTailwindFallback = leafTailwindDeclaration?.hasDeclaration ?? true;
@@ -36950,38 +37325,15 @@ const findDependencyInfoFromMonorepoRoot = (directory) => {
36950
37325
  const reactCatalogVersion = shouldUseReactFallback ? resolveCatalogVersion(rootPackageJson, "react", monorepoRoot, leafReactDeclaration?.catalogReference) : null;
36951
37326
  const tailwindCatalogVersion = shouldUseTailwindFallback ? resolveCatalogVersion(rootPackageJson, "tailwindcss", monorepoRoot, leafTailwindDeclaration?.catalogReference) : null;
36952
37327
  const zodCatalogVersion = shouldUseZodFallback ? resolveCatalogVersion(rootPackageJson, "zod", monorepoRoot, leafZodDeclaration?.catalogReference) : null;
36953
- const workspaceInfo = findReactInWorkspaces(monorepoRoot, rootPackageJson);
37328
+ const workspaceFacts = collectWorkspaceFacts(monorepoRoot, rootPackageJson, { collectReactGroup: true });
36954
37329
  return {
36955
- reactVersion: shouldUseReactFallback ? reactCatalogVersion ?? rootInfo.reactVersion ?? workspaceInfo.reactVersion : rootInfo.reactVersion ?? workspaceInfo.reactVersion,
36956
- tailwindVersion: shouldUseTailwindFallback ? tailwindCatalogVersion ?? rootInfo.tailwindVersion ?? workspaceInfo.tailwindVersion : null,
36957
- zodVersion: shouldUseZodFallback ? zodCatalogVersion ?? rootInfo.zodVersion ?? workspaceInfo.zodVersion : null,
36958
- framework: rootInfo.framework !== "unknown" ? rootInfo.framework : workspaceInfo.framework
37330
+ reactVersion: shouldUseReactFallback ? reactCatalogVersion ?? rootInfo.reactVersion ?? workspaceFacts.reactVersion : rootInfo.reactVersion ?? workspaceFacts.reactVersion,
37331
+ tailwindVersion: shouldUseTailwindFallback ? tailwindCatalogVersion ?? rootInfo.tailwindVersion ?? workspaceFacts.tailwindVersion : null,
37332
+ zodVersion: shouldUseZodFallback ? zodCatalogVersion ?? rootInfo.zodVersion ?? workspaceFacts.zodVersion : null,
37333
+ framework: rootInfo.framework !== "unknown" ? rootInfo.framework : workspaceFacts.framework
36959
37334
  };
36960
37335
  };
36961
37336
  /**
36962
- * Walk up from `startDirectory` to the nearest ancestor that owns a
36963
- * `package.json`, stopping at (and including) the enclosing project boundary
36964
- * — the working tree's git root or a monorepo root. Lets a scan of a package
36965
- * subfolder that has no `package.json` of its own adopt the nearest enclosing
36966
- * package as its project root, so it inherits that package's dependency +
36967
- * framework detection instead of synthesizing an empty (React-blind) project.
36968
- *
36969
- * Generalizes the older monorepo-root-only lookup: the nearest ancestor is the
36970
- * most specific owning package (a leaf workspace, not just the repo root), so
36971
- * a subfolder of one workspace no longer borrows a sibling's capabilities.
36972
- *
36973
- * Returns `null` when no ancestor `package.json` exists inside the project
36974
- * boundary (a loose tree of files, or a subfolder of a package-less repo).
36975
- */
36976
- const findNearestAncestorPackageJson = (startDirectory) => {
36977
- if (isProjectBoundary(startDirectory)) return null;
36978
- for (const directory of ancestorDirectories(startDirectory, { includeStart: false })) {
36979
- if (isFile(Path.join(directory, "package.json"))) return directory;
36980
- if (isProjectBoundary(directory)) return null;
36981
- }
36982
- return null;
36983
- };
36984
- /**
36985
37337
  * Last-resort React detection: locate React the way Node's `node_modules`
36986
37338
  * resolution would from `directory` — check each `node_modules/react` up the
36987
37339
  * tree — but bounded to the enclosing repo so a globally installed React can't
@@ -37026,295 +37378,6 @@ const resolveInstalledReactVersion = (directory) => {
37026
37378
  }
37027
37379
  return null;
37028
37380
  };
37029
- const findInWorkspacePackageJsons = (rootDirectory, rootPackageJson, select) => {
37030
- const rootValue = select(rootPackageJson);
37031
- if (rootValue !== null) return rootValue;
37032
- const patterns = getWorkspacePatterns(rootDirectory, rootPackageJson);
37033
- if (patterns.length === 0) return null;
37034
- const visitedDirectories = /* @__PURE__ */ new Set();
37035
- for (const pattern of patterns) {
37036
- const directories = [...resolveWorkspaceDirectories(rootDirectory, pattern)].sort();
37037
- for (const workspaceDirectory of directories) {
37038
- if (visitedDirectories.has(workspaceDirectory)) continue;
37039
- visitedDirectories.add(workspaceDirectory);
37040
- const value = select(readPackageJson$1(Path.join(workspaceDirectory, "package.json")));
37041
- if (value !== null) return value;
37042
- }
37043
- }
37044
- return null;
37045
- };
37046
- const someWorkspacePackageJson = (rootDirectory, rootPackageJson, predicate) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => predicate(packageJson) ? true : null) !== null;
37047
- const NAMES = new Set([
37048
- "react-native",
37049
- "react-native-tvos",
37050
- ...new Set([
37051
- "expo",
37052
- "expo-router",
37053
- "@expo/cli",
37054
- "@expo/metro-config",
37055
- "@expo/metro-runtime"
37056
- ]),
37057
- "react-native-windows",
37058
- "react-native-macos"
37059
- ]);
37060
- const PREFIXES = ["@react-native/", "@react-native-"];
37061
- const isReactNativeDependencyName = (dependencyName) => {
37062
- if (NAMES.has(dependencyName)) return true;
37063
- for (const prefix of PREFIXES) if (dependencyName.startsWith(prefix)) return true;
37064
- return false;
37065
- };
37066
- const containsAnyReactNativeDependency = (section) => {
37067
- if (!section) return false;
37068
- for (const dependencyName of Object.keys(section)) if (isReactNativeDependencyName(dependencyName)) return true;
37069
- return false;
37070
- };
37071
- const isPackageJsonReactNativeAware = (packageJson) => {
37072
- if (typeof packageJson["react-native"] === "string") return true;
37073
- if (containsAnyReactNativeDependency(packageJson.dependencies)) return true;
37074
- if (containsAnyReactNativeDependency(packageJson.devDependencies)) return true;
37075
- if (containsAnyReactNativeDependency(packageJson.peerDependencies)) return true;
37076
- if (containsAnyReactNativeDependency(packageJson.optionalDependencies)) return true;
37077
- return false;
37078
- };
37079
- const hasReactNativeWorkspaceAnywhere = (rootDirectory, rootPackageJson) => someWorkspacePackageJson(rootDirectory, rootPackageJson, isPackageJsonReactNativeAware);
37080
- const getDependencySpec = (packageJson, packageName) => {
37081
- const spec = packageJson.dependencies?.[packageName] ?? packageJson.devDependencies?.[packageName] ?? packageJson.peerDependencies?.[packageName] ?? packageJson.optionalDependencies?.[packageName];
37082
- return typeof spec === "string" ? spec : null;
37083
- };
37084
- const findExpoVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, "expo"));
37085
- const SHOPIFY_FLASH_LIST_PACKAGE_NAME = "@shopify/flash-list";
37086
- const findShopifyFlashListVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, SHOPIFY_FLASH_LIST_PACKAGE_NAME));
37087
- const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, version }) => {
37088
- if (version === null || !isCatalogReference(version)) return version;
37089
- const catalogName = extractCatalogName(version);
37090
- const resolvedLocalVersion = resolveCatalogVersion(rootPackageJson, packageName, rootDirectory, catalogName);
37091
- if (resolvedLocalVersion) return resolvedLocalVersion;
37092
- const monorepoRoot = findMonorepoRoot(rootDirectory);
37093
- if (!monorepoRoot) return version;
37094
- const monorepoPackageJsonPath = path.join(monorepoRoot, "package.json");
37095
- if (!isFile(monorepoPackageJsonPath)) return version;
37096
- return resolveCatalogVersion(readPackageJson$1(monorepoPackageJsonPath), packageName, monorepoRoot, catalogName) ?? version;
37097
- };
37098
- const findNextjsVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, "next"));
37099
- const getPreactVersion = (packageJson) => {
37100
- return {
37101
- ...packageJson.peerDependencies,
37102
- ...packageJson.dependencies,
37103
- ...packageJson.devDependencies
37104
- }.preact ?? null;
37105
- };
37106
- const TANSTACK_QUERY_PACKAGES = new Set([
37107
- "@tanstack/react-query",
37108
- "@tanstack/query-core",
37109
- "react-query"
37110
- ]);
37111
- const hasTanStackQuery = (packageJson) => {
37112
- const allDependencies = {
37113
- ...packageJson.peerDependencies,
37114
- ...packageJson.dependencies,
37115
- ...packageJson.devDependencies
37116
- };
37117
- return Object.keys(allDependencies).some((packageName) => TANSTACK_QUERY_PACKAGES.has(packageName));
37118
- };
37119
- const REANIMATED_DEPENDENCY_NAME = "react-native-reanimated";
37120
- const isPackageJsonReanimatedAware = (packageJson) => {
37121
- const allDependencies = {
37122
- ...packageJson.peerDependencies,
37123
- ...packageJson.dependencies,
37124
- ...packageJson.devDependencies,
37125
- ...packageJson.optionalDependencies
37126
- };
37127
- return Object.hasOwn(allDependencies, REANIMATED_DEPENDENCY_NAME);
37128
- };
37129
- const parseZodMajor = (zodVersion) => {
37130
- if (typeof zodVersion !== "string") return null;
37131
- return getLowestDependencyMajor(zodVersion);
37132
- };
37133
- const hasUpperBoundOnlyPeerRange = (range) => {
37134
- if (typeof range !== "string") return false;
37135
- const normalizedRange = normalizeDependencyVersion(range);
37136
- if (normalizedRange === null) return false;
37137
- return splitDependencyVersionBranches(normalizedRange).some((branch) => {
37138
- const normalizedBranch = normalizeDependencyVersion(branch);
37139
- return normalizedBranch !== null && getBranchLowestMajor(normalizedBranch) === null && hasUpperBoundComparator(normalizedBranch);
37140
- });
37141
- };
37142
- const peerRangeMinMajor = (range) => {
37143
- if (typeof range !== "string") return null;
37144
- return getLowestDependencyMajor(range);
37145
- };
37146
- const resolveEffectiveReactMajor = (reactVersion, packageJson) => {
37147
- const installedReactMajor = parseReactMajor(reactVersion);
37148
- const peerReactRange = packageJson.peerDependencies?.react;
37149
- if (typeof peerReactRange !== "string") return installedReactMajor;
37150
- const peerFloor = peerRangeMinMajor(peerReactRange);
37151
- if (peerFloor === null) return hasUpperBoundOnlyPeerRange(peerReactRange) ? null : installedReactMajor;
37152
- return installedReactMajor !== null ? Math.min(installedReactMajor, peerFloor) : peerFloor;
37153
- };
37154
- const TSCONFIG_FILENAME = "tsconfig.json";
37155
- const isRelativeExtendsValue = (extendsValue) => extendsValue.startsWith("./") || extendsValue.startsWith("../") || Path.isAbsolute(extendsValue);
37156
- const ensureJsonExtension = (filePath) => Path.extname(filePath) === "" ? `${filePath}.json` : filePath;
37157
- const resolvePackageExtendsPath = (extendsValue, fromConfigDirectory) => {
37158
- const requireFromConfig = createRequire(Path.join(fromConfigDirectory, "tsconfig.json"));
37159
- const candidates = [
37160
- extendsValue,
37161
- ensureJsonExtension(extendsValue),
37162
- `${extendsValue.replace(/\/$/, "")}/tsconfig.json`
37163
- ];
37164
- for (const candidate of candidates) try {
37165
- return requireFromConfig.resolve(candidate);
37166
- } catch {
37167
- continue;
37168
- }
37169
- return null;
37170
- };
37171
- const resolveExtendsPath = (extendsValue, fromConfigDirectory) => {
37172
- if (isRelativeExtendsValue(extendsValue)) return ensureJsonExtension(Path.resolve(fromConfigDirectory, extendsValue));
37173
- return resolvePackageExtendsPath(extendsValue, fromConfigDirectory);
37174
- };
37175
- const normalizeCompilerOptions = (compilerOptions) => {
37176
- if (!isPlainObject(compilerOptions)) return { hasExplicitLib: false };
37177
- const target = typeof compilerOptions.target === "string" ? compilerOptions.target : void 0;
37178
- const hasExplicitLib = Object.hasOwn(compilerOptions, "lib");
37179
- return {
37180
- target,
37181
- lib: Array.isArray(compilerOptions.lib) ? compilerOptions.lib.filter((entry) => typeof entry === "string") : void 0,
37182
- hasExplicitLib
37183
- };
37184
- };
37185
- const normalizeReferencePaths = (references) => {
37186
- if (!Array.isArray(references)) return [];
37187
- return references.map((reference) => isPlainObject(reference) && typeof reference.path === "string" ? reference.path : null).filter((referencePath) => referencePath !== null);
37188
- };
37189
- const readTsConfig = (filePath) => {
37190
- let content;
37191
- try {
37192
- content = NFS.readFileSync(filePath, "utf-8");
37193
- } catch {
37194
- return null;
37195
- }
37196
- const parsed = ts.parseConfigFileTextToJson(filePath, content);
37197
- if (!isPlainObject(parsed.config)) return null;
37198
- return {
37199
- extends: typeof parsed.config.extends === "string" ? parsed.config.extends : void 0,
37200
- referencePaths: normalizeReferencePaths(parsed.config.references),
37201
- compilerOptions: normalizeCompilerOptions(parsed.config.compilerOptions)
37202
- };
37203
- };
37204
- const mergeCompilerOptions = (inherited, current) => {
37205
- const target = current.target ?? inherited?.target;
37206
- const hasExplicitLib = current.hasExplicitLib || Boolean(inherited?.hasExplicitLib);
37207
- return {
37208
- target,
37209
- lib: current.hasExplicitLib ? current.lib : inherited?.lib,
37210
- hasExplicitLib
37211
- };
37212
- };
37213
- const readResolvedCompilerOptions = (tsConfigPath, extendsDepth, visitedPaths) => {
37214
- const realPath = NFS.realpathSync.native(tsConfigPath);
37215
- if (visitedPaths.has(realPath)) return null;
37216
- const tsConfig = readTsConfig(realPath);
37217
- if (!tsConfig) return null;
37218
- const nextVisitedPaths = new Set(visitedPaths);
37219
- nextVisitedPaths.add(realPath);
37220
- if (tsConfig.extends && extendsDepth < 8) {
37221
- const parentPath = resolveExtendsPath(tsConfig.extends, Path.dirname(realPath));
37222
- if (parentPath && isFile(parentPath)) return mergeCompilerOptions(readResolvedCompilerOptions(parentPath, extendsDepth + 1, nextVisitedPaths), tsConfig.compilerOptions);
37223
- }
37224
- return tsConfig.compilerOptions;
37225
- };
37226
- const targetYearIsPreES2023 = (target) => {
37227
- const year = ES_TARGET_YEAR_BY_NAME[target.toLowerCase()];
37228
- return year !== void 0 && year < 2023;
37229
- };
37230
- const libEntryIncludesES2023Array = (entry) => {
37231
- const normalizedEntry = entry.toLowerCase();
37232
- if (normalizedEntry === "esnext" || normalizedEntry === "esnext.array") return true;
37233
- const esYearMatch = /^es(\d{4})(?:\.(.+))?$/.exec(normalizedEntry);
37234
- if (!esYearMatch) return false;
37235
- if (Number(esYearMatch[1]) < 2023) return false;
37236
- const component = esYearMatch[2];
37237
- return component === void 0 || component === "array";
37238
- };
37239
- const libIncludesES2023 = (lib) => lib.some(libEntryIncludesES2023Array);
37240
- const compilerOptionsArePreES2023 = (compilerOptions) => {
37241
- if (compilerOptions.target && targetYearIsPreES2023(compilerOptions.target)) return true;
37242
- if (compilerOptions.hasExplicitLib) return !libIncludesES2023(compilerOptions.lib ?? []);
37243
- return false;
37244
- };
37245
- const compilerOptionsDeclareTargetOrLib = (compilerOptions) => compilerOptions.hasExplicitLib || compilerOptions.target !== void 0;
37246
- const detectPreES2023FromConfig = (tsConfigPath, visitedConfigPaths = /* @__PURE__ */ new Set()) => {
37247
- if (visitedConfigPaths.has(tsConfigPath)) return false;
37248
- const compilerOptions = readResolvedCompilerOptions(tsConfigPath, 0, /* @__PURE__ */ new Set());
37249
- if (!compilerOptions) return false;
37250
- if (!compilerOptionsDeclareTargetOrLib(compilerOptions)) {
37251
- const tsConfig = readTsConfig(tsConfigPath);
37252
- if (!tsConfig) return false;
37253
- const nextVisitedConfigPaths = new Set(visitedConfigPaths);
37254
- nextVisitedConfigPaths.add(tsConfigPath);
37255
- const configDirectory = Path.dirname(tsConfigPath);
37256
- return tsConfig.referencePaths.some((referencePath) => {
37257
- const resolvedReferencePath = Path.resolve(configDirectory, referencePath);
37258
- const referencedConfigPath = isFile(resolvedReferencePath) ? resolvedReferencePath : Path.join(resolvedReferencePath, TSCONFIG_FILENAME);
37259
- return isFile(referencedConfigPath) && detectPreES2023FromConfig(referencedConfigPath, nextVisitedConfigPaths);
37260
- });
37261
- }
37262
- return compilerOptionsArePreES2023(compilerOptions);
37263
- };
37264
- const FALLBACK_TSCONFIG_FILENAMES = ["tsconfig.app.json", "tsconfig.build.json"];
37265
- const detectPreES2023Target = (directory) => {
37266
- const tsConfigPath = Path.join(directory, TSCONFIG_FILENAME);
37267
- if (isFile(tsConfigPath)) return detectPreES2023FromConfig(tsConfigPath);
37268
- for (const fallbackFilename of FALLBACK_TSCONFIG_FILENAMES) {
37269
- const fallbackPath = Path.join(directory, fallbackFilename);
37270
- if (isFile(fallbackPath)) return detectPreES2023FromConfig(fallbackPath);
37271
- }
37272
- return false;
37273
- };
37274
- const REACT_DEPENDENCY_NAMES = new Set([
37275
- "react",
37276
- "react-native",
37277
- "next",
37278
- "preact"
37279
- ]);
37280
- const hasReactDependency = (packageJson) => {
37281
- const allDependencies = {
37282
- ...packageJson.peerDependencies,
37283
- ...packageJson.dependencies,
37284
- ...packageJson.devDependencies
37285
- };
37286
- return Object.keys(allDependencies).some((packageName) => REACT_DEPENDENCY_NAMES.has(packageName));
37287
- };
37288
- const listWorkspacePackages = (rootDirectory) => {
37289
- const packageJsonPath = Path.join(rootDirectory, "package.json");
37290
- if (!isFile(packageJsonPath)) return [];
37291
- const packageJson = readPackageJson$1(packageJsonPath);
37292
- const patterns = getWorkspacePatterns(rootDirectory, packageJson);
37293
- if (patterns.length === 0) return [];
37294
- const packages = [];
37295
- const seenDirectories = /* @__PURE__ */ new Set();
37296
- const pushIfNew = (workspacePackage) => {
37297
- if (seenDirectories.has(workspacePackage.directory)) return;
37298
- seenDirectories.add(workspacePackage.directory);
37299
- packages.push(workspacePackage);
37300
- };
37301
- if (hasReactDependency(packageJson)) pushIfNew({
37302
- name: packageJson.name ?? Path.basename(rootDirectory),
37303
- directory: rootDirectory
37304
- });
37305
- for (const pattern of patterns) {
37306
- const directories = resolveWorkspaceDirectories(rootDirectory, pattern);
37307
- for (const workspaceDirectory of directories) {
37308
- const workspacePackageJson = readPackageJson$1(Path.join(workspaceDirectory, "package.json"));
37309
- if (!hasReactDependency(workspacePackageJson)) continue;
37310
- pushIfNew({
37311
- name: workspacePackageJson.name ?? Path.basename(workspaceDirectory),
37312
- directory: workspaceDirectory
37313
- });
37314
- }
37315
- }
37316
- return packages;
37317
- };
37318
37381
  const toReactWorkspacePackages = (directories) => {
37319
37382
  const packages = [];
37320
37383
  for (const directory of directories) {
@@ -37384,14 +37447,13 @@ const discoverReactSubprojects = (rootDirectory) => {
37384
37447
  const cachedProjectInfos = /* @__PURE__ */ new Map();
37385
37448
  /**
37386
37449
  * Build a `ProjectInfo` for a directory that has no `package.json` of
37387
- * its own — a package subfolder like `repo/packages` or `app/src/features`,
37388
- * or any loose tree of TypeScript/JavaScript files. Dependency + framework
37389
- * detection is inherited from the nearest enclosing package (a leaf
37390
- * workspace, a plain app root, or a monorepo root — whichever is closest,
37391
- * bounded by the git root), so scanning a subdirectory of a React project
37392
- * still gets the React capabilities; a standalone non-React directory simply
37393
- * scans with the framework-agnostic rules. Throws only when the directory has
37394
- * nothing to scan (no enclosing project and no source files of its own).
37450
+ * its own — a monorepo subfolder like `repo/packages`, or any loose tree
37451
+ * of TypeScript/JavaScript files. Dependency + framework detection is
37452
+ * inherited from the enclosing workspace root when there is one, so
37453
+ * scanning a subdirectory of a React monorepo still gets the React
37454
+ * capabilities; a standalone non-React directory simply scans with the
37455
+ * framework-agnostic rules. Throws only when the directory has nothing
37456
+ * to scan (no enclosing project and no source files of its own).
37395
37457
  */
37396
37458
  const discoverProjectWithoutPackageJson = (directory) => {
37397
37459
  const sourceFileCount = countSourceFiles(directory);
@@ -37429,6 +37491,7 @@ const discoverProjectWithoutPackageJson = (directory) => {
37429
37491
  hasReanimated: false,
37430
37492
  reanimatedVersion: null,
37431
37493
  isPreES2023Target: hasOwnTsConfig && detectPreES2023Target(directory),
37494
+ isStaticExport: false,
37432
37495
  sourceFileCount
37433
37496
  };
37434
37497
  };
@@ -37442,90 +37505,85 @@ const discoverProject = (directory) => {
37442
37505
  return synthesized;
37443
37506
  }
37444
37507
  const packageJson = readPackageJson$1(packageJsonPath);
37445
- let { reactVersion, tailwindVersion, zodVersion, framework } = extractDependencyInfo(packageJson);
37446
- const reactDeclaration = getDependencyDeclaration({
37447
- packageJson,
37448
- packageName: "react",
37449
- sections: [
37450
- "dependencies",
37451
- "peerDependencies",
37452
- "devDependencies"
37453
- ]
37454
- });
37455
- const tailwindDeclaration = getDependencyDeclaration({
37456
- packageJson,
37457
- packageName: "tailwindcss",
37458
- sections: [
37459
- "dependencies",
37460
- "devDependencies",
37461
- "peerDependencies"
37462
- ]
37463
- });
37464
- const zodDeclaration = getDependencyDeclaration({
37508
+ const rootInfo = extractDependencyInfo(packageJson);
37509
+ let framework = rootInfo.framework;
37510
+ const tracked = {
37511
+ react: {
37512
+ version: rootInfo.reactVersion,
37513
+ sections: REACT_SECTIONS
37514
+ },
37515
+ tailwindcss: {
37516
+ version: rootInfo.tailwindVersion,
37517
+ sections: TAILWIND_ZOD_SECTIONS
37518
+ },
37519
+ zod: {
37520
+ version: rootInfo.zodVersion,
37521
+ sections: TAILWIND_ZOD_SECTIONS
37522
+ }
37523
+ };
37524
+ const declarations = Object.fromEntries(Object.entries(tracked).map(([packageName, entry]) => [packageName, getDependencyDeclaration({
37465
37525
  packageJson,
37466
- packageName: "zod",
37467
- sections: [
37468
- "dependencies",
37469
- "devDependencies",
37470
- "peerDependencies"
37471
- ]
37472
- });
37473
- if (!reactVersion && reactDeclaration.hasDeclaration) reactVersion = resolveCatalogVersion(packageJson, "react", directory, reactDeclaration.catalogReference);
37474
- if (!tailwindVersion && tailwindDeclaration.hasDeclaration) tailwindVersion = resolveCatalogVersion(packageJson, "tailwindcss", directory, tailwindDeclaration.catalogReference);
37475
- if (!zodVersion && zodDeclaration.hasDeclaration) zodVersion = resolveCatalogVersion(packageJson, "zod", directory, zodDeclaration.catalogReference);
37476
- if (!reactVersion || !tailwindVersion || !zodVersion) {
37526
+ packageName,
37527
+ sections: entry.sections
37528
+ })]));
37529
+ const fillFromCatalogs = (source, sourceDirectory) => {
37530
+ for (const [packageName, entry] of Object.entries(tracked)) if (!entry.version && declarations[packageName].hasDeclaration) entry.version = resolveCatalogVersion(source, packageName, sourceDirectory, declarations[packageName].catalogReference);
37531
+ };
37532
+ fillFromCatalogs(packageJson, directory);
37533
+ if (!tracked.react.version || !tracked.tailwindcss.version || !tracked.zod.version) {
37477
37534
  const monorepoRoot = findMonorepoRoot(directory);
37478
37535
  if (monorepoRoot) {
37479
37536
  const monorepoPackageJsonPath = Path.join(monorepoRoot, "package.json");
37480
- if (isFile(monorepoPackageJsonPath)) {
37481
- const rootPackageJson = readPackageJson$1(monorepoPackageJsonPath);
37482
- if (!reactVersion && reactDeclaration.hasDeclaration) reactVersion = resolveCatalogVersion(rootPackageJson, "react", monorepoRoot, reactDeclaration.catalogReference);
37483
- if (!tailwindVersion && tailwindDeclaration.hasDeclaration) tailwindVersion = resolveCatalogVersion(rootPackageJson, "tailwindcss", monorepoRoot, tailwindDeclaration.catalogReference);
37484
- if (!zodVersion && zodDeclaration.hasDeclaration) zodVersion = resolveCatalogVersion(rootPackageJson, "zod", monorepoRoot, zodDeclaration.catalogReference);
37485
- }
37537
+ if (isFile(monorepoPackageJsonPath)) fillFromCatalogs(readPackageJson$1(monorepoPackageJsonPath), monorepoRoot);
37486
37538
  }
37487
37539
  }
37488
- if (!reactVersion || framework === "unknown") {
37489
- const workspaceInfo = findReactInWorkspaces(directory, packageJson);
37490
- if (!reactVersion && workspaceInfo.reactVersion) reactVersion = workspaceInfo.reactVersion;
37491
- if (!tailwindVersion && workspaceInfo.tailwindVersion) tailwindVersion = workspaceInfo.tailwindVersion;
37492
- if (!zodVersion && workspaceInfo.zodVersion) zodVersion = workspaceInfo.zodVersion;
37493
- if (framework === "unknown" && workspaceInfo.framework !== "unknown") framework = workspaceInfo.framework;
37540
+ const shouldCollectReactGroup = !tracked.react.version || framework === "unknown";
37541
+ const workspaceFacts = collectWorkspaceFacts(directory, packageJson, { collectReactGroup: shouldCollectReactGroup });
37542
+ if (shouldCollectReactGroup) {
37543
+ tracked.react.version ||= workspaceFacts.reactVersion;
37544
+ tracked.tailwindcss.version ||= workspaceFacts.tailwindVersion;
37545
+ tracked.zod.version ||= workspaceFacts.zodVersion;
37546
+ if (framework === "unknown" && workspaceFacts.framework !== "unknown") framework = workspaceFacts.framework;
37494
37547
  }
37495
- if ((!reactVersion || framework === "unknown") && !isMonorepoRoot(directory)) {
37548
+ if ((!tracked.react.version || framework === "unknown") && !isMonorepoRoot(directory)) {
37496
37549
  const monorepoInfo = findDependencyInfoFromMonorepoRoot(directory);
37497
- if (!reactVersion) reactVersion = monorepoInfo.reactVersion;
37498
- if (!tailwindVersion) tailwindVersion = monorepoInfo.tailwindVersion;
37499
- if (!zodVersion) zodVersion = monorepoInfo.zodVersion;
37550
+ tracked.react.version ||= monorepoInfo.reactVersion;
37551
+ tracked.tailwindcss.version ||= monorepoInfo.tailwindVersion;
37552
+ tracked.zod.version ||= monorepoInfo.zodVersion;
37500
37553
  if (framework === "unknown") framework = monorepoInfo.framework;
37501
37554
  }
37502
- if (!reactVersion && reactDeclaration.version && !isCatalogReference(reactDeclaration.version)) reactVersion = reactDeclaration.version;
37503
- if (!tailwindVersion && tailwindDeclaration.version && !isCatalogReference(tailwindDeclaration.version)) tailwindVersion = tailwindDeclaration.version;
37504
- if (!zodVersion && zodDeclaration.version && !isCatalogReference(zodDeclaration.version)) zodVersion = zodDeclaration.version;
37555
+ for (const [packageName, entry] of Object.entries(tracked)) {
37556
+ const declaredVersion = declarations[packageName].version;
37557
+ if (!entry.version && declaredVersion && !isCatalogReference(declaredVersion)) entry.version = declaredVersion;
37558
+ }
37559
+ const { react, tailwindcss, zod } = tracked;
37560
+ let reactVersion = react.version;
37505
37561
  if (!reactVersion || parseReactMajor(reactVersion) === null) reactVersion = resolveInstalledReactVersion(directory) ?? reactVersion;
37562
+ const tailwindVersion = tailwindcss.version;
37563
+ const zodVersion = zod.version;
37506
37564
  const projectName = packageJson.name ?? Path.basename(directory);
37507
37565
  const hasTypeScript = NFS.existsSync(Path.join(directory, "tsconfig.json"));
37508
37566
  const sourceFileCount = countSourceFiles(directory);
37509
- const hasReactNativeWorkspace = framework === "expo" || framework === "react-native" || hasReactNativeWorkspaceAnywhere(directory, packageJson);
37567
+ const hasReactNativeWorkspace = framework === "expo" || framework === "react-native" || workspaceFacts.hasReactNativeAwarePackage;
37510
37568
  const expoVersion = hasReactNativeWorkspace ? resolveCatalogBackedDependencyVersion({
37511
37569
  rootDirectory: directory,
37512
37570
  rootPackageJson: packageJson,
37513
37571
  packageName: "expo",
37514
- version: findExpoVersion(directory, packageJson)
37572
+ version: workspaceFacts.expo.version
37515
37573
  }) : null;
37516
37574
  const shopifyFlashListVersion = hasReactNativeWorkspace ? resolveCatalogBackedDependencyVersion({
37517
37575
  rootDirectory: directory,
37518
37576
  rootPackageJson: packageJson,
37519
37577
  packageName: SHOPIFY_FLASH_LIST_PACKAGE_NAME,
37520
- version: findShopifyFlashListVersion(directory, packageJson)
37578
+ version: workspaceFacts.shopifyFlashList.version
37521
37579
  }) : null;
37522
- const hasReanimated = hasReactNativeWorkspace && someWorkspacePackageJson(directory, packageJson, isPackageJsonReanimatedAware);
37523
- const reanimatedVersion = hasReanimated ? findInWorkspacePackageJsons(directory, packageJson, (workspacePackageJson) => getDependencySpec(workspacePackageJson, REANIMATED_DEPENDENCY_NAME)) : null;
37580
+ const hasReanimated = hasReactNativeWorkspace && workspaceFacts.hasReanimatedAwarePackage;
37581
+ const reanimatedVersion = hasReanimated ? workspaceFacts.reanimatedVersion : null;
37524
37582
  const nextjsVersion = framework === "nextjs" ? resolveCatalogBackedDependencyVersion({
37525
37583
  rootDirectory: directory,
37526
37584
  rootPackageJson: packageJson,
37527
37585
  packageName: "next",
37528
- version: findNextjsVersion(directory, packageJson)
37586
+ version: workspaceFacts.next.version
37529
37587
  }) : null;
37530
37588
  const preactVersion = getPreactVersion(packageJson);
37531
37589
  const isPreES2023Target = hasTypeScript && detectPreES2023Target(directory);
@@ -37536,7 +37594,7 @@ const discoverProject = (directory) => {
37536
37594
  reactMajorVersion: resolveEffectiveReactMajor(reactVersion, packageJson),
37537
37595
  tailwindVersion,
37538
37596
  zodVersion,
37539
- zodMajorVersion: parseZodMajor(zodVersion),
37597
+ zodMajorVersion: zodVersion === null ? null : getLowestDependencyMajor(zodVersion),
37540
37598
  framework,
37541
37599
  hasTypeScript,
37542
37600
  hasReactCompiler: detectReactCompiler(directory, packageJson),
@@ -37552,6 +37610,7 @@ const discoverProject = (directory) => {
37552
37610
  hasReanimated,
37553
37611
  reanimatedVersion,
37554
37612
  isPreES2023Target,
37613
+ isStaticExport: framework === "nextjs" && detectNextjsStaticExport(workspaceFacts.next.sourceDirectory ?? directory),
37555
37614
  sourceFileCount
37556
37615
  };
37557
37616
  cachedProjectInfos.set(directory, projectInfo);
@@ -37567,57 +37626,6 @@ const discoverProject = (directory) => {
37567
37626
  * single source file has nothing to analyze.
37568
37627
  */
37569
37628
  const isAnalyzableProject = (project) => project.reactVersion !== null || project.preactVersion !== null || project.sourceFileCount > 0;
37570
- const MAJOR_MINOR_PATTERN = /(\d{1,4})\.(\d{1,4})/;
37571
- const MAJOR_ONLY_PATTERN = /(\d{1,4})/;
37572
- const UPPER_BOUND_COMPARATOR_PATTERN = /<=?\s{0,8}\d{1,4}(?:\.\d{1,4}){0,2}(?:-[^\s,|]+)?/g;
37573
- const parseReactMajorMinor = (reactVersion) => {
37574
- if (typeof reactVersion !== "string") return null;
37575
- const trimmed = reactVersion.trim();
37576
- if (trimmed.length === 0) return null;
37577
- const lowerBoundsOnly = trimmed.replace(UPPER_BOUND_COMPARATOR_PATTERN, " ").trim();
37578
- if (lowerBoundsOnly.length === 0) return null;
37579
- const majorMinorMatch = lowerBoundsOnly.match(MAJOR_MINOR_PATTERN);
37580
- if (majorMinorMatch) {
37581
- const major = Number.parseInt(majorMinorMatch[1], 10);
37582
- const minor = Number.parseInt(majorMinorMatch[2], 10);
37583
- if (!Number.isFinite(major) || major <= 0) return null;
37584
- if (!Number.isFinite(minor) || minor < 0) return null;
37585
- return {
37586
- major,
37587
- minor
37588
- };
37589
- }
37590
- const majorOnlyMatch = lowerBoundsOnly.match(MAJOR_ONLY_PATTERN);
37591
- if (!majorOnlyMatch) return null;
37592
- const major = Number.parseInt(majorOnlyMatch[1], 10);
37593
- if (!Number.isFinite(major) || major <= 0) return null;
37594
- return {
37595
- major,
37596
- minor: 0
37597
- };
37598
- };
37599
- const isReactAtLeast = (detected, required) => {
37600
- if (detected === null) return true;
37601
- if (detected.major !== required.major) return detected.major > required.major;
37602
- return detected.minor >= required.minor;
37603
- };
37604
- const parseLowerBoundVersion = (versionSpec) => import_semver.validRange(versionSpec) !== null ? import_semver.minVersion(versionSpec) : import_semver.coerce(versionSpec);
37605
- const parseTailwindMajorMinor = (tailwindVersion) => {
37606
- if (typeof tailwindVersion !== "string") return null;
37607
- const trimmed = tailwindVersion.trim();
37608
- if (trimmed.length === 0) return null;
37609
- const lowerBound = parseLowerBoundVersion(trimmed);
37610
- if (lowerBound === null || lowerBound.major <= 0) return null;
37611
- return {
37612
- major: lowerBound.major,
37613
- minor: lowerBound.minor
37614
- };
37615
- };
37616
- const isTailwindAtLeast = (detected, required) => {
37617
- if (detected === null) return true;
37618
- if (detected.major !== required.major) return detected.major > required.major;
37619
- return detected.minor >= required.minor;
37620
- };
37621
37629
  const messageFromUnknown = (error) => error instanceof Error ? error.message : String(error);
37622
37630
  var InvalidGlobPatternError = class extends Error {
37623
37631
  pattern;
@@ -40428,64 +40436,64 @@ function* collectSecurityScanFiles(rootDirectory) {
40428
40436
  }
40429
40437
  }
40430
40438
  }
40431
- /**
40432
- * Whether project discovery resolved a React-compatible runtime (React or
40433
- * Preact, which ships the same hooks + JSX model). This is the same
40434
- * predicate that grants the `react` capability, so `false` means every
40435
- * React-runtime rule family was gated off for the project.
40436
- */
40437
- const hasReactRuntime = (project) => project.reactVersion !== null || project.preactVersion !== null;
40439
+ const CLIENT_ONLY_FRAMEWORKS = new Set([
40440
+ "vite",
40441
+ "cra",
40442
+ "gatsby",
40443
+ "react-native",
40444
+ "expo"
40445
+ ]);
40446
+ const addMajorLadder = (capabilities, name, major, earliest, latest) => {
40447
+ if (major === null) return;
40448
+ const cappedMajor = Math.min(major, latest);
40449
+ for (let candidate = earliest; candidate <= cappedMajor; candidate += 1) capabilities.add(`${name}:${candidate}`);
40450
+ };
40438
40451
  const buildCapabilities = (project) => {
40439
40452
  const capabilities = /* @__PURE__ */ new Set();
40440
40453
  capabilities.add(project.framework);
40441
- if (hasReactRuntime(project)) capabilities.add("react");
40454
+ if (project.reactVersion !== null || project.preactVersion !== null) capabilities.add("react");
40442
40455
  if (project.framework === "expo" || project.framework === "react-native" || project.hasReactNativeWorkspace) capabilities.add("react-native");
40443
40456
  if (project.expoVersion !== null) capabilities.add("expo");
40457
+ if ((project.framework === "nextjs" || project.framework === "tanstack-start" || project.framework === "remix") && !project.isStaticExport) capabilities.add("server-actions");
40458
+ if (CLIENT_ONLY_FRAMEWORKS.has(project.framework)) capabilities.add("client-only");
40459
+ if (project.isStaticExport) capabilities.add("nextjs:static-export");
40444
40460
  if (project.nextjsMajorVersion !== null && project.nextjsMajorVersion >= 15) capabilities.add("nextjs:15");
40445
- const reactMajor = project.reactMajorVersion;
40446
- if (reactMajor !== null) {
40447
- const cappedReactMajor = Math.min(reactMajor, 30);
40448
- for (let major = 17; major <= cappedReactMajor; major++) capabilities.add(`react:${major}`);
40449
- if (reactMajor >= 19) {
40450
- if (isReactAtLeast(parseReactMajorMinor(project.reactVersion), {
40451
- major: 19,
40452
- minor: 2
40453
- })) capabilities.add("react:19.2");
40454
- }
40455
- }
40456
- if (project.tailwindVersion !== null) {
40457
- capabilities.add("tailwind");
40458
- if (isTailwindAtLeast(parseTailwindMajorMinor(project.tailwindVersion), {
40459
- major: 3,
40460
- minor: 4
40461
- })) capabilities.add("tailwind:3.4");
40462
- }
40463
- if (project.zodVersion !== null) {
40464
- capabilities.add("zod");
40465
- if (project.zodMajorVersion !== null && project.zodMajorVersion >= 4) capabilities.add("zod:4");
40466
- }
40461
+ addMajorLadder(capabilities, "react", project.reactMajorVersion, 17, 30);
40462
+ if (project.reactMajorVersion !== null && project.reactMajorVersion >= 19 && isMajorMinorAtLeast(parseReactMajorMinor(project.reactVersion), {
40463
+ major: 19,
40464
+ minor: 2
40465
+ })) capabilities.add("react:19.2");
40466
+ if (project.tailwindVersion !== null) capabilities.add("tailwind");
40467
+ if (project.tailwindVersion !== null && isMajorMinorAtLeast(parseTailwindMajorMinor(project.tailwindVersion), {
40468
+ major: 3,
40469
+ minor: 4
40470
+ })) capabilities.add("tailwind:3.4");
40471
+ if (project.zodVersion !== null) capabilities.add("zod");
40472
+ if (project.zodMajorVersion !== null && project.zodMajorVersion >= 4) capabilities.add("zod:4");
40467
40473
  if (project.isPreES2023Target) capabilities.add("pre-es2023");
40468
40474
  if (project.hasReactCompiler) capabilities.add("react-compiler");
40469
40475
  if (project.hasTanStackQuery) capabilities.add("tanstack-query");
40470
40476
  if (project.hasTypeScript) capabilities.add("typescript");
40471
- if (project.preactVersion !== null) {
40472
- capabilities.add("preact");
40473
- const preactMajor = project.preactMajorVersion;
40474
- if (preactMajor !== null) {
40475
- const cappedPreactMajor = Math.min(preactMajor, 20);
40476
- for (let major = 10; major <= cappedPreactMajor; major++) capabilities.add(`preact:${major}`);
40477
- }
40478
- if (project.reactVersion === null) capabilities.add("pure-preact");
40479
- }
40477
+ if (project.preactVersion !== null) capabilities.add("preact");
40478
+ addMajorLadder(capabilities, "preact", project.preactMajorVersion, 10, 20);
40479
+ if (project.preactVersion !== null && project.reactVersion === null) capabilities.add("pure-preact");
40480
+ return capabilities;
40481
+ };
40482
+ const capabilitiesByProject = /* @__PURE__ */ new WeakMap();
40483
+ const getCapabilities = (project) => {
40484
+ const cached = capabilitiesByProject.get(project);
40485
+ if (cached !== void 0) return cached;
40486
+ const capabilities = buildCapabilities(project);
40487
+ capabilitiesByProject.set(project, capabilities);
40480
40488
  return capabilities;
40481
40489
  };
40482
- const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledBy) => {
40490
+ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledWhen) => {
40483
40491
  if (requires) {
40484
40492
  for (const capability of requires) if (!capabilities.has(capability)) return false;
40485
40493
  }
40486
40494
  if (tags?.includes("react-jsx-only") && !capabilities.has("react")) return false;
40487
- if (disabledBy) {
40488
- for (const capability of disabledBy) if (capabilities.has(capability)) return false;
40495
+ if (disabledWhen) {
40496
+ for (const capability of disabledWhen) if (capabilities.has(capability)) return false;
40489
40497
  }
40490
40498
  if (tags) {
40491
40499
  for (const tag of tags) if (ignoredTags.has(tag)) return false;
@@ -40496,14 +40504,14 @@ const yieldToEventLoop = () => new Promise((resolve) => {
40496
40504
  setImmediate(resolve);
40497
40505
  });
40498
40506
  const createSecurityScanSession = (rootDirectory, options) => {
40499
- const capabilities = options.project ? buildCapabilities(options.project) : /* @__PURE__ */ new Set();
40507
+ const capabilities = options.project ? getCapabilities(options.project) : /* @__PURE__ */ new Set();
40500
40508
  const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
40501
40509
  const enabledScanRules = REACT_DOCTOR_RULES.flatMap((entry) => {
40502
40510
  const rule = entry.rule;
40503
40511
  const scan = rule.scan;
40504
40512
  if (typeof scan !== "function") return [];
40505
40513
  if (rule.defaultEnabled === false) return [];
40506
- if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledBy)) return [];
40514
+ if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledWhen)) return [];
40507
40515
  return [{
40508
40516
  entry,
40509
40517
  scan,
@@ -40523,6 +40531,7 @@ const createSecurityScanSession = (rootDirectory, options) => {
40523
40531
  return status === true;
40524
40532
  };
40525
40533
  const scanFileByRule = function* (file) {
40534
+ if (file === null) return;
40526
40535
  for (const { entry, scan, committedFilesOnly } of enabledScanRules) {
40527
40536
  for (const finding of scan(file)) {
40528
40537
  if (committedFilesOnly && isFileGitIgnored(file)) continue;
@@ -42419,7 +42428,7 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
42419
42428
  const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
42420
42429
  const jsPlugins = [];
42421
42430
  if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
42422
- const capabilities = buildCapabilities(project);
42431
+ const capabilities = getCapabilities(project);
42423
42432
  const enabledReactDoctorRules = {};
42424
42433
  for (const registryEntry of REACT_DOCTOR_RULES) {
42425
42434
  const rule = reactDoctorPlugin.rules[registryEntry.id];
@@ -42430,7 +42439,7 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
42430
42439
  if (rule.scan !== void 0) continue;
42431
42440
  if (customRulesOnly && registryEntry.originallyExternal) continue;
42432
42441
  if (rule.framework !== "global" && !rule.requires) continue;
42433
- if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledBy)) continue;
42442
+ if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledWhen)) continue;
42434
42443
  const explicitRuleOverride = resolveRuleSeverityOverride({ ruleKey: registryEntry.key }, severityControls);
42435
42444
  if (rule.defaultEnabled === false && explicitRuleOverride === void 0) continue;
42436
42445
  const severity = resolveRuleSeverityOverride({
@@ -42461,6 +42470,7 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
42461
42470
  settings: { "react-doctor": {
42462
42471
  framework: project.framework,
42463
42472
  rootDirectory: resolveSettingsRootDirectory(project.rootDirectory),
42473
+ capabilities: [...capabilities].sort(),
42464
42474
  ...project.shopifyFlashListMajorVersion !== null ? { shopifyFlashListMajorVersion: project.shopifyFlashListMajorVersion } : {},
42465
42475
  ...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {}
42466
42476
  } },
@@ -42751,21 +42761,6 @@ const mapWithConcurrency = async (items, concurrency, task) => {
42751
42761
  if (errors.length > 0) throw errors[0];
42752
42762
  return results;
42753
42763
  };
42754
- const getPublicEnvPrefix = (framework) => {
42755
- switch (framework) {
42756
- case "nextjs": return "NEXT_PUBLIC_*";
42757
- case "vite":
42758
- case "tanstack-start": return "VITE_*";
42759
- case "cra": return "REACT_APP_*";
42760
- case "gatsby": return "GATSBY_*";
42761
- default: return null;
42762
- }
42763
- };
42764
- const buildNoSecretsRecommendation = (project, fallbackRecommendation) => {
42765
- const publicEnvPrefix = getPublicEnvPrefix(project.framework);
42766
- if (!publicEnvPrefix) return fallbackRecommendation;
42767
- 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`;
42768
- };
42769
42764
  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";
42770
42765
  const ACCESSORS_MIN_VERSION = "3.15.0";
42771
42766
  const supportsAccessors = (reanimatedVersion) => {
@@ -43446,8 +43441,14 @@ const PLUGIN_CATEGORY_MAP = {
43446
43441
  };
43447
43442
  const lookupOwnString = (record, key) => Object.hasOwn(record, key) ? record[key] : void 0;
43448
43443
  const getRuleRecommendation = (ruleName, project) => {
43449
- if (ruleName === "no-secrets-in-client-code") return buildNoSecretsRecommendation(project, reactDoctorPlugin.rules["no-secrets-in-client-code"]?.recommendation ?? "Move secrets to server-only code");
43450
- return reactDoctorPlugin.rules[ruleName]?.recommendation;
43444
+ const rule = reactDoctorPlugin.rules[ruleName];
43445
+ if (!rule) return void 0;
43446
+ if (rule.recommendationFor) {
43447
+ const capabilities = getCapabilities(project);
43448
+ const conditionalRecommendation = rule.recommendationFor((capability) => capabilities.has(capability));
43449
+ if (conditionalRecommendation !== void 0) return conditionalRecommendation;
43450
+ }
43451
+ return rule.recommendation;
43451
43452
  };
43452
43453
  const getRuleCategory = (ruleName) => reactDoctorPlugin.rules[ruleName]?.category;
43453
43454
  const getRuleTitle = (ruleName) => reactDoctorPlugin.rules[ruleName]?.title;
@@ -45796,6 +45797,13 @@ const summarizeDiagnostics = (diagnostics, worstScore = null, worstScoreLabel =
45796
45797
  scoreLabel: worstScoreLabel
45797
45798
  };
45798
45799
  };
45800
+ /**
45801
+ * Whether project discovery resolved a React-compatible runtime (React or
45802
+ * Preact, which ships the same hooks + JSX model). This is the same
45803
+ * predicate that grants the `react` capability, so `false` means every
45804
+ * React-runtime rule family was gated off for the project.
45805
+ */
45806
+ const hasReactRuntime = (project) => project.reactVersion !== null || project.preactVersion !== null;
45799
45807
  const toJsonDiff = (diff) => {
45800
45808
  if (!diff) return null;
45801
45809
  return {
@@ -46378,7 +46386,7 @@ const makeNoopConsole = () => ({
46378
46386
  });
46379
46387
  //#endregion
46380
46388
  //#region src/cli/utils/version.ts
46381
- const VERSION = "0.7.2-dev.aa519e5";
46389
+ const VERSION = "0.7.2-dev.cb8f726";
46382
46390
  //#endregion
46383
46391
  //#region src/cli/utils/json-mode.ts
46384
46392
  let context = null;
@@ -46747,13 +46755,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
46747
46755
  * uploads source-map artifacts under, so stack frames symbolicate. Honors the
46748
46756
  * standard `SENTRY_RELEASE` override.
46749
46757
  */
46750
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.2-dev.aa519e5`;
46758
+ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.2-dev.cb8f726`;
46751
46759
  /**
46752
46760
  * Deployment environment shown in Sentry's environment filter. Defaults to
46753
46761
  * `production` for tagged releases and `development` for dev/unbuilt versions,
46754
46762
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
46755
46763
  */
46756
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.2-dev.aa519e5") ? "development" : "production");
46764
+ const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.2-dev.cb8f726") ? "development" : "production");
46757
46765
  /**
46758
46766
  * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
46759
46767
  * (set to `0` to disable tracing) and falls back to
@@ -64928,4 +64936,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
64928
64936
  export {};
64929
64937
 
64930
64938
  //# sourceMappingURL=cli.js.map
64931
- //# debugId=a8ebd975-0f3b-5218-ad38-b9c8095f6668
64939
+ //# debugId=91ba16f6-8b1a-5cd9-a5d2-fc0b6fce2508