ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Valibot 迁移指南:用 @valibot/zod-to-valibot Codemod 将 Zod 模式自动转换为 Valibot

Valibot 迁移指南:用 @valibot/zod-to-valibot Codemod 将 Zod 模式自动转换为 Valibot 后端前端【免费下载链接】valibotThe modular and type safe schema library for validating structural data 项目地址https://gitcode.com/gh_mirrors/va/valibot点击查看免费下载Valibot 官方提供的valibot/zod-to-valibot是一个基于 jscodeshift 的自动化迁移工具codemod它能够把项目中的 Zod 模式、校验规则、链式方法、类型推导与解析调用批量改写为等价的 Valibot 代码。本文以 codemod/zod-to-valibot/README.md 为主线结合仓库源码与测试夹具完整讲解命令行用法、转换覆盖范围、核心映射规则、底层实现原理与测试验证方式帮助你安全、高效地完成从 Zod 到 Valibot 的迁移。为什么需要这个 CodemodZod 与 Valibot 都是结构化数据的模式校验库但两者的 API 形态差异明显Zod 以方法链 校验函数为主Valibot 则把校验逻辑拆分为独立的 action并通过v.pipe()组合类型推导、解析结果的返回结构、Coerce 的写法也不尽相同。对于拥有大量 schema 定义的老项目手工迁移既繁琐又容易出错。valibot/zod-to-valibot正是为此设计的自动化方案。它利用 Facebook 的 jscodeshift 在 AST抽象语法树层面完成代码改写支持 TypeScript/JavaScript 与 JSX能够在修改代码的同时保留原有的注释、缩进和大部分函数逻辑只替换模式定义与调用方式。从 package.json 可以看到该工具以bin字段暴露zod-to-valibot命令其唯一运行时依赖是jscodeshift^17.3.0版本为 0.1.2许可证 MIT。快速上手一条命令完成迁移官方 README 给出的基本用法是npx valibot/zod-to-valibot src/**/*该命令会扫描src目录下所有匹配**/*的文件默认按扩展名过滤见下文对其中的 Zod schema 进行就地改写。底层实现位于 cli.mjs它解析出当前目录下的dist/index.mjs作为 transform 脚本并定位jscodeshift/bin/jscodeshift.js最终以jscodeshift -t transformPath files的形式执行。需要说明的是cli.mjs 在没有任何参数时会打印使用说明并退出exit code 0因此直接运行npx valibot/zod-to-valibot不会误改任何文件——必须先指定目标文件或目录通配符。命令行选项详解README 提供了四组常用选项它们最终都会透传给 jscodeshift 引擎# Dry run预览改动不写入文件 npx valibot/zod-to-valibot --dry src/**/* # 输出详细日志 npx valibot/zod-to-valibot --verbose2 src/**/* # 指定解析器默认为 --parserts npx valibot/zod-to-valibot --parserbabel src/**/* # 指定文件扩展名默认为 --extensionsts,tsx,js,jsx npx valibot/zod-to-valibot --extensionsts src/**/*--dry仅计算并打印改动结果不触碰源文件。建议在真正执行迁移前先跑一遍人工核对 diff 是否符合预期。--verbose2提升日志详细程度便于排查某个文件为什么没被转换。--parser选择 AST 解析器。cli.mjs 会检查参数中是否已包含--parser或--parser前缀若没有则自动在参数最前面补上--parserts所以默认即为ts解析器可覆盖.ts、.tsx、.js、.jsx四种扩展名手动传入--parserbabel则切换为 babel 解析器。--extensions默认值同样是 cli.mjs 注入的ts,tsx,js,jsx你可以按需收窄例如只处理.ts文件--extensionsts。由于工具本身只是 jscodeshift 的前端封装README 也注明其余可用选项与 jscodeshift 保持一致。在本仓库中cli.mjs 打印的帮助文本列举了--dry、--print打印输出、--verbose2、--parserts等常用项并提示可通过jscodeshift --help查看完整列表。转换覆盖范围到底能转换什么README 把转换能力归纳为四类仓库源码中 constants.ts 以常量数组的形式给出了精确清单1. 基础 schemaany、array、bigint、boolean、custom、date、discriminatedUnion、enum、instanceof、intersection、literal、nan、nativeEnum、null、nullable、record、map、never、number、object、optional、set、string、symbol、tuple、undefined、union、unknown、void共 29 种。2. 校验规则validatorsbase64、cuid2、date、datetime、email、emoji、endsWith、finite、includes、int、ip、length、max、min、multipleOf同时兼容 Zod 的step、nanoid、negative、nonempty、nonnegative、nonpositive、positive、readonly、regex、safe、size、startsWith、toLowerCase、toUpperCase、trim、url、gt、gte、lt、lte、time、ulid、uuid等。其中base64url、cidr、cuid、duration、jwt由于 Valibot 暂时没有对应 action会被标记为未实现见下文限制与注意事项。3. 链式方法与属性方法ZOD_METHODSarray、catchall、default、deepPartial、exclude、extend、extract、keyof、omit、optional、or、merge、nullable、nullish、parse、parseAsync、partial、passthrough、pick、refine、required、rest、safeParse、safeParseAsync、strict、strip、spa、transform、unwrap。属性ZOD_PROPERTIESelement、description、shape以及解析结果的data、error。4. 类型推导infer、input、output三类类型引用会被改写为 Valibot 的类型工具详见下文类型推导的改写。核心映射规则从测试夹具看实际转换仓库在 codemod/zod-to-valibot/testfixtures下为每种转换场景准备了input.ts/output.ts成对夹具下面的映射均以这些真实夹具为准。基础 schema 与校验规则array-schema夹具展示了数组与内联校验的转换// 转换前input.ts const Schema2 z.array(z.string().email()); const Schema5 z.string().array(); const Schema7 Schema6.array(); // 转换后output.ts const Schema2 v.array(v.pipe(v.string(), v.email())); const Schema5 v.array(v.string()); const Schema7 v.array(Schema6);可以看到三个关键行为同名基础 schema 直接替换z.string()→v.string()z.array(...)→v.array(...)前缀z统一替换为v链式校验折叠进v.pipe()z.string().email()变成v.pipe(v.string(), v.email())反向链写法归一化z.string().array()与独立变量引用Schema6.array()都被改写为v.array(Schema6)。对数值与字符串的校验器转换遵循按目标类型选择 action的规则z.number().min(0)会变成v.minValue(0)z.number().int()、z.string().min(5)等则分别映射到对应的 Valibot action可对照 validators 目录 中min、max、int、regex、email、url等实现。transform夹具中z.number().min(0).transform(...)的输出为v.pipe(v.number(), v.minValue(0), v.transform(...))验证了min在数值上下文中落地为minValue。Coercionz.coerce 的特殊映射README 特别指出z.coerce.*会转换为v.pipe(v.unknown(), v.toX())。coerce-string-schema夹具证实了这一映射并且同时支持两种等价写法// 转换前 const Schema1 z.coerce.string(); const Schema2 z.string({ coerce: true }); // 转换后 const Schema1 v.pipe(v.unknown(), v.toString()); const Schema2 v.pipe(v.unknown(), v.toString());在 string/string.ts 的源码实现中getSchemaComps会同时接收前缀标记的 coercez.coerce.string()与选项对象的 coercez.string({ coerce: true })两种信号一旦判定为 coercion就构建v.pipe(v.unknown(), v.toString())boolean/number/bigint/date 同理对应v.toBoolean()、v.toNumber()、v.toBigint()、v.toDate()可通过 schemas 目录 下各 schema 实现确认。coerce 链上的后续校验同样保留z.coerce.string().email()输出v.pipe(v.unknown(), v.toString(), v.email())。对象、联合、可选与更多组合z.union([...])→v.union([...])z.discriminatedUnion(key, {...})→v.variant(key, {...})z.optional(...)、z.nullable(...)、z.nullish(...)分别映射为v.optional(...)、v.nullable(...)、v.nullish(...)z.object({ ... })→v.object({ ... })且对象链上的.strict()、.passthrough()、.strip()会被检测为对象修饰符见 schemas-and-links.ts 中的checkForObjectModifierInChain分别改写为v.strictObject(...)、v.looseObject(...)与默认的v.object(...)对象方法pick/omit/partial/required/extend/merge/deepPartial等均有对应转换如z.object({...}).pick({...})会改写为v.pick(v.object({...}), {...})相应实现分布在 methods 目录z.record(...)→v.record(...)z.map(...)/z.set(...)→v.map(...)/v.set(...)z.tuple([...])→v.tuple([...])z.instanceof(Class)→v.instance(Class)校验器的message参数会转换为 Valibot action 的第二个字符串参数例如z.array(z.string(), { message: some message })输出v.array(v.string(), some message)description选项则被提升为v.pipe(v.array(v.string()), v.description(some description))其参数提取逻辑在 schemas/helpers.ts 的getTransformedMsgs/getOptions中实现。解析调用与结果属性的改写parsing夹具完整展示了运行时 API 的迁移// 转换前 const output1 Schema.parse(to parse); const result1 Schema.safeParse(to safeParse); if (result1.success) { const output result1.data; } else { const errors result1.error; } // 转换后 const output1 v.parse(Schema, to parse); const result1 v.safeParse(Schema, to safeParse); if (result1.success) { const output result1.output; } else { const errors result1.issues; }要点方法调用变为独立的顶层函数Schema.parse(x)→v.parse(Schema, x)safeParse/parseAsync/safeParseAsync同理Schema.spa(x)旧版别名也统一映射到v.safeParseAsync(Schema, x)结果对象属性重命名result.data→result.outputresult.error→result.issues见toValiPropExp中data、error两个分支。transform 与 refinez.string().transform(fn)→v.pipe(v.string(), v.transform(fn))连续多个 transform 会依次放入同一个 pipez.number().refine(fn)对应改写为 pipe 中的v.check(fn)形态z.string().regex(...)→v.regex(...)被赋给变量的 schema 引用也会被追踪const BaseSchema z.string(); const T BaseSchema.transform(...)会输出v.pipe(BaseSchema, v.transform(...))——这正是transformSchemasAndLinks递归跟踪变量链接的结果。类型推导的改写type-inference场景见toValiTypeExp的映射为z.infertypeof Schema→v.InferOutputtypeof Schemaz.outputtypeof Schema→v.InferOutputtypeof Schemaz.inputtypeof Schema→v.InferInputtypeof Schema类型引用同样会从z前缀替换为v前缀。源码工作机制两阶段转换整个 transform 的入口在 src/transform/index.ts流程分两个阶段imports 阶段transformImports见 imports/imports.ts。它查找from zod或from zod/v4的 import 语句要求恰好一条 import、恰好一个 specifier否则判定失败文件里根本没有 zod import 时直接跳过skipimport 写法不满足要求时在文件头部插入valibot-migrate: unable to transform imports from Zod to Valibot: 原因注释并原样返回。成功后把 import 改写为import * as v from valibot若原标识符不是z如import { z as zod } from zod则保留原标识符作为 Valibot 的命名空间名。当原标识符恰为z时还会把所有z.xxx成员表达式与z.xxx类型引用批量替换为v.xxx。schemas-and-links 阶段transformSchemasAndLinks见 schemas-and-links.ts。它遍历 AST 中所有以 Valibot 标识符为根的成员表达式与TSTypeReference从内到外.reverse()逐个处理调用链识别到 schema 名如string→ 生成对应的 Valibot schema 调用识别到 validator 名如email→ 通过 helpers.ts 的addToPipe把 action 追加进v.pipe(...)若已是 pipe 则直接追加参数否则新建 pipe识别到方法名如optional、parse→ 调用对应的方法转换器遇到不认识的属性名或无法安全解析的链则标记transformLinks false整条链保持原样避免破坏代码。转换器通过ZOD_SCHEMA_TO_TYPE记录每个 schema 的类型族如string→length、number→value、set→size以便min/max/nonempty等通用校验器在目标类型上选择正确的 Valibot action。值得注意的细节被赋值的 schema 变量会被追踪。当链的根表达式是const X zod表达式的右值时转换器会递归地以变量名X继续扫描文件中所有引用X的表达式见transformSchemasAndLinksHelper末尾的变量链接追踪因此const Schema z.string(); Schema.parse(...)这类跨语句引用也能被正确改写。如何验证转换结果测试夹具机制仓库没有为转换结果提供独立的验收脚本而是把测试内嵌在 src/utils.ts 的defineTests中它读取__testfixtures__下每个子目录查找input*与output*命名的文件对用 jscodeshift 的 ts 解析器对input执行当前 transform并将结果与output做逐字比较expect(output?.trim()).toBe(expectedOutput.trim())。测试运行器是 vitest脚本定义在 package.json 的test: vitest。这意味着每个转换场景都有一条输入 → 输出的金标准如果你在迁移中遇到某个写法的转换结果不符合预期可以对照testfixtures中对应场景的output.ts判断是工具的限制还是自己写法超出了覆盖范围。仓库还提供了test-setup.test.ts作为测试装配入口。限制与注意事项从源码与夹具可以确认以下几类边界情况迁移前值得提前排查import 写法有严格要求必须恰好一条from zod或zod/v4import 且恰好一个 specifier多条 import、同时存在默认导入与命名导入等写法会导致文件被跳过并插入说明注释。因此建议在运行 codemod 前先统一 import 风格如import { z } from zod或import * as z from zod。部分校验器未实现base64url、cidr、cuid、duration、jwt等见 schemas-and-links.ts 中transformUnimplemented的分支会保留原调用或需要手工替换为等价实现属于预期内的降级行为。自定义 schema 的泛型z.customT(...)依赖类型参数的透传改写时需要保证 Valibot 对应 API 的泛型语义一致源码注释也指出 parser 与类型系统并不完全同步ts-expect-error因此对高度依赖泛型推断的代码要额外 review。未知链保持原样任何无法识别的属性名或非标识符属性如schema[email]()都会让整条链跳过转换确保不产生错误代码——这同时意味着迁移后需要人工处理这些残留。结果语义的细微差异result.error→result.issues、result.data→result.output是 Valibot 的命名约定z.string({ coerce: true })与z.coerce.string()会统一成v.pipe(v.unknown(), v.toString())语义上等价但运行时多了一层unknown解析需要按项目对性能与错误信息的要求确认。先 dry-run再动手建议始终先--dry预览改动配合--verbose2观察被跳过的文件随后用--extensions分批灰度迁移并用 vitest 的夹具机制作为回归基线。迁移建议总结迁移前统一 Zod import 写法确保每个文件只有一条、一个 specifier 的 import用npx valibot/zod-to-valibot --dry src/**/*预览全量改动检查被跳过文件与未实现 validator 的清单确认无误后再执行真实迁移并配合类型检查与测试套件可参考__testfixtures__的输入输出对做全量回归对z.coerce.*、泛型z.customT、结果对象属性访问等特殊写法重点 review必要时手工微调。借助这套官方 codemod从 Zod 迁移到 Valibot 的大部分机械性工作可以交给自动化完成团队只需把精力集中在少数边界场景与业务语义核对上是平滑切换 schema 库的务实起点。赞分享后端前端【免费下载链接】valibotThe modular and type safe schema library for validating structural data 项目地址https://gitcode.com/gh_mirrors/va/valibot点击查看免费下载相关推荐中兴光猫工厂模式解锁终极指南zteOnu开源工具快速上手与避坑实战中兴光猫工厂模式解锁终极指南zteOnu开源工具快速上手与避坑实战 家里的 中兴光猫 是不是总感觉缺了点什么Web管理页面功能寥寥Telnet默认关闭后端前端终极Valibot迁移指南从Zod无缝转换的7个关键步骤终极Valibot迁移指南从Zod无缝转换的7个关键步骤 Valibot是一个模块化且类型安全的 schema验证库 专门用于验证结构化数据。如果你正在使用后端前端Valibot 官方 Zod 迁移 Codemod从 v0.1.0 到 ES2020 构建目标的版本演进与实现剖析Valibot 官方 Zod 迁移 Codemod从 v0.1.0 到 ES2020 构建目标的版本演进与实现剖析 valibot/zod to valib后端前端上一篇Arduino ESP32 核移植指南为 arduino-esp32 添加全新 SoC 支持的完整流程下一篇Loki 仓库内 etcd 官方 Go 客户端 clientv3 完全指南连接管理、错误处理与配置调优创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进