Skip to content

Commit 1e06af8

Browse files
authored
Implement z.codec (#5113)
* WIP * WIP * WIP * Fix pipe * Add mini codec tests * Fix test * WIP * Add codecs graphic * Add reverse tests for pipe * WIP * More fixes * Remove logs * WIP
1 parent b01b6f3 commit 1e06af8

33 files changed

+2141
-101
lines changed

.cursor/rules/zod-project-guide.mdc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ This is the Zod TypeScript-first schema validation library project. The project
1414
### Development
1515
- `pnpm dev` - Start development server with tsx
1616
- `pnpm dev:watch` - Start development server with watch mode
17-
- `pnpm dev:play` - Run play.ts file for testing
17+
- `pnpm dev:play` - Run play.ts file for quick experimentation.
1818

1919
### Testing
2020
- `pnpm test` - Run all tests with vitest

CLAUDE.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Development Commands
6+
7+
The project uses pnpm workspaces. Key commands:
8+
9+
- `pnpm build` - Build all packages (runs recursive build command)
10+
- `pnpm test` - Run all tests with Vitest
11+
- `pnpm test:watch` - Run tests in watch mode
12+
- `pnpm dev` - Execute code with tsx under source conditions
13+
- `pnpm dev:play` - Execute play.ts for experimentation
14+
- `pnpm lint` - Run biome linter with auto-fix
15+
- `pnpm format` - Format code with biome
16+
- `pnpm fix` - Run both format and lint
17+
18+
### Testing
19+
20+
- Tests use Vitest with workspace-based configuration
21+
- Test files are located in `src/*/tests/` directories
22+
- Run specific tests: `pnpm test <pattern>` or `pnpm test --filter <workspace> <pattern>`
23+
- Tests include type checking via `typecheck.enabled = true`
24+
25+
### Package-Specific Commands
26+
27+
In the `packages/zod/` workspace:
28+
- `pnpm build` - Uses zshy build tool with tsconfig.build.json
29+
- `pnpm clean` - Clean build artifacts (preserving node_modules)
30+
31+
## Architecture Overview
32+
33+
Zod is a TypeScript-first schema validation library organized as a monorepo with multiple versions and variants:
34+
35+
### Repository Structure
36+
37+
- **Root**: Monorepo configuration with pnpm workspaces
38+
- **packages/zod/**: Main Zod package with multiple version exports
39+
- **packages/docs/**: Documentation website (Next.js)
40+
- **packages/bench/**: Performance benchmarks
41+
- **packages/resolution/**: Module resolution testing
42+
- **packages/treeshake/**: Bundle size analysis
43+
- **packages/tsc/**: TypeScript compilation benchmarks
44+
45+
### Version Architecture
46+
47+
The main zod package exports multiple versions:
48+
49+
1. **v4 (default)**: Current version, exports from `v4/classic/external.js`
50+
2. **v4/core**: Core v4 implementation without legacy compatibility
51+
3. **v4/mini**: Lightweight v4 variant
52+
4. **v3**: Legacy version for backward compatibility
53+
5. **mini**: General minimal build
54+
55+
### Key Implementation Files
56+
57+
- `src/v4/core/`: Core validation logic, schemas, parsing, and error handling
58+
- `src/v4/classic/`: v4 with legacy compatibility layer
59+
- `src/v4/mini/`: Minimal v4 implementation
60+
- `src/v3/`: Legacy v3 implementation
61+
- `src/locales/`: Internationalization support
62+
63+
### Export Strategy
64+
65+
The package uses conditional exports with:
66+
- `@zod/source`: Development condition pointing to TypeScript source
67+
- Standard ESM/CJS exports for distribution
68+
- Multiple entry points for different versions and variants
69+
70+
## Code Standards
71+
72+
### Linting and Formatting
73+
74+
- Uses Biome for both linting and formatting
75+
- Line width: 120 characters
76+
- Trailing commas: ES5 style for JavaScript, none for JSON
77+
- Notable lint rule relaxations:
78+
- `noExplicitAny: "off"` - `any` is allowed
79+
- `noParameterAssign: "off"` - Required for performance optimizations
80+
- `noNonNullAssertion: "off"` - Non-null assertions are allowed
81+
82+
### TypeScript Configuration
83+
84+
- Strict mode enabled with exact optional property types
85+
- Node.js module resolution (NodeNext)
86+
- Target: ES2020
87+
- Custom conditions support for `@zod/source`
88+
89+
## Development Workflow
90+
91+
1. Use `play.ts` for experimentation with `pnpm dev:play`
92+
2. Write tests in appropriate `tests/` directories
93+
3. Build with `pnpm build` before testing changes
94+
4. Run linting/formatting with `pnpm fix`
95+
5. All changes must pass tests and type checking
96+
97+
## Build System
98+
99+
- Uses `zshy` build tool for the main package
100+
- Generates both ES modules and CommonJS outputs
101+
- Supports source maps and declaration files
102+
- Post-build formatting with Biome
103+
104+
## Performance Considerations
105+
106+
- Performance is critical - parameter reassignment is allowed for optimization
107+
- Benchmarks available in `packages/bench/`
108+
- Bundle size monitoring in `packages/treeshake/`
109+
- TypeScript compilation performance tracked in `packages/tsc/`
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"use client";
2+
3+
import Image from "next/image";
4+
5+
interface ThemedImageProps {
6+
lightSrc: string;
7+
darkSrc: string;
8+
alt: string;
9+
className?: string;
10+
}
11+
12+
export function ThemedImage({ lightSrc, darkSrc, alt, className }: ThemedImageProps) {
13+
return (
14+
<div className={`relative ${className || ""}`}>
15+
{/* Light mode image */}
16+
<Image
17+
className="block dark:hidden"
18+
alt={alt}
19+
src={lightSrc}
20+
width={800}
21+
height={400}
22+
quality={100}
23+
style={{ height: "auto", width: "100%" }}
24+
/>
25+
26+
{/* Dark mode image */}
27+
<Image
28+
className="hidden dark:block"
29+
alt={alt}
30+
src={darkSrc}
31+
width={800}
32+
height={400}
33+
quality={100}
34+
style={{ height: "auto", width: "100%" }}
35+
/>
36+
</div>
37+
);
38+
}

