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.
Files changed (67) hide show
  1. package/dist/entity/entity-manager.d.ts.map +1 -1
  2. package/dist/entity/entity-manager.js +1 -0
  3. package/dist/entity/entity-manager.js.map +1 -1
  4. package/dist/entity/entity-utils.d.ts.map +1 -1
  5. package/dist/entity/entity-utils.js +1 -1
  6. package/dist/entity/entity-utils.js.map +1 -1
  7. package/dist/entity/entity.d.ts +4 -0
  8. package/dist/entity/entity.d.ts.map +1 -1
  9. package/dist/entity/entity.js +74 -22
  10. package/dist/entity/entity.js.map +1 -1
  11. package/dist/entity/migrator.d.ts.map +1 -1
  12. package/dist/entity/migrator.js +43 -12
  13. package/dist/entity/migrator.js.map +1 -1
  14. package/dist/syncer/syncer.d.ts +6 -1
  15. package/dist/syncer/syncer.d.ts.map +1 -1
  16. package/dist/syncer/syncer.js +62 -41
  17. package/dist/syncer/syncer.js.map +1 -1
  18. package/dist/templates/entity.template.d.ts +1 -1
  19. package/dist/templates/entity.template.d.ts.map +1 -1
  20. package/dist/templates/entity.template.js +43 -13
  21. package/dist/templates/entity.template.js.map +1 -1
  22. package/dist/templates/generated.template.d.ts.map +1 -1
  23. package/dist/templates/generated.template.js +18 -0
  24. package/dist/templates/generated.template.js.map +1 -1
  25. package/dist/templates/service.template.d.ts.map +1 -1
  26. package/dist/templates/service.template.js +4 -4
  27. package/dist/templates/service.template.js.map +1 -1
  28. package/dist/templates/view_enums_dropdown.template.d.ts +2 -2
  29. package/dist/templates/view_enums_dropdown.template.d.ts.map +1 -1
  30. package/dist/templates/view_enums_dropdown.template.js +14 -13
  31. package/dist/templates/view_enums_dropdown.template.js.map +1 -1
  32. package/dist/templates/view_enums_select.template.d.ts +1 -1
  33. package/dist/templates/view_enums_select.template.d.ts.map +1 -1
  34. package/dist/templates/view_enums_select.template.js +4 -4
  35. package/dist/templates/view_enums_select.template.js.map +1 -1
  36. package/dist/templates/view_form.template.d.ts +2 -5
  37. package/dist/templates/view_form.template.d.ts.map +1 -1
  38. package/dist/templates/view_form.template.js +14 -8
  39. package/dist/templates/view_form.template.js.map +1 -1
  40. package/dist/templates/view_list.template.d.ts +2 -5
  41. package/dist/templates/view_list.template.d.ts.map +1 -1
  42. package/dist/templates/view_list.template.js +17 -20
  43. package/dist/templates/view_list.template.js.map +1 -1
  44. package/dist/types/types.d.ts +15 -30
  45. package/dist/types/types.d.ts.map +1 -1
  46. package/dist/types/types.js +0 -3
  47. package/dist/types/types.js.map +1 -1
  48. package/dist/utils/utils.d.ts +1 -1
  49. package/dist/utils/utils.d.ts.map +1 -1
  50. package/dist/utils/utils.js +4 -1
  51. package/dist/utils/utils.js.map +1 -1
  52. package/package.json +1 -1
  53. package/src/entity/entity-manager.ts +1 -0
  54. package/src/entity/entity-utils.ts +2 -0
  55. package/src/entity/entity.ts +101 -32
  56. package/src/entity/migrator.ts +54 -13
  57. package/src/shared/web.shared.ts.txt +21 -9
  58. package/src/syncer/syncer.ts +88 -45
  59. package/src/templates/entity.template.ts +42 -9
  60. package/src/templates/generated.template.ts +27 -2
  61. package/src/templates/service.template.ts +6 -4
  62. package/src/templates/view_enums_dropdown.template.ts +12 -17
  63. package/src/templates/view_enums_select.template.ts +4 -8
  64. package/src/templates/view_form.template.ts +16 -10
  65. package/src/templates/view_list.template.ts +16 -23
  66. package/src/types/types.ts +4 -6
  67. package/src/utils/utils.ts +5 -1
