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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -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]="10e3e1b0-f291-5306-b5fd-79b158b00159")}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]="7b7e0da8-d676-570d-b0e2-7fc3a4fe85ab")}catch(e){}}();
3
3
  import { r as __toESM$1, t as __commonJSMin$1 } from "./chunk-N93fKeF6.js";
4
4
  import { createRequire } from "node:module";
5
5
  import * as NFS from "node:fs";
@@ -32791,6 +32791,25 @@ var AmbiguousProjectError = class extends Error {
32791
32791
  }
32792
32792
  };
32793
32793
  const isProjectDiscoveryError = (value) => value instanceof ProjectNotFoundError || value instanceof NoReactDependencyError || value instanceof PackageJsonNotFoundError || value instanceof NotADirectoryError || value instanceof AmbiguousProjectError;
32794
+ const isErrnoException = (error) => error instanceof Error && "code" in error;
32795
+ const IGNORABLE_READDIR_ERROR_CODES = new Set([
32796
+ "EACCES",
32797
+ "EPERM",
32798
+ "ENOENT",
32799
+ "ENOTDIR",
32800
+ "EINVAL",
32801
+ "ELOOP",
32802
+ "ENAMETOOLONG"
32803
+ ]);
32804
+ const isIgnorableReaddirError = (error) => isErrnoException(error) && typeof error.code === "string" && IGNORABLE_READDIR_ERROR_CODES.has(error.code);
32805
+ const readDirectoryEntries = (directoryPath) => {
32806
+ try {
32807
+ return NFS.readdirSync(directoryPath, { withFileTypes: true });
32808
+ } catch (error) {
32809
+ if (isIgnorableReaddirError(error)) return [];
32810
+ throw error;
32811
+ }
32812
+ };
32794
32813
  const isFile = (filePath) => {
32795
32814
  try {
32796
32815
  return NFS.statSync(filePath).isFile();
@@ -32798,6 +32817,18 @@ const isFile = (filePath) => {
32798
32817
  return false;
32799
32818
  }
32800
32819
  };
32820
+ const isDirectory = (directoryPath) => {
32821
+ try {
32822
+ return NFS.statSync(directoryPath).isDirectory();
32823
+ } catch {
32824
+ return false;
32825
+ }
32826
+ };
32827
+ const isPlainObject = (value) => {
32828
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
32829
+ const prototype = Object.getPrototypeOf(value);
32830
+ return prototype === null || prototype === Object.prototype;
32831
+ };
32801
32832
  const SOURCE_FILE_PATTERN = /\.(tsx?|jsx?|mts|mjs)$/;
32802
32833
  const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
32803
32834
  const GENERATED_SOURCE_DIRECTORY_PATTERN = /(?:^|\/)__generated__\//;
@@ -32994,25 +33025,6 @@ const isLargeMinifiedFile = (absolutePath, knownSizeBytes) => {
32994
33025
  cachedIsLargeMinifiedByPath.set(absolutePath, result);
32995
33026
  return result;
32996
33027
  };
32997
- const isErrnoException = (error) => error instanceof Error && "code" in error;
32998
- const IGNORABLE_READDIR_ERROR_CODES = new Set([
32999
- "EACCES",
33000
- "EPERM",
33001
- "ENOENT",
33002
- "ENOTDIR",
33003
- "EINVAL",
33004
- "ELOOP",
33005
- "ENAMETOOLONG"
33006
- ]);
33007
- const isIgnorableReaddirError = (error) => isErrnoException(error) && typeof error.code === "string" && IGNORABLE_READDIR_ERROR_CODES.has(error.code);
33008
- const readDirectoryEntries = (directoryPath) => {
33009
- try {
33010
- return NFS.readdirSync(directoryPath, { withFileTypes: true });
33011
- } catch (error) {
33012
- if (isIgnorableReaddirError(error)) return [];
33013
- throw error;
33014
- }
33015
- };
33016
33028
  function* walkSourceTreeFiles(rootDirectory) {
33017
33029
  const stack = [rootDirectory];
33018
33030
  while (stack.length > 0) {
@@ -33067,23 +33079,6 @@ const listSourceFilesViaFilesystem = (rootDirectory) => {
33067
33079
  const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort());
33068
33080
  const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
33069
33081
  const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
33070
- /**
33071
- * The chain of ancestor directories of `startDirectory`, nearest first, up to
33072
- * and including the filesystem root. With `includeStart`, the chain begins at
33073
- * `startDirectory` itself; otherwise at its parent. The one place the "walk up
33074
- * the tree" traversal is expressed, so each caller adds only its own match /
33075
- * stop policy instead of re-deriving the `path.dirname` loop.
33076
- */
33077
- const ancestorDirectories = (startDirectory, { includeStart }) => {
33078
- const directories = [];
33079
- let currentDirectory = includeStart ? startDirectory : Path.dirname(startDirectory);
33080
- while (true) {
33081
- directories.push(currentDirectory);
33082
- const parentDirectory = Path.dirname(currentDirectory);
33083
- if (parentDirectory === currentDirectory) return directories;
33084
- currentDirectory = parentDirectory;
33085
- }
33086
- };
33087
33082
  const cachedPackageJsons = /* @__PURE__ */ new Map();
33088
33083
  const clearPackageJsonCache = () => {
33089
33084
  cachedPackageJsons.clear();
@@ -33117,7 +33112,11 @@ const isMonorepoRoot = (directory) => {
33117
33112
  return Array.isArray(packageJson.workspaces) || Boolean(packageJson.workspaces?.packages);
33118
33113
  };
33119
33114
  const findMonorepoRoot = (startDirectory) => {
33120
- for (const directory of ancestorDirectories(startDirectory, { includeStart: false })) if (isMonorepoRoot(directory)) return directory;
33115
+ let currentDirectory = Path.dirname(startDirectory);
33116
+ while (currentDirectory !== Path.dirname(currentDirectory)) {
33117
+ if (isMonorepoRoot(currentDirectory)) return currentDirectory;
33118
+ currentDirectory = Path.dirname(currentDirectory);
33119
+ }
33121
33120
  return null;
33122
33121
  };
33123
33122
  /**
@@ -33131,6 +33130,146 @@ const findMonorepoRoot = (startDirectory) => {
33131
33130
  * byte-equivalent copy.
33132
33131
  */
33133
33132
  const isProjectBoundary = (directory) => NFS.existsSync(Path.join(directory, ".git")) || isMonorepoRoot(directory);
33133
+ const TSCONFIG_FILENAME = "tsconfig.json";
33134
+ const isRelativeExtendsValue = (extendsValue) => extendsValue.startsWith("./") || extendsValue.startsWith("../") || Path.isAbsolute(extendsValue);
33135
+ const ensureJsonExtension = (filePath) => Path.extname(filePath) === "" ? `${filePath}.json` : filePath;
33136
+ const resolvePackageExtendsPath = (extendsValue, fromConfigDirectory) => {
33137
+ const requireFromConfig = createRequire(Path.join(fromConfigDirectory, "tsconfig.json"));
33138
+ const candidates = [
33139
+ extendsValue,
33140
+ ensureJsonExtension(extendsValue),
33141
+ `${extendsValue.replace(/\/$/, "")}/tsconfig.json`
33142
+ ];
33143
+ for (const candidate of candidates) try {
33144
+ return requireFromConfig.resolve(candidate);
33145
+ } catch {
33146
+ continue;
33147
+ }
33148
+ return null;
33149
+ };
33150
+ const resolveExtendsPath = (extendsValue, fromConfigDirectory) => {
33151
+ if (isRelativeExtendsValue(extendsValue)) return ensureJsonExtension(Path.resolve(fromConfigDirectory, extendsValue));
33152
+ return resolvePackageExtendsPath(extendsValue, fromConfigDirectory);
33153
+ };
33154
+ const normalizeCompilerOptions = (compilerOptions) => {
33155
+ if (!isPlainObject(compilerOptions)) return { hasExplicitLib: false };
33156
+ const target = typeof compilerOptions.target === "string" ? compilerOptions.target : void 0;
33157
+ const hasExplicitLib = Object.hasOwn(compilerOptions, "lib");
33158
+ return {
33159
+ target,
33160
+ lib: Array.isArray(compilerOptions.lib) ? compilerOptions.lib.filter((entry) => typeof entry === "string") : void 0,
33161
+ hasExplicitLib
33162
+ };
33163
+ };
33164
+ const readTsConfig = (filePath) => {
33165
+ let content;
33166
+ try {
33167
+ content = NFS.readFileSync(filePath, "utf-8");
33168
+ } catch {
33169
+ return null;
33170
+ }
33171
+ const parsed = ts.parseConfigFileTextToJson(filePath, content);
33172
+ if (!isPlainObject(parsed.config)) return null;
33173
+ return {
33174
+ extends: typeof parsed.config.extends === "string" ? parsed.config.extends : void 0,
33175
+ referencePaths: normalizeReferencePaths(parsed.config.references),
33176
+ compilerOptions: normalizeCompilerOptions(parsed.config.compilerOptions)
33177
+ };
33178
+ };
33179
+ const normalizeReferencePaths = (references) => {
33180
+ if (!Array.isArray(references)) return [];
33181
+ return references.map((reference) => isPlainObject(reference) && typeof reference.path === "string" ? reference.path : null).filter((referencePath) => referencePath !== null);
33182
+ };
33183
+ const mergeCompilerOptions = (inherited, current) => {
33184
+ const target = current.target ?? inherited?.target;
33185
+ const hasExplicitLib = current.hasExplicitLib || Boolean(inherited?.hasExplicitLib);
33186
+ return {
33187
+ target,
33188
+ lib: current.hasExplicitLib ? current.lib : inherited?.lib,
33189
+ hasExplicitLib
33190
+ };
33191
+ };
33192
+ const readResolvedCompilerOptions = (tsConfigPath, extendsDepth, visitedPaths) => {
33193
+ const realPath = NFS.realpathSync.native(tsConfigPath);
33194
+ if (visitedPaths.has(realPath)) return null;
33195
+ const tsConfig = readTsConfig(realPath);
33196
+ if (!tsConfig) return null;
33197
+ const nextVisitedPaths = new Set(visitedPaths);
33198
+ nextVisitedPaths.add(realPath);
33199
+ if (tsConfig.extends && extendsDepth < 8) {
33200
+ const parentPath = resolveExtendsPath(tsConfig.extends, Path.dirname(realPath));
33201
+ if (parentPath && isFile(parentPath)) return mergeCompilerOptions(readResolvedCompilerOptions(parentPath, extendsDepth + 1, nextVisitedPaths), tsConfig.compilerOptions);
33202
+ }
33203
+ return tsConfig.compilerOptions;
33204
+ };
33205
+ const targetYearIsPreES2023 = (target) => {
33206
+ const year = ES_TARGET_YEAR_BY_NAME[target.toLowerCase()];
33207
+ return year !== void 0 && year < 2023;
33208
+ };
33209
+ const libEntryIncludesES2023Array = (entry) => {
33210
+ const normalizedEntry = entry.toLowerCase();
33211
+ if (normalizedEntry === "esnext" || normalizedEntry === "esnext.array") return true;
33212
+ const esYearMatch = /^es(\d{4})(?:\.(.+))?$/.exec(normalizedEntry);
33213
+ if (!esYearMatch) return false;
33214
+ if (Number(esYearMatch[1]) < 2023) return false;
33215
+ const component = esYearMatch[2];
33216
+ return component === void 0 || component === "array";
33217
+ };
33218
+ const libIncludesES2023 = (lib) => lib.some(libEntryIncludesES2023Array);
33219
+ const compilerOptionsArePreES2023 = (compilerOptions) => {
33220
+ if (compilerOptions.target) return targetYearIsPreES2023(compilerOptions.target);
33221
+ if (compilerOptions.hasExplicitLib) return !libIncludesES2023(compilerOptions.lib ?? []);
33222
+ return false;
33223
+ };
33224
+ const compilerOptionsDeclareTargetOrLib = (compilerOptions) => compilerOptions.hasExplicitLib || compilerOptions.target !== void 0;
33225
+ const detectPreES2023FromConfig = (tsConfigPath, visitedConfigPaths = /* @__PURE__ */ new Set()) => {
33226
+ if (visitedConfigPaths.has(tsConfigPath)) return false;
33227
+ const compilerOptions = readResolvedCompilerOptions(tsConfigPath, 0, /* @__PURE__ */ new Set());
33228
+ if (!compilerOptions) return false;
33229
+ if (!compilerOptionsDeclareTargetOrLib(compilerOptions)) {
33230
+ const tsConfig = readTsConfig(tsConfigPath);
33231
+ if (!tsConfig) return false;
33232
+ const nextVisitedConfigPaths = new Set(visitedConfigPaths);
33233
+ nextVisitedConfigPaths.add(tsConfigPath);
33234
+ const configDirectory = Path.dirname(tsConfigPath);
33235
+ return tsConfig.referencePaths.some((referencePath) => {
33236
+ const resolvedReferencePath = Path.resolve(configDirectory, referencePath);
33237
+ const referencedConfigPath = isFile(resolvedReferencePath) ? resolvedReferencePath : Path.join(resolvedReferencePath, TSCONFIG_FILENAME);
33238
+ return isFile(referencedConfigPath) && detectPreES2023FromConfig(referencedConfigPath, nextVisitedConfigPaths);
33239
+ });
33240
+ }
33241
+ return compilerOptionsArePreES2023(compilerOptions);
33242
+ };
33243
+ const detectPreES2023Target = (directory) => {
33244
+ const tsConfigPath = Path.join(directory, TSCONFIG_FILENAME);
33245
+ if (isFile(tsConfigPath)) return detectPreES2023FromConfig(tsConfigPath);
33246
+ for (const fallbackFilename of FALLBACK_TSCONFIG_FILENAMES) {
33247
+ const fallbackPath = Path.join(directory, fallbackFilename);
33248
+ if (isFile(fallbackPath)) return detectPreES2023FromConfig(fallbackPath);
33249
+ }
33250
+ return false;
33251
+ };
33252
+ const FALLBACK_TSCONFIG_FILENAMES = ["tsconfig.app.json", "tsconfig.build.json"];
33253
+ const FRAMEWORK_PACKAGES = {
33254
+ next: "nextjs",
33255
+ "@tanstack/react-start": "tanstack-start",
33256
+ vite: "vite",
33257
+ "react-scripts": "cra",
33258
+ "@remix-run/react": "remix",
33259
+ gatsby: "gatsby",
33260
+ expo: "expo",
33261
+ "react-native": "react-native"
33262
+ };
33263
+ const detectFramework = (dependencies) => {
33264
+ for (const [packageName, frameworkName] of Object.entries(FRAMEWORK_PACKAGES)) if (dependencies[packageName]) return frameworkName;
33265
+ if (dependencies.preact && !dependencies.react) return "preact";
33266
+ return "unknown";
33267
+ };
33268
+ const MOBILE_FRAMEWORKS = new Set(["expo", "react-native"]);
33269
+ const frameworkMergeRank = (framework) => {
33270
+ if (framework === "unknown") return 3;
33271
+ return MOBILE_FRAMEWORKS.has(framework) ? 2 : 1;
33272
+ };
33134
33273
  const REACT_COMPILER_PACKAGES = new Set([
33135
33274
  "babel-plugin-react-compiler",
33136
33275
  "react-compiler-runtime",
@@ -33167,6 +33306,7 @@ const EXPO_APP_CONFIG_FILENAMES = [
33167
33306
  ];
33168
33307
  const REACT_COMPILER_PACKAGE_REFERENCE_PATTERN = /babel-plugin-react-compiler|react-compiler-runtime|eslint-plugin-react-compiler|["']react-compiler["']/;
33169
33308
  const REACT_COMPILER_ENABLED_FLAG_PATTERN = /["']?reactCompiler["']?\s*:\s*(?:true\b|\{)/;
33309
+ const STATIC_EXPORT_OUTPUT_PATTERN = /(?:^|[^.\w])["']?output["']?\s*:\s*["']export["']/m;
33170
33310
  const hasCompilerPackage = (packageJson) => {
33171
33311
  const allDependencies = {
33172
33312
  ...packageJson.peerDependencies,
@@ -33199,34 +33339,10 @@ const detectReactCompiler = (directory, packageJson) => {
33199
33339
  }
33200
33340
  return false;
33201
33341
  };
33202
- const FRAMEWORK_PACKAGES = {
33203
- next: "nextjs",
33204
- "@tanstack/react-start": "tanstack-start",
33205
- vite: "vite",
33206
- "react-scripts": "cra",
33207
- "@remix-run/react": "remix",
33208
- gatsby: "gatsby",
33209
- expo: "expo",
33210
- "react-native": "react-native"
33211
- };
33212
- const FRAMEWORK_DISPLAY_NAMES = {
33213
- nextjs: "Next.js",
33214
- "tanstack-start": "TanStack Start",
33215
- vite: "Vite",
33216
- cra: "Create React App",
33217
- remix: "Remix",
33218
- gatsby: "Gatsby",
33219
- expo: "Expo",
33220
- "react-native": "React Native",
33221
- preact: "Preact",
33222
- unknown: "React"
33223
- };
33224
- const formatFrameworkName = (framework) => FRAMEWORK_DISPLAY_NAMES[framework];
33225
- const detectFramework = (dependencies) => {
33226
- for (const [packageName, frameworkName] of Object.entries(FRAMEWORK_PACKAGES)) if (dependencies[packageName]) return frameworkName;
33227
- if (dependencies.preact && !dependencies.react) return "preact";
33228
- return "unknown";
33229
- };
33342
+ const detectNextjsStaticExport = (directory) => NEXT_CONFIG_FILENAMES.some((filename) => {
33343
+ const filePath = Path.join(directory, filename);
33344
+ return isFile(filePath) && STATIC_EXPORT_OUTPUT_PATTERN.test(NFS.readFileSync(filePath, "utf-8"));
33345
+ });
33230
33346
  const UNRESOLVABLE_PROTOCOL_VERSION = /^(?:file|git|github|https?|link|patch|portal|workspace|npm):/i;
33231
33347
  const DIST_TAG_VERSION = /^[a-z][a-z0-9._-]*$/i;
33232
33348
  const WILDCARD_VERSION = /^[*xX](?:\.[*xX])*$/;
@@ -33364,10 +33480,76 @@ const isConcreteDependencyVersion = (version) => {
33364
33480
  const normalizedVersion = normalizeDependencyVersion(version);
33365
33481
  return normalizedVersion !== null && /\d/.test(normalizedVersion);
33366
33482
  };
33367
- const isPlainObject = (value) => {
33368
- if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
33369
- const prototype = Object.getPrototypeOf(value);
33370
- return prototype === null || prototype === Object.prototype;
33483
+ const isMajorMinorAtLeast = (detected, required) => {
33484
+ if (detected === null) return true;
33485
+ if (detected.major !== required.major) return detected.major > required.major;
33486
+ return detected.minor >= required.minor;
33487
+ };
33488
+ const parseReactMajor = (reactVersion) => {
33489
+ if (typeof reactVersion !== "string") return null;
33490
+ return getLowestDependencyMajor(reactVersion);
33491
+ };
33492
+ const MAJOR_MINOR_PATTERN = /(\d{1,4})\.(\d{1,4})/;
33493
+ const MAJOR_ONLY_PATTERN = /(\d{1,4})/;
33494
+ const UPPER_BOUND_COMPARATOR_PATTERN = /<=?\s{0,8}\d{1,4}(?:\.\d{1,4}){0,2}(?:-[^\s,|]+)?/g;
33495
+ const parseReactMajorMinor = (reactVersion) => {
33496
+ if (typeof reactVersion !== "string") return null;
33497
+ const trimmed = reactVersion.trim();
33498
+ if (trimmed.length === 0) return null;
33499
+ const lowerBoundsOnly = trimmed.replace(UPPER_BOUND_COMPARATOR_PATTERN, " ").trim();
33500
+ if (lowerBoundsOnly.length === 0) return null;
33501
+ const majorMinorMatch = lowerBoundsOnly.match(MAJOR_MINOR_PATTERN);
33502
+ if (majorMinorMatch) {
33503
+ const major = Number.parseInt(majorMinorMatch[1], 10);
33504
+ const minor = Number.parseInt(majorMinorMatch[2], 10);
33505
+ if (!Number.isFinite(major) || major <= 0) return null;
33506
+ if (!Number.isFinite(minor) || minor < 0) return null;
33507
+ return {
33508
+ major,
33509
+ minor
33510
+ };
33511
+ }
33512
+ const majorOnlyMatch = lowerBoundsOnly.match(MAJOR_ONLY_PATTERN);
33513
+ if (!majorOnlyMatch) return null;
33514
+ const major = Number.parseInt(majorOnlyMatch[1], 10);
33515
+ if (!Number.isFinite(major) || major <= 0) return null;
33516
+ return {
33517
+ major,
33518
+ minor: 0
33519
+ };
33520
+ };
33521
+ const parseLowerBoundVersion = (versionSpec) => import_semver.validRange(versionSpec) !== null ? import_semver.minVersion(versionSpec) : import_semver.coerce(versionSpec);
33522
+ const parseTailwindMajorMinor = (tailwindVersion) => {
33523
+ if (typeof tailwindVersion !== "string") return null;
33524
+ const trimmed = tailwindVersion.trim();
33525
+ if (trimmed.length === 0) return null;
33526
+ const lowerBound = parseLowerBoundVersion(trimmed);
33527
+ if (lowerBound === null || lowerBound.major <= 0) return null;
33528
+ return {
33529
+ major: lowerBound.major,
33530
+ minor: lowerBound.minor
33531
+ };
33532
+ };
33533
+ const hasUpperBoundOnlyPeerRange = (range) => {
33534
+ if (typeof range !== "string") return false;
33535
+ const normalizedRange = normalizeDependencyVersion(range);
33536
+ if (normalizedRange === null) return false;
33537
+ return splitDependencyVersionBranches(normalizedRange).some((branch) => {
33538
+ const normalizedBranch = normalizeDependencyVersion(branch);
33539
+ return normalizedBranch !== null && getBranchLowestMajor(normalizedBranch) === null && hasUpperBoundComparator(normalizedBranch);
33540
+ });
33541
+ };
33542
+ const peerRangeMinMajor = (range) => {
33543
+ if (typeof range !== "string") return null;
33544
+ return getLowestDependencyMajor(range);
33545
+ };
33546
+ const resolveEffectiveReactMajor = (reactVersion, packageJson) => {
33547
+ const installedReactMajor = parseReactMajor(reactVersion);
33548
+ const peerReactRange = packageJson.peerDependencies?.react;
33549
+ if (typeof peerReactRange !== "string") return installedReactMajor;
33550
+ const peerFloor = peerRangeMinMajor(peerReactRange);
33551
+ if (peerFloor === null) return hasUpperBoundOnlyPeerRange(peerReactRange) ? null : installedReactMajor;
33552
+ return installedReactMajor !== null ? Math.min(installedReactMajor, peerFloor) : peerFloor;
33371
33553
  };
33372
33554
  const isCatalogReference = (version) => version.startsWith("catalog:");
33373
33555
  const extractCatalogName = (version) => {
@@ -33509,6 +33691,16 @@ const resolveCatalogVersion = (packageJson, packageName, rootDirectory, explicit
33509
33691
  }
33510
33692
  return null;
33511
33693
  };
33694
+ const REACT_SECTIONS = [
33695
+ "dependencies",
33696
+ "peerDependencies",
33697
+ "devDependencies"
33698
+ ];
33699
+ const TAILWIND_ZOD_SECTIONS = [
33700
+ "dependencies",
33701
+ "devDependencies",
33702
+ "peerDependencies"
33703
+ ];
33512
33704
  const EMPTY_DEPENDENCY_INFO = {
33513
33705
  reactVersion: null,
33514
33706
  tailwindVersion: null,
@@ -33549,6 +33741,10 @@ const extractDependencyInfo = (packageJson) => {
33549
33741
  framework: detectFramework(allDependencies)
33550
33742
  };
33551
33743
  };
33744
+ const getDependencySpec = (packageJson, packageName) => {
33745
+ const spec = packageJson.dependencies?.[packageName] ?? packageJson.devDependencies?.[packageName] ?? packageJson.peerDependencies?.[packageName] ?? packageJson.optionalDependencies?.[packageName];
33746
+ return typeof spec === "string" ? spec : null;
33747
+ };
33552
33748
  const getDependencyDeclaration = ({ packageJson, packageName, sections }) => {
33553
33749
  for (const section of sections) {
33554
33750
  const version = packageJson[section]?.[packageName];
@@ -33565,31 +33761,141 @@ const getDependencyDeclaration = ({ packageJson, packageName, sections }) => {
33565
33761
  version: null
33566
33762
  };
33567
33763
  };
33568
- const isDirectory = (directoryPath) => {
33569
- try {
33570
- return NFS.statSync(directoryPath).isDirectory();
33571
- } catch {
33572
- return false;
33764
+ const REACT_DEPENDENCY_NAMES = new Set([
33765
+ "react",
33766
+ "react-native",
33767
+ "next",
33768
+ "preact"
33769
+ ]);
33770
+ const hasReactDependency = (packageJson) => {
33771
+ const allDependencies = {
33772
+ ...packageJson.peerDependencies,
33773
+ ...packageJson.dependencies,
33774
+ ...packageJson.devDependencies
33775
+ };
33776
+ return Object.keys(allDependencies).some((packageName) => REACT_DEPENDENCY_NAMES.has(packageName));
33777
+ };
33778
+ const getPreactVersion = (packageJson) => {
33779
+ return {
33780
+ ...packageJson.peerDependencies,
33781
+ ...packageJson.dependencies,
33782
+ ...packageJson.devDependencies
33783
+ }.preact ?? null;
33784
+ };
33785
+ const TANSTACK_QUERY_PACKAGES = new Set([
33786
+ "@tanstack/react-query",
33787
+ "@tanstack/query-core",
33788
+ "react-query"
33789
+ ]);
33790
+ const hasTanStackQuery = (packageJson) => {
33791
+ const allDependencies = {
33792
+ ...packageJson.peerDependencies,
33793
+ ...packageJson.dependencies,
33794
+ ...packageJson.devDependencies
33795
+ };
33796
+ return Object.keys(allDependencies).some((packageName) => TANSTACK_QUERY_PACKAGES.has(packageName));
33797
+ };
33798
+ const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, version }) => {
33799
+ if (version === null || !isCatalogReference(version)) return version;
33800
+ const catalogName = extractCatalogName(version);
33801
+ const resolvedLocalVersion = resolveCatalogVersion(rootPackageJson, packageName, rootDirectory, catalogName);
33802
+ if (resolvedLocalVersion) return resolvedLocalVersion;
33803
+ const monorepoRoot = findMonorepoRoot(rootDirectory);
33804
+ if (!monorepoRoot) return version;
33805
+ const monorepoPackageJsonPath = Path.join(monorepoRoot, "package.json");
33806
+ if (!isFile(monorepoPackageJsonPath)) return version;
33807
+ return resolveCatalogVersion(readPackageJson(monorepoPackageJsonPath), packageName, monorepoRoot, catalogName) ?? version;
33808
+ };
33809
+ /**
33810
+ * The chain of ancestor directories of `startDirectory`, nearest first, up to
33811
+ * and including the filesystem root. With `includeStart`, the chain begins at
33812
+ * `startDirectory` itself; otherwise at its parent. The one place the "walk up
33813
+ * the tree" traversal is expressed, so each caller adds only its own match /
33814
+ * stop policy instead of re-deriving the `path.dirname` loop.
33815
+ */
33816
+ const ancestorDirectories = (startDirectory, { includeStart }) => {
33817
+ const directories = [];
33818
+ let currentDirectory = includeStart ? startDirectory : Path.dirname(startDirectory);
33819
+ while (true) {
33820
+ directories.push(currentDirectory);
33821
+ const parentDirectory = Path.dirname(currentDirectory);
33822
+ if (parentDirectory === currentDirectory) return directories;
33823
+ currentDirectory = parentDirectory;
33573
33824
  }
33574
33825
  };
33575
- const NX_PROJECT_DISCOVERY_DIRS = [
33576
- "apps",
33577
- "libs",
33578
- "packages"
33579
- ];
33580
- const getNxWorkspaceDirectories = (rootDirectory) => {
33581
- if (!isFile(Path.join(rootDirectory, "nx.json"))) return [];
33582
- const collected = [];
33583
- for (const candidate of NX_PROJECT_DISCOVERY_DIRS) {
33584
- const candidatePath = Path.join(rootDirectory, candidate);
33585
- if (!isDirectory(candidatePath)) continue;
33586
- for (const entry of readDirectoryEntries(candidatePath)) {
33587
- if (!entry.isDirectory()) continue;
33588
- const projectDirectory = Path.join(candidatePath, entry.name);
33589
- if (isFile(Path.join(projectDirectory, "project.json")) || isFile(Path.join(projectDirectory, "package.json"))) collected.push(`${candidate}/${entry.name}`);
33590
- }
33826
+ /**
33827
+ * Walk up from `startDirectory` to the nearest ancestor that owns a
33828
+ * `package.json`, stopping at (and including) the enclosing project boundary
33829
+ * — the working tree's git root or a monorepo root. Lets a scan of a package
33830
+ * subfolder that has no `package.json` of its own adopt the nearest enclosing
33831
+ * package as its project root, so it inherits that package's dependency +
33832
+ * framework detection instead of synthesizing an empty (React-blind) project.
33833
+ *
33834
+ * Generalizes the older monorepo-root-only lookup: the nearest ancestor is the
33835
+ * most specific owning package (a leaf workspace, not just the repo root), so
33836
+ * a subfolder of one workspace no longer borrows a sibling's capabilities.
33837
+ *
33838
+ * Returns `null` when no ancestor `package.json` exists inside the project
33839
+ * boundary (a loose tree of files, or a subfolder of a package-less repo).
33840
+ */
33841
+ const findNearestAncestorPackageJson = (startDirectory) => {
33842
+ if (isProjectBoundary(startDirectory)) return null;
33843
+ for (const directory of ancestorDirectories(startDirectory, { includeStart: false })) {
33844
+ if (isFile(Path.join(directory, "package.json"))) return directory;
33845
+ if (isProjectBoundary(directory)) return null;
33591
33846
  }
33592
- return collected;
33847
+ return null;
33848
+ };
33849
+ const NAMES = new Set([
33850
+ "react-native",
33851
+ "react-native-tvos",
33852
+ ...new Set([
33853
+ "expo",
33854
+ "expo-router",
33855
+ "@expo/cli",
33856
+ "@expo/metro-config",
33857
+ "@expo/metro-runtime"
33858
+ ]),
33859
+ "react-native-windows",
33860
+ "react-native-macos"
33861
+ ]);
33862
+ const PREFIXES = ["@react-native/", "@react-native-"];
33863
+ const isReactNativeDependencyName = (dependencyName) => {
33864
+ if (NAMES.has(dependencyName)) return true;
33865
+ for (const prefix of PREFIXES) if (dependencyName.startsWith(prefix)) return true;
33866
+ return false;
33867
+ };
33868
+ const containsAnyReactNativeDependency = (section) => {
33869
+ if (!section) return false;
33870
+ for (const dependencyName of Object.keys(section)) if (isReactNativeDependencyName(dependencyName)) return true;
33871
+ return false;
33872
+ };
33873
+ const isPackageJsonReactNativeAware = (packageJson) => {
33874
+ if (typeof packageJson["react-native"] === "string") return true;
33875
+ if (containsAnyReactNativeDependency(packageJson.dependencies)) return true;
33876
+ if (containsAnyReactNativeDependency(packageJson.devDependencies)) return true;
33877
+ if (containsAnyReactNativeDependency(packageJson.peerDependencies)) return true;
33878
+ if (containsAnyReactNativeDependency(packageJson.optionalDependencies)) return true;
33879
+ return false;
33880
+ };
33881
+ const REANIMATED_DEPENDENCY_NAME$1 = "react-native-reanimated";
33882
+ const isPackageJsonReanimatedAware = (packageJson) => {
33883
+ const allDependencies = {
33884
+ ...packageJson.peerDependencies,
33885
+ ...packageJson.dependencies,
33886
+ ...packageJson.devDependencies,
33887
+ ...packageJson.optionalDependencies
33888
+ };
33889
+ return Object.hasOwn(allDependencies, REANIMATED_DEPENDENCY_NAME$1);
33890
+ };
33891
+ const getWorkspacePatterns = (rootDirectory, packageJson) => {
33892
+ const pnpmPatterns = parsePnpmWorkspacePatterns(rootDirectory);
33893
+ if (pnpmPatterns.length > 0) return pnpmPatterns;
33894
+ if (Array.isArray(packageJson.workspaces)) return packageJson.workspaces;
33895
+ if (packageJson.workspaces?.packages) return packageJson.workspaces.packages;
33896
+ const nxPatterns = getNxWorkspaceDirectories(rootDirectory);
33897
+ if (nxPatterns.length > 0) return nxPatterns;
33898
+ return [];
33593
33899
  };
33594
33900
  const parsePnpmWorkspacePatterns = (rootDirectory) => {
33595
33901
  const workspacePath = Path.join(rootDirectory, "pnpm-workspace.yaml");
@@ -33608,18 +33914,24 @@ const parsePnpmWorkspacePatterns = (rootDirectory) => {
33608
33914
  }
33609
33915
  return patterns;
33610
33916
  };
33611
- const getWorkspacePatterns = (rootDirectory, packageJson) => {
33612
- const pnpmPatterns = parsePnpmWorkspacePatterns(rootDirectory);
33613
- if (pnpmPatterns.length > 0) return pnpmPatterns;
33614
- if (Array.isArray(packageJson.workspaces)) return packageJson.workspaces;
33615
- if (packageJson.workspaces?.packages) return packageJson.workspaces.packages;
33616
- const nxPatterns = getNxWorkspaceDirectories(rootDirectory);
33617
- if (nxPatterns.length > 0) return nxPatterns;
33618
- return [];
33619
- };
33620
- const parseReactMajor = (reactVersion) => {
33621
- if (typeof reactVersion !== "string") return null;
33622
- return getLowestDependencyMajor(reactVersion);
33917
+ const NX_PROJECT_DISCOVERY_DIRS = [
33918
+ "apps",
33919
+ "libs",
33920
+ "packages"
33921
+ ];
33922
+ const getNxWorkspaceDirectories = (rootDirectory) => {
33923
+ if (!isFile(Path.join(rootDirectory, "nx.json"))) return [];
33924
+ const collected = [];
33925
+ for (const candidate of NX_PROJECT_DISCOVERY_DIRS) {
33926
+ const candidatePath = Path.join(rootDirectory, candidate);
33927
+ if (!isDirectory(candidatePath)) continue;
33928
+ for (const entry of readDirectoryEntries(candidatePath)) {
33929
+ if (!entry.isDirectory()) continue;
33930
+ const projectDirectory = Path.join(candidatePath, entry.name);
33931
+ if (isFile(Path.join(projectDirectory, "project.json")) || isFile(Path.join(projectDirectory, "package.json"))) collected.push(`${candidate}/${entry.name}`);
33932
+ }
33933
+ }
33934
+ return collected;
33623
33935
  };
33624
33936
  const resolveWorkspaceDirectories = (rootDirectory, pattern) => {
33625
33937
  const cleanPattern = pattern.replace(/["']/g, "").replace(/\/\*\*$/, "/*");
@@ -33639,6 +33951,38 @@ const resolveWorkspaceDirectories = (rootDirectory, pattern) => {
33639
33951
  }
33640
33952
  return resolved;
33641
33953
  };
33954
+ const listWorkspacePackages = (rootDirectory) => {
33955
+ const packageJsonPath = Path.join(rootDirectory, "package.json");
33956
+ if (!isFile(packageJsonPath)) return [];
33957
+ const packageJson = readPackageJson(packageJsonPath);
33958
+ const patterns = getWorkspacePatterns(rootDirectory, packageJson);
33959
+ if (patterns.length === 0) return [];
33960
+ const packages = [];
33961
+ const seenDirectories = /* @__PURE__ */ new Set();
33962
+ const pushIfNew = (workspacePackage) => {
33963
+ if (seenDirectories.has(workspacePackage.directory)) return;
33964
+ seenDirectories.add(workspacePackage.directory);
33965
+ packages.push(workspacePackage);
33966
+ };
33967
+ if (hasReactDependency(packageJson)) pushIfNew({
33968
+ name: packageJson.name ?? Path.basename(rootDirectory),
33969
+ directory: rootDirectory
33970
+ });
33971
+ for (const pattern of patterns) {
33972
+ const directories = resolveWorkspaceDirectories(rootDirectory, pattern);
33973
+ for (const workspaceDirectory of directories) {
33974
+ const workspacePackageJson = readPackageJson(Path.join(workspaceDirectory, "package.json"));
33975
+ if (!hasReactDependency(workspacePackageJson)) continue;
33976
+ pushIfNew({
33977
+ name: workspacePackageJson.name ?? Path.basename(workspaceDirectory),
33978
+ directory: workspaceDirectory
33979
+ });
33980
+ }
33981
+ }
33982
+ return packages;
33983
+ };
33984
+ const REANIMATED_DEPENDENCY_NAME = "react-native-reanimated";
33985
+ const SHOPIFY_FLASH_LIST_PACKAGE_NAME = "@shopify/flash-list";
33642
33986
  const resolveWorkspaceDependencyVersion = ({ concreteVersion, packageName, rootDirectory, rootPackageJson, sections, workspaceDirectory, workspacePackageJson }) => {
33643
33987
  const dependencyDeclaration = getDependencyDeclaration({
33644
33988
  packageJson: workspacePackageJson,
@@ -33656,24 +34000,76 @@ const shouldReplaceReactVersion = (currentVersion, nextVersion) => {
33656
34000
  if (nextMajor === null) return false;
33657
34001
  return nextMajor < currentMajor;
33658
34002
  };
33659
- const findReactInWorkspaces = (rootDirectory, packageJson) => {
33660
- const patterns = getWorkspacePatterns(rootDirectory, packageJson);
33661
- const result = { ...EMPTY_DEPENDENCY_INFO };
33662
- for (const pattern of patterns) {
33663
- const directories = resolveWorkspaceDirectories(rootDirectory, pattern);
34003
+ const evaluateManifestFacts = (facts, packageJson, directory) => {
34004
+ if (facts.expo.version === null) {
34005
+ const spec = getDependencySpec(packageJson, "expo");
34006
+ if (spec !== null) facts.expo = {
34007
+ version: spec,
34008
+ sourceDirectory: directory
34009
+ };
34010
+ }
34011
+ if (facts.next.version === null) {
34012
+ const spec = getDependencySpec(packageJson, "next");
34013
+ if (spec !== null) facts.next = {
34014
+ version: spec,
34015
+ sourceDirectory: directory
34016
+ };
34017
+ }
34018
+ if (facts.shopifyFlashList.version === null) {
34019
+ const spec = getDependencySpec(packageJson, SHOPIFY_FLASH_LIST_PACKAGE_NAME);
34020
+ if (spec !== null) facts.shopifyFlashList = {
34021
+ version: spec,
34022
+ sourceDirectory: directory
34023
+ };
34024
+ }
34025
+ if (facts.reanimatedVersion === null) {
34026
+ const spec = getDependencySpec(packageJson, REANIMATED_DEPENDENCY_NAME);
34027
+ if (spec !== null) facts.reanimatedVersion = spec;
34028
+ }
34029
+ facts.hasReactNativeAwarePackage = facts.hasReactNativeAwarePackage || isPackageJsonReactNativeAware(packageJson);
34030
+ facts.hasReanimatedAwarePackage = facts.hasReanimatedAwarePackage || isPackageJsonReanimatedAware(packageJson);
34031
+ };
34032
+ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGroup }) => {
34033
+ const facts = {
34034
+ reactVersion: null,
34035
+ tailwindVersion: null,
34036
+ zodVersion: null,
34037
+ framework: "unknown",
34038
+ expo: {
34039
+ version: null,
34040
+ sourceDirectory: null
34041
+ },
34042
+ next: {
34043
+ version: null,
34044
+ sourceDirectory: null
34045
+ },
34046
+ shopifyFlashList: {
34047
+ version: null,
34048
+ sourceDirectory: null
34049
+ },
34050
+ hasReactNativeAwarePackage: false,
34051
+ hasReanimatedAwarePackage: false,
34052
+ reanimatedVersion: null
34053
+ };
34054
+ evaluateManifestFacts(facts, rootPackageJson, rootDirectory);
34055
+ let isReactGroupSettled = !collectReactGroup;
34056
+ const visitedDirectories = /* @__PURE__ */ new Set();
34057
+ for (const pattern of getWorkspacePatterns(rootDirectory, rootPackageJson)) {
34058
+ const directories = [...resolveWorkspaceDirectories(rootDirectory, pattern)].sort();
33664
34059
  for (const workspaceDirectory of directories) {
34060
+ if (visitedDirectories.has(workspaceDirectory)) continue;
34061
+ visitedDirectories.add(workspaceDirectory);
33665
34062
  const workspacePackageJson = readPackageJson(Path.join(workspaceDirectory, "package.json"));
34063
+ evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory);
33666
34064
  const info = extractDependencyInfo(workspacePackageJson);
34065
+ if (info.framework !== "unknown" && frameworkMergeRank(info.framework) < frameworkMergeRank(facts.framework)) facts.framework = info.framework;
34066
+ if (isReactGroupSettled) continue;
33667
34067
  const reactVersion = resolveWorkspaceDependencyVersion({
33668
34068
  concreteVersion: info.reactVersion,
33669
34069
  packageName: "react",
33670
34070
  rootDirectory,
33671
- rootPackageJson: packageJson,
33672
- sections: [
33673
- "dependencies",
33674
- "peerDependencies",
33675
- "devDependencies"
33676
- ],
34071
+ rootPackageJson,
34072
+ sections: REACT_SECTIONS,
33677
34073
  workspaceDirectory,
33678
34074
  workspacePackageJson
33679
34075
  });
@@ -33681,12 +34077,8 @@ const findReactInWorkspaces = (rootDirectory, packageJson) => {
33681
34077
  concreteVersion: info.tailwindVersion,
33682
34078
  packageName: "tailwindcss",
33683
34079
  rootDirectory,
33684
- rootPackageJson: packageJson,
33685
- sections: [
33686
- "dependencies",
33687
- "devDependencies",
33688
- "peerDependencies"
33689
- ],
34080
+ rootPackageJson,
34081
+ sections: TAILWIND_ZOD_SECTIONS,
33690
34082
  workspaceDirectory,
33691
34083
  workspacePackageJson
33692
34084
  });
@@ -33694,24 +34086,19 @@ const findReactInWorkspaces = (rootDirectory, packageJson) => {
33694
34086
  concreteVersion: info.zodVersion,
33695
34087
  packageName: "zod",
33696
34088
  rootDirectory,
33697
- rootPackageJson: packageJson,
33698
- sections: [
33699
- "dependencies",
33700
- "devDependencies",
33701
- "peerDependencies"
33702
- ],
34089
+ rootPackageJson,
34090
+ sections: TAILWIND_ZOD_SECTIONS,
33703
34091
  workspaceDirectory,
33704
34092
  workspacePackageJson
33705
34093
  });
33706
- if (reactVersion && shouldReplaceReactVersion(result.reactVersion, reactVersion)) result.reactVersion = reactVersion;
33707
- if (tailwindVersion && !result.tailwindVersion) result.tailwindVersion = tailwindVersion;
33708
- if (zodVersion && !result.zodVersion) result.zodVersion = zodVersion;
33709
- if (info.framework !== "unknown" && result.framework === "unknown") result.framework = info.framework;
33710
- const resultReactMajor = parseReactMajor(result.reactVersion);
33711
- if (result.reactVersion && result.tailwindVersion && result.framework !== "unknown" && resultReactMajor !== null && resultReactMajor <= 17) return result;
34094
+ if (reactVersion && shouldReplaceReactVersion(facts.reactVersion, reactVersion)) facts.reactVersion = reactVersion;
34095
+ if (tailwindVersion && !facts.tailwindVersion) facts.tailwindVersion = tailwindVersion;
34096
+ if (zodVersion && !facts.zodVersion) facts.zodVersion = zodVersion;
34097
+ const settledReactMajor = parseReactMajor(facts.reactVersion);
34098
+ isReactGroupSettled = Boolean(facts.reactVersion && facts.tailwindVersion && facts.framework !== "unknown" && settledReactMajor !== null && settledReactMajor <= 17);
33712
34099
  }
33713
34100
  }
33714
- return result;
34101
+ return facts;
33715
34102
  };
33716
34103
  const findDependencyInfoFromMonorepoRoot = (directory) => {
33717
34104
  const monorepoRoot = findMonorepoRoot(directory);
@@ -33725,29 +34112,17 @@ const findDependencyInfoFromMonorepoRoot = (directory) => {
33725
34112
  const leafReactDeclaration = leafPackageJson ? getDependencyDeclaration({
33726
34113
  packageJson: leafPackageJson,
33727
34114
  packageName: "react",
33728
- sections: [
33729
- "dependencies",
33730
- "peerDependencies",
33731
- "devDependencies"
33732
- ]
34115
+ sections: REACT_SECTIONS
33733
34116
  }) : null;
33734
34117
  const leafTailwindDeclaration = leafPackageJson ? getDependencyDeclaration({
33735
34118
  packageJson: leafPackageJson,
33736
34119
  packageName: "tailwindcss",
33737
- sections: [
33738
- "dependencies",
33739
- "devDependencies",
33740
- "peerDependencies"
33741
- ]
34120
+ sections: TAILWIND_ZOD_SECTIONS
33742
34121
  }) : null;
33743
34122
  const leafZodDeclaration = leafPackageJson ? getDependencyDeclaration({
33744
34123
  packageJson: leafPackageJson,
33745
34124
  packageName: "zod",
33746
- sections: [
33747
- "dependencies",
33748
- "devDependencies",
33749
- "peerDependencies"
33750
- ]
34125
+ sections: TAILWIND_ZOD_SECTIONS
33751
34126
  }) : null;
33752
34127
  const shouldUseReactFallback = !leafReactDeclaration?.hasDeclaration;
33753
34128
  const shouldUseTailwindFallback = leafTailwindDeclaration?.hasDeclaration ?? true;
@@ -33755,38 +34130,15 @@ const findDependencyInfoFromMonorepoRoot = (directory) => {
33755
34130
  const reactCatalogVersion = shouldUseReactFallback ? resolveCatalogVersion(rootPackageJson, "react", monorepoRoot, leafReactDeclaration?.catalogReference) : null;
33756
34131
  const tailwindCatalogVersion = shouldUseTailwindFallback ? resolveCatalogVersion(rootPackageJson, "tailwindcss", monorepoRoot, leafTailwindDeclaration?.catalogReference) : null;
33757
34132
  const zodCatalogVersion = shouldUseZodFallback ? resolveCatalogVersion(rootPackageJson, "zod", monorepoRoot, leafZodDeclaration?.catalogReference) : null;
33758
- const workspaceInfo = findReactInWorkspaces(monorepoRoot, rootPackageJson);
34133
+ const workspaceFacts = collectWorkspaceFacts(monorepoRoot, rootPackageJson, { collectReactGroup: true });
33759
34134
  return {
33760
- reactVersion: shouldUseReactFallback ? reactCatalogVersion ?? rootInfo.reactVersion ?? workspaceInfo.reactVersion : rootInfo.reactVersion ?? workspaceInfo.reactVersion,
33761
- tailwindVersion: shouldUseTailwindFallback ? tailwindCatalogVersion ?? rootInfo.tailwindVersion ?? workspaceInfo.tailwindVersion : null,
33762
- zodVersion: shouldUseZodFallback ? zodCatalogVersion ?? rootInfo.zodVersion ?? workspaceInfo.zodVersion : null,
33763
- framework: rootInfo.framework !== "unknown" ? rootInfo.framework : workspaceInfo.framework
34135
+ reactVersion: shouldUseReactFallback ? reactCatalogVersion ?? rootInfo.reactVersion ?? workspaceFacts.reactVersion : rootInfo.reactVersion ?? workspaceFacts.reactVersion,
34136
+ tailwindVersion: shouldUseTailwindFallback ? tailwindCatalogVersion ?? rootInfo.tailwindVersion ?? workspaceFacts.tailwindVersion : null,
34137
+ zodVersion: shouldUseZodFallback ? zodCatalogVersion ?? rootInfo.zodVersion ?? workspaceFacts.zodVersion : null,
34138
+ framework: rootInfo.framework !== "unknown" ? rootInfo.framework : workspaceFacts.framework
33764
34139
  };
33765
34140
  };
33766
34141
  /**
33767
- * Walk up from `startDirectory` to the nearest ancestor that owns a
33768
- * `package.json`, stopping at (and including) the enclosing project boundary
33769
- * — the working tree's git root or a monorepo root. Lets a scan of a package
33770
- * subfolder that has no `package.json` of its own adopt the nearest enclosing
33771
- * package as its project root, so it inherits that package's dependency +
33772
- * framework detection instead of synthesizing an empty (React-blind) project.
33773
- *
33774
- * Generalizes the older monorepo-root-only lookup: the nearest ancestor is the
33775
- * most specific owning package (a leaf workspace, not just the repo root), so
33776
- * a subfolder of one workspace no longer borrows a sibling's capabilities.
33777
- *
33778
- * Returns `null` when no ancestor `package.json` exists inside the project
33779
- * boundary (a loose tree of files, or a subfolder of a package-less repo).
33780
- */
33781
- const findNearestAncestorPackageJson = (startDirectory) => {
33782
- if (isProjectBoundary(startDirectory)) return null;
33783
- for (const directory of ancestorDirectories(startDirectory, { includeStart: false })) {
33784
- if (isFile(Path.join(directory, "package.json"))) return directory;
33785
- if (isProjectBoundary(directory)) return null;
33786
- }
33787
- return null;
33788
- };
33789
- /**
33790
34142
  * Last-resort React detection: locate React the way Node's `node_modules`
33791
34143
  * resolution would from `directory` — check each `node_modules/react` up the
33792
34144
  * tree — but bounded to the enclosing repo so a globally installed React can't
@@ -33831,295 +34183,6 @@ const resolveInstalledReactVersion = (directory) => {
33831
34183
  }
33832
34184
  return null;
33833
34185
  };
33834
- const findInWorkspacePackageJsons = (rootDirectory, rootPackageJson, select) => {
33835
- const rootValue = select(rootPackageJson);
33836
- if (rootValue !== null) return rootValue;
33837
- const patterns = getWorkspacePatterns(rootDirectory, rootPackageJson);
33838
- if (patterns.length === 0) return null;
33839
- const visitedDirectories = /* @__PURE__ */ new Set();
33840
- for (const pattern of patterns) {
33841
- const directories = [...resolveWorkspaceDirectories(rootDirectory, pattern)].sort();
33842
- for (const workspaceDirectory of directories) {
33843
- if (visitedDirectories.has(workspaceDirectory)) continue;
33844
- visitedDirectories.add(workspaceDirectory);
33845
- const value = select(readPackageJson(Path.join(workspaceDirectory, "package.json")));
33846
- if (value !== null) return value;
33847
- }
33848
- }
33849
- return null;
33850
- };
33851
- const someWorkspacePackageJson = (rootDirectory, rootPackageJson, predicate) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => predicate(packageJson) ? true : null) !== null;
33852
- const NAMES = new Set([
33853
- "react-native",
33854
- "react-native-tvos",
33855
- ...new Set([
33856
- "expo",
33857
- "expo-router",
33858
- "@expo/cli",
33859
- "@expo/metro-config",
33860
- "@expo/metro-runtime"
33861
- ]),
33862
- "react-native-windows",
33863
- "react-native-macos"
33864
- ]);
33865
- const PREFIXES = ["@react-native/", "@react-native-"];
33866
- const isReactNativeDependencyName = (dependencyName) => {
33867
- if (NAMES.has(dependencyName)) return true;
33868
- for (const prefix of PREFIXES) if (dependencyName.startsWith(prefix)) return true;
33869
- return false;
33870
- };
33871
- const containsAnyReactNativeDependency = (section) => {
33872
- if (!section) return false;
33873
- for (const dependencyName of Object.keys(section)) if (isReactNativeDependencyName(dependencyName)) return true;
33874
- return false;
33875
- };
33876
- const isPackageJsonReactNativeAware = (packageJson) => {
33877
- if (typeof packageJson["react-native"] === "string") return true;
33878
- if (containsAnyReactNativeDependency(packageJson.dependencies)) return true;
33879
- if (containsAnyReactNativeDependency(packageJson.devDependencies)) return true;
33880
- if (containsAnyReactNativeDependency(packageJson.peerDependencies)) return true;
33881
- if (containsAnyReactNativeDependency(packageJson.optionalDependencies)) return true;
33882
- return false;
33883
- };
33884
- const hasReactNativeWorkspaceAnywhere = (rootDirectory, rootPackageJson) => someWorkspacePackageJson(rootDirectory, rootPackageJson, isPackageJsonReactNativeAware);
33885
- const getDependencySpec = (packageJson, packageName) => {
33886
- const spec = packageJson.dependencies?.[packageName] ?? packageJson.devDependencies?.[packageName] ?? packageJson.peerDependencies?.[packageName] ?? packageJson.optionalDependencies?.[packageName];
33887
- return typeof spec === "string" ? spec : null;
33888
- };
33889
- const findExpoVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, "expo"));
33890
- const SHOPIFY_FLASH_LIST_PACKAGE_NAME = "@shopify/flash-list";
33891
- const findShopifyFlashListVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, SHOPIFY_FLASH_LIST_PACKAGE_NAME));
33892
- const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, version }) => {
33893
- if (version === null || !isCatalogReference(version)) return version;
33894
- const catalogName = extractCatalogName(version);
33895
- const resolvedLocalVersion = resolveCatalogVersion(rootPackageJson, packageName, rootDirectory, catalogName);
33896
- if (resolvedLocalVersion) return resolvedLocalVersion;
33897
- const monorepoRoot = findMonorepoRoot(rootDirectory);
33898
- if (!monorepoRoot) return version;
33899
- const monorepoPackageJsonPath = path.join(monorepoRoot, "package.json");
33900
- if (!isFile(monorepoPackageJsonPath)) return version;
33901
- return resolveCatalogVersion(readPackageJson(monorepoPackageJsonPath), packageName, monorepoRoot, catalogName) ?? version;
33902
- };
33903
- const findNextjsVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, "next"));
33904
- const getPreactVersion = (packageJson) => {
33905
- return {
33906
- ...packageJson.peerDependencies,
33907
- ...packageJson.dependencies,
33908
- ...packageJson.devDependencies
33909
- }.preact ?? null;
33910
- };
33911
- const TANSTACK_QUERY_PACKAGES = new Set([
33912
- "@tanstack/react-query",
33913
- "@tanstack/query-core",
33914
- "react-query"
33915
- ]);
33916
- const hasTanStackQuery = (packageJson) => {
33917
- const allDependencies = {
33918
- ...packageJson.peerDependencies,
33919
- ...packageJson.dependencies,
33920
- ...packageJson.devDependencies
33921
- };
33922
- return Object.keys(allDependencies).some((packageName) => TANSTACK_QUERY_PACKAGES.has(packageName));
33923
- };
33924
- const REANIMATED_DEPENDENCY_NAME = "react-native-reanimated";
33925
- const isPackageJsonReanimatedAware = (packageJson) => {
33926
- const allDependencies = {
33927
- ...packageJson.peerDependencies,
33928
- ...packageJson.dependencies,
33929
- ...packageJson.devDependencies,
33930
- ...packageJson.optionalDependencies
33931
- };
33932
- return Object.hasOwn(allDependencies, REANIMATED_DEPENDENCY_NAME);
33933
- };
33934
- const parseZodMajor = (zodVersion) => {
33935
- if (typeof zodVersion !== "string") return null;
33936
- return getLowestDependencyMajor(zodVersion);
33937
- };
33938
- const hasUpperBoundOnlyPeerRange = (range) => {
33939
- if (typeof range !== "string") return false;
33940
- const normalizedRange = normalizeDependencyVersion(range);
33941
- if (normalizedRange === null) return false;
33942
- return splitDependencyVersionBranches(normalizedRange).some((branch) => {
33943
- const normalizedBranch = normalizeDependencyVersion(branch);
33944
- return normalizedBranch !== null && getBranchLowestMajor(normalizedBranch) === null && hasUpperBoundComparator(normalizedBranch);
33945
- });
33946
- };
33947
- const peerRangeMinMajor = (range) => {
33948
- if (typeof range !== "string") return null;
33949
- return getLowestDependencyMajor(range);
33950
- };
33951
- const resolveEffectiveReactMajor = (reactVersion, packageJson) => {
33952
- const installedReactMajor = parseReactMajor(reactVersion);
33953
- const peerReactRange = packageJson.peerDependencies?.react;
33954
- if (typeof peerReactRange !== "string") return installedReactMajor;
33955
- const peerFloor = peerRangeMinMajor(peerReactRange);
33956
- if (peerFloor === null) return hasUpperBoundOnlyPeerRange(peerReactRange) ? null : installedReactMajor;
33957
- return installedReactMajor !== null ? Math.min(installedReactMajor, peerFloor) : peerFloor;
33958
- };
33959
- const TSCONFIG_FILENAME = "tsconfig.json";
33960
- const isRelativeExtendsValue = (extendsValue) => extendsValue.startsWith("./") || extendsValue.startsWith("../") || Path.isAbsolute(extendsValue);
33961
- const ensureJsonExtension = (filePath) => Path.extname(filePath) === "" ? `${filePath}.json` : filePath;
33962
- const resolvePackageExtendsPath = (extendsValue, fromConfigDirectory) => {
33963
- const requireFromConfig = createRequire(Path.join(fromConfigDirectory, "tsconfig.json"));
33964
- const candidates = [
33965
- extendsValue,
33966
- ensureJsonExtension(extendsValue),
33967
- `${extendsValue.replace(/\/$/, "")}/tsconfig.json`
33968
- ];
33969
- for (const candidate of candidates) try {
33970
- return requireFromConfig.resolve(candidate);
33971
- } catch {
33972
- continue;
33973
- }
33974
- return null;
33975
- };
33976
- const resolveExtendsPath = (extendsValue, fromConfigDirectory) => {
33977
- if (isRelativeExtendsValue(extendsValue)) return ensureJsonExtension(Path.resolve(fromConfigDirectory, extendsValue));
33978
- return resolvePackageExtendsPath(extendsValue, fromConfigDirectory);
33979
- };
33980
- const normalizeCompilerOptions = (compilerOptions) => {
33981
- if (!isPlainObject(compilerOptions)) return { hasExplicitLib: false };
33982
- const target = typeof compilerOptions.target === "string" ? compilerOptions.target : void 0;
33983
- const hasExplicitLib = Object.hasOwn(compilerOptions, "lib");
33984
- return {
33985
- target,
33986
- lib: Array.isArray(compilerOptions.lib) ? compilerOptions.lib.filter((entry) => typeof entry === "string") : void 0,
33987
- hasExplicitLib
33988
- };
33989
- };
33990
- const normalizeReferencePaths = (references) => {
33991
- if (!Array.isArray(references)) return [];
33992
- return references.map((reference) => isPlainObject(reference) && typeof reference.path === "string" ? reference.path : null).filter((referencePath) => referencePath !== null);
33993
- };
33994
- const readTsConfig = (filePath) => {
33995
- let content;
33996
- try {
33997
- content = NFS.readFileSync(filePath, "utf-8");
33998
- } catch {
33999
- return null;
34000
- }
34001
- const parsed = ts.parseConfigFileTextToJson(filePath, content);
34002
- if (!isPlainObject(parsed.config)) return null;
34003
- return {
34004
- extends: typeof parsed.config.extends === "string" ? parsed.config.extends : void 0,
34005
- referencePaths: normalizeReferencePaths(parsed.config.references),
34006
- compilerOptions: normalizeCompilerOptions(parsed.config.compilerOptions)
34007
- };
34008
- };
34009
- const mergeCompilerOptions = (inherited, current) => {
34010
- const target = current.target ?? inherited?.target;
34011
- const hasExplicitLib = current.hasExplicitLib || Boolean(inherited?.hasExplicitLib);
34012
- return {
34013
- target,
34014
- lib: current.hasExplicitLib ? current.lib : inherited?.lib,
34015
- hasExplicitLib
34016
- };
34017
- };
34018
- const readResolvedCompilerOptions = (tsConfigPath, extendsDepth, visitedPaths) => {
34019
- const realPath = NFS.realpathSync.native(tsConfigPath);
34020
- if (visitedPaths.has(realPath)) return null;
34021
- const tsConfig = readTsConfig(realPath);
34022
- if (!tsConfig) return null;
34023
- const nextVisitedPaths = new Set(visitedPaths);
34024
- nextVisitedPaths.add(realPath);
34025
- if (tsConfig.extends && extendsDepth < 8) {
34026
- const parentPath = resolveExtendsPath(tsConfig.extends, Path.dirname(realPath));
34027
- if (parentPath && isFile(parentPath)) return mergeCompilerOptions(readResolvedCompilerOptions(parentPath, extendsDepth + 1, nextVisitedPaths), tsConfig.compilerOptions);
34028
- }
34029
- return tsConfig.compilerOptions;
34030
- };
34031
- const targetYearIsPreES2023 = (target) => {
34032
- const year = ES_TARGET_YEAR_BY_NAME[target.toLowerCase()];
34033
- return year !== void 0 && year < 2023;
34034
- };
34035
- const libEntryIncludesES2023Array = (entry) => {
34036
- const normalizedEntry = entry.toLowerCase();
34037
- if (normalizedEntry === "esnext" || normalizedEntry === "esnext.array") return true;
34038
- const esYearMatch = /^es(\d{4})(?:\.(.+))?$/.exec(normalizedEntry);
34039
- if (!esYearMatch) return false;
34040
- if (Number(esYearMatch[1]) < 2023) return false;
34041
- const component = esYearMatch[2];
34042
- return component === void 0 || component === "array";
34043
- };
34044
- const libIncludesES2023 = (lib) => lib.some(libEntryIncludesES2023Array);
34045
- const compilerOptionsArePreES2023 = (compilerOptions) => {
34046
- if (compilerOptions.target && targetYearIsPreES2023(compilerOptions.target)) return true;
34047
- if (compilerOptions.hasExplicitLib) return !libIncludesES2023(compilerOptions.lib ?? []);
34048
- return false;
34049
- };
34050
- const compilerOptionsDeclareTargetOrLib = (compilerOptions) => compilerOptions.hasExplicitLib || compilerOptions.target !== void 0;
34051
- const detectPreES2023FromConfig = (tsConfigPath, visitedConfigPaths = /* @__PURE__ */ new Set()) => {
34052
- if (visitedConfigPaths.has(tsConfigPath)) return false;
34053
- const compilerOptions = readResolvedCompilerOptions(tsConfigPath, 0, /* @__PURE__ */ new Set());
34054
- if (!compilerOptions) return false;
34055
- if (!compilerOptionsDeclareTargetOrLib(compilerOptions)) {
34056
- const tsConfig = readTsConfig(tsConfigPath);
34057
- if (!tsConfig) return false;
34058
- const nextVisitedConfigPaths = new Set(visitedConfigPaths);
34059
- nextVisitedConfigPaths.add(tsConfigPath);
34060
- const configDirectory = Path.dirname(tsConfigPath);
34061
- return tsConfig.referencePaths.some((referencePath) => {
34062
- const resolvedReferencePath = Path.resolve(configDirectory, referencePath);
34063
- const referencedConfigPath = isFile(resolvedReferencePath) ? resolvedReferencePath : Path.join(resolvedReferencePath, TSCONFIG_FILENAME);
34064
- return isFile(referencedConfigPath) && detectPreES2023FromConfig(referencedConfigPath, nextVisitedConfigPaths);
34065
- });
34066
- }
34067
- return compilerOptionsArePreES2023(compilerOptions);
34068
- };
34069
- const FALLBACK_TSCONFIG_FILENAMES = ["tsconfig.app.json", "tsconfig.build.json"];
34070
- const detectPreES2023Target = (directory) => {
34071
- const tsConfigPath = Path.join(directory, TSCONFIG_FILENAME);
34072
- if (isFile(tsConfigPath)) return detectPreES2023FromConfig(tsConfigPath);
34073
- for (const fallbackFilename of FALLBACK_TSCONFIG_FILENAMES) {
34074
- const fallbackPath = Path.join(directory, fallbackFilename);
34075
- if (isFile(fallbackPath)) return detectPreES2023FromConfig(fallbackPath);
34076
- }
34077
- return false;
34078
- };
34079
- const REACT_DEPENDENCY_NAMES = new Set([
34080
- "react",
34081
- "react-native",
34082
- "next",
34083
- "preact"
34084
- ]);
34085
- const hasReactDependency = (packageJson) => {
34086
- const allDependencies = {
34087
- ...packageJson.peerDependencies,
34088
- ...packageJson.dependencies,
34089
- ...packageJson.devDependencies
34090
- };
34091
- return Object.keys(allDependencies).some((packageName) => REACT_DEPENDENCY_NAMES.has(packageName));
34092
- };
34093
- const listWorkspacePackages = (rootDirectory) => {
34094
- const packageJsonPath = Path.join(rootDirectory, "package.json");
34095
- if (!isFile(packageJsonPath)) return [];
34096
- const packageJson = readPackageJson(packageJsonPath);
34097
- const patterns = getWorkspacePatterns(rootDirectory, packageJson);
34098
- if (patterns.length === 0) return [];
34099
- const packages = [];
34100
- const seenDirectories = /* @__PURE__ */ new Set();
34101
- const pushIfNew = (workspacePackage) => {
34102
- if (seenDirectories.has(workspacePackage.directory)) return;
34103
- seenDirectories.add(workspacePackage.directory);
34104
- packages.push(workspacePackage);
34105
- };
34106
- if (hasReactDependency(packageJson)) pushIfNew({
34107
- name: packageJson.name ?? Path.basename(rootDirectory),
34108
- directory: rootDirectory
34109
- });
34110
- for (const pattern of patterns) {
34111
- const directories = resolveWorkspaceDirectories(rootDirectory, pattern);
34112
- for (const workspaceDirectory of directories) {
34113
- const workspacePackageJson = readPackageJson(Path.join(workspaceDirectory, "package.json"));
34114
- if (!hasReactDependency(workspacePackageJson)) continue;
34115
- pushIfNew({
34116
- name: workspacePackageJson.name ?? Path.basename(workspaceDirectory),
34117
- directory: workspaceDirectory
34118
- });
34119
- }
34120
- }
34121
- return packages;
34122
- };
34123
34186
  const toReactWorkspacePackages = (directories) => {
34124
34187
  const packages = [];
34125
34188
  for (const directory of directories) {
@@ -34192,14 +34255,13 @@ const clearProjectCache = () => {
34192
34255
  };
34193
34256
  /**
34194
34257
  * Build a `ProjectInfo` for a directory that has no `package.json` of
34195
- * its own — a package subfolder like `repo/packages` or `app/src/features`,
34196
- * or any loose tree of TypeScript/JavaScript files. Dependency + framework
34197
- * detection is inherited from the nearest enclosing package (a leaf
34198
- * workspace, a plain app root, or a monorepo root — whichever is closest,
34199
- * bounded by the git root), so scanning a subdirectory of a React project
34200
- * still gets the React capabilities; a standalone non-React directory simply
34201
- * scans with the framework-agnostic rules. Throws only when the directory has
34202
- * nothing to scan (no enclosing project and no source files of its own).
34258
+ * its own — a monorepo subfolder like `repo/packages`, or any loose tree
34259
+ * of TypeScript/JavaScript files. Dependency + framework detection is
34260
+ * inherited from the enclosing workspace root when there is one, so
34261
+ * scanning a subdirectory of a React monorepo still gets the React
34262
+ * capabilities; a standalone non-React directory simply scans with the
34263
+ * framework-agnostic rules. Throws only when the directory has nothing
34264
+ * to scan (no enclosing project and no source files of its own).
34203
34265
  */
34204
34266
  const discoverProjectWithoutPackageJson = (directory) => {
34205
34267
  const sourceFileCount = countSourceFiles(directory);
@@ -34237,6 +34299,7 @@ const discoverProjectWithoutPackageJson = (directory) => {
34237
34299
  hasReanimated: false,
34238
34300
  reanimatedVersion: null,
34239
34301
  isPreES2023Target: hasOwnTsConfig && detectPreES2023Target(directory),
34302
+ isStaticExport: false,
34240
34303
  sourceFileCount
34241
34304
  };
34242
34305
  };
@@ -34250,90 +34313,85 @@ const discoverProject = (directory) => {
34250
34313
  return synthesized;
34251
34314
  }
34252
34315
  const packageJson = readPackageJson(packageJsonPath);
34253
- let { reactVersion, tailwindVersion, zodVersion, framework } = extractDependencyInfo(packageJson);
34254
- const reactDeclaration = getDependencyDeclaration({
34255
- packageJson,
34256
- packageName: "react",
34257
- sections: [
34258
- "dependencies",
34259
- "peerDependencies",
34260
- "devDependencies"
34261
- ]
34262
- });
34263
- const tailwindDeclaration = getDependencyDeclaration({
34264
- packageJson,
34265
- packageName: "tailwindcss",
34266
- sections: [
34267
- "dependencies",
34268
- "devDependencies",
34269
- "peerDependencies"
34270
- ]
34271
- });
34272
- const zodDeclaration = getDependencyDeclaration({
34316
+ const rootInfo = extractDependencyInfo(packageJson);
34317
+ let framework = rootInfo.framework;
34318
+ const tracked = {
34319
+ react: {
34320
+ version: rootInfo.reactVersion,
34321
+ sections: REACT_SECTIONS
34322
+ },
34323
+ tailwindcss: {
34324
+ version: rootInfo.tailwindVersion,
34325
+ sections: TAILWIND_ZOD_SECTIONS
34326
+ },
34327
+ zod: {
34328
+ version: rootInfo.zodVersion,
34329
+ sections: TAILWIND_ZOD_SECTIONS
34330
+ }
34331
+ };
34332
+ const declarations = Object.fromEntries(Object.entries(tracked).map(([packageName, entry]) => [packageName, getDependencyDeclaration({
34273
34333
  packageJson,
34274
- packageName: "zod",
34275
- sections: [
34276
- "dependencies",
34277
- "devDependencies",
34278
- "peerDependencies"
34279
- ]
34280
- });
34281
- if (!reactVersion && reactDeclaration.hasDeclaration) reactVersion = resolveCatalogVersion(packageJson, "react", directory, reactDeclaration.catalogReference);
34282
- if (!tailwindVersion && tailwindDeclaration.hasDeclaration) tailwindVersion = resolveCatalogVersion(packageJson, "tailwindcss", directory, tailwindDeclaration.catalogReference);
34283
- if (!zodVersion && zodDeclaration.hasDeclaration) zodVersion = resolveCatalogVersion(packageJson, "zod", directory, zodDeclaration.catalogReference);
34284
- if (!reactVersion || !tailwindVersion || !zodVersion) {
34334
+ packageName,
34335
+ sections: entry.sections
34336
+ })]));
34337
+ const fillFromCatalogs = (source, sourceDirectory) => {
34338
+ for (const [packageName, entry] of Object.entries(tracked)) if (!entry.version && declarations[packageName].hasDeclaration) entry.version = resolveCatalogVersion(source, packageName, sourceDirectory, declarations[packageName].catalogReference);
34339
+ };
34340
+ fillFromCatalogs(packageJson, directory);
34341
+ if (!tracked.react.version || !tracked.tailwindcss.version || !tracked.zod.version) {
34285
34342
  const monorepoRoot = findMonorepoRoot(directory);
34286
34343
  if (monorepoRoot) {
34287
34344
  const monorepoPackageJsonPath = Path.join(monorepoRoot, "package.json");
34288
- if (isFile(monorepoPackageJsonPath)) {
34289
- const rootPackageJson = readPackageJson(monorepoPackageJsonPath);
34290
- if (!reactVersion && reactDeclaration.hasDeclaration) reactVersion = resolveCatalogVersion(rootPackageJson, "react", monorepoRoot, reactDeclaration.catalogReference);
34291
- if (!tailwindVersion && tailwindDeclaration.hasDeclaration) tailwindVersion = resolveCatalogVersion(rootPackageJson, "tailwindcss", monorepoRoot, tailwindDeclaration.catalogReference);
34292
- if (!zodVersion && zodDeclaration.hasDeclaration) zodVersion = resolveCatalogVersion(rootPackageJson, "zod", monorepoRoot, zodDeclaration.catalogReference);
34293
- }
34345
+ if (isFile(monorepoPackageJsonPath)) fillFromCatalogs(readPackageJson(monorepoPackageJsonPath), monorepoRoot);
34294
34346
  }
34295
34347
  }
34296
- if (!reactVersion || framework === "unknown") {
34297
- const workspaceInfo = findReactInWorkspaces(directory, packageJson);
34298
- if (!reactVersion && workspaceInfo.reactVersion) reactVersion = workspaceInfo.reactVersion;
34299
- if (!tailwindVersion && workspaceInfo.tailwindVersion) tailwindVersion = workspaceInfo.tailwindVersion;
34300
- if (!zodVersion && workspaceInfo.zodVersion) zodVersion = workspaceInfo.zodVersion;
34301
- if (framework === "unknown" && workspaceInfo.framework !== "unknown") framework = workspaceInfo.framework;
34348
+ const shouldCollectReactGroup = !tracked.react.version || framework === "unknown";
34349
+ const workspaceFacts = collectWorkspaceFacts(directory, packageJson, { collectReactGroup: shouldCollectReactGroup });
34350
+ if (shouldCollectReactGroup) {
34351
+ tracked.react.version ||= workspaceFacts.reactVersion;
34352
+ tracked.tailwindcss.version ||= workspaceFacts.tailwindVersion;
34353
+ tracked.zod.version ||= workspaceFacts.zodVersion;
34354
+ if (framework === "unknown" && workspaceFacts.framework !== "unknown") framework = workspaceFacts.framework;
34302
34355
  }
34303
- if ((!reactVersion || framework === "unknown") && !isMonorepoRoot(directory)) {
34356
+ if ((!tracked.react.version || framework === "unknown") && !isMonorepoRoot(directory)) {
34304
34357
  const monorepoInfo = findDependencyInfoFromMonorepoRoot(directory);
34305
- if (!reactVersion) reactVersion = monorepoInfo.reactVersion;
34306
- if (!tailwindVersion) tailwindVersion = monorepoInfo.tailwindVersion;
34307
- if (!zodVersion) zodVersion = monorepoInfo.zodVersion;
34358
+ tracked.react.version ||= monorepoInfo.reactVersion;
34359
+ tracked.tailwindcss.version ||= monorepoInfo.tailwindVersion;
34360
+ tracked.zod.version ||= monorepoInfo.zodVersion;
34308
34361
  if (framework === "unknown") framework = monorepoInfo.framework;
34309
34362
  }
34310
- if (!reactVersion && reactDeclaration.version && !isCatalogReference(reactDeclaration.version)) reactVersion = reactDeclaration.version;
34311
- if (!tailwindVersion && tailwindDeclaration.version && !isCatalogReference(tailwindDeclaration.version)) tailwindVersion = tailwindDeclaration.version;
34312
- if (!zodVersion && zodDeclaration.version && !isCatalogReference(zodDeclaration.version)) zodVersion = zodDeclaration.version;
34363
+ for (const [packageName, entry] of Object.entries(tracked)) {
34364
+ const declaredVersion = declarations[packageName].version;
34365
+ if (!entry.version && declaredVersion && !isCatalogReference(declaredVersion)) entry.version = declaredVersion;
34366
+ }
34367
+ const { react, tailwindcss, zod } = tracked;
34368
+ let reactVersion = react.version;
34313
34369
  if (!reactVersion || parseReactMajor(reactVersion) === null) reactVersion = resolveInstalledReactVersion(directory) ?? reactVersion;
34370
+ const tailwindVersion = tailwindcss.version;
34371
+ const zodVersion = zod.version;
34314
34372
  const projectName = packageJson.name ?? Path.basename(directory);
34315
34373
  const hasTypeScript = NFS.existsSync(Path.join(directory, "tsconfig.json"));
34316
34374
  const sourceFileCount = countSourceFiles(directory);
34317
- const hasReactNativeWorkspace = framework === "expo" || framework === "react-native" || hasReactNativeWorkspaceAnywhere(directory, packageJson);
34375
+ const hasReactNativeWorkspace = framework === "expo" || framework === "react-native" || workspaceFacts.hasReactNativeAwarePackage;
34318
34376
  const expoVersion = hasReactNativeWorkspace ? resolveCatalogBackedDependencyVersion({
34319
34377
  rootDirectory: directory,
34320
34378
  rootPackageJson: packageJson,
34321
34379
  packageName: "expo",
34322
- version: findExpoVersion(directory, packageJson)
34380
+ version: workspaceFacts.expo.version
34323
34381
  }) : null;
34324
34382
  const shopifyFlashListVersion = hasReactNativeWorkspace ? resolveCatalogBackedDependencyVersion({
34325
34383
  rootDirectory: directory,
34326
34384
  rootPackageJson: packageJson,
34327
34385
  packageName: SHOPIFY_FLASH_LIST_PACKAGE_NAME,
34328
- version: findShopifyFlashListVersion(directory, packageJson)
34386
+ version: workspaceFacts.shopifyFlashList.version
34329
34387
  }) : null;
34330
- const hasReanimated = hasReactNativeWorkspace && someWorkspacePackageJson(directory, packageJson, isPackageJsonReanimatedAware);
34331
- const reanimatedVersion = hasReanimated ? findInWorkspacePackageJsons(directory, packageJson, (workspacePackageJson) => getDependencySpec(workspacePackageJson, REANIMATED_DEPENDENCY_NAME)) : null;
34388
+ const hasReanimated = hasReactNativeWorkspace && workspaceFacts.hasReanimatedAwarePackage;
34389
+ const reanimatedVersion = hasReanimated ? workspaceFacts.reanimatedVersion : null;
34332
34390
  const nextjsVersion = framework === "nextjs" ? resolveCatalogBackedDependencyVersion({
34333
34391
  rootDirectory: directory,
34334
34392
  rootPackageJson: packageJson,
34335
34393
  packageName: "next",
34336
- version: findNextjsVersion(directory, packageJson)
34394
+ version: workspaceFacts.next.version
34337
34395
  }) : null;
34338
34396
  const preactVersion = getPreactVersion(packageJson);
34339
34397
  const isPreES2023Target = hasTypeScript && detectPreES2023Target(directory);
@@ -34344,7 +34402,7 @@ const discoverProject = (directory) => {
34344
34402
  reactMajorVersion: resolveEffectiveReactMajor(reactVersion, packageJson),
34345
34403
  tailwindVersion,
34346
34404
  zodVersion,
34347
- zodMajorVersion: parseZodMajor(zodVersion),
34405
+ zodMajorVersion: zodVersion === null ? null : getLowestDependencyMajor(zodVersion),
34348
34406
  framework,
34349
34407
  hasTypeScript,
34350
34408
  hasReactCompiler: detectReactCompiler(directory, packageJson),
@@ -34360,6 +34418,7 @@ const discoverProject = (directory) => {
34360
34418
  hasReanimated,
34361
34419
  reanimatedVersion,
34362
34420
  isPreES2023Target,
34421
+ isStaticExport: framework === "nextjs" && detectNextjsStaticExport(workspaceFacts.next.sourceDirectory ?? directory),
34363
34422
  sourceFileCount
34364
34423
  };
34365
34424
  cachedProjectInfos.set(directory, projectInfo);
@@ -34375,57 +34434,6 @@ const discoverProject = (directory) => {
34375
34434
  * single source file has nothing to analyze.
34376
34435
  */
34377
34436
  const isAnalyzableProject = (project) => project.reactVersion !== null || project.preactVersion !== null || project.sourceFileCount > 0;
34378
- const MAJOR_MINOR_PATTERN = /(\d{1,4})\.(\d{1,4})/;
34379
- const MAJOR_ONLY_PATTERN = /(\d{1,4})/;
34380
- const UPPER_BOUND_COMPARATOR_PATTERN = /<=?\s{0,8}\d{1,4}(?:\.\d{1,4}){0,2}(?:-[^\s,|]+)?/g;
34381
- const parseReactMajorMinor = (reactVersion) => {
34382
- if (typeof reactVersion !== "string") return null;
34383
- const trimmed = reactVersion.trim();
34384
- if (trimmed.length === 0) return null;
34385
- const lowerBoundsOnly = trimmed.replace(UPPER_BOUND_COMPARATOR_PATTERN, " ").trim();
34386
- if (lowerBoundsOnly.length === 0) return null;
34387
- const majorMinorMatch = lowerBoundsOnly.match(MAJOR_MINOR_PATTERN);
34388
- if (majorMinorMatch) {
34389
- const major = Number.parseInt(majorMinorMatch[1], 10);
34390
- const minor = Number.parseInt(majorMinorMatch[2], 10);
34391
- if (!Number.isFinite(major) || major <= 0) return null;
34392
- if (!Number.isFinite(minor) || minor < 0) return null;
34393
- return {
34394
- major,
34395
- minor
34396
- };
34397
- }
34398
- const majorOnlyMatch = lowerBoundsOnly.match(MAJOR_ONLY_PATTERN);
34399
- if (!majorOnlyMatch) return null;
34400
- const major = Number.parseInt(majorOnlyMatch[1], 10);
34401
- if (!Number.isFinite(major) || major <= 0) return null;
34402
- return {
34403
- major,
34404
- minor: 0
34405
- };
34406
- };
34407
- const isReactAtLeast = (detected, required) => {
34408
- if (detected === null) return true;
34409
- if (detected.major !== required.major) return detected.major > required.major;
34410
- return detected.minor >= required.minor;
34411
- };
34412
- const parseLowerBoundVersion = (versionSpec) => import_semver.validRange(versionSpec) !== null ? import_semver.minVersion(versionSpec) : import_semver.coerce(versionSpec);
34413
- const parseTailwindMajorMinor = (tailwindVersion) => {
34414
- if (typeof tailwindVersion !== "string") return null;
34415
- const trimmed = tailwindVersion.trim();
34416
- if (trimmed.length === 0) return null;
34417
- const lowerBound = parseLowerBoundVersion(trimmed);
34418
- if (lowerBound === null || lowerBound.major <= 0) return null;
34419
- return {
34420
- major: lowerBound.major,
34421
- minor: lowerBound.minor
34422
- };
34423
- };
34424
- const isTailwindAtLeast = (detected, required) => {
34425
- if (detected === null) return true;
34426
- if (detected.major !== required.major) return detected.major > required.major;
34427
- return detected.minor >= required.minor;
34428
- };
34429
34437
  const messageFromUnknown = (error) => error instanceof Error ? error.message : String(error);
34430
34438
  var InvalidGlobPatternError = class extends Error {
34431
34439
  pattern;
@@ -37201,64 +37209,64 @@ function* collectSecurityScanFiles(rootDirectory) {
37201
37209
  }
37202
37210
  }
37203
37211
  }
37204
- /**
37205
- * Whether project discovery resolved a React-compatible runtime (React or
37206
- * Preact, which ships the same hooks + JSX model). This is the same
37207
- * predicate that grants the `react` capability, so `false` means every
37208
- * React-runtime rule family was gated off for the project.
37209
- */
37210
- const hasReactRuntime = (project) => project.reactVersion !== null || project.preactVersion !== null;
37212
+ const CLIENT_ONLY_FRAMEWORKS = new Set([
37213
+ "vite",
37214
+ "cra",
37215
+ "gatsby",
37216
+ "react-native",
37217
+ "expo"
37218
+ ]);
37219
+ const addMajorLadder = (capabilities, name, major, earliest, latest) => {
37220
+ if (major === null) return;
37221
+ const cappedMajor = Math.min(major, latest);
37222
+ for (let candidate = earliest; candidate <= cappedMajor; candidate += 1) capabilities.add(`${name}:${candidate}`);
37223
+ };
37211
37224
  const buildCapabilities = (project) => {
37212
37225
  const capabilities = /* @__PURE__ */ new Set();
37213
37226
  capabilities.add(project.framework);
37214
- if (hasReactRuntime(project)) capabilities.add("react");
37227
+ if (project.reactVersion !== null || project.preactVersion !== null) capabilities.add("react");
37215
37228
  if (project.framework === "expo" || project.framework === "react-native" || project.hasReactNativeWorkspace) capabilities.add("react-native");
37216
37229
  if (project.expoVersion !== null) capabilities.add("expo");
37230
+ if ((project.framework === "nextjs" || project.framework === "tanstack-start" || project.framework === "remix") && !project.isStaticExport) capabilities.add("server-actions");
37231
+ if (CLIENT_ONLY_FRAMEWORKS.has(project.framework)) capabilities.add("client-only");
37232
+ if (project.isStaticExport) capabilities.add("nextjs:static-export");
37217
37233
  if (project.nextjsMajorVersion !== null && project.nextjsMajorVersion >= 15) capabilities.add("nextjs:15");
37218
- const reactMajor = project.reactMajorVersion;
37219
- if (reactMajor !== null) {
37220
- const cappedReactMajor = Math.min(reactMajor, 30);
37221
- for (let major = 17; major <= cappedReactMajor; major++) capabilities.add(`react:${major}`);
37222
- if (reactMajor >= 19) {
37223
- if (isReactAtLeast(parseReactMajorMinor(project.reactVersion), {
37224
- major: 19,
37225
- minor: 2
37226
- })) capabilities.add("react:19.2");
37227
- }
37228
- }
37229
- if (project.tailwindVersion !== null) {
37230
- capabilities.add("tailwind");
37231
- if (isTailwindAtLeast(parseTailwindMajorMinor(project.tailwindVersion), {
37232
- major: 3,
37233
- minor: 4
37234
- })) capabilities.add("tailwind:3.4");
37235
- }
37236
- if (project.zodVersion !== null) {
37237
- capabilities.add("zod");
37238
- if (project.zodMajorVersion !== null && project.zodMajorVersion >= 4) capabilities.add("zod:4");
37239
- }
37234
+ addMajorLadder(capabilities, "react", project.reactMajorVersion, 17, 30);
37235
+ if (project.reactMajorVersion !== null && project.reactMajorVersion >= 19 && isMajorMinorAtLeast(parseReactMajorMinor(project.reactVersion), {
37236
+ major: 19,
37237
+ minor: 2
37238
+ })) capabilities.add("react:19.2");
37239
+ if (project.tailwindVersion !== null) capabilities.add("tailwind");
37240
+ if (project.tailwindVersion !== null && isMajorMinorAtLeast(parseTailwindMajorMinor(project.tailwindVersion), {
37241
+ major: 3,
37242
+ minor: 4
37243
+ })) capabilities.add("tailwind:3.4");
37244
+ if (project.zodVersion !== null) capabilities.add("zod");
37245
+ if (project.zodMajorVersion !== null && project.zodMajorVersion >= 4) capabilities.add("zod:4");
37240
37246
  if (project.isPreES2023Target) capabilities.add("pre-es2023");
37241
37247
  if (project.hasReactCompiler) capabilities.add("react-compiler");
37242
37248
  if (project.hasTanStackQuery) capabilities.add("tanstack-query");
37243
37249
  if (project.hasTypeScript) capabilities.add("typescript");
37244
- if (project.preactVersion !== null) {
37245
- capabilities.add("preact");
37246
- const preactMajor = project.preactMajorVersion;
37247
- if (preactMajor !== null) {
37248
- const cappedPreactMajor = Math.min(preactMajor, 20);
37249
- for (let major = 10; major <= cappedPreactMajor; major++) capabilities.add(`preact:${major}`);
37250
- }
37251
- if (project.reactVersion === null) capabilities.add("pure-preact");
37252
- }
37250
+ if (project.preactVersion !== null) capabilities.add("preact");
37251
+ addMajorLadder(capabilities, "preact", project.preactMajorVersion, 10, 20);
37252
+ if (project.preactVersion !== null && project.reactVersion === null) capabilities.add("pure-preact");
37253
+ return capabilities;
37254
+ };
37255
+ const capabilitiesByProject = /* @__PURE__ */ new WeakMap();
37256
+ const getCapabilities = (project) => {
37257
+ const cached = capabilitiesByProject.get(project);
37258
+ if (cached !== void 0) return cached;
37259
+ const capabilities = buildCapabilities(project);
37260
+ capabilitiesByProject.set(project, capabilities);
37253
37261
  return capabilities;
37254
37262
  };
37255
- const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledBy) => {
37263
+ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledWhen) => {
37256
37264
  if (requires) {
37257
37265
  for (const capability of requires) if (!capabilities.has(capability)) return false;
37258
37266
  }
37259
37267
  if (tags?.includes("react-jsx-only") && !capabilities.has("react")) return false;
37260
- if (disabledBy) {
37261
- for (const capability of disabledBy) if (capabilities.has(capability)) return false;
37268
+ if (disabledWhen) {
37269
+ for (const capability of disabledWhen) if (capabilities.has(capability)) return false;
37262
37270
  }
37263
37271
  if (tags) {
37264
37272
  for (const tag of tags) if (ignoredTags.has(tag)) return false;
@@ -37269,14 +37277,14 @@ const yieldToEventLoop = () => new Promise((resolve) => {
37269
37277
  setImmediate(resolve);
37270
37278
  });
37271
37279
  const createSecurityScanSession = (rootDirectory, options) => {
37272
- const capabilities = options.project ? buildCapabilities(options.project) : /* @__PURE__ */ new Set();
37280
+ const capabilities = options.project ? getCapabilities(options.project) : /* @__PURE__ */ new Set();
37273
37281
  const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
37274
37282
  const enabledScanRules = REACT_DOCTOR_RULES.flatMap((entry) => {
37275
37283
  const rule = entry.rule;
37276
37284
  const scan = rule.scan;
37277
37285
  if (typeof scan !== "function") return [];
37278
37286
  if (rule.defaultEnabled === false) return [];
37279
- if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledBy)) return [];
37287
+ if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledWhen)) return [];
37280
37288
  return [{
37281
37289
  entry,
37282
37290
  scan,
@@ -37296,6 +37304,7 @@ const createSecurityScanSession = (rootDirectory, options) => {
37296
37304
  return status === true;
37297
37305
  };
37298
37306
  const scanFileByRule = function* (file) {
37307
+ if (file === null) return;
37299
37308
  for (const { entry, scan, committedFilesOnly } of enabledScanRules) {
37300
37309
  for (const finding of scan(file)) {
37301
37310
  if (committedFilesOnly && isFileGitIgnored(file)) continue;
@@ -39176,7 +39185,7 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
39176
39185
  const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
39177
39186
  const jsPlugins = [];
39178
39187
  if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
39179
- const capabilities = buildCapabilities(project);
39188
+ const capabilities = getCapabilities(project);
39180
39189
  const enabledReactDoctorRules = {};
39181
39190
  for (const registryEntry of REACT_DOCTOR_RULES) {
39182
39191
  const rule = reactDoctorPlugin.rules[registryEntry.id];
@@ -39187,7 +39196,7 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
39187
39196
  if (rule.scan !== void 0) continue;
39188
39197
  if (customRulesOnly && registryEntry.originallyExternal) continue;
39189
39198
  if (rule.framework !== "global" && !rule.requires) continue;
39190
- if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledBy)) continue;
39199
+ if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledWhen)) continue;
39191
39200
  const explicitRuleOverride = resolveRuleSeverityOverride({ ruleKey: registryEntry.key }, severityControls);
39192
39201
  if (rule.defaultEnabled === false && explicitRuleOverride === void 0) continue;
39193
39202
  const severity = resolveRuleSeverityOverride({
@@ -39218,6 +39227,7 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
39218
39227
  settings: { "react-doctor": {
39219
39228
  framework: project.framework,
39220
39229
  rootDirectory: resolveSettingsRootDirectory(project.rootDirectory),
39230
+ capabilities: [...capabilities].sort(),
39221
39231
  ...project.shopifyFlashListMajorVersion !== null ? { shopifyFlashListMajorVersion: project.shopifyFlashListMajorVersion } : {},
39222
39232
  ...serverAuthFunctionNames && serverAuthFunctionNames.length > 0 ? { serverAuthFunctionNames: [...serverAuthFunctionNames] } : {}
39223
39233
  } },
@@ -39508,21 +39518,6 @@ const mapWithConcurrency = async (items, concurrency, task) => {
39508
39518
  if (errors.length > 0) throw errors[0];
39509
39519
  return results;
39510
39520
  };
39511
- const getPublicEnvPrefix = (framework) => {
39512
- switch (framework) {
39513
- case "nextjs": return "NEXT_PUBLIC_*";
39514
- case "vite":
39515
- case "tanstack-start": return "VITE_*";
39516
- case "cra": return "REACT_APP_*";
39517
- case "gatsby": return "GATSBY_*";
39518
- default: return null;
39519
- }
39520
- };
39521
- const buildNoSecretsRecommendation = (project, fallbackRecommendation) => {
39522
- const publicEnvPrefix = getPublicEnvPrefix(project.framework);
39523
- if (!publicEnvPrefix) return fallbackRecommendation;
39524
- 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`;
39525
- };
39526
39521
  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";
39527
39522
  const ACCESSORS_MIN_VERSION = "3.15.0";
39528
39523
  const supportsAccessors = (reanimatedVersion) => {
@@ -40203,8 +40198,14 @@ const PLUGIN_CATEGORY_MAP = {
40203
40198
  };
40204
40199
  const lookupOwnString = (record, key) => Object.hasOwn(record, key) ? record[key] : void 0;
40205
40200
  const getRuleRecommendation = (ruleName, project) => {
40206
- if (ruleName === "no-secrets-in-client-code") return buildNoSecretsRecommendation(project, reactDoctorPlugin.rules["no-secrets-in-client-code"]?.recommendation ?? "Move secrets to server-only code");
40207
- return reactDoctorPlugin.rules[ruleName]?.recommendation;
40201
+ const rule = reactDoctorPlugin.rules[ruleName];
40202
+ if (!rule) return void 0;
40203
+ if (rule.recommendationFor) {
40204
+ const capabilities = getCapabilities(project);
40205
+ const conditionalRecommendation = rule.recommendationFor((capability) => capabilities.has(capability));
40206
+ if (conditionalRecommendation !== void 0) return conditionalRecommendation;
40207
+ }
40208
+ return rule.recommendation;
40208
40209
  };
40209
40210
  const getRuleCategory = (ruleName) => reactDoctorPlugin.rules[ruleName]?.category;
40210
40211
  const getRuleTitle = (ruleName) => reactDoctorPlugin.rules[ruleName]?.title;
@@ -42529,6 +42530,13 @@ const summarizeDiagnostics = (diagnostics, worstScore = null, worstScoreLabel =
42529
42530
  scoreLabel: worstScoreLabel
42530
42531
  };
42531
42532
  };
42533
+ /**
42534
+ * Whether project discovery resolved a React-compatible runtime (React or
42535
+ * Preact, which ships the same hooks + JSX model). This is the same
42536
+ * predicate that grants the `react` capability, so `false` means every
42537
+ * React-runtime rule family was gated off for the project.
42538
+ */
42539
+ const hasReactRuntime = (project) => project.reactVersion !== null || project.preactVersion !== null;
42532
42540
  const toJsonDiff = (diff) => {
42533
42541
  if (!diff) return null;
42534
42542
  return {
@@ -42895,4 +42903,4 @@ const toJsonReport = (result, options) => buildJsonReport({
42895
42903
  export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
42896
42904
 
42897
42905
  //# sourceMappingURL=index.js.map
42898
- //# debugId=10e3e1b0-f291-5306-b5fd-79b158b00159
42906
+ //# debugId=7b7e0da8-d676-570d-b0e2-7fc3a4fe85ab