react-doctor 0.2.11-dev.b5cf767 → 0.2.11-dev.b65b157

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.
@@ -2281,15 +2281,91 @@ const detectFramework = (dependencies) => {
2281
2281
  if (dependencies.preact && !dependencies.react) return "preact";
2282
2282
  return "unknown";
2283
2283
  };
2284
- const UPPER_BOUND_COMPARATOR = /<\s*=?\s*\d+(?:\.\d+){0,2}(?:-[^\s,|]+)?/g;
2285
- const HAS_UPPER_BOUND_COMPARATOR = /<\s*=?\s*\d+(?:\.\d+){0,2}(?:-[^\s,|]+)?/;
2286
- const OR_SEPARATOR = /\s*\|\|\s*/;
2287
2284
  const UNRESOLVABLE_PROTOCOL_VERSION = /^(?:file|git|github|https?|link|patch|portal|workspace|npm):/i;
2288
2285
  const DIST_TAG_VERSION = /^[a-z][a-z0-9._-]*$/i;
2289
2286
  const WILDCARD_VERSION = /^[*xX](?:\.[*xX])*$/;
2290
- const NON_LOWER_BOUND_COMPARATOR = /(?:^|[\s,|])(?:>(?!=)|!={0,2})\s*\d/;
2291
- const LOWER_BOUND_MAJOR = /(?:^|[\s,|])(?:>=\s*|[~^=v]\s*)?(\d+)(?=$|[\s,|.*xX-])/g;
2292
2287
  const NPM_ALIAS_VERSION = /^npm:(?:@[^/]+\/[^@]+|[^@]+)@(.+)$/i;
2288
+ const isDigit = (value) => value !== void 0 && value >= "0" && value <= "9";
2289
+ const isWhitespace = (value) => value === " " || value === " " || value === "\n" || value === "\r" || value === "\f" || value === "\v";
2290
+ const isSeparator = (value) => isWhitespace(value) || value === "," || value === "|";
2291
+ const skipWhitespace = (value, start) => {
2292
+ let index = start;
2293
+ while (isWhitespace(value[index])) index += 1;
2294
+ return index;
2295
+ };
2296
+ const skipSeparators = (value, start) => {
2297
+ let index = start;
2298
+ while (isSeparator(value[index])) index += 1;
2299
+ return index;
2300
+ };
2301
+ const readDigits = (value, start) => {
2302
+ let index = start;
2303
+ while (isDigit(value[index])) index += 1;
2304
+ return index;
2305
+ };
2306
+ const getUpperBoundComparatorEnd = (version, start) => {
2307
+ if (version[start] !== "<") return null;
2308
+ let index = skipWhitespace(version, start + 1);
2309
+ if (version[index] === "=") index = skipWhitespace(version, index + 1);
2310
+ const majorStart = index;
2311
+ index = readDigits(version, index);
2312
+ if (index === majorStart) return null;
2313
+ for (let segments = 0; segments < 2 && version[index] === "."; segments += 1) {
2314
+ const segmentStart = index + 1;
2315
+ const segmentEnd = readDigits(version, segmentStart);
2316
+ if (segmentEnd === segmentStart) break;
2317
+ index = segmentEnd;
2318
+ }
2319
+ if (version[index] === "-") {
2320
+ index += 1;
2321
+ while (index < version.length && !isSeparator(version[index])) index += 1;
2322
+ }
2323
+ return index;
2324
+ };
2325
+ const stripUpperBoundComparators = (version) => {
2326
+ let stripped = "";
2327
+ let index = 0;
2328
+ while (index < version.length) {
2329
+ const comparatorEnd = getUpperBoundComparatorEnd(version, index);
2330
+ if (comparatorEnd === null) {
2331
+ stripped += version[index];
2332
+ index += 1;
2333
+ continue;
2334
+ }
2335
+ stripped += " ";
2336
+ index = comparatorEnd;
2337
+ }
2338
+ return stripped;
2339
+ };
2340
+ const hasNonLowerBoundComparator = (branch) => {
2341
+ for (let index = 0; index < branch.length; index += 1) {
2342
+ if (index > 0 && !isSeparator(branch[index - 1])) continue;
2343
+ if (branch[index] === ">" && branch[index + 1] !== "=") {
2344
+ if (isDigit(branch[skipWhitespace(branch, index + 1)])) return true;
2345
+ continue;
2346
+ }
2347
+ if (branch[index] !== "!") continue;
2348
+ let valueIndex = index + 1;
2349
+ if (branch[valueIndex] === "=") valueIndex += 1;
2350
+ if (branch[valueIndex] === "=") valueIndex += 1;
2351
+ valueIndex = skipWhitespace(branch, valueIndex);
2352
+ if (isDigit(branch[valueIndex])) return true;
2353
+ }
2354
+ return false;
2355
+ };
2356
+ const isMajorTerminator = (value) => value === void 0 || isSeparator(value) || value === "." || value === "*" || value === "x" || value === "X" || value === "-";
2357
+ const getLowerBoundMajorAt = (branch, start) => {
2358
+ let index = start;
2359
+ if (branch[index] === ">" && branch[index + 1] === "=") index = skipWhitespace(branch, index + 2);
2360
+ else if (branch[index] === "~" || branch[index] === "^" || branch[index] === "=" || branch[index] === "v") index = skipWhitespace(branch, index + 1);
2361
+ const majorStart = index;
2362
+ const majorEnd = readDigits(branch, majorStart);
2363
+ if (majorEnd === majorStart || !isMajorTerminator(branch[majorEnd])) return null;
2364
+ return {
2365
+ end: majorEnd,
2366
+ major: Number.parseInt(branch.slice(majorStart, majorEnd), 10)
2367
+ };
2368
+ };
2293
2369
  const normalizeDependencyVersion = (version) => {
2294
2370
  const trimmed = version.trim();
2295
2371
  if (trimmed.length === 0) return null;
@@ -2299,17 +2375,29 @@ const normalizeDependencyVersion = (version) => {
2299
2375
  if (WILDCARD_VERSION.test(normalizedVersion)) return null;
2300
2376
  return normalizedVersion;
2301
2377
  };
2302
- const splitDependencyVersionBranches = (version) => version.split(OR_SEPARATOR).filter(Boolean);
2303
- const hasUpperBoundComparator = (version) => HAS_UPPER_BOUND_COMPARATOR.test(version);
2378
+ const splitDependencyVersionBranches = (version) => version.split("||").map((branch) => branch.trim()).filter(Boolean);
2379
+ const hasUpperBoundComparator = (version) => {
2380
+ for (let index = 0; index < version.length; index += 1) if (getUpperBoundComparatorEnd(version, index) !== null) return true;
2381
+ return false;
2382
+ };
2304
2383
  const getBranchLowestMajor = (branch) => {
2305
- if (NON_LOWER_BOUND_COMPARATOR.test(branch)) return null;
2306
- const lowerBoundComparators = branch.replace(UPPER_BOUND_COMPARATOR, " ").trim();
2384
+ if (hasNonLowerBoundComparator(branch)) return null;
2385
+ const lowerBoundComparators = stripUpperBoundComparators(branch).trim();
2307
2386
  if (lowerBoundComparators.length === 0) return null;
2308
2387
  let branchLowestMajor = null;
2309
- for (const match of lowerBoundComparators.matchAll(LOWER_BOUND_MAJOR)) {
2310
- const major = Number.parseInt(match[1], 10);
2311
- if (!Number.isFinite(major) || major <= 0) continue;
2312
- if (branchLowestMajor === null || major < branchLowestMajor) branchLowestMajor = major;
2388
+ let index = 0;
2389
+ while (index < lowerBoundComparators.length) {
2390
+ const lowerBoundStart = skipSeparators(lowerBoundComparators, index);
2391
+ if (lowerBoundStart > 0 && !isSeparator(lowerBoundComparators[lowerBoundStart - 1])) {
2392
+ index = lowerBoundStart + 1;
2393
+ continue;
2394
+ }
2395
+ const lowerBoundMajor = getLowerBoundMajorAt(lowerBoundComparators, lowerBoundStart);
2396
+ if (lowerBoundMajor !== null && Number.isFinite(lowerBoundMajor.major) && lowerBoundMajor.major > 0) {
2397
+ const major = lowerBoundMajor.major;
2398
+ if (branchLowestMajor === null || major < branchLowestMajor) branchLowestMajor = major;
2399
+ }
2400
+ index = lowerBoundMajor?.end ?? lowerBoundStart + 1;
2313
2401
  }
2314
2402
  return branchLowestMajor;
2315
2403
  };
@@ -2478,6 +2566,7 @@ const resolveCatalogVersion = (packageJson, packageName, rootDirectory, explicit
2478
2566
  const EMPTY_DEPENDENCY_INFO = {
2479
2567
  reactVersion: null,
2480
2568
  tailwindVersion: null,
2569
+ zodVersion: null,
2481
2570
  framework: "unknown"
2482
2571
  };
2483
2572
  const pickConcreteVersion = (packageJson, packageName, sections) => {
@@ -2506,6 +2595,11 @@ const extractDependencyInfo = (packageJson) => {
2506
2595
  "devDependencies",
2507
2596
  "peerDependencies"
2508
2597
  ]),
2598
+ zodVersion: pickConcreteVersion(packageJson, "zod", [
2599
+ "dependencies",
2600
+ "devDependencies",
2601
+ "peerDependencies"
2602
+ ]),
2509
2603
  framework: detectFramework(allDependencies)
2510
2604
  };
2511
2605
  };
@@ -2650,8 +2744,22 @@ const findReactInWorkspaces = (rootDirectory, packageJson) => {
2650
2744
  workspaceDirectory,
2651
2745
  workspacePackageJson
2652
2746
  });
