zod-to-x 2.0.2-dev.2 → 2.1.0-dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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
  };
@@ -1,6 +1,5 @@
1
- import { ZodDiscriminatedUnion, ZodUnion } from "zod";
2
1
  import { IZod2AstOpt } from "../../core";
3
- import { ZodObject } from "../../lib/zod_helpers";
2
+ import { ZodObject, ZodDiscriminatedUnion, ZodUnion } from "../../lib/zod_helpers";
4
3
  import { IZod2ProtoV3Opt } from "./options";
5
4
  /**
6
5
  * Converts a Zod schema into a Protocol Buffers v3 definition.
@@ -17,5 +16,5 @@ import { IZod2ProtoV3Opt } from "./options";
17
16
  * definition.
18
17
  * @returns The Protocol Buffers v3 definition as a string.
19
18
  */
20
- export declare function zod2ProtoV3(schema: ZodObject<any> | ZodDiscriminatedUnion<string, any> | ZodUnion<any>, // TODO: fix any to force only ZodObjects
21
- opt?: Pick<IZod2AstOpt, "strict"> & Pick<IZod2ProtoV3Opt, "packageName" | "keepKeys" | "header" | "indent" | "includeComments" | "encodeDoubleAsInt">): string;
19
+ export declare function zod2ProtoV3(schema: ZodObject<any> | ZodDiscriminatedUnion | ZodUnion<any>, // TODO: fix any to force only ZodObjects
20
+ opt?: Pick<IZod2AstOpt, "strict"> & Pick<IZod2ProtoV3Opt, "packageName" | "keepKeys" | "header" | "indent" | "includeComments" | "encodeDoubleAsInt" | "useExplicitOptional">): string;
@@ -26,11 +26,11 @@ const allowedKeyTypes = [
26
26
  class Zod2ProtoV3 extends core_1.Zod2X {
27
27
  constructor(opt = {}) {
28
28
  super(Object.assign(Object.assign({}, options_1.defaultOpts), opt));
29
+ this.commentKey = "//";
29
30
  this.getUnionType = () => {
30
31
  /** Covered by "transpileUnion" method */
31
32
  return "";
32
33
  };
33
- this.getComment = (data, indent = "") => `${indent}// ${data}`;
34
34
  this.getBooleanType = () => "bool";
35
35
  this.getStringType = () => "string";
36
36
  this.getNumberType = (isInt, range) => {
@@ -101,6 +101,10 @@ class Zod2ProtoV3 extends core_1.Zod2X {
101
101
  // Zod2ProtoV3 does not support layered modeling.
102
102
  return "";
103
103
  }
104
+ getGenericTemplatesTranslation(data) {
105
+ // Zod2ProtoV3 does not support layered modeling nor generics.
106
+ return "";
107
+ }
104
108
  addExtendedType(name, parentNamespace, aliasOf) {
105
109
  // Zod2ProtoV3 does not support layered modeling.
106
110
  return;
@@ -160,11 +164,17 @@ class Zod2ProtoV3 extends core_1.Zod2X {
160
164
  this.addComment(data.description);
161
165
  this.push0(`message ${data.name} {`);
162
166
  Object.entries(data.properties).forEach(([key, value], index) => {
163
- if (value.description && !this.isTranspilerable(value)) {
167
+ if (this.opt.includeComments &&
168
+ value.description &&
169
+ !value.name &&
170
+ !this.isTranspilerable(value)) {
164
171
  // Avoid duplicated descriptions for transpiled items.
165
172
  this.addComment(value.description, `\n${this.indent[1]}`);
166
173
  }
167
- 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};`);
168
178
  });
169
179
  this.push0("}\n");
170
180
  }
@@ -32,14 +32,19 @@ export type ASTAliasedTypes = Simple.ASTString | Simple.ASTNumber | Simple.ASTBo
32
32
  /**
33
33
  * Represents a usage of any existing ASTNode. Used to reduce node size.
34
34
  */
35
- export declare class 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.
@@ -315,7 +352,7 @@ class Zod2Ast {
315
352
  var _a, _b, _c;
316
353
  const def = schema.def;
317
354
  const discriminator = zod_helpers_1.ZodHelpers.isZodDiscriminatedUnion(schema)
318
- ? schema.def.discriminator
355
+ ? schema._zod.def.discriminator
319
356
  : undefined;
320
357
  const { name, parentFile, parentNamespace, aliasOf } = this._getNames(schema);
321
358
  const item = new core_1.ASTUnion({
@@ -340,12 +377,17 @@ class Zod2Ast {
340
377
  this.warnings.push(`[affected type: ${name}] Using ZodUnion is a bad data modeling practice. ` +
341
378
  "Use ZodDiscriminatedUnion instead, or disable strict mode if not possible.");
342
379
  }
380
+ const unifiedProperties = this._unionAstNodes(item.options);
343
381
  item.newObject = new core_1.ASTObject({
344
382
  name,
345
- properties: 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
  },