@nestia/sdk 1.3.1 → 1.3.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 (51) hide show
  1. package/assets/config/nestia.config.ts +70 -70
  2. package/lib/INestiaConfig.d.ts +13 -0
  3. package/lib/executable/internal/NestiaSdkConfig.js +6 -2
  4. package/lib/executable/internal/NestiaSdkConfig.js.map +1 -1
  5. package/lib/executable/sdk.js +11 -11
  6. package/lib/generates/SwaggerGenerator.js +9 -9
  7. package/lib/generates/internal/DistributionComposer.js +1 -1
  8. package/lib/generates/internal/DistributionComposer.js.map +1 -1
  9. package/lib/generates/internal/E2eFileProgrammer.js +12 -12
  10. package/lib/generates/internal/SdkFileProgrammer.js +3 -1
  11. package/lib/generates/internal/SdkFileProgrammer.js.map +1 -1
  12. package/lib/generates/internal/SdkFunctionProgrammer.js +24 -43
  13. package/lib/generates/internal/SdkFunctionProgrammer.js.map +1 -1
  14. package/package.json +4 -4
  15. package/src/INestiaConfig.ts +204 -190
  16. package/src/NestiaSdkApplication.ts +262 -262
  17. package/src/analyses/ControllerAnalyzer.ts +261 -261
  18. package/src/analyses/GenericAnalyzer.ts +53 -53
  19. package/src/analyses/ImportAnalyzer.ts +164 -164
  20. package/src/analyses/PathAnalyzer.ts +58 -58
  21. package/src/analyses/ReflectAnalyzer.ts +321 -321
  22. package/src/executable/internal/CommandParser.ts +15 -15
  23. package/src/executable/internal/NestiaConfigCompilerOptions.ts +18 -18
  24. package/src/executable/internal/NestiaSdkCommand.ts +156 -156
  25. package/src/executable/internal/NestiaSdkConfig.ts +36 -36
  26. package/src/executable/internal/nestia.config.getter.ts +12 -12
  27. package/src/executable/sdk.ts +70 -70
  28. package/src/generates/E2eGenerator.ts +67 -67
  29. package/src/generates/SdkGenerator.ts +56 -56
  30. package/src/generates/SwaggerGenerator.ts +504 -504
  31. package/src/generates/internal/DistributionComposer.ts +98 -97
  32. package/src/generates/internal/E2eFileProgrammer.ts +135 -135
  33. package/src/generates/internal/SdkFileProgrammer.ts +148 -144
  34. package/src/generates/internal/SdkFunctionProgrammer.ts +30 -52
  35. package/src/generates/internal/SdkRouteDirectory.ts +21 -21
  36. package/src/index.ts +4 -4
  37. package/src/module.ts +2 -2
  38. package/src/structures/IController.ts +31 -31
  39. package/src/structures/IRoute.ts +39 -39
  40. package/src/structures/ISwaggerDocument.ts +120 -120
  41. package/src/structures/ITypeTuple.ts +6 -6
  42. package/src/structures/MethodType.ts +11 -11
  43. package/src/structures/ParamCategory.ts +1 -1
  44. package/src/structures/TypeEntry.ts +22 -22
  45. package/src/utils/ArrayUtil.ts +26 -26
  46. package/src/utils/FileRetriever.ts +22 -22
  47. package/src/utils/ImportDictionary.ts +56 -56
  48. package/src/utils/MapUtil.ts +14 -14
  49. package/src/utils/NestiaConfigUtil.ts +21 -21
  50. package/src/utils/SourceFinder.ts +60 -60
  51. package/src/utils/StripEnums.ts +10 -10
