@autobe/compiler 0.28.0 → 0.29.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.
- package/lib/prisma/AutoBePrismaCompiler.js +2 -2
- package/lib/prisma/AutoBePrismaCompiler.js.map +1 -1
- package/lib/raw/AutoBeCompilerTestTemplate.js +1 -1
- package/lib/raw/nestjs.json +14 -15
- package/lib/raw/test.json +9 -10
- package/lib/utils/FilePrinter.js +9 -1
- package/lib/utils/FilePrinter.js.map +1 -1
- package/package.json +8 -8
- package/src/prisma/AutoBePrismaCompiler.ts +1 -1
- package/src/raw/AutoBeCompilerTestTemplate.ts +1 -1
- package/src/raw/nestjs.json +14 -15
- package/src/raw/test.json +9 -10
- package/src/utils/FilePrinter.ts +12 -1
- package/lib/prisma/writePrismaApplication.d.ts +0 -5
- package/lib/prisma/writePrismaApplication.js +0 -341
- package/lib/prisma/writePrismaApplication.js.map +0 -1
- package/src/prisma/writePrismaApplication.ts +0 -438
|
@@ -1,438 +0,0 @@
|
|
|
1
|
-
import { AutoBePrisma } from "@autobe/interface";
|
|
2
|
-
import { MapUtil, StringUtil } from "@autobe/utils";
|
|
3
|
-
import crypto from "crypto";
|
|
4
|
-
|
|
5
|
-
import { ArrayUtil } from "../utils/ArrayUtil";
|
|
6
|
-
|
|
7
|
-
export function writePrismaApplication(props: {
|
|
8
|
-
dbms: "postgres" | "sqlite";
|
|
9
|
-
application: AutoBePrisma.IApplication;
|
|
10
|
-
}): Record<string, string> {
|
|
11
|
-
for (const file of props.application.files)
|
|
12
|
-
for (const model of file.models) fillMappingName(model);
|
|
13
|
-
return {
|
|
14
|
-
...Object.fromEntries(
|
|
15
|
-
props.application.files
|
|
16
|
-
.filter((file) => file.filename !== "main.prisma")
|
|
17
|
-
.map((file) => [
|
|
18
|
-
file.filename,
|
|
19
|
-
writeFile({
|
|
20
|
-
...props,
|
|
21
|
-
file,
|
|
22
|
-
}),
|
|
23
|
-
]),
|
|
24
|
-
),
|
|
25
|
-
"main.prisma":
|
|
26
|
-
props.dbms === "postgres" ? POSTGRES_MAIN_FILE : SQLITE_MAIN_FILE,
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function writeFile(props: {
|
|
31
|
-
dbms: "postgres" | "sqlite";
|
|
32
|
-
application: AutoBePrisma.IApplication;
|
|
33
|
-
file: AutoBePrisma.IFile;
|
|
34
|
-
}): string {
|
|
35
|
-
return props.file.models
|
|
36
|
-
.map((model) =>
|
|
37
|
-
writeModel({
|
|
38
|
-
...props,
|
|
39
|
-
model,
|
|
40
|
-
}),
|
|
41
|
-
)
|
|
42
|
-
.join("\n\n");
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function writeModel(props: {
|
|
46
|
-
dbms: "postgres" | "sqlite";
|
|
47
|
-
application: AutoBePrisma.IApplication;
|
|
48
|
-
file: AutoBePrisma.IFile;
|
|
49
|
-
model: AutoBePrisma.IModel;
|
|
50
|
-
}): string {
|
|
51
|
-
return [
|
|
52
|
-
writeComment(
|
|
53
|
-
[
|
|
54
|
-
props.model.description,
|
|
55
|
-
"",
|
|
56
|
-
...(props.model.material ? [] : [`@namespace ${props.file.namespace}`]),
|
|
57
|
-
"@author AutoBE - https://github.com/wrtnlabs/autobe",
|
|
58
|
-
].join("\n"),
|
|
59
|
-
80,
|
|
60
|
-
),
|
|
61
|
-
`model ${props.model.name} {`,
|
|
62
|
-
addIndent(
|
|
63
|
-
ArrayUtil.paddle([writeColumns(props), writeRelations(props)]).join("\n"),
|
|
64
|
-
),
|
|
65
|
-
"}",
|
|
66
|
-
].join("\n");
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function fillMappingName(model: AutoBePrisma.IModel): void {
|
|
70
|
-
const group: Map<string, AutoBePrisma.IForeignField[]> = new Map();
|
|
71
|
-
for (const ff of model.foreignFields) {
|
|
72
|
-
MapUtil.take(group, ff.relation.targetModel, () => []).push(ff);
|
|
73
|
-
if (ff.relation.targetModel == model.name)
|
|
74
|
-
ff.relation.mappingName = "recursive";
|
|
75
|
-
}
|
|
76
|
-
for (const array of group.values())
|
|
77
|
-
if (array.length !== 1)
|
|
78
|
-
for (const ff of array)
|
|
79
|
-
ff.relation.mappingName = shortName(`${model.name}_of_${ff.name}`);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/* -----------------------------------------------------------
|
|
83
|
-
COLUMNS
|
|
84
|
-
----------------------------------------------------------- */
|
|
85
|
-
function writeColumns(props: {
|
|
86
|
-
dbms: "postgres" | "sqlite";
|
|
87
|
-
model: AutoBePrisma.IModel;
|
|
88
|
-
}): string[] {
|
|
89
|
-
return [
|
|
90
|
-
"//----",
|
|
91
|
-
"// COLUMNS",
|
|
92
|
-
"//----",
|
|
93
|
-
writePrimary({
|
|
94
|
-
dbms: props.dbms,
|
|
95
|
-
model: props.model,
|
|
96
|
-
field: props.model.primaryField,
|
|
97
|
-
}),
|
|
98
|
-
...props.model.foreignFields
|
|
99
|
-
.map((x) => [
|
|
100
|
-
"",
|
|
101
|
-
writeField({
|
|
102
|
-
dbms: props.dbms,
|
|
103
|
-
field: x,
|
|
104
|
-
}),
|
|
105
|
-
])
|
|
106
|
-
.flat(),
|
|
107
|
-
...props.model.plainFields
|
|
108
|
-
.map((x) => [
|
|
109
|
-
"",
|
|
110
|
-
writeField({
|
|
111
|
-
dbms: props.dbms,
|
|
112
|
-
field: x,
|
|
113
|
-
}),
|
|
114
|
-
])
|
|
115
|
-
.flat(),
|
|
116
|
-
];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function writePrimary(props: {
|
|
120
|
-
dbms: "postgres" | "sqlite";
|
|
121
|
-
model: AutoBePrisma.IModel;
|
|
122
|
-
field: AutoBePrisma.IPrimaryField;
|
|
123
|
-
}): string {
|
|
124
|
-
const type: string | undefined =
|
|
125
|
-
props.dbms === "postgres" ? POSTGRES_PHYSICAL_TYPES.uuid : undefined;
|
|
126
|
-
const pkeyName: string = `${props.model.name}__pkey`;
|
|
127
|
-
const signature: string =
|
|
128
|
-
pkeyName.length <= MAX_IDENTIFIER_LENGTH
|
|
129
|
-
? "@id"
|
|
130
|
-
: `@id(map: "${shortName(pkeyName)}")`;
|
|
131
|
-
return [
|
|
132
|
-
writeComment(props.field.description, 78),
|
|
133
|
-
`${props.field.name} String ${signature}${type ? ` ${type}` : ""}`,
|
|
134
|
-
].join("\n");
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function writeField(props: {
|
|
138
|
-
dbms: "postgres" | "sqlite";
|
|
139
|
-
field: AutoBePrisma.IPlainField;
|
|
140
|
-
}): string {
|
|
141
|
-
const logical: string = LOGICAL_TYPES[props.field.type];
|
|
142
|
-
const physical: string | undefined =
|
|
143
|
-
props.dbms === "postgres"
|
|
144
|
-
? POSTGRES_PHYSICAL_TYPES[
|
|
145
|
-
props.field.type as keyof typeof POSTGRES_PHYSICAL_TYPES
|
|
146
|
-
]
|
|
147
|
-
: undefined;
|
|
148
|
-
return [
|
|
149
|
-
writeComment(props.field.description, 78),
|
|
150
|
-
[
|
|
151
|
-
props.field.name,
|
|
152
|
-
`${logical}${props.field.nullable ? "?" : ""}`,
|
|
153
|
-
...(physical ? [physical] : []),
|
|
154
|
-
].join(" "),
|
|
155
|
-
].join("\n");
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/* -----------------------------------------------------------
|
|
159
|
-
RELATIONS
|
|
160
|
-
----------------------------------------------------------- */
|
|
161
|
-
function writeRelations(props: {
|
|
162
|
-
dbms: "postgres" | "sqlite";
|
|
163
|
-
application: AutoBePrisma.IApplication;
|
|
164
|
-
model: AutoBePrisma.IModel;
|
|
165
|
-
}): string[] {
|
|
166
|
-
interface IHasRelationship {
|
|
167
|
-
modelName: string;
|
|
168
|
-
unique: boolean;
|
|
169
|
-
mappingName?: string;
|
|
170
|
-
}
|
|
171
|
-
const hasRelationships: IHasRelationship[] = props.application.files
|
|
172
|
-
.map((otherFile) =>
|
|
173
|
-
otherFile.models.map((otherModel) =>
|
|
174
|
-
otherModel.foreignFields
|
|
175
|
-
.filter(
|
|
176
|
-
(otherForeign) =>
|
|
177
|
-
otherForeign.relation.targetModel === props.model.name,
|
|
178
|
-
)
|
|
179
|
-
.map((otherForeign) => ({
|
|
180
|
-
modelName: otherModel.name,
|
|
181
|
-
unique: otherForeign.unique,
|
|
182
|
-
mappingName: otherForeign.relation.mappingName,
|
|
183
|
-
})),
|
|
184
|
-
),
|
|
185
|
-
)
|
|
186
|
-
.flat(2);
|
|
187
|
-
const foreignIndexes: AutoBePrisma.IForeignField[] =
|
|
188
|
-
props.model.foreignFields.filter((f) => {
|
|
189
|
-
if (f.unique === true)
|
|
190
|
-
return props.model.uniqueIndexes.every(
|
|
191
|
-
(u) => u.fieldNames.length !== 1 || u.fieldNames[0] !== f.name,
|
|
192
|
-
);
|
|
193
|
-
return (
|
|
194
|
-
props.model.uniqueIndexes.every((u) => u.fieldNames[0] !== f.name) &&
|
|
195
|
-
props.model.plainIndexes.every((p) => p.fieldNames[0] !== f.name)
|
|
196
|
-
);
|
|
197
|
-
});
|
|
198
|
-
const contents: string[][] = [
|
|
199
|
-
props.model.foreignFields.map((foreign) =>
|
|
200
|
-
writeConstraint({
|
|
201
|
-
dbms: props.dbms,
|
|
202
|
-
model: props.model,
|
|
203
|
-
foreign,
|
|
204
|
-
}),
|
|
205
|
-
),
|
|
206
|
-
hasRelationships.map((r) =>
|
|
207
|
-
[
|
|
208
|
-
r.mappingName ?? r.modelName,
|
|
209
|
-
`${r.modelName}${r.unique ? "?" : "[]"}`,
|
|
210
|
-
...(r.mappingName ? [`@relation("${r.mappingName}")`] : []),
|
|
211
|
-
].join(" "),
|
|
212
|
-
),
|
|
213
|
-
foreignIndexes.map((field) =>
|
|
214
|
-
writeForeignIndex({
|
|
215
|
-
model: props.model,
|
|
216
|
-
field,
|
|
217
|
-
}),
|
|
218
|
-
),
|
|
219
|
-
[
|
|
220
|
-
...props.model.uniqueIndexes.map((unique) =>
|
|
221
|
-
writeUniqueIndex({
|
|
222
|
-
model: props.model,
|
|
223
|
-
unique,
|
|
224
|
-
}),
|
|
225
|
-
),
|
|
226
|
-
...props.model.plainIndexes.map((plain) =>
|
|
227
|
-
writePlainIndex({
|
|
228
|
-
model: props.model,
|
|
229
|
-
plain,
|
|
230
|
-
}),
|
|
231
|
-
),
|
|
232
|
-
...(props.dbms === "postgres"
|
|
233
|
-
? props.model.ginIndexes.map((gin) =>
|
|
234
|
-
writeGinIndex({
|
|
235
|
-
model: props.model,
|
|
236
|
-
gin,
|
|
237
|
-
}),
|
|
238
|
-
)
|
|
239
|
-
: []),
|
|
240
|
-
],
|
|
241
|
-
];
|
|
242
|
-
if (contents.every((c) => c.length === 0)) return [];
|
|
243
|
-
return [
|
|
244
|
-
"//----",
|
|
245
|
-
"// RELATIONS",
|
|
246
|
-
"//----",
|
|
247
|
-
// paddled content
|
|
248
|
-
...ArrayUtil.paddle(contents),
|
|
249
|
-
];
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function writeConstraint(props: {
|
|
253
|
-
dbms: "postgres" | "sqlite";
|
|
254
|
-
model: AutoBePrisma.IModel;
|
|
255
|
-
foreign: AutoBePrisma.IForeignField;
|
|
256
|
-
}): string {
|
|
257
|
-
// spellchecker:ignore-next-line
|
|
258
|
-
const name: string = `${props.model.name}_${props.foreign.name}_rela`;
|
|
259
|
-
const tooMuchLong: boolean =
|
|
260
|
-
props.dbms === "postgres" && name.length > MAX_IDENTIFIER_LENGTH;
|
|
261
|
-
const body: string = [
|
|
262
|
-
props.foreign.relation.name,
|
|
263
|
-
`${props.foreign.relation.targetModel}${props.foreign.nullable ? "?" : ""}`,
|
|
264
|
-
`@relation(${[
|
|
265
|
-
...(props.foreign.relation.mappingName
|
|
266
|
-
? [`"${props.foreign.relation.mappingName}"`]
|
|
267
|
-
: []),
|
|
268
|
-
`fields: [${props.foreign.name}]`,
|
|
269
|
-
`references: [id]`,
|
|
270
|
-
`onDelete: Cascade`,
|
|
271
|
-
...(tooMuchLong ? [`map: "${shortName(name)}"`] : []),
|
|
272
|
-
].join(", ")})`,
|
|
273
|
-
].join(" ");
|
|
274
|
-
return tooMuchLong
|
|
275
|
-
? StringUtil.trim`
|
|
276
|
-
// spellchecker: ignore-next-line
|
|
277
|
-
${body}
|
|
278
|
-
`
|
|
279
|
-
: body;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
function writeForeignIndex(props: {
|
|
283
|
-
model: AutoBePrisma.IModel;
|
|
284
|
-
field: AutoBePrisma.IForeignField;
|
|
285
|
-
}): string {
|
|
286
|
-
const name: string = `${props.model.name}_${props.field.name}_fkey`;
|
|
287
|
-
const prefix: string = `@@${props.field.unique === true ? "unique" : "index"}([${props.field.name}]`;
|
|
288
|
-
if (name.length <= MAX_IDENTIFIER_LENGTH) return `${prefix})`;
|
|
289
|
-
return StringUtil.trim`
|
|
290
|
-
// spellchecker: ignore-next-line
|
|
291
|
-
${prefix}, map: "${shortName(name)}")
|
|
292
|
-
`;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
function writeUniqueIndex(props: {
|
|
296
|
-
model: AutoBePrisma.IModel;
|
|
297
|
-
unique: AutoBePrisma.IUniqueIndex;
|
|
298
|
-
}): string {
|
|
299
|
-
const name: string = `${props.model.name}_${props.unique.fieldNames.join("_")}_key`;
|
|
300
|
-
const prefix: string = `@@unique([${props.unique.fieldNames.join(", ")}]`;
|
|
301
|
-
if (name.length <= MAX_IDENTIFIER_LENGTH) return `${prefix})`;
|
|
302
|
-
return StringUtil.trim`
|
|
303
|
-
// spellchecker: ignore-next-line
|
|
304
|
-
${prefix}, map: "${shortName(name)}")
|
|
305
|
-
`;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
function writePlainIndex(props: {
|
|
309
|
-
model: AutoBePrisma.IModel;
|
|
310
|
-
plain: AutoBePrisma.IPlainIndex;
|
|
311
|
-
}): string {
|
|
312
|
-
const name: string = `${props.model.name}_${props.plain.fieldNames.join("_")}_idx`;
|
|
313
|
-
const prefix: string = `@@index([${props.plain.fieldNames.join(", ")}]`;
|
|
314
|
-
if (name.length <= MAX_IDENTIFIER_LENGTH) return `${prefix})`;
|
|
315
|
-
return StringUtil.trim`
|
|
316
|
-
// spellchecker: ignore-next-line
|
|
317
|
-
${prefix}, map: "${shortName(name)}")
|
|
318
|
-
`;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
function writeGinIndex(props: {
|
|
322
|
-
model: AutoBePrisma.IModel;
|
|
323
|
-
gin: AutoBePrisma.IGinIndex;
|
|
324
|
-
}): string {
|
|
325
|
-
const name: string = `${props.model.name}_${props.gin.fieldName}_idx`;
|
|
326
|
-
const prefix: string = `@@index([${props.gin.fieldName}(ops: raw("gin_trgm_ops"))], type: Gin`;
|
|
327
|
-
if (name.length <= MAX_IDENTIFIER_LENGTH) return `${prefix})`;
|
|
328
|
-
return StringUtil.trim`
|
|
329
|
-
// spellchecker: ignore-next-line
|
|
330
|
-
${prefix}, map: "${shortName(name)}")
|
|
331
|
-
`;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/* -----------------------------------------------------------
|
|
335
|
-
BACKGROUND
|
|
336
|
-
----------------------------------------------------------- */
|
|
337
|
-
function writeComment(content: string, length: number): string {
|
|
338
|
-
return content
|
|
339
|
-
.split("\r\n")
|
|
340
|
-
.join("\n")
|
|
341
|
-
.split("\n")
|
|
342
|
-
.map((line) => line.trim())
|
|
343
|
-
.map((line) => {
|
|
344
|
-
// 77자에서 "/// " 4자를 뺀 73자가 실제 컨텐츠 최대 길이
|
|
345
|
-
if (line.length <= length - 4) return [line];
|
|
346
|
-
const words: string[] = line.split(" ");
|
|
347
|
-
const result: string[] = [];
|
|
348
|
-
let currentLine = "";
|
|
349
|
-
|
|
350
|
-
for (const word of words) {
|
|
351
|
-
const potentialLine = currentLine ? `${currentLine} ${word}` : word;
|
|
352
|
-
if (potentialLine.length <= 73) {
|
|
353
|
-
currentLine = potentialLine;
|
|
354
|
-
} else {
|
|
355
|
-
if (currentLine) result.push(currentLine);
|
|
356
|
-
currentLine = word;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
if (currentLine) result.push(currentLine);
|
|
361
|
-
return result;
|
|
362
|
-
})
|
|
363
|
-
.flat()
|
|
364
|
-
.map((str) => `///${str.length ? ` ${str}` : ""}`)
|
|
365
|
-
.join("\n")
|
|
366
|
-
.trim();
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
function addIndent(content: string): string {
|
|
370
|
-
return content
|
|
371
|
-
.split("\r\n")
|
|
372
|
-
.join("\n")
|
|
373
|
-
.split("\n")
|
|
374
|
-
.map((str) => ` ${str}`)
|
|
375
|
-
.join("\n");
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
function shortName(name: string): string {
|
|
379
|
-
if (name.length <= MAX_IDENTIFIER_LENGTH) return name;
|
|
380
|
-
const hash: string = crypto
|
|
381
|
-
.createHash("md5")
|
|
382
|
-
.update(name)
|
|
383
|
-
.digest("hex")
|
|
384
|
-
.substring(0, HASH_TRUNCATION_LENGTH);
|
|
385
|
-
return `${name.substring(0, MAX_IDENTIFIER_LENGTH - HASH_TRUNCATION_LENGTH - 1)}_${hash}`;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
const LOGICAL_TYPES = {
|
|
389
|
-
// native types
|
|
390
|
-
boolean: "Boolean",
|
|
391
|
-
int: "Int",
|
|
392
|
-
double: "Float",
|
|
393
|
-
string: "String",
|
|
394
|
-
// formats
|
|
395
|
-
datetime: "DateTime",
|
|
396
|
-
uuid: "String",
|
|
397
|
-
uri: "String",
|
|
398
|
-
};
|
|
399
|
-
const POSTGRES_PHYSICAL_TYPES = {
|
|
400
|
-
int: "@db.Integer",
|
|
401
|
-
double: "@db.DoublePrecision",
|
|
402
|
-
uuid: "@db.Uuid",
|
|
403
|
-
datetime: "@db.Timestamptz",
|
|
404
|
-
uri: "@db.VarChar(80000)",
|
|
405
|
-
};
|
|
406
|
-
|
|
407
|
-
const POSTGRES_MAIN_FILE = StringUtil.trim`
|
|
408
|
-
generator client {
|
|
409
|
-
provider = "prisma-client-js"
|
|
410
|
-
engineType = "client"
|
|
411
|
-
previewFeatures = ["postgresqlExtensions", "views"]
|
|
412
|
-
}
|
|
413
|
-
datasource db {
|
|
414
|
-
provider = "postgresql"
|
|
415
|
-
url = env("DATABASE_URL")
|
|
416
|
-
extensions = [pg_trgm]
|
|
417
|
-
}
|
|
418
|
-
generator markdown {
|
|
419
|
-
provider = "prisma-markdown"
|
|
420
|
-
output = "../../docs/ERD.md"
|
|
421
|
-
}
|
|
422
|
-
`;
|
|
423
|
-
const SQLITE_MAIN_FILE = StringUtil.trim`
|
|
424
|
-
generator client {
|
|
425
|
-
provider = "prisma-client-js"
|
|
426
|
-
engineType = "client"
|
|
427
|
-
}
|
|
428
|
-
datasource db {
|
|
429
|
-
provider = "sqlite"
|
|
430
|
-
url = "file:../db.sqlite"
|
|
431
|
-
}
|
|
432
|
-
generator markdown {
|
|
433
|
-
provider = "prisma-markdown"
|
|
434
|
-
output = "../../docs/ERD.md"
|
|
435
|
-
}
|
|
436
|
-
`;
|
|
437
|
-
const MAX_IDENTIFIER_LENGTH = 63;
|
|
438
|
-
const HASH_TRUNCATION_LENGTH = 8;
|