@nestia/migrate 0.7.6 → 0.7.8

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 (76) hide show
  1. package/lib/MigrateApplication.js +7 -4
  2. package/lib/MigrateApplication.js.map +1 -1
  3. package/lib/analyzers/MigrateAnalyzer.d.ts +2 -3
  4. package/lib/analyzers/MigrateAnalyzer.js +1 -5
  5. package/lib/analyzers/MigrateAnalyzer.js.map +1 -1
  6. package/lib/analyzers/MigrateControllerAnalyzer.d.ts +2 -2
  7. package/lib/analyzers/MigrateControllerAnalyzer.js +22 -13
  8. package/lib/analyzers/MigrateControllerAnalyzer.js.map +1 -1
  9. package/lib/analyzers/MigrateMethodAnalyzer.d.ts +2 -2
  10. package/lib/analyzers/MigrateMethodAnalyzer.js +16 -16
  11. package/lib/analyzers/MigrateMethodAnalyzer.js.map +1 -1
  12. package/lib/bundles/NEST_TEMPLATE.js +2 -2
  13. package/lib/bundles/SDK_TEMPLATE.js +1 -1
  14. package/lib/internal/MigrateCommander.js +12 -1
  15. package/lib/internal/MigrateCommander.js.map +1 -1
  16. package/lib/module.d.ts +8 -0
  17. package/lib/module.js +8 -0
  18. package/lib/module.js.map +1 -1
  19. package/lib/programmers/MigrateApiFileProgrammer.d.ts +2 -2
  20. package/lib/programmers/MigrateApiFileProgrammer.js.map +1 -1
  21. package/lib/programmers/MigrateApiFunctionProgrammer.d.ts +2 -2
  22. package/lib/programmers/MigrateApiFunctionProgrammer.js.map +1 -1
  23. package/lib/programmers/MigrateApiNamespaceProgrammer.d.ts +2 -2
  24. package/lib/programmers/MigrateApiNamespaceProgrammer.js.map +1 -1
  25. package/lib/programmers/MigrateApiProgrammer.js +3 -3
  26. package/lib/programmers/MigrateApiProgrammer.js.map +1 -1
  27. package/lib/programmers/MigrateImportProgrammer.js +5 -5
  28. package/lib/programmers/MigrateImportProgrammer.js.map +1 -1
  29. package/lib/programmers/MigrateNestModuleProgrammer.js +1 -1
  30. package/lib/programmers/MigrateNestModuleProgrammer.js.map +1 -1
  31. package/lib/programmers/MigrateSchemaProgrammer.d.ts +1 -1
  32. package/lib/structures/IMigrateDto.d.ts +1 -1
  33. package/lib/structures/IMigrateProgram.d.ts +20 -4
  34. package/lib/structures/IMigrateRoute.d.ts +1 -1
  35. package/lib/structures/ISwaggerComponents.d.ts +1 -1
  36. package/lib/structures/ISwaggerRoute.d.ts +1 -1
  37. package/lib/structures/{ISwaggeSchema.js → ISwaggerSchema.js} +1 -1
  38. package/lib/structures/ISwaggerSchema.js.map +1 -0
  39. package/lib/utils/SwaggerTypeChecker.d.ts +1 -1
  40. package/package.json +70 -68
  41. package/src/MigrateApplication.ts +54 -48
  42. package/src/analyzers/MigrateAnalyzer.ts +9 -13
  43. package/src/analyzers/MigrateControllerAnalyzer.ts +139 -122
  44. package/src/analyzers/MigrateMethodAnalyzer.ts +350 -348
  45. package/src/archivers/MigrateFileArchiver.ts +38 -38
  46. package/src/bundles/NEST_TEMPLATE.ts +2 -2
  47. package/src/bundles/SDK_TEMPLATE.ts +1 -1
  48. package/src/executable/bundle.ts +109 -109
  49. package/src/internal/MigrateCommander.ts +62 -51
  50. package/src/module.ts +14 -4
  51. package/src/programmers/MigrateApiFileProgrammer.ts +53 -53
  52. package/src/programmers/MigrateApiFunctionProgrammer.ts +203 -203
  53. package/src/programmers/MigrateApiNamespaceProgrammer.ts +430 -430
  54. package/src/programmers/MigrateApiProgrammer.ts +132 -132
  55. package/src/programmers/MigrateApiSimulatationProgrammer.ts +328 -328
  56. package/src/programmers/MigrateDtoProgrammer.ts +78 -78
  57. package/src/programmers/MigrateImportProgrammer.ts +114 -116
  58. package/src/programmers/MigrateNestControllerProgrammer.ts +50 -50
  59. package/src/programmers/MigrateNestMethodProgrammer.ts +249 -249
  60. package/src/programmers/MigrateNestModuleProgrammer.ts +63 -62
  61. package/src/programmers/MigrateNestProgrammer.ts +74 -74
  62. package/src/programmers/MigrateSchemaProgrammer.ts +257 -257
  63. package/src/structures/IMigrateDto.ts +8 -8
  64. package/src/structures/IMigrateProgram.ts +25 -9
  65. package/src/structures/IMigrateRoute.ts +46 -46
  66. package/src/structures/ISwaggerComponents.ts +7 -7
  67. package/src/structures/ISwaggerRoute.ts +55 -55
  68. package/src/structures/{ISwaggeSchema.ts → ISwaggerSchema.ts} +90 -90
  69. package/src/utils/FilePrinter.ts +36 -36
  70. package/src/utils/SwaggerTypeChecker.ts +67 -67
  71. package/lib/IMigrateConfig.d.ts +0 -4
  72. package/lib/IMigrateConfig.js +0 -3
  73. package/lib/IMigrateConfig.js.map +0 -1
  74. package/lib/structures/ISwaggeSchema.js.map +0 -1
  75. package/src/IMigrateConfig.ts +0 -4
  76. /package/lib/structures/{ISwaggeSchema.d.ts → ISwaggerSchema.d.ts} +0 -0
