@nestia/migrate 4.6.1-dev.20250117 → 4.6.1

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 (49) hide show
  1. package/README.md +87 -87
  2. package/lib/bundles/NEST_TEMPLATE.js +66 -66
  3. package/lib/bundles/NEST_TEMPLATE.js.map +1 -1
  4. package/lib/bundles/SDK_TEMPLATE.js +30 -30
  5. package/lib/bundles/SDK_TEMPLATE.js.map +1 -1
  6. package/lib/index.mjs +92 -92
  7. package/lib/utils/openapi-down-convert/converter.js +2 -2
  8. package/package.json +6 -6
  9. package/src/MigrateApplication.ts +107 -107
  10. package/src/analyzers/MigrateApplicationAnalyzer.ts +18 -18
  11. package/src/analyzers/MigrateControllerAnalyzer.ts +51 -51
  12. package/src/archivers/MigrateFileArchiver.ts +38 -38
  13. package/src/bundles/NEST_TEMPLATE.ts +66 -66
  14. package/src/bundles/SDK_TEMPLATE.ts +30 -30
  15. package/src/executable/bundle.js +125 -125
  16. package/src/executable/migrate.ts +7 -7
  17. package/src/factories/TypeLiteralFactory.ts +57 -57
  18. package/src/index.ts +4 -4
  19. package/src/internal/MigrateCommander.ts +86 -86
  20. package/src/internal/MigrateInquirer.ts +89 -89
  21. package/src/module.ts +8 -8
  22. package/src/programmers/MigrateApiFileProgrammer.ts +49 -49
  23. package/src/programmers/MigrateApiFunctionProgrammer.ts +210 -210
  24. package/src/programmers/MigrateApiNamespaceProgrammer.ts +417 -417
  25. package/src/programmers/MigrateApiProgrammer.ts +103 -103
  26. package/src/programmers/MigrateApiSimulatationProgrammer.ts +324 -324
  27. package/src/programmers/MigrateApiStartProgrammer.ts +194 -194
  28. package/src/programmers/MigrateDtoProgrammer.ts +87 -87
  29. package/src/programmers/MigrateE2eFileProgrammer.ts +117 -117
  30. package/src/programmers/MigrateE2eProgrammer.ts +34 -34
  31. package/src/programmers/MigrateImportProgrammer.ts +118 -118
  32. package/src/programmers/MigrateNestControllerProgrammer.ts +50 -50
  33. package/src/programmers/MigrateNestMethodProgrammer.ts +393 -393
  34. package/src/programmers/MigrateNestModuleProgrammer.ts +65 -65
  35. package/src/programmers/MigrateNestProgrammer.ts +81 -81
  36. package/src/programmers/MigrateSchemaProgrammer.ts +373 -373
  37. package/src/structures/IHttpMigrateController.ts +8 -8
  38. package/src/structures/IHttpMigrateDto.ts +8 -8
  39. package/src/structures/IHttpMigrateFile.ts +5 -5
  40. package/src/structures/IHttpMigrateProgram.ts +27 -27
  41. package/src/structures/IHttpMigrateRoute.ts +1 -1
  42. package/src/structures/IHttpMigrateSchema.ts +4 -4
  43. package/src/utils/FilePrinter.ts +36 -36
  44. package/src/utils/MapUtil.ts +13 -13
  45. package/src/utils/OpenApiTypeChecker.ts +73 -73
  46. package/src/utils/SetupWizard.ts +12 -12
  47. package/src/utils/StringUtil.ts +113 -113
  48. package/src/utils/openapi-down-convert/RefVisitor.ts +139 -139
  49. package/src/utils/openapi-down-convert/converter.ts +527 -527