2747
+ const zodVersion = resolveWorkspaceDependencyVersion({
2748
+ concreteVersion: info.zodVersion,
2749
+ packageName: "zod",
2750
+ rootDirectory,
2751
+ rootPackageJson: packageJson,
2752
+ sections: [
2753
+ "dependencies",
2754
+ "devDependencies",
2755
+ "peerDependencies"
2756
+ ],
2757
+ workspaceDirectory,
2758
+ workspacePackageJson
2759
+ });
2653
2760
  if (reactVersion && shouldReplaceReactVersion(result.reactVersion, reactVersion)) result.reactVersion = reactVersion;
2654
2761
  if (tailwindVersion && !result.tailwindVersion) result.tailwindVersion = tailwindVersion;
2762
+ if (zodVersion && !result.zodVersion) result.zodVersion = zodVersion;
2655
2763
  if (info.framework !== "unknown" && result.framework === "unknown") result.framework = info.framework;
2656
2764
  const resultReactMajor = parseReactMajor(result.reactVersion);
2657
2765
  if (result.reactVersion && result.tailwindVersion && result.framework !== "unknown" && resultReactMajor !== null && resultReactMajor <= 17) return result;
@@ -2686,14 +2794,26 @@ const findDependencyInfoFromMonorepoRoot = (directory) => {
2686
2794
  "peerDependencies"
2687
2795
  ]
2688
2796
  }) : null;
2797
+ const leafZodDeclaration = leafPackageJson ? getDependencyDeclaration({
2798
+ packageJson: leafPackageJson,
2799
+ packageName: "zod",
2800
+ sections: [
2801
+ "dependencies",
2802
+ "devDependencies",
2803
+ "peerDependencies"
2804
+ ]
2805
+ }) : null;
2689
2806
  const shouldUseReactFallback = !leafReactDeclaration?.hasDeclaration;
