zod-to-x 2.0.2-dev.5 → 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 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) <sup>*(new)*</sup>
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 becosa an alias
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;
@@ -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
  * @returns The Protocol Buffers v3 definition as a string.
18
18
  */
19
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">): string;
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;
@@ -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
  });
@@ -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
- properties[key] = this._zodToAST(shape[key]);
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.
@@ -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: this._unionAstNodes(item.options).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: this._intersectAstNodes(item.left, item.right).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.
@@ -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
  },
@@ -89,6 +89,22 @@ export interface IZod2xMetadata {
89
89
  */
90
90
  aliasOf?: string;
91
91
  parentLayer?: IZod2xLayerMetadata;
92
+ /**
93
+ * For Layered Modeling defining generics.
94
+ * When a generic type is used with `useGenericType`, it stores the templates translation for
95
+ * the associated key.
96
+ */
97
+ genericTypes?: {
98
+ typeName: string;
99
+ layer: IZod2xLayerMetadata;
100
+ }[];
101
+ /**
102
+ * For Layered Modeling defining generics.
103
+ * Indicates when useGenericType was used to create a definition (set to true during Layer
104
+ * decorator) or if it is used inline as part of another definition (set to false).
105
+ * Defaults to false.
106
+ */
107
+ isGenericChild?: boolean;
92
108
  }