@@ -1,348 +1,350 @@
1
- import { Escaper } from "typia/lib/utils/Escaper";
2
-
3
- import { ISwagger } from "../module";
4
- import { IMigrateRoute } from "../structures/IMigrateRoute";
5
- import { ISwaggerSchema } from "../structures/ISwaggeSchema";
6
- import { ISwaggerRoute } from "../structures/ISwaggerRoute";
7
- import { StringUtil } from "../utils/StringUtil";
8
- import { SwaggerSwaggerChecker } from "../utils/SwaggerTypeChecker";
9
-
10
- export namespace MigrateMethodAnalzyer {
11
- export const analyze =
12
- (swagger: ISwagger) =>
13
- (props: { path: string; method: string }) =>
14
- (route: ISwaggerRoute): IMigrateRoute | null => {
15
- const body = emplaceBodySchema("request")(
16
- emplaceReference(swagger)("body"),
17
- )(route.requestBody);
18
- const success = emplaceBodySchema("response")(
19
- emplaceReference(swagger)("response"),
20
- )(route.responses?.["201"] ?? route.responses?.["200"]);
21
-
22
- const failures: string[] = [];
23
- if (body === false)
24
- failures.push(
25
- `supports only "application/json", "application/x-www-form-urlencoded", "multipart/form-data" and "text/plain" content type in the request body.`,
26
- );
27
- if (success === false)
28
- failures.push(
29
- `supports only "application/json", "application/x-www-form-urlencoded" and "text/plain" content type in the response body.`,
30
- );
31
- if (SUPPORTED_METHODS.has(props.method.toUpperCase()) === false)
32
- failures.push(`does not support ${props.method.toUpperCase()} method.`);
33
- if (failures.length) {
34
- console.log(
35
- `Failed to migrate ${props.method.toUpperCase()} ${props.path}`,
36
- ...failures.map((f) => ` - ${f}`),
37
- );
38
- return null;
39
- }
40
-
41
- const [headers, query] = ["header", "query"].map((type) => {
42
- const parameters = (route.parameters ?? []).filter(
43
- (p) => p.in === type,
44
- );
45
- if (parameters.length === 0) return null;
46
-
47
- const objects = parameters
48
- .map((p) =>
49
- SwaggerSwaggerChecker.isObject(p.schema)
50
- ? p.schema
51
- : SwaggerSwaggerChecker.isReference(p.schema) &&
52
- SwaggerSwaggerChecker.isObject(
53
- (swagger.components.schemas ?? {})[
54
- p.schema.$ref.replace(`#/components/schemas/`, ``)
55
- ] ?? {},
56
- )
57
- ? p.schema
58
- : null!,
59
- )
60
- .filter((s) => !!s);
61
- const primitives = parameters.filter(
62
- (p) =>
63
- SwaggerSwaggerChecker.isBoolean(p.schema) ||
64
- SwaggerSwaggerChecker.isNumber(p.schema) ||
65
- SwaggerSwaggerChecker.isInteger(p.schema) ||
66
- SwaggerSwaggerChecker.isString(p.schema) ||
67
- SwaggerSwaggerChecker.isArray(p.schema),
68
- );
69
- if (objects.length === 1 && primitives.length === 0) return objects[0];
70
- else if (objects.length > 1)
71
- throw new Error(
72
- `Error on nestia.migrate.RouteProgrammer.analze(): ${type} typed parameters must be only one object type - ${StringUtil.capitalize(
73
- props.method,
74
- )} "${props.path}".`,
75
- );
76
-
77
- const dto: ISwaggerSchema.IObject | null = objects[0]
78
- ? SwaggerSwaggerChecker.isObject(objects[0])
79
- ? objects[0]
80
- : ((swagger.components.schemas ?? {})[
81
- (objects[0] as ISwaggerSchema.IReference).$ref.replace(
82
- `#/components/schemas/`,
83
- ``,
84
- )
85
- ] as ISwaggerSchema.IObject)
86
- : null;
87
- const entire: ISwaggerSchema.IObject[] = [
88
- ...objects.map((o) =>
89
- SwaggerSwaggerChecker.isObject(o)
90
- ? o
91
- : (swagger.components.schemas?.[
92
- o.$ref.replace(`#/components/schemas/`, ``)
93
- ]! as ISwaggerSchema.IObject),
94
- ),
95
- {
96
- type: "object",
97
- properties: Object.fromEntries([
98
- ...primitives.map((p) => [
99
- p.name,
100
- {
101
- ...p.schema,
102
- description: p.schema.description ?? p.description,
103
- },
104
- ]),
105
- ...(dto ? Object.entries(dto.properties ?? {}) : []),
106
- ]),
107
- required: [
108
- ...primitives.filter((p) => p.required).map((p) => p.name!),
109
- ...(dto ? dto.required ?? [] : []),
110
- ],
111
- },
112
- ];
113
- return parameters.length === 0
114
- ? null
115
- : emplaceReference(swagger)(
116
- StringUtil.pascal(`I/Api/${props.path}`) +
117
- "." +
118
- StringUtil.pascal(`${props.method}/${type}`),
119
- )({
120
- type: "object",
121
- properties: Object.fromEntries([
122
- ...new Map<string, ISwaggerSchema>(
123
- entire
124
- .map((o) =>
125
- Object.entries(o.properties ?? {}).map(
126
- ([name, schema]) =>
127
- [
128
- name,
129
- {
130
- ...schema,
131
- description:
132
- schema.description ?? schema.description,
133
- } as ISwaggerSchema,
134
- ] as const,
135
- ),
136
- )
137
- .flat(),
138
- ),
139
- ]),
140
- required: [
141
- ...new Set(entire.map((o) => o.required ?? []).flat()),
142
- ],
143
- });
144
- });
145
-
146
- const parameterNames: string[] = StringUtil.splitWithNormalization(
147
- props.path,
148
- )
149
- .filter((str) => str[0] === "{" || str[0] === ":")
150
- .map((str) =>
151
- str[0] === "{" ? str.substring(1, str.length - 1) : str.substring(1),
152
- );
153
- if (
154
- parameterNames.length !==
155
- (route.parameters ?? []).filter((p) => p.in === "path").length
156
- ) {
157
- console.log(
158
- `Failed to migrate ${props.method.toUpperCase()} ${
159
- props.path
160
- }: number of path parameters are not matched with its full path.`,
161
- );
162
- return null;
163
- }
164
- return {
165
- name: "@lazy",
166
- path: props.path,
167
- method: props.method,
168
- accessor: ["@lazy"],
169
- headers: headers
170
- ? {
171
- key: "headers",
172
- schema: headers,
173
- }
174
- : null,
175
- parameters: (route.parameters ?? [])
176
- .filter((p) => p.in === "path")
177
- .map((p, i) => ({
178
- key: (() => {
179
- let key: string = StringUtil.normalize(parameterNames[i]);
180
- if (Escaper.variable(key)) return key;
181
- while (true) {
182
- key = "_" + key;
183
- if (!parameterNames.some((s) => s === key)) return key;
184
- }
185
- })(),
186
- schema: {
187
- ...p.schema,
188
- description: p.schema.description ?? p.description,
189
- },
190
- })),
191
- query: query
192
- ? {
193
- key: "query",
194
- schema: query,
195
- }
196
- : null,
197
- body: body as IMigrateRoute.IBody,
198
- success: success as IMigrateRoute.IBody,
199
- exceptions: Object.fromEntries(
200
- Object.entries(route.responses ?? {})
201
- .filter(
202
- ([key, value]) =>
203
- key !== "200" &&
204
- key !== "201" &&
205
- !!value.content?.["application/json"],
206
- )
207
- .map(([key, value]) => [
208
- key,
209
- {
210
- description: value.description,
211
- schema: value.content?.["application/json"]?.schema ?? {},
212
- },
213
- ]),
214
- ),
215
- deprecated: route.deprecated ?? false,
216
- description: describe(route),
217
- tags: route.tags ?? [],
218
- };
219
- };
220
-
221
- const describe = (route: ISwaggerRoute): string | undefined => {
222
- const commentTags: string[] = [];
223
- const add = (text: string) => {
224
- if (commentTags.every((line) => line !== text)) commentTags.push(text);
225
- };
226
-
227
- let description: string | undefined = route.description;
228
- if (route.summary) {
229
- const emended: string = route.summary.endsWith(".")
230
- ? route.summary
231
- : route.summary + ".";
232
- if (
233
- description !== undefined &&
234
- !description?.startsWith(route.summary) &&
235
- !route["x-nestia-jsDocTags"]?.some((t) => t.name === "summary")
236
- )
237
- description = `${emended}\n${description}`;
238
- }
239
- if (route.tags) route.tags.forEach((name) => add(`@tag ${name}`));
240
- if (route.deprecated) add("@deprecated");
241
- for (const security of route.security ?? [])
242
- for (const [name, scopes] of Object.entries(security))
243
- add(`@security ${[name, ...scopes].join("")}`);
244
- for (const jsDocTag of route["x-nestia-jsDocTags"] ?? [])
245
- if (jsDocTag.text?.length)
246
- add(
247
- `@${jsDocTag.name} ${jsDocTag.text
248
- .map((text) => text.text)
249
- .join("")}`,
250
- );
251
- else add(`@${jsDocTag.name}`);
252
- return description?.length
253
- ? commentTags.length
254
- ? `${description}\n\n${commentTags.join("\n")}`
255
- : description
256
- : commentTags.join("\n");
257
- };
258
-
259
- const isNotObjectLiteral = (schema: ISwaggerSchema): boolean =>
260
- SwaggerSwaggerChecker.isReference(schema) ||
261
- SwaggerSwaggerChecker.isBoolean(schema) ||
262
- SwaggerSwaggerChecker.isNumber(schema) ||
263
- SwaggerSwaggerChecker.isString(schema) ||
264
- SwaggerSwaggerChecker.isUnknown(schema) ||
265
- (SwaggerSwaggerChecker.isAnyOf(schema) &&
266
- schema.anyOf.every(isNotObjectLiteral)) ||
267
- (SwaggerSwaggerChecker.isOneOf(schema) &&
268
- schema.oneOf.every(isNotObjectLiteral)) ||
269
- (SwaggerSwaggerChecker.isArray(schema) && isNotObjectLiteral(schema.items));
270
-
271
- const emplaceBodySchema =
272
- (from: "request" | "response") =>
273
- (emplacer: (schema: ISwaggerSchema) => ISwaggerSchema.IReference) =>
274
- (meta?: {
275
- description?: string;
276
- content?: ISwaggerRoute.IContent;
277
- "x-nestia-encrypted"?: boolean;
278
- }): false | null | IMigrateRoute.IBody => {
279
- if (!meta?.content) return null;
280
-
281
- const entries: [string, { schema: ISwaggerSchema }][] = Object.entries(
282
- meta.content,
283
- );
284
- const json = entries.find((e) =>
285
- meta["x-nestia-encrypted"] === true
286
- ? e[0].includes("text/plain") || e[0].includes("application/json")
287
- : e[0].includes("application/json") || e[0].includes("*/*"),
288
- );
289
- if (json) {
290
- const { schema } = json[1];
291
- return {
292
- type: "application/json",
293
- key: "body",
294
- schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
295
- "x-nestia-encrypted": meta["x-nestia-encrypted"],
296
- };
297
- }
298
-
299
- const query = entries.find((e) =>
300
- e[0].includes("application/x-www-form-urlencoded"),
301
- );
302
- if (query) {
303
- const { schema } = query[1];
304
- return {
305
- type: "application/x-www-form-urlencoded",
306
- key: "body",
307
- schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
308
- };
309
- }
310
-
311
- const text = entries.find((e) => e[0].includes("text/plain"));
312
- if (text)
313
- return { type: "text/plain", key: "body", schema: { type: "string" } };
314
-
315
- if (from === "request") {
316
- const multipart = entries.find((e) =>
317
- e[0].includes("multipart/form-data"),
318
- );
319
- if (multipart) {
320
- const { schema } = multipart[1];
321
- return {
322
- type: "multipart/form-data",
323
- key: "body",
324
- schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
325
- };
326
- }
327
- }
328
- return false;
329
- };
330
-
331
- const emplaceReference =
332
- (swagger: ISwagger) =>
333
- (name: string) =>
334
- (schema: ISwaggerSchema): ISwaggerSchema.IReference => {
335
- swagger.components.schemas ??= {};
336
- swagger.components.schemas[name] = schema;
337
- return { $ref: `#/components/schemas/${name}` };
338
- };
339
- }
340
-
341
- const SUPPORTED_METHODS: Set<string> = new Set([
342
- "GET",
343
- "POST",
344
- "PUT",
345
- "PATCH",
346
- "DELETE",
347
- "HEAD",
348
- ]);
1
+ import { Escaper } from "typia/lib/utils/Escaper";
2
+
3
+ import { IMigrateProgram, ISwagger } from "../module";
4
+ import { IMigrateRoute } from "../structures/IMigrateRoute";
5
+ import { ISwaggerSchema } from "../structures/ISwaggerSchema";
6
+ import { ISwaggerRoute } from "../structures/ISwaggerRoute";
7
+ import { StringUtil } from "../utils/StringUtil";
8
+ import { SwaggerSwaggerChecker } from "../utils/SwaggerTypeChecker";
9
+
10
+ export namespace MigrateMethodAnalzyer {
11
+ export const analyze =
12
+ (props: Omit<IMigrateProgram.IProps, "dictionary">) =>
13
+ (endpoint: { path: string; method: string }) =>
14
+ (route: ISwaggerRoute): IMigrateRoute | null => {
15
+ const body = emplaceBodySchema("request")(
16
+ emplaceReference(props.swagger)("body"),
17
+ )(route.requestBody);
18
+ const success = emplaceBodySchema("response")(
19
+ emplaceReference(props.swagger)("response"),
20
+ )(route.responses?.["201"] ?? route.responses?.["200"]);
21
+
22
+ const failures: string[] = [];
23
+ if (body === false)
24
+ failures.push(
25
+ `supports only "application/json", "application/x-www-form-urlencoded", "multipart/form-data" and "text/plain" content type in the request body.`,
26
+ );
27
+ if (success === false)
28
+ failures.push(
29
+ `supports only "application/json", "application/x-www-form-urlencoded" and "text/plain" content type in the response body.`,
30
+ );
31
+ if (SUPPORTED_METHODS.has(endpoint.method.toUpperCase()) === false)
32
+ failures.push(
33
+ `does not support ${endpoint.method.toUpperCase()} method.`,
34
+ );
35
+ if (failures.length) {
36
+ console.log(
37
+ `Failed to migrate ${endpoint.method.toUpperCase()} ${endpoint.path}`,
38
+ ...failures.map((f) => ` - ${f}`),
39
+ );
40
+ return null;
41
+ }
42
+
43
+ const [headers, query] = ["header", "query"].map((type) => {
44
+ const parameters = (route.parameters ?? []).filter(
45
+ (p) => p.in === type,
46
+ );
47
+ if (parameters.length === 0) return null;
48
+
49
+ const objects = parameters
50
+ .map((p) =>
51
+ SwaggerSwaggerChecker.isObject(p.schema)
52
+ ? p.schema
53
+ : SwaggerSwaggerChecker.isReference(p.schema) &&
54
+ SwaggerSwaggerChecker.isObject(
55
+ (props.swagger.components.schemas ?? {})[
56
+ p.schema.$ref.replace(`#/components/schemas/`, ``)
57
+ ] ?? {},
58
+ )
59
+ ? p.schema
60
+ : null!,
61
+ )
62
+ .filter((s) => !!s);
63
+ const primitives = parameters.filter(
64
+ (p) =>
65
+ SwaggerSwaggerChecker.isBoolean(p.schema) ||
66
+ SwaggerSwaggerChecker.isNumber(p.schema) ||
67
+ SwaggerSwaggerChecker.isInteger(p.schema) ||
68
+ SwaggerSwaggerChecker.isString(p.schema) ||
69
+ SwaggerSwaggerChecker.isArray(p.schema),
70
+ );
71
+ if (objects.length === 1 && primitives.length === 0) return objects[0];
72
+ else if (objects.length > 1)
73
+ throw new Error(
74
+ `Error on nestia.migrate.RouteProgrammer.analze(): ${type} typed parameters must be only one object type - ${StringUtil.capitalize(
75
+ endpoint.method,
76
+ )} "${endpoint.path}".`,
77
+ );
78
+
79
+ const dto: ISwaggerSchema.IObject | null = objects[0]
80
+ ? SwaggerSwaggerChecker.isObject(objects[0])
81
+ ? objects[0]
82
+ : ((props.swagger.components.schemas ?? {})[
83
+ (objects[0] as ISwaggerSchema.IReference).$ref.replace(
84
+ `#/components/schemas/`,
85
+ ``,
86
+ )
87
+ ] as ISwaggerSchema.IObject)
88
+ : null;
89
+ const entire: ISwaggerSchema.IObject[] = [
90
+ ...objects.map((o) =>
91
+ SwaggerSwaggerChecker.isObject(o)
92
+ ? o
93
+ : (props.swagger.components.schemas?.[
94
+ o.$ref.replace(`#/components/schemas/`, ``)
95
+ ]! as ISwaggerSchema.IObject),
96
+ ),
97
+ {
98
+ type: "object",
99
+ properties: Object.fromEntries([
100
+ ...primitives.map((p) => [
101
+ p.name,
102
+ {
103
+ ...p.schema,
104
+ description: p.schema.description ?? p.description,
105
+ },
106
+ ]),
107
+ ...(dto ? Object.entries(dto.properties ?? {}) : []),
108
+ ]),
109
+ required: [
110
+ ...primitives.filter((p) => p.required).map((p) => p.name!),
111
+ ...(dto ? dto.required ?? [] : []),
112
+ ],
113
+ },
114
+ ];
115
+ return parameters.length === 0
116
+ ? null
117
+ : emplaceReference(props.swagger)(
118
+ StringUtil.pascal(`I/Api/${endpoint.path}`) +
119
+ "." +
120
+ StringUtil.pascal(`${endpoint.method}/${type}`),
121
+ )({
122
+ type: "object",
123
+ properties: Object.fromEntries([
124
+ ...new Map<string, ISwaggerSchema>(
125
+ entire
126
+ .map((o) =>
127
+ Object.entries(o.properties ?? {}).map(
128
+ ([name, schema]) =>
129
+ [
130
+ name,
131
+ {
132
+ ...schema,
133
+ description:
134
+ schema.description ?? schema.description,
135
+ } as ISwaggerSchema,
136
+ ] as const,
137
+ ),
138
+ )
139
+ .flat(),
140
+ ),
141
+ ]),
142
+ required: [
143
+ ...new Set(entire.map((o) => o.required ?? []).flat()),
144
+ ],
145
+ });
146
+ });
147
+
148
+ const parameterNames: string[] = StringUtil.splitWithNormalization(
149
+ endpoint.path,
150
+ )
151
+ .filter((str) => str[0] === "{" || str[0] === ":")
152
+ .map((str) =>
153
+ str[0] === "{" ? str.substring(1, str.length - 1) : str.substring(1),
154
+ );
155
+ if (
156
+ parameterNames.length !==
157
+ (route.parameters ?? []).filter((p) => p.in === "path").length
158
+ ) {
159
+ console.log(
160
+ `Failed to migrate ${endpoint.method.toUpperCase()} ${
161
+ endpoint.path
162
+ }: number of path parameters are not matched with its full path.`,
163
+ );
164
+ return null;
165
+ }
166
+ return {
167
+ name: "@lazy",
168
+ path: endpoint.path,
169
+ method: endpoint.method,
170
+ accessor: ["@lazy"],
171
+ headers: headers
172
+ ? {
173
+ key: "headers",
174
+ schema: headers,
175
+ }
176
+ : null,
177
+ parameters: (route.parameters ?? [])
178
+ .filter((p) => p.in === "path")
179
+ .map((p, i) => ({
180
+ key: (() => {
181
+ let key: string = StringUtil.normalize(parameterNames[i]);
182
+ if (Escaper.variable(key)) return key;
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: query
194
+ ? {
195
+ key: "query",
196
+ schema: query,
197
+ }
198
+ : null,
199
+ body: body as IMigrateRoute.IBody,
200
+ success: success as IMigrateRoute.IBody,
201
+ exceptions: Object.fromEntries(
202
+ Object.entries(route.responses ?? {})
203
+ .filter(
204
+ ([key, value]) =>
205
+ key !== "200" &&
206
+ key !== "201" &&
207
+ !!value.content?.["application/json"],
208
+ )
209
+ .map(([key, value]) => [
210
+ key,
211
+ {
212
+ description: value.description,
213
+ schema: value.content?.["application/json"]?.schema ?? {},
214
+ },
215
+ ]),
216
+ ),
217
+ deprecated: route.deprecated ?? false,
218
+ description: describe(route),
219
+ tags: route.tags ?? [],
220
+ };
221
+ };
222
+
223
+ const describe = (route: ISwaggerRoute): string | undefined => {
224
+ const commentTags: string[] = [];
225
+ const add = (text: string) => {
226
+ if (commentTags.every((line) => line !== text)) commentTags.push(text);
227
+ };
228
+
229
+ let description: string | undefined = route.description;
230
+ if (route.summary) {
231
+ const emended: string = route.summary.endsWith(".")
232
+ ? route.summary
233
+ : route.summary + ".";
234
+ if (
235
+ description !== undefined &&
236
+ !description?.startsWith(route.summary) &&
237
+ !route["x-nestia-jsDocTags"]?.some((t) => t.name === "summary")
238
+ )
239
+ description = `${emended}\n${description}`;
240
+ }
241
+ if (route.tags) route.tags.forEach((name) => add(`@tag ${name}`));
242
+ if (route.deprecated) add("@deprecated");
243
+ for (const security of route.security ?? [])
244
+ for (const [name, scopes] of Object.entries(security))
245
+ add(`@security ${[name, ...scopes].join("")}`);
246
+ for (const jsDocTag of route["x-nestia-jsDocTags"] ?? [])
247
+ if (jsDocTag.text?.length)
248
+ add(
249
+ `@${jsDocTag.name} ${jsDocTag.text
250
+ .map((text) => text.text)
251
+ .join("")}`,
252
+ );
253
+ else add(`@${jsDocTag.name}`);
254
+ return description?.length
255
+ ? commentTags.length
256
+ ? `${description}\n\n${commentTags.join("\n")}`
257
+ : description
258
+ : commentTags.join("\n");
259
+ };
260
+
261
+ const isNotObjectLiteral = (schema: ISwaggerSchema): boolean =>
262
+ SwaggerSwaggerChecker.isReference(schema) ||
263
+ SwaggerSwaggerChecker.isBoolean(schema) ||
264
+ SwaggerSwaggerChecker.isNumber(schema) ||
265
+ SwaggerSwaggerChecker.isString(schema) ||
266
+ SwaggerSwaggerChecker.isUnknown(schema) ||
267
+ (SwaggerSwaggerChecker.isAnyOf(schema) &&
268
+ schema.anyOf.every(isNotObjectLiteral)) ||
269
+ (SwaggerSwaggerChecker.isOneOf(schema) &&
270
+ schema.oneOf.every(isNotObjectLiteral)) ||
271
+ (SwaggerSwaggerChecker.isArray(schema) && isNotObjectLiteral(schema.items));
272
+
273
+ const emplaceBodySchema =
274
+ (from: "request" | "response") =>
275
+ (emplacer: (schema: ISwaggerSchema) => ISwaggerSchema.IReference) =>
276
+ (meta?: {
277
+ description?: string;
278
+ content?: ISwaggerRoute.IContent;
279
+ "x-nestia-encrypted"?: boolean;
280
+ }): false | null | IMigrateRoute.IBody => {
281
+ if (!meta?.content) return null;
282
+
283
+ const entries: [string, { schema: ISwaggerSchema }][] = Object.entries(
284
+ meta.content,
285
+ );
286
+ const json = entries.find((e) =>
287
+ meta["x-nestia-encrypted"] === true
288
+ ? e[0].includes("text/plain") || e[0].includes("application/json")
289
+ : e[0].includes("application/json") || e[0].includes("*/*"),
290
+ );
291
+ if (json) {
292
+ const { schema } = json[1];
293
+ return {
294
+ type: "application/json",
295
+ key: "body",
296
+ schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
297
+ "x-nestia-encrypted": meta["x-nestia-encrypted"],
298
+ };
299
+ }
300
+
301
+ const query = entries.find((e) =>
302
+ e[0].includes("application/x-www-form-urlencoded"),
303
+ );
304
+ if (query) {
305
+ const { schema } = query[1];
306
+ return {
307
+ type: "application/x-www-form-urlencoded",
308
+ key: "body",
309
+ schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
310
+ };
311
+ }
312
+
313
+ const text = entries.find((e) => e[0].includes("text/plain"));
314
+ if (text)
315
+ return { type: "text/plain", key: "body", schema: { type: "string" } };
316
+
317
+ if (from === "request") {
318
+ const multipart = entries.find((e) =>
319
+ e[0].includes("multipart/form-data"),
320
+ );
321
+ if (multipart) {
322
+ const { schema } = multipart[1];
323
+ return {
324
+ type: "multipart/form-data",
325
+ key: "body",
326
+ schema: isNotObjectLiteral(schema) ? schema : emplacer(schema),
327
+ };
328
+ }
329
+ }
330
+ return false;
331
+ };
332
+
333
+ const emplaceReference =
334
+ (swagger: ISwagger) =>
335
+ (name: string) =>
336
+ (schema: ISwaggerSchema): ISwaggerSchema.IReference => {
337
+ swagger.components.schemas ??= {};
338
+ swagger.components.schemas[name] = schema;
339
+ return { $ref: `#/components/schemas/${name}` };
340
+ };
341
+ }
342
+
343
+ const SUPPORTED_METHODS: Set<string> = new Set([
344
+ "GET",
345
+ "POST",
346
+ "PUT",
347
+ "PATCH",
348
+ "DELETE",
349
+ "HEAD",
350
+ ]);