2690
2807
  const shouldUseTailwindFallback = leafTailwindDeclaration?.hasDeclaration ?? true;
2808
+ const shouldUseZodFallback = leafZodDeclaration?.hasDeclaration ?? true;
2691
2809
  const reactCatalogVersion = shouldUseReactFallback ? resolveCatalogVersion(rootPackageJson, "react", monorepoRoot, leafReactDeclaration?.catalogReference) : null;
2692
2810
  const tailwindCatalogVersion = shouldUseTailwindFallback ? resolveCatalogVersion(rootPackageJson, "tailwindcss", monorepoRoot, leafTailwindDeclaration?.catalogReference) : null;
2811
+ const zodCatalogVersion = shouldUseZodFallback ? resolveCatalogVersion(rootPackageJson, "zod", monorepoRoot, leafZodDeclaration?.catalogReference) : null;
2693
2812
  const workspaceInfo = findReactInWorkspaces(monorepoRoot, rootPackageJson);
2694
2813
  return {
2695
2814
  reactVersion: shouldUseReactFallback ? reactCatalogVersion ?? rootInfo.reactVersion ?? workspaceInfo.reactVersion : rootInfo.reactVersion ?? workspaceInfo.reactVersion,
2696
2815
  tailwindVersion: shouldUseTailwindFallback ? tailwindCatalogVersion ?? rootInfo.tailwindVersion ?? workspaceInfo.tailwindVersion : null,
2816
+ zodVersion: shouldUseZodFallback ? zodCatalogVersion ?? rootInfo.zodVersion ?? workspaceInfo.zodVersion : null,
2697
2817
  framework: rootInfo.framework !== "unknown" ? rootInfo.framework : workspaceInfo.framework
2698
2818
  };
2699
2819
  };
@@ -2773,6 +2893,10 @@ const isPackageJsonReanimatedAware = (packageJson) => {
2773
2893
  };
2774
2894
  return Object.hasOwn(allDependencies, REANIMATED_DEPENDENCY_NAME);
2775
2895
  };
