sonamu 0.2.36 → 0.2.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/.pnp.cjs +2117 -86
  2. package/dist/api/decorators.js +5 -2
  3. package/dist/api/decorators.js.map +1 -1
  4. package/dist/api/sonamu.js +3 -3
  5. package/dist/api/sonamu.js.map +1 -1
  6. package/dist/bin/cli-wrapper.d.ts +3 -0
  7. package/dist/bin/cli-wrapper.d.ts.map +1 -0
  8. package/dist/bin/cli-wrapper.js +27 -0
  9. package/dist/bin/cli-wrapper.js.map +1 -0
  10. package/dist/bin/cli.js +10 -10
  11. package/dist/bin/cli.js.map +1 -1
  12. package/dist/database/base-model.d.ts.map +1 -1
  13. package/dist/database/base-model.js +14 -13
  14. package/dist/database/base-model.js.map +1 -1
  15. package/dist/database/db.js +1 -1
  16. package/dist/database/db.js.map +1 -1
  17. package/dist/database/upsert-builder.js +8 -5
  18. package/dist/database/upsert-builder.js.map +1 -1
  19. package/dist/entity/entity-manager.d.ts.map +1 -1
  20. package/dist/entity/entity-manager.js +14 -12
  21. package/dist/entity/entity-manager.js.map +1 -1
  22. package/dist/entity/entity-utils.js +5 -2
  23. package/dist/entity/entity-utils.js.map +1 -1
  24. package/dist/entity/entity.d.ts.map +1 -1
  25. package/dist/entity/entity.js +20 -18
  26. package/dist/entity/entity.js.map +1 -1
  27. package/dist/entity/migrator.d.ts.map +1 -1
  28. package/dist/entity/migrator.js +59 -55
  29. package/dist/entity/migrator.js.map +1 -1
  30. package/dist/smd/smd-manager.d.ts.map +1 -1
  31. package/dist/smd/smd-manager.js +13 -9
  32. package/dist/smd/smd-manager.js.map +1 -1
  33. package/dist/smd/smd.d.ts.map +1 -1
  34. package/dist/smd/smd.js +13 -14
  35. package/dist/smd/smd.js.map +1 -1
  36. package/dist/syncer/syncer.d.ts +1 -1
  37. package/dist/syncer/syncer.d.ts.map +1 -1
  38. package/dist/syncer/syncer.js +68 -67
  39. package/dist/syncer/syncer.js.map +1 -1
  40. package/dist/templates/generated.template.js +6 -3
  41. package/dist/templates/generated.template.js.map +1 -1
  42. package/dist/templates/generated_sso.template.js +7 -4
  43. package/dist/templates/generated_sso.template.js.map +1 -1
  44. package/dist/templates/service.template.js +13 -10
  45. package/dist/templates/service.template.js.map +1 -1
  46. package/dist/templates/view_enums_dropdown.template.js +5 -2
  47. package/dist/templates/view_enums_dropdown.template.js.map +1 -1
  48. package/dist/templates/view_form.template.d.ts.map +1 -1
  49. package/dist/templates/view_form.template.js +7 -4
  50. package/dist/templates/view_form.template.js.map +1 -1
  51. package/dist/templates/view_id_async_select.template.js.map +1 -1
  52. package/dist/templates/view_list.template.js +10 -7
  53. package/dist/templates/view_list.template.js.map +1 -1
  54. package/dist/testing/fixture-manager.js +3 -3
  55. package/dist/testing/fixture-manager.js.map +1 -1
  56. package/dist/utils/sql-parser.js +5 -2
  57. package/dist/utils/sql-parser.js.map +1 -1
  58. package/dist/utils/utils.js +2 -2
  59. package/dist/utils/utils.js.map +1 -1
  60. package/package.json +19 -5
  61. package/src/api/decorators.ts +3 -3
  62. package/src/api/sonamu.ts +3 -3
  63. package/src/bin/cli-wrapper.ts +34 -0
  64. package/src/bin/cli.ts +10 -16
  65. package/src/database/base-model.ts +18 -15
  66. package/src/database/db.ts +1 -1
  67. package/src/database/upsert-builder.ts +5 -5
  68. package/src/entity/entity-manager.ts +14 -12
  69. package/src/entity/entity-utils.ts +2 -2
  70. package/src/entity/entity.ts +13 -11
  71. package/src/entity/migrator.ts +73 -85
  72. package/src/smd/smd-manager.ts +13 -9
  73. package/src/smd/smd.ts +10 -11
  74. package/src/syncer/syncer.ts +44 -44
  75. package/src/templates/generated.template.ts +3 -3
  76. package/src/templates/generated_sso.template.ts +4 -4
  77. package/src/templates/service.template.ts +10 -10
  78. package/src/templates/view_enums_dropdown.template.ts +2 -2
  79. package/src/templates/view_form.template.ts +36 -33
  80. package/src/templates/view_id_async_select.template.ts +4 -4
  81. package/src/templates/view_list.template.ts +18 -18
  82. package/src/testing/fixture-manager.ts +3 -3
  83. package/src/utils/sql-parser.ts +2 -2
  84. package/src/utils/utils.ts +2 -2
  85. package/tsup.config.js +21 -0
