sonamu 0.4.13 → 0.5.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.
Files changed (371) hide show
  1. package/.swcrc +15 -0
  2. package/dist/api/base-frame.d.ts +8 -0
  3. package/dist/api/base-frame.d.ts.map +1 -0
  4. package/dist/api/base-frame.js +2 -0
  5. package/dist/api/base-frame.js.map +1 -0
  6. package/dist/api/caster.d.ts +5 -0
  7. package/dist/api/caster.d.ts.map +1 -0
  8. package/dist/api/caster.js +2 -0
  9. package/dist/api/caster.js.map +1 -0
  10. package/dist/api/code-converters.d.ts +23 -0
  11. package/dist/api/code-converters.d.ts.map +1 -0
  12. package/dist/api/code-converters.js +2 -0
  13. package/dist/api/code-converters.js.map +1 -0
  14. package/dist/api/context.d.ts +16 -0
  15. package/dist/api/context.d.ts.map +1 -0
  16. package/dist/api/context.js +2 -0
  17. package/dist/api/context.js.map +1 -0
  18. package/dist/api/decorators.d.ts +50 -0
  19. package/dist/api/decorators.d.ts.map +1 -0
  20. package/dist/api/decorators.js +2 -0
  21. package/dist/api/decorators.js.map +1 -0
  22. package/dist/api/index.d.ts +8 -0
  23. package/dist/api/index.d.ts.map +1 -0
  24. package/dist/api/index.js +2 -0
  25. package/dist/api/index.js.map +1 -0
  26. package/dist/api/sonamu.d.ts +83 -0
  27. package/dist/api/sonamu.d.ts.map +1 -0
  28. package/dist/api/sonamu.js +2 -0
  29. package/dist/api/sonamu.js.map +1 -0
  30. package/dist/api/sonamu.types.d.ts +30 -0
  31. package/dist/api/sonamu.types.d.ts.map +1 -0
  32. package/dist/api/sonamu.types.js +2 -0
  33. package/dist/api/sonamu.types.js.map +1 -0
  34. package/dist/bin/build-config.d.ts +5 -0
  35. package/dist/bin/build-config.d.ts.map +1 -0
  36. package/dist/bin/build-config.js +2 -0
  37. package/dist/bin/build-config.js.map +1 -0
  38. package/dist/bin/cli-wrapper.d.ts +2 -0
  39. package/dist/bin/cli-wrapper.d.ts.map +1 -0
  40. package/dist/bin/cli-wrapper.js +1 -38
  41. package/dist/bin/cli-wrapper.js.map +1 -1
  42. package/dist/bin/cli.d.ts +2 -2
  43. package/dist/bin/cli.d.ts.map +1 -0
  44. package/dist/bin/cli.js +1 -903
  45. package/dist/bin/cli.js.map +1 -1
  46. package/dist/bin/cli.mjs +2 -2
  47. package/dist/{chunk-DMJSNO2L.js → chunk-2WAC2GER.js} +44 -44
  48. package/dist/{chunk-DMJSNO2L.js.map → chunk-2WAC2GER.js.map} +1 -1
  49. package/dist/{chunk-NI37CY4T.mjs → chunk-C3IPIF6O.mjs} +2 -2
  50. package/dist/{chunk-DYFCACHD.js → chunk-EXHKSVTE.js} +7 -7
  51. package/dist/{chunk-QJFHDCBN.mjs → chunk-FCERKIIF.mjs} +2 -2
  52. package/dist/chunk-FCERKIIF.mjs.map +1 -0
  53. package/dist/{chunk-DDJ7T4MA.mjs → chunk-HGIBJYOU.mjs} +2 -2
  54. package/dist/{chunk-NIFOTHBW.mjs → chunk-JKSOJRQA.mjs} +2 -2
  55. package/dist/{chunk-CXAVBVKC.js → chunk-OTKKFP3Y.js} +100 -100
  56. package/dist/{chunk-J6S43O7G.js → chunk-UZ2IY5VE.js} +4 -4
  57. package/dist/database/_batch_update.d.ts +15 -0
  58. package/dist/database/_batch_update.d.ts.map +1 -0
  59. package/dist/database/_batch_update.js +2 -0
  60. package/dist/database/_batch_update.js.map +1 -0
  61. package/dist/database/base-model.d.ts +41 -0
  62. package/dist/database/base-model.d.ts.map +1 -0
  63. package/dist/database/base-model.js +2 -0
  64. package/dist/database/base-model.js.map +1 -0
  65. package/dist/database/code-generator.d.ts +13 -0
  66. package/dist/database/code-generator.d.ts.map +1 -0
  67. package/dist/database/code-generator.js +2 -0
  68. package/dist/database/code-generator.js.map +1 -0
  69. package/dist/database/db.d.ts +40 -0
  70. package/dist/database/db.d.ts.map +1 -0
  71. package/dist/database/db.js +2 -0
  72. package/dist/database/db.js.map +1 -0
  73. package/dist/database/drivers/knex/base-model.js +8 -8
  74. package/dist/database/drivers/knex/base-model.mjs +3 -3
  75. package/dist/database/drivers/kysely/base-model.js +9 -9
  76. package/dist/database/drivers/kysely/base-model.mjs +3 -3
  77. package/dist/database/knex-plugins/knex-on-duplicate-update.d.ts +2 -0
  78. package/dist/database/knex-plugins/knex-on-duplicate-update.d.ts.map +1 -0
  79. package/dist/database/knex-plugins/knex-on-duplicate-update.js +2 -0
  80. package/dist/database/knex-plugins/knex-on-duplicate-update.js.map +1 -0
  81. package/dist/database/puri-wrapper.d.ts +34 -0
  82. package/dist/database/puri-wrapper.d.ts.map +1 -0
  83. package/dist/database/puri-wrapper.js +2 -0
  84. package/dist/database/puri-wrapper.js.map +1 -0
  85. package/dist/database/puri.d.ts +83 -0
  86. package/dist/database/puri.d.ts.map +1 -0
  87. package/dist/database/puri.js +2 -0
  88. package/dist/database/puri.js.map +1 -0
  89. package/dist/database/puri.types.d.ts +60 -0
  90. package/dist/database/puri.types.d.ts.map +1 -0
  91. package/dist/database/puri.types.js +2 -0
  92. package/dist/database/puri.types.js.map +1 -0
  93. package/dist/database/transaction-context.d.ts +9 -0
  94. package/dist/database/transaction-context.d.ts.map +1 -0
  95. package/dist/database/transaction-context.js +2 -0
  96. package/dist/database/transaction-context.js.map +1 -0
  97. package/dist/database/types.d.ts +39 -0
  98. package/dist/database/types.d.ts.map +1 -0
  99. package/dist/database/types.js +2 -0
  100. package/dist/database/types.js.map +1 -0
  101. package/dist/database/upsert-builder.d.ts +34 -0
  102. package/dist/database/upsert-builder.d.ts.map +1 -0
  103. package/dist/database/upsert-builder.js +2 -0
  104. package/dist/database/upsert-builder.js.map +1 -0
  105. package/dist/entity/entity-manager.d.ts +32 -0
  106. package/dist/entity/entity-manager.d.ts.map +1 -0
  107. package/dist/entity/entity-manager.js +2 -0
  108. package/dist/entity/entity-manager.js.map +1 -0
  109. package/dist/entity/entity-utils.d.ts +61 -0
  110. package/dist/entity/entity-utils.d.ts.map +1 -0
  111. package/dist/entity/entity-utils.js +2 -0
  112. package/dist/entity/entity-utils.js.map +1 -0
  113. package/dist/entity/entity.d.ts +62 -0
  114. package/dist/entity/entity.d.ts.map +1 -0
  115. package/dist/entity/entity.js +2 -0
  116. package/dist/entity/entity.js.map +1 -0
  117. package/dist/entity/migrator.d.ts +135 -0
  118. package/dist/entity/migrator.d.ts.map +1 -0
  119. package/dist/entity/migrator.js +2 -0
  120. package/dist/entity/migrator.js.map +1 -0
  121. package/dist/exceptions/error-handler.d.ts +3 -0
  122. package/dist/exceptions/error-handler.d.ts.map +1 -0
  123. package/dist/exceptions/error-handler.js +2 -0
  124. package/dist/exceptions/error-handler.js.map +1 -0
  125. package/dist/exceptions/so-exceptions.d.ts +48 -0
  126. package/dist/exceptions/so-exceptions.d.ts.map +1 -0
  127. package/dist/exceptions/so-exceptions.js +2 -0
  128. package/dist/exceptions/so-exceptions.js.map +1 -0
  129. package/dist/file-storage/driver.d.ts +45 -0
  130. package/dist/file-storage/driver.d.ts.map +1 -0
  131. package/dist/file-storage/driver.js +2 -0
  132. package/dist/file-storage/driver.js.map +1 -0
  133. package/dist/file-storage/file-storage.d.ts +50 -0
  134. package/dist/file-storage/file-storage.d.ts.map +1 -0
  135. package/dist/file-storage/file-storage.js +2 -0
  136. package/dist/file-storage/file-storage.js.map +1 -0
  137. package/dist/index.d.ts +22 -813
  138. package/dist/index.d.ts.map +1 -0
  139. package/dist/index.js +1 -433
  140. package/dist/index.js.map +1 -1
  141. package/dist/index.mjs +3 -3
  142. package/dist/migration/code-generation.d.ts +15 -0
  143. package/dist/migration/code-generation.d.ts.map +1 -0
  144. package/dist/migration/code-generation.js +2 -0
  145. package/dist/migration/code-generation.js.map +1 -0
  146. package/dist/migration/migration-set.d.ts +17 -0
  147. package/dist/migration/migration-set.d.ts.map +1 -0
  148. package/dist/migration/migration-set.js +2 -0
  149. package/dist/migration/migration-set.js.map +1 -0
  150. package/dist/migration/migrator.d.ts +130 -0
  151. package/dist/migration/migrator.d.ts.map +1 -0
  152. package/dist/migration/migrator.js +2 -0
  153. package/dist/migration/migrator.js.map +1 -0
  154. package/dist/migration/types.d.ts +52 -0
  155. package/dist/migration/types.d.ts.map +1 -0
  156. package/dist/migration/types.js +2 -0
  157. package/dist/migration/types.js.map +1 -0
  158. package/dist/smd/smd-manager.d.ts +28 -0
  159. package/dist/smd/smd-manager.d.ts.map +1 -0
  160. package/dist/smd/smd-manager.js +2 -0
  161. package/dist/smd/smd-manager.js.map +1 -0
  162. package/dist/smd/smd.d.ts +40 -0
  163. package/dist/smd/smd.d.ts.map +1 -0
  164. package/dist/smd/smd.js +2 -0
  165. package/dist/smd/smd.js.map +1 -0
  166. package/dist/syncer/index.d.ts +2 -0
  167. package/dist/syncer/index.d.ts.map +1 -0
  168. package/dist/syncer/index.js +2 -0
  169. package/dist/syncer/index.js.map +1 -0
  170. package/dist/syncer/syncer.d.ts +127 -0
  171. package/dist/syncer/syncer.d.ts.map +1 -0
  172. package/dist/syncer/syncer.js +2 -0
  173. package/dist/syncer/syncer.js.map +1 -0
  174. package/dist/templates/base-template.d.ts +13 -0
  175. package/dist/templates/base-template.d.ts.map +1 -0
  176. package/dist/templates/base-template.js +2 -0
  177. package/dist/templates/base-template.js.map +1 -0
  178. package/dist/templates/entity.template.d.ts +17 -0
  179. package/dist/templates/entity.template.d.ts.map +1 -0
  180. package/dist/templates/entity.template.js +2 -0
  181. package/dist/templates/entity.template.js.map +1 -0
  182. package/dist/templates/generated.template.d.ts +27 -0
  183. package/dist/templates/generated.template.d.ts.map +1 -0
  184. package/dist/templates/generated.template.js +2 -0
  185. package/dist/templates/generated.template.js.map +1 -0
  186. package/dist/templates/generated_http.template.d.ts +24 -0
  187. package/dist/templates/generated_http.template.d.ts.map +1 -0
  188. package/dist/templates/generated_http.template.js +2 -0
  189. package/dist/templates/generated_http.template.js.map +1 -0
  190. package/dist/templates/generated_sso.template.d.ts +20 -0
  191. package/dist/templates/generated_sso.template.d.ts.map +1 -0
  192. package/dist/templates/generated_sso.template.js +2 -0
  193. package/dist/templates/generated_sso.template.js.map +1 -0
  194. package/dist/templates/index.d.ts +2 -0
  195. package/dist/templates/index.d.ts.map +1 -0
  196. package/dist/templates/index.js +2 -0
  197. package/dist/templates/index.js.map +1 -0
  198. package/dist/templates/init_types.template.d.ts +17 -0
  199. package/dist/templates/init_types.template.d.ts.map +1 -0
  200. package/dist/templates/init_types.template.js +2 -0
  201. package/dist/templates/init_types.template.js.map +1 -0
  202. package/dist/templates/model.template.d.ts +17 -0
  203. package/dist/templates/model.template.d.ts.map +1 -0
  204. package/dist/templates/model.template.js +2 -0
  205. package/dist/templates/model.template.js.map +1 -0
  206. package/dist/templates/model_test.template.d.ts +17 -0
  207. package/dist/templates/model_test.template.d.ts.map +1 -0
  208. package/dist/templates/model_test.template.js +2 -0
  209. package/dist/templates/model_test.template.js.map +1 -0
  210. package/dist/templates/service.template.d.ts +29 -0
  211. package/dist/templates/service.template.d.ts.map +1 -0
  212. package/dist/templates/service.template.js +2 -0
  213. package/dist/templates/service.template.js.map +1 -0
  214. package/dist/templates/view_enums_buttonset.template.d.ts +17 -0
  215. package/dist/templates/view_enums_buttonset.template.d.ts.map +1 -0
  216. package/dist/templates/view_enums_buttonset.template.js +2 -0
  217. package/dist/templates/view_enums_buttonset.template.js.map +1 -0
  218. package/dist/templates/view_enums_dropdown.template.d.ts +18 -0
  219. package/dist/templates/view_enums_dropdown.template.d.ts.map +1 -0
  220. package/dist/templates/view_enums_dropdown.template.js +2 -0
  221. package/dist/templates/view_enums_dropdown.template.js.map +1 -0
  222. package/dist/templates/view_enums_select.template.d.ts +17 -0
  223. package/dist/templates/view_enums_select.template.d.ts.map +1 -0
  224. package/dist/templates/view_enums_select.template.js +2 -0
  225. package/dist/templates/view_enums_select.template.js.map +1 -0
  226. package/dist/templates/view_form.template.d.ts +26 -0
  227. package/dist/templates/view_form.template.d.ts.map +1 -0
  228. package/dist/templates/view_form.template.js +2 -0
  229. package/dist/templates/view_form.template.js.map +1 -0
  230. package/dist/templates/view_id_all_select.template.d.ts +17 -0
  231. package/dist/templates/view_id_all_select.template.d.ts.map +1 -0
  232. package/dist/templates/view_id_all_select.template.js +2 -0
  233. package/dist/templates/view_id_all_select.template.js.map +1 -0
  234. package/dist/templates/view_id_async_select.template.d.ts +17 -0
  235. package/dist/templates/view_id_async_select.template.d.ts.map +1 -0
  236. package/dist/templates/view_id_async_select.template.js +2 -0
  237. package/dist/templates/view_id_async_select.template.js.map +1 -0
  238. package/dist/templates/view_list.template.d.ts +38 -0
  239. package/dist/templates/view_list.template.d.ts.map +1 -0
  240. package/dist/templates/view_list.template.js +2 -0
  241. package/dist/templates/view_list.template.js.map +1 -0
  242. package/dist/templates/view_list_columns.template.d.ts +17 -0
  243. package/dist/templates/view_list_columns.template.d.ts.map +1 -0
  244. package/dist/templates/view_list_columns.template.js +2 -0
  245. package/dist/templates/view_list_columns.template.js.map +1 -0
  246. package/dist/templates/view_search_input.template.d.ts +17 -0
  247. package/dist/templates/view_search_input.template.d.ts.map +1 -0
  248. package/dist/templates/view_search_input.template.js +2 -0
  249. package/dist/templates/view_search_input.template.js.map +1 -0
  250. package/dist/testing/_relation-graph.d.ts +7 -0
  251. package/dist/testing/_relation-graph.d.ts.map +1 -0
  252. package/dist/testing/_relation-graph.js +2 -0
  253. package/dist/testing/_relation-graph.js.map +1 -0
  254. package/dist/testing/fixture-manager.d.ts +35 -0
  255. package/dist/testing/fixture-manager.d.ts.map +1 -0
  256. package/dist/testing/fixture-manager.js +2 -0
  257. package/dist/testing/fixture-manager.js.map +1 -0
  258. package/dist/types/types.d.ts +609 -0
  259. package/dist/types/types.d.ts.map +1 -0
  260. package/dist/types/types.js +2 -0
  261. package/dist/types/types.js.map +1 -0
  262. package/dist/typings/knex.d.js +2 -0
  263. package/dist/typings/knex.d.js.map +1 -0
  264. package/dist/utils/async-utils.d.ts +25 -0
  265. package/dist/utils/async-utils.d.ts.map +1 -0
  266. package/dist/utils/async-utils.js +2 -0
  267. package/dist/utils/async-utils.js.map +1 -0
  268. package/dist/utils/controller.d.ts +9 -0
  269. package/dist/utils/controller.d.ts.map +1 -0
  270. package/dist/utils/controller.js +2 -0
  271. package/dist/utils/controller.js.map +1 -0
  272. package/dist/utils/fs-utils.d.ts +9 -0
  273. package/dist/utils/fs-utils.d.ts.map +1 -0
  274. package/dist/utils/fs-utils.js +2 -0
  275. package/dist/utils/fs-utils.js.map +1 -0
  276. package/dist/utils/lodash-able.d.ts +2 -0
  277. package/dist/utils/lodash-able.d.ts.map +1 -0
  278. package/dist/utils/lodash-able.js +2 -0
  279. package/dist/utils/lodash-able.js.map +1 -0
  280. package/dist/utils/model.d.ts +17 -0
  281. package/dist/utils/model.d.ts.map +1 -0
  282. package/dist/utils/model.js +2 -0
  283. package/dist/utils/model.js.map +1 -0
  284. package/dist/utils/sql-parser.d.ts +4 -0
  285. package/dist/utils/sql-parser.d.ts.map +1 -0
  286. package/dist/utils/sql-parser.js +2 -0
  287. package/dist/utils/sql-parser.js.map +1 -0
  288. package/dist/utils/utils.d.ts +9 -0
  289. package/dist/utils/utils.d.ts.map +1 -0
  290. package/dist/utils/utils.js +2 -0
  291. package/dist/utils/utils.js.map +1 -0
  292. package/dist/utils/zod-error.d.ts +8 -0
  293. package/dist/utils/zod-error.d.ts.map +1 -0
  294. package/dist/utils/zod-error.js +2 -0
  295. package/dist/utils/zod-error.js.map +1 -0
  296. package/nodemon.json +6 -0
  297. package/package.json +29 -44
  298. package/src/api/base-frame.ts +3 -4
  299. package/src/api/caster.ts +22 -23
  300. package/src/api/code-converters.ts +170 -134
  301. package/src/api/context.ts +13 -6
  302. package/src/api/decorators.ts +146 -20
  303. package/src/api/index.ts +2 -0
  304. package/src/api/sonamu.ts +374 -165
  305. package/src/bin/build-config.ts +5 -0
  306. package/src/bin/cli-wrapper.ts +29 -40
  307. package/src/bin/cli.ts +132 -190
  308. package/src/database/_batch_update.ts +10 -15
  309. package/src/database/base-model.ts +300 -216
  310. package/src/database/db.ts +191 -21
  311. package/src/database/{drivers/knex/plugins → knex-plugins}/knex-on-duplicate-update.ts +1 -1
  312. package/src/database/puri-wrapper.ts +129 -0
  313. package/src/database/puri.ts +808 -0
  314. package/src/database/puri.types.ts +222 -0
  315. package/src/database/transaction-context.ts +18 -0
  316. package/src/database/upsert-builder.ts +32 -35
  317. package/src/entity/entity-manager.ts +7 -15
  318. package/src/entity/entity.ts +9 -31
  319. package/src/entity/migrator-/354/235/264/354/202/254/352/260/224/354/226/264/354/232/224.md +1 -0
  320. package/src/file-storage/driver.ts +121 -0
  321. package/src/file-storage/file-storage.ts +100 -0
  322. package/src/index.ts +14 -11
  323. package/src/migration/code-generation.ts +777 -0
  324. package/src/migration/migration-set.ts +453 -0
  325. package/src/migration/migrator.ts +823 -0
  326. package/src/migration/types.ts +53 -0
  327. package/src/shared/web.shared.ts.txt +33 -2
  328. package/src/syncer/syncer.ts +294 -127
  329. package/src/templates/generated.template.ts +13 -1
  330. package/src/templates/generated_http.template.ts +15 -12
  331. package/src/templates/generated_sso.template.ts +50 -2
  332. package/src/templates/model.template.ts +138 -2
  333. package/src/templates/service.template.ts +0 -1
  334. package/src/templates/view_form.template.ts +11 -7
  335. package/src/templates/view_list.template.ts +12 -4
  336. package/src/testing/fixture-manager.ts +229 -174
  337. package/src/types/types.ts +102 -14
  338. package/src/utils/async-utils.ts +64 -0
  339. package/src/utils/fs-utils.ts +17 -0
  340. package/src/utils/model.ts +0 -2
  341. package/src/utils/utils.ts +14 -58
  342. package/src/utils/zod-error.ts +12 -176
  343. package/tsconfig.json +2 -0
  344. package/tsup.config.js +4 -2
  345. package/.pnp.cjs +0 -14363
  346. package/.pnp.loader.mjs +0 -2047
  347. package/.vscode/extensions.json +0 -6
  348. package/.vscode/settings.json +0 -9
  349. package/.yarnrc.yml +0 -5
  350. package/dist/chunk-QJFHDCBN.mjs.map +0 -1
  351. package/src/database/base-model.abstract.ts +0 -97
  352. package/src/database/db.abstract.ts +0 -75
  353. package/src/database/drivers/knex/base-model.ts +0 -55
  354. package/src/database/drivers/knex/client.ts +0 -209
  355. package/src/database/drivers/knex/db.ts +0 -232
  356. package/src/database/drivers/knex/generator.ts +0 -659
  357. package/src/database/drivers/kysely/base-model.ts +0 -89
  358. package/src/database/drivers/kysely/client.ts +0 -309
  359. package/src/database/drivers/kysely/db.ts +0 -238
  360. package/src/database/drivers/kysely/generator.ts +0 -714
  361. package/src/database/types.ts +0 -118
  362. package/src/entity/migrator.ts +0 -1400
  363. package/src/smd/smd-manager.ts +0 -139
  364. package/src/smd/smd.ts +0 -571
  365. package/src/templates/kysely_types.template.ts +0 -205
  366. /package/dist/{chunk-NI37CY4T.mjs.map → chunk-C3IPIF6O.mjs.map} +0 -0
  367. /package/dist/{chunk-DYFCACHD.js.map → chunk-EXHKSVTE.js.map} +0 -0
  368. /package/dist/{chunk-DDJ7T4MA.mjs.map → chunk-HGIBJYOU.mjs.map} +0 -0
  369. /package/dist/{chunk-NIFOTHBW.mjs.map → chunk-JKSOJRQA.mjs.map} +0 -0
  370. /package/dist/{chunk-CXAVBVKC.js.map → chunk-OTKKFP3Y.js.map} +0 -0
  371. /package/dist/{chunk-J6S43O7G.js.map → chunk-UZ2IY5VE.js.map} +0 -0
