sonamu 0.5.7 → 0.7.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/.swcrc.project-default +18 -0
- package/bin/cli.js +24 -0
- package/dist/ai/agents/agent.d.ts +11 -0
- package/dist/ai/agents/agent.d.ts.map +1 -0
- package/dist/ai/agents/agent.js +65 -0
- package/dist/ai/agents/index.d.ts +3 -0
- package/dist/ai/agents/index.d.ts.map +1 -0
- package/dist/ai/agents/index.js +4 -0
- package/dist/ai/agents/types.d.ts +43 -0
- package/dist/ai/agents/types.d.ts.map +1 -0
- package/dist/ai/agents/types.js +3 -0
- package/dist/ai/index.d.ts +2 -0
- package/dist/ai/index.d.ts.map +1 -0
- package/dist/ai/index.js +3 -0
- package/dist/ai/providers/rtzr/api.d.ts +22 -0
- package/dist/ai/providers/rtzr/api.d.ts.map +1 -0
- package/dist/ai/providers/rtzr/api.js +28 -0
- package/dist/ai/providers/rtzr/error.d.ts +18 -0
- package/dist/ai/providers/rtzr/error.d.ts.map +1 -0
- package/dist/ai/providers/rtzr/error.js +29 -0
- package/dist/ai/providers/rtzr/index.d.ts +5 -0
- package/dist/ai/providers/rtzr/index.d.ts.map +1 -0
- package/dist/ai/providers/rtzr/index.js +6 -0
- package/dist/ai/providers/rtzr/model.d.ts +52 -0
- package/dist/ai/providers/rtzr/model.d.ts.map +1 -0
- package/dist/ai/providers/rtzr/model.js +137 -0
- package/dist/ai/providers/rtzr/options.d.ts +7 -0
- package/dist/ai/providers/rtzr/options.d.ts.map +1 -0
- package/dist/ai/providers/rtzr/options.js +47 -0
- package/dist/ai/providers/rtzr/provider.d.ts +18 -0
- package/dist/ai/providers/rtzr/provider.d.ts.map +1 -0
- package/dist/ai/providers/rtzr/provider.js +54 -0
- package/dist/ai/providers/rtzr/utils.d.ts +19 -0
- package/dist/ai/providers/rtzr/utils.d.ts.map +1 -0
- package/dist/ai/providers/rtzr/utils.js +88 -0
- package/dist/api/base-frame.d.ts +2 -2
- package/dist/api/base-frame.d.ts.map +1 -1
- package/dist/api/base-frame.js +13 -2
- package/dist/api/caster.d.ts.map +1 -1
- package/dist/api/caster.js +71 -2
- package/dist/api/code-converters.d.ts +58 -14
- package/dist/api/code-converters.d.ts.map +1 -1
- package/dist/api/code-converters.js +258 -2
- package/dist/api/config.d.ts +90 -0
- package/dist/api/config.d.ts.map +1 -0
- package/dist/api/config.js +25 -0
- package/dist/api/context.d.ts +4 -2
- package/dist/api/context.d.ts.map +1 -1
- package/dist/api/context.js +3 -2
- package/dist/api/decorators.d.ts +20 -6
- package/dist/api/decorators.d.ts.map +1 -1
- package/dist/api/decorators.js +235 -2
- package/dist/api/index.d.ts +2 -2
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +9 -2
- package/dist/api/sonamu.d.ts +10 -24
- package/dist/api/sonamu.d.ts.map +1 -1
- package/dist/api/sonamu.js +514 -2
- package/dist/api/validator.d.ts +6 -0
- package/dist/api/validator.d.ts.map +1 -0
- package/dist/api/validator.js +81 -0
- package/dist/bin/build-config.d.ts +6 -1
- package/dist/bin/build-config.d.ts.map +1 -1
- package/dist/bin/build-config.js +15 -2
- package/dist/bin/cli.js +519 -2
- package/dist/bin/hot-hook-register.d.ts +11 -0
- package/dist/bin/hot-hook-register.d.ts.map +1 -0
- package/dist/bin/hot-hook-register.js +21 -0
- package/dist/bin/loader-register.d.ts +2 -0
- package/dist/bin/loader-register.d.ts.map +1 -0
- package/dist/bin/loader-register.js +34 -0
- package/dist/database/_batch_update.d.ts +5 -3
- package/dist/database/_batch_update.d.ts.map +1 -1
- package/dist/database/_batch_update.js +95 -2
- package/dist/database/base-model.d.ts +96 -10
- package/dist/database/base-model.d.ts.map +1 -1
- package/dist/database/base-model.js +390 -2
- package/dist/database/base-model.types.d.ts +93 -0
- package/dist/database/base-model.types.d.ts.map +1 -0
- package/dist/database/base-model.types.js +10 -0
- package/dist/database/code-generator.d.ts +1 -1
- package/dist/database/code-generator.d.ts.map +1 -1
- package/dist/database/code-generator.js +54 -2
- package/dist/database/db.d.ts +6 -21
- package/dist/database/db.d.ts.map +1 -1
- package/dist/database/db.js +129 -2
- package/dist/database/puri-subset.test-d.js +81 -0
- package/dist/database/puri-subset.types.d.ts +123 -0
- package/dist/database/puri-subset.types.d.ts.map +1 -0
- package/dist/database/puri-subset.types.js +16 -0
- package/dist/database/puri-wrapper.d.ts +13 -11
- package/dist/database/puri-wrapper.d.ts.map +1 -1
- package/dist/database/puri-wrapper.js +109 -2
- package/dist/database/puri.d.ts +41 -23
- package/dist/database/puri.d.ts.map +1 -1
- package/dist/database/puri.js +601 -2
- package/dist/database/puri.types.d.ts +25 -6
- package/dist/database/puri.types.d.ts.map +1 -1
- package/dist/database/puri.types.js +6 -2
- package/dist/database/transaction-context.d.ts +1 -1
- package/dist/database/transaction-context.d.ts.map +1 -1
- package/dist/database/transaction-context.js +14 -2
- package/dist/database/upsert-builder.d.ts +9 -3
- package/dist/database/upsert-builder.d.ts.map +1 -1
- package/dist/database/upsert-builder.js +365 -2
- package/dist/entity/entity-manager.d.ts +167 -2
- package/dist/entity/entity-manager.d.ts.map +1 -1
- package/dist/entity/entity-manager.js +130 -2
- package/dist/entity/entity.d.ts +5 -3
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +750 -2
- package/dist/exceptions/error-handler.d.ts +1 -1
- package/dist/exceptions/error-handler.d.ts.map +1 -1
- package/dist/exceptions/error-handler.js +29 -2
- package/dist/exceptions/so-exceptions.d.ts +1 -1
- package/dist/exceptions/so-exceptions.d.ts.map +1 -1
- package/dist/exceptions/so-exceptions.js +85 -2
- package/dist/file-storage/driver.d.ts +1 -1
- package/dist/file-storage/driver.d.ts.map +1 -1
- package/dist/file-storage/driver.js +79 -2
- package/dist/file-storage/file-storage.js +75 -2
- package/dist/index.d.ts +18 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -2
- package/dist/migration/code-generation.d.ts +1 -1
- package/dist/migration/code-generation.d.ts.map +1 -1
- package/dist/migration/code-generation.js +614 -2
- package/dist/migration/migration-set.d.ts +2 -10
- package/dist/migration/migration-set.d.ts.map +1 -1
- package/dist/migration/migration-set.js +213 -2
- package/dist/migration/migrator.d.ts +24 -82
- package/dist/migration/migrator.d.ts.map +1 -1
- package/dist/migration/migrator.js +330 -2
- package/dist/migration/postgresql-schema-reader.d.ts +51 -0
- package/dist/migration/postgresql-schema-reader.d.ts.map +1 -0
- package/dist/migration/postgresql-schema-reader.js +245 -0
- package/dist/migration/types.d.ts +6 -38
- package/dist/migration/types.d.ts.map +1 -1
- package/dist/migration/types.js +3 -2
- package/dist/naite/messaging-types.d.ts +43 -0
- package/dist/naite/messaging-types.d.ts.map +1 -0
- package/dist/naite/messaging-types.js +7 -0
- package/dist/naite/naite-reporter.d.ts +41 -0
- package/dist/naite/naite-reporter.d.ts.map +1 -0
- package/dist/naite/naite-reporter.js +102 -0
- package/dist/naite/naite.d.ts +95 -0
- package/dist/naite/naite.d.ts.map +1 -0
- package/dist/naite/naite.js +316 -0
- package/dist/stream/index.js +3 -2
- package/dist/stream/sse.d.ts +2 -2
- package/dist/stream/sse.d.ts.map +1 -1
- package/dist/stream/sse.js +38 -2
- package/dist/syncer/api-parser.d.ts +10 -0
- package/dist/syncer/api-parser.d.ts.map +1 -0
- package/dist/syncer/api-parser.js +240 -0
- package/dist/syncer/checksum.d.ts +21 -0
- package/dist/syncer/checksum.d.ts.map +1 -0
- package/dist/syncer/checksum.js +98 -0
- package/dist/syncer/code-generator.d.ts +20 -0
- package/dist/syncer/code-generator.d.ts.map +1 -0
- package/dist/syncer/code-generator.js +161 -0
- package/dist/syncer/entity-operations.d.ts +17 -0
- package/dist/syncer/entity-operations.d.ts.map +1 -0
- package/dist/syncer/entity-operations.js +59 -0
- package/dist/syncer/file-patterns.d.ts +29 -0
- package/dist/syncer/file-patterns.d.ts.map +1 -0
- package/dist/syncer/file-patterns.js +38 -0
- package/dist/syncer/index.d.ts +6 -0
- package/dist/syncer/index.d.ts.map +1 -1
- package/dist/syncer/index.js +9 -2
- package/dist/syncer/module-loader.d.ts +35 -0
- package/dist/syncer/module-loader.d.ts.map +1 -0
- package/dist/syncer/module-loader.js +87 -0
- package/dist/syncer/syncer.d.ts +98 -106
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +422 -2
- package/dist/template/entity-converter.d.ts +14 -0
- package/dist/template/entity-converter.d.ts.map +1 -0
- package/dist/template/entity-converter.js +108 -0
- package/dist/template/helpers.d.ts +23 -0
- package/dist/template/helpers.d.ts.map +1 -0
- package/dist/template/helpers.js +64 -0
- package/dist/{templates → template/implementations}/entity.template.d.ts +3 -3
- package/dist/template/implementations/entity.template.d.ts.map +1 -0
- package/dist/template/implementations/entity.template.js +86 -0
- package/dist/{templates → template/implementations}/generated.template.d.ts +3 -4
- package/dist/template/implementations/generated.template.d.ts.map +1 -0
- package/dist/template/implementations/generated.template.js +249 -0
- package/dist/{templates → template/implementations}/generated_http.template.d.ts +3 -4
- package/dist/template/implementations/generated_http.template.d.ts.map +1 -0
- package/dist/template/implementations/generated_http.template.js +131 -0
- package/dist/{templates → template/implementations}/generated_sso.template.d.ts +4 -5
- package/dist/template/implementations/generated_sso.template.d.ts.map +1 -0
- package/dist/template/implementations/generated_sso.template.js +134 -0
- package/dist/{templates → template/implementations}/init_types.template.d.ts +3 -3
- package/dist/template/implementations/init_types.template.d.ts.map +1 -0
- package/dist/template/implementations/init_types.template.js +38 -0
- package/dist/template/implementations/model.template.d.ts +17 -0
- package/dist/template/implementations/model.template.d.ts.map +1 -0
- package/dist/template/implementations/model.template.js +181 -0
- package/dist/{templates → template/implementations}/model_test.template.d.ts +3 -3
- package/dist/template/implementations/model_test.template.d.ts.map +1 -0
- package/dist/template/implementations/model_test.template.js +35 -0
- package/dist/{templates → template/implementations}/service.template.d.ts +6 -6
- package/dist/template/implementations/service.template.d.ts.map +1 -0
- package/dist/template/implementations/service.template.js +201 -0
- package/dist/{templates → template/implementations}/view_enums_buttonset.template.d.ts +3 -3
- package/dist/template/implementations/view_enums_buttonset.template.d.ts.map +1 -0
- package/dist/template/implementations/view_enums_buttonset.template.js +31 -0
- package/dist/{templates → template/implementations}/view_enums_dropdown.template.d.ts +3 -4
- package/dist/template/implementations/view_enums_dropdown.template.d.ts.map +1 -0
- package/dist/template/implementations/view_enums_dropdown.template.js +50 -0
- package/dist/{templates → template/implementations}/view_enums_select.template.d.ts +3 -3
- package/dist/template/implementations/view_enums_select.template.d.ts.map +1 -0
- package/dist/template/implementations/view_enums_select.template.js +55 -0
- package/dist/{templates → template/implementations}/view_form.template.d.ts +5 -5
- package/dist/template/implementations/view_form.template.d.ts.map +1 -0
- package/dist/template/implementations/view_form.template.js +337 -0
- package/dist/{templates → template/implementations}/view_id_all_select.template.d.ts +3 -3
- package/dist/template/implementations/view_id_all_select.template.d.ts.map +1 -0
- package/dist/template/implementations/view_id_all_select.template.js +31 -0
- package/dist/{templates → template/implementations}/view_id_async_select.template.d.ts +3 -3
- package/dist/template/implementations/view_id_async_select.template.d.ts.map +1 -0
- package/dist/template/implementations/view_id_async_select.template.js +105 -0
- package/dist/{templates → template/implementations}/view_list.template.d.ts +5 -13
- package/dist/template/implementations/view_list.template.d.ts.map +1 -0
- package/dist/template/implementations/view_list.template.js +475 -0
- package/dist/template/implementations/view_list_columns.template.d.ts +17 -0
- package/dist/template/implementations/view_list_columns.template.d.ts.map +1 -0
- package/dist/template/implementations/view_list_columns.template.js +49 -0
- package/dist/{templates → template/implementations}/view_search_input.template.d.ts +3 -3
- package/dist/template/implementations/view_search_input.template.d.ts.map +1 -0
- package/dist/template/implementations/view_search_input.template.js +64 -0
- package/dist/template/index.d.ts +7 -0
- package/dist/template/index.d.ts.map +1 -0
- package/dist/template/index.js +8 -0
- package/dist/template/template-manager.d.ts +56 -0
- package/dist/template/template-manager.d.ts.map +1 -0
- package/dist/template/template-manager.js +125 -0
- package/dist/template/template-types.d.ts +16 -0
- package/dist/template/template-types.d.ts.map +1 -0
- package/dist/template/template-types.js +7 -0
- package/dist/template/template.d.ts +49 -0
- package/dist/template/template.d.ts.map +1 -0
- package/dist/template/template.js +60 -0
- package/dist/template/zod-converter.d.ts +51 -0
- package/dist/template/zod-converter.d.ts.map +1 -0
- package/dist/template/zod-converter.js +449 -0
- package/dist/testing/_relation-graph.d.ts +1 -1
- package/dist/testing/_relation-graph.d.ts.map +1 -1
- package/dist/testing/_relation-graph.js +89 -2
- package/dist/testing/fixture-manager.d.ts +42 -11
- package/dist/testing/fixture-manager.d.ts.map +1 -1
- package/dist/testing/fixture-manager.js +623 -2
- package/dist/types/types.d.ts +747 -143
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +546 -2
- package/dist/typings/knex.d.js +3 -2
- package/dist/utils/async-utils.d.ts +7 -0
- package/dist/utils/async-utils.d.ts.map +1 -1
- package/dist/utils/async-utils.js +57 -2
- package/dist/utils/console-util.d.ts +2 -0
- package/dist/utils/console-util.d.ts.map +1 -0
- package/dist/utils/console-util.js +6 -0
- package/dist/utils/controller.d.ts +1 -0
- package/dist/utils/controller.d.ts.map +1 -1
- package/dist/utils/controller.js +29 -2
- package/dist/utils/esm-utils.d.ts +39 -0
- package/dist/utils/esm-utils.d.ts.map +1 -0
- package/dist/utils/esm-utils.js +49 -0
- package/dist/utils/formatter.d.ts +3 -0
- package/dist/utils/formatter.d.ts.map +1 -0
- package/dist/utils/formatter.js +110 -0
- package/dist/utils/fs-utils.d.ts +1 -1
- package/dist/utils/fs-utils.d.ts.map +1 -1
- package/dist/utils/fs-utils.js +17 -2
- package/dist/utils/lodash-able.d.ts.map +1 -1
- package/dist/utils/lodash-able.js +6 -2
- package/dist/utils/model.js +22 -2
- package/dist/utils/object-utils.d.ts +44 -0
- package/dist/utils/object-utils.d.ts.map +1 -0
- package/dist/utils/object-utils.js +191 -0
- package/dist/utils/path-utils.d.ts +89 -0
- package/dist/utils/path-utils.d.ts.map +1 -0
- package/dist/utils/path-utils.js +60 -0
- package/dist/utils/process-utils.d.ts +13 -0
- package/dist/utils/process-utils.d.ts.map +1 -0
- package/dist/utils/process-utils.js +36 -0
- package/dist/utils/sql-parser.d.ts +5 -1
- package/dist/utils/sql-parser.d.ts.map +1 -1
- package/dist/utils/sql-parser.js +46 -2
- package/dist/utils/type-utils.d.ts +23 -0
- package/dist/utils/type-utils.d.ts.map +1 -0
- package/dist/utils/type-utils.js +45 -0
- package/dist/utils/utils.d.ts +10 -7
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +72 -2
- package/dist/utils/zod-error.d.ts +1 -1
- package/dist/utils/zod-error.d.ts.map +1 -1
- package/dist/utils/zod-error.js +19 -2
- package/package.json +65 -27
- package/src/ai/agents/agent.ts +87 -0
- package/src/ai/agents/index.ts +2 -0
- package/src/ai/agents/types.ts +47 -0
- package/src/ai/index.ts +1 -0
- package/src/ai/providers/rtzr/api.ts +37 -0
- package/src/ai/providers/rtzr/error.ts +34 -0
- package/src/ai/providers/rtzr/index.ts +4 -0
- package/src/ai/providers/rtzr/model.ts +201 -0
- package/src/ai/providers/rtzr/options.ts +49 -0
- package/src/ai/providers/rtzr/provider.ts +91 -0
- package/src/ai/providers/rtzr/utils.ts +127 -0
- package/src/api/base-frame.ts +4 -2
- package/src/api/caster.ts +17 -23
- package/src/api/code-converters.ts +178 -535
- package/src/api/config.ts +125 -0
- package/src/api/context.ts +7 -17
- package/src/api/decorators.ts +176 -46
- package/src/api/index.ts +2 -2
- package/src/api/sonamu.ts +190 -167
- package/src/api/validator.ts +83 -0
- package/src/bin/build-config.ts +8 -1
- package/src/bin/cli.ts +258 -124
- package/src/bin/hot-hook-register.ts +22 -0
- package/src/bin/loader-register.ts +38 -0
- package/src/database/_batch_update.ts +46 -31
- package/src/database/base-model.ts +390 -182
- package/src/database/base-model.types.ts +155 -0
- package/src/database/code-generator.ts +13 -32
- package/src/database/db.ts +40 -96
- package/src/database/puri-subset.test-d.ts +471 -0
- package/src/database/puri-subset.types.ts +195 -0
- package/src/database/puri-wrapper.ts +58 -67
- package/src/database/puri.ts +229 -148
- package/src/database/puri.types.ts +76 -30
- package/src/database/transaction-context.ts +1 -1
- package/src/database/upsert-builder.ts +262 -132
- package/src/entity/entity-manager.ts +48 -36
- package/src/entity/entity.ts +330 -248
- package/src/exceptions/error-handler.ts +3 -3
- package/src/exceptions/so-exceptions.ts +11 -11
- package/src/file-storage/driver.ts +5 -5
- package/src/file-storage/file-storage.ts +2 -2
- package/src/index.ts +18 -10
- package/src/migration/code-generation.ts +185 -172
- package/src/migration/migration-set.ts +80 -293
- package/src/migration/migrator.ts +199 -571
- package/src/migration/mysql-schema-reader.ts.txt +272 -0
- package/src/migration/postgresql-schema-reader.ts +310 -0
- package/src/migration/types.ts +6 -39
- package/src/naite/messaging-types.ts +51 -0
- package/src/naite/naite-reporter.ts +128 -0
- package/src/naite/naite.ts +415 -0
- package/src/shared/web.shared.ts.txt +20 -24
- package/src/stream/sse.ts +5 -5
- package/src/syncer/api-parser.ts +282 -0
- package/src/syncer/checksum.ts +140 -0
- package/src/syncer/code-generator.ts +198 -0
- package/src/syncer/entity-operations.ts +65 -0
- package/src/syncer/file-patterns.ts +56 -0
- package/src/syncer/index.ts +6 -0
- package/src/syncer/module-loader.ts +128 -0
- package/src/syncer/syncer.ts +389 -1453
- package/src/template/entity-converter.ts +114 -0
- package/src/template/helpers.ts +81 -0
- package/src/{templates → template/implementations}/entity.template.ts +7 -7
- package/src/{templates → template/implementations}/generated.template.ts +101 -101
- package/src/{templates → template/implementations}/generated_http.template.ts +27 -57
- package/src/template/implementations/generated_sso.template.ts +151 -0
- package/src/{templates → template/implementations}/init_types.template.ts +5 -7
- package/src/{templates → template/implementations}/model.template.ts +52 -43
- package/src/{templates → template/implementations}/model_test.template.ts +5 -5
- package/src/{templates → template/implementations}/service.template.ts +66 -82
- package/src/{templates → template/implementations}/view_enums_buttonset.template.ts +3 -3
- package/src/{templates → template/implementations}/view_enums_dropdown.template.ts +4 -20
- package/src/{templates → template/implementations}/view_enums_select.template.ts +4 -4
- package/src/{templates → template/implementations}/view_form.template.ts +40 -83
- package/src/{templates → template/implementations}/view_id_all_select.template.ts +3 -3
- package/src/{templates → template/implementations}/view_id_async_select.template.ts +10 -24
- package/src/{templates → template/implementations}/view_list.template.ts +60 -152
- package/src/{templates → template/implementations}/view_list_columns.template.ts +5 -11
- package/src/{templates → template/implementations}/view_search_input.template.ts +3 -3
- package/src/template/index.ts +6 -0
- package/src/template/template-manager.ts +166 -0
- package/src/template/template-types.ts +16 -0
- package/src/template/template.ts +105 -0
- package/src/template/zod-converter.ts +525 -0
- package/src/testing/_relation-graph.ts +18 -11
- package/src/testing/fixture-manager.ts +472 -359
- package/src/types/types.ts +553 -308
- package/src/typings/knex.d.ts +7 -9
- package/src/utils/async-utils.ts +23 -10
- package/src/utils/console-util.ts +4 -0
- package/src/utils/controller.ts +3 -0
- package/src/utils/esm-utils.ts +59 -0
- package/src/utils/formatter.ts +109 -0
- package/src/utils/fs-utils.ts +1 -1
- package/src/utils/lodash-able.ts +1 -4
- package/src/utils/object-utils.ts +217 -0
- package/src/utils/path-utils.ts +99 -0
- package/src/utils/process-utils.ts +46 -0
- package/src/utils/sql-parser.ts +23 -5
- package/src/utils/type-utils.ts +83 -0
- package/src/utils/utils.ts +66 -43
- package/src/utils/zod-error.ts +3 -4
- package/dist/api/base-frame.js.map +0 -1
- package/dist/api/caster.js.map +0 -1
- package/dist/api/code-converters.js.map +0 -1
- package/dist/api/context.js.map +0 -1
- package/dist/api/decorators.js.map +0 -1
- package/dist/api/index.js.map +0 -1
- package/dist/api/sonamu.js.map +0 -1
- package/dist/bin/build-config.js.map +0 -1
- package/dist/bin/cli-wrapper.d.ts +0 -3
- package/dist/bin/cli-wrapper.d.ts.map +0 -1
- package/dist/bin/cli-wrapper.js +0 -3
- package/dist/bin/cli-wrapper.js.map +0 -1
- package/dist/bin/cli.js.map +0 -1
- package/dist/database/_batch_update.js.map +0 -1
- package/dist/database/base-model.js.map +0 -1
- package/dist/database/code-generator.js.map +0 -1
- package/dist/database/db.js.map +0 -1
- package/dist/database/knex-plugins/knex-on-duplicate-update.d.ts +0 -2
- package/dist/database/knex-plugins/knex-on-duplicate-update.d.ts.map +0 -1
- package/dist/database/knex-plugins/knex-on-duplicate-update.js +0 -2
- package/dist/database/knex-plugins/knex-on-duplicate-update.js.map +0 -1
- package/dist/database/puri-wrapper.js.map +0 -1
- package/dist/database/puri.js.map +0 -1
- package/dist/database/puri.types.js.map +0 -1
- package/dist/database/transaction-context.js.map +0 -1
- package/dist/database/upsert-builder.js.map +0 -1
- package/dist/entity/entity-manager.js.map +0 -1
- package/dist/entity/entity-utils.d.ts +0 -61
- package/dist/entity/entity-utils.d.ts.map +0 -1
- package/dist/entity/entity-utils.js +0 -2
- package/dist/entity/entity-utils.js.map +0 -1
- package/dist/entity/entity.js.map +0 -1
- package/dist/exceptions/error-handler.js.map +0 -1
- package/dist/exceptions/so-exceptions.js.map +0 -1
- package/dist/file-storage/driver.js.map +0 -1
- package/dist/file-storage/file-storage.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/migration/code-generation.js.map +0 -1
- package/dist/migration/migration-set.js.map +0 -1
- package/dist/migration/migrator.js.map +0 -1
- package/dist/migration/types.js.map +0 -1
- package/dist/stream/index.js.map +0 -1
- package/dist/stream/sse.js.map +0 -1
- package/dist/syncer/index.js.map +0 -1
- package/dist/syncer/syncer.js.map +0 -1
- package/dist/templates/base-template.d.ts +0 -13
- package/dist/templates/base-template.d.ts.map +0 -1
- package/dist/templates/base-template.js +0 -2
- package/dist/templates/base-template.js.map +0 -1
- package/dist/templates/entity.template.d.ts.map +0 -1
- package/dist/templates/entity.template.js +0 -2
- package/dist/templates/entity.template.js.map +0 -1
- package/dist/templates/generated.template.d.ts.map +0 -1
- package/dist/templates/generated.template.js +0 -2
- package/dist/templates/generated.template.js.map +0 -1
- package/dist/templates/generated_http.template.d.ts.map +0 -1
- package/dist/templates/generated_http.template.js +0 -2
- package/dist/templates/generated_http.template.js.map +0 -1
- package/dist/templates/generated_sso.template.d.ts.map +0 -1
- package/dist/templates/generated_sso.template.js +0 -2
- package/dist/templates/generated_sso.template.js.map +0 -1
- package/dist/templates/index.d.ts +0 -2
- package/dist/templates/index.d.ts.map +0 -1
- package/dist/templates/index.js +0 -2
- package/dist/templates/index.js.map +0 -1
- package/dist/templates/init_types.template.d.ts.map +0 -1
- package/dist/templates/init_types.template.js +0 -2
- package/dist/templates/init_types.template.js.map +0 -1
- package/dist/templates/model.template.d.ts +0 -17
- package/dist/templates/model.template.d.ts.map +0 -1
- package/dist/templates/model.template.js +0 -2
- package/dist/templates/model.template.js.map +0 -1
- package/dist/templates/model_test.template.d.ts.map +0 -1
- package/dist/templates/model_test.template.js +0 -2
- package/dist/templates/model_test.template.js.map +0 -1
- package/dist/templates/service.template.d.ts.map +0 -1
- package/dist/templates/service.template.js +0 -2
- package/dist/templates/service.template.js.map +0 -1
- package/dist/templates/view_enums_buttonset.template.d.ts.map +0 -1
- package/dist/templates/view_enums_buttonset.template.js +0 -2
- package/dist/templates/view_enums_buttonset.template.js.map +0 -1
- package/dist/templates/view_enums_dropdown.template.d.ts.map +0 -1
- package/dist/templates/view_enums_dropdown.template.js +0 -2
- package/dist/templates/view_enums_dropdown.template.js.map +0 -1
- package/dist/templates/view_enums_select.template.d.ts.map +0 -1
- package/dist/templates/view_enums_select.template.js +0 -2
- package/dist/templates/view_enums_select.template.js.map +0 -1
- package/dist/templates/view_form.template.d.ts.map +0 -1
- package/dist/templates/view_form.template.js +0 -2
- package/dist/templates/view_form.template.js.map +0 -1
- package/dist/templates/view_id_all_select.template.d.ts.map +0 -1
- package/dist/templates/view_id_all_select.template.js +0 -2
- package/dist/templates/view_id_all_select.template.js.map +0 -1
- package/dist/templates/view_id_async_select.template.d.ts.map +0 -1
- package/dist/templates/view_id_async_select.template.js +0 -2
- package/dist/templates/view_id_async_select.template.js.map +0 -1
- package/dist/templates/view_list.template.d.ts.map +0 -1
- package/dist/templates/view_list.template.js +0 -2
- package/dist/templates/view_list.template.js.map +0 -1
- package/dist/templates/view_list_columns.template.d.ts +0 -17
- package/dist/templates/view_list_columns.template.d.ts.map +0 -1
- package/dist/templates/view_list_columns.template.js +0 -2
- package/dist/templates/view_list_columns.template.js.map +0 -1
- package/dist/templates/view_search_input.template.d.ts.map +0 -1
- package/dist/templates/view_search_input.template.js +0 -2
- package/dist/templates/view_search_input.template.js.map +0 -1
- package/dist/testing/_relation-graph.js.map +0 -1
- package/dist/testing/fixture-manager.js.map +0 -1
- package/dist/types/types.js.map +0 -1
- package/dist/typings/knex.d.js.map +0 -1
- package/dist/utils/async-utils.js.map +0 -1
- package/dist/utils/controller.js.map +0 -1
- package/dist/utils/fs-utils.js.map +0 -1
- package/dist/utils/lodash-able.js.map +0 -1
- package/dist/utils/model.js.map +0 -1
- package/dist/utils/sql-parser.js.map +0 -1
- package/dist/utils/utils.js.map +0 -1
- package/dist/utils/zod-error.js.map +0 -1
- package/src/bin/cli-wrapper.ts +0 -75
- package/src/database/knex-plugins/knex-on-duplicate-update.ts +0 -45
- package/src/entity/entity-utils.ts +0 -291
- package/src/templates/base-template.ts +0 -19
- package/src/templates/generated_sso.template.ts +0 -138
- package/src/templates/index.ts +0 -1
package/src/syncer/syncer.ts
CHANGED
|
@@ -1,300 +1,217 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { globAsync, importMultiple } from "../utils/utils";
|
|
3
|
-
import { createReadStream } from "fs";
|
|
4
|
-
import { mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
5
|
-
import { exists } from "../utils/fs-utils";
|
|
6
|
-
import crypto from "crypto";
|
|
7
|
-
import equal from "fast-deep-equal";
|
|
8
|
-
import _, { chunk } from "lodash";
|
|
9
|
-
import inflection from "inflection";
|
|
10
|
-
import { EntityManager, EntityNamesRecord } from "../entity/entity-manager";
|
|
11
|
-
import ts from "typescript";
|
|
12
|
-
import {
|
|
13
|
-
ApiParam,
|
|
14
|
-
ApiParamType,
|
|
15
|
-
EntityProp,
|
|
16
|
-
EntityPropNode,
|
|
17
|
-
isBelongsToOneRelationProp,
|
|
18
|
-
isBigIntegerProp,
|
|
19
|
-
isBooleanProp,
|
|
20
|
-
isDateProp,
|
|
21
|
-
isDateTimeProp,
|
|
22
|
-
isDecimalProp,
|
|
23
|
-
isDoubleProp,
|
|
24
|
-
isEnumProp,
|
|
25
|
-
isFloatProp,
|
|
26
|
-
isIntegerProp,
|
|
27
|
-
isJsonProp,
|
|
28
|
-
isOneToOneRelationProp,
|
|
29
|
-
isRelationProp,
|
|
30
|
-
isStringProp,
|
|
31
|
-
isTextProp,
|
|
32
|
-
isTimeProp,
|
|
33
|
-
isTimestampProp,
|
|
34
|
-
isUuidProp,
|
|
35
|
-
isVirtualProp,
|
|
36
|
-
} from "../types/types";
|
|
37
|
-
import {
|
|
38
|
-
ApiDecoratorOptions,
|
|
39
|
-
registeredApis,
|
|
40
|
-
ExtendedApi,
|
|
41
|
-
} from "../api/decorators";
|
|
42
|
-
import { z } from "zod";
|
|
43
|
-
import chalk from "chalk";
|
|
44
|
-
import {
|
|
45
|
-
TemplateKey,
|
|
46
|
-
PathAndCode,
|
|
47
|
-
TemplateOptions,
|
|
48
|
-
GenerateOptions,
|
|
49
|
-
RenderingNode,
|
|
50
|
-
} from "../types/types";
|
|
51
|
-
import {
|
|
52
|
-
AlreadyProcessedException,
|
|
53
|
-
BadRequestException,
|
|
54
|
-
ServiceUnavailableException,
|
|
55
|
-
} from "../exceptions/so-exceptions";
|
|
56
|
-
import { wrapIf } from "../utils/lodash-able";
|
|
57
|
-
import { getTextTypeLength } from "../api/code-converters";
|
|
58
|
-
import { Template } from "../templates/base-template";
|
|
59
|
-
import { Template__generated } from "../templates/generated.template";
|
|
60
|
-
import { Template__init_types } from "../templates/init_types.template";
|
|
61
|
-
import { Template__entity } from "../templates/entity.template";
|
|
62
|
-
import { Template__model } from "../templates/model.template";
|
|
63
|
-
import { Template__model_test } from "../templates/model_test.template";
|
|
64
|
-
import { Template__service } from "../templates/service.template";
|
|
65
|
-
import { Template__view_form } from "../templates/view_form.template";
|
|
66
|
-
import { Template__view_list } from "../templates/view_list.template";
|
|
67
|
-
import prettier from "prettier";
|
|
68
|
-
import { Template__view_id_all_select } from "../templates/view_id_all_select.template";
|
|
69
|
-
import { Template__view_id_async_select } from "../templates/view_id_async_select.template";
|
|
70
|
-
import { Template__view_enums_dropdown } from "../templates/view_enums_dropdown.template";
|
|
71
|
-
import { Template__view_enums_select } from "../templates/view_enums_select.template";
|
|
72
|
-
import { Template__view_enums_buttonset } from "../templates/view_enums_buttonset.template";
|
|
73
|
-
import { Template__view_search_input } from "../templates/view_search_input.template";
|
|
74
|
-
import { Template__view_list_columns } from "../templates/view_list_columns.template";
|
|
75
|
-
import { Template__generated_http } from "../templates/generated_http.template";
|
|
76
|
-
import { Sonamu } from "../api/sonamu";
|
|
77
|
-
import { Template__generated_sso } from "../templates/generated_sso.template";
|
|
78
|
-
import { setTimeout as setTimeoutPromises } from "timers/promises";
|
|
1
|
+
import { hot } from "@sonamu-kit/hot-hook";
|
|
79
2
|
import assert from "assert";
|
|
80
|
-
import
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
81
5
|
import { minimatch } from "minimatch";
|
|
6
|
+
import path, { dirname } from "path";
|
|
7
|
+
import { group, unique } from "radashi";
|
|
8
|
+
import type { z } from "zod";
|
|
9
|
+
import { registeredApis } from "../api/decorators";
|
|
10
|
+
import { Sonamu } from "../api/sonamu";
|
|
11
|
+
import { EntityManager, type EntityNamesRecord } from "../entity/entity-manager";
|
|
12
|
+
import { Naite } from "../naite/naite";
|
|
13
|
+
import { TemplateManager } from "../template/template-manager";
|
|
14
|
+
import type { GenerateOptions, PathAndCode } from "../types/types";
|
|
15
|
+
import { TemplateKey, type TemplateOptions } from "../types/types";
|
|
16
|
+
import { mapAsync, reduceAsync } from "../utils/async-utils";
|
|
17
|
+
import { centerText } from "../utils/console-util";
|
|
18
|
+
import { isTest } from "../utils/controller";
|
|
19
|
+
import { exists } from "../utils/fs-utils";
|
|
20
|
+
import type { AbsolutePath } from "../utils/path-utils";
|
|
21
|
+
import { runWithGracefulShutdown } from "../utils/process-utils";
|
|
22
|
+
import { areFilesSame, findChangedFilesUsingChecksums, renewChecksums } from "./checksum";
|
|
23
|
+
import { generateTemplate, renderTemplate } from "./code-generator";
|
|
24
|
+
import { createEntity, delEntity } from "./entity-operations";
|
|
25
|
+
import { type FileType, getChecksumPatternGroupInAbsolutePath } from "./file-patterns";
|
|
82
26
|
import {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
| "types"
|
|
92
|
-
| "functions"
|
|
93
|
-
| "generated"
|
|
94
|
-
| "entity"
|
|
95
|
-
| "frame";
|
|
96
|
-
type GlobPattern = {
|
|
97
|
-
[key in FileType]: string;
|
|
98
|
-
};
|
|
99
|
-
type PathAndChecksum = {
|
|
100
|
-
path: string;
|
|
101
|
-
checksum: string;
|
|
102
|
-
};
|
|
27
|
+
type LoadedApis,
|
|
28
|
+
type LoadedModels,
|
|
29
|
+
type LoadedTypes,
|
|
30
|
+
loadApis,
|
|
31
|
+
loadModels,
|
|
32
|
+
loadTypes,
|
|
33
|
+
} from "./module-loader";
|
|
34
|
+
|
|
103
35
|
type DiffGroups = {
|
|
104
|
-
[key in FileType]:
|
|
105
|
-
};
|
|
106
|
-
export type RenderedTemplate = {
|
|
107
|
-
target: string;
|
|
108
|
-
path: string;
|
|
109
|
-
body: string;
|
|
110
|
-
importKeys: string[];
|
|
111
|
-
customHeaders?: string[];
|
|
112
|
-
preTemplates?: {
|
|
113
|
-
key: TemplateKey;
|
|
114
|
-
options: TemplateOptions[TemplateKey];
|
|
115
|
-
}[];
|
|
36
|
+
[key in FileType]: AbsolutePath[];
|
|
116
37
|
};
|
|
117
38
|
|
|
118
39
|
export class Syncer {
|
|
119
|
-
apis:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
returnType: ApiParamType;
|
|
123
|
-
modelName: string;
|
|
124
|
-
methodName: string;
|
|
125
|
-
path: string;
|
|
126
|
-
options: ApiDecoratorOptions;
|
|
127
|
-
}[] = [];
|
|
128
|
-
types: { [typeName: string]: z.ZodObject<any> } = {};
|
|
129
|
-
models: { [modelName: string]: unknown } = {};
|
|
40
|
+
apis: LoadedApis = [];
|
|
41
|
+
types: LoadedTypes = {};
|
|
42
|
+
models: LoadedModels = {};
|
|
130
43
|
isSyncing: boolean = false;
|
|
131
44
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
functions: Sonamu.apiRootPath + "/src/application/**/*.functions.ts",
|
|
138
|
-
/* compiled-JS 체크 */
|
|
139
|
-
model: Sonamu.apiRootPath + "/dist/application/**/*.model.js",
|
|
140
|
-
frame: Sonamu.apiRootPath + "/dist/application/**/*.frame.js",
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
get checksumsPath(): string {
|
|
144
|
-
return path.join(Sonamu.apiRootPath, "/sonamu.lock");
|
|
145
|
-
}
|
|
146
|
-
public constructor() {}
|
|
147
|
-
|
|
45
|
+
/**
|
|
46
|
+
* 체크섬이 변경된 부분에 대해 싱크를 진행합니다.
|
|
47
|
+
* 다만 sonamu.shared.ts는 체크섬 비교 없이 무조건 싱크(복사)합니다.
|
|
48
|
+
* @returns
|
|
49
|
+
*/
|
|
148
50
|
async sync(): Promise<void> {
|
|
149
51
|
const { targets } = Sonamu.config.sync;
|
|
150
52
|
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
? __dirname
|
|
154
|
-
: path.join(__dirname, "./syncer");
|
|
53
|
+
// sonamu.shared.ts는 무조건 싱크(복사)합니다.
|
|
54
|
+
await this.copySharedToTargets(targets);
|
|
155
55
|
|
|
156
|
-
//
|
|
157
|
-
await
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (!(await exists(srcCodePath))) {
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const dstCodePath = path.join(
|
|
167
|
-
Sonamu.appRootPath,
|
|
168
|
-
target,
|
|
169
|
-
"src/services/sonamu.shared.ts"
|
|
170
|
-
);
|
|
56
|
+
// 그 다음부터는 변경된 파일을 찾아서 동기화 작업을 실행합니다.
|
|
57
|
+
const changedFiles = await findChangedFilesUsingChecksums();
|
|
58
|
+
if (changedFiles.length === 0) {
|
|
59
|
+
console.log(chalk.black.bgGreen(centerText("All files are synced!")));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
171
62
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
})();
|
|
63
|
+
// 만약 싱크 중에 프로세스가 죽으면 꼬여버리기 때문에,
|
|
64
|
+
// 시그널에도 잠시 버틸 수 있는 환경 속에서 싱크를 실행합니다.
|
|
65
|
+
await runWithGracefulShutdown(
|
|
66
|
+
async () => {
|
|
67
|
+
// 얘가 싱크 작업 수행하는 본체입니다.
|
|
68
|
+
await this.doSyncActions(changedFiles);
|
|
179
69
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
console.log(chalk.blue("shared.ts is synced"));
|
|
185
|
-
})
|
|
70
|
+
// 싱크 액션이 끝나면 항상 체크섬을 다시 갱신합니다.
|
|
71
|
+
await renewChecksums();
|
|
72
|
+
},
|
|
73
|
+
{ whenThisHappens: "SIGUSR2", waitForUpTo: 20000 },
|
|
186
74
|
);
|
|
75
|
+
}
|
|
187
76
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (
|
|
196
|
-
const msg = "All files are synced!";
|
|
197
|
-
const margin = (process.stdout.columns - msg.length) / 2;
|
|
198
|
-
console.log(
|
|
199
|
-
chalk.black.bgGreen(" ".repeat(margin) + msg + " ".repeat(margin))
|
|
200
|
-
);
|
|
77
|
+
/**
|
|
78
|
+
* Watcher가 감지한 파일 변경 사항에 대해 싱크를 진행합니다.
|
|
79
|
+
* 주어진 변경 파일들 중 체크섬 관리 대상인 것들만 가져다가 싱크를 진행합니다.
|
|
80
|
+
* 체크섬 파일 업데이트는 여기에서 하지 않습니다. 호출자가 합니다.
|
|
81
|
+
* @param diffFilePath - 변경 파일들. 프로젝트 루트부터 "src/" 또는 "dist/"로 시작하는 상대 경로입니다. 예시: "src/application/user/user.model.ts"
|
|
82
|
+
*/
|
|
83
|
+
async syncFromWatcher(event: string, diffFilePath: AbsolutePath): Promise<void> {
|
|
84
|
+
if (event !== "change" && event !== "add" && event !== "unlink") {
|
|
201
85
|
return;
|
|
202
86
|
}
|
|
203
87
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
88
|
+
// 일단 변경된 파일과 dependent 파일들을 invalidate 합니다.
|
|
89
|
+
// 한 번 이상 import된 친구들에 대해서만 실제 작업이 일어납니다.
|
|
90
|
+
// 그러니 안심하고 invalidate 해도 됩니다.
|
|
91
|
+
// 테스트 환경에서는 hot.invalidateFile시 초기 에러가 발생하기 때문에 invalidate 하지 않습니다.
|
|
92
|
+
if (!isTest()) {
|
|
93
|
+
const invalidatedPaths = (await hot.invalidateFile(diffFilePath, event)) as AbsolutePath[];
|
|
94
|
+
|
|
95
|
+
if (invalidatedPaths.length > 0) {
|
|
96
|
+
console.log(chalk.bold(`🔄 Invalidated:`));
|
|
97
|
+
|
|
98
|
+
for (const invalidatedPath of invalidatedPaths) {
|
|
99
|
+
// 만약 model.ts 파일이 변경(invalidate)되었다? 그러면 registeredApis 중에서 이 모델에 해당하는 api들은 지워줘요.
|
|
100
|
+
// registeredApis는 통으로 다 날려버릴 수 없습니다. registeredApis에 올라오는 친구들은 초기 로드시 또는 HMR시에만 등록되기 때문입니다.
|
|
101
|
+
// 따라서 model.ts 파일의 변경으로 다음번 새로운 eval이 예상되는 이 시점에서만, 이 모델에서 나온 registeredApis들을 지워줄 수 있습니다.
|
|
102
|
+
const removedApis = this.removeInvalidatedRegisteredApis(invalidatedPath);
|
|
103
|
+
if (removedApis.length > 0) {
|
|
104
|
+
console.log(
|
|
105
|
+
chalk.blue(`- ${path.relative(Sonamu.apiRootPath, invalidatedPath)}`),
|
|
106
|
+
chalk.gray(`(with ${removedApis.length} APIs)`),
|
|
107
|
+
);
|
|
108
|
+
} else {
|
|
109
|
+
console.log(chalk.blue(`- ${path.relative(Sonamu.apiRootPath, invalidatedPath)}`));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
209
112
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
// 싱크 완료 대기
|
|
213
|
-
try {
|
|
214
|
-
await setTimeoutPromises(20000, "waiting-sync", { signal: abc.signal });
|
|
215
|
-
} catch {}
|
|
216
|
-
console.log(chalk.magentaBright(`Syncing DONE!`));
|
|
217
|
-
process.exit(0);
|
|
218
|
-
};
|
|
219
|
-
process.on("SIGUSR2", onSIGUSR2);
|
|
113
|
+
}
|
|
220
114
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
currentChecksums,
|
|
224
|
-
previousChecksums,
|
|
225
|
-
_.isEqual
|
|
115
|
+
const isInCheckPatternGroup = Object.values(getChecksumPatternGroupInAbsolutePath()).some(
|
|
116
|
+
(pattern) => minimatch(diffFilePath, pattern),
|
|
226
117
|
);
|
|
227
|
-
const diffFiles = diff.map((r) => r.path);
|
|
228
|
-
console.log("Changed Files: ", diffFiles);
|
|
229
118
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
// checksum 오버라이드 (액션 실행 과정 중간에 체크섬이 바뀐 경우)
|
|
235
|
-
currentChecksums = changedChecksums ?? currentChecksums;
|
|
119
|
+
// 할 일(sync)이 있으면 합니다.
|
|
120
|
+
if (isInCheckPatternGroup) {
|
|
121
|
+
await this.doSyncActions([diffFilePath]);
|
|
122
|
+
}
|
|
236
123
|
|
|
237
|
-
//
|
|
238
|
-
|
|
124
|
+
// 싱크 작업이 끝나면 모든 모듈을 로드합니다.
|
|
125
|
+
// hot-hook에 의해 invalidate된 부분들이 아니라면 캐시 그대로 유지합니다.
|
|
126
|
+
await this.autoloadTypes();
|
|
127
|
+
await this.autoloadModels();
|
|
128
|
+
await this.autoloadApis();
|
|
239
129
|
|
|
240
|
-
|
|
241
|
-
this.isSyncing = false;
|
|
242
|
-
abc.abort();
|
|
243
|
-
process.off("SIGUSR2", onSIGUSR2);
|
|
130
|
+
this.syncUI();
|
|
244
131
|
}
|
|
245
132
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}> {
|
|
253
|
-
// 다른 부분 찾아 액션
|
|
254
|
-
const diffGroups = _.groupBy(diffFiles, (r) => {
|
|
255
|
-
const matched = r.match(
|
|
256
|
-
/\.(model|types|functions|entity|generated|frame)\.[tj]s/
|
|
257
|
-
);
|
|
258
|
-
return matched?.[1] ?? "unknown";
|
|
259
|
-
}) as unknown as DiffGroups;
|
|
133
|
+
removeInvalidatedRegisteredApis(
|
|
134
|
+
invalidatedPath: AbsolutePath,
|
|
135
|
+
): (typeof registeredApis)[number][] {
|
|
136
|
+
if (!invalidatedPath.endsWith(".model.ts" /*소스 코드를 다루는 상황이니 .ts 경로로 봅니다.*/)) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
260
139
|
|
|
261
|
-
|
|
262
|
-
const
|
|
140
|
+
const entityId = EntityManager.getEntityIdFromPath(invalidatedPath);
|
|
141
|
+
const toRemove = registeredApis.filter((api) => api.modelName === `${entityId}Model`);
|
|
142
|
+
for (const api of toRemove) {
|
|
143
|
+
registeredApis.splice(registeredApis.indexOf(api), 1);
|
|
144
|
+
}
|
|
263
145
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
//
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
146
|
+
return toRemove;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async copySharedToTargets(targets: string[]): Promise<void> {
|
|
150
|
+
for (const target of targets) {
|
|
151
|
+
// 지금 가져가려는 이 파일은 Sonamu 코드베이스의 일부입니다.
|
|
152
|
+
// 그런데 dist 속 빌드된 소스 코드 파일이 필요한 것이 아니고, src에만 있는 텍스트 파일이 필요합니다.
|
|
153
|
+
// 따라서 /src/에서 찾습니다.
|
|
154
|
+
const srcPath = path.join(
|
|
155
|
+
import.meta.dirname.replace("/dist/", "/src/"),
|
|
156
|
+
`../shared/${target}.shared.ts.txt`,
|
|
157
|
+
);
|
|
158
|
+
if (!(await exists(srcPath))) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (!(await exists(path.join(Sonamu.appRootPath, target)))) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
`Tried to copy sonamu.shared.ts to target '${target}' but the target directory does not exist. Please check your project directory structure.`,
|
|
281
164
|
);
|
|
282
|
-
if (entity.parentId === undefined && !(await exists(typeFilePath))) {
|
|
283
|
-
await this.generateTemplate("init_types", { entityId });
|
|
284
|
-
}
|
|
285
165
|
}
|
|
286
166
|
|
|
287
|
-
//
|
|
288
|
-
|
|
289
|
-
...(diffGroups["generated"] ?? []),
|
|
290
|
-
"/src/application/sonamu.generated.ts",
|
|
291
|
-
]);
|
|
292
|
-
diffTypes.push("generated");
|
|
167
|
+
// 이건 프로젝트에 .ts 소스 코드 파일을 생성하는 것이므로 src의 .ts 경로로 갑니다.
|
|
168
|
+
const destPath = path.join(Sonamu.appRootPath, target, "./sonamu.shared.ts");
|
|
293
169
|
|
|
294
|
-
//
|
|
295
|
-
if (
|
|
296
|
-
|
|
170
|
+
// 정말 혹시나지만 target 디렉토리는 있어도 src/services 디렉토리는 없을 수 있으므로 미리 생성해줍니다.
|
|
171
|
+
if (!(await exists(path.dirname(destPath)))) {
|
|
172
|
+
await mkdir(path.dirname(destPath), { recursive: true });
|
|
173
|
+
console.warn(`Created directory '${path.dirname(destPath)}' because it did not exist.`);
|
|
297
174
|
}
|
|
175
|
+
|
|
176
|
+
if (await areFilesSame(srcPath, destPath)) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
await writeFile(destPath, await readFile(srcPath));
|
|
181
|
+
|
|
182
|
+
!isTest() &&
|
|
183
|
+
console.log(
|
|
184
|
+
chalk.bold("Copied: ") + chalk.blue(path.relative(Sonamu.appRootPath, destPath)),
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async autoloadTypes() {
|
|
190
|
+
this.types = await loadTypes();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async autoloadModels() {
|
|
194
|
+
this.models = await loadModels();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async autoloadApis() {
|
|
198
|
+
this.apis = await loadApis();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* 실제 싱크를 수행하는 본체입니다.
|
|
203
|
+
* 변경된 파일들을 타입별로 분류하고 각 타입에 맞는 액션을 실행합니다.
|
|
204
|
+
* @param diffFilePaths - 변경된 파일들의 절대 경로 목록
|
|
205
|
+
* @returns diffTypes - 변경된 파일의 타입 목록 (entity, types, model 등)
|
|
206
|
+
*/
|
|
207
|
+
async doSyncActions(diffFilePaths: AbsolutePath[]): Promise<{ diffTypes: string[] }> {
|
|
208
|
+
const diffGroups = this.calculateDiffGroups(diffFilePaths);
|
|
209
|
+
const diffTypes = Object.keys(diffGroups);
|
|
210
|
+
|
|
211
|
+
// 트리거: entity, types
|
|
212
|
+
// 액션: 스키마 생성
|
|
213
|
+
if (diffTypes.includes("entity")) {
|
|
214
|
+
await this.handleEntityChange(diffGroups, diffTypes);
|
|
298
215
|
}
|
|
299
216
|
|
|
300
217
|
// 트리거: types, enums, generated 변경시
|
|
@@ -304,244 +221,192 @@ export class Syncer {
|
|
|
304
221
|
diffTypes.includes("functions") ||
|
|
305
222
|
diffTypes.includes("generated")
|
|
306
223
|
) {
|
|
307
|
-
|
|
308
|
-
[
|
|
309
|
-
...(diffGroups["types"] ?? []),
|
|
310
|
-
...(diffGroups["functions"] ?? []),
|
|
311
|
-
...(diffGroups["generated"] ?? []),
|
|
312
|
-
].map((p) => p.replace("/dist/", "/src/").replace(".js", ".ts"))
|
|
313
|
-
);
|
|
314
|
-
await this.actionSyncFilesToTargets(tsPaths);
|
|
224
|
+
await this.handleTypesOrFunctionsOrGeneratedChange(diffGroups);
|
|
315
225
|
}
|
|
316
226
|
|
|
317
227
|
// 트리거: model
|
|
318
228
|
if (diffTypes.includes("model") || diffTypes.includes("frame")) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
...(diffGroups["frame"] ?? []),
|
|
322
|
-
];
|
|
323
|
-
|
|
324
|
-
// registeredApis 초기화
|
|
325
|
-
await this.autoloadModels();
|
|
326
|
-
|
|
327
|
-
// Syncer.apis 초기화
|
|
328
|
-
await this.autoloadApis();
|
|
329
|
-
|
|
330
|
-
const params: { namesRecord: EntityNamesRecord; modelTsPath: string }[] =
|
|
331
|
-
mergedGroup.map((modelPath) => {
|
|
332
|
-
if (modelPath.endsWith(".model.js")) {
|
|
333
|
-
const entityId = this.getEntityIdFromPath([modelPath])[0];
|
|
334
|
-
assert(entityId);
|
|
335
|
-
return {
|
|
336
|
-
namesRecord: EntityManager.getNamesFromId(entityId),
|
|
337
|
-
modelTsPath: path.join(
|
|
338
|
-
Sonamu.apiRootPath,
|
|
339
|
-
modelPath
|
|
340
|
-
.replace("/dist/", "/src/")
|
|
341
|
-
.replace(".model.js", ".model.ts")
|
|
342
|
-
),
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
if (modelPath.endsWith("frame.js")) {
|
|
346
|
-
const [, frameName] = modelPath.match(/.+\/(.+)\.frame.js$/) ?? [];
|
|
347
|
-
assert(frameName);
|
|
348
|
-
return {
|
|
349
|
-
namesRecord: EntityManager.getNamesFromId(frameName),
|
|
350
|
-
modelTsPath: path.join(
|
|
351
|
-
Sonamu.apiRootPath,
|
|
352
|
-
modelPath
|
|
353
|
-
.replace("/dist/", "/src/")
|
|
354
|
-
.replace(".frame.js", ".frame.ts")
|
|
355
|
-
),
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
throw new Error("not reachable");
|
|
359
|
-
});
|
|
360
|
-
await this.actionGenerateServices(params);
|
|
229
|
+
await this.handleModelOrFrameChange(diffGroups);
|
|
230
|
+
}
|
|
361
231
|
|
|
362
|
-
|
|
232
|
+
// 트리거: config
|
|
233
|
+
if (diffTypes.includes("config")) {
|
|
234
|
+
await this.actionSyncConfig();
|
|
363
235
|
}
|
|
364
236
|
|
|
365
237
|
return {
|
|
366
238
|
diffTypes,
|
|
367
|
-
changedChecksums: currentChecksums,
|
|
368
239
|
};
|
|
369
240
|
}
|
|
370
241
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
let transpiledFilePaths: string[] = [];
|
|
378
|
-
for (const chunk of chunks) {
|
|
379
|
-
const _transpiledFilePaths = await Promise.all(
|
|
380
|
-
chunk.map(async (diffFile) => {
|
|
381
|
-
const { code, map } = await swc.transformFile(diffFile, {
|
|
382
|
-
module: {
|
|
383
|
-
type: "commonjs",
|
|
384
|
-
},
|
|
385
|
-
jsc: {
|
|
386
|
-
parser: {
|
|
387
|
-
syntax: "typescript",
|
|
388
|
-
decorators: true,
|
|
389
|
-
},
|
|
390
|
-
target: "es5",
|
|
391
|
-
},
|
|
392
|
-
sourceMaps: true,
|
|
393
|
-
});
|
|
242
|
+
calculateDiffGroups(diffFiles: AbsolutePath[]): DiffGroups {
|
|
243
|
+
return group(diffFiles, (r) => {
|
|
244
|
+
const matched = r.match(/\.(model|types|functions|entity|generated|frame|config)\.[tj]s/);
|
|
245
|
+
return matched?.[1] ?? "unknown";
|
|
246
|
+
}) as unknown as DiffGroups;
|
|
247
|
+
}
|
|
394
248
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
await writeFile(jsPath, code);
|
|
400
|
-
|
|
401
|
-
if (map) {
|
|
402
|
-
const mapPath = jsPath + ".map";
|
|
403
|
-
await mkdir(path.dirname(mapPath), { recursive: true });
|
|
404
|
-
await writeFile(mapPath, map);
|
|
405
|
-
|
|
406
|
-
const sourceMapComment =
|
|
407
|
-
"\n//# sourceMappingURL=" + path.basename(mapPath);
|
|
408
|
-
await writeFile(jsPath, sourceMapComment, {
|
|
409
|
-
flag: "a" /*파일 끝에 붙이기만 해요*/,
|
|
410
|
-
});
|
|
411
|
-
}
|
|
249
|
+
async handleEntityChange(diffGroups: DiffGroups, diffTypes: string[]): Promise<void> {
|
|
250
|
+
Naite.t("handleEntityChange", { diffGroups, diffTypes });
|
|
251
|
+
|
|
252
|
+
await EntityManager.reload();
|
|
412
253
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
254
|
+
// types 생성(entity 새로 추가된 경우)
|
|
255
|
+
// parentId가 없고, types가 없는 경우에만 생성
|
|
256
|
+
const entityId = EntityManager.getEntityIdFromPath(diffGroups.entity?.[0]);
|
|
257
|
+
|
|
258
|
+
if (entityId) {
|
|
259
|
+
const entity = EntityManager.get(entityId);
|
|
260
|
+
// 프로젝트에 생성되어야 하는 .ts 파일의 경로입니다.
|
|
261
|
+
const typeFilePath = path.join(
|
|
262
|
+
Sonamu.apiRootPath,
|
|
263
|
+
`src/application/${entity.names.fs}/${entity.names.fs}.types.ts`,
|
|
419
264
|
);
|
|
420
|
-
|
|
265
|
+
if (entity.parentId === undefined && !(await exists(typeFilePath))) {
|
|
266
|
+
await generateTemplate("init_types", { entityId });
|
|
267
|
+
}
|
|
421
268
|
}
|
|
422
269
|
|
|
423
|
-
|
|
424
|
-
function clearModuleAndDependents(filePath: string) {
|
|
425
|
-
const resolved = require.resolve(filePath);
|
|
426
|
-
const toDelete = new Set([resolved]);
|
|
270
|
+
await this.actionGenerateSchemas();
|
|
427
271
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
});
|
|
272
|
+
diffGroups.generated = unique([
|
|
273
|
+
...(diffGroups.generated ?? []),
|
|
274
|
+
path.join(Sonamu.apiRootPath, "src/application/sonamu.generated.ts") as AbsolutePath,
|
|
275
|
+
]);
|
|
276
|
+
diffTypes.push("generated");
|
|
277
|
+
}
|
|
435
278
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
// chalk.blue(`${key.replace(Sonamu.apiRootPath, "api")}`)
|
|
444
|
-
// );
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
transpiledFilePaths.map((filePath) => {
|
|
448
|
-
clearModuleAndDependents(filePath);
|
|
449
|
-
});
|
|
279
|
+
async handleTypesOrFunctionsOrGeneratedChange(diffGroups: DiffGroups): Promise<FileType[]> {
|
|
280
|
+
const tsPaths = unique([
|
|
281
|
+
...(diffGroups.types ?? []),
|
|
282
|
+
...(diffGroups.functions ?? []),
|
|
283
|
+
...(diffGroups.generated ?? []),
|
|
284
|
+
]);
|
|
285
|
+
Naite.t("handleTypesOrFunctionsOrGeneratedChange", { diffGroups });
|
|
450
286
|
|
|
451
|
-
//
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
minimatch(filePath, pattern)
|
|
457
|
-
);
|
|
458
|
-
})
|
|
459
|
-
.map((filePath) => "/" + path.relative(Sonamu.apiRootPath, filePath));
|
|
460
|
-
await this.doSyncActions(targetFilePaths);
|
|
287
|
+
// console.log(
|
|
288
|
+
// chalk.gray(
|
|
289
|
+
// `[Processing] Handling types/functions/generated changes: ${tsPaths.map((p) => path.relative(Sonamu.apiRootPath, p)).join(", ")}`
|
|
290
|
+
// )
|
|
291
|
+
// );
|
|
461
292
|
|
|
462
|
-
this.
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
293
|
+
await this.actionSyncFilesToTargets(tsPaths);
|
|
294
|
+
|
|
295
|
+
return [];
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async handleModelOrFrameChange(diffGroups: DiffGroups): Promise<void> {
|
|
299
|
+
Naite.t("handleModelOrFrameChange", { diffGroups });
|
|
300
|
+
const mergedGroup = [...(diffGroups.model ?? []), ...(diffGroups.frame ?? [])];
|
|
301
|
+
|
|
302
|
+
// console.log(
|
|
303
|
+
// chalk.gray(
|
|
304
|
+
// `[Processing] Handling model/frame changes: ${mergedGroup.map((p) => path.relative(Sonamu.apiRootPath, p)).join(", ")}`
|
|
305
|
+
// )
|
|
306
|
+
// );
|
|
307
|
+
|
|
308
|
+
// generated_http.template.ts에서 syncer.types를 씁니다.
|
|
309
|
+
// service.template.ts에서 syncer.apis를 씁니다.
|
|
466
310
|
await this.autoloadModels();
|
|
311
|
+
await this.autoloadTypes();
|
|
467
312
|
await this.autoloadApis();
|
|
468
313
|
|
|
469
|
-
|
|
314
|
+
const params: {
|
|
315
|
+
namesRecord: EntityNamesRecord;
|
|
316
|
+
}[] = mergedGroup.map((modelPath) => {
|
|
317
|
+
if (modelPath.endsWith(".model.ts")) {
|
|
318
|
+
const entityId = EntityManager.getEntityIdFromPath(modelPath);
|
|
319
|
+
assert(entityId);
|
|
320
|
+
return {
|
|
321
|
+
namesRecord: EntityManager.getNamesFromId(entityId),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
if (modelPath.endsWith("frame.ts")) {
|
|
325
|
+
const [, frameName] = modelPath.match(/.+\/(.+)\.frame.js$/) ?? [];
|
|
326
|
+
assert(frameName);
|
|
327
|
+
return {
|
|
328
|
+
namesRecord: EntityManager.getNamesFromId(frameName),
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
throw new Error("not reachable");
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
await this.actionGenerateServices(params);
|
|
335
|
+
await this.actionGenerateHttps();
|
|
470
336
|
}
|
|
471
337
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
338
|
+
// web/.sonamu.env 에 현재 설정값 저장
|
|
339
|
+
async actionSyncConfig() {
|
|
340
|
+
const { host, port } = Sonamu.config.server.listen ?? {};
|
|
341
|
+
const content = `API_HOST=${host ?? "localhost"}\nAPI_PORT=${port ?? 3000}`;
|
|
342
|
+
|
|
343
|
+
Naite.t("actionSyncConfig", { content });
|
|
344
|
+
await Promise.all(
|
|
345
|
+
Sonamu.config.sync.targets.map(async (target) => {
|
|
346
|
+
await writeFile(path.join(Sonamu.appRootPath, target, ".sonamu.env"), content);
|
|
347
|
+
}),
|
|
479
348
|
);
|
|
480
349
|
}
|
|
481
350
|
|
|
482
|
-
|
|
351
|
+
/**
|
|
352
|
+
* sonamu.generated.ts와 sonamu.generated.sso.ts를 생성합니다.
|
|
353
|
+
* @returns 생성된 파일 경로 배열.
|
|
354
|
+
*/
|
|
355
|
+
async actionGenerateSchemas(): Promise<AbsolutePath[]> {
|
|
483
356
|
return (
|
|
484
357
|
await Promise.all([
|
|
485
|
-
|
|
486
|
-
|
|
358
|
+
generateTemplate("generated_sso", {}, { overwrite: true }),
|
|
359
|
+
generateTemplate("generated", {}, { overwrite: true }),
|
|
487
360
|
])
|
|
488
361
|
)
|
|
489
362
|
.flat()
|
|
490
363
|
.flat();
|
|
491
364
|
}
|
|
492
365
|
|
|
366
|
+
/**
|
|
367
|
+
* *.service.ts를 생성합니다.
|
|
368
|
+
* @param paramsArray
|
|
369
|
+
* @returns 생성된 파일 경로 배열.
|
|
370
|
+
*/
|
|
493
371
|
async actionGenerateServices(
|
|
494
372
|
paramsArray: {
|
|
495
373
|
namesRecord: EntityNamesRecord;
|
|
496
|
-
|
|
497
|
-
}[]
|
|
374
|
+
}[],
|
|
498
375
|
): Promise<string[]> {
|
|
376
|
+
Naite.t("actionGenerateServices", paramsArray);
|
|
499
377
|
return (
|
|
500
378
|
await Promise.all(
|
|
501
379
|
paramsArray.map(async (params) =>
|
|
502
|
-
|
|
380
|
+
generateTemplate("service", params as TemplateOptions["service"], {
|
|
503
381
|
overwrite: true,
|
|
504
|
-
})
|
|
505
|
-
)
|
|
382
|
+
}),
|
|
383
|
+
),
|
|
506
384
|
)
|
|
507
385
|
)
|
|
508
386
|
.flat()
|
|
509
387
|
.flat();
|
|
510
388
|
}
|
|
511
389
|
|
|
512
|
-
|
|
513
|
-
|
|
390
|
+
/**
|
|
391
|
+
* sonamu.generated.http를 생성합니다.
|
|
392
|
+
* @returns 생성된 파일 경로.
|
|
393
|
+
*/
|
|
394
|
+
async actionGenerateHttps(): Promise<AbsolutePath> {
|
|
395
|
+
const [res] = await generateTemplate(
|
|
514
396
|
"generated_http",
|
|
515
|
-
{},
|
|
516
|
-
{ overwrite: true }
|
|
397
|
+
{ entityId: "dummy" },
|
|
398
|
+
{ overwrite: true },
|
|
517
399
|
);
|
|
518
400
|
assert(res);
|
|
519
401
|
return res;
|
|
520
402
|
}
|
|
521
403
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
const newFileContent = (() => {
|
|
530
|
-
const nfc = oldFileContent.replace(
|
|
531
|
-
/from "sonamu"/g,
|
|
532
|
-
`from "src/services/sonamu.shared"`
|
|
533
|
-
);
|
|
534
|
-
|
|
535
|
-
if (toPath.includes("/web/")) {
|
|
536
|
-
return nfc.replace(/from "lodash";/g, `from "lodash-es";`);
|
|
537
|
-
} else {
|
|
538
|
-
return nfc;
|
|
539
|
-
}
|
|
540
|
-
})();
|
|
541
|
-
return writeFile(toPath, newFileContent);
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
async actionSyncFilesToTargets(tsPaths: string[]): Promise<string[]> {
|
|
404
|
+
/**
|
|
405
|
+
* *.types.ts, *.functions.ts, *.generated.ts를 타겟 디렉토리에 복사합니다.
|
|
406
|
+
* @param tsPaths
|
|
407
|
+
* @returns 복사된 파일 경로 배열.
|
|
408
|
+
*/
|
|
409
|
+
async actionSyncFilesToTargets(tsPaths: AbsolutePath[]): Promise<string[]> {
|
|
545
410
|
const { targets } = Sonamu.config.sync;
|
|
546
411
|
const { dir: apiDir } = Sonamu.config.api;
|
|
547
412
|
|
|
@@ -549,8 +414,7 @@ export class Syncer {
|
|
|
549
414
|
await Promise.all(
|
|
550
415
|
targets.map(async (target) =>
|
|
551
416
|
Promise.all(
|
|
552
|
-
tsPaths.map(async (
|
|
553
|
-
const realSrc = Sonamu.apiRootPath + src;
|
|
417
|
+
tsPaths.map(async (realSrc) => {
|
|
554
418
|
const dst = realSrc
|
|
555
419
|
.replace(`/${apiDir}/`, `/${target}/`)
|
|
556
420
|
.replace("/application/", "/services/");
|
|
@@ -558,686 +422,52 @@ export class Syncer {
|
|
|
558
422
|
if (!(await exists(dir))) {
|
|
559
423
|
await mkdir(dir, { recursive: true });
|
|
560
424
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
chalk.blue(
|
|
564
|
-
|
|
565
|
-
)
|
|
566
|
-
);
|
|
425
|
+
!isTest() &&
|
|
426
|
+
console.log(
|
|
427
|
+
chalk.bold("Copied: ") + chalk.blue(dst.replace(`${Sonamu.appRootPath}/`, "")),
|
|
428
|
+
);
|
|
567
429
|
await this.copyFileWithReplaceCoreToShared(realSrc, dst);
|
|
568
430
|
return dst;
|
|
569
|
-
})
|
|
570
|
-
)
|
|
571
|
-
)
|
|
572
|
-
)
|
|
573
|
-
).flat();
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
async getCurrentChecksums(): Promise<PathAndChecksum[]> {
|
|
577
|
-
const filePaths = (
|
|
578
|
-
await Promise.all(
|
|
579
|
-
Object.entries(this.checksumPatternGroup).map(
|
|
580
|
-
async ([_fileType, pattern]) => {
|
|
581
|
-
return globAsync(pattern);
|
|
582
|
-
}
|
|
583
|
-
)
|
|
584
|
-
)
|
|
585
|
-
)
|
|
586
|
-
.flat()
|
|
587
|
-
.sort();
|
|
588
|
-
|
|
589
|
-
const fileChecksums: {
|
|
590
|
-
path: string;
|
|
591
|
-
checksum: string;
|
|
592
|
-
}[] = await Promise.all(
|
|
593
|
-
filePaths.map(async (filePath) => {
|
|
594
|
-
return {
|
|
595
|
-
path: filePath.substring(Sonamu.apiRootPath.length),
|
|
596
|
-
checksum: await this.getChecksumOfFile(filePath),
|
|
597
|
-
};
|
|
598
|
-
})
|
|
599
|
-
);
|
|
600
|
-
return fileChecksums;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
async getPreviousChecksums(): Promise<PathAndChecksum[]> {
|
|
604
|
-
if (!(await exists(this.checksumsPath))) {
|
|
605
|
-
return [];
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
const previousChecksums = JSON.parse(
|
|
609
|
-
(await readFile(this.checksumsPath, "utf-8"))
|
|
610
|
-
) as PathAndChecksum[];
|
|
611
|
-
return previousChecksums;
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
async saveChecksums(checksums: PathAndChecksum[]): Promise<void> {
|
|
615
|
-
await writeFile(
|
|
616
|
-
this.checksumsPath,
|
|
617
|
-
JSON.stringify(checksums, null, 2),
|
|
618
|
-
"utf-8"
|
|
619
|
-
);
|
|
620
|
-
console.log("checksum saved", this.checksumsPath);
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
async getChecksumOfFile(filePath: string): Promise<string> {
|
|
624
|
-
return new Promise<string>((resolve, reject) => {
|
|
625
|
-
const hash = crypto.createHash("sha1");
|
|
626
|
-
const input = createReadStream(filePath);
|
|
627
|
-
input.on("error", reject);
|
|
628
|
-
input.on("data", function (chunk: any) {
|
|
629
|
-
hash.update(chunk);
|
|
630
|
-
});
|
|
631
|
-
input.on("close", function () {
|
|
632
|
-
resolve(hash.digest("hex"));
|
|
633
|
-
});
|
|
634
|
-
});
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
async readApisFromFile(filePath: string) {
|
|
638
|
-
const sourceFile = ts.createSourceFile(
|
|
639
|
-
filePath,
|
|
640
|
-
(await readFile(filePath)).toString(),
|
|
641
|
-
ts.ScriptTarget.Latest
|
|
642
|
-
);
|
|
643
|
-
|
|
644
|
-
const methods: Omit<ExtendedApi, "path" | "options">[] = [];
|
|
645
|
-
let modelName: string = "UnknownModel";
|
|
646
|
-
let methodName: string = "unknownMethod";
|
|
647
|
-
const visitor = (node: ts.Node) => {
|
|
648
|
-
if (ts.isClassDeclaration(node)) {
|
|
649
|
-
if (node.name && ts.isIdentifier(node.name)) {
|
|
650
|
-
modelName = node.name.escapedText.toString().replace(/Class$/, "");
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
if (ts.isMethodDeclaration(node)) {
|
|
654
|
-
if (ts.isIdentifier(node.name)) {
|
|
655
|
-
methodName = node.name.escapedText.toString();
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
const typeParameters: ApiParamType.TypeParam[] = (
|
|
659
|
-
node.typeParameters ?? []
|
|
660
|
-
).map((typeParam) => {
|
|
661
|
-
const tp = typeParam as ts.TypeParameterDeclaration;
|
|
662
|
-
|
|
663
|
-
return {
|
|
664
|
-
t: "type-param",
|
|
665
|
-
id: tp.name.escapedText.toString(),
|
|
666
|
-
constraint: tp.constraint
|
|
667
|
-
? this.resolveTypeNode(tp.constraint)
|
|
668
|
-
: undefined,
|
|
669
|
-
};
|
|
670
|
-
});
|
|
671
|
-
const parameters: ApiParam[] = node.parameters.map(
|
|
672
|
-
(paramDec, index) => {
|
|
673
|
-
const defaultDef = this.printNode(paramDec.initializer, sourceFile);
|
|
674
|
-
|
|
675
|
-
// 기본값이 있는 경우 paramDec.type가 undefined로 나옴
|
|
676
|
-
|
|
677
|
-
return this.resolveParamDec(
|
|
678
|
-
{
|
|
679
|
-
name: paramDec.name,
|
|
680
|
-
type: paramDec.type as ts.TypeNode,
|
|
681
|
-
optional:
|
|
682
|
-
paramDec.questionToken !== undefined ||
|
|
683
|
-
paramDec.initializer !== undefined,
|
|
684
|
-
defaultDef,
|
|
685
|
-
},
|
|
686
|
-
index
|
|
687
|
-
);
|
|
688
|
-
}
|
|
689
|
-
);
|
|
690
|
-
if (node.type === undefined) {
|
|
691
|
-
throw new Error(
|
|
692
|
-
`리턴 타입이 기재되지 않은 메소드 ${modelName}.${methodName}`
|
|
693
|
-
);
|
|
694
|
-
}
|
|
695
|
-
const returnType = this.resolveTypeNode(node.type!);
|
|
696
|
-
|
|
697
|
-
methods.push({
|
|
698
|
-
modelName,
|
|
699
|
-
methodName,
|
|
700
|
-
typeParameters,
|
|
701
|
-
parameters,
|
|
702
|
-
returnType,
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
ts.forEachChild(node, visitor);
|
|
706
|
-
};
|
|
707
|
-
visitor(sourceFile);
|
|
708
|
-
|
|
709
|
-
if (methods.length === 0) {
|
|
710
|
-
return [];
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
// 현재 파일의 등록된 API 필터
|
|
714
|
-
const currentModelApis = registeredApis.filter((api) => {
|
|
715
|
-
return methods.find(
|
|
716
|
-
(method) =>
|
|
717
|
-
method.modelName === api.modelName &&
|
|
718
|
-
method.methodName === api.methodName
|
|
719
|
-
);
|
|
720
|
-
});
|
|
721
|
-
if (currentModelApis.length === 0) {
|
|
722
|
-
// const p = path.join(tmpdir(), "sonamu-syncer-error.json");
|
|
723
|
-
// writeFileSync(p, JSON.stringify(registeredApis, null, 2));
|
|
724
|
-
// execSync(`open ${p}`);
|
|
725
|
-
throw new Error(`현재 파일에 사전 등록된 API가 없습니다. ${filePath}`);
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
// 등록된 API에 현재 메소드 타입 정보 확장
|
|
729
|
-
const extendedApis = currentModelApis.map((api) => {
|
|
730
|
-
const foundMethod = methods.find(
|
|
731
|
-
(method) =>
|
|
732
|
-
method.modelName === api.modelName &&
|
|
733
|
-
method.methodName === api.methodName
|
|
734
|
-
);
|
|
735
|
-
return {
|
|
736
|
-
...api,
|
|
737
|
-
typeParameters: foundMethod!.typeParameters,
|
|
738
|
-
parameters: foundMethod!.parameters,
|
|
739
|
-
returnType: foundMethod!.returnType,
|
|
740
|
-
};
|
|
741
|
-
});
|
|
742
|
-
return extendedApis;
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
resolveTypeNode(typeNode: ts.TypeNode): ApiParamType {
|
|
746
|
-
switch (typeNode?.kind) {
|
|
747
|
-
case ts.SyntaxKind.AnyKeyword:
|
|
748
|
-
return "any";
|
|
749
|
-
case ts.SyntaxKind.UnknownKeyword:
|
|
750
|
-
return "unknown";
|
|
751
|
-
case ts.SyntaxKind.StringKeyword:
|
|
752
|
-
return "string";
|
|
753
|
-
case ts.SyntaxKind.NumberKeyword:
|
|
754
|
-
return "number";
|
|
755
|
-
case ts.SyntaxKind.BooleanKeyword:
|
|
756
|
-
return "boolean";
|
|
757
|
-
case ts.SyntaxKind.UndefinedKeyword:
|
|
758
|
-
return "undefined";
|
|
759
|
-
case ts.SyntaxKind.NullKeyword:
|
|
760
|
-
return "null";
|
|
761
|
-
case ts.SyntaxKind.VoidKeyword:
|
|
762
|
-
return "void";
|
|
763
|
-
case ts.SyntaxKind.LiteralType:
|
|
764
|
-
const literal = (typeNode as ts.LiteralTypeNode).literal;
|
|
765
|
-
if (ts.isStringLiteral(literal)) {
|
|
766
|
-
return {
|
|
767
|
-
t: "string-literal",
|
|
768
|
-
value: literal.text,
|
|
769
|
-
};
|
|
770
|
-
} else if (ts.isNumericLiteral(literal)) {
|
|
771
|
-
return {
|
|
772
|
-
t: "numeric-literal",
|
|
773
|
-
value: Number(literal.text),
|
|
774
|
-
};
|
|
775
|
-
} else {
|
|
776
|
-
if (literal.kind === ts.SyntaxKind.NullKeyword) {
|
|
777
|
-
return "null";
|
|
778
|
-
} else if (literal.kind === ts.SyntaxKind.UndefinedKeyword) {
|
|
779
|
-
return "undefined";
|
|
780
|
-
} else if (literal.kind === ts.SyntaxKind.TrueKeyword) {
|
|
781
|
-
return "true";
|
|
782
|
-
} else if (literal.kind === ts.SyntaxKind.FalseKeyword) {
|
|
783
|
-
return "false";
|
|
784
|
-
}
|
|
785
|
-
throw new Error("알 수 없는 리터럴");
|
|
786
|
-
}
|
|
787
|
-
case ts.SyntaxKind.ArrayType:
|
|
788
|
-
const arrNode = typeNode as ts.ArrayTypeNode;
|
|
789
|
-
return {
|
|
790
|
-
t: "array",
|
|
791
|
-
elementsType: this.resolveTypeNode(arrNode.elementType),
|
|
792
|
-
};
|
|
793
|
-
case ts.SyntaxKind.TypeLiteral:
|
|
794
|
-
const literalNode = typeNode as ts.TypeLiteralNode;
|
|
795
|
-
return {
|
|
796
|
-
t: "object",
|
|
797
|
-
props: literalNode.members.map((member) => {
|
|
798
|
-
if (ts.isIndexSignatureDeclaration(member)) {
|
|
799
|
-
assert(member.parameters[0]);
|
|
800
|
-
const res = this.resolveParamDec({
|
|
801
|
-
name: member.parameters[0].name as ts.Identifier,
|
|
802
|
-
type: member.parameters[0].type as ts.TypeNode,
|
|
803
|
-
});
|
|
804
|
-
|
|
805
|
-
return this.resolveParamDec({
|
|
806
|
-
name: {
|
|
807
|
-
escapedText: `[${res.name}${res.optional ? "?" : ""}: ${
|
|
808
|
-
res.type
|
|
809
|
-
}]`,
|
|
810
|
-
} as ts.Identifier,
|
|
811
|
-
type: member.type as ts.TypeNode,
|
|
812
|
-
});
|
|
813
|
-
} else {
|
|
814
|
-
return this.resolveParamDec({
|
|
815
|
-
name: (member as ts.PropertySignature).name as ts.Identifier,
|
|
816
|
-
type: (member as ts.PropertySignature).type as ts.TypeNode,
|
|
817
|
-
optional:
|
|
818
|
-
(member as ts.PropertySignature).questionToken !== undefined,
|
|
819
|
-
});
|
|
820
|
-
}
|
|
821
|
-
}),
|
|
822
|
-
};
|
|
823
|
-
case ts.SyntaxKind.TypeReference:
|
|
824
|
-
return {
|
|
825
|
-
t: "ref",
|
|
826
|
-
id: (
|
|
827
|
-
(typeNode as ts.TypeReferenceNode).typeName as ts.Identifier
|
|
828
|
-
).escapedText.toString(),
|
|
829
|
-
args: (typeNode as ts.TypeReferenceNode).typeArguments?.map(
|
|
830
|
-
(typeArg) => this.resolveTypeNode(typeArg)
|
|
831
|
-
),
|
|
832
|
-
};
|
|
833
|
-
case ts.SyntaxKind.UnionType:
|
|
834
|
-
return {
|
|
835
|
-
t: "union",
|
|
836
|
-
types: (typeNode as ts.UnionTypeNode).types.map((type) =>
|
|
837
|
-
this.resolveTypeNode(type)
|
|
838
|
-
),
|
|
839
|
-
};
|
|
840
|
-
case ts.SyntaxKind.IntersectionType:
|
|
841
|
-
return {
|
|
842
|
-
t: "intersection",
|
|
843
|
-
types: (typeNode as ts.IntersectionTypeNode).types.map((type) =>
|
|
844
|
-
this.resolveTypeNode(type)
|
|
431
|
+
}),
|
|
845
432
|
),
|
|
846
|
-
|
|
847
|
-
case ts.SyntaxKind.IndexedAccessType:
|
|
848
|
-
return {
|
|
849
|
-
t: "indexed-access",
|
|
850
|
-
object: this.resolveTypeNode(
|
|
851
|
-
(typeNode as ts.IndexedAccessTypeNode).objectType
|
|
852
|
-
),
|
|
853
|
-
index: this.resolveTypeNode(
|
|
854
|
-
(typeNode as ts.IndexedAccessTypeNode).indexType
|
|
855
|
-
),
|
|
856
|
-
};
|
|
857
|
-
case ts.SyntaxKind.TupleType:
|
|
858
|
-
if (ts.isTupleTypeNode(typeNode)) {
|
|
859
|
-
return {
|
|
860
|
-
t: "tuple-type",
|
|
861
|
-
elements: typeNode.elements.map((elem) =>
|
|
862
|
-
this.resolveTypeNode(elem)
|
|
863
|
-
),
|
|
864
|
-
};
|
|
865
|
-
}
|
|
866
|
-
break;
|
|
867
|
-
case undefined:
|
|
868
|
-
throw new Error(`typeNode undefined`);
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
console.debug(typeNode);
|
|
872
|
-
throw new Error(`알 수 없는 SyntaxKind ${typeNode.kind}`);
|
|
873
|
-
}
|
|
874
|
-
|
|
875
|
-
resolveParamDec = (
|
|
876
|
-
paramDec: {
|
|
877
|
-
name: ts.BindingName;
|
|
878
|
-
type: ts.TypeNode;
|
|
879
|
-
optional?: boolean;
|
|
880
|
-
defaultDef?: string;
|
|
881
|
-
},
|
|
882
|
-
index: number = 0
|
|
883
|
-
): ApiParam => {
|
|
884
|
-
const name = paramDec.name as ts.Identifier;
|
|
885
|
-
const type = this.resolveTypeNode(paramDec.type);
|
|
886
|
-
|
|
887
|
-
if (name === undefined) {
|
|
888
|
-
console.debug({ name, type, paramDec });
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
const result: ApiParam = {
|
|
892
|
-
name: name.escapedText ? name.escapedText.toString() : `nonameAt${index}`,
|
|
893
|
-
type,
|
|
894
|
-
optional: paramDec.optional === true,
|
|
895
|
-
defaultDef: paramDec?.defaultDef,
|
|
896
|
-
};
|
|
897
|
-
|
|
898
|
-
// 구조분해할당의 경우 타입이름 사용
|
|
899
|
-
if (
|
|
900
|
-
ts.isObjectBindingPattern(name) &&
|
|
901
|
-
ts.isTypeReferenceNode(paramDec.type) &&
|
|
902
|
-
ts.isIdentifier(paramDec.type.typeName)
|
|
903
|
-
) {
|
|
904
|
-
result.name = inflection.camelize(paramDec.type.typeName.text, true);
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
return result;
|
|
908
|
-
};
|
|
909
|
-
|
|
910
|
-
printNode(
|
|
911
|
-
node: ts.Node | undefined,
|
|
912
|
-
sourceFile: ts.SourceFile
|
|
913
|
-
): string | undefined {
|
|
914
|
-
if (node === undefined) {
|
|
915
|
-
return undefined;
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
919
|
-
return printer.printNode(ts.EmitHint.Unspecified, node, sourceFile);
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
async autoloadApis() {
|
|
923
|
-
const pathPattern = path.join(
|
|
924
|
-
Sonamu.apiRootPath,
|
|
925
|
-
"/src/application/**/*.{model,frame}.ts"
|
|
926
|
-
);
|
|
927
|
-
// console.debug(chalk.yellow(`autoload:APIs @ ${pathPattern}`));
|
|
928
|
-
|
|
929
|
-
const filePaths = await globAsync(pathPattern);
|
|
930
|
-
const result = await Promise.all(
|
|
931
|
-
filePaths.map((filePath) => this.readApisFromFile(filePath))
|
|
932
|
-
);
|
|
933
|
-
this.apis = result.flat();
|
|
934
|
-
return this.apis;
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
async autoloadModels(): Promise<{ [modelName: string]: unknown }> {
|
|
938
|
-
const pathPattern = path.join(
|
|
939
|
-
Sonamu.apiRootPath,
|
|
940
|
-
"dist/application/**/*.{model,frame}.js"
|
|
941
|
-
);
|
|
942
|
-
// console.debug(chalk.yellow(`autoload:models @ ${pathPattern}`));
|
|
943
|
-
|
|
944
|
-
const filePaths = await filterAsync(
|
|
945
|
-
await globAsync(pathPattern),
|
|
946
|
-
async (path) => {
|
|
947
|
-
// src 디렉터리 내에 있는 해당 파일이 존재할 경우에만 로드
|
|
948
|
-
// 삭제된 파일이지만 dist에 남아있는 경우 BaseSchema undefined 에러 방지
|
|
949
|
-
const srcPath = path.replace("/dist/", "/src/").replace(".js", ".ts");
|
|
950
|
-
return await exists(srcPath);
|
|
951
|
-
}
|
|
952
|
-
);
|
|
953
|
-
const modules = await importMultiple(filePaths);
|
|
954
|
-
const functions = modules
|
|
955
|
-
.map(({ imported }) => Object.entries(imported))
|
|
956
|
-
.flat();
|
|
957
|
-
this.models = Object.fromEntries(
|
|
958
|
-
functions.filter(
|
|
959
|
-
([name]) => name.endsWith("Model") || name.endsWith("Frame")
|
|
433
|
+
),
|
|
960
434
|
)
|
|
961
|
-
);
|
|
962
|
-
return this.models;
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
async autoloadTypes(
|
|
966
|
-
doRefresh: boolean = false
|
|
967
|
-
): Promise<{ [typeName: string]: z.ZodObject<any> }> {
|
|
968
|
-
if (!doRefresh && Object.keys(this.types).length > 0) {
|
|
969
|
-
return this.types;
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
const pathPatterns = [
|
|
973
|
-
path.join(Sonamu.apiRootPath, "/dist/application/**/*.types.js"),
|
|
974
|
-
path.join(Sonamu.apiRootPath, "/dist/application/**/*.generated.js"),
|
|
975
|
-
];
|
|
976
|
-
// console.debug(chalk.magenta(`autoload:types @ ${pathPatterns.join("\n")}`));
|
|
977
|
-
|
|
978
|
-
const filePaths = await filterAsync(
|
|
979
|
-
(await mapAsync(pathPatterns, globAsync)).flat(),
|
|
980
|
-
async (path) => {
|
|
981
|
-
// src 디렉터리 내에 있는 해당 파일이 존재할 경우에만 로드
|
|
982
|
-
// 삭제된 파일이지만 dist에 남아있는 경우 BaseSchema undefined 에러 방지
|
|
983
|
-
const srcPath = path.replace("/dist/", "/src/").replace(".js", ".ts");
|
|
984
|
-
return await exists(srcPath);
|
|
985
|
-
}
|
|
986
|
-
);
|
|
987
|
-
const modules = await importMultiple(filePaths, doRefresh);
|
|
988
|
-
const functions = modules
|
|
989
|
-
.map(({ imported }) => Object.entries(imported))
|
|
990
|
-
.flat();
|
|
991
|
-
this.types = Object.fromEntries(
|
|
992
|
-
functions.filter(([, f]) => f instanceof z.ZodType)
|
|
993
|
-
) as typeof this.types;
|
|
994
|
-
return this.types;
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
getTemplate(key: TemplateKey): Template {
|
|
998
|
-
if (key === "entity") {
|
|
999
|
-
return new Template__entity();
|
|
1000
|
-
} else if (key === "init_types") {
|
|
1001
|
-
return new Template__init_types();
|
|
1002
|
-
} else if (key === "generated") {
|
|
1003
|
-
return new Template__generated();
|
|
1004
|
-
} else if (key === "generated_sso") {
|
|
1005
|
-
return new Template__generated_sso();
|
|
1006
|
-
} else if (key === "generated_http") {
|
|
1007
|
-
return new Template__generated_http();
|
|
1008
|
-
} else if (key === "model") {
|
|
1009
|
-
return new Template__model();
|
|
1010
|
-
} else if (key === "model_test") {
|
|
1011
|
-
return new Template__model_test();
|
|
1012
|
-
} else if (key === "service") {
|
|
1013
|
-
return new Template__service();
|
|
1014
|
-
} else if (key === "view_list") {
|
|
1015
|
-
return new Template__view_list();
|
|
1016
|
-
} else if (key === "view_list_columns") {
|
|
1017
|
-
return new Template__view_list_columns();
|
|
1018
|
-
} else if (key === "view_search_input") {
|
|
1019
|
-
return new Template__view_search_input();
|
|
1020
|
-
} else if (key === "view_form") {
|
|
1021
|
-
return new Template__view_form();
|
|
1022
|
-
} else if (key === "view_id_all_select") {
|
|
1023
|
-
return new Template__view_id_all_select();
|
|
1024
|
-
} else if (key === "view_id_async_select") {
|
|
1025
|
-
return new Template__view_id_async_select();
|
|
1026
|
-
} else if (key === "view_enums_select") {
|
|
1027
|
-
return new Template__view_enums_select();
|
|
1028
|
-
} else if (key === "view_enums_dropdown") {
|
|
1029
|
-
return new Template__view_enums_dropdown();
|
|
1030
|
-
} else if (key === "view_enums_buttonset") {
|
|
1031
|
-
return new Template__view_enums_buttonset();
|
|
1032
|
-
} else {
|
|
1033
|
-
throw new BadRequestException(`잘못된 템플릿 키 ${key}`);
|
|
1034
|
-
}
|
|
435
|
+
).flat();
|
|
1035
436
|
}
|
|
1036
437
|
|
|
1037
|
-
async
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
): Promise<PathAndCode[]> {
|
|
1041
|
-
const template: Template = this.getTemplate(key);
|
|
1042
|
-
|
|
1043
|
-
let extra: unknown[] = [];
|
|
1044
|
-
if (key === "service") {
|
|
1045
|
-
// service 필요 정보 (API 리스트)
|
|
1046
|
-
const { modelTsPath } = options as TemplateOptions["service"];
|
|
1047
|
-
extra = [await this.readApisFromFile(modelTsPath)];
|
|
1048
|
-
} else if (["model", "view_list", "view_form"].includes(key)) {
|
|
1049
|
-
const entityId = (options as TemplateOptions["model"]).entityId;
|
|
1050
|
-
if (key === "view_list" || key === "model") {
|
|
1051
|
-
// view_list 필요 정보 (컬럼 노드, 리스트파라미터 노드)
|
|
1052
|
-
const columnsNode = await this.getColumnsNode(entityId, "A");
|
|
1053
|
-
const listParamsZodType = await this.getZodTypeById(
|
|
1054
|
-
`${entityId}ListParams`
|
|
1055
|
-
);
|
|
1056
|
-
const listParamsNode = this.zodTypeToRenderingNode(listParamsZodType);
|
|
1057
|
-
extra = [columnsNode, listParamsNode];
|
|
1058
|
-
} else if (key === "view_form") {
|
|
1059
|
-
// view_form 필요 정보 (세이브파라미터 노드)
|
|
1060
|
-
const saveParamsZodType = await this.getZodTypeById(
|
|
1061
|
-
`${entityId}SaveParams`
|
|
1062
|
-
);
|
|
1063
|
-
const saveParamsNode = this.zodTypeToRenderingNode(saveParamsZodType);
|
|
1064
|
-
extra = [saveParamsNode];
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
const rendered = await template.render(options, ...extra);
|
|
1069
|
-
const resolved = await this.resolveRenderedTemplate(key, rendered);
|
|
1070
|
-
|
|
1071
|
-
let preTemplateResolved: PathAndCode[] = [];
|
|
1072
|
-
if (rendered.preTemplates) {
|
|
1073
|
-
preTemplateResolved = (
|
|
1074
|
-
await Promise.all(
|
|
1075
|
-
rendered.preTemplates.map(({ key, options }) => {
|
|
1076
|
-
return this.renderTemplate(key, options);
|
|
1077
|
-
})
|
|
1078
|
-
)
|
|
1079
|
-
).flat();
|
|
438
|
+
private async copyFileWithReplaceCoreToShared(fromPath: string, toPath: string) {
|
|
439
|
+
if (!(await exists(fromPath))) {
|
|
440
|
+
return;
|
|
1080
441
|
}
|
|
1081
442
|
|
|
1082
|
-
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
async resolveRenderedTemplate(
|
|
1086
|
-
key: TemplateKey,
|
|
1087
|
-
result: RenderedTemplate
|
|
1088
|
-
): Promise<PathAndCode> {
|
|
1089
|
-
const { target, path: filePath, body, importKeys, customHeaders } = result;
|
|
1090
|
-
|
|
1091
|
-
// import 할 대상의 대상 path 추출
|
|
1092
|
-
const importDefs = importKeys
|
|
1093
|
-
.reduce(
|
|
1094
|
-
(r, importKey) => {
|
|
1095
|
-
const modulePath = EntityManager.getModulePath(importKey);
|
|
1096
|
-
let importPath = modulePath;
|
|
1097
|
-
if (modulePath.includes("/") || modulePath.includes(".")) {
|
|
1098
|
-
importPath = wrapIf(
|
|
1099
|
-
path.relative(path.dirname(filePath), modulePath),
|
|
1100
|
-
(p) => [p.startsWith(".") === false, "./" + p]
|
|
1101
|
-
);
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
// 같은 파일에서 import 하는 경우 keys 로 나열 처리
|
|
1105
|
-
const existsOne = r.find(
|
|
1106
|
-
(importDef) => importDef.from === importPath
|
|
1107
|
-
);
|
|
1108
|
-
if (existsOne) {
|
|
1109
|
-
existsOne.keys = _.uniq(existsOne.keys.concat(importKey));
|
|
1110
|
-
} else {
|
|
1111
|
-
r.push({
|
|
1112
|
-
keys: [importKey],
|
|
1113
|
-
from: importPath,
|
|
1114
|
-
});
|
|
1115
|
-
}
|
|
1116
|
-
return r;
|
|
1117
|
-
},
|
|
1118
|
-
[] as {
|
|
1119
|
-
keys: string[];
|
|
1120
|
-
from: string;
|
|
1121
|
-
}[]
|
|
1122
|
-
)
|
|
1123
|
-
// 셀프 참조 방지
|
|
1124
|
-
.filter(
|
|
1125
|
-
(importDef) =>
|
|
1126
|
-
filePath.endsWith(importDef.from.replace("./", "") + ".ts") === false
|
|
1127
|
-
);
|
|
1128
|
-
|
|
1129
|
-
// 커스텀 헤더 포함하여 헤더 생성
|
|
1130
|
-
const header = [
|
|
1131
|
-
...(customHeaders ?? []),
|
|
1132
|
-
...importDefs.map(
|
|
1133
|
-
(importDef) =>
|
|
1134
|
-
`import { ${importDef.keys.join(", ")} } from '${importDef.from}'`
|
|
1135
|
-
),
|
|
1136
|
-
].join("\n");
|
|
1137
|
-
|
|
1138
|
-
const formatted = await (async () => {
|
|
1139
|
-
if (key === "generated_http") {
|
|
1140
|
-
return [header, body].join("\n\n");
|
|
1141
|
-
} else {
|
|
1142
|
-
return prettier.format([header, body].join("\n\n"), {
|
|
1143
|
-
parser: key === "entity" ? "json" : "typescript",
|
|
1144
|
-
});
|
|
1145
|
-
}
|
|
1146
|
-
})();
|
|
1147
|
-
|
|
1148
|
-
return {
|
|
1149
|
-
path: target + "/" + filePath,
|
|
1150
|
-
code: formatted,
|
|
1151
|
-
};
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
async writeCodeToPath(pathAndCode: PathAndCode): Promise<string[]> {
|
|
1155
|
-
const { targets } = Sonamu.config.sync;
|
|
1156
|
-
const { appRootPath } = Sonamu;
|
|
1157
|
-
const filePath = `${Sonamu.appRootPath}/${pathAndCode.path}`;
|
|
1158
|
-
|
|
1159
|
-
const dstFilePaths = _.uniq(
|
|
1160
|
-
targets.map((target) => filePath.replace("/:target/", `/${target}/`))
|
|
1161
|
-
);
|
|
1162
|
-
return await Promise.all(
|
|
1163
|
-
dstFilePaths.map(async (dstFilePath) => {
|
|
1164
|
-
const dir = path.dirname(dstFilePath);
|
|
1165
|
-
if (!(await exists(dir))) {
|
|
1166
|
-
await mkdir(dir, { recursive: true });
|
|
1167
|
-
}
|
|
1168
|
-
await writeFile(dstFilePath, pathAndCode.code);
|
|
1169
|
-
console.log(
|
|
1170
|
-
chalk.bold("Generated: ") +
|
|
1171
|
-
chalk.blue(`${dstFilePath.replace(appRootPath + "/", "")}`)
|
|
1172
|
-
);
|
|
1173
|
-
return dstFilePath;
|
|
1174
|
-
})
|
|
1175
|
-
);
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
async generateTemplate(
|
|
1179
|
-
key: TemplateKey,
|
|
1180
|
-
templateOptions: any,
|
|
1181
|
-
_generateOptions?: GenerateOptions
|
|
1182
|
-
) {
|
|
1183
|
-
const generateOptions = {
|
|
1184
|
-
overwrite: false,
|
|
1185
|
-
..._generateOptions,
|
|
1186
|
-
};
|
|
1187
|
-
|
|
1188
|
-
// 키 children
|
|
1189
|
-
const keys: TemplateKey[] = [key];
|
|
1190
|
-
|
|
1191
|
-
// 템플릿 렌더
|
|
1192
|
-
const pathAndCodes = (
|
|
1193
|
-
await Promise.all(
|
|
1194
|
-
keys.map(async (key) => {
|
|
1195
|
-
return await this.renderTemplate(key, templateOptions);
|
|
1196
|
-
})
|
|
1197
|
-
)
|
|
1198
|
-
).flat();
|
|
443
|
+
const oldFileContent = (await readFile(fromPath)).toString();
|
|
1199
444
|
|
|
1200
|
-
const
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
} else {
|
|
1204
|
-
return await filterAsync(pathAndCodes, async (pathAndCode) => {
|
|
1205
|
-
const { targets } = Sonamu.config.sync;
|
|
1206
|
-
const filePath = `${Sonamu.appRootPath}/${pathAndCode.path}`;
|
|
1207
|
-
const dstFilePaths = targets.map((target) =>
|
|
1208
|
-
filePath.replace("/:target/", `/${target}/`)
|
|
1209
|
-
);
|
|
1210
|
-
return await everyAsync(
|
|
1211
|
-
dstFilePaths,
|
|
1212
|
-
async (dstPath) => !(await exists(dstPath))
|
|
1213
|
-
);
|
|
1214
|
-
});
|
|
1215
|
-
}
|
|
445
|
+
const newFileContent = (() => {
|
|
446
|
+
const nfc = oldFileContent.replace(/from "sonamu"/g, `from "./sonamu.shared"`);
|
|
447
|
+
return nfc;
|
|
1216
448
|
})();
|
|
1217
|
-
|
|
1218
|
-
throw new AlreadyProcessedException(
|
|
1219
|
-
"이미 경로에 모든 파일이 존재합니다."
|
|
1220
|
-
);
|
|
1221
|
-
}
|
|
1222
|
-
|
|
1223
|
-
return Promise.all(
|
|
1224
|
-
filteredPathAndCodes.map((pathAndCode) =>
|
|
1225
|
-
this.writeCodeToPath(pathAndCode)
|
|
1226
|
-
)
|
|
1227
|
-
);
|
|
449
|
+
return writeFile(toPath, newFileContent);
|
|
1228
450
|
}
|
|
1229
451
|
|
|
452
|
+
/**
|
|
453
|
+
* 주어진 엔티티와 템플릿 키에 대해, 생성된 코드가 존재하는지 확인합니다.
|
|
454
|
+
* @param entityId 엔티티 ID
|
|
455
|
+
* @param templateKey 템플릿 키
|
|
456
|
+
* @param enumId 열거형 ID
|
|
457
|
+
* @returns 생성된 코드가 존재하는지 여부
|
|
458
|
+
*/
|
|
1230
459
|
async checkExistsGenCode(
|
|
1231
460
|
entityId: string,
|
|
1232
461
|
templateKey: TemplateKey,
|
|
1233
|
-
enumId?: string
|
|
462
|
+
enumId?: string,
|
|
1234
463
|
): Promise<{ subPath: string; fullPath: string; isExists: boolean }> {
|
|
1235
|
-
const { target, path: genPath } =
|
|
1236
|
-
|
|
1237
|
-
|
|
464
|
+
const { target, path: genPath } = TemplateManager.get(templateKey).getTargetAndPath(
|
|
465
|
+
EntityManager.getNamesFromId(entityId),
|
|
466
|
+
enumId,
|
|
467
|
+
);
|
|
1238
468
|
|
|
1239
|
-
const fullPath = path.join(Sonamu.appRootPath, target, genPath);
|
|
1240
469
|
const subPath = path.join(target, genPath);
|
|
470
|
+
const fullPath = path.join(Sonamu.appRootPath, subPath);
|
|
1241
471
|
return {
|
|
1242
472
|
subPath,
|
|
1243
473
|
fullPath,
|
|
@@ -1245,30 +475,31 @@ export class Syncer {
|
|
|
1245
475
|
};
|
|
1246
476
|
}
|
|
1247
477
|
|
|
478
|
+
/**
|
|
479
|
+
* 주어진 엔티티와 열거형에 대해, 생성된 코드가 존재하는지 확인합니다.
|
|
480
|
+
* @param entityId 엔티티 ID
|
|
481
|
+
* @param enums 열거형 레이블
|
|
482
|
+
* @returns 생성된 코드가 존재하는지 여부
|
|
483
|
+
*/
|
|
1248
484
|
async checkExists(
|
|
1249
485
|
entityId: string,
|
|
1250
486
|
enums: {
|
|
1251
|
-
[name: string]: z.ZodEnum<
|
|
1252
|
-
}
|
|
487
|
+
[name: string]: z.ZodEnum<Readonly<Record<string, string | number>>>;
|
|
488
|
+
},
|
|
1253
489
|
): Promise<Record<`${TemplateKey}${string}`, boolean>> {
|
|
1254
490
|
const keys: TemplateKey[] = TemplateKey.options;
|
|
1255
491
|
const names = EntityManager.getNamesFromId(entityId);
|
|
1256
|
-
const enumsKeys = Object.keys(enums).filter(
|
|
1257
|
-
(name) => name !== names.constant
|
|
1258
|
-
);
|
|
492
|
+
const enumsKeys = Object.keys(enums).filter((name) => name !== names.constant);
|
|
1259
493
|
|
|
1260
494
|
return await reduceAsync(
|
|
1261
495
|
keys,
|
|
1262
496
|
async (result, key) => {
|
|
1263
|
-
const tpl =
|
|
497
|
+
const tpl = TemplateManager.get(key);
|
|
1264
498
|
if (key.startsWith("view_enums")) {
|
|
1265
499
|
await mapAsync(enumsKeys, async (componentId) => {
|
|
1266
|
-
const { target, path: p } = tpl.getTargetAndPath(
|
|
1267
|
-
names,
|
|
1268
|
-
componentId
|
|
1269
|
-
);
|
|
500
|
+
const { target, path: p } = tpl.getTargetAndPath(names, componentId);
|
|
1270
501
|
result[`${key}__${componentId}`] = await exists(
|
|
1271
|
-
path.join(Sonamu.appRootPath, target, p)
|
|
502
|
+
path.join(Sonamu.appRootPath, target, p),
|
|
1272
503
|
);
|
|
1273
504
|
});
|
|
1274
505
|
return result;
|
|
@@ -1279,7 +510,7 @@ export class Syncer {
|
|
|
1279
510
|
if (target.includes(":target")) {
|
|
1280
511
|
await mapAsync(targets, async (t) => {
|
|
1281
512
|
result[`${key}__${t}`] = await exists(
|
|
1282
|
-
path.join(Sonamu.appRootPath, target.replace(":target", t), p)
|
|
513
|
+
path.join(Sonamu.appRootPath, target.replace(":target", t), p),
|
|
1283
514
|
);
|
|
1284
515
|
});
|
|
1285
516
|
} else {
|
|
@@ -1288,354 +519,59 @@ export class Syncer {
|
|
|
1288
519
|
|
|
1289
520
|
return result;
|
|
1290
521
|
},
|
|
1291
|
-
{} as Record<`${TemplateKey}${string}`, boolean
|
|
1292
|
-
);
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
async getZodTypeById(zodTypeId: string): Promise<z.ZodTypeAny> {
|
|
1296
|
-
const modulePath = EntityManager.getModulePath(zodTypeId);
|
|
1297
|
-
const moduleAbsPath = path.join(
|
|
1298
|
-
Sonamu.apiRootPath,
|
|
1299
|
-
"dist",
|
|
1300
|
-
"application",
|
|
1301
|
-
modulePath + ".js"
|
|
522
|
+
{} as Record<`${TemplateKey}${string}`, boolean>,
|
|
1302
523
|
);
|
|
1303
|
-
const importPath = "./" + path.relative(__dirname, moduleAbsPath);
|
|
1304
|
-
const imported = await import(importPath);
|
|
1305
|
-
|
|
1306
|
-
if (!imported[zodTypeId]) {
|
|
1307
|
-
throw new Error(`존재하지 않는 zodTypeId ${zodTypeId}`);
|
|
1308
|
-
}
|
|
1309
|
-
return imported[zodTypeId].describe(zodTypeId);
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
async propNodeToZodType(propNode: EntityPropNode): Promise<z.ZodTypeAny> {
|
|
1313
|
-
if (propNode.nodeType === "plain") {
|
|
1314
|
-
return this.propToZodType(propNode.prop);
|
|
1315
|
-
} else if (propNode.nodeType === "array") {
|
|
1316
|
-
if (propNode.prop === undefined) {
|
|
1317
|
-
throw new Error();
|
|
1318
|
-
} else if (propNode.children.length > 0) {
|
|
1319
|
-
return (
|
|
1320
|
-
await this.propNodeToZodType({
|
|
1321
|
-
...propNode,
|
|
1322
|
-
nodeType: "object",
|
|
1323
|
-
})
|
|
1324
|
-
).array();
|
|
1325
|
-
} else {
|
|
1326
|
-
const innerType = await this.propToZodType(propNode.prop);
|
|
1327
|
-
if (propNode.prop.nullable === true) {
|
|
1328
|
-
return z.array(innerType).nullable();
|
|
1329
|
-
} else {
|
|
1330
|
-
return z.array(innerType);
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
} else if (propNode.nodeType === "object") {
|
|
1334
|
-
const obj = await propNode.children.reduce(
|
|
1335
|
-
async (promise, childPropNode) => {
|
|
1336
|
-
const result = await promise;
|
|
1337
|
-
result[childPropNode.prop!.name] =
|
|
1338
|
-
await this.propNodeToZodType(childPropNode);
|
|
1339
|
-
return result;
|
|
1340
|
-
},
|
|
1341
|
-
{} as any
|
|
1342
|
-
);
|
|
1343
|
-
|
|
1344
|
-
if (propNode.prop?.nullable === true) {
|
|
1345
|
-
return z.object(obj).nullable();
|
|
1346
|
-
} else {
|
|
1347
|
-
return z.object(obj);
|
|
1348
|
-
}
|
|
1349
|
-
} else {
|
|
1350
|
-
throw Error;
|
|
1351
|
-
}
|
|
1352
524
|
}
|
|
1353
|
-
async propToZodType(prop: EntityProp): Promise<z.ZodTypeAny> {
|
|
1354
|
-
let zodType: z.ZodTypeAny = z.unknown();
|
|
1355
|
-
if (isIntegerProp(prop)) {
|
|
1356
|
-
zodType = z.number().int();
|
|
1357
|
-
} else if (isBigIntegerProp(prop)) {
|
|
1358
|
-
zodType = z.bigint();
|
|
1359
|
-
} else if (isTextProp(prop)) {
|
|
1360
|
-
zodType = z.string().max(getTextTypeLength(prop.textType));
|
|
1361
|
-
} else if (isEnumProp(prop)) {
|
|
1362
|
-
zodType = await this.getZodTypeById(prop.id);
|
|
1363
|
-
} else if (isStringProp(prop)) {
|
|
1364
|
-
zodType = z.string().max(prop.length);
|
|
1365
|
-
} else if (isFloatProp(prop) || isDoubleProp(prop)) {
|
|
1366
|
-
zodType = z.number();
|
|
1367
|
-
} else if (isDecimalProp(prop)) {
|
|
1368
|
-
zodType = z.string();
|
|
1369
|
-
} else if (isBooleanProp(prop)) {
|
|
1370
|
-
zodType = z.boolean();
|
|
1371
|
-
} else if (isDateProp(prop)) {
|
|
1372
|
-
zodType = z.string().length(10);
|
|
1373
|
-
} else if (isTimeProp(prop)) {
|
|
1374
|
-
zodType = z.string().length(8);
|
|
1375
|
-
} else if (isDateTimeProp(prop)) {
|
|
1376
|
-
zodType = z.date();
|
|
1377
|
-
} else if (isTimestampProp(prop)) {
|
|
1378
|
-
zodType = z.date();
|
|
1379
|
-
} else if (isJsonProp(prop)) {
|
|
1380
|
-
zodType = await this.getZodTypeById(prop.id);
|
|
1381
|
-
} else if (isUuidProp(prop)) {
|
|
1382
|
-
zodType = z.uuid();
|
|
1383
|
-
} else if (isVirtualProp(prop)) {
|
|
1384
|
-
zodType = await this.getZodTypeById(prop.id);
|
|
1385
|
-
} else if (isRelationProp(prop)) {
|
|
1386
|
-
if (
|
|
1387
|
-
isBelongsToOneRelationProp(prop) ||
|
|
1388
|
-
(isOneToOneRelationProp(prop) && prop.hasJoinColumn)
|
|
1389
|
-
) {
|
|
1390
|
-
zodType = z.number().int();
|
|
1391
|
-
}
|
|
1392
|
-
} else {
|
|
1393
|
-
throw new Error(`prop을 zodType으로 변환하는데 실패 ${prop}}`);
|
|
1394
|
-
}
|
|
1395
525
|
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
}
|
|
1399
|
-
if (prop.nullable) {
|
|
1400
|
-
zodType = zodType.nullable();
|
|
1401
|
-
}
|
|
1402
|
-
|
|
1403
|
-
return zodType;
|
|
1404
|
-
}
|
|
526
|
+
syncUI() {
|
|
527
|
+
const uiPort = Sonamu.config.ui?.port ?? 57000;
|
|
1405
528
|
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
if (zodType instanceof z.ZodDate) {
|
|
1411
|
-
return "datetime";
|
|
1412
|
-
} else if (zodType instanceof z.ZodString) {
|
|
1413
|
-
if (key.includes("img") || key.includes("image")) {
|
|
1414
|
-
return "string-image";
|
|
1415
|
-
} else if (zodType.description === "SQLDateTimeString") {
|
|
1416
|
-
return "string-datetime";
|
|
1417
|
-
} else if (key.endsWith("date")) {
|
|
1418
|
-
return "string-date";
|
|
1419
|
-
} else {
|
|
1420
|
-
return "string-plain";
|
|
1421
|
-
}
|
|
1422
|
-
} else if (zodType instanceof z.ZodNumber) {
|
|
1423
|
-
if (key === "id") {
|
|
1424
|
-
return "number-id";
|
|
1425
|
-
} else if (key.endsWith("_id")) {
|
|
1426
|
-
return "number-fk_id";
|
|
1427
|
-
} else {
|
|
1428
|
-
return "number-plain";
|
|
1429
|
-
}
|
|
1430
|
-
} else if (zodType instanceof z.ZodBoolean) {
|
|
1431
|
-
return "boolean";
|
|
1432
|
-
} else if (zodType instanceof z.ZodEnum) {
|
|
1433
|
-
return "enums";
|
|
1434
|
-
} else if (zodType instanceof z.ZodRecord) {
|
|
1435
|
-
return "record";
|
|
1436
|
-
} else if (zodType instanceof z.ZodAny || zodType instanceof z.ZodUnknown) {
|
|
1437
|
-
return "string-plain";
|
|
1438
|
-
} else if (zodType instanceof z.ZodUnion) {
|
|
1439
|
-
return "string-plain";
|
|
1440
|
-
} else if (zodType instanceof z.ZodLiteral) {
|
|
1441
|
-
return "string-plain";
|
|
1442
|
-
} else {
|
|
1443
|
-
throw new Error(`타입 파싱 불가 ${key} ${zodType.def.type}`);
|
|
529
|
+
if (!isTest()) {
|
|
530
|
+
fetch(`http://127.0.0.1:${uiPort}/api/reload`, {
|
|
531
|
+
method: "GET",
|
|
532
|
+
}).catch((e) => console.log(chalk.dim(`Failed to reload Sonamu UI: ${e.message}`)));
|
|
1444
533
|
}
|
|
1445
534
|
}
|
|
1446
535
|
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
name: baseKey,
|
|
1453
|
-
label: inflection.camelize(baseKey, false),
|
|
1454
|
-
zodType,
|
|
1455
|
-
};
|
|
1456
|
-
if (zodType instanceof z.ZodObject) {
|
|
1457
|
-
const columnKeys = Object.keys(zodType.shape);
|
|
1458
|
-
const children = columnKeys.map((key) => {
|
|
1459
|
-
const innerType = zodType.shape[key];
|
|
1460
|
-
return this.zodTypeToRenderingNode(innerType, key);
|
|
1461
|
-
});
|
|
1462
|
-
return {
|
|
1463
|
-
...def,
|
|
1464
|
-
renderType: "object",
|
|
1465
|
-
children,
|
|
1466
|
-
};
|
|
1467
|
-
} else if (zodType instanceof z.ZodArray) {
|
|
1468
|
-
const innerType = (zodType as z.ZodArray<z.ZodType<any>>).def.element;
|
|
1469
|
-
if (innerType instanceof z.ZodString && baseKey.includes("images")) {
|
|
1470
|
-
return {
|
|
1471
|
-
...def,
|
|
1472
|
-
renderType: "array-images",
|
|
1473
|
-
};
|
|
1474
|
-
}
|
|
1475
|
-
return {
|
|
1476
|
-
...def,
|
|
1477
|
-
renderType: "array",
|
|
1478
|
-
element: this.zodTypeToRenderingNode(innerType, baseKey),
|
|
1479
|
-
};
|
|
1480
|
-
} else if (zodType instanceof z.ZodUnion) {
|
|
1481
|
-
const optionNodes = (zodType as z.ZodUnion<z.ZodType[]>).def.options.map((opt) =>
|
|
1482
|
-
this.zodTypeToRenderingNode(opt, baseKey)
|
|
1483
|
-
);
|
|
1484
|
-
// TODO: ZodUnion이 들어있는 경우 핸들링
|
|
1485
|
-
return optionNodes[0];
|
|
1486
|
-
} else if (zodType instanceof z.ZodOptional) {
|
|
1487
|
-
return {
|
|
1488
|
-
...this.zodTypeToRenderingNode((zodType as z.ZodOptional<z.ZodType>).def.innerType, baseKey),
|
|
1489
|
-
optional: true,
|
|
1490
|
-
};
|
|
1491
|
-
} else if (zodType instanceof z.ZodNullable) {
|
|
1492
|
-
return {
|
|
1493
|
-
...this.zodTypeToRenderingNode((zodType as z.ZodNullable<z.ZodType>).def.innerType, baseKey),
|
|
1494
|
-
nullable: true,
|
|
1495
|
-
};
|
|
1496
|
-
} else {
|
|
1497
|
-
return {
|
|
1498
|
-
...def,
|
|
1499
|
-
renderType: this.resolveRenderType(baseKey, zodType),
|
|
1500
|
-
};
|
|
1501
|
-
}
|
|
536
|
+
/**
|
|
537
|
+
* 하위호환용 프록시 메소드입니다.
|
|
538
|
+
*/
|
|
539
|
+
async createEntity(form: TemplateOptions["entity"]) {
|
|
540
|
+
return await createEntity(form);
|
|
1502
541
|
}
|
|
1503
542
|
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
): Promise<
|
|
1508
|
-
|
|
1509
|
-
const subsetA = entity.subsets[subsetKey];
|
|
1510
|
-
if (subsetA === undefined) {
|
|
1511
|
-
throw new ServiceUnavailableException("SubsetA 가 없습니다.");
|
|
1512
|
-
}
|
|
1513
|
-
const propNodes = entity.fieldExprsToPropNodes(subsetA);
|
|
1514
|
-
const rootPropNode: EntityPropNode = {
|
|
1515
|
-
nodeType: "object",
|
|
1516
|
-
children: propNodes,
|
|
1517
|
-
};
|
|
1518
|
-
|
|
1519
|
-
const columnsZodType = (await this.propNodeToZodType(
|
|
1520
|
-
rootPropNode
|
|
1521
|
-
)) as z.ZodObject<any>;
|
|
1522
|
-
|
|
1523
|
-
const columnsNode = this.zodTypeToRenderingNode(columnsZodType);
|
|
1524
|
-
columnsNode.children = columnsNode.children!.map((child) => {
|
|
1525
|
-
if (child.renderType === "object") {
|
|
1526
|
-
const pickedCol = child.children!.find((cc) =>
|
|
1527
|
-
["title", "name"].includes(cc.name)
|
|
1528
|
-
);
|
|
1529
|
-
if (pickedCol) {
|
|
1530
|
-
return {
|
|
1531
|
-
...child,
|
|
1532
|
-
renderType: "object-pick",
|
|
1533
|
-
config: {
|
|
1534
|
-
picked: pickedCol.name,
|
|
1535
|
-
},
|
|
1536
|
-
};
|
|
1537
|
-
} else {
|
|
1538
|
-
return child;
|
|
1539
|
-
}
|
|
1540
|
-
} else if (
|
|
1541
|
-
child.renderType === "array" &&
|
|
1542
|
-
child.element &&
|
|
1543
|
-
child.element.renderType === "object"
|
|
1544
|
-
) {
|
|
1545
|
-
const pickedCol = child.element!.children!.find((cc) =>
|
|
1546
|
-
["title", "name"].includes(cc.name)
|
|
1547
|
-
);
|
|
1548
|
-
if (pickedCol) {
|
|
1549
|
-
return {
|
|
1550
|
-
...child,
|
|
1551
|
-
element: {
|
|
1552
|
-
...child.element,
|
|
1553
|
-
renderType: "object-pick",
|
|
1554
|
-
config: {
|
|
1555
|
-
picked: pickedCol.name,
|
|
1556
|
-
},
|
|
1557
|
-
},
|
|
1558
|
-
};
|
|
1559
|
-
} else {
|
|
1560
|
-
return child;
|
|
1561
|
-
}
|
|
1562
|
-
}
|
|
1563
|
-
return child;
|
|
1564
|
-
});
|
|
1565
|
-
|
|
1566
|
-
return columnsNode;
|
|
543
|
+
/**
|
|
544
|
+
* 하위호환용 프록시 메소드입니다.
|
|
545
|
+
*/
|
|
546
|
+
async delEntity(entityId: string): Promise<{ delPaths: string[] }> {
|
|
547
|
+
return await delEntity(entityId);
|
|
1567
548
|
}
|
|
1568
549
|
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
// reload entities
|
|
1579
|
-
await EntityManager.reload();
|
|
1580
|
-
|
|
1581
|
-
// syncFromWatcher에서 처리하므로 주석처리
|
|
1582
|
-
// this.actionGenerateSchemas();
|
|
1583
|
-
|
|
1584
|
-
// // generate schemas, types
|
|
1585
|
-
// await Promise.all([
|
|
1586
|
-
// ...(form.parentId === undefined
|
|
1587
|
-
// ? [
|
|
1588
|
-
// this.generateTemplate("init_types", {
|
|
1589
|
-
// entityId: form.entityId,
|
|
1590
|
-
// }),
|
|
1591
|
-
// ]
|
|
1592
|
-
// : []),
|
|
1593
|
-
// ]);
|
|
550
|
+
/**
|
|
551
|
+
* 하위호환용 프록시 메소드입니다.
|
|
552
|
+
*/
|
|
553
|
+
async generateTemplate<T extends TemplateKey>(
|
|
554
|
+
key: T,
|
|
555
|
+
templateOptions: TemplateOptions[T],
|
|
556
|
+
_generateOptions?: GenerateOptions,
|
|
557
|
+
): Promise<AbsolutePath[]> {
|
|
558
|
+
return await generateTemplate(key, templateOptions, _generateOptions);
|
|
1594
559
|
}
|
|
1595
560
|
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
} else {
|
|
1605
|
-
return [
|
|
1606
|
-
`${Sonamu.apiRootPath}/src/application/${entity.names.fs}`,
|
|
1607
|
-
`${Sonamu.apiRootPath}/dist/application/${entity.names.fs}`,
|
|
1608
|
-
...Sonamu.config.sync.targets
|
|
1609
|
-
.map((target) => [
|
|
1610
|
-
`${Sonamu.appRootPath}/${target}/src/services/${entity.names.fs}`,
|
|
1611
|
-
])
|
|
1612
|
-
.flat(),
|
|
1613
|
-
];
|
|
1614
|
-
}
|
|
1615
|
-
})(); // iife
|
|
1616
|
-
|
|
1617
|
-
for await (const delPath of delPaths) {
|
|
1618
|
-
if (await exists(delPath)) {
|
|
1619
|
-
console.log(chalk.red(`DELETE ${delPath}`));
|
|
1620
|
-
await rm(delPath, { recursive: true, force: true });
|
|
1621
|
-
} else {
|
|
1622
|
-
console.log(chalk.yellow(`NOT_EXISTS ${delPath}`));
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
// reload entities
|
|
1627
|
-
await EntityManager.reload();
|
|
1628
|
-
|
|
1629
|
-
return { delPaths };
|
|
561
|
+
/**
|
|
562
|
+
* 하위호환용 프록시 메소드입니다.
|
|
563
|
+
*/
|
|
564
|
+
async renderTemplate<T extends keyof TemplateOptions>(
|
|
565
|
+
key: T,
|
|
566
|
+
templateOptions: TemplateOptions[T],
|
|
567
|
+
): Promise<PathAndCode[]> {
|
|
568
|
+
return await renderTemplate(key, templateOptions);
|
|
1630
569
|
}
|
|
1631
570
|
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
}).catch((e) =>
|
|
1638
|
-
console.log(chalk.dim(`Failed to reload Sonamu UI: ${e.message}`))
|
|
1639
|
-
);
|
|
571
|
+
/**
|
|
572
|
+
* 하위호환용 프록시 메소드입니다.
|
|
573
|
+
*/
|
|
574
|
+
async renewChecksums(): Promise<void> {
|
|
575
|
+
return await renewChecksums();
|
|
1640
576
|
}
|
|
1641
577
|
}
|