@nestia/migrate 0.6.1 → 0.6.3

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 (86) hide show
  1. package/lib/IMigrateConfig.d.ts +3 -0
  2. package/lib/IMigrateConfig.js +3 -0
  3. package/lib/IMigrateConfig.js.map +1 -0
  4. package/lib/MigrateApplication.d.ts +4 -2
  5. package/lib/MigrateApplication.js +49 -29
  6. package/lib/MigrateApplication.js.map +1 -1
  7. package/lib/analyzers/ControllerAnalyzer.js +11 -2
  8. package/lib/analyzers/ControllerAnalyzer.js.map +1 -1
  9. package/lib/analyzers/MethodAnalyzer.js +18 -5
  10. package/lib/analyzers/MethodAnalyzer.js.map +1 -1
  11. package/lib/analyzers/MigrateAnalyzer.d.ts +2 -1
  12. package/lib/analyzers/MigrateAnalyzer.js +2 -1
  13. package/lib/analyzers/MigrateAnalyzer.js.map +1 -1
  14. package/lib/archivers/FileArchiver.d.ts +3 -3
  15. package/lib/archivers/FileArchiver.js +16 -7
  16. package/lib/archivers/FileArchiver.js.map +1 -1
  17. package/lib/executable/migrate.js +22 -10
  18. package/lib/executable/migrate.js.map +1 -1
  19. package/lib/internal/MigrateArguments.d.ts +8 -0
  20. package/lib/internal/MigrateArguments.js +67 -0
  21. package/lib/internal/MigrateArguments.js.map +1 -0
  22. package/lib/programmers/ApiFileProgrammer.d.ts +18 -0
  23. package/lib/programmers/ApiFileProgrammer.js +28 -0
  24. package/lib/programmers/ApiFileProgrammer.js.map +1 -0
  25. package/lib/programmers/ApiFunctionProgrammer.d.ts +15 -0
  26. package/lib/programmers/ApiFunctionProgrammer.js +96 -0
  27. package/lib/programmers/ApiFunctionProgrammer.js.map +1 -0
  28. package/lib/programmers/ApiNamespaceProgrammer.d.ts +15 -0
  29. package/lib/programmers/ApiNamespaceProgrammer.js +147 -0
  30. package/lib/programmers/ApiNamespaceProgrammer.js.map +1 -0
  31. package/lib/programmers/ApiProgrammer.d.ts +5 -0
  32. package/lib/programmers/ApiProgrammer.js +62 -0
  33. package/lib/programmers/ApiProgrammer.js.map +1 -0
  34. package/lib/programmers/ApiSimulatationProgrammer.d.ts +14 -0
  35. package/lib/programmers/ApiSimulatationProgrammer.js +127 -0
  36. package/lib/programmers/ApiSimulatationProgrammer.js.map +1 -0
  37. package/lib/programmers/DtoProgrammer.js +2 -2
  38. package/lib/programmers/DtoProgrammer.js.map +1 -1
  39. package/lib/programmers/ImportProgrammer.d.ts +2 -1
  40. package/lib/programmers/ImportProgrammer.js +19 -4
  41. package/lib/programmers/ImportProgrammer.js.map +1 -1
  42. package/lib/programmers/NestControllerProgrammer.js +2 -1
  43. package/lib/programmers/NestControllerProgrammer.js.map +1 -1
  44. package/lib/programmers/NestMethodProgrammer.js +33 -18
  45. package/lib/programmers/NestMethodProgrammer.js.map +1 -1
  46. package/lib/programmers/NestProgrammer.d.ts +1 -1
  47. package/lib/programmers/NestProgrammer.js +5 -5
  48. package/lib/programmers/NestProgrammer.js.map +1 -1
  49. package/lib/programmers/SchemaProgrammer.d.ts +1 -1
  50. package/lib/programmers/SchemaProgrammer.js +17 -17
  51. package/lib/programmers/SchemaProgrammer.js.map +1 -1
  52. package/lib/structures/IMigrateProgram.d.ts +2 -0
  53. package/lib/structures/IMigrateRoute.d.ts +13 -4
  54. package/lib/utils/SetupWizard.js +0 -3
  55. package/lib/utils/SetupWizard.js.map +1 -1
  56. package/lib/utils/StringUtil.d.ts +1 -0
  57. package/lib/utils/StringUtil.js +4 -2
  58. package/lib/utils/StringUtil.js.map +1 -1
  59. package/package.json +67 -64
  60. package/src/IMigrateConfig.ts +3 -0
  61. package/src/MigrateApplication.ts +42 -33
  62. package/src/analyzers/ControllerAnalyzer.ts +19 -3
  63. package/src/analyzers/MethodAnalyzer.ts +18 -5
  64. package/src/analyzers/MigrateAnalyzer.ts +8 -4
  65. package/src/archivers/FileArchiver.ts +10 -7
  66. package/src/executable/migrate.ts +15 -9
  67. package/src/internal/MigrateArguments.ts +74 -0
  68. package/src/programmers/ApiFileProgrammer.ts +53 -0
  69. package/src/programmers/ApiFunctionProgrammer.ts +204 -0
  70. package/src/programmers/ApiNamespaceProgrammer.ts +418 -0
  71. package/src/programmers/ApiProgrammer.ts +70 -0
  72. package/src/programmers/ApiSimulatationProgrammer.ts +328 -0
  73. package/src/programmers/DtoProgrammer.ts +3 -3
  74. package/src/programmers/ImportProgrammer.ts +37 -21
  75. package/src/programmers/NestControllerProgrammer.ts +2 -1
  76. package/src/programmers/NestMethodProgrammer.ts +39 -22
  77. package/src/programmers/NestProgrammer.ts +1 -1
  78. package/src/programmers/SchemaProgrammer.ts +22 -25
  79. package/src/structures/IMigrateProgram.ts +2 -0
  80. package/src/structures/IMigrateRoute.ts +13 -5
  81. package/src/utils/SetupWizard.ts +0 -3
  82. package/src/utils/StringUtil.ts +11 -2
  83. package/lib/analyzers/RouteAnalyzer.d.ts +0 -0
  84. package/lib/analyzers/RouteAnalyzer.js +0 -2
  85. package/lib/analyzers/RouteAnalyzer.js.map +0 -1
  86. package/src/analyzers/RouteAnalyzer.ts +0 -0