@@ -1,373 +1,373 @@
1
- import { OpenApi } from "@samchon/openapi";
2
- import ts from "typescript";
3
- import typia from "typia";
4
- import { TypeFactory } from "typia/lib/factories/TypeFactory";
5
- import { FormatCheatSheet } from "typia/lib/tags/internal/FormatCheatSheet";
6
- import { Escaper } from "typia/lib/utils/Escaper";
7
-
8
- import { FilePrinter } from "../utils/FilePrinter";
9
- import { OpenApiTypeChecker } from "../utils/OpenApiTypeChecker";
10
- import { StringUtil } from "../utils/StringUtil";
11
- import { MigrateImportProgrammer } from "./MigrateImportProgrammer";
12
-
13
- export namespace MigrateSchemaProgrammer {
14
- /* -----------------------------------------------------------
15
- FACADE
16
- ----------------------------------------------------------- */
17
- export const write =
18
- (components: OpenApi.IComponents) =>
19
- (importer: MigrateImportProgrammer) =>
20
- (schema: OpenApi.IJsonSchema): ts.TypeNode => {
21
- // CONSIDER ANY TYPE CASE
22
- const union: ts.TypeNode[] = [];
23
- if (OpenApiTypeChecker.isUnknown(schema))
24
- return TypeFactory.keyword("any");
25
-
26
- // ITERATION
27
- const type: ts.TypeNode = (() => {
28
- // ATOMIC
29
- if (OpenApiTypeChecker.isConstant(schema))
30
- return writeConstant(importer)(schema);
31
- else if (OpenApiTypeChecker.isBoolean(schema))
32
- return writeBoolean(importer)(schema);
33
- else if (OpenApiTypeChecker.isInteger(schema))
34
- return writeInteger(importer)(schema);
35
- else if (OpenApiTypeChecker.isNumber(schema))
36
- return writeNumber(importer)(schema);
37
- else if (OpenApiTypeChecker.isString(schema))
38
- return writeString(importer)(schema);
39
- // INSTANCES
40
- else if (OpenApiTypeChecker.isArray(schema))
41
- return writeArray(components)(importer)(schema);
42
- else if (OpenApiTypeChecker.isTuple(schema))
43
- return writeTuple(components)(importer)(schema);
44
- else if (OpenApiTypeChecker.isObject(schema))
45
- return writeObject(components)(importer)(schema);
46
- else if (OpenApiTypeChecker.isReference(schema))
47
- return writeReference(importer)(schema);
48
- // UNION
49
- else if (OpenApiTypeChecker.isOneOf(schema))
50
- return writeUnion(components)(importer)(schema.oneOf);
51
- else if (OpenApiTypeChecker.isNull(schema)) return createNode("null");
52
- else return TypeFactory.keyword("any");
53
- })();
54
- union.push(type);
55
-
56
- // DETERMINE
57
- if (union.length === 0) return TypeFactory.keyword("any");
58
- else if (union.length === 1) return union[0];
59
- return ts.factory.createUnionTypeNode(union);
60
- };
61
-
62
- /* -----------------------------------------------------------
63
- ATOMICS
64
- ----------------------------------------------------------- */
65
- const writeConstant =
66
- (importer: MigrateImportProgrammer) =>
67
- (schema: OpenApi.IJsonSchema.IConstant): ts.TypeNode => {
68
- const intersection: ts.TypeNode[] = [
69
- ts.factory.createLiteralTypeNode(
70
- typeof schema.const === "boolean"
71
- ? schema.const === true
72
- ? ts.factory.createTrue()
73
- : ts.factory.createFalse()
74
- : typeof schema.const === "number"
75
- ? schema.const < 0
76
- ? ts.factory.createPrefixUnaryExpression(
77
- ts.SyntaxKind.MinusToken,
78
- ts.factory.createNumericLiteral(-schema.const),
79
- )
80
- : ts.factory.createNumericLiteral(schema.const)
81
- : ts.factory.createStringLiteral(schema.const),
82
- ),
83
- ];
84
- writePlugin({
85
- importer,
86
- regular: typia.misc.literals<keyof OpenApi.IJsonSchema.IConstant>(),
87
- intersection,
88
- })(schema);
89
- return intersection.length === 1
90
- ? intersection[0]
91
- : ts.factory.createIntersectionTypeNode(intersection);
92
- };
93
-
94
- const writeBoolean =
95
- (importer: MigrateImportProgrammer) =>
96
- (schema: OpenApi.IJsonSchema.IBoolean): ts.TypeNode => {
97
- const intersection: ts.TypeNode[] = [TypeFactory.keyword("boolean")];
98
- writePlugin({
99
- importer,
100
- regular: typia.misc.literals<keyof OpenApi.IJsonSchema.IBoolean>(),
101
- intersection,
102
- })(schema);
103
- return intersection.length === 1
104
- ? intersection[0]
105
- : ts.factory.createIntersectionTypeNode(intersection);
106
- };
107
-
108
- const writeInteger =
109
- (importer: MigrateImportProgrammer) =>
110
- (schema: OpenApi.IJsonSchema.IInteger): ts.TypeNode =>
111
- writeNumeric(() => [
112
- TypeFactory.keyword("number"),
113
- importer.tag("Type", "int32"),
114
- ])(importer)(schema);
115
-
116
- const writeNumber =
117
- (importer: MigrateImportProgrammer) =>
118
- (schema: OpenApi.IJsonSchema.INumber): ts.TypeNode =>
119
- writeNumeric(() => [TypeFactory.keyword("number")])(importer)(schema);
120
-
121
- const writeNumeric =
122
- (factory: () => ts.TypeNode[]) =>
123
- (importer: MigrateImportProgrammer) =>
124
- (
125
- schema: OpenApi.IJsonSchema.IInteger | OpenApi.IJsonSchema.INumber,
126
- ): ts.TypeNode => {
127
- const intersection: ts.TypeNode[] = factory();
128
- if (schema.default !== undefined)
129
- intersection.push(importer.tag("Default", schema.default));
130
- if (schema.minimum !== undefined)
131
- intersection.push(
132
- importer.tag(
133
- schema.exclusiveMinimum ? "ExclusiveMinimum" : "Minimum",
134
- schema.minimum,
135
- ),
136
- );
137
- if (schema.maximum !== undefined)
138
- intersection.push(
139
- importer.tag(
140
- schema.exclusiveMaximum ? "ExclusiveMaximum" : "Maximum",
141
- schema.maximum,
142
- ),
143
- );
144
- if (schema.multipleOf !== undefined)
145
- intersection.push(importer.tag("MultipleOf", schema.multipleOf));
146
- writePlugin({
147
- importer,
148
- regular: typia.misc.literals<keyof OpenApi.IJsonSchema.INumber>(),
149
- intersection,
150
- })(schema);
151
- return intersection.length === 1
152
- ? intersection[0]
153
- : ts.factory.createIntersectionTypeNode(intersection);
154
- };
155
-
156
- const writeString =
157
- (importer: MigrateImportProgrammer) =>
158
- (schema: OpenApi.IJsonSchema.IString): ts.TypeNode => {
159
- if (schema.format === "binary")
160
- return ts.factory.createTypeReferenceNode("File");
161
-
162
- const intersection: ts.TypeNode[] = [TypeFactory.keyword("string")];
163
- if (schema.default !== undefined)
164
- intersection.push(importer.tag("Default", schema.default));
165
- if (schema.minLength !== undefined)
166
- intersection.push(importer.tag("MinLength", schema.minLength));
167
- if (schema.maxLength !== undefined)
168
- intersection.push(importer.tag("MaxLength", schema.maxLength));
169
- if (schema.pattern !== undefined)
170
- intersection.push(importer.tag("Pattern", schema.pattern));
171
- if (
172
- schema.format !== undefined &&
173
- (FormatCheatSheet as Record<string, string>)[schema.format] !==
174
- undefined
175
- )
176
- intersection.push(importer.tag("Format", schema.format));
177
- if (schema.contentMediaType !== undefined)
178
- intersection.push(
179
- importer.tag("ContentMediaType", schema.contentMediaType),
180
- );
181
- writePlugin({
182
- importer,
183
- regular: typia.misc.literals<keyof OpenApi.IJsonSchema.IString>(),
184
- intersection,
185
- })(schema);
186
- return intersection.length === 1
187
- ? intersection[0]
188
- : ts.factory.createIntersectionTypeNode(intersection);
189
- };
190
-
191
- /* -----------------------------------------------------------
192
- INSTANCES
193
- ----------------------------------------------------------- */
194
- const writeArray =
195
- (components: OpenApi.IComponents) =>
196
- (importer: MigrateImportProgrammer) =>
197
- (schema: OpenApi.IJsonSchema.IArray): ts.TypeNode => {
198
- const intersection: ts.TypeNode[] = [
199
- ts.factory.createArrayTypeNode(
200
- write(components)(importer)(schema.items),
201
- ),
202
- ];
203
- if (schema.minItems !== undefined)
204
- intersection.push(importer.tag("MinItems", schema.minItems));
205
- if (schema.maxItems !== undefined)
206
- intersection.push(importer.tag("MaxItems", schema.maxItems));
207
- if (schema.uniqueItems === true)
208
- intersection.push(importer.tag("UniqueItems"));
209
- writePlugin({
210
- importer,
211
- regular: typia.misc.literals<keyof OpenApi.IJsonSchema.IArray>(),
212
- intersection,
213
- })(schema);
214
- return intersection.length === 1
215
- ? intersection[0]
216
- : ts.factory.createIntersectionTypeNode(intersection);
217
- };
218
-
219
- const writeTuple =
220
- (components: OpenApi.IComponents) =>
221
- (importer: MigrateImportProgrammer) =>
222
- (schema: OpenApi.IJsonSchema.ITuple): ts.TypeNode => {
223
- const tuple: ts.TypeNode = ts.factory.createTupleTypeNode([
224
- ...schema.prefixItems.map(write(components)(importer)),
225
- ...(typeof schema.additionalItems === "object" &&
226
- schema.additionalItems !== null
227
- ? [
228
- ts.factory.createRestTypeNode(
229
- write(components)(importer)(schema.additionalItems),
230
- ),
231
- ]
232
- : schema.additionalItems === true
233
- ? [
234
- ts.factory.createRestTypeNode(
235
- ts.factory.createArrayTypeNode(
236
- ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
237
- ),
238
- ),
239
- ]
240
- : []),
241
- ]);
242
- const intersection: ts.TypeNode[] = [tuple];
243
- writePlugin({
244
- importer,
245
- regular: typia.misc.literals<keyof OpenApi.IJsonSchema.ITuple>(),
246
- intersection,
247
- })(schema);
248
- return intersection.length === 1
249
- ? intersection[0]
250
- : ts.factory.createIntersectionTypeNode(intersection);
251
- };
252
-
253
- const writeObject =
254
- (components: OpenApi.IComponents) =>
255
- (importer: MigrateImportProgrammer) =>
256
- (schema: OpenApi.IJsonSchema.IObject): ts.TypeNode => {
257
- const regular = () =>
258
- ts.factory.createTypeLiteralNode(
259
- Object.entries(schema.properties ?? []).map(([key, value]) =>
260
- writeRegularProperty(components)(importer)(schema.required ?? [])(
261
- key,
262
- value,
263
- ),
264
- ),
265
- );
266
- const dynamic = () =>
267
- ts.factory.createTypeLiteralNode([
268
- writeDynamicProperty(components)(importer)(
269
- schema.additionalProperties as OpenApi.IJsonSchema,
270
- ),
271
- ]);
272
- return !!schema.properties?.length &&
273
- typeof schema.additionalProperties === "object"
274
- ? ts.factory.createIntersectionTypeNode([regular(), dynamic()])
275
- : typeof schema.additionalProperties === "object"
276
- ? dynamic()
277
- : regular();
278
- };
279
-
280
- const writeRegularProperty =
281
- (components: OpenApi.IComponents) =>
282
- (importer: MigrateImportProgrammer) =>
283
- (required: string[]) =>
284
- (key: string, value: OpenApi.IJsonSchema) =>
285
- FilePrinter.description(
286
- ts.factory.createPropertySignature(
287
- undefined,
288
- Escaper.variable(key)
289
- ? ts.factory.createIdentifier(key)
290
- : ts.factory.createStringLiteral(key),
291
- required.includes(key)
292
- ? undefined
293
- : ts.factory.createToken(ts.SyntaxKind.QuestionToken),
294
- write(components)(importer)(value),
295
- ),
296
- writeComment(value),
297
- );
298
-
299
- const writeDynamicProperty =
300
- (components: OpenApi.IComponents) =>
301
- (importer: MigrateImportProgrammer) =>
302
- (value: OpenApi.IJsonSchema) =>
303
- FilePrinter.description(
304
- ts.factory.createIndexSignature(
305
- undefined,
306
- [
307
- ts.factory.createParameterDeclaration(
308
- undefined,
309
- undefined,
310
- ts.factory.createIdentifier("key"),
311
- undefined,
312
- TypeFactory.keyword("string"),
313
- ),
314
- ],
315
- write(components)(importer)(value),
316
- ),
317
- writeComment(value),
318
- );
319
-
320
- const writeReference =
321
- (importer: MigrateImportProgrammer) =>
322
- (
323
- schema: OpenApi.IJsonSchema.IReference,
324
- ): ts.TypeReferenceNode | ts.KeywordTypeNode => {
325
- if (schema.$ref.startsWith("#/components/schemas") === false)
326
- return TypeFactory.keyword("any");
327
- const name: string = schema.$ref
328
- .split("/")
329
- .slice(3)
330
- .filter((str) => str.length !== 0)
331
- .map(StringUtil.escapeNonVariable)
332
- .join("");
333
- if (name === "") return TypeFactory.keyword("any");
334
- return importer.dto(name);
335
- };
336
-
337
- /* -----------------------------------------------------------
338
- UNIONS
339
- ----------------------------------------------------------- */
340
- const writeUnion =
341
- (components: OpenApi.IComponents) =>
342
- (importer: MigrateImportProgrammer) =>
343
- (elements: OpenApi.IJsonSchema[]): ts.UnionTypeNode =>
344
- ts.factory.createUnionTypeNode(elements.map(write(components)(importer)));
345
- }
346
- const createNode = (text: string) => ts.factory.createTypeReferenceNode(text);
347
- const writeComment = (schema: OpenApi.IJsonSchema): string =>
348
- [
349
- ...(schema.description?.length ? [schema.description] : []),
350
- ...(schema.description?.length &&
351
- (schema.title !== undefined || schema.deprecated === true)
352
- ? [""]
353
- : []),
354
- ...(schema.title !== undefined ? [`@title ${schema.title}`] : []),
355
- ...(schema.deprecated === true ? [`@deprecated`] : []),
356
- ]
357
- .join("\n")
358
- .split("*/")
359
- .join("*\\/");
360
- const writePlugin =
361
- (props: {
362
- importer: MigrateImportProgrammer;
363
- regular: string[];
364
- intersection: ts.TypeNode[];
365
- }) =>
366
- (schema: any) => {
367
- const extra: any = {};
368
- for (const [key, value] of Object.entries(schema))
369
- if (value !== undefined && false === props.regular.includes(key))
370
- extra[key] = value;
371
- if (Object.keys(extra).length !== 0)
372
- props.intersection.push(props.importer.tag("JsonSchemaPlugin", extra));
373
- };
1
+ import { OpenApi } from "@samchon/openapi";
2
+ import ts from "typescript";
3
+ import typia from "typia";
4
+ import { TypeFactory } from "typia/lib/factories/TypeFactory";
5
+ import { FormatCheatSheet } from "typia/lib/tags/internal/FormatCheatSheet";
6
+ import { Escaper } from "typia/lib/utils/Escaper";
7
+
8
+ import { FilePrinter } from "../utils/FilePrinter";
9
+ import { OpenApiTypeChecker } from "../utils/OpenApiTypeChecker";
10
+ import { StringUtil } from "../utils/StringUtil";
11
+ import { MigrateImportProgrammer } from "./MigrateImportProgrammer";
12
+
13
+ export namespace MigrateSchemaProgrammer {
14
+ /* -----------------------------------------------------------
15
+ FACADE
16
+ ----------------------------------------------------------- */
17
+ export const write =
18
+ (components: OpenApi.IComponents) =>
19
+ (importer: MigrateImportProgrammer) =>
20
+ (schema: OpenApi.IJsonSchema): ts.TypeNode => {
21
+ // CONSIDER ANY TYPE CASE
22
+ const union: ts.TypeNode[] = [];
23
+ if (OpenApiTypeChecker.isUnknown(schema))
24
+ return TypeFactory.keyword("any");
25
+
26
+ // ITERATION
27
+ const type: ts.TypeNode = (() => {
28
+ // ATOMIC
29
+ if (OpenApiTypeChecker.isConstant(schema))
30
+ return writeConstant(importer)(schema);
31
+ else if (OpenApiTypeChecker.isBoolean(schema))
32
+ return writeBoolean(importer)(schema);
33
+ else if (OpenApiTypeChecker.isInteger(schema))
34
+ return writeInteger(importer)(schema);
35
+ else if (OpenApiTypeChecker.isNumber(schema))
36
+ return writeNumber(importer)(schema);
37
+ else if (OpenApiTypeChecker.isString(schema))
38
+ return writeString(importer)(schema);
39
+ // INSTANCES
40
+ else if (OpenApiTypeChecker.isArray(schema))
41
+ return writeArray(components)(importer)(schema);
42
+ else if (OpenApiTypeChecker.isTuple(schema))
43
+ return writeTuple(components)(importer)(schema);
44
+ else if (OpenApiTypeChecker.isObject(schema))
45
+ return writeObject(components)(importer)(schema);
46
+ else if (OpenApiTypeChecker.isReference(schema))
47
+ return writeReference(importer)(schema);
48
+ // UNION
49
+ else if (OpenApiTypeChecker.isOneOf(schema))
50
+ return writeUnion(components)(importer)(schema.oneOf);
51
+ else if (OpenApiTypeChecker.isNull(schema)) return createNode("null");
52
+ else return TypeFactory.keyword("any");
53
+ })();
54
+ union.push(type);
55
+
56
+ // DETERMINE
57
+ if (union.length === 0) return TypeFactory.keyword("any");
58
+ else if (union.length === 1) return union[0];
59
+ return ts.factory.createUnionTypeNode(union);
60
+ };
61
+
62
+ /* -----------------------------------------------------------
63
+ ATOMICS
64
+ ----------------------------------------------------------- */
65
+ const writeConstant =
66
+ (importer: MigrateImportProgrammer) =>
67
+ (schema: OpenApi.IJsonSchema.IConstant): ts.TypeNode => {
68
+ const intersection: ts.TypeNode[] = [
69
+ ts.factory.createLiteralTypeNode(
70
+ typeof schema.const === "boolean"
71
+ ? schema.const === true
72
+ ? ts.factory.createTrue()
73
+ : ts.factory.createFalse()
74
+ : typeof schema.const === "number"
75
+ ? schema.const < 0
76
+ ? ts.factory.createPrefixUnaryExpression(
77
+ ts.SyntaxKind.MinusToken,
78
+ ts.factory.createNumericLiteral(-schema.const),
79
+ )
80
+ : ts.factory.createNumericLiteral(schema.const)
81
+ : ts.factory.createStringLiteral(schema.const),
82
+ ),
83
+ ];
84
+ writePlugin({
85
+ importer,
86
+ regular: typia.misc.literals<keyof OpenApi.IJsonSchema.IConstant>(),
87
+ intersection,
88
+ })(schema);
89
+ return intersection.length === 1
90
+ ? intersection[0]
91
+ : ts.factory.createIntersectionTypeNode(intersection);
92
+ };
93
+
94
+ const writeBoolean =
95
+ (importer: MigrateImportProgrammer) =>
96
+ (schema: OpenApi.IJsonSchema.IBoolean): ts.TypeNode => {
97
+ const intersection: ts.TypeNode[] = [TypeFactory.keyword("boolean")];
98
+ writePlugin({
99
+ importer,
100
+ regular: typia.misc.literals<keyof OpenApi.IJsonSchema.IBoolean>(),
101
+ intersection,
102
+ })(schema);
103
+ return intersection.length === 1
104
+ ? intersection[0]
105
+ : ts.factory.createIntersectionTypeNode(intersection);
106
+ };
107
+
108
+ const writeInteger =
109
+ (importer: MigrateImportProgrammer) =>
110
+ (schema: OpenApi.IJsonSchema.IInteger): ts.TypeNode =>
111
+ writeNumeric(() => [
112
+ TypeFactory.keyword("number"),
113
+ importer.tag("Type", "int32"),
114
+ ])(importer)(schema);
115
+
116
+ const writeNumber =
117
+ (importer: MigrateImportProgrammer) =>
118
+ (schema: OpenApi.IJsonSchema.INumber): ts.TypeNode =>
119
+ writeNumeric(() => [TypeFactory.keyword("number")])(importer)(schema);
120
+
121
+ const writeNumeric =
122
+ (factory: () => ts.TypeNode[]) =>
123
+ (importer: MigrateImportProgrammer) =>
124
+ (
125
+ schema: OpenApi.IJsonSchema.IInteger | OpenApi.IJsonSchema.INumber,
126
+ ): ts.TypeNode => {
127
+ const intersection: ts.TypeNode[] = factory();
128
+ if (schema.default !== undefined)
129
+ intersection.push(importer.tag("Default", schema.default));
130
+ if (schema.minimum !== undefined)
131
+ intersection.push(
132
+ importer.tag(
133
+ schema.exclusiveMinimum ? "ExclusiveMinimum" : "Minimum",
134
+ schema.minimum,
135
+ ),
136
+ );
137
+ if (schema.maximum !== undefined)
138
+ intersection.push(
139
+ importer.tag(
140
+ schema.exclusiveMaximum ? "ExclusiveMaximum" : "Maximum",
141
+ schema.maximum,
142
+ ),
143
+ );
144
+ if (schema.multipleOf !== undefined)
145
+ intersection.push(importer.tag("MultipleOf", schema.multipleOf));
146
+ writePlugin({
147
+ importer,
148
+ regular: typia.misc.literals<keyof OpenApi.IJsonSchema.INumber>(),
149
+ intersection,
150
+ })(schema);
151
+ return intersection.length === 1
152
+ ? intersection[0]
153
+ : ts.factory.createIntersectionTypeNode(intersection);
154
+ };
155
+
156
+ const writeString =
157
+ (importer: MigrateImportProgrammer) =>
158
+ (schema: OpenApi.IJsonSchema.IString): ts.TypeNode => {
159
+ if (schema.format === "binary")
160
+ return ts.factory.createTypeReferenceNode("File");
161
+
162
+ const intersection: ts.TypeNode[] = [TypeFactory.keyword("string")];
163
+ if (schema.default !== undefined)
164
+ intersection.push(importer.tag("Default", schema.default));
165
+ if (schema.minLength !== undefined)
166
+ intersection.push(importer.tag("MinLength", schema.minLength));
167
+ if (schema.maxLength !== undefined)
168
+ intersection.push(importer.tag("MaxLength", schema.maxLength));
169
+ if (schema.pattern !== undefined)
170
+ intersection.push(importer.tag("Pattern", schema.pattern));
171
+ if (
172
+ schema.format !== undefined &&
173
+ (FormatCheatSheet as Record<string, string>)[schema.format] !==
174
+ undefined
175
+ )
176
+ intersection.push(importer.tag("Format", schema.format));
177
+ if (schema.contentMediaType !== undefined)
178
+ intersection.push(
179
+ importer.tag("ContentMediaType", schema.contentMediaType),
180
+ );
181
+ writePlugin({
182
+ importer,
183
+ regular: typia.misc.literals<keyof OpenApi.IJsonSchema.IString>(),
184
+ intersection,
185
+ })(schema);
186
+ return intersection.length === 1
187
+ ? intersection[0]
188
+ : ts.factory.createIntersectionTypeNode(intersection);
189
+ };
190
+
191
+ /* -----------------------------------------------------------
192
+ INSTANCES
193
+ ----------------------------------------------------------- */
194
+ const writeArray =
195
+ (components: OpenApi.IComponents) =>
196
+ (importer: MigrateImportProgrammer) =>
197
+ (schema: OpenApi.IJsonSchema.IArray): ts.TypeNode => {
198
+ const intersection: ts.TypeNode[] = [
199
+ ts.factory.createArrayTypeNode(
200
+ write(components)(importer)(schema.items),
201
+ ),
202
+ ];
203
+ if (schema.minItems !== undefined)
204
+ intersection.push(importer.tag("MinItems", schema.minItems));
205
+ if (schema.maxItems !== undefined)
206
+ intersection.push(importer.tag("MaxItems", schema.maxItems));
207
+ if (schema.uniqueItems === true)
208
+ intersection.push(importer.tag("UniqueItems"));
209
+ writePlugin({
210
+ importer,
211
+ regular: typia.misc.literals<keyof OpenApi.IJsonSchema.IArray>(),
212
+ intersection,
213
+ })(schema);
214
+ return intersection.length === 1
215
+ ? intersection[0]
216
+ : ts.factory.createIntersectionTypeNode(intersection);
217
+ };
218
+
219
+ const writeTuple =
220
+ (components: OpenApi.IComponents) =>
221
+ (importer: MigrateImportProgrammer) =>
222
+ (schema: OpenApi.IJsonSchema.ITuple): ts.TypeNode => {
223
+ const tuple: ts.TypeNode = ts.factory.createTupleTypeNode([
224
+ ...schema.prefixItems.map(write(components)(importer)),
225
+ ...(typeof schema.additionalItems === "object" &&
226
+ schema.additionalItems !== null
227
+ ? [
228
+ ts.factory.createRestTypeNode(
229
+ write(components)(importer)(schema.additionalItems),
230
+ ),
231
+ ]
232
+ : schema.additionalItems === true
233
+ ? [
234
+ ts.factory.createRestTypeNode(
235
+ ts.factory.createArrayTypeNode(
236
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
237
+ ),
238
+ ),
239
+ ]
240
+ : []),
241
+ ]);
242
+ const intersection: ts.TypeNode[] = [tuple];
243
+ writePlugin({
244
+ importer,
245
+ regular: typia.misc.literals<keyof OpenApi.IJsonSchema.ITuple>(),
246
+ intersection,
247
+ })(schema);
248
+ return intersection.length === 1
249
+ ? intersection[0]
250
+ : ts.factory.createIntersectionTypeNode(intersection);
251
+ };
252
+
253
+ const writeObject =
254
+ (components: OpenApi.IComponents) =>
255
+ (importer: MigrateImportProgrammer) =>
256
+ (schema: OpenApi.IJsonSchema.IObject): ts.TypeNode => {
257
+ const regular = () =>
258
+ ts.factory.createTypeLiteralNode(
259
+ Object.entries(schema.properties ?? []).map(([key, value]) =>
260
+ writeRegularProperty(components)(importer)(schema.required ?? [])(
261
+ key,
262
+ value,
263
+ ),
264
+ ),
265
+ );
266
+ const dynamic = () =>
267
+ ts.factory.createTypeLiteralNode([
268
+ writeDynamicProperty(components)(importer)(
269
+ schema.additionalProperties as OpenApi.IJsonSchema,
270
+ ),
271
+ ]);
272
+ return !!schema.properties?.length &&
273
+ typeof schema.additionalProperties === "object"
274
+ ? ts.factory.createIntersectionTypeNode([regular(), dynamic()])
275
+ : typeof schema.additionalProperties === "object"
276
+ ? dynamic()
277
+ : regular();
278
+ };
279
+
280
+ const writeRegularProperty =
281
+ (components: OpenApi.IComponents) =>
282
+ (importer: MigrateImportProgrammer) =>
283
+ (required: string[]) =>
284
+ (key: string, value: OpenApi.IJsonSchema) =>
285
+ FilePrinter.description(
286
+ ts.factory.createPropertySignature(
287
+ undefined,
288
+ Escaper.variable(key)
289
+ ? ts.factory.createIdentifier(key)
290
+ : ts.factory.createStringLiteral(key),
291
+ required.includes(key)
292
+ ? undefined
293
+ : ts.factory.createToken(ts.SyntaxKind.QuestionToken),
294
+ write(components)(importer)(value),
295
+ ),
296
+ writeComment(value),
297
+ );
298
+
299
+ const writeDynamicProperty =
300
+ (components: OpenApi.IComponents) =>
301
+ (importer: MigrateImportProgrammer) =>
302
+ (value: OpenApi.IJsonSchema) =>
303
+ FilePrinter.description(
304
+ ts.factory.createIndexSignature(
305
+ undefined,
306
+ [
307
+ ts.factory.createParameterDeclaration(
308
+ undefined,
309
+ undefined,
310
+ ts.factory.createIdentifier("key"),
311
+ undefined,
312
+ TypeFactory.keyword("string"),
313
+ ),
314
+ ],
315
+ write(components)(importer)(value),
316
+ ),
317
+ writeComment(value),
318
+ );
319
+
320
+ const writeReference =
321
+ (importer: MigrateImportProgrammer) =>
322
+ (
323
+ schema: OpenApi.IJsonSchema.IReference,
324
+ ): ts.TypeReferenceNode | ts.KeywordTypeNode => {
325
+ if (schema.$ref.startsWith("#/components/schemas") === false)
326
+ return TypeFactory.keyword("any");
327
+ const name: string = schema.$ref
328
+ .split("/")
329
+ .slice(3)
330
+ .filter((str) => str.length !== 0)
331
+ .map(StringUtil.escapeNonVariable)
332
+ .join("");
333
+ if (name === "") return TypeFactory.keyword("any");
334
+ return importer.dto(name);
335
+ };
336
+
337
+ /* -----------------------------------------------------------
338
+ UNIONS
339
+ ----------------------------------------------------------- */
340
+ const writeUnion =
341
+ (components: OpenApi.IComponents) =>
342
+ (importer: MigrateImportProgrammer) =>
343
+ (elements: OpenApi.IJsonSchema[]): ts.UnionTypeNode =>
344
+ ts.factory.createUnionTypeNode(elements.map(write(components)(importer)));
345
+ }
346
+ const createNode = (text: string) => ts.factory.createTypeReferenceNode(text);
347
+ const writeComment = (schema: OpenApi.IJsonSchema): string =>
348
+ [
349
+ ...(schema.description?.length ? [schema.description] : []),
350
+ ...(schema.description?.length &&
351
+ (schema.title !== undefined || schema.deprecated === true)
352
+ ? [""]
353
+ : []),
354
+ ...(schema.title !== undefined ? [`@title ${schema.title}`] : []),
355
+ ...(schema.deprecated === true ? [`@deprecated`] : []),
356
+ ]
357
+ .join("\n")
358
+ .split("*/")
359
+ .join("*\\/");
360
+ const writePlugin =
361
+ (props: {
362
+ importer: MigrateImportProgrammer;
363
+ regular: string[];
364
+ intersection: ts.TypeNode[];
365
+ }) =>
366
+ (schema: any) => {
367
+ const extra: any = {};
368
+ for (const [key, value] of Object.entries(schema))
369
+ if (value !== undefined && false === props.regular.includes(key))
370
+ extra[key] = value;
371
+ if (Object.keys(extra).length !== 0)
372
+ props.intersection.push(props.importer.tag("JsonSchemaPlugin", extra));
373
+ };