@@ -9,7 +9,7 @@ import {
9
9
  import { EntityManager, EntityNamesRecord } from "../entity/entity-manager";
10
10
  import { Entity } from "../entity/entity";
11
11
  import { EntityPropNode, SubsetQuery } from "../types/types";
12
- import { propNodeToZodTypeDef } from "../api/code-converters";
12
+ import { propNodeToZodTypeDef, zodTypeToZodCode } from "../api/code-converters";
13
13
  import { Template } from "./base-template";
14
14
 
15
15
  export class Template__generated extends Template {
@@ -33,7 +33,7 @@ export class Template__generated extends Template {
33
33
  ...(entity.parentId === undefined
34
34
  ? [
35
35
  this.getBaseListParamsTypeSource(entity),
36
- this.getSubsetTypeSource(entity),
36
+ this.getSubsetTypeSource(entity)!,
37
37
  ]
38
38
  : []),
39
39
  ].reduce(
@@ -52,6 +52,31 @@ export class Template__generated extends Template {
52
52
  }
53
53
  );
54
54
 
55
+ // .types.ts의 타입을 참조하는 경우 순환참조(상호참조)가 발생하므로 해당 타입을 가져와 인라인 처리
56
+ const entityTypeKeys = Object.keys(entity.types);
57
+ const cdImportKeys = uniq(typeSource.importKeys).filter((importKey) =>
58
+ entityTypeKeys.includes(importKey)
59
+ );
60
+ if (cdImportKeys.length > 0) {
61
+ typeSource.lines = [
62
+ ...cdImportKeys
63
+ .map((importKey) => [
64
+ `// Imported CustomScalar: ${importKey}`,
65
+ `const ${importKey} = ${zodTypeToZodCode(
66
+ entity.types[importKey]
67
+ )};`,
68
+ `type ${importKey} = z.infer<typeof ${importKey}>`,
69
+ "",
70
+ ])
71
+ .flat(),
72
+ "",
73
+ ...typeSource.lines,
74
+ ];
75
+ typeSource.importKeys = typeSource.importKeys.filter(
76
+ (importKey) => !cdImportKeys.includes(importKey)
77
+ );
78
+ }
79
+
55
80
  // targetAndPath
56
81
  const names = EntityManager.getNamesFromId(entityId);
57
82
  const targetAndPath = this.getTargetAndPath(names);
@@ -40,7 +40,7 @@ export class Template__service extends Template {
40
40
  `import { z } from 'zod';`,
41
41
  `import qs from "qs";`,
42
42
  `import useSWR, { SWRResponse } from "swr";`,
43
- `import { fetch, ListResult, SWRError, SwrOptions, handleConditional } from '../sonamu.shared';`,
43
+ `import { fetch, ListResult, SWRError, SwrOptions, handleConditional, swrPostFetcher } from '../sonamu.shared';`,
44
44
  ],
45
45
  };
46
46
  }
