Universal plugin for transforming JSDoc comments on Zod schemas into runtime metadata.
- 🔄 Universal: Works with Vite, Rollup, Rolldown, webpack, Rspack, esbuild, and Farm
- 📝 Transforms JSDoc comments into Zod v4
.meta()calls - 🎯 TypeScript support
- ⚡ Fast and efficient by using the oxc-toolchain
- 🛠 Zero configuration required
Important
This plugin only works with Zod >= 3.25.0 and < 4.0.0.
You need to import zod/v4 instead of zod to use this plugin.
npm install unplugin-zod-jsdoc --save-devVite
// vite.config.ts
import ZodJsdoc from "unplugin-zod-jsdoc/vite";
export default defineConfig({
plugins: [ZodJsdoc()],
});Rollup
// rollup.config.js
import ZodJsdoc from "unplugin-zod-jsdoc/rollup";
export default {
plugins: [ZodJsdoc()],
};Rolldown
// rolldown.config.js
import ZodJsdoc from "unplugin-zod-jsdoc/rolldown";
export default {
plugins: [ZodJsdoc()],
};Webpack
// webpack.config.js
module.exports = {
/* ... */
plugins: [require("unplugin-zod-jsdoc/webpack")()],
};Rspack
// rspack.config.js
module.exports = {
/* ... */
plugins: [require("unplugin-zod-jsdoc/rspack")()],
};ESBuild
// esbuild.config.js
import { build } from "esbuild";
import ZodJsdoc from "unplugin-zod-jsdoc/esbuild";
build({
plugins: [ZodJsdoc()],
});Farm
// farm.config.ts
import ZodJsdoc from "unplugin-zod-jsdoc/farm";
export default defineConfig({
plugins: [ZodJsdoc()],
});You can pass optional options to the plugin for further customization.
interface PluginOptions {
/**
* Enable in development mode.
* Can improve performance by disabling the transformation in development.
* @default true
*/
enableInDev?: boolean;
}/**
* User's full name.
* Second line in description.
*/
const nameSchema = z.string().min(1);
/**
* User's email address
*/
const emailSchema = z.string().email();
const userSchema = z.object({
/**
* Unique identifier for the user
*/
id: z.string().uuid(),
name: nameSchema,
email: emailSchema,
});/**
* User's full name.
* Second line in description.
*/
const nameSchema = z
.string()
.min(1)
.meta({ description: `User's full name. Second line in description.` });
/**
* User's email address
*/
const emailSchema = z
.string()
.email()
.meta({ description: `User's email address` });
const userSchema = z.object({
/**
* Unique identifier for the user
*/
id: z.string().uuid().meta({ description: `Unique identifier for the user` }),
name: nameSchema,
email: emailSchema,
});As you can see, newlines are not preserved in the output.
The plugin can also be used to add additional metadata specific for JSON Schema generation.
For this, the following jsdoc-tags are supported:
@example@id@title@deprecated
/**
* An object representing a logged-in user
* @id User
* @title User Schema
* @example { id: '123-456 }
* @deprecated Superseded by User2
*/
const userSchema = z.object({
/** Unique identifier for the user */
id: z.string(),
});
const dataSchema = z.object({
user1: userSchema,
user2: userSchema,
});
const jsonSchema = z.toJSONSchema(dataSchema, { target: 'draft-7', reused: 'ref' });
console.log(jsonSchema); // =>
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"user1": { "$ref": "#/definitions/User" },
"user2": { "$ref": "#/definitions/User" }
},
"required": ["user1", "user2"],
"additionalProperties": false,
"definitions": {
"User": {
"description": "An object representing a logged-in user",
"title": "User Schema",
"examples": ["{ id: '123-456' }"],
"id": "User",
"deprecated": true,
"type": "object",
"properties": {
"id": {
"description": "The unique identifier for the user",
"type": "string"
},
},
"required": ["id"],
"additionalProperties": false
}
}
}The plugin:
- Parses TypeScript/JavaScript files looking for Zod schemas
- Finds JSDoc comments that precede zod-calls
- Transforms comments into
.meta({ description: "..." })calls - Preserves existing
.meta()or.description()calls (doesn't override them)
MIT License © 2025