sonamu 0.1.2 → 0.1.5
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/dist/entity/entity-manager.d.ts.map +1 -1
- package/dist/entity/entity-manager.js +1 -0
- package/dist/entity/entity-manager.js.map +1 -1
- package/dist/entity/entity-utils.d.ts.map +1 -1
- package/dist/entity/entity-utils.js +1 -1
- package/dist/entity/entity-utils.js.map +1 -1
- package/dist/entity/entity.d.ts +4 -0
- package/dist/entity/entity.d.ts.map +1 -1
- package/dist/entity/entity.js +74 -22
- package/dist/entity/entity.js.map +1 -1
- package/dist/entity/migrator.d.ts.map +1 -1
- package/dist/entity/migrator.js +43 -12
- package/dist/entity/migrator.js.map +1 -1
- package/dist/syncer/syncer.d.ts +6 -1
- package/dist/syncer/syncer.d.ts.map +1 -1
- package/dist/syncer/syncer.js +62 -41
- package/dist/syncer/syncer.js.map +1 -1
- package/dist/templates/entity.template.d.ts +1 -1
- package/dist/templates/entity.template.d.ts.map +1 -1
- package/dist/templates/entity.template.js +43 -13
- package/dist/templates/entity.template.js.map +1 -1
- package/dist/templates/generated.template.d.ts.map +1 -1
- package/dist/templates/generated.template.js +18 -0
- package/dist/templates/generated.template.js.map +1 -1
- package/dist/templates/service.template.d.ts.map +1 -1
- package/dist/templates/service.template.js +4 -4
- package/dist/templates/service.template.js.map +1 -1
- package/dist/templates/view_enums_dropdown.template.d.ts +2 -2
- package/dist/templates/view_enums_dropdown.template.d.ts.map +1 -1
- package/dist/templates/view_enums_dropdown.template.js +14 -13
- package/dist/templates/view_enums_dropdown.template.js.map +1 -1
- package/dist/templates/view_enums_select.template.d.ts +1 -1
- package/dist/templates/view_enums_select.template.d.ts.map +1 -1
- package/dist/templates/view_enums_select.template.js +4 -4
- package/dist/templates/view_enums_select.template.js.map +1 -1
- package/dist/templates/view_form.template.d.ts +2 -5
- package/dist/templates/view_form.template.d.ts.map +1 -1
- package/dist/templates/view_form.template.js +14 -8
- package/dist/templates/view_form.template.js.map +1 -1
- package/dist/templates/view_list.template.d.ts +2 -5
- package/dist/templates/view_list.template.d.ts.map +1 -1
- package/dist/templates/view_list.template.js +17 -20
- package/dist/templates/view_list.template.js.map +1 -1
- package/dist/types/types.d.ts +15 -30
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/types.js +0 -3
- package/dist/types/types.js.map +1 -1
- package/dist/utils/utils.d.ts +1 -1
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +4 -1
- package/dist/utils/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/entity/entity-manager.ts +1 -0
- package/src/entity/entity-utils.ts +2 -0
- package/src/entity/entity.ts +101 -32
- package/src/entity/migrator.ts +54 -13
- package/src/shared/web.shared.ts.txt +21 -9
- package/src/syncer/syncer.ts +88 -45
- package/src/templates/entity.template.ts +42 -9
- package/src/templates/generated.template.ts +27 -2
- package/src/templates/service.template.ts +6 -4
- package/src/templates/view_enums_dropdown.template.ts +12 -17
- package/src/templates/view_enums_select.template.ts +4 -8
- package/src/templates/view_form.template.ts +16 -10
- package/src/templates/view_list.template.ts +16 -23
- package/src/types/types.ts +4 -6
- package/src/utils/utils.ts +5 -1
package/src/entity/entity.ts
CHANGED
|
@@ -24,6 +24,7 @@ import { existsSync, writeFileSync } from "fs";
|
|
|
24
24
|
import { z } from "zod";
|
|
25
25
|
import { Sonamu } from "../api/sonamu";
|
|
26
26
|
import prettier from "prettier";
|
|
27
|
+
import { nonNullable } from "../utils/utils";
|
|
27
28
|
|
|
28
29
|
export class Entity {
|
|
29
30
|
id: string;
|
|
@@ -414,9 +415,10 @@ export class Entity {
|
|
|
414
415
|
};
|
|
415
416
|
}
|
|
416
417
|
|
|
417
|
-
const prop = entity.
|
|
418
|
+
const prop = entity.props.find((p) => p.name === propName);
|
|
418
419
|
if (prop === undefined) {
|
|
419
|
-
|
|
420
|
+
console.log({ propName, groups });
|
|
421
|
+
throw new Error(`${entity.id} -- 잘못된 FieldExpr ${propName}`);
|
|
420
422
|
}
|
|
421
423
|
return {
|
|
422
424
|
nodeType: "plain" as const,
|
|
@@ -501,7 +503,21 @@ export class Entity {
|
|
|
501
503
|
}
|
|
502
504
|
|
|
503
505
|
getTableColumns(): string[] {
|
|
504
|
-
return this.props
|
|
506
|
+
return this.props
|
|
507
|
+
.map((prop) => {
|
|
508
|
+
if (prop.type === "relation") {
|
|
509
|
+
if (
|
|
510
|
+
prop.relationType === "BelongsToOne" ||
|
|
511
|
+
(prop.relationType === "OneToOne" && prop.hasJoinColumn === true)
|
|
512
|
+
) {
|
|
513
|
+
return `${prop.name}_id`;
|
|
514
|
+
} else {
|
|
515
|
+
return null;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return prop.name;
|
|
519
|
+
})
|
|
520
|
+
.filter(nonNullable);
|
|
505
521
|
}
|
|
506
522
|
|
|
507
523
|
registerModulePaths() {
|
|
@@ -673,10 +689,40 @@ export class Entity {
|
|
|
673
689
|
await this.save();
|
|
674
690
|
}
|
|
675
691
|
|
|
692
|
+
analyzeSubsetField(subsetField: string): {
|
|
693
|
+
entityId: string;
|
|
694
|
+
propName: string;
|
|
695
|
+
}[] {
|
|
696
|
+
const arr = subsetField.split(".");
|
|
697
|
+
|
|
698
|
+
let entityId = this.id;
|
|
699
|
+
const result: {
|
|
700
|
+
entityId: string;
|
|
701
|
+
propName: string;
|
|
702
|
+
}[] = [];
|
|
703
|
+
for (let i = 0; i < arr.length; i++) {
|
|
704
|
+
const propName = arr[i];
|
|
705
|
+
result.push({
|
|
706
|
+
entityId,
|
|
707
|
+
propName,
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
const prop = EntityManager.get(entityId).props.find(
|
|
711
|
+
(p) => p.name === propName
|
|
712
|
+
);
|
|
713
|
+
if (!prop) {
|
|
714
|
+
throw new Error(`${entityId}의 잘못된 서브셋키 ${subsetField}`);
|
|
715
|
+
}
|
|
716
|
+
if (isRelationProp(prop)) {
|
|
717
|
+
entityId = prop.with;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return result;
|
|
721
|
+
}
|
|
722
|
+
|
|
676
723
|
async modifyProp(newProp: EntityProp, at: number): Promise<void> {
|
|
677
|
-
// 프롭
|
|
724
|
+
// 이전 프롭 이름 저장
|
|
678
725
|
const oldName = this.props[at].name;
|
|
679
|
-
this.props[at] = newProp;
|
|
680
726
|
|
|
681
727
|
// 저장할 엔티티
|
|
682
728
|
const entities: Entity[] = [this];
|
|
@@ -690,30 +736,39 @@ export class Entity {
|
|
|
690
736
|
const relEntitySubsetKeys = Object.keys(relEntity.subsets);
|
|
691
737
|
for (const subsetKey of relEntitySubsetKeys) {
|
|
692
738
|
const subset = relEntity.subsets[subsetKey];
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
739
|
+
|
|
740
|
+
// 서브셋 필드를 순회하며, 엔티티-프롭 단위로 분석한 후 현재 엔티티-프롭과 일치하는 경우 수정 처리
|
|
741
|
+
const modifiedSubsetFields = subset.map((subsetField) => {
|
|
742
|
+
const analyzed = relEntity.analyzeSubsetField(subsetField);
|
|
743
|
+
const modified = analyzed.map((a) =>
|
|
744
|
+
a.propName === oldName && a.entityId === this.id
|
|
745
|
+
? {
|
|
746
|
+
...a,
|
|
747
|
+
propName: newProp.name,
|
|
748
|
+
}
|
|
749
|
+
: a
|
|
704
750
|
);
|
|
751
|
+
// 분석한 필드를 다시 서브셋 필드로 복구
|
|
752
|
+
return modified.map((a) => a.propName).join(".");
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
if (subset.join(",") !== modifiedSubsetFields.join(",")) {
|
|
756
|
+
relEntity.subsets[subsetKey] = modifiedSubsetFields;
|
|
705
757
|
entities.push(relEntity);
|
|
706
758
|
}
|
|
707
759
|
}
|
|
708
760
|
}
|
|
709
761
|
}
|
|
710
762
|
|
|
763
|
+
// 프롭 수정
|
|
764
|
+
this.props[at] = newProp;
|
|
765
|
+
|
|
711
766
|
await Promise.all(entities.map(async (entity) => entity.save()));
|
|
712
767
|
}
|
|
713
768
|
|
|
714
769
|
async delProp(at: number): Promise<void> {
|
|
770
|
+
// 이전 프롭 이름 저장
|
|
715
771
|
const oldName = this.props[at].name;
|
|
716
|
-
this.props.splice(at, 1);
|
|
717
772
|
|
|
718
773
|
// 저장할 엔티티
|
|
719
774
|
const entities: Entity[] = [this];
|
|
@@ -725,20 +780,32 @@ export class Entity {
|
|
|
725
780
|
const relEntitySubsetKeys = Object.keys(relEntity.subsets);
|
|
726
781
|
for (const subsetKey of relEntitySubsetKeys) {
|
|
727
782
|
const subset = relEntity.subsets[subsetKey];
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
relEntity.
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
783
|
+
// 서브셋 필드를 순회하며, 엔티티-프롭 단위로 분석한 후 현재 엔티티-프롭과 일치하는 경우 이후의 필드를 제외
|
|
784
|
+
const modifiedSubsetFields = subset
|
|
785
|
+
.map((subsetField) => {
|
|
786
|
+
const analyzed = relEntity.analyzeSubsetField(subsetField);
|
|
787
|
+
if (
|
|
788
|
+
analyzed.find(
|
|
789
|
+
(a) => a.propName === oldName && a.entityId === this.id
|
|
790
|
+
)
|
|
791
|
+
) {
|
|
792
|
+
return null;
|
|
793
|
+
} else {
|
|
794
|
+
return subsetField;
|
|
795
|
+
}
|
|
796
|
+
})
|
|
797
|
+
.filter(nonNullable);
|
|
798
|
+
|
|
799
|
+
if (subset.join(",") !== modifiedSubsetFields.join(",")) {
|
|
800
|
+
relEntity.subsets[subsetKey] = modifiedSubsetFields;
|
|
737
801
|
entities.push(relEntity);
|
|
738
802
|
}
|
|
739
803
|
}
|
|
740
804
|
}
|
|
741
805
|
|
|
806
|
+
// 프롭 삭제
|
|
807
|
+
this.props.splice(at, 1);
|
|
808
|
+
|
|
742
809
|
await Promise.all(entities.map(async (entity) => entity.save()));
|
|
743
810
|
}
|
|
744
811
|
|
|
@@ -751,15 +818,17 @@ export class Entity {
|
|
|
751
818
|
const arr = subsetField.split(".").slice(0, -1);
|
|
752
819
|
|
|
753
820
|
// 서브셋 필드를 내려가면서 마지막으로 relation된 엔티티를 찾음
|
|
754
|
-
const
|
|
755
|
-
const relProp =
|
|
821
|
+
const lastEntityId = arr.reduce((entityId, field) => {
|
|
822
|
+
const relProp = EntityManager.get(entityId).props.find(
|
|
823
|
+
(p) => p.name === field
|
|
824
|
+
);
|
|
756
825
|
if (!relProp || relProp.type !== "relation") {
|
|
757
|
-
console.debug({ arr, thisId: this.id });
|
|
826
|
+
console.debug({ arr, thisId: this.id, entityId, field });
|
|
758
827
|
throw new Error(`잘못된 서브셋키 ${subsetField}`);
|
|
759
828
|
}
|
|
760
|
-
return
|
|
761
|
-
}, this
|
|
762
|
-
return
|
|
829
|
+
return relProp.with;
|
|
830
|
+
}, this.id);
|
|
831
|
+
return lastEntityId;
|
|
763
832
|
}
|
|
764
833
|
|
|
765
834
|
async moveProp(at: number, to: number): Promise<void> {
|
package/src/entity/migrator.ts
CHANGED
|
@@ -137,6 +137,17 @@ export class Migrator {
|
|
|
137
137
|
}> {
|
|
138
138
|
const srcMigrationsDir = `${Sonamu.apiRootPath}/src/migrations`;
|
|
139
139
|
const distMigrationsDir = `${Sonamu.apiRootPath}/dist/migrations`;
|
|
140
|
+
|
|
141
|
+
if (existsSync(srcMigrationsDir) === false) {
|
|
142
|
+
mkdirSync(srcMigrationsDir, {
|
|
143
|
+
recursive: true,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
if (existsSync(distMigrationsDir) === false) {
|
|
147
|
+
mkdirSync(distMigrationsDir, {
|
|
148
|
+
recursive: true,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
140
151
|
const srcMigrations = readdirSync(srcMigrationsDir)
|
|
141
152
|
.filter((f) => f.endsWith(".ts"))
|
|
142
153
|
.map((f) => f.split(".")[0]);
|
|
@@ -203,9 +214,10 @@ export class Migrator {
|
|
|
203
214
|
connKeys.map(async (connKey) => {
|
|
204
215
|
const knexOptions = Sonamu.dbConfig[connKey];
|
|
205
216
|
const tConn = knex(knexOptions);
|
|
217
|
+
|
|
206
218
|
const status = await (async () => {
|
|
207
219
|
try {
|
|
208
|
-
return tConn.migrate.status();
|
|
220
|
+
return await tConn.migrate.status();
|
|
209
221
|
} catch (err) {
|
|
210
222
|
return "error";
|
|
211
223
|
}
|
|
@@ -874,12 +886,14 @@ export class Migrator {
|
|
|
874
886
|
name: dbColumn.Field,
|
|
875
887
|
nullable: dbColumn.Null !== "NO",
|
|
876
888
|
...dbColType,
|
|
877
|
-
...
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
889
|
+
...(() => {
|
|
890
|
+
if (dbColumn.Default !== null) {
|
|
891
|
+
return {
|
|
892
|
+
defaultTo: dbColumn.Default,
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
return {};
|
|
896
|
+
})(),
|
|
883
897
|
};
|
|
884
898
|
});
|
|
885
899
|
|
|
@@ -1020,7 +1034,23 @@ export class Migrator {
|
|
|
1020
1034
|
): Promise<[DBColumn[], DBIndex[], DBForeign[]]> {
|
|
1021
1035
|
// 테이블 정보
|
|
1022
1036
|
try {
|
|
1023
|
-
const [
|
|
1037
|
+
const [_cols] = (await compareDB.raw(
|
|
1038
|
+
`SHOW FIELDS FROM ${tableName}`
|
|
1039
|
+
)) as [DBColumn[]];
|
|
1040
|
+
const cols = _cols.map((col) => ({
|
|
1041
|
+
...col,
|
|
1042
|
+
// Default 값은 숫자나 MySQL Expression이 아닌 경우 ""로 감싸줌
|
|
1043
|
+
...(col.Default !== null
|
|
1044
|
+
? {
|
|
1045
|
+
Default:
|
|
1046
|
+
col.Default.replace(/[0-9]+/g, "").length > 0 &&
|
|
1047
|
+
col.Extra !== "DEFAULT_GENERATED"
|
|
1048
|
+
? `"${col.Default}"`
|
|
1049
|
+
: col.Default,
|
|
1050
|
+
}
|
|
1051
|
+
: {}),
|
|
1052
|
+
}));
|
|
1053
|
+
|
|
1024
1054
|
const [indexes] = await compareDB.raw(`SHOW INDEX FROM ${tableName}`);
|
|
1025
1055
|
const [[row]] = await compareDB.raw(`SHOW CREATE TABLE ${tableName}`);
|
|
1026
1056
|
const ddl = row["Create Table"];
|
|
@@ -1115,10 +1145,14 @@ export class Migrator {
|
|
|
1115
1145
|
? { length: prop.length }
|
|
1116
1146
|
: {}),
|
|
1117
1147
|
nullable: prop.nullable === true,
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1148
|
+
...(() => {
|
|
1149
|
+
if (prop.dbDefault !== undefined) {
|
|
1150
|
+
return {
|
|
1151
|
+
defaultTo: prop.dbDefault,
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
return {};
|
|
1155
|
+
})(),
|
|
1122
1156
|
// Decimal, Float 타입의 경우 precision, scale 추가
|
|
1123
1157
|
...(isDecimalProp(prop) || isFloatProp(prop)
|
|
1124
1158
|
? {
|
|
@@ -1286,7 +1320,14 @@ export class Migrator {
|
|
|
1286
1320
|
|
|
1287
1321
|
// defaultTo
|
|
1288
1322
|
if (column.defaultTo !== undefined) {
|
|
1289
|
-
|
|
1323
|
+
if (
|
|
1324
|
+
typeof column.defaultTo === "string" &&
|
|
1325
|
+
column.defaultTo.startsWith(`"`)
|
|
1326
|
+
) {
|
|
1327
|
+
chains.push(`defaultTo(${column.defaultTo})`);
|
|
1328
|
+
} else {
|
|
1329
|
+
chains.push(`defaultTo(knex.raw('${column.defaultTo}'))`);
|
|
1330
|
+
}
|
|
1290
1331
|
}
|
|
1291
1332
|
|
|
1292
1333
|
return `table.${chains.join(".")};`;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import type { AxiosRequestConfig } from "axios";
|
|
5
5
|
import axios from "axios";
|
|
6
6
|
import { z, ZodIssue } from "zod";
|
|
7
|
+
import qs from 'qs';
|
|
7
8
|
|
|
8
9
|
export async function fetch(options: AxiosRequestConfig) {
|
|
9
10
|
try {
|
|
@@ -71,12 +72,23 @@ export type SWRError = {
|
|
|
71
72
|
message: string;
|
|
72
73
|
statusCode: number;
|
|
73
74
|
};
|
|
74
|
-
export async function swrFetcher(
|
|
75
|
-
url: string,
|
|
76
|
-
params: string = ""
|
|
77
|
-
): Promise<any> {
|
|
75
|
+
export async function swrFetcher(args: [string, object]): Promise<any> {
|
|
78
76
|
try {
|
|
79
|
-
const
|
|
77
|
+
const [url, params] = args;
|
|
78
|
+
const res = await axios.get(`${url}?${qs.stringify(params)}`);
|
|
79
|
+
return res.data;
|
|
80
|
+
} catch (e: any) {
|
|
81
|
+
const error: any = new Error(
|
|
82
|
+
e.response.data.message ?? e.response.message ?? "Unknown"
|
|
83
|
+
);
|
|
84
|
+
error.statusCode = e.response?.data.statusCode ?? e.response.status;
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export async function swrPostFetcher(args: [string, object]): Promise<any> {
|
|
89
|
+
try {
|
|
90
|
+
const [url, params] = args;
|
|
91
|
+
const res = await axios.post(url, params);
|
|
80
92
|
return res.data;
|
|
81
93
|
} catch (e: any) {
|
|
82
94
|
const error: any = new Error(
|
|
@@ -87,13 +99,13 @@ export async function swrFetcher(
|
|
|
87
99
|
}
|
|
88
100
|
}
|
|
89
101
|
export function handleConditional(
|
|
90
|
-
|
|
102
|
+
args: [string, object],
|
|
91
103
|
conditional?: () => boolean
|
|
92
|
-
): string
|
|
104
|
+
): [string, object] | null {
|
|
93
105
|
if (conditional) {
|
|
94
|
-
return conditional() ?
|
|
106
|
+
return conditional() ? args : null;
|
|
95
107
|
}
|
|
96
|
-
return
|
|
108
|
+
return args;
|
|
97
109
|
}
|
|
98
110
|
|
|
99
111
|
/*
|
package/src/syncer/syncer.ts
CHANGED
|
@@ -159,7 +159,7 @@ export class Syncer {
|
|
|
159
159
|
);
|
|
160
160
|
|
|
161
161
|
// 현재 checksums
|
|
162
|
-
|
|
162
|
+
let currentChecksums = await this.getCurrentChecksums();
|
|
163
163
|
// 이전 checksums
|
|
164
164
|
const previousChecksums = await this.getPreviousChecksums();
|
|
165
165
|
|
|
@@ -190,12 +190,26 @@ export class Syncer {
|
|
|
190
190
|
// 변경된 파일들을 타입별로 분리하여 각 타입별 액션 처리
|
|
191
191
|
const diffTypes = Object.keys(diffGroups);
|
|
192
192
|
|
|
193
|
-
// 트리거: entity
|
|
193
|
+
// 트리거: entity, types
|
|
194
194
|
// 액션: 스키마 생성
|
|
195
|
-
if (diffTypes.includes("entity")) {
|
|
195
|
+
if (diffTypes.includes("entity") || diffTypes.includes("types")) {
|
|
196
196
|
console.log("// 액션: 스키마 생성");
|
|
197
|
-
const entityIds = this.getEntityIdFromPath(
|
|
197
|
+
const entityIds = this.getEntityIdFromPath([
|
|
198
|
+
...(diffGroups["entity"] ?? []),
|
|
199
|
+
...(diffGroups["types"] ?? []),
|
|
200
|
+
]);
|
|
198
201
|
await this.actionGenerateSchemas(entityIds);
|
|
202
|
+
|
|
203
|
+
// 타입이 변경된 경우 generated 싱크까지 동시에 처리 후 체크섬 갱신
|
|
204
|
+
if (diffTypes.includes("types")) {
|
|
205
|
+
diffGroups["generated"] = uniq([
|
|
206
|
+
...(diffGroups["generated"] ?? []),
|
|
207
|
+
...diffGroups["types"].map((p) =>
|
|
208
|
+
p.replace(".types.ts", ".generated.ts")
|
|
209
|
+
),
|
|
210
|
+
]);
|
|
211
|
+
currentChecksums = await this.getCurrentChecksums();
|
|
212
|
+
}
|
|
199
213
|
}
|
|
200
214
|
|
|
201
215
|
// 트리거: types, enums, generated 변경시
|
|
@@ -207,11 +221,13 @@ export class Syncer {
|
|
|
207
221
|
) {
|
|
208
222
|
console.log("// 액션: 파일 싱크 types / functions / generated");
|
|
209
223
|
|
|
210
|
-
const tsPaths =
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
224
|
+
const tsPaths = uniq(
|
|
225
|
+
[
|
|
226
|
+
...(diffGroups["types"] ?? []),
|
|
227
|
+
...(diffGroups["functions"] ?? []),
|
|
228
|
+
...(diffGroups["generated"] ?? []),
|
|
229
|
+
].map((p) => p.replace("/dist/", "/src/").replace(".js", ".ts"))
|
|
230
|
+
);
|
|
215
231
|
await this.actionSyncFilesToTargets(tsPaths);
|
|
216
232
|
}
|
|
217
233
|
|
|
@@ -229,10 +245,12 @@ export class Syncer {
|
|
|
229
245
|
}
|
|
230
246
|
|
|
231
247
|
getEntityIdFromPath(filePaths: string[]): string[] {
|
|
232
|
-
return
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
248
|
+
return uniq(
|
|
249
|
+
filePaths.map((p) => {
|
|
250
|
+
const matched = p.match(/application\/(.+)\//);
|
|
251
|
+
return camelize(matched![1].replace(/\-/g, "_"));
|
|
252
|
+
})
|
|
253
|
+
);
|
|
236
254
|
}
|
|
237
255
|
|
|
238
256
|
async actionGenerateSchemas(entityIds: string[]): Promise<string[]> {
|
|
@@ -702,8 +720,10 @@ export class Syncer {
|
|
|
702
720
|
return this.models;
|
|
703
721
|
}
|
|
704
722
|
|
|
705
|
-
async autoloadTypes(
|
|
706
|
-
|
|
723
|
+
async autoloadTypes(
|
|
724
|
+
doRefresh: boolean = false
|
|
725
|
+
): Promise<{ [typeName: string]: z.ZodObject<any> }> {
|
|
726
|
+
if (!doRefresh && Object.keys(this.types).length > 0) {
|
|
707
727
|
return this.types;
|
|
708
728
|
}
|
|
709
729
|
|
|
@@ -716,7 +736,7 @@ export class Syncer {
|
|
|
716
736
|
const filePaths = (
|
|
717
737
|
await Promise.all(pathPatterns.map((pattern) => globAsync(pattern)))
|
|
718
738
|
).flat();
|
|
719
|
-
const modules = await importMultiple(filePaths);
|
|
739
|
+
const modules = await importMultiple(filePaths, doRefresh);
|
|
720
740
|
const functions = modules
|
|
721
741
|
.map(({ imported }) => Object.entries(imported))
|
|
722
742
|
.flat();
|
|
@@ -917,10 +937,7 @@ export class Syncer {
|
|
|
917
937
|
};
|
|
918
938
|
|
|
919
939
|
// 키 children
|
|
920
|
-
|
|
921
|
-
// if (key === "entity") {
|
|
922
|
-
// keys = ["entity", "init_generated", "init_types"];
|
|
923
|
-
// }
|
|
940
|
+
const keys: TemplateKey[] = [key];
|
|
924
941
|
|
|
925
942
|
// 템플릿 렌더
|
|
926
943
|
const pathAndCodes = (
|
|
@@ -940,27 +957,30 @@ export class Syncer {
|
|
|
940
957
|
- 옵션3 : 메인 파일만 그대로 두고, 파생 파일은 전부 생성함 => 이게 맞지 않나?
|
|
941
958
|
*/
|
|
942
959
|
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
"이미 경로에 모든 파일이 존재합니다."
|
|
962
|
-
);
|
|
960
|
+
const filteredPathAndCodes: PathAndCode[] = (() => {
|
|
961
|
+
if (generateOptions.overwrite === true) {
|
|
962
|
+
return pathAndCodes;
|
|
963
|
+
} else {
|
|
964
|
+
return pathAndCodes.filter((pathAndCode, index) => {
|
|
965
|
+
if (index === 0) {
|
|
966
|
+
const { targets } = Sonamu.config.sync;
|
|
967
|
+
const filePath = `${Sonamu.appRootPath}/${pathAndCode.path}`;
|
|
968
|
+
const dstFilePaths = targets.map((target) =>
|
|
969
|
+
filePath.replace("/:target/", `/${target}/`)
|
|
970
|
+
);
|
|
971
|
+
return dstFilePaths.every(
|
|
972
|
+
(dstPath) => existsSync(dstPath) === false
|
|
973
|
+
);
|
|
974
|
+
} else {
|
|
975
|
+
return true;
|
|
976
|
+
}
|
|
977
|
+
});
|
|
963
978
|
}
|
|
979
|
+
})();
|
|
980
|
+
if (filteredPathAndCodes.length === 0) {
|
|
981
|
+
throw new AlreadyProcessedException(
|
|
982
|
+
"이미 경로에 모든 파일이 존재합니다."
|
|
983
|
+
);
|
|
964
984
|
}
|
|
965
985
|
|
|
966
986
|
return Promise.all(
|
|
@@ -970,6 +990,24 @@ export class Syncer {
|
|
|
970
990
|
);
|
|
971
991
|
}
|
|
972
992
|
|
|
993
|
+
checkExistsGenCode(
|
|
994
|
+
entityId: string,
|
|
995
|
+
templateKey: TemplateKey,
|
|
996
|
+
enumId?: string
|
|
997
|
+
): { subPath: string; fullPath: string; isExists: boolean } {
|
|
998
|
+
const { target, path: genPath } = this.getTemplate(
|
|
999
|
+
templateKey
|
|
1000
|
+
).getTargetAndPath(EntityManager.getNamesFromId(entityId), enumId);
|
|
1001
|
+
|
|
1002
|
+
const fullPath = path.join(Sonamu.appRootPath, target, genPath);
|
|
1003
|
+
const subPath = path.join(target, genPath);
|
|
1004
|
+
return {
|
|
1005
|
+
subPath,
|
|
1006
|
+
fullPath,
|
|
1007
|
+
isExists: existsSync(fullPath),
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
|
|
973
1011
|
checkExists(
|
|
974
1012
|
entityId: string,
|
|
975
1013
|
enums: {
|
|
@@ -1217,7 +1255,10 @@ export class Syncer {
|
|
|
1217
1255
|
}
|
|
1218
1256
|
}
|
|
1219
1257
|
|
|
1220
|
-
async getColumnsNode(
|
|
1258
|
+
async getColumnsNode(
|
|
1259
|
+
entityId: string,
|
|
1260
|
+
subsetKey: string
|
|
1261
|
+
): Promise<RenderingNode> {
|
|
1221
1262
|
const entity = await EntityManager.get(entityId);
|
|
1222
1263
|
const subsetA = entity.subsets[subsetKey];
|
|
1223
1264
|
if (subsetA === undefined) {
|
|
@@ -1296,12 +1337,14 @@ export class Syncer {
|
|
|
1296
1337
|
await EntityManager.reload();
|
|
1297
1338
|
|
|
1298
1339
|
// generate schemas
|
|
1299
|
-
await this.actionGenerateSchemas([entityId]);
|
|
1340
|
+
await this.actionGenerateSchemas([parentId ?? entityId]);
|
|
1300
1341
|
|
|
1301
1342
|
// generate types
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1343
|
+
if (parentId === undefined) {
|
|
1344
|
+
await this.generateTemplate("init_types", {
|
|
1345
|
+
entityId,
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1305
1348
|
}
|
|
1306
1349
|
|
|
1307
1350
|
async delEntity(entityId: string): Promise<{ delPaths: string[] }> {
|
|
@@ -7,10 +7,10 @@ export class Template__entity extends Template {
|
|
|
7
7
|
super("entity");
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
getTargetAndPath(names: EntityNamesRecord) {
|
|
10
|
+
getTargetAndPath(names: EntityNamesRecord, parentNames?: EntityNamesRecord) {
|
|
11
11
|
return {
|
|
12
12
|
target: "api/src/application",
|
|
13
|
-
path: `${names.fs}/${names.fs}.entity.json`,
|
|
13
|
+
path: `${(parentNames ?? names).fs}/${names.fs}.entity.json`,
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -18,30 +18,63 @@ export class Template__entity extends Template {
|
|
|
18
18
|
const { entityId, title, parentId, table } = options;
|
|
19
19
|
const names = EntityManager.getNamesFromId(entityId);
|
|
20
20
|
|
|
21
|
+
const parent = (() => {
|
|
22
|
+
if (parentId) {
|
|
23
|
+
return {
|
|
24
|
+
names: EntityManager.getNamesFromId(parentId),
|
|
25
|
+
entity: EntityManager.get(parentId),
|
|
26
|
+
};
|
|
27
|
+
} else {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
})();
|
|
31
|
+
|
|
21
32
|
return {
|
|
22
|
-
...this.getTargetAndPath(names),
|
|
33
|
+
...this.getTargetAndPath(names, parent?.names ?? names),
|
|
23
34
|
body: JSON.stringify({
|
|
24
35
|
id: entityId,
|
|
25
36
|
title: title ?? entityId,
|
|
26
37
|
parentId,
|
|
27
38
|
table: table ?? names.fsPlural.replace(/\-/g, "_"),
|
|
28
39
|
props: [
|
|
29
|
-
{ name: "id", type: "integer", unsigned: true },
|
|
40
|
+
{ name: "id", type: "integer", unsigned: true, desc: "ID" },
|
|
41
|
+
...(parent
|
|
42
|
+
? [
|
|
43
|
+
{
|
|
44
|
+
type: "relation",
|
|
45
|
+
name: parent.names.camel,
|
|
46
|
+
relationType: "BelongsToOne",
|
|
47
|
+
with: parentId,
|
|
48
|
+
onUpdate: "CASCADE",
|
|
49
|
+
onDelete: "CASCADE",
|
|
50
|
+
desc: parent.entity.title,
|
|
51
|
+
},
|
|
52
|
+
]
|
|
53
|
+
: []),
|
|
30
54
|
{
|
|
31
55
|
name: "created_at",
|
|
32
56
|
type: "timestamp",
|
|
57
|
+
desc: "등록일시",
|
|
33
58
|
dbDefault: "CURRENT_TIMESTAMP",
|
|
34
59
|
},
|
|
35
60
|
],
|
|
36
61
|
indexes: [],
|
|
37
62
|
subsets: {
|
|
38
|
-
|
|
63
|
+
...(parentId
|
|
64
|
+
? {}
|
|
65
|
+
: {
|
|
66
|
+
A: ["id", "created_at"],
|
|
67
|
+
}),
|
|
39
68
|
},
|
|
40
69
|
enums: {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
70
|
+
...(parentId
|
|
71
|
+
? {}
|
|
72
|
+
: {
|
|
73
|
+
[`${names.capital}OrderBy`]: {
|
|
74
|
+
"id-desc": "ID최신순",
|
|
75
|
+
},
|
|
76
|
+
[`${names.capital}SearchField`]: { id: "ID" },
|
|
77
|
+
}),
|
|
45
78
|
},
|
|
46
79
|
}).trim(),
|
|
47
80
|
importKeys: [],
|