import { CodegenMode } from "../core/codegen/context.js"; import { HoistOptions } from "./hoist.js"; //#region src/unplugin/types.d.ts interface TransformOptions { mode: CodegenMode; runtimeId?: string | undefined; zodCompat?: boolean | undefined; /** Compact output: compile only the fast path; delegate cold errors to the retained Zod schema. */ compact?: boolean | undefined; verbose?: boolean | undefined; autoDiscover?: boolean | undefined; hoist?: boolean | HoistOptions | undefined; onBuildStats?: (stats: BuildStats) => void; /** Fired when discovery (file execution) is about to run — used by the disk cache to decide which results are worth persisting. */ onDiscovery?: () => void; /** * Fired when the transform did parse-level work short of discovery (hoist * source scan, static export filter). The disk cache persists even null * results for these files: re-deriving "no transform needed" costs a full * scan per zod-importing file per run (a field report measured 35.8s/run * of hoist scans in hoist-only mode, all producing never-cached nulls). * Purely textual bail-outs never fire this — caching those would trade a * substring check for a disk entry per source file in the project. */ onSubstantialWork?: () => void; /** * Fired when discovery was aborted by a build-time `process.exit` (an * env-validation guard in a secret-less build). The recovered result is a * function of the *environment*, not the file content, so the disk cache * must NOT persist it keyed on content hashes: a later build with secrets * present would otherwise be served the stale "nothing compiled" entry and * silently ship un-optimized schemas. The in-memory (content-keyed, single- * process) cache is unaffected. */ onUncacheableResult?: () => void; } interface BuildStats { files: number; schemas: number; optimized: number; failed: number; } declare class BuildStatsAccumulator implements BuildStats { files: number; schemas: number; optimized: number; failed: number; add(s: BuildStats): void; reset(): void; } interface ZodCompilerPluginOptions { /** Glob patterns to include (default: ["**\/*.ts", "**\/*.tsx"]) */ include?: string[]; /** Glob patterns to exclude (default: ["node_modules/**", "**\/*.d.ts"]) */ exclude?: string[]; /** * How schemas are found. * * - `"auto"` (default): every exported plain Zod schema compiles — no * wrappers, no zod-compiler imports in source. Detection scans files * with a runtime `import ... from "zod"`, statically pre-filters ones * whose exports provably aren't schemas, and executes the remaining * candidates to check exports for `_zod.def`. Also enables build-time * compilation of hoisted in-function schemas (anonymous schemas like * `sql.type(z.object(...))` — there is no exported name to opt in with). * - `"explicit"`: only schemas wrapped in `compile()` from zod-compiler * are compiled; build-time file execution is limited to files that * import it. Hoisting still applies, but hoisted schemas stay plain Zod. * * **Note:** in `"auto"` mode, candidate files are executed at build time * via `loadSourceFile()`. Use `include` to limit scope if your project * has schema-shaped files with side effects. A module that calls * `process.exit()` during this execution (e.g. an env-validation guard in a * secret-less CI build) is caught — the file falls back to runtime Zod * instead of crashing the build. Guard the exit on `process.env.ZOD_COMPILER` * to keep such schemas compiled. * @default "auto" */ schemas?: "explicit" | "auto" | undefined; /** * What a compiled schema export evaluates to. * * - `"schema"` (default): the original Zod schema object with the compiled * `parse`/`safeParse`/`parseAsync`/`safeParseAsync` installed as own * properties — identity is preserved, so `.shape`, `.meta()`, * `z.toJSONSchema()`, `instanceof`, Standard Schema, and libraries like * @hono/zod-validator and tRPC keep working. * - `"bag"`: a minimal plain object with just the compiled methods — * smaller bundles (the Zod construction can tree-shake away), but * anything expecting a real Zod schema breaks. * - `"compact"`: like `"schema"` (Zod identity preserved, full API), but only * the fast validation path is compiled. The cold error path is delegated to * the retained Zod schema's own `safeParse`, dropping the compiled slow walk * (64–77% of generated bytes). Errors are byte-identical to Zod's (no second * validation engine). The hot path — `parse`/`safeParse` of valid input and * `.is()` — is unchanged; only error *reporting* on invalid input runs Zod * (deferred until `.error` is read, so `.success`/`.is()` checks stay fast). * Modelled rewrites (`default`/`coerce`/`transform`/string overwrites) keep * their compiled build pass and delegate only issue production; unmodelled * mutations such as `catch`, URL normalization and `superRefine` keep the * fully compiled path. Best for large schema sets where bundle size dominates. * @default "schema" */ output?: "schema" | "bag" | "compact" | undefined; /** * Enable verbose logging during build. * Logs per-schema compilation status and a build summary. * @default false */ verbose?: boolean | undefined; /** * Hoist Zod schema construction out of function bodies to module scope * (the babel-plugin-zod-hoist optimization). A schema defined inside a * function — a React component, a request handler — is otherwise rebuilt * on every call: * * ```typescript * function getSchema() { * return z.object({ name: z.string() }); // rebuilt per call * } * // becomes * const _zh_94b7f5c1 = z.object({ name: z.string() }); * function getSchema() { * return _zh_94b7f5c1; // built once * } * ``` * * Only expressions built purely from imported bindings and literals are * hoisted — anything referencing local variables, module-level bindings, * `this`, or globals stays put (safe globals like `Number` are allowed * inside callbacks, which run per call regardless). Identical schemas * dedupe to one binding. * * Pass an object to configure `schemaNamePattern` (default `/ZodSchema$/`): * imported identifiers matching it are hoistable combinator-chain roots * even without an inline z.* reference (`UserZodSchema.partial()`). Set it * to `null` to disable name-based matching. * @default true */ hoist?: boolean | { schemaNamePattern?: RegExp | string | null | undefined; } | undefined; /** * **Vite only** (other bundlers ignore this option): when the plugin runs. * * By default the plugin compiles production builds **and test runs** * (Vitest is detected via the `VITEST` env var / `"test"` mode), so tests * exercise — and benefit from — the same compiled validators that ship. * Plain dev servers skip AOT compilation cost; `compile()` transparently * falls back to Zod's runtime validation there, so behavior stays correct. * * Set `"build"` to also skip tests, `"serve"` for dev/test-only, or * `"all"` to compile everywhere including the dev server. * @default builds + Vitest */ apply?: "build" | "serve" | "all" | undefined; /** * Override the codegen mode used by the plugin. * * - `"lean"` (default for bundlers): runtime helpers are imported from * `virtual:zod-compiler/runtime`, which the bundler resolves via its * `onResolve`/`onLoad` hooks. Produces smaller per-file output and lets * the bundler tree-shake unused helpers. * - `"inline"`: runtime helpers are emitted directly into each transformed * file. Use this for transpile-only builds (e.g. `esbuild` without * `--bundle`, `astro-scripts build`) where the bundler's module hooks * never fire for already-transformed output and the `virtual:` specifier * would survive into `dist/` causing `ERR_UNSUPPORTED_ESM_URL_SCHEME`. * * @default determined by bundler */ codegenMode?: "lean" | "inline" | undefined; /** * Run transforms on worker threads instead of the bundler's own (Node.js only). * * Schema discovery executes each file's import graph in-process, and the * loader serializes those executions so concurrent transforms cannot * double-execute a shared dependency — so a cold build compiles one file at * a time on one thread. Workers lift that limit: each owns a private loader * and module cache, which is exactly why running several concurrently is * sound. Measured on 120 files of deeply nested schemas, 3,633 ms sequential * became 1,508 ms at four workers. * * **Whether it pays depends on your import graph, not your core count.** A * dependency shared by many schema files is executed once in-process and * once PER WORKER here, so files with independent graphs win big and files * chained through each other can lose: the same fixture rewired into a * 120-deep import chain measured 1,045 ms at four workers against 945 ms * in-process. Measure with `ZOD_COMPILER_TIMING=1` before adopting it. * * Opt-in for that reason and for memory: every worker holds its own copy of * zod and of the graph it executed. That compounds with runners which * already shard across processes (Vitest pools, Nx, Turborepo), where the * cores are spoken for. * * - `false` (default): transforms run in-process, exactly as before. * - `true`: one worker per core, less one for the bundler, capped at 4 — * throughput peaks around four and declines past it. * - ``: exactly that many workers (1-32). * * Falls back to in-process transforms — with a warning — if workers cannot * be started. The disk cache, the static dependency crawl and all result * bookkeeping stay on the bundler thread either way, so cache contents and * emitted output are identical to a serial build. * @default false */ parallel?: boolean | number | undefined; /** * Persistent transform-result cache (Node.js only). * * Schema discovery executes schema files (and their import graphs) inside * the bundler process; without a disk cache that cost is re-paid on every * `vitest run` / build even when nothing changed. Entries are validated * against content hashes of every first-party module the discovery * executed, so edits to a schema file *or any file it imports* invalidate * exactly as the in-memory watch invalidation does — but across processes. * * Caveat: module-scope dynamism in schema files (schemas derived from * `process.env`, `Date.now()`, …) is frozen until a watched source file * changes. Disable the cache for such setups. * * Pass a string to use a custom cache directory. * @default true (node_modules/.cache/zod-compiler) */ cache?: boolean | string | undefined; } //#endregion export { BuildStats, BuildStatsAccumulator, TransformOptions, ZodCompilerPluginOptions }; //# sourceMappingURL=types.d.ts.map