zod-compiler 1.26.4 → 1.28.0
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/README.md +77 -1
- package/dist/jit.d.ts +2 -0
- package/dist/jit.d.ts.map +1 -1
- package/dist/jit.js +9 -6
- package/dist/jit.js.map +1 -1
- package/dist/register/config.d.ts +20 -0
- package/dist/register/config.d.ts.map +1 -0
- package/dist/register/config.js +1 -0
- package/dist/register/index.d.ts +1 -0
- package/dist/register/index.js +68 -0
- package/dist/register/index.js.map +1 -0
- package/dist/register/transform.d.ts +16 -0
- package/dist/register/transform.d.ts.map +1 -0
- package/dist/register/transform.js +148 -0
- package/dist/register/transform.js.map +1 -0
- package/dist/swc.d.ts +5 -1
- package/dist/swc.d.ts.map +1 -1
- package/dist/swc.js.map +1 -1
- package/dist/turbopack.d.ts +6 -1
- package/dist/turbopack.d.ts.map +1 -1
- package/dist/turbopack.js.map +1 -1
- package/dist/unplugin/disk-cache.d.ts.map +1 -1
- package/dist/unplugin/disk-cache.js +1 -0
- package/dist/unplugin/disk-cache.js.map +1 -1
- package/dist/unplugin/index.d.ts.map +1 -1
- package/dist/unplugin/index.js +56 -9
- package/dist/unplugin/index.js.map +1 -1
- package/dist/unplugin/pool-worker.d.ts +1 -0
- package/dist/unplugin/pool-worker.js +116 -0
- package/dist/unplugin/pool-worker.js.map +1 -0
- package/dist/unplugin/pool.d.ts +162 -0
- package/dist/unplugin/pool.d.ts.map +1 -0
- package/dist/unplugin/pool.js +445 -0
- package/dist/unplugin/pool.js.map +1 -0
- package/dist/unplugin/transform.d.ts +2 -1
- package/dist/unplugin/transform.d.ts.map +1 -1
- package/dist/unplugin/transform.js +1 -1
- package/dist/unplugin/transform.js.map +1 -1
- package/dist/unplugin/types.d.ts +36 -1
- package/dist/unplugin/types.d.ts.map +1 -1
- package/dist/unplugin/types.js.map +1 -1
- package/package.json +12 -1
- package/schema.json +80 -0
|
@@ -47,6 +47,7 @@ declare const TRANSFORM_ID_FILTER: {
|
|
|
47
47
|
*/
|
|
48
48
|
declare function transformCodeFilter(options?: ZodCompilerPluginOptions): RegExp | undefined;
|
|
49
49
|
declare function log(msg: string): void;
|
|
50
|
+
declare function warn(msg: string): void;
|
|
50
51
|
interface TransformOutput {
|
|
51
52
|
code: string;
|
|
52
53
|
map: TransformSourceMap | null;
|
|
@@ -87,5 +88,5 @@ declare function rewriteSourceAutoDiscover(code: string, schemas: CompiledSchema
|
|
|
87
88
|
*/
|
|
88
89
|
declare function removeCompileImport(code: string): string;
|
|
89
90
|
//#endregion
|
|
90
|
-
export { HAS_RUNTIME_ZOD_IMPORT, TRANSFORM_ID_FILTER, TransformOutput, TransformSourceMap, findExpressionEnd, log, removeCompileImport, rewriteSource, rewriteSourceAutoDiscover, shouldTransform, transformCode, transformCodeFilter, transformCodeWithMap };
|
|
91
|
+
export { HAS_RUNTIME_ZOD_IMPORT, TRANSFORM_ID_FILTER, TransformOutput, TransformSourceMap, findExpressionEnd, log, removeCompileImport, rewriteSource, rewriteSourceAutoDiscover, shouldTransform, transformCode, transformCodeFilter, transformCodeWithMap, warn };
|
|
91
92
|
//# sourceMappingURL=transform.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform.d.ts","names":[],"sources":["../../src/unplugin/transform.ts"],"mappings":";;;;UA8BiB;EACf;EACA;EACA;EACA;EACA;EACA;;;;;;;;;;cAsHW,wBAAsB;;;;iBAgDnB,gBAAgB,YAAY,UAAU;;;;;;;;;;;;;cA6BzC;EAAuB,SAAS;EAAU,SAAS;;;;;;;;iBAgChD,oBAAoB,UAAU,2BAA2B;iBAOzD,IAAI;
|
|
1
|
+
{"version":3,"file":"transform.d.ts","names":[],"sources":["../../src/unplugin/transform.ts"],"mappings":";;;;UA8BiB;EACf;EACA;EACA;EACA;EACA;EACA;;;;;;;;;;cAsHW,wBAAsB;;;;iBAgDnB,gBAAgB,YAAY,UAAU;;;;;;;;;;;;;cA6BzC;EAAuB,SAAS;EAAU,SAAS;;;;;;;;iBAgChD,oBAAoB,UAAU,2BAA2B;iBAOzD,IAAI;iBAKJ,KAAK;UAKJ;EACf;EACA,KAAK;;;;;;;iBAQe,cACpB,cACA,YACA,SAAS,mBACR;;;;;;iBAUmB,qBACpB,cACA,YACA,SAAS,mBACR,QAAQ;;;;iBAqXK,cACd,cACA,SAAS,sBACT;EAAY;;;;;;iBAiDE,kBAAkB,cAAc;;;;;iBAgBhC,0BACd,cACA,SAAS,sBACT;EAAY;;;;;;iBAgGE,oBAAoB"}
|
|
@@ -531,6 +531,6 @@ function collectRemoveCompileImportEdits(code) {
|
|
|
531
531
|
return edits;
|
|
532
532
|
}
|
|
533
533
|
//#endregion
|
|
534
|
-
export { HAS_RUNTIME_ZOD_IMPORT, TRANSFORM_ID_FILTER, findExpressionEnd, log, removeCompileImport, rewriteSource, rewriteSourceAutoDiscover, shouldTransform, transformCode, transformCodeFilter, transformCodeWithMap };
|
|
534
|
+
export { HAS_RUNTIME_ZOD_IMPORT, TRANSFORM_ID_FILTER, findExpressionEnd, log, removeCompileImport, rewriteSource, rewriteSourceAutoDiscover, shouldTransform, transformCode, transformCodeFilter, transformCodeWithMap, warn };
|
|
535
535
|
|
|
536
536
|
//# sourceMappingURL=transform.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform.js","names":[],"sources":["../../src/unplugin/transform.ts"],"sourcesContent":["import remapping from \"@jridgewell/remapping\";\nimport { parseExpressionAt } from \"acorn\";\nimport MagicString from \"magic-string\";\nimport picomatch from \"picomatch\";\nimport type { CodegenMode } from \"../core/codegen/context.js\";\nimport { SHARED_BLOCK_MARKER } from \"../core/codegen/dedupe.js\";\nimport {\n FAIL_CLASS_DECL,\n FAILZ_CLASS_DECL,\n FIN_DECL,\n FIN_DEFERRED_DECL,\n FINZ_DECL,\n generateIIFE,\n iifeDerefsSchema,\n MK_VALIDATOR_DECL,\n ZOD_CONFIG_IMPORT,\n ZOD_MSG_DECLARATION,\n} from \"../core/iife.js\";\nimport { aggregateUsedHelpers, type CompiledSchemaInfo, compileSchemas } from \"../core/pipeline.js\";\nimport type { DiscoveredSchema } from \"../core/types.js\";\nimport { discoverSchemas } from \"../discovery.js\";\nimport { ProcessExitDuringLoadError } from \"../loader.js\";\nimport { mayExportSchemas } from \"../static-filter.js\";\nimport { applyEdits, type Edit, type Insertion, moduleHeadOffset } from \"./edits.js\";\nimport { hoistZodSchemasMeta } from \"./hoist.js\";\nimport { compileHoistedSchemas } from \"./hoist-compile.js\";\nimport type { TransformOptions, ZodCompilerPluginOptions } from \"./types.js\";\nimport { VIRTUAL_RUNTIME_ID } from \"./virtual.js\";\n\n/** JSON shape of the composed sourcemap returned alongside transformed code. */\nexport interface TransformSourceMap {\n version: number;\n sources: (string | null)[];\n sourcesContent?: (string | null)[];\n names: string[];\n mappings: string;\n file?: string | null;\n}\n\n/**\n * The transform pipeline as a chain of edit batches. Each batch is applied\n * to the CURRENT text through a MagicString (one stage map per batch); the\n * final original→output map is the remapping-composed chain. Deriving the\n * output string and the map from the same edit list makes divergence\n * impossible.\n */\nclass StagedTransform {\n current: string;\n private readonly source: string;\n private readonly maps: unknown[] = [];\n\n constructor(original: string, source: string) {\n this.current = original;\n this.source = source;\n }\n\n apply(edits: readonly Edit[], insert?: Insertion): void {\n this.stage(edits, (s) => {\n if (insert === undefined) return false;\n s.appendLeft(insert.offset, insert.text);\n return true;\n });\n }\n\n /**\n * Apply `edits`, then prepend `deferred`'s text to the module head — both\n * inside ONE stage.\n *\n * The head injection (runtime import + shared dedup block) has to be decided\n * from the REWRITTEN source, because `computeRuntimePrefix` probes it for\n * already-present markers. Staging it separately made a whole second\n * `generateMap` over the full generated output — for what is only a prepend at\n * the module head — and then forced `remapping` to compose the two. Together\n * those were the dominant cost of a transform: on a 320-schema project they ran\n * to 64% of total wall time, more than discovery and codegen combined. Deferring\n * the insertion into the same MagicString buys byte-identical output and an\n * equivalent map for one generation and no composition — 1.6x (small schemas)\n * to 3.4x (large ones) on the transform, scaling with how much code a file\n * emits, since that is what both costs are proportional to.\n *\n * `deferred` returns TEXT, not an `Insertion`: `appendLeft` resolves offsets\n * against the PRE-edit text while `deferred` is shown the POST-edit text, so a\n * callback-supplied offset would be in the wrong coordinate system. Deriving it\n * here from `this.current` keeps the two in step by construction.\n */\n applyThen(edits: readonly Edit[], deferred?: (rewritten: string) => string | undefined): void {\n this.stage(edits, (s, rewritten) => {\n // `?.()` short-circuits its arguments, so a stage with no deferred step\n // never materializes the rewritten text.\n const head = deferred?.(rewritten());\n if (head === undefined) return false;\n s.appendLeft(moduleHeadOffset(this.current), head);\n return true;\n });\n }\n\n /**\n * One stage: apply `edits` to a fresh MagicString, let `inject` add at most\n * one insertion, then commit the text and its map. `inject` reports whether\n * it inserted, so a no-op stage can be skipped entirely.\n *\n * `rewritten` is a thunk, not a string: materializing it costs a full\n * `toString()` over generated-code-sized input, and the injectors that do not\n * read it (every `apply()` call) must not pay for it.\n */\n private stage(\n edits: readonly Edit[],\n inject: (s: MagicString, rewritten: () => string) => boolean,\n ): void {\n const s = new MagicString(this.current);\n for (const e of edits) {\n if (e.start === e.end) {\n s.appendLeft(e.start, e.text);\n } else {\n s.overwrite(e.start, e.end, e.text);\n }\n }\n // `toString()` is the only way to show the deferred step what the rewrite\n // produced; it measured well under 1% of a transform.\n const inserted = inject(s, () => (edits.length === 0 ? this.current : s.toString()));\n if (edits.length === 0 && !inserted) return;\n this.current = s.toString();\n // `hires: \"boundary\"` is load-bearing, not a tuning knob: without it every\n // mapping collapses to column 0, so a stack frame or debugger breakpoint in\n // untouched user code below a compiled schema lands at the start of its line\n // instead of the right column (tests/unplugin/sourcemap.test.ts pins it). It\n // is also the most expensive thing here, which is why the stage COUNT is\n // what to economize on.\n this.maps.push(s.generateMap({ source: this.source, hires: \"boundary\", includeContent: true }));\n }\n\n /** Composed original→current map, or null when nothing was applied. */\n map(): TransformSourceMap | null {\n if (this.maps.length === 0) return null;\n // A single stage needs no composition: `remapping` over a one-map chain\n // reproduces that map, and it is expensive on generated-code-sized input.\n const [only] = this.maps;\n if (this.maps.length === 1) return only as TransformSourceMap;\n const chain = [...this.maps].reverse();\n return remapping(\n chain as Parameters<typeof remapping>[0],\n () => null,\n ) as unknown as TransformSourceMap;\n }\n}\n\n/**\n * Matches a runtime (non-type-only) import from \"zod\".\n *\n * One of the three triggers ZOD_MENTION (the transform hook's `code` filter)\n * must remain a superset of — widening this to a specifier that does not\n * contain \"zod\" silently strips those files from every bundler with native\n * hook filters. `describe(\"code filter soundness\")` fails if it drifts.\n */\nexport const HAS_RUNTIME_ZOD_IMPORT =\n /import\\s+(?!type\\s)[^;]*from\\s+[\"']zod(?:\\/v\\d+)?(?:[/-]mini)?[\"']/;\n\n/**\n * Opt-in phase timing (ZOD_COMPILER_TIMING=1): accumulates per-phase wall time\n * across all transform calls and prints a summary on process exit. Used to\n * attribute plugin overhead in real builds/test runs.\n */\nconst TIMING = process.env[\"ZOD_COMPILER_TIMING\"] === \"1\";\n\n/** A single file's discovery exceeding this is worth an actionable warning. */\nconst SLOW_DISCOVERY_WARN_MS = 5_000;\nconst phaseTotals = new Map<string, { ms: number; calls: number }>();\nlet timingHookInstalled = false;\n\n/** Dedupes the process.exit-during-discovery warning to one per process. */\nlet warnedProcessExit = false;\n\nfunction timePhase<T>(phase: string, fn: () => T): T {\n if (!TIMING) return fn();\n const t0 = performance.now();\n const done = (): void => {\n const dt = performance.now() - t0;\n const agg = phaseTotals.get(phase) ?? { ms: 0, calls: 0 };\n agg.ms += dt;\n agg.calls++;\n phaseTotals.set(phase, agg);\n };\n if (!timingHookInstalled) {\n timingHookInstalled = true;\n process.on(\"exit\", () => {\n const rows = [...phaseTotals.entries()].sort((a, b) => b[1].ms - a[1].ms);\n for (const [name, { ms, calls }] of rows) {\n log(`timing ${name}: ${ms.toFixed(1)}ms over ${calls} call(s)`);\n }\n });\n }\n const r = fn();\n if (r instanceof Promise) {\n return r.finally(done) as T;\n }\n done();\n return r;\n}\n\n/**\n * Check if a file should be transformed by the plugin.\n */\nexport function shouldTransform(id: string, options?: ZodCompilerPluginOptions): boolean {\n if (!/\\.[cm]?[jt]sx?$/.test(id)) return false;\n if (id.includes(\"node_modules\")) return false;\n if (id.endsWith(\".d.ts\")) return false;\n if (id.endsWith(\".compiled.ts\") || id.endsWith(\".compiled.js\")) return false;\n\n if (options?.exclude?.some((pattern) => picomatch.isMatch(id, pattern, { contains: true })))\n return false;\n if (\n options?.include &&\n !options.include.some((pattern) => picomatch.isMatch(id, pattern, { contains: true }))\n )\n return false;\n\n return true;\n}\n\n/**\n * `id` half of the transform hook filter: the option-independent checks of\n * shouldTransform(), restated as patterns the bundler can evaluate itself.\n * Rolldown, Vite and Rollup 4.40+ apply hook filters natively, so a rejected\n * module never crosses into JS; unplugin applies the same patterns in JS for\n * the rest (webpack/rspack skip installing the transform loader entirely).\n *\n * The `include`/`exclude` options stay out of the filter on purpose: they are\n * matched with picomatch's `contains: true` semantics, which the native glob\n * support (patterns resolved against cwd, matched whole) would silently\n * narrow — shouldTransform() keeps applying them inside the handler.\n */\nexport const TRANSFORM_ID_FILTER: { exclude: RegExp[]; include: RegExp[] } = {\n exclude: [/node_modules/, /\\.d\\.ts$/, /\\.compiled\\.[jt]s$/],\n include: [/\\.[cm]?[jt]sx?$/],\n};\n\n/**\n * Every transform path that can change a file needs the substring \"zod\" (or\n * \"Zod\") somewhere in the source. There are exactly three triggers, and each\n * one is pinned by `describe(\"code filter soundness\")` in the transform tests:\n *\n * 1. auto-discovery — HAS_RUNTIME_ZOD_IMPORT (above), which only matches\n * specifiers spelled \"zod…\";\n * 2. `schemas: \"explicit\"` — an import from \"zod-compiler\" (the package name);\n * 3. hoisting — a root imported from ZOD_MODULES or an identifier matching\n * SCHEMA_NAME_PATTERN (both in hoist.ts; a *custom* pattern drops this\n * filter entirely, see transformCodeFilter).\n *\n * Adding a fourth trigger that can fire without a \"zod\" mention MUST widen\n * this pattern, or those files are silently skipped on every bundler with\n * native hook filters — no error, schemas just quietly stay uncompiled.\n *\n * Spelled as a character class rather than an `i` flag: native filters\n * recompile these patterns outside JS, where flag support is narrower.\n */\nconst ZOD_MENTION = /[Zz]od/;\n\n/**\n * `code` half of the transform hook filter, or undefined when no sound filter\n * exists. A custom `hoist.schemaNamePattern` promotes arbitrary imported\n * identifiers to schema roots (`UserModel`), so nothing in such a file is\n * guaranteed to mention zod — those setups keep the unfiltered behavior.\n */\nexport function transformCodeFilter(options?: ZodCompilerPluginOptions): RegExp | undefined {\n const namePattern = typeof options?.hoist === \"object\" ? options.hoist.schemaNamePattern : null;\n // `null` disables name matching and `undefined` keeps the default\n // /ZodSchema$/ — both leave a \"zod\" mention as the only way in.\n return namePattern === null || namePattern === undefined ? ZOD_MENTION : undefined;\n}\n\nexport function log(msg: string): void {\n // oxlint-disable-next-line no-console -- build output\n console.log(`[zod-compiler] ${msg}`);\n}\n\nfunction warn(msg: string): void {\n // oxlint-disable-next-line no-console -- build output\n console.warn(`[zod-compiler] ${msg}`);\n}\n\nexport interface TransformOutput {\n code: string;\n map: TransformSourceMap | null;\n}\n\n/**\n * Transform source code by replacing compile() calls with optimized validators.\n * Returns the transformed code or null if no transformation was needed.\n * Compatibility wrapper over transformCodeWithMap() — discards the map.\n */\nexport async function transformCode(\n code: string,\n id: string,\n options: TransformOptions,\n): Promise<string | null> {\n const result = await transformCodeWithMap(code, id, options);\n return result === null ? null : result.code;\n}\n\n/**\n * transformCode + a composed sourcemap (original → output). Stack traces in\n * transformed files shift by prepended declarations and expanded IIFEs\n * without it — a vitest assertion can be reported dozens of lines off.\n */\nexport async function transformCodeWithMap(\n code: string,\n id: string,\n options: TransformOptions,\n): Promise<TransformOutput | null> {\n const verbose = options.verbose === true;\n const autoDiscover = options.autoDiscover === true;\n const mode = options.mode;\n const staged = new StagedTransform(code, id);\n\n // Hoist Zod schema construction out of function bodies to module scope\n // (babel-plugin-zod-hoist equivalent). Mode-independent: inline schemas\n // live exactly in the files that export none. hoistZodSchemasMeta() bails\n // in microseconds when no eligible imports exist.\n let hoistedSchemas: ReturnType<typeof hoistZodSchemasMeta> = null;\n if (options.hoist !== false && code.includes(\"import\")) {\n hoistedSchemas = timePhase(\"hoist\", () =>\n hoistZodSchemasMeta(code, {\n ...(typeof options.hoist === \"object\" ? options.hoist : undefined),\n onScan: options.onSubstantialWork,\n }),\n );\n if (hoistedSchemas !== null) {\n staged.apply(hoistedSchemas.edits, hoistedSchemas.insert);\n if (verbose) {\n log(`Hoisted inline Zod schemas in ${id}`);\n }\n }\n }\n\n // Compile the hoisted schemas (autoDiscover only — compiling anonymous\n // schemas is auto-discovery of unexported module-scope schemas). Each\n // hoisted `const _zh_x = z.object({...});` whose construction is\n // deterministic (eager refs are zod bindings only) is evaluated at build\n // time and its initializer replaced with the compiled validator IIFE;\n // anything ineligible stays a plain hoist.\n const hoistHelpers = new Set<string>();\n let hoistCompiledCount = 0;\n if (autoDiscover && hoistedSchemas !== null && hoistedSchemas.schemas.length > 0) {\n const hoistCompiled = await timePhase(\"hoist-compile\", () =>\n compileHoistedSchemas(hoistedSchemas.schemas, code, id, mode),\n );\n const spliceEdits: Edit[] = [];\n for (const h of hoistCompiled) {\n const decl = `const ${h.name} = ${h.text};`;\n const at = staged.current.indexOf(decl);\n if (at === -1) continue;\n const iife = generateIIFE(h.text, h.info, { zodCompat: options.zodCompat });\n spliceEdits.push({ start: at, end: at + decl.length, text: `const ${h.name} = ${iife};` });\n hoistCompiledCount++;\n for (const helper of h.info.codegenResult.usedHelpers) {\n hoistHelpers.add(helper);\n }\n if (verbose) {\n log(` ✓ ${h.name} (hoisted schema compiled)`);\n }\n }\n staged.apply(spliceEdits);\n if (hoistCompiledCount > 0) {\n hoistHelpers.add(\"__zcMkv\");\n hoistHelpers.add(\"__zcFin\");\n }\n }\n\n // When only hoisting (± hoisted-schema compilation) changed the file, that\n // is still a transform result — with runtime helpers injected if any\n // hoisted schema compiled.\n const finishHoistOnly = (): TransformOutput | null => {\n if (staged.current === code) return null;\n if (hoistCompiledCount > 0) {\n options.onBuildStats?.({\n files: 1,\n schemas: hoistedSchemas?.schemas.length ?? 0,\n optimized: hoistCompiledCount,\n failed: 0,\n });\n const prefix = computeRuntimePrefix(staged.current, hoistHelpers, mode, options.runtimeId);\n if (prefix !== null) {\n staged.applyThen([], () => prefix);\n }\n }\n return { code: staged.current, map: staged.map() };\n };\n\n // Quick bail-out check. Both gates are also encoded in the transform hook's\n // `code` filter (ZOD_MENTION) so bundlers can skip the hook call entirely —\n // relaxing either one here without widening that pattern makes the bundler\n // drop those files before this code ever runs.\n if (autoDiscover) {\n // autoDiscover: any file with a runtime Zod import is a candidate.\n // Skip `import type` — these files have no runtime schemas.\n if (!HAS_RUNTIME_ZOD_IMPORT.test(staged.current)) return finishHoistOnly();\n } else {\n // Legacy mode: require compile() from zod-compiler. The word-boundary\n // check matters: the package name itself contains the substring\n // \"compile\", so a plain includes(\"compile\") would match every import of\n // \"zod-compiler\" — \\bcompile\\b does not match inside \"zod-compiler\"\n // (no boundary between \"e\" and \"r\") but matches compile( / { compile }.\n if (!staged.current.includes(\"zod-compiler\") || !/\\bcompile\\b/.test(staged.current))\n return finishHoistOnly();\n }\n\n // Static pre-filter: skip files whose exports provably cannot be schemas\n // (functions, components, constants, type-only modules) without executing\n // them. Conservative — anything ambiguous stays a candidate. The filter\n // transpiles + parses the file, so its outcome is worth persisting even\n // when the eventual result is null.\n options.onSubstantialWork?.();\n if (!(await timePhase(\"static-filter\", () => mayExportSchemas(staged.current, id))))\n return finishHoistOnly();\n\n // Discover schemas by executing the file. Module executions are cached in\n // the shared loader; watch/HMR changes invalidate via invalidateModuleCache().\n options.onDiscovery?.();\n let schemas: DiscoveredSchema[];\n const discoverStart = performance.now();\n try {\n schemas = await timePhase(\"discover\", () => discoverSchemas(id, { autoDiscover }));\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n // A module in the import graph called process.exit() during discovery —\n // typically an env-validation guard in a CI build where secrets are\n // intentionally absent. The loader converts that exit into a catchable\n // error so the build survives; the affected files just fall back to\n // runtime Zod. Handle it in both modes (never crash on a build-time exit)\n // and surface the cooperative remedy once.\n if (e instanceof ProcessExitDuringLoadError) {\n // The result reflects missing secrets, not file content — never persist\n // it to the content-hashed disk cache (a later secret-ful build would be\n // served this stale \"nothing compiled\" entry).\n options.onUncacheableResult?.();\n if (!warnedProcessExit) {\n warnedProcessExit = true;\n warn(\n `${id} (or a module it imports) called process.exit during build-time schema ` +\n `discovery — those files fall back to runtime Zod instead of crashing the build. ` +\n `This is usually an env-validation guard; wrap it in ` +\n `\\`if (!process.env.ZOD_COMPILER) { ... }\\` to keep these schemas compiled, or set ` +\n `schemas:\"explicit\" / use include to scope discovery.`,\n );\n }\n return finishHoistOnly();\n }\n // In autoDiscover mode, files that can't be loaded (JSX components,\n // unresolved path aliases, etc.) are expected — warn and skip.\n if (autoDiscover) {\n if (verbose) {\n warn(`Skipping ${id}: ${msg}`);\n }\n return finishHoistOnly();\n }\n throw new Error(`[zod-compiler] Failed to load schemas from ${id}: ${msg}`);\n }\n const discoverMs = performance.now() - discoverStart;\n if (discoverMs >= SLOW_DISCOVERY_WARN_MS) {\n // Discovery executes the file's whole first-party import graph inside\n // the bundler's single-threaded process — on saturated CI hosts a large\n // graph can stall the event loop long enough to trip test timeouts.\n // Surface the cost with the two effective remedies.\n warn(\n `Discovery of ${id} took ${(discoverMs / 1000).toFixed(1)}s executing its import graph ` +\n `in the bundler process. Persist node_modules/.cache/zod-compiler across CI runs to pay ` +\n `this once, or narrow autoDiscover/include for test runs (see README \"Large projects ` +\n `and CI\"). ZOD_COMPILER_TIMING=1 prints a per-phase breakdown.`,\n );\n }\n if (schemas.length === 0) return finishHoistOnly();\n\n // Lean mode (every bundler in VIRTUAL_MODULE_FRAMEWORKS — Vite/Rollup/webpack/rspack/etc.)\n // imports shared helpers from a runtime module for cross-file dedup: virtual:zod-compiler/runtime\n // on virtual-friendly bundlers, the __zod-compiler-runtime__ bare specifier on webpack/rspack.\n // Inline mode (CLI emitter, and any bundler not in VIRTUAL_MODULE_FRAMEWORKS) emits self-contained\n // file-level helpers.\n let failedCount = 0;\n const { schemas: compiled, shared } = timePhase(\"compile\", () =>\n compileSchemas(schemas, {\n mode,\n compact: options.compact,\n onError(exportName, error) {\n failedCount++;\n warn(\n `Failed to compile \"${exportName}\" in ${id}: ${error.message}. Keeping original${autoDiscover ? \"\" : \" compile()\"} call.`,\n );\n },\n }),\n );\n\n if (verbose) {\n if (autoDiscover) {\n log(\n `Auto-discovering: ${id} (${schemas.length} Zod export${schemas.length > 1 ? \"s\" : \"\"} found)`,\n );\n }\n for (const s of compiled) {\n const rfCount = s.refEntries.length;\n const rfSuffix = rfCount > 0 ? ` (${rfCount} ref${rfCount > 1 ? \"s\" : \"\"})` : \"\";\n log(` ✓ ${s.exportName}${rfSuffix}`);\n }\n if (failedCount > 0) {\n log(` ✗ ${failedCount} schema(s) failed`);\n }\n }\n\n if (compiled.length === 0) return finishHoistOnly();\n\n // Report build stats only when at least one schema was compiled\n // (hoisted-schema compiles count alongside export schemas).\n options.onBuildStats?.({\n files: 1,\n schemas: schemas.length + (hoistedSchemas?.schemas.length ?? 0),\n optimized: compiled.length + hoistCompiledCount,\n failed: failedCount,\n });\n\n // __zcMkv and __zcFin are always needed (they wrap every IIFE). Helpers used\n // by compiled hoisted schemas ride along in the same injection.\n const usedHelpers = aggregateUsedHelpers(compiled);\n usedHelpers.add(\"__zcMkv\");\n usedHelpers.add(\"__zcFin\");\n for (const helper of hoistHelpers) {\n usedHelpers.add(helper);\n }\n // Shared dedup validators ride the same runtime import.\n for (const helper of shared.usedHelpers) {\n usedHelpers.add(helper);\n }\n\n // Two-pass rewrite: separate compile() schemas from autoDiscover schemas.\n // Both passes collect edits against the same pristine stage input — their\n // target regions are disjoint (compile() assignments vs plain exported\n // declarations of OTHER names), so one batched application is equivalent\n // to the historical sequential rewrites.\n let rewriteEdits: readonly Edit[];\n if (autoDiscover) {\n // Detect compile() schemas by checking source code patterns\n const compileExportNames = new Set<string>();\n for (const s of compiled) {\n const pattern = new RegExp(`\\\\b${s.exportName}\\\\s*=\\\\s*compile[\\\\s<(]`);\n if (pattern.test(staged.current)) {\n compileExportNames.add(s.exportName);\n }\n }\n const compileSchemaInfos = compiled.filter((s) => compileExportNames.has(s.exportName));\n const autoDiscoverSchemaInfos = compiled.filter((s) => !compileExportNames.has(s.exportName));\n\n const edits: Edit[] = [];\n // Pass 1: compile() schemas (includes compile-import removal — only when\n // compile() schemas were actually rewritten, mirroring the historical\n // conditional rewriteSource call)\n if (compileSchemaInfos.length > 0) {\n edits.push(\n ...collectCompileRewriteEdits(staged.current, compileSchemaInfos, {\n zodCompat: options.zodCompat,\n }),\n );\n }\n // Pass 2: plain exported schemas\n if (autoDiscoverSchemaInfos.length > 0) {\n edits.push(\n ...collectAutoDiscoverEdits(staged.current, autoDiscoverSchemaInfos, {\n zodCompat: options.zodCompat,\n }),\n );\n }\n rewriteEdits = edits;\n } else {\n rewriteEdits = collectCompileRewriteEdits(staged.current, compiled, {\n zodCompat: options.zodCompat,\n });\n }\n\n // Head = runtime helpers/import, then the file-level shared dedup block, then\n // the rewritten source. Shared constants and `__zcSw_N` functions live at\n // module scope so every IIFE closes over them; they must follow the runtime\n // import (lean) and the helper decls (inline) that they reference. Guard against\n // double-injection on watch/HMR re-runs the same way computeRuntimePrefix\n // does — a second copy would redeclare every `__zcSw_N`.\n //\n // Deferred into the rewrite's own stage rather than staged after it: both\n // decisions read the REWRITTEN text, and giving a bare prepend its own stage\n // doubled the sourcemap work and added a composition pass (see applyThen).\n staged.applyThen(rewriteEdits, (rewritten) => {\n const prefix = computeRuntimePrefix(rewritten, usedHelpers, mode, options.runtimeId);\n const needsShared = shared.code !== \"\" && !rewritten.includes(SHARED_BLOCK_MARKER);\n const head = (prefix ?? \"\") + (needsShared ? `${shared.code}\\n` : \"\");\n return head === \"\" ? undefined : head;\n });\n return { code: staged.current, map: staged.map() };\n}\n\n/**\n * Prepend the runtime helpers required by the rewritten source.\n *\n * Lean mode emits a single `import { ... } from \"<runtimeId>\";` line —\n * bundlers whose resolveId hook intercepts the specifier dedup helpers across\n * every transformed file into one shared virtual module.\n *\n * Inline mode prepends file-level `function __zcMkv` / `function __zcFin`\n * declarations directly so the file is self-contained.\n *\n * Idempotent: if the file already contains the relevant marker (re-run during\n * watch/HMR), we skip re-injection.\n */\n/** The runtime-helper text to prepend, or null when nothing is needed. */\nfunction computeRuntimePrefix(\n code: string,\n usedHelpers: Set<string>,\n mode: CodegenMode,\n runtimeId: string = VIRTUAL_RUNTIME_ID,\n): string | null {\n if (mode === \"lean\") {\n if (usedHelpers.size === 0) return null;\n // Match the import STATEMENT, not the bare id. `zod-compiler/runtime` is a\n // plain package specifier and a substring of `virtual:zod-compiler/runtime`,\n // so a file merely mentioning either — a comment, a docs snippet — would\n // suppress the import while codegen still emits calls to the helpers.\n if (code.includes(`from \"${runtimeId}\"`)) return null;\n const names = [...usedHelpers].sort().join(\", \");\n return `import { ${names} } from \"${runtimeId}\";\\n`;\n }\n // Inline mode (CLI emitter, and any bundler not in VIRTUAL_MODULE_FRAMEWORKS):\n // ship file-level helper declarations instead of a virtual import.\n // Codegen emits per-IIFE issue literals + per-IIFE `__re_*` decls,\n // so we only need __zcMkv / __zcFin (plus __zcMsg via the zod config import).\n if (!code.includes(\"__zcMkv\")) return null;\n const prefix: string[] = [];\n if (!code.includes(\"__zodCompilerConfig\")) {\n prefix.push(ZOD_CONFIG_IMPORT, ZOD_MSG_DECLARATION);\n }\n if (!code.includes(\"function __zcMkv(\")) {\n prefix.push(MK_VALIDATOR_DECL);\n }\n // __zcFin and __zcFinD both construct __ZcFail; declare it once before either,\n // guarding against a header already shipped earlier in the same module.\n const needsFin = !code.includes(\"function __zcFin(\");\n const needsFinD = code.includes(\"__zcFinD(\") && !code.includes(\"function __zcFinD(\");\n if ((needsFin || needsFinD) && !code.includes(\"function __ZcFail(\")) {\n prefix.push(FAIL_CLASS_DECL);\n }\n if (needsFin) {\n prefix.push(FIN_DECL);\n }\n if (needsFinD) {\n prefix.push(FIN_DEFERRED_DECL);\n }\n // Compact mode (output: \"compact\") delegates cold errors to zod via __zcFinZ,\n // which constructs its own __ZcFailZ (distinct from __ZcFail).\n const needsFinZ = code.includes(\"__zcFinZ(\") && !code.includes(\"function __zcFinZ(\");\n if (needsFinZ && !code.includes(\"function __ZcFailZ(\")) {\n prefix.push(FAILZ_CLASS_DECL);\n }\n if (needsFinZ) {\n prefix.push(FINZ_DECL);\n }\n return prefix.length > 0 ? `${prefix.join(\"\\n\")}\\n` : null;\n}\n\n/**\n * Find the matching closing parenthesis for a compile() call,\n * handling nested parentheses like compile(z.object({...})).\n * Returns the index of the closing ')' or -1 if not found.\n */\nfunction findMatchingParen(code: string, openIndex: number): number {\n let depth = 1;\n for (let i = openIndex + 1; i < code.length; i++) {\n if (code[i] === \"(\") depth++;\n else if (code[i] === \")\") {\n depth--;\n if (depth === 0) return i;\n }\n }\n return -1;\n}\n\n/**\n * Rewrite source code by replacing compile() calls with IIFE-wrapped optimized validators.\n */\nexport function rewriteSource(\n code: string,\n schemas: CompiledSchemaInfo[],\n options?: { zodCompat?: boolean | undefined },\n): string {\n return applyEdits(code, collectCompileRewriteEdits(code, schemas, options));\n}\n\n/**\n * Edits for rewriteSource (compile() call replacements + compile-import\n * removal), collected against pristine `code`. Each schema's declaration is\n * a distinct region and the import statement is distinct from all of them,\n * so the batch is non-overlapping and order-independent.\n */\nfunction collectCompileRewriteEdits(\n code: string,\n schemas: CompiledSchemaInfo[],\n options?: { zodCompat?: boolean | undefined },\n): Edit[] {\n const edits: Edit[] = [];\n for (const schema of schemas) {\n // Match: <exportName> = compile<...>( with word boundary to prevent substring matches\n const prefixPattern = new RegExp(\n `(\\\\b${schema.exportName}\\\\s*=\\\\s*)compile\\\\s*(?:<[^>]*(?:<[^>]*>[^>]*)?>)?\\\\s*\\\\(`,\n );\n const match = prefixPattern.exec(code);\n if (!match) continue;\n\n // Find the matching closing paren (handles nested parens)\n const openParenIndex = match.index + match[0].length - 1;\n const closeParenIndex = findMatchingParen(code, openParenIndex);\n if (closeParenIndex === -1) continue;\n\n const schemaArgName = code\n .slice(openParenIndex + 1, closeParenIndex)\n .trim()\n .replace(/,\\s*$/, \"\");\n const prefix = match[1] ?? \"\";\n edits.push({\n start: match.index,\n end: closeParenIndex + 1,\n text: prefix + generateIIFE(schemaArgName, schema, options),\n });\n }\n edits.push(...collectRemoveCompileImportEdits(code));\n return edits;\n}\n\n/**\n * Find the end position of a JavaScript expression starting at `start` using acorn.\n * Returns the end offset, or -1 if the expression cannot be parsed.\n */\nexport function findExpressionEnd(code: string, start: number): number {\n try {\n const node = parseExpressionAt(code, start, {\n ecmaVersion: \"latest\",\n sourceType: \"module\",\n });\n return node.end;\n } catch {\n return -1;\n }\n}\n\n/**\n * Rewrite source code by replacing plain Zod schema exports with IIFE-wrapped optimized validators.\n * Used by autoDiscover mode (no compile() wrappers needed).\n */\nexport function rewriteSourceAutoDiscover(\n code: string,\n schemas: CompiledSchemaInfo[],\n options?: { zodCompat?: boolean | undefined },\n): string {\n return applyEdits(code, collectAutoDiscoverEdits(code, schemas, options));\n}\n\n/**\n * Does `expr` mention `name` as an identifier?\n *\n * Deliberately lexical, and deliberately biased toward YES. A false positive\n * costs one export its `@__PURE__` annotation; a false negative emits an IIFE\n * that dereferences a binding still under initialization. The expression text\n * is often TypeScript (`z.custom<T>(...)`), which no JS parser here can be\n * trusted to walk, so a word-boundary scan — which cannot miss a real\n * identifier reference — is the sound direction to be wrong in.\n */\nfunction mentionsIdentifier(expr: string, name: string): boolean {\n return new RegExp(`\\\\b${name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}\\\\b`).test(expr);\n}\n\n/** Edits for rewriteSourceAutoDiscover, collected against pristine `code`. */\nfunction collectAutoDiscoverEdits(\n code: string,\n schemas: CompiledSchemaInfo[],\n options?: { zodCompat?: boolean | undefined },\n): Edit[] {\n const edits: Edit[] = [];\n for (const schema of schemas) {\n const escapedName = schema.exportName.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n // Match: export? (const|let|var) ExportName[: TypeAnnotation] = <expr>\n const assignPattern = new RegExp(\n `((?:export\\\\s+)?(?:const|let|var)\\\\s+${escapedName}(?:\\\\s*:[^=]*)?\\\\s*=\\\\s*)`,\n );\n const match = assignPattern.exec(code);\n if (!match) continue;\n\n const rhsStart = match.index + match[0].length;\n const rhsEnd = findExpressionEnd(code, rhsStart);\n if (rhsEnd === -1) continue;\n\n const originalExpr = code.slice(rhsStart, rhsEnd).trim();\n\n // A RECURSIVE schema defers its self-reference through a callback —\n // `z.lazy(() => z.array(Node))`, or zod v4's getter form\n // `get children() { return z.array(Node) }` — and that callback closes over\n // the module binding declared here. Replacing the initializer puts the\n // IIFE's `var __rf=[__zs._zod.innerType...]` preamble INSIDE that binding's\n // own initializer, so forcing the callback re-enters a binding that is not\n // yet assigned: a TDZ ReferenceError at module init, or — once a bundler\n // lowers the top-level `const` to `var`, as esbuild does — a silent\n // `undefined` that zod's `defineLazy` then CACHES, permanently poisoning\n // the schema for every consumer (`z.array(undefined)`).\n //\n // So the deref moves out of the initializer: the declaration keeps its\n // original expression and the IIFE follows it as a statement, mutating the\n // now-assigned schema in place. `__zcMkv` returns its argument (identity is\n // preserved by design), so the export is the same object either way, and\n // `__rfp_N`'s pristine-`safeParse` capture still happens before the\n // trailing `__zcMkv` installs anything. The cost is this export's\n // `@__PURE__` annotation — a self-referential schema is no longer\n // droppable when unused.\n if (iifeDerefsSchema(schema) && mentionsIdentifier(originalExpr, schema.exportName)) {\n // `output: \"bag\"` replaces the export with a method bag rather than\n // mutating the schema, so there is nothing to mutate in place — and the\n // user's own recursive reference would resolve to the bag regardless.\n if (options?.zodCompat === false) {\n warn(\n `Skipping self-referential export \"${schema.exportName}\": output \"bag\" cannot preserve its recursive reference. Keeping the original schema.`,\n );\n continue;\n }\n // Re-emitting `originalExpr` verbatim is what makes splitting the\n // declaration safe for `const Schema = <expr>, other = 1;`:\n // findExpressionEnd parses an Expression, and the comma operator makes\n // that span the whole declarator list, so the siblings are inside the\n // text being written back rather than after the statement terminator.\n edits.push({\n start: rhsStart,\n end: rhsEnd,\n text: `${originalExpr};\\n${generateIIFE(schema.exportName, schema, { ...options, pure: false })};`,\n });\n continue;\n }\n\n edits.push({\n start: rhsStart,\n end: rhsEnd,\n text: generateIIFE(originalExpr, schema, options),\n });\n }\n return edits;\n}\n\n/**\n * Remove the `compile` binding from `import { compile, ... } from \"zod-compiler\"` statements.\n * If `compile` is the only import, the entire import line is removed.\n */\nexport function removeCompileImport(code: string): string {\n return applyEdits(code, collectRemoveCompileImportEdits(code));\n}\n\n/** Edits stripping the `compile` binding from zod-compiler import statements. */\nfunction collectRemoveCompileImportEdits(code: string): Edit[] {\n // Match: import { ... } from \"zod-compiler\" or 'zod-compiler'\n const importPattern = /import\\s*\\{([^}]*)\\}\\s*from\\s*[\"']zod-compiler[\"'];?/g;\n const edits: Edit[] = [];\n for (const match of code.matchAll(importPattern)) {\n const imports = match[1] ?? \"\";\n const names = imports\n .split(\",\")\n .map((n) => n.trim())\n .filter(Boolean);\n const remaining = names.filter((n) => n !== \"compile\");\n const text =\n remaining.length === 0 ? \"\" : `import { ${remaining.join(\", \")} } from \"zod-compiler\";`;\n if (text !== match[0]) {\n edits.push({ start: match.index, end: match.index + match[0].length, text });\n }\n }\n return edits;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"transform.js","names":[],"sources":["../../src/unplugin/transform.ts"],"sourcesContent":["import remapping from \"@jridgewell/remapping\";\nimport { parseExpressionAt } from \"acorn\";\nimport MagicString from \"magic-string\";\nimport picomatch from \"picomatch\";\nimport type { CodegenMode } from \"../core/codegen/context.js\";\nimport { SHARED_BLOCK_MARKER } from \"../core/codegen/dedupe.js\";\nimport {\n FAIL_CLASS_DECL,\n FAILZ_CLASS_DECL,\n FIN_DECL,\n FIN_DEFERRED_DECL,\n FINZ_DECL,\n generateIIFE,\n iifeDerefsSchema,\n MK_VALIDATOR_DECL,\n ZOD_CONFIG_IMPORT,\n ZOD_MSG_DECLARATION,\n} from \"../core/iife.js\";\nimport { aggregateUsedHelpers, type CompiledSchemaInfo, compileSchemas } from \"../core/pipeline.js\";\nimport type { DiscoveredSchema } from \"../core/types.js\";\nimport { discoverSchemas } from \"../discovery.js\";\nimport { ProcessExitDuringLoadError } from \"../loader.js\";\nimport { mayExportSchemas } from \"../static-filter.js\";\nimport { applyEdits, type Edit, type Insertion, moduleHeadOffset } from \"./edits.js\";\nimport { hoistZodSchemasMeta } from \"./hoist.js\";\nimport { compileHoistedSchemas } from \"./hoist-compile.js\";\nimport type { TransformOptions, ZodCompilerPluginOptions } from \"./types.js\";\nimport { VIRTUAL_RUNTIME_ID } from \"./virtual.js\";\n\n/** JSON shape of the composed sourcemap returned alongside transformed code. */\nexport interface TransformSourceMap {\n version: number;\n sources: (string | null)[];\n sourcesContent?: (string | null)[];\n names: string[];\n mappings: string;\n file?: string | null;\n}\n\n/**\n * The transform pipeline as a chain of edit batches. Each batch is applied\n * to the CURRENT text through a MagicString (one stage map per batch); the\n * final original→output map is the remapping-composed chain. Deriving the\n * output string and the map from the same edit list makes divergence\n * impossible.\n */\nclass StagedTransform {\n current: string;\n private readonly source: string;\n private readonly maps: unknown[] = [];\n\n constructor(original: string, source: string) {\n this.current = original;\n this.source = source;\n }\n\n apply(edits: readonly Edit[], insert?: Insertion): void {\n this.stage(edits, (s) => {\n if (insert === undefined) return false;\n s.appendLeft(insert.offset, insert.text);\n return true;\n });\n }\n\n /**\n * Apply `edits`, then prepend `deferred`'s text to the module head — both\n * inside ONE stage.\n *\n * The head injection (runtime import + shared dedup block) has to be decided\n * from the REWRITTEN source, because `computeRuntimePrefix` probes it for\n * already-present markers. Staging it separately made a whole second\n * `generateMap` over the full generated output — for what is only a prepend at\n * the module head — and then forced `remapping` to compose the two. Together\n * those were the dominant cost of a transform: on a 320-schema project they ran\n * to 64% of total wall time, more than discovery and codegen combined. Deferring\n * the insertion into the same MagicString buys byte-identical output and an\n * equivalent map for one generation and no composition — 1.6x (small schemas)\n * to 3.4x (large ones) on the transform, scaling with how much code a file\n * emits, since that is what both costs are proportional to.\n *\n * `deferred` returns TEXT, not an `Insertion`: `appendLeft` resolves offsets\n * against the PRE-edit text while `deferred` is shown the POST-edit text, so a\n * callback-supplied offset would be in the wrong coordinate system. Deriving it\n * here from `this.current` keeps the two in step by construction.\n */\n applyThen(edits: readonly Edit[], deferred?: (rewritten: string) => string | undefined): void {\n this.stage(edits, (s, rewritten) => {\n // `?.()` short-circuits its arguments, so a stage with no deferred step\n // never materializes the rewritten text.\n const head = deferred?.(rewritten());\n if (head === undefined) return false;\n s.appendLeft(moduleHeadOffset(this.current), head);\n return true;\n });\n }\n\n /**\n * One stage: apply `edits` to a fresh MagicString, let `inject` add at most\n * one insertion, then commit the text and its map. `inject` reports whether\n * it inserted, so a no-op stage can be skipped entirely.\n *\n * `rewritten` is a thunk, not a string: materializing it costs a full\n * `toString()` over generated-code-sized input, and the injectors that do not\n * read it (every `apply()` call) must not pay for it.\n */\n private stage(\n edits: readonly Edit[],\n inject: (s: MagicString, rewritten: () => string) => boolean,\n ): void {\n const s = new MagicString(this.current);\n for (const e of edits) {\n if (e.start === e.end) {\n s.appendLeft(e.start, e.text);\n } else {\n s.overwrite(e.start, e.end, e.text);\n }\n }\n // `toString()` is the only way to show the deferred step what the rewrite\n // produced; it measured well under 1% of a transform.\n const inserted = inject(s, () => (edits.length === 0 ? this.current : s.toString()));\n if (edits.length === 0 && !inserted) return;\n this.current = s.toString();\n // `hires: \"boundary\"` is load-bearing, not a tuning knob: without it every\n // mapping collapses to column 0, so a stack frame or debugger breakpoint in\n // untouched user code below a compiled schema lands at the start of its line\n // instead of the right column (tests/unplugin/sourcemap.test.ts pins it). It\n // is also the most expensive thing here, which is why the stage COUNT is\n // what to economize on.\n this.maps.push(s.generateMap({ source: this.source, hires: \"boundary\", includeContent: true }));\n }\n\n /** Composed original→current map, or null when nothing was applied. */\n map(): TransformSourceMap | null {\n if (this.maps.length === 0) return null;\n // A single stage needs no composition: `remapping` over a one-map chain\n // reproduces that map, and it is expensive on generated-code-sized input.\n const [only] = this.maps;\n if (this.maps.length === 1) return only as TransformSourceMap;\n const chain = [...this.maps].reverse();\n return remapping(\n chain as Parameters<typeof remapping>[0],\n () => null,\n ) as unknown as TransformSourceMap;\n }\n}\n\n/**\n * Matches a runtime (non-type-only) import from \"zod\".\n *\n * One of the three triggers ZOD_MENTION (the transform hook's `code` filter)\n * must remain a superset of — widening this to a specifier that does not\n * contain \"zod\" silently strips those files from every bundler with native\n * hook filters. `describe(\"code filter soundness\")` fails if it drifts.\n */\nexport const HAS_RUNTIME_ZOD_IMPORT =\n /import\\s+(?!type\\s)[^;]*from\\s+[\"']zod(?:\\/v\\d+)?(?:[/-]mini)?[\"']/;\n\n/**\n * Opt-in phase timing (ZOD_COMPILER_TIMING=1): accumulates per-phase wall time\n * across all transform calls and prints a summary on process exit. Used to\n * attribute plugin overhead in real builds/test runs.\n */\nconst TIMING = process.env[\"ZOD_COMPILER_TIMING\"] === \"1\";\n\n/** A single file's discovery exceeding this is worth an actionable warning. */\nconst SLOW_DISCOVERY_WARN_MS = 5_000;\nconst phaseTotals = new Map<string, { ms: number; calls: number }>();\nlet timingHookInstalled = false;\n\n/** Dedupes the process.exit-during-discovery warning to one per process. */\nlet warnedProcessExit = false;\n\nfunction timePhase<T>(phase: string, fn: () => T): T {\n if (!TIMING) return fn();\n const t0 = performance.now();\n const done = (): void => {\n const dt = performance.now() - t0;\n const agg = phaseTotals.get(phase) ?? { ms: 0, calls: 0 };\n agg.ms += dt;\n agg.calls++;\n phaseTotals.set(phase, agg);\n };\n if (!timingHookInstalled) {\n timingHookInstalled = true;\n process.on(\"exit\", () => {\n const rows = [...phaseTotals.entries()].sort((a, b) => b[1].ms - a[1].ms);\n for (const [name, { ms, calls }] of rows) {\n log(`timing ${name}: ${ms.toFixed(1)}ms over ${calls} call(s)`);\n }\n });\n }\n const r = fn();\n if (r instanceof Promise) {\n return r.finally(done) as T;\n }\n done();\n return r;\n}\n\n/**\n * Check if a file should be transformed by the plugin.\n */\nexport function shouldTransform(id: string, options?: ZodCompilerPluginOptions): boolean {\n if (!/\\.[cm]?[jt]sx?$/.test(id)) return false;\n if (id.includes(\"node_modules\")) return false;\n if (id.endsWith(\".d.ts\")) return false;\n if (id.endsWith(\".compiled.ts\") || id.endsWith(\".compiled.js\")) return false;\n\n if (options?.exclude?.some((pattern) => picomatch.isMatch(id, pattern, { contains: true })))\n return false;\n if (\n options?.include &&\n !options.include.some((pattern) => picomatch.isMatch(id, pattern, { contains: true }))\n )\n return false;\n\n return true;\n}\n\n/**\n * `id` half of the transform hook filter: the option-independent checks of\n * shouldTransform(), restated as patterns the bundler can evaluate itself.\n * Rolldown, Vite and Rollup 4.40+ apply hook filters natively, so a rejected\n * module never crosses into JS; unplugin applies the same patterns in JS for\n * the rest (webpack/rspack skip installing the transform loader entirely).\n *\n * The `include`/`exclude` options stay out of the filter on purpose: they are\n * matched with picomatch's `contains: true` semantics, which the native glob\n * support (patterns resolved against cwd, matched whole) would silently\n * narrow — shouldTransform() keeps applying them inside the handler.\n */\nexport const TRANSFORM_ID_FILTER: { exclude: RegExp[]; include: RegExp[] } = {\n exclude: [/node_modules/, /\\.d\\.ts$/, /\\.compiled\\.[jt]s$/],\n include: [/\\.[cm]?[jt]sx?$/],\n};\n\n/**\n * Every transform path that can change a file needs the substring \"zod\" (or\n * \"Zod\") somewhere in the source. There are exactly three triggers, and each\n * one is pinned by `describe(\"code filter soundness\")` in the transform tests:\n *\n * 1. auto-discovery — HAS_RUNTIME_ZOD_IMPORT (above), which only matches\n * specifiers spelled \"zod…\";\n * 2. `schemas: \"explicit\"` — an import from \"zod-compiler\" (the package name);\n * 3. hoisting — a root imported from ZOD_MODULES or an identifier matching\n * SCHEMA_NAME_PATTERN (both in hoist.ts; a *custom* pattern drops this\n * filter entirely, see transformCodeFilter).\n *\n * Adding a fourth trigger that can fire without a \"zod\" mention MUST widen\n * this pattern, or those files are silently skipped on every bundler with\n * native hook filters — no error, schemas just quietly stay uncompiled.\n *\n * Spelled as a character class rather than an `i` flag: native filters\n * recompile these patterns outside JS, where flag support is narrower.\n */\nconst ZOD_MENTION = /[Zz]od/;\n\n/**\n * `code` half of the transform hook filter, or undefined when no sound filter\n * exists. A custom `hoist.schemaNamePattern` promotes arbitrary imported\n * identifiers to schema roots (`UserModel`), so nothing in such a file is\n * guaranteed to mention zod — those setups keep the unfiltered behavior.\n */\nexport function transformCodeFilter(options?: ZodCompilerPluginOptions): RegExp | undefined {\n const namePattern = typeof options?.hoist === \"object\" ? options.hoist.schemaNamePattern : null;\n // `null` disables name matching and `undefined` keeps the default\n // /ZodSchema$/ — both leave a \"zod\" mention as the only way in.\n return namePattern === null || namePattern === undefined ? ZOD_MENTION : undefined;\n}\n\nexport function log(msg: string): void {\n // oxlint-disable-next-line no-console -- build output\n console.log(`[zod-compiler] ${msg}`);\n}\n\nexport function warn(msg: string): void {\n // oxlint-disable-next-line no-console -- build output\n console.warn(`[zod-compiler] ${msg}`);\n}\n\nexport interface TransformOutput {\n code: string;\n map: TransformSourceMap | null;\n}\n\n/**\n * Transform source code by replacing compile() calls with optimized validators.\n * Returns the transformed code or null if no transformation was needed.\n * Compatibility wrapper over transformCodeWithMap() — discards the map.\n */\nexport async function transformCode(\n code: string,\n id: string,\n options: TransformOptions,\n): Promise<string | null> {\n const result = await transformCodeWithMap(code, id, options);\n return result === null ? null : result.code;\n}\n\n/**\n * transformCode + a composed sourcemap (original → output). Stack traces in\n * transformed files shift by prepended declarations and expanded IIFEs\n * without it — a vitest assertion can be reported dozens of lines off.\n */\nexport async function transformCodeWithMap(\n code: string,\n id: string,\n options: TransformOptions,\n): Promise<TransformOutput | null> {\n const verbose = options.verbose === true;\n const autoDiscover = options.autoDiscover === true;\n const mode = options.mode;\n const staged = new StagedTransform(code, id);\n\n // Hoist Zod schema construction out of function bodies to module scope\n // (babel-plugin-zod-hoist equivalent). Mode-independent: inline schemas\n // live exactly in the files that export none. hoistZodSchemasMeta() bails\n // in microseconds when no eligible imports exist.\n let hoistedSchemas: ReturnType<typeof hoistZodSchemasMeta> = null;\n if (options.hoist !== false && code.includes(\"import\")) {\n hoistedSchemas = timePhase(\"hoist\", () =>\n hoistZodSchemasMeta(code, {\n ...(typeof options.hoist === \"object\" ? options.hoist : undefined),\n onScan: options.onSubstantialWork,\n }),\n );\n if (hoistedSchemas !== null) {\n staged.apply(hoistedSchemas.edits, hoistedSchemas.insert);\n if (verbose) {\n log(`Hoisted inline Zod schemas in ${id}`);\n }\n }\n }\n\n // Compile the hoisted schemas (autoDiscover only — compiling anonymous\n // schemas is auto-discovery of unexported module-scope schemas). Each\n // hoisted `const _zh_x = z.object({...});` whose construction is\n // deterministic (eager refs are zod bindings only) is evaluated at build\n // time and its initializer replaced with the compiled validator IIFE;\n // anything ineligible stays a plain hoist.\n const hoistHelpers = new Set<string>();\n let hoistCompiledCount = 0;\n if (autoDiscover && hoistedSchemas !== null && hoistedSchemas.schemas.length > 0) {\n const hoistCompiled = await timePhase(\"hoist-compile\", () =>\n compileHoistedSchemas(hoistedSchemas.schemas, code, id, mode),\n );\n const spliceEdits: Edit[] = [];\n for (const h of hoistCompiled) {\n const decl = `const ${h.name} = ${h.text};`;\n const at = staged.current.indexOf(decl);\n if (at === -1) continue;\n const iife = generateIIFE(h.text, h.info, { zodCompat: options.zodCompat });\n spliceEdits.push({ start: at, end: at + decl.length, text: `const ${h.name} = ${iife};` });\n hoistCompiledCount++;\n for (const helper of h.info.codegenResult.usedHelpers) {\n hoistHelpers.add(helper);\n }\n if (verbose) {\n log(` ✓ ${h.name} (hoisted schema compiled)`);\n }\n }\n staged.apply(spliceEdits);\n if (hoistCompiledCount > 0) {\n hoistHelpers.add(\"__zcMkv\");\n hoistHelpers.add(\"__zcFin\");\n }\n }\n\n // When only hoisting (± hoisted-schema compilation) changed the file, that\n // is still a transform result — with runtime helpers injected if any\n // hoisted schema compiled.\n const finishHoistOnly = (): TransformOutput | null => {\n if (staged.current === code) return null;\n if (hoistCompiledCount > 0) {\n options.onBuildStats?.({\n files: 1,\n schemas: hoistedSchemas?.schemas.length ?? 0,\n optimized: hoistCompiledCount,\n failed: 0,\n });\n const prefix = computeRuntimePrefix(staged.current, hoistHelpers, mode, options.runtimeId);\n if (prefix !== null) {\n staged.applyThen([], () => prefix);\n }\n }\n return { code: staged.current, map: staged.map() };\n };\n\n // Quick bail-out check. Both gates are also encoded in the transform hook's\n // `code` filter (ZOD_MENTION) so bundlers can skip the hook call entirely —\n // relaxing either one here without widening that pattern makes the bundler\n // drop those files before this code ever runs.\n if (autoDiscover) {\n // autoDiscover: any file with a runtime Zod import is a candidate.\n // Skip `import type` — these files have no runtime schemas.\n if (!HAS_RUNTIME_ZOD_IMPORT.test(staged.current)) return finishHoistOnly();\n } else {\n // Legacy mode: require compile() from zod-compiler. The word-boundary\n // check matters: the package name itself contains the substring\n // \"compile\", so a plain includes(\"compile\") would match every import of\n // \"zod-compiler\" — \\bcompile\\b does not match inside \"zod-compiler\"\n // (no boundary between \"e\" and \"r\") but matches compile( / { compile }.\n if (!staged.current.includes(\"zod-compiler\") || !/\\bcompile\\b/.test(staged.current))\n return finishHoistOnly();\n }\n\n // Static pre-filter: skip files whose exports provably cannot be schemas\n // (functions, components, constants, type-only modules) without executing\n // them. Conservative — anything ambiguous stays a candidate. The filter\n // transpiles + parses the file, so its outcome is worth persisting even\n // when the eventual result is null.\n options.onSubstantialWork?.();\n if (!(await timePhase(\"static-filter\", () => mayExportSchemas(staged.current, id))))\n return finishHoistOnly();\n\n // Discover schemas by executing the file. Module executions are cached in\n // the shared loader; watch/HMR changes invalidate via invalidateModuleCache().\n options.onDiscovery?.();\n let schemas: DiscoveredSchema[];\n const discoverStart = performance.now();\n try {\n schemas = await timePhase(\"discover\", () => discoverSchemas(id, { autoDiscover }));\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n // A module in the import graph called process.exit() during discovery —\n // typically an env-validation guard in a CI build where secrets are\n // intentionally absent. The loader converts that exit into a catchable\n // error so the build survives; the affected files just fall back to\n // runtime Zod. Handle it in both modes (never crash on a build-time exit)\n // and surface the cooperative remedy once.\n if (e instanceof ProcessExitDuringLoadError) {\n // The result reflects missing secrets, not file content — never persist\n // it to the content-hashed disk cache (a later secret-ful build would be\n // served this stale \"nothing compiled\" entry).\n options.onUncacheableResult?.();\n if (!warnedProcessExit) {\n warnedProcessExit = true;\n warn(\n `${id} (or a module it imports) called process.exit during build-time schema ` +\n `discovery — those files fall back to runtime Zod instead of crashing the build. ` +\n `This is usually an env-validation guard; wrap it in ` +\n `\\`if (!process.env.ZOD_COMPILER) { ... }\\` to keep these schemas compiled, or set ` +\n `schemas:\"explicit\" / use include to scope discovery.`,\n );\n }\n return finishHoistOnly();\n }\n // In autoDiscover mode, files that can't be loaded (JSX components,\n // unresolved path aliases, etc.) are expected — warn and skip.\n if (autoDiscover) {\n if (verbose) {\n warn(`Skipping ${id}: ${msg}`);\n }\n return finishHoistOnly();\n }\n throw new Error(`[zod-compiler] Failed to load schemas from ${id}: ${msg}`);\n }\n const discoverMs = performance.now() - discoverStart;\n if (discoverMs >= SLOW_DISCOVERY_WARN_MS) {\n // Discovery executes the file's whole first-party import graph inside\n // the bundler's single-threaded process — on saturated CI hosts a large\n // graph can stall the event loop long enough to trip test timeouts.\n // Surface the cost with the two effective remedies.\n warn(\n `Discovery of ${id} took ${(discoverMs / 1000).toFixed(1)}s executing its import graph ` +\n `in the bundler process. Persist node_modules/.cache/zod-compiler across CI runs to pay ` +\n `this once, or narrow autoDiscover/include for test runs (see README \"Large projects ` +\n `and CI\"). ZOD_COMPILER_TIMING=1 prints a per-phase breakdown.`,\n );\n }\n if (schemas.length === 0) return finishHoistOnly();\n\n // Lean mode (every bundler in VIRTUAL_MODULE_FRAMEWORKS — Vite/Rollup/webpack/rspack/etc.)\n // imports shared helpers from a runtime module for cross-file dedup: virtual:zod-compiler/runtime\n // on virtual-friendly bundlers, the __zod-compiler-runtime__ bare specifier on webpack/rspack.\n // Inline mode (CLI emitter, and any bundler not in VIRTUAL_MODULE_FRAMEWORKS) emits self-contained\n // file-level helpers.\n let failedCount = 0;\n const { schemas: compiled, shared } = timePhase(\"compile\", () =>\n compileSchemas(schemas, {\n mode,\n compact: options.compact,\n onError(exportName, error) {\n failedCount++;\n warn(\n `Failed to compile \"${exportName}\" in ${id}: ${error.message}. Keeping original${autoDiscover ? \"\" : \" compile()\"} call.`,\n );\n },\n }),\n );\n\n if (verbose) {\n if (autoDiscover) {\n log(\n `Auto-discovering: ${id} (${schemas.length} Zod export${schemas.length > 1 ? \"s\" : \"\"} found)`,\n );\n }\n for (const s of compiled) {\n const rfCount = s.refEntries.length;\n const rfSuffix = rfCount > 0 ? ` (${rfCount} ref${rfCount > 1 ? \"s\" : \"\"})` : \"\";\n log(` ✓ ${s.exportName}${rfSuffix}`);\n }\n if (failedCount > 0) {\n log(` ✗ ${failedCount} schema(s) failed`);\n }\n }\n\n if (compiled.length === 0) return finishHoistOnly();\n\n // Report build stats only when at least one schema was compiled\n // (hoisted-schema compiles count alongside export schemas).\n options.onBuildStats?.({\n files: 1,\n schemas: schemas.length + (hoistedSchemas?.schemas.length ?? 0),\n optimized: compiled.length + hoistCompiledCount,\n failed: failedCount,\n });\n\n // __zcMkv and __zcFin are always needed (they wrap every IIFE). Helpers used\n // by compiled hoisted schemas ride along in the same injection.\n const usedHelpers = aggregateUsedHelpers(compiled);\n usedHelpers.add(\"__zcMkv\");\n usedHelpers.add(\"__zcFin\");\n for (const helper of hoistHelpers) {\n usedHelpers.add(helper);\n }\n // Shared dedup validators ride the same runtime import.\n for (const helper of shared.usedHelpers) {\n usedHelpers.add(helper);\n }\n\n // Two-pass rewrite: separate compile() schemas from autoDiscover schemas.\n // Both passes collect edits against the same pristine stage input — their\n // target regions are disjoint (compile() assignments vs plain exported\n // declarations of OTHER names), so one batched application is equivalent\n // to the historical sequential rewrites.\n let rewriteEdits: readonly Edit[];\n if (autoDiscover) {\n // Detect compile() schemas by checking source code patterns\n const compileExportNames = new Set<string>();\n for (const s of compiled) {\n const pattern = new RegExp(`\\\\b${s.exportName}\\\\s*=\\\\s*compile[\\\\s<(]`);\n if (pattern.test(staged.current)) {\n compileExportNames.add(s.exportName);\n }\n }\n const compileSchemaInfos = compiled.filter((s) => compileExportNames.has(s.exportName));\n const autoDiscoverSchemaInfos = compiled.filter((s) => !compileExportNames.has(s.exportName));\n\n const edits: Edit[] = [];\n // Pass 1: compile() schemas (includes compile-import removal — only when\n // compile() schemas were actually rewritten, mirroring the historical\n // conditional rewriteSource call)\n if (compileSchemaInfos.length > 0) {\n edits.push(\n ...collectCompileRewriteEdits(staged.current, compileSchemaInfos, {\n zodCompat: options.zodCompat,\n }),\n );\n }\n // Pass 2: plain exported schemas\n if (autoDiscoverSchemaInfos.length > 0) {\n edits.push(\n ...collectAutoDiscoverEdits(staged.current, autoDiscoverSchemaInfos, {\n zodCompat: options.zodCompat,\n }),\n );\n }\n rewriteEdits = edits;\n } else {\n rewriteEdits = collectCompileRewriteEdits(staged.current, compiled, {\n zodCompat: options.zodCompat,\n });\n }\n\n // Head = runtime helpers/import, then the file-level shared dedup block, then\n // the rewritten source. Shared constants and `__zcSw_N` functions live at\n // module scope so every IIFE closes over them; they must follow the runtime\n // import (lean) and the helper decls (inline) that they reference. Guard against\n // double-injection on watch/HMR re-runs the same way computeRuntimePrefix\n // does — a second copy would redeclare every `__zcSw_N`.\n //\n // Deferred into the rewrite's own stage rather than staged after it: both\n // decisions read the REWRITTEN text, and giving a bare prepend its own stage\n // doubled the sourcemap work and added a composition pass (see applyThen).\n staged.applyThen(rewriteEdits, (rewritten) => {\n const prefix = computeRuntimePrefix(rewritten, usedHelpers, mode, options.runtimeId);\n const needsShared = shared.code !== \"\" && !rewritten.includes(SHARED_BLOCK_MARKER);\n const head = (prefix ?? \"\") + (needsShared ? `${shared.code}\\n` : \"\");\n return head === \"\" ? undefined : head;\n });\n return { code: staged.current, map: staged.map() };\n}\n\n/**\n * Prepend the runtime helpers required by the rewritten source.\n *\n * Lean mode emits a single `import { ... } from \"<runtimeId>\";` line —\n * bundlers whose resolveId hook intercepts the specifier dedup helpers across\n * every transformed file into one shared virtual module.\n *\n * Inline mode prepends file-level `function __zcMkv` / `function __zcFin`\n * declarations directly so the file is self-contained.\n *\n * Idempotent: if the file already contains the relevant marker (re-run during\n * watch/HMR), we skip re-injection.\n */\n/** The runtime-helper text to prepend, or null when nothing is needed. */\nfunction computeRuntimePrefix(\n code: string,\n usedHelpers: Set<string>,\n mode: CodegenMode,\n runtimeId: string = VIRTUAL_RUNTIME_ID,\n): string | null {\n if (mode === \"lean\") {\n if (usedHelpers.size === 0) return null;\n // Match the import STATEMENT, not the bare id. `zod-compiler/runtime` is a\n // plain package specifier and a substring of `virtual:zod-compiler/runtime`,\n // so a file merely mentioning either — a comment, a docs snippet — would\n // suppress the import while codegen still emits calls to the helpers.\n if (code.includes(`from \"${runtimeId}\"`)) return null;\n const names = [...usedHelpers].sort().join(\", \");\n return `import { ${names} } from \"${runtimeId}\";\\n`;\n }\n // Inline mode (CLI emitter, and any bundler not in VIRTUAL_MODULE_FRAMEWORKS):\n // ship file-level helper declarations instead of a virtual import.\n // Codegen emits per-IIFE issue literals + per-IIFE `__re_*` decls,\n // so we only need __zcMkv / __zcFin (plus __zcMsg via the zod config import).\n if (!code.includes(\"__zcMkv\")) return null;\n const prefix: string[] = [];\n if (!code.includes(\"__zodCompilerConfig\")) {\n prefix.push(ZOD_CONFIG_IMPORT, ZOD_MSG_DECLARATION);\n }\n if (!code.includes(\"function __zcMkv(\")) {\n prefix.push(MK_VALIDATOR_DECL);\n }\n // __zcFin and __zcFinD both construct __ZcFail; declare it once before either,\n // guarding against a header already shipped earlier in the same module.\n const needsFin = !code.includes(\"function __zcFin(\");\n const needsFinD = code.includes(\"__zcFinD(\") && !code.includes(\"function __zcFinD(\");\n if ((needsFin || needsFinD) && !code.includes(\"function __ZcFail(\")) {\n prefix.push(FAIL_CLASS_DECL);\n }\n if (needsFin) {\n prefix.push(FIN_DECL);\n }\n if (needsFinD) {\n prefix.push(FIN_DEFERRED_DECL);\n }\n // Compact mode (output: \"compact\") delegates cold errors to zod via __zcFinZ,\n // which constructs its own __ZcFailZ (distinct from __ZcFail).\n const needsFinZ = code.includes(\"__zcFinZ(\") && !code.includes(\"function __zcFinZ(\");\n if (needsFinZ && !code.includes(\"function __ZcFailZ(\")) {\n prefix.push(FAILZ_CLASS_DECL);\n }\n if (needsFinZ) {\n prefix.push(FINZ_DECL);\n }\n return prefix.length > 0 ? `${prefix.join(\"\\n\")}\\n` : null;\n}\n\n/**\n * Find the matching closing parenthesis for a compile() call,\n * handling nested parentheses like compile(z.object({...})).\n * Returns the index of the closing ')' or -1 if not found.\n */\nfunction findMatchingParen(code: string, openIndex: number): number {\n let depth = 1;\n for (let i = openIndex + 1; i < code.length; i++) {\n if (code[i] === \"(\") depth++;\n else if (code[i] === \")\") {\n depth--;\n if (depth === 0) return i;\n }\n }\n return -1;\n}\n\n/**\n * Rewrite source code by replacing compile() calls with IIFE-wrapped optimized validators.\n */\nexport function rewriteSource(\n code: string,\n schemas: CompiledSchemaInfo[],\n options?: { zodCompat?: boolean | undefined },\n): string {\n return applyEdits(code, collectCompileRewriteEdits(code, schemas, options));\n}\n\n/**\n * Edits for rewriteSource (compile() call replacements + compile-import\n * removal), collected against pristine `code`. Each schema's declaration is\n * a distinct region and the import statement is distinct from all of them,\n * so the batch is non-overlapping and order-independent.\n */\nfunction collectCompileRewriteEdits(\n code: string,\n schemas: CompiledSchemaInfo[],\n options?: { zodCompat?: boolean | undefined },\n): Edit[] {\n const edits: Edit[] = [];\n for (const schema of schemas) {\n // Match: <exportName> = compile<...>( with word boundary to prevent substring matches\n const prefixPattern = new RegExp(\n `(\\\\b${schema.exportName}\\\\s*=\\\\s*)compile\\\\s*(?:<[^>]*(?:<[^>]*>[^>]*)?>)?\\\\s*\\\\(`,\n );\n const match = prefixPattern.exec(code);\n if (!match) continue;\n\n // Find the matching closing paren (handles nested parens)\n const openParenIndex = match.index + match[0].length - 1;\n const closeParenIndex = findMatchingParen(code, openParenIndex);\n if (closeParenIndex === -1) continue;\n\n const schemaArgName = code\n .slice(openParenIndex + 1, closeParenIndex)\n .trim()\n .replace(/,\\s*$/, \"\");\n const prefix = match[1] ?? \"\";\n edits.push({\n start: match.index,\n end: closeParenIndex + 1,\n text: prefix + generateIIFE(schemaArgName, schema, options),\n });\n }\n edits.push(...collectRemoveCompileImportEdits(code));\n return edits;\n}\n\n/**\n * Find the end position of a JavaScript expression starting at `start` using acorn.\n * Returns the end offset, or -1 if the expression cannot be parsed.\n */\nexport function findExpressionEnd(code: string, start: number): number {\n try {\n const node = parseExpressionAt(code, start, {\n ecmaVersion: \"latest\",\n sourceType: \"module\",\n });\n return node.end;\n } catch {\n return -1;\n }\n}\n\n/**\n * Rewrite source code by replacing plain Zod schema exports with IIFE-wrapped optimized validators.\n * Used by autoDiscover mode (no compile() wrappers needed).\n */\nexport function rewriteSourceAutoDiscover(\n code: string,\n schemas: CompiledSchemaInfo[],\n options?: { zodCompat?: boolean | undefined },\n): string {\n return applyEdits(code, collectAutoDiscoverEdits(code, schemas, options));\n}\n\n/**\n * Does `expr` mention `name` as an identifier?\n *\n * Deliberately lexical, and deliberately biased toward YES. A false positive\n * costs one export its `@__PURE__` annotation; a false negative emits an IIFE\n * that dereferences a binding still under initialization. The expression text\n * is often TypeScript (`z.custom<T>(...)`), which no JS parser here can be\n * trusted to walk, so a word-boundary scan — which cannot miss a real\n * identifier reference — is the sound direction to be wrong in.\n */\nfunction mentionsIdentifier(expr: string, name: string): boolean {\n return new RegExp(`\\\\b${name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}\\\\b`).test(expr);\n}\n\n/** Edits for rewriteSourceAutoDiscover, collected against pristine `code`. */\nfunction collectAutoDiscoverEdits(\n code: string,\n schemas: CompiledSchemaInfo[],\n options?: { zodCompat?: boolean | undefined },\n): Edit[] {\n const edits: Edit[] = [];\n for (const schema of schemas) {\n const escapedName = schema.exportName.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n // Match: export? (const|let|var) ExportName[: TypeAnnotation] = <expr>\n const assignPattern = new RegExp(\n `((?:export\\\\s+)?(?:const|let|var)\\\\s+${escapedName}(?:\\\\s*:[^=]*)?\\\\s*=\\\\s*)`,\n );\n const match = assignPattern.exec(code);\n if (!match) continue;\n\n const rhsStart = match.index + match[0].length;\n const rhsEnd = findExpressionEnd(code, rhsStart);\n if (rhsEnd === -1) continue;\n\n const originalExpr = code.slice(rhsStart, rhsEnd).trim();\n\n // A RECURSIVE schema defers its self-reference through a callback —\n // `z.lazy(() => z.array(Node))`, or zod v4's getter form\n // `get children() { return z.array(Node) }` — and that callback closes over\n // the module binding declared here. Replacing the initializer puts the\n // IIFE's `var __rf=[__zs._zod.innerType...]` preamble INSIDE that binding's\n // own initializer, so forcing the callback re-enters a binding that is not\n // yet assigned: a TDZ ReferenceError at module init, or — once a bundler\n // lowers the top-level `const` to `var`, as esbuild does — a silent\n // `undefined` that zod's `defineLazy` then CACHES, permanently poisoning\n // the schema for every consumer (`z.array(undefined)`).\n //\n // So the deref moves out of the initializer: the declaration keeps its\n // original expression and the IIFE follows it as a statement, mutating the\n // now-assigned schema in place. `__zcMkv` returns its argument (identity is\n // preserved by design), so the export is the same object either way, and\n // `__rfp_N`'s pristine-`safeParse` capture still happens before the\n // trailing `__zcMkv` installs anything. The cost is this export's\n // `@__PURE__` annotation — a self-referential schema is no longer\n // droppable when unused.\n if (iifeDerefsSchema(schema) && mentionsIdentifier(originalExpr, schema.exportName)) {\n // `output: \"bag\"` replaces the export with a method bag rather than\n // mutating the schema, so there is nothing to mutate in place — and the\n // user's own recursive reference would resolve to the bag regardless.\n if (options?.zodCompat === false) {\n warn(\n `Skipping self-referential export \"${schema.exportName}\": output \"bag\" cannot preserve its recursive reference. Keeping the original schema.`,\n );\n continue;\n }\n // Re-emitting `originalExpr` verbatim is what makes splitting the\n // declaration safe for `const Schema = <expr>, other = 1;`:\n // findExpressionEnd parses an Expression, and the comma operator makes\n // that span the whole declarator list, so the siblings are inside the\n // text being written back rather than after the statement terminator.\n edits.push({\n start: rhsStart,\n end: rhsEnd,\n text: `${originalExpr};\\n${generateIIFE(schema.exportName, schema, { ...options, pure: false })};`,\n });\n continue;\n }\n\n edits.push({\n start: rhsStart,\n end: rhsEnd,\n text: generateIIFE(originalExpr, schema, options),\n });\n }\n return edits;\n}\n\n/**\n * Remove the `compile` binding from `import { compile, ... } from \"zod-compiler\"` statements.\n * If `compile` is the only import, the entire import line is removed.\n */\nexport function removeCompileImport(code: string): string {\n return applyEdits(code, collectRemoveCompileImportEdits(code));\n}\n\n/** Edits stripping the `compile` binding from zod-compiler import statements. */\nfunction collectRemoveCompileImportEdits(code: string): Edit[] {\n // Match: import { ... } from \"zod-compiler\" or 'zod-compiler'\n const importPattern = /import\\s*\\{([^}]*)\\}\\s*from\\s*[\"']zod-compiler[\"'];?/g;\n const edits: Edit[] = [];\n for (const match of code.matchAll(importPattern)) {\n const imports = match[1] ?? \"\";\n const names = imports\n .split(\",\")\n .map((n) => n.trim())\n .filter(Boolean);\n const remaining = names.filter((n) => n !== \"compile\");\n const text =\n remaining.length === 0 ? \"\" : `import { ${remaining.join(\", \")} } from \"zod-compiler\";`;\n if (text !== match[0]) {\n edits.push({ start: match.index, end: match.index + match[0].length, text });\n }\n }\n return edits;\n}\n"],"mappings":"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"}
|
package/dist/unplugin/types.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { HoistOptions } from "./hoist.js";
|
|
|
3
3
|
//#region src/unplugin/types.d.ts
|
|
4
4
|
interface TransformOptions {
|
|
5
5
|
mode: CodegenMode;
|
|
6
|
-
runtimeId?: string;
|
|
6
|
+
runtimeId?: string | undefined;
|
|
7
7
|
zodCompat?: boolean | undefined;
|
|
8
8
|
/** Compact output: compile only the fast path; delegate cold errors to the retained Zod schema. */
|
|
9
9
|
compact?: boolean | undefined;
|
|
@@ -170,6 +170,41 @@ interface ZodCompilerPluginOptions {
|
|
|
170
170
|
* @default determined by bundler
|
|
171
171
|
*/
|
|
172
172
|
codegenMode?: "lean" | "inline" | undefined;
|
|
173
|
+
/**
|
|
174
|
+
* Run transforms on worker threads instead of the bundler's own (Node.js only).
|
|
175
|
+
*
|
|
176
|
+
* Schema discovery executes each file's import graph in-process, and the
|
|
177
|
+
* loader serializes those executions so concurrent transforms cannot
|
|
178
|
+
* double-execute a shared dependency — so a cold build compiles one file at
|
|
179
|
+
* a time on one thread. Workers lift that limit: each owns a private loader
|
|
180
|
+
* and module cache, which is exactly why running several concurrently is
|
|
181
|
+
* sound. Measured on 120 files of deeply nested schemas, 3,633 ms sequential
|
|
182
|
+
* became 1,508 ms at four workers.
|
|
183
|
+
*
|
|
184
|
+
* **Whether it pays depends on your import graph, not your core count.** A
|
|
185
|
+
* dependency shared by many schema files is executed once in-process and
|
|
186
|
+
* once PER WORKER here, so files with independent graphs win big and files
|
|
187
|
+
* chained through each other can lose: the same fixture rewired into a
|
|
188
|
+
* 120-deep import chain measured 1,045 ms at four workers against 945 ms
|
|
189
|
+
* in-process. Measure with `ZOD_COMPILER_TIMING=1` before adopting it.
|
|
190
|
+
*
|
|
191
|
+
* Opt-in for that reason and for memory: every worker holds its own copy of
|
|
192
|
+
* zod and of the graph it executed. That compounds with runners which
|
|
193
|
+
* already shard across processes (Vitest pools, Nx, Turborepo), where the
|
|
194
|
+
* cores are spoken for.
|
|
195
|
+
*
|
|
196
|
+
* - `false` (default): transforms run in-process, exactly as before.
|
|
197
|
+
* - `true`: one worker per core, less one for the bundler, capped at 4 —
|
|
198
|
+
* throughput peaks around four and declines past it.
|
|
199
|
+
* - `<number>`: exactly that many workers (1-32).
|
|
200
|
+
*
|
|
201
|
+
* Falls back to in-process transforms — with a warning — if workers cannot
|
|
202
|
+
* be started. The disk cache, the static dependency crawl and all result
|
|
203
|
+
* bookkeeping stay on the bundler thread either way, so cache contents and
|
|
204
|
+
* emitted output are identical to a serial build.
|
|
205
|
+
* @default false
|
|
206
|
+
*/
|
|
207
|
+
parallel?: boolean | number | undefined;
|
|
173
208
|
/**
|
|
174
209
|
* Persistent transform-result cache (Node.js only).
|
|
175
210
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/unplugin/types.ts"],"mappings":";;;UAGiB;EACf,MAAM;EACN;EACA;;EAEA;EACA;EACA;EACA,kBAAkB;EAClB,gBAAgB,OAAO;;EAEvB;;;;;;;;;;EAUA;;;;;;;;;;EAUA;;UAGe;EACf;EACA;EACA;EACA;;cAGW,iCAAiC;EAC5C;EACA;EACA;EACA;EAEA,IAAI,GAAG;EAOP;;UAQe;;EAEf;;EAEA;;;;;;;;;;;;;;;;;;;;;;;;EAwBA;;;;;;;;;;;;;;;;;;;;;;;;;EAyBA;;;;;;EAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8BA;IAAoB,oBAAoB;;;;;;;;;;;;;;;EAcxC;;;;;;;;;;;;;;;;EAgBA;;;;;;;;;;;;;;;;;;EAkBA"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","names":[],"sources":["../../src/unplugin/types.ts"],"mappings":";;;UAGiB;EACf,MAAM;EACN;EACA;;EAEA;EACA;EACA;EACA,kBAAkB;EAClB,gBAAgB,OAAO;;EAEvB;;;;;;;;;;EAUA;;;;;;;;;;EAUA;;UAGe;EACf;EACA;EACA;EACA;;cAGW,iCAAiC;EAC5C;EACA;EACA;EACA;EAEA,IAAI,GAAG;EAOP;;UAQe;;EAEf;;EAEA;;;;;;;;;;;;;;;;;;;;;;;;EAwBA;;;;;;;;;;;;;;;;;;;;;;;;;EAyBA;;;;;;EAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8BA;IAAoB,oBAAoB;;;;;;;;;;;;;;;EAcxC;;;;;;;;;;;;;;;;EAgBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmCA;;;;;;;;;;;;;;;;;;EAkBA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","names":[],"sources":["../../src/unplugin/types.ts"],"sourcesContent":["import type { CodegenMode } from \"../core/codegen/context.js\";\nimport type { HoistOptions } from \"./hoist.js\";\n\nexport interface TransformOptions {\n mode: CodegenMode;\n runtimeId?: string;\n zodCompat?: boolean | undefined;\n /** Compact output: compile only the fast path; delegate cold errors to the retained Zod schema. */\n compact?: boolean | undefined;\n verbose?: boolean | undefined;\n autoDiscover?: boolean | undefined;\n hoist?: boolean | HoistOptions | undefined;\n onBuildStats?: (stats: BuildStats) => void;\n /** Fired when discovery (file execution) is about to run — used by the disk cache to decide which results are worth persisting. */\n onDiscovery?: () => void;\n /**\n * Fired when the transform did parse-level work short of discovery (hoist\n * source scan, static export filter). The disk cache persists even null\n * results for these files: re-deriving \"no transform needed\" costs a full\n * scan per zod-importing file per run (a field report measured 35.8s/run\n * of hoist scans in hoist-only mode, all producing never-cached nulls).\n * Purely textual bail-outs never fire this — caching those would trade a\n * substring check for a disk entry per source file in the project.\n */\n onSubstantialWork?: () => void;\n /**\n * Fired when discovery was aborted by a build-time `process.exit` (an\n * env-validation guard in a secret-less build). The recovered result is a\n * function of the *environment*, not the file content, so the disk cache\n * must NOT persist it keyed on content hashes: a later build with secrets\n * present would otherwise be served the stale \"nothing compiled\" entry and\n * silently ship un-optimized schemas. The in-memory (content-keyed, single-\n * process) cache is unaffected.\n */\n onUncacheableResult?: () => void;\n}\n\nexport interface BuildStats {\n files: number;\n schemas: number;\n optimized: number;\n failed: number;\n}\n\nexport class BuildStatsAccumulator implements BuildStats {\n files = 0;\n schemas = 0;\n optimized = 0;\n failed = 0;\n\n add(s: BuildStats): void {\n this.files += s.files;\n this.schemas += s.schemas;\n this.optimized += s.optimized;\n this.failed += s.failed;\n }\n\n reset(): void {\n this.files = 0;\n this.schemas = 0;\n this.optimized = 0;\n this.failed = 0;\n }\n}\n\nexport interface ZodCompilerPluginOptions {\n /** Glob patterns to include (default: [\"**\\/*.ts\", \"**\\/*.tsx\"]) */\n include?: string[];\n /** Glob patterns to exclude (default: [\"node_modules/**\", \"**\\/*.d.ts\"]) */\n exclude?: string[];\n /**\n * How schemas are found.\n *\n * - `\"auto\"` (default): every exported plain Zod schema compiles — no\n * wrappers, no zod-compiler imports in source. Detection scans files\n * with a runtime `import ... from \"zod\"`, statically pre-filters ones\n * whose exports provably aren't schemas, and executes the remaining\n * candidates to check exports for `_zod.def`. Also enables build-time\n * compilation of hoisted in-function schemas (anonymous schemas like\n * `sql.type(z.object(...))` — there is no exported name to opt in with).\n * - `\"explicit\"`: only schemas wrapped in `compile()` from zod-compiler\n * are compiled; build-time file execution is limited to files that\n * import it. Hoisting still applies, but hoisted schemas stay plain Zod.\n *\n * **Note:** in `\"auto\"` mode, candidate files are executed at build time\n * via `loadSourceFile()`. Use `include` to limit scope if your project\n * has schema-shaped files with side effects. A module that calls\n * `process.exit()` during this execution (e.g. an env-validation guard in a\n * secret-less CI build) is caught — the file falls back to runtime Zod\n * instead of crashing the build. Guard the exit on `process.env.ZOD_COMPILER`\n * to keep such schemas compiled.\n * @default \"auto\"\n */\n schemas?: \"explicit\" | \"auto\" | undefined;\n /**\n * What a compiled schema export evaluates to.\n *\n * - `\"schema\"` (default): the original Zod schema object with the compiled\n * `parse`/`safeParse`/`parseAsync`/`safeParseAsync` installed as own\n * properties — identity is preserved, so `.shape`, `.meta()`,\n * `z.toJSONSchema()`, `instanceof`, Standard Schema, and libraries like\n * @hono/zod-validator and tRPC keep working.\n * - `\"bag\"`: a minimal plain object with just the compiled methods —\n * smaller bundles (the Zod construction can tree-shake away), but\n * anything expecting a real Zod schema breaks.\n * - `\"compact\"`: like `\"schema\"` (Zod identity preserved, full API), but only\n * the fast validation path is compiled. The cold error path is delegated to\n * the retained Zod schema's own `safeParse`, dropping the compiled slow walk\n * (64–77% of generated bytes). Errors are byte-identical to Zod's (no second\n * validation engine). The hot path — `parse`/`safeParse` of valid input and\n * `.is()` — is unchanged; only error *reporting* on invalid input runs Zod\n * (deferred until `.error` is read, so `.success`/`.is()` checks stay fast).\n * Modelled rewrites (`default`/`coerce`/`transform`/string overwrites) keep\n * their compiled build pass and delegate only issue production; unmodelled\n * mutations such as `catch`, URL normalization and `superRefine` keep the\n * fully compiled path. Best for large schema sets where bundle size dominates.\n * @default \"schema\"\n */\n output?: \"schema\" | \"bag\" | \"compact\" | undefined;\n /**\n * Enable verbose logging during build.\n * Logs per-schema compilation status and a build summary.\n * @default false\n */\n verbose?: boolean | undefined;\n /**\n * Hoist Zod schema construction out of function bodies to module scope\n * (the babel-plugin-zod-hoist optimization). A schema defined inside a\n * function — a React component, a request handler — is otherwise rebuilt\n * on every call:\n *\n * ```typescript\n * function getSchema() {\n * return z.object({ name: z.string() }); // rebuilt per call\n * }\n * // becomes\n * const _zh_94b7f5c1 = z.object({ name: z.string() });\n * function getSchema() {\n * return _zh_94b7f5c1; // built once\n * }\n * ```\n *\n * Only expressions built purely from imported bindings and literals are\n * hoisted — anything referencing local variables, module-level bindings,\n * `this`, or globals stays put (safe globals like `Number` are allowed\n * inside callbacks, which run per call regardless). Identical schemas\n * dedupe to one binding.\n *\n * Pass an object to configure `schemaNamePattern` (default `/ZodSchema$/`):\n * imported identifiers matching it are hoistable combinator-chain roots\n * even without an inline z.* reference (`UserZodSchema.partial()`). Set it\n * to `null` to disable name-based matching.\n * @default true\n */\n hoist?: boolean | { schemaNamePattern?: RegExp | string | null | undefined } | undefined;\n /**\n * **Vite only** (other bundlers ignore this option): when the plugin runs.\n *\n * By default the plugin compiles production builds **and test runs**\n * (Vitest is detected via the `VITEST` env var / `\"test\"` mode), so tests\n * exercise — and benefit from — the same compiled validators that ship.\n * Plain dev servers skip AOT compilation cost; `compile()` transparently\n * falls back to Zod's runtime validation there, so behavior stays correct.\n *\n * Set `\"build\"` to also skip tests, `\"serve\"` for dev/test-only, or\n * `\"all\"` to compile everywhere including the dev server.\n * @default builds + Vitest\n */\n apply?: \"build\" | \"serve\" | \"all\" | undefined;\n /**\n * Override the codegen mode used by the plugin.\n *\n * - `\"lean\"` (default for bundlers): runtime helpers are imported from\n * `virtual:zod-compiler/runtime`, which the bundler resolves via its\n * `onResolve`/`onLoad` hooks. Produces smaller per-file output and lets\n * the bundler tree-shake unused helpers.\n * - `\"inline\"`: runtime helpers are emitted directly into each transformed\n * file. Use this for transpile-only builds (e.g. `esbuild` without\n * `--bundle`, `astro-scripts build`) where the bundler's module hooks\n * never fire for already-transformed output and the `virtual:` specifier\n * would survive into `dist/` causing `ERR_UNSUPPORTED_ESM_URL_SCHEME`.\n *\n * @default determined by bundler\n */\n codegenMode?: \"lean\" | \"inline\" | undefined;\n /**\n * Persistent transform-result cache (Node.js only).\n *\n * Schema discovery executes schema files (and their import graphs) inside\n * the bundler process; without a disk cache that cost is re-paid on every\n * `vitest run` / build even when nothing changed. Entries are validated\n * against content hashes of every first-party module the discovery\n * executed, so edits to a schema file *or any file it imports* invalidate\n * exactly as the in-memory watch invalidation does — but across processes.\n *\n * Caveat: module-scope dynamism in schema files (schemas derived from\n * `process.env`, `Date.now()`, …) is frozen until a watched source file\n * changes. Disable the cache for such setups.\n *\n * Pass a string to use a custom cache directory.\n * @default true (node_modules/.cache/zod-compiler)\n */\n cache?: boolean | string | undefined;\n}\n"],"mappings":";AA4CA,IAAa,wBAAb,MAAyD;CACvD,QAAQ;CACR,UAAU;CACV,YAAY;CACZ,SAAS;CAET,IAAI,GAAqB;EACvB,KAAK,SAAS,EAAE;EAChB,KAAK,WAAW,EAAE;EAClB,KAAK,aAAa,EAAE;EACpB,KAAK,UAAU,EAAE;CACnB;CAEA,QAAc;EACZ,KAAK,QAAQ;EACb,KAAK,UAAU;EACf,KAAK,YAAY;EACjB,KAAK,SAAS;CAChB;AACF"}
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../../src/unplugin/types.ts"],"sourcesContent":["import type { CodegenMode } from \"../core/codegen/context.js\";\nimport type { HoistOptions } from \"./hoist.js\";\n\nexport interface TransformOptions {\n mode: CodegenMode;\n runtimeId?: string | undefined;\n zodCompat?: boolean | undefined;\n /** Compact output: compile only the fast path; delegate cold errors to the retained Zod schema. */\n compact?: boolean | undefined;\n verbose?: boolean | undefined;\n autoDiscover?: boolean | undefined;\n hoist?: boolean | HoistOptions | undefined;\n onBuildStats?: (stats: BuildStats) => void;\n /** Fired when discovery (file execution) is about to run — used by the disk cache to decide which results are worth persisting. */\n onDiscovery?: () => void;\n /**\n * Fired when the transform did parse-level work short of discovery (hoist\n * source scan, static export filter). The disk cache persists even null\n * results for these files: re-deriving \"no transform needed\" costs a full\n * scan per zod-importing file per run (a field report measured 35.8s/run\n * of hoist scans in hoist-only mode, all producing never-cached nulls).\n * Purely textual bail-outs never fire this — caching those would trade a\n * substring check for a disk entry per source file in the project.\n */\n onSubstantialWork?: () => void;\n /**\n * Fired when discovery was aborted by a build-time `process.exit` (an\n * env-validation guard in a secret-less build). The recovered result is a\n * function of the *environment*, not the file content, so the disk cache\n * must NOT persist it keyed on content hashes: a later build with secrets\n * present would otherwise be served the stale \"nothing compiled\" entry and\n * silently ship un-optimized schemas. The in-memory (content-keyed, single-\n * process) cache is unaffected.\n */\n onUncacheableResult?: () => void;\n}\n\nexport interface BuildStats {\n files: number;\n schemas: number;\n optimized: number;\n failed: number;\n}\n\nexport class BuildStatsAccumulator implements BuildStats {\n files = 0;\n schemas = 0;\n optimized = 0;\n failed = 0;\n\n add(s: BuildStats): void {\n this.files += s.files;\n this.schemas += s.schemas;\n this.optimized += s.optimized;\n this.failed += s.failed;\n }\n\n reset(): void {\n this.files = 0;\n this.schemas = 0;\n this.optimized = 0;\n this.failed = 0;\n }\n}\n\nexport interface ZodCompilerPluginOptions {\n /** Glob patterns to include (default: [\"**\\/*.ts\", \"**\\/*.tsx\"]) */\n include?: string[];\n /** Glob patterns to exclude (default: [\"node_modules/**\", \"**\\/*.d.ts\"]) */\n exclude?: string[];\n /**\n * How schemas are found.\n *\n * - `\"auto\"` (default): every exported plain Zod schema compiles — no\n * wrappers, no zod-compiler imports in source. Detection scans files\n * with a runtime `import ... from \"zod\"`, statically pre-filters ones\n * whose exports provably aren't schemas, and executes the remaining\n * candidates to check exports for `_zod.def`. Also enables build-time\n * compilation of hoisted in-function schemas (anonymous schemas like\n * `sql.type(z.object(...))` — there is no exported name to opt in with).\n * - `\"explicit\"`: only schemas wrapped in `compile()` from zod-compiler\n * are compiled; build-time file execution is limited to files that\n * import it. Hoisting still applies, but hoisted schemas stay plain Zod.\n *\n * **Note:** in `\"auto\"` mode, candidate files are executed at build time\n * via `loadSourceFile()`. Use `include` to limit scope if your project\n * has schema-shaped files with side effects. A module that calls\n * `process.exit()` during this execution (e.g. an env-validation guard in a\n * secret-less CI build) is caught — the file falls back to runtime Zod\n * instead of crashing the build. Guard the exit on `process.env.ZOD_COMPILER`\n * to keep such schemas compiled.\n * @default \"auto\"\n */\n schemas?: \"explicit\" | \"auto\" | undefined;\n /**\n * What a compiled schema export evaluates to.\n *\n * - `\"schema\"` (default): the original Zod schema object with the compiled\n * `parse`/`safeParse`/`parseAsync`/`safeParseAsync` installed as own\n * properties — identity is preserved, so `.shape`, `.meta()`,\n * `z.toJSONSchema()`, `instanceof`, Standard Schema, and libraries like\n * @hono/zod-validator and tRPC keep working.\n * - `\"bag\"`: a minimal plain object with just the compiled methods —\n * smaller bundles (the Zod construction can tree-shake away), but\n * anything expecting a real Zod schema breaks.\n * - `\"compact\"`: like `\"schema\"` (Zod identity preserved, full API), but only\n * the fast validation path is compiled. The cold error path is delegated to\n * the retained Zod schema's own `safeParse`, dropping the compiled slow walk\n * (64–77% of generated bytes). Errors are byte-identical to Zod's (no second\n * validation engine). The hot path — `parse`/`safeParse` of valid input and\n * `.is()` — is unchanged; only error *reporting* on invalid input runs Zod\n * (deferred until `.error` is read, so `.success`/`.is()` checks stay fast).\n * Modelled rewrites (`default`/`coerce`/`transform`/string overwrites) keep\n * their compiled build pass and delegate only issue production; unmodelled\n * mutations such as `catch`, URL normalization and `superRefine` keep the\n * fully compiled path. Best for large schema sets where bundle size dominates.\n * @default \"schema\"\n */\n output?: \"schema\" | \"bag\" | \"compact\" | undefined;\n /**\n * Enable verbose logging during build.\n * Logs per-schema compilation status and a build summary.\n * @default false\n */\n verbose?: boolean | undefined;\n /**\n * Hoist Zod schema construction out of function bodies to module scope\n * (the babel-plugin-zod-hoist optimization). A schema defined inside a\n * function — a React component, a request handler — is otherwise rebuilt\n * on every call:\n *\n * ```typescript\n * function getSchema() {\n * return z.object({ name: z.string() }); // rebuilt per call\n * }\n * // becomes\n * const _zh_94b7f5c1 = z.object({ name: z.string() });\n * function getSchema() {\n * return _zh_94b7f5c1; // built once\n * }\n * ```\n *\n * Only expressions built purely from imported bindings and literals are\n * hoisted — anything referencing local variables, module-level bindings,\n * `this`, or globals stays put (safe globals like `Number` are allowed\n * inside callbacks, which run per call regardless). Identical schemas\n * dedupe to one binding.\n *\n * Pass an object to configure `schemaNamePattern` (default `/ZodSchema$/`):\n * imported identifiers matching it are hoistable combinator-chain roots\n * even without an inline z.* reference (`UserZodSchema.partial()`). Set it\n * to `null` to disable name-based matching.\n * @default true\n */\n hoist?: boolean | { schemaNamePattern?: RegExp | string | null | undefined } | undefined;\n /**\n * **Vite only** (other bundlers ignore this option): when the plugin runs.\n *\n * By default the plugin compiles production builds **and test runs**\n * (Vitest is detected via the `VITEST` env var / `\"test\"` mode), so tests\n * exercise — and benefit from — the same compiled validators that ship.\n * Plain dev servers skip AOT compilation cost; `compile()` transparently\n * falls back to Zod's runtime validation there, so behavior stays correct.\n *\n * Set `\"build\"` to also skip tests, `\"serve\"` for dev/test-only, or\n * `\"all\"` to compile everywhere including the dev server.\n * @default builds + Vitest\n */\n apply?: \"build\" | \"serve\" | \"all\" | undefined;\n /**\n * Override the codegen mode used by the plugin.\n *\n * - `\"lean\"` (default for bundlers): runtime helpers are imported from\n * `virtual:zod-compiler/runtime`, which the bundler resolves via its\n * `onResolve`/`onLoad` hooks. Produces smaller per-file output and lets\n * the bundler tree-shake unused helpers.\n * - `\"inline\"`: runtime helpers are emitted directly into each transformed\n * file. Use this for transpile-only builds (e.g. `esbuild` without\n * `--bundle`, `astro-scripts build`) where the bundler's module hooks\n * never fire for already-transformed output and the `virtual:` specifier\n * would survive into `dist/` causing `ERR_UNSUPPORTED_ESM_URL_SCHEME`.\n *\n * @default determined by bundler\n */\n codegenMode?: \"lean\" | \"inline\" | undefined;\n /**\n * Run transforms on worker threads instead of the bundler's own (Node.js only).\n *\n * Schema discovery executes each file's import graph in-process, and the\n * loader serializes those executions so concurrent transforms cannot\n * double-execute a shared dependency — so a cold build compiles one file at\n * a time on one thread. Workers lift that limit: each owns a private loader\n * and module cache, which is exactly why running several concurrently is\n * sound. Measured on 120 files of deeply nested schemas, 3,633 ms sequential\n * became 1,508 ms at four workers.\n *\n * **Whether it pays depends on your import graph, not your core count.** A\n * dependency shared by many schema files is executed once in-process and\n * once PER WORKER here, so files with independent graphs win big and files\n * chained through each other can lose: the same fixture rewired into a\n * 120-deep import chain measured 1,045 ms at four workers against 945 ms\n * in-process. Measure with `ZOD_COMPILER_TIMING=1` before adopting it.\n *\n * Opt-in for that reason and for memory: every worker holds its own copy of\n * zod and of the graph it executed. That compounds with runners which\n * already shard across processes (Vitest pools, Nx, Turborepo), where the\n * cores are spoken for.\n *\n * - `false` (default): transforms run in-process, exactly as before.\n * - `true`: one worker per core, less one for the bundler, capped at 4 —\n * throughput peaks around four and declines past it.\n * - `<number>`: exactly that many workers (1-32).\n *\n * Falls back to in-process transforms — with a warning — if workers cannot\n * be started. The disk cache, the static dependency crawl and all result\n * bookkeeping stay on the bundler thread either way, so cache contents and\n * emitted output are identical to a serial build.\n * @default false\n */\n parallel?: boolean | number | undefined;\n /**\n * Persistent transform-result cache (Node.js only).\n *\n * Schema discovery executes schema files (and their import graphs) inside\n * the bundler process; without a disk cache that cost is re-paid on every\n * `vitest run` / build even when nothing changed. Entries are validated\n * against content hashes of every first-party module the discovery\n * executed, so edits to a schema file *or any file it imports* invalidate\n * exactly as the in-memory watch invalidation does — but across processes.\n *\n * Caveat: module-scope dynamism in schema files (schemas derived from\n * `process.env`, `Date.now()`, …) is frozen until a watched source file\n * changes. Disable the cache for such setups.\n *\n * Pass a string to use a custom cache directory.\n * @default true (node_modules/.cache/zod-compiler)\n */\n cache?: boolean | string | undefined;\n}\n"],"mappings":";AA4CA,IAAa,wBAAb,MAAyD;CACvD,QAAQ;CACR,UAAU;CACV,YAAY;CACZ,SAAS;CAET,IAAI,GAAqB;EACvB,KAAK,SAAS,EAAE;EAChB,KAAK,WAAW,EAAE;EAClB,KAAK,aAAa,EAAE;EACpB,KAAK,UAAU,EAAE;CACnB;CAEA,QAAc;EACZ,KAAK,QAAQ;EACb,KAAK,UAAU;EACf,KAAK,YAAY;EACjB,KAAK,SAAS;CAChB;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zod-compiler",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.28.0",
|
|
4
4
|
"description": "Compile Zod schemas into zero-overhead validation functions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"aot",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
24
|
"dist",
|
|
25
|
+
"schema.json",
|
|
25
26
|
"LICENSE"
|
|
26
27
|
],
|
|
27
28
|
"type": "module",
|
|
@@ -39,6 +40,13 @@
|
|
|
39
40
|
"import": "./dist/jit.js",
|
|
40
41
|
"default": "./dist/jit.js"
|
|
41
42
|
},
|
|
43
|
+
"./register": {
|
|
44
|
+
"types": "./dist/register/index.d.ts",
|
|
45
|
+
"source": "./src/register/index.ts",
|
|
46
|
+
"import": "./dist/register/index.js",
|
|
47
|
+
"default": "./dist/register/index.js"
|
|
48
|
+
},
|
|
49
|
+
"./schema.json": "./schema.json",
|
|
42
50
|
"./vite": {
|
|
43
51
|
"types": "./dist/unplugin/vite.d.ts",
|
|
44
52
|
"source": "./src/unplugin/vite.ts",
|
|
@@ -125,6 +133,7 @@
|
|
|
125
133
|
"dependencies": {
|
|
126
134
|
"@jridgewell/remapping": "^2.3.5",
|
|
127
135
|
"acorn": "^8.16.0",
|
|
136
|
+
"es-module-lexer": "^2.3.1",
|
|
128
137
|
"get-tsconfig": "^4.14.0",
|
|
129
138
|
"jiti": "^2.7.0",
|
|
130
139
|
"magic-string": "^0.30.21",
|
|
@@ -141,6 +150,8 @@
|
|
|
141
150
|
"knip": "^6.29.0",
|
|
142
151
|
"publint": "^0.3.22",
|
|
143
152
|
"semantic-release": "^25.0.3",
|
|
153
|
+
"ts-json-schema-generator": "^2.9.0",
|
|
154
|
+
"tsx": "catalog:",
|
|
144
155
|
"typescript": "catalog:",
|
|
145
156
|
"vite": "catalog:",
|
|
146
157
|
"vite-plus": "catalog:",
|
package/schema.json
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"include": {
|
|
6
|
+
"type": "array",
|
|
7
|
+
"items": {
|
|
8
|
+
"type": "string"
|
|
9
|
+
},
|
|
10
|
+
"description": "Glob patterns to include (default: [\"**\\/*.ts\", \"**\\/*.tsx\"])"
|
|
11
|
+
},
|
|
12
|
+
"exclude": {
|
|
13
|
+
"type": "array",
|
|
14
|
+
"items": {
|
|
15
|
+
"type": "string"
|
|
16
|
+
},
|
|
17
|
+
"description": "Glob patterns to exclude (default: [\"node_modules/**\", \"**\\/*.d.ts\"])"
|
|
18
|
+
},
|
|
19
|
+
"schemas": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"enum": [
|
|
22
|
+
"explicit",
|
|
23
|
+
"auto"
|
|
24
|
+
],
|
|
25
|
+
"description": "How schemas are found.\n\n- `\"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).\n- `\"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.\n\n**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.",
|
|
26
|
+
"default": "auto"
|
|
27
|
+
},
|
|
28
|
+
"hoist": {
|
|
29
|
+
"anyOf": [
|
|
30
|
+
{
|
|
31
|
+
"type": "boolean"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"type": "object",
|
|
35
|
+
"properties": {
|
|
36
|
+
"schemaNamePattern": {
|
|
37
|
+
"anyOf": [
|
|
38
|
+
{
|
|
39
|
+
"type": "string",
|
|
40
|
+
"format": "regex"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"type": "string"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"type": "null"
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"additionalProperties": false
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"description": "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:\n\n```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 } ```\n\nOnly 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.\n\nPass 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.",
|
|
55
|
+
"default": true
|
|
56
|
+
},
|
|
57
|
+
"$schema": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"description": "JSON Schema used by editors.",
|
|
60
|
+
"default": "./node_modules/zod-compiler/schema.json"
|
|
61
|
+
},
|
|
62
|
+
"eager": {
|
|
63
|
+
"type": "boolean",
|
|
64
|
+
"description": "Compile immediately instead of when a validation method is first read.",
|
|
65
|
+
"default": false
|
|
66
|
+
},
|
|
67
|
+
"output": {
|
|
68
|
+
"type": "string",
|
|
69
|
+
"enum": [
|
|
70
|
+
"schema",
|
|
71
|
+
"compact"
|
|
72
|
+
],
|
|
73
|
+
"description": "Preserve the complete compiled validator, or delegate cold error production to the retained Zod schema in compact mode.",
|
|
74
|
+
"default": "schema"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"additionalProperties": false,
|
|
78
|
+
"description": "Configuration loaded by `zod-compiler/register` from the current working directory.",
|
|
79
|
+
"definitions": {}
|
|
80
|
+
}
|