zod-to-x
Version:
Multi language types generation from Zod schemas.
245 lines (244 loc) • 9.17 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Zod2Ts = void 0;
const zod_1 = require("zod");
const case_1 = __importDefault(require("case"));
const core_1 = require("../../core");
const options_1 = require("./options");
class Zod2Ts extends core_1.Zod2X {
constructor(opt = {}) {
super(Object.assign(Object.assign({}, options_1.defaultOpts), opt));
this.getComment = (data, indent = "") => `${indent}// ${data}`;
this.getAnyType = () => "any";
this.getBooleanType = () => "boolean";
this.getDateType = () => "Date";
/** Ex: Set<TypeA> */
this.getSetType = (itemType) => `Set<${itemType}>`;
this.getStringType = () => "string";
/** Ex: [TypeA, TypeB] */
this.getTupleType = (itemsType) => `[${itemsType.join(", ")}]`;
/** Ex: TypeA | TypeB */
this.getUnionType = (itemsType) => itemsType.join(" | ");
/** Ex: TypeA & TypeB */
this.getIntersectionType = (itemsType) => itemsType.join(" & ");
this.getNumberType = () => "number";
}
runAfter() { }
runBefore() { }
addImportFromFile(filename, namespace) {
const filenameWithoutExtension = filename.endsWith(".ts")
? filename.slice(0, -3)
: filename;
return `import * as ${namespace} from "./${filenameWithoutExtension}";`;
}
getTypeFromExternalNamespace(namespace, typeName) {
return `${namespace}.${typeName}`;
}
addExtendedType(name, parentNamespace, parentTypeName, opt) {
const extendedType = this.getTypeFromExternalNamespace(parentNamespace, parentTypeName);
if (this.opt.outType === "class") {
if (opt === null || opt === void 0 ? void 0 : opt.isDiscriminatedUnion) {
this.push0(`export type ${name} = ${extendedType};\n`);
}
else {
this.push0(`export class ${name} extends ${extendedType} {}\n`);
}
}
else {
if (opt === null || opt === void 0 ? void 0 : opt.isUnion) {
this.push0(`export type ${name} = ${extendedType};\n`);
}
else if (opt === null || opt === void 0 ? void 0 : opt.isDiscriminatedUnion) {
this.push0(`export type ${name} = ${extendedType};\n`);
}
else {
this.push0(`export interface ${name} extends ${extendedType} {}\n`);
}
}
}
/** Ex: Array<Array<TypeA[]>> */
getArrayType(arrayType, arrayDeep) {
let output = arrayType.includes("|") || arrayType.includes("&")
? `(${arrayType})[]`
: `${arrayType}[]`;
for (let i = 0; i < arrayDeep - 1; i++) {
output = `Array<${output}>`;
}
return output;
}
getLiteralStringType(value, parentEnumNameKey) {
return parentEnumNameKey
? `${parentEnumNameKey[0]}.${case_1.default.pascal(parentEnumNameKey[1])}`
: isNaN(Number(value))
? `"${value}"`
: value;
}
/** Ex: Map<TypeA, TypeB> */
getMapType(keyType, valueType) {
return `Map<${keyType}, ${valueType}>`;
}
/** Ex: Record<TypeA, TypeB> */
getRecordType(keyType, valueType) {
return `Record<${keyType}, ${valueType}>`;
}
/** Ex:
* enum {
* ItemKey1: 0, // case of nativeEnum
* ItemKey2: "ItemValue2" // case of Enum
* }
*/
transpileEnum(data) {
if (this.addExternalTypeImport(data)) {
return;
}
this.addComment(data.description);
this.push0(`export enum ${data.name} {`);
data.values.forEach((i) => {
// If enum key starts with number, it is stored between quotes.
const key = case_1.default.pascal(i[0]);
const keyValue = isNaN(Number(key.at(0))) ? key : `"${key}"`;
// Enum value is stored between quotes if not nativeEnum.
const enumValue = typeof i[1] === "string" ? `"${i[1]}"` : `${i[1]}`;
this.push1(`${keyValue} = ${enumValue},`);
});
this.push0("}\n");
}
/** Ex:
* // Interface output
* // Class output if non-object intersection
* type TypeC = TypeA & TypeB
*
* // Class output all-object intersection
* class TypeC {
* ...attributesTypeA,
* ...attributesTypeB
*
* constructor(data: TypeC) {
* ...attributesAssignment
* }
* }
* */
transpileIntersection(data) {
var _a;
if (this.addExternalTypeImport(data)) {
if (data.parentTypeName) {
this.addExtendedType(data.name, data.parentNamespace, data.parentTypeName);
}
return;
}
if (this.opt.outType === "class" && data.newObject) {
this.addComment((_a = data.newObject) === null || _a === void 0 ? void 0 : _a.description);
this._transpileStructAsClass(data.newObject);
}
else {
this.addComment(data.description);
const attributesTypes = [data.left, data.right].map(this.getAttributeType.bind(this));
this.push0(`export type ${data.name} = ${this.getIntersectionType(attributesTypes)};\n`);
}
}
transpileStruct(data) {
if (this.addExternalTypeImport(data)) {
if (data.parentTypeName) {
this.addExtendedType(data.name, data.parentNamespace, data.parentTypeName);
}
return;
}
this.addComment(data.description);
if (this.opt.outType === "class") {
this._transpileStructAsClass(data);
}
else {
this._transpileStructuAsInterface(data);
}
}
/** Ex:
* // Interface output
* // Class output for Discriminated Union or non-objects union
* type TypeC = TypeA | TypeB
*
* // Class output for all-object Union
* class TypeC {
* ...attributesTypeA,
* ...attributesTypeB
*
* constructor(data: TypeC) {
* ...attributesAssignment
* }
* }
* */
transpileUnion(data) {
var _a;
if (this.addExternalTypeImport(data)) {
if (data.parentTypeName) {
this.addExtendedType(data.name, data.parentNamespace, data.parentTypeName, {
isUnion: data.type === zod_1.ZodFirstPartyTypeKind.ZodUnion,
isDiscriminatedUnion: data.type === zod_1.ZodFirstPartyTypeKind.ZodDiscriminatedUnion,
});
}
return;
}
if (this.opt.outType === "class" && data.newObject) {
this.addComment((_a = data.newObject) === null || _a === void 0 ? void 0 : _a.description);
this._transpileStructAsClass(data.newObject);
}
else {
this.addComment(data.description);
const attributesTypes = data.options.map(this.getAttributeType.bind(this));
this.push0(`export type ${data.name} = ${this.getUnionType(attributesTypes)};\n`);
}
}
/** Ex:
* interface MyStruct {
* att1: TypeA;
* att2?: TypeB;
* }
* */
_transpileStructuAsInterface(data) {
this.push0(`export interface ${data.name} {`);
for (const [key, value] of Object.entries(data.properties)) {
this._transpileMember(case_1.default.camel(key), value);
}
this.push0("}\n");
}
/** Ex:
* class MyStruct {
* att1: TypeA;
* att2?: TypeB;
*
* constructor(data: MyStruct) {
* this.att1 = data.att1;
* this.att2 = data.att2;
* }
* }
* */
_transpileStructAsClass(data) {
this.push0(`export class ${data.name} {`);
const constructorBody = [];
for (const [key, value] of Object.entries(data.properties)) {
this._transpileMember(key, value);
constructorBody.push(`this.${key} = data.${key};`);
}
this.push0("");
this.push1(`constructor(data: ${data.name}) {`);
constructorBody.forEach((i) => this.push2(i));
this.push1("}");
this.push0("}\n");
}
/** For Interface/Class attributes.
* Ex: attribute1?: TypeA | null */
_transpileMember(memberName, memberNode) {
const keyName = memberNode.isOptional ? `${memberName}?: ` : `${memberName}: `;
const setNullable = memberNode.isNullable ? " | null" : "";
if (memberNode.description &&
!memberNode.reference &&
!this.isTranspilerable(memberNode)) {
// Avoid duplicated descriptions for transpiled items.
this.addComment(memberNode.description, `\n${this.indent[1]}`);
}
this.push1(`${keyName}${this.getAttributeType(memberNode)}${setNullable};`);
}
}
exports.Zod2Ts = Zod2Ts;