@schemashift/zod-valibot 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +200 -0
- package/dist/index.cjs +487 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +58 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +457 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# @schemashift/zod-valibot
|
|
2
|
+
|
|
3
|
+
Zod to Valibot transformer for SchemaShift. Converts Zod's fluent method chain API to Valibot's pipe-based API with AST-based transformations.
|
|
4
|
+
|
|
5
|
+
**Tier:** Pro
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @schemashift/zod-valibot
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { createZodToValibotHandler } from '@schemashift/zod-valibot';
|
|
17
|
+
import { TransformEngine } from '@schemashift/core';
|
|
18
|
+
|
|
19
|
+
const engine = new TransformEngine();
|
|
20
|
+
engine.registerHandler('zod', 'valibot', createZodToValibotHandler());
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## API Model Difference
|
|
24
|
+
|
|
25
|
+
Zod uses a fluent **method chain** API:
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
z.string().email().min(5).max(100)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Valibot uses a **pipe-based** API where validations are arguments to `v.pipe()`:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
v.pipe(v.string(), v.email(), v.minLength(5), v.maxLength(100))
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Modifiers like `.optional()` and `.nullable()` become wrapping functions:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
// Zod
|
|
41
|
+
z.string().optional()
|
|
42
|
+
|
|
43
|
+
// Valibot
|
|
44
|
+
v.optional(v.string())
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Transformation Mappings
|
|
48
|
+
|
|
49
|
+
### Imports
|
|
50
|
+
|
|
51
|
+
| Zod | Valibot |
|
|
52
|
+
|-----|---------|
|
|
53
|
+
| `import { z } from 'zod'` | `import * as v from 'valibot'` |
|
|
54
|
+
| `import * as z from 'zod'` | `import * as v from 'valibot'` |
|
|
55
|
+
|
|
56
|
+
### Factory Methods
|
|
57
|
+
|
|
58
|
+
| Zod | Valibot |
|
|
59
|
+
|-----|---------|
|
|
60
|
+
| `z.string()` | `v.string()` |
|
|
61
|
+
| `z.number()` | `v.number()` |
|
|
62
|
+
| `z.boolean()` | `v.boolean()` |
|
|
63
|
+
| `z.date()` | `v.date()` |
|
|
64
|
+
| `z.bigint()` | `v.bigint()` |
|
|
65
|
+
| `z.symbol()` | `v.symbol()` |
|
|
66
|
+
| `z.undefined()` | `v.undefined_()` |
|
|
67
|
+
| `z.null()` | `v.null_()` |
|
|
68
|
+
| `z.void()` | `v.void_()` |
|
|
69
|
+
| `z.any()` | `v.any()` |
|
|
70
|
+
| `z.unknown()` | `v.unknown()` |
|
|
71
|
+
| `z.never()` | `v.never()` |
|
|
72
|
+
| `z.nan()` | `v.nan()` |
|
|
73
|
+
| `z.literal(val)` | `v.literal(val)` |
|
|
74
|
+
| `z.enum([...])` | `v.picklist([...])` |
|
|
75
|
+
| `z.nativeEnum(E)` | `v.enum_(E)` |
|
|
76
|
+
| `z.array(s)` | `v.array(s)` |
|
|
77
|
+
| `z.object({...})` | `v.object({...})` |
|
|
78
|
+
| `z.record(k, v)` | `v.record(k, v)` |
|
|
79
|
+
| `z.tuple([...])` | `v.tuple([...])` |
|
|
80
|
+
| `z.union([...])` | `v.union([...])` |
|
|
81
|
+
| `z.discriminatedUnion(d, [...])` | `v.variant(d, [...])` |
|
|
82
|
+
| `z.intersection(a, b)` | `v.intersect(a, b)` |
|
|
83
|
+
| `z.lazy(() => s)` | `v.lazy(() => s)` |
|
|
84
|
+
| `z.promise(s)` | `v.promise(s)` |
|
|
85
|
+
| `z.instanceof(C)` | `v.instance(C)` |
|
|
86
|
+
|
|
87
|
+
### String Validations (Pipe)
|
|
88
|
+
|
|
89
|
+
| Zod | Valibot |
|
|
90
|
+
|-----|---------|
|
|
91
|
+
| `.email()` | `v.email()` |
|
|
92
|
+
| `.url()` | `v.url()` |
|
|
93
|
+
| `.uuid()` | `v.uuid()` |
|
|
94
|
+
| `.regex(r)` | `v.regex(r)` |
|
|
95
|
+
| `.ip()` | `v.ip()` |
|
|
96
|
+
| `.min(n)` | `v.minLength(n)` |
|
|
97
|
+
| `.max(n)` | `v.maxLength(n)` |
|
|
98
|
+
| `.length(n)` | `v.length(n)` |
|
|
99
|
+
| `.trim()` | `v.trim()` |
|
|
100
|
+
| `.toLowerCase()` | `v.toLowerCase()` |
|
|
101
|
+
| `.toUpperCase()` | `v.toUpperCase()` |
|
|
102
|
+
| `.includes(s)` | `v.includes(s)` |
|
|
103
|
+
| `.startsWith(s)` | `v.startsWith(s)` |
|
|
104
|
+
| `.endsWith(s)` | `v.endsWith(s)` |
|
|
105
|
+
| `.datetime()` | `v.isoDateTime()` |
|
|
106
|
+
| `.date()` | `v.isoDate()` |
|
|
107
|
+
| `.time()` | `v.isoTime()` |
|
|
108
|
+
| `.emoji()` | `v.emoji()` |
|
|
109
|
+
|
|
110
|
+
### Number Validations (Pipe)
|
|
111
|
+
|
|
112
|
+
| Zod | Valibot |
|
|
113
|
+
|-----|---------|
|
|
114
|
+
| `.min(n)` | `v.minValue(n)` |
|
|
115
|
+
| `.max(n)` | `v.maxValue(n)` |
|
|
116
|
+
| `.gt(n)` | `v.minValue(n + 1)` |
|
|
117
|
+
| `.lt(n)` | `v.maxValue(n - 1)` |
|
|
118
|
+
| `.gte(n)` | `v.minValue(n)` |
|
|
119
|
+
| `.lte(n)` | `v.maxValue(n)` |
|
|
120
|
+
| `.int()` | `v.integer()` |
|
|
121
|
+
| `.positive()` | `v.minValue(1)` |
|
|
122
|
+
| `.negative()` | `v.maxValue(-1)` |
|
|
123
|
+
| `.nonnegative()` | `v.minValue(0)` |
|
|
124
|
+
| `.nonpositive()` | `v.maxValue(0)` |
|
|
125
|
+
| `.multipleOf(n)` | `v.multipleOf(n)` |
|
|
126
|
+
| `.finite()` | `v.finite()` |
|
|
127
|
+
| `.safe()` | `v.safeInteger()` |
|
|
128
|
+
|
|
129
|
+
### Array Validations
|
|
130
|
+
|
|
131
|
+
| Zod | Valibot |
|
|
132
|
+
|-----|---------|
|
|
133
|
+
| `.min(n)` | `v.minLength(n)` |
|
|
134
|
+
| `.max(n)` | `v.maxLength(n)` |
|
|
135
|
+
| `.nonempty()` | `v.nonEmpty()` |
|
|
136
|
+
|
|
137
|
+
### Modifiers (Wrapping)
|
|
138
|
+
|
|
139
|
+
| Zod | Valibot |
|
|
140
|
+
|-----|---------|
|
|
141
|
+
| `.optional()` | `v.optional(schema)` |
|
|
142
|
+
| `.nullable()` | `v.nullable(schema)` |
|
|
143
|
+
| `.nullish()` | `v.nullish(schema)` |
|
|
144
|
+
| `.default(val)` | `v.optional(schema, val)` |
|
|
145
|
+
| `.catch(val)` | `v.fallback(schema, val)` |
|
|
146
|
+
| `.readonly()` | `v.readonly(schema)` |
|
|
147
|
+
| `.transform(fn)` | `v.pipe(schema, v.transform(fn))` |
|
|
148
|
+
| `.refine(fn, msg)` | `v.pipe(schema, v.check(fn, msg))` |
|
|
149
|
+
| `.brand(name)` | `v.pipe(schema, v.brand(name))` |
|
|
150
|
+
| `.describe(msg)` | (removed — Valibot has no equivalent) |
|
|
151
|
+
|
|
152
|
+
### Object Methods (Wrapping)
|
|
153
|
+
|
|
154
|
+
| Zod | Valibot |
|
|
155
|
+
|-----|---------|
|
|
156
|
+
| `.partial()` | `v.partial(schema)` |
|
|
157
|
+
| `.required()` | `v.required(schema)` |
|
|
158
|
+
| `.pick({...})` | `v.pick(schema, {...})` |
|
|
159
|
+
| `.omit({...})` | `v.omit(schema, {...})` |
|
|
160
|
+
| `.extend(other)` | `v.merge([schema, other])` |
|
|
161
|
+
| `.merge(other)` | `v.merge([schema, other])` |
|
|
162
|
+
| `.strict()` | `v.strict(schema)` |
|
|
163
|
+
| `.passthrough()` | (no-op — Valibot objects are loose by default) |
|
|
164
|
+
| `.strip()` | (no-op — Valibot strips unknown by default with v.object) |
|
|
165
|
+
|
|
166
|
+
### Type Helpers
|
|
167
|
+
|
|
168
|
+
| Zod | Valibot |
|
|
169
|
+
|-----|---------|
|
|
170
|
+
| `z.infer<typeof s>` | `v.InferOutput<typeof s>` |
|
|
171
|
+
| `z.input<typeof s>` | `v.InferInput<typeof s>` |
|
|
172
|
+
| `z.output<typeof s>` | `v.InferOutput<typeof s>` |
|
|
173
|
+
|
|
174
|
+
## Patterns Requiring Manual Review
|
|
175
|
+
|
|
176
|
+
### `.superRefine()`
|
|
177
|
+
|
|
178
|
+
`.superRefine()` is converted with a warning since Valibot's custom validation has a different API:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
// Zod
|
|
182
|
+
z.object({...}).superRefine((data, ctx) => {
|
|
183
|
+
ctx.addIssue({ code: 'custom', message: '...' });
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Valibot (needs manual conversion)
|
|
187
|
+
v.pipe(v.object({...}), /* TODO: superRefine -> custom validation */ v.check(...))
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### `.catchall()`
|
|
191
|
+
|
|
192
|
+
Zod's `.catchall()` for extra properties requires manual conversion to Valibot's `v.record()` or custom validation.
|
|
193
|
+
|
|
194
|
+
### `.keyof()`
|
|
195
|
+
|
|
196
|
+
Zod's `.keyof()` requires manual conversion to `v.picklist(Object.keys(...))`.
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
FACTORY_MAPPINGS: () => FACTORY_MAPPINGS,
|
|
24
|
+
MODIFIER_METHODS: () => MODIFIER_METHODS,
|
|
25
|
+
NUMBER_VALIDATION_MAPPINGS: () => NUMBER_VALIDATION_MAPPINGS,
|
|
26
|
+
OBJECT_METHODS: () => OBJECT_METHODS,
|
|
27
|
+
VALIDATION_MAPPINGS: () => VALIDATION_MAPPINGS,
|
|
28
|
+
ZodToValibotTransformer: () => ZodToValibotTransformer,
|
|
29
|
+
createZodToValibotHandler: () => createZodToValibotHandler
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(index_exports);
|
|
32
|
+
|
|
33
|
+
// src/transformer.ts
|
|
34
|
+
var import_core = require("@schemashift/core");
|
|
35
|
+
var import_ts_morph = require("ts-morph");
|
|
36
|
+
|
|
37
|
+
// src/mappings.ts
|
|
38
|
+
var FACTORY_MAPPINGS = {
|
|
39
|
+
string: "v.string",
|
|
40
|
+
number: "v.number",
|
|
41
|
+
boolean: "v.boolean",
|
|
42
|
+
date: "v.date",
|
|
43
|
+
bigint: "v.bigint",
|
|
44
|
+
symbol: "v.symbol",
|
|
45
|
+
undefined: "v.undefined_",
|
|
46
|
+
null: "v.null_",
|
|
47
|
+
void: "v.void_",
|
|
48
|
+
any: "v.any",
|
|
49
|
+
unknown: "v.unknown",
|
|
50
|
+
never: "v.never",
|
|
51
|
+
nan: "v.nan",
|
|
52
|
+
literal: "v.literal",
|
|
53
|
+
enum: "v.picklist",
|
|
54
|
+
nativeEnum: "v.enum_",
|
|
55
|
+
array: "v.array",
|
|
56
|
+
object: "v.object",
|
|
57
|
+
record: "v.record",
|
|
58
|
+
tuple: "v.tuple",
|
|
59
|
+
union: "v.union",
|
|
60
|
+
discriminatedUnion: "v.variant",
|
|
61
|
+
intersection: "v.intersect",
|
|
62
|
+
lazy: "v.lazy",
|
|
63
|
+
promise: "v.promise",
|
|
64
|
+
instanceof: "v.instance"
|
|
65
|
+
};
|
|
66
|
+
var VALIDATION_MAPPINGS = {
|
|
67
|
+
// String validations
|
|
68
|
+
email: "v.email",
|
|
69
|
+
url: "v.url",
|
|
70
|
+
uuid: "v.uuid",
|
|
71
|
+
cuid: "v.cuid2",
|
|
72
|
+
cuid2: "v.cuid2",
|
|
73
|
+
ulid: "v.ulid",
|
|
74
|
+
regex: "v.regex",
|
|
75
|
+
ip: "v.ip",
|
|
76
|
+
datetime: "v.isoDateTime",
|
|
77
|
+
date: "v.isoDate",
|
|
78
|
+
time: "v.isoTime",
|
|
79
|
+
emoji: "v.emoji",
|
|
80
|
+
includes: "v.includes",
|
|
81
|
+
startsWith: "v.startsWith",
|
|
82
|
+
endsWith: "v.endsWith",
|
|
83
|
+
trim: "v.trim",
|
|
84
|
+
toLowerCase: "v.toLowerCase",
|
|
85
|
+
toUpperCase: "v.toUpperCase",
|
|
86
|
+
// Shared validations (string/number/array/date)
|
|
87
|
+
min: "v.minLength",
|
|
88
|
+
// Will be context-sensitive
|
|
89
|
+
max: "v.maxLength",
|
|
90
|
+
// Will be context-sensitive
|
|
91
|
+
length: "v.length",
|
|
92
|
+
// Number validations
|
|
93
|
+
gt: "v.minValue",
|
|
94
|
+
gte: "v.minValue",
|
|
95
|
+
lt: "v.maxValue",
|
|
96
|
+
lte: "v.maxValue",
|
|
97
|
+
int: "v.integer",
|
|
98
|
+
positive: "v.minValue",
|
|
99
|
+
negative: "v.maxValue",
|
|
100
|
+
nonnegative: "v.minValue",
|
|
101
|
+
nonpositive: "v.maxValue",
|
|
102
|
+
multipleOf: "v.multipleOf",
|
|
103
|
+
finite: "v.finite",
|
|
104
|
+
safe: "v.safeInteger",
|
|
105
|
+
// Array validations
|
|
106
|
+
nonempty: "v.nonEmpty",
|
|
107
|
+
// Schema modifiers (not pipe validators)
|
|
108
|
+
optional: null,
|
|
109
|
+
// Handled specially
|
|
110
|
+
nullable: null,
|
|
111
|
+
// Handled specially
|
|
112
|
+
nullish: null,
|
|
113
|
+
// Handled specially
|
|
114
|
+
default: null,
|
|
115
|
+
// Handled specially
|
|
116
|
+
catch: null,
|
|
117
|
+
// Handled specially
|
|
118
|
+
transform: null,
|
|
119
|
+
// Handled specially
|
|
120
|
+
refine: null,
|
|
121
|
+
// Handled specially
|
|
122
|
+
superRefine: null,
|
|
123
|
+
// Handled specially
|
|
124
|
+
brand: null,
|
|
125
|
+
// Handled specially
|
|
126
|
+
readonly: null,
|
|
127
|
+
// Handled specially
|
|
128
|
+
pipe: null,
|
|
129
|
+
// Handled specially
|
|
130
|
+
describe: null,
|
|
131
|
+
// Handled specially
|
|
132
|
+
// Object methods
|
|
133
|
+
partial: null,
|
|
134
|
+
// Handled specially
|
|
135
|
+
required: null,
|
|
136
|
+
// Handled specially
|
|
137
|
+
pick: null,
|
|
138
|
+
// Handled specially
|
|
139
|
+
omit: null,
|
|
140
|
+
// Handled specially
|
|
141
|
+
extend: null,
|
|
142
|
+
// Handled specially
|
|
143
|
+
merge: null,
|
|
144
|
+
// Handled specially
|
|
145
|
+
passthrough: null,
|
|
146
|
+
// Handled specially
|
|
147
|
+
strict: null,
|
|
148
|
+
// Handled specially
|
|
149
|
+
strip: null,
|
|
150
|
+
// Handled specially
|
|
151
|
+
catchall: null,
|
|
152
|
+
// Handled specially
|
|
153
|
+
keyof: null,
|
|
154
|
+
// Handled specially
|
|
155
|
+
// Array methods
|
|
156
|
+
element: null
|
|
157
|
+
// Part of factory
|
|
158
|
+
};
|
|
159
|
+
var NUMBER_VALIDATION_MAPPINGS = {
|
|
160
|
+
min: { name: "v.minValue", args: (a) => a },
|
|
161
|
+
max: { name: "v.maxValue", args: (a) => a },
|
|
162
|
+
gt: { name: "v.minValue", args: (a) => a.length > 0 ? [String(Number(a[0]) + 1)] : a },
|
|
163
|
+
lt: { name: "v.maxValue", args: (a) => a.length > 0 ? [String(Number(a[0]) - 1)] : a },
|
|
164
|
+
gte: { name: "v.minValue", args: (a) => a },
|
|
165
|
+
lte: { name: "v.maxValue", args: (a) => a },
|
|
166
|
+
positive: { name: "v.minValue", args: () => ["1"] },
|
|
167
|
+
negative: { name: "v.maxValue", args: () => ["-1"] },
|
|
168
|
+
nonnegative: { name: "v.minValue", args: () => ["0"] },
|
|
169
|
+
nonpositive: { name: "v.maxValue", args: () => ["0"] }
|
|
170
|
+
};
|
|
171
|
+
var MODIFIER_METHODS = /* @__PURE__ */ new Set([
|
|
172
|
+
"optional",
|
|
173
|
+
"nullable",
|
|
174
|
+
"nullish",
|
|
175
|
+
"default",
|
|
176
|
+
"catch",
|
|
177
|
+
"readonly",
|
|
178
|
+
"brand",
|
|
179
|
+
"describe",
|
|
180
|
+
"transform",
|
|
181
|
+
"refine",
|
|
182
|
+
"superRefine",
|
|
183
|
+
"pipe"
|
|
184
|
+
]);
|
|
185
|
+
var OBJECT_METHODS = /* @__PURE__ */ new Set([
|
|
186
|
+
"partial",
|
|
187
|
+
"required",
|
|
188
|
+
"pick",
|
|
189
|
+
"omit",
|
|
190
|
+
"extend",
|
|
191
|
+
"merge",
|
|
192
|
+
"passthrough",
|
|
193
|
+
"strict",
|
|
194
|
+
"strip",
|
|
195
|
+
"catchall",
|
|
196
|
+
"keyof"
|
|
197
|
+
]);
|
|
198
|
+
|
|
199
|
+
// src/transformer.ts
|
|
200
|
+
var ZodToValibotTransformer = class {
|
|
201
|
+
errors = [];
|
|
202
|
+
warnings = [];
|
|
203
|
+
transform(sourceFile) {
|
|
204
|
+
this.errors = [];
|
|
205
|
+
this.warnings = [];
|
|
206
|
+
const filePath = sourceFile.getFilePath();
|
|
207
|
+
const originalCode = sourceFile.getFullText();
|
|
208
|
+
try {
|
|
209
|
+
this.updateImports(sourceFile);
|
|
210
|
+
this.transformTypeHelpers(sourceFile);
|
|
211
|
+
this.transformSchemaExpressions(sourceFile);
|
|
212
|
+
return {
|
|
213
|
+
success: this.errors.length === 0,
|
|
214
|
+
filePath,
|
|
215
|
+
originalCode,
|
|
216
|
+
transformedCode: sourceFile.getFullText(),
|
|
217
|
+
errors: this.errors,
|
|
218
|
+
warnings: this.warnings
|
|
219
|
+
};
|
|
220
|
+
} catch (error) {
|
|
221
|
+
this.errors.push({
|
|
222
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
223
|
+
});
|
|
224
|
+
return {
|
|
225
|
+
success: false,
|
|
226
|
+
filePath,
|
|
227
|
+
originalCode,
|
|
228
|
+
errors: this.errors,
|
|
229
|
+
warnings: this.warnings
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
updateImports(sourceFile) {
|
|
234
|
+
const zodImports = sourceFile.getImportDeclarations().filter((imp) => imp.getModuleSpecifierValue() === "zod");
|
|
235
|
+
for (const imp of zodImports) {
|
|
236
|
+
imp.setModuleSpecifier("valibot");
|
|
237
|
+
const namespaceImport = imp.getNamespaceImport();
|
|
238
|
+
const defaultImport = imp.getDefaultImport();
|
|
239
|
+
const namedImports = imp.getNamedImports();
|
|
240
|
+
if (namespaceImport) imp.removeNamespaceImport();
|
|
241
|
+
if (defaultImport) imp.removeDefaultImport();
|
|
242
|
+
if (namedImports.length > 0) imp.removeNamedImports();
|
|
243
|
+
imp.setNamespaceImport("v");
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
transformTypeHelpers(sourceFile) {
|
|
247
|
+
const fullText = sourceFile.getFullText();
|
|
248
|
+
let newText = fullText;
|
|
249
|
+
newText = newText.replace(/\bz\.infer</g, "v.InferOutput<");
|
|
250
|
+
newText = newText.replace(/\bz\.input</g, "v.InferInput<");
|
|
251
|
+
newText = newText.replace(/\bz\.output</g, "v.InferOutput<");
|
|
252
|
+
if (newText !== fullText) {
|
|
253
|
+
sourceFile.replaceWithText(newText);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
transformSchemaExpressions(sourceFile) {
|
|
257
|
+
const nodesToTransform = [];
|
|
258
|
+
sourceFile.forEachDescendant((node) => {
|
|
259
|
+
if (import_ts_morph.Node.isCallExpression(node)) {
|
|
260
|
+
if ((0, import_core.isInsideStringLiteral)(node)) return;
|
|
261
|
+
const text = node.getText();
|
|
262
|
+
if (text.startsWith("z.")) {
|
|
263
|
+
nodesToTransform.push(node);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
const outermost = this.filterOutermostNodes(nodesToTransform);
|
|
268
|
+
outermost.sort((a, b) => b.getStart() - a.getStart());
|
|
269
|
+
for (const node of outermost) {
|
|
270
|
+
this.transformCallChain(node);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
filterOutermostNodes(nodes) {
|
|
274
|
+
const result = [];
|
|
275
|
+
for (const node of nodes) {
|
|
276
|
+
let isChild = false;
|
|
277
|
+
for (const other of nodes) {
|
|
278
|
+
if (other === node) continue;
|
|
279
|
+
if (other.getStart() <= node.getStart() && other.getEnd() >= node.getEnd()) {
|
|
280
|
+
isChild = true;
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (!isChild) result.push(node);
|
|
285
|
+
}
|
|
286
|
+
return result;
|
|
287
|
+
}
|
|
288
|
+
transformCallChain(node) {
|
|
289
|
+
const chain = (0, import_core.parseCallChain)(node);
|
|
290
|
+
if (!chain) {
|
|
291
|
+
this.transformCallChainFallback(node);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if (chain.base !== "z") return;
|
|
295
|
+
const filePath = node.getSourceFile().getFilePath();
|
|
296
|
+
const lineNumber = node.getStartLineNumber();
|
|
297
|
+
const factoryMapping = FACTORY_MAPPINGS[chain.factoryMethod];
|
|
298
|
+
if (!factoryMapping) {
|
|
299
|
+
this.warnings.push(
|
|
300
|
+
`${filePath}:${lineNumber}: Unknown Zod factory z.${chain.factoryMethod}() \u2014 kept as-is`
|
|
301
|
+
);
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
const factoryType = chain.factoryMethod;
|
|
305
|
+
const pipeValidators = [];
|
|
306
|
+
const modifiers = [];
|
|
307
|
+
const objectMethods = [];
|
|
308
|
+
for (const method of chain.methods) {
|
|
309
|
+
const result2 = this.categorizeMethod(method, factoryType, filePath, lineNumber);
|
|
310
|
+
if (result2.type === "pipe") {
|
|
311
|
+
pipeValidators.push(result2.code);
|
|
312
|
+
} else if (result2.type === "modifier") {
|
|
313
|
+
modifiers.push({ name: result2.name, args: result2.args });
|
|
314
|
+
} else if (result2.type === "object") {
|
|
315
|
+
objectMethods.push({ name: result2.name, args: result2.args });
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
let result;
|
|
319
|
+
const factoryCall = `${factoryMapping}(${chain.factoryArgs.join(", ")})`;
|
|
320
|
+
if (pipeValidators.length > 0) {
|
|
321
|
+
result = `v.pipe(${factoryCall}, ${pipeValidators.join(", ")})`;
|
|
322
|
+
} else {
|
|
323
|
+
result = factoryCall;
|
|
324
|
+
}
|
|
325
|
+
for (const objMethod of objectMethods) {
|
|
326
|
+
result = this.applyObjectMethod(objMethod.name, objMethod.args, result, filePath, lineNumber);
|
|
327
|
+
}
|
|
328
|
+
for (const modifier of modifiers) {
|
|
329
|
+
result = this.applyModifier(modifier.name, modifier.args, result, filePath, lineNumber);
|
|
330
|
+
}
|
|
331
|
+
result = this.transformNestedZodReferences(result);
|
|
332
|
+
node.replaceWithText(result);
|
|
333
|
+
}
|
|
334
|
+
transformNestedZodReferences(text) {
|
|
335
|
+
let result = text;
|
|
336
|
+
for (const [zodName, valibotName] of Object.entries(FACTORY_MAPPINGS)) {
|
|
337
|
+
const regex = new RegExp(`\\bz\\.${zodName}\\(`, "g");
|
|
338
|
+
result = result.replace(regex, `${valibotName}(`);
|
|
339
|
+
}
|
|
340
|
+
return result;
|
|
341
|
+
}
|
|
342
|
+
categorizeMethod(method, factoryType, filePath, lineNumber) {
|
|
343
|
+
const { name, args } = method;
|
|
344
|
+
if (MODIFIER_METHODS.has(name)) {
|
|
345
|
+
return { type: "modifier", name, args };
|
|
346
|
+
}
|
|
347
|
+
if (OBJECT_METHODS.has(name)) {
|
|
348
|
+
return { type: "object", name, args };
|
|
349
|
+
}
|
|
350
|
+
if ((factoryType === "number" || factoryType === "bigint") && NUMBER_VALIDATION_MAPPINGS[name]) {
|
|
351
|
+
const mapping = NUMBER_VALIDATION_MAPPINGS[name];
|
|
352
|
+
if (!mapping) return { type: "skip" };
|
|
353
|
+
const mappedArgs = mapping.args(args);
|
|
354
|
+
return { type: "pipe", code: `${mapping.name}(${mappedArgs.join(", ")})` };
|
|
355
|
+
}
|
|
356
|
+
if (factoryType === "string" || factoryType === "array") {
|
|
357
|
+
if (name === "min") {
|
|
358
|
+
const vName = factoryType === "string" ? "v.minLength" : "v.minLength";
|
|
359
|
+
return { type: "pipe", code: `${vName}(${args.join(", ")})` };
|
|
360
|
+
}
|
|
361
|
+
if (name === "max") {
|
|
362
|
+
const vName = factoryType === "string" ? "v.maxLength" : "v.maxLength";
|
|
363
|
+
return { type: "pipe", code: `${vName}(${args.join(", ")})` };
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const validationMapping = VALIDATION_MAPPINGS[name];
|
|
367
|
+
if (validationMapping === null) {
|
|
368
|
+
return { type: "skip" };
|
|
369
|
+
}
|
|
370
|
+
if (validationMapping) {
|
|
371
|
+
return { type: "pipe", code: `${validationMapping}(${args.join(", ")})` };
|
|
372
|
+
}
|
|
373
|
+
this.warnings.push(
|
|
374
|
+
`${filePath}:${lineNumber}: Unknown Zod method .${name}() \u2014 kept as-is in pipe`
|
|
375
|
+
);
|
|
376
|
+
return { type: "pipe", code: `/* TODO: .${name}(${args.join(", ")}) */` };
|
|
377
|
+
}
|
|
378
|
+
applyModifier(name, args, inner, filePath, lineNumber) {
|
|
379
|
+
switch (name) {
|
|
380
|
+
case "optional":
|
|
381
|
+
return `v.optional(${inner})`;
|
|
382
|
+
case "nullable":
|
|
383
|
+
return `v.nullable(${inner})`;
|
|
384
|
+
case "nullish":
|
|
385
|
+
return `v.nullish(${inner})`;
|
|
386
|
+
case "default":
|
|
387
|
+
return `v.optional(${inner}, ${args.join(", ")})`;
|
|
388
|
+
case "catch":
|
|
389
|
+
return `v.fallback(${inner}, ${args.join(", ")})`;
|
|
390
|
+
case "readonly":
|
|
391
|
+
return `v.readonly(${inner})`;
|
|
392
|
+
case "transform":
|
|
393
|
+
return `v.pipe(${inner}, v.transform(${args.join(", ")}))`;
|
|
394
|
+
case "refine":
|
|
395
|
+
if (args.length >= 2) {
|
|
396
|
+
return `v.pipe(${inner}, v.check(${args[0]}, ${args.slice(1).join(", ")}))`;
|
|
397
|
+
}
|
|
398
|
+
return `v.pipe(${inner}, v.check(${args.join(", ")}))`;
|
|
399
|
+
case "superRefine":
|
|
400
|
+
this.warnings.push(
|
|
401
|
+
`${filePath}:${lineNumber}: .superRefine() requires manual conversion to Valibot custom validation`
|
|
402
|
+
);
|
|
403
|
+
return `v.pipe(${inner}, /* TODO: superRefine -> custom validation */ v.check(${args.join(", ")}))`;
|
|
404
|
+
case "brand":
|
|
405
|
+
return `v.pipe(${inner}, v.brand(${args.join(", ")}))`;
|
|
406
|
+
case "describe":
|
|
407
|
+
return inner;
|
|
408
|
+
case "pipe":
|
|
409
|
+
return `v.pipe(${inner}, ${args.join(", ")})`;
|
|
410
|
+
default:
|
|
411
|
+
return inner;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
applyObjectMethod(name, args, inner, filePath, lineNumber) {
|
|
415
|
+
switch (name) {
|
|
416
|
+
case "partial":
|
|
417
|
+
return `v.partial(${inner})`;
|
|
418
|
+
case "required":
|
|
419
|
+
return `v.required(${inner})`;
|
|
420
|
+
case "pick":
|
|
421
|
+
return `v.pick(${inner}, ${args.join(", ")})`;
|
|
422
|
+
case "omit":
|
|
423
|
+
return `v.omit(${inner}, ${args.join(", ")})`;
|
|
424
|
+
case "extend":
|
|
425
|
+
case "merge":
|
|
426
|
+
return `v.merge([${inner}, ${args.join(", ")}])`;
|
|
427
|
+
case "passthrough":
|
|
428
|
+
this.warnings.push(
|
|
429
|
+
`${filePath}:${lineNumber}: .passthrough() \u2014 Valibot objects are loose by default, no action needed`
|
|
430
|
+
);
|
|
431
|
+
return inner;
|
|
432
|
+
case "strict":
|
|
433
|
+
return `v.strict(${inner})`;
|
|
434
|
+
case "strip":
|
|
435
|
+
return inner;
|
|
436
|
+
// Valibot strips unknown by default when using v.object
|
|
437
|
+
case "catchall":
|
|
438
|
+
this.warnings.push(
|
|
439
|
+
`${filePath}:${lineNumber}: .catchall() requires manual conversion to Valibot record or custom validation`
|
|
440
|
+
);
|
|
441
|
+
return inner;
|
|
442
|
+
case "keyof":
|
|
443
|
+
this.warnings.push(
|
|
444
|
+
`${filePath}:${lineNumber}: .keyof() requires manual conversion to v.picklist(Object.keys(...))`
|
|
445
|
+
);
|
|
446
|
+
return inner;
|
|
447
|
+
default:
|
|
448
|
+
return inner;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
transformCallChainFallback(node) {
|
|
452
|
+
const text = node.getText();
|
|
453
|
+
const filePath = node.getSourceFile().getFilePath();
|
|
454
|
+
const lineNumber = node.getStartLineNumber();
|
|
455
|
+
let transformed = text;
|
|
456
|
+
transformed = transformed.replace(/^z\./, "v.");
|
|
457
|
+
for (const [zodName, valibotName] of Object.entries(FACTORY_MAPPINGS)) {
|
|
458
|
+
const regex = new RegExp(`\\bv\\.${zodName}\\(`, "g");
|
|
459
|
+
transformed = transformed.replace(regex, `${valibotName}(`);
|
|
460
|
+
}
|
|
461
|
+
this.warnings.push(
|
|
462
|
+
`${filePath}:${lineNumber}: Complex Zod expression used fallback conversion \u2014 manual review recommended`
|
|
463
|
+
);
|
|
464
|
+
node.replaceWithText(transformed);
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
// src/handler.ts
|
|
469
|
+
function createZodToValibotHandler() {
|
|
470
|
+
const transformer = new ZodToValibotTransformer();
|
|
471
|
+
return {
|
|
472
|
+
transform(sourceFile, _options) {
|
|
473
|
+
return transformer.transform(sourceFile);
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
478
|
+
0 && (module.exports = {
|
|
479
|
+
FACTORY_MAPPINGS,
|
|
480
|
+
MODIFIER_METHODS,
|
|
481
|
+
NUMBER_VALIDATION_MAPPINGS,
|
|
482
|
+
OBJECT_METHODS,
|
|
483
|
+
VALIDATION_MAPPINGS,
|
|
484
|
+
ZodToValibotTransformer,
|
|
485
|
+
createZodToValibotHandler
|
|
486
|
+
});
|
|
487
|
+
//# sourceMappingURL=index.cjs.map
|