UNPKG

@autobe/compiler

Version:

AI backend server code generator

319 lines 10.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.writePrismaApplication = writePrismaApplication; const utils_1 = require("@autobe/utils"); const crypto_1 = __importDefault(require("crypto")); const ArrayUtil_1 = require("../utils/ArrayUtil"); function writePrismaApplication(props) { for (const file of props.application.files) for (const model of file.models) fillMappingName(model); return Object.assign(Object.assign({}, Object.fromEntries(props.application.files .filter((file) => file.filename !== "main.prisma") .map((file) => [ file.filename, writeFile(Object.assign(Object.assign({}, props), { file })), ]))), { "main.prisma": props.dbms === "postgres" ? POSTGRES_MAIN_FILE : SQLITE_MAIN_FILE }); } function writeFile(props) { return props.file.models .map((model) => writeModel(Object.assign(Object.assign({}, props), { model }))) .join("\n\n"); } function writeModel(props) { return [ writeComment([ props.model.description, "", ...(props.model.material ? [] : [`@namespace ${props.file.namespace}`]), "@author AutoBE - https://github.com/wrtnlabs/autobe", ].join("\n"), 80), `model ${props.model.name} {`, addIndent(ArrayUtil_1.ArrayUtil.paddle([writeColumns(props), writeRelations(props)]).join("\n")), "}", ].join("\n"); } function fillMappingName(model) { const group = new Map(); for (const ff of model.foreignFields) { utils_1.MapUtil.take(group, ff.relation.targetModel, () => []).push(ff); if (ff.relation.targetModel == model.name) ff.relation.mappingName = "recursive"; } for (const array of group.values()) if (array.length !== 1) for (const ff of array) ff.relation.mappingName = shortName(`${model.name}_of_${ff.name}`); } /* ----------------------------------------------------------- COLUMNS ----------------------------------------------------------- */ function writeColumns(props) { return [ "//----", "// COLUMNS", "//----", writePrimary({ dbms: props.dbms, field: props.model.primaryField, }), ...props.model.foreignFields .map((x) => [ "", writeField({ dbms: props.dbms, field: x, }), ]) .flat(), ...props.model.plainFields .map((x) => [ "", writeField({ dbms: props.dbms, field: x, }), ]) .flat(), ]; } function writePrimary(props) { const type = props.dbms === "postgres" ? POSTGRES_PHYSICAL_TYPES.uuid : undefined; return [ writeComment(props.field.description, 78), `${props.field.name} String @id${type ? ` ${type}` : ""}`, ].join("\n"); } function writeField(props) { const logical = LOGICAL_TYPES[props.field.type]; const physical = props.dbms === "postgres" ? POSTGRES_PHYSICAL_TYPES[props.field.type] : undefined; return [ writeComment(props.field.description, 78), [ props.field.name, `${logical}${props.field.nullable ? "?" : ""}`, ...(physical ? [physical] : []), ].join(" "), ].join("\n"); } /* ----------------------------------------------------------- RELATIONS ----------------------------------------------------------- */ function writeRelations(props) { const hasRelationships = props.application.files .map((otherFile) => otherFile.models.map((otherModel) => otherModel.foreignFields .filter((otherForeign) => otherForeign.relation.targetModel === props.model.name) .map((otherForeign) => ({ modelName: otherModel.name, unique: otherForeign.unique, mappingName: otherForeign.relation.mappingName, })))) .flat(2); const foreignIndexes = props.model.foreignFields.filter((f) => { if (f.unique === true) return props.model.uniqueIndexes.every((u) => u.fieldNames.length !== 1 || u.fieldNames[0] !== f.name); return (props.model.uniqueIndexes.every((u) => u.fieldNames[0] !== f.name) && props.model.plainIndexes.every((p) => p.fieldNames[0] !== f.name)); }); const contents = [ props.model.foreignFields.map((foreign) => writeConstraint({ dbms: props.dbms, model: props.model, foreign, })), hasRelationships.map((r) => { var _a; return [ (_a = r.mappingName) !== null && _a !== void 0 ? _a : r.modelName, `${r.modelName}${r.unique ? "?" : "[]"}`, ...(r.mappingName ? [`@relation("${r.mappingName}")`] : []), ].join(" "); }), foreignIndexes.map((field) => writeForeignIndex({ model: props.model, field, })), [ ...props.model.uniqueIndexes.map((unique) => writeUniqueIndex({ model: props.model, unique, })), ...props.model.plainIndexes.map((plain) => writePlainIndex({ model: props.model, plain, })), ...(props.dbms === "postgres" ? props.model.ginIndexes.map((gin) => writeGinIndex({ model: props.model, gin, })) : []), ], ]; if (contents.every((c) => c.length === 0)) return []; return [ "//----", "// RELATIONS", "//----", // paddled content ...ArrayUtil_1.ArrayUtil.paddle(contents), ]; } function writeConstraint(props) { // spellchecker:ignore-next-line const name = `${props.model.name}_${props.foreign.name}_rela`; return [ props.foreign.relation.name, `${props.foreign.relation.targetModel}${props.foreign.nullable ? "?" : ""}`, `@relation(${[ ...(props.foreign.relation.mappingName ? [`"${props.foreign.relation.mappingName}"`] : []), `fields: [${props.foreign.name}]`, `references: [id]`, `onDelete: Cascade`, ...(props.dbms === "sqlite" || name.length <= MAX_IDENTIFIER_LENGTH ? [] : [`map: "${shortName(name)}"`]), ].join(", ")})`, ].join(" "); } function writeForeignIndex(props) { const name = `${props.model.name}_${props.field.name}_fkey`; const prefix = `@@${props.field.unique === true ? "unique" : "index"}([${props.field.name}]`; if (name.length <= MAX_IDENTIFIER_LENGTH) return `${prefix})`; return `${prefix}, map: "${shortName(name)}")`; } function writeUniqueIndex(props) { const name = `${props.model.name}_${props.unique.fieldNames.join("_")}_key`; const prefix = `@@unique([${props.unique.fieldNames.join(", ")}]`; if (name.length <= MAX_IDENTIFIER_LENGTH) return `${prefix})`; return `${prefix}, map: "${shortName(name)}")`; } function writePlainIndex(props) { const name = `${props.model.name}_${props.plain.fieldNames.join("_")}_idx`; const prefix = `@@index([${props.plain.fieldNames.join(", ")}]`; if (name.length <= MAX_IDENTIFIER_LENGTH) return `${prefix})`; return `${prefix}, map: "${shortName(name)}")`; } function writeGinIndex(props) { const name = `${props.model.name}_${props.gin.fieldName}_idx`; const prefix = `@@index([${props.gin.fieldName}(ops: raw("gin_trgm_ops"))], type: Gin`; if (name.length <= MAX_IDENTIFIER_LENGTH) return `${prefix})`; return `${prefix}, map: "${shortName(name)}")`; } /* ----------------------------------------------------------- BACKGROUND ----------------------------------------------------------- */ function writeComment(content, length) { return content .split("\r\n") .join("\n") .split("\n") .map((line) => line.trim()) .map((line) => { // 77자에서 "/// " 4자를 뺀 73자가 실제 컨텐츠 최대 길이 if (line.length <= length - 4) return [line]; const words = line.split(" "); const result = []; let currentLine = ""; for (const word of words) { const potentialLine = currentLine ? `${currentLine} ${word}` : word; if (potentialLine.length <= 73) { currentLine = potentialLine; } else { if (currentLine) result.push(currentLine); currentLine = word; } } if (currentLine) result.push(currentLine); return result; }) .flat() .map((str) => `///${str.length ? ` ${str}` : ""}`) .join("\n") .trim(); } function addIndent(content) { return content .split("\r\n") .join("\n") .split("\n") .map((str) => ` ${str}`) .join("\n"); } function shortName(name) { if (name.length <= MAX_IDENTIFIER_LENGTH) return name; const hash = crypto_1.default .createHash("md5") .update(name) .digest("hex") .substring(0, HASH_TRUNCATION_LENGTH); return `${name.substring(0, MAX_IDENTIFIER_LENGTH - HASH_TRUNCATION_LENGTH - 1)}_${hash}`; } const LOGICAL_TYPES = { // native types boolean: "Boolean", int: "Int", double: "Float", string: "String", // formats datetime: "DateTime", uuid: "String", uri: "String", }; const POSTGRES_PHYSICAL_TYPES = { int: "@db.Integer", double: "@db.DoublePrecision", uuid: "@db.Uuid", datetime: "@db.Timestamptz", uri: "@db.VarChar(80000)", }; const POSTGRES_MAIN_FILE = utils_1.StringUtil.trim ` generator client { provider = "prisma-client-js" engineType = "client" previewFeatures = ["postgresqlExtensions", "views"] } datasource db { provider = "postgresql" url = env("DATABASE_URL") extensions = [pg_trgm] } generator markdown { provider = "prisma-markdown" output = "../../docs/ERD.md" } `; const SQLITE_MAIN_FILE = utils_1.StringUtil.trim ` generator client { provider = "prisma-client-js" engineType = "client" } datasource db { provider = "sqlite" url = "file:../db.sqlite" } generator markdown { provider = "prisma-markdown" output = "../../docs/ERD.md" } `; const MAX_IDENTIFIER_LENGTH = 63; const HASH_TRUNCATION_LENGTH = 8; //# sourceMappingURL=writePrismaApplication.js.map