zod-to-x 2.4.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Zod2Ast = void 0;
4
4
  const core_1 = require("../core");
5
+ const zod2x_metadata_1 = require("../lib/zod2x_metadata");
5
6
  const zod_helpers_1 = require("../lib/zod_helpers");
6
7
  const logger_1 = require("../utils/logger");
7
8
  const errors_1 = require("./errors");
@@ -14,6 +15,7 @@ class Zod2Ast {
14
15
  constructor(opt = {}) {
15
16
  var _a;
16
17
  this.nodes = new Map();
18
+ this.nodeSchemas = new Map();
17
19
  this.lazyPointers = [];
18
20
  this.warnings = [];
19
21
  this.opt = Object.assign(Object.assign({}, opt), { strict: (_a = opt.strict) !== null && _a !== void 0 ? _a : true });
@@ -30,6 +32,77 @@ class Zod2Ast {
30
32
  parentFile === undefined ||
31
33
  ((_a = this.opt.layer) === null || _a === void 0 ? void 0 : _a.file) === parentFile);
32
34
  }
35
+ /**
36
+ * Returns the key used to store a transpilerable node. Types imported from other files are
37
+ * namespaced by their file, so they do not clash with local types that share the same name.
38
+ * Aliases of external types are local definitions.
39
+ */
40
+ _getNodeKey(item) {
41
+ return item.parentFile !== undefined && item.aliasOf === undefined
42
+ ? `${item.parentFile}::${item.name}`
43
+ : item.name;
44
+ }
45
+ /**
46
+ * Determines if two schemas that share the same type name represent the same definition.
47
+ * Schemas created from the same definition (`.zod2x()`, `.describe()`, `.meta()`, layer
48
+ * aliases) and instances of the same generic type are considered equal. Otherwise, a shallow
49
+ * structural comparison is performed.
50
+ */
51
+ _isSameDefinition(a, b) {
52
+ var _a, _b;
53
+ if (a === b || a.def === b.def) {
54
+ return true;
55
+ }
56
+ if (((_a = (0, zod2x_metadata_1.resolveZod2xMetadata)(a)) === null || _a === void 0 ? void 0 : _a.genericTypes) !== undefined ||
57
+ ((_b = (0, zod2x_metadata_1.resolveZod2xMetadata)(b)) === null || _b === void 0 ? void 0 : _b.genericTypes) !== undefined) {
58
+ return true;
59
+ }
60
+ if (a.def.type !== b.def.type) {
61
+ return false;
62
+ }
63
+ const sameSignatures = (x, y) => x.length === y.length &&
64
+ x.every((i, j) => zod_helpers_1.ZodHelpers.getShallowSignature(i) === zod_helpers_1.ZodHelpers.getShallowSignature(y[j]));
65
+ if (zod_helpers_1.ZodHelpers.isZodObject(a) && zod_helpers_1.ZodHelpers.isZodObject(b)) {
66
+ const keysA = Object.keys(a.def.shape);
67
+ const keysB = Object.keys(b.def.shape);
68
+ return (keysA.length === keysB.length &&
69
+ keysA.every((key) => key in b.def.shape) &&
70
+ sameSignatures(keysA.map((key) => a.def.shape[key]), keysA.map((key) => b.def.shape[key])));
71
+ }
72
+ else if (zod_helpers_1.ZodHelpers.isZodEnum(a) && zod_helpers_1.ZodHelpers.isZodEnum(b)) {
73
+ return JSON.stringify(a.enum) === JSON.stringify(b.enum);
74
+ }
75
+ else if (zod_helpers_1.ZodHelpers.isZodAnyUnionType(a) && zod_helpers_1.ZodHelpers.isZodAnyUnionType(b)) {
76
+ return sameSignatures(a.def.options, b.def.options);
77
+ }
78
+ else if (zod_helpers_1.ZodHelpers.isZodIntersection(a) && zod_helpers_1.ZodHelpers.isZodIntersection(b)) {
79
+ return sameSignatures([a.def.left, a.def.right], [b.def.left, b.def.right]);
80
+ }
81
+ else if (zod_helpers_1.ZodHelpers.isZodArray(a) && zod_helpers_1.ZodHelpers.isZodArray(b)) {
82
+ return sameSignatures([a.element], [b.element]);
83
+ }
84
+ return true;
85
+ }
86
+ /**
87
+ * Checks if a transpilerable node is already registered.
88
+ *
89
+ * @throws AstTypeNameDefinitionError - If the node key is used by a different schema.
90
+ */
91
+ _hasNode(key, schema) {
92
+ const registered = this.nodeSchemas.get(key);
93
+ if (registered === undefined) {
94
+ return false;
95
+ }
96
+ if (!this._isSameDefinition(registered, schema)) {
97
+ throw new errors_1.AstTypeNameDefinitionError(`Type name '${key}' is used by different schemas. Each transpilerable type ` +
98
+ `must have a unique name.`);
99
+ }
100
+ return true;
101
+ }
102
+ _setNode(key, schema, node) {
103
+ this.nodes.set(key, node);
104
+ this.nodeSchemas.set(key, schema);
105
+ }
33
106
  /**
34
107
  * Check if the layer of the item is compatible with the layer of the schema. If does and the
35
108
  * transpilerable item is in a different file, it returns the file name.
@@ -44,19 +117,24 @@ class Zod2Ast {
44
117
  if (this.opt.layer !== undefined && (metadata === null || metadata === void 0 ? void 0 : metadata.layer) !== undefined) {
45
118
  if (Array.isArray(metadata.genericTypes)) {
46
119
  // 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)) {
120
+ if (metadata.genericTypes.some((i) => i.layer !== undefined && this.opt.layer.index < i.layer.index)) {
48
121
  throw new errors_1.BadLayerDefinitionError(`${itemName}: Layer with number ${this.opt.layer.index} can only use models` +
49
122
  `from the same or lower layer. Review templates used for generic ` +
50
123
  `type ${metadata.typeName}.`);
51
124
  }
52
125
  }
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,
126
+ const templatesTranslation = (_a = metadata.genericTypes) === null || _a === void 0 ? void 0 : _a.map((i) => {
127
+ var _a;
128
+ if (i.typeName === undefined) {
129
+ // Not named types (basic types, arrays, ...) are used directly.
130
+ return { type: this._zodToAST(i.schema) };
131
+ }
132
+ const isExternal = i.layer !== undefined && ((_a = this.opt.layer) === null || _a === void 0 ? void 0 : _a.file) !== i.layer.file;
133
+ return {
134
+ parentFile: isExternal ? i.layer.file : undefined,
135
+ parentNamespace: isExternal ? i.layer.namespace : undefined,
58
136
  aliasOf: i.typeName,
59
- });
137
+ };
60
138
  });
61
139
  if (metadata.layer.file === this.opt.layer.file) {
62
140
  // Case 1: Only layer exists and belongs to the same file
@@ -113,7 +191,7 @@ class Zod2Ast {
113
191
  _createDefinition(node, constraints = {}, templatesTranslation = []) {
114
192
  return new core_1.ASTDefinition({
115
193
  name: node.name,
116
- instanceType: node.constructor.name,
194
+ instanceType: (0, core_1.getASTKind)(node),
117
195
  parentFile: node.parentFile,
118
196
  parentNamespace: node.parentNamespace,
119
197
  aliasOf: node.aliasOf,
@@ -183,13 +261,13 @@ class Zod2Ast {
183
261
  */
184
262
  _unionAstNodes(options) {
185
263
  let typeA, typeB;
186
- const data = options.map((i) => this.nodes.get(i.name));
264
+ const data = options.map((i) => this.nodes.get(this._getNodeKey(i)));
187
265
  const properties = data.reduce((acc, i, j) => {
188
266
  for (const key in i.properties) {
189
267
  if (acc[key]) {
190
268
  acc[key] = Object.assign(Object.create(Object.getPrototypeOf(acc[key])), acc[key]);
191
- typeA = acc[key].constructor.name;
192
- typeB = i.properties[key].constructor.name;
269
+ typeA = (0, core_1.getASTKind)(acc[key]);
270
+ typeB = (0, core_1.getASTKind)(i.properties[key]);
193
271
  if (typeA !== typeB) {
194
272
  this.warnings.push(`Merging properties with different types: ${typeA} ` +
195
273
  `(from a previous variant) and ${typeB} ` +
@@ -243,7 +321,7 @@ class Zod2Ast {
243
321
  */
244
322
  _getNames(schema) {
245
323
  var _a, _b, _c;
246
- const name = (_a = schema._zod2x) === null || _a === void 0 ? void 0 : _a.typeName;
324
+ const name = (_a = (0, zod2x_metadata_1.resolveZod2xMetadata)(schema)) === null || _a === void 0 ? void 0 : _a.typeName;
247
325
  if (!name) {
248
326
  let itemProperties = ["Unknown type"];
249
327
  if (zod_helpers_1.ZodHelpers.isZodObject(schema)) {
@@ -252,15 +330,15 @@ class Zod2Ast {
252
330
  else if (zod_helpers_1.ZodHelpers.isZodAnyUnionType(schema)) {
253
331
  itemProperties = schema.def.options.map((i) => {
254
332
  var _a;
255
- return ((_a = i._zod2x) === null || _a === void 0 ? void 0 : _a.typeName) || i.def.type;
333
+ return ((_a = (0, zod2x_metadata_1.resolveZod2xMetadata)(i)) === null || _a === void 0 ? void 0 : _a.typeName) || i.def.type;
256
334
  });
257
335
  }
258
336
  else if (zod_helpers_1.ZodHelpers.isZodIntersection(schema)) {
259
337
  const left = schema.def.left;
260
338
  const right = schema.def.right;
261
339
  itemProperties = [
262
- ((_b = left._zod2x) === null || _b === void 0 ? void 0 : _b.typeName) || left.def.type,
263
- ((_c = right._zod2x) === null || _c === void 0 ? void 0 : _c.typeName) || right.def.type,
340
+ ((_b = (0, zod2x_metadata_1.resolveZod2xMetadata)(left)) === null || _b === void 0 ? void 0 : _b.typeName) || left.def.type,
341
+ ((_c = (0, zod2x_metadata_1.resolveZod2xMetadata)(right)) === null || _c === void 0 ? void 0 : _c.typeName) || right.def.type,
264
342
  ];
265
343
  }
266
344
  else if (zod_helpers_1.ZodHelpers.isZodEnum(schema)) {
@@ -270,7 +348,8 @@ class Zod2Ast {
270
348
  `avoid nesting schemas definitions. Otherwise, use zod2x method to provide one. ` +
271
349
  `Affected type properties: ${itemProperties.join(", ")}`);
272
350
  }
273
- return Object.assign({ name }, this._getTranspilerableFile(name, schema._zod2x));
351
+ const metadata = (0, zod2x_metadata_1.resolveZod2xMetadata)(schema);
352
+ return Object.assign({ name, genericOf: metadata === null || metadata === void 0 ? void 0 : metadata.genericOf }, this._getTranspilerableFile(name, metadata));
274
353
  }
275
354
  /**
276
355
  * Generates an Abstract Syntax Tree (AST) definition for a Zod enum schema.
@@ -293,8 +372,9 @@ class Zod2Ast {
293
372
  aliasOf,
294
373
  isFromDiscriminatedUnion: opt === null || opt === void 0 ? void 0 : opt.isInjectedEnum,
295
374
  });
296
- if (!this.nodes.has(name)) {
297
- this.nodes.set(name, item);
375
+ const key = this._getNodeKey(item);
376
+ if (!this._hasNode(key, schema)) {
377
+ this._setNode(key, schema, item);
298
378
  }
299
379
  return this._createDefinition(item);
300
380
  }
@@ -309,10 +389,11 @@ class Zod2Ast {
309
389
  */
310
390
  _getObjectAst(schema, opt) {
311
391
  var _a;
312
- const { name, parentFile, parentNamespace, aliasOf, templatesTranslation, isGenericChild } = this._getNames(schema);
392
+ const { name, parentFile, parentNamespace, aliasOf, templatesTranslation, isGenericChild, genericOf, } = this._getNames(schema);
313
393
  let discriminantValue = undefined;
314
394
  const shape = schema.def.shape;
315
- if (!this.nodes.has(name)) {
395
+ const key = this._getNodeKey({ name, parentFile, aliasOf });
396
+ if (!this._hasNode(key, schema)) {
316
397
  const properties = {};
317
398
  const templates = new Set();
318
399
  for (const key in shape) {
@@ -332,7 +413,7 @@ class Zod2Ast {
332
413
  if (opt === null || opt === void 0 ? void 0 : opt.skipLayerClass) {
333
414
  return {}; // Layer classes are not transpilerable
334
415
  }
335
- this.nodes.set(name, new core_1.ASTObject({
416
+ this._setNode(key, schema, new core_1.ASTObject({
336
417
  name,
337
418
  properties,
338
419
  description: (_a = schema.meta()) === null || _a === void 0 ? void 0 : _a.description,
@@ -341,9 +422,10 @@ class Zod2Ast {
341
422
  aliasOf,
342
423
  templates,
343
424
  templatesTranslation: templatesTranslation || [],
425
+ genericOf,
344
426
  }));
345
427
  }
346
- const item = this.nodes.get(name);
428
+ const item = this.nodes.get(key);
347
429
  if (opt === null || opt === void 0 ? void 0 : opt.discriminantKey) {
348
430
  if (Object.keys(item.properties).includes(opt.discriminantKey)) {
349
431
  const key = opt.discriminantKey;
@@ -357,7 +439,7 @@ class Zod2Ast {
357
439
  }
358
440
  }
359
441
  }
360
- return this._createDefinition(this.nodes.get(name), { discriminantValue }, isGenericChild ? undefined : templatesTranslation);
442
+ return this._createDefinition(item, { discriminantValue }, isGenericChild ? undefined : templatesTranslation);
361
443
  }
362
444
  /**
363
445
  * Generates an Abstract Syntax Tree (AST) definition for a Zod union schema.
@@ -415,8 +497,9 @@ class Zod2Ast {
415
497
  templatesTranslation: [],
416
498
  });
417
499
  }
418
- if (name && !this.nodes.has(name)) {
419
- this.nodes.set(name, item);
500
+ const key = this._getNodeKey(item);
501
+ if (!this._hasNode(key, schema)) {
502
+ this._setNode(key, schema, item);
420
503
  }
421
504
  return this._createDefinition(item);
422
505
  }
@@ -464,8 +547,9 @@ class Zod2Ast {
464
547
  templatesTranslation: [],
465
548
  });
466
549
  }
467
- if (name && !this.nodes.has(name)) {
468
- this.nodes.set(name, item);
550
+ const key = this._getNodeKey(item);
551
+ if (!this._hasNode(key, schema)) {
552
+ this._setNode(key, schema, item);
469
553
  }
470
554
  return this._createDefinition(item);
471
555
  }
@@ -487,14 +571,16 @@ class Zod2Ast {
487
571
  parentNamespace,
488
572
  aliasOf,
489
573
  });
490
- if (!this.nodes.has(name)) {
491
- this.nodes.set(name, item);
574
+ const key = this._getNodeKey(item);
575
+ if (!this._hasNode(key, schema)) {
576
+ this._setNode(key, schema, item);
492
577
  }
493
578
  return this._createDefinition(item);
494
579
  }
495
580
  _getAliasAst(schema, item) {
496
581
  var _a;
497
- if (((_a = schema._zod2x) === null || _a === void 0 ? void 0 : _a.typeName) === undefined || this.opt.skipBasicTypes === true) {
582
+ if (((_a = (0, zod2x_metadata_1.resolveZod2xMetadata)(schema)) === null || _a === void 0 ? void 0 : _a.typeName) === undefined ||
583
+ this.opt.skipBasicTypes === true) {
498
584
  return item;
499
585
  }
500
586
  const { name, parentFile, parentNamespace, aliasOf } = this._getNames(schema);
@@ -502,8 +588,9 @@ class Zod2Ast {
502
588
  item.parentFile = parentFile;
503
589
  item.parentNamespace = parentNamespace;
504
590
  item.aliasOf = aliasOf;
505
- if (!this.nodes.has(name)) {
506
- this.nodes.set(name, item);
591
+ const key = this._getNodeKey(item);
592
+ if (!this._hasNode(key, schema)) {
593
+ this._setNode(key, schema, item);
507
594
  }
508
595
  return this._createDefinition(item);
509
596
  }
@@ -513,7 +600,7 @@ class Zod2Ast {
513
600
  * @returns
514
601
  */
515
602
  _zodToAST(schema, opt) {
516
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
603
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
517
604
  if (zod_helpers_1.ZodHelpers.isZodString(schema)) {
518
605
  return this._getAliasAst(schema, new core_1.ASTString({ description: (_a = schema.meta()) === null || _a === void 0 ? void 0 : _a.description }));
519
606
  }
@@ -544,19 +631,33 @@ class Zod2Ast {
544
631
  subSchema.description = ((_g = schema.meta()) === null || _g === void 0 ? void 0 : _g.description) || subSchema.description;
545
632
  return subSchema;
546
633
  }
547
- else if (zod_helpers_1.ZodHelpers.isZodDefault(schema)) {
548
- const subSchema = this._zodToAST(schema.def.defaultValue());
634
+ else if (zod_helpers_1.ZodHelpers.isZodNonOptional(schema)) {
635
+ const subSchema = this._zodToAST(schema.def.innerType, opt);
636
+ subSchema.isOptional = false;
549
637
  subSchema.description = ((_h = schema.meta()) === null || _h === void 0 ? void 0 : _h.description) || subSchema.description;
550
638
  return subSchema;
551
639
  }
640
+ else if (zod_helpers_1.ZodHelpers.isZodTransparentWrapper(schema)) {
641
+ const subSchema = this._zodToAST(schema.def.innerType, opt);
642
+ subSchema.description = ((_j = schema.meta()) === null || _j === void 0 ? void 0 : _j.description) || subSchema.description;
643
+ return subSchema;
644
+ }
645
+ else if (zod_helpers_1.ZodHelpers.isZodPipe(schema)) {
646
+ const subSchema = this._zodToAST(zod_helpers_1.ZodHelpers.getPipeModelSchema(schema), opt);
647
+ subSchema.description = ((_k = schema.meta()) === null || _k === void 0 ? void 0 : _k.description) || subSchema.description;
648
+ return subSchema;
649
+ }
650
+ else if (zod_helpers_1.ZodHelpers.isZodUnknown(schema)) {
651
+ return new core_1.ASTAny({ description: (_l = schema.meta()) === null || _l === void 0 ? void 0 : _l.description });
652
+ }
552
653
  else if (zod_helpers_1.ZodHelpers.isZodArray(schema)) {
553
654
  const isParentArray = (opt === null || opt === void 0 ? void 0 : opt.calledFromArray) !== true;
554
655
  const subSchema = this._zodToAST(schema.element, { calledFromArray: true });
555
- subSchema.description = ((_j = schema.meta()) === null || _j === void 0 ? void 0 : _j.description) || subSchema.description;
656
+ subSchema.description = ((_m = schema.meta()) === null || _m === void 0 ? void 0 : _m.description) || subSchema.description;
556
657
  subSchema.arrayDimension = Number.isInteger(subSchema.arrayDimension)
557
658
  ? ++subSchema.arrayDimension
558
659
  : 1;
559
- if (isParentArray && ((_k = schema._zod2x) === null || _k === void 0 ? void 0 : _k.typeName)) {
660
+ if (isParentArray && ((_o = (0, zod2x_metadata_1.resolveZod2xMetadata)(schema)) === null || _o === void 0 ? void 0 : _o.typeName)) {
560
661
  return this._getArrayAst(schema, subSchema);
561
662
  }
562
663
  else {
@@ -566,22 +667,22 @@ class Zod2Ast {
566
667
  else if (zod_helpers_1.ZodHelpers.isZodSet(schema)) {
567
668
  return this._getAliasAst(schema, new core_1.ASTSet({
568
669
  value: this._zodToAST(schema.def.valueType),
569
- description: (_l = schema.meta()) === null || _l === void 0 ? void 0 : _l.description,
670
+ description: (_p = schema.meta()) === null || _p === void 0 ? void 0 : _p.description,
570
671
  }));
571
672
  }
572
673
  else if (zod_helpers_1.ZodHelpers.isZodLiteral(schema)) {
573
674
  let parentEnum = undefined;
574
675
  let parentEnumKey = undefined;
575
676
  // TODO: Support multiple values
576
- if ((_m = schema._zod2x) === null || _m === void 0 ? void 0 : _m.parentEnum) {
577
- parentEnumKey = (_p = this._getEnumValues((_o = schema._zod2x) === null || _o === void 0 ? void 0 : _o.parentEnum).find((i) => i[1] === schema.def.values.at(0))) === null || _p === void 0 ? void 0 : _p[0];
578
- parentEnum = this._zodToAST((_q = schema._zod2x) === null || _q === void 0 ? void 0 : _q.parentEnum, {
677
+ if ((_q = (0, zod2x_metadata_1.resolveZod2xMetadata)(schema)) === null || _q === void 0 ? void 0 : _q.parentEnum) {
678
+ parentEnumKey = (_s = this._getEnumValues((_r = (0, zod2x_metadata_1.resolveZod2xMetadata)(schema)) === null || _r === void 0 ? void 0 : _r.parentEnum).find((i) => i[1] === schema.def.values.at(0))) === null || _s === void 0 ? void 0 : _s[0];
679
+ parentEnum = this._zodToAST((_t = (0, zod2x_metadata_1.resolveZod2xMetadata)(schema)) === null || _t === void 0 ? void 0 : _t.parentEnum, {
579
680
  isInjectedEnum: true,
580
681
  });
581
682
  }
582
683
  return new core_1.ASTLiteral({
583
684
  value: schema.def.values.at(0), // TODO: Support multiple values
584
- description: (_r = schema.meta()) === null || _r === void 0 ? void 0 : _r.description,
685
+ description: (_u = schema.meta()) === null || _u === void 0 ? void 0 : _u.description,
585
686
  parentEnum: parentEnum,
586
687
  parentEnumKey,
587
688
  });
@@ -591,21 +692,29 @@ class Zod2Ast {
591
692
  type: zod_helpers_1.ZodHelpers.isZodRecord(schema) ? "record" : "map",
592
693
  key: this._zodToAST(schema.keyType),
593
694
  value: this._zodToAST(schema.valueType),
594
- description: (_s = schema.meta()) === null || _s === void 0 ? void 0 : _s.description,
695
+ description: (_v = schema.meta()) === null || _v === void 0 ? void 0 : _v.description,
595
696
  }));
596
697
  }
597
698
  else if (zod_helpers_1.ZodHelpers.isZodLazy(schema)) {
598
699
  /** Lazy items use to be recursive schemas of its own, so the are trated as another
599
700
  * definition */
600
- const lazySchema = schema.def.getter();
701
+ let lazySchema = schema;
702
+ while (zod_helpers_1.ZodHelpers.isZodLazy(lazySchema)) {
703
+ // innerType is cached by Zod, so the getter is evaluated only once per instance.
704
+ lazySchema = lazySchema._zod.innerType;
705
+ }
601
706
  const lazyPointer = this._createDefinition({ name: "pending" });
602
- this.lazyPointers.push([lazyPointer, lazySchema]);
707
+ this.lazyPointers.push([
708
+ lazyPointer,
709
+ lazySchema,
710
+ (opt === null || opt === void 0 ? void 0 : opt.discriminantKey) ? { discriminantKey: opt.discriminantKey } : undefined,
711
+ ]);
603
712
  return lazyPointer;
604
713
  }
605
714
  else if (zod_helpers_1.ZodHelpers.isZodTuple(schema)) {
606
715
  return this._getAliasAst(schema, new core_1.ASTTuple({
607
716
  items: schema.def.items.map(this._zodToAST.bind(this)),
608
- description: (_t = schema.meta()) === null || _t === void 0 ? void 0 : _t.description,
717
+ description: (_w = schema.meta()) === null || _w === void 0 ? void 0 : _w.description,
609
718
  }));
610
719
  /**
611
720
  *
@@ -628,7 +737,9 @@ class Zod2Ast {
628
737
  return this._getIntersectionAst(schema);
629
738
  }
630
739
  else {
631
- logger_1.log.warn(`Unsupported Zod type: ${JSON.stringify(schema)}`);
740
+ logger_1.log.warn(`Unsupported Zod type: ${schema.def.type}`);
741
+ this.warnings.push(`Unsupported Zod type '${schema.def.type}' is transpiled as an any type. Avoid ` +
742
+ "it, or disable strict mode if not possible.");
632
743
  return new core_1.ASTAny({
633
744
  description: `Unsupported Zod type: ${schema.def.type}`,
634
745
  });
@@ -640,20 +751,29 @@ class Zod2Ast {
640
751
  * @returns Transpilerable nodes.
641
752
  */
642
753
  build(schema) {
643
- var _a, _b, _c;
754
+ var _a, _b, _c, _d;
644
755
  this._zodToAST(schema, {
645
756
  skipLayerClass: ((_a = this.opt) === null || _a === void 0 ? void 0 : _a.layer) !== undefined && ((_c = (_b = this.opt) === null || _b === void 0 ? void 0 : _b.layer) === null || _c === void 0 ? void 0 : _c.skipLayerInterface) !== false,
646
757
  });
647
758
  while (this.lazyPointers.length > 0) {
648
- const [pointer, schema] = this.lazyPointers.shift();
649
- const lazyResolve = this._zodToAST(schema);
759
+ const [pointer, schema, opt] = this.lazyPointers.shift();
760
+ const lazyResolve = this._zodToAST(schema, opt);
761
+ // Modifiers applied by wrappers of the lazy schema (optional, nullable, array, ...)
762
+ // were set on the pointer and shall be kept.
763
+ const { arrayDimension, isOptional, isNullable, description } = pointer;
650
764
  /** Pointer to the pending AST node is updated with the lazy resolve */
651
765
  Object.keys(pointer).forEach((key) => {
652
766
  delete pointer[key];
653
767
  });
654
- Object.entries(lazyResolve).forEach(([key, value]) => {
655
- pointer[key] = value;
656
- });
768
+ Object.setPrototypeOf(pointer, Object.getPrototypeOf(lazyResolve));
769
+ Object.assign(pointer, lazyResolve);
770
+ const resolved = pointer;
771
+ if (arrayDimension) {
772
+ resolved.arrayDimension = ((_d = resolved.arrayDimension) !== null && _d !== void 0 ? _d : 0) + arrayDimension;
773
+ }
774
+ resolved.isOptional = isOptional !== null && isOptional !== void 0 ? isOptional : resolved.isOptional;
775
+ resolved.isNullable = isNullable !== null && isNullable !== void 0 ? isNullable : resolved.isNullable;
776
+ resolved.description = description || resolved.description;
657
777
  }
658
778
  if (this.opt.strict !== false && this.warnings.length > 0) {
659
779
  throw new errors_1.AstNodeError(this.warnings.join("\n"));
@@ -1,4 +1,4 @@
1
- import { ASTAliasedTypes, ASTEnum, ASTIntersection, ASTNode, ASTNodes, ASTObject, ASTType, ASTUnion } from "../core";
1
+ import { ASTAliasedTypes, ASTDefinition, ASTEnum, ASTIntersection, ASTNode, ASTNodes, ASTObject, ASTType, ASTUnion } from "../core";
2
2
  import { TIndentationLevels } from "../utils/string_utils";
3
3
  /**
4
4
  * Optional user settings
@@ -24,6 +24,10 @@ export interface IZodToXOpt extends Record<string, any> {
24
24
  */
25
25
  useImports?: boolean;
26
26
  }
27
+ /**
28
+ * For Layered Modeling. Kind of type that extends another one.
29
+ */
30
+ export type TExtendedTypeKind = "alias" | "union" | "d-union";
27
31
  /**
28
32
  * Abstract base class for transpiling Zod schemas into other programming languages.
29
33
  * Extend this class and implement the abstract methods to define how each Zod type
@@ -56,10 +60,14 @@ export declare abstract class Zod2X<T extends IZodToXOpt> {
56
60
  * @param typeName
57
61
  */
58
62
  protected abstract getTypeFromExternalNamespace(namespace: string, typeName: string): string;
63
+ /**
64
+ * Opening and closing brackets of generic templates in the target language.
65
+ */
66
+ protected readonly genericTemplatesBrackets: [string, string];
59
67
  /**
60
68
  * Returns the translation of generic templates for a given AST node.
61
69
  */
62
- protected abstract getGenericTemplatesTranslation(data: ASTNode): string | undefined;
70
+ protected getGenericTemplatesTranslation(data: ASTNode | ASTDefinition): string | undefined;
63
71
  /**
64
72
  * For Layered Modeling.
65
73
  * If a property type is an imported type, without any modification, the transpiled type will be
@@ -68,7 +76,26 @@ export declare abstract class Zod2X<T extends IZodToXOpt> {
68
76
  * @param parentNamespace
69
77
  * @param aliasOf
70
78
  */
71
- protected abstract addExtendedType(name: string, parentNamespace: string, aliasOf: string): void;
79
+ protected abstract addExtendedType(name: string, parentNamespace: string, aliasOf: string, opt?: {
80
+ isInternal?: boolean;
81
+ }): void;
82
+ /**
83
+ * For Layered Modeling.
84
+ * Returns the language specific options used by `addExtendedType` to declare a type that
85
+ * extends another one.
86
+ * @param data - The AST node of the type.
87
+ * @param type - Kind of the type.
88
+ */
89
+ protected getExtendedTypeOptions(data: ASTNode, type?: TExtendedTypeKind): object;
90
+ /**
91
+ * For Layered Modeling.
92
+ * Emits the declaration of a type that extends another layered type, if the node is an alias.
93
+ * @param data - The AST node to transpile.
94
+ * @param type - Kind of the type.
95
+ * @returns `true` if the node shall not be transpiled as a new type: it is an alias, or a
96
+ * type imported from another file.
97
+ */
98
+ protected checkExtendedTypeInclusion(data: ASTNode, type?: TExtendedTypeKind): boolean;
72
99
  /**
73
100
  * Returns the keyword representing a string type in the target language.
74
101
  */
@@ -210,7 +237,7 @@ export declare abstract class Zod2X<T extends IZodToXOpt> {
210
237
  * properties of the type to evaluate.
211
238
  * @returns `true` if the type is an external type import; otherwise, `false`.
212
239
  */
213
- protected isExternalTypeImport(item: ASTNode): boolean;
240
+ protected isExternalTypeImport(item: Pick<ASTNode, "parentFile" | "parentNamespace">): boolean;
214
241
  /**
215
242
  * Adds an external type import to the transpiler's imports if the provided transpiled item
216
243
  * is located into another file and namespace, and if the `useImports` option is not disabled.
@@ -219,7 +246,7 @@ export declare abstract class Zod2X<T extends IZodToXOpt> {
219
246
  * about the type to be imported, including its parent file and namespace.
220
247
  * @returns `true` if the import was successfully added, otherwise `false`.
221
248
  */
222
- protected addExternalTypeImport(item: ASTNode): boolean;
249
+ protected addExternalTypeImport(item: Pick<ASTNode, "parentFile" | "parentNamespace">): boolean;
223
250
  /**
224
251
  * Transpiles a single item from the transpiler queue.
225
252
  * @param item - The transpilerable type to transpile.
@@ -13,6 +13,10 @@ const string_utils_1 = __importDefault(require("../utils/string_utils"));
13
13
  */
14
14
  class Zod2X {
15
15
  constructor(opt) {
16
+ /**
17
+ * Opening and closing brackets of generic templates in the target language.
18
+ */
19
+ this.genericTemplatesBrackets = ["<", ">"];
16
20
  /**
17
21
  * Returns a comment.
18
22
  */
@@ -34,6 +38,62 @@ class Zod2X {
34
38
  this.indent = string_utils_1.default.getIndentationLevels(opt.indent || 4);
35
39
  this.opt = opt;
36
40
  }
41
+ /**
42
+ * Returns the translation of generic templates for a given AST node.
43
+ */
44
+ getGenericTemplatesTranslation(data) {
45
+ if ((data instanceof core_1.ASTObject || data instanceof core_1.ASTDefinition) &&
46
+ data.templatesTranslation.length > 0) {
47
+ const [open, close] = this.genericTemplatesBrackets;
48
+ const templates = data.templatesTranslation.map((t) => {
49
+ if (t.type !== undefined) {
50
+ return this.getAttributeType(t.type);
51
+ }
52
+ else if (this.isExternalTypeImport(t)) {
53
+ this.addExternalTypeImport(t);
54
+ return this.getTypeFromExternalNamespace(t.parentNamespace, t.aliasOf);
55
+ }
56
+ else {
57
+ return t.aliasOf;
58
+ }
59
+ });
60
+ return `${open}${templates.join(", ")}${close}`;
61
+ }
62
+ }
63
+ /**
64
+ * For Layered Modeling.
65
+ * Returns the language specific options used by `addExtendedType` to declare a type that
66
+ * extends another one.
67
+ * @param data - The AST node of the type.
68
+ * @param type - Kind of the type.
69
+ */
70
+ getExtendedTypeOptions(data, type) {
71
+ return { type };
72
+ }
73
+ /**
74
+ * For Layered Modeling.
75
+ * Emits the declaration of a type that extends another layered type, if the node is an alias.
76
+ * @param data - The AST node to transpile.
77
+ * @param type - Kind of the type.
78
+ * @returns `true` if the node shall not be transpiled as a new type: it is an alias, or a
79
+ * type imported from another file.
80
+ */
81
+ checkExtendedTypeInclusion(data, type) {
82
+ // Options are computed in advance because they can have side effects (imports, ...).
83
+ const options = this.getExtendedTypeOptions(data, type);
84
+ if (this.isExternalTypeImport(data)) {
85
+ if (data.aliasOf) {
86
+ this.addExtendedType(data.name, data.parentNamespace, data.aliasOf, options);
87
+ this.addExternalTypeImport(data);
88
+ }
89
+ return true;
90
+ }
91
+ else if (data.aliasOf) {
92
+ this.addExtendedType(data.name, data.parentNamespace, data.aliasOf, Object.assign(Object.assign({}, options), { isInternal: true }));
93
+ return true;
94
+ }
95
+ return false;
96
+ }
37
97
  /**
38
98
  * Determines if the given type token can be transpiled into the target language.
39
99
  * @param token - The type token to check.
@@ -146,7 +206,7 @@ class Zod2X {
146
206
  varType = token.name;
147
207
  }
148
208
  else {
149
- console.log(" # Unknown attribute equivalent for ---> ", token.constructor.name);
209
+ console.log(" # Unknown attribute equivalent for ---> ", (0, core_1.getASTKind)(token));
150
210
  }
151
211
  return token.arrayDimension ? this.getArrayType(varType, token.arrayDimension) : varType;
152
212
  }
@@ -198,7 +258,7 @@ class Zod2X {
198
258
  this.transpileAliasedType(item);
199
259
  }
200
260
  else if (item instanceof core_1.ASTCommon) {
201
- console.log(`Under construction: ${item.constructor.name}`);
261
+ console.log(`Under construction: ${(0, core_1.getASTKind)(item)}`);
202
262
  }
203
263
  else {
204
264
  throw new Error(`Unexpected item for transpilation: ${JSON.stringify(item)}`);