@nestia/sdk 3.0.0-dev.20231209 → 3.0.0-dev.20240412
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.
- package/README.md +12 -9
- package/assets/config/nestia.config.ts +82 -79
- package/lib/INestiaConfig.d.ts +28 -6
- package/lib/NestiaSdkApplication.js +12 -10
- package/lib/NestiaSdkApplication.js.map +1 -1
- package/lib/analyses/ConfigAnalyzer.js +1 -1
- package/lib/analyses/ConfigAnalyzer.js.map +1 -1
- package/lib/analyses/ControllerAnalyzer.js +30 -15
- package/lib/analyses/ControllerAnalyzer.js.map +1 -1
- package/lib/analyses/ExceptionAnalyzer.js +35 -6
- package/lib/analyses/ExceptionAnalyzer.js.map +1 -1
- package/lib/analyses/ImportAnalyzer.d.ts +1 -2
- package/lib/analyses/ImportAnalyzer.js +2 -2
- package/lib/analyses/ImportAnalyzer.js.map +1 -1
- package/lib/analyses/PathAnalyzer.d.ts +2 -4
- package/lib/analyses/PathAnalyzer.js +27 -11
- package/lib/analyses/PathAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectAnalyzer.js +34 -22
- package/lib/analyses/ReflectAnalyzer.js.map +1 -1
- package/lib/analyses/SecurityAnalyzer.js +13 -8
- package/lib/analyses/SecurityAnalyzer.js.map +1 -1
- package/lib/executable/internal/NestiaConfigLoader.js +300 -220
- package/lib/executable/internal/NestiaConfigLoader.js.map +1 -1
- package/lib/executable/sdk.js +11 -11
- package/lib/generates/CloneGenerator.d.ts +6 -0
- package/lib/generates/CloneGenerator.js +62 -0
- package/lib/generates/CloneGenerator.js.map +1 -0
- package/lib/generates/E2eGenerator.d.ts +2 -1
- package/lib/generates/E2eGenerator.js +2 -2
- package/lib/generates/E2eGenerator.js.map +1 -1
- package/lib/generates/SdkGenerator.js +3 -11
- package/lib/generates/SdkGenerator.js.map +1 -1
- package/lib/generates/SwaggerGenerator.d.ts +2 -0
- package/lib/generates/SwaggerGenerator.js +119 -62
- package/lib/generates/SwaggerGenerator.js.map +1 -1
- package/lib/generates/internal/E2eFileProgrammer.d.ts +2 -1
- package/lib/generates/internal/E2eFileProgrammer.js +49 -53
- package/lib/generates/internal/E2eFileProgrammer.js.map +1 -1
- package/lib/generates/internal/FilePrinter.d.ts +10 -0
- package/lib/generates/internal/FilePrinter.js +46 -0
- package/lib/generates/internal/FilePrinter.js.map +1 -0
- package/lib/{utils → generates/internal}/ImportDictionary.d.ts +2 -1
- package/lib/{utils → generates/internal}/ImportDictionary.js +20 -14
- package/lib/generates/internal/ImportDictionary.js.map +1 -0
- package/lib/generates/internal/SdkAliasCollection.d.ts +12 -0
- package/lib/generates/internal/SdkAliasCollection.js +97 -0
- package/lib/generates/internal/SdkAliasCollection.js.map +1 -0
- package/lib/generates/internal/SdkCloneProgrammer.d.ts +12 -0
- package/lib/generates/internal/SdkCloneProgrammer.js +99 -0
- package/lib/generates/internal/SdkCloneProgrammer.js.map +1 -0
- package/lib/generates/internal/SdkFileProgrammer.d.ts +2 -1
- package/lib/generates/internal/SdkFileProgrammer.js +27 -28
- package/lib/generates/internal/SdkFileProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkFunctionProgrammer.d.ts +7 -2
- package/lib/generates/internal/SdkFunctionProgrammer.js +115 -322
- package/lib/generates/internal/SdkFunctionProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkImportWizard.d.ts +1 -1
- package/lib/generates/internal/SdkNamespaceProgrammer.d.ts +11 -0
- package/lib/generates/internal/SdkNamespaceProgrammer.js +180 -0
- package/lib/generates/internal/SdkNamespaceProgrammer.js.map +1 -0
- package/lib/generates/internal/SdkRouteProgrammer.d.ts +7 -0
- package/lib/generates/internal/SdkRouteProgrammer.js +55 -0
- package/lib/generates/internal/SdkRouteProgrammer.js.map +1 -0
- package/lib/generates/internal/SdkSimulationProgrammer.d.ts +8 -2
- package/lib/generates/internal/SdkSimulationProgrammer.js +103 -89
- package/lib/generates/internal/SdkSimulationProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkTypeProgrammer.d.ts +9 -0
- package/lib/generates/internal/SdkTypeProgrammer.js +228 -0
- package/lib/generates/internal/SdkTypeProgrammer.js.map +1 -0
- package/lib/generates/internal/SwaggerSchemaGenerator.d.ts +4 -4
- package/lib/generates/internal/SwaggerSchemaGenerator.js +30 -28
- package/lib/generates/internal/SwaggerSchemaGenerator.js.map +1 -1
- package/lib/structures/IController.d.ts +4 -2
- package/lib/structures/IRoute.d.ts +5 -4
- package/lib/structures/ISwaggerLazyProperty.d.ts +2 -2
- package/lib/structures/ISwaggerLazySchema.d.ts +2 -2
- package/lib/structures/ParamCategory.d.ts +1 -1
- package/lib/structures/TypeEntry.js +2 -2
- package/lib/structures/TypeEntry.js.map +1 -1
- package/lib/utils/StringUtil.d.ts +3 -0
- package/lib/utils/StringUtil.js +8 -0
- package/lib/utils/StringUtil.js.map +1 -0
- package/package.json +12 -16
- package/src/INestiaConfig.ts +30 -6
- package/src/NestiaSdkApplication.ts +255 -253
- package/src/analyses/AccessorAnalyzer.ts +60 -60
- package/src/analyses/ConfigAnalyzer.ts +147 -147
- package/src/analyses/ControllerAnalyzer.ts +42 -19
- package/src/analyses/ExceptionAnalyzer.ts +148 -115
- package/src/analyses/GenericAnalyzer.ts +51 -51
- package/src/analyses/ImportAnalyzer.ts +1 -2
- package/src/analyses/PathAnalyzer.ts +110 -98
- package/src/analyses/ReflectAnalyzer.ts +39 -35
- package/src/analyses/SecurityAnalyzer.ts +24 -20
- package/src/executable/internal/CommandParser.ts +15 -15
- package/src/executable/internal/NestiaConfigLoader.ts +67 -67
- package/src/executable/internal/NestiaSdkCommand.ts +60 -60
- package/src/executable/sdk.ts +73 -73
- package/src/generates/CloneGenerator.ts +62 -0
- package/src/generates/E2eGenerator.ts +66 -64
- package/src/generates/SdkGenerator.ts +84 -96
- package/src/generates/SwaggerGenerator.ts +145 -53
- package/src/generates/internal/E2eFileProgrammer.ts +182 -123
- package/src/generates/internal/FilePrinter.ts +53 -0
- package/src/{utils → generates/internal}/ImportDictionary.ts +35 -13
- package/src/generates/internal/SdkAliasCollection.ts +152 -0
- package/src/generates/internal/SdkCloneProgrammer.ts +155 -0
- package/src/generates/internal/SdkDistributionComposer.ts +91 -91
- package/src/generates/internal/SdkFileProgrammer.ts +115 -106
- package/src/generates/internal/SdkFunctionProgrammer.ts +298 -518
- package/src/generates/internal/SdkImportWizard.ts +55 -55
- package/src/generates/internal/SdkNamespaceProgrammer.ts +510 -0
- package/src/generates/internal/SdkRouteDirectory.ts +17 -17
- package/src/generates/internal/SdkRouteProgrammer.ts +83 -0
- package/src/generates/internal/SdkSimulationProgrammer.ts +365 -133
- package/src/generates/internal/SdkTypeProgrammer.ts +386 -0
- package/src/generates/internal/SwaggerSchemaGenerator.ts +437 -427
- package/src/generates/internal/SwaggerSchemaValidator.ts +198 -198
- package/src/index.ts +4 -4
- package/src/module.ts +2 -2
- package/src/structures/IController.ts +94 -95
- package/src/structures/IErrorReport.ts +6 -6
- package/src/structures/INestiaProject.ts +13 -13
- package/src/structures/INormalizedInput.ts +20 -20
- package/src/structures/IRoute.ts +53 -53
- package/src/structures/ISwaggerLazyProperty.ts +2 -2
- package/src/structures/ISwaggerLazySchema.ts +2 -2
- package/src/structures/ITypeTuple.ts +6 -6
- package/src/structures/MethodType.ts +5 -5
- package/src/structures/ParamCategory.ts +1 -1
- package/src/structures/TypeEntry.ts +1 -1
- package/src/utils/ArrayUtil.ts +26 -26
- package/src/utils/FileRetriever.ts +22 -22
- package/src/utils/MapUtil.ts +14 -14
- package/src/utils/PathUtil.ts +10 -10
- package/src/utils/SourceFinder.ts +66 -66
- package/src/utils/StringUtil.ts +6 -0
- package/src/utils/StripEnums.ts +5 -5
- package/assets/bundle/api/utils/NestiaSimulator.ts +0 -70
- package/lib/generates/internal/SdkDtoGenerator.d.ts +0 -9
- package/lib/generates/internal/SdkDtoGenerator.js +0 -294
- package/lib/generates/internal/SdkDtoGenerator.js.map +0 -1
- package/lib/generates/internal/SdkTypeDefiner.d.ts +0 -11
- package/lib/generates/internal/SdkTypeDefiner.js +0 -82
- package/lib/generates/internal/SdkTypeDefiner.js.map +0 -1
- package/lib/structures/ISwagger.d.ts +0 -72
- package/lib/structures/ISwagger.js +0 -3
- package/lib/structures/ISwagger.js.map +0 -1
- package/lib/structures/ISwaggerComponents.d.ts +0 -26
- package/lib/structures/ISwaggerComponents.js +0 -3
- package/lib/structures/ISwaggerComponents.js.map +0 -1
- package/lib/structures/ISwaggerInfo.d.ts +0 -71
- package/lib/structures/ISwaggerInfo.js +0 -3
- package/lib/structures/ISwaggerInfo.js.map +0 -1
- package/lib/structures/ISwaggerRoute.d.ts +0 -47
- package/lib/structures/ISwaggerRoute.js +0 -3
- package/lib/structures/ISwaggerRoute.js.map +0 -1
- package/lib/structures/ISwaggerSecurityScheme.d.ts +0 -56
- package/lib/structures/ISwaggerSecurityScheme.js +0 -3
- package/lib/structures/ISwaggerSecurityScheme.js.map +0 -1
- package/lib/utils/ImportDictionary.js.map +0 -1
- package/src/generates/internal/SdkDtoGenerator.ts +0 -424
- package/src/generates/internal/SdkTypeDefiner.ts +0 -119
- package/src/structures/ISwagger.ts +0 -91
- package/src/structures/ISwaggerComponents.ts +0 -29
- package/src/structures/ISwaggerInfo.ts +0 -80
- package/src/structures/ISwaggerRoute.ts +0 -51
- package/src/structures/ISwaggerSecurityScheme.ts +0 -65
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nestia/sdk",
|
|
3
|
-
"version": "3.0.0-dev.
|
|
3
|
+
"version": "3.0.0-dev.20240412",
|
|
4
4
|
"description": "Nestia SDK and Swagger generator",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
@@ -11,8 +11,6 @@
|
|
|
11
11
|
"build": "rimraf lib && tsc",
|
|
12
12
|
"dev": "rimraf lib && tsc --watch",
|
|
13
13
|
"eslint": "eslint ./**/*.ts",
|
|
14
|
-
"package:latest": "npm run build && npm publish --access public",
|
|
15
|
-
"package:next": "npm run package:latest -- --tag next",
|
|
16
14
|
"prepare": "ts-patch install && typia patch"
|
|
17
15
|
},
|
|
18
16
|
"repository": {
|
|
@@ -34,44 +32,42 @@
|
|
|
34
32
|
},
|
|
35
33
|
"homepage": "https://nestia.io",
|
|
36
34
|
"dependencies": {
|
|
37
|
-
"@nestia/fetcher": "^3.0.0-dev.
|
|
35
|
+
"@nestia/fetcher": "^3.0.0-dev.20240412",
|
|
36
|
+
"@samchon/openapi": "^0.1.2",
|
|
38
37
|
"cli": "^1.0.1",
|
|
39
38
|
"get-function-location": "^2.0.0",
|
|
40
39
|
"glob": "^7.2.0",
|
|
41
40
|
"path-to-regexp": "^6.2.1",
|
|
42
|
-
"
|
|
41
|
+
"prettier": "^3.2.5",
|
|
43
42
|
"tsconfck": "^2.0.1",
|
|
44
43
|
"tsconfig-paths": "^4.1.1",
|
|
45
|
-
"
|
|
46
|
-
"
|
|
44
|
+
"ts-node": ">=10.6.0",
|
|
45
|
+
"tstl": "^3.0.0",
|
|
46
|
+
"typia": "^6.0.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@nestia/fetcher": ">=3.0.0-dev.
|
|
49
|
+
"@nestia/fetcher": ">=3.0.0-dev.20240412",
|
|
50
50
|
"@nestjs/common": ">=7.0.1",
|
|
51
51
|
"@nestjs/core": ">=7.0.1",
|
|
52
52
|
"reflect-metadata": ">=0.1.12",
|
|
53
53
|
"ts-node": ">=10.6.0",
|
|
54
|
-
"
|
|
55
|
-
"typia": ">=5.3.4 <6.0.0"
|
|
54
|
+
"typia": ">=6.0.0 <7.0.0"
|
|
56
55
|
},
|
|
57
56
|
"devDependencies": {
|
|
58
|
-
"@nestia/e2e": "^0.
|
|
59
|
-
"@
|
|
60
|
-
"@nestjs/core": ">= 7.0.1",
|
|
57
|
+
"@nestia/e2e": "^0.4.1",
|
|
58
|
+
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
|
61
59
|
"@types/cli": "^0.11.21",
|
|
62
60
|
"@types/express": "^4.17.15",
|
|
63
61
|
"@types/glob": "^7.2.0",
|
|
64
62
|
"@types/node": "^18.11.15",
|
|
65
|
-
"@types/reflect-metadata": "^0.1.0",
|
|
66
63
|
"@types/uuid": "^9.0.0",
|
|
67
64
|
"@typescript-eslint/eslint-plugin": "^5.46.1",
|
|
68
65
|
"@typescript-eslint/parser": "^5.46.1",
|
|
69
66
|
"eslint": "^8.29.0",
|
|
70
67
|
"eslint-plugin-deprecation": "^1.4.1",
|
|
71
68
|
"rimraf": "^3.0.2",
|
|
72
|
-
"ts-node": "^10.9.1",
|
|
73
69
|
"ts-patch": "v3.0.2",
|
|
74
|
-
"typescript": "^5.
|
|
70
|
+
"typescript": "^5.4.2",
|
|
75
71
|
"typescript-transform-paths": "^3.4.4",
|
|
76
72
|
"uuid": "^9.0.0"
|
|
77
73
|
},
|
package/src/INestiaConfig.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type { INestApplication } from "@nestjs/common";
|
|
2
2
|
|
|
3
3
|
import type { INormalizedInput } from "./structures/INormalizedInput";
|
|
4
|
-
import
|
|
5
|
-
import type { ISwaggerInfo } from "./structures/ISwaggerInfo";
|
|
6
|
-
import type { ISwaggerSecurityScheme } from "./structures/ISwaggerSecurityScheme";
|
|
4
|
+
import { OpenApi } from "@samchon/openapi";
|
|
7
5
|
|
|
8
6
|
/**
|
|
9
7
|
* Definition for the `nestia.config.ts` file.
|
|
@@ -193,17 +191,31 @@ export namespace INestiaConfig {
|
|
|
193
191
|
*/
|
|
194
192
|
beautify?: boolean | number;
|
|
195
193
|
|
|
194
|
+
/**
|
|
195
|
+
* Whether to include additional information or not.
|
|
196
|
+
*
|
|
197
|
+
* If configured to be `true`, those properties would be added into each
|
|
198
|
+
* API endpoinnt.
|
|
199
|
+
*
|
|
200
|
+
* - `x-nestia-method`
|
|
201
|
+
* - `x-nestia-namespace`
|
|
202
|
+
* ` `x-nestia-jsDocTags`
|
|
203
|
+
*
|
|
204
|
+
* @default false
|
|
205
|
+
*/
|
|
206
|
+
additional?: boolean;
|
|
207
|
+
|
|
196
208
|
/**
|
|
197
209
|
* API information.
|
|
198
210
|
*
|
|
199
211
|
* If omitted, `package.json` content would be used instead.
|
|
200
212
|
*/
|
|
201
|
-
info?: Partial<
|
|
213
|
+
info?: Partial<OpenApi.IDocument.IInfo>;
|
|
202
214
|
|
|
203
215
|
/**
|
|
204
216
|
* List of server addresses.
|
|
205
217
|
*/
|
|
206
|
-
servers?:
|
|
218
|
+
servers?: OpenApi.IServer[];
|
|
207
219
|
|
|
208
220
|
/**
|
|
209
221
|
* Security schemes.
|
|
@@ -212,7 +224,19 @@ export namespace INestiaConfig {
|
|
|
212
224
|
* theirs methods have a security key which is not enrolled in here property,
|
|
213
225
|
* it would be an error.
|
|
214
226
|
*/
|
|
215
|
-
security?: Record<string,
|
|
227
|
+
security?: Record<string, OpenApi.ISecurityScheme>;
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* List of tag names with description.
|
|
231
|
+
*
|
|
232
|
+
* It is possible to omit this property or skip some tag name even if
|
|
233
|
+
* the tag name is used in the API routes. In that case, the tag name
|
|
234
|
+
* would be used without description.
|
|
235
|
+
*
|
|
236
|
+
* Of course, if you've written a comment like `@tag {name} {descrition}`,
|
|
237
|
+
* you can entirely replace this property specification.
|
|
238
|
+
*/
|
|
239
|
+
tags?: OpenApi.IDocument.ITag[];
|
|
216
240
|
|
|
217
241
|
/**
|
|
218
242
|
* Decompose query DTO.
|
|
@@ -1,253 +1,255 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import ts from "typescript";
|
|
4
|
-
|
|
5
|
-
import { INestiaConfig } from "./INestiaConfig";
|
|
6
|
-
import { AccessorAnalyzer } from "./analyses/AccessorAnalyzer";
|
|
7
|
-
import { ConfigAnalyzer } from "./analyses/ConfigAnalyzer";
|
|
8
|
-
import { ControllerAnalyzer } from "./analyses/ControllerAnalyzer";
|
|
9
|
-
import { ReflectAnalyzer } from "./analyses/ReflectAnalyzer";
|
|
10
|
-
import { E2eGenerator } from "./generates/E2eGenerator";
|
|
11
|
-
import { SdkGenerator } from "./generates/SdkGenerator";
|
|
12
|
-
import { SwaggerGenerator } from "./generates/SwaggerGenerator";
|
|
13
|
-
import { IController } from "./structures/IController";
|
|
14
|
-
import { IErrorReport } from "./structures/IErrorReport";
|
|
15
|
-
import { INestiaProject } from "./structures/INestiaProject";
|
|
16
|
-
import { IRoute } from "./structures/IRoute";
|
|
17
|
-
import { MapUtil } from "./utils/MapUtil";
|
|
18
|
-
|
|
19
|
-
export class NestiaSdkApplication {
|
|
20
|
-
public constructor(
|
|
21
|
-
private readonly config: INestiaConfig,
|
|
22
|
-
private readonly compilerOptions: ts.CompilerOptions,
|
|
23
|
-
) {}
|
|
24
|
-
|
|
25
|
-
public async e2e(): Promise<void> {
|
|
26
|
-
if (!this.config.output)
|
|
27
|
-
throw new Error(
|
|
28
|
-
"Error on NestiaApplication.e2e(): output path of SDK is not specified.",
|
|
29
|
-
);
|
|
30
|
-
else if (!this.config.e2e)
|
|
31
|
-
throw new Error(
|
|
32
|
-
"Error on NestiaApplication.e2e(): output path of e2e test files is not specified.",
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
const validate =
|
|
36
|
-
(title: string) =>
|
|
37
|
-
async (location: string): Promise<void> => {
|
|
38
|
-
const parent: string = path.resolve(location + "/..");
|
|
39
|
-
const stats: fs.Stats = await fs.promises.lstat(parent);
|
|
40
|
-
if (stats.isDirectory() === false)
|
|
41
|
-
throw new Error(
|
|
42
|
-
`Error on NestiaApplication.e2e(): output directory of ${title} does not exists.`,
|
|
43
|
-
);
|
|
44
|
-
};
|
|
45
|
-
await validate("sdk")(this.config.output);
|
|
46
|
-
await validate("e2e")(this.config.e2e);
|
|
47
|
-
|
|
48
|
-
print_title("Nestia E2E Generator");
|
|
49
|
-
await this.generate(
|
|
50
|
-
"e2e",
|
|
51
|
-
(config) => config,
|
|
52
|
-
(checker) => (config) => async (routes) => {
|
|
53
|
-
await SdkGenerator.generate(checker)(config)(routes);
|
|
54
|
-
await E2eGenerator.generate(config)(routes);
|
|
55
|
-
},
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
public async sdk(): Promise<void> {
|
|
60
|
-
if (!this.config.output)
|
|
61
|
-
throw new Error(
|
|
62
|
-
"Error on NestiaApplication.sdk(): output path is not specified.",
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
const parent: string = path.resolve(this.config.output + "/..");
|
|
66
|
-
const stats: fs.Stats = await fs.promises.lstat(parent);
|
|
67
|
-
if (stats.isDirectory() === false)
|
|
68
|
-
throw new Error(
|
|
69
|
-
"Error on NestiaApplication.sdk(): output directory does not exists.",
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
print_title("Nestia SDK Generator");
|
|
73
|
-
await this.generate("sdk", (config) => config, SdkGenerator.generate);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
public async swagger(): Promise<void> {
|
|
77
|
-
if (!this.config.swagger?.output)
|
|
78
|
-
throw new Error(
|
|
79
|
-
`Error on NestiaApplication.swagger(): output path of the "swagger.json" is not specified.`,
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
const parsed: path.ParsedPath = path.parse(this.config.swagger.output);
|
|
83
|
-
const directory: string = !!parsed.ext
|
|
84
|
-
? path.resolve(parsed.dir)
|
|
85
|
-
: this.config.swagger.output;
|
|
86
|
-
const stats: fs.Stats = await fs.promises.lstat(directory);
|
|
87
|
-
if (stats.isDirectory() === false)
|
|
88
|
-
throw new Error(
|
|
89
|
-
"Error on NestiaApplication.swagger(): output directory does not exists.",
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
print_title("Nestia Swagger Generator");
|
|
93
|
-
await this.generate(
|
|
94
|
-
"swagger",
|
|
95
|
-
(config) => config.swagger!,
|
|
96
|
-
SwaggerGenerator.generate,
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
private async generate<Config>(
|
|
101
|
-
method: string,
|
|
102
|
-
config: (entire: INestiaConfig) => Config,
|
|
103
|
-
archiver: (
|
|
104
|
-
checker: ts.TypeChecker,
|
|
105
|
-
) => (config: Config) => (routes: IRoute[]) => Promise<void>,
|
|
106
|
-
): Promise<void> {
|
|
107
|
-
//----
|
|
108
|
-
// ANALYZE REFLECTS
|
|
109
|
-
//----
|
|
110
|
-
const unique: WeakSet<any> = new WeakSet();
|
|
111
|
-
const controllers: IController[] = [];
|
|
112
|
-
const project: INestiaProject = {
|
|
113
|
-
config: this.config,
|
|
114
|
-
input: await ConfigAnalyzer.input(this.config),
|
|
115
|
-
checker: null!,
|
|
116
|
-
errors: [],
|
|
117
|
-
warnings: [],
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
console.log("Analyzing reflections");
|
|
121
|
-
for (const include of (await ConfigAnalyzer.input(this.config)).include)
|
|
122
|
-
controllers.push(
|
|
123
|
-
...(await ReflectAnalyzer.analyze(project)(
|
|
124
|
-
unique,
|
|
125
|
-
include.file,
|
|
126
|
-
include.paths,
|
|
127
|
-
include.controller,
|
|
128
|
-
)),
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
const agg: number = (() => {
|
|
132
|
-
const set: Set<string> = new Set();
|
|
133
|
-
for (const c of controllers)
|
|
134
|
-
for (const cPath of c.paths)
|
|
135
|
-
for (const f of c.functions)
|
|
136
|
-
for (const fPath of f.paths)
|
|
137
|
-
set.add(`${f.method}::${cPath}/${fPath}`);
|
|
138
|
-
return set.size;
|
|
139
|
-
})();
|
|
140
|
-
|
|
141
|
-
console.log(` - controllers: #${controllers.length}`);
|
|
142
|
-
console.log(` - paths: #${agg}`);
|
|
143
|
-
console.log(
|
|
144
|
-
` - routes: #${controllers
|
|
145
|
-
.map(
|
|
146
|
-
(c) =>
|
|
147
|
-
c.paths.length *
|
|
148
|
-
c.functions.map((f) => f.paths.length).reduce((a, b) => a + b, 0),
|
|
149
|
-
)
|
|
150
|
-
.reduce((a, b) => a + b, 0)}`,
|
|
151
|
-
);
|
|
152
|
-
|
|
153
|
-
//----
|
|
154
|
-
// ANALYZE TYPESCRIPT CODE
|
|
155
|
-
//----
|
|
156
|
-
console.log("Analyzing source codes");
|
|
157
|
-
|
|
158
|
-
const program: ts.Program = ts.createProgram(
|
|
159
|
-
controllers.map((c) => c.file),
|
|
160
|
-
this.compilerOptions,
|
|
161
|
-
);
|
|
162
|
-
project.checker = program.getTypeChecker();
|
|
163
|
-
|
|
164
|
-
const routeList: IRoute[] = [];
|
|
165
|
-
for (const c of controllers) {
|
|
166
|
-
const file: ts.SourceFile | undefined = program.getSourceFile(c.file);
|
|
167
|
-
if (file === undefined) continue;
|
|
168
|
-
routeList.push(...(await ControllerAnalyzer.analyze(project)(file, c)));
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// REPORT ERRORS
|
|
172
|
-
if (project.errors.length) {
|
|
173
|
-
report_errors("error")(project.errors);
|
|
174
|
-
process.exit(-1);
|
|
175
|
-
}
|
|
176
|
-
if (project.warnings.length) report_errors("warning")(project.warnings);
|
|
177
|
-
|
|
178
|
-
// FIND IMPLICIT TYPES
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
(
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
console.log("-----------------------------------------------------------");
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
// key:
|
|
227
|
-
//
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import ts from "typescript";
|
|
4
|
+
|
|
5
|
+
import { INestiaConfig } from "./INestiaConfig";
|
|
6
|
+
import { AccessorAnalyzer } from "./analyses/AccessorAnalyzer";
|
|
7
|
+
import { ConfigAnalyzer } from "./analyses/ConfigAnalyzer";
|
|
8
|
+
import { ControllerAnalyzer } from "./analyses/ControllerAnalyzer";
|
|
9
|
+
import { ReflectAnalyzer } from "./analyses/ReflectAnalyzer";
|
|
10
|
+
import { E2eGenerator } from "./generates/E2eGenerator";
|
|
11
|
+
import { SdkGenerator } from "./generates/SdkGenerator";
|
|
12
|
+
import { SwaggerGenerator } from "./generates/SwaggerGenerator";
|
|
13
|
+
import { IController } from "./structures/IController";
|
|
14
|
+
import { IErrorReport } from "./structures/IErrorReport";
|
|
15
|
+
import { INestiaProject } from "./structures/INestiaProject";
|
|
16
|
+
import { IRoute } from "./structures/IRoute";
|
|
17
|
+
import { MapUtil } from "./utils/MapUtil";
|
|
18
|
+
|
|
19
|
+
export class NestiaSdkApplication {
|
|
20
|
+
public constructor(
|
|
21
|
+
private readonly config: INestiaConfig,
|
|
22
|
+
private readonly compilerOptions: ts.CompilerOptions,
|
|
23
|
+
) {}
|
|
24
|
+
|
|
25
|
+
public async e2e(): Promise<void> {
|
|
26
|
+
if (!this.config.output)
|
|
27
|
+
throw new Error(
|
|
28
|
+
"Error on NestiaApplication.e2e(): output path of SDK is not specified.",
|
|
29
|
+
);
|
|
30
|
+
else if (!this.config.e2e)
|
|
31
|
+
throw new Error(
|
|
32
|
+
"Error on NestiaApplication.e2e(): output path of e2e test files is not specified.",
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const validate =
|
|
36
|
+
(title: string) =>
|
|
37
|
+
async (location: string): Promise<void> => {
|
|
38
|
+
const parent: string = path.resolve(location + "/..");
|
|
39
|
+
const stats: fs.Stats = await fs.promises.lstat(parent);
|
|
40
|
+
if (stats.isDirectory() === false)
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Error on NestiaApplication.e2e(): output directory of ${title} does not exists.`,
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
await validate("sdk")(this.config.output);
|
|
46
|
+
await validate("e2e")(this.config.e2e);
|
|
47
|
+
|
|
48
|
+
print_title("Nestia E2E Generator");
|
|
49
|
+
await this.generate(
|
|
50
|
+
"e2e",
|
|
51
|
+
(config) => config,
|
|
52
|
+
(checker) => (config) => async (routes) => {
|
|
53
|
+
await SdkGenerator.generate(checker)(config)(routes);
|
|
54
|
+
await E2eGenerator.generate(checker)(config)(routes);
|
|
55
|
+
},
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public async sdk(): Promise<void> {
|
|
60
|
+
if (!this.config.output)
|
|
61
|
+
throw new Error(
|
|
62
|
+
"Error on NestiaApplication.sdk(): output path is not specified.",
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const parent: string = path.resolve(this.config.output + "/..");
|
|
66
|
+
const stats: fs.Stats = await fs.promises.lstat(parent);
|
|
67
|
+
if (stats.isDirectory() === false)
|
|
68
|
+
throw new Error(
|
|
69
|
+
"Error on NestiaApplication.sdk(): output directory does not exists.",
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
print_title("Nestia SDK Generator");
|
|
73
|
+
await this.generate("sdk", (config) => config, SdkGenerator.generate);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public async swagger(): Promise<void> {
|
|
77
|
+
if (!this.config.swagger?.output)
|
|
78
|
+
throw new Error(
|
|
79
|
+
`Error on NestiaApplication.swagger(): output path of the "swagger.json" is not specified.`,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const parsed: path.ParsedPath = path.parse(this.config.swagger.output);
|
|
83
|
+
const directory: string = !!parsed.ext
|
|
84
|
+
? path.resolve(parsed.dir)
|
|
85
|
+
: this.config.swagger.output;
|
|
86
|
+
const stats: fs.Stats = await fs.promises.lstat(directory);
|
|
87
|
+
if (stats.isDirectory() === false)
|
|
88
|
+
throw new Error(
|
|
89
|
+
"Error on NestiaApplication.swagger(): output directory does not exists.",
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
print_title("Nestia Swagger Generator");
|
|
93
|
+
await this.generate(
|
|
94
|
+
"swagger",
|
|
95
|
+
(config) => config.swagger!,
|
|
96
|
+
SwaggerGenerator.generate,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private async generate<Config>(
|
|
101
|
+
method: string,
|
|
102
|
+
config: (entire: INestiaConfig) => Config,
|
|
103
|
+
archiver: (
|
|
104
|
+
checker: ts.TypeChecker,
|
|
105
|
+
) => (config: Config) => (routes: IRoute[]) => Promise<void>,
|
|
106
|
+
): Promise<void> {
|
|
107
|
+
//----
|
|
108
|
+
// ANALYZE REFLECTS
|
|
109
|
+
//----
|
|
110
|
+
const unique: WeakSet<any> = new WeakSet();
|
|
111
|
+
const controllers: IController[] = [];
|
|
112
|
+
const project: INestiaProject = {
|
|
113
|
+
config: this.config,
|
|
114
|
+
input: await ConfigAnalyzer.input(this.config),
|
|
115
|
+
checker: null!,
|
|
116
|
+
errors: [],
|
|
117
|
+
warnings: [],
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
console.log("Analyzing reflections");
|
|
121
|
+
for (const include of (await ConfigAnalyzer.input(this.config)).include)
|
|
122
|
+
controllers.push(
|
|
123
|
+
...(await ReflectAnalyzer.analyze(project)(
|
|
124
|
+
unique,
|
|
125
|
+
include.file,
|
|
126
|
+
include.paths,
|
|
127
|
+
include.controller,
|
|
128
|
+
)),
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const agg: number = (() => {
|
|
132
|
+
const set: Set<string> = new Set();
|
|
133
|
+
for (const c of controllers)
|
|
134
|
+
for (const cPath of c.paths)
|
|
135
|
+
for (const f of c.functions)
|
|
136
|
+
for (const fPath of f.paths)
|
|
137
|
+
set.add(`${f.method}::${cPath}/${fPath}`);
|
|
138
|
+
return set.size;
|
|
139
|
+
})();
|
|
140
|
+
|
|
141
|
+
console.log(` - controllers: #${controllers.length}`);
|
|
142
|
+
console.log(` - paths: #${agg}`);
|
|
143
|
+
console.log(
|
|
144
|
+
` - routes: #${controllers
|
|
145
|
+
.map(
|
|
146
|
+
(c) =>
|
|
147
|
+
c.paths.length *
|
|
148
|
+
c.functions.map((f) => f.paths.length).reduce((a, b) => a + b, 0),
|
|
149
|
+
)
|
|
150
|
+
.reduce((a, b) => a + b, 0)}`,
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
//----
|
|
154
|
+
// ANALYZE TYPESCRIPT CODE
|
|
155
|
+
//----
|
|
156
|
+
console.log("Analyzing source codes");
|
|
157
|
+
|
|
158
|
+
const program: ts.Program = ts.createProgram(
|
|
159
|
+
controllers.map((c) => c.file),
|
|
160
|
+
this.compilerOptions,
|
|
161
|
+
);
|
|
162
|
+
project.checker = program.getTypeChecker();
|
|
163
|
+
|
|
164
|
+
const routeList: IRoute[] = [];
|
|
165
|
+
for (const c of controllers) {
|
|
166
|
+
const file: ts.SourceFile | undefined = program.getSourceFile(c.file);
|
|
167
|
+
if (file === undefined) continue;
|
|
168
|
+
routeList.push(...(await ControllerAnalyzer.analyze(project)(file, c)));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// REPORT ERRORS
|
|
172
|
+
if (project.errors.length) {
|
|
173
|
+
report_errors("error")(project.errors);
|
|
174
|
+
process.exit(-1);
|
|
175
|
+
}
|
|
176
|
+
if (project.warnings.length) report_errors("warning")(project.warnings);
|
|
177
|
+
|
|
178
|
+
// FIND IMPLICIT TYPES
|
|
179
|
+
if (this.config.clone !== true) {
|
|
180
|
+
const implicit: IRoute[] = routeList.filter(is_implicit_return_typed);
|
|
181
|
+
if (implicit.length > 0)
|
|
182
|
+
throw new Error(
|
|
183
|
+
`NestiaApplication.${method}(): implicit return type is not allowed.\n` +
|
|
184
|
+
"\n" +
|
|
185
|
+
"List of implicit return typed routes:\n" +
|
|
186
|
+
implicit
|
|
187
|
+
.map(
|
|
188
|
+
(it) =>
|
|
189
|
+
` - ${it.target.class.name}.${it.target.function.name} at "${it.location}"`,
|
|
190
|
+
)
|
|
191
|
+
.join("\n"),
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// DO GENERATE
|
|
196
|
+
AccessorAnalyzer.analyze(routeList);
|
|
197
|
+
await archiver(project.checker)(config(this.config))(routeList);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const print_title = (str: string): void => {
|
|
202
|
+
console.log("-----------------------------------------------------------");
|
|
203
|
+
console.log(` ${str}`);
|
|
204
|
+
console.log("-----------------------------------------------------------");
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const is_implicit_return_typed = (route: IRoute): boolean => {
|
|
208
|
+
const name: string = route.output.typeName;
|
|
209
|
+
if (name === "void") return false;
|
|
210
|
+
else if (name.indexOf("readonly [") !== -1) return true;
|
|
211
|
+
|
|
212
|
+
const pos: number = name.indexOf("__object");
|
|
213
|
+
if (pos === -1) return false;
|
|
214
|
+
|
|
215
|
+
const before: number = pos - 1;
|
|
216
|
+
const after: number = pos + "__object".length;
|
|
217
|
+
for (const i of [before, after])
|
|
218
|
+
if (name[i] === undefined) continue;
|
|
219
|
+
else if (VARIABLE.test(name[i])) return false;
|
|
220
|
+
return true;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const report_errors =
|
|
224
|
+
(type: "error" | "warning") =>
|
|
225
|
+
(errors: IErrorReport[]): void => {
|
|
226
|
+
// key: file
|
|
227
|
+
// key: controller
|
|
228
|
+
// key: function
|
|
229
|
+
// value: message
|
|
230
|
+
const map: Map<string, Map<string, Map<string, Set<string>>>> = new Map();
|
|
231
|
+
for (const e of errors) {
|
|
232
|
+
const file = MapUtil.take(map, e.file, () => new Map());
|
|
233
|
+
const controller = MapUtil.take(file, e.controller, () => new Map());
|
|
234
|
+
const func = MapUtil.take(controller, e.function, () => new Set());
|
|
235
|
+
func.add(e.message);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
console.log("");
|
|
239
|
+
print_title(`Nestia ${type[0].toUpperCase()}${type.slice(1)} Report`);
|
|
240
|
+
for (const [file, cMap] of map) {
|
|
241
|
+
for (const [controller, fMap] of cMap)
|
|
242
|
+
for (const [func, messages] of fMap) {
|
|
243
|
+
const location: string = path.relative(process.cwd(), file);
|
|
244
|
+
console.log(
|
|
245
|
+
`${location} - ${
|
|
246
|
+
func !== null ? `${controller}.${func}()` : controller
|
|
247
|
+
}`,
|
|
248
|
+
);
|
|
249
|
+
for (const msg of messages) console.log(` - ${msg}`);
|
|
250
|
+
console.log("");
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const VARIABLE = /[a-zA-Z_$0-9]/;
|