packages/docs/components/sidebar-item.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const Tags: Record<string, string> = {
1111
"/packages/mini": "New",
1212
"/json-schema": "New",
1313
"/metadata": "New",
14+
"/codecs": "New",
1415
"/packages/v3": "Legacy",
1516
};
1617
export const SidebarItem = ({
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"use client";
2+
3+
import Image from "next/image";
4+
5+
interface ThemedImageProps {
6+
lightSrc: string;
7+
darkSrc: string;
8+
alt: string;
9+
className?: string;
10+
}
11+
12+
export function ThemedImage({ lightSrc, darkSrc, alt, className }: ThemedImageProps) {
13+
return (
14+
<div className={`relative ${className || ""}`}>
15+
{/* Light mode image */}
16+
<Image
17+
className="block dark:hidden"
18+
alt={alt}
19+
src={lightSrc}
20+
width={800}
21+
height={400}
22+
quality={100}
23+
style={{ height: "auto", width: "100%" }}
24+
/>
25+
26+
{/* Dark mode image */}
27+
<Image
28+
className="hidden dark:block"
29+
alt={alt}
30+
src={darkSrc}
31+
width={800}
32+
height={400}
33+
quality={100}
34+
style={{ height: "auto", width: "100%" }}
35+
/>
36+
</div>
37+
);
38+
}

packages/docs/content/api.mdx

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2375,6 +2375,43 @@ const UniqueStringArray = z.array(z.string()).check((ctx) => {
23752375
</Accordion>
23762376
</Accordions>
23772377
2378+
## Codecs
2379+
2380+
> **New in Zod 4.1** — Refer to the dedicated [Codecs](/codecs) page for more information.
2381+
2382+
Codecs are a special kind of schema that implement *bidirectional transformations* between two other schemas.
2383+
2384+
```ts
2385+
const stringToDate = z.codec(
2386+
z.iso.datetime(), // input schema: ISO date string
2387+
z.date(), // output schema: Date object
2388+
{
2389+
decode: (isoString) => new Date(isoString), // ISO string → Date
2390+
encode: (date) => date.toISOString(), // Date → ISO string
2391+
}
2392+
);
2393+
```
2394+
2395+
A regular `.parse()` operations performs the *forward transform*. It calls the codec's `decode` function.
2396+
2397+
```ts
2398+
stringToDate.parse("2024-01-15T10:30:00.000Z"); // => Date
2399+
```
2400+
2401+
You can alternatively use the top-level `z.decode()` function. Unlike `.parse()` (which accepts `unknown` input), `z.decode()` expects a strongly-typed input (`string` in this example).
2402+
2403+
```ts
2404+
z.decode(stringToDate, "2024-01-15T10:30:00.000Z"); // => Date
2405+
```
2406+
2407+
To perform the *reverse transform*, use the inverse: `z.encode()`.
2408+
2409+
```ts
2410+
z.encode(stringToDate, new Date("2024-01-15")); // => "2024-01-15T00:00:00.000Z"
2411+
```
2412+
2413+
Refer to the dedicated [Codecs](/codecs) page for more information.
2414+
23782415
## Pipes
23792416
23802417
Schemas can be chained together into "pipes". Pipes are primarily useful when used in conjunction with [Transforms](#transforms).
@@ -2399,7 +2436,9 @@ z.parse(stringToLength, "hello"); // => 5
23992436
24002437
## Transforms
24012438
2402-
Transforms are a special kind of schema. Instead of validating input, they accept anything and perform some transformation on the data. To define a transform:
2439+
> **Note** — For bi-directional transforms, use [codecs](/codecs).
2440+
2441+
Transforms are a special kind of schema that perform a unidirectional transformation. Instead of validating input, they accept anything and perform some transformation on the data. To define a transform:
24032442
24042443
<Tabs groupId="lib" items={["Zod", "Zod Mini"]}>
24052444
<Tab value="Zod">

0 commit comments

Comments
 (0)