zod-to-x 1.4.8-dev.4 → 1.5.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 CHANGED
@@ -27,9 +27,10 @@
27
27
  - [Intersections and Unions](#intersections-and-unions)
28
28
  - [Expected outputs](#expected-outputs)
29
29
  - [Tips for discriminated unions](#tips-for-discriminated-unions)
30
- - [Layered modeling](#layered-modeling) <sup>*(new)*</sup>
30
+ - [Layered modeling](#layered-modeling)
31
31
  - [Usage example](#usage-example)
32
32
  - [Custom layers](#custom-layers)
33
+ - [Generic types](#generic-types) <sup>*(new)*</sup>
33
34
  - [Currently supported output languages](#currently-supported-output-languages)
34
35
  - [Typescript](#1-typescript)
35
36
  - [C++](#2-c)
@@ -502,6 +503,106 @@ class MyEntityModels extends Zod2XModel {
502
503
  }
503
504
  ```
504
505
 
506
+ ### Generic types
507
+ 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:
508
+ 1) Generic types can only be created for `ZodObject` properties. The new `createGenericType` method should be used to indicate the generic property.
509
+ ```ts
510
+ @Infrastructure({
511
+ namespace: "GENERICS_INFRA",
512
+ file: "layered_generics.infra",
513
+ })
514
+ class GenericsInfrastructure extends Zod2XModel {
515
+ readonly HttpSuccessfulResponse = z.object({
516
+ success: z.literal(true),
517
+ data: createGenericType("T"),
518
+ });
519
+
520
+ readonly HttpUnsuccessfulResponse = z.object({
521
+ success: z.literal(false),
522
+ message: z.string(),
523
+ details: z.record(z.any()).optional(),
524
+ });
525
+ }
526
+ // Output:
527
+ // export interface HttpSuccessfulResponse<T> {
528
+ // success: true;
529
+ // data: T;
530
+ // }
531
+ //
532
+ // export interface HttpUnsuccessfulResponse {
533
+ // success: false;
534
+ // message: string;
535
+ // details?: Record<string, any>;
536
+ // }
537
+ ```
538
+ 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.
539
+ ```ts
540
+ // inside GenericsInfrastructure class
541
+ readonly SomeDtoResult = z.object({
542
+ id: z.string(),
543
+ name: z.string(),
544
+ age: z.number().int().nonnegative(),
545
+ });
546
+
547
+ readonly ResponseItem = useGenericType(this.HttpSuccessfulResponse, {
548
+ data: this.SomeDtoResult, // Indicate the generic property and the value it takes
549
+ // ... other generic properties, if exist.
550
+ });
551
+
552
+ // Output:
553
+ // export interface SomeDtoResult {
554
+ // id: string;
555
+ // name: string;
556
+ // age: number;
557
+ // }
558
+ //
559
+ // export interface ResponseItem extends HttpSuccessfulResponse<SomeDtoResult> {}
560
+
561
+ ```
562
+ 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:
563
+ ```ts
564
+ // inside GenericsInfrastructure class
565
+ readonly ObjectWithGeneric = z.object({
566
+ // Property with reference to a generic based type: requires lazy operator
567
+ directGenericUse: z.lazy(() => this.ResponseItem),
568
+
569
+ // Property with inline generic usage: `useGenericType` is lazy by default
570
+ indirectGenericUse: useGenericType(this.HttpSuccessfulResponse, {
571
+ data: this.SomeDtoResult
572
+ }),
573
+ });
574
+
575
+ // Case of ZodUnion, ZodDiscriminatedUnion or ZodIntersection.
576
+ readonly DiscriminantDataRetrieve = z.lazy(() => // Lazy the zod type itself
577
+ z.discriminatedUnion(
578
+ "success",
579
+ [
580
+ // Inline generic usage
581
+ useGenericType(
582
+ this.HttpSuccessfulResponse,
583
+ { data: this.SomeDtoResult },
584
+ true // Disable default lazy wrapper
585
+ ),
586
+
587
+ // Non generic types: normal references
588
+ this.HttpUnsuccessfulResponse,
589
+ ]
590
+ )
591
+ );
592
+
593
+ // Output
594
+ // export interface ObjectWithGeneric {
595
+ // directGenericUse: ResponseItem;
596
+ // indirectGenericUse: HttpSuccessfulResponse<SomeDtoResult>;
597
+ // }
598
+ //
599
+ // export type DiscriminantDataRetrieve =
600
+ // | HttpSuccessfulResponse<SomeDtoResult>
601
+ // | HttpUnsuccessfulResponse;
602
+ ```
603
+
604
+ Complete definition examples can be found [here](https://github.com/rroumenov/zod-to-x/blob/main/test/common/layered_generics.ts).
605
+
505
606
  ## Currently supported output languages
506
607
  Common options:
507
608
  - **header**: Text to add as a comment at the beginning of the output.
@@ -645,7 +746,7 @@ class UserDtos extends Zod2XModel {
645
746
  createUserUseCaseResultDtoV2 = this.createUserUseCaseResultDto;
646
747
 
647
748
  // OK, but avoid it - Redeclaration of an alias. It will wait until
648
- // "createUserUseCaseResultDto" is aliased and then will becosa an alias
749
+ // "createUserUseCaseResultDto" is aliased and then will become an alias
649
750
  // of "createUserUseCaseResultDto"
650
751
  createUserUseCaseResultDtoV3 = z.lazy(() => this.createUserUseCaseResultDto),
651
752
  }
@@ -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;
@@ -6,5 +6,6 @@ exports.defaultOpts = {
6
6
  indent: 4,
7
7
  keepKeys: false,
8
8
  encodeDoubleAsInt: false,
9
+ useExplicitOptional: false,
9
10
  useImports: false, // Not required for protobuf files
10
11
  };
@@ -17,4 +17,4 @@ import { IZod2ProtoV3Opt } from "./options";
17
17
  * definition.
18
18
  * @returns The Protocol Buffers v3 definition as a string.
19
19
  */
20
- export declare function zod2ProtoV3(schema: ZodObject<any> | ZodDiscriminatedUnion<string, ZodObject<any>[]> | ZodUnion<[ZodObject<any>, ...ZodObject<any>[]]>, opt?: Pick<IZod2AstOpt, "strict"> & Pick<IZod2ProtoV3Opt, "packageName" | "keepKeys" | "header" | "indent" | "includeComments" | "encodeDoubleAsInt">): string;
20
+ export declare function zod2ProtoV3(schema: ZodObject<any> | ZodDiscriminatedUnion<string, ZodObject<any>[]> | ZodUnion<[ZodObject<any>, ...ZodObject<any>[]]>, 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;
@@ -167,7 +171,10 @@ class Zod2ProtoV3 extends core_1.Zod2X {
167
171
  // Avoid duplicated descriptions for transpiled items.
168
172
  this.addComment(value.description, `\n${this.indent[1]}`);
169
173
  }
170
- this.push1(`${this.getAttributeType(value)} ${this._adaptField(key)} = ${index + 1};`);
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};`);
171
178
  });
172
179
  this.push0("}\n");
173
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 ASTDefintion extends ASTCommon {
35
+ export declare class ASTDefinition extends ASTCommon {
36
36
  name: string;
37
37
  instanceType: string;
38
38
  constraints?: Record<string, any>;
39
- constructor(data: ASTDefintion & ASTCommon);
39
+ templatesTranslation: Pick<ASTCommon, "parentFile" | "parentNamespace" | "aliasOf">[];
40
+ constructor(data: ASTDefinition & ASTCommon);
40
41
  }
41
- export type ASTType = ASTNode | ASTDefintion;
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.ASTDefintion = exports.ASTCommon = void 0;
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 ASTDefintion extends ASTCommon {
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.ASTDefintion = ASTDefintion;
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, ASTDefintion } from "./ast_common";
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: any;
32
- parentEnum?: ASTDefintion;
31
+ value: string | number | boolean;
32
+ parentEnum?: ASTDefinition;
33
33
  parentEnumKey?: string;
34
34
  constructor(data: ASTLiteral & ASTCommon);
35
35
  }
@@ -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 = (_a = metadata.parentLayer) !== null && _a !== void 0 ? _a : metadata.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.ASTDefintion({
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
  });
@@ -263,13 +286,24 @@ class Zod2Ast {
263
286
  * @returns The AST definition for the provided Zod object schema.
264
287
  */
265
288
  _getObjectAst(schema, opt) {
266
- const { name, parentFile, parentNamespace, aliasOf } = this._getNames(schema);
289
+ const { name, parentFile, parentNamespace, aliasOf, templatesTranslation, isGenericChild } = this._getNames(schema);
267
290
  let discriminantValue = undefined;
268
291
  const shape = schema._def.shape();
269
292
  if (!this.nodes.has(name)) {
270
293
  const properties = {};
294
+ const templates = new Set();
271
295
  for (const key in shape) {
272
- properties[key] = this._zodToAST(shape[key]);
296
+ if (zod_helpers_1.ZodHelpers.isZodPromise(shape[key]) && zod_helpers_1.ZodHelpers.isZod2XGeneric(shape[key])) {
297
+ const templateKey = shape[key]._def.type.value;
298
+ properties[key] = new core_1.ASTGenericType(templateKey);
299
+ if (templates.has(templateKey)) {
300
+ throw new errors_1.AstTypeNameDefinitionError(`Duplicate template key found for model ${name}: ${templateKey}`);
301
+ }
302
+ templates.add(templateKey);
303
+ }
304
+ else {
305
+ properties[key] = this._zodToAST(shape[key]);
306
+ }
273
307
  }
274
308
  if (opt === null || opt === void 0 ? void 0 : opt.skipLayerClass) {
275
309
  return {}; // Layer classes are not transpilerable
@@ -281,6 +315,8 @@ class Zod2Ast {
281
315
  parentFile,
282
316
  parentNamespace,
283
317
  aliasOf,
318
+ templates,
319
+ templatesTranslation: templatesTranslation || [],
284
320
  }));
285
321
  }
286
322
  const item = this.nodes.get(name);
@@ -297,7 +333,7 @@ class Zod2Ast {
297
333
  }
298
334
  }
299
335
  }
300
- return this._createDefinition(this.nodes.get(name), { discriminantValue });
336
+ return this._createDefinition(this.nodes.get(name), { discriminantValue }, isGenericChild ? undefined : templatesTranslation);
301
337
  }
302
338
  /**
303
339
  * Generates an Abstract Syntax Tree (AST) definition for a Zod union schema.
@@ -341,12 +377,17 @@ class Zod2Ast {
341
377
  this.warnings.push(`[affected type: ${name}] Using ZodUnion is a bad data modeling practice. ` +
342
378
  "Use ZodDiscriminatedUnion instead, or disable strict mode if not possible.");
343
379
  }
380
+ const unifiedProperties = this._unionAstNodes(item.options);
344
381
  item.newObject = new core_1.ASTObject({
345
382
  name,
346
- properties: this._unionAstNodes(item.options).properties,
383
+ properties: unifiedProperties.properties,
347
384
  description: (schema.description ? `${schema.description} - ` : "") +
348
385
  `Built from union of ` +
349
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: [],
350
391
  });
351
392
  }
352
393
  if (name && !this.nodes.has(name)) {
@@ -382,13 +423,18 @@ class Zod2Ast {
382
423
  }
383
424
  }
384
425
  else {
426
+ const intersectedProperties = this._intersectAstNodes(item.left, item.right);
385
427
  item.newObject = new core_1.ASTObject({
386
428
  name,
387
- properties: this._intersectAstNodes(item.left, item.right).properties,
429
+ properties: intersectedProperties.properties,
388
430
  description: (schema.description ? `${schema.description} - ` : "") +
389
431
  `Built from intersection of ` +
390
432
  `${item.left.name} and ` +
391
433
  `${item.right.name}`,
434
+ templates: new Set(Object.values(intersectedProperties.properties)
435
+ .filter((i) => i instanceof core_1.ASTGenericType)
436
+ .map((i) => i.name)),
437
+ templatesTranslation: [],
392
438
  });
393
439
  }
394
440
  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.
@@ -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.ASTDefintion) {
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(...this.opt.header.split("\n").map((i) => this.getComment(i)));
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
  },
@@ -88,6 +88,22 @@ export interface IZod2xMetadata {
88
88
  */
89
89
  aliasOf?: string;
90
90
  parentLayer?: IZod2xLayerMetadata;
91
+ /**
92
+ * For Layered Modeling defining generics.
93
+ * When a generic type is used with `useGenericType`, it stores the templates translation for
94
+ * the associated key.
95
+ */
96
+ genericTypes?: {
97
+ typeName: string;
98
+ layer: IZod2xLayerMetadata;
99
+ }[];
100
+ /**
101
+ * For Layered Modeling defining generics.
102
+ * Indicates when useGenericType was used to create a definition (set to true during Layer
103
+ * decorator) or if it is used inline as part of another definition (set to false).
104
+ * Defaults to false.
105
+ */
106
+ isGenericChild?: boolean;
91
107
  }
92
108
  declare module "zod" {
93
109
  interface ZodType {
@@ -34,6 +34,7 @@ export declare class ZodHelpers {
34
34
  static isZodOptional(i: ZodTypeAny): i is z.ZodOptional<any>;
35
35
  static isZodNullable(i: ZodTypeAny): i is z.ZodNullable<any>;
36
36
  static isZodDefault(i: ZodTypeAny): i is z.ZodDefault<any>;
37
+ static isZodPromise(i: ZodTypeAny): i is z.ZodPromise<any>;
37
38
  static isZodAnyUnionType(i: ZodTypeAny): i is z.ZodUnion<any> | z.ZodDiscriminatedUnion<any, any>;
38
39
  static isZodAnyEnumType(i: ZodTypeAny): i is z.ZodEnum<any> | z.ZodNativeEnum<any>;
39
40
  static isZodAnyNumberType(i: ZodTypeAny): i is z.ZodNumber | z.ZodBigInt;
@@ -53,7 +54,23 @@ export declare class ZodHelpers {
53
54
  * @returns
54
55
  */
55
56
  static isTranspilerableAliasedZodType(zodType: string | ZodTypeAny, onlyArray?: boolean): boolean;
57
+ /**
58
+ * Zod2X generics are represented as Promise<"TypeName">.
59
+ * Ex: z.promise(z.literal("K")) will be transpiled to Template<K>
60
+ */
61
+ static isZod2XGeneric(i: ZodTypeAny): boolean;
56
62
  static cloneZod(i: ZodTypeAny): any;
57
63
  static createZodObject(properties: Map<string, ZodTypeAny>): ZodObject<any>;
58
64
  static getZodNumberConstraints(i: ZodNumber | z.ZodBigInt): ZodNumberConstraints;
59
65
  }
66
+ export declare function createGenericType(name: string): any;
67
+ /**
68
+ * Use generic types defined in a generic ZodObject by replacing them with the actual child types.
69
+ * It preserves the zod2x metadata including the genericTypes array which will be moved to the
70
+ * new type during layer modeling metadata assignment.
71
+ * @param genObj ZodObject with generic types.
72
+ * @param childrens Record of child types to replace generics.
73
+ * @returns The extended ZodObject with replaced generic types.
74
+ */
75
+ export declare function useGenericType(genObj: ZodObject<any>, childrens: Record<string, ZodTypeAny>, skipLazy: true): ZodObject<any>;
76
+ export declare function useGenericType(genObj: ZodObject<any>, childrens: Record<string, ZodTypeAny>, skipLazy?: false): z.ZodLazy<ZodObject<any>>;
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ZodHelpers = void 0;
4
+ exports.createGenericType = createGenericType;
5
+ exports.useGenericType = useGenericType;
4
6
  const zod_1 = require("zod");
5
7
  const zod_ext_1 = require("./zod_ext");
6
8
  /**
@@ -100,6 +102,10 @@ class ZodHelpers {
100
102
  var _a;
101
103
  return ((_a = i === null || i === void 0 ? void 0 : i._def) === null || _a === void 0 ? void 0 : _a.typeName) === zod_1.ZodFirstPartyTypeKind.ZodDefault;
102
104
  }
105
+ static isZodPromise(i) {
106
+ var _a;
107
+ return ((_a = i === null || i === void 0 ? void 0 : i._def) === null || _a === void 0 ? void 0 : _a.typeName) === zod_1.ZodFirstPartyTypeKind.ZodPromise;
108
+ }
103
109
  static isZodAnyUnionType(i) {
104
110
  return this.isZodUnion(i) || this.isZodDiscriminatedUnion(i);
105
111
  }
@@ -153,6 +159,15 @@ class ZodHelpers {
153
159
  type === zod_1.ZodFirstPartyTypeKind.ZodTuple ||
154
160
  type === zod_1.ZodFirstPartyTypeKind.ZodArray);
155
161
  }
162
+ /**
163
+ * Zod2X generics are represented as Promise<"TypeName">.
164
+ * Ex: z.promise(z.literal("K")) will be transpiled to Template<K>
165
+ */
166
+ static isZod2XGeneric(i) {
167
+ return (this.isZodPromise(i) &&
168
+ this.isZodLiteral(i._def.type) &&
169
+ typeof i._def.type._def.value === "string");
170
+ }
156
171
  static cloneZod(i) {
157
172
  const zodType = i._def.typeName;
158
173
  return new (zod_ext_1.Extended.getZ()[zodType])(Object.assign({}, i._def));
@@ -179,3 +194,34 @@ class ZodHelpers {
179
194
  }
180
195
  }
181
196
  exports.ZodHelpers = ZodHelpers;
197
+ function createGenericType(name) {
198
+ return zod_ext_1.Extended.getZ().promise(zod_ext_1.Extended.getZ().literal(name));
199
+ }
200
+ function useGenericType(genObj, childrens, skipLazy) {
201
+ const builder = () => {
202
+ let extended = genObj;
203
+ for (const [key, property] of Object.entries(genObj.shape)) {
204
+ if (ZodHelpers.isZod2XGeneric(property)) {
205
+ const childType = childrens[key];
206
+ if (!childType) {
207
+ throw new Error(`Missing child type for generic property ${key} in ${genObj._zod2x.typeName}.`);
208
+ }
209
+ const zod2xMeta = structuredClone(extended._zod2x);
210
+ extended = extended.extend({ [key]: childType });
211
+ extended._zod2x = zod2xMeta;
212
+ extended._zod2x.isGenericChild = false;
213
+ if (!Array.isArray(extended._zod2x.genericTypes)) {
214
+ extended._zod2x.genericTypes = [];
215
+ }
216
+ if (childType._zod2x !== undefined) {
217
+ extended._zod2x.genericTypes.push({
218
+ typeName: childType._zod2x.typeName,
219
+ layer: childType._zod2x.layer,
220
+ });
221
+ }
222
+ }
223
+ }
224
+ return extended;
225
+ };
226
+ return skipLazy ? builder() : zod_1.z.lazy(builder);
227
+ }
@@ -4,6 +4,7 @@ import { IZod2CppOpt } from "./options";
4
4
  * @description Transpiler for Zod schemas to C++11 code.
5
5
  */
6
6
  export declare class Zod2Cpp extends Zod2X<IZod2CppOpt> {
7
+ protected readonly commentKey = "//";
7
8
  protected serializers: string[];
8
9
  protected useBoost: boolean;
9
10
  protected lib: {
@@ -26,10 +27,11 @@ export declare class Zod2Cpp extends Zod2X<IZod2CppOpt> {
26
27
  protected addExtendedType(name: string, parentNamespace: string, aliasOf: string, opt?: {
27
28
  type?: "union" | "alias";
28
29
  isInternal?: boolean;
30
+ templates?: string;
29
31
  }): void;
32
+ protected getGenericTemplatesTranslation(data: ASTNode): string | undefined;
30
33
  protected checkExtendedTypeInclusion(data: ASTNode, type?: "union" | "alias"): boolean;
31
34
  protected runAfter(): void;
32
- protected getComment: (data: string, indent?: string) => string;
33
35
  protected getDateType: () => string;
34
36
  protected getBooleanType: () => string;
35
37
  protected getStringType: () => string;
@@ -53,11 +55,13 @@ export declare class Zod2Cpp extends Zod2X<IZod2CppOpt> {
53
55
  }) => string;
54
56
  /** Ex: std::vector<std::vector<TypeA>> */
55
57
  protected getArrayType(arrayType: string, arrayDeep: number): string;
56
- protected getLiteralStringType(value: string | number, parentEnumNameKey?: [string, string]): string;
58
+ protected getLiteralStringType(value: string | number | boolean, parentEnumNameKey?: [string, string]): string;
57
59
  /** Ex: std::unordered_map<TypeA> */
58
60
  protected getMapType(keyType: string, valueType: string): string;
59
61
  protected getRecordType(keyType: string, valueType: string): string;
60
62
  protected _getOptional(type: string): string;
63
+ protected _addExtendedTypeSerializer(typeName: string, parentNamespace: string): void;
64
+ protected _addExtendedTypeDeserializer(typeName: string, parentNamespace: string): void;
61
65
  protected transpileAliasedType(data: ASTAliasedTypes): void;
62
66
  /** Ex:
63
67
  * enum class EnumA: int {
@@ -83,6 +87,7 @@ export declare class Zod2Cpp extends Zod2X<IZod2CppOpt> {
83
87
  /** Ex: using TypeC = boost::variant<TypeA, TypeB> */
84
88
  protected transpileUnion(data: ASTUnion): void;
85
89
  protected transpileStruct(data: ASTObject): void;
90
+ private _getTemplates;
86
91
  /** Ex:
87
92
  * struct MyStruct {
88
93
  * TypeA attribute1;
@@ -139,6 +144,7 @@ export declare class Zod2Cpp extends Zod2X<IZod2CppOpt> {
139
144
  * }
140
145
  * @param parent - Name of the serialized structure
141
146
  * @param childs - Structure attributes data.
147
+ * @param templates - Generic templates string (if any)
142
148
  */
143
149
  private _createStructSerializer;
144
150
  /**
@@ -151,6 +157,7 @@ export declare class Zod2Cpp extends Zod2X<IZod2CppOpt> {
151
157
  * }
152
158
  * @param parent - Name of the deserialized structure
153
159
  * @param childs - Structure attributes data.
160
+ * @param templates - Generic templates string (if any)
154
161
  */
155
162
  private _createStructDeserializer;
156
163
  /**
@@ -166,6 +173,7 @@ export declare class Zod2Cpp extends Zod2X<IZod2CppOpt> {
166
173
  * }
167
174
  * @param parent - Name of the serialized structure
168
175
  * @param childs - Structure attributes data.
176
+ * @param templates - Generic templates string (if any)
169
177
  */
170
178
  private _createClassSerializer;
171
179
  /**
@@ -178,6 +186,7 @@ export declare class Zod2Cpp extends Zod2X<IZod2CppOpt> {
178
186
  * }
179
187
  * @param parent - Name of the deserialized structure
180
188
  * @param childs - Structure attributes data.
189
+ * @param templates - Generic templates string (if any)
181
190
  */
182
191
  private _createClassDeserializer;
183
192
  /**
@@ -16,11 +16,11 @@ const options_1 = require("./options");
16
16
  class Zod2Cpp extends core_1.Zod2X {
17
17
  constructor(opt = {}) {
18
18
  super(Object.assign(Object.assign({}, options_1.defaultOpts), opt));
19
+ this.commentKey = "//";
19
20
  this.getIntersectionType = () => {
20
21
  /** Covered by "transpileIntersection" method */
21
22
  return "";
22
23
  };
23
- this.getComment = (data, indent = "") => `${indent}// ${data}`;
24
24
  this.getDateType = () => this.getStringType(); // Representing ISO date as a string
25
25
  this.getBooleanType = () => "bool";
26
26
  this.getStringType = () => {
@@ -95,26 +95,51 @@ class Zod2Cpp extends core_1.Zod2X {
95
95
  return `${namespace}::${typeName}`;
96
96
  }
97
97
  addExtendedType(name, parentNamespace, aliasOf, opt) {
98
+ var _a;
98
99
  const extendedType = (opt === null || opt === void 0 ? void 0 : opt.isInternal)
99
100
  ? aliasOf
100
101
  : this.getTypeFromExternalNamespace(parentNamespace, aliasOf);
102
+ const templates = (_a = opt === null || opt === void 0 ? void 0 : opt.templates) !== null && _a !== void 0 ? _a : "";
101
103
  if ((opt === null || opt === void 0 ? void 0 : opt.type) === "union" || (opt === null || opt === void 0 ? void 0 : opt.type) === "alias") {
102
- this.push0(`using ${name} = ${extendedType};\n`);
104
+ this.push0(`using ${name} = ${extendedType}${templates};\n`);
103
105
  }
104
106
  else {
105
107
  if (this.opt.outType === "class") {
106
- this.push0(`class ${name} : public ${extendedType} {};\n`);
108
+ this.push0(`class ${name} : public ${extendedType}${templates} {};\n`);
107
109
  }
108
110
  else {
109
- this.push0(`struct ${name} : public ${extendedType} {};\n`);
111
+ this.push0(`struct ${name} : public ${extendedType}${templates} {};\n`);
110
112
  }
111
113
  }
114
+ if ((opt === null || opt === void 0 ? void 0 : opt.type) !== "alias" && parentNamespace !== undefined) {
115
+ this._addExtendedTypeSerializer(name, parentNamespace);
116
+ this._addExtendedTypeDeserializer(name, parentNamespace);
117
+ }
118
+ }
119
+ getGenericTemplatesTranslation(data) {
120
+ if ((data instanceof core_1.ASTObject || data instanceof core_1.ASTDefinition) &&
121
+ data.templatesTranslation.length > 0) {
122
+ return ("<" +
123
+ data.templatesTranslation
124
+ .map((t) => {
125
+ if (this.isExternalTypeImport(t)) {
126
+ this.addExternalTypeImport(t);
127
+ return this.getTypeFromExternalNamespace(t.parentNamespace, t.aliasOf);
128
+ }
129
+ else {
130
+ return t.aliasOf;
131
+ }
132
+ })
133
+ .join(", ") +
134
+ ">");
135
+ }
112
136
  }
113
137
  checkExtendedTypeInclusion(data, type) {
114
138
  if (this.isExternalTypeImport(data)) {
115
139
  if (data.aliasOf) {
116
140
  this.addExtendedType(data.name, data.parentNamespace, data.aliasOf, {
117
141
  type,
142
+ templates: this.getGenericTemplatesTranslation(data),
118
143
  });
119
144
  this.addExternalTypeImport(data);
120
145
  }
@@ -124,6 +149,7 @@ class Zod2Cpp extends core_1.Zod2X {
124
149
  this.addExtendedType(data.name, data.parentNamespace, data.aliasOf, {
125
150
  type,
126
151
  isInternal: true,
152
+ templates: this.getGenericTemplatesTranslation(data),
127
153
  });
128
154
  return true;
129
155
  }
@@ -158,12 +184,14 @@ class Zod2Cpp extends core_1.Zod2X {
158
184
  }
159
185
  getLiteralStringType(value, parentEnumNameKey) {
160
186
  var _a;
161
- return ((_a = parentEnumNameKey === null || parentEnumNameKey === void 0 ? void 0 : parentEnumNameKey[0]) !== null && _a !== void 0 ? _a : (isNaN(Number(value))
162
- ? this.getStringType()
163
- : this.getNumberType(Number.isInteger(value), {
164
- min: value,
165
- max: value,
166
- })));
187
+ return ((_a = parentEnumNameKey === null || parentEnumNameKey === void 0 ? void 0 : parentEnumNameKey[0]) !== null && _a !== void 0 ? _a : (typeof value === "boolean"
188
+ ? this.getBooleanType()
189
+ : isNaN(Number(value))
190
+ ? this.getStringType()
191
+ : this.getNumberType(Number.isInteger(value), {
192
+ min: value,
193
+ max: value,
194
+ })));
167
195
  }
168
196
  /** Ex: std::unordered_map<TypeA> */
169
197
  getMapType(keyType, valueType) {
@@ -177,6 +205,16 @@ class Zod2Cpp extends core_1.Zod2X {
177
205
  this.imports.add(this.lib.optional);
178
206
  return `boost::optional<${type}>`;
179
207
  }
208
+ _addExtendedTypeSerializer(typeName, parentNamespace) {
209
+ this._push0(this.serializers, `inline void to_json(${nlohmann_1.NLOHMANN}& j, const ${typeName}& x) {`);
210
+ this._push1(this.serializers, `${parentNamespace}::to_json(j, x);`);
211
+ this._push0(this.serializers, "}\n");
212
+ }
213
+ _addExtendedTypeDeserializer(typeName, parentNamespace) {
214
+ this._push0(this.serializers, `inline void from_json(const ${nlohmann_1.NLOHMANN}& j, ${typeName}& x) {`);
215
+ this._push1(this.serializers, `${parentNamespace}::from_json(j, x);`);
216
+ this._push0(this.serializers, "}\n");
217
+ }
180
218
  transpileAliasedType(data) {
181
219
  if (this.checkExtendedTypeInclusion(data, "alias")) {
182
220
  return;
@@ -269,6 +307,7 @@ class Zod2Cpp extends core_1.Zod2X {
269
307
  return {
270
308
  type: this.getAttributeType(i),
271
309
  discriminantValue: (_a = i.constraints) === null || _a === void 0 ? void 0 : _a.discriminantValue,
310
+ templates: this.getGenericTemplatesTranslation(i),
272
311
  };
273
312
  });
274
313
  const attributesTypes = attributesData.map((i) => i.type);
@@ -288,6 +327,14 @@ class Zod2Cpp extends core_1.Zod2X {
288
327
  this._transpileStructAsStruct(data);
289
328
  }
290
329
  }
330
+ _getTemplates(templates) {
331
+ return {
332
+ templateDefinition: templates.size > 0
333
+ ? `template<${[...templates].map((i) => "typename " + i).join(", ")}>`
334
+ : "",
335
+ templateList: templates.size > 0 ? `<${[...templates].join(", ")}>` : "",
336
+ };
337
+ }
291
338
  /** Ex:
292
339
  * struct MyStruct {
293
340
  * TypeA attribute1;
@@ -295,6 +342,10 @@ class Zod2Cpp extends core_1.Zod2X {
295
342
  * }
296
343
  */
297
344
  _transpileStructAsStruct(data) {
345
+ const { templateDefinition } = this._getTemplates(data.templates);
346
+ if (templateDefinition) {
347
+ this.push0(templateDefinition);
348
+ }
298
349
  this.push0(`struct ${data.name} {`);
299
350
  const serializeData = [];
300
351
  Object.entries(data.properties).forEach(([key, value]) => {
@@ -308,8 +359,8 @@ class Zod2Cpp extends core_1.Zod2X {
308
359
  });
309
360
  });
310
361
  this.push0("};\n");
311
- this._createStructSerializer(data.name, serializeData);
312
- this._createStructDeserializer(data.name, serializeData);
362
+ this._createStructSerializer(data.name, serializeData, data.templates);
363
+ this._createStructDeserializer(data.name, serializeData, data.templates);
313
364
  }
314
365
  /** Ex:
315
366
  * class MyClass {
@@ -328,6 +379,10 @@ class Zod2Cpp extends core_1.Zod2X {
328
379
  * }
329
380
  */
330
381
  _transpileStructAsClass(data) {
382
+ const { templateDefinition } = this._getTemplates(data.templates);
383
+ if (templateDefinition) {
384
+ this.push0(templateDefinition);
385
+ }
331
386
  this.push0(`class ${data.name} {`);
332
387
  this.push0(`private:`);
333
388
  const setterGetter = [];
@@ -349,8 +404,8 @@ class Zod2Cpp extends core_1.Zod2X {
349
404
  this.push1(`virtual ~${data.name}() = default;`);
350
405
  setterGetter.forEach((i) => this.push1(i));
351
406
  this.push0("};\n");
352
- this._createClassSerializer(data.name, serializeData);
353
- this._createClassDeserializer(data.name, serializeData);
407
+ this._createClassSerializer(data.name, serializeData, data.templates);
408
+ this._createClassDeserializer(data.name, serializeData, data.templates);
354
409
  }
355
410
  /**
356
411
  * @description Transpiles an individual member of a C++ class based on the provided ASTNode.
@@ -414,10 +469,15 @@ class Zod2Cpp extends core_1.Zod2X {
414
469
  * }
415
470
  * @param parent - Name of the serialized structure
416
471
  * @param childs - Structure attributes data.
472
+ * @param templates - Generic templates string (if any)
417
473
  */
418
- _createStructSerializer(parent, childs) {
474
+ _createStructSerializer(parent, childs, templates) {
419
475
  const prefix = this.opt.namespace ? `${this.opt.namespace}::` : "";
420
- this._push0(this.serializers, `inline void to_json(${nlohmann_1.NLOHMANN}& j, const ${parent}& x) {`);
476
+ const { templateDefinition, templateList } = this._getTemplates(templates);
477
+ if (templateDefinition) {
478
+ this._push0(this.serializers, templateDefinition);
479
+ }
480
+ this._push0(this.serializers, `inline void to_json(${nlohmann_1.NLOHMANN}& j, const ${parent}${templateList}& x) {`);
421
481
  childs.forEach((i) => {
422
482
  if (i.required) {
423
483
  this._push1(this.serializers, `j["${i.origName}"] = x.${i.snakeName};`);
@@ -438,10 +498,15 @@ class Zod2Cpp extends core_1.Zod2X {
438
498
  * }
439
499
  * @param parent - Name of the deserialized structure
440
500
  * @param childs - Structure attributes data.
501
+ * @param templates - Generic templates string (if any)
441
502
  */
442
- _createStructDeserializer(parent, childs) {
503
+ _createStructDeserializer(parent, childs, templates) {
443
504
  const prefix = this.opt.namespace ? `${this.opt.namespace}::` : "";
444
- this._push0(this.serializers, `inline void from_json(const ${nlohmann_1.NLOHMANN}& j, ${parent}& x) {`);
505
+ const { templateDefinition, templateList } = this._getTemplates(templates);
506
+ if (templateDefinition) {
507
+ this._push0(this.serializers, templateDefinition);
508
+ }
509
+ this._push0(this.serializers, `inline void from_json(const ${nlohmann_1.NLOHMANN}& j, ${parent}${templateList}& x) {`);
445
510
  childs.forEach((i) => {
446
511
  if (i.required) {
447
512
  this._push1(this.serializers, `x.${i.snakeName} = j.at("${i.origName}").get<${i.typeName}>();`);
@@ -465,10 +530,15 @@ class Zod2Cpp extends core_1.Zod2X {
465
530
  * }
466
531
  * @param parent - Name of the serialized structure
467
532
  * @param childs - Structure attributes data.
533
+ * @param templates - Generic templates string (if any)
468
534
  */
469
- _createClassSerializer(parent, childs) {
535
+ _createClassSerializer(parent, childs, templates) {
470
536
  const prefix = this.opt.namespace ? `${this.opt.namespace}::` : "";
471
- this._push0(this.serializers, `inline void to_json(${nlohmann_1.NLOHMANN}& j, const ${parent}& x) {`);
537
+ const { templateDefinition, templateList } = this._getTemplates(templates);
538
+ if (templateDefinition) {
539
+ this._push0(this.serializers, templateDefinition);
540
+ }
541
+ this._push0(this.serializers, `inline void to_json(${nlohmann_1.NLOHMANN}& j, const ${parent}${templateList}& x) {`);
472
542
  childs.forEach((i) => {
473
543
  if (i.required) {
474
544
  this._push1(this.serializers, `j["${i.origName}"] = x.get_${i.snakeName}();`);
@@ -489,10 +559,15 @@ class Zod2Cpp extends core_1.Zod2X {
489
559
  * }
490
560
  * @param parent - Name of the deserialized structure
491
561
  * @param childs - Structure attributes data.
562
+ * @param templates - Generic templates string (if any)
492
563
  */
493
- _createClassDeserializer(parent, childs) {
564
+ _createClassDeserializer(parent, childs, templates) {
494
565
  const prefix = this.opt.namespace ? `${this.opt.namespace}::` : "";
495
- this._push0(this.serializers, `inline void from_json(const ${nlohmann_1.NLOHMANN}& j, ${parent}& x) {`);
566
+ const { templateDefinition, templateList } = this._getTemplates(templates);
567
+ if (templateDefinition) {
568
+ this._push0(this.serializers, templateDefinition);
569
+ }
570
+ this._push0(this.serializers, `inline void from_json(const ${nlohmann_1.NLOHMANN}& j, ${parent}${templateList}& x) {`);
496
571
  childs.forEach((i) => {
497
572
  if (i.required) {
498
573
  this._push1(this.serializers, `x.set_${i.snakeName}(j.at("${i.origName}").get<${i.typeName}>());`);
@@ -1,6 +1,7 @@
1
1
  import { ASTAliasedTypes, ASTEnum, ASTIntersection, ASTNode, ASTObject, ASTUnion, Zod2X } from "../../core";
2
2
  import { IZod2TsOpt } from "./options";
3
3
  export declare class Zod2Ts extends Zod2X<IZod2TsOpt> {
4
+ protected readonly commentKey = "//";
4
5
  constructor(opt?: IZod2TsOpt);
5
6
  protected runAfter(): void;
6
7
  protected runBefore(): void;
@@ -9,9 +10,10 @@ export declare class Zod2Ts extends Zod2X<IZod2TsOpt> {
9
10
  protected addExtendedType(name: string, parentNamespace: string, aliasOf: string, opt?: {
10
11
  type?: "union" | "d-union" | "alias";
11
12
  isInternal?: boolean;
13
+ templates?: string;
12
14
  }): void;
15
+ protected getGenericTemplatesTranslation(data: ASTNode): string | undefined;
13
16
  protected checkExtendedTypeInclusion(data: ASTNode, type?: "alias" | "union" | "d-union"): boolean;
14
- protected getComment: (data: string, indent?: string) => string;
15
17
  protected getAnyType: () => string;
16
18
  protected getBooleanType: () => string;
17
19
  protected getDateType: () => string;
@@ -10,7 +10,7 @@ const options_1 = require("./options");
10
10
  class Zod2Ts extends core_1.Zod2X {
11
11
  constructor(opt = {}) {
12
12
  super(Object.assign(Object.assign({}, options_1.defaultOpts), opt));
13
- this.getComment = (data, indent = "") => `${indent}// ${data}`;
13
+ this.commentKey = "//";
14
14
  this.getAnyType = () => "any";
15
15
  this.getBooleanType = () => "boolean";
16
16
  this.getDateType = () => "Date";
@@ -37,34 +37,55 @@ class Zod2Ts extends core_1.Zod2X {
37
37
  return `${namespace}.${typeName}`;
38
38
  }
39
39
  addExtendedType(name, parentNamespace, aliasOf, opt) {
40
+ var _a;
40
41
  const extendedType = (opt === null || opt === void 0 ? void 0 : opt.isInternal)
41
42
  ? aliasOf
42
43
  : this.getTypeFromExternalNamespace(parentNamespace, aliasOf);
44
+ const templates = (_a = opt === null || opt === void 0 ? void 0 : opt.templates) !== null && _a !== void 0 ? _a : "";
43
45
  if ((opt === null || opt === void 0 ? void 0 : opt.type) === "alias") {
44
- this.push0(`export type ${name} = ${extendedType};\n`);
46
+ this.push0(`export type ${name} = ${extendedType}${templates};\n`);
45
47
  }
46
48
  else if (this.opt.outType === "class") {
47
49
  if ((opt === null || opt === void 0 ? void 0 : opt.type) === "d-union") {
48
- this.push0(`export type ${name} = ${extendedType};\n`);
50
+ this.push0(`export type ${name} = ${extendedType}${templates};\n`);
49
51
  }
50
52
  else {
51
- this.push0(`export class ${name} extends ${extendedType} {}\n`);
53
+ this.push0(`export class ${name} extends ${extendedType}${templates} {}\n`);
52
54
  }
53
55
  }
54
56
  else {
55
57
  if ((opt === null || opt === void 0 ? void 0 : opt.type) === "union" || (opt === null || opt === void 0 ? void 0 : opt.type) === "d-union") {
56
- this.push0(`export type ${name} = ${extendedType};\n`);
58
+ this.push0(`export type ${name} = ${extendedType}${templates};\n`);
57
59
  }
58
60
  else {
59
- this.push0(`export interface ${name} extends ${extendedType} {}\n`);
61
+ this.push0(`export interface ${name} extends ${extendedType}${templates} {}\n`);
60
62
  }
61
63
  }
62
64
  }
65
+ getGenericTemplatesTranslation(data) {
66
+ if ((data instanceof core_1.ASTObject || data instanceof core_1.ASTDefinition) &&
67
+ data.templatesTranslation.length > 0) {
68
+ return ("<" +
69
+ data.templatesTranslation
70
+ .map((t) => {
71
+ if (this.isExternalTypeImport(t)) {
72
+ this.addExternalTypeImport(t);
73
+ return this.getTypeFromExternalNamespace(t.parentNamespace, t.aliasOf);
74
+ }
75
+ else {
76
+ return t.aliasOf;
77
+ }
78
+ })
79
+ .join(", ") +
80
+ ">");
81
+ }
82
+ }
63
83
  checkExtendedTypeInclusion(data, type) {
64
84
  if (this.isExternalTypeImport(data)) {
65
85
  if (data.aliasOf) {
66
86
  this.addExtendedType(data.name, data.parentNamespace, data.aliasOf, {
67
87
  type,
88
+ templates: this.getGenericTemplatesTranslation(data),
68
89
  });
69
90
  this.addExternalTypeImport(data);
70
91
  }
@@ -74,6 +95,7 @@ class Zod2Ts extends core_1.Zod2X {
74
95
  this.addExtendedType(data.name, data.parentNamespace, data.aliasOf, {
75
96
  type,
76
97
  isInternal: true,
98
+ templates: this.getGenericTemplatesTranslation(data),
77
99
  });
78
100
  return true;
79
101
  }
@@ -221,7 +243,8 @@ class Zod2Ts extends core_1.Zod2X {
221
243
  * }
222
244
  * */
223
245
  _transpileStructuAsInterface(data) {
224
- this.push0(`export interface ${data.name} {`);
246
+ const templates = data.templates.size > 0 ? `<${[...data.templates].join(", ")}>` : "";
247
+ this.push0(`export interface ${data.name}${templates} {`);
225
248
  for (const [key, value] of Object.entries(data.properties)) {
226
249
  this._transpileMember(this.opt.keepKeys === true ? key : case_1.default.camel(key), value);
227
250
  }
@@ -239,7 +262,8 @@ class Zod2Ts extends core_1.Zod2X {
239
262
  * }
240
263
  * */
241
264
  _transpileStructAsClass(data) {
242
- this.push0(`export class ${data.name} {`);
265
+ const templates = data.templates.size > 0 ? `<${[...data.templates].join(", ")}>` : "";
266
+ this.push0(`export class ${data.name}${templates} {`);
243
267
  const constructorBody = [];
244
268
  for (const [key, value] of Object.entries(data.properties)) {
245
269
  const keyName = this.opt.keepKeys === true ? key : case_1.default.camel(key);
@@ -247,7 +271,7 @@ class Zod2Ts extends core_1.Zod2X {
247
271
  constructorBody.push(`this.${keyName} = data.${keyName};`);
248
272
  }
249
273
  this.push0("");
250
- this.push1(`constructor(data: ${data.name}) {`);
274
+ this.push1(`constructor(data: ${data.name}${templates}) {`);
251
275
  constructorBody.forEach((i) => this.push2(i));
252
276
  this.push1("}");
253
277
  this.push0("}\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zod-to-x",
3
- "version": "1.4.8-dev.4",
3
+ "version": "1.5.0-dev.2",
4
4
  "description": "Multi language types generation from Zod schemas.",
5
5
  "main": "dist/index.js",
6
6
  "files": [