zod-to-x 2.2.0 → 2.3.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 +21 -9
- package/dist/converters/protobuf_v3/runner.js +1 -1
- package/dist/core/ast_node.d.ts +8 -1
- package/dist/core/ast_node.js +48 -35
- package/dist/layered-modeling/layer.js +3 -1
- package/dist/layered-modeling/model.d.ts +1 -0
- package/dist/transpilers/cpp/runner.d.ts +15 -2
- package/dist/transpilers/cpp/runner.js +45 -8
- package/dist/transpilers/go/libs.d.ts +8 -0
- package/dist/transpilers/go/libs.js +13 -0
- package/dist/transpilers/go/options.d.ts +17 -0
- package/dist/transpilers/go/options.js +11 -0
- package/dist/transpilers/go/runner.d.ts +100 -0
- package/dist/transpilers/go/runner.js +399 -0
- package/dist/transpilers/index.d.ts +1 -0
- package/dist/transpilers/index.js +3 -1
- package/dist/transpilers/python/runner.d.ts +7 -0
- package/dist/transpilers/python/runner.js +40 -3
- package/dist/transpilers/typescript/runner.d.ts +6 -0
- package/dist/transpilers/typescript/runner.js +22 -8
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -28,29 +28,35 @@
|
|
|
28
28
|
<img src="https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript">
|
|
29
29
|
<img src="https://img.shields.io/badge/Python-3776AB?style=for-the-badge&logo=python&logoColor=white" alt="Python">
|
|
30
30
|
<img src="https://img.shields.io/badge/C++-00599C?style=for-the-badge&logo=cplusplus&logoColor=white" alt="C++">
|
|
31
|
+
<img src="https://img.shields.io/badge/Go-00ADD8?style=for-the-badge&logo=go&logoColor=white" alt="Go">
|
|
31
32
|
<img src="https://img.shields.io/badge/Protobuf-4285F4?style=for-the-badge&logo=google&logoColor=white" alt="Protobuf">
|
|
32
33
|
</p>
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
## Table of contents
|
|
37
|
-
- [
|
|
38
|
+
- [Table of contents](#table-of-contents)
|
|
39
|
+
- [Why Use `@zod-to-x`?](#why-use-zod-to-x)
|
|
38
40
|
- [Installation](#installation)
|
|
41
|
+
- [1) Install `@zod-to-x` and `@zod(*)` dependency.](#1-install-zod-to-x-and-zod-dependency)
|
|
42
|
+
- [2) Extend Zod using the `extendZod` method after the first `@zod` import:](#2-extend-zod-using-the-extendzod-method-after-the-first-zod-import)
|
|
39
43
|
- [Quick start](#quick-start)
|
|
40
44
|
- [Intersections and Unions](#intersections-and-unions)
|
|
41
45
|
- [Expected outputs](#expected-outputs)
|
|
42
46
|
- [Tips for discriminated unions](#tips-for-discriminated-unions)
|
|
43
47
|
- [Layered modeling](#layered-modeling)
|
|
44
48
|
- [Usage example](#usage-example)
|
|
45
|
-
- [Custom
|
|
49
|
+
- [Custom Layers](#custom-layers)
|
|
46
50
|
- [Generic types](#generic-types)
|
|
47
51
|
- [Currently supported output languages](#currently-supported-output-languages)
|
|
48
|
-
- [
|
|
49
|
-
- [
|
|
50
|
-
- [
|
|
52
|
+
- [0) ASTNode](#0-astnode)
|
|
53
|
+
- [1) Typescript](#1-typescript)
|
|
54
|
+
- [2) Python](#2-python)
|
|
55
|
+
- [3) C++](#3-c)
|
|
56
|
+
- [4) Go](#4-go)
|
|
51
57
|
- [Additional utils](#additional-utils)
|
|
52
|
-
- [
|
|
53
|
-
- [Mapping of supported Zod Types by
|
|
58
|
+
- [1) `zod2ProtoV3`](#1-zod2protov3)
|
|
59
|
+
- [Mapping of supported Zod Types by Langauge](#mapping-of-supported-zod-types-by-langauge)
|
|
54
60
|
- [Considerations](#considerations)
|
|
55
61
|
|
|
56
62
|
|
|
@@ -62,7 +68,7 @@ Managing data consistency across multiple layers and languages is a common chall
|
|
|
62
68
|
Define your data structures in one place using the powerful [`@zod`](https://github.com/colinhacks/zod) library. This eliminates redundancy, reduces inconsistencies, and simplifies maintenance across your entire codebase, all while allowing you to continue leveraging any npm package in the [`@zod`](https://github.com/colinhacks/zod) ecosystem.
|
|
63
69
|
|
|
64
70
|
2. **Multi-Language Compatibility**
|
|
65
|
-
Generate data models for TypeScript, Python (Pydantic), C++, and Protobuf V3
|
|
71
|
+
Generate data models for TypeScript, Python (Pydantic), C++, Go, and Protobuf V3. No more manually rewriting models for different platforms.
|
|
66
72
|
|
|
67
73
|
3. **Enhanced Productivity**
|
|
68
74
|
Automate the transpilation of data models to save time, reduce errors, and let your team focus on business logic instead of boilerplate code.
|
|
@@ -628,7 +634,13 @@ Common options:
|
|
|
628
634
|
- **keepKeys**: Specifies whether property names should follow the C++ naming convention (false) or remain as originally defined (true). The default is `false`.
|
|
629
635
|
- [Examples](https://github.com/rroumenov/zod-to-x/blob/main/test/test_zod2cpp)
|
|
630
636
|
|
|
631
|
-
|
|
637
|
+
### 4) Go
|
|
638
|
+
Generates idiomatic Go structs with `encoding/json` struct tags. Requires Go 1.18+ for generic type support.
|
|
639
|
+
- Options:
|
|
640
|
+
- **packageName**: The Go package name for the generated file. Defaults to `"models"`.
|
|
641
|
+
- **keepKeys**: Specifies whether field names should remain as originally defined (true) or be converted to exported PascalCase with the original name used in the JSON tag (false). The default is `false`.
|
|
642
|
+
- **useJsonTags**: Whether to emit `json:"fieldName"` struct tags. Defaults to `true`.
|
|
643
|
+
- [Examples](https://github.com/rroumenov/zod-to-x/blob/main/test/test_zod2go)
|
|
632
644
|
|
|
633
645
|
## Additional utils
|
|
634
646
|
Additional useful tools to convert Zod Schemas into different formats.
|
|
@@ -255,6 +255,6 @@ class Zod2ProtoV3 extends core_1.Zod2X {
|
|
|
255
255
|
*/
|
|
256
256
|
function zod2ProtoV3(schema, // TODO: fix any to force only ZodObjects
|
|
257
257
|
opt = {}) {
|
|
258
|
-
const astNode = new core_1.Zod2Ast({ strict: opt.strict }).build(schema);
|
|
258
|
+
const astNode = new core_1.Zod2Ast({ strict: opt.strict, skipBasicTypes: true }).build(schema);
|
|
259
259
|
return new Zod2ProtoV3(opt).transpile(astNode);
|
|
260
260
|
}
|
package/dist/core/ast_node.d.ts
CHANGED
|
@@ -12,6 +12,13 @@ export interface IZod2AstOpt {
|
|
|
12
12
|
* the schema.
|
|
13
13
|
*/
|
|
14
14
|
layer?: IZod2xLayerMetadata;
|
|
15
|
+
/**
|
|
16
|
+
* If true, aliased basic types (string, number, boolean, etc.) from layered modeling
|
|
17
|
+
* will not be extracted as named AST nodes. Instead, they will be inlined as their
|
|
18
|
+
* underlying type. Useful for targets like Protobuf that don't support type aliases.
|
|
19
|
+
* Default is false.
|
|
20
|
+
*/
|
|
21
|
+
skipBasicTypes?: boolean;
|
|
15
22
|
}
|
|
16
23
|
/**
|
|
17
24
|
* This class creates AST nodes used to transpile Zod Schemas to other languages.
|
|
@@ -75,7 +82,7 @@ export declare class Zod2Ast {
|
|
|
75
82
|
private _intersectAstNodes;
|
|
76
83
|
/**
|
|
77
84
|
* Merges multiple AST definitions into a single AST object containing combined properties.
|
|
78
|
-
* - Equal properties
|
|
85
|
+
* - Equal properties must have the same type and array dimension.
|
|
79
86
|
* - If a property is optional in one definition and required in another, it will be considered
|
|
80
87
|
* optional in the merged object.
|
|
81
88
|
* - If a property is nullable in one definition and non-nullable in another, it will be
|
package/dist/core/ast_node.js
CHANGED
|
@@ -151,6 +151,7 @@ class Zod2Ast {
|
|
|
151
151
|
for (const schema of [left, right]) {
|
|
152
152
|
const shape = schema.def.shape;
|
|
153
153
|
for (const key in shape) {
|
|
154
|
+
const prevWasRequired = properties[key] !== undefined && !properties[key].isOptional;
|
|
154
155
|
if (zod_helpers_1.ZodHelpers.isZodPromise(shape[key]) &&
|
|
155
156
|
zod_helpers_1.ZodHelpers.isZod2XGeneric(shape[key])) {
|
|
156
157
|
const templateKey = shape[key].unwrap().def.values[0];
|
|
@@ -159,13 +160,18 @@ class Zod2Ast {
|
|
|
159
160
|
else {
|
|
160
161
|
properties[key] = this._zodToAST(shape[key]);
|
|
161
162
|
}
|
|
163
|
+
if (prevWasRequired && properties[key].isOptional) {
|
|
164
|
+
// In intersection, if a property is required in one schema and optional in
|
|
165
|
+
// another, it should be considered required.
|
|
166
|
+
properties[key].isOptional = false;
|
|
167
|
+
}
|
|
162
168
|
}
|
|
163
169
|
}
|
|
164
170
|
return { properties };
|
|
165
171
|
}
|
|
166
172
|
/**
|
|
167
173
|
* Merges multiple AST definitions into a single AST object containing combined properties.
|
|
168
|
-
* - Equal properties
|
|
174
|
+
* - Equal properties must have the same type and array dimension.
|
|
169
175
|
* - If a property is optional in one definition and required in another, it will be considered
|
|
170
176
|
* optional in the merged object.
|
|
171
177
|
* - If a property is nullable in one definition and non-nullable in another, it will be
|
|
@@ -178,42 +184,49 @@ class Zod2Ast {
|
|
|
178
184
|
_unionAstNodes(options) {
|
|
179
185
|
let typeA, typeB;
|
|
180
186
|
const data = options.map((i) => this.nodes.get(i.name));
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
`${i.properties[key].arrayDimension} (from ${i.name})`);
|
|
198
|
-
acc[key].arrayDimension = Math.max(acc[key].arrayDimension || 0, i.properties[key].arrayDimension || 0);
|
|
199
|
-
}
|
|
200
|
-
if (acc[key].isNullable !== i.properties[key].isNullable) {
|
|
201
|
-
acc[key].isNullable = true;
|
|
202
|
-
}
|
|
203
|
-
if (acc[key].isOptional !== i.properties[key].isOptional) {
|
|
204
|
-
acc[key].isOptional = true;
|
|
205
|
-
}
|
|
206
|
-
if (i.properties[key].description) {
|
|
207
|
-
acc[key].description = i.properties[key].description;
|
|
208
|
-
}
|
|
187
|
+
const properties = data.reduce((acc, i, j) => {
|
|
188
|
+
for (const key in i.properties) {
|
|
189
|
+
if (acc[key]) {
|
|
190
|
+
acc[key] = Object.assign(Object.create(Object.getPrototypeOf(acc[key])), acc[key]);
|
|
191
|
+
typeA = acc[key].constructor.name;
|
|
192
|
+
typeB = i.properties[key].constructor.name;
|
|
193
|
+
if (typeA !== typeB) {
|
|
194
|
+
this.warnings.push(`Merging properties with different types: ${typeA} ` +
|
|
195
|
+
`(from a previous variant) and ${typeB} ` +
|
|
196
|
+
`(from ${i.name})`);
|
|
197
|
+
}
|
|
198
|
+
if (acc[key].arrayDimension !== i.properties[key].arrayDimension) {
|
|
199
|
+
this.warnings.push(`Merging properties with different array dimensions: ` +
|
|
200
|
+
`${acc[key].arrayDimension} (from a previous variant) and ` +
|
|
201
|
+
`${i.properties[key].arrayDimension} (from ${i.name})`);
|
|
202
|
+
acc[key].arrayDimension = Math.max(acc[key].arrayDimension || 0, i.properties[key].arrayDimension || 0);
|
|
209
203
|
}
|
|
210
|
-
|
|
211
|
-
acc[key] =
|
|
204
|
+
if (acc[key].isNullable !== i.properties[key].isNullable) {
|
|
205
|
+
acc[key].isNullable = true;
|
|
206
|
+
}
|
|
207
|
+
if (acc[key].isOptional !== i.properties[key].isOptional) {
|
|
208
|
+
acc[key].isOptional = true;
|
|
209
|
+
}
|
|
210
|
+
if (i.properties[key].description) {
|
|
211
|
+
acc[key].description = i.properties[key].description;
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
214
|
+
else {
|
|
215
|
+
acc[key] = i.properties[key]; // New property, just add it
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return acc;
|
|
219
|
+
}, {});
|
|
220
|
+
for (const key in properties) {
|
|
221
|
+
if (!data.every((variant) => key in variant.properties)) {
|
|
222
|
+
// In Union, if a property is not present in all variants, it should be considered
|
|
223
|
+
// optional. Shallow-clone preserving prototype before mutating, to avoid corrupting
|
|
224
|
+
// the original cached AST node.
|
|
225
|
+
properties[key] = Object.assign(Object.create(Object.getPrototypeOf(properties[key])), properties[key]);
|
|
226
|
+
properties[key].isOptional = true;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return { properties };
|
|
217
230
|
}
|
|
218
231
|
/**
|
|
219
232
|
* Retrieves the name and associated transpilerable file information for a given Zod schema.
|
|
@@ -481,7 +494,7 @@ class Zod2Ast {
|
|
|
481
494
|
}
|
|
482
495
|
_getAliasAst(schema, item) {
|
|
483
496
|
var _a;
|
|
484
|
-
if (((_a = schema._zod2x) === null || _a === void 0 ? void 0 : _a.typeName) === undefined) {
|
|
497
|
+
if (((_a = schema._zod2x) === null || _a === void 0 ? void 0 : _a.typeName) === undefined || this.opt.skipBasicTypes === true) {
|
|
485
498
|
return item;
|
|
486
499
|
}
|
|
487
500
|
const { name, parentFile, parentNamespace, aliasOf } = this._getNames(schema);
|
|
@@ -99,7 +99,9 @@ function Layer(opt) {
|
|
|
99
99
|
layer: opt,
|
|
100
100
|
typeName: name,
|
|
101
101
|
// Generics associated to parent, but related to current type.
|
|
102
|
-
genericTypes: metadata.
|
|
102
|
+
genericTypes: metadata.isGenericChild === false
|
|
103
|
+
? metadata.genericTypes
|
|
104
|
+
: undefined,
|
|
103
105
|
isGenericChild: true,
|
|
104
106
|
};
|
|
105
107
|
}
|
|
@@ -44,5 +44,6 @@ export declare class Zod2XModel {
|
|
|
44
44
|
transpile(target: Target<"Zod2Cpp17">, opt?: TargetOpt<"Zod2Cpp17">, astOpt?: AstOpt): string;
|
|
45
45
|
transpile(target: Target<"Zod2Ts">, opt?: TargetOpt<"Zod2Ts">, astOpt?: AstOpt): string;
|
|
46
46
|
transpile(target: Target<"Zod2Py">, opt?: TargetOpt<"Zod2Py">, astOpt?: AstOpt): string;
|
|
47
|
+
transpile(target: Target<"Zod2Go">, opt?: TargetOpt<"Zod2Go">, astOpt?: AstOpt): string;
|
|
47
48
|
}
|
|
48
49
|
export {};
|
|
@@ -28,8 +28,15 @@ export declare class Zod2Cpp extends Zod2X<IZod2CppOpt> {
|
|
|
28
28
|
type?: "union" | "alias";
|
|
29
29
|
isInternal?: boolean;
|
|
30
30
|
templates?: string;
|
|
31
|
+
templateDefinition?: string;
|
|
32
|
+
templateList?: string;
|
|
31
33
|
}): void;
|
|
32
34
|
protected getGenericTemplatesTranslation(data: ASTNode): string | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Emits an alias/extension declaration early for layered references.
|
|
37
|
+
* It keeps concrete template translations and falls back to declared templates (e.g. <T>)
|
|
38
|
+
* for aliases of generic templates.
|
|
39
|
+
*/
|
|
33
40
|
protected checkExtendedTypeInclusion(data: ASTNode, type?: "union" | "alias"): boolean;
|
|
34
41
|
protected runAfter(): void;
|
|
35
42
|
protected getDateType: () => string;
|
|
@@ -60,8 +67,14 @@ export declare class Zod2Cpp extends Zod2X<IZod2CppOpt> {
|
|
|
60
67
|
protected getMapType(keyType: string, valueType: string): string;
|
|
61
68
|
protected getRecordType(keyType: string, valueType: string): string;
|
|
62
69
|
protected _getOptional(type: string): string;
|
|
63
|
-
protected _addExtendedTypeSerializer(typeName: string, parentNamespace: string
|
|
64
|
-
|
|
70
|
+
protected _addExtendedTypeSerializer(typeName: string, parentNamespace: string, opt?: {
|
|
71
|
+
templateDefinition?: string;
|
|
72
|
+
templateList?: string;
|
|
73
|
+
}): void;
|
|
74
|
+
protected _addExtendedTypeDeserializer(typeName: string, parentNamespace: string, opt?: {
|
|
75
|
+
templateDefinition?: string;
|
|
76
|
+
templateList?: string;
|
|
77
|
+
}): void;
|
|
65
78
|
protected transpileAliasedType(data: ASTAliasedTypes): void;
|
|
66
79
|
/** Ex:
|
|
67
80
|
* enum class EnumA: int {
|
|
@@ -100,6 +100,9 @@ class Zod2Cpp extends core_1.Zod2X {
|
|
|
100
100
|
? aliasOf
|
|
101
101
|
: this.getTypeFromExternalNamespace(parentNamespace, aliasOf);
|
|
102
102
|
const templates = (_a = opt === null || opt === void 0 ? void 0 : opt.templates) !== null && _a !== void 0 ? _a : "";
|
|
103
|
+
if (opt === null || opt === void 0 ? void 0 : opt.templateDefinition) {
|
|
104
|
+
this.push0(opt.templateDefinition);
|
|
105
|
+
}
|
|
103
106
|
if ((opt === null || opt === void 0 ? void 0 : opt.type) === "union" || (opt === null || opt === void 0 ? void 0 : opt.type) === "alias") {
|
|
104
107
|
this.push0(`using ${name} = ${extendedType}${templates};\n`);
|
|
105
108
|
}
|
|
@@ -112,8 +115,14 @@ class Zod2Cpp extends core_1.Zod2X {
|
|
|
112
115
|
}
|
|
113
116
|
}
|
|
114
117
|
if ((opt === null || opt === void 0 ? void 0 : opt.type) !== "alias" && parentNamespace !== undefined) {
|
|
115
|
-
this._addExtendedTypeSerializer(name, parentNamespace
|
|
116
|
-
|
|
118
|
+
this._addExtendedTypeSerializer(name, parentNamespace, {
|
|
119
|
+
templateDefinition: opt === null || opt === void 0 ? void 0 : opt.templateDefinition,
|
|
120
|
+
templateList: opt === null || opt === void 0 ? void 0 : opt.templateList,
|
|
121
|
+
});
|
|
122
|
+
this._addExtendedTypeDeserializer(name, parentNamespace, {
|
|
123
|
+
templateDefinition: opt === null || opt === void 0 ? void 0 : opt.templateDefinition,
|
|
124
|
+
templateList: opt === null || opt === void 0 ? void 0 : opt.templateList,
|
|
125
|
+
});
|
|
117
126
|
}
|
|
118
127
|
}
|
|
119
128
|
getGenericTemplatesTranslation(data) {
|
|
@@ -134,12 +143,28 @@ class Zod2Cpp extends core_1.Zod2X {
|
|
|
134
143
|
">");
|
|
135
144
|
}
|
|
136
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Emits an alias/extension declaration early for layered references.
|
|
148
|
+
* It keeps concrete template translations and falls back to declared templates (e.g. <T>)
|
|
149
|
+
* for aliases of generic templates.
|
|
150
|
+
*/
|
|
137
151
|
checkExtendedTypeInclusion(data, type) {
|
|
152
|
+
const templatesMeta = data instanceof core_1.ASTObject && data.templates.size > 0
|
|
153
|
+
? this._getTemplates(data.templates)
|
|
154
|
+
: undefined;
|
|
155
|
+
const translatedTemplates = this.getGenericTemplatesTranslation(data);
|
|
156
|
+
const templates = translatedTemplates || (templatesMeta === null || templatesMeta === void 0 ? void 0 : templatesMeta.templateList);
|
|
157
|
+
const templateList = translatedTemplates ? undefined : templatesMeta === null || templatesMeta === void 0 ? void 0 : templatesMeta.templateList;
|
|
158
|
+
const templateDefinition = translatedTemplates
|
|
159
|
+
? undefined
|
|
160
|
+
: templatesMeta === null || templatesMeta === void 0 ? void 0 : templatesMeta.templateDefinition;
|
|
138
161
|
if (this.isExternalTypeImport(data)) {
|
|
139
162
|
if (data.aliasOf) {
|
|
140
163
|
this.addExtendedType(data.name, data.parentNamespace, data.aliasOf, {
|
|
141
164
|
type,
|
|
142
|
-
templates
|
|
165
|
+
templates,
|
|
166
|
+
templateDefinition,
|
|
167
|
+
templateList,
|
|
143
168
|
});
|
|
144
169
|
this.addExternalTypeImport(data);
|
|
145
170
|
}
|
|
@@ -149,7 +174,9 @@ class Zod2Cpp extends core_1.Zod2X {
|
|
|
149
174
|
this.addExtendedType(data.name, data.parentNamespace, data.aliasOf, {
|
|
150
175
|
type,
|
|
151
176
|
isInternal: true,
|
|
152
|
-
templates
|
|
177
|
+
templates,
|
|
178
|
+
templateDefinition,
|
|
179
|
+
templateList,
|
|
153
180
|
});
|
|
154
181
|
return true;
|
|
155
182
|
}
|
|
@@ -205,13 +232,23 @@ class Zod2Cpp extends core_1.Zod2X {
|
|
|
205
232
|
this.imports.add(this.lib.optional);
|
|
206
233
|
return `boost::optional<${type}>`;
|
|
207
234
|
}
|
|
208
|
-
_addExtendedTypeSerializer(typeName, parentNamespace) {
|
|
209
|
-
|
|
235
|
+
_addExtendedTypeSerializer(typeName, parentNamespace, opt) {
|
|
236
|
+
var _a;
|
|
237
|
+
if (opt === null || opt === void 0 ? void 0 : opt.templateDefinition) {
|
|
238
|
+
this._push0(this.serializers, opt.templateDefinition);
|
|
239
|
+
}
|
|
240
|
+
const typeNameWithTemplates = `${typeName}${(_a = opt === null || opt === void 0 ? void 0 : opt.templateList) !== null && _a !== void 0 ? _a : ""}`;
|
|
241
|
+
this._push0(this.serializers, `inline void to_json(${nlohmann_1.NLOHMANN}& j, const ${typeNameWithTemplates}& x) {`);
|
|
210
242
|
this._push1(this.serializers, `${parentNamespace}::to_json(j, x);`);
|
|
211
243
|
this._push0(this.serializers, "}\n");
|
|
212
244
|
}
|
|
213
|
-
_addExtendedTypeDeserializer(typeName, parentNamespace) {
|
|
214
|
-
|
|
245
|
+
_addExtendedTypeDeserializer(typeName, parentNamespace, opt) {
|
|
246
|
+
var _a;
|
|
247
|
+
if (opt === null || opt === void 0 ? void 0 : opt.templateDefinition) {
|
|
248
|
+
this._push0(this.serializers, opt.templateDefinition);
|
|
249
|
+
}
|
|
250
|
+
const typeNameWithTemplates = `${typeName}${(_a = opt === null || opt === void 0 ? void 0 : opt.templateList) !== null && _a !== void 0 ? _a : ""}`;
|
|
251
|
+
this._push0(this.serializers, `inline void from_json(const ${nlohmann_1.NLOHMANN}& j, ${typeNameWithTemplates}& x) {`);
|
|
215
252
|
this._push1(this.serializers, `${parentNamespace}::from_json(j, x);`);
|
|
216
253
|
this._push0(this.serializers, "}\n");
|
|
217
254
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getLibs = getLibs;
|
|
4
|
+
/**
|
|
5
|
+
* Go standard library and common package import strings.
|
|
6
|
+
*/
|
|
7
|
+
function getLibs() {
|
|
8
|
+
return {
|
|
9
|
+
timePackage: `"time"`,
|
|
10
|
+
jsonPackage: `"encoding/json"`,
|
|
11
|
+
fmtPackage: `"fmt"`,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { IZodToXOpt } from "../../core";
|
|
2
|
+
export interface IZod2GoOpt extends IZodToXOpt {
|
|
3
|
+
/**
|
|
4
|
+
* The Go package name for the generated file. Default: "models"
|
|
5
|
+
*/
|
|
6
|
+
packageName?: string;
|
|
7
|
+
/**
|
|
8
|
+
* By default (false), struct field names are exported (PascalCase) and JSON tags use the
|
|
9
|
+
* original property name. If set to true, original property names are preserved as-is.
|
|
10
|
+
*/
|
|
11
|
+
keepKeys?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Whether to emit `json:"fieldName"` struct tags. Default: true
|
|
14
|
+
*/
|
|
15
|
+
useJsonTags?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare const defaultOpts: IZod2GoOpt;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultOpts = void 0;
|
|
4
|
+
exports.defaultOpts = {
|
|
5
|
+
includeComments: true,
|
|
6
|
+
indent: 4,
|
|
7
|
+
useImports: true,
|
|
8
|
+
packageName: "models",
|
|
9
|
+
keepKeys: false,
|
|
10
|
+
useJsonTags: true,
|
|
11
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { ASTAliasedTypes, ASTEnum, ASTIntersection, ASTNode, ASTObject, ASTUnion, Zod2X } from "../../core";
|
|
2
|
+
import { IZod2GoOpt } from "./options";
|
|
3
|
+
/**
|
|
4
|
+
* Transpiler for Zod schemas to Go structs and types.
|
|
5
|
+
*/
|
|
6
|
+
export declare class Zod2Go extends Zod2X<IZod2GoOpt> {
|
|
7
|
+
protected readonly commentKey = "//";
|
|
8
|
+
protected lib: {
|
|
9
|
+
timePackage: string;
|
|
10
|
+
jsonPackage: string;
|
|
11
|
+
fmtPackage: string;
|
|
12
|
+
};
|
|
13
|
+
constructor(opt?: IZod2GoOpt);
|
|
14
|
+
protected runBefore(): void;
|
|
15
|
+
protected runAfter(): void;
|
|
16
|
+
/**
|
|
17
|
+
* Consolidates all collected imports into a proper Go import block.
|
|
18
|
+
* Single import → `import "pkg"`, multiple → `import (\n\t"pkg"\n)`.
|
|
19
|
+
*/
|
|
20
|
+
private _consolidateImports;
|
|
21
|
+
protected addImportFromFile(filename: string, namespace: string): string;
|
|
22
|
+
protected getTypeFromExternalNamespace(namespace: string, typeName: string): string;
|
|
23
|
+
protected addExtendedType(name: string, parentNamespace: string, aliasOf: string, opt?: {
|
|
24
|
+
type?: "union" | "alias";
|
|
25
|
+
isInternal?: boolean;
|
|
26
|
+
templates?: string;
|
|
27
|
+
declaredTemplates?: string;
|
|
28
|
+
}): void;
|
|
29
|
+
protected getGenericTemplatesTranslation(data: ASTNode): string | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Emits an alias/extension declaration early when a node references another layered type.
|
|
32
|
+
*/
|
|
33
|
+
protected checkExtendedTypeInclusion(data: ASTNode, type?: "union" | "alias"): boolean;
|
|
34
|
+
protected getStringType: () => string;
|
|
35
|
+
protected getBooleanType: () => string;
|
|
36
|
+
protected getAnyType: () => string;
|
|
37
|
+
protected getDateType: () => string;
|
|
38
|
+
protected getNumberType: (isInt: boolean, range: {
|
|
39
|
+
min?: number;
|
|
40
|
+
max?: number;
|
|
41
|
+
}) => string;
|
|
42
|
+
protected getLiteralStringType(value: string | number | boolean, parentEnumNameKey?: [string, string]): string | number;
|
|
43
|
+
/** Ex: []TypeA, [][]TypeA */
|
|
44
|
+
protected getArrayType(arrayType: string, arrayDeep: number): string;
|
|
45
|
+
/** Ex: map[TypeA]struct{} */
|
|
46
|
+
protected getSetType: (itemType: string) => string;
|
|
47
|
+
/** Ex: map[KeyType]ValueType */
|
|
48
|
+
protected getMapType: (keyType: string, valueType: string) => string;
|
|
49
|
+
/** Ex: map[KeyType]ValueType */
|
|
50
|
+
protected getRecordType: (keyType: string, valueType: string) => string;
|
|
51
|
+
/** Go has no native tuple; use []any */
|
|
52
|
+
protected getTupleType: (_itemsType: string[]) => string;
|
|
53
|
+
/** Go has no native union; use any */
|
|
54
|
+
protected getUnionType: (_itemsType: string[]) => string;
|
|
55
|
+
/** Handled entirely in transpileIntersection via struct embedding */
|
|
56
|
+
protected getIntersectionType: () => string;
|
|
57
|
+
protected transpileAliasedType(data: ASTAliasedTypes): void;
|
|
58
|
+
/**
|
|
59
|
+
* Emit a Go enum using a typed string or int alias + const block.
|
|
60
|
+
*
|
|
61
|
+
* All-string values:
|
|
62
|
+
* type EnumItem string
|
|
63
|
+
* const (
|
|
64
|
+
* EnumItemEnum1 EnumItem = "Enum1"
|
|
65
|
+
* )
|
|
66
|
+
*
|
|
67
|
+
* All-int values:
|
|
68
|
+
* type EnumItem int
|
|
69
|
+
* const (
|
|
70
|
+
* EnumItemNativeEnum1 EnumItem = 1
|
|
71
|
+
* )
|
|
72
|
+
*
|
|
73
|
+
* Mixed (int + string): untyped constants with warning comment.
|
|
74
|
+
*/
|
|
75
|
+
protected transpileEnum(data: ASTEnum): void;
|
|
76
|
+
/**
|
|
77
|
+
* Go union: emit `type Name any` with a comment listing possible types.
|
|
78
|
+
*
|
|
79
|
+
* For discriminated unions: emit a marker interface + marker stubs on each
|
|
80
|
+
* member type + an `UnmarshalXxx` helper that dispatches on the discriminant
|
|
81
|
+
* key using a `json.RawMessage` probe (uniform for string, bool, and number
|
|
82
|
+
* discriminant values).
|
|
83
|
+
*/
|
|
84
|
+
protected transpileUnion(data: ASTUnion): void;
|
|
85
|
+
/**
|
|
86
|
+
* Go intersection: struct embedding.
|
|
87
|
+
*
|
|
88
|
+
* type IntersectionItem struct {
|
|
89
|
+
* ObjectItem
|
|
90
|
+
* OtherObjectItem
|
|
91
|
+
* }
|
|
92
|
+
*/
|
|
93
|
+
protected transpileIntersection(data: ASTIntersection): void;
|
|
94
|
+
protected transpileStruct(data: ASTObject): void;
|
|
95
|
+
/** Render a Go struct body for an ASTObject. */
|
|
96
|
+
private _transpileStructBody;
|
|
97
|
+
/** Render a single struct field: `FieldName Type \`json:"key"\`` */
|
|
98
|
+
private _transpileMember;
|
|
99
|
+
private _buildJsonTag;
|
|
100
|
+
}
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Zod2Go = void 0;
|
|
7
|
+
const case_1 = __importDefault(require("case"));
|
|
8
|
+
const core_1 = require("../../core");
|
|
9
|
+
const number_limits_1 = require("../../utils/number_limits");
|
|
10
|
+
const libs_1 = require("./libs");
|
|
11
|
+
const options_1 = require("./options");
|
|
12
|
+
/**
|
|
13
|
+
* Transpiler for Zod schemas to Go structs and types.
|
|
14
|
+
*/
|
|
15
|
+
class Zod2Go extends core_1.Zod2X {
|
|
16
|
+
constructor(opt = {}) {
|
|
17
|
+
super(Object.assign(Object.assign({}, options_1.defaultOpts), opt));
|
|
18
|
+
this.commentKey = "//";
|
|
19
|
+
// ── Primitive type methods ──────────────────────────────────────────────
|
|
20
|
+
this.getStringType = () => "string";
|
|
21
|
+
this.getBooleanType = () => "bool";
|
|
22
|
+
this.getAnyType = () => "any";
|
|
23
|
+
this.getDateType = () => {
|
|
24
|
+
this.imports.add(this.lib.timePackage);
|
|
25
|
+
return "time.Time";
|
|
26
|
+
};
|
|
27
|
+
this.getNumberType = (isInt, range) => {
|
|
28
|
+
if (!isInt)
|
|
29
|
+
return "float64";
|
|
30
|
+
const min = range.min;
|
|
31
|
+
const max = range.max;
|
|
32
|
+
if (min !== undefined &&
|
|
33
|
+
max !== undefined &&
|
|
34
|
+
min >= number_limits_1.INT32_RANGES[0] &&
|
|
35
|
+
max <= number_limits_1.INT32_RANGES[1]) {
|
|
36
|
+
return "int32";
|
|
37
|
+
}
|
|
38
|
+
return "int64";
|
|
39
|
+
};
|
|
40
|
+
/** Ex: map[TypeA]struct{} */
|
|
41
|
+
this.getSetType = (itemType) => `map[${itemType}]struct{}`;
|
|
42
|
+
/** Ex: map[KeyType]ValueType */
|
|
43
|
+
this.getMapType = (keyType, valueType) => `map[${keyType}]${valueType}`;
|
|
44
|
+
/** Ex: map[KeyType]ValueType */
|
|
45
|
+
this.getRecordType = (keyType, valueType) => `map[${keyType}]${valueType}`;
|
|
46
|
+
/** Go has no native tuple; use []any */
|
|
47
|
+
this.getTupleType = (_itemsType) => "[]any";
|
|
48
|
+
/** Go has no native union; use any */
|
|
49
|
+
this.getUnionType = (_itemsType) => "any";
|
|
50
|
+
/** Handled entirely in transpileIntersection via struct embedding */
|
|
51
|
+
this.getIntersectionType = () => "";
|
|
52
|
+
this.lib = (0, libs_1.getLibs)();
|
|
53
|
+
}
|
|
54
|
+
runBefore() {
|
|
55
|
+
var _a;
|
|
56
|
+
this.preImports.add(`package ${(_a = this.opt.packageName) !== null && _a !== void 0 ? _a : "models"}`);
|
|
57
|
+
}
|
|
58
|
+
runAfter() {
|
|
59
|
+
this._consolidateImports();
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Consolidates all collected imports into a proper Go import block.
|
|
63
|
+
* Single import → `import "pkg"`, multiple → `import (\n\t"pkg"\n)`.
|
|
64
|
+
*/
|
|
65
|
+
_consolidateImports() {
|
|
66
|
+
if (this.imports.size === 0)
|
|
67
|
+
return;
|
|
68
|
+
const sorted = Array.from(this.imports).sort();
|
|
69
|
+
let block;
|
|
70
|
+
if (sorted.length === 1) {
|
|
71
|
+
block = `import ${sorted[0]}`;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
block = `import (\n${sorted.map((s) => `\t${s}`).join("\n")}\n)`;
|
|
75
|
+
}
|
|
76
|
+
// Replace the individual entries with the consolidated block
|
|
77
|
+
this.imports.clear();
|
|
78
|
+
this.imports.add(block);
|
|
79
|
+
}
|
|
80
|
+
addImportFromFile(filename, namespace) {
|
|
81
|
+
const base = filename.endsWith(".go") ? filename.slice(0, -3) : filename;
|
|
82
|
+
return `${namespace} "./${base}"`;
|
|
83
|
+
}
|
|
84
|
+
getTypeFromExternalNamespace(namespace, typeName) {
|
|
85
|
+
return `${namespace}.${typeName}`;
|
|
86
|
+
}
|
|
87
|
+
addExtendedType(name, parentNamespace, aliasOf, opt) {
|
|
88
|
+
var _a, _b;
|
|
89
|
+
const extendedType = (opt === null || opt === void 0 ? void 0 : opt.isInternal)
|
|
90
|
+
? aliasOf
|
|
91
|
+
: this.getTypeFromExternalNamespace(parentNamespace, aliasOf);
|
|
92
|
+
const templates = (_a = opt === null || opt === void 0 ? void 0 : opt.templates) !== null && _a !== void 0 ? _a : "";
|
|
93
|
+
const declaredTemplates = (_b = opt === null || opt === void 0 ? void 0 : opt.declaredTemplates) !== null && _b !== void 0 ? _b : "";
|
|
94
|
+
if ((opt === null || opt === void 0 ? void 0 : opt.type) === "alias" || (opt === null || opt === void 0 ? void 0 : opt.type) === "union") {
|
|
95
|
+
this.push0(`type ${name}${declaredTemplates} = ${extendedType}${templates}\n`);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// Struct embedding: type ChildName struct { ParentType }
|
|
99
|
+
this.push0(`type ${name}${declaredTemplates} struct {`);
|
|
100
|
+
this.push1(`${extendedType}${templates}`);
|
|
101
|
+
this.push0(`}\n`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
getGenericTemplatesTranslation(data) {
|
|
105
|
+
if ((data instanceof core_1.ASTObject || data instanceof core_1.ASTDefinition) &&
|
|
106
|
+
data.templatesTranslation.length > 0) {
|
|
107
|
+
return ("[" +
|
|
108
|
+
data.templatesTranslation
|
|
109
|
+
.map((t) => {
|
|
110
|
+
if (this.isExternalTypeImport(t)) {
|
|
111
|
+
this.addExternalTypeImport(t);
|
|
112
|
+
return this.getTypeFromExternalNamespace(t.parentNamespace, t.aliasOf);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
return t.aliasOf;
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
.join(", ") +
|
|
119
|
+
"]");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Emits an alias/extension declaration early when a node references another layered type.
|
|
124
|
+
*/
|
|
125
|
+
checkExtendedTypeInclusion(data, type) {
|
|
126
|
+
const isStruct = data instanceof core_1.ASTObject ||
|
|
127
|
+
(data instanceof core_1.ASTIntersection && data.newObject !== undefined);
|
|
128
|
+
const translatedTemplates = this.getGenericTemplatesTranslation(data);
|
|
129
|
+
const templates = translatedTemplates || undefined;
|
|
130
|
+
// For declared-template fallback on the declared (definition) side
|
|
131
|
+
const declaredTemplates = !translatedTemplates && data instanceof core_1.ASTObject && data.templates.size > 0
|
|
132
|
+
? `[${[...data.templates].map((t) => `${t} any`).join(", ")}]`
|
|
133
|
+
: undefined;
|
|
134
|
+
if (this.isExternalTypeImport(data)) {
|
|
135
|
+
if (data.aliasOf) {
|
|
136
|
+
this.addExtendedType(data.name, data.parentNamespace, data.aliasOf, {
|
|
137
|
+
type: isStruct ? undefined : type,
|
|
138
|
+
templates,
|
|
139
|
+
declaredTemplates,
|
|
140
|
+
});
|
|
141
|
+
this.addExternalTypeImport(data);
|
|
142
|
+
}
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
else if (data.aliasOf) {
|
|
146
|
+
this.addExtendedType(data.name, data.parentNamespace, data.aliasOf, {
|
|
147
|
+
type: isStruct ? undefined : type,
|
|
148
|
+
isInternal: true,
|
|
149
|
+
templates,
|
|
150
|
+
declaredTemplates,
|
|
151
|
+
});
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
getLiteralStringType(value, parentEnumNameKey) {
|
|
157
|
+
if (parentEnumNameKey) {
|
|
158
|
+
// Go constants cannot be used as types; use the parent enum type name
|
|
159
|
+
return parentEnumNameKey[0];
|
|
160
|
+
}
|
|
161
|
+
// Go has no literal types; return the underlying primitive type
|
|
162
|
+
if (typeof value === "boolean")
|
|
163
|
+
return "bool";
|
|
164
|
+
if (typeof value === "number")
|
|
165
|
+
return isNaN(value) ? "float64" : Number.isInteger(value) ? "int64" : "float64";
|
|
166
|
+
return "string";
|
|
167
|
+
}
|
|
168
|
+
// ── Composite type methods ──────────────────────────────────────────────
|
|
169
|
+
/** Ex: []TypeA, [][]TypeA */
|
|
170
|
+
getArrayType(arrayType, arrayDeep) {
|
|
171
|
+
let output = `[]${arrayType}`;
|
|
172
|
+
for (let i = 0; i < arrayDeep - 1; i++) {
|
|
173
|
+
output = `[]${output}`;
|
|
174
|
+
}
|
|
175
|
+
return output;
|
|
176
|
+
}
|
|
177
|
+
// ── Transpile methods ───────────────────────────────────────────────────
|
|
178
|
+
transpileAliasedType(data) {
|
|
179
|
+
if (this.checkExtendedTypeInclusion(data, "alias")) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
this.addComment(data.description);
|
|
183
|
+
let extendedType;
|
|
184
|
+
if (data instanceof core_1.ASTArray) {
|
|
185
|
+
extendedType = this.getAttributeType(data.item);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
extendedType = this.getAttributeType(data);
|
|
189
|
+
}
|
|
190
|
+
this.push0(`type ${data.name} = ${extendedType}\n`);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Emit a Go enum using a typed string or int alias + const block.
|
|
194
|
+
*
|
|
195
|
+
* All-string values:
|
|
196
|
+
* type EnumItem string
|
|
197
|
+
* const (
|
|
198
|
+
* EnumItemEnum1 EnumItem = "Enum1"
|
|
199
|
+
* )
|
|
200
|
+
*
|
|
201
|
+
* All-int values:
|
|
202
|
+
* type EnumItem int
|
|
203
|
+
* const (
|
|
204
|
+
* EnumItemNativeEnum1 EnumItem = 1
|
|
205
|
+
* )
|
|
206
|
+
*
|
|
207
|
+
* Mixed (int + string): untyped constants with warning comment.
|
|
208
|
+
*/
|
|
209
|
+
transpileEnum(data) {
|
|
210
|
+
if (this.checkExtendedTypeInclusion(data, "alias")) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
this.addComment(data.description);
|
|
214
|
+
const allStrings = data.values.every(([, v]) => typeof v === "string");
|
|
215
|
+
const allInts = data.values.every(([, v]) => typeof v === "number");
|
|
216
|
+
if (allStrings) {
|
|
217
|
+
this.push0(`type ${data.name} string\n`);
|
|
218
|
+
this.push0(`const (`);
|
|
219
|
+
data.values.forEach(([key, value]) => {
|
|
220
|
+
const constName = `${data.name}${case_1.default.pascal(key)}`;
|
|
221
|
+
this.push1(`${constName} ${data.name} = "${value}"`);
|
|
222
|
+
});
|
|
223
|
+
this.push0(`)\n`);
|
|
224
|
+
}
|
|
225
|
+
else if (allInts) {
|
|
226
|
+
this.push0(`type ${data.name} int\n`);
|
|
227
|
+
this.push0(`const (`);
|
|
228
|
+
data.values.forEach(([key, value]) => {
|
|
229
|
+
const constName = `${data.name}${case_1.default.pascal(key)}`;
|
|
230
|
+
this.push1(`${constName} ${data.name} = ${value}`);
|
|
231
|
+
});
|
|
232
|
+
this.push0(`)\n`);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
// Mixed types — Go cannot express this as a single typed enum.
|
|
236
|
+
// Declare as `any` so struct fields can reference the type name.
|
|
237
|
+
this.push0(`type ${data.name} = any\n`);
|
|
238
|
+
this.output.push(`// ${data.name}: mixed-type enum — no single Go base type available`);
|
|
239
|
+
this.push0(`const (`);
|
|
240
|
+
data.values.forEach(([key, value]) => {
|
|
241
|
+
const constName = `${data.name}${case_1.default.pascal(key)}`;
|
|
242
|
+
const v = typeof value === "string" ? `"${value}"` : `${value}`;
|
|
243
|
+
this.push1(`${constName} = ${v}`);
|
|
244
|
+
});
|
|
245
|
+
this.push0(`)\n`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Go union: emit `type Name any` with a comment listing possible types.
|
|
250
|
+
*
|
|
251
|
+
* For discriminated unions: emit a marker interface + marker stubs on each
|
|
252
|
+
* member type + an `UnmarshalXxx` helper that dispatches on the discriminant
|
|
253
|
+
* key using a `json.RawMessage` probe (uniform for string, bool, and number
|
|
254
|
+
* discriminant values).
|
|
255
|
+
*/
|
|
256
|
+
transpileUnion(data) {
|
|
257
|
+
if (this.checkExtendedTypeInclusion(data, "union")) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
this.addComment(data.description);
|
|
261
|
+
const optionNames = data.options.map(this.getAttributeType.bind(this));
|
|
262
|
+
if (data.discriminantKey) {
|
|
263
|
+
const methodName = `is${data.name}`;
|
|
264
|
+
this.push0(`// ${data.name} is a discriminated union on "${data.discriminantKey}".`);
|
|
265
|
+
this.push0(`// Possible types: ${optionNames.join(", ")}`);
|
|
266
|
+
this.push0(`type ${data.name} interface {`);
|
|
267
|
+
this.push1(`${methodName}()`);
|
|
268
|
+
this.push0(`}\n`);
|
|
269
|
+
// Marker stubs: one no-op method per member type so each satisfies the interface.
|
|
270
|
+
// For generic instantiations (e.g. "HttpSuccessfulResponse[SomeDtoResult]") we emit
|
|
271
|
+
// a generic receiver "func (t Base[T]) isXxx() {}" using the base type name only.
|
|
272
|
+
for (const name of optionNames) {
|
|
273
|
+
const bracketIdx = name.indexOf("[");
|
|
274
|
+
const receiver = bracketIdx !== -1 ? `${name.slice(0, bracketIdx)}[T]` : name;
|
|
275
|
+
this.push0(`func (t ${receiver}) ${methodName}() {}\n`);
|
|
276
|
+
}
|
|
277
|
+
// UnmarshalXxx helper — only when every member has a discriminant value in the AST.
|
|
278
|
+
const optionsData = data.options.map((opt, i) => {
|
|
279
|
+
var _a;
|
|
280
|
+
return ({
|
|
281
|
+
typeName: optionNames[i],
|
|
282
|
+
discriminantValue: (_a = opt.constraints) === null || _a === void 0 ? void 0 : _a.discriminantValue,
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
const allHaveDiscriminantValue = optionsData.every((o) => o.discriminantValue !== undefined);
|
|
286
|
+
if (allHaveDiscriminantValue) {
|
|
287
|
+
this.imports.add(this.lib.jsonPackage);
|
|
288
|
+
this.imports.add(this.lib.fmtPackage);
|
|
289
|
+
const probeField = data.discriminantKey.charAt(0).toUpperCase() + data.discriminantKey.slice(1);
|
|
290
|
+
this.push0(`// Unmarshal${data.name} deserializes JSON into the correct ${data.name} concrete type`);
|
|
291
|
+
this.push0(`// by probing the "${data.discriminantKey}" discriminant field.`);
|
|
292
|
+
this.push0(`func Unmarshal${data.name}(data []byte) (${data.name}, error) {`);
|
|
293
|
+
this.push1(`var probe struct {`);
|
|
294
|
+
this.push2(`${probeField} json.RawMessage \`json:"${data.discriminantKey}"\``);
|
|
295
|
+
this.push1(`}`);
|
|
296
|
+
this.push1(`if err := json.Unmarshal(data, &probe); err != nil {`);
|
|
297
|
+
this.push2(`return nil, err`);
|
|
298
|
+
this.push1(`}`);
|
|
299
|
+
this.push1(`switch string(probe.${probeField}) {`);
|
|
300
|
+
for (const opt of optionsData) {
|
|
301
|
+
const dv = opt.discriminantValue;
|
|
302
|
+
// Determine raw JSON representation of the case value.
|
|
303
|
+
// String literals appear in JSON with surrounding quotes ("Enum1" → "Enum1").
|
|
304
|
+
// Bool and number literals appear without quotes (true → true, 42 → 42).
|
|
305
|
+
const isStringLiteral = isNaN(Number(dv)) && dv !== "true" && dv !== "false";
|
|
306
|
+
const caseVal = isStringLiteral ? `\`"${dv}"\`` : `"${dv}"`;
|
|
307
|
+
this.push1(`case ${caseVal}:`);
|
|
308
|
+
this.push2(`var v ${opt.typeName}`);
|
|
309
|
+
this.push2(`if err := json.Unmarshal(data, &v); err != nil {`);
|
|
310
|
+
this.push3(`return nil, err`);
|
|
311
|
+
this.push2(`}`);
|
|
312
|
+
this.push2(`return v, nil`);
|
|
313
|
+
}
|
|
314
|
+
this.push1(`}`);
|
|
315
|
+
this.push1(`return nil, fmt.Errorf("failed to deserialize ${data.name}: unknown discriminator %s", string(probe.${probeField}))`);
|
|
316
|
+
this.push0(`}\n`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
this.push0(`// ${data.name} is a union of: ${optionNames.join(", ")}`);
|
|
321
|
+
this.push0(`type ${data.name} = any\n`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Go intersection: struct embedding.
|
|
326
|
+
*
|
|
327
|
+
* type IntersectionItem struct {
|
|
328
|
+
* ObjectItem
|
|
329
|
+
* OtherObjectItem
|
|
330
|
+
* }
|
|
331
|
+
*/
|
|
332
|
+
transpileIntersection(data) {
|
|
333
|
+
if (this.checkExtendedTypeInclusion(data)) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
this.addComment(data.description);
|
|
337
|
+
if (data.newObject) {
|
|
338
|
+
// Flatten the merged object into a plain struct
|
|
339
|
+
this._transpileStructBody(data.newObject);
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
// Embed both sides
|
|
343
|
+
const leftType = this.getAttributeType(data.left);
|
|
344
|
+
const rightType = this.getAttributeType(data.right);
|
|
345
|
+
this.push0(`type ${data.name} struct {`);
|
|
346
|
+
this.push1(leftType);
|
|
347
|
+
this.push1(rightType);
|
|
348
|
+
this.push0(`}\n`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
transpileStruct(data) {
|
|
352
|
+
if (this.checkExtendedTypeInclusion(data)) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
this.addComment(data.description);
|
|
356
|
+
this._transpileStructBody(data);
|
|
357
|
+
}
|
|
358
|
+
/** Render a Go struct body for an ASTObject. */
|
|
359
|
+
_transpileStructBody(data) {
|
|
360
|
+
const templateParams = data.templates.size > 0
|
|
361
|
+
? `[${[...data.templates].map((t) => `${t} any`).join(", ")}]`
|
|
362
|
+
: "";
|
|
363
|
+
this.push0(`type ${data.name}${templateParams} struct {`);
|
|
364
|
+
const hasProperties = Object.keys(data.properties).length > 0;
|
|
365
|
+
if (!hasProperties) {
|
|
366
|
+
this.push0(`}\n`);
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
for (const [key, value] of Object.entries(data.properties)) {
|
|
370
|
+
const fieldName = this.opt.keepKeys === true ? key : case_1.default.pascal(key);
|
|
371
|
+
this._transpileMember(fieldName, key, value);
|
|
372
|
+
}
|
|
373
|
+
this.push0(`}\n`);
|
|
374
|
+
}
|
|
375
|
+
/** Render a single struct field: `FieldName Type \`json:"key"\`` */
|
|
376
|
+
_transpileMember(fieldName, originalKey, memberNode) {
|
|
377
|
+
const isOptionalOrNullable = memberNode.isOptional || memberNode.isNullable;
|
|
378
|
+
let varType = this.getAttributeType(memberNode);
|
|
379
|
+
// Optional/nullable fields use pointer types
|
|
380
|
+
if (isOptionalOrNullable) {
|
|
381
|
+
// Avoid double-pointer for types already expressed as pointers/interfaces
|
|
382
|
+
if (!varType.startsWith("*") && varType !== "any") {
|
|
383
|
+
varType = `*${varType}`;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (memberNode.description && !memberNode.name && !this.isTranspilerable(memberNode)) {
|
|
387
|
+
this.addComment(memberNode.description, `\n${this.indent[1]}`);
|
|
388
|
+
}
|
|
389
|
+
const tag = this._buildJsonTag(originalKey, isOptionalOrNullable !== null && isOptionalOrNullable !== void 0 ? isOptionalOrNullable : false);
|
|
390
|
+
this.push1(`${fieldName} ${varType}${tag}`);
|
|
391
|
+
}
|
|
392
|
+
_buildJsonTag(originalKey, omitempty) {
|
|
393
|
+
if (this.opt.useJsonTags === false)
|
|
394
|
+
return "";
|
|
395
|
+
const flags = omitempty ? `,omitempty` : "";
|
|
396
|
+
return ` \`json:"${originalKey}${flags}"\``;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
exports.Zod2Go = Zod2Go;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Zod2Py = exports.Zod2Cpp17 = exports.Zod2Cpp = exports.Zod2Ts = void 0;
|
|
3
|
+
exports.Zod2Go = exports.Zod2Py = exports.Zod2Cpp17 = exports.Zod2Cpp = exports.Zod2Ts = void 0;
|
|
4
4
|
var runner_1 = require("./typescript/runner");
|
|
5
5
|
Object.defineProperty(exports, "Zod2Ts", { enumerable: true, get: function () { return runner_1.Zod2Ts; } });
|
|
6
6
|
var runner_2 = require("./cpp/runner");
|
|
@@ -8,3 +8,5 @@ Object.defineProperty(exports, "Zod2Cpp", { enumerable: true, get: function () {
|
|
|
8
8
|
Object.defineProperty(exports, "Zod2Cpp17", { enumerable: true, get: function () { return runner_2.Zod2Cpp17; } });
|
|
9
9
|
var runner_3 = require("./python/runner");
|
|
10
10
|
Object.defineProperty(exports, "Zod2Py", { enumerable: true, get: function () { return runner_3.Zod2Py; } });
|
|
11
|
+
var runner_4 = require("./go/runner");
|
|
12
|
+
Object.defineProperty(exports, "Zod2Go", { enumerable: true, get: function () { return runner_4.Zod2Go; } });
|
|
@@ -23,6 +23,7 @@ export declare class Zod2Py extends Zod2X<IZod2PyOpt> {
|
|
|
23
23
|
};
|
|
24
24
|
private baseSchemaAdded;
|
|
25
25
|
private typeVars;
|
|
26
|
+
private pendingTypeVars;
|
|
26
27
|
constructor(opt?: IZod2PyOpt);
|
|
27
28
|
protected runAfter(): void;
|
|
28
29
|
protected runBefore(): void;
|
|
@@ -31,6 +32,7 @@ export declare class Zod2Py extends Zod2X<IZod2PyOpt> {
|
|
|
31
32
|
* This is the base class for all Pydantic models with shared configuration.
|
|
32
33
|
*/
|
|
33
34
|
private _addBaseSchema;
|
|
35
|
+
private _flushPendingTypeVars;
|
|
34
36
|
/**
|
|
35
37
|
* Declares TypeVars that haven't been declared yet.
|
|
36
38
|
* Adds them right before their first usage.
|
|
@@ -51,6 +53,11 @@ export declare class Zod2Py extends Zod2X<IZod2PyOpt> {
|
|
|
51
53
|
isClass?: boolean;
|
|
52
54
|
}): void;
|
|
53
55
|
protected getGenericTemplatesTranslation(data: ASTNode): string | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Emits an alias/extension declaration early for layered references.
|
|
58
|
+
* It keeps concrete template translations and falls back to declared templates (e.g. [T])
|
|
59
|
+
* for aliases of generic templates.
|
|
60
|
+
*/
|
|
54
61
|
protected checkExtendedTypeInclusion(data: ASTNode, type?: "alias" | "union" | "d-union"): boolean;
|
|
55
62
|
protected getAnyType: () => string;
|
|
56
63
|
protected getBooleanType: () => string;
|
|
@@ -14,6 +14,7 @@ class Zod2Py extends core_1.Zod2X {
|
|
|
14
14
|
this.commentKey = "#";
|
|
15
15
|
this.baseSchemaAdded = false;
|
|
16
16
|
this.typeVars = new Set();
|
|
17
|
+
this.pendingTypeVars = new Set();
|
|
17
18
|
this.getAnyType = () => {
|
|
18
19
|
this.imports.add(this.lib.anyType);
|
|
19
20
|
return "Any";
|
|
@@ -51,6 +52,9 @@ class Zod2Py extends core_1.Zod2X {
|
|
|
51
52
|
this.lib = (0, libs_1.getLibs)();
|
|
52
53
|
}
|
|
53
54
|
runAfter() {
|
|
55
|
+
if (!this.baseSchemaAdded) {
|
|
56
|
+
this._flushPendingTypeVars(true);
|
|
57
|
+
}
|
|
54
58
|
this._consolidateImports();
|
|
55
59
|
}
|
|
56
60
|
runBefore() { }
|
|
@@ -76,6 +80,22 @@ class Zod2Py extends core_1.Zod2X {
|
|
|
76
80
|
this.push2("use_enum_values=True");
|
|
77
81
|
this.push1(")");
|
|
78
82
|
this.push0("");
|
|
83
|
+
this._flushPendingTypeVars(false);
|
|
84
|
+
}
|
|
85
|
+
_flushPendingTypeVars(prepend) {
|
|
86
|
+
if (this.pendingTypeVars.size === 0)
|
|
87
|
+
return;
|
|
88
|
+
const pending = Array.from(this.pendingTypeVars);
|
|
89
|
+
const lines = pending.map((typeVar) => `${typeVar} = TypeVar('${typeVar}')`);
|
|
90
|
+
if (prepend) {
|
|
91
|
+
this.output = [...lines, "", ...this.output];
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
lines.forEach((line) => this.push0(line));
|
|
95
|
+
this.push0("");
|
|
96
|
+
}
|
|
97
|
+
pending.forEach((typeVar) => this.typeVars.add(typeVar));
|
|
98
|
+
this.pendingTypeVars.clear();
|
|
79
99
|
}
|
|
80
100
|
/**
|
|
81
101
|
* Declares TypeVars that haven't been declared yet.
|
|
@@ -83,10 +103,14 @@ class Zod2Py extends core_1.Zod2X {
|
|
|
83
103
|
* Ex: T = TypeVar('T')
|
|
84
104
|
*/
|
|
85
105
|
_declareNewTypeVars(templates) {
|
|
86
|
-
const newTypeVars = Array.from(templates).filter((t) => !this.typeVars.has(t));
|
|
106
|
+
const newTypeVars = Array.from(templates).filter((t) => !this.typeVars.has(t) && !this.pendingTypeVars.has(t));
|
|
87
107
|
if (newTypeVars.length === 0)
|
|
88
108
|
return;
|
|
89
109
|
this.imports.add(this.lib.typeVarType);
|
|
110
|
+
if (!this.baseSchemaAdded) {
|
|
111
|
+
newTypeVars.forEach((typeVar) => this.pendingTypeVars.add(typeVar));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
90
114
|
newTypeVars.forEach((typeVar) => {
|
|
91
115
|
this.push0(`${typeVar} = TypeVar('${typeVar}')`);
|
|
92
116
|
this.typeVars.add(typeVar);
|
|
@@ -183,15 +207,28 @@ class Zod2Py extends core_1.Zod2X {
|
|
|
183
207
|
"]");
|
|
184
208
|
}
|
|
185
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Emits an alias/extension declaration early for layered references.
|
|
212
|
+
* It keeps concrete template translations and falls back to declared templates (e.g. [T])
|
|
213
|
+
* for aliases of generic templates.
|
|
214
|
+
*/
|
|
186
215
|
checkExtendedTypeInclusion(data, type) {
|
|
187
216
|
// Determine if the aliased type is a class (ASTObject or ASTIntersection with newObject)
|
|
188
217
|
const isClass = data instanceof core_1.ASTObject ||
|
|
189
218
|
(data instanceof core_1.ASTIntersection && data.newObject !== undefined);
|
|
219
|
+
const declaredTemplatesFallback = data instanceof core_1.ASTObject && data.templates.size > 0 && this.isExternalTypeImport(data)
|
|
220
|
+
? `[${[...data.templates].join(", ")}]`
|
|
221
|
+
: undefined;
|
|
222
|
+
const translatedTemplates = this.getGenericTemplatesTranslation(data);
|
|
223
|
+
const templates = translatedTemplates || declaredTemplatesFallback;
|
|
224
|
+
if (!translatedTemplates && data instanceof core_1.ASTObject && data.templates.size > 0) {
|
|
225
|
+
this._declareNewTypeVars(data.templates);
|
|
226
|
+
}
|
|
190
227
|
if (this.isExternalTypeImport(data)) {
|
|
191
228
|
if (data.aliasOf) {
|
|
192
229
|
this.addExtendedType(data.name, data.parentNamespace, data.aliasOf, {
|
|
193
230
|
type,
|
|
194
|
-
templates
|
|
231
|
+
templates,
|
|
195
232
|
isClass,
|
|
196
233
|
});
|
|
197
234
|
this.addExternalTypeImport(data);
|
|
@@ -202,7 +239,7 @@ class Zod2Py extends core_1.Zod2X {
|
|
|
202
239
|
this.addExtendedType(data.name, data.parentNamespace, data.aliasOf, {
|
|
203
240
|
type,
|
|
204
241
|
isInternal: true,
|
|
205
|
-
templates
|
|
242
|
+
templates,
|
|
206
243
|
isClass,
|
|
207
244
|
});
|
|
208
245
|
return true;
|
|
@@ -11,8 +11,14 @@ export declare class Zod2Ts extends Zod2X<IZod2TsOpt> {
|
|
|
11
11
|
type?: "union" | "d-union" | "alias";
|
|
12
12
|
isInternal?: boolean;
|
|
13
13
|
templates?: string;
|
|
14
|
+
declaredTemplates?: string;
|
|
14
15
|
}): void;
|
|
15
16
|
protected getGenericTemplatesTranslation(data: ASTNode): string | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Emits an alias/extension declaration early when a node references another layered type.
|
|
19
|
+
* It preserves concrete template translations and falls back to declared templates (e.g. <T>)
|
|
20
|
+
* for aliases of generic templates.
|
|
21
|
+
*/
|
|
16
22
|
protected checkExtendedTypeInclusion(data: ASTNode, type?: "alias" | "union" | "d-union"): boolean;
|
|
17
23
|
protected getAnyType: () => string;
|
|
18
24
|
protected getBooleanType: () => string;
|
|
@@ -37,28 +37,29 @@ class Zod2Ts extends core_1.Zod2X {
|
|
|
37
37
|
return `${namespace}.${typeName}`;
|
|
38
38
|
}
|
|
39
39
|
addExtendedType(name, parentNamespace, aliasOf, opt) {
|
|
40
|
-
var _a;
|
|
40
|
+
var _a, _b;
|
|
41
41
|
const extendedType = (opt === null || opt === void 0 ? void 0 : opt.isInternal)
|
|
42
42
|
? aliasOf
|
|
43
43
|
: this.getTypeFromExternalNamespace(parentNamespace, aliasOf);
|
|
44
44
|
const templates = (_a = opt === null || opt === void 0 ? void 0 : opt.templates) !== null && _a !== void 0 ? _a : "";
|
|
45
|
+
const declaredName = `${name}${(_b = opt === null || opt === void 0 ? void 0 : opt.declaredTemplates) !== null && _b !== void 0 ? _b : ""}`;
|
|
45
46
|
if ((opt === null || opt === void 0 ? void 0 : opt.type) === "alias") {
|
|
46
|
-
this.push0(`export type ${
|
|
47
|
+
this.push0(`export type ${declaredName} = ${extendedType}${templates};\n`);
|
|
47
48
|
}
|
|
48
49
|
else if (this.opt.outType === "class") {
|
|
49
50
|
if ((opt === null || opt === void 0 ? void 0 : opt.type) === "d-union") {
|
|
50
|
-
this.push0(`export type ${
|
|
51
|
+
this.push0(`export type ${declaredName} = ${extendedType}${templates};\n`);
|
|
51
52
|
}
|
|
52
53
|
else {
|
|
53
|
-
this.push0(`export class ${
|
|
54
|
+
this.push0(`export class ${declaredName} extends ${extendedType}${templates} {}\n`);
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
else {
|
|
57
58
|
if ((opt === null || opt === void 0 ? void 0 : opt.type) === "union" || (opt === null || opt === void 0 ? void 0 : opt.type) === "d-union") {
|
|
58
|
-
this.push0(`export type ${
|
|
59
|
+
this.push0(`export type ${declaredName} = ${extendedType}${templates};\n`);
|
|
59
60
|
}
|
|
60
61
|
else {
|
|
61
|
-
this.push0(`export interface ${
|
|
62
|
+
this.push0(`export interface ${declaredName} extends ${extendedType}${templates} {}\n`);
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
65
|
}
|
|
@@ -80,12 +81,24 @@ class Zod2Ts extends core_1.Zod2X {
|
|
|
80
81
|
">");
|
|
81
82
|
}
|
|
82
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Emits an alias/extension declaration early when a node references another layered type.
|
|
86
|
+
* It preserves concrete template translations and falls back to declared templates (e.g. <T>)
|
|
87
|
+
* for aliases of generic templates.
|
|
88
|
+
*/
|
|
83
89
|
checkExtendedTypeInclusion(data, type) {
|
|
90
|
+
const declaredTemplatesFallback = data instanceof core_1.ASTObject && data.templates.size > 0
|
|
91
|
+
? `<${[...data.templates].join(", ")}>`
|
|
92
|
+
: undefined;
|
|
93
|
+
const translatedTemplates = this.getGenericTemplatesTranslation(data);
|
|
94
|
+
const templates = translatedTemplates || declaredTemplatesFallback;
|
|
95
|
+
const declaredTemplates = translatedTemplates ? undefined : declaredTemplatesFallback;
|
|
84
96
|
if (this.isExternalTypeImport(data)) {
|
|
85
97
|
if (data.aliasOf) {
|
|
86
98
|
this.addExtendedType(data.name, data.parentNamespace, data.aliasOf, {
|
|
87
99
|
type,
|
|
88
|
-
templates
|
|
100
|
+
templates,
|
|
101
|
+
declaredTemplates,
|
|
89
102
|
});
|
|
90
103
|
this.addExternalTypeImport(data);
|
|
91
104
|
}
|
|
@@ -95,7 +108,8 @@ class Zod2Ts extends core_1.Zod2X {
|
|
|
95
108
|
this.addExtendedType(data.name, data.parentNamespace, data.aliasOf, {
|
|
96
109
|
type,
|
|
97
110
|
isInternal: true,
|
|
98
|
-
templates
|
|
111
|
+
templates,
|
|
112
|
+
declaredTemplates,
|
|
99
113
|
});
|
|
100
114
|
return true;
|
|
101
115
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zod-to-x",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Multi language types generation from Zod schemas.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"files": [
|
|
@@ -11,10 +11,12 @@
|
|
|
11
11
|
"build": "npm run clear_dist && tsc && tsc-alias",
|
|
12
12
|
"build:debug": "npm run clear_dist && tsc --project tsconfig.dev.json && tsc-alias",
|
|
13
13
|
"format:check": "prettier --check .",
|
|
14
|
+
"swap": "npm install && npm run build",
|
|
14
15
|
"test": "find ./test -name \"err-*\" -delete && vitest --run",
|
|
15
16
|
"test:cpp": "bash ./test/test_zod2cpp/test_cpp.sh",
|
|
16
17
|
"test:py": "bash ./test/test_zod2py/test_py.sh",
|
|
17
|
-
"test:
|
|
18
|
+
"test:go": "bash ./test/test_zod2go/test_go.sh",
|
|
19
|
+
"test:all": "npm run test:cpp && npm run test:py && npm run test:go",
|
|
18
20
|
"ts-run": "ts-node -r tsconfig-paths/register",
|
|
19
21
|
"prepare": "husky"
|
|
20
22
|
},
|
|
@@ -29,7 +31,7 @@
|
|
|
29
31
|
"type",
|
|
30
32
|
"generator",
|
|
31
33
|
"zod-to-cpp",
|
|
32
|
-
"
|
|
34
|
+
"zod-to-c++",
|
|
33
35
|
"zod-to-ts",
|
|
34
36
|
"zod-to-typescript",
|
|
35
37
|
"typescript",
|
|
@@ -59,7 +61,7 @@
|
|
|
59
61
|
"husky": "^9.1.7",
|
|
60
62
|
"lint-staged": "^15.2.11",
|
|
61
63
|
"prettier": "^3.4.2",
|
|
62
|
-
"protobufjs": "7.
|
|
64
|
+
"protobufjs": "7.5.5",
|
|
63
65
|
"ts-node": "^10.9.2",
|
|
64
66
|
"tsc-alias": "^1.8.10",
|
|
65
67
|
"tsconfig-paths": "4.2.0",
|