zod-to-x
Version:
Multi language types generation from Zod schemas.
231 lines (230 loc) • 9.18 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.Zod2ProtoV3 = void 0;
const case_1 = __importDefault(require("case"));
const core_1 = require("../../core");
const number_limits_1 = require("../../utils/number_limits");
const options_1 = require("./options");
const allowedKeyTypes = [
"int32",
"int64",
"uint32",
"uint64",
"sint32",
"sint64",
"fixed32",
"fixed64",
"sfixed32",
"sfixed64",
"bool",
"string",
];
/**
* @deprecated Zod2ProtoV3 will not be considered as a transpilerable programming language, but as
* another utility such as `zod2JsonSchemaDefinitions`.
*/
class Zod2ProtoV3 extends core_1.Zod2X {
constructor(opt = {}) {
super(Object.assign(Object.assign({}, options_1.defaultOpts), opt));
this.getUnionType = () => {
/** Covered by "transpileUnion" method */
return "";
};
this.getComment = (data, indent = "") => `${indent}// ${data}`;
this.getBooleanType = () => "bool";
this.getStringType = () => "string";
this.getNumberType = (isInt, range) => {
if (!isInt) {
return "double";
}
if ((range === null || range === void 0 ? void 0 : range.min) >= number_limits_1.UINT32_RANGES[0]) {
if ((range === null || range === void 0 ? void 0 : range.max) <= number_limits_1.UINT32_RANGES[1]) {
return "uint32";
}
else {
return "uint64";
}
}
else {
if ((range === null || range === void 0 ? void 0 : range.max) <= number_limits_1.INT32_RANGES[1] && (range === null || range === void 0 ? void 0 : range.min) >= number_limits_1.INT32_RANGES[0]) {
return "int32";
}
else {
return "int64";
}
}
};
this.getAnyType = () => {
this.imports.add(`import "google/protobuf/any.proto";`);
return "google.protobuf.Any";
};
this.getDateType = () => {
this.imports.add(`import "google/protobuf/timestamp.proto";`);
return "google.protobuf.Timestamp";
};
this.getSetType = (itemType) => {
return this.getArrayType(itemType, 1);
};
/**
* @description Determines the equivalent Protobuf type for a tuple based on its item types.
*
* Protobuf v3 does not directly support tuples. However, if all the types
* in the tuple are identical, it can be represented as a `repeated` field
* of that type. If the tuple contains mixed types, Protobuf cannot represent
* it directly, and an alternative approach (e.g., defining a Protobuf message)
* should be considered.
*
* @param itemsType - An array of strings representing the types of the tuple elements.
* @returns A string representing the Protobuf type for the tuple.
* If all tuple elements are of the same type, it returns a `repeated` field of that
* type.
* @throws NotTranspilerableTypeError if the tuple contains mixed types.
*/
this.getTupleType = (itemsType) => {
const uniqueTypes = new Set(itemsType);
if (uniqueTypes.size === 1) {
return this.getArrayType(itemsType[0], 1);
}
else {
throw new core_1.NotTranspilerableTypeError("Protobuf v3 does not support mixed-type tuples. Consider defining a message type.");
}
};
this.getIntersectionType = (itemsType) => {
throw new core_1.NotTranspilerableTypeError("Protobuf v3 does not support intersection types directly.");
};
}
addImportFromFile(filename, namespace) {
// Zod2ProtoV3 does not support layered modeling.
return "";
}
getTypeFromExternalNamespace(namespace, typeName) {
// Zod2ProtoV3 does not support layered modeling.
return "";
}
addExtendedType(name, parentNamespace, parentTypeName) {
// Zod2ProtoV3 does not support layered modeling.
return;
}
getArrayType(arrayType, arrayDeep) {
if (arrayDeep === 1) {
return `repeated ${arrayType}`;
}
else {
throw new core_1.NotTranspilerableTypeError("Protobuf v3 does not support multidimensional arrays directly. " +
"You need to define nested message types for deeper arrays");
}
}
getLiteralStringType(value) {
if (typeof value === "string") {
return this.getStringType();
}
else if (typeof value === "number") {
return this.getNumberType(Number.isInteger(value), { min: value, max: value });
}
else {
throw new core_1.NotTranspilerableTypeError(`Protobuf v3 does not support Literals for this value type: ${value}`);
}
}
getMapType(keyType, valueType) {
if (!allowedKeyTypes.includes(keyType)) {
throw new core_1.NotTranspilerableTypeError(`Protobuf map keys must be an integral or string type, got '${keyType}'.`);
}
return `map<${keyType}, ${valueType}>`;
}
getRecordType(keyType, valueType) {
return this.getMapType(keyType, valueType);
}
transpileEnum(data) {
if (data.isFromDiscriminatedUnion === true) {
// Injected enum from ZodDiscriminatedUnion are not transpiled.
return;
}
this.addComment(data.description);
this.push0(`enum ${data.name} {`);
data.values.forEach(([key, value], index) => {
if (Number.isInteger(key.at(0))) {
throw new core_1.NotTranspilerableTypeError(`Enumerate item name cannot start with number: ${key}`);
}
this.push1(`${key} = ${index};`);
});
this.push0("}\n");
}
transpileIntersection(data) {
throw new core_1.NotTranspilerableTypeError(`Protobuf does not support message intersections.`);
}
transpileStruct(data) {
this.addComment(data.description);
this.push0(`message ${data.name} {`);
Object.entries(data.properties).forEach(([key, value], index) => {
if (value.description && !this.isTranspilerable(value)) {
// Avoid duplicated descriptions for transpiled items.
this.addComment(value.description, `\n${this.indent[1]}`);
}
this.push1(`${this.getAttributeType(value)} ${this._adaptField(key)} = ${index + 1};`);
});
this.push0("}\n");
}
/**
* Transpiles a Zod union or discriminated union into a Protobuf-compatible `oneof` message.
*
* @limitations Currently supports `oneOf` for options that can be represented as a
* Protobuf message or enum. Other types are not yet supported.
* @param data The AST representation of a union or discriminated union, including its
* common metadata.
* @example
* Input:
* {
* name: "UserContact",
* options: ["EmailContact", "PhoneContact", "SocialContact"],
* description: "Represents different ways to contact a user."
* }
*
* Generated Output:
* message UserContact {
* oneof user_contact_oneof {
* EmailContact email_contact = 1;
* PhoneContact phone_contact = 2;
* SocialContact social_contact = 3;
* }
* }
*/
transpileUnion(data) {
this.addComment(data.description);
const attributesTypes = data.options.map(this.getAttributeType.bind(this));
if (attributesTypes.find((i) => i.startsWith("map<") || i.startsWith("repeated "))) {
throw new core_1.NotTranspilerableTypeError("Map and Repeated fields are not suported by Protobuf oneOf");
}
this.push0(`message ${data.name} {`);
this.push1(`oneof ${this._adaptField(data.name + "Oneof")} {`);
attributesTypes.forEach((item, index) => {
this.push2(`${item} ${this._adaptField(item)} = ${index + 1};`);
});
this.push1(`}`);
this.push0("}\n");
}
runBefore() {
var _a, _b;
this.preImports.add(`syntax = "proto3";`);
if ((_a = this.opt) === null || _a === void 0 ? void 0 : _a.packageName) {
this.preImports.add(`package ${(_b = this.opt) === null || _b === void 0 ? void 0 : _b.packageName};`);
}
}
runAfter() { }
/**
* Adapt field name according to user input.
* @param fieldName
* @returns
*/
_adaptField(fieldName) {
if (this.opt.useCamelCase) {
return case_1.default.camel(fieldName);
}
else {
return case_1.default.snake(fieldName);
}
}
}
exports.Zod2ProtoV3 = Zod2ProtoV3;