93
109
  declare module "zod/v4" {
94
110
  interface ZodType {
@@ -1,5 +1,5 @@
1
1
  import { z, ZodEnum, ZodNumber, ZodObject, ZodType } from "zod/v4";
2
- export type { ZodArray, ZodType, ZodIntersection, ZodObject, ZodEnum, ZodDiscriminatedUnion, ZodUnion, } from "zod/v4";
2
+ export type { ZodArray, ZodType, ZodIntersection, ZodObject, ZodEnum, ZodDiscriminatedUnion, ZodUnion, ZodLiteral, } from "zod/v4";
3
3
  export type ZodAnyUnionType = z.ZodUnion<any> | z.ZodDiscriminatedUnion<any>;
4
4
  type ZodNumberConstraints = {
5
5
  min?: number;
@@ -26,7 +26,8 @@ export declare enum ZodFirstPartyTypeKind {
26
26
  ZodTuple = "tuple",
27
27
  ZodOptional = "optional",
28
28
  ZodNullable = "nullable",
29
- ZodDefault = "default"
29
+ ZodDefault = "default",
30
+ ZodPromise = "promise"
30
31
  }
31
32
  /**
32
33
  * Zod's type is checked insted of instanceof to resolve Bun incomatibilities.
@@ -54,6 +55,7 @@ export declare class ZodHelpers {
54
55
  static isZodOptional(i: ZodType): i is z.ZodOptional<any>;
55
56
  static isZodNullable(i: ZodType): i is z.ZodNullable<any>;
56
57
  static isZodDefault(i: ZodType): i is z.ZodDefault<any>;
58
+ static isZodPromise<T extends ZodType>(i: ZodType): i is z.ZodPromise<T>;
57
59
  static isZodAnyUnionType(i: ZodType): i is z.ZodUnion<any> | z.ZodDiscriminatedUnion<readonly z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>[], string>;
58
60
  static isZodAnyNumberType(i: ZodType): i is z.ZodNumber | z.ZodBigInt;
59
61
  static isZodAnyMapType(i: ZodType): i is z.ZodRecord<any, any> | z.ZodMap<any, any>;
@@ -73,6 +75,22 @@ export declare class ZodHelpers {
73
75
  */
74
76
  static isTranspilerableAliasedZodType(zodType: string | ZodType, onlyArray?: boolean): boolean;
75
77
  static cloneZod(i: ZodType): z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
78
+ /**
79
+ * Zod2X generics are represented as Promise<"TypeName">.
80
+ * Ex: z.promise(z.literal("K")) will be transpiled to Template<K>
81
+ */
82
+ static isZod2XGeneric(i: ZodType): boolean;
76
83
  static createZodObject(properties: Map<string, ZodType>): ZodObject<any>;
77
84
  static getZodNumberConstraints(i: ZodNumber | z.ZodBigInt): ZodNumberConstraints;
78
85
  }
86
+ export declare function createGenericType(name: string): any;
87
+ /**
88
+ * Use generic types defined in a generic ZodObject by replacing them with the actual child types.
89
+ * It preserves the zod2x metadata including the genericTypes array which will be moved to the
90
+ * new type during layer modeling metadata assignment.
91
+ * @param genObj ZodObject with generic types.
92
+ * @param childrens Record of child types to replace generics.
93
+ * @returns The extended ZodObject with replaced generic types.
94
+ */
95
+ export declare function useGenericType(genObj: ZodObject<any>, childrens: Record<string, ZodType>, skipLazy: true): ZodObject<any>;
96
+ export declare function useGenericType(genObj: ZodObject<any>, childrens: Record<string, ZodType>, skipLazy?: false): z.ZodLazy<ZodObject<any>>;
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ZodHelpers = exports.ZodFirstPartyTypeKind = void 0;
4
+ exports.createGenericType = createGenericType;
5
+ exports.useGenericType = useGenericType;
6
+ const v4_1 = require("zod/v4");
4
7
  const zod_ext_1 = require("./zod_ext");
5
8
  var ZodFirstPartyTypeKind;
6
9
  (function (ZodFirstPartyTypeKind) {
@@ -24,6 +27,7 @@ var ZodFirstPartyTypeKind;
24
27
  ZodFirstPartyTypeKind["ZodOptional"] = "optional";
25
28
  ZodFirstPartyTypeKind["ZodNullable"] = "nullable";
26
29
  ZodFirstPartyTypeKind["ZodDefault"] = "default";
30
+ ZodFirstPartyTypeKind["ZodPromise"] = "promise";
27
31
  })(ZodFirstPartyTypeKind || (exports.ZodFirstPartyTypeKind = ZodFirstPartyTypeKind = {}));
28
32
  /**
29
33
  * Zod's type is checked insted of instanceof to resolve Bun incomatibilities.
@@ -119,6 +123,10 @@ class ZodHelpers {
119
123
  var _a;
120
124
  return ((_a = i === null || i === void 0 ? void 0 : i.def) === null || _a === void 0 ? void 0 : _a.type) === ZodFirstPartyTypeKind.ZodDefault;
121
125
  }
126
+ static isZodPromise(i) {
127
+ var _a;
128
+ return ((_a = i === null || i === void 0 ? void 0 : i.def) === null || _a === void 0 ? void 0 : _a.type) === ZodFirstPartyTypeKind.ZodPromise;
129
+ }
122
130
  static isZodAnyUnionType(i) {
123
131
  return this.isZodUnion(i) || this.isZodDiscriminatedUnion(i);
124
132
  }
@@ -170,6 +178,15 @@ class ZodHelpers {
170
178
  static cloneZod(i) {
171
179
  return i.meta(Object.assign({}, (i.meta() || {})));
172
180
  }
181
+ /**
182
+ * Zod2X generics are represented as Promise<"TypeName">.
183
+ * Ex: z.promise(z.literal("K")) will be transpiled to Template<K>
184
+ */
185
+ static isZod2XGeneric(i) {
186
+ return (this.isZodPromise(i) &&
187
+ this.isZodLiteral(i.unwrap()) &&
188
+ typeof i.unwrap().def.values[0] === "string");
189
+ }
173
190
  static createZodObject(properties) {
174
191
  return zod_ext_1.Extended.getZ().object(Object.fromEntries(properties));
175
192
  }
@@ -191,3 +208,34 @@ class ZodHelpers {
191
208
  }
192
209
  }
193
210
  exports.ZodHelpers = ZodHelpers;
211
+ function createGenericType(name) {
212
+ return zod_ext_1.Extended.getZ().promise(zod_ext_1.Extended.getZ().literal(name));
213
+ }
214
+ function useGenericType(genObj, childrens, skipLazy) {
215
+ const builder = () => {
216
+ let extended = genObj;
217
+ for (const [key, property] of Object.entries(genObj.shape)) {
218
+ if (ZodHelpers.isZod2XGeneric(property)) {
219
+ const childType = childrens[key];
220
+ if (!childType) {
221
+ throw new Error(`Missing child type for generic property ${key} in ${genObj._zod2x.typeName}.`);
222
+ }
223
+ const zod2xMeta = structuredClone(extended._zod2x);
224
+ extended = extended.extend({ [key]: childType });
225
+ extended._zod2x = zod2xMeta;
226
+ extended._zod2x.isGenericChild = false;
227
+ if (!Array.isArray(extended._zod2x.genericTypes)) {
228
+ extended._zod2x.genericTypes = [];
229
+ }
230
+ if (childType._zod2x !== undefined) {
231
+ extended._zod2x.genericTypes.push({
232
+ typeName: childType._zod2x.typeName,
233
+ layer: childType._zod2x.layer,
234
+ });
235
+ }
236
+ }
237
+ }
238
+ return extended;
239
+ };
240
+ return skipLazy ? builder() : v4_1.z.lazy(builder);
241
+ }
@@ -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": "2.0.2-dev.5",
3
+ "version": "2.1.0-dev.2",
4
4
  "description": "Multi language types generation from Zod schemas.",
5
5
  "main": "dist/index.js",
6
6
  "files": [