1. 3層パッケージトポロジ (v4/core, classic, mini)
Zod v4.6.7 は packages/zod/src/ 配下の全モジュールを、3つの主要アーキテクチャ層(v4/core、v4/classic、v4/mini)と64個の多言語ロケールファイル(v4/locales)、およびサブパスエントリポイントから成る厳密な 有向非巡回グラフ(DAG) として構成しています。すべての依存矢印は上位から下位への一方向のみに向いており、v4/core は v4/classic や v4/mini を一切インポートせず、また v4/classic と v4/mini の間にも相互依存は存在しません。
Source Code
from drawlib.canvas import save, setup
from drawlib.icons import phosphor
from drawlib.lines import line
from drawlib.smartarts import CardList, Panel
from drawlib.styles import Styles
from drawlib.text import text
setup(width=156, height=110)
text(
(78, 105.5),
"Zod v4.6.7 モジュール依存関係 DAG (packages/zod/src/)",
style=Styles.DarkBold.patch(text_size=13.5),
)
text(
(78, 101.5),
"サブパスエントリポイント -> Classic / Mini ファサード -> Core & Locales への厳密な下位方向インポート",
style=Styles.Muted.patch(text_size=10.0),
)
# Tier 0: Top-level Entrypoints
p_top = Panel(
"サブパスエントリポイント (packages/zod/src/*.ts)",
style=Styles.Neutral,
header_style=Styles.Secondary,
text_style=Styles.WhiteBold.patch(text_size=10.5),
header_height=5.8,
)
p_top.draw(xy=(6, 80), width=144, height=17.5)
top_cards = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="horizontal",
spacing=2.0,
)
top_cards.add("src/index.ts", description="export { z, z as default }", style=Styles.PrimaryNeutral)
top_cards.add("src/locales/index.ts", description="49+ 言語バレル")
top_cards.add("src/v4/core/index.ts", description="core バレルエクスポート")
top_cards.add("src/compile.ts", description="AOT postProcessor")
top_cards.add("src/mini/index.ts", description="v4/mini 再エクスポート", style=Styles.PrimaryNeutral)
top_cards.draw(xy=(8.5, 81.5), width=139, height=8.5)
# Tier 1 Left: v4/classic (12 modules)
p_classic = Panel(
"v4/classic/ (12モジュール)",
style=Styles.PrimaryNeutral,
header_style=Styles.Primary,
text_style=Styles.WhiteBold.patch(text_size=10.5),
icon=phosphor.stack,
icon_style=Styles.White,
header_height=5.8,
)
p_classic.draw(xy=(6, 45), width=52, height=28)
classic_list = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="vertical",
spacing=1.5,
)
classic_list.add("external.ts & schemas.ts", description="ZodType + 35+ チェーンメソッド")
classic_list.add("from-json-schema.ts", description="JSON Schema 逆変換 + compat.ts")
classic_list.draw(xy=(8.5, 47.0), width=47, height=18.0)
# Tier 1 Center: v4/locales (64 files) — adjacent to v4/classic
p_loc = Panel(
"v4/locales/ (64種)",
style=Styles.Neutral,
header_style=Styles.Secondary,
text_style=Styles.WhiteBold.patch(text_size=10.5),
icon=phosphor.globe,
icon_style=Styles.White,
header_height=5.8,
)
p_loc.draw(xy=(66, 45), width=34, height=28)
loc_list = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="vertical",
spacing=1.5,
)
loc_list.add("en.ts (既定ロケール)", description="classic が自動登録")
loc_list.add("ja.ts, de.ts, fr.ts...", description="z.config() で選択導入")
loc_list.draw(xy=(68.0, 47.0), width=30, height=18.0)
# Tier 1 Right: v4/mini (9 modules)
p_mini = Panel(
"v4/mini/ (9モジュール)",
style=Styles.PrimaryNeutral,
header_style=Styles.Primary,
text_style=Styles.WhiteBold.patch(text_size=10.5),
icon=phosphor.feather,
icon_style=Styles.White,
header_height=5.8,
)
p_mini.draw(xy=(104, 45), width=46, height=28)
mini_list = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="vertical",
spacing=1.5,
)
mini_list.add("external.ts & schemas.ts", description="ZodMiniType (チェーンなし)")
mini_list.add("checks.ts, parse.ts, iso.ts", description="Tree-Shakable 検証関数群")
mini_list.draw(xy=(106.5, 47.0), width=41, height=18.0)
# Tier 2 Bottom: v4/core (21 modules)
p_core = Panel(
"v4/core/ — 共通ランタイム・トレイト・JITコンパイラ基盤 (21モジュール — 上位依存ゼロ)",
style=Styles.Neutral,
header_style=Styles.PrimaryFlat,
text_style=Styles.WhiteBold.patch(text_size=10.5),
icon=phosphor.cpu,
icon_style=Styles.White,
header_height=5.8,
)
p_core.draw(xy=(6, 5), width=144, height=33)
core_r1 = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="horizontal",
spacing=2.5,
)
core_r1.add(
"core.ts & util.ts",
description="$constructor, globalConfig, clone()",
style=Styles.PrimaryFlat,
text_style=Styles.WhiteBold.patch(text_size=9.5),
description_style=Styles.White.patch(text_size=9.5),
)
core_r1.add(
"schemas.ts & api.ts",
description="$ZodType & 共通ファクトリ (_string)",
style=Styles.PrimaryNeutral,
)
core_r1.add(
"checks.ts, regexes.ts & parse.ts",
description="$ZodCheck, 正規表現, _parse, _encode",
)
core_r1.draw(xy=(8.5, 20.5), width=139, height=9.5)
core_r2 = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="horizontal",
spacing=2.5,
)
core_r2.add(
"doc.ts & compile.ts",
description="Doc クロージャ生成 & JIT コンパイラ",
style=Styles.AccentFlat,
text_style=Styles.WhiteBold.patch(text_size=9.5),
description_style=Styles.White.patch(text_size=9.5),
)
core_r2.add(
"registries.ts & memoizer.ts",
description="WeakMap レジストリ & 循環参照保護",
)
core_r2.add(
"to-json-schema.ts & standard-schema",
description="JSON Schema 変換 & ~standard v1",
)
core_r2.draw(xy=(8.5, 8.0), width=139, height=9.5)
# Downward arrows (gap = 7.0 units)
line((32, 80), (32, 73), arrow_head="->", style=Styles.PrimaryBold)
line((83, 80), (83, 73), arrow_head="->", style=Styles.SecondaryBold)
line((127, 80), (127, 73), arrow_head="->", style=Styles.PrimaryBold)
# Classic imports en.ts from adjacent locales (gap = 8.0 units)
line((58, 59), (66, 59), arrow_head="->", style=Styles.SecondaryBold)
text((62, 62.0), "en()", style=Styles.Secondary.patch(text_size=9.5))
# Classic, Locales, Mini -> Core (gap = 7.0 units)
line((32, 45), (32, 38), arrow_head="->", style=Styles.PrimaryBold)
text((44, 41.5), "import * as core", style=Styles.Primary.patch(text_size=9.5))
line((83, 45), (83, 38), arrow_head="->", style=Styles.SecondaryBold)
line((127, 45), (127, 38), arrow_head="->", style=Styles.PrimaryBold)
text((139, 41.5), "import * as core", style=Styles.Primary.patch(text_size=9.5))
save()
1. 全4ディレクトリのモジュール完全インベントリ
1.1 packages/zod/src/v4/core/(全21モジュール — コアエンジン層)
v4/core には、すべてのランタイム検証アルゴリズム、型レベル推論プリミティブ、検証チェック実装、JITコンパイラパス、およびJSON Schemaプロセッサが集約されています。この21モジュールのいずれも、スキーマのプロトタイプ上にラッパーメソッド(.optional() や .min() など)を直接付与しません。
モジュール (v4/core/) |
規模 | 主要な責務とエクスポートシンボル |
|---|---|---|
core.ts |
約9.7 KB | $constructor()、NEVER、newError()、$brand、input<T>、output<T>、infer<T>、および globalConfig(globalThis.__zod_globalConfig)を定義。 |
util.ts |
約48.1 KB | ランタイム・型ユーティリティ群:Cached / cached()、defineLazy()、defineLazyInternal()、installLazyProp()、members()、derived()、clone()、mergeDefs()、aborted()、finalizeIssue()、toZod<T>()、allowsEval。 |
schemas.ts |
約177.1 KB (5,227行) | $ZodType および45以上のコアスキーマコンストラクタ($ZodString、$ZodNumber、$ZodObject、$ZodObjectJIT、$ZodUnion、$ZodDiscriminatedUnion、$ZodPipe、$ZodCodec 等)、ParsePayload、ParseContextInternal を定義。 |
api.ts |
約58.2 KB | 具象クラス Class: util.SchemaClass<...> を第1引数に取る共通ファクトリ関数群(_string、_number、_email、_uuid、_isoDateTime 等)。v4/classic と v4/mini で引数正規化ロジックを100%共有。 |
checks.ts |
約39.0 KB | $ZodCheck 基底トレイトおよび25以上の検証・変換チェックトレイト($ZodCheckLessThan、$ZodCheckGreaterThan、$ZodCheckMinLength、$ZodCheckRegex、$ZodCheckStringFormat、$ZodCheckOverwrite 等)を定義。 |
parse.ts |
約12.6 KB | 高階実行関数群:_parse、_parseAsync、_safeParse、_safeParseAsync、validate、validateAsync、_encode、_decode、_safeEncode、_safeDecode。 |
errors.ts |
約18.9 KB | 判別可能なエラーユニオン($ZodIssue、$ZodRawIssue)、メッセージ遅延生成対応の $ZodError / $ZodRealError、および整形関数(treeifyError、prettifyError、formatError、flattenError)。 |
regexes.ts |
約12.7 KB | 事前コンパイル済み正規表現群(email、uuid、ipv4、ipv6、base64、cuid2、ulid、datetime、duration 等)と動的生成ヘルパー。 |
doc.ts |
約1.5 KB | インデント付きJavaScriptソースコード行を組み立て、new Function(...Object.keys(closed), body) でクロージャ関数にコンパイルする Doc ビルダ。 |
compile.ts |
約97.9 KB | スキーマグラフ全体から同期バリデータと高速パーサを生成する JIT/AOT コンパイラ(compile()、withParser())。 |
registries.ts |
約3.4 KB | $ZodRegistry(WeakMap メタデータ保持 + Map ID索引)、$output / $input プレースホルダシンボル、$replace<Meta, S>、および globalRegistry。 |
memoizer.ts |
約12.1 KB | 循環参照を含むオブジェクト・配列・Map・Setの入力グラフを無限再帰なしにパース可能にする $ZodMemoizer 実装(memoizer())。 |
to-json-schema.ts |
約35.0 KB | Draft-04、Draft-07、Draft-2020-12、OpenAPI 3.0 に対応する関数型 JSON Schema 変換パイプライン(initializeContext、processSchema、extractDefs、finalize)。 |
json-schema-processors.ts |
約38.4 KB | スキーマ型別の JSON Schema プロセッサ群(stringProcessor、numberProcessor、objectProcessor、aggregateChecks、toJSONSchema)。 |
json-schema-generator.ts |
約4.6 KB | to-json-schema.ts をラップする後方互換クラス JSONSchemaGenerator。 |
json-schema.ts |
約3.9 KB | JSON Schema Draft ペイロードの TypeScript 型定義(BaseSchema、ObjectSchema 等)。 |
standard-schema.ts |
約6.3 KB | Standard Schema v1(~standard)および Standard JSON Schema の型契約定義(StandardSchemaV1、StandardSchemaWithJSONProps)。 |
visit.ts & zsf.ts |
約13.6 KB | スキーマグラフ走査ユーティリティ(visit())および内部シリアライズ形式(zsf.ts)。 |
versions.ts & index.ts |
約0.8 KB | バージョン定数(4.6.7)および zod/v4/core のバレルエクスポート。 |
1.2 packages/zod/src/v4/classic/ と packages/zod/src/v4/mini/ の構造比較
v4/classic と v4/mini はどちらも $constructor のトレイト合成によって v4/core のコンストラクタをラップする薄いファサード層であり、インスタンス生成処理自体は v4/core/api.ts の core._string(Class, params) 等に委譲しています。
// packages/zod/src/v4/classic/schemas.ts:
export function string(params?: string | core.$ZodStringParams): ZodString {
return core._string(ZodString, params);
}
// packages/zod/src/v4/mini/schemas.ts:
export function string(params?: string | core.$ZodStringParams): ZodMiniString<string> {
return core._string(ZodMiniString, params) as any;
}
では、なぜ小規模なフロントエンドバンドルにおいて zod/mini は 約85%ものバンドルサイズ削減 を達成できるのでしょうか。ソースコード上には明確な3つの理由があります。
ZodMiniType.prototypeからラッパー生成メソッドを完全排除:v4/classic/schemas.ts(L184-357)のZodTypeは、プロトタイプ上に 35個以上のメソッド(.optional(),.nullable(),.nullish(),.array(),.or(),.and(),.transform(),.default(),.catch(),.pipe(),.readonly(),.describe(),.meta(),.toJSONSchema()等)をインストールします。これらのメソッド実装が内部でoptional()、nullable()、array()、union()、pipe()、createToJSONSchemaMethod()を直接参照しているため、Rollup・esbuild・Webpack はたとえアプリ側でz.string().parse("hi")しか呼んでいなくても、全ラッパースキーマと JSON Schema 変換器を Tree-Shaking で除去できません。 一方、ZodMiniType(v4/mini/schemas.ts:L55-103)がプロトタイプに持つのはparse、parseAsync、safeParse、safeParseAsync、check(with)、clone、brand、register、applyのみです。ZodMiniString.prototypeからフォーマット検証メソッドを排除:v4/classic/schemas.ts(L535-614)のZodStringは、互換性のために.email()、.url()、.jwt()、.uuid()、.cuid2()、.ulid()、.ipv4()、.cidrv6()、.e164()、.datetime()等をプロトタイプに保持しており、これらがZodEmailやZodUUIDおよびv4/core/regexes.tsの正規表現群を引き込みます。v4/mini/schemas.ts(L116-122)のZodMiniStringは プロトタイプメソッドを一切持たず、z.string().check(z.minLength(5), z.email())のように関数として渡されたチェックのみがバンドルに残ります。v4/miniにおけるデフォルト英語ロケール自動登録の省略:v4/classic/schemas.ts:L14-16には以下の初期化フックが存在します。function _ensureDefaultLocale(): void { if (!core.globalConfig.localeError) core.config(en()); }ZodTypeは最初のスキーマ構築時に_ensureDefaultLocale()を呼び出すため、v4/locales/en.ts(約5.1 KBの英語エラーメッセージ辞書)が自動的にバンドルへ含まれます。ZodMiniTypeは_ensureDefaultLocale()を呼ばないため、開発者が明示的にz.config(z.locales.ja())やz.config(z.locales.en())を呼ばない限りロケール辞書ゼロで動作し、エラー発生時はfinalizeIssue()(v4/core/util.ts:L978)の"Invalid input"フォールバックを使用します。
2. デュアル ESM/CJS ビルドパイプライン(zshy & @zod/source)
Zod v4 は zshy(packages/zod/package.json:L38-126)を用いて src/ 配下の TypeScript ソースから ESM(.js + .d.ts)と CommonJS(.cjs + .d.cts)のデュアル出力を生成しつつ、カスタム条件 @zod/source によってモノレポ内でのゼロビルド開発を実現しています。
Source Code
from drawlib.canvas import save, setup
from drawlib.lines import line
from drawlib.smartarts import CardList, ChevronProcess, Panel
from drawlib.styles import Styles
from drawlib.text import text
setup(width=156, height=90)
text(
(78, 85.5),
"パッケージエクスポート解決と zshy デュアル ESM/CJS ビルドパイプライン",
style=Styles.DarkBold.patch(text_size=13.5),
)
# Top pipeline chevrons
pipe = ChevronProcess(
style=Styles.PrimaryNeutral,
text_style=Styles.DarkBold.patch(text_size=10.0),
description_style=Styles.Dark.patch(text_size=9.5),
corner_angle=60.0,
spacing=1.5,
flat_left_end=True,
)
pipe.add(
"1. TypeScript ソース",
description="packages/zod/src/**/*.ts",
style=Styles.PrimaryFlat,
text_style=Styles.WhiteBold.patch(text_size=10.0),
description_style=Styles.White.patch(text_size=9.5),
)
pipe.add("2. zshy コンパイラ", description="tsconfig.build.json")
pipe.add("3. Postbuild & Stub生成", description="write-stub-package-jsons.ts")
pipe.add(
"4. 条件付きエクスポート",
description="ESM / CJS / @zod/source",
style=Styles.AccentFlat,
text_style=Styles.WhiteBold.patch(text_size=10.0),
description_style=Styles.White.patch(text_size=9.5),
)
pipe.draw(xy=(6, 67), width=144, height=13)
# Left Panel: 11 Subpath Exports in package.json
p_exp = Panel(
"package.json の11サブパス",
style=Styles.Neutral,
header_style=Styles.Primary,
text_style=Styles.WhiteBold.patch(text_size=10.5),
header_height=6.0,
)
p_exp.draw(xy=(6, 6), width=46, height=54)
exp_cards = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="vertical",
spacing=1.3,
)
exp_cards.add(". & ./v4", description="./src/index.ts (Classic API)", style=Styles.PrimaryNeutral)
exp_cards.add("./mini & ./v4/mini", description="./src/mini/index.ts (Mini API)", style=Styles.PrimaryNeutral)
exp_cards.add("./v4/core", description="./src/v4/core/index.ts (Engine)")
exp_cards.add("./compile", description="./src/compile.ts (AOT 副作用登録)")
exp_cards.add("./locales & ./v4/locales/*", description="./src/v4/locales/* (49+ 言語)")
exp_cards.draw(xy=(8.5, 8.0), width=41, height=44.0)
# Center Panel: 4 Export Conditions per Subpath
p_cond = Panel(
"サブパスごとの4段階解決条件",
style=Styles.Neutral,
header_style=Styles.Secondary,
text_style=Styles.WhiteBold.patch(text_size=10.5),
header_height=6.0,
)
p_cond.draw(xy=(55, 6), width=46, height=54)
cond_cards = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="vertical",
spacing=1.5,
)
cond_cards.add(
'"@zod/source": "./src/*.ts"',
description="Vitest / モノレポでのTS直接実行",
style=Styles.PrimaryNeutral,
)
cond_cards.add('"types": "./*.d.cts"', description="TypeScript 型定義解決")
cond_cards.add('"import": "./*.js"', description="標準 ES Module 出力 (.js + .d.ts)")
cond_cards.add('"require": "./*.cjs"', description="CommonJS 出力 (sealCjsExports)")
cond_cards.draw(xy=(57.5, 8.0), width=41, height=44.0)
# Right Panel: Bundler Tree-Shaking Optimizations
p_shake = Panel(
"Tree-Shaking と純粋性保証",
style=Styles.Neutral,
header_style=Styles.PrimaryFlat,
text_style=Styles.WhiteBold.patch(text_size=10.5),
header_height=6.0,
)
p_shake.draw(xy=(104, 6), width=46, height=54)
shake_cards = CardList(
style=Styles.White,
text_style=Styles.DarkBold.patch(text_size=9.5),
description_style=Styles.Muted.patch(text_size=9.5),
badge_shape="none",
direction="vertical",
spacing=1.5,
)
shake_cards.add(
'sideEffects: ["./compile.*"]',
description="他すべてのモジュールを副作用なしと宣言",
style=Styles.AccentFlat,
text_style=Styles.WhiteBold.patch(text_size=9.5),
description_style=Styles.White.patch(text_size=9.5),
)
shake_cards.add("/*@__PURE__*/ 純粋性注釈", description="ファクトリの @__NO_SIDE_EFFECTS__ 併用")
shake_cards.add("export { z, z as default }", description="Rollup/Webpack の default import 最適化")
shake_cards.add("IIFE NUMBER_FORMAT_RANGES", description="Number.* 参照によるデッドコード防止")
shake_cards.draw(xy=(106.5, 8.0), width=41, height=44.0)
line((29, 67), (29, 60), arrow_head="->", style=Styles.PrimaryBold)
line((78, 67), (78, 60), arrow_head="->", style=Styles.SecondaryBold)
line((127, 67), (127, 60), arrow_head="->", style=Styles.PrimaryBold)
save()
2.1 import * as core と export { z, z as default } が重要な理由
JavaScript バンドラ(Rollup、Webpack、esbuild)における典型的な落とし穴として、import * as z で束ねた名前空間オブジェクトを export default z で再エクスポートした際の Tree-Shaking 阻害があります。packages/zod/src/index.ts のコメントを見てみましょう。
// packages/zod/src/index.ts
import * as z from "./v4/classic/external.js";
export * from "./v4/classic/external.js";
// Aliasing the binding, instead of `export default z`, keeps the default export a
// re-export of `z` rather than a fresh namespace value. Rollup and Webpack can then
// shake `import z from "zod"` as they already shake `import { z } from "zod"` — without
// this they pull in every locale. See #6050.
export { z, z as default };
もし export default z と記述してしまうと、Rollup や Webpack はモジュール名前空間オブジェクト自体を実体化された値として扱うため、external.js が持つすべてのプロパティ(49言語以上の辞書を含む export * as locales from "../locales/index.js" まで)がバンドルに引き込まれてしまいます。export { z, z as default } とエイリアスバインディングで再エクスポートすることで、import z from "zod" と書いても import { z } from "zod" と同様に z.string() の静的メンバ解決と Tree-Shaking が機能します。
2.2 zod/compile のみを指定した sideEffects ホワイトリスト
packages/zod/package.json:L13-17 では、AOTコンパイルを有効化するエントリポイントのみが sideEffects に登録されています。
"sideEffects": [
"./compile.js",
"./compile.cjs",
"./src/compile.ts"
],
それ以外のすべてのモジュールはバンドラから副作用なし(純粋)として扱われます。
- すべての
$constructor(...)呼び出しには/*@__PURE__*/が付与され、$constructor関数自体にも/*@__NO_SIDE_EFFECTS__*/(packages/zod/src/v4/core/core.ts:L51)が付与されています。 Number.MIN_SAFE_INTEGERなどの組み込みグローバルのプロパティを参照するトップレベル定数(NUMBER_FORMAT_RANGES、packages/zod/src/v4/core/util.ts:L741)は、esbuild がNumber.*のプロパティアクセスを含むオブジェクトリテラルを副作用ありとみなして残してしまう挙動を回避するため、/*@__PURE__*/ (() => ({ ... }))()の即時実行関数(IIFE)で包まれています。- 反対に
import "zod/compile"(packages/zod/src/compile.ts)は、インポート時にcore.globalConfig.postProcessorを書き換えて以降に生成される全スキーマへ JIT/AOT コンパイルシムを注入するため、sideEffectsに明記することでバンドラによる削除を防いでいます。