@@ -0,0 +1,204 @@
1
+ import ts from "typescript";
2
+ import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory";
3
+
4
+ import { IMigrateConfig } from "../IMigrateConfig";
5
+ import { IMigrateController } from "../structures/IMigrateController";
6
+ import { IMigrateRoute } from "../structures/IMigrateRoute";
7
+ import { ISwaggerComponents } from "../structures/ISwaggerComponents";
8
+ import { FilePrinter } from "../utils/FilePrinter";
9
+ import { ImportProgrammer } from "./ImportProgrammer";
10
+ import { SchemaProgrammer } from "./SchemaProgrammer";
11
+
12
+ export namespace ApiFunctionProgrammer {
13
+ export interface IProps {
14
+ controller: IMigrateController;
15
+ route: IMigrateRoute;
16
+ alias: string;
17
+ }
18
+
19
+ export const write =
20
+ (config: IMigrateConfig) =>
21
+ (components: ISwaggerComponents) =>
22
+ (importer: ImportProgrammer) =>
23
+ (props: IProps): ts.FunctionDeclaration =>
24
+ FilePrinter.description(
25
+ ts.factory.createFunctionDeclaration(
26
+ [
27
+ ts.factory.createModifier(ts.SyntaxKind.ExportKeyword),
28
+ ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword),
29
+ ],
30
+ undefined,
31
+ props.alias,
32
+ undefined,
33
+ writeParameterDeclarations(components)(importer)(props),
34
+ ts.factory.createTypeReferenceNode("Promise", [
35
+ ts.factory.createTypeReferenceNode(
36
+ props.route.success === null ? "void" : `${props.alias}.Output`,
37
+ ),
38
+ ]),
39
+ ts.factory.createBlock(writeBody(config)(importer)(props), true),
40
+ ),
41
+ writeDescription(props),
42
+ );
43
+
44
+ export const writeParameterDeclarations =
45
+ (components: ISwaggerComponents) =>
46
+ (importer: ImportProgrammer) =>
47
+ (props: IProps): ts.ParameterDeclaration[] => [
48
+ IdentifierFactory.parameter(
49
+ "connection",
50
+ ts.factory.createTypeReferenceNode(
51
+ importer.external({
52
+ type: "instance",
53
+ library: "@nestia/Fetcher",
54
+ name: "IConnection",
55
+ }),
56
+ props.route.headers
57
+ ? [ts.factory.createTypeReferenceNode(`${props.alias}.Headers`)]
58
+ : undefined,
59
+ ),
60
+ ),
61
+ ...props.route.parameters.map((p) =>
62
+ IdentifierFactory.parameter(
63
+ p.key,
64
+ SchemaProgrammer.write(components)(importer)(p.schema),
65
+ ),
66
+ ),
67
+ ...(props.route.query
68
+ ? [
69
+ IdentifierFactory.parameter(
70
+ props.route.query.key,
71
+ ts.factory.createTypeReferenceNode(`${props.alias}.Query`),
72
+ ),
73
+ ]
74
+ : []),
75
+ ...(props.route.body
76
+ ? [
77
+ IdentifierFactory.parameter(
78
+ props.route.body.key,
79
+ ts.factory.createTypeReferenceNode(`${props.alias}.Input`),
80
+ ),
81
+ ]
82
+ : []),
83
+ ];
84
+
85
+ const writeDescription = (props: IProps): string =>
86
+ [
87
+ ...(props.route.description?.length
88
+ ? [props.route.description.length, ""]
89
+ : []),
90
+ ...(props.route.deprecated ? ["@deprecated"] : []),
91
+ ...props.route.tags.map((value) => `@tag ${value}`),
92
+ `@controller ${props.controller.name}`,
93
+ `@path ${props.route.path}`,
94
+ "@nestia Generated by Nestia - https://github.com/samchon/nestia",
95
+ ].join("\n");
96
+
97
+ const writeBody =
98
+ (config: IMigrateConfig) =>
99
+ (importer: ImportProgrammer) =>
100
+ (props: IProps): ts.Statement[] => {
101
+ const encrypted: boolean = !!props.route.success?.["x-nestia-encrypted"];
102
+ const contentType: string =
103
+ props.route.success?.type ?? "application/json";
104
+
105
+ const caller = () =>
106
+ ts.factory.createCallExpression(
107
+ IdentifierFactory.access(
108
+ ts.factory.createIdentifier(
109
+ importer.external({
110
+ type: "instance",
111
+ library: `@nestia/fetcher/lib/${encrypted ? "EncryptedFetcher" : "PlainFetcher"}`,
112
+ name: encrypted ? "EncryptedFetcher" : "PlainFetcher",
113
+ }),
114
+ ),
115
+ )("fetch"),
116
+ undefined,
117
+ [
118
+ contentType
119
+ ? ts.factory.createObjectLiteralExpression(
120
+ [
121
+ ts.factory.createSpreadAssignment(
122
+ ts.factory.createIdentifier("connection"),
123
+ ),
124
+ ts.factory.createPropertyAssignment(
125
+ "headers",
126
+ ts.factory.createObjectLiteralExpression(
127
+ [
128
+ ts.factory.createSpreadAssignment(
129
+ IdentifierFactory.access(
130
+ ts.factory.createIdentifier("connection"),
131
+ )("headers"),
132
+ ),
133
+ ts.factory.createPropertyAssignment(
134
+ ts.factory.createStringLiteral("Content-Type"),
135
+ ts.factory.createStringLiteral(contentType),
136
+ ),
137
+ ],
138
+ true,
139
+ ),
140
+ ),
141
+ ],
142
+ true,
143
+ )
144
+ : ts.factory.createIdentifier("connection"),
145
+ ts.factory.createObjectLiteralExpression(
146
+ [
147
+ ts.factory.createSpreadAssignment(
148
+ IdentifierFactory.access(
149
+ ts.factory.createIdentifier(props.alias),
150
+ )("METADATA"),
151
+ ),
152
+ ts.factory.createPropertyAssignment(
153
+ "path",
154
+ ts.factory.createCallExpression(
155
+ IdentifierFactory.access(
156
+ ts.factory.createIdentifier(props.alias),
157
+ )("path"),
158
+ undefined,
159
+ [
160
+ ...props.route.parameters.map((p) =>
161
+ ts.factory.createIdentifier(p.key),
162
+ ),
163
+ ...(props.route.query
164
+ ? [ts.factory.createIdentifier(props.route.query.key)]
165
+ : []),
166
+ ],
167
+ ),
168
+ ),
169
+ ts.factory.createPropertyAssignment(
170
+ "status",
171
+ ts.factory.createNull(),
172
+ ),
173
+ ],
174
+ true,
175
+ ),
176
+ ...(props.route.body
177
+ ? [ts.factory.createIdentifier(props.route.body.key)]
178
+ : []),
179
+ ],
180
+ );
181
+ if (config.simulate !== true)
182
+ return [ts.factory.createReturnStatement(caller())];
183
+ return [
184
+ ts.factory.createReturnStatement(
185
+ ts.factory.createConditionalExpression(
186
+ ts.factory.createIdentifier("!!connection.simulate"),
187
+ undefined,
188
+ ts.factory.createCallExpression(
189
+ ts.factory.createIdentifier(`${props.alias}.simulate`),
190
+ [],
191
+ [
192
+ "connection",
193
+ ...props.route.parameters.map((p) => p.key),
194
+ ...(props.route.query ? [props.route.query.key] : []),
195
+ ...(props.route.body ? [props.route.body.key] : []),
196
+ ].map((key) => ts.factory.createIdentifier(key)),
197
+ ),
198
+ undefined,
199
+ caller(),
200
+ ),
201
+ ),
202
+ ];
203
+ };
204
+ }
@@ -0,0 +1,418 @@
1
+ import ts from "typescript";
2
+ import { ExpressionFactory } from "typia/lib/factories/ExpressionFactory";
3
+ import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory";
4
+ import { LiteralFactory } from "typia/lib/factories/LiteralFactory";
5
+ import { TypeFactory } from "typia/lib/factories/TypeFactory";
6
+
7
+ import { IMigrateConfig } from "../IMigrateConfig";
8
+ import { IMigrateController } from "../structures/IMigrateController";
9
+ import { IMigrateRoute } from "../structures/IMigrateRoute";
10
+ import { ISwaggerComponents } from "../structures/ISwaggerComponents";
11
+ import { FilePrinter } from "../utils/FilePrinter";
12
+ import { ApiSimulatationProgrammer } from "./ApiSimulatationProgrammer";
13
+ import { ImportProgrammer } from "./ImportProgrammer";
14
+ import { SchemaProgrammer } from "./SchemaProgrammer";
15
+
16
+ export namespace ApiNamespaceProgrammer {
17
+ export interface IProps {
18
+ controller: IMigrateController;
19
+ route: IMigrateRoute;
20
+ alias: string;
21
+ }
22
+
23
+ export const write =
24
+ (config: IMigrateConfig) =>
25
+ (components: ISwaggerComponents) =>
26
+ (importer: ImportProgrammer) =>
27
+ (props: IProps): ts.ModuleDeclaration => {
28
+ const types = writeTypes(components)(importer)(props.route);
29
+ return ts.factory.createModuleDeclaration(
30
+ [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
31
+ ts.factory.createIdentifier(props.alias),
32
+ ts.factory.createModuleBlock([
33
+ ...types,
34
+ ...(types.length ? [FilePrinter.enter()] : []),
35
+ writeMetadata(components)(importer)(props),
36
+ FilePrinter.enter(),
37
+ writePath(components)(importer)(props),
38
+ ...(config.simulate
39
+ ? [
40
+ ApiSimulatationProgrammer.random(components)(importer)(props),
41
+ ApiSimulatationProgrammer.simulate(components)(importer)(props),
42
+ ]
43
+ : []),
44
+ ]),
45
+ ts.NodeFlags.Namespace,
46
+ );
47
+ };
48
+
49
+ export const writePathCallExpression = (props: IProps) =>
50
+ ts.factory.createCallExpression(
51
+ ts.factory.createIdentifier(`${props.alias}.path`),
52
+ undefined,
53
+ [
54
+ ...props.route.parameters.map((p) =>
55
+ ts.factory.createIdentifier(p.key),
56
+ ),
57
+ ...(props.route.query
58
+ ? [ts.factory.createIdentifier(props.route.query.key)]
59
+ : []),
60
+ ],
61
+ );
62
+
63
+ const writeTypes =
64
+ (components: ISwaggerComponents) =>
65
+ (importer: ImportProgrammer) =>
66
+ (route: IMigrateRoute): ts.TypeAliasDeclaration[] => {
67
+ const array: ts.TypeAliasDeclaration[] = [];
68
+ const declare = (name: string, type: ts.TypeNode) =>
69
+ array.push(
70
+ ts.factory.createTypeAliasDeclaration(
71
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
72
+ name,
73
+ undefined,
74
+ type,
75
+ ),
76
+ );
77
+ if (route.headers)
78
+ declare(
79
+ "Headers",
80
+ SchemaProgrammer.write(components)(importer)(route.headers.schema),
81
+ );
82
+ if (route.query)
83
+ declare(
84
+ "Query",
85
+ SchemaProgrammer.write(components)(importer)(route.query.schema),
86
+ );
87
+ if (route.body)
88
+ declare(
89
+ "Input",
90
+ SchemaProgrammer.write(components)(importer)(route.body.schema),
91
+ );
92
+ if (route.success)
93
+ declare(
94
+ "Output",
95
+ SchemaProgrammer.write(components)(importer)(route.success.schema),
96
+ );
97
+ return array;
98
+ };
99
+
100
+ const writeMetadata =
101
+ (components: ISwaggerComponents) =>
102
+ (importer: ImportProgrammer) =>
103
+ (props: IProps): ts.VariableStatement =>
104
+ constant("METADATA")(
105
+ ts.factory.createAsExpression(
106
+ ts.factory.createObjectLiteralExpression(
107
+ [
108
+ ts.factory.createPropertyAssignment(
109
+ "method",
110
+ ts.factory.createStringLiteral(
111
+ props.route.method.toUpperCase(),
112
+ ),
113
+ ),
114
+ ts.factory.createPropertyAssignment(
115
+ "path",
116
+ ts.factory.createStringLiteral(getPath(props)),
117
+ ),
118
+ ts.factory.createPropertyAssignment(
119
+ "request",
120
+ props.route.body
121
+ ? LiteralFactory.generate({
122
+ type: "application/json",
123
+ encrypted: !!props.route.body["x-nestia-encrypted"],
124
+ })
125
+ : ts.factory.createNull(),
126
+ ),
127
+ ts.factory.createPropertyAssignment(
128
+ "response",
129
+ props.route.method.toUpperCase() !== "HEAD"
130
+ ? LiteralFactory.generate({
131
+ type: props.route.success?.type ?? "application/json",
132
+ encrypted: !!props.route.success?.["x-nestia-encrypted"],
133
+ })
134
+ : ts.factory.createNull(),
135
+ ),
136
+ ...(props.route.success?.type ===
137
+ "application/x-www-form-urlencoded"
138
+ ? [
139
+ ts.factory.createPropertyAssignment(
140
+ "parseQuery",
141
+ ts.factory.createCallExpression(
142
+ ts.factory.createIdentifier(
143
+ `${importer.external({
144
+ type: "default",
145
+ library: "typia",
146
+ name: "typia",
147
+ })}.http.createAssertQuery`,
148
+ ),
149
+ [
150
+ SchemaProgrammer.write(components)(importer)(
151
+ props.route.success.schema,
152
+ ),
153
+ ],
154
+ undefined,
155
+ ),
156
+ ),
157
+ ]
158
+ : []),
159
+ ],
160
+ true,
161
+ ),
162
+ ts.factory.createTypeReferenceNode(
163
+ ts.factory.createIdentifier("const"),
164
+ ),
165
+ ),
166
+ );
167
+
168
+ const writePath =
169
+ (components: ISwaggerComponents) =>
170
+ (importer: ImportProgrammer) =>
171
+ (props: IProps): ts.VariableStatement => {
172
+ const out = (body: ts.ConciseBody) =>
173
+ constant("path")(
174
+ ts.factory.createArrowFunction(
175
+ [],
176
+ [],
177
+ [
178
+ ...props.route.parameters.map((p) =>
179
+ IdentifierFactory.parameter(
180
+ p.key,
181
+ SchemaProgrammer.write(components)(importer)(p.schema),
182
+ ),
183
+ ),
184
+ ...(props.route.query
185
+ ? [
186
+ IdentifierFactory.parameter(
187
+ props.route.query.key,
188
+ ts.factory.createTypeReferenceNode(
189
+ `${props.alias}.Query`,
190
+ ),
191
+ ),
192
+ ]
193
+ : []),
194
+ ],
195
+ undefined,
196
+ undefined,
197
+ body,
198
+ ),
199
+ );
200
+ const template = () => {
201
+ const path: string = getPath(props);
202
+ const splitted: string[] = path.split(":");
203
+ if (splitted.length === 1) return ts.factory.createStringLiteral(path);
204
+ return ts.factory.createTemplateExpression(
205
+ ts.factory.createTemplateHead(splitted[0]),
206
+ splitted.slice(1).map((s, i, arr) => {
207
+ const name: string = s.split("/")[0];
208
+ return ts.factory.createTemplateSpan(
209
+ ts.factory.createCallExpression(
210
+ ts.factory.createIdentifier("encodeURIComponent"),
211
+ undefined,
212
+ [
213
+ ts.factory.createBinaryExpression(
214
+ ts.factory.createIdentifier(
215
+ props.route.parameters.find((p) => p.key === name)!.key,
216
+ ),
217
+ ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
218
+ ts.factory.createStringLiteral("null"),
219
+ ),
220
+ ],
221
+ ),
222
+ (i !== arr.length - 1
223
+ ? ts.factory.createTemplateMiddle
224
+ : ts.factory.createTemplateTail)(s.substring(name.length)),
225
+ );
226
+ }),
227
+ );
228
+ };
229
+ if (!props.route.query) return out(template());
230
+
231
+ const computeName = (str: string): string =>
232
+ props.route.parameters.find((p) => p.key === str) !== undefined
233
+ ? computeName("_" + str)
234
+ : str;
235
+ const variables: string = computeName("variables");
236
+ return out(
237
+ ts.factory.createBlock(
238
+ [
239
+ local(variables)("URLSearchParams")(
240
+ ts.factory.createNewExpression(
241
+ ts.factory.createIdentifier("URLSearchParams"),
242
+ [],
243
+ [],
244
+ ),
245
+ ),
246
+ ts.factory.createForOfStatement(
247
+ undefined,
248
+ ts.factory.createVariableDeclarationList(
249
+ [
250
+ ts.factory.createVariableDeclaration(
251
+ ts.factory.createArrayBindingPattern([
252
+ ts.factory.createBindingElement(
253
+ undefined,
254
+ undefined,
255
+ ts.factory.createIdentifier("key"),
256
+ undefined,
257
+ ),
258
+ ts.factory.createBindingElement(
259
+ undefined,
260
+ undefined,
261
+ ts.factory.createIdentifier("value"),
262
+ undefined,
263
+ ),
264
+ ]),
265
+ undefined,
266
+ undefined,
267
+ undefined,
268
+ ),
269
+ ],
270
+ ts.NodeFlags.Const,
271
+ ),
272
+ ts.factory.createCallExpression(
273
+ ts.factory.createIdentifier("Object.entries"),
274
+ undefined,
275
+ [
276
+ ts.factory.createAsExpression(
277
+ ts.factory.createIdentifier(props.route.query.key),
278
+ TypeFactory.keyword("any"),
279
+ ),
280
+ ],
281
+ ),
282
+ ts.factory.createIfStatement(
283
+ ts.factory.createStrictEquality(
284
+ ts.factory.createIdentifier("undefined"),
285
+ ts.factory.createIdentifier("value"),
286
+ ),
287
+ ts.factory.createContinueStatement(),
288
+ ts.factory.createIfStatement(
289
+ ts.factory.createCallExpression(
290
+ ts.factory.createIdentifier("Array.isArray"),
291
+ undefined,
292
+ [ts.factory.createIdentifier("value")],
293
+ ),
294
+ ts.factory.createExpressionStatement(
295
+ ts.factory.createCallExpression(
296
+ ts.factory.createPropertyAccessExpression(
297
+ ts.factory.createIdentifier("value"),
298
+ ts.factory.createIdentifier("forEach"),
299
+ ),
300
+ undefined,
301
+ [
302
+ ts.factory.createArrowFunction(
303
+ undefined,
304
+ undefined,
305
+ [IdentifierFactory.parameter("elem")],
306
+ undefined,
307
+ undefined,
308
+ ts.factory.createCallExpression(
309
+ IdentifierFactory.access(
310
+ ts.factory.createIdentifier(variables),
311
+ )("append"),
312
+ undefined,
313
+ [
314
+ ts.factory.createIdentifier("key"),
315
+ ts.factory.createCallExpression(
316
+ ts.factory.createIdentifier("String"),
317
+ undefined,
318
+ [ts.factory.createIdentifier("elem")],
319
+ ),
320
+ ],
321
+ ),
322
+ ),
323
+ ],
324
+ ),
325
+ ),
326
+ ts.factory.createExpressionStatement(
327
+ ts.factory.createCallExpression(
328
+ IdentifierFactory.access(
329
+ ts.factory.createIdentifier(variables),
330
+ )("set"),
331
+ undefined,
332
+ [
333
+ ts.factory.createIdentifier("key"),
334
+ ts.factory.createCallExpression(
335
+ ts.factory.createIdentifier("String"),
336
+ undefined,
337
+ [ts.factory.createIdentifier("value")],
338
+ ),
339
+ ],
340
+ ),
341
+ ),
342
+ ),
343
+ ),
344
+ ),
345
+ local("location")("string")(template()),
346
+ ts.factory.createReturnStatement(
347
+ ts.factory.createConditionalExpression(
348
+ ts.factory.createStrictEquality(
349
+ ExpressionFactory.number(0),
350
+ IdentifierFactory.access(
351
+ ts.factory.createIdentifier(variables),
352
+ )("size"),
353
+ ),
354
+ undefined,
355
+ ts.factory.createIdentifier("location"),
356
+ undefined,
357
+ ts.factory.createTemplateExpression(
358
+ ts.factory.createTemplateHead(""),
359
+ [
360
+ ts.factory.createTemplateSpan(
361
+ ts.factory.createIdentifier("location"),
362
+ ts.factory.createTemplateMiddle("?"),
363
+ ),
364
+ ts.factory.createTemplateSpan(
365
+ ts.factory.createCallExpression(
366
+ IdentifierFactory.access(
367
+ ts.factory.createIdentifier(variables),
368
+ )("toString"),
369
+ undefined,
370
+ undefined,
371
+ ),
372
+ ts.factory.createTemplateTail(""),
373
+ ),
374
+ ],
375
+ ),
376
+ ),
377
+ ),
378
+ ],
379
+ true,
380
+ ),
381
+ );
382
+ };
383
+ }
384
+
385
+ const constant = (name: string) => (expression: ts.Expression) =>
386
+ ts.factory.createVariableStatement(
387
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
388
+ ts.factory.createVariableDeclarationList(
389
+ [
390
+ ts.factory.createVariableDeclaration(
391
+ name,
392
+ undefined,
393
+ undefined,
394
+ expression,
395
+ ),
396
+ ],
397
+ ts.NodeFlags.Const,
398
+ ),
399
+ );
400
+ const getPath = (props: ApiNamespaceProgrammer.IProps) =>
401
+ [...props.controller.path.split("/"), ...props.route.path.split("/")]
402
+ .filter((str) => !!str.length)
403
+ .join("/");
404
+ const local = (name: string) => (type: string) => (expression: ts.Expression) =>
405
+ ts.factory.createVariableStatement(
406
+ [],
407
+ ts.factory.createVariableDeclarationList(
408
+ [
409
+ ts.factory.createVariableDeclaration(
410
+ name,
411
+ undefined,
412
+ ts.factory.createTypeReferenceNode(type),
413
+ expression,
414
+ ),
415
+ ],
416
+ ts.NodeFlags.Const,
417
+ ),
418
+ );
@@ -0,0 +1,70 @@
1
+ import { HashMap, IPointer, hash } from "tstl";
2
+ import { Escaper } from "typia/lib/utils/Escaper";
3
+
4
+ import { IMigrateProgram } from "../module";
5
+ import { IMigrateFile } from "../structures/IMigrateFile";
6
+ import { FilePrinter } from "../utils/FilePrinter";
7
+ import { StringUtil } from "../utils/StringUtil";
8
+ import { ApiFileProgrammer } from "./ApiFileProgrammer";
9
+
10
+ export namespace ApiProgrammer {
11
+ export const write = (program: IMigrateProgram): IMigrateFile[] => {
12
+ const dict: HashMap<string[], ApiFileProgrammer.IProps> = new HashMap(
13
+ (x) => hash(x.join(".")),
14
+ (a, b) => a.join(".") === b.join("."),
15
+ );
16
+ for (const controller of program.controllers)
17
+ for (const route of controller.routes) {
18
+ const namespace: string[] = [
19
+ ...controller.path.split("/"),
20
+ ...route.path.split("/"),
21
+ ]
22
+ .filter((str) => !!str.length && str[0] !== ":")
23
+ .map(StringUtil.normalize)
24
+ .map((str) => (Escaper.variable(str) ? str : `_${str}`));
25
+ const last: IPointer<ApiFileProgrammer.IProps> = {
26
+ value: dict.take(namespace, () => ({
27
+ namespace,
28
+ children: new Set(),
29
+ entries: [],
30
+ })),
31
+ };
32
+ last.value.entries.push({
33
+ controller,
34
+ route,
35
+ alias: route.name,
36
+ });
37
+ namespace.slice(0, -1).forEach((_i, i, array) => {
38
+ const partial: string[] = namespace.slice(0, array.length - i);
39
+ const props: ApiFileProgrammer.IProps = dict.take(partial, () => ({
40
+ namespace: partial,
41
+ children: new Set(),
42
+ entries: [],
43
+ }));
44
+ props.children.add(last.value.namespace.at(-1)!);
45
+ last.value = props;
46
+ });
47
+ }
48
+ for (const { second: props } of dict)
49
+ props.entries.forEach(
50
+ (entry, i) =>
51
+ (entry.alias = StringUtil.escapeDuplicate([
52
+ ...props.children,
53
+ ...entry.route.parameters.map((p) => p.key),
54
+ ...(entry.route.body ? [entry.route.body.key] : []),
55
+ ...(entry.route.query ? [entry.route.query.key] : []),
56
+ ...props.entries.filter((_, j) => i !== j).map((e) => e.alias),
57
+ ])(entry.alias)),
58
+ );
59
+
60
+ return [...dict].map(({ second: props }) => ({
61
+ location: `src/api/functional/${props.namespace.join("/")}`,
62
+ file: "index.ts",
63
+ content: FilePrinter.write({
64
+ statements: ApiFileProgrammer.write(program.config)(
65
+ program.swagger.components,
66
+ )(props),
67
+ }),
68
+ }));
69
+ };
70
+ }