@nestia/migrate 0.6.2 → 0.7.0-dev.20240201

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 (59) hide show
  1. package/lib/IMigrateConfig.d.ts +4 -0
  2. package/lib/IMigrateConfig.js +3 -0
  3. package/lib/IMigrateConfig.js.map +1 -0
  4. package/lib/MigrateApplication.d.ts +2 -12
  5. package/lib/MigrateApplication.js +20 -60
  6. package/lib/MigrateApplication.js.map +1 -1
  7. package/lib/analyzers/MigrateAnalyzer.d.ts +2 -1
  8. package/lib/analyzers/MigrateAnalyzer.js +2 -1
  9. package/lib/analyzers/MigrateAnalyzer.js.map +1 -1
  10. package/lib/bundles/NEST_TEMPLATE.d.ts +5 -0
  11. package/{src/bundles/TEMPLATE.ts → lib/bundles/NEST_TEMPLATE.js} +71 -27
  12. package/lib/bundles/NEST_TEMPLATE.js.map +1 -0
  13. package/lib/bundles/SDK_TEMPLATE.d.ts +5 -0
  14. package/lib/bundles/SDK_TEMPLATE.js +61 -0
  15. package/lib/bundles/SDK_TEMPLATE.js.map +1 -0
  16. package/lib/bundles/TEMPLATE.d.ts +1 -1
  17. package/lib/bundles/TEMPLATE.js +23 -23
  18. package/lib/bundles/TEMPLATE.js.map +1 -1
  19. package/lib/executable/bundle.js +70 -51
  20. package/lib/executable/bundle.js.map +1 -1
  21. package/lib/executable/migrate.js +2 -59
  22. package/lib/executable/migrate.js.map +1 -1
  23. package/lib/internal/MigrateCli.d.ts +3 -0
  24. package/lib/internal/MigrateCli.js +59 -0
  25. package/lib/internal/MigrateCli.js.map +1 -0
  26. package/lib/internal/MigrateCommander.d.ts +9 -0
  27. package/lib/internal/MigrateCommander.js +70 -0
  28. package/lib/internal/MigrateCommander.js.map +1 -0
  29. package/lib/programmers/ApiFileProgrammer.d.ts +2 -1
  30. package/lib/programmers/ApiFileProgrammer.js +3 -3
  31. package/lib/programmers/ApiFileProgrammer.js.map +1 -1
  32. package/lib/programmers/ApiFunctionProgrammer.d.ts +3 -1
  33. package/lib/programmers/ApiFunctionProgrammer.js +34 -23
  34. package/lib/programmers/ApiFunctionProgrammer.js.map +1 -1
  35. package/lib/programmers/ApiNamespaceProgrammer.d.ts +3 -1
  36. package/lib/programmers/ApiNamespaceProgrammer.js +15 -2
  37. package/lib/programmers/ApiNamespaceProgrammer.js.map +1 -1
  38. package/lib/programmers/ApiProgrammer.js +48 -3
  39. package/lib/programmers/ApiProgrammer.js.map +1 -1
  40. package/lib/programmers/ApiSimulatationProgrammer.d.ts +14 -0
  41. package/lib/programmers/ApiSimulatationProgrammer.js +127 -0
  42. package/lib/programmers/ApiSimulatationProgrammer.js.map +1 -0
  43. package/lib/structures/IMigrateProgram.d.ts +2 -0
  44. package/package.json +68 -64
  45. package/src/IMigrateConfig.ts +4 -0
  46. package/src/MigrateApplication.ts +20 -63
  47. package/src/analyzers/MigrateAnalyzer.ts +8 -4
  48. package/src/bundles/NEST_TEMPLATE.ts +202 -0
  49. package/src/bundles/SDK_TEMPLATE.ts +57 -0
  50. package/src/executable/bundle.ts +80 -50
  51. package/src/executable/migrate.ts +2 -54
  52. package/src/internal/MigrateCommander.ts +52 -0
  53. package/src/internal/MigrateInquirer.ts +79 -0
  54. package/src/programmers/ApiFileProgrammer.ts +4 -2
  55. package/src/programmers/ApiFunctionProgrammer.ts +47 -20
  56. package/src/programmers/ApiNamespaceProgrammer.ts +24 -1
  57. package/src/programmers/ApiProgrammer.ts +63 -3
  58. package/src/programmers/ApiSimulatationProgrammer.ts +328 -0
  59. package/src/structures/IMigrateProgram.ts +2 -0