package/package.json CHANGED
@@ -1,16 +1,29 @@
1
1
  {
2
2
  "name": "sonamu",
3
- "version": "0.2.36",
3
+ "version": "0.2.38",
4
4
  "description": "Sonamu — TypeScript Fullstack API Framework",
5
5
  "keywords": [
6
6
  "typescript",
7
7
  "framework",
8
8
  "orm"
9
9
  ],
10
- "main": "dist/index.js",
11
- "types": "dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": {
13
+ "types": "./dist/index.d.mts",
14
+ "default": "./dist/index.mjs"
15
+ },
16
+ "require": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ }
20
+ }
21
+ },
22
+ "main": "./dist/index.js",
23
+ "module": "./dist/index.mjs",
12
24
  "scripts": {
13
- "dev": "yarn tsc -w -p ./tsconfig.json"
25
+ "dev": "yarn tsc -w -p ./tsconfig.json",
26
+ "build": "tsup --config ./tsup.config.js"
14
27
  },
15
28
  "license": "MIT",
16
29
  "author": {
@@ -22,7 +35,7 @@
22
35
  "type": "git",
23
36
  "url": "https://github.com/ping-alive/sonamu.git"
24
37
  },
25
- "bin": "./dist/bin/cli.js",
38
+ "bin": "./dist/bin/cli-wrapper.mjs",
26
39
  "dependencies": {
27
40
  "chalk": "^4.1.2",
28
41
  "dotenv": "^16.0.2",
@@ -55,6 +68,7 @@
55
68
  "@types/uuid": "^8.3.4",
56
69
  "prettier": "^3.2.5",
57
70
  "source-map-support": "^0.5.21",
71
+ "tsup": "^8.1.0",
58
72
  "typescript": "^5.2.2"
59
73
  },
