@nestia/migrate 0.1.11 → 0.2.1-dev.20230802

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 (38) hide show
  1. package/lib/NestiaMigrateApplication.js +76 -11
  2. package/lib/NestiaMigrateApplication.js.map +1 -1
  3. package/lib/archivers/FileArchiver.js +0 -1
  4. package/lib/archivers/FileArchiver.js.map +1 -1
  5. package/lib/bundles/TEMPLATE.js +10 -5
  6. package/lib/bundles/TEMPLATE.js.map +1 -1
  7. package/lib/programmers/ControllerProgrammer.d.ts +2 -1
  8. package/lib/programmers/ControllerProgrammer.js +12 -17
  9. package/lib/programmers/ControllerProgrammer.js.map +1 -1
  10. package/lib/programmers/DtoProgrammer.d.ts +2 -1
  11. package/lib/programmers/DtoProgrammer.js +5 -5
  12. package/lib/programmers/DtoProgrammer.js.map +1 -1
  13. package/lib/programmers/ImportProgrammer.d.ts +11 -0
  14. package/lib/programmers/ImportProgrammer.js +20 -0
  15. package/lib/programmers/ImportProgrammer.js.map +1 -0
  16. package/lib/programmers/MigrateProgrammer.d.ts +2 -1
  17. package/lib/programmers/MigrateProgrammer.js +3 -3
  18. package/lib/programmers/MigrateProgrammer.js.map +1 -1
  19. package/lib/programmers/RouteProgrammer.d.ts +2 -1
  20. package/lib/programmers/RouteProgrammer.js +93 -37
  21. package/lib/programmers/RouteProgrammer.js.map +1 -1
  22. package/lib/programmers/SchemaProgrammer.d.ts +2 -1
  23. package/lib/programmers/SchemaProgrammer.js +64 -42
  24. package/lib/programmers/SchemaProgrammer.js.map +1 -1
  25. package/lib/structures/IMigrateRoute.d.ts +3 -0
  26. package/lib/structures/ISwaggerRoute.d.ts +3 -1
  27. package/package.json +4 -4
  28. package/src/NestiaMigrateApplication.ts +28 -3
  29. package/src/archivers/FileArchiver.ts +0 -1
  30. package/src/bundles/TEMPLATE.ts +10 -5
  31. package/src/programmers/ControllerProgrammer.ts +50 -54
  32. package/src/programmers/DtoProgrammer.ts +29 -25
  33. package/src/programmers/ImportProgrammer.ts +29 -0
  34. package/src/programmers/MigrateProgrammer.ts +17 -14
  35. package/src/programmers/RouteProgrammer.ts +158 -52
  36. package/src/programmers/SchemaProgrammer.ts +82 -40
  37. package/src/structures/IMigrateRoute.ts +4 -0
  38. package/src/structures/ISwaggerRoute.ts +4 -1
@@ -1,6 +1,7 @@
1
1
  import { IMigrateDto } from "../structures/IMigrateDto";
2
2
  import { ISwaggerSchema } from "../structures/ISwaggeSchema";
3
3
  import { ISwagger } from "../structures/ISwagger";
4
+ import { ISwaggerComponents } from "../structures/ISwaggerComponents";
4
5
  import { MapUtil } from "../utils/MapUtil";
5
6
  import { SchemaProgrammer } from "./SchemaProgrammer";
6
7
 
@@ -25,30 +26,33 @@ export namespace DtoProgrammer {
25
26
  return modulo;
26
27
  };
27
28
 
28
- export const write = (dto: IMigrateDto): string => {
29
- const references: ISwaggerSchema.IReference[] = [];
30
- const body: string = iterate(references)(dto);
31
- const imports: string[] = [
32
- ...new Set(
33
- references
34
- .map(
35
- (s) =>
36
- s.$ref
37
- .replace(`#/components/schemas/`, ``)
38
- .split(".")[0],
39
- )
40
- .filter((str) => str !== dto.name),
41
- ),
42
- ];
43
- const content: string[] = [
44
- ...imports.map((i) => `import { ${i} } from "./${i}";`),
45
- ...(imports.length ? [""] : []),
46
- body,
47
- ];
48
- return content.join("\n");
49
- };
29
+ export const write =
30
+ (components: ISwaggerComponents) =>
31
+ (dto: IMigrateDto): string => {
32
+ const references: ISwaggerSchema.IReference[] = [];
33
+ const body: string = iterate(components)(references)(dto);
34
+ const imports: string[] = [
35
+ ...new Set(
36
+ references
37
+ .map(
38
+ (s) =>
39
+ s.$ref
40
+ .replace(`#/components/schemas/`, ``)
41
+ .split(".")[0],
42
+ )
43
+ .filter((str) => str !== dto.name),
44
+ ),
45
+ ];
46
+ const content: string[] = [
47
+ ...imports.map((i) => `import { ${i} } from "./${i}";`),
48
+ ...(imports.length ? [""] : []),
49
+ body,
50
+ ];
51
+ return content.join("\n");
52
+ };
50
53
 
