sonamu 0.2.54 → 0.4.1

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.
Files changed (85) hide show
  1. package/.pnp.cjs +11 -0
  2. package/dist/base-model-BzMJ2E_I.d.mts +43 -0
  3. package/dist/base-model-CWRKUX49.d.ts +43 -0
  4. package/dist/bin/cli.js +118 -89
  5. package/dist/bin/cli.js.map +1 -1
  6. package/dist/bin/cli.mjs +74 -45
  7. package/dist/bin/cli.mjs.map +1 -1
  8. package/dist/chunk-FLPD24HS.mjs +231 -0
  9. package/dist/chunk-FLPD24HS.mjs.map +1 -0
  10. package/dist/chunk-I2MMJRJN.mjs +1550 -0
  11. package/dist/chunk-I2MMJRJN.mjs.map +1 -0
  12. package/dist/{chunk-L4KELCY7.mjs → chunk-PP2PSSAG.mjs} +5241 -5571
  13. package/dist/chunk-PP2PSSAG.mjs.map +1 -0
  14. package/dist/chunk-QK5XXJUX.mjs +280 -0
  15. package/dist/chunk-QK5XXJUX.mjs.map +1 -0
  16. package/dist/chunk-U636LQJJ.js +231 -0
  17. package/dist/chunk-U636LQJJ.js.map +1 -0
  18. package/dist/chunk-W7KDVJLQ.js +280 -0
  19. package/dist/chunk-W7KDVJLQ.js.map +1 -0
  20. package/dist/{chunk-JOHF7PK4.js → chunk-XT6LHCX5.js} +5292 -5622
  21. package/dist/chunk-XT6LHCX5.js.map +1 -0
  22. package/dist/chunk-Z2P7XTXE.js +1550 -0
  23. package/dist/chunk-Z2P7XTXE.js.map +1 -0
  24. package/dist/database/drivers/knex/base-model.d.mts +16 -0
  25. package/dist/database/drivers/knex/base-model.d.ts +16 -0
  26. package/dist/database/drivers/knex/base-model.js +55 -0
  27. package/dist/database/drivers/knex/base-model.js.map +1 -0
  28. package/dist/database/drivers/knex/base-model.mjs +56 -0
  29. package/dist/database/drivers/knex/base-model.mjs.map +1 -0
  30. package/dist/database/drivers/kysely/base-model.d.mts +22 -0
  31. package/dist/database/drivers/kysely/base-model.d.ts +22 -0
  32. package/dist/database/drivers/kysely/base-model.js +64 -0
  33. package/dist/database/drivers/kysely/base-model.js.map +1 -0
  34. package/dist/database/drivers/kysely/base-model.mjs +65 -0
  35. package/dist/database/drivers/kysely/base-model.mjs.map +1 -0
  36. package/dist/index.d.mts +222 -928
  37. package/dist/index.d.ts +222 -928
  38. package/dist/index.js +13 -26
  39. package/dist/index.js.map +1 -1
  40. package/dist/index.mjs +18 -31
  41. package/dist/index.mjs.map +1 -1
  42. package/dist/model-CAH_4oQh.d.mts +1042 -0
  43. package/dist/model-CAH_4oQh.d.ts +1042 -0
  44. package/import-to-require.js +27 -0
  45. package/package.json +23 -2
  46. package/src/api/caster.ts +6 -0
  47. package/src/api/code-converters.ts +3 -1
  48. package/src/api/sonamu.ts +41 -22
  49. package/src/bin/cli.ts +78 -46
  50. package/src/database/_batch_update.ts +16 -11
  51. package/src/database/base-model.abstract.ts +97 -0
  52. package/src/database/base-model.ts +214 -280
  53. package/src/database/code-generator.ts +72 -0
  54. package/src/database/db.abstract.ts +75 -0
  55. package/src/database/db.ts +21 -82
  56. package/src/database/drivers/knex/base-model.ts +55 -0
  57. package/src/database/drivers/knex/client.ts +209 -0
  58. package/src/database/drivers/knex/db.ts +227 -0
  59. package/src/database/drivers/knex/generator.ts +659 -0
  60. package/src/database/drivers/kysely/base-model.ts +89 -0
  61. package/src/database/drivers/kysely/client.ts +309 -0
  62. package/src/database/drivers/kysely/db.ts +238 -0
  63. package/src/database/drivers/kysely/generator.ts +714 -0
  64. package/src/database/types.ts +117 -0
  65. package/src/database/upsert-builder.ts +31 -18
  66. package/src/entity/entity-manager.ts +9 -5
  67. package/src/entity/entity-utils.ts +1 -1
  68. package/src/entity/entity.ts +9 -13
  69. package/src/entity/migrator.ts +98 -693
  70. package/src/index.ts +1 -1
  71. package/src/syncer/syncer.ts +103 -56
  72. package/src/templates/generated_http.template.ts +14 -0
  73. package/src/templates/kysely_types.template.ts +205 -0
  74. package/src/templates/model.template.ts +2 -139
  75. package/src/templates/service.template.ts +3 -1
  76. package/src/testing/_relation-graph.ts +111 -0
  77. package/src/testing/fixture-manager.ts +216 -332
  78. package/src/types/types.ts +56 -6
  79. package/src/utils/utils.ts +56 -4
  80. package/src/utils/zod-error.ts +189 -0
  81. package/tsconfig.json +2 -2
  82. package/tsup.config.js +11 -10
  83. package/dist/chunk-JOHF7PK4.js.map +0 -1
  84. package/dist/chunk-L4KELCY7.mjs.map +0 -1
  85. /package/src/database/{knex-plugins → drivers/knex/plugins}/knex-on-duplicate-update.ts +0 -0