@@ -1,262 +1,262 @@
1
- import fs from "fs";
2
- import path from "path";
3
- import * as runner from "ts-node";
4
- import { Pair, Singleton } from "tstl";
5
- import ts from "typescript";
6
-
7
- import { INestiaConfig } from "./INestiaConfig";
8
- import { ControllerAnalyzer } from "./analyses/ControllerAnalyzer";
9
- import { ReflectAnalyzer } from "./analyses/ReflectAnalyzer";
10
- import { NestiaConfigCompilerOptions } from "./executable/internal/NestiaConfigCompilerOptions";
11
- import { E2eGenerator } from "./generates/E2eGenerator";
12
- import { SdkGenerator } from "./generates/SdkGenerator";
13
- import { SwaggerGenerator } from "./generates/SwaggerGenerator";
14
- import { IController } from "./structures/IController";
15
- import { IRoute } from "./structures/IRoute";
16
- import { ArrayUtil } from "./utils/ArrayUtil";
17
- import { NestiaConfigUtil } from "./utils/NestiaConfigUtil";
18
- import { SourceFinder } from "./utils/SourceFinder";
19
-
20
- export class NestiaSdkApplication {
21
- private readonly config_: INestiaConfig;
22
- private readonly bundle_checker_: Singleton<
23
- Promise<(str: string) => boolean>
24
- >;
25
-
26
- public constructor(config: INestiaConfig) {
27
- this.config_ = config;
28
- this.bundle_checker_ = new Singleton(async () => {
29
- if (!this.config_.output) return () => false;
30
-
31
- const bundles: string[] = await fs.promises.readdir(
32
- SdkGenerator.BUNDLE_PATH,
33
- );
34
- const tuples: Pair<string, boolean>[] = await ArrayUtil.asyncMap(
35
- bundles,
36
- async (file) => {
37
- const relative: string = path.join(
38
- this.config_.output!,
39
- file,
40
- );
41
- const location: string = path.join(
42
- SdkGenerator.BUNDLE_PATH,
43
- file,
44
- );
45
- const stats: fs.Stats = await fs.promises.stat(location);
46
-
47
- return new Pair(relative, stats.isDirectory());
48
- },
49
- );
50
-
51
- return (file: string): boolean => {
52
- for (const it of tuples)
53
- if (it.second === false && file === it.first) return true;
54
- else if (it.second === true && file.indexOf(it.first) === 0)
55
- return true;
56
- return false;
57
- };
58
- });
59
- }
60
-
61
- public async e2e(): Promise<void> {
62
- if (!this.config_.output)
63
- throw new Error(
64
- "Error on NestiaApplication.e2e(): output path of SDK is not specified.",
65
- );
66
- else if (!this.config_.e2e)
67
- throw new Error(
68
- "Error on NestiaApplication.e2e(): output path of e2e test files is not specified.",
69
- );
70
-
71
- const validate =
72
- (title: string) =>
73
- async (location: string): Promise<void> => {
74
- const parent: string = path.resolve(location + "/..");
75
- const stats: fs.Stats = await fs.promises.lstat(parent);
76
- if (stats.isDirectory() === false)
77
- throw new Error(
78
- `Error on NestiaApplication.e2e(): output directory of ${title} does not exists.`,
79
- );
80
- };
81
- await validate("sdk")(this.config_.output);
82
- await validate("e2e")(this.config_.e2e);
83
-
84
- title("Nestia E2E Generator");
85
- await this.generate(
86
- (config) => config,
87
- () => (config) => async (routes) => {
88
- await SdkGenerator.generate(config)(routes);
89
- await E2eGenerator.generate(config)(routes);
90
- },
91
- );
92
- }
93
-
94
- public async sdk(): Promise<void> {
95
- if (!this.config_.output)
96
- throw new Error(
97
- "Error on NestiaApplication.sdk(): output path is not specified.",
98
- );
99
-
100
- const parent: string = path.resolve(this.config_.output + "/..");
101
- const stats: fs.Stats = await fs.promises.lstat(parent);
102
- if (stats.isDirectory() === false)
103
- throw new Error(
104
- "Error on NestiaApplication.sdk(): output directory does not exists.",
105
- );
106
-
107
- title("Nestia SDK Generator");
108
- await this.generate(
109
- (config) => config,
110
- () => SdkGenerator.generate,
111
- );
112
- }
113
-
114
- public async swagger(): Promise<void> {
115
- if (!this.config_.swagger?.output)
116
- throw new Error(
117
- `Error on NestiaApplication.swagger(): output path of the "swagger.json" is not specified.`,
118
- );
119
-
120
- const parsed: path.ParsedPath = path.parse(this.config_.swagger.output);
121
- const directory: string = !!parsed.ext
122
- ? path.resolve(parsed.dir)
123
- : this.config_.swagger.output;
124
- const stats: fs.Stats = await fs.promises.lstat(directory);
125
- if (stats.isDirectory() === false)
126
- throw new Error(
127
- "Error on NestiaApplication.swagger(): output directory does not exists.",
128
- );
129
-
130
- title("Nestia Swagger Generator");
131
- await this.generate(
132
- (config) => config.swagger!,
133
- SwaggerGenerator.generate,
134
- );
135
- }
136
-
137
- private async generate<Config>(
138
- config: (entire: INestiaConfig) => Config,
139
- archiver: (
140
- checker: ts.TypeChecker,
141
- ) => (config: Config) => (routes: IRoute[]) => Promise<void>,
142
- ): Promise<void> {
143
- // MOUNT TS-NODE
144
- this.prepare();
145
-
146
- // LOAD CONTROLLER FILES
147
- const input: INestiaConfig.IInput = NestiaConfigUtil.input(
148
- this.config_.input,
149
- );
150
- const fileList: string[] = await ArrayUtil.asyncFilter(
151
- await SourceFinder.find({
152
- include: input.include,
153
- exclude: input.exclude,
154
- filter: (file) =>
155
- file.substring(file.length - 3) === ".ts" &&
156
- file.substring(file.length - 5) !== ".d.ts",
157
- }),
158
- (file) => this.is_not_excluded(file),
159
- );
160
-
161
- // ANALYZE REFLECTS
162
- const unique: WeakSet<any> = new WeakSet();
163
- const controllers: IController[] = [];
164
-
165
- console.log("Analyzing reflections");
166
- for (const file of fileList)
167
- controllers.push(...(await ReflectAnalyzer.analyze(unique, file)));
168
-
169
- const agg: number = (() => {
170
- const set: Set<string> = new Set();
171
- for (const c of controllers)
172
- for (const cPath of c.paths)
173
- for (const f of c.functions)
174
- for (const fPath of f.paths)
175
- set.add(`${f.method}::${cPath}/${fPath}`);
176
- return set.size;
177
- })();
178
-
179
- console.log(` - controllers: #${controllers.length}`);
180
- console.log(` - paths: #${agg}`);
181
- console.log(
182
- ` - routes: #${controllers
183
- .map(
184
- (c) =>
185
- c.paths.length *
186
- c.functions
187
- .map((f) => f.paths.length)
188
- .reduce((a, b) => a + b, 0),
189
- )
190
- .reduce((a, b) => a + b, 0)}`,
191
- );
192
-
193
- // ANALYZE TYPESCRIPT CODE
194
- console.log("Analyzing source codes");
195
- const program: ts.Program = ts.createProgram(
196
- controllers.map((c) => c.file),
197
- this.config_.compilerOptions || { noEmit: true },
198
- );
199
- const checker: ts.TypeChecker = program.getTypeChecker();
200
-
201
- const routeList: IRoute[] = [];
202
- for (const c of controllers) {
203
- const file: ts.SourceFile | undefined = program.getSourceFile(
204
- c.file,
205
- );
206
- if (file === undefined) continue;
207
-
208
- routeList.push(...ControllerAnalyzer.analyze(checker, file, c));
209
- }
210
-
211
- // DO GENERATE
212
- await archiver(checker)(config(this.config_))(routeList);
213
- }
214
-
215
- private prepare(): void {
216
- // CONSTRUCT OPTIONS
217
- if (!this.config_.compilerOptions)
218
- this.config_.compilerOptions =
219
- NestiaConfigCompilerOptions.DEFAULT_OPTIONS as any;
220
- const absoluted: boolean = !!this.config_.compilerOptions?.baseUrl;
221
-
222
- const ttsc: boolean =
223
- ts.version < "5.0.0" &&
224
- (() => {
225
- try {
226
- require.resolve("ttypescript");
227
- return true;
228
- } catch (e) {
229
- return false;
230
- }
231
- })();
232
-
233
- // MOUNT TS-NODE
234
- runner.register({
235
- emit: false,
236
- compiler: ttsc ? "ttypescript" : undefined,
237
- compilerOptions: this.config_.compilerOptions,
238
- require: absoluted ? ["tsconfig-paths/register"] : undefined,
239
- });
240
- }
241
-
242
- private async is_not_excluded(file: string): Promise<boolean> {
243
- if (this.config_.output)
244
- return (
245
- file.indexOf(path.join(this.config_.output, "functional")) ===
246
- -1 && (await this.bundle_checker_.get())(file) === false
247
- );
248
-
249
- const content: string = await fs.promises.readFile(file, "utf8");
250
- return (
251
- content.indexOf(
252
- " * @nestia Generated by Nestia - https://github.com/samchon/nestia",
253
- ) === -1
254
- );
255
- }
256
- }
257
-
258
- const title = (str: string): void => {
259
- console.log("-----------------------------------------------------------");
260
- console.log(` ${str}`);
261
- console.log("-----------------------------------------------------------");
262
- };
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import * as runner from "ts-node";
4
+ import { Pair, Singleton } from "tstl";
5
+ import ts from "typescript";
6
+
7
+ import { INestiaConfig } from "./INestiaConfig";
8
+ import { ControllerAnalyzer } from "./analyses/ControllerAnalyzer";
9
+ import { ReflectAnalyzer } from "./analyses/ReflectAnalyzer";
10
+ import { NestiaConfigCompilerOptions } from "./executable/internal/NestiaConfigCompilerOptions";
11
+ import { E2eGenerator } from "./generates/E2eGenerator";
12
+ import { SdkGenerator } from "./generates/SdkGenerator";
13
+ import { SwaggerGenerator } from "./generates/SwaggerGenerator";
14
+ import { IController } from "./structures/IController";
15
+ import { IRoute } from "./structures/IRoute";
16
+ import { ArrayUtil } from "./utils/ArrayUtil";
17
+ import { NestiaConfigUtil } from "./utils/NestiaConfigUtil";
18
+ import { SourceFinder } from "./utils/SourceFinder";
19
+
20
+ export class NestiaSdkApplication {
21
+ private readonly config_: INestiaConfig;
22
+ private readonly bundle_checker_: Singleton<
23
+ Promise<(str: string) => boolean>
24
+ >;
25
+
26
+ public constructor(config: INestiaConfig) {
27
+ this.config_ = config;
28
+ this.bundle_checker_ = new Singleton(async () => {
29
+ if (!this.config_.output) return () => false;
30
+
31
+ const bundles: string[] = await fs.promises.readdir(
32
+ SdkGenerator.BUNDLE_PATH,
33
+ );
34
+ const tuples: Pair<string, boolean>[] = await ArrayUtil.asyncMap(
35
+ bundles,
36
+ async (file) => {
37
+ const relative: string = path.join(
38
+ this.config_.output!,
39
+ file,
40
+ );
41
+ const location: string = path.join(
42
+ SdkGenerator.BUNDLE_PATH,
43
+ file,
44
+ );
45
+ const stats: fs.Stats = await fs.promises.stat(location);
46
+
47
+ return new Pair(relative, stats.isDirectory());
48
+ },
49
+ );
50
+
51
+ return (file: string): boolean => {
52
+ for (const it of tuples)
53
+ if (it.second === false && file === it.first) return true;
54
+ else if (it.second === true && file.indexOf(it.first) === 0)
55
+ return true;
56
+ return false;
57
+ };
58
+ });
59
+ }
60
+
61
+ public async e2e(): Promise<void> {
62
+ if (!this.config_.output)
63
+ throw new Error(
64
+ "Error on NestiaApplication.e2e(): output path of SDK is not specified.",
65
+ );
66
+ else if (!this.config_.e2e)
67
+ throw new Error(
68
+ "Error on NestiaApplication.e2e(): output path of e2e test files is not specified.",
69
+ );
70
+
71
+ const validate =
72
+ (title: string) =>
73
+ async (location: string): Promise<void> => {
74
+ const parent: string = path.resolve(location + "/..");
75
+ const stats: fs.Stats = await fs.promises.lstat(parent);
76
+ if (stats.isDirectory() === false)
77
+ throw new Error(
78
+ `Error on NestiaApplication.e2e(): output directory of ${title} does not exists.`,
79
+ );
80
+ };
81
+ await validate("sdk")(this.config_.output);
82
+ await validate("e2e")(this.config_.e2e);
83
+
84
+ title("Nestia E2E Generator");
85
+ await this.generate(
86
+ (config) => config,
87
+ () => (config) => async (routes) => {
88
+ await SdkGenerator.generate(config)(routes);
89
+ await E2eGenerator.generate(config)(routes);
90
+ },
91
+ );
92
+ }
93
+
94
+ public async sdk(): Promise<void> {
95
+ if (!this.config_.output)
96
+ throw new Error(
97
+ "Error on NestiaApplication.sdk(): output path is not specified.",
98
+ );
99
+
100
+ const parent: string = path.resolve(this.config_.output + "/..");
101
+ const stats: fs.Stats = await fs.promises.lstat(parent);
102
+ if (stats.isDirectory() === false)
103
+ throw new Error(
104
+ "Error on NestiaApplication.sdk(): output directory does not exists.",
105
+ );
106
+
107
+ title("Nestia SDK Generator");
108
+ await this.generate(
109
+ (config) => config,
110
+ () => SdkGenerator.generate,
111
+ );
112
+ }
113
+
114
+ public async swagger(): Promise<void> {
115
+ if (!this.config_.swagger?.output)
116
+ throw new Error(
117
+ `Error on NestiaApplication.swagger(): output path of the "swagger.json" is not specified.`,
118
+ );
119
+
120
+ const parsed: path.ParsedPath = path.parse(this.config_.swagger.output);
121
+ const directory: string = !!parsed.ext
122
+ ? path.resolve(parsed.dir)
123
+ : this.config_.swagger.output;
124
+ const stats: fs.Stats = await fs.promises.lstat(directory);
125
+ if (stats.isDirectory() === false)
126
+ throw new Error(
127
+ "Error on NestiaApplication.swagger(): output directory does not exists.",
128
+ );
129
+
130
+ title("Nestia Swagger Generator");
131
+ await this.generate(
132
+ (config) => config.swagger!,
133
+ SwaggerGenerator.generate,
134
+ );
135
+ }
136
+
137
+ private async generate<Config>(
138
+ config: (entire: INestiaConfig) => Config,
139
+ archiver: (
140
+ checker: ts.TypeChecker,
141
+ ) => (config: Config) => (routes: IRoute[]) => Promise<void>,
142
+ ): Promise<void> {
143
+ // MOUNT TS-NODE
144
+ this.prepare();
145
+
146
+ // LOAD CONTROLLER FILES
147
+ const input: INestiaConfig.IInput = NestiaConfigUtil.input(
148
+ this.config_.input,
149
+ );
150
+ const fileList: string[] = await ArrayUtil.asyncFilter(
151
+ await SourceFinder.find({
152
+ include: input.include,
153
+ exclude: input.exclude,
154
+ filter: (file) =>
155
+ file.substring(file.length - 3) === ".ts" &&
156
+ file.substring(file.length - 5) !== ".d.ts",
157
+ }),
158
+ (file) => this.is_not_excluded(file),
159
+ );
160
+
161
+ // ANALYZE REFLECTS
162
+ const unique: WeakSet<any> = new WeakSet();
163
+ const controllers: IController[] = [];
164
+
165
+ console.log("Analyzing reflections");
166
+ for (const file of fileList)
167
+ controllers.push(...(await ReflectAnalyzer.analyze(unique, file)));
168
+
169
+ const agg: number = (() => {
170
+ const set: Set<string> = new Set();
171
+ for (const c of controllers)
172
+ for (const cPath of c.paths)
173
+ for (const f of c.functions)
174
+ for (const fPath of f.paths)
175
+ set.add(`${f.method}::${cPath}/${fPath}`);
176
+ return set.size;
177
+ })();
178
+
179
+ console.log(` - controllers: #${controllers.length}`);
180
+ console.log(` - paths: #${agg}`);
181
+ console.log(
182
+ ` - routes: #${controllers
183
+ .map(
184
+ (c) =>
185
+ c.paths.length *
186
+ c.functions
187
+ .map((f) => f.paths.length)
188
+ .reduce((a, b) => a + b, 0),
189
+ )
190
+ .reduce((a, b) => a + b, 0)}`,
191
+ );
192
+
193
+ // ANALYZE TYPESCRIPT CODE
194
+ console.log("Analyzing source codes");
195
+ const program: ts.Program = ts.createProgram(
196
+ controllers.map((c) => c.file),
197
+ this.config_.compilerOptions || { noEmit: true },
198
+ );
199
+ const checker: ts.TypeChecker = program.getTypeChecker();
200
+
201
+ const routeList: IRoute[] = [];
202
+ for (const c of controllers) {
203
+ const file: ts.SourceFile | undefined = program.getSourceFile(
204
+ c.file,
205
+ );
206
+ if (file === undefined) continue;
207
+
208
+ routeList.push(...ControllerAnalyzer.analyze(checker, file, c));
209
+ }
210
+
211
+ // DO GENERATE
212
+ await archiver(checker)(config(this.config_))(routeList);
213
+ }
214
+
215
+ private prepare(): void {
216
+ // CONSTRUCT OPTIONS
217
+ if (!this.config_.compilerOptions)
218
+ this.config_.compilerOptions =
219
+ NestiaConfigCompilerOptions.DEFAULT_OPTIONS as any;
220
+ const absoluted: boolean = !!this.config_.compilerOptions?.baseUrl;
221
+
222
+ const ttsc: boolean =
223
+ ts.version < "5.0.0" &&
224
+ (() => {
225
+ try {
226
+ require.resolve("ttypescript");
227
+ return true;
228
+ } catch (e) {
229
+ return false;
230
+ }
231
+ })();
232
+
233
+ // MOUNT TS-NODE
234
+ runner.register({
235
+ emit: false,
236
+ compiler: ttsc ? "ttypescript" : undefined,
237
+ compilerOptions: this.config_.compilerOptions,
238
+ require: absoluted ? ["tsconfig-paths/register"] : undefined,
239
+ });
240
+ }
241
+
242
+ private async is_not_excluded(file: string): Promise<boolean> {
243
+ if (this.config_.output)
244
+ return (
245
+ file.indexOf(path.join(this.config_.output, "functional")) ===
246
+ -1 && (await this.bundle_checker_.get())(file) === false
247
+ );
248
+
249
+ const content: string = await fs.promises.readFile(file, "utf8");
250
+ return (
251
+ content.indexOf(
252
+ " * @nestia Generated by Nestia - https://github.com/samchon/nestia",
253
+ ) === -1
254
+ );
255
+ }
256
+ }
257
+
258
+ const title = (str: string): void => {
259
+ console.log("-----------------------------------------------------------");
260
+ console.log(` ${str}`);
261
+ console.log("-----------------------------------------------------------");
262
+ };