zod-to-x 2.2.1-dev.0 → 2.4.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 +32 -8
- package/dist/core/ast_node.d.ts +1 -1
- package/dist/core/ast_node.js +47 -34
- package/dist/layered-modeling/layer.js +3 -1
- package/dist/layered-modeling/model.d.ts +2 -0
- package/dist/layered-modeling/model.js +1 -1
- package/dist/lib/zod_helpers.js +8 -0
- package/dist/transpilers/cpp/runner.d.ts +15 -2
- package/dist/transpilers/cpp/runner.js +45 -8
- package/dist/transpilers/dart/libs.d.ts +6 -0
- package/dist/transpilers/dart/libs.js +11 -0
- package/dist/transpilers/dart/options.d.ts +21 -0
- package/dist/transpilers/dart/options.js +10 -0
- package/dist/transpilers/dart/runner.d.ts +149 -0
- package/dist/transpilers/dart/runner.js +624 -0
- 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 +2 -0
- package/dist/transpilers/index.js +5 -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 +7 -4
package/README.md
CHANGED
|
@@ -28,29 +28,37 @@
|
|
|
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">
|
|
32
|
+
<img src="https://img.shields.io/badge/Dart-0175C2?style=for-the-badge&logo=dart&logoColor=white" alt="Dart">
|
|
31
33
|
<img src="https://img.shields.io/badge/Protobuf-4285F4?style=for-the-badge&logo=google&logoColor=white" alt="Protobuf">
|
|
32
34
|
</p>
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
## Table of contents
|
|
37
|
-
- [
|
|
39
|
+
- [Table of contents](#table-of-contents)
|
|
40
|
+
- [Why Use `@zod-to-x`?](#why-use-zod-to-x)
|
|
38
41
|
- [Installation](#installation)
|
|
42
|
+
- [1) Install `@zod-to-x` and `@zod(*)` dependency.](#1-install-zod-to-x-and-zod-dependency)
|
|
43
|
+
- [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
44
|
- [Quick start](#quick-start)
|
|
40
45
|
- [Intersections and Unions](#intersections-and-unions)
|
|
41
46
|
- [Expected outputs](#expected-outputs)
|
|
42
47
|
- [Tips for discriminated unions](#tips-for-discriminated-unions)
|
|
43
48
|
- [Layered modeling](#layered-modeling)
|
|
44
49
|
- [Usage example](#usage-example)
|
|
45
|
-
- [Custom
|
|
50
|
+
- [Custom Layers](#custom-layers)
|
|
46
51
|
- [Generic types](#generic-types)
|
|
47
52
|
- [Currently supported output languages](#currently-supported-output-languages)
|
|
48
|
-
- [
|
|
49
|
-
- [
|
|
50
|
-
- [
|
|
53
|
+
- [0) ASTNode](#0-astnode)
|
|
54
|
+
- [1) Typescript](#1-typescript)
|
|
55
|
+
- [2) Python](#2-python)
|
|
56
|
+
- [3) C++](#3-c)
|
|
57
|
+
- [4) Go](#4-go)
|
|
58
|
+
- [5) Dart](#5-dart)
|
|
51
59
|
- [Additional utils](#additional-utils)
|
|
52
|
-
- [
|
|
53
|
-
- [Mapping of supported Zod Types by
|
|
60
|
+
- [1) `zod2ProtoV3`](#1-zod2protov3)
|
|
61
|
+
- [Mapping of supported Zod Types by Langauge](#mapping-of-supported-zod-types-by-langauge)
|
|
54
62
|
- [Considerations](#considerations)
|
|
55
63
|
|
|
56
64
|
|
|
@@ -62,7 +70,7 @@ Managing data consistency across multiple layers and languages is a common chall
|
|
|
62
70
|
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
71
|
|
|
64
72
|
2. **Multi-Language Compatibility**
|
|
65
|
-
Generate data models for TypeScript, Python (Pydantic), C++, and Protobuf V3
|
|
73
|
+
Generate data models for TypeScript, Python (Pydantic), C++, Go, and Protobuf V3. No more manually rewriting models for different platforms.
|
|
66
74
|
|
|
67
75
|
3. **Enhanced Productivity**
|
|
68
76
|
Automate the transpilation of data models to save time, reduce errors, and let your team focus on business logic instead of boilerplate code.
|
|
@@ -628,6 +636,22 @@ Common options:
|
|
|
628
636
|
- **keepKeys**: Specifies whether property names should follow the C++ naming convention (false) or remain as originally defined (true). The default is `false`.
|
|
629
637
|
- [Examples](https://github.com/rroumenov/zod-to-x/blob/main/test/test_zod2cpp)
|
|
630
638
|
|
|
639
|
+
### 4) Go
|
|
640
|
+
Generates idiomatic Go structs with `encoding/json` struct tags. Requires Go 1.18+ for generic type support.
|
|
641
|
+
- Options:
|
|
642
|
+
- **packageName**: The Go package name for the generated file. Defaults to `"models"`.
|
|
643
|
+
- **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`.
|
|
644
|
+
- **useJsonTags**: Whether to emit `json:"fieldName"` struct tags. Defaults to `true`.
|
|
645
|
+
- [Examples](https://github.com/rroumenov/zod-to-x/blob/main/test/test_zod2go)
|
|
646
|
+
|
|
647
|
+
### 5) Dart
|
|
648
|
+
`json_annotation` + `json_serializable` are used for JSON serialization/deserialization. The `.g.dart` part file is generated externally by `dart run build_runner build`.
|
|
649
|
+
- Options:
|
|
650
|
+
- **keepKeys**: Specifies whether property names should follow the Dart naming convention (false) or remain as originally defined (true). The default is `false`.
|
|
651
|
+
- **useJsonSerialization**: When true (default), emits `@JsonSerializable()`, `@JsonEnum`, `@JsonKey` annotations and `fromJson`/`toJson` stubs. Requires `package:json_annotation`.
|
|
652
|
+
- **partFile**: Filename (without extension) for the `part '${partFile}.g.dart'` directive. Automatically set when using layered modeling. If omitted, no `part` directive is emitted.
|
|
653
|
+
- [Examples](https://github.com/rroumenov/zod-to-x/blob/main/test/test_zod2dart)
|
|
654
|
+
|
|
631
655
|
|
|
632
656
|
|
|
633
657
|
## Additional utils
|
package/dist/core/ast_node.d.ts
CHANGED
|
@@ -82,7 +82,7 @@ export declare class Zod2Ast {
|
|
|
82
82
|
private _intersectAstNodes;
|
|
83
83
|
/**
|
|
84
84
|
* Merges multiple AST definitions into a single AST object containing combined properties.
|
|
85
|
-
* - Equal properties
|
|
85
|
+
* - Equal properties must have the same type and array dimension.
|
|
86
86
|
* - If a property is optional in one definition and required in another, it will be considered
|
|
87
87
|
* optional in the merged object.
|
|
88
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.
|
|
@@ -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,7 @@ 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;
|
|
48
|
+
transpile(target: Target<"Zod2Dart">, opt?: TargetOpt<"Zod2Dart">, astOpt?: AstOpt): string;
|
|
47
49
|
}
|
|
48
50
|
export {};
|
|
@@ -59,7 +59,7 @@ class Zod2XModel {
|
|
|
59
59
|
return this.astNodes;
|
|
60
60
|
}
|
|
61
61
|
transpile(target, opt = {}, astOpt) {
|
|
62
|
-
const transpilerInstance = new target(Object.assign(Object.assign({}, opt), { namespace: this.getLayerMetadata().namespace }));
|
|
62
|
+
const transpilerInstance = new target(Object.assign(Object.assign({}, opt), { namespace: this.getLayerMetadata().namespace, partFile: this.getLayerMetadata().file }));
|
|
63
63
|
return transpilerInstance.transpile(this.getAstNode(astOpt));
|
|
64
64
|
}
|
|
65
65
|
}
|
package/dist/lib/zod_helpers.js
CHANGED
|
@@ -214,6 +214,11 @@ function createGenericType(name) {
|
|
|
214
214
|
function useGenericType(genObj, childrens, skipLazy) {
|
|
215
215
|
const builder = () => {
|
|
216
216
|
let extended = genObj;
|
|
217
|
+
// Zod 4's extend() doesn't copy meta() (unlike Zod 3 which spread _def).
|
|
218
|
+
// Capture it once so it can be restored after each extend call.
|
|
219
|
+
// The description (e.g. "GenericUserEntity") is used by transpilers to
|
|
220
|
+
// identify the base generic class for cross-layer type aliases.
|
|
221
|
+
const genObjMeta = genObj.meta();
|
|
217
222
|
for (const [key, property] of Object.entries(genObj.shape)) {
|
|
218
223
|
if (ZodHelpers.isZod2XGeneric(property)) {
|
|
219
224
|
const childType = childrens[key];
|
|
@@ -222,6 +227,9 @@ function useGenericType(genObj, childrens, skipLazy) {
|
|
|
222
227
|
}
|
|
223
228
|
const zod2xMeta = structuredClone(extended._zod2x);
|
|
224
229
|
extended = extended.extend({ [key]: childType });
|
|
230
|
+
if (genObjMeta && Object.keys(genObjMeta).length > 0) {
|
|
231
|
+
extended = extended.meta(Object.assign({}, genObjMeta));
|
|
232
|
+
}
|
|
225
233
|
extended._zod2x = zod2xMeta;
|
|
226
234
|
extended._zod2x.isGenericChild = false;
|
|
227
235
|
if (!Array.isArray(extended._zod2x.genericTypes)) {
|
|
@@ -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,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getLibs = getLibs;
|
|
4
|
+
/**
|
|
5
|
+
* Dart package imports for json_annotation / json_serializable.
|
|
6
|
+
*/
|
|
7
|
+
function getLibs() {
|
|
8
|
+
return {
|
|
9
|
+
jsonAnnotation: "import 'package:json_annotation/json_annotation.dart';",
|
|
10
|
+
};
|
|
11
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { IZodToXOpt } from "../../core";
|
|
2
|
+
export interface IZod2DartOpt extends IZodToXOpt {
|
|
3
|
+
/**
|
|
4
|
+
* By default (false), class property names are converted to camelCase following Dart
|
|
5
|
+
* conventions. If set to true, the original property names are preserved as-is.
|
|
6
|
+
*/
|
|
7
|
+
keepKeys?: boolean;
|
|
8
|
+
/**
|
|
9
|
+
* When true (default), emits `@JsonSerializable()` annotations, `@JsonEnum`, `@JsonKey`,
|
|
10
|
+
* and `fromJson`/`toJson` delegation stubs. Requires `package:json_annotation`.
|
|
11
|
+
* The `.g.dart` part file is generated externally by `dart run build_runner build`.
|
|
12
|
+
*/
|
|
13
|
+
useJsonSerialization?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Filename (without extension) used for the `part '${partFile}.g.dart';` directive.
|
|
16
|
+
* When using layered modeling, this is automatically set to the layer's `file` metadata.
|
|
17
|
+
* If omitted, no `part` directive is emitted.
|
|
18
|
+
*/
|
|
19
|
+
partFile?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare const defaultOpts: IZod2DartOpt;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { ASTAliasedTypes, ASTEnum, ASTIntersection, ASTNode, ASTNodes, ASTObject, ASTUnion, Zod2X } from "../../core";
|
|
2
|
+
import { IZod2DartOpt } from "./options";
|
|
3
|
+
/**
|
|
4
|
+
* Transpiler for Zod schemas to Dart classes using package:json_annotation.
|
|
5
|
+
*
|
|
6
|
+
* The `.g.dart` part file is NOT generated by this transpiler — it must be produced
|
|
7
|
+
* externally via `dart run build_runner build`.
|
|
8
|
+
*/
|
|
9
|
+
export declare class Zod2Dart extends Zod2X<IZod2DartOpt> {
|
|
10
|
+
protected readonly commentKey = "//";
|
|
11
|
+
protected lib: {
|
|
12
|
+
jsonAnnotation: string;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Maps a local struct/class name to the discriminated union names it participates in.
|
|
16
|
+
* Populated by a pre-scan of the AST before the normal transpile pass.
|
|
17
|
+
*/
|
|
18
|
+
private _unionMembers;
|
|
19
|
+
/**
|
|
20
|
+
* Names of locally-defined discriminated union abstract classes.
|
|
21
|
+
* Used to emit @JsonKey(fromJson/toJson) when such a type appears as a field.
|
|
22
|
+
*/
|
|
23
|
+
private _discriminatedUnionTypes;
|
|
24
|
+
/**
|
|
25
|
+
* Names of typedef-emitted types (alias, union typedef, struct extends).
|
|
26
|
+
* When these appear as field types in @JsonSerializable classes, we emit
|
|
27
|
+
* @JsonKey(fromJson: TypeName.fromJson) to prevent json_serializable from
|
|
28
|
+
* following the typedef chain and referencing transitive imports.
|
|
29
|
+
*/
|
|
30
|
+
private _typedefNames;
|
|
31
|
+
/**
|
|
32
|
+
* Maps a disc union typedef name (e.g. "NewDiscriminatedUnionItem") to the
|
|
33
|
+
* fromJson function name of the underlying entity-level union
|
|
34
|
+
* (e.g. "discriminatedUnionItemFromJson").
|
|
35
|
+
* Populated during pre-scan when an ASTUnion node has aliasOf set.
|
|
36
|
+
*/
|
|
37
|
+
private _discriminatedUnionDelegateFromJson;
|
|
38
|
+
/**
|
|
39
|
+
* Unified set of type names needing custom @JsonKey annotations.
|
|
40
|
+
* - Discriminated union types: fromJson = <camelName>FromJson (local function)
|
|
41
|
+
* - Typedef types: fromJson = _<camelName>FromJson (standalone wrapper)
|
|
42
|
+
* - TypedefList: list fromJson/toJson helpers
|
|
43
|
+
* _toJson helpers are emitted in runAfter() for all entries.
|
|
44
|
+
*/
|
|
45
|
+
private _usedCustomFromJsonTypes;
|
|
46
|
+
/**
|
|
47
|
+
* Maps a type name (e.g. "GenericUserEntity") to its full Dart type with
|
|
48
|
+
* template args (e.g. "GenericUserEntity<SomeDtoResult>"). Used in runAfter()
|
|
49
|
+
* to generate helpers with correct generic return types and factory calls.
|
|
50
|
+
*/
|
|
51
|
+
private _usedCustomFromJsonFullTypes;
|
|
52
|
+
/**
|
|
53
|
+
* Maps a typedef name to its full extended type (the right-hand side of the
|
|
54
|
+
* typedef). E.g. "AdminUserEntity" → "GenericUserEntity<AdminUserMetadata>".
|
|
55
|
+
* Populated in addExtendedType() when the extended type has generic args.
|
|
56
|
+
* Used in runAfter() to generate 2-arg fromJson calls for generic classes.
|
|
57
|
+
*/
|
|
58
|
+
private _typedefToExtendedType;
|
|
59
|
+
constructor(opt?: IZod2DartOpt);
|
|
60
|
+
/**
|
|
61
|
+
* Overrides transpile() to pre-scan discriminated union memberships before the normal pass.
|
|
62
|
+
* This allows transpileStruct() to emit `implements UnionName` on member classes.
|
|
63
|
+
*/
|
|
64
|
+
transpile(transpilerQueue: ASTNodes): string;
|
|
65
|
+
protected runBefore(): void;
|
|
66
|
+
protected runAfter(): void;
|
|
67
|
+
protected addImportFromFile(filename: string, _namespace: string): string;
|
|
68
|
+
protected getTypeFromExternalNamespace(_namespace: string, typeName: string): string;
|
|
69
|
+
protected getGenericTemplatesTranslation(data: ASTNode): string | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* Emits an alias/extension declaration for cross-layer references.
|
|
72
|
+
*/
|
|
73
|
+
protected checkExtendedTypeInclusion(data: ASTNode, type?: "union" | "alias"): boolean;
|
|
74
|
+
protected addExtendedType(name: string, parentNamespace: string, aliasOf: string, opt?: {
|
|
75
|
+
type?: "union" | "alias";
|
|
76
|
+
isInternal?: boolean;
|
|
77
|
+
templates?: string;
|
|
78
|
+
declaredTemplates?: string;
|
|
79
|
+
}): void;
|
|
80
|
+
protected getStringType: () => string;
|
|
81
|
+
protected getBooleanType: () => string;
|
|
82
|
+
protected getAnyType: () => string;
|
|
83
|
+
protected getDateType: () => string;
|
|
84
|
+
protected getNumberType(isInt: boolean, _range: {
|
|
85
|
+
min?: number;
|
|
86
|
+
max?: number;
|
|
87
|
+
}): string;
|
|
88
|
+
/**
|
|
89
|
+
* Returns the Dart type for a literal value.
|
|
90
|
+
* Dart has no structural literal types, so we return the underlying primitive type.
|
|
91
|
+
* When a parent enum is provided, returns the enum type name (not a member expression),
|
|
92
|
+
* since Dart enum members cannot be used as types.
|
|
93
|
+
*/
|
|
94
|
+
protected getLiteralStringType(value: string | number | boolean, parentEnumNameKey?: [string, string]): string | number;
|
|
95
|
+
/** Ex: List<List<T>> for 2D array */
|
|
96
|
+
protected getArrayType(arrayType: string, arrayDeep: number): string;
|
|
97
|
+
/** Ex: Set<T> */
|
|
98
|
+
protected getSetType: (itemType: string) => string;
|
|
99
|
+
/** Ex: Map<K, V> */
|
|
100
|
+
protected getMapType: (keyType: string, valueType: string) => string;
|
|
101
|
+
/** Ex: Map<String, V> (record key is always String) */
|
|
102
|
+
protected getRecordType: (keyType: string, valueType: string) => string;
|
|
103
|
+
/** Ex: (T1, T2, T3) — Dart 3 positional record type */
|
|
104
|
+
protected getTupleType: (itemsType: string[]) => string;
|
|
105
|
+
/** Dart has no native union type; use Object */
|
|
106
|
+
protected getUnionType: (_itemsType: string[]) => string;
|
|
107
|
+
/** Dart has no native intersection type; use Object */
|
|
108
|
+
protected getIntersectionType: (_itemsType: string[]) => string;
|
|
109
|
+
protected transpileAliasedType(data: ASTAliasedTypes): void;
|
|
110
|
+
/**
|
|
111
|
+
* Emits a Dart enhanced enum with a typed `value` field and `@JsonEnum` annotation.
|
|
112
|
+
*
|
|
113
|
+
* All-string values:
|
|
114
|
+
* @JsonEnum(valueField: 'value')
|
|
115
|
+
* enum EnumItem {
|
|
116
|
+
* enum1('Enum1'),
|
|
117
|
+
* enum2('Enum2');
|
|
118
|
+
*
|
|
119
|
+
* const EnumItem(this.value);
|
|
120
|
+
* final String value;
|
|
121
|
+
* }
|
|
122
|
+
*
|
|
123
|
+
* Mixed (int + string): typedef alias with comment.
|
|
124
|
+
*/
|
|
125
|
+
protected transpileEnum(data: ASTEnum): void;
|
|
126
|
+
protected transpileStruct(data: ASTObject): void;
|
|
127
|
+
/**
|
|
128
|
+
* Dart intersection:
|
|
129
|
+
* - If the AST produced a merged `newObject`, emit it as a flat class.
|
|
130
|
+
* - Otherwise emit a typedef alias to Object with a comment.
|
|
131
|
+
*/
|
|
132
|
+
protected transpileIntersection(data: ASTIntersection): void;
|
|
133
|
+
/**
|
|
134
|
+
* Dart union:
|
|
135
|
+
* - Non-discriminated: typedef alias to Object with a comment listing member types.
|
|
136
|
+
* - Discriminated: abstract base class + factory unmarshal function using Dart 3 switch.
|
|
137
|
+
*/
|
|
138
|
+
protected transpileUnion(data: ASTUnion): void;
|
|
139
|
+
/** Renders the full body of a Dart class for an ASTObject. */
|
|
140
|
+
private _transpileStructBody;
|
|
141
|
+
/** Renders a single class field declaration with optional `@JsonKey` annotation. */
|
|
142
|
+
private _transpileMember;
|
|
143
|
+
/**
|
|
144
|
+
* Emits `fromJson` factory and `toJson` method delegation stubs.
|
|
145
|
+
* For generic classes, extra `fromJsonT` / `toJsonT` function parameters are added
|
|
146
|
+
* as required by `json_serializable` with `genericArgumentFactories: true`.
|
|
147
|
+
*/
|
|
148
|
+
private _emitFromToJson;
|
|
149
|
+
}
|