@@ -1,59 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import fs from "fs";
3
- import path from "path";
2
+ import { MigrateCommander } from "../internal/MigrateCommander";
4
3
 
5
- import { MigrateApplication } from "../MigrateApplication";
6
- import { ISwagger } from "../structures/ISwagger";
7
- import { SetupWizard } from "../utils/SetupWizard";
8
-
9
- const USAGE = `Wrong command has been detected. Use like below:
10
-
11
- npx @nestia/migrate [input] [output]
12
-
13
- ex) npx @nestia/migrate swagger.json my-new-project
14
- `;
15
-
16
- function halt(desc: string): never {
17
- console.error(desc);
18
- process.exit(-1);
19
- }
20
-
21
- const main = async (argv: string[]): Promise<void> => {
22
- const resolve = (str: string | undefined) =>
23
- str ? path.resolve(str).split("\\").join("/") : undefined;
24
- const input: string | undefined = resolve(argv[0]);
25
- const output: string | undefined = resolve(argv[1]);
26
-
27
- // VALIDATE ARGUMENTS
28
- if (input === undefined || output === undefined) halt(USAGE);
29
-
30
- // VALIDATE OUTPUT DIRECTORY
31
- const parent: string = resolve(output + "/..")!;
32
- if (fs.existsSync(output)) halt("Output directory alreay exists.");
33
- else if (fs.existsSync(parent) === false)
34
- halt("Output directory's parent directory does not exist.");
35
- else if (fs.statSync(parent).isDirectory() === false)
36
- halt("Output directory's parent is not a directory.");
37
-
38
- // READ SWAGGER
39
- const swagger: ISwagger = (() => {
40
- if (fs.existsSync(input) === false)
41
- halt("Unable to find the input swagger.json file.");
42
- const stats: fs.Stats = fs.statSync(input);
43
- if (stats.isFile() === false) halt("The input swagger.json is not a file.");
44
- const content: string = fs.readFileSync(input, "utf-8");
45
- const swagger: ISwagger = JSON.parse(content);
46
- return swagger;
47
- })();
48
-
49
- // DO GENERATE
50
- const app: MigrateApplication = new MigrateApplication(swagger);
51
- await app.generate(output);
52
-
53
- // RUN SCRIPTS
54
- SetupWizard.setup(output);
55
- };
56
- main(process.argv.slice(2)).catch((exp) => {
4
+ MigrateCommander.main().catch((exp) => {
57
5
  console.error(exp);
58
6
  process.exit(-1);
59
7
  });
@@ -0,0 +1,52 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ import { FileArchiver } from "../archivers/FileArchiver";
5
+ import { MigrateApplication } from "../module";
6
+ import { IMigrateFile } from "../structures/IMigrateFile";
7
+ import { ISwagger } from "../structures/ISwagger";
8
+ import { MigrateInquirer } from "./MigrateInquirer";
9
+
10
+ export namespace MigrateCommander {
11
+ export const main = async (): Promise<void> => {
12
+ const resolve = (str: string | undefined) =>
13
+ str ? path.resolve(str).split("\\").join("/") : undefined;
14
+ const options: MigrateInquirer.IOutput = await MigrateInquirer.parse();
15
+
16
+ // VALIDATE OUTPUT DIRECTORY
17
+ const parent: string = resolve(options.output + "/..")!;
18
+ if (fs.existsSync(options.output)) halt("Output directory alreay exists.");
19
+ else if (fs.existsSync(parent) === false)
20
+ halt("Output directory's parent directory does not exist.");
21
+ else if (fs.statSync(parent).isDirectory() === false)
22
+ halt("Output directory's parent is not a directory.");
23
+
24
+ // READ SWAGGER
25
+ const swagger: ISwagger = (() => {
26
+ if (fs.existsSync(options.input) === false)
27
+ halt("Unable to find the input swagger.json file.");
28
+ const stats: fs.Stats = fs.statSync(options.input);
29
+ if (stats.isFile() === false)
30
+ halt("The input swagger.json is not a file.");
31
+ const content: string = fs.readFileSync(options.input, "utf-8");
32
+ const swagger: ISwagger = JSON.parse(content);
33
+ return swagger;
34
+ })();
35
+
36
+ const app: MigrateApplication = new MigrateApplication(swagger);
37
+ const files: IMigrateFile[] =
38
+ options.mode === "nest"
39
+ ? app.nest(options.simulate)
40
+ : app.sdk(options.simulate);
41
+ await FileArchiver.archive({
42
+ mkdir: fs.promises.mkdir,
43
+ writeFile: (file, content) =>
44
+ fs.promises.writeFile(file, content, "utf-8"),
45
+ })(options.output)(files);
46
+ };
47
+
48
+ const halt = (desc: string): never => {
49
+ console.error(desc);
50
+ process.exit(-1);
51
+ };
52
+ }
@@ -0,0 +1,79 @@
1
+ import commander from "commander";
2
+ import inquirer from "inquirer";
3
+
4
+ export namespace MigrateInquirer {
5
+ export interface IOutput {
6
+ mode: "nest" | "sdk";
7
+ simulate: boolean;
8
+ input: string;
9
+ output: string;
10
+ }
11
+
12
+ export const parse = async (): Promise<IOutput> => {
13
+ // PREPARE ASSETS
14
+ commander.program.option("--mode [nest/sdk]", "migration mode");
15
+ commander.program.option(
16
+ "--input [swagger.json]",
17
+ "location of target swagger.json file",
18
+ );
19
+ commander.program.option("--output [directory]", "output directory path");
20
+ commander.program.option("--simulate", "Mockup simulator");
21
+
22
+ // INTERNAL PROCEDURES
23
+ const questioned = { value: false };
24
+ const action = (closure: (options: Partial<IOutput>) => Promise<IOutput>) =>
25
+ new Promise<IOutput>((resolve, reject) => {
26
+ commander.program.action(async (options) => {
27
+ try {
28
+ resolve(await closure(options));
29
+ } catch (exp) {
30
+ reject(exp);
31
+ }
32
+ });
33
+ commander.program.parseAsync().catch(reject);
34
+ });
35
+ const select =
36
+ (name: string) =>
37
+ (message: string) =>
38
+ async <Choice extends string>(
39
+ choices: Choice[],
40
+ filter?: (value: string) => Choice,
41
+ ): Promise<Choice> => {
42
+ questioned.value = true;
43
+ return (
44
+ await inquirer.createPromptModule()({
45
+ type: "list",
46
+ name: name,
47
+ message: message,
48
+ choices: choices,
49
+ filter,
50
+ })
51
+ )[name];
52
+ };
53
+ const input = (name: string) => async (message: string) =>
54
+ (
55
+ await inquirer.createPromptModule()({
56
+ type: "input",
57
+ name,
58
+ message,
59
+ })
60
+ )[name];
61
+
62
+ // DO CONSTRUCT
63
+ return action(async (partial) => {
64
+ partial.mode ??= await select("mode")("Migration mode")(
65
+ ["NestJS" as "nest", "SDK" as "sdk"],
66
+ (value) => (value === "NestJS" ? "nest" : "sdk"),
67
+ );
68
+ partial.input ??= await input("input")("Swagger file location");
69
+ partial.output ??= await input("output")("Output directory path");
70
+ if (partial.simulate)
71
+ partial.simulate = (partial.simulate as any) === "true";
72
+ else
73
+ partial.simulate =
74
+ (await select("simulate")("Mokup Simulator")(["true", "false"])) ===
75
+ "true";
76
+ return partial as IOutput;
77
+ });
78
+ };
79
+ }
@@ -1,5 +1,6 @@
1
1
  import ts from "typescript";
2
2
 
3
+ import { IMigrateConfig } from "../IMigrateConfig";
3
4
  import { IMigrateController } from "../structures/IMigrateController";
4
5
  import { IMigrateRoute } from "../structures/IMigrateRoute";
5
6
  import { ISwaggerComponents } from "../structures/ISwaggerComponents";
@@ -20,13 +21,14 @@ export namespace ApiFileProgrammer {
20
21
  }
21
22
 
22
23
  export const write =
24
+ (config: IMigrateConfig) =>
23
25
  (components: ISwaggerComponents) =>
24
26
  (props: IProps): ts.Statement[] => {
25
27
  const importer: ImportProgrammer = new ImportProgrammer();
26
28
  const statements: ts.Statement[] = props.entries
27
29
  .map((p) => [
28
- ApiFunctionProgrammer.write(components)(importer)(p),
29
- ApiNamespaceProgrammer.write(components)(importer)(p),
30
+ ApiFunctionProgrammer.write(config)(components)(importer)(p),
31
+ ApiNamespaceProgrammer.write(config)(components)(importer)(p),
30
32
  ])
31
33
  .flat();
32
34
  return [
@@ -1,6 +1,7 @@
1
1
  import ts from "typescript";
2
2
  import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory";
3
3
 
4
+ import { IMigrateConfig } from "../IMigrateConfig";
4
5
  import { IMigrateController } from "../structures/IMigrateController";
5
6
  import { IMigrateRoute } from "../structures/IMigrateRoute";
6
7
  import { ISwaggerComponents } from "../structures/ISwaggerComponents";
@@ -16,6 +17,7 @@ export namespace ApiFunctionProgrammer {
16
17
  }
17
18
 
18
19
  export const write =
20
+ (config: IMigrateConfig) =>
19
21
  (components: ISwaggerComponents) =>
20
22
  (importer: ImportProgrammer) =>
21
23
  (props: IProps): ts.FunctionDeclaration =>
@@ -28,30 +30,18 @@ export namespace ApiFunctionProgrammer {
28
30
  undefined,
29
31
  props.alias,
30
32
  undefined,
31
- writeParameteers(components)(importer)(props),
33
+ writeParameterDeclarations(components)(importer)(props),
32
34
  ts.factory.createTypeReferenceNode("Promise", [
33
35
  ts.factory.createTypeReferenceNode(
34
36
  props.route.success === null ? "void" : `${props.alias}.Output`,
35
37
  ),
36
38
  ]),
37
- ts.factory.createBlock(writeBody(importer)(props), true),
39
+ ts.factory.createBlock(writeBody(config)(importer)(props), true),
38
40
  ),
39
41
  writeDescription(props),
40
42
  );
41
43
 
42
- const writeDescription = (props: IProps): string =>
43
- [
44
- ...(props.route.description?.length
45
- ? [props.route.description.length, ""]
46
- : []),
47
- ...(props.route.deprecated ? ["@deprecated"] : []),
48
- ...props.route.tags.map((value) => `@tag ${value}`),
49
- `@controller ${props.controller.name}`,
50
- `@path ${props.route.path}`,
51
- "@nestia Generated by Nestia - https://github.com/samchon/nestia",
52
- ].join("\n");
53
-
54
- const writeParameteers =
44
+ export const writeParameterDeclarations =
55
45
  (components: ISwaggerComponents) =>
56
46
  (importer: ImportProgrammer) =>
57
47
  (props: IProps): ts.ParameterDeclaration[] => [
@@ -77,7 +67,7 @@ export namespace ApiFunctionProgrammer {
77
67
  ...(props.route.query
78
68
  ? [
79
69
  IdentifierFactory.parameter(
80
- "query",
70
+ props.route.query.key,
81
71
  ts.factory.createTypeReferenceNode(`${props.alias}.Query`),
82
72
  ),
83
73
  ]
@@ -85,19 +75,33 @@ export namespace ApiFunctionProgrammer {
85
75
  ...(props.route.body
86
76
  ? [
87
77
  IdentifierFactory.parameter(
88
- "input",
78
+ props.route.body.key,
89
79
  ts.factory.createTypeReferenceNode(`${props.alias}.Input`),
90
80
  ),
91
81
  ]
92
82
  : []),
93
83
  ];
94
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
+
95
97
  const writeBody =
98
+ (config: IMigrateConfig) =>
96
99
  (importer: ImportProgrammer) =>
97
100
  (props: IProps): ts.Statement[] => {
98
101
  const encrypted: boolean = !!props.route.success?.["x-nestia-encrypted"];
99
102
  const contentType: string =
100
103
  props.route.success?.type ?? "application/json";
104
+
101
105
  const caller = () =>
102
106
  ts.factory.createCallExpression(
103
107
  IdentifierFactory.access(
@@ -157,7 +161,7 @@ export namespace ApiFunctionProgrammer {
157
161
  ts.factory.createIdentifier(p.key),
158
162
  ),
159
163
  ...(props.route.query
160
- ? [ts.factory.createIdentifier("query")]
164
+ ? [ts.factory.createIdentifier(props.route.query.key)]
161
165
  : []),
162
166
  ],
163
167
  ),
@@ -169,9 +173,32 @@ export namespace ApiFunctionProgrammer {
169
173
  ],
170
174
  true,
171
175
  ),
172
- ...(props.route.body ? [ts.factory.createIdentifier("input")] : []),
176
+ ...(props.route.body
177
+ ? [ts.factory.createIdentifier(props.route.body.key)]
178
+ : []),
173
179
  ],
174
180
  );
175
- return [ts.factory.createReturnStatement(caller())];
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
+ ];
176
203
  };
177
204
  }
@@ -4,10 +4,12 @@ import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory";
4
4
  import { LiteralFactory } from "typia/lib/factories/LiteralFactory";
5
5
  import { TypeFactory } from "typia/lib/factories/TypeFactory";
6
6
 
7
+ import { IMigrateConfig } from "../IMigrateConfig";
7
8
  import { IMigrateController } from "../structures/IMigrateController";
8
9
  import { IMigrateRoute } from "../structures/IMigrateRoute";
9
10
  import { ISwaggerComponents } from "../structures/ISwaggerComponents";
10
11
  import { FilePrinter } from "../utils/FilePrinter";
12
+ import { ApiSimulatationProgrammer } from "./ApiSimulatationProgrammer";
11
13
  import { ImportProgrammer } from "./ImportProgrammer";
12
14
  import { SchemaProgrammer } from "./SchemaProgrammer";
13
15
 
@@ -19,6 +21,7 @@ export namespace ApiNamespaceProgrammer {
19
21
  }
20
22
 
21
23
  export const write =
24
+ (config: IMigrateConfig) =>
22
25
  (components: ISwaggerComponents) =>
23
26
  (importer: ImportProgrammer) =>
24
27
  (props: IProps): ts.ModuleDeclaration => {
@@ -32,11 +35,31 @@ export namespace ApiNamespaceProgrammer {
32
35
  writeMetadata(components)(importer)(props),
33
36
  FilePrinter.enter(),
34
37
  writePath(components)(importer)(props),
38
+ ...(config.simulate
39
+ ? [
40
+ ApiSimulatationProgrammer.random(components)(importer)(props),
41
+ ApiSimulatationProgrammer.simulate(components)(importer)(props),
42
+ ]
43
+ : []),
35
44
  ]),
36
45
  ts.NodeFlags.Namespace,
37
46
  );
