zod-compiler 2.0.6 → 2.0.8
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 +59 -55
- package/dist/cli/emitter.d.ts.map +1 -1
- package/dist/cli/emitter.js +10 -4
- package/dist/cli/emitter.js.map +1 -1
- package/dist/core/codegen/build-path.d.ts.map +1 -1
- package/dist/core/codegen/build-path.js +15 -6
- package/dist/core/codegen/build-path.js.map +1 -1
- package/dist/core/codegen/context.d.ts +21 -3
- package/dist/core/codegen/context.d.ts.map +1 -1
- package/dist/core/codegen/context.js +17 -1
- package/dist/core/codegen/context.js.map +1 -1
- package/dist/core/codegen/index.d.ts +8 -0
- package/dist/core/codegen/index.d.ts.map +1 -1
- package/dist/core/codegen/index.js +13 -11
- package/dist/core/codegen/index.js.map +1 -1
- package/dist/core/codegen/issue-decls.d.ts +11 -1
- package/dist/core/codegen/issue-decls.d.ts.map +1 -1
- package/dist/core/codegen/issue-decls.js +12 -1
- package/dist/core/codegen/issue-decls.js.map +1 -1
- package/dist/core/codegen/schemas/string.d.ts +15 -2
- package/dist/core/codegen/schemas/string.d.ts.map +1 -1
- package/dist/core/codegen/schemas/string.js +97 -35
- package/dist/core/codegen/schemas/string.js.map +1 -1
- package/dist/core/compile.d.ts +4 -3
- package/dist/core/compile.d.ts.map +1 -1
- package/dist/core/compile.js +1 -0
- package/dist/core/compile.js.map +1 -1
- package/dist/core/extract/error-class.d.ts +32 -0
- package/dist/core/extract/error-class.d.ts.map +1 -0
- package/dist/core/extract/error-class.js +13 -0
- package/dist/core/extract/error-class.js.map +1 -0
- package/dist/core/iife.d.ts +52 -18
- package/dist/core/iife.d.ts.map +1 -1
- package/dist/core/iife.js +82 -20
- package/dist/core/iife.js.map +1 -1
- package/dist/core/pipeline.d.ts +1 -0
- package/dist/core/pipeline.d.ts.map +1 -1
- package/dist/core/pipeline.js +6 -1
- package/dist/core/pipeline.js.map +1 -1
- package/dist/core/types.d.ts +16 -5
- package/dist/core/types.d.ts.map +1 -1
- package/dist/is-zod-schema.d.ts +5 -3
- package/dist/is-zod-schema.d.ts.map +1 -1
- package/dist/is-zod-schema.js +3 -1
- package/dist/is-zod-schema.js.map +1 -1
- package/dist/jit.d.ts +5 -4
- package/dist/jit.d.ts.map +1 -1
- package/dist/jit.js +17 -3
- package/dist/jit.js.map +1 -1
- package/dist/register/index.js.map +1 -1
- package/dist/runtime.d.ts +3 -0
- package/dist/runtime.js +7 -2
- package/dist/static-filter.d.ts +19 -1
- package/dist/static-filter.d.ts.map +1 -1
- package/dist/static-filter.js +27 -1
- package/dist/static-filter.js.map +1 -1
- package/dist/unplugin/hoist-compile.d.ts.map +1 -1
- package/dist/unplugin/hoist-compile.js +5 -1
- package/dist/unplugin/hoist-compile.js.map +1 -1
- package/dist/unplugin/transform.d.ts.map +1 -1
- package/dist/unplugin/transform.js +10 -4
- package/dist/unplugin/transform.js.map +1 -1
- package/dist/unplugin/virtual.d.ts.map +1 -1
- package/dist/unplugin/virtual.js +6 -1
- package/dist/unplugin/virtual.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
**Compile Zod schemas into zero-overhead validation functions at build time.**
|
|
4
4
|
|
|
5
|
-
Keep your existing Zod schemas. Get **up to 43x faster** validation, and up to **
|
|
5
|
+
Keep your existing Zod schemas. Get **up to 43x faster** validation, and up to **140x** on rejected
|
|
6
6
|
input. No code changes required.
|
|
7
7
|
|
|
8
8
|
Requires **Zod ≥ 4.5**; compiled output reproduces 4.5's semantics exactly, so it does not match
|
|
9
9
|
earlier 4.x releases. Use zod-compiler 1.x for Zod 4.0–4.4. An older Zod is refused with an explicit
|
|
10
10
|
error; the build plugin and `jit()` warn once and leave schemas as plain Zod.
|
|
11
11
|
|
|
12
|
+
Classic `zod` and `zod/mini` schemas compile alike, and `compile()` and `jit()` accept both. A compiled
|
|
13
|
+
schema fails the way its flavor does: classic schemas with `ZodError`, Mini schemas with Zod core's
|
|
14
|
+
`$ZodError`.
|
|
15
|
+
|
|
12
16
|
- [What Gets Compiled](#what-gets-compiled)
|
|
13
17
|
- [Schema Hoisting](#schema-hoisting)
|
|
14
18
|
- [z.compile vs zod-compiler](#zcompile-vs-zod-compiler)
|
|
@@ -26,7 +30,7 @@ loads pre-generated validators.
|
|
|
26
30
|
| | zod-compiler (build plugins / CLI) | Zod `z.compile()` |
|
|
27
31
|
| -------------------------------------- | -------------------------------------------- | --------------------------------------------- |
|
|
28
32
|
| Compilation | Build time (true AOT) | Runtime (`z.compile()` or the first parse) |
|
|
29
|
-
| Reported validation speedup | Up to 43x; up to
|
|
33
|
+
| Reported validation speedup | Up to 43x; up to 140x on rejected input | ~9x in Zod's headline example |
|
|
30
34
|
| Uses `new Function()` at runtime | No | Yes |
|
|
31
35
|
| Cold start | Fast; the validator is already generated | Pays for code generation at startup/first use |
|
|
32
36
|
| Strict CSP without `'unsafe-eval'` | Supported | Compilation is unavailable |
|
|
@@ -548,7 +552,7 @@ A schema delegates to Zod when it reaches JavaScript the generated code cannot r
|
|
|
548
552
|
| ---------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
549
553
|
| `.check(fn)`, `superRefine` + later checks | The callback holds Zod's payload unmediated, or `fatal` aborts Zod's chain |
|
|
550
554
|
| `ctx`-taking or `async` callbacks | Needs Zod's parse context / the async pipeline |
|
|
551
|
-
| `z.
|
|
555
|
+
| `z.jwt()` | Algorithmic format (signature parsing) |
|
|
552
556
|
| Overlapping or policy-sensitive object intersections | Zod's independent parse-and-merge semantics cannot be safely collapsed |
|
|
553
557
|
| `.readonly()` over a pass-through container | Zod freezes the output it rebuilt; these are the caller's own input |
|
|
554
558
|
| Dynamic error maps, unresolvable `z.lazy()` | Not knowable at build time |
|
|
@@ -580,55 +584,55 @@ Schema-level `error` and `z.config()` maps are unaffected; for a per-call map us
|
|
|
580
584
|
|
|
581
585
|
| Scenario | Zod v3 | Zod v4 | **zod-compiler** | Typia | AJV | vs Zod v4 |
|
|
582
586
|
| ----------------------------------------------- | ------ | ------ | ---------------- | ----- | ----- | --------- |
|
|
583
|
-
| simple string |
|
|
584
|
-
| string (min/max) | 12.
|
|
585
|
-
| number (int+positive) | 12.
|
|
586
|
-
| enum | 11.6M | 15.
|
|
587
|
-
| bigint (min/max) | 11.
|
|
588
|
-
| tuple [string, int, bool] | 5.
|
|
589
|
-
| record\<string, number\> | 3.2M | 2.
|
|
590
|
-
| set\<string\> (5 items) | 3.6M | 2.3M | **15.
|
|
591
|
-
| set\<string\> (20 items) | 1.3M | 679K | **12.
|
|
592
|
-
| map\<string, number\> (5 entries) | 2.0M | 1.3M | **13.
|
|
593
|
-
| map\<string, number\> (20 entries) |
|
|
594
|
-
| pipe (non-transform) | 8.
|
|
595
|
-
| discriminatedUnion (3 variants) | 3.
|
|
596
|
-
| discriminatedUnion (8 variants, rotating) | 2.6M | 4.6M | **10.
|
|
597
|
-
| plain union of 8 tagged objects (auto-discrim.) |
|
|
598
|
-
| strict object (DB row) | 1.8M |
|
|
599
|
-
| medium object (valid) |
|
|
600
|
-
| medium object (extra keys stripped) | 1.8M | 2.1M | **9.
|
|
601
|
-
| medium object (invalid) |
|
|
602
|
-
| large object (10 items) |
|
|
603
|
-
| large object (100 items) | 13K | 18K | **
|
|
604
|
-
| readonly field (wrapper compiles away) | 3.1M | 6.6M | **17.
|
|
605
|
-
| readonly root object (rebuild + freeze) | 2.
|
|
606
|
-
| readonly array (delegates to Zod) |
|
|
607
|
-
| recursive tree (7 nodes) |
|
|
608
|
-
| recursive tree (121 nodes) | 32K |
|
|
609
|
-
| nested recursion (7 nodes) |
|
|
610
|
-
| nested recursion (121 nodes) | 24K |
|
|
611
|
-
| deeply nested object (243 leaves) | 11K |
|
|
612
|
-
| event log (combined) |
|
|
613
|
-
| catalog product with image URLs (valid) |
|
|
614
|
-
| catalog product with image URLs (invalid) |
|
|
615
|
-
| catalog page (20 products) | 21K | 23K | **
|
|
616
|
-
| object with transform (zero-capture) | 1.1M | 1.9M | **7.
|
|
617
|
-
| array 10 × transform (zero-capture) |
|
|
618
|
-
| array 50 × transform (zero-capture) |
|
|
619
|
-
| object with captured transform | 1.2M |
|
|
620
|
-
| object with captured refine (cross-field) | 1.5M | 2.2M | **11.
|
|
621
|
-
| object with superRefine (cross-field) | 1.
|
|
622
|
-
| coerced query object (valid) | 1.8M |
|
|
623
|
-
| coerced query object (invalid) | 1.0M |
|
|
624
|
-
| preprocessed query object (valid) |
|
|
625
|
-
| preprocessed query object (invalid) |
|
|
626
|
-
| stringbool config object (valid) | — | 2.8M | **6.
|
|
627
|
-
| stringbool config object (invalid) | — |
|
|
628
|
-
| custom/instanceof request (valid) |
|
|
629
|
-
| custom/instanceof request (invalid) |
|
|
630
|
-
| disjoint object intersection (valid) | 1.4M | 1.6M | **
|
|
631
|
-
| disjoint object intersection (invalid) |
|
|
587
|
+
| simple string | 13.1M | 15.9M | **17.3M** | 18.0M | 17.6M | 1.1x |
|
|
588
|
+
| string (min/max) | 12.3M | 7.3M | **17.3M** | 17.2M | 15.6M | 2.4x |
|
|
589
|
+
| number (int+positive) | 12.4M | 9.7M | **17.5M** | 17.7M | 17.0M | 1.8x |
|
|
590
|
+
| enum | 11.6M | 15.5M | **17.8M** | 18.1M | 17.5M | 1.1x |
|
|
591
|
+
| bigint (min/max) | 11.9M | 8.6M | **17.3M** | — | — | 2.0x |
|
|
592
|
+
| tuple [string, int, bool] | 5.5M | 7.4M | **16.7M** | 16.0M | 15.8M | 2.3x |
|
|
593
|
+
| record\<string, number\> | 3.2M | 2.5M | **12.9M** | 12.0M | 15.2M | 5.1x |
|
|
594
|
+
| set\<string\> (5 items) | 3.6M | 2.3M | **15.3M** | — | — | 6.8x |
|
|
595
|
+
| set\<string\> (20 items) | 1.3M | 679K | **12.2M** | — | — | **18x** |
|
|
596
|
+
| map\<string, number\> (5 entries) | 2.0M | 1.3M | **13.5M** | — | — | **10x** |
|
|
597
|
+
| map\<string, number\> (20 entries) | 628K | 346K | **8.5M** | — | — | **25x** |
|
|
598
|
+
| pipe (non-transform) | 8.8M | 4.7M | **16.9M** | — | — | 3.6x |
|
|
599
|
+
| discriminatedUnion (3 variants) | 3.4M | 5.2M | **17.1M** | 16.5M | 7.9M | 3.3x |
|
|
600
|
+
| discriminatedUnion (8 variants, rotating) | 2.6M | 4.6M | **10.5M** | — | — | 2.3x |
|
|
601
|
+
| plain union of 8 tagged objects (auto-discrim.) | 357K | 1.3M | **10.5M** | — | — | 7.9x |
|
|
602
|
+
| strict object (DB row) | 1.8M | 2.9M | **11.4M** | — | — | 3.8x |
|
|
603
|
+
| medium object (valid) | 1.9M | 2.3M | **9.8M** | 11.1M | 7.6M | 4.2x |
|
|
604
|
+
| medium object (extra keys stripped) | 1.8M | 2.1M | **9.7M** | — | — | 4.6x |
|
|
605
|
+
| medium object (invalid) | 517K | 364K | **15.9M** | 2.8M | 7.5M | **44x** |
|
|
606
|
+
| large object (10 items) | 120K | 175K | **5.3M** | 6.0M | 1.2M | **30x** |
|
|
607
|
+
| large object (100 items) | 13K | 18K | **764K** | 1.3M | 124K | **43x** |
|
|
608
|
+
| readonly field (wrapper compiles away) | 3.1M | 6.6M | **17.8M** | — | — | 2.7x |
|
|
609
|
+
| readonly root object (rebuild + freeze) | 2.9M | 5.6M | **13.5M** | — | — | 2.4x |
|
|
610
|
+
| readonly array (delegates to Zod) | 4.0M | 4.4M | **4.4M** | — | — | 1.0x |
|
|
611
|
+
| recursive tree (7 nodes) | 574K | 997K | **9.0M** | 11.9M | 4.8M | 9.0x |
|
|
612
|
+
| recursive tree (121 nodes) | 32K | 58K | **986K** | 1.9M | 375K | **17x** |
|
|
613
|
+
| nested recursion (7 nodes) | 390K | 696K | **7.8M** | 10.5M | 2.9M | **11x** |
|
|
614
|
+
| nested recursion (121 nodes) | 24K | 42K | **816K** | 1.6M | 205K | **20x** |
|
|
615
|
+
| deeply nested object (243 leaves) | 11K | 28K | **845K** | 1.0M | 125K | **30x** |
|
|
616
|
+
| event log (combined) | 375K | 808K | **7.6M** | — | — | 9.4x |
|
|
617
|
+
| catalog product with image URLs (valid) | 432K | 467K | **2.1M** | — | — | 4.6x |
|
|
618
|
+
| catalog product with image URLs (invalid) | 114K | 106K | **14.8M** | — | — | **140x** |
|
|
619
|
+
| catalog page (20 products) | 21K | 23K | **122K** | — | — | 5.2x |
|
|
620
|
+
| object with transform (zero-capture) | 1.1M | 1.9M | **7.6M** | — | — | 4.0x |
|
|
621
|
+
| array 10 × transform (zero-capture) | 123K | 209K | **4.3M** | — | — | **21x** |
|
|
622
|
+
| array 50 × transform (zero-capture) | 26K | 41K | **1.0M** | — | — | **25x** |
|
|
623
|
+
| object with captured transform | 1.2M | 8.3M | **16.2M** | — | — | 2.0x |
|
|
624
|
+
| object with captured refine (cross-field) | 1.5M | 2.2M | **11.7M** | — | — | 5.3x |
|
|
625
|
+
| object with superRefine (cross-field) | 1.5M | 2.2M | **9.6M** | — | — | 4.3x |
|
|
626
|
+
| coerced query object (valid) | 1.8M | 3.0M | **5.4M** | — | — | 1.8x |
|
|
627
|
+
| coerced query object (invalid) | 1.0M | 837K | **10.6M** | — | — | **13x** |
|
|
628
|
+
| preprocessed query object (valid) | 432K | 1.8M | **5.4M** | — | — | 3.0x |
|
|
629
|
+
| preprocessed query object (invalid) | 393K | 796K | **12.7M** | — | — | **16x** |
|
|
630
|
+
| stringbool config object (valid) | — | 2.8M | **6.3M** | — | — | 2.2x |
|
|
631
|
+
| stringbool config object (invalid) | — | 698K | **13.5M** | — | — | **19x** |
|
|
632
|
+
| custom/instanceof request (valid) | 979K | 3.2M | **10.4M** | — | — | 3.3x |
|
|
633
|
+
| custom/instanceof request (invalid) | 795K | 955K | **13.6M** | — | — | **14x** |
|
|
634
|
+
| disjoint object intersection (valid) | 1.4M | 1.6M | **9.7M** | — | — | 6.0x |
|
|
635
|
+
| disjoint object intersection (invalid) | 494K | 335K | **15.7M** | — | — | **47x** |
|
|
632
636
|
|
|
633
637
|
_ops/s, higher is better. `vp test bench` on an Apple M4 Max (zod 4.5.2, zod v3 3.23.8, typia 12, ajv 8),
|
|
634
638
|
best of three runs. The harness costs ~60 ns per iteration, so the fastest rows sit at that floor and gaps
|
|
@@ -646,9 +650,9 @@ vp run benchmark # run locally
|
|
|
646
650
|
|
|
647
651
|
A schema compiles to a **fast path** — one zero-allocation `&&` chain, shared by `.is()` and
|
|
648
652
|
`parse()` — plus a **slow path** that collects errors only on failure, deferred until `.error` is read.
|
|
649
|
-
Schemas that reshape their input (stripping objects, coercions, `stringbool`, defaults,
|
|
650
|
-
context-free preprocessors, disjoint-key intersections) instead validate and rebuild in a
|
|
651
|
-
that bails on the first failure.
|
|
653
|
+
Schemas that reshape their input (stripping objects, coercions, `stringbool`, `z.url()`, defaults,
|
|
654
|
+
transforms, context-free preprocessors, disjoint-key intersections) instead validate and rebuild in a
|
|
655
|
+
single pass that bails on the first failure.
|
|
652
656
|
|
|
653
657
|
Other optimizations:
|
|
654
658
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emitter.d.ts","names":[],"sources":["../../src/cli/emitter.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"emitter.d.ts","names":[],"sources":["../../src/cli/emitter.ts"],"mappings":";;;;;UAsBiB;;;;;;EAMf;;;;;EAKA;;iBAGc,4BACd,SAAS,sBACT,uBACA,UAAU;;;;iBA+EI,kBAAkB,mBAAmB;;;;iBAmB/B,kBAAkB,oBAAoB,kBAAkB"}
|
package/dist/cli/emitter.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FAILZ_CLASS_DECL, FAIL_CLASS_DECL, FINZ_DECL, FIN_DECL, FIN_DEFERRED_DECL, MK_VALIDATOR_DECL, ZOD_CONFIG_IMPORT, ZOD_MSG_DECLARATION, generateIIFE } from "../core/iife.js";
|
|
1
|
+
import { FAILZ_CLASS_DECL, FAIL_CLASS_DECL, FAIL_CORE_CLASS_DECL, FINZ_DECL, FIN_CORE_DECL, FIN_DECL, FIN_DEFERRED_CORE_DECL, FIN_DEFERRED_DECL, MK_VALIDATOR_DECL, ZOD_CONFIG_IMPORT, ZOD_MSG_DECLARATION, generateIIFE, usesCoreErrors } from "../core/iife.js";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
//#region src/cli/emitter.ts
|
|
@@ -7,6 +7,8 @@ function generateCompiledFileContent(schemas, sourceRelPath, options) {
|
|
|
7
7
|
if (!importPath.startsWith(".")) importPath = `./${importPath}`;
|
|
8
8
|
const zodCompat = options?.zodCompat !== false;
|
|
9
9
|
const usesFinZ = schemas.some((s) => s.codegenResult.usedHelpers.has("__zcFinZ"));
|
|
10
|
+
const usesCore = schemas.some((s) => usesCoreErrors(s.codegenResult.usedHelpers));
|
|
11
|
+
const classicFinalizers = schemas.some((s) => s.codegenResult.usedHelpers.has("__zcFin") || s.codegenResult.usedHelpers.has("__zcFinD")) || !usesCore;
|
|
10
12
|
const needsSource = (s) => zodCompat || s.refEntries.length > 0 || s.codegenResult.usesRetainedSchema === true;
|
|
11
13
|
const schemasNeedingImport = schemas.filter(needsSource);
|
|
12
14
|
const exports = schemas.map((schema) => {
|
|
@@ -22,10 +24,14 @@ function generateCompiledFileContent(schemas, sourceRelPath, options) {
|
|
|
22
24
|
"",
|
|
23
25
|
ZOD_CONFIG_IMPORT,
|
|
24
26
|
ZOD_MSG_DECLARATION,
|
|
25
|
-
FAIL_CLASS_DECL,
|
|
27
|
+
...classicFinalizers ? [FAIL_CLASS_DECL] : [],
|
|
26
28
|
MK_VALIDATOR_DECL,
|
|
27
|
-
FIN_DECL,
|
|
28
|
-
|
|
29
|
+
...classicFinalizers ? [FIN_DECL, FIN_DEFERRED_DECL] : [],
|
|
30
|
+
...usesCore ? [
|
|
31
|
+
FAIL_CORE_CLASS_DECL,
|
|
32
|
+
FIN_CORE_DECL,
|
|
33
|
+
FIN_DEFERRED_CORE_DECL
|
|
34
|
+
] : [],
|
|
29
35
|
...usesFinZ ? [FAILZ_CLASS_DECL, FINZ_DECL] : [],
|
|
30
36
|
...options?.sharedCode ? ["", options.sharedCode] : [],
|
|
31
37
|
"",
|
package/dist/cli/emitter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emitter.js","names":[],"sources":["../../src/cli/emitter.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport {\n FAIL_CLASS_DECL,\n FAILZ_CLASS_DECL,\n FIN_DECL,\n FIN_DEFERRED_DECL,\n FINZ_DECL,\n generateIIFE,\n MK_VALIDATOR_DECL,\n ZOD_CONFIG_IMPORT,\n ZOD_MSG_DECLARATION,\n} from \"../core/iife.js\";\nimport type { CompiledSchemaInfo } from \"../core/pipeline.js\";\n\n/**\n * Generate the content of a .compiled.ts file from multiple schemas.\n */\nexport interface EmitterOptions {\n /**\n * When true (default), installs the compiled methods on the original schema\n * object for Zod-compatible output.\n * When false, produces a minimal plain object (smaller bundle).\n */\n zodCompat?: boolean | undefined;\n /**\n * File-level shared validator and constant declarations. Emitted once at\n * module scope so each compiled export can reference them.\n */\n sharedCode?: string | undefined;\n}\n\nexport function generateCompiledFileContent(\n schemas: CompiledSchemaInfo[],\n sourceRelPath: string,\n options?: EmitterOptions,\n): string {\n let importPath = sourceRelPath.replace(/\\.[cm]?[jt]sx?$/, \"\");\n if (!importPath.startsWith(\".\")) {\n importPath = `./${importPath}`;\n }\n\n const zodCompat = options?.zodCompat !== false;\n\n // Compact-mode schemas delegate cold errors to zod via __zcFinZ (its own\n // __ZcFailZ class), emitted only when some export uses it.\n const usesFinZ = schemas.some((s) => s.codegenResult.usedHelpers.has(\"__zcFinZ\"));\n\n // A schema needs the source import when:\n // - zodCompat: true (__zcMkv installs compiled methods on the schema)\n // - has refEntries (fallback schemas referenced via __rf[])\n // - delegates its cold path to the retained schema (reads the __zs binding)\n //\n // The last clause must stay in lockstep with generateIIFE's own test for\n // emitting `var __zs=<expr>;`: hand it an empty expression for a schema it\n // decides to bind and the file is a syntax error (`var __zs=;`).\n const needsSource = (s: CompiledSchemaInfo): boolean =>\n zodCompat || s.refEntries.length > 0 || s.codegenResult.usesRetainedSchema === true;\n const schemasNeedingImport = schemas.filter(needsSource);\n\n const exports = schemas.map((schema) => {\n const needsImport = needsSource(schema);\n // `compile()` returns the schema augmented with a non-enumerable `schema`\n // self-reference; auto-discovery (the default) instead finds plain exported\n // Zod schemas, which have no such property. Both shapes must resolve, so\n // fall through to the export itself — reading `.schema` alone yields\n // undefined there, which silently cost __zcMkv the real schema to augment\n // and hard-crashed every `__rf[]` base at module load.\n const src = `__src_${schema.exportName}`;\n const schemaExpr = needsImport ? `((${src} as any).schema ?? ${src})` : \"\";\n return `export const ${schema.exportName} = ${generateIIFE(schemaExpr, schema, { zodCompat })};`;\n });\n\n const importLine =\n schemasNeedingImport.length > 0\n ? [\n `import { ${schemasNeedingImport.map((s) => `${s.exportName} as __src_${s.exportName}`).join(\", \")} } from \"${importPath}\";`,\n \"\",\n ]\n : [];\n\n return [\n \"// AUTO-GENERATED by zod-compiler — DO NOT EDIT\",\n `// Source: ${sourceRelPath}`,\n \"\",\n ZOD_CONFIG_IMPORT,\n ZOD_MSG_DECLARATION,\n FAIL_CLASS_DECL,\n MK_VALIDATOR_DECL,\n FIN_DECL,\n
|
|
1
|
+
{"version":3,"file":"emitter.js","names":[],"sources":["../../src/cli/emitter.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport {\n FAIL_CLASS_DECL,\n FAIL_CORE_CLASS_DECL,\n FAILZ_CLASS_DECL,\n FIN_CORE_DECL,\n FIN_DECL,\n FIN_DEFERRED_CORE_DECL,\n FIN_DEFERRED_DECL,\n FINZ_DECL,\n generateIIFE,\n MK_VALIDATOR_DECL,\n usesCoreErrors,\n ZOD_CONFIG_IMPORT,\n ZOD_MSG_DECLARATION,\n} from \"../core/iife.js\";\nimport type { CompiledSchemaInfo } from \"../core/pipeline.js\";\n\n/**\n * Generate the content of a .compiled.ts file from multiple schemas.\n */\nexport interface EmitterOptions {\n /**\n * When true (default), installs the compiled methods on the original schema\n * object for Zod-compatible output.\n * When false, produces a minimal plain object (smaller bundle).\n */\n zodCompat?: boolean | undefined;\n /**\n * File-level shared validator and constant declarations. Emitted once at\n * module scope so each compiled export can reference them.\n */\n sharedCode?: string | undefined;\n}\n\nexport function generateCompiledFileContent(\n schemas: CompiledSchemaInfo[],\n sourceRelPath: string,\n options?: EmitterOptions,\n): string {\n let importPath = sourceRelPath.replace(/\\.[cm]?[jt]sx?$/, \"\");\n if (!importPath.startsWith(\".\")) {\n importPath = `./${importPath}`;\n }\n\n const zodCompat = options?.zodCompat !== false;\n\n // Compact-mode schemas delegate cold errors to zod via __zcFinZ (its own\n // __ZcFailZ class), emitted only when some export uses it.\n const usesFinZ = schemas.some((s) => s.codegenResult.usedHelpers.has(\"__zcFinZ\"));\n\n // Exports of zod/mini schemas fail with core's `$ZodError`, through their own\n // finalizer pair (see FAIL_CORE_CLASS_DECL). The classic pair is emitted as it\n // always has been, whether called or not, except in a file whose validators\n // all build core errors: there nothing calls it.\n const usesCore = schemas.some((s) => usesCoreErrors(s.codegenResult.usedHelpers));\n const usesClassic = schemas.some(\n (s) =>\n s.codegenResult.usedHelpers.has(\"__zcFin\") || s.codegenResult.usedHelpers.has(\"__zcFinD\"),\n );\n const classicFinalizers = usesClassic || !usesCore;\n\n // A schema needs the source import when:\n // - zodCompat: true (__zcMkv installs compiled methods on the schema)\n // - has refEntries (fallback schemas referenced via __rf[])\n // - delegates its cold path to the retained schema (reads the __zs binding)\n //\n // The last clause must stay in lockstep with generateIIFE's own test for\n // emitting `var __zs=<expr>;`: hand it an empty expression for a schema it\n // decides to bind and the file is a syntax error (`var __zs=;`).\n const needsSource = (s: CompiledSchemaInfo): boolean =>\n zodCompat || s.refEntries.length > 0 || s.codegenResult.usesRetainedSchema === true;\n const schemasNeedingImport = schemas.filter(needsSource);\n\n const exports = schemas.map((schema) => {\n const needsImport = needsSource(schema);\n // `compile()` returns the schema augmented with a non-enumerable `schema`\n // self-reference; auto-discovery (the default) instead finds plain exported\n // Zod schemas, which have no such property. Both shapes must resolve, so\n // fall through to the export itself — reading `.schema` alone yields\n // undefined there, which silently cost __zcMkv the real schema to augment\n // and hard-crashed every `__rf[]` base at module load.\n const src = `__src_${schema.exportName}`;\n const schemaExpr = needsImport ? `((${src} as any).schema ?? ${src})` : \"\";\n return `export const ${schema.exportName} = ${generateIIFE(schemaExpr, schema, { zodCompat })};`;\n });\n\n const importLine =\n schemasNeedingImport.length > 0\n ? [\n `import { ${schemasNeedingImport.map((s) => `${s.exportName} as __src_${s.exportName}`).join(\", \")} } from \"${importPath}\";`,\n \"\",\n ]\n : [];\n\n return [\n \"// AUTO-GENERATED by zod-compiler — DO NOT EDIT\",\n `// Source: ${sourceRelPath}`,\n \"\",\n ZOD_CONFIG_IMPORT,\n ZOD_MSG_DECLARATION,\n ...(classicFinalizers ? [FAIL_CLASS_DECL] : []),\n MK_VALIDATOR_DECL,\n ...(classicFinalizers ? [FIN_DECL, FIN_DEFERRED_DECL] : []),\n ...(usesCore ? [FAIL_CORE_CLASS_DECL, FIN_CORE_DECL, FIN_DEFERRED_CORE_DECL] : []),\n ...(usesFinZ ? [FAILZ_CLASS_DECL, FINZ_DECL] : []),\n ...(options?.sharedCode ? [\"\", options.sharedCode] : []),\n \"\",\n ...importLine,\n ...exports,\n \"\",\n ].join(\"\\n\");\n}\n\n/**\n * Resolve the output file path for a given input file.\n */\nexport function resolveOutputPath(inputPath: string, outputFlag: string | undefined): string {\n if (outputFlag) {\n // If output flag ends with / or is a directory, put the compiled file inside it\n if (outputFlag.endsWith(\"/\") || outputFlag.endsWith(path.sep)) {\n const baseName = path.basename(inputPath, path.extname(inputPath));\n return path.join(outputFlag, `${baseName}.compiled.ts`);\n }\n return outputFlag;\n }\n\n // Default: replace .ts with .compiled.ts\n const dir = path.dirname(inputPath);\n const baseName = path.basename(inputPath, path.extname(inputPath));\n return path.join(dir, `${baseName}.compiled.ts`);\n}\n\n/**\n * Write the compiled file content to disk.\n */\nexport async function writeCompiledFile(outputPath: string, content: string): Promise<void> {\n const dir = path.dirname(outputPath);\n await fs.promises.mkdir(dir, { recursive: true });\n await fs.promises.writeFile(outputPath, content, \"utf-8\");\n}\n"],"mappings":";;;;AAoCA,SAAgB,4BACd,SACA,eACA,SACQ;CACR,IAAI,aAAa,cAAc,QAAQ,mBAAmB,EAAE;CAC5D,IAAI,CAAC,WAAW,WAAW,GAAG,GAC5B,aAAa,KAAK;CAGpB,MAAM,YAAY,SAAS,cAAc;CAIzC,MAAM,WAAW,QAAQ,MAAM,MAAM,EAAE,cAAc,YAAY,IAAI,UAAU,CAAC;CAMhF,MAAM,WAAW,QAAQ,MAAM,MAAM,eAAe,EAAE,cAAc,WAAW,CAAC;CAKhF,MAAM,oBAJc,QAAQ,MACzB,MACC,EAAE,cAAc,YAAY,IAAI,SAAS,KAAK,EAAE,cAAc,YAAY,IAAI,UAAU,CAExD,KAAK,CAAC;CAU1C,MAAM,eAAe,MACnB,aAAa,EAAE,WAAW,SAAS,KAAK,EAAE,cAAc,uBAAuB;CACjF,MAAM,uBAAuB,QAAQ,OAAO,WAAW;CAEvD,MAAM,UAAU,QAAQ,KAAK,WAAW;EACtC,MAAM,cAAc,YAAY,MAAM;EAOtC,MAAM,MAAM,SAAS,OAAO;EAC5B,MAAM,aAAa,cAAc,KAAK,IAAI,qBAAqB,IAAI,KAAK;EACxE,OAAO,gBAAgB,OAAO,WAAW,KAAK,aAAa,YAAY,QAAQ,EAAE,UAAU,CAAC,EAAE;CAChG,CAAC;CAED,MAAM,aACJ,qBAAqB,SAAS,IAC1B,CACE,YAAY,qBAAqB,KAAK,MAAM,GAAG,EAAE,WAAW,YAAY,EAAE,YAAY,CAAC,CAAC,KAAK,IAAI,EAAE,WAAW,WAAW,KACzH,EACF,IACA,CAAC;CAEP,OAAO;EACL;EACA,cAAc;EACd;EACA;EACA;EACA,GAAI,oBAAoB,CAAC,eAAe,IAAI,CAAC;EAC7C;EACA,GAAI,oBAAoB,CAAC,UAAU,iBAAiB,IAAI,CAAC;EACzD,GAAI,WAAW;GAAC;GAAsB;GAAe;EAAsB,IAAI,CAAC;EAChF,GAAI,WAAW,CAAC,kBAAkB,SAAS,IAAI,CAAC;EAChD,GAAI,SAAS,aAAa,CAAC,IAAI,QAAQ,UAAU,IAAI,CAAC;EACtD;EACA,GAAG;EACH,GAAG;EACH;CACF,CAAC,CAAC,KAAK,IAAI;AACb;;;;AAKA,SAAgB,kBAAkB,WAAmB,YAAwC;CAC3F,IAAI,YAAY;EAEd,IAAI,WAAW,SAAS,GAAG,KAAK,WAAW,SAAS,KAAK,GAAG,GAAG;GAC7D,MAAM,WAAW,KAAK,SAAS,WAAW,KAAK,QAAQ,SAAS,CAAC;GACjE,OAAO,KAAK,KAAK,YAAY,GAAG,SAAS,aAAa;EACxD;EACA,OAAO;CACT;CAGA,MAAM,MAAM,KAAK,QAAQ,SAAS;CAClC,MAAM,WAAW,KAAK,SAAS,WAAW,KAAK,QAAQ,SAAS,CAAC;CACjE,OAAO,KAAK,KAAK,KAAK,GAAG,SAAS,aAAa;AACjD;;;;AAKA,eAAsB,kBAAkB,YAAoB,SAAgC;CAC1F,MAAM,MAAM,KAAK,QAAQ,UAAU;CACnC,MAAM,GAAG,SAAS,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;CAChD,MAAM,GAAG,SAAS,UAAU,YAAY,SAAS,OAAO;AAC1D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-path.d.ts","names":[],"sources":["../../../src/core/codegen/build-path.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"build-path.d.ts","names":[],"sources":["../../../src/core/codegen/build-path.ts"],"mappings":";;;;iBAwMgB,eAAe,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiCnB,kBAAkB,IAAI;;;;;;;;;;;iBA+EtB,sBAAsB,IAAI;;;;;;iBAmD1B,cAAc,IAAI,UAAU,KAAK"}
|
|
@@ -5,7 +5,7 @@ import { detectUnionDiscriminator } from "./schemas/discriminated-union.js";
|
|
|
5
5
|
import { estimateFastCost, orderByRuntimeCost, predictedInlineSize } from "./fast-size.js";
|
|
6
6
|
import { parsedProperties } from "./schemas/object.js";
|
|
7
7
|
import { innerAppliesDefaultOnUndefined } from "./schemas/optional.js";
|
|
8
|
-
import { fastStringCheck } from "./schemas/string.js";
|
|
8
|
+
import { buildUrlCheck, fastStringCheck, isUrlRewrite } from "./schemas/string.js";
|
|
9
9
|
import { emitStringBoolMap, stringBoolInlineHit, stringBoolUsesInline } from "./schemas/string-bool.js";
|
|
10
10
|
import { createFastGen, generateFast } from "./fast-path.js";
|
|
11
11
|
//#region src/core/codegen/build-path.ts
|
|
@@ -40,7 +40,7 @@ function rebuildSet(root, includeProtoScrub = true) {
|
|
|
40
40
|
for (const node of nodes) {
|
|
41
41
|
if (rebuilds.has(node)) continue;
|
|
42
42
|
const target = node.type === "recursiveRef" ? targets.get(node.refId ?? 0) : void 0;
|
|
43
|
-
if (node.type === "object" && node.stripUnknownKeys === true || node.type === "readonly" && node.freeze === true || node.type === "default" || node.type === "tuple" && tupleRewritesShortInput(node) || includeProtoScrub && needsProtoScrub(node) || node.type === "stringBool" || node.type === "string" && (node.coerce === true || node.checks.some((c) => c.kind === "overwrite_effect")) || (node.type === "number" || node.type === "boolean" || node.type === "bigint" || node.type === "date") && node.coerce === true || node.type === "effect" || target !== void 0 && rebuilds.has(target) || children(node).some((child) => rebuilds.has(child))) {
|
|
43
|
+
if (node.type === "object" && node.stripUnknownKeys === true || node.type === "readonly" && node.freeze === true || node.type === "default" || node.type === "tuple" && tupleRewritesShortInput(node) || includeProtoScrub && needsProtoScrub(node) || node.type === "stringBool" || node.type === "string" && (node.coerce === true || node.checks.some((c) => c.kind === "overwrite_effect" || isUrlRewrite(c))) || (node.type === "number" || node.type === "boolean" || node.type === "bigint" || node.type === "date") && node.coerce === true || node.type === "effect" || target !== void 0 && rebuilds.has(target) || children(node).some((child) => rebuilds.has(child))) {
|
|
44
44
|
rebuilds.add(node);
|
|
45
45
|
changed = true;
|
|
46
46
|
}
|
|
@@ -95,7 +95,7 @@ function fastResultIsInput(ir) {
|
|
|
95
95
|
}
|
|
96
96
|
/**
|
|
97
97
|
* True when the subtree mutates for any reason the build pass cannot reproduce —
|
|
98
|
-
* `.catch()`, `
|
|
98
|
+
* `.catch()`, `superRefine`. Those rewrite values in ways this pass
|
|
99
99
|
* (which validates, coerces, decodes string booleans, substitutes declared
|
|
100
100
|
* defaults, applies ordered string rewrites and copies) does not model, so the
|
|
101
101
|
* schema keeps the eager walk.
|
|
@@ -110,7 +110,7 @@ function mutatesBeyondStrip(ir) {
|
|
|
110
110
|
*/
|
|
111
111
|
function mutatesHere(ir) {
|
|
112
112
|
switch (ir.type) {
|
|
113
|
-
case "string": return superRefines(ir.checks)
|
|
113
|
+
case "string": return superRefines(ir.checks);
|
|
114
114
|
case "number": return superRefines(ir.checks);
|
|
115
115
|
case "boolean":
|
|
116
116
|
case "bigint":
|
|
@@ -181,6 +181,9 @@ function generateBuild(ir, ctx) {
|
|
|
181
181
|
temps: [],
|
|
182
182
|
used: 0
|
|
183
183
|
};
|
|
184
|
+
const name = emitTemp(ctx, "vb");
|
|
185
|
+
const recNames = ctx.buildRecNames ??= /* @__PURE__ */ new Map();
|
|
186
|
+
recNames.set(0, name);
|
|
184
187
|
const built = build(ir, "input", {
|
|
185
188
|
ctx,
|
|
186
189
|
extractable: false,
|
|
@@ -188,8 +191,10 @@ function generateBuild(ir, ctx) {
|
|
|
188
191
|
rebuilds,
|
|
189
192
|
scope
|
|
190
193
|
});
|
|
191
|
-
if (built === null)
|
|
192
|
-
|
|
194
|
+
if (built === null) {
|
|
195
|
+
recNames.delete(0);
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
193
198
|
ctx.preamble.push(`function ${name}(input){${declareFastTemps(scope)}${built.code}return ${built.value};}`);
|
|
194
199
|
return name;
|
|
195
200
|
}
|
|
@@ -731,6 +736,10 @@ function buildString(ir, input, g) {
|
|
|
731
736
|
break;
|
|
732
737
|
case "super_refine_effect": return null;
|
|
733
738
|
default: {
|
|
739
|
+
if (check.kind === "string_format" && isUrlRewrite(check)) {
|
|
740
|
+
code += buildUrlCheck(check, value, g.fail, (prefix) => local(g, prefix), g.ctx);
|
|
741
|
+
break;
|
|
742
|
+
}
|
|
734
743
|
const expr = fastStringCheck(check, value, g.ctx);
|
|
735
744
|
if (expr === null) return null;
|
|
736
745
|
code += `if(!(${expr}))return ${g.fail};`;
|