@@ -241,10 +241,12 @@ export async function ${api.methodName}${typeParamsDef}(
241
241
  )}${typeParamsDef}(${[paramsDef, "options?: SwrOptions"]
242
242
  .filter((p) => p !== "")
243
243
  .join(",")}, ): SWRResponse<${returnTypeDef}, SWRError> {
244
- return useSWR<${returnTypeDef}, SWRError>(handleConditional([
244
+ return useSWR(handleConditional([
245
245
  \`${apiBaseUrl}\`,
246
- qs.stringify(${payloadDef}),
247
- ], options?.conditional));
246
+ ${payloadDef},
247
+ ], options?.conditional)${
248
+ api.options.httpMethod === "POST" ? ", swrPostFetcher" : ""
249
+ });
248
250
  }`;
249
251
  }
250
252
 
@@ -14,13 +14,9 @@ export class Template__view_enums_dropdown extends Template {
14
14
  };
15
15
  }
16
16
 
17
- render({
18
- entityId,
19
- enumId,
20
- idConstant,
21
- }: TemplateOptions["view_enums_dropdown"]) {
17
+ render({ entityId, enumId }: TemplateOptions["view_enums_dropdown"]) {
22
18
  const names = EntityManager.getNamesFromId(entityId);
23
- const label = getLabel(idConstant);
19
+ const label = getLabel(enumId);
24
20
 
25
21
  return {
26
22
  ...this.getTargetAndPath(names, enumId),
@@ -31,14 +27,14 @@ import {
31
27
  DropdownProps,
32
28
  } from 'semantic-ui-react';
33
29
 
34
- import { ${names.constant} } from 'src/services/${names.fs}/${names.fs}.enums';
30
+ import { ${enumId}Label } from 'src/services/${names.fs}/${names.fs}.generated';
35
31
 
36
32
  export function ${enumId}Dropdown(props: DropdownProps) {
37
- const options = Object.entries(${names.constant}.${idConstant}).map(([key, { ko }]) => {
33
+ const options = Object.entries(${enumId}Label).map(([key, label]) => {
38
34
  return {
39
35
  key,
40
36
  value: key,
41
- text: "${label}: " + ko,
37
+ text: "${label}: " + label,
42
38
  };
43
39
  });
44
40
  return (
@@ -55,13 +51,12 @@ export function ${enumId}Dropdown(props: DropdownProps) {
55
51
  }
56
52
  }
57
53
 
58
- export function getLabel(idConstant: string): string {
59
- switch (idConstant) {
60
- case "ORDER_BY":
61
- return "정렬";
62
- case "SEARCH_FIELD":
63
- return "검색";
64
- default:
65
- return idConstant;
54
+ export function getLabel(enumId: string): string {
55
+ if (enumId.endsWith("OrderBy")) {
56
+ return "정렬";
57
+ } else if (enumId.endsWith("SearchField")) {
58
+ return "검색";
59
+ } else {
60
+ return enumId;
66
61
  }
67
62
  }
@@ -15,13 +15,9 @@ export class Template__view_enums_select extends Template {
15
15
  };
16
16
  }
17
17
 
18
- render({
19
- entityId,
20
- enumId,
21
- idConstant,
22
- }: TemplateOptions["view_enums_select"]) {
18
+ render({ entityId, enumId }: TemplateOptions["view_enums_select"]) {
23
19
  const names = EntityManager.getNamesFromId(entityId);
24
- const label = getLabel(idConstant);
20
+ const label = getLabel(enumId);
25
21
 
26
22
  return {
27
23
  ...this.getTargetAndPath(names, enumId),
@@ -32,7 +28,7 @@ import {
32
28
  DropdownProps,
33
29
  } from 'semantic-ui-react';
34
30
 
35
- import { ${enumId}, ${names.constant} } from 'src/services/${names.fs}/${names.fs}.enums';
31
+ import { ${enumId}, ${enumId}Label } from 'src/services/${names.fs}/${names.fs}.generated';
36
32
 
37
33
  export type ${enumId}SelectProps = {
38
34
  placeholder?: string;
@@ -42,7 +38,7 @@ export function ${enumId}Select({placeholder, textPrefix, ...props}: ${enumId}Se
42
38
  const typeOptions = ${enumId}.options.map((key) => ({
43
39
  key,
44
40
  value: key,
45
- text: (textPrefix ?? '${label}: ') + ${names.constant}.${idConstant}[key].ko,
41
+ text: (textPrefix ?? '${label}: ') + ${enumId}Label[key],
46
42
  }));
47
43
 
48
44
  return (
@@ -163,10 +163,17 @@ export class Template__view_form extends Template {
163
163
  { entityId }: TemplateOptions["view_form"],
164
164
  saveParamsNode: RenderingNode
165
165
  ) {
166
+ const entity = EntityManager.get(entityId);
166
167
  const names = EntityManager.getNamesFromId(entityId);
167
- const columns = (saveParamsNode.children as RenderingNode[]).filter(
168
- (col) => col.name !== "id"
169
- );
168
+ const columns = (saveParamsNode.children as RenderingNode[])
169
+ .filter((col) => col.name !== "id")
170
+ .map((col) => {
171
+ const propCandidate = entity.props.find(
172
+ (prop) => prop.name === col.name
173
+ );
174
+ col.label = propCandidate?.desc ?? col.label;
175
+ return col;
176
+ });
170
177
 
171
178
  const defaultValue = this.resolveDefaultValue(columns);
172
179
 
@@ -198,16 +205,14 @@ export class Template__view_form extends Template {
198
205
  let key: TemplateKey;
199
206
  let targetMdId = entityId;
200
207
  let enumId: string | undefined;
201
- let idConstant: string | undefined;
202
208
  if (col.renderType === "enums") {
203
209
  key = "view_enums_select";
204
- const { targetMDNames, id, name } = getEnumInfoFromColName(
210
+ const { targetMDNames, id } = getEnumInfoFromColName(
205
211
  entityId,
206
212
  col.name
207
213
  );
208
214
  targetMdId = targetMDNames.capital;
209
215
  enumId = id;
210
- idConstant = name;
211
216
  } else {
212
217
  key = "view_id_async_select";
213
218
  const relProp = getRelationPropFromColName(
@@ -223,7 +228,6 @@ export class Template__view_form extends Template {
223
228
  entityId: targetMdId,
224
229
  node: col,
225
230
  enumId,
226
- idConstant,
227
231
  },
228
232
  };
229
233
  })
@@ -258,8 +262,8 @@ import { DateTime } from "luxon";
258
262
 
259
263
  import { BackLink, LinkInput, NumberInput, BooleanToggle, SQLDateTimeInput, SQLDateInput, useTypeForm, useGoBack } from "@sonamu-kit/react-sui";
260
264
  import { defaultCatch } from 'src/services/sonamu.shared';
261
- import { ImageUploader } from 'src/components/core/ImageUploader';
262
- import { useCommonModal } from "src/components/core/CommonModal";
265
+ import { ImageUploader } from 'src/admin-common/ImageUploader';
266
+ import { useCommonModal } from "src/admin-common/CommonModal";
263
267
 
264
268
  import { ${names.capital}SaveParams } from 'src/services/${names.fs}/${
265
269
  names.fs
@@ -349,7 +353,9 @@ export function ${names.capitalPlural}Form({ id, mode }: ${
349
353
 
350
354
  // 페이지
351
355
  const PAGE = {
352
- title: \`${names.capital}\${id ? \`#\${id} 수정\` : ' 등록'}\`,
356
+ title: \`${
357
+ entity.title ?? names.capital
358
+ }\${id ? \`#\${id} 수정\` : ' 등록'}\`,
353
359
  }
354
360
 
355
361
  return (
@@ -6,6 +6,7 @@ import { EntityManager, EntityNamesRecord } from "../entity/entity-manager";
6
6
  import { isEnumProp, isRelationProp, RelationProp } from "../types/types";
7
7
  import { RenderedTemplate } from "../syncer/syncer";
8
8
  import { Template } from "./base-template";
9
+
9
10
  export class Template__view_list extends Template {
10
11
  constructor() {
11
12
  super("view_list");
@@ -55,20 +56,17 @@ export class Template__view_list extends Template {
55
56
  }<img src={${colName}} />}</>`;
56
57
  case "string-datetime":
57
58
  if (col.nullable) {
58
- return `<span className="text-tiny">{${colName} === null ? '-' : DateTime.fromSQL(${colName}).toSQL().slice(0, 10)}</span>`;
59
+ return `<span className="text-tiny">{${colName} === null ? '-' : dateF(${colName})}</span>`;
59
60
  } else {
60
- return `<span className="text-tiny">{DateTime.fromSQL(${colName}).toSQL().slice(0, 10)}</span>`;
61
+ return `<span className="text-tiny">{dateF(${colName})}</span>`;
61
62
  }
62
63
  case "boolean":
63
64
  return `<>{${colName} ? <Label color='green' circular>O</Label> : <Label color='grey' circular>X</Label> }</>`;
64
65
  case "enums":
65
- const { targetMDNames, name } = getEnumInfoFromColName(
66
- entityId,
67
- col.name
68
- );
69
- return `<>{${col.nullable ? `${colName} && ` : ""}${
70
- targetMDNames.constant
71
- }.${name}[${colName}].ko}</>`;
66
+ const { id: enumId } = getEnumInfoFromColName(entityId, col.name);
67
+ return `<>{${
68
+ col.nullable ? `${colName} && ` : ""
69
+ }${enumId}Label[${colName}]}</>`;
72
70
  case "array-images":
73
71
  return `<>{ ${colName}.map(r => ${
74
72
  col.nullable ? `r && ` : ""
@@ -110,13 +108,11 @@ export class Template__view_list extends Template {
110
108
  names: EntityNamesRecord
111
109
  ): (string | null)[] {
112
110
  if (col.renderType === "enums") {
113
- const { modulePath, targetMDNames } = getEnumInfoFromColName(
111
+ const { modulePath, id: enumId } = getEnumInfoFromColName(
114
112
  names.capital,
115
113
  col.name
116
114
  );
117
- return [
118
- `import { ${targetMDNames.constant} } from 'src/services/${modulePath}';`,
119
- ];
115
+ return [`import { ${enumId}Label } from 'src/services/${modulePath}';`];
120
116
  } else if (col.renderType === "object") {
121
117
  try {
122
118
  const relProp = getRelationPropFromColName(entityId, col.name);
@@ -246,14 +242,16 @@ export class Template__view_list extends Template {
246
242
  listParamsNode: RenderingNode
247
243
  ) {
248
244
  const names = EntityManager.getNamesFromId(entityId);
245
+ const entity = EntityManager.get(entityId);
249
246
 
250
247
  // 실제 리스트 컬럼
251
248
  const columns = (columnsNode.children as RenderingNode[])
252
249
  .filter((col) => col.name !== "id")
253
250
  .map((col) => {
251
+ const propCandidate = entity.props.find((p) => p.name === col.name);
254
252
  return {
255
253
  name: col.name,
256
- label: col.label,
254
+ label: propCandidate?.desc ?? col.label,
257
255
  tc: `(row) => ${this.renderColumn(entityId, col, names)}`,
258
256
  };
259
257
  });
@@ -277,24 +275,21 @@ export class Template__view_list extends Template {
277
275
  let key: TemplateKey;
278
276
  let targetMdId = entityId;
279
277
  let enumId: string | undefined;
280
- let idConstant: string | undefined;
281
278
 
282
279
  if (col.renderType === "enums") {
283
280
  if (col.name === "search") {
284
281
  key = "view_enums_dropdown";
285
282
  enumId = `${names.capital}SearchField`;
286
283
  targetMdId = names.capital;
287
- idConstant = "SEARCH_FIELD";
288
284
  } else {
289
285
  key = "view_enums_select";
290
286
  try {
291
- const { targetMDNames, id, name } = getEnumInfoFromColName(
287
+ const { targetMDNames, id } = getEnumInfoFromColName(
292
288
  entityId,
293
289
  col.name
294
290
  );
295
291
  targetMdId = targetMDNames.capital;
296
292
  enumId = id;
297
- idConstant = name;
298
293
  } catch {
299
294
  continue;
300
295
  }
@@ -317,7 +312,6 @@ export class Template__view_list extends Template {
317
312
  options: {
318
313
  entityId: targetMdId,
319
314
  enumId,
320
- idConstant,
321
315
  },
322
316
  });
323
317
  }
@@ -362,7 +356,7 @@ import {
362
356
  } from 'semantic-ui-react';
363
357
  import classNames from 'classnames';
364
358
  import { DateTime } from "luxon";
365
- import { DelButton, EditButton, AppBreadcrumbs, AddButton, useSelection, useListParams, SonamuCol, numF } from '@sonamu-kit/react-sui';
359
+ import { DelButton, EditButton, AppBreadcrumbs, AddButton, useSelection, useListParams, SonamuCol, numF, dateF, datetimeF } from '@sonamu-kit/react-sui';
366
360
 
367
361
  import { ${names.capital}SubsetA } from "src/services/${names.fs}/${
368
362
  names.fs
@@ -391,11 +385,10 @@ export default function ${names.capital}List({}: ${names.capital}ListProps) {
391
385
  });
392
386
 
393
387
  // 리스트 쿼리
394
- const { data, mutate, error } = ${names.capital}Service.use${
388
+ const { data, mutate, error, isLoading } = ${names.capital}Service.use${
395
389
  names.capitalPlural
396
390
  }('A', listParams);
397
391
  const { rows, total } = data ?? {};
398
- const isLoading = !error && !data;
399
392
 
400
393
  // 삭제
401
394
  const confirmDel = (ids: number[]) => {
@@ -424,7 +417,7 @@ export default function ${names.capital}List({}: ${names.capital}ListProps) {
424
417
  // 현재 경로와 타이틀
425
418
  const PAGE = {
426
419
  route: '/admin/${names.fsPlural}',
427
- title: '${names.capital}',
420
+ title: '${entity.title ?? names.capital}',
428
421
  };
429
422
 
430
423
  // 선택
@@ -41,7 +41,7 @@ export type CommonProp = {
41
41
  nullable?: boolean;
42
42
  toFilter?: true;
43
43
  desc?: string;
44
- dbDefault?: string | number | { raw: string };
44
+ dbDefault?: string;
45
45
  };
46
46
  export type IntegerProp = CommonProp & {
47
47
  type: "integer";
@@ -73,6 +73,8 @@ export type FloatProp = CommonProp & {
73
73
  export type DoubleProp = CommonProp & {
74
74
  type: "double";
75
75
  unsigned?: true;
76
+ precision: number;
77
+ scale: number;
76
78
  };
77
79
  export type DecimalProp = CommonProp & {
78
80
  type: "decimal";
@@ -113,7 +115,6 @@ export type RelationType =
113
115
  | "ManyToMany"
114
116
  | "OneToOne";
115
117
  export type RelationOn =
116
- | "UPDATE"
117
118
  | "CASCADE"
118
119
  | "SET NULL"
119
120
  | "NO ACTION"
@@ -394,7 +395,7 @@ export type MigrationColumn = {
394
395
  nullable: boolean;
395
396
  unsigned?: boolean;
396
397
  length?: number;
397
- defaultTo?: string | number;
398
+ defaultTo?: string;
398
399
  precision?: number;
399
400
  scale?: number;
400
401
  };
@@ -662,17 +663,14 @@ export const TemplateOptions = z.object({
662
663
  view_enums_select: z.object({
663
664
  entityId: z.string(),
664
665
  enumId: z.string(),
665
- idConstant: z.string(),
666
666
  }),
667
667
  view_enums_dropdown: z.object({
668
668
  entityId: z.string(),
669
669
  enumId: z.string(),
670
- idConstant: z.string(),
671
670
  }),
672
671
  view_enums_buttonset: z.object({
673
672
  entityId: z.string(),
674
673
  enumId: z.string(),
675
- idConstant: z.string(),
676
674
  }),
677
675
  });
678
676
  export type TemplateOptions = z.infer<typeof TemplateOptions>;
@@ -14,11 +14,15 @@ export function globAsync(pathPattern: string): Promise<string[]> {
14
14
  });
15
15
  }
16
16
  export async function importMultiple(
17
- filePaths: string[]
17
+ filePaths: string[],
18
+ doRefresh: boolean = false
18
19
  ): Promise<{ filePath: string; imported: any }[]> {
19
20
  return Promise.all(
20
21
  filePaths.map(async (filePath) => {
21
22
  const importPath = "./" + path.relative(__dirname, filePath);
23
+ if (doRefresh) {
24
+ delete require.cache[require.resolve(importPath)];
25
+ }
22
26
  const imported = await import(importPath);
23
27
  return {
24
28
  filePath,