38
47
  };
39
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
+
40
63
  const writeTypes =
41
64
  (components: ISwaggerComponents) =>
42
65
  (importer: ImportProgrammer) =>
@@ -251,7 +274,7 @@ export namespace ApiNamespaceProgrammer {
251
274
  undefined,
252
275
  [
253
276
  ts.factory.createAsExpression(
254
- ts.factory.createIdentifier("query"),
277
+ ts.factory.createIdentifier(props.route.query.key),
255
278
  TypeFactory.keyword("any"),
256
279
  ),
257
280
  ],
@@ -1,4 +1,5 @@
1
1
  import { HashMap, IPointer, hash } from "tstl";
2
+ import ts from "typescript";
2
3
  import { Escaper } from "typia/lib/utils/Escaper";
3
4
 
4
5
  import { IMigrateProgram } from "../module";
@@ -6,6 +7,8 @@ import { IMigrateFile } from "../structures/IMigrateFile";
6
7
  import { FilePrinter } from "../utils/FilePrinter";
7
8
  import { StringUtil } from "../utils/StringUtil";
8
9
  import { ApiFileProgrammer } from "./ApiFileProgrammer";
10
+ import { DtoProgrammer } from "./DtoProgrammer";
11
+ import { ImportProgrammer } from "./ImportProgrammer";
9
12
 
10
13
  export namespace ApiProgrammer {
11
14
  export const write = (program: IMigrateProgram): IMigrateFile[] => {
@@ -44,6 +47,12 @@ export namespace ApiProgrammer {
44
47
  props.children.add(last.value.namespace.at(-1)!);
45
48
  last.value = props;
46
49
  });
50
+ const top = dict.take([], () => ({
51
+ namespace: [],
52
+ children: new Set(),
53
+ entries: [],
54
+ }));
55
+ if (namespace.length) top.children.add(namespace[0]);
47
56
  }
48
57
  for (const { second: props } of dict)
49
58
  props.entries.forEach(
@@ -57,12 +66,63 @@ export namespace ApiProgrammer {
57
66
  ])(entry.alias)),
