unplugin-valibot-to-json-schema
v0.1.3
Published
Transform Valibot schemas to JSON Schema at build time
Maintainers
Readme
unplugin-valibot-to-json-schema
Experimental: This plugin is still being hardened. APIs and behavior may change between minor versions.
A build-time transformer that replaces toJsonSchema(...) calls with the serialized JSON Schema output, so schemas never ship to the client or execute at runtime.
Why?
JSON Schema is a language-independent serialization of your Valibot schemas. It lets you share validation rules across the stack, feed them to generic validators, form builders, AI tools, and LED emitting front-end libraries — without shipping the schema-building code or paying the runtime cost of generating the schema.
Runtime savings
Instead of shipping @valibot/to-json-schema (and executing it on every schema definition), the plugin bakes the schema into a plain JSON object at build time:
| Approach | Runtime work |
| ------------------------------------- | -------------------------------- |
| toJsonSchema(schema) at runtime | Executes the schema on every load |
| unplugin-valibot-to-json-schema | Sends zero calls to toJsonSchema |
This plugin automatically finds your toJsonSchema(...) calls, statically evaluates the referenced Valibot schemas, and replaces the call with the final JSON object. No runtime dependency on @valibot/to-json-schema.
Features
- Replaces every
toJsonSchema(...)call with its serialized JSON Schema output - Statically evaluates schemas from local constants and imported modules
- Handles inline
v.object({...})schemas and schemas backed by imported bindings - Supports the
toJsonSchema(schema, config)config object (e.g.target: 'draft-2020-12') - Removes the now-unused
@valibot/to-json-schemaimport - Emits source maps for debugging
Install
npm install unplugin-valibot-to-json-schemaUsage
Vite
// vite.config.ts
import valibotToJsonSchema from "unplugin-valibot-to-json-schema/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [valibotToJsonSchema()],
});Rollup
// rollup.config.js
import valibotToJsonSchema from "unplugin-valibot-to-json-schema/rollup";
export default {
plugins: [valibotToJsonSchema()],
};Webpack
// webpack.config.js
module.exports = {
plugins: [require("unplugin-valibot-to-json-schema/webpack")()],
};Nuxt
// nuxt.config.ts
export default defineNuxtConfig({
modules: ["unplugin-valibot-to-json-schema/nuxt"],
});esbuild
// esbuild.config.js
import { build } from "esbuild";
import valibotToJsonSchema from "unplugin-valibot-to-json-schema/esbuild";
build({
plugins: [valibotToJsonSchema()],
});Rspack
// rspack.config.js
import valibotToJsonSchema from "unplugin-valibot-to-json-schema/rspack";
export default {
plugins: [
valibotToJsonSchema({
// options
}),
],
};Example
Before transformation:
import { toJsonSchema } from "@valibot/to-json-schema";
import * as v from "valibot";
const minUserId = 1;
const userSearchSchema = v.object({
userId: v.pipe(
v.number(),
v.integer(),
v.minValue(minUserId),
v.description("User identifier"),
),
startDate: v.pipe(
v.string(),
v.isoDate("Expected an ISO date"),
v.description("Search start date"),
),
});
export const searchUsersDefinition = {
name: "search_users",
schema: toJsonSchema(userSearchSchema),
};After transformation (build output):
import * as v from "valibot";
const minUserId = 1;
const userSearchSchema = v.object({
userId: v.pipe(
v.number(),
v.integer(),
v.minValue(minUserId),
v.description("User identifier"),
),
startDate: v.pipe(
v.string(),
v.isoDate("Expected an ISO date"),
v.description("Search start date"),
),
});
export const searchUsersDefinition = {
name: "search_users",
schema: {
$schema: "http://json-schema.org/draft-07/schema#",
type: "object",
properties: {
userId: {
type: "integer",
minimum: 1,
description: "User identifier",
},
startDate: {
type: "string",
format: "date",
description: "Search start date",
},
},
required: ["userId", "startDate"],
additionalProperties: true,
},
};The toJsonSchema call is gone, the JSON Schema is inline, and @valibot/to-json-schema is no longer imported.
Options
interface PluginOptions {
// Only transform files whose id matches this RegExp
include?: RegExp;
// Skip files whose id matches this RegExp
exclude?: RegExp;
}Limitations
- Schemas that cannot be statically evaluated (e.g. dependency on runtime-only values that are not resolvable at build time) may fail — check the error message and report them here :]
Credits
- valibot — the schema library
- @valibot/to-json-schema — the runtime JSON Schema converter
- unplugin — the unified plugin system
