@nestia/migrate 0.5.0 → 0.6.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.
Files changed (72) hide show
  1. package/lib/NestiaMigrateApplication.js +15 -15
  2. package/lib/NestiaMigrateApplication.js.map +1 -1
  3. package/lib/archivers/FileArchiver.js.map +1 -1
  4. package/lib/bundles/TEMPLATE.js +11 -11
  5. package/lib/bundles/TEMPLATE.js.map +1 -1
  6. package/lib/executable/bundle.js.map +1 -1
  7. package/lib/executable/migrate.js +5 -5
  8. package/lib/executable/migrate.js.map +1 -1
  9. package/lib/programmers/ControllerProgrammer.d.ts +2 -1
  10. package/lib/programmers/ControllerProgrammer.js +21 -18
  11. package/lib/programmers/ControllerProgrammer.js.map +1 -1
  12. package/lib/programmers/DtoProgrammer.d.ts +8 -4
  13. package/lib/programmers/DtoProgrammer.js +36 -77
  14. package/lib/programmers/DtoProgrammer.js.map +1 -1
  15. package/lib/programmers/ImportProgrammer.d.ts +5 -5
  16. package/lib/programmers/ImportProgrammer.js +27 -19
  17. package/lib/programmers/ImportProgrammer.js.map +1 -1
  18. package/lib/programmers/MigrateProgrammer.d.ts +1 -2
  19. package/lib/programmers/MigrateProgrammer.js +52 -36
  20. package/lib/programmers/MigrateProgrammer.js.map +1 -1
  21. package/lib/programmers/ModuleProgrammer.d.ts +5 -0
  22. package/lib/programmers/ModuleProgrammer.js +29 -0
  23. package/lib/programmers/ModuleProgrammer.js.map +1 -0
  24. package/lib/programmers/RouteProgrammer.d.ts +2 -2
  25. package/lib/programmers/RouteProgrammer.js +108 -111
  26. package/lib/programmers/RouteProgrammer.js.map +1 -1
  27. package/lib/programmers/SchemaProgrammer.d.ts +2 -1
  28. package/lib/programmers/SchemaProgrammer.js +122 -189
  29. package/lib/programmers/SchemaProgrammer.js.map +1 -1
  30. package/lib/structures/IMigrateProgram.d.ts +2 -2
  31. package/lib/structures/ISwaggerInfo.d.ts +3 -3
  32. package/lib/utils/FilePrinter.d.ts +9 -0
  33. package/lib/utils/FilePrinter.js +25 -0
  34. package/lib/utils/FilePrinter.js.map +1 -0
  35. package/lib/utils/JsonTypeChecker.d.ts +3 -1
  36. package/lib/utils/JsonTypeChecker.js +31 -18
  37. package/lib/utils/JsonTypeChecker.js.map +1 -1
  38. package/lib/utils/MapUtil.js.map +1 -1
  39. package/lib/utils/SetupWizard.js.map +1 -1
  40. package/lib/utils/StringUtil.js.map +1 -1
  41. package/package.json +11 -9
  42. package/src/NestiaMigrateApplication.ts +68 -73
  43. package/src/archivers/FileArchiver.ts +35 -38
  44. package/src/bundles/TEMPLATE.ts +11 -11
  45. package/src/executable/bundle.ts +72 -78
  46. package/src/executable/migrate.ts +59 -60
  47. package/src/index.ts +4 -4
  48. package/src/module.ts +4 -4
  49. package/src/programmers/ControllerProgrammer.ts +155 -157
  50. package/src/programmers/DtoProgrammer.ts +74 -118
  51. package/src/programmers/ImportProgrammer.ts +98 -60
  52. package/src/programmers/MigrateProgrammer.ts +75 -62
  53. package/src/programmers/ModuleProgrammer.ts +62 -0
  54. package/src/programmers/RouteProgrammer.ts +486 -466
  55. package/src/programmers/SchemaProgrammer.ts +247 -339
  56. package/src/structures/IMigrateController.ts +8 -8
  57. package/src/structures/IMigrateDto.ts +8 -8
  58. package/src/structures/IMigrateFile.ts +5 -5
  59. package/src/structures/IMigrateProgram.ts +7 -7
  60. package/src/structures/IMigrateRoute.ts +36 -36
  61. package/src/structures/IMigrateSchema.ts +4 -4
  62. package/src/structures/ISwaggeSchema.ts +82 -82
  63. package/src/structures/ISwagger.ts +20 -20
  64. package/src/structures/ISwaggerComponents.ts +7 -7
  65. package/src/structures/ISwaggerInfo.ts +57 -57
  66. package/src/structures/ISwaggerRoute.ts +52 -52
  67. package/src/structures/ISwaggerSecurity.ts +47 -47
  68. package/src/utils/FilePrinter.ts +36 -0
  69. package/src/utils/JsonTypeChecker.ts +67 -52
  70. package/src/utils/MapUtil.ts +13 -13
  71. package/src/utils/SetupWizard.ts +15 -15
  72. package/src/utils/StringUtil.ts +51 -51
@@ -1,3 +1,7 @@
1
+ import ts from "typescript";
2
+ import { ExpressionFactory } from "typia/lib/factories/ExpressionFactory";
3
+ import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory";
4
+ import { TypeFactory } from "typia/lib/factories/TypeFactory";
1
5
  import { Escaper } from "typia/lib/utils/Escaper";