60
74
  "peerDependencies": {
@@ -1,5 +1,5 @@
1
1
  import { HTTPMethods } from "fastify";
2
- import { camelize } from "inflection";
2
+ import inflection from "inflection";
3
3
  import { ApiParam, ApiParamType } from "../types/types";
4
4
 
5
5
  export type ServiceClient =
@@ -49,10 +49,10 @@ export function api(options: ApiDecoratorOptions = {}) {
49
49
  return function (target: Object, propertyKey: string) {
50
50
  const modelName = target.constructor.name.match(/(.+)Class$/)![1];
51
51
  const methodName = propertyKey;
52
- const defaultPath = `/${camelize(
52
+ const defaultPath = `/${inflection.camelize(
53
53
  modelName.replace(/Model$/, ""),
54
54
  true
55
- )}/${camelize(propertyKey, true)}`;
55
+ )}/${inflection.camelize(propertyKey, true)}`;
56
56
 
57
57
  const api = {
58
58
  modelName,
package/src/api/sonamu.ts CHANGED
@@ -14,7 +14,7 @@ import { DB, SonamuDBConfig } from "../database/db";
14
14
  import { BaseModel } from "../database/base-model";
15
15
  import { findApiRootPath } from "../utils/utils";
16
16
  import path from "path";
17
- import { existsSync, readFileSync } from "fs";
17
+ import fs from "fs-extra";
18
18
  import { ApiDecoratorOptions } from "./decorators";
19
19
 
20
20
  export type SonamuConfig = {
@@ -128,11 +128,11 @@ class SonamuClass {
128
128
 
129
129
  this.apiRootPath = apiRootPath ?? (await findApiRootPath());
130
130
  const configPath = path.join(this.apiRootPath, "sonamu.config.json");
131
- if (existsSync(configPath) === false) {
131
+ if (fs.existsSync(configPath) === false) {
132
132
  throw new Error(`Cannot find sonamu.config.json in ${configPath}`);
133
133
  }
134
134
  this.config = JSON.parse(
135
- readFileSync(configPath).toString()
135
+ fs.readFileSync(configPath).toString()
136
136
  ) as SonamuConfig;
137
137
 
138
138
  // DB 로드
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ts-node
2
+
3
+ import { spawnSync } from "child_process";
4
+ import { resolve } from "path";
5
+ import { existsSync, readFileSync } from "fs";
6
+
7
+ const cjsPath = resolve(__dirname, "bin/cli.js");
8
+ const esmPath = resolve(__dirname, "bin/cli.mjs");
9
+
10
+ const isESM = () => {
11
+ const packageJsonPath = resolve(process.cwd(), "package.json");
12
+ if (existsSync(packageJsonPath)) {
13
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
14
+ return packageJson.type === "module";
15
+ }
16
+ return false;
17
+ };
18
+
19
+ const scriptPath = isESM() ? esmPath : cjsPath;
20
+
21
+ if (!existsSync(scriptPath)) {
22
+ console.error(`Error: Script not found at ${scriptPath}`);
23
+ process.exit(1);
24
+ }
25
+
26
+ const result = spawnSync(
27
+ process.execPath,
28
+ [scriptPath, ...process.argv.slice(2)],
29
+ {
30
+ stdio: "inherit",
31
+ }
32
+ );
33
+
34
+ process.exit(result.status ?? 1);
package/src/bin/cli.ts CHANGED
@@ -12,16 +12,10 @@ import { Migrator } from "../entity/migrator";
12
12
  import { FixtureManager } from "../testing/fixture-manager";
13
13
  import { tsicli } from "tsicli";
14
14
  import { execSync } from "child_process";
15
- import {
16
- existsSync,
17
- mkdirSync,
18
- readdirSync,
19
- unlinkSync,
20
- writeFileSync,
21
- } from "fs";
15
+ import fs from "fs-extra";
22
16
  import { Sonamu } from "../api";
23
17
  import knex, { Knex } from "knex";
24
- import { camelize } from "inflection";
18
+ import inflection from "inflection";
25
19
  import prettier from "prettier";
26
20
  import { SMDManager } from "../smd/smd-manager";
27
21
  import process from "process";
@@ -241,11 +235,11 @@ async function fixture_sync() {
241
235
 
242
236
  async function stub_practice(name: string) {
243
237
  const practiceDir = path.join(Sonamu.apiRootPath, "src", "practices");
244
- const fileNames = readdirSync(practiceDir);
238
+ const fileNames = fs.readdirSync(practiceDir);
245
239
 
246
240
  const maxSeqNo = (() => {
247
- if (existsSync(practiceDir) === false) {
248
- mkdirSync(practiceDir, { recursive: true });
241
+ if (fs.existsSync(practiceDir) === false) {
242
+ fs.mkdirSync(practiceDir, { recursive: true });
249
243
  }
250
244
 
251
245
  const filteredSeqs = fileNames
@@ -282,7 +276,7 @@ async function stub_practice(name: string) {
282
276
  `await BaseModel.destroy();`,
283
277
  `});`,
284
278
  ].join("\n");
285
- writeFileSync(dstPath, code);
279
+ fs.writeFileSync(dstPath, code);
286
280
 
287
281
  execSync(`code ${dstPath}`);
288
282
 
@@ -338,7 +332,7 @@ async function smd_migration() {
338
332
  return Object.fromEntries(
339
333
  Object.entries(enumLabels).map(([enumLabelName, enumLabel]) => {
340
334
  const enumName =
341
- entityId + camelize(enumLabelName.toLowerCase(), false);
335
+ entityId + inflection.camelize(enumLabelName.toLowerCase(), false);
342
336
  return [
343
337
  enumName,
344
338
  Object.fromEntries(
@@ -373,7 +367,7 @@ async function smd_migration() {
373
367
  const formatted = await prettier.format(JSON.stringify(entityJson), {
374
368
  parser: "json",
375
369
  });
376
- writeFileSync(dstPath, formatted);
370
+ fs.writeFileSync(dstPath, formatted);
377
371
  console.log(chalk.blue(`CREATED: ${dstPath}`));
378
372
 
379
373
  // smd.ts, enums.ts, genereated.ts 삭제 (트랜스파일 된 js파일도 삭제)
@@ -421,11 +415,11 @@ async function smd_migration() {
421
415
  srcGeneratedPath,
422
416
  dstGeneratedPath,
423
417
  ].map((p) => {
424
- if (existsSync(p) === false) {
418
+ if (fs.existsSync(p) === false) {
425
419
  console.log(chalk.yellow(`NOT FOUND: ${p}`));
426
420
  return;
427
421
  }
428
- unlinkSync(p);
422
+ fs.unlinkSync(p);
429
423
  console.log(chalk.red(`DELETED: ${p}`));
430
424
  });
431
425
  }
@@ -1,15 +1,15 @@
1
1
  import { DateTime } from "luxon";
2
2
  import { Knex } from "knex";
3
- import { chunk, groupBy, isObject, omit, set, uniq } from "lodash";
3
+ import _ from "lodash";
4
4
  import { attachOnDuplicateUpdate } from "./knex-plugins/knex-on-duplicate-update";
5
5
  attachOnDuplicateUpdate();
6
6
  import { DBPreset, DB } from "./db";
7
7
  import { isCustomJoinClause, SubsetQuery } from "../types/types";
8
8
  import { BaseListParams } from "../utils/model";
9
- import { pluralize, underscore } from "inflection";
9
+ import inflection from "inflection";
10
10
  import chalk from "chalk";
11
11
  import { UpsertBuilder } from "./upsert-builder";
12
- import { Parser } from "node-sql-parser";
12
+ import SqlParser from "node-sql-parser";
13
13
  import { getTableName, getTableNamesFromWhere } from "../utils/sql-parser";
14
14
 
15
15
  export class BaseModelClass {
@@ -55,7 +55,7 @@ export class BaseModelClass {
55
55
  selectField = unqKeyFields[0];
56
56
  unqKeys = rows.map((row) => row[unqKeyFields[0]]);
57
57
  }
58
- const chunks = chunk(unqKeys, chunkSize);
58
+ const chunks = _.chunk(unqKeys, chunkSize);
59
59
 
60
60
  let resultIds: number[] = [];
61
61
  for (let chunk of chunks) {
@@ -114,7 +114,7 @@ export class BaseModelClass {
114
114
  `${loader.manyJoin.toTable}.${loader.manyJoin.toCol}`
115
115
  )
116
116
  .whereIn(idColumn, fromIds)
117
- .select(uniq([...loader.select, idColumn]));
117
+ .select(_.uniq([...loader.select, idColumn]));
118
118
 
119
119
  // ManyToMany에서 OneJoin이 있는 경우
120
120
  loader.oneJoins.map((join) => {
@@ -140,10 +140,10 @@ export class BaseModelClass {
140
140
  }
141
141
 
142
142
  // 불러온 row들을 참조ID 기준으로 분류 배치
143
- const subRowGroups = groupBy(subRows, toCol);
143
+ const subRowGroups = _.groupBy(subRows, toCol);
144
144
  rows = rows.map((row) => {
145
145
  row[loader.as] = (subRowGroups[row[loader.manyJoin.idField]] ?? []).map(
146
- (r) => omit(r, toCol)
146
+ (r) => _.omit(r, toCol)
147
147
  );
148
148
  return row;
149
149
  });
@@ -155,7 +155,7 @@ export class BaseModelClass {
155
155
  return rows.map((row: any) => {
156
156
  // nullable relation인 경우 관련된 필드가 전부 null로 생성되는 것 방지하는 코드
157
157
  const nestedKeys = Object.keys(row).filter((key) => key.includes("__"));
158
- const groups = groupBy(nestedKeys, (key) => key.split("__")[0]);
158
+ const groups = _.groupBy(nestedKeys, (key) => key.split("__")[0]);
159
159
  const nullKeys = Object.keys(groups).filter(
160
160
  (key) =>
161
161
  groups[key].length > 1 &&
@@ -164,7 +164,7 @@ export class BaseModelClass {
164
164
 
165
165
  const hydrated = Object.keys(row).reduce((r, field) => {
166
166
  if (!field.includes("__")) {
167
- if (Array.isArray(row[field]) && isObject(row[field][0])) {
167
+ if (Array.isArray(row[field]) && _.isObject(row[field][0])) {
168
168
  r[field] = this.hydrate(row[field]);
169
169
  return r;
170
170
  } else {
@@ -180,10 +180,10 @@ export class BaseModelClass {
180
180
  .slice(1)
181
181
  .map((part) => `[${part}]`)
182
182
  .join("");
183
- set(
183
+ _.set(
184
184
  r,
185
185
  objPath,
186
- row[field] && Array.isArray(row[field]) && isObject(row[field][0])
186
+ row[field] && Array.isArray(row[field]) && _.isObject(row[field][0])
187
187
  ? this.hydrate(row[field])
188
188
  : row[field]
189
189
  );
@@ -227,7 +227,8 @@ export class BaseModelClass {
227
227
  qb: Knex.QueryBuilder;
228
228
  }> {
229
229
  const db = _db ?? this.getDB(subset.startsWith("A") ? "w" : "r");
230
- baseTable = baseTable ?? pluralize(underscore(this.modelName));
230
+ baseTable =
231
+ baseTable ?? inflection.pluralize(inflection.underscore(this.modelName));
231
232
  const queryMode =
232
233
  params.queryMode ?? (params.id !== undefined ? "list" : "both");
233
234
 
@@ -266,15 +267,17 @@ export class BaseModelClass {
266
267
  }
267
268
 
268
269
  const clonedQb = qb.clone().clear("order").clear("offset").clear("limit");
269
- const parser = new Parser();
270
+ const parser = new SqlParser.Parser();
270
271
 
271
272
  // optmizeCountQuery가 true인 경우 다른 clause에 영향을 주지 않는 모든 join을 제외함
272
273
  if (optimizeCountQuery) {
273
274
  const parsedQuery = parser.astify(clonedQb.toQuery());
274
275
  const tables = getTableNamesFromWhere(parsedQuery);
275
276
  // where절에 사용되는 테이블의 조인을 위해 사용되는 테이블
276
- const needToJoin = uniq(
277
- tables.flatMap((table) => table.split("__").map((t) => pluralize(t)))
277
+ const needToJoin = _.uniq(
278
+ tables.flatMap((table) =>
279
+ table.split("__").map((t) => inflection.pluralize(t))
280
+ )
278
281
  );
279
282
  applyJoinClause(
280
283
  clonedQb,
@@ -21,7 +21,7 @@ class DBClass {
21
21
  async readKnexfile(): Promise<SonamuDBConfig> {
22
22
  const dbConfigPath: string = path.join(
23
23
  Sonamu.apiRootPath,
24
- "/dist/configs/db"
24
+ "/dist/configs/db.js"
25
25
  );
26
26
  try {
27
27
  const knexfileModule = await import(dbConfigPath);
@@ -1,5 +1,5 @@
1
1
  import { v4 as uuidv4 } from "uuid";
2
- import { defaults, groupBy, uniq } from "lodash";
2
+ import _ from "lodash";
3
3
  import { Knex } from "knex";
4
4
  import { EntityManager } from "../entity/entity-manager";
5
5
  import { nonNullable } from "../utils/utils";
@@ -178,7 +178,7 @@ export class UpsertBuilder {
178
178
  }
179
179
 
180
180
  // 내부 참조 있는 경우 필터하여 분리
181
- const groups = groupBy(table.rows, (row) =>
181
+ const groups = _.groupBy(table.rows, (row) =>
182
182
  Object.entries(row).some(([, value]) => isRefField(value))
183
183
  ? "selfRef"
184
184
  : "normal"
@@ -212,14 +212,14 @@ export class UpsertBuilder {
212
212
  }
213
213
  );
214
214
 
215
- const extractFields = uniq(references).map(
215
+ const extractFields = _.uniq(references).map(
216
216
  (reference) => reference.split(".")[1]
217
217
  );
218
218
 
219
219
  // UUID 기준으로 id 추출
220
220
  const uuids = table.rows.map((row) => row.uuid);
221
221
  const upsertedRows = await wdb(tableName)
222
- .select(uniq(["uuid", "id", ...extractFields]))
222
+ .select(_.uniq(["uuid", "id", ...extractFields]))
223
223
  .whereIn("uuid", uuids);
224
224
  const uuidMap = new Map<string, any>(
225
225
  upsertedRows.map((row: any) => [row.uuid, row])
@@ -263,7 +263,7 @@ export class UpsertBuilder {
263
263
  where?: string;
264
264
  }
265
265
  ): Promise<void> {
266
- options = defaults(options, {
266
+ options = _.defaults(options, {
267
267
  chunkSize: 500,
268
268
  where: "id",
269
269
  });
@@ -1,12 +1,12 @@
1
1
  import chalk from "chalk";
2
- import { glob } from "glob";
3
- import { dasherize, underscore, pluralize, camelize } from "inflection";
2
+ import glob from "glob";
3
+ import inflection from "inflection";
4
4
  import _ from "lodash";
5
5
  import path from "path";
6
6
  import { Entity } from "./entity";
7
7
  import { EntityJson } from "../types/types";
8
8
  import { Sonamu } from "../api/sonamu";
9
- import { readFileSync } from "fs";
9
+ import fs from "fs-extra";
10
10
 
11
11
  export type EntityNamesRecord = Record<
12
12
  | "fs"
@@ -41,10 +41,10 @@ class EntityManagerClass {
41
41
  !doSilent && console.log(chalk.yellow(`autoload ${pathPattern}`));
42
42
 
43
43
  return new Promise((resolve) => {
44
- glob(path.resolve(pathPattern!), (_err, files) => {
44
+ glob.glob(path.resolve(pathPattern!), (_err, files) => {
45
45
  Promise.all(
46
46
  files.map(async (file) => {
47
- this.register(JSON.parse(readFileSync(file).toString()));
47
+ this.register(JSON.parse(fs.readFileSync(file).toString()));
48
48
  })
49
49
  ).then(() => {
50
50
  resolve("ok");
@@ -139,19 +139,21 @@ class EntityManagerClass {
139
139
  getNamesFromId(entityId: string): EntityNamesRecord {
140
140
  // entityId가 단복수 동형 단어인 경우 List 붙여서 생성
141
141
  const pluralized =
142
- pluralize(entityId) === entityId
142
+ inflection.pluralize(entityId) === entityId
143
143
  ? `${entityId}List`
144
- : pluralize(entityId);
144
+ : inflection.pluralize(entityId);
145
145
 
146
146
  return {
147
- fs: dasherize(underscore(entityId)).toLowerCase(),
148
- fsPlural: dasherize(underscore(pluralized)).toLowerCase(),
149
- camel: camelize(entityId, true),
150
- camelPlural: camelize(pluralized, true),
147
+ fs: inflection.dasherize(inflection.underscore(entityId)).toLowerCase(),
148
+ fsPlural: inflection
149
+ .dasherize(inflection.underscore(pluralized))
150
+ .toLowerCase(),
151
+ camel: inflection.camelize(entityId, true),
152
+ camelPlural: inflection.camelize(pluralized, true),
151
153
  capital: entityId,
152
154
  capitalPlural: pluralized,
153
155
  upper: entityId.toUpperCase(),
154
- constant: underscore(entityId).toUpperCase(),
156
+ constant: inflection.underscore(entityId).toUpperCase(),
155
157
  };
156
158
  }
157
159
  }
@@ -1,4 +1,4 @@
1
- import { camelize } from "inflection";
1
+ import inflection from "inflection";
2
2
  import {
3
3
  BelongsToOneRelationProp,
4
4
  BigIntegerProp,
@@ -208,7 +208,7 @@ function enums(
208
208
  return {
209
209
  name,
210
210
  type: "enum",
211
- id: option.id ?? `$Model${camelize(name)}`,
211
+ id: option.id ?? `$Model${inflection.camelize(name)}`,
212
212
  ...option,
213
213
  };
214
214
  }
@@ -1,6 +1,5 @@
1
- import { groupBy, uniq } from "lodash";
1
+ import _ from "lodash";
2
2
  import { EntityManager as EntityManager } from "./entity-manager";
3
- import { dasherize, pluralize, underscore } from "inflection";
4
3
  import {
5
4
  EntityProp,
6
5
  RelationProp,
@@ -20,7 +19,7 @@ import {
20
19
  } from "../types/types";
21
20
  import inflection from "inflection";
22
21
  import path from "path";
23
- import { existsSync, writeFileSync } from "fs";
22
+ import fs from "fs-extra";
24
23
  import { z } from "zod";
25
24
  import { Sonamu } from "../api/sonamu";
26
25
  import prettier from "prettier";
@@ -73,7 +72,7 @@ export class Entity {
73
72
  this.id = id;
74
73
  this.parentId = parentId;
75
74
  this.title = title ?? this.id;
76
- this.table = table ?? underscore(pluralize(id));
75
+ this.table = table ?? inflection.underscore(inflection.pluralize(id));
77
76
 
78
77
  // props
79
78
  if (props) {
@@ -128,8 +127,10 @@ export class Entity {
128
127
 
129
128
  // names
130
129
  this.names = {
131
- parentFs: dasherize(underscore(parentId ?? id)).toLowerCase(),
132
- fs: dasherize(underscore(id)).toLowerCase(),
130
+ parentFs: inflection
131
+ .dasherize(inflection.underscore(parentId ?? id))
132
+ .toLowerCase(),
133
+ fs: inflection.dasherize(inflection.underscore(id)).toLowerCase(),
133
134
  module: id,
134
135
  };
135
136
 
@@ -158,7 +159,7 @@ export class Entity {
158
159
  prefix = prefix.replace(/\./g, "__");
159
160
 
160
161
  // 서브셋을 1뎁스만 분리하여 그룹핑
161
- const subsetGroup = groupBy(fields, (field) => {
162
+ const subsetGroup = _.groupBy(fields, (field) => {
162
163
  if (field.includes(".")) {
163
164
  const [rel] = field.split(".");
164
165
  return rel;
@@ -271,7 +272,7 @@ export class Entity {
271
272
  to = `${joinAs}.id`;
272
273
  } else {
273
274
  from = `${fromTable}.id`;
274
- to = `${joinAs}.${underscore(
275
+ to = `${joinAs}.${inflection.underscore(
275
276
  this.names.fs.replace(/\-/g, "_")
276
277
  )}_id`;
277
278
  }
@@ -302,6 +303,7 @@ export class Entity {
302
303
  manyJoin: loader.manyJoin,
303
304
  oneJoins: loader.oneJoins,
304
305
  select: loader.select,
306
+ loaders: loader.loaders,
305
307
  };
306
308
  });
307
309
 
@@ -575,7 +577,7 @@ export class Entity {
575
577
  `dist/application/${typesModulePath}.js`
576
578
  );
577
579
 
578
- if (existsSync(typesFileDistPath)) {
580
+ if (fs.existsSync(typesFileDistPath)) {
579
581
  const importPath = path.relative(__dirname, typesFileDistPath);
580
582
  import(importPath).then((t) => {
581
583
  this.types = Object.keys(t).reduce((result, key) => {
@@ -629,7 +631,7 @@ export class Entity {
629
631
  `src/application/${this.names.parentFs}/${this.names.fs}.entity.json`
630
632
  );
631
633
  const json = this.toJson();
632
- writeFileSync(
634
+ fs.writeFileSync(
633
635
  jsonPath,
634
636
  await prettier.format(JSON.stringify(json), {
635
637
  parser: "json",
@@ -650,7 +652,7 @@ export class Entity {
650
652
 
651
653
  const subsets = _subsets ?? this.subsets;
652
654
  const subsetKeys = Object.keys(subsets);
653
- const allFields = uniq(subsetKeys.map((key) => subsets[key]).flat());
655
+ const allFields = _.uniq(subsetKeys.map((key) => subsets[key]).flat());
654
656
 
655
657
  return this.props.map((prop) => {
656
658
  if (