2896
+ const parseZodMajor = (zodVersion) => {
2897
+ if (typeof zodVersion !== "string") return null;
2898
+ return getLowestDependencyMajor(zodVersion);
2899
+ };
2776
2900
  const hasUpperBoundOnlyPeerRange = (range) => {
2777
2901
  if (typeof range !== "string") return false;
2778
2902
  const normalizedRange = normalizeDependencyVersion(range);
@@ -2911,7 +3035,7 @@ const discoverProject = (directory) => {
2911
3035
  const packageJsonPath = path.join(directory, "package.json");
2912
3036
  if (!isFile(packageJsonPath)) throw new PackageJsonNotFoundError(directory);
2913
3037
  const packageJson = readPackageJson(packageJsonPath);
2914
- let { reactVersion, tailwindVersion, framework } = extractDependencyInfo(packageJson);
3038
+ let { reactVersion, tailwindVersion, zodVersion, framework } = extractDependencyInfo(packageJson);
2915
3039
  const reactDeclaration = getDependencyDeclaration({
2916
3040
  packageJson,
2917
3041
  packageName: "react",
@@ -2930,9 +3054,19 @@ const discoverProject = (directory) => {
2930
3054
  "peerDependencies"
2931
3055
  ]
2932
3056
  });
3057
+ const zodDeclaration = getDependencyDeclaration({
3058
+ packageJson,
3059
+ packageName: "zod",
3060
+ sections: [
3061
+ "dependencies",
3062
+ "devDependencies",
3063
+ "peerDependencies"
3064
+ ]
3065
+ });
2933
3066
  if (!reactVersion && reactDeclaration.hasDeclaration) reactVersion = resolveCatalogVersion(packageJson, "react", directory, reactDeclaration.catalogReference);
2934
3067
  if (!tailwindVersion && tailwindDeclaration.hasDeclaration) tailwindVersion = resolveCatalogVersion(packageJson, "tailwindcss", directory, tailwindDeclaration.catalogReference);
2935
- if (!reactVersion || !tailwindVersion) {
3068
+ if (!zodVersion && zodDeclaration.hasDeclaration) zodVersion = resolveCatalogVersion(packageJson, "zod", directory, zodDeclaration.catalogReference);
3069
+ if (!reactVersion || !tailwindVersion || !zodVersion) {
2936
3070
  const monorepoRoot = findMonorepoRoot(directory);
2937
3071
  if (monorepoRoot) {
2938
3072
  const monorepoPackageJsonPath = path.join(monorepoRoot, "package.json");
@@ -2940,6 +3074,7 @@ const discoverProject = (directory) => {
2940
3074
  const rootPackageJson = readPackageJson(monorepoPackageJsonPath);
2941
3075
  if (!reactVersion && reactDeclaration.hasDeclaration) reactVersion = resolveCatalogVersion(rootPackageJson, "react", monorepoRoot, reactDeclaration.catalogReference);
2942
3076
  if (!tailwindVersion && tailwindDeclaration.hasDeclaration) tailwindVersion = resolveCatalogVersion(rootPackageJson, "tailwindcss", monorepoRoot, tailwindDeclaration.catalogReference);
3077
+ if (!zodVersion && zodDeclaration.hasDeclaration) zodVersion = resolveCatalogVersion(rootPackageJson, "zod", monorepoRoot, zodDeclaration.catalogReference);
2943
3078
  }
2944
3079
  }
2945
3080
  }
@@ -2947,16 +3082,19 @@ const discoverProject = (directory) => {
2947
3082
  const workspaceInfo = findReactInWorkspaces(directory, packageJson);
2948
3083
  if (!reactVersion && workspaceInfo.reactVersion) reactVersion = workspaceInfo.reactVersion;
2949
3084
  if (!tailwindVersion && workspaceInfo.tailwindVersion) tailwindVersion = workspaceInfo.tailwindVersion;
3085
+ if (!zodVersion && workspaceInfo.zodVersion) zodVersion = workspaceInfo.zodVersion;
2950
3086
  if (framework === "unknown" && workspaceInfo.framework !== "unknown") framework = workspaceInfo.framework;
2951
3087
  }
2952
3088
  if ((!reactVersion || framework === "unknown") && !isMonorepoRoot(directory)) {
2953
3089
  const monorepoInfo = findDependencyInfoFromMonorepoRoot(directory);
2954
3090
  if (!reactVersion) reactVersion = monorepoInfo.reactVersion;
2955
3091
  if (!tailwindVersion) tailwindVersion = monorepoInfo.tailwindVersion;
3092
+ if (!zodVersion) zodVersion = monorepoInfo.zodVersion;
2956
3093
  if (framework === "unknown") framework = monorepoInfo.framework;
2957
3094
  }
2958
3095
  if (!reactVersion && reactDeclaration.version && !isCatalogReference(reactDeclaration.version)) reactVersion = reactDeclaration.version;
2959
3096
  if (!tailwindVersion && tailwindDeclaration.version && !isCatalogReference(tailwindDeclaration.version)) tailwindVersion = tailwindDeclaration.version;
3097
+ if (!zodVersion && zodDeclaration.version && !isCatalogReference(zodDeclaration.version)) zodVersion = zodDeclaration.version;
2960
3098
  const projectName = packageJson.name ?? path.basename(directory);
2961
3099
  const hasTypeScript = fs.existsSync(path.join(directory, "tsconfig.json"));
2962
3100
  const sourceFileCount = countSourceFiles(directory);
@@ -2969,6 +3107,8 @@ const discoverProject = (directory) => {
2969
3107
  reactVersion,
2970
3108
  reactMajorVersion: resolveEffectiveReactMajor(reactVersion, packageJson),
2971
3109
  tailwindVersion,
3110
+ zodVersion,
3111
+ zodMajorVersion: parseZodMajor(zodVersion),
2972
3112
  framework,
2973
3113
  hasTypeScript,
2974
3114
  hasReactCompiler: detectReactCompiler(directory, packageJson),
@@ -5492,6 +5632,10 @@ const buildCapabilities = (project) => {
5492
5632
  minor: 4
5493
5633
  })) capabilities.add("tailwind:3.4");
5494
5634
  }
5635
+ if (project.zodVersion !== null) {
5636
+ capabilities.add("zod");
5637
+ if (project.zodMajorVersion !== null && project.zodMajorVersion >= 4) capabilities.add("zod:4");
5638
+ }
5495
5639
  if (project.hasReactCompiler) capabilities.add("react-compiler");
5496
5640
  if (project.hasTanStackQuery) capabilities.add("tanstack-query");
5497
5641
  if (project.hasTypeScript) capabilities.add("typescript");
@@ -7417,4 +7561,4 @@ const cliLogger = {
7417
7561
  //#endregion
7418
7562
  export { isMonorepoRoot as A, filterDiagnosticsForSurface as C, getDiffInfo as D, formatReactDoctorError as E, restoreLegacyThrow as F, runInspect as I, toRelativePath as L, layerOtlp as M, listWorkspacePackages as N, groupBy as O, resolveScanTarget as P, discoverReactSubprojects as S, formatErrorChain as T, Score as _, DeadCode as a, buildJsonReportError as b, LintPartialFailures as c, OXLINT_NODE_REQUIREMENT as d, Progress as f, SKILL_NAME as g, SHARE_BASE_URL as h, Config as i, isReactDoctorError as j, highlighter as k, Linter as l, Reporter as m, cli_logger_exports as n, Files as o, Project as p, CANONICAL_GITHUB_URL as r, Git as s, cliLogger as t, NodeResolver as u, StagedFiles as v, filterSourceFiles as w, buildRulePromptUrl as x, buildJsonReport as y };
7419
7563
 
7420
- //# sourceMappingURL=cli-logger-CmMJBgYF.js.map
7564
+ //# sourceMappingURL=cli-logger-CSZagq1E.js.map
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { i as __toESM, n as __exportAll, r as __require, t as __commonJSMin } from "./rolldown-runtime-uZX_iqCz.js";
2
- import { A as isMonorepoRoot, C as filterDiagnosticsForSurface, D as getDiffInfo, E as formatReactDoctorError, F as restoreLegacyThrow, I as runInspect, L as toRelativePath, M as layerOtlp, N as listWorkspacePackages, O as groupBy, P as resolveScanTarget, S as discoverReactSubprojects, T as formatErrorChain, _ as Score, a as DeadCode, b as buildJsonReportError, c as LintPartialFailures, d as OXLINT_NODE_REQUIREMENT, f as Progress, g as SKILL_NAME, h as SHARE_BASE_URL, i as Config, j as isReactDoctorError, k as highlighter, l as Linter, m as Reporter, o as Files, p as Project, r as CANONICAL_GITHUB_URL, s as Git, t as cliLogger, u as NodeResolver, v as StagedFiles, w as filterSourceFiles, x as buildRulePromptUrl, y as buildJsonReport } from "./cli-logger-CmMJBgYF.js";
2
+ import { A as isMonorepoRoot, C as filterDiagnosticsForSurface, D as getDiffInfo, E as formatReactDoctorError, F as restoreLegacyThrow, I as runInspect, L as toRelativePath, M as layerOtlp, N as listWorkspacePackages, O as groupBy, P as resolveScanTarget, S as discoverReactSubprojects, T as formatErrorChain, _ as Score, a as DeadCode, b as buildJsonReportError, c as LintPartialFailures, d as OXLINT_NODE_REQUIREMENT, f as Progress, g as SKILL_NAME, h as SHARE_BASE_URL, i as Config, j as isReactDoctorError, k as highlighter, l as Linter, m as Reporter, o as Files, p as Project, r as CANONICAL_GITHUB_URL, s as Git, t as cliLogger, u as NodeResolver, v as StagedFiles, w as filterSourceFiles, x as buildRulePromptUrl, y as buildJsonReport } from "./cli-logger-CSZagq1E.js";
3
3
  import { createRequire } from "node:module";
4
4
  import { execFileSync, execSync } from "node:child_process";
5
5
  import path, { join } from "node:path";
@@ -6674,7 +6674,7 @@ const resolveOxlintNodeEffect = (isLintEnabled, isQuiet) => Effect.gen(function*
6674
6674
  const resolveOxlintNode = (isLintEnabled, isQuiet) => Effect.runPromise(resolveOxlintNodeEffect(isLintEnabled, isQuiet).pipe(Effect.provide(NodeResolver.layerNode)));
6675
6675
  //#endregion
6676
6676
  //#region src/cli/utils/version.ts
6677
- const VERSION = "0.2.11-dev.b5cf767";
6677
+ const VERSION = "0.2.11-dev.b65b157";
6678
6678
  //#endregion
6679
6679
  //#region src/inspect.ts
6680
6680
  const silentConsole = makeNoopConsole();
@@ -7495,7 +7495,7 @@ const warnSetupPromptFailure = async (options, error) => {
7495
7495
  return;
7496
7496
  }
7497
7497
  try {
7498
- const { cliLogger } = await import("./cli-logger-CmMJBgYF.js").then((n) => n.n);
7498
+ const { cliLogger } = await import("./cli-logger-CSZagq1E.js").then((n) => n.n);
7499
7499
  cliLogger.warn(message);
7500
7500
  } catch {}
7501
7501
  };
package/dist/index.d.ts CHANGED
@@ -305,6 +305,9 @@ interface ProjectInfo {
305
305
  reactVersion: string | null;
306
306
  reactMajorVersion: number | null;
307
307
  tailwindVersion: string | null;
308
+ zodVersion: string | null;
309
+ /** Parsed major from `zodVersion`, or `null` when absent/unparseable. Mirrors `reactMajorVersion`. */
310
+ zodMajorVersion: number | null;
308
311
  framework: Framework;
309
312
  hasTypeScript: boolean;
310
313
  hasReactCompiler: boolean;
package/dist/index.js CHANGED
@@ -2307,15 +2307,91 @@ const detectFramework = (dependencies) => {
2307
2307
  if (dependencies.preact && !dependencies.react) return "preact";
2308
2308
  return "unknown";
2309
2309
  };
2310
- const UPPER_BOUND_COMPARATOR = /<\s*=?\s*\d+(?:\.\d+){0,2}(?:-[^\s,|]+)?/g;
2311
- const HAS_UPPER_BOUND_COMPARATOR = /<\s*=?\s*\d+(?:\.\d+){0,2}(?:-[^\s,|]+)?/;
2312
- const OR_SEPARATOR = /\s*\|\|\s*/;
2313
2310
  const UNRESOLVABLE_PROTOCOL_VERSION = /^(?:file|git|github|https?|link|patch|portal|workspace|npm):/i;
2314
2311
  const DIST_TAG_VERSION = /^[a-z][a-z0-9._-]*$/i;
2315
2312
  const WILDCARD_VERSION = /^[*xX](?:\.[*xX])*$/;
2316
- const NON_LOWER_BOUND_COMPARATOR = /(?:^|[\s,|])(?:>(?!=)|!={0,2})\s*\d/;
2317
- const LOWER_BOUND_MAJOR = /(?:^|[\s,|])(?:>=\s*|[~^=v]\s*)?(\d+)(?=$|[\s,|.*xX-])/g;
2318
2313
  const NPM_ALIAS_VERSION = /^npm:(?:@[^/]+\/[^@]+|[^@]+)@(.+)$/i;
2314
+ const isDigit = (value) => value !== void 0 && value >= "0" && value <= "9";
2315
+ const isWhitespace = (value) => value === " " || value === " " || value === "\n" || value === "\r" || value === "\f" || value === "\v";
2316
+ const isSeparator = (value) => isWhitespace(value) || value === "," || value === "|";
2317
+ const skipWhitespace = (value, start) => {
2318
+ let index = start;
2319
+ while (isWhitespace(value[index])) index += 1;
2320
+ return index;
2321
+ };
2322
+ const skipSeparators = (value, start) => {
2323
+ let index = start;
2324
+ while (isSeparator(value[index])) index += 1;
2325
+ return index;
2326
+ };
2327
+ const readDigits = (value, start) => {
2328
+ let index = start;
2329
+ while (isDigit(value[index])) index += 1;
2330
+ return index;
2331
+ };
2332
+ const getUpperBoundComparatorEnd = (version, start) => {
2333
+ if (version[start] !== "<") return null;
2334
+ let index = skipWhitespace(version, start + 1);
2335
+ if (version[index] === "=") index = skipWhitespace(version, index + 1);
2336
+ const majorStart = index;
2337
+ index = readDigits(version, index);
2338
+ if (index === majorStart) return null;
2339
+ for (let segments = 0; segments < 2 && version[index] === "."; segments += 1) {
2340
+ const segmentStart = index + 1;
2341
+ const segmentEnd = readDigits(version, segmentStart);
2342
+ if (segmentEnd === segmentStart) break;
2343
+ index = segmentEnd;
2344
+ }
2345
+ if (version[index] === "-") {
2346
+ index += 1;
2347
+ while (index < version.length && !isSeparator(version[index])) index += 1;
2348
+ }
2349
+ return index;
2350
+ };
2351
+ const stripUpperBoundComparators = (version) => {
2352
+ let stripped = "";
2353
+ let index = 0;
2354
+ while (index < version.length) {
2355
+ const comparatorEnd = getUpperBoundComparatorEnd(version, index);
2356
+ if (comparatorEnd === null) {
2357
+ stripped += version[index];
2358
+ index += 1;
2359
+ continue;
2360
+ }
2361
+ stripped += " ";
2362
+ index = comparatorEnd;
2363
+ }
2364
+ return stripped;
2365
+ };
2366
+ const hasNonLowerBoundComparator = (branch) => {
2367
+ for (let index = 0; index < branch.length; index += 1) {
2368
+ if (index > 0 && !isSeparator(branch[index - 1])) continue;
2369
+ if (branch[index] === ">" && branch[index + 1] !== "=") {
2370
+ if (isDigit(branch[skipWhitespace(branch, index + 1)])) return true;
2371
+ continue;
2372
+ }
2373
+ if (branch[index] !== "!") continue;
2374
+ let valueIndex = index + 1;
2375
+ if (branch[valueIndex] === "=") valueIndex += 1;
2376
+ if (branch[valueIndex] === "=") valueIndex += 1;
2377
+ valueIndex = skipWhitespace(branch, valueIndex);
2378
+ if (isDigit(branch[valueIndex])) return true;
2379
+ }
2380
+ return false;
2381
+ };
2382
+ const isMajorTerminator = (value) => value === void 0 || isSeparator(value) || value === "." || value === "*" || value === "x" || value === "X" || value === "-";
2383
+ const getLowerBoundMajorAt = (branch, start) => {
2384
+ let index = start;
2385
+ if (branch[index] === ">" && branch[index + 1] === "=") index = skipWhitespace(branch, index + 2);
2386
+ else if (branch[index] === "~" || branch[index] === "^" || branch[index] === "=" || branch[index] === "v") index = skipWhitespace(branch, index + 1);
2387
+ const majorStart = index;
2388
+ const majorEnd = readDigits(branch, majorStart);
2389
+ if (majorEnd === majorStart || !isMajorTerminator(branch[majorEnd])) return null;
2390
+ return {
2391
+ end: majorEnd,
2392
+ major: Number.parseInt(branch.slice(majorStart, majorEnd), 10)
2393
+ };
2394
+ };
2319
2395
  const normalizeDependencyVersion = (version) => {
2320
2396
  const trimmed = version.trim();
2321
2397
  if (trimmed.length === 0) return null;
@@ -2325,17 +2401,29 @@ const normalizeDependencyVersion = (version) => {
2325
2401
  if (WILDCARD_VERSION.test(normalizedVersion)) return null;
2326
2402
  return normalizedVersion;
2327
2403
  };
2328
- const splitDependencyVersionBranches = (version) => version.split(OR_SEPARATOR).filter(Boolean);
2329
- const hasUpperBoundComparator = (version) => HAS_UPPER_BOUND_COMPARATOR.test(version);
2404
+ const splitDependencyVersionBranches = (version) => version.split("||").map((branch) => branch.trim()).filter(Boolean);
2405
+ const hasUpperBoundComparator = (version) => {
2406
+ for (let index = 0; index < version.length; index += 1) if (getUpperBoundComparatorEnd(version, index) !== null) return true;
2407
+ return false;
2408
+ };
2330
2409
  const getBranchLowestMajor = (branch) => {
2331
- if (NON_LOWER_BOUND_COMPARATOR.test(branch)) return null;
2332
- const lowerBoundComparators = branch.replace(UPPER_BOUND_COMPARATOR, " ").trim();
2410
+ if (hasNonLowerBoundComparator(branch)) return null;
2411
+ const lowerBoundComparators = stripUpperBoundComparators(branch).trim();
2333
2412
  if (lowerBoundComparators.length === 0) return null;
2334
2413
  let branchLowestMajor = null;
2335
- for (const match of lowerBoundComparators.matchAll(LOWER_BOUND_MAJOR)) {
2336
- const major = Number.parseInt(match[1], 10);
2337
- if (!Number.isFinite(major) || major <= 0) continue;
2338
- if (branchLowestMajor === null || major < branchLowestMajor) branchLowestMajor = major;
2414
+ let index = 0;
2415
+ while (index < lowerBoundComparators.length) {
2416
+ const lowerBoundStart = skipSeparators(lowerBoundComparators, index);
2417
+ if (lowerBoundStart > 0 && !isSeparator(lowerBoundComparators[lowerBoundStart - 1])) {
2418
+ index = lowerBoundStart + 1;
2419
+ continue;
2420
+ }
2421
+ const lowerBoundMajor = getLowerBoundMajorAt(lowerBoundComparators, lowerBoundStart);
2422
+ if (lowerBoundMajor !== null && Number.isFinite(lowerBoundMajor.major) && lowerBoundMajor.major > 0) {
2423
+ const major = lowerBoundMajor.major;
2424
+ if (branchLowestMajor === null || major < branchLowestMajor) branchLowestMajor = major;
2425
+ }
2426
+ index = lowerBoundMajor?.end ?? lowerBoundStart + 1;
2339
2427
  }
2340
2428
  return branchLowestMajor;
2341
2429
  };
@@ -2504,6 +2592,7 @@ const resolveCatalogVersion = (packageJson, packageName, rootDirectory, explicit
2504
2592
  const EMPTY_DEPENDENCY_INFO = {
2505
2593
  reactVersion: null,
2506
2594
  tailwindVersion: null,
2595
+ zodVersion: null,
2507
2596
  framework: "unknown"
2508
2597
  };
2509
2598
  const pickConcreteVersion = (packageJson, packageName, sections) => {
@@ -2532,6 +2621,11 @@ const extractDependencyInfo = (packageJson) => {
2532
2621
  "devDependencies",
2533
2622
  "peerDependencies"
2534
2623
  ]),
2624
+ zodVersion: pickConcreteVersion(packageJson, "zod", [
2625
+ "dependencies",
2626
+ "devDependencies",
2627
+ "peerDependencies"
2628
+ ]),
2535
2629
  framework: detectFramework(allDependencies)
2536
2630
  };
2537
2631
  };
@@ -2676,8 +2770,22 @@ const findReactInWorkspaces = (rootDirectory, packageJson) => {
2676
2770
  workspaceDirectory,
2677
2771
  workspacePackageJson
2678
2772
  });
2773
+ const zodVersion = resolveWorkspaceDependencyVersion({
2774
+ concreteVersion: info.zodVersion,
2775
+ packageName: "zod",
2776
+ rootDirectory,
2777
+ rootPackageJson: packageJson,
2778
+ sections: [
2779
+ "dependencies",
2780
+ "devDependencies",
2781
+ "peerDependencies"
2782
+ ],
2783
+ workspaceDirectory,
2784
+ workspacePackageJson
2785
+ });
2679
2786
  if (reactVersion && shouldReplaceReactVersion(result.reactVersion, reactVersion)) result.reactVersion = reactVersion;
2680
2787
  if (tailwindVersion && !result.tailwindVersion) result.tailwindVersion = tailwindVersion;
2788
+ if (zodVersion && !result.zodVersion) result.zodVersion = zodVersion;
2681
2789
  if (info.framework !== "unknown" && result.framework === "unknown") result.framework = info.framework;
2682
2790
  const resultReactMajor = parseReactMajor(result.reactVersion);
2683
2791
  if (result.reactVersion && result.tailwindVersion && result.framework !== "unknown" && resultReactMajor !== null && resultReactMajor <= 17) return result;
@@ -2712,14 +2820,26 @@ const findDependencyInfoFromMonorepoRoot = (directory) => {
2712
2820
  "peerDependencies"
2713
2821
  ]
2714
2822
  }) : null;
2823
+ const leafZodDeclaration = leafPackageJson ? getDependencyDeclaration({
2824
+ packageJson: leafPackageJson,
2825
+ packageName: "zod",
2826
+ sections: [
2827
+ "dependencies",
2828
+ "devDependencies",
2829
+ "peerDependencies"
2830
+ ]
2831
+ }) : null;
2715
2832
  const shouldUseReactFallback = !leafReactDeclaration?.hasDeclaration;
2716
2833
  const shouldUseTailwindFallback = leafTailwindDeclaration?.hasDeclaration ?? true;
2834
+ const shouldUseZodFallback = leafZodDeclaration?.hasDeclaration ?? true;
2717
2835
  const reactCatalogVersion = shouldUseReactFallback ? resolveCatalogVersion(rootPackageJson, "react", monorepoRoot, leafReactDeclaration?.catalogReference) : null;
2718
2836
  const tailwindCatalogVersion = shouldUseTailwindFallback ? resolveCatalogVersion(rootPackageJson, "tailwindcss", monorepoRoot, leafTailwindDeclaration?.catalogReference) : null;
2837
+ const zodCatalogVersion = shouldUseZodFallback ? resolveCatalogVersion(rootPackageJson, "zod", monorepoRoot, leafZodDeclaration?.catalogReference) : null;
2719
2838
  const workspaceInfo = findReactInWorkspaces(monorepoRoot, rootPackageJson);
2720
2839
  return {
2721
2840
  reactVersion: shouldUseReactFallback ? reactCatalogVersion ?? rootInfo.reactVersion ?? workspaceInfo.reactVersion : rootInfo.reactVersion ?? workspaceInfo.reactVersion,
2722
2841
  tailwindVersion: shouldUseTailwindFallback ? tailwindCatalogVersion ?? rootInfo.tailwindVersion ?? workspaceInfo.tailwindVersion : null,
2842
+ zodVersion: shouldUseZodFallback ? zodCatalogVersion ?? rootInfo.zodVersion ?? workspaceInfo.zodVersion : null,
2723
2843
  framework: rootInfo.framework !== "unknown" ? rootInfo.framework : workspaceInfo.framework
2724
2844
  };
2725
2845
  };
