zod-to-x
Version:
Multi language types generation from Zod schemas.
251 lines (250 loc) • 10 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Zod2X = void 0;
const core_1 = require("../core");
const string_utils_1 = __importDefault(require("../utils/string_utils"));
/**
* Abstract base class for transpiling Zod schemas into other programming languages.
* Extend this class and implement the abstract methods to define how each Zod type
* should be converted to the target language's syntax.
*/
class Zod2X {
constructor(opt) {
/**
* Returns a comment.
*/
this.getComment = (data, indent = "") => {
return data
.split("\n")
.map((line) => `${indent}${this.commentKey} ${line}`)
.join("\n");
};
// Push with indentation helpers
this.push0 = (data) => this.output.push(`${this.indent[0]}${data}`);
this.push1 = (data) => this.output.push(`${this.indent[1]}${data}`);
this.push2 = (data) => this.output.push(`${this.indent[2]}${data}`);
this.push3 = (data) => this.output.push(`${this.indent[3]}${data}`);
this.output = [];
this.preImports = new Set();
this.imports = new Set();
this.postImports = new Set();
this.indent = string_utils_1.default.getIndentationLevels(opt.indent || 4);
this.opt = opt;
}
/**
* Determines if the given type token can be transpiled into the target language.
* @param token - The type token to check.
* @returns `true` if the type is transpilerable; otherwise, `false`.
*/
isTranspilerable(token) {
return (token instanceof core_1.ASTEnum ||
token instanceof core_1.ASTObject ||
token instanceof core_1.ASTUnion ||
token instanceof core_1.ASTIntersection);
}
/**
* Determines if the given AST node represents an aliased type. *
* @param token - The AST node to evaluate.
* @returns `true` if the token is an instance of an aliased type, otherwise `false`.
*/
isAliasedType(token) {
return (token instanceof core_1.ASTString ||
token instanceof core_1.ASTNumber ||
token instanceof core_1.ASTBoolean ||
token instanceof core_1.ASTLiteral ||
token instanceof core_1.ASTDate ||
token instanceof core_1.ASTAny ||
token instanceof core_1.ASTMap ||
token instanceof core_1.ASTSet ||
token instanceof core_1.ASTTuple ||
token instanceof core_1.ASTArray);
}
/**
* Adds a comment to the transpiled output.
* @param data - The comment text to add.
* @param indent - Optional indentation to apply before the comment.
*/
addComment(data = "", indent = "") {
if (data && this.opt.includeComments) {
this.output.push(this.getComment(data, indent));
}
}
/**
* Retrieves the equivalent type representation of an AST node in the target language.
* @param token - The AST node or transpilerable type to convert.
* @returns A string representing the type in the target language.
*/
getAttributeType(token) {
var _a, _b, _c;
let varType = "";
if (token instanceof core_1.ASTDefinition) {
const template = this.getGenericTemplatesTranslation(token) || "";
if (this.opt.useImports === true && token.parentNamespace) {
this.addExternalTypeImport({
parentNamespace: token.parentNamespace,
parentFile: token.parentFile,
});
if (token.aliasOf) {
varType = token.name;
}
else {
varType = this.getTypeFromExternalNamespace(token.parentNamespace, token.name);
}
}
else {
varType = token.name;
}
varType += template;
}
else if (this.isTranspilerable(token)) {
varType = token.name;
}
else if (token instanceof core_1.ASTString) {
varType = this.getStringType();
}
else if (token instanceof core_1.ASTBoolean) {
varType = this.getBooleanType();
}
else if (token instanceof core_1.ASTAny) {
varType = this.getAnyType();
}
else if (token instanceof core_1.ASTDate) {
varType = this.getDateType();
}
else if (token instanceof core_1.ASTLiteral) {
const parentEnum = token.parentEnumKey && token.parentEnum
? [this.getAttributeType(token.parentEnum), token.parentEnumKey]
: undefined;
varType = this.getLiteralStringType(token.value, parentEnum);
}
else if (token instanceof core_1.ASTSet) {
varType = this.getSetType(this.getAttributeType(token.value));
}
else if (token instanceof core_1.ASTNumber) {
varType = this.getNumberType(((_a = token.constraints) === null || _a === void 0 ? void 0 : _a.isInt) === true, {
min: (_b = token.constraints) === null || _b === void 0 ? void 0 : _b.min,
max: (_c = token.constraints) === null || _c === void 0 ? void 0 : _c.max,
});
}
else if (token instanceof core_1.ASTTuple) {
const tupleAttributeTypes = token.items.map(this.getAttributeType.bind(this));
varType = this.getTupleType(tupleAttributeTypes);
}
else if (token instanceof core_1.ASTMap) {
const [key, value] = [token.key, token.value].map(this.getAttributeType.bind(this));
if (token.type === "map") {
varType = this.getMapType(key, value);
}
else {
varType = this.getRecordType(key, value);
}
}
else if (token instanceof core_1.ASTGenericType) {
varType = token.name;
}
else {
console.log(" # Unknown attribute equivalent for ---> ", token.constructor.name);
}
return token.arrayDimension ? this.getArrayType(varType, token.arrayDimension) : varType;
}
/**
* Determines whether a given type is an external type import.
*
* @param item - An object containing the `parentFile` and `parentNamespace`
* properties of the type to evaluate.
* @returns `true` if the type is an external type import; otherwise, `false`.
*/
isExternalTypeImport(item) {
return (item.parentFile !== undefined &&
item.parentNamespace !== undefined &&
this.opt.useImports !== false);
}
/**
* Adds an external type import to the transpiler's imports if the provided transpiled item
* is located into another file and namespace, and if the `useImports` option is not disabled.
*
* @param item - An object of type `TranspilerableTypes` containing information
* about the type to be imported, including its parent file and namespace.
* @returns `true` if the import was successfully added, otherwise `false`.
*/
addExternalTypeImport(item) {
if (this.isExternalTypeImport(item)) {
this.imports.add(this.addImportFromFile(item.parentFile, item.parentNamespace));
return true;
}
return false;
}
/**
* Transpiles a single item from the transpiler queue.
* @param item - The transpilerable type to transpile.
*/
_transpileItem(item) {
if (item instanceof core_1.ASTEnum) {
this.transpileEnum(item);
}
else if (item instanceof core_1.ASTObject) {
this.transpileStruct(item);
}
else if (item instanceof core_1.ASTUnion) {
this.transpileUnion(item);
}
else if (item instanceof core_1.ASTIntersection) {
this.transpileIntersection(item);
}
else if (this.isAliasedType(item)) {
this.transpileAliasedType(item);
}
else if (item instanceof core_1.ASTCommon) {
console.log(`Under construction: ${item.constructor.name}`);
}
else {
throw new Error(`Unexpected item for transpilation: ${JSON.stringify(item)}`);
}
}
/**
* Constructs and returns an array of strings representing the header section
* of the transpiled output. The header may include custom comments, pre-imports,
* imports, and post-imports, depending on the provided options and internal state.
*
* Each section is separated by an empty string for readability.
*
* @returns An array of strings representing the header section.
*
*/
_getHeader() {
const header = [];
if (this.opt.header) {
header.push(this.getComment(this.opt.header));
header.push("");
}
if (this.preImports.size > 0) {
header.push(...this.preImports);
header.push("");
}
if (this.imports.size > 0) {
header.push(...[...this.imports].sort());
header.push("");
}
if (this.postImports.size > 0) {
header.push(...this.postImports);
header.push("");
}
return header;
}
/**
* Transpiles a queue of AST nodes into the target language.
* @param transpilerQueue - An array of transpilerable types (AST nodes with names).
* @returns The transpiled code as a string.
*/
transpile(transpilerQueue) {
this.runBefore();
transpilerQueue.nodes.forEach(this._transpileItem.bind(this));
this.runAfter();
this.output = [...this._getHeader(), ...this.output];
return this.output.join("\n");
}
}
exports.Zod2X = Zod2X;