@@ -0,0 +1,777 @@
1
+ import _ from "lodash";
2
+ import prettier from "prettier";
3
+ import equal from "fast-deep-equal";
4
+ import {
5
+ GenMigrationCode,
6
+ MigrationColumn,
7
+ MigrationForeign,
8
+ MigrationIndex,
9
+ MigrationSet,
10
+ } from "../types/types";
11
+
12
+ /**
13
+ * 테이블 생성하는 케이스 - 컬럼/인덱스 생성
14
+ */
15
+ async function generateCreateCode_ColumnAndIndexes(
16
+ table: string,
17
+ columns: MigrationColumn[],
18
+ indexes: MigrationIndex[]
19
+ ): Promise<GenMigrationCode> {
20
+ // fulltext index 분리
21
+ const [ngramIndexes, standardIndexes] = _.partition(
22
+ indexes,
23
+ (i) => i.type === "fulltext" && i.parser === "ngram"
24
+ );
25
+
26
+ // 컬럼, 인덱스 처리
27
+ const lines: string[] = [
28
+ 'import { Knex } from "knex";',
29
+ "",
30
+ "export async function up(knex: Knex): Promise<void> {",
31
+ `await knex.schema.createTable("${table}", (table) => {`,
32
+ "// columns",
33
+ ...genColumnDefinitions(columns),
34
+ "",
35
+ "// indexes",
36
+ ...standardIndexes.map((index) => genIndexDefinition(index, table)),
37
+ "});",
38
+ // ngram은 knex.raw로 처리하므로 createTable 밖에서 실행
39
+ ...ngramIndexes.map((index) => genIndexDefinition(index, table)),
40
+ "}",
41
+ "",
42
+ "export async function down(knex: Knex): Promise<void> {",
43
+ ` return knex.schema.dropTable("${table}");`,
44
+ "}",
45
+ ];
46
+ return {
47
+ table,
48
+ type: "normal",
49
+ title: `create__${table}`,
50
+ formatted: await prettier.format(lines.join("\n"), {
51
+ parser: "typescript",
52
+ }),
53
+ };
54
+ }
55
+
56
+ /**
57
+ * MigrationColumn[] 읽어서 컬럼 정의하는 구문 생성
58
+ */
59
+ function genColumnDefinitions(columns: MigrationColumn[]): string[] {
60
+ return columns.map((column) => {
61
+ const chains: string[] = [];
62
+ if (column.name === "id") {
63
+ return `table.increments().primary();`;
64
+ }
65
+
66
+ // FIXME: float(M,D) deprecated -> decimal(M,D) 이용하도록 하고, float/double 처리 추가
67
+ if (column.type === "float" || column.type === "decimal") {
68
+ chains.push(
69
+ `${column.type}('${column.name}', ${column.precision}, ${column.scale})`
70
+ );
71
+ } else {
72
+ // type, length
73
+ let columnType = column.type;
74
+ let extraType: string | undefined;
75
+ if (columnType.includes("text") && columnType !== "text") {
76
+ extraType = columnType;
77
+ columnType = "text";
78
+ }
79
+ chains.push(
80
+ `${column.type}('${column.name}'${
81
+ column.length ? `, ${column.length}` : ""
82
+ }${extraType ? `, '${extraType}'` : ""})`
83
+ );
84
+ }
85
+ if (column.unsigned) {
86
+ chains.push("unsigned()");
87
+ }
88
+
89
+ // nullable
90
+ chains.push(column.nullable ? "nullable()" : "notNullable()");
91
+
92
+ // defaultTo
93
+ if (column.defaultTo !== undefined) {
94
+ if (
95
+ typeof column.defaultTo === "string" &&
96
+ column.defaultTo.startsWith(`"`)
97
+ ) {
98
+ chains.push(`defaultTo(${column.defaultTo})`);
99
+ } else {
100
+ chains.push(`defaultTo(knex.raw('${column.defaultTo}'))`);
101
+ }
102
+ }
103
+
104
+ return `table.${chains.join(".")};`;
105
+ });
106
+ }
107
+
108
+ /**
109
+ * 개별 인덱스 정의 생성
110
+ */
111
+ function genIndexDefinition(index: MigrationIndex, table: string) {
112
+ const methodMap = {
113
+ index: "index",
114
+ fulltext: "index",
115
+ unique: "unique",
116
+ };
117
+
118
+ if (index.type === "fulltext" && index.parser === "ngram") {
119
+ const indexName = `${table}_${index.columns.join("_")}_index`;
120
+ return `await knex.raw(\`ALTER TABLE ${table} ADD FULLTEXT INDEX ${indexName} (${index.columns.join(
121
+ ", "
122
+ )}) WITH PARSER ngram\`);`;
123
+ }
124
+
125
+ return `table.${methodMap[index.type]}([${index.columns
126
+ .map((col) => `'${col}'`)
127
+ .join(",")}]${index.type === "fulltext" ? ", undefined, 'FULLTEXT'" : ""})`;
128
+ }
129
+
130
+ /**
131
+ * 테이블 생성하는 케이스 - FK 생성
132
+ */
133
+ async function generateCreateCode_Foreign(
134
+ table: string,
135
+ foreigns: MigrationForeign[]
136
+ ): Promise<GenMigrationCode[]> {
137
+ if (foreigns.length === 0) {
138
+ return [];
139
+ }
140
+
141
+ const { up, down } = genForeignDefinitions(table, foreigns);
142
+ if (up.length === 0 && down.length === 0) {
143
+ console.log("fk 가 뭔가 다릅니다");
144
+ return [];
145
+ }
146
+
147
+ const lines: string[] = [
148
+ 'import { Knex } from "knex";',
149
+ "",
150
+ "export async function up(knex: Knex): Promise<void> {",
151
+ `return knex.schema.alterTable("${table}", (table) => {`,
152
+ "// create fk",
153
+ ...up,
154
+ "});",
155
+ "}",
156
+ "",
157
+ "export async function down(knex: Knex): Promise<void> {",
158
+ `return knex.schema.alterTable("${table}", (table) => {`,
159
+ "// drop fk",
160
+ ...down,
161
+ "});",
162
+ "}",
163
+ ];
164
+
165
+ const foreignKeysString = foreigns
166
+ .map((foreign) => foreign.columns.join("_"))
167
+ .join("_");
168
+ return [
169
+ {
170
+ table,
171
+ type: "foreign",
172
+ title: `foreign__${table}__${foreignKeysString}`,
173
+ formatted: await prettier.format(lines.join("\n"), {
174
+ parser: "typescript",
175
+ }),
176
+ },
177
+ ];
178
+ }
179
+
180
+ /**
181
+ * MigrationForeign[] 읽어서 외부키 constraint 정의하는 구문 생성
182
+ */
183
+ function genForeignDefinitions(
184
+ table: string,
185
+ foreigns: MigrationForeign[]
186
+ ): { up: string[]; down: string[] } {
187
+ return foreigns.reduce(
188
+ (r, foreign) => {
189
+ const columnsStringQuote = foreign.columns
190
+ .map((col) => `'${col.replace(`${table}.`, "")}'`)
191
+ .join(",");
192
+ r.up.push(
193
+ `table.foreign('${foreign.columns.join(",")}')
194
+ .references('${foreign.to}')
195
+ .onUpdate('${foreign.onUpdate}')
196
+ .onDelete('${foreign.onDelete}')`
197
+ );
198
+ r.down.push(`table.dropForeign([${columnsStringQuote}])`);
199
+ return r;
200
+ },
201
+ {
202
+ up: [] as string[],
203
+ down: [] as string[],
204
+ }
205
+ );
206
+ }
207
+
208
+ /**
209
+ * 테이블 변경 케이스 - 컬럼/인덱스 변경
210
+ */
211
+ async function generateAlterCode_ColumnAndIndexes(
212
+ table: string,
213
+ entityColumns: MigrationColumn[],
214
+ entityIndexes: MigrationIndex[],
215
+ dbColumns: MigrationColumn[],
216
+ dbIndexes: MigrationIndex[],
217
+ dbForeigns: MigrationForeign[]
218
+ ): Promise<GenMigrationCode[]> {
219
+ /*
220
+ 세부 비교 후 다른점 찾아서 코드 생성
221
+
222
+ 1. 컬럼갯수 다름: MD에 있으나, DB에 없다면 추가
223
+ 2. 컬럼갯수 다름: MD에 없으나, DB에 있다면 삭제
224
+ 3. 그외 컬럼(컬럼 갯수가 동일하거나, 다른 경우 동일한 컬럼끼리) => alter
225
+ 4. 다른거 다 동일하고 index만 변경되는 경우
226
+
227
+ ** 컬럼명을 변경하는 경우는 따로 핸들링하지 않음
228
+ => drop/add 형태의 마이그레이션 코드가 생성되는데, 수동으로 rename 코드로 수정하여 처리
229
+ */
230
+
231
+ // 각 컬럼 이름 기준으로 add, drop, alter 여부 확인
232
+ const alterColumnsTo = getAlterColumnsTo(entityColumns, dbColumns);
233
+
234
+ // 추출된 컬럼들을 기준으로 각각 라인 생성
235
+ const alterColumnLinesTo = getAlterColumnLinesTo(
236
+ alterColumnsTo,
237
+ entityColumns,
238
+ table,
239
+ dbForeigns
240
+ );
241
+
242
+ // 인덱스의 add, drop 여부 확인
243
+ const alterIndexesTo = getAlterIndexesTo(entityIndexes, dbIndexes);
244
+
245
+ // fulltext index 분리
246
+ const [ngramIndexes, standardIndexes] = _.partition(
247
+ alterIndexesTo.add,
248
+ (i) => i.type === "fulltext" && i.parser === "ngram"
249
+ );
250
+
251
+ // 인덱스가 삭제되는 경우, 컬럼과 같이 삭제된 케이스에는 drop에서 제외해야함!
252
+ const indexNeedsToDrop = alterIndexesTo.drop.filter(
253
+ (index) =>
254
+ index.columns.every((colName) =>
255
+ alterColumnsTo.drop.map((col) => col.name).includes(colName)
256
+ ) === false
257
+ );
258
+
259
+ const lines: string[] = [
260
+ 'import { Knex } from "knex";',
261
+ "",
262
+ "export async function up(knex: Knex): Promise<void> {",
263
+ `await knex.schema.alterTable("${table}", (table) => {`,
264
+ // 1. add column
265
+ ...(alterColumnsTo.add.length > 0 ? alterColumnLinesTo.add.up : []),
266
+ // 2. drop column
267
+ ...(alterColumnsTo.drop.length > 0 ? alterColumnLinesTo.drop.up : []),
268
+ // 3. alter column
269
+ ...(alterColumnsTo.alter.length > 0 ? alterColumnLinesTo.alter.up : []),
270
+ // 4. add index
271
+ ...standardIndexes.map((index) => genIndexDefinition(index, table)),
272
+ // 5. drop index
273
+ ...indexNeedsToDrop.map(genIndexDropDefinition),
274
+ "});",
275
+ // ngram은 knex.raw로 처리하므로 alterTable 밖에서 실행
276
+ ...ngramIndexes.map((index) => genIndexDefinition(index, table)),
277
+ "}",
278
+ "",
279
+ "export async function down(knex: Knex): Promise<void> {",
280
+ `return knex.schema.alterTable("${table}", (table) => {`,
281
+ ...(alterColumnsTo.add.length > 0 ? alterColumnLinesTo.add.down : []),
282
+ ...(alterColumnsTo.drop.length > 0 ? alterColumnLinesTo.drop.down : []),
283
+ ...(alterColumnsTo.alter.length > 0 ? alterColumnLinesTo.alter.down : []),
284
+ ...alterIndexesTo.add
285
+ .filter(
286
+ (index) =>
287
+ index.columns.every((colName) =>
288
+ alterColumnsTo.add.map((col) => col.name).includes(colName)
289
+ ) === false
290
+ )
291
+ .map(genIndexDropDefinition),
292
+ ...indexNeedsToDrop.map((index) => genIndexDefinition(index, table)),
293
+ "});",
294
+ "}",
295
+ ];
296
+
297
+ const formatted = await prettier.format(lines.join("\n"), {
298
+ parser: "typescript",
299
+ });
300
+
301
+ const title = [
302
+ "alter",
303
+ table,
304
+ ...(["add", "drop", "alter"] as const)
305
+ .map((action) => {
306
+ const len = alterColumnsTo[action].length;
307
+ if (len > 0) {
308
+ return action + len;
309
+ }
310
+ return null;
311
+ })
312
+ .filter((part) => part !== null),
313
+ ].join("_");
314
+
315
+ return [
316
+ {
317
+ table,
318
+ title,
319
+ formatted,
320
+ type: "normal",
321
+ },
322
+ ];
323
+ }
324
+
325
+ /**
326
+ * 각 컬럼 이름 기준으로 add, drop, alter 여부 확인
327
+ */
328
+ function getAlterColumnsTo(
329
+ entityColumns: MigrationColumn[],
330
+ dbColumns: MigrationColumn[]
331
+ ) {
332
+ const columnsTo = {
333
+ add: [] as MigrationColumn[],
334
+ drop: [] as MigrationColumn[],
335
+ alter: [] as MigrationColumn[],
336
+ };
337
+
338
+ // 컬럼명 기준 비교
339
+ const extraColumns = {
340
+ db: _.differenceBy(dbColumns, entityColumns, (col) => col.name),
341
+ entity: _.differenceBy(entityColumns, dbColumns, (col) => col.name),
342
+ };
343
+ if (extraColumns.entity.length > 0) {
344
+ columnsTo.add = columnsTo.add.concat(extraColumns.entity);
345
+ }
346
+ if (extraColumns.db.length > 0) {
347
+ columnsTo.drop = columnsTo.drop.concat(extraColumns.db);
348
+ }
349
+
350
+ // 동일 컬럼명의 세부 필드 비교
351
+ const sameDbColumns = _.intersectionBy(
352
+ dbColumns,
353
+ entityColumns,
354
+ (col) => col.name
355
+ );
356
+ const sameMdColumns = _.intersectionBy(
357
+ entityColumns,
358
+ dbColumns,
359
+ (col) => col.name
360
+ );
361
+ columnsTo.alter = _.differenceWith(sameDbColumns, sameMdColumns, (a, b) =>
362
+ equal(a, b)
363
+ );
364
+
365
+ return columnsTo;
366
+ }
367
+
368
+ /**
369
+ * 추출된 컬럼들을 기준으로 각각 라인 생성
370
+ */
371
+ function getAlterColumnLinesTo(
372
+ columnsTo: ReturnType<typeof getAlterColumnsTo>,
373
+ entityColumns: MigrationColumn[],
374
+ table: string,
375
+ dbForeigns: MigrationForeign[]
376
+ ) {
377
+ let linesTo = {
378
+ add: {
379
+ up: [] as string[],
380
+ down: [] as string[],
381
+ },
382
+ drop: {
383
+ up: [] as string[],
384
+ down: [] as string[],
385
+ },
386
+ alter: {
387
+ up: [] as string[],
388
+ down: [] as string[],
389
+ },
390
+ };
391
+
392
+ linesTo.add = {
393
+ up: ["// add", ...genColumnDefinitions(columnsTo.add)],
394
+ down: [
395
+ "// rollback - add",
396
+ `table.dropColumns(${columnsTo.add
397
+ .map((col) => `'${col.name}'`)
398
+ .join(", ")})`,
399
+ ],
400
+ };
401
+
402
+ // drop할 컬럼에 걸린 FK 찾기
403
+ const dropColumnNames = columnsTo.drop.map((col) => col.name);
404
+ const fkToDropBeforeColumn = dbForeigns.filter((fk) =>
405
+ fk.columns.some((col) => dropColumnNames.includes(col))
406
+ );
407
+
408
+ const dropFkLines = fkToDropBeforeColumn.map((fk) => {
409
+ const columnsStringQuote = fk.columns.map((col) => `'${col}'`).join(",");
410
+ return `table.dropForeign([${columnsStringQuote}])`;
411
+ });
412
+
413
+ const restoreFkLines = genForeignDefinitions(table, fkToDropBeforeColumn).up;
414
+
415
+ linesTo.drop = {
416
+ up: [
417
+ ...(dropFkLines.length > 0
418
+ ? ["// drop foreign keys on columns to be dropped", ...dropFkLines]
419
+ : []),
420
+ "// drop columns",
421
+ `table.dropColumns(${columnsTo.drop
422
+ .map((col) => `'${col.name}'`)
423
+ .join(", ")})`,
424
+ ],
425
+ down: [
426
+ "// rollback - drop columns",
427
+ ...genColumnDefinitions(columnsTo.drop),
428
+ ...(restoreFkLines.length > 0
429
+ ? ["// restore foreign keys", ...restoreFkLines]
430
+ : []),
431
+ ],
432
+ };
433
+ linesTo.alter = columnsTo.alter.reduce(
434
+ (r, dbColumn) => {
435
+ const entityColumn = entityColumns.find(
436
+ (col) => col.name == dbColumn.name
437
+ );
438
+ if (entityColumn === undefined) {
439
+ return r;
440
+ }
441
+
442
+ // 컬럼 변경사항
443
+ const columnDiffUp = _.difference(
444
+ genColumnDefinitions([entityColumn]),
445
+ genColumnDefinitions([dbColumn])
446
+ );
447
+ const columnDiffDown = _.difference(
448
+ genColumnDefinitions([dbColumn]),
449
+ genColumnDefinitions([entityColumn])
450
+ );
451
+ if (columnDiffUp.length > 0) {
452
+ r.up = [
453
+ ...r.up,
454
+ "// alter column",
455
+ ...columnDiffUp.map((l) => l.replace(";", "") + ".alter();"),
456
+ ];
457
+ r.down = [
458
+ ...r.down,
459
+ "// rollback - alter column",
460
+ ...columnDiffDown.map((l) => l.replace(";", "") + ".alter();"),
461
+ ];
462
+ }
463
+
464
+ return r;
465
+ },
466
+ {
467
+ up: [] as string[],
468
+ down: [] as string[],
469
+ }
470
+ );
471
+
472
+ return linesTo;
473
+ }
474
+
475
+ /**
476
+ * 인덱스의 add, drop 여부 확인
477
+ */
478
+ function getAlterIndexesTo(
479
+ entityIndexes: MigrationIndex[],
480
+ dbIndexes: MigrationIndex[]
481
+ ) {
482
+ // 인덱스 비교
483
+ let indexesTo = {
484
+ add: [] as MigrationIndex[],
485
+ drop: [] as MigrationIndex[],
486
+ };
487
+ const extraIndexes = {
488
+ db: _.differenceBy(dbIndexes, entityIndexes, (col) =>
489
+ [col.type, col.columns.join("-")].join("//")
490
+ ),
491
+ entity: _.differenceBy(entityIndexes, dbIndexes, (col) =>
492
+ [col.type, col.columns.join("-")].join("//")
493
+ ),
494
+ };
495
+ if (extraIndexes.entity.length > 0) {
496
+ indexesTo.add = indexesTo.add.concat(extraIndexes.entity);
497
+ }
498
+ if (extraIndexes.db.length > 0) {
499
+ indexesTo.drop = indexesTo.drop.concat(extraIndexes.db);
500
+ }
501
+
502
+ return indexesTo;
503
+ }
504
+
505
+ /**
506
+ * 인덱스 삭제 정의 생성
507
+ */
508
+ function genIndexDropDefinition(index: MigrationIndex) {
509
+ const methodMap = {
510
+ index: "Index",
511
+ fulltext: "Index",
512
+ unique: "Unique",
513
+ };
514
+
515
+ return `table.drop${methodMap[index.type]}([${index.columns
516
+ .map((columnName) => `'${columnName}'`)
517
+ .join(",")}])`;
518
+ }
519
+
520
+ /**
521
+ * 테이블 변경 케이스 - Foreign Key 변경
522
+ */
523
+ async function generateAlterCode_Foreigns(
524
+ table: string,
525
+ entityForeigns: MigrationForeign[],
526
+ dbForeigns: MigrationForeign[],
527
+ droppingColumns: MigrationColumn[] = []
528
+ ): Promise<GenMigrationCode[]> {
529
+ // console.log({ entityForeigns, dbForeigns });
530
+
531
+ const getKey = (mf: MigrationForeign): string => {
532
+ return [mf.columns.join("-"), mf.to].join("///");
533
+ };
534
+
535
+ // 삭제될 컬럼명 목록
536
+ const droppingColumnNames = droppingColumns.map((col) => col.name);
537
+
538
+ const fkTo = entityForeigns.reduce(
539
+ (result, entityF) => {
540
+ const matchingDbF = dbForeigns.find(
541
+ (dbF) => getKey(entityF) === getKey(dbF)
542
+ );
543
+ if (!matchingDbF) {
544
+ result.add.push(entityF);
545
+ return result;
546
+ }
547
+
548
+ if (equal(entityF, matchingDbF) === false) {
549
+ result.alterSrc.push(matchingDbF);
550
+ result.alterDst.push(entityF);
551
+ return result;
552
+ }
553
+ return result;
554
+ },
555
+ {
556
+ add: [] as MigrationForeign[],
557
+ drop: [] as MigrationForeign[],
558
+ alterSrc: [] as MigrationForeign[],
559
+ alterDst: [] as MigrationForeign[],
560
+ }
561
+ );
562
+
563
+ // dbForeigns에는 있지만 entityForeigns에는 없는 경우 (삭제된 FK)
564
+ // 단, 삭제될 컬럼의 FK는 제외 (generateAlterCode_ColumnAndIndexes에서 처리)
565
+ dbForeigns.forEach((dbF) => {
566
+ const matchingEntityF = entityForeigns.find(
567
+ (entityF) => getKey(entityF) === getKey(dbF)
568
+ );
569
+ if (!matchingEntityF) {
570
+ // 이 FK의 컬럼이 삭제될 컬럼 목록에 있는지 확인
571
+ const isColumnDropping = dbF.columns.some((col) =>
572
+ droppingColumnNames.includes(col)
573
+ );
574
+ // 컬럼이 삭제되지 않는 경우에만 FK drop 목록에 추가
575
+ if (!isColumnDropping) {
576
+ fkTo.drop.push(dbF);
577
+ }
578
+ }
579
+ });
580
+
581
+ const linesTo = {
582
+ add: genForeignDefinitions(table, fkTo.add),
583
+ drop: genForeignDefinitions(table, fkTo.drop),
584
+ alterSrc: genForeignDefinitions(table, fkTo.alterSrc),
585
+ alterDst: genForeignDefinitions(table, fkTo.alterDst),
586
+ };
587
+
588
+ // drop fk columns인 경우(생성될 코드 없는 경우) 패스
589
+ const hasLines = Object.values(linesTo).some(
590
+ (l) => l.up.length > 0 || l.down.length > 0
591
+ );
592
+ if (!hasLines) {
593
+ return [];
594
+ }
595
+
596
+ const lines: string[] = [
597
+ 'import { Knex } from "knex";',
598
+ "",
599
+ "export async function up(knex: Knex): Promise<void> {",
600
+ `return knex.schema.alterTable("${table}", (table) => {`,
601
+ ...linesTo.drop.down,
602
+ ...linesTo.add.up,
603
+ ...linesTo.alterSrc.down,
604
+ ...linesTo.alterDst.up,
605
+ "})",
606
+ "}",
607
+ "",
608
+ "export async function down(knex: Knex): Promise<void> {",
609
+ `return knex.schema.alterTable("${table}", (table) => {`,
610
+ ...linesTo.add.down,
611
+ ...linesTo.alterDst.down,
612
+ ...linesTo.alterSrc.up,
613
+ ...linesTo.drop.up,
614
+ "})",
615
+ "}",
616
+ ];
617
+
618
+ const formatted = await prettier.format(lines.join("\n"), {
619
+ parser: "typescript",
620
+ });
621
+
622
+ const title = [
623
+ "alter",
624
+ table,
625
+ "foreigns",
626
+ // TODO 바뀌는 부분
627
+ ].join("_");
628
+
629
+ return [
630
+ {
631
+ table,
632
+ title,
633
+ formatted,
634
+ type: "normal",
635
+ },
636
+ ];
637
+ }
638
+
639
+ /**
640
+ * 주어진 EntitySet을 기반으로 테이블 CREATE 마이그레이션 코드를 생성합니다.
641
+ * @param entitySet
642
+ * @returns CREATE 마이그레이션 코드
643
+ */
644
+ export async function generateCreateCode(
645
+ entitySet: MigrationSet
646
+ ): Promise<GenMigrationCode[]> {
647
+ return [
648
+ await generateCreateCode_ColumnAndIndexes(
649
+ entitySet.table,
650
+ entitySet.columns,
651
+ entitySet.indexes
652
+ ),
653
+ ...(await generateCreateCode_Foreign(entitySet.table, entitySet.foreigns)),
654
+ ];
655
+ }
656
+
657
+ /**
658
+ * 주어진 entitySet을 목표로, dbSet을 현 상황으로 하여 테이블 ALTER 마이그레이션 코드를 생성합니다.
659
+ * @param entitySet 현 상황의 MigrationSet
660
+ * @param dbSet 목표 상황의 MigrationSet
661
+ * @returns ALTER 마이그레이션 코드
662
+ */
663
+ export async function generateAlterCode(
664
+ entitySet: MigrationSet,
665
+ dbSet: MigrationSet
666
+ ): Promise<GenMigrationCode[]> {
667
+ const replaceColumnDefaultTo = (col: MigrationColumn) => {
668
+ // float인 경우 기본값을 0으로 지정하는 경우 "0.00"으로 변환되는 케이스 대응
669
+ if (
670
+ col.type === "float" &&
671
+ col.defaultTo &&
672
+ String(col.defaultTo).includes('"') === false
673
+ ) {
674
+ col.defaultTo = `"${Number(col.defaultTo).toFixed(col.scale ?? 2)}"`;
675
+ }
676
+ // string인 경우 기본값이 빈 스트링인 경우 대응
677
+ if (col.type === "string" && col.defaultTo === "") {
678
+ col.defaultTo = '""';
679
+ }
680
+ // boolean인 경우 기본값 정규화 (MySQL에서는 TINYINT(1)로 저장되므로 0 또는 1로 정규화)
681
+ // TODO: db.ts에 typeCase 설정 확인하여 처리하도록 수정 필요
682
+ if (col.type === "boolean" && col.defaultTo !== undefined) {
683
+ if (col.defaultTo === "0" || col.defaultTo === "false") {
684
+ col.defaultTo = "0";
685
+ } else if (col.defaultTo === "1" || col.defaultTo === "true") {
686
+ col.defaultTo = "1";
687
+ }
688
+ }
689
+ return col;
690
+ };
691
+ const entityColumns = _.sortBy(entitySet.columns, (a) => a.name).map(
692
+ replaceColumnDefaultTo
693
+ );
694
+ const dbColumns = _.sortBy(dbSet.columns, (a) => a.name).map(
695
+ replaceColumnDefaultTo
696
+ );
697
+
698
+ /* 디버깅용 코드, 특정 컬럼에서 불일치 발생할 때 확인
699
+ const entityColumn = entitySet.columns.find(
700
+ (col) => col.name === "price_krw"
701
+ );
702
+ const dbColumn = dbSet.columns.find(
703
+ (col) => col.name === "price_krw"
704
+ );
705
+ console.debug({ entityColumn, dbColumn });
706
+ */
707
+
708
+ const entityIndexes = _.sortBy(entitySet.indexes, (a) =>
709
+ [a.type, ...a.columns.sort((c1, c2) => (c1 > c2 ? 1 : -1))].join("-")
710
+ );
711
+ const dbIndexes = _.sortBy(dbSet.indexes, (a) =>
712
+ [a.type, ...a.columns.sort((c1, c2) => (c1 > c2 ? 1 : -1))].join("-")
713
+ );
714
+
715
+ const replaceNoActionOnMySQL = (f: MigrationForeign) => {
716
+ // MySQL에서 RESTRICT와 NO ACTION은 동일함
717
+ const { onDelete, onUpdate } = f;
718
+ return {
719
+ ...f,
720
+ onUpdate: onUpdate === "RESTRICT" ? "NO ACTION" : onUpdate,
721
+ onDelete: onDelete === "RESTRICT" ? "NO ACTION" : onDelete,
722
+ };
723
+ };
724
+
725
+ const entityForeigns = _.sortBy(entitySet.foreigns, (a) =>
726
+ [a.to, ...a.columns].join("-")
727
+ ).map((f) => replaceNoActionOnMySQL(f));
728
+ const dbForeigns = _.sortBy(dbSet.foreigns, (a) =>
729
+ [a.to, ...a.columns].join("-")
730
+ ).map((f) => replaceNoActionOnMySQL(f));
731
+
732
+ // 삭제될 컬럼 목록 계산
733
+ const droppingColumns = _.differenceBy(
734
+ dbColumns,
735
+ entityColumns,
736
+ (col) => col.name
737
+ );
738
+
739
+ const alterCodes: (GenMigrationCode | GenMigrationCode[] | null)[] = [];
740
+
741
+ // 1. columnsAndIndexes 처리
742
+ const isEqualColumns = equal(entityColumns, dbColumns);
743
+ const isEqualIndexes = equal(
744
+ entityIndexes.map((index) => _.omit(index, ["parser"])),
745
+ dbIndexes
746
+ );
747
+ if (!isEqualColumns || !isEqualIndexes) {
748
+ alterCodes.push(
749
+ await generateAlterCode_ColumnAndIndexes(
750
+ entitySet.table,
751
+ entityColumns,
752
+ entityIndexes,
753
+ dbColumns,
754
+ dbIndexes,
755
+ dbSet.foreigns
756
+ )
757
+ );
758
+ }
759
+
760
+ // 2. foreigns 처리 (삭제될 컬럼 정보 전달)
761
+ if (equal(entityForeigns, dbForeigns) === false) {
762
+ alterCodes.push(
763
+ await generateAlterCode_Foreigns(
764
+ entitySet.table,
765
+ entityForeigns,
766
+ dbForeigns,
767
+ droppingColumns
768
+ )
769
+ );
770
+ }
771
+
772
+ if (alterCodes.every((alterCode) => alterCode === null)) {
773
+ return [];
774
+ }
775
+
776
+ return alterCodes.filter((alterCode) => alterCode !== null).flat();
777
+ }