@@ -2799,6 +2919,10 @@ const isPackageJsonReanimatedAware = (packageJson) => {
2799
2919
  };
2800
2920
  return Object.hasOwn(allDependencies, REANIMATED_DEPENDENCY_NAME);
2801
2921
  };
2922
+ const parseZodMajor = (zodVersion) => {
2923
+ if (typeof zodVersion !== "string") return null;
2924
+ return getLowestDependencyMajor(zodVersion);
2925
+ };
2802
2926
  const hasUpperBoundOnlyPeerRange = (range) => {
2803
2927
  if (typeof range !== "string") return false;
2804
2928
  const normalizedRange = normalizeDependencyVersion(range);
@@ -2940,7 +3064,7 @@ const discoverProject = (directory) => {
2940
3064
  const packageJsonPath = path.join(directory, "package.json");
2941
3065
  if (!isFile(packageJsonPath)) throw new PackageJsonNotFoundError(directory);
2942
3066
  const packageJson = readPackageJson(packageJsonPath);
2943
- let { reactVersion, tailwindVersion, framework } = extractDependencyInfo(packageJson);
3067
+ let { reactVersion, tailwindVersion, zodVersion, framework } = extractDependencyInfo(packageJson);
2944
3068
  const reactDeclaration = getDependencyDeclaration({
2945
3069
  packageJson,
2946
3070
  packageName: "react",
@@ -2959,9 +3083,19 @@ const discoverProject = (directory) => {
2959
3083
  "peerDependencies"
2960
3084
  ]
2961
3085
  });
3086
+ const zodDeclaration = getDependencyDeclaration({
3087
+ packageJson,
3088
+ packageName: "zod",
3089
+ sections: [
3090
+ "dependencies",
3091
+ "devDependencies",
3092
+ "peerDependencies"
3093
+ ]
3094
+ });
2962
3095
  if (!reactVersion && reactDeclaration.hasDeclaration) reactVersion = resolveCatalogVersion(packageJson, "react", directory, reactDeclaration.catalogReference);
2963
3096
  if (!tailwindVersion && tailwindDeclaration.hasDeclaration) tailwindVersion = resolveCatalogVersion(packageJson, "tailwindcss", directory, tailwindDeclaration.catalogReference);
2964
- if (!reactVersion || !tailwindVersion) {
3097
+ if (!zodVersion && zodDeclaration.hasDeclaration) zodVersion = resolveCatalogVersion(packageJson, "zod", directory, zodDeclaration.catalogReference);
3098
+ if (!reactVersion || !tailwindVersion || !zodVersion) {
2965
3099
  const monorepoRoot = findMonorepoRoot(directory);
2966
3100
  if (monorepoRoot) {
2967
3101
  const monorepoPackageJsonPath = path.join(monorepoRoot, "package.json");
@@ -2969,6 +3103,7 @@ const discoverProject = (directory) => {
2969
3103
  const rootPackageJson = readPackageJson(monorepoPackageJsonPath);
2970
3104
  if (!reactVersion && reactDeclaration.hasDeclaration) reactVersion = resolveCatalogVersion(rootPackageJson, "react", monorepoRoot, reactDeclaration.catalogReference);
2971
3105
  if (!tailwindVersion && tailwindDeclaration.hasDeclaration) tailwindVersion = resolveCatalogVersion(rootPackageJson, "tailwindcss", monorepoRoot, tailwindDeclaration.catalogReference);
3106
+ if (!zodVersion && zodDeclaration.hasDeclaration) zodVersion = resolveCatalogVersion(rootPackageJson, "zod", monorepoRoot, zodDeclaration.catalogReference);
2972
3107
  }
2973
3108
  }
2974
3109
  }
@@ -2976,16 +3111,19 @@ const discoverProject = (directory) => {
2976
3111
  const workspaceInfo = findReactInWorkspaces(directory, packageJson);
2977
3112
  if (!reactVersion && workspaceInfo.reactVersion) reactVersion = workspaceInfo.reactVersion;
2978
3113
  if (!tailwindVersion && workspaceInfo.tailwindVersion) tailwindVersion = workspaceInfo.tailwindVersion;
3114
+ if (!zodVersion && workspaceInfo.zodVersion) zodVersion = workspaceInfo.zodVersion;
2979
3115
  if (framework === "unknown" && workspaceInfo.framework !== "unknown") framework = workspaceInfo.framework;
2980
3116
  }
2981
3117
  if ((!reactVersion || framework === "unknown") && !isMonorepoRoot(directory)) {
2982
3118
  const monorepoInfo = findDependencyInfoFromMonorepoRoot(directory);
2983
3119
  if (!reactVersion) reactVersion = monorepoInfo.reactVersion;
2984
3120
  if (!tailwindVersion) tailwindVersion = monorepoInfo.tailwindVersion;
3121
+ if (!zodVersion) zodVersion = monorepoInfo.zodVersion;
2985
3122
  if (framework === "unknown") framework = monorepoInfo.framework;
2986
3123
  }
2987
3124
  if (!reactVersion && reactDeclaration.version && !isCatalogReference(reactDeclaration.version)) reactVersion = reactDeclaration.version;
2988
3125
  if (!tailwindVersion && tailwindDeclaration.version && !isCatalogReference(tailwindDeclaration.version)) tailwindVersion = tailwindDeclaration.version;
3126
+ if (!zodVersion && zodDeclaration.version && !isCatalogReference(zodDeclaration.version)) zodVersion = zodDeclaration.version;
2989
3127
  const projectName = packageJson.name ?? path.basename(directory);
2990
3128
  const hasTypeScript = fs.existsSync(path.join(directory, "tsconfig.json"));
2991
3129
  const sourceFileCount = countSourceFiles(directory);
@@ -2998,6 +3136,8 @@ const discoverProject = (directory) => {
2998
3136
  reactVersion,
2999
3137
  reactMajorVersion: resolveEffectiveReactMajor(reactVersion, packageJson),
3000
3138
  tailwindVersion,
3139
+ zodVersion,
3140
+ zodMajorVersion: parseZodMajor(zodVersion),
3001
3141
  framework,
3002
3142
  hasTypeScript,
3003
3143
  hasReactCompiler: detectReactCompiler(directory, packageJson),
@@ -5522,6 +5662,10 @@ const buildCapabilities = (project) => {
5522
5662
  minor: 4
5523
5663
  })) capabilities.add("tailwind:3.4");
5524
5664
  }
5665
+ if (project.zodVersion !== null) {
5666
+ capabilities.add("zod");
5667
+ if (project.zodMajorVersion !== null && project.zodMajorVersion >= 4) capabilities.add("zod:4");
5668
+ }
5525
5669
  if (project.hasReactCompiler) capabilities.add("react-compiler");
5526
5670
  if (project.hasTanStackQuery) capabilities.add("tanstack-query");
5527
5671
  if (project.hasTypeScript) capabilities.add("typescript");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-doctor",
3
- "version": "0.2.11-dev.b5cf767",
3
+ "version": "0.2.11-dev.b65b157",
4
4
  "description": "Diagnose and fix React codebases for security, performance, correctness, accessibility, bundle-size, and architecture issues",
5
5
  "keywords": [
6
6
  "accessibility",
@@ -58,7 +58,7 @@
58
58
  "oxlint": "^1.66.0",
59
59
  "prompts": "^2.4.2",
60
60
  "typescript": ">=5.0.4 <7",
61
- "oxlint-plugin-react-doctor": "0.2.11-dev.b5cf767"
61
+ "oxlint-plugin-react-doctor": "0.2.11-dev.b65b157"
62
62
  },
63
63
  "devDependencies": {
64
64
  "@types/prompts": "^2.4.9",