2
6
 
3
7
  import { IMigrateRoute } from "../structures/IMigrateRoute";
@@ -5,502 +9,518 @@ import { ISwaggerSchema } from "../structures/ISwaggeSchema";
5
9
  import { ISwagger } from "../structures/ISwagger";
6
10
  import { ISwaggerComponents } from "../structures/ISwaggerComponents";
7
11
  import { ISwaggerRoute } from "../structures/ISwaggerRoute";
8
- import { JsonTypeChecker } from "../utils/JsonTypeChecker";
12
+ import { FilePrinter } from "../utils/FilePrinter";
13
+ import { SwaggerTypeChecker } from "../utils/JsonTypeChecker";
9
14
  import { StringUtil } from "../utils/StringUtil";
10
15
  import { ImportProgrammer } from "./ImportProgrammer";
11
16
  import { SchemaProgrammer } from "./SchemaProgrammer";
12
17
 
13
18
  export namespace RouteProgrammer {
14
- export const analyze =
15
- (swagger: ISwagger) =>
16
- (props: { path: string; method: string }) =>
17
- (route: ISwaggerRoute): IMigrateRoute | null => {
18
- const body = emplaceBodySchema(emplaceReference(swagger)("body"))(
19
- route.requestBody,
20
- );
21
- const success = emplaceBodySchema(
22
- emplaceReference(swagger)("response"),
23
- )(route.responses?.["201"] ?? route.responses?.["200"]);
24
- if (body === false || success === false) {
25
- console.log(
26
- `Failed to migrate ${props.method.toUpperCase()} ${
27
- props.path
28
- }: @nestia/migrate supports only application/json, application/x-www-form-urlencoded or text/plain format yet.`,
29
- );
30
- return null;
31
- } else if (
32
- SUPPORTED_METHODS.has(props.method.toUpperCase()) === false
33
- ) {
34
- console.log(
35
- `Failed to migrate ${props.method.toUpperCase()} ${
36
- props.path
37
- }: @nestia/migrate does not support ${props.method.toUpperCase()} method.`,
38
- );
39
- return null;
40
- }
19
+ /* -----------------------------------------------------------
20
+ ANALYZERS
21
+ ----------------------------------------------------------- */
22
+ export const analyze =
23
+ (swagger: ISwagger) =>
24
+ (props: { path: string; method: string }) =>
25
+ (route: ISwaggerRoute): IMigrateRoute | null => {
26
+ const body = emplaceBodySchema(emplaceReference(swagger)("body"))(
27
+ route.requestBody,
28
+ );
29
+ const success = emplaceBodySchema(emplaceReference(swagger)("response"))(
30
+ route.responses?.["201"] ?? route.responses?.["200"],
31
+ );
32
+ if (body === false || success === false) {
33
+ console.log(
34
+ `Failed to migrate ${props.method.toUpperCase()} ${
35
+ props.path
36
+ }: @nestia/migrate supports only application/json, application/x-www-form-urlencoded or text/plain format yet.`,
37
+ );
38
+ return null;
39
+ } else if (SUPPORTED_METHODS.has(props.method.toUpperCase()) === false) {
40
+ console.log(
41
+ `Failed to migrate ${props.method.toUpperCase()} ${
42
+ props.path
43
+ }: @nestia/migrate does not support ${props.method.toUpperCase()} method.`,
44
+ );
45
+ return null;
46
+ }
41
47
 
42
- const [headers, query] = ["header", "query"].map((type) => {
43
- const parameters = (route.parameters ?? []).filter(
44
- (p) => p.in === type,
45
- );
46
- if (parameters.length === 0) return null;
48
+ const [headers, query] = ["header", "query"].map((type) => {
49
+ const parameters = (route.parameters ?? []).filter(
50
+ (p) => p.in === type,
51
+ );
52
+ if (parameters.length === 0) return null;
47
53
 
48
- const objects = parameters
49
- .map((p) =>
50
- JsonTypeChecker.isObject(p.schema)
51
- ? p.schema
52
- : JsonTypeChecker.isReference(p.schema) &&
53
- JsonTypeChecker.isObject(
54
- (swagger.components.schemas ?? {})[
55
- p.schema.$ref.replace(
56
- `#/components/schemas/`,
57
- ``,
58
- )
59
- ] ?? {},
60
- )
61
- ? p.schema
62
- : null!,
63
- )
64
- .filter((s) => !!s);
65
- const primitives = parameters.filter(
66
- (p) =>
67
- JsonTypeChecker.isBoolean(p.schema) ||
68
- JsonTypeChecker.isNumber(p.schema) ||
69
- JsonTypeChecker.isInteger(p.schema) ||
70
- JsonTypeChecker.isString(p.schema) ||
71
- JsonTypeChecker.isArray(p.schema),
72
- );
73
- if (objects.length === 1 && primitives.length === 0)
74
- return objects[0];
75
- else if (objects.length > 1)
76
- throw new Error(
77
- `Error on nestia.migrate.RouteProgrammer.analze(): ${type} typed parameters must be only one object type - ${StringUtil.capitalize(
78
- props.method,
79
- )} "${props.path}".`,
80
- );
81
-
82
- const dto: ISwaggerSchema.IObject | null = objects[0]
83
- ? JsonTypeChecker.isObject(objects[0])
84
- ? objects[0]
85
- : ((swagger.components.schemas ?? {})[
86
- (
87
- objects[0] as ISwaggerSchema.IReference
88
- ).$ref.replace(`#/components/schemas/`, ``)
89
- ] as ISwaggerSchema.IObject)
90
- : null;
91
- const entire: ISwaggerSchema.IObject[] = [
92
- ...objects.map((o) =>
93
- JsonTypeChecker.isObject(o)
94
- ? o
95
- : (swagger.components.schemas?.[
96
- o.$ref.replace(`#/components/schemas/`, ``)
97
- ]! as ISwaggerSchema.IObject),
98
- ),
99
- {
100
- type: "object",
101
- properties: Object.fromEntries([
102
- ...primitives.map((p) => [
103
- p.name,
104
- {
105
- ...p.schema,
106
- description:
107
- p.schema.description ?? p.description,
108
- },
109
- ]),
110
- ...(dto
111
- ? Object.entries(dto.properties ?? {})
112
- : []),
113
- ]),
114
- required: [
115
- ...primitives
116
- .filter((p) => p.required)
117
- .map((p) => p.name!),
118
- ...(dto ? dto.required ?? [] : []),
119
- ],
120
- },
121
- ];
122
- return parameters.length === 0
123
- ? null
124
- : emplaceReference(swagger)(
125
- StringUtil.pascal(`I/Api/${props.path}`) +
126
- "." +
127
- StringUtil.pascal(`${props.method}/${type}`),
128
- )({
129
- type: "object",
130
- properties: Object.fromEntries([
131
- ...new Map<string, ISwaggerSchema>(
132
- entire
133
- .map((o) =>
134
- Object.entries(
135
- o.properties ?? {},
136
- ).map(
137
- ([name, schema]) =>
138
- [
139
- name,
140
- {
141
- ...schema,
142
- description:
143
- schema.description ??
144
- schema.description,
145
- } as ISwaggerSchema,
146
- ] as const,
147
- ),
148
- )
149
- .flat(),
150
- ),
151
- ]),
152
- required: [
153
- ...new Set(
154
- entire.map((o) => o.required ?? []).flat(),
155
- ),
156
- ],
157
- });
158
- });
159
-
160
- const parameterNames: string[] = StringUtil.splitWithNormalization(
161
- props.path,
162
- )
163
- .filter((str) => str[0] === "{" || str[0] === ":")
164
- .map((str) =>
165
- str[0] === "{"
166
- ? str.substring(1, str.length - 1)
167
- : str.substring(1),
168
- );
169
- if (
170
- parameterNames.length !==
171
- (route.parameters ?? []).filter((p) => p.in === "path").length
172
- ) {
173
- console.log(
174
- `Failed to migrate ${props.method.toUpperCase()} ${
175
- props.path
176
- }: number of path parameters are not matched with its full path.`,
177
- );
178
- return null;
179
- }
180
- return {
181
- name: "@lazy",
182
- path: props.path,
183
- method: props.method,
184
- headers,
185
- parameters: (route.parameters ?? [])
186
- .filter((p) => p.in === "path")
187
- .map((p, i) => ({
188
- key: (() => {
189
- let key: string = StringUtil.normalize(
190
- parameterNames[i],
191
- );
192
- if (Escaper.variable(key)) return key;
54
+ const objects = parameters
55
+ .map((p) =>
56
+ SwaggerTypeChecker.isObject(p.schema)
57
+ ? p.schema
58
+ : SwaggerTypeChecker.isReference(p.schema) &&
59
+ SwaggerTypeChecker.isObject(
60
+ (swagger.components.schemas ?? {})[
61
+ p.schema.$ref.replace(`#/components/schemas/`, ``)
62
+ ] ?? {},
63
+ )
64
+ ? p.schema
65
+ : null!,
66
+ )
67
+ .filter((s) => !!s);
68
+ const primitives = parameters.filter(
69
+ (p) =>
70
+ SwaggerTypeChecker.isBoolean(p.schema) ||
71
+ SwaggerTypeChecker.isNumber(p.schema) ||
72
+ SwaggerTypeChecker.isInteger(p.schema) ||
73
+ SwaggerTypeChecker.isString(p.schema) ||
74
+ SwaggerTypeChecker.isArray(p.schema),
75
+ );
76
+ if (objects.length === 1 && primitives.length === 0) return objects[0];
77
+ else if (objects.length > 1)
78
+ throw new Error(
79
+ `Error on nestia.migrate.RouteProgrammer.analze(): ${type} typed parameters must be only one object type - ${StringUtil.capitalize(
80
+ props.method,
81
+ )} "${props.path}".`,
82
+ );
193
83
 
194
- while (true) {
195
- key = "_" + key;
196
- if (!parameterNames.some((s) => s === key))
197
- return key;
198
- }
199
- })(),
200
- schema: {
201
- ...p.schema,
202
- description: p.schema.description ?? p.description,
203
- },
204
- })),
205
- query,
206
- body,
207
- success,
208
- exceptions: Object.fromEntries(
209
- Object.entries(route.responses ?? {})
210
- .filter(
211
- ([key, value]) =>
212
- key !== "200" &&
213
- key !== "201" &&
214
- !!value.content?.["application/json"],
215
- )
216
- .map(([key, value]) => [
217
- key,
84
+ const dto: ISwaggerSchema.IObject | null = objects[0]
85
+ ? SwaggerTypeChecker.isObject(objects[0])
86
+ ? objects[0]
87
+ : ((swagger.components.schemas ?? {})[
88
+ (objects[0] as ISwaggerSchema.IReference).$ref.replace(
89
+ `#/components/schemas/`,
90
+ ``,
91
+ )
92
+ ] as ISwaggerSchema.IObject)
93
+ : null;
94
+ const entire: ISwaggerSchema.IObject[] = [
95
+ ...objects.map((o) =>
96
+ SwaggerTypeChecker.isObject(o)
97
+ ? o
98
+ : (swagger.components.schemas?.[
99
+ o.$ref.replace(`#/components/schemas/`, ``)
100
+ ]! as ISwaggerSchema.IObject),
101
+ ),
102
+ {
103
+ type: "object",
104
+ properties: Object.fromEntries([
105
+ ...primitives.map((p) => [
106
+ p.name,
107
+ {
108
+ ...p.schema,
109
+ description: p.schema.description ?? p.description,
110
+ },
111
+ ]),
112
+ ...(dto ? Object.entries(dto.properties ?? {}) : []),
113
+ ]),
114
+ required: [
115
+ ...primitives.filter((p) => p.required).map((p) => p.name!),
116
+ ...(dto ? dto.required ?? [] : []),
117
+ ],
118
+ },
119
+ ];
120
+ return parameters.length === 0
121
+ ? null
122
+ : emplaceReference(swagger)(
123
+ StringUtil.pascal(`I/Api/${props.path}`) +
124
+ "." +
125
+ StringUtil.pascal(`${props.method}/${type}`),
126
+ )({
127
+ type: "object",
128
+ properties: Object.fromEntries([
129
+ ...new Map<string, ISwaggerSchema>(
130
+ entire
131
+ .map((o) =>
132
+ Object.entries(o.properties ?? {}).map(
133
+ ([name, schema]) =>
134
+ [
135
+ name,
218
136
  {
219
- description: value.description,
220
- schema:
221
- value.content?.["application/json"]
222
- ?.schema ?? {},
223
- },
224
- ]),
137
+ ...schema,
138
+ description:
139
+ schema.description ?? schema.description,
140
+ } as ISwaggerSchema,
141
+ ] as const,
142
+ ),
143
+ )
144
+ .flat(),
225
145
  ),
226
- description: describe(route),
227
- "x-nestia-jsDocTags": route["x-nestia-jsDocTags"],
228
- };
229
- };
146
+ ]),
147
+ required: [
148
+ ...new Set(entire.map((o) => o.required ?? []).flat()),
149
+ ],
150
+ });
151
+ });
230
152
 
231
- const describe = (route: ISwaggerRoute): string | undefined => {
232
- const commentTags: string[] = [];
233
- const add = (text: string) => {
234
- if (commentTags.every((line) => line !== text))
235
- commentTags.push(text);
236
- };
153
+ const parameterNames: string[] = StringUtil.splitWithNormalization(
154
+ props.path,
155
+ )
156
+ .filter((str) => str[0] === "{" || str[0] === ":")
157
+ .map((str) =>
158
+ str[0] === "{" ? str.substring(1, str.length - 1) : str.substring(1),
159
+ );
160
+ if (
161
+ parameterNames.length !==
162
+ (route.parameters ?? []).filter((p) => p.in === "path").length
163
+ ) {
164
+ console.log(
165
+ `Failed to migrate ${props.method.toUpperCase()} ${
166
+ props.path
167
+ }: number of path parameters are not matched with its full path.`,
168
+ );
169
+ return null;
170
+ }
171
+ return {
172
+ name: "@lazy",
173
+ path: props.path,
174
+ method: props.method,
175
+ headers,
176
+ parameters: (route.parameters ?? [])
177
+ .filter((p) => p.in === "path")
178
+ .map((p, i) => ({
179
+ key: (() => {
180
+ let key: string = StringUtil.normalize(parameterNames[i]);
181
+ if (Escaper.variable(key)) return key;
237
182
 
238
- let description: string | undefined = route.description;
239
- if (route.summary) {
240
- const emended: string = route.summary.endsWith(".")
241
- ? route.summary
242
- : route.summary + ".";
243
- if (
244
- description !== undefined &&
245
- !description?.startsWith(route.summary) &&
246
- !route["x-nestia-jsDocTags"]?.some((t) => t.name === "summary")
183
+ while (true) {
184
+ key = "_" + key;
185
+ if (!parameterNames.some((s) => s === key)) return key;
186
+ }
187
+ })(),
188
+ schema: {
189
+ ...p.schema,
190
+ description: p.schema.description ?? p.description,
191
+ },
192
+ })),
193
+ query,
194
+ body,
195
+ success,
196
+ exceptions: Object.fromEntries(
197
+ Object.entries(route.responses ?? {})
198
+ .filter(
199
+ ([key, value]) =>
200
+ key !== "200" &&
201
+ key !== "201" &&
202
+ !!value.content?.["application/json"],
247
203
  )
248
- description = `${emended}\n${description}`;
249
- }
250
- if (route.tags) route.tags.forEach((name) => add(`@tag ${name}`));
251
- if (route.deprecated) add("@deprecated");
252
- for (const security of route.security ?? [])
253
- for (const [name, scopes] of Object.entries(security))
254
- add(`@security ${[name, ...scopes].join("")}`);
255
- for (const jsDocTag of route["x-nestia-jsDocTags"] ?? [])
256
- if (jsDocTag.text?.length)
257
- add(
258
- `@${jsDocTag.name} ${jsDocTag.text
259
- .map((text) => text.text)
260
- .join("")}`,
261
- );
262
- else add(`@${jsDocTag.name}`);
263
- return description?.length
264
- ? commentTags.length
265
- ? `${description}\n\n${commentTags.join("\n")}`
266
- : description
267
- : commentTags.join("\n");
204
+ .map(([key, value]) => [
205
+ key,
206
+ {
207
+ description: value.description,
208
+ schema: value.content?.["application/json"]?.schema ?? {},
209
+ },
210
+ ]),
211
+ ),
212
+ description: describe(route),
213
+ "x-nestia-jsDocTags": route["x-nestia-jsDocTags"],
214
+ };
268
215
  };
269
216
 
270
- const isNotObjectLiteral = (schema: ISwaggerSchema): boolean =>
271
- JsonTypeChecker.isReference(schema) ||
272
- JsonTypeChecker.isBoolean(schema) ||
273
- JsonTypeChecker.isNumber(schema) ||
274
- JsonTypeChecker.isString(schema) ||
275
- JsonTypeChecker.isUnknown(schema) ||
276
- (JsonTypeChecker.isAnyOf(schema) &&
277
- schema.anyOf.every(isNotObjectLiteral)) ||
278
- (JsonTypeChecker.isOneOf(schema) &&
279
- schema.oneOf.every(isNotObjectLiteral)) ||
280
- (JsonTypeChecker.isArray(schema) && isNotObjectLiteral(schema.items));
217
+ const describe = (route: ISwaggerRoute): string | undefined => {
218
+ const commentTags: string[] = [];
219
+ const add = (text: string) => {
220
+ if (commentTags.every((line) => line !== text)) commentTags.push(text);
221
+ };
281
222
 
282
- const emplaceBodySchema =
283
- (emplacer: (schema: ISwaggerSchema) => ISwaggerSchema.IReference) =>
284
- (meta?: {
285
- description?: string;
286
- content?: ISwaggerRoute.IContent;
287
- "x-nestia-encrypted"?: boolean;
288
- }): false | null | IMigrateRoute.IBody => {
289
- if (!meta?.content) return null;
223
+ let description: string | undefined = route.description;
224
+ if (route.summary) {
225
+ const emended: string = route.summary.endsWith(".")
226
+ ? route.summary
227
+ : route.summary + ".";
228
+ if (
229
+ description !== undefined &&
230
+ !description?.startsWith(route.summary) &&
231
+ !route["x-nestia-jsDocTags"]?.some((t) => t.name === "summary")
232
+ )
233
+ description = `${emended}\n${description}`;
234
+ }
235
+ if (route.tags) route.tags.forEach((name) => add(`@tag ${name}`));
236
+ if (route.deprecated) add("@deprecated");
237
+ for (const security of route.security ?? [])
238
+ for (const [name, scopes] of Object.entries(security))
239
+ add(`@security ${[name, ...scopes].join("")}`);
240
+ for (const jsDocTag of route["x-nestia-jsDocTags"] ?? [])
241
+ if (jsDocTag.text?.length)
242
+ add(
243
+ `@${jsDocTag.name} ${jsDocTag.text
244
+ .map((text) => text.text)
245
+ .join("")}`,
246
+ );
247
+ else add(`@${jsDocTag.name}`);
248
+ return description?.length
249
+ ? commentTags.length
250
+ ? `${description}\n\n${commentTags.join("\n")}`
251
+ : description
252
+ : commentTags.join("\n");
253
+ };
290
254
 
291
- const entries: [string, { schema: ISwaggerSchema }][] =
292
- Object.entries(meta.content);
293
- const json = entries.find((e) =>
294
- meta["x-nestia-encrypted"] === true
295
- ? e[0].includes("text/plain") ||
296
- e[0].includes("application/json")
297
- : e[0].includes("application/json") || e[0].includes("*/*"),
298
- );
299
- if (json) {
300
- const { schema } = json[1];
301
- return {
302
- type: "application/json",
303
- schema: isNotObjectLiteral(schema)
304
- ? schema
305
- : emplacer(schema),
306
- "x-nestia-encrypted": meta["x-nestia-encrypted"],
307
- };
308
- }
255
+ const isNotObjectLiteral = (schema: ISwaggerSchema): boolean =>
256
+ SwaggerTypeChecker.isReference(schema) ||
257
+ SwaggerTypeChecker.isBoolean(schema) ||
258
+ SwaggerTypeChecker.isNumber(schema) ||
259
+ SwaggerTypeChecker.isString(schema) ||
260
+ SwaggerTypeChecker.isUnknown(schema) ||
261
+ (SwaggerTypeChecker.isAnyOf(schema) &&
262
+ schema.anyOf.every(isNotObjectLiteral)) ||
263
+ (SwaggerTypeChecker.isOneOf(schema) &&
264
+ schema.oneOf.every(isNotObjectLiteral)) ||
265
+ (SwaggerTypeChecker.isArray(schema) && isNotObjectLiteral(schema.items));
309
266
 
310
- const query = entries.find((e) =>
311
- e[0].includes("application/x-www-form-urlencoded"),
312
- );
313
- if (query) {
314
- const { schema } = query[1];
315
- return {
316
- type: "application/x-www-form-urlencoded",
317
- schema: isNotObjectLiteral(schema)
318
- ? schema
319
- : emplacer(schema),
320
- };
321
- }
267
+ const emplaceBodySchema =
268
+ (emplacer: (schema: ISwaggerSchema) => ISwaggerSchema.IReference) =>
269
+ (meta?: {
270
+ description?: string;
271
+ content?: ISwaggerRoute.IContent;
272
+ "x-nestia-encrypted"?: boolean;
273
+ }): false | null | IMigrateRoute.IBody => {
274
+ if (!meta?.content) return null;
322
275
 
323
- const text = entries.find((e) => e[0].includes("text/plain"));
324
- if (text) return { type: "text/plain", schema: { type: "string" } };
325
- return false;
276
+ const entries: [string, { schema: ISwaggerSchema }][] = Object.entries(
277
+ meta.content,
278
+ );
279
+ const json = entries.find((e) =>
280
+ meta["x-nestia-encrypted"] === true
281
+ ? e[0].includes("text/plain") || e[0].includes("application/json")
282
+ : e[0].includes("application/json") || e[0].includes("*/*"),
283
+ );
284
+ if (json) {
285
+ const { schema } = json[1];
286
+ return {
287
+ type: "application/json",
288
+ schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
289
+ "x-nestia-encrypted": meta["x-nestia-encrypted"],
326
290
  };
291
+ }
327
292
 
328
- const emplaceReference =
329
- (swagger: ISwagger) =>
330
- (name: string) =>
331
- (schema: ISwaggerSchema): ISwaggerSchema.IReference => {
332
- swagger.components.schemas ??= {};
333
- swagger.components.schemas[name] = schema;
334
- return { $ref: `#/components/schemas/${name}` };
293
+ const query = entries.find((e) =>
294
+ e[0].includes("application/x-www-form-urlencoded"),
295
+ );
296
+ if (query) {
297
+ const { schema } = query[1];
298
+ return {
299
+ type: "application/x-www-form-urlencoded",
300
+ schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
335
301
  };
302
+ }
336
303
 
337
- export const write =
338
- (components: ISwaggerComponents) =>
339
- (references: ISwaggerSchema.IReference[]) =>
340
- (importer: ImportProgrammer) =>
341
- (route: IMigrateRoute): string => {
342
- const output: string = route.success
343
- ? SchemaProgrammer.write(components)(references)(importer)(
344
- route.success.schema,
345
- )
346
- : "void";
304
+ const text = entries.find((e) => e[0].includes("text/plain"));
305
+ if (text) return { type: "text/plain", schema: { type: "string" } };
306
+ return false;
307
+ };
347
308
 
348
- const methoder = (composer: (name: string) => string) =>
349
- `${composer(
350
- StringUtil.capitalize(route.method),
351
- )}(${JSON.stringify(route.path)})`;
352
- const external = (lib: string) => (instance: string) =>
353
- importer.external({ library: lib, instance });
309
+ const emplaceReference =
310
+ (swagger: ISwagger) =>
311
+ (name: string) =>
312
+ (schema: ISwaggerSchema): ISwaggerSchema.IReference => {
313
+ swagger.components.schemas ??= {};
314
+ swagger.components.schemas[name] = schema;
315
+ return { $ref: `#/components/schemas/${name}` };
316
+ };
354
317
 
355
- const decorator: string[] =
356
- route.success?.["x-nestia-encrypted"] === true
357
- ? [
358
- `@${external("@nestia/core")(
359
- "EncryptedRoute",
360
- )}.${methoder((str) => str)}`,
361
- ]
362
- : route.success?.type === "text/plain"
363
- ? [
364
- `@${external("@nestjs/common")(
365
- "Header",
366
- )}("Content-Type", "text/plain")`,
367
- `@${methoder((str) =>
368
- external("@nestjs/common")(str),
369
- )}`,
370
- ]
371
- : route.success?.type ===
372
- "application/x-www-form-urlencoded"
373
- ? [
374
- `@${external("@nestia/core")(
375
- "TypedQuery",
376
- )}.${methoder((str) => str)}`,
377
- ]
378
- : route.method === "head"
379
- ? [
380
- `@${external("@nestjs/common")("Head")}${methoder(
381
- () => "",
382
- )}`,
383
- ]
384
- : [
385
- `@${external("@nestia/core")(
386
- "TypedRoute",
387
- )}.${methoder((str) => str)}`,
388
- ];
389
- for (const [key, value] of Object.entries(route.exceptions ?? {}))
390
- decorator.push(
391
- `@${external("@nestia/core")(
392
- "TypedException",
393
- )}<${SchemaProgrammer.write(components)(references)(
394
- importer,
395
- )(value.schema)}>(${
396
- isNaN(Number(key)) ? JSON.stringify(key) : key
397
- }, ${JSON.stringify(value.description)})`,
398
- );
318
+ /* -----------------------------------------------------------
319
+ WRITERS
320
+ ----------------------------------------------------------- */
321
+ export const write =
322
+ (components: ISwaggerComponents) =>
323
+ (importer: ImportProgrammer) =>
324
+ (route: IMigrateRoute): ts.MethodDeclaration => {
325
+ const output: ts.TypeNode = route.success
326
+ ? SchemaProgrammer.write(importer)(components)(route.success.schema)
327
+ : TypeFactory.keyword("void");
399
328
 
400
- const content: string[] = [
401
- ...(route.description
402
- ? [
403
- "/**",
404
- ...route.description
405
- .split("\n")
406
- .map((line) => ` * ${line}`),
407
- " */",
408
- ]
409
- : []),
410
- ...decorator,
411
- `public async ${route.name}(`,
412
- ...route.parameters.map(
413
- (param) =>
414
- ` ${writeParameter(components)(importer)(param)},`,
329
+ const method: ts.MethodDeclaration = ts.factory.createMethodDeclaration(
330
+ [
331
+ ...writeMethodDecorators(components)(importer)(route),
332
+ ts.factory.createToken(ts.SyntaxKind.PublicKeyword),
333
+ ts.factory.createToken(ts.SyntaxKind.AsyncKeyword),
334
+ ],
335
+ undefined,
336
+ route.name,
337
+ undefined,
338
+ undefined,
339
+ writeParameters(importer)(components)(route),
340
+ ts.factory.createTypeReferenceNode("Promise", [output]),
341
+ ts.factory.createBlock(
342
+ [
343
+ ...[
344
+ ...route.parameters.map((p) => StringUtil.normalize(p.key)),
345
+ ...(route.headers ? ["headers"] : []),
346
+ ...(route.query ? ["query"] : []),
347
+ ...(route.body ? ["body"] : []),
348
+ ].map((str) =>
349
+ ts.factory.createExpressionStatement(
350
+ ts.factory.createIdentifier(str),
351
+ ),
352
+ ),
353
+ ts.factory.createReturnStatement(
354
+ ts.factory.createCallExpression(
355
+ ts.factory.createIdentifier(
356
+ importer.external({
357
+ library: "typia",
358
+ instance: "random",
359
+ }),
415
360
  ),
416
- ...(route.headers
417
- ? [
418
- ` @${external("@nestia/core")(
419
- "TypedHeaders",
420
- )}() headers: ${SchemaProgrammer.write(components)(
421
- references,
422
- )(importer)(route.headers)},`,
423
- ]
424
- : []),
425
- ...(route.query
426
- ? [
427
- ` @${external("@nestia/core")(
428
- "TypedQuery",
429
- )}() query: ${SchemaProgrammer.write(components)(
430
- references,
431
- )(importer)(route.query)},`,
432
- ]
433
- : []),
434
- ...(route.body
435
- ? route.body["x-nestia-encrypted"] === true
436
- ? [
437
- ` @${external("@nestia/core")(
438
- "EncryptedBody",
439
- )}() body: ${SchemaProgrammer.write(components)(
440
- references,
441
- )(importer)(route.body.schema)},`,
442
- ]
443
- : route.body.type === "application/json"
444
- ? [
445
- ` @${external("@nestia/core")(
446
- "TypedBody",
447
- )}() body: ${SchemaProgrammer.write(components)(
448
- references,
449
- )(importer)(route.body.schema)},`,
450
- ]
451
- : route.body.type ===
452
- "application/x-www-form-urlencoded"
453
- ? [
454
- ` @${external("@nestia/core")(
455
- "TypedQuery",
456
- )}.Body() body: ${SchemaProgrammer.write(
457
- components,
458
- )(references)(importer)(route.body.schema)},`,
459
- ]
460
- : [
461
- ` @${external("@nestia/core")(
462
- "PlainBody",
463
- )}() body: string,`,
464
- ]
465
- : []),
466
- `): Promise<${output}> {`,
467
- ...route.parameters.map(
468
- (p) => ` ${StringUtil.normalize(p.key)};`,
361
+ [output],
362
+ undefined,
363
+ ),
364
+ ),
365
+ ],
366
+ true,
367
+ ),
368
+ );
369
+ return FilePrinter.description(method, route.description ?? "");
370
+ };
371
+
372
+ const writeMethodDecorators =
373
+ (components: ISwaggerComponents) =>
374
+ (importer: ImportProgrammer) =>
375
+ (route: IMigrateRoute): ts.Decorator[] => {
376
+ const external =
377
+ (lib: string) =>
378
+ (instance: string): ts.Identifier =>
379
+ ts.factory.createIdentifier(
380
+ importer.external({ library: lib, instance }),
381
+ );
382
+ const router = (instance: string) =>
383
+ ts.factory.createDecorator(
384
+ ts.factory.createCallExpression(
385
+ IdentifierFactory.access(external("@nestia/core")(instance))(
386
+ StringUtil.capitalize(route.method),
387
+ ),
388
+ [],
389
+ [ts.factory.createStringLiteral(route.path)],
390
+ ),
391
+ );
392
+
393
+ const decorators: ts.Decorator[] = [];
394
+ if (route.success?.["x-nestia-encrypted"])
395
+ decorators.push(router("EncryptedRoute"));
396
+ else if (route.success?.type === "text/plain")
397
+ decorators.push(
398
+ ts.factory.createDecorator(
399
+ ts.factory.createCallExpression(
400
+ external("@nestjs/common")(StringUtil.capitalize(route.method)),
401
+ [],
402
+ [ts.factory.createStringLiteral(route.path)],
403
+ ),
404
+ ),
405
+ );
406
+ else if (route.success?.type === "application/x-www-form-urlencoded")
407
+ decorators.push(router("TypedQuery"));
408
+ else if (route.success?.type === "application/json")
409
+ decorators.push(router("TypedRoute"));
410
+ else if (route.method === "head")
411
+ decorators.push(
412
+ ts.factory.createDecorator(
413
+ ts.factory.createCallExpression(
414
+ external("@nestjs/common")("Head"),
415
+ [],
416
+ [ts.factory.createStringLiteral(route.path)],
417
+ ),
418
+ ),
419
+ );
420
+ for (const [key, value] of Object.entries(route.exceptions ?? {}))
421
+ decorators.push(
422
+ ts.factory.createDecorator(
423
+ ts.factory.createCallExpression(
424
+ external("@nestia/core")("TypedException"),
425
+ [SchemaProgrammer.write(importer)(components)(value.schema)],
426
+ [
427
+ isNaN(Number(key))
428
+ ? ts.factory.createStringLiteral(key)
429
+ : ExpressionFactory.number(Number(key)),
430
+ ...(value.description?.length
431
+ ? [ts.factory.createStringLiteral(value.description)]
432
+ : []),
433
+ ],
434
+ ),
435
+ ),
436
+ );
437
+ return decorators;
438
+ };
439
+
440
+ const writeParameters =
441
+ (importer: ImportProgrammer) =>
442
+ (components: ISwaggerComponents) =>
443
+ (route: IMigrateRoute): ts.ParameterDeclaration[] => [
444
+ ...route.parameters.map(({ key, schema: value }) =>
445
+ ts.factory.createParameterDeclaration(
446
+ [
447
+ ts.factory.createDecorator(
448
+ ts.factory.createCallExpression(
449
+ ts.factory.createIdentifier(
450
+ importer.external({
451
+ library: "@nestia/core",
452
+ instance: "TypedParam",
453
+ }),
469
454
  ),
470
- ...(route.headers ? [" headers;"] : []),
471
- ...(route.query ? [" query;"] : []),
472
- ...(route.body ? [" body;"] : []),
473
- ...(output !== "void"
474
- ? [
475
- ` return ${external("typia")(
476
- "random",
477
- )}<${output}>();`,
478
- ]
479
- : []),
480
- `}`,
481
- ];
482
- return content.join("\n");
483
- };
455
+ undefined,
456
+ [ts.factory.createStringLiteral(key)],
457
+ ),
458
+ ),
459
+ ],
460
+ undefined,
461
+ StringUtil.normalize(key),
462
+ undefined,
463
+ SchemaProgrammer.write(importer)(components)(value),
464
+ ),
465
+ ),
466
+ ...(route.headers
467
+ ? [
468
+ writeDtoParameter({ method: "TypedHeaders", variable: "headers" })(
469
+ importer,
470
+ )(components)(route.headers),
471
+ ]
472
+ : []),
473
+ ...(route.query
474
+ ? [
475
+ writeDtoParameter({ method: "TypedQuery", variable: "query" })(
476
+ importer,
477
+ )(components)(route.query),
478
+ ]
479
+ : []),
480
+ ...(route.body
481
+ ? [
482
+ writeDtoParameter({
483
+ method: route.body?.["x-nestia-encrypted"]
484
+ ? "EncryptedBody"
485
+ : "TypedBody",
486
+ variable: "body",
487
+ })(importer)(components)(route.body.schema),
488
+ ]
489
+ : []),
490
+ ];
484
491
 
485
- const writeParameter =
486
- (components: ISwaggerComponents) =>
487
- (importer: ImportProgrammer) =>
488
- ({ key, schema }: IMigrateRoute.IParameter): string => {
489
- const variable = StringUtil.normalize(key);
490
- return `@${importer.external({
491
- library: "@nestia/core",
492
- instance: "TypedParam",
493
- })}(${JSON.stringify(key)}) ${variable}: ${SchemaProgrammer.write(
494
- components,
495
- )([])(importer)(schema)}`;
496
- };
492
+ const writeDtoParameter =
493
+ (accessor: { method: string; variable: string }) =>
494
+ (importer: ImportProgrammer) =>
495
+ (components: ISwaggerComponents) =>
496
+ (schema: ISwaggerSchema): ts.ParameterDeclaration =>
497
+ ts.factory.createParameterDeclaration(
498
+ [
499
+ ts.factory.createDecorator(
500
+ ts.factory.createCallExpression(
501
+ ts.factory.createIdentifier(
502
+ importer.external({
503
+ library: "@nestia/core",
504
+ instance: accessor.method,
505
+ }),
506
+ ),
507
+ undefined,
508
+ undefined,
509
+ ),
510
+ ),
511
+ ],
512
+ undefined,
513
+ StringUtil.normalize(accessor.variable),
514
+ undefined,
515
+ SchemaProgrammer.write(importer)(components)(schema),
516
+ );
497
517
  }
498
518
 
499
519
  const SUPPORTED_METHODS: Set<string> = new Set([
500
- "GET",
501
- "POST",
502
- "PUT",
503
- "PATCH",
504
- "DELETE",
505
- "HEAD",
520
+ "GET",
521
+ "POST",
522
+ "PUT",
523
+ "PATCH",
524
+ "DELETE",
525
+ "HEAD",
506
526
  ]);