58
67
  );
59
68
 
60
- return [...dict].map(({ second: props }) => ({
61
- location: `src/api/functional/${props.namespace.join("/")}`,
69
+ const output: IMigrateFile[] = [...dict].map(({ second: props }) => ({
70
+ location: `src/${program.config.mode === "nest" ? "api/" : ""}functional/${props.namespace.join("/")}`,
62
71
  file: "index.ts",
63
72
  content: FilePrinter.write({
64
- statements: ApiFileProgrammer.write(program.swagger.components)(props),
73
+ statements: ApiFileProgrammer.write(program.config)(
74
+ program.swagger.components,
75
+ )(props),
65
76
  }),
66
77
  }));
78
+ if (program.config.mode === "sdk")
79
+ output.push(
80
+ ...[...DtoProgrammer.write(program.swagger.components).entries()].map(
81
+ ([key, value]) => ({
82
+ location: "src/structures",
83
+ file: `${key}.ts`,
84
+ content: FilePrinter.write({
85
+ statements: writeDtoFile(key, value),
86
+ }),
87
+ }),
88
+ ),
89
+ );
90
+ return output;
91
+ };
92
+
93
+ const writeDtoFile = (
94
+ key: string,
95
+ modulo: DtoProgrammer.IModule,
96
+ ): ts.Statement[] => {
97
+ const importer = new ImportProgrammer();
98
+ const statements: ts.Statement[] = iterate(importer)(modulo);
99
+ if (statements.length === 0) return [];
100
+
101
+ return [
102
+ ...importer.toStatements((name) => `./${name}`, key),
103
+ ...(importer.empty() ? [] : [FilePrinter.enter()]),
104
+ ...statements,
105
+ ];
67
106
  };
107
+
108
+ const iterate =
109
+ (importer: ImportProgrammer) =>
110
+ (modulo: DtoProgrammer.IModule): ts.Statement[] => {
111
+ const output: ts.Statement[] = [];
112
+ if (modulo.programmer !== null) output.push(modulo.programmer(importer));
113
+ if (modulo.children.size) {
114
+ const internal: ts.Statement[] = [];
115
+ for (const child of modulo.children.values())
116
+ internal.push(...iterate(importer)(child));
117
+ output.push(
118
+ ts.factory.createModuleDeclaration(
119
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
120
+ ts.factory.createIdentifier(modulo.name),
121
+ ts.factory.createModuleBlock(internal),
122
+ ts.NodeFlags.Namespace,
123
+ ),
124
+ );
125
+ }
126
+ return output;
127
+ };
68
128
  }