@@ -0,0 +1,659 @@
1
+ import _ from "lodash";
2
+ import prettier from "prettier";
3
+ import inflection from "inflection";
4
+ import equal from "fast-deep-equal";
5
+ import {
6
+ GenMigrationCode,
7
+ MigrationColumn,
8
+ MigrationForeign,
9
+ MigrationIndex,
10
+ } from "../../../types/types";
11
+ import { CodeGenerator } from "../../code-generator";
12
+ import { EntityManager } from "../../../entity/entity-manager";
13
+
14
+ export class KnexGenerator extends CodeGenerator {
15
+ async generateCreateCode_ColumnAndIndexes(
16
+ table: string,
17
+ columns: MigrationColumn[],
18
+ indexes: MigrationIndex[]
19
+ ): Promise<GenMigrationCode> {
20
+ // 컬럼, 인덱스 처리
21
+ const lines: string[] = [
22
+ 'import { Knex } from "knex";',
23
+ "",
24
+ "export async function up(knex: Knex): Promise<void> {",
25
+ `return knex.schema.createTable("${table}", (table) => {`,
26
+ "// columns",
27
+ ...this.genColumnDefinitions(columns),
28
+ "",
29
+ "// indexes",
30
+ ...this.genIndexDefinitions(indexes),
31
+ "});",
32
+ "}",
33
+ "",
34
+ "export async function down(knex: Knex): Promise<void> {",
35
+ ` return knex.schema.dropTable("${table}");`,
36
+ "}",
37
+ ];
38
+ return {
39
+ table,
40
+ type: "normal",
41
+ title: `create__${table}`,
42
+ formatted: await prettier.format(lines.join("\n"), {
43
+ parser: "typescript",
44
+ }),
45
+ };
46
+ }
47
+
48
+ /*
49
+ 테이블 생성하는 케이스 - FK 생성
50
+ */
51
+ async generateCreateCode_Foreign(
52
+ table: string,
53
+ foreigns: MigrationForeign[]
54
+ ): Promise<GenMigrationCode[]> {
55
+ if (foreigns.length === 0) {
56
+ return [];
57
+ }
58
+
59
+ const { up, down } = this.genForeignDefinitions(table, foreigns);
60
+ if (up.length === 0 && down.length === 0) {
61
+ console.log("fk 가 뭔가 다릅니다");
62
+ return [];
63
+ }
64
+
65
+ const lines: string[] = [
66
+ 'import { Knex } from "knex";',
67
+ "",
68
+ "export async function up(knex: Knex): Promise<void> {",
69
+ `return knex.schema.alterTable("${table}", (table) => {`,
70
+ "// create fk",
71
+ ...up,
72
+ "});",
73
+ "}",
74
+ "",
75
+ "export async function down(knex: Knex): Promise<void> {",
76
+ `return knex.schema.alterTable("${table}", (table) => {`,
77
+ "// drop fk",
78
+ ...down,
79
+ "});",
80
+ "}",
81
+ ];
82
+
83
+ const foreignKeysString = foreigns
84
+ .map((foreign) => foreign.columns.join("_"))
85
+ .join("_");
86
+ return [
87
+ {
88
+ table,
89
+ type: "foreign",
90
+ title: `foreign__${table}__${foreignKeysString}`,
91
+ formatted: await prettier.format(lines.join("\n"), {
92
+ parser: "typescript",
93
+ }),
94
+ },
95
+ ];
96
+ }
97
+
98
+ async generateAlterCode_ColumnAndIndexes(
99
+ table: string,
100
+ entityColumns: MigrationColumn[],
101
+ entityIndexes: MigrationIndex[],
102
+ dbColumns: MigrationColumn[],
103
+ dbIndexes: MigrationIndex[]
104
+ ): Promise<GenMigrationCode[]> {
105
+ /*
106
+ 세부 비교 후 다른점 찾아서 코드 생성
107
+
108
+ 1. 컬럼갯수 다름: MD에 있으나, DB에 없다면 추가
109
+ 2. 컬럼갯수 다름: MD에 없으나, DB에 있다면 삭제
110
+ 3. 그외 컬럼(컬럼 갯수가 동일하거나, 다른 경우 동일한 컬럼끼리) => alter
111
+ 4. 다른거 다 동일하고 index만 변경되는 경우
112
+
113
+ ** 컬럼명을 변경하는 경우는 따로 핸들링하지 않음
114
+ => drop/add 형태의 마이그레이션 코드가 생성되는데, 수동으로 rename 코드로 수정하여 처리
115
+ */
116
+
117
+ // 각 컬럼 이름 기준으로 add, drop, alter 여부 확인
118
+ const alterColumnsTo = this.getAlterColumnsTo(entityColumns, dbColumns);
119
+
120
+ // 추출된 컬럼들을 기준으로 각각 라인 생성
121
+ const alterColumnLinesTo = this.getAlterColumnLinesTo(
122
+ alterColumnsTo,
123
+ entityColumns
124
+ );
125
+
126
+ // 인덱스의 add, drop 여부 확인
127
+ const alterIndexesTo = this.getAlterIndexesTo(entityIndexes, dbIndexes);
128
+
129
+ // 추출된 인덱스들을 기준으로 각각 라인 생성
130
+ const alterIndexLinesTo = this.getAlterIndexLinesTo(
131
+ alterIndexesTo,
132
+ alterColumnsTo
133
+ );
134
+
135
+ const lines: string[] = [
136
+ 'import { Knex } from "knex";',
137
+ "",
138
+ "export async function up(knex: Knex): Promise<void> {",
139
+ `return knex.schema.alterTable("${table}", (table) => {`,
140
+ ...(alterColumnsTo.add.length > 0 ? alterColumnLinesTo.add.up : []),
141
+ ...(alterColumnsTo.drop.length > 0 ? alterColumnLinesTo.drop.up : []),
142
+ ...(alterColumnsTo.alter.length > 0 ? alterColumnLinesTo.alter.up : []),
143
+ ...(alterIndexesTo.add.length > 0 ? alterIndexLinesTo.add.up : []),
144
+ ...(alterIndexesTo.drop.length > 0 ? alterIndexLinesTo.drop.up : []),
145
+ "})",
146
+ "}",
147
+ "",
148
+ "export async function down(knex: Knex): Promise<void> {",
149
+ `return knex.schema.alterTable("${table}", (table) => {`,
150
+ ...(alterColumnsTo.add.length > 0 ? alterColumnLinesTo.add.down : []),
151
+ ...(alterColumnsTo.drop.length > 0 ? alterColumnLinesTo.drop.down : []),
152
+ ...(alterColumnsTo.alter.length > 0 ? alterColumnLinesTo.alter.down : []),
153
+ ...(alterIndexLinesTo.add.down.length > 0
154
+ ? alterIndexLinesTo.add.down
155
+ : []),
156
+ ...(alterIndexLinesTo.drop.down.length > 0
157
+ ? alterIndexLinesTo.drop.down
158
+ : []),
159
+ "})",
160
+ "}",
161
+ ];
162
+
163
+ const formatted = await prettier.format(lines.join("\n"), {
164
+ parser: "typescript",
165
+ });
166
+
167
+ const title = [
168
+ "alter",
169
+ table,
170
+ ...(["add", "drop", "alter"] as const)
171
+ .map((action) => {
172
+ const len = alterColumnsTo[action].length;
173
+ if (len > 0) {
174
+ return action + len;
175
+ }
176
+ return null;
177
+ })
178
+ .filter((part) => part !== null),
179
+ ].join("_");
180
+
181
+ return [
182
+ {
183
+ table,
184
+ title,
185
+ formatted,
186
+ type: "normal",
187
+ },
188
+ ];
189
+ }
190
+
191
+ async generateAlterCode_Foreigns(
192
+ table: string,
193
+ entityForeigns: MigrationForeign[],
194
+ dbForeigns: MigrationForeign[]
195
+ ): Promise<GenMigrationCode[]> {
196
+ const getKey = (mf: MigrationForeign): string => {
197
+ return [mf.columns.join("-"), mf.to].join("///");
198
+ };
199
+ const fkTo = entityForeigns.reduce(
200
+ (result, entityF) => {
201
+ const matchingDbF = dbForeigns.find(
202
+ (dbF) => getKey(entityF) === getKey(dbF)
203
+ );
204
+ if (!matchingDbF) {
205
+ result.add.push(entityF);
206
+ return result;
207
+ }
208
+
209
+ if (equal(entityF, matchingDbF) === false) {
210
+ result.alterSrc.push(matchingDbF);
211
+ result.alterDst.push(entityF);
212
+ return result;
213
+ }
214
+ return result;
215
+ },
216
+ {
217
+ add: [] as MigrationForeign[],
218
+ alterSrc: [] as MigrationForeign[],
219
+ alterDst: [] as MigrationForeign[],
220
+ }
221
+ );
222
+
223
+ const linesTo = {
224
+ add: this.genForeignDefinitions(table, fkTo.add),
225
+ alterSrc: this.genForeignDefinitions(table, fkTo.alterSrc),
226
+ alterDst: this.genForeignDefinitions(table, fkTo.alterDst),
227
+ };
228
+
229
+ const lines: string[] = [
230
+ 'import { Knex } from "knex";',
231
+ "",
232
+ "export async function up(knex: Knex): Promise<void> {",
233
+ `return knex.schema.alterTable("${table}", (table) => {`,
234
+ ...linesTo.add.up,
235
+ ...linesTo.alterSrc.down,
236
+ ...linesTo.alterDst.up,
237
+ "})",
238
+ "}",
239
+ "",
240
+ "export async function down(knex: Knex): Promise<void> {",
241
+ `return knex.schema.alterTable("${table}", (table) => {`,
242
+ ...linesTo.add.down,
243
+ ...linesTo.alterDst.down,
244
+ ...linesTo.alterSrc.up,
245
+ "})",
246
+ "}",
247
+ ];
248
+
249
+ const formatted = await prettier.format(lines.join("\n"), {
250
+ parser: "typescript",
251
+ });
252
+
253
+ const title = [
254
+ "alter",
255
+ table,
256
+ "foreigns",
257
+ // TODO 바뀌는 부분
258
+ ].join("_");
259
+
260
+ return [
261
+ {
262
+ table,
263
+ title,
264
+ formatted,
265
+ type: "normal",
266
+ },
267
+ ];
268
+ }
269
+
270
+ generateModelTemplate(
271
+ entityId: string,
272
+ def: { orderBy: string; search: string }
273
+ ) {
274
+ const names = EntityManager.getNamesFromId(entityId);
275
+ const entity = EntityManager.get(entityId);
276
+
277
+ return `
278
+ import { ListResult, asArray, NotFoundException, BadRequestException, api } from 'sonamu';
279
+ import { BaseModelClass } from 'sonamu/knex';
280
+ import {
281
+ ${entityId}SubsetKey,
282
+ ${entityId}SubsetMapping,
283
+ } from "../sonamu.generated";
284
+ import {
285
+ ${names.camel}SubsetQueries,
286
+ } from "../sonamu.generated.sso";
287
+ import { ${entityId}ListParams, ${entityId}SaveParams } from "./${names.fs}.types";
288
+
289
+ /*
290
+ ${entityId} Model
291
+ */
292
+ class ${entityId}ModelClass extends BaseModelClass {
293
+ modelName = "${entityId}";
294
+
295
+ @api({ httpMethod: "GET", clients: ["axios", "swr"], resourceName: "${entityId}" })
296
+ async findById<T extends ${entityId}SubsetKey>(
297
+ subset: T,
298
+ id: number
299
+ ): Promise<${entityId}SubsetMapping[T]> {
300
+ const { rows } = await this.findMany(subset, {
301
+ id,
302
+ num: 1,
303
+ page: 1,
304
+ });
305
+ if (rows.length == 0) {
306
+ throw new NotFoundException(\`존재하지 않는 ${names.capital} ID \${id}\`);
307
+ }
308
+
309
+ return rows[0];
310
+ }
311
+
312
+ async findOne<T extends ${entityId}SubsetKey>(
313
+ subset: T,
314
+ listParams: ${entityId}ListParams
315
+ ): Promise<${entityId}SubsetMapping[T] | null> {
316
+ const { rows } = await this.findMany(subset, {
317
+ ...listParams,
318
+ num: 1,
319
+ page: 1,
320
+ });
321
+
322
+ return rows[0] ?? null;
323
+ }
324
+
325
+ @api({ httpMethod: "GET", clients: ["axios", "swr"], resourceName: "${names.capitalPlural}" })
326
+ async findMany<T extends ${entityId}SubsetKey>(
327
+ subset: T,
328
+ params: ${entityId}ListParams = {}
329
+ ): Promise<ListResult<${entityId}SubsetMapping[T]>> {
330
+ // params with defaults
331
+ params = {
332
+ num: 24,
333
+ page: 1,
334
+ search: "${def.search}",
335
+ orderBy: "${def.orderBy}",
336
+ ...params,
337
+ };
338
+
339
+ // build queries
340
+ let { rows, total } = await this.runSubsetQuery({
341
+ subset,
342
+ params,
343
+ subsetQuery: ${names.camel}SubsetQueries[subset],
344
+ build: ({ qb }) => {
345
+ // id
346
+ if (params.id) {
347
+ qb.whereIn("${entity.table}.id", asArray(params.id));
348
+ }
349
+
350
+ // search-keyword
351
+ if (params.search && params.keyword && params.keyword.length > 0) {
352
+ if (params.search === "id") {
353
+ qb.where("${entity.table}.id", params.keyword);
354
+ }
355
+ // } else if (params.search === "field") {
356
+ // qb.where("${entity.table}.field", "like", \`%\${params.keyword}%\`);
357
+ // }
358
+ else {
359
+ throw new BadRequestException(
360
+ \`구현되지 않은 검색 필드 \${params.search}\`
361
+ );
362
+ }
363
+ }
364
+
365
+ // orderBy
366
+ if (params.orderBy) {
367
+ // default orderBy
368
+ const [orderByField, orderByDirec] = params.orderBy.split("-");
369
+ qb.orderBy("${entity.table}." + orderByField, orderByDirec);
370
+ }
371
+
372
+ return qb;
373
+ },
374
+ debug: false,
375
+ });
376
+
377
+ return {
378
+ rows,
379
+ total,
380
+ };
381
+ }
382
+
383
+ @api({ httpMethod: "POST" })
384
+ async save(
385
+ spa: ${entityId}SaveParams[]
386
+ ): Promise<number[]> {
387
+ const wdb = this.getDB("w");
388
+ const ub = this.getUpsertBuilder();
389
+
390
+ // register
391
+ spa.map((sp) => {
392
+ ub.register("${entity.table}", sp);
393
+ });
394
+
395
+ // transaction
396
+ return wdb.transaction(async (trx) => {
397
+ const ids = await ub.upsert(trx, "${entity.table}");
398
+
399
+ return ids;
400
+ });
401
+ }
402
+
403
+ @api({ httpMethod: "POST", guards: [ "admin" ] })
404
+ async del(ids: number[]): Promise<number> {
405
+ const wdb = this.getDB("w");
406
+
407
+ // transaction
408
+ await wdb.transaction(async (trx) => {
409
+ return trx("${entity.table}").whereIn("${entity.table}.id", ids).delete();
410
+ });
411
+
412
+ return ids.length;
413
+ }
414
+ }
415
+
416
+ export const ${entityId}Model = new ${entityId}ModelClass();
417
+ `.trim();
418
+ }
419
+
420
+ /*
421
+ MigrationColumn[] 읽어서 컬럼 정의하는 구문 생성
422
+ */
423
+ private genColumnDefinitions(columns: MigrationColumn[]): string[] {
424
+ return columns.map((column) => {
425
+ const chains: string[] = [];
426
+ if (column.name === "id") {
427
+ return `table.increments().primary();`;
428
+ }
429
+
430
+ // FIXME: float(M,D) deprecated -> decimal(M,D) 이용하도록 하고, float/double 처리 추가
431
+ if (column.type === "float" || column.type === "decimal") {
432
+ chains.push(
433
+ `${column.type}('${column.name}', ${column.precision}, ${column.scale})`
434
+ );
435
+ } else {
436
+ let columnType = column.type;
437
+ let extraType: string | undefined;
438
+ if (columnType.includes("text") && columnType !== "text") {
439
+ extraType = columnType;
440
+ columnType = "text";
441
+ }
442
+ chains.push(
443
+ `${column.type}('${column.name}'${
444
+ column.length ? `, ${column.length}` : ""
445
+ }${extraType ? `, '${extraType}'` : ""})`
446
+ );
447
+ }
448
+ if (column.unsigned) {
449
+ chains.push("unsigned()");
450
+ }
451
+
452
+ chains.push(column.nullable ? "nullable()" : "notNullable()");
453
+
454
+ if (column.defaultTo !== undefined) {
455
+ if (
456
+ typeof column.defaultTo === "string" &&
457
+ column.defaultTo.startsWith(`"`)
458
+ ) {
459
+ chains.push(`defaultTo(${column.defaultTo})`);
460
+ } else {
461
+ chains.push(`defaultTo(knex.raw('${column.defaultTo}'))`);
462
+ }
463
+ }
464
+
465
+ return `table.${chains.join(".")};`;
466
+ });
467
+ }
468
+
469
+ /*
470
+ MigrationIndex[] 읽어서 인덱스/유니크 정의하는 구문 생성
471
+ */
472
+ private genIndexDefinitions(indexes: MigrationIndex[]): string[] {
473
+ if (indexes.length === 0) {
474
+ return [];
475
+ }
476
+ const lines = _.uniq(
477
+ indexes.reduce((r, index) => {
478
+ r.push(
479
+ `table.${index.type}([${index.columns
480
+ .map((col) => `'${col}'`)
481
+ .join(",")}])`
482
+ );
483
+ return r;
484
+ }, [] as string[])
485
+ );
486
+ return lines;
487
+ }
488
+
489
+ private getAlterColumnLinesTo(
490
+ columnsTo: ReturnType<KnexGenerator["getAlterColumnsTo"]>,
491
+ entityColumns: MigrationColumn[]
492
+ ) {
493
+ let linesTo = {
494
+ add: {
495
+ up: [] as string[],
496
+ down: [] as string[],
497
+ },
498
+ drop: {
499
+ up: [] as string[],
500
+ down: [] as string[],
501
+ },
502
+ alter: {
503
+ up: [] as string[],
504
+ down: [] as string[],
505
+ },
506
+ };
507
+
508
+ linesTo.add = {
509
+ up: ["// add", ...this.genColumnDefinitions(columnsTo.add)],
510
+ down: [
511
+ "// rollback - add",
512
+ `table.dropColumns(${columnsTo.add
513
+ .map((col) => `'${col.name}'`)
514
+ .join(", ")})`,
515
+ ],
516
+ };
517
+ linesTo.drop = {
518
+ up: [
519
+ "// drop",
520
+ `table.dropColumns(${columnsTo.drop
521
+ .map((col) => `'${col.name}'`)
522
+ .join(", ")})`,
523
+ ],
524
+ down: [
525
+ "// rollback - drop",
526
+ ...this.genColumnDefinitions(columnsTo.drop),
527
+ ],
528
+ };
529
+ linesTo.alter = columnsTo.alter.reduce(
530
+ (r, dbColumn) => {
531
+ const entityColumn = entityColumns.find(
532
+ (col) => col.name == dbColumn.name
533
+ );
534
+ if (entityColumn === undefined) {
535
+ return r;
536
+ }
537
+
538
+ // 컬럼 변경사항
539
+ const columnDiffUp = _.difference(
540
+ this.genColumnDefinitions([entityColumn]),
541
+ this.genColumnDefinitions([dbColumn])
542
+ );
543
+ const columnDiffDown = _.difference(
544
+ this.genColumnDefinitions([dbColumn]),
545
+ this.genColumnDefinitions([entityColumn])
546
+ );
547
+ if (columnDiffUp.length > 0) {
548
+ r.up = [
549
+ ...r.up,
550
+ "// alter column",
551
+ ...columnDiffUp.map((l) => l.replace(";", "") + ".alter();"),
552
+ ];
553
+ r.down = [
554
+ ...r.down,
555
+ "// rollback - alter column",
556
+ ...columnDiffDown.map((l) => l.replace(";", "") + ".alter();"),
557
+ ];
558
+ }
559
+
560
+ return r;
561
+ },
562
+ {
563
+ up: [] as string[],
564
+ down: [] as string[],
565
+ }
566
+ );
567
+
568
+ return linesTo;
569
+ }
570
+
571
+ private getAlterIndexLinesTo(
572
+ indexesTo: ReturnType<KnexGenerator["getAlterIndexesTo"]>,
573
+ columnsTo: ReturnType<KnexGenerator["getAlterColumnsTo"]>
574
+ ) {
575
+ let linesTo = {
576
+ add: {
577
+ up: [] as string[],
578
+ down: [] as string[],
579
+ },
580
+ drop: {
581
+ up: [] as string[],
582
+ down: [] as string[],
583
+ },
584
+ };
585
+
586
+ // 인덱스가 추가되는 경우, 컬럼과 같이 추가된 케이스에는 drop에서 제외해야함!
587
+ linesTo.add = {
588
+ up: ["// add indexes", ...this.genIndexDefinitions(indexesTo.add)],
589
+ down: [
590
+ "// rollback - add indexes",
591
+ ...indexesTo.add
592
+ .filter(
593
+ (index) =>
594
+ index.columns.every((colName) =>
595
+ columnsTo.add.map((col) => col.name).includes(colName)
596
+ ) === false
597
+ )
598
+ .map(
599
+ (index) =>
600
+ `table.drop${inflection.capitalize(index.type)}([${index.columns
601
+ .map((columnName) => `'${columnName}'`)
602
+ .join(",")}])`
603
+ ),
604
+ ],
605
+ };
606
+ // 인덱스가 삭제되는 경우, 컬럼과 같이 삭제된 케이스에는 drop에서 제외해야함!
607
+ linesTo.drop = {
608
+ up: [
609
+ ...indexesTo.drop
610
+ .filter(
611
+ (index) =>
612
+ index.columns.every((colName) =>
613
+ columnsTo.drop.map((col) => col.name).includes(colName)
614
+ ) === false
615
+ )
616
+ .map(
617
+ (index) =>
618
+ `table.drop${inflection.capitalize(index.type)}([${index.columns
619
+ .map((columnName) => `'${columnName}'`)
620
+ .join(",")}])`
621
+ ),
622
+ ],
623
+ down: [
624
+ "// rollback - drop indexes",
625
+ ...this.genIndexDefinitions(indexesTo.drop),
626
+ ],
627
+ };
628
+
629
+ return linesTo;
630
+ }
631
+
632
+ /*
633
+ MigrationForeign[] 읽어서 외부키 constraint 정의하는 구문 생성
634
+ */
635
+ private genForeignDefinitions(
636
+ table: string,
637
+ foreigns: MigrationForeign[]
638
+ ): { up: string[]; down: string[] } {
639
+ return foreigns.reduce(
640
+ (r, foreign) => {
641
+ const columnsStringQuote = foreign.columns
642
+ .map((col) => `'${col.replace(`${table}.`, "")}'`)
643
+ .join(",");
644
+ r.up.push(
645
+ `table.foreign('${foreign.columns.join(",")}')
646
+ .references('${foreign.to}')
647
+ .onUpdate('${foreign.onUpdate}')
648
+ .onDelete('${foreign.onDelete}')`
649
+ );
650
+ r.down.push(`table.dropForeign([${columnsStringQuote}])`);
651
+ return r;
652
+ },
653
+ {
654
+ up: [] as string[],
655
+ down: [] as string[],
656
+ }
657
+ );
658
+ }
659
+ }