51
54
  const iterate =
55
+ (components: ISwaggerComponents) =>
52
56
  (references: ISwaggerSchema.IReference[]) =>
53
57
  (dto: IMigrateDto): string => {
54
58
  const content: string[] = [];
@@ -63,15 +67,15 @@ export namespace DtoProgrammer {
63
67
  ]
64
68
  : []),
65
69
  `export type ${dto.name} = ${SchemaProgrammer.write(
66
- references,
67
- )(dto.schema)}`,
70
+ components,
71
+ )(references)(dto.schema)}`,
68
72
  );
69
73
  }
70
74
  if (dto.children.length) {
71
75
  content.push(
72
76
  `export namespace ${dto.name} {`,
73
77
  ...dto.children.map((c) =>
74
- iterate(references)(c)
78
+ iterate(components)(references)(c)
75
79
  .split("\n")
76
80
  .map((l) => ` ${l}`)
77
81
  .join("\n"),
@@ -0,0 +1,29 @@
1
+ import { MapUtil } from "../utils/MapUtil";
2
+
3
+ export class ImportProgrammer {
4
+ private dict: Map<string, Set<string>> = new Map();
5
+
6
+ public add(props: ImportProgrammer.IProps): string {
7
+ MapUtil.take(this.dict)(props.library)(() => new Set()).add(
8
+ props.instance,
9
+ );
10
+ return props.instance;
11
+ }
12
+
13
+ public toScript(): string {
14
+ return [...this.dict.entries()]
15
+ .map(
16
+ ([library, properties]) =>
17
+ `import { ${[...properties].join(
18
+ ", ",
19
+ )} } from "${library}";`,
20
+ )
21
+ .join("\n");
22
+ }
23
+ }
24
+ export namespace ImportProgrammer {
25
+ export interface IProps {
26
+ library: string;
27
+ instance: string;
28
+ }
29
+ }
@@ -1,6 +1,7 @@
1
1
  import { IMigrateFile } from "../structures/IMigrateFile";
2
2
  import { IMigrateProgram } from "../structures/IMigrateProgram";
3
3
  import { ISwagger } from "../structures/ISwagger";
4
+ import { ISwaggerComponents } from "../structures/ISwaggerComponents";
4
5
  import { ControllerProgrammer } from "./ControllerProgrammer";
5
6
  import { DtoProgrammer } from "./DtoProgrammer";
6
7
 
@@ -14,18 +15,20 @@ export namespace MigrateProgrammer {
14
15
  };
15
16
  };
16
17
 
17
- export const write = (program: IMigrateProgram): IMigrateFile[] => {
18
- return [
19
- ...program.controllers.map((c) => ({
20
- location: c.location,
21
- file: `${c.name}.ts`,
22
- content: ControllerProgrammer.write(c),
23
- })),
24
- ...program.structures.map((s) => ({
25
- location: s.location,
26
- file: `${s.name}.ts`,
27
- content: DtoProgrammer.write(s),
28
- })),
29
- ];
30
- };
18
+ export const write =
19
+ (components: ISwaggerComponents) =>
20
+ (program: IMigrateProgram): IMigrateFile[] => {
21
+ return [
22
+ ...program.controllers.map((c) => ({
23
+ location: c.location,
24
+ file: `${c.name}.ts`,
25
+ content: ControllerProgrammer.write(components)(c),
26
+ })),
27
+ ...program.structures.map((s) => ({
28
+ location: s.location,
29
+ file: `${s.name}.ts`,
30
+ content: DtoProgrammer.write(components)(s),
31
+ })),
32
+ ];
33
+ };
31
34
  }
@@ -3,6 +3,7 @@ import { Escaper } from "typia/lib/utils/Escaper";
3
3
  import { IMigrateRoute } from "../structures/IMigrateRoute";
4
4
  import { ISwaggerSchema } from "../structures/ISwaggeSchema";
5
5
  import { ISwagger } from "../structures/ISwagger";
6
+ import { ISwaggerComponents } from "../structures/ISwaggerComponents";
6
7
  import { ISwaggerRoute } from "../structures/ISwaggerRoute";
7
8
  import { JsonTypeChecker } from "../utils/JsonTypeChecker";
8
9
  import { StringUtil } from "../utils/StringUtil";
@@ -14,11 +15,11 @@ export namespace RouteProgrammer {
14
15
  (props: { path: string; method: string }) =>
15
16
  (route: ISwaggerRoute): IMigrateRoute | null => {
16
17
  const body = emplaceBodySchema(emplaceReference(swagger)("body"))(
17
- route.requestBody?.content,
18
+ route.requestBody,
18
19
  );
19
20
  const response = emplaceBodySchema(
20
21
  emplaceReference(swagger)("response"),
21
- )((route.responses?.["200"] ?? route.responses?.["201"])?.content);
22
+ )(route.responses?.["201"] ?? route.responses?.["200"]);
22
23
  if (body === false || response === false) {
23
24
  console.log(
24
25
  `Failed to migrate ${props.method.toUpperCase()} ${
@@ -26,6 +27,15 @@ export namespace RouteProgrammer {
26
27
  }: @nestia/migrate supports only application/json or text/plain format yet.`,
27
28
  );
28
29
  return null;
30
+ } else if (
31
+ SUPPORTED_METHODS.has(props.method.toUpperCase()) === false
32
+ ) {
33
+ console.log(
34
+ `Failed to migrate ${props.method.toUpperCase()} ${
35
+ props.path
36
+ }: @nestia/migrate does not support ${props.method.toUpperCase()} method.`,
37
+ );
38
+ return null;
29
39
  }
30
40
 
31
41
  const [headers, query] = ["header", "query"].map((type) => {
@@ -193,32 +203,47 @@ export namespace RouteProgrammer {
193
203
  body,
194
204
  response,
195
205
  description: describe(route),
206
+ "x-nestia-jsDocTags": route["x-nestia-jsDocTags"],
196
207
  };
197
208
  };
198
209
 
199
210
  const describe = (route: ISwaggerRoute): string | undefined => {
200
- const content: string[] = [];
211
+ const commentTags: string[] = [];
201
212
  const add = (text: string) => {
202
- if (!route.description || route.description.indexOf(text) === -1)
203
- content.push(text);
213
+ if (commentTags.every((line) => line !== text))
214
+ commentTags.push(text);
204
215
  };
205
- if (route.summary)
206
- add(
207
- (route.summary.endsWith(".")
208
- ? route.summary
209
- : route.summary + ".") + "\n",
210
- );
211
- if (route.description) {
212
- content.push(...route.description.split("\n"));
213
- if (!route.description.split("\n").at(-1)?.startsWith("@"))
214
- content.push("");
216
+
217
+ let description: string | undefined = route.description;
218
+ if (route.summary) {
219
+ const emended: string = route.summary.endsWith(".")
220
+ ? route.summary
221
+ : route.summary + ".";
222
+ if (
223
+ description !== undefined &&
224
+ !description?.startsWith(route.summary) &&
225
+ !route["x-nestia-jsDocTags"]?.some((t) => t.name === "summary")
226
+ )
227
+ description = `${emended}\n${description}`;
215
228
  }
216
229
  if (route.tags) route.tags.forEach((name) => add(`@tag ${name}`));
217
230
  if (route.deprecated) add("@deprecated");
218
231
  for (const security of route.security ?? [])
219
232
  for (const [name, scopes] of Object.entries(security))
220
233
  add(`@security ${[name, ...scopes].join("")}`);
221
- return content.length ? content.join("\n") : undefined;
234
+ for (const jsDocTag of route["x-nestia-jsDocTags"] ?? [])
235
+ if (jsDocTag.text?.length)
236
+ add(
237
+ `@${jsDocTag.name} ${jsDocTag.text
238
+ .map((text) => text.text)
239
+ .join("")}`,
240
+ );
241
+ else add(`@${jsDocTag.name}`);
242
+ return description?.length
243
+ ? commentTags.length
244
+ ? `${description}\n\n${commentTags.join("\n")}`
245
+ : description
246
+ : commentTags.join("\n");
222
247
  };
223
248
 
224
249
  const isNotObjectLiteral = (schema: ISwaggerSchema): boolean =>
@@ -235,14 +260,21 @@ export namespace RouteProgrammer {
235
260
 
236
261
  const emplaceBodySchema =
237
262
  (emplacer: (schema: ISwaggerSchema) => ISwaggerSchema.IReference) =>
238
- (
239
- content?: ISwaggerRoute.IContent,
240
- ): false | null | IMigrateRoute.IBody => {
241
- if (!content) return null;
263
+ (meta?: {
264
+ description?: string;
265
+ content?: ISwaggerRoute.IContent;
266
+ "x-nestia-encrypted"?: boolean;
267
+ }): false | null | IMigrateRoute.IBody => {
268
+ if (!meta?.content) return null;
242
269
 
243
270
  const entries: [string, { schema: ISwaggerSchema }][] =
244
- Object.entries(content);
245
- const json = entries.find((e) => e[0].includes("application/json"));
271
+ Object.entries(meta.content);
272
+ const json = entries.find((e) =>
273
+ meta["x-nestia-encrypted"] === true
274
+ ? e[0].includes("text/plain") ||
275
+ e[0].includes("application/json")
276
+ : e[0].includes("application/json"),
277
+ );
246
278
 
247
279
  if (json) {
248
280
  const { schema } = json[1];
@@ -251,6 +283,7 @@ export namespace RouteProgrammer {
251
283
  schema: isNotObjectLiteral(schema)
252
284
  ? schema
253
285
  : emplacer(schema),
286
+ "x-nestia-encrypted": meta["x-nestia-encrypted"],
254
287
  };
255
288
  }
256
289
 
@@ -269,15 +302,47 @@ export namespace RouteProgrammer {
269
302
  };
270
303
 
271
304
  export const write =
305
+ (importer: (module: string) => (instance: string) => string) =>
306
+ (components: ISwaggerComponents) =>
272
307
  (references: ISwaggerSchema.IReference[]) =>
273
308
  (route: IMigrateRoute): string => {
274
309
  const output: string = route.response
275
- ? SchemaProgrammer.write(references)(route.response.schema)
310
+ ? SchemaProgrammer.write(components)(references)(
311
+ route.response.schema,
312
+ )
276
313
  : "void";
277
- const decorator: string =
278
- route.body?.type === "text/plain"
279
- ? [`@Header("Content-Type", "text/plain")`, `@`].join("\n")
280
- : "@core.TypedRoute.";
314
+
315
+ const methoder = (composer: (name: string) => string) =>
316
+ `${composer(
317
+ StringUtil.capitalize(route.method),
318
+ )}(${JSON.stringify(route.path)})`;
319
+ const decorator: string[] =
320
+ route.response?.["x-nestia-encrypted"] === true
321
+ ? [
322
+ `@${importer("@nestia/core")(
323
+ "EncryptedRoute",
324
+ )}.${methoder((str) => str)}`,
325
+ ]
326
+ : route.response?.type === "text/plain"
327
+ ? [
328
+ `@${importer("@nestjs/common")(
329
+ "Header",
330
+ )}("Content-Type", "text/plain")`,
331
+ `@${methoder((str) =>
332
+ importer("@nestjs/common")(str),
333
+ )}`,
334
+ ]
335
+ : route.method === "head"
336
+ ? [
337
+ `@${importer("@nestjs/common")("Head")}${methoder(
338
+ () => "",
339
+ )}`,
340
+ ]
341
+ : [
342
+ `@${importer("@nestia/core")(
343
+ "TypedRoute",
344
+ )}.${methoder((str) => str)}`,
345
+ ];
281
346
  const content: string[] = [
282
347
  ...(route.description
283
348
  ? [
@@ -288,54 +353,95 @@ export namespace RouteProgrammer {
288
353
  " */",
289
354
  ]
290
355
  : []),
291
- `${decorator}${StringUtil.capitalize(route.method)}${
292
- route.path.length ? `(${JSON.stringify(route.path)})` : "()"
293
- }`,
356
+ ...decorator,
294
357
  `public async ${route.name}(`,
295
- ...route.parameters.map((p) => ` ${writeParameter(p)},`),
358
+ ...route.parameters.map(
359
+ (param) =>
360
+ ` ${writeParameter(components)(importer)(param)},`,
361
+ ),
362
+ ...(route.headers
363
+ ? [
364
+ ` @${importer("@nestia/core")(
365
+ "TypedHeaders",
366
+ )}() headers: ${SchemaProgrammer.write(components)(
367
+ references,
368
+ )(route.headers)},`,
369
+ ]
370
+ : []),
296
371
  ...(route.query
297
372
  ? [
298
- ` @core.TypedQuery() query: ${SchemaProgrammer.write(
373
+ ` @${importer("@nestia/core")(
374
+ "TypedQuery",
375
+ )}() query: ${SchemaProgrammer.write(components)(
299
376
  references,
300
- )(route.query)}`,
377
+ )(route.query)},`,
301
378
  ]
302
379
  : []),
303
380
  ...(route.body
304
- ? route.body.type === "application/json"
381
+ ? route.body["x-nestia-encrypted"] === true
305
382
  ? [
306
- ` @core.TypedBody() body: ${SchemaProgrammer.write(
383
+ ` @${importer("@nestia/core")(
384
+ "EncryptedBody",
385
+ )}() body: ${SchemaProgrammer.write(components)(
307
386
  references,
308
387
  )(route.body.schema)},`,
309
388
  ]
310
- : [` @core.PlainBody() body: string,`]
389
+ : route.body.type === "application/json"
390
+ ? [
391
+ ` @${importer("@nestia/core")(
392
+ "TypedBody",
393
+ )}() body: ${SchemaProgrammer.write(components)(
394
+ references,
395
+ )(route.body.schema)},`,
396
+ ]
397
+ : [
398
+ ` @${importer("@nestia/core")(
399
+ "PlainBody",
400
+ )}() body: string,`,
401
+ ]
311
402
  : []),
312
403
  `): Promise<${output}> {`,
313
404
  ...route.parameters.map(
314
405
  (p) => ` ${StringUtil.normalize(p.key)};`,
315
406
  ),
316
- // ...(route.headers ? ["headers;"] : []),
407
+ ...(route.headers ? [" headers;"] : []),
317
408
  ...(route.query ? [" query;"] : []),
318
409
  ...(route.body ? [" body;"] : []),
319
410
  ...(output !== "void"
320
- ? [` return typia.random<${output}>();`]
411
+ ? [
412
+ ` return ${importer("typia")(
413
+ "random",
414
+ )}<${output}>();`,
415
+ ]
321
416
  : []),
322
417
  `}`,
323
418
  ];
324
419
  return content.join("\n");
325
420
  };
326
421
 
327
- const writeParameter = ({
328
- key,
329
- schema,
330
- }: IMigrateRoute.IParameter): string => {
331
- const variable = StringUtil.normalize(key);
332
- const format =
333
- JsonTypeChecker.isString(schema) &&
334
- (schema.format === "uuid" || schema.format === "date")
335
- ? schema.format
336
- : null;
337
- return `@core.TypedParam(${JSON.stringify(key)}${
338
- format ? `, ${JSON.stringify(format)}` : ""
339
- }) ${variable}: ${SchemaProgrammer.write([])(schema)}`;
340
- };
422
+ const writeParameter =
423
+ (components: ISwaggerComponents) =>
424
+ (importer: (library: string) => (instance: string) => string) =>
425
+ ({ key, schema }: IMigrateRoute.IParameter): string => {
426
+ const variable = StringUtil.normalize(key);
427
+ const format =
428
+ JsonTypeChecker.isString(schema) &&
429
+ (schema.format === "uuid" || schema.format === "date")
430
+ ? schema.format
431
+ : null;
432
+ return `@${importer("@nestia/core")("TypedParam")}(${JSON.stringify(
433
+ key,
434
+ )}${
435
+ format ? `, ${JSON.stringify(format)}` : ""
436
+ }) ${variable}: ${SchemaProgrammer.write(components)([])(schema)}`;
437
+ };
341
438
  }
439
+
440
+ const SUPPORTED_METHODS: Set<string> = new Set([
441
+ "GET",
442
+ "POST",
443
+ "PUT",
444
+ "PATCH",
445
+ "DELETE",
446
+ "HEAD",
447
+ ]);