zod-to-x 2.0.2-dev.2 → 2.1.0-dev.2
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 +103 -2
- package/dist/converters/protobuf_v3/options.d.ts +5 -0
- package/dist/converters/protobuf_v3/options.js +1 -0
- package/dist/converters/protobuf_v3/runner.d.ts +3 -4
- package/dist/converters/protobuf_v3/runner.js +13 -3
- package/dist/core/ast-types/ast_common.d.ts +8 -3
- package/dist/core/ast-types/ast_common.js +10 -3
- package/dist/core/ast-types/ast_complex.d.ts +2 -0
- package/dist/core/ast-types/ast_complex.js +2 -0
- package/dist/core/ast-types/ast_simple.d.ts +3 -3
- package/dist/core/ast_node.js +57 -10
- package/dist/core/transpiler.d.ts +10 -5
- package/dist/core/transpiler.js +16 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -1
- package/dist/layered-modeling/layer.js +15 -0
- package/dist/lib/zod_ext.d.ts +21 -4
- package/dist/lib/zod_helpers.d.ts +22 -4
- package/dist/lib/zod_helpers.js +52 -4
- package/dist/transpilers/cpp/libs.d.ts +0 -3
- package/dist/transpilers/cpp/libs.js +0 -4
- package/dist/transpilers/cpp/nlohmann.d.ts +1 -0
- package/dist/transpilers/cpp/nlohmann.js +5 -3
- package/dist/transpilers/cpp/runner.d.ts +23 -14
- package/dist/transpilers/cpp/runner.js +122 -46
- package/dist/transpilers/typescript/runner.d.ts +3 -1
- package/dist/transpilers/typescript/runner.js +34 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -29,9 +29,10 @@
|
|
|
29
29
|
- [Intersections and Unions](#intersections-and-unions)
|
|
30
30
|
- [Expected outputs](#expected-outputs)
|
|
31
31
|
- [Tips for discriminated unions](#tips-for-discriminated-unions)
|
|
32
|
-
- [Layered modeling](#layered-modeling)
|
|
32
|
+
- [Layered modeling](#layered-modeling)
|
|
33
33
|
- [Usage example](#usage-example)
|
|
34
34
|
- [Custom layers](#custom-layers)
|
|
35
|
+
- [Generic types](#generic-types) <sup>*(new)*</sup>
|
|
35
36
|
- [Currently supported output languages](#currently-supported-output-languages)
|
|
36
37
|
- [Typescript](#1-typescript)
|
|
37
38
|
- [C++](#2-c)
|
|
@@ -483,6 +484,106 @@ class MyEntityModels extends Zod2XModel {
|
|
|
483
484
|
}
|
|
484
485
|
```
|
|
485
486
|
|
|
487
|
+
### Generic types
|
|
488
|
+
Since `v1.5.0`, basic generic types can be generated using layered modeling. A few key points should be kept in mind when working with them:
|
|
489
|
+
1) Generic types can only be created for `ZodObject` properties. The new `createGenericType` method should be used to indicate the generic property.
|
|
490
|
+
```ts
|
|
491
|
+
@Infrastructure({
|
|
492
|
+
namespace: "GENERICS_INFRA",
|
|
493
|
+
file: "layered_generics.infra",
|
|
494
|
+
})
|
|
495
|
+
class GenericsInfrastructure extends Zod2XModel {
|
|
496
|
+
readonly HttpSuccessfulResponse = z.object({
|
|
497
|
+
success: z.literal(true),
|
|
498
|
+
data: createGenericType("T"),
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
readonly HttpUnsuccessfulResponse = z.object({
|
|
502
|
+
success: z.literal(false),
|
|
503
|
+
message: z.string(),
|
|
504
|
+
details: z.record(z.any()).optional(),
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
// Output:
|
|
508
|
+
// export interface HttpSuccessfulResponse<T> {
|
|
509
|
+
// success: true;
|
|
510
|
+
// data: T;
|
|
511
|
+
// }
|
|
512
|
+
//
|
|
513
|
+
// export interface HttpUnsuccessfulResponse {
|
|
514
|
+
// success: false;
|
|
515
|
+
// message: string;
|
|
516
|
+
// details?: Record<string, any>;
|
|
517
|
+
// }
|
|
518
|
+
```
|
|
519
|
+
2) To create a type based on a generic type definition, the `useGenericType` method should be used to indicate the parent generic type and the value that the generic will take, which should be a previously defined type.
|
|
520
|
+
```ts
|
|
521
|
+
// inside GenericsInfrastructure class
|
|
522
|
+
readonly SomeDtoResult = z.object({
|
|
523
|
+
id: z.string(),
|
|
524
|
+
name: z.string(),
|
|
525
|
+
age: z.number().int().nonnegative(),
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
readonly ResponseItem = useGenericType(this.HttpSuccessfulResponse, {
|
|
529
|
+
data: this.SomeDtoResult, // Indicate the generic property and the value it takes
|
|
530
|
+
// ... other generic properties, if exist.
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
// Output:
|
|
534
|
+
// export interface SomeDtoResult {
|
|
535
|
+
// id: string;
|
|
536
|
+
// name: string;
|
|
537
|
+
// age: number;
|
|
538
|
+
// }
|
|
539
|
+
//
|
|
540
|
+
// export interface ResponseItem extends HttpSuccessfulResponse<SomeDtoResult> {}
|
|
541
|
+
|
|
542
|
+
```
|
|
543
|
+
3) New types based on generics, due to how TypeScript and Decorators work, should be used with Zod's `z.lazy()` operator. This is **required** to ensure metadata existence. It is applied to the used type, **except when dealing with `ZodIntersect`, `ZodUnion` or `ZodDiscriminatedUnion`**, where it should be applied to these methods instead of the type. Additionally, they support only inline generics usage:
|
|
544
|
+
```ts
|
|
545
|
+
// inside GenericsInfrastructure class
|
|
546
|
+
readonly ObjectWithGeneric = z.object({
|
|
547
|
+
// Property with reference to a generic based type: requires lazy operator
|
|
548
|
+
directGenericUse: z.lazy(() => this.ResponseItem),
|
|
549
|
+
|
|
550
|
+
// Property with inline generic usage: `useGenericType` is lazy by default
|
|
551
|
+
indirectGenericUse: useGenericType(this.HttpSuccessfulResponse, {
|
|
552
|
+
data: this.SomeDtoResult
|
|
553
|
+
}),
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// Case of ZodUnion, ZodDiscriminatedUnion or ZodIntersection.
|
|
557
|
+
readonly DiscriminantDataRetrieve = z.lazy(() => // Lazy the zod type itself
|
|
558
|
+
z.discriminatedUnion(
|
|
559
|
+
"success",
|
|
560
|
+
[
|
|
561
|
+
// Inline generic usage
|
|
562
|
+
useGenericType(
|
|
563
|
+
this.HttpSuccessfulResponse,
|
|
564
|
+
{ data: this.SomeDtoResult },
|
|
565
|
+
true // Disable default lazy wrapper
|
|
566
|
+
),
|
|
567
|
+
|
|
568
|
+
// Non generic types: normal references
|
|
569
|
+
this.HttpUnsuccessfulResponse,
|
|
570
|
+
]
|
|
571
|
+
)
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
// Output
|
|
575
|
+
// export interface ObjectWithGeneric {
|
|
576
|
+
// directGenericUse: ResponseItem;
|
|
577
|
+
// indirectGenericUse: HttpSuccessfulResponse<SomeDtoResult>;
|
|
578
|
+
// }
|
|
579
|
+
//
|
|
580
|
+
// export type DiscriminantDataRetrieve =
|
|
581
|
+
// | HttpSuccessfulResponse<SomeDtoResult>
|
|
582
|
+
// | HttpUnsuccessfulResponse;
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
Complete definition examples can be found [here](https://github.com/rroumenov/zod-to-x/blob/main/test/common/layered_generics.ts).
|
|
586
|
+
|
|
486
587
|
## Currently supported output languages
|
|
487
588
|
Common options:
|
|
488
589
|
- **header**: Text to add as a comment at the beginning of the output.
|
|
@@ -597,7 +698,7 @@ class UserDtos extends Zod2XModel {
|
|
|
597
698
|
createUserUseCaseResultDtoV2 = this.createUserUseCaseResultDto;
|
|
598
699
|
|
|
599
700
|
// OK, but avoid it - Redeclaration of an alias. It will wait until
|
|
600
|
-
// "createUserUseCaseResultDto" is aliased and then will
|
|
701
|
+
// "createUserUseCaseResultDto" is aliased and then will become an alias
|
|
601
702
|
// of "createUserUseCaseResultDto"
|
|
602
703
|
createUserUseCaseResultDtoV3 = z.lazy(() => this.createUserUseCaseResultDto),
|
|
603
704
|
}
|
|
@@ -15,5 +15,10 @@ export interface IZod2ProtoV3Opt extends IZodToXOpt {
|
|
|
15
15
|
* output will be more compact. Default is false.
|
|
16
16
|
*/
|
|
17
17
|
encodeDoubleAsInt?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* ProtoV3 fields are optional by default, but this setting makes the "optional" keyword
|
|
20
|
+
* explicit improving compatibility with ProtoV2. Default is false.
|
|
21
|
+
*/
|
|
22
|
+
useExplicitOptional?: boolean;
|
|
18
23
|
}
|
|
19
24
|
export declare const defaultOpts: IZod2ProtoV3Opt;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { ZodDiscriminatedUnion, ZodUnion } from "zod";
|
|
2
1
|
import { IZod2AstOpt } from "../../core";
|
|
3
|
-
import { ZodObject } from "../../lib/zod_helpers";
|
|
2
|
+
import { ZodObject, ZodDiscriminatedUnion, ZodUnion } from "../../lib/zod_helpers";
|
|
4
3
|
import { IZod2ProtoV3Opt } from "./options";
|
|
5
4
|
/**
|
|
6
5
|
* Converts a Zod schema into a Protocol Buffers v3 definition.
|
|
@@ -17,5 +16,5 @@ import { IZod2ProtoV3Opt } from "./options";
|
|
|
17
16
|
* definition.
|
|
18
17
|
* @returns The Protocol Buffers v3 definition as a string.
|
|
19
18
|
*/
|
|
20
|
-
export declare function zod2ProtoV3(schema: ZodObject<any> | ZodDiscriminatedUnion
|
|
21
|
-
opt?: Pick<IZod2AstOpt, "strict"> & Pick<IZod2ProtoV3Opt, "packageName" | "keepKeys" | "header" | "indent" | "includeComments" | "encodeDoubleAsInt">): string;
|
|
19
|
+
export declare function zod2ProtoV3(schema: ZodObject<any> | ZodDiscriminatedUnion | ZodUnion<any>, // TODO: fix any to force only ZodObjects
|
|
20
|
+
opt?: Pick<IZod2AstOpt, "strict"> & Pick<IZod2ProtoV3Opt, "packageName" | "keepKeys" | "header" | "indent" | "includeComments" | "encodeDoubleAsInt" | "useExplicitOptional">): string;
|
|
@@ -26,11 +26,11 @@ const allowedKeyTypes = [
|
|
|
26
26
|
class Zod2ProtoV3 extends core_1.Zod2X {
|
|
27
27
|
constructor(opt = {}) {
|
|
28
28
|
super(Object.assign(Object.assign({}, options_1.defaultOpts), opt));
|
|
29
|
+
this.commentKey = "//";
|
|
29
30
|
this.getUnionType = () => {
|
|
30
31
|
/** Covered by "transpileUnion" method */
|
|
31
32
|
return "";
|
|
32
33
|
};
|
|
33
|
-
this.getComment = (data, indent = "") => `${indent}// ${data}`;
|
|
34
34
|
this.getBooleanType = () => "bool";
|
|
35
35
|
this.getStringType = () => "string";
|
|
36
36
|
this.getNumberType = (isInt, range) => {
|
|
@@ -101,6 +101,10 @@ class Zod2ProtoV3 extends core_1.Zod2X {
|
|
|
101
101
|
// Zod2ProtoV3 does not support layered modeling.
|
|
102
102
|
return "";
|
|
103
103
|
}
|
|
104
|
+
getGenericTemplatesTranslation(data) {
|
|
105
|
+
// Zod2ProtoV3 does not support layered modeling nor generics.
|
|
106
|
+
return "";
|
|
107
|
+
}
|
|
104
108
|
addExtendedType(name, parentNamespace, aliasOf) {
|
|
105
109
|
// Zod2ProtoV3 does not support layered modeling.
|
|
106
110
|
return;
|
|
@@ -160,11 +164,17 @@ class Zod2ProtoV3 extends core_1.Zod2X {
|
|
|
160
164
|
this.addComment(data.description);
|
|
161
165
|
this.push0(`message ${data.name} {`);
|
|
162
166
|
Object.entries(data.properties).forEach(([key, value], index) => {
|
|
163
|
-
if (
|
|
167
|
+
if (this.opt.includeComments &&
|
|
168
|
+
value.description &&
|
|
169
|
+
!value.name &&
|
|
170
|
+
!this.isTranspilerable(value)) {
|
|
164
171
|
// Avoid duplicated descriptions for transpiled items.
|
|
165
172
|
this.addComment(value.description, `\n${this.indent[1]}`);
|
|
166
173
|
}
|
|
167
|
-
|
|
174
|
+
const fieldType = value.isOptional && this.opt.useExplicitOptional
|
|
175
|
+
? `optional ${this.getAttributeType(value)}`
|
|
176
|
+
: this.getAttributeType(value);
|
|
177
|
+
this.push1(`${fieldType} ${this._adaptField(key)} = ${index + 1};`);
|
|
168
178
|
});
|
|
169
179
|
this.push0("}\n");
|
|
170
180
|
}
|
|
@@ -32,14 +32,19 @@ export type ASTAliasedTypes = Simple.ASTString | Simple.ASTNumber | Simple.ASTBo
|
|
|
32
32
|
/**
|
|
33
33
|
* Represents a usage of any existing ASTNode. Used to reduce node size.
|
|
34
34
|
*/
|
|
35
|
-
export declare class
|
|
35
|
+
export declare class ASTDefinition extends ASTCommon {
|
|
36
36
|
name: string;
|
|
37
37
|
instanceType: string;
|
|
38
38
|
constraints?: Record<string, any>;
|
|
39
|
-
|
|
39
|
+
templatesTranslation: Pick<ASTCommon, "parentFile" | "parentNamespace" | "aliasOf">[];
|
|
40
|
+
constructor(data: ASTDefinition & ASTCommon);
|
|
40
41
|
}
|
|
41
|
-
export type ASTType = ASTNode |
|
|
42
|
+
export type ASTType = ASTNode | ASTDefinition;
|
|
42
43
|
export type ASTNodes = {
|
|
43
44
|
nodes: Map<string, ASTNode>;
|
|
44
45
|
warnings: string[];
|
|
45
46
|
};
|
|
47
|
+
export declare class ASTGenericType {
|
|
48
|
+
name: string;
|
|
49
|
+
constructor(name: string);
|
|
50
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.ASTGenericType = exports.ASTDefinition = exports.ASTCommon = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Shared properties for all AST node types.
|
|
6
6
|
*/
|
|
@@ -19,12 +19,19 @@ exports.ASTCommon = ASTCommon;
|
|
|
19
19
|
/**
|
|
20
20
|
* Represents a usage of any existing ASTNode. Used to reduce node size.
|
|
21
21
|
*/
|
|
22
|
-
class
|
|
22
|
+
class ASTDefinition extends ASTCommon {
|
|
23
23
|
constructor(data) {
|
|
24
24
|
super(data);
|
|
25
25
|
this.name = data.name;
|
|
26
26
|
this.instanceType = data.instanceType;
|
|
27
27
|
this.constraints = data.constraints;
|
|
28
|
+
this.templatesTranslation = data.templatesTranslation;
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
|
-
exports.
|
|
31
|
+
exports.ASTDefinition = ASTDefinition;
|
|
32
|
+
class ASTGenericType {
|
|
33
|
+
constructor(name) {
|
|
34
|
+
this.name = name;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.ASTGenericType = ASTGenericType;
|
|
@@ -17,6 +17,8 @@ export declare class ASTEnum extends ASTCommon {
|
|
|
17
17
|
export declare class ASTObject extends ASTCommon {
|
|
18
18
|
name: string;
|
|
19
19
|
properties: Record<string, ASTType>;
|
|
20
|
+
templates: Set<string>;
|
|
21
|
+
templatesTranslation: Pick<ASTCommon, "parentFile" | "parentNamespace" | "aliasOf">[];
|
|
20
22
|
constructor(data: ASTObject & ASTCommon);
|
|
21
23
|
}
|
|
22
24
|
/**
|
|
@@ -22,6 +22,8 @@ class ASTObject extends ast_common_1.ASTCommon {
|
|
|
22
22
|
super(data);
|
|
23
23
|
this.name = data.name;
|
|
24
24
|
this.properties = data.properties;
|
|
25
|
+
this.templates = data.templates;
|
|
26
|
+
this.templatesTranslation = data.templatesTranslation;
|
|
25
27
|
}
|
|
26
28
|
}
|
|
27
29
|
exports.ASTObject = ASTObject;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ASTCommon,
|
|
1
|
+
import { ASTCommon, ASTDefinition } from "./ast_common";
|
|
2
2
|
/**
|
|
3
3
|
* Handle ZodString
|
|
4
4
|
*/
|
|
@@ -28,8 +28,8 @@ export declare class ASTNumber extends ASTCommon {
|
|
|
28
28
|
*/
|
|
29
29
|
export declare class ASTLiteral extends ASTCommon {
|
|
30
30
|
name?: string;
|
|
31
|
-
value:
|
|
32
|
-
parentEnum?:
|
|
31
|
+
value: string | number | boolean;
|
|
32
|
+
parentEnum?: ASTDefinition;
|
|
33
33
|
parentEnumKey?: string;
|
|
34
34
|
constructor(data: ASTLiteral & ASTCommon);
|
|
35
35
|
}
|
package/dist/core/ast_node.js
CHANGED
|
@@ -39,14 +39,30 @@ class Zod2Ast {
|
|
|
39
39
|
* @returns
|
|
40
40
|
*/
|
|
41
41
|
_getTranspilerableFile(itemName, metadata) {
|
|
42
|
-
var _a;
|
|
42
|
+
var _a, _b;
|
|
43
43
|
let layer;
|
|
44
44
|
if (this.opt.layer !== undefined && (metadata === null || metadata === void 0 ? void 0 : metadata.layer) !== undefined) {
|
|
45
|
+
if (Array.isArray(metadata.genericTypes)) {
|
|
46
|
+
// Check that all templates used with a generic type follows the layering rules
|
|
47
|
+
if (metadata.genericTypes.some((i) => this.opt.layer.index < i.layer.index)) {
|
|
48
|
+
throw new errors_1.BadLayerDefinitionError(`${itemName}: Layer with number ${this.opt.layer.index} can only use models` +
|
|
49
|
+
`from the same or lower layer. Review templates used for generic ` +
|
|
50
|
+
`type ${metadata.typeName}.`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const templatesTranslation = (_a = metadata === null || metadata === void 0 ? void 0 : metadata.genericTypes) === null || _a === void 0 ? void 0 : _a.map((i) => {
|
|
54
|
+
var _a, _b;
|
|
55
|
+
return ({
|
|
56
|
+
parentFile: ((_a = this.opt.layer) === null || _a === void 0 ? void 0 : _a.file) !== i.layer.file ? i.layer.file : undefined,
|
|
57
|
+
parentNamespace: ((_b = this.opt.layer) === null || _b === void 0 ? void 0 : _b.file) !== i.layer.file ? i.layer.namespace : undefined,
|
|
58
|
+
aliasOf: i.typeName,
|
|
59
|
+
});
|
|
60
|
+
});
|
|
45
61
|
if (metadata.layer.file === this.opt.layer.file) {
|
|
46
62
|
// Case 1: Only layer exists and belongs to the same file
|
|
47
63
|
// Case 2: Layer (belongs to same file) and parentLayer exist
|
|
48
64
|
// Behaviour: New type is created extending the parent layer (if any)
|
|
49
|
-
layer = (
|
|
65
|
+
layer = (_b = metadata.parentLayer) !== null && _b !== void 0 ? _b : metadata.layer;
|
|
50
66
|
if (this.opt.layer.index < layer.index) {
|
|
51
67
|
throw new errors_1.BadLayerDefinitionError(`${itemName}: Layer with number ${this.opt.layer.index} can only use models` +
|
|
52
68
|
`from the same or lower layer. Found layer with number ${layer.index}`);
|
|
@@ -56,11 +72,15 @@ class Zod2Ast {
|
|
|
56
72
|
parentFile: layer.file,
|
|
57
73
|
parentNamespace: layer.namespace,
|
|
58
74
|
aliasOf: metadata === null || metadata === void 0 ? void 0 : metadata.aliasOf,
|
|
75
|
+
templatesTranslation,
|
|
76
|
+
isGenericChild: metadata === null || metadata === void 0 ? void 0 : metadata.isGenericChild,
|
|
59
77
|
};
|
|
60
78
|
}
|
|
61
79
|
else {
|
|
62
80
|
return {
|
|
63
81
|
aliasOf: metadata === null || metadata === void 0 ? void 0 : metadata.aliasOf,
|
|
82
|
+
templatesTranslation,
|
|
83
|
+
isGenericChild: metadata === null || metadata === void 0 ? void 0 : metadata.isGenericChild,
|
|
64
84
|
};
|
|
65
85
|
}
|
|
66
86
|
}
|
|
@@ -77,6 +97,8 @@ class Zod2Ast {
|
|
|
77
97
|
parentFile: layer.file,
|
|
78
98
|
parentNamespace: layer.namespace,
|
|
79
99
|
aliasOf: undefined,
|
|
100
|
+
templatesTranslation,
|
|
101
|
+
isGenericChild: metadata === null || metadata === void 0 ? void 0 : metadata.isGenericChild,
|
|
80
102
|
};
|
|
81
103
|
}
|
|
82
104
|
}
|
|
@@ -88,13 +110,14 @@ class Zod2Ast {
|
|
|
88
110
|
* @param constraints - Constraints to be added to the definition
|
|
89
111
|
* @returns
|
|
90
112
|
*/
|
|
91
|
-
_createDefinition(node, constraints = {}) {
|
|
92
|
-
return new core_1.
|
|
113
|
+
_createDefinition(node, constraints = {}, templatesTranslation = []) {
|
|
114
|
+
return new core_1.ASTDefinition({
|
|
93
115
|
name: node.name,
|
|
94
116
|
instanceType: node.constructor.name,
|
|
95
117
|
parentFile: node.parentFile,
|
|
96
118
|
parentNamespace: node.parentNamespace,
|
|
97
119
|
aliasOf: node.aliasOf,
|
|
120
|
+
templatesTranslation,
|
|
98
121
|
constraints: "constraints" in node
|
|
99
122
|
? Object.assign(Object.assign({}, node.constraints), constraints) : constraints,
|
|
100
123
|
});
|
|
@@ -261,13 +284,25 @@ class Zod2Ast {
|
|
|
261
284
|
*/
|
|
262
285
|
_getObjectAst(schema, opt) {
|
|
263
286
|
var _a;
|
|
264
|
-
const { name, parentFile, parentNamespace, aliasOf } = this._getNames(schema);
|
|
287
|
+
const { name, parentFile, parentNamespace, aliasOf, templatesTranslation, isGenericChild } = this._getNames(schema);
|
|
265
288
|
let discriminantValue = undefined;
|
|
266
289
|
const shape = schema.def.shape;
|
|
267
290
|
if (!this.nodes.has(name)) {
|
|
268
291
|
const properties = {};
|
|
292
|
+
const templates = new Set();
|
|
269
293
|
for (const key in shape) {
|
|
270
|
-
|
|
294
|
+
if (zod_helpers_1.ZodHelpers.isZodPromise(shape[key]) &&
|
|
295
|
+
zod_helpers_1.ZodHelpers.isZod2XGeneric(shape[key])) {
|
|
296
|
+
const templateKey = shape[key].unwrap().def.values[0];
|
|
297
|
+
properties[key] = new core_1.ASTGenericType(templateKey);
|
|
298
|
+
if (templates.has(templateKey)) {
|
|
299
|
+
throw new errors_1.AstTypeNameDefinitionError(`Duplicate template key found for model ${name}: ${templateKey}`);
|
|
300
|
+
}
|
|
301
|
+
templates.add(templateKey);
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
properties[key] = this._zodToAST(shape[key]);
|
|
305
|
+
}
|
|
271
306
|
}
|
|
272
307
|
if (opt === null || opt === void 0 ? void 0 : opt.skipLayerClass) {
|
|
273
308
|
return {}; // Layer classes are not transpilerable
|
|
@@ -279,6 +314,8 @@ class Zod2Ast {
|
|
|
279
314
|
parentFile,
|
|
280
315
|
parentNamespace,
|
|
281
316
|
aliasOf,
|
|
317
|
+
templates,
|
|
318
|
+
templatesTranslation: templatesTranslation || [],
|
|
282
319
|
}));
|
|
283
320
|
}
|
|
284
321
|
const item = this.nodes.get(name);
|
|
@@ -295,7 +332,7 @@ class Zod2Ast {
|
|
|
295
332
|
}
|
|
296
333
|
}
|
|
297
334
|
}
|
|
298
|
-
return this._createDefinition(this.nodes.get(name), { discriminantValue });
|
|
335
|
+
return this._createDefinition(this.nodes.get(name), { discriminantValue }, isGenericChild ? undefined : templatesTranslation);
|
|
299
336
|
}
|
|
300
337
|
/**
|
|
301
338
|
* Generates an Abstract Syntax Tree (AST) definition for a Zod union schema.
|
|
@@ -315,7 +352,7 @@ class Zod2Ast {
|
|
|
315
352
|
var _a, _b, _c;
|
|
316
353
|
const def = schema.def;
|
|
317
354
|
const discriminator = zod_helpers_1.ZodHelpers.isZodDiscriminatedUnion(schema)
|
|
318
|
-
? schema.def.discriminator
|
|
355
|
+
? schema._zod.def.discriminator
|
|
319
356
|
: undefined;
|
|
320
357
|
const { name, parentFile, parentNamespace, aliasOf } = this._getNames(schema);
|
|
321
358
|
const item = new core_1.ASTUnion({
|
|
@@ -340,12 +377,17 @@ class Zod2Ast {
|
|
|
340
377
|
this.warnings.push(`[affected type: ${name}] Using ZodUnion is a bad data modeling practice. ` +
|
|
341
378
|
"Use ZodDiscriminatedUnion instead, or disable strict mode if not possible.");
|
|
342
379
|
}
|
|
380
|
+
const unifiedProperties = this._unionAstNodes(item.options);
|
|
343
381
|
item.newObject = new core_1.ASTObject({
|
|
344
382
|
name,
|
|
345
|
-
properties:
|
|
383
|
+
properties: unifiedProperties.properties,
|
|
346
384
|
description: (((_b = schema.meta()) === null || _b === void 0 ? void 0 : _b.description) ? `${(_c = schema.meta()) === null || _c === void 0 ? void 0 : _c.description} - ` : "") +
|
|
347
385
|
`Built from union of ` +
|
|
348
386
|
`${item.options.map((i) => i.name).join(", ")}`,
|
|
387
|
+
templates: new Set(Object.values(unifiedProperties.properties)
|
|
388
|
+
.filter((i) => i instanceof core_1.ASTGenericType)
|
|
389
|
+
.map((i) => i.name)),
|
|
390
|
+
templatesTranslation: [],
|
|
349
391
|
});
|
|
350
392
|
}
|
|
351
393
|
if (name && !this.nodes.has(name)) {
|
|
@@ -383,13 +425,18 @@ class Zod2Ast {
|
|
|
383
425
|
}
|
|
384
426
|
}
|
|
385
427
|
else {
|
|
428
|
+
const intersectedProperties = this._intersectAstNodes(item.left, item.right);
|
|
386
429
|
item.newObject = new core_1.ASTObject({
|
|
387
430
|
name,
|
|
388
|
-
properties:
|
|
431
|
+
properties: intersectedProperties.properties,
|
|
389
432
|
description: (((_b = schema.meta()) === null || _b === void 0 ? void 0 : _b.description) ? `${(_c = schema.meta()) === null || _c === void 0 ? void 0 : _c.description} - ` : "") +
|
|
390
433
|
`Built from intersection of ` +
|
|
391
434
|
`${item.left.name} and ` +
|
|
392
435
|
`${item.right.name}`,
|
|
436
|
+
templates: new Set(Object.values(intersectedProperties.properties)
|
|
437
|
+
.filter((i) => i instanceof core_1.ASTGenericType)
|
|
438
|
+
.map((i) => i.name)),
|
|
439
|
+
templatesTranslation: [],
|
|
393
440
|
});
|
|
394
441
|
}
|
|
395
442
|
if (name && !this.nodes.has(name)) {
|
|
@@ -35,6 +35,7 @@ export declare abstract class Zod2X<T extends IZodToXOpt> {
|
|
|
35
35
|
protected preImports: Set<string>;
|
|
36
36
|
protected imports: Set<string>;
|
|
37
37
|
protected postImports: Set<string>;
|
|
38
|
+
protected abstract readonly commentKey: string;
|
|
38
39
|
protected opt: Partial<T>;
|
|
39
40
|
protected constructor(opt: Partial<T>);
|
|
40
41
|
/**
|
|
@@ -55,6 +56,10 @@ export declare abstract class Zod2X<T extends IZodToXOpt> {
|
|
|
55
56
|
* @param typeName
|
|
56
57
|
*/
|
|
57
58
|
protected abstract getTypeFromExternalNamespace(namespace: string, typeName: string): string;
|
|
59
|
+
/**
|
|
60
|
+
* Returns the translation of generic templates for a given AST node.
|
|
61
|
+
*/
|
|
62
|
+
protected abstract getGenericTemplatesTranslation(data: ASTNode): string | undefined;
|
|
58
63
|
/**
|
|
59
64
|
* For Layered Modeling.
|
|
60
65
|
* If a property type is an imported type, without any modification, the transpiled type will be
|
|
@@ -64,10 +69,6 @@ export declare abstract class Zod2X<T extends IZodToXOpt> {
|
|
|
64
69
|
* @param aliasOf
|
|
65
70
|
*/
|
|
66
71
|
protected abstract addExtendedType(name: string, parentNamespace: string, aliasOf: string): void;
|
|
67
|
-
/**
|
|
68
|
-
* Returns a comment.
|
|
69
|
-
*/
|
|
70
|
-
protected abstract getComment(data: string, indent?: string): string;
|
|
71
72
|
/**
|
|
72
73
|
* Returns the keyword representing a string type in the target language.
|
|
73
74
|
*/
|
|
@@ -90,7 +91,7 @@ export declare abstract class Zod2X<T extends IZodToXOpt> {
|
|
|
90
91
|
* @param value - The literal value to represent.
|
|
91
92
|
* @param parentEnumNameKey - Optional tuple containing the parent enum name and key reference.
|
|
92
93
|
*/
|
|
93
|
-
protected abstract getLiteralStringType(value: string | number, parentEnumNameKey?: [string, string]): string | number;
|
|
94
|
+
protected abstract getLiteralStringType(value: string | number | boolean, parentEnumNameKey?: [string, string]): string | number;
|
|
94
95
|
/**
|
|
95
96
|
* Returns the keyword representing a generic 'any' type in the target language.
|
|
96
97
|
*/
|
|
@@ -170,6 +171,10 @@ export declare abstract class Zod2X<T extends IZodToXOpt> {
|
|
|
170
171
|
* @param data - The AST node representing the aliased type.
|
|
171
172
|
*/
|
|
172
173
|
protected abstract transpileAliasedType(data: ASTAliasedTypes): void;
|
|
174
|
+
/**
|
|
175
|
+
* Returns a comment.
|
|
176
|
+
*/
|
|
177
|
+
protected getComment: (data: string, indent?: string) => string;
|
|
173
178
|
/**
|
|
174
179
|
* Determines if the given type token can be transpiled into the target language.
|
|
175
180
|
* @param token - The type token to check.
|
package/dist/core/transpiler.js
CHANGED
|
@@ -13,6 +13,15 @@ const string_utils_1 = __importDefault(require("../utils/string_utils"));
|
|
|
13
13
|
*/
|
|
14
14
|
class Zod2X {
|
|
15
15
|
constructor(opt) {
|
|
16
|
+
/**
|
|
17
|
+
* Returns a comment.
|
|
18
|
+
*/
|
|
19
|
+
this.getComment = (data, indent = "") => {
|
|
20
|
+
return data
|
|
21
|
+
.split("\n")
|
|
22
|
+
.map((line) => `${indent}${this.commentKey} ${line}`)
|
|
23
|
+
.join("\n");
|
|
24
|
+
};
|
|
16
25
|
// Push with indentation helpers
|
|
17
26
|
this.push0 = (data) => this.output.push(`${this.indent[0]}${data}`);
|
|
18
27
|
this.push1 = (data) => this.output.push(`${this.indent[1]}${data}`);
|
|
@@ -71,7 +80,8 @@ class Zod2X {
|
|
|
71
80
|
getAttributeType(token) {
|
|
72
81
|
var _a, _b, _c;
|
|
73
82
|
let varType = "";
|
|
74
|
-
if (token instanceof core_1.
|
|
83
|
+
if (token instanceof core_1.ASTDefinition) {
|
|
84
|
+
const template = this.getGenericTemplatesTranslation(token) || "";
|
|
75
85
|
if (this.opt.useImports === true && token.parentNamespace) {
|
|
76
86
|
this.addExternalTypeImport({
|
|
77
87
|
parentNamespace: token.parentNamespace,
|
|
@@ -87,6 +97,7 @@ class Zod2X {
|
|
|
87
97
|
else {
|
|
88
98
|
varType = token.name;
|
|
89
99
|
}
|
|
100
|
+
varType += template;
|
|
90
101
|
}
|
|
91
102
|
else if (this.isTranspilerable(token)) {
|
|
92
103
|
varType = token.name;
|
|
@@ -131,6 +142,9 @@ class Zod2X {
|
|
|
131
142
|
varType = this.getRecordType(key, value);
|
|
132
143
|
}
|
|
133
144
|
}
|
|
145
|
+
else if (token instanceof core_1.ASTGenericType) {
|
|
146
|
+
varType = token.name;
|
|
147
|
+
}
|
|
134
148
|
else {
|
|
135
149
|
console.log(" # Unknown attribute equivalent for ---> ", token.constructor.name);
|
|
136
150
|
}
|
|
@@ -203,7 +217,7 @@ class Zod2X {
|
|
|
203
217
|
_getHeader() {
|
|
204
218
|
const header = [];
|
|
205
219
|
if (this.opt.header) {
|
|
206
|
-
header.push(
|
|
220
|
+
header.push(this.getComment(this.opt.header));
|
|
207
221
|
header.push("");
|
|
208
222
|
}
|
|
209
223
|
if (this.preImports.size > 0) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { extendZod, IZod2xLayerMetadata } from "./lib/zod_ext";
|
|
2
|
+
export { createGenericType, useGenericType } from "./lib/zod_helpers";
|
|
2
3
|
export * as Zod2XTypes from "./core/ast-types";
|
|
3
4
|
export { Zod2Ast } from "./core/ast_node";
|
|
4
5
|
export { Zod2X } from "./core/transpiler";
|
package/dist/index.js
CHANGED
|
@@ -36,10 +36,13 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
36
36
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.Zod2XConverters = exports.Zod2XTranspilers = exports.Zod2X = exports.Zod2Ast = exports.Zod2XTypes = exports.extendZod = void 0;
|
|
39
|
+
exports.Zod2XConverters = exports.Zod2XTranspilers = exports.Zod2X = exports.Zod2Ast = exports.Zod2XTypes = exports.useGenericType = exports.createGenericType = exports.extendZod = void 0;
|
|
40
40
|
// Core
|
|
41
41
|
var zod_ext_1 = require("./lib/zod_ext");
|
|
42
42
|
Object.defineProperty(exports, "extendZod", { enumerable: true, get: function () { return zod_ext_1.extendZod; } });
|
|
43
|
+
var zod_helpers_1 = require("./lib/zod_helpers");
|
|
44
|
+
Object.defineProperty(exports, "createGenericType", { enumerable: true, get: function () { return zod_helpers_1.createGenericType; } });
|
|
45
|
+
Object.defineProperty(exports, "useGenericType", { enumerable: true, get: function () { return zod_helpers_1.useGenericType; } });
|
|
43
46
|
exports.Zod2XTypes = __importStar(require("./core/ast-types"));
|
|
44
47
|
var ast_node_1 = require("./core/ast_node");
|
|
45
48
|
Object.defineProperty(exports, "Zod2Ast", { enumerable: true, get: function () { return ast_node_1.Zod2Ast; } });
|
|
@@ -98,16 +98,31 @@ function Layer(opt) {
|
|
|
98
98
|
aliasOf: metadata.typeName,
|
|
99
99
|
layer: opt,
|
|
100
100
|
typeName: name,
|
|
101
|
+
// Generics associated to parent, but related to current type.
|
|
102
|
+
genericTypes: metadata.genericTypes,
|
|
103
|
+
isGenericChild: true,
|
|
101
104
|
};
|
|
102
105
|
}
|
|
103
106
|
return zodItem;
|
|
104
107
|
};
|
|
108
|
+
const lazyProperties = [];
|
|
105
109
|
Object.getOwnPropertyNames(this).forEach((prop) => {
|
|
106
110
|
const item = this[prop];
|
|
107
111
|
if (zod_helpers_1.ZodHelpers.isTranspilerableZodType(item) ||
|
|
108
112
|
zod_helpers_1.ZodHelpers.isTranspilerableAliasedZodType(item, opt.basicTypes === false)) {
|
|
109
113
|
this[prop] = setMetadata(case_1.default.pascal(prop), item, opt);
|
|
110
114
|
}
|
|
115
|
+
else if (zod_helpers_1.ZodHelpers.isZodLazy(item)) {
|
|
116
|
+
lazyProperties.push(prop);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
// Process lazy properties after initial pass to ensure proper metadata assignment.
|
|
120
|
+
lazyProperties.forEach((i) => {
|
|
121
|
+
const lazyItem = this[i]._def.getter();
|
|
122
|
+
if (zod_helpers_1.ZodHelpers.isTranspilerableZodType(lazyItem) ||
|
|
123
|
+
zod_helpers_1.ZodHelpers.isTranspilerableAliasedZodType(lazyItem, opt.basicTypes === false)) {
|
|
124
|
+
this[i] = setMetadata(case_1.default.pascal(i), lazyItem, opt);
|
|
125
|
+
}
|
|
111
126
|
});
|
|
112
127
|
}
|
|
113
128
|
},
|