@nestia/sdk 3.0.0-dev.20231209 → 3.0.0
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
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
import { Escaper } from "typia/lib/utils/Escaper";
|
|
2
|
-
|
|
3
|
-
import { IRoute } from "../structures/IRoute";
|
|
4
|
-
|
|
5
|
-
export namespace AccessorAnalyzer {
|
|
6
|
-
export const analyze = (routes: IRoute[]) => {
|
|
7
|
-
shrink(routes);
|
|
8
|
-
variable(routes);
|
|
9
|
-
shrink(routes);
|
|
10
|
-
for (const r of routes) r.name = r.accessors.at(-1) ?? r.name;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const prepare = (routeList: IRoute[]): Map<string, number> => {
|
|
14
|
-
const dict: Map<string, number> = new Map();
|
|
15
|
-
for (const route of routeList)
|
|
16
|
-
route.accessors.forEach((_a, i) => {
|
|
17
|
-
const key: string = route.accessors.slice(0, i + 1).join(".");
|
|
18
|
-
dict.set(key, (dict.get(key) ?? 0) + 1);
|
|
19
|
-
});
|
|
20
|
-
return dict;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
const variable = (routeList: IRoute[]) => {
|
|
24
|
-
const dict: Map<string, number> = prepare(routeList);
|
|
25
|
-
for (const route of routeList) {
|
|
26
|
-
const emended: string[] = route.accessors.slice();
|
|
27
|
-
route.accessors.forEach((accessor, i) => {
|
|
28
|
-
if (Escaper.variable(accessor)) return;
|
|
29
|
-
while (true) {
|
|
30
|
-
accessor = "$" + accessor;
|
|
31
|
-
const partial: string = [
|
|
32
|
-
...route.accessors.slice(0, i),
|
|
33
|
-
accessor,
|
|
34
|
-
].join(".");
|
|
35
|
-
if (dict.has(partial) === false) {
|
|
36
|
-
emended[i] = accessor;
|
|
37
|
-
break;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
route.accessors.splice(0, route.accessors.length, ...emended);
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const shrink = (routeList: IRoute[]) => {
|
|
46
|
-
const dict: Map<string, number> = prepare(routeList);
|
|
47
|
-
for (const route of routeList) {
|
|
48
|
-
if (
|
|
49
|
-
route.accessors.length < 2 ||
|
|
50
|
-
route.accessors.at(-1) !== route.accessors.at(-2)
|
|
51
|
-
)
|
|
52
|
-
continue;
|
|
53
|
-
|
|
54
|
-
const cut: string[] = route.accessors.slice(0, -1);
|
|
55
|
-
if ((dict.get(cut.join(".")) ?? 0) > 1) continue;
|
|
56
|
-
|
|
57
|
-
route.accessors = cut;
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
}
|
|
1
|
+
import { Escaper } from "typia/lib/utils/Escaper";
|
|
2
|
+
|
|
3
|
+
import { IRoute } from "../structures/IRoute";
|
|
4
|
+
|
|
5
|
+
export namespace AccessorAnalyzer {
|
|
6
|
+
export const analyze = (routes: IRoute[]) => {
|
|
7
|
+
shrink(routes);
|
|
8
|
+
variable(routes);
|
|
9
|
+
shrink(routes);
|
|
10
|
+
for (const r of routes) r.name = r.accessors.at(-1) ?? r.name;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const prepare = (routeList: IRoute[]): Map<string, number> => {
|
|
14
|
+
const dict: Map<string, number> = new Map();
|
|
15
|
+
for (const route of routeList)
|
|
16
|
+
route.accessors.forEach((_a, i) => {
|
|
17
|
+
const key: string = route.accessors.slice(0, i + 1).join(".");
|
|
18
|
+
dict.set(key, (dict.get(key) ?? 0) + 1);
|
|
19
|
+
});
|
|
20
|
+
return dict;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const variable = (routeList: IRoute[]) => {
|
|
24
|
+
const dict: Map<string, number> = prepare(routeList);
|
|
25
|
+
for (const route of routeList) {
|
|
26
|
+
const emended: string[] = route.accessors.slice();
|
|
27
|
+
route.accessors.forEach((accessor, i) => {
|
|
28
|
+
if (Escaper.variable(accessor)) return;
|
|
29
|
+
while (true) {
|
|
30
|
+
accessor = "$" + accessor;
|
|
31
|
+
const partial: string = [
|
|
32
|
+
...route.accessors.slice(0, i),
|
|
33
|
+
accessor,
|
|
34
|
+
].join(".");
|
|
35
|
+
if (dict.has(partial) === false) {
|
|
36
|
+
emended[i] = accessor;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
route.accessors.splice(0, route.accessors.length, ...emended);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const shrink = (routeList: IRoute[]) => {
|
|
46
|
+
const dict: Map<string, number> = prepare(routeList);
|
|
47
|
+
for (const route of routeList) {
|
|
48
|
+
if (
|
|
49
|
+
route.accessors.length < 2 ||
|
|
50
|
+
route.accessors.at(-1) !== route.accessors.at(-2)
|
|
51
|
+
)
|
|
52
|
+
continue;
|
|
53
|
+
|
|
54
|
+
const cut: string[] = route.accessors.slice(0, -1);
|
|
55
|
+
if ((dict.get(cut.join(".")) ?? 0) > 1) continue;
|
|
56
|
+
|
|
57
|
+
route.accessors = cut;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -1,147 +1,147 @@
|
|
|
1
|
-
import { INestApplication, VersioningType } from "@nestjs/common";
|
|
2
|
-
import { MODULE_PATH } from "@nestjs/common/constants";
|
|
3
|
-
import { NestContainer } from "@nestjs/core";
|
|
4
|
-
import { Module } from "@nestjs/core/injector/module";
|
|
5
|
-
import fs from "fs";
|
|
6
|
-
import path from "path";
|
|
7
|
-
import { HashMap, Pair, Singleton } from "tstl";
|
|
8
|
-
|
|
9
|
-
import { INestiaConfig } from "../INestiaConfig";
|
|
10
|
-
import { SdkGenerator } from "../generates/SdkGenerator";
|
|
11
|
-
import { INormalizedInput } from "../structures/INormalizedInput";
|
|
12
|
-
import { ArrayUtil } from "../utils/ArrayUtil";
|
|
13
|
-
import { MapUtil } from "../utils/MapUtil";
|
|
14
|
-
import { SourceFinder } from "../utils/SourceFinder";
|
|
15
|
-
|
|
16
|
-
export namespace ConfigAnalyzer {
|
|
17
|
-
export const input = (config: INestiaConfig): Promise<INormalizedInput> =>
|
|
18
|
-
MapUtil.take(memory, config, async () => {
|
|
19
|
-
const input = config.input;
|
|
20
|
-
if (Array.isArray(input)) return transform_input(config)(input);
|
|
21
|
-
else if (typeof input === "function")
|
|
22
|
-
return analyze_application(await input());
|
|
23
|
-
else if (typeof input === "object")
|
|
24
|
-
if (input === null)
|
|
25
|
-
throw new Error("Invalid input config. It can't be null.");
|
|
26
|
-
else return transform_input(config)(input.include, input.exclude);
|
|
27
|
-
else if (typeof input === "string")
|
|
28
|
-
return transform_input(config)([input]);
|
|
29
|
-
else throw new Error("Invalid input config.");
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
const analyze_application = async (
|
|
33
|
-
app: INestApplication,
|
|
34
|
-
): Promise<INormalizedInput> => {
|
|
35
|
-
const files: HashMap<Pair<Function, string>, Set<string>> = new HashMap();
|
|
36
|
-
const container: NestContainer = (app as any).container as NestContainer;
|
|
37
|
-
const modules: Module[] = [...container.getModules().values()].filter(
|
|
38
|
-
(m) => !!m.controllers.size,
|
|
39
|
-
);
|
|
40
|
-
for (const m of modules) {
|
|
41
|
-
const path: string =
|
|
42
|
-
Reflect.getMetadata(
|
|
43
|
-
MODULE_PATH + container.getModules().applicationId,
|
|
44
|
-
m.metatype,
|
|
45
|
-
) ??
|
|
46
|
-
Reflect.getMetadata(MODULE_PATH, m.metatype) ??
|
|
47
|
-
"";
|
|
48
|
-
for (const controller of [...m.controllers.keys()]) {
|
|
49
|
-
const file: string | null =
|
|
50
|
-
(await require("get-function-location")(controller))?.source ?? null;
|
|
51
|
-
if (file === null) continue;
|
|
52
|
-
|
|
53
|
-
const location: string = normalize_file(file);
|
|
54
|
-
if (location.length === 0) continue;
|
|
55
|
-
|
|
56
|
-
const key: Pair<Function, string> = new Pair(
|
|
57
|
-
controller as Function,
|
|
58
|
-
location,
|
|
59
|
-
);
|
|
60
|
-
files.take(key, () => new Set([])).add(path);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const versioning = (app as any).config?.versioningOptions;
|
|
65
|
-
return {
|
|
66
|
-
include: files.toJSON().map((pair) => ({
|
|
67
|
-
controller: pair.first.first,
|
|
68
|
-
file: pair.first.second,
|
|
69
|
-
paths: [...pair.second.values()],
|
|
70
|
-
})),
|
|
71
|
-
globalPrefix:
|
|
72
|
-
typeof (app as any).config?.globalPrefix === "string"
|
|
73
|
-
? {
|
|
74
|
-
prefix: (app as any).config.globalPrefix,
|
|
75
|
-
exclude: (app as any).config.globalPrefixOptions?.exclude ?? {},
|
|
76
|
-
}
|
|
77
|
-
: undefined,
|
|
78
|
-
versioning:
|
|
79
|
-
versioning === undefined || versioning.type !== VersioningType.URI
|
|
80
|
-
? undefined
|
|
81
|
-
: {
|
|
82
|
-
prefix:
|
|
83
|
-
versioning.prefix === undefined || versioning.prefix === false
|
|
84
|
-
? "v"
|
|
85
|
-
: versioning.prefix,
|
|
86
|
-
defaultVersion: versioning.defaultVersion,
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const normalize_file = (str: string) =>
|
|
92
|
-
str.substring(
|
|
93
|
-
str.startsWith("file:///")
|
|
94
|
-
? process.cwd()[0] === "/"
|
|
95
|
-
? 7
|
|
96
|
-
: 8
|
|
97
|
-
: str.startsWith("file://")
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
const transform_input =
|
|
103
|
-
(config: INestiaConfig) =>
|
|
104
|
-
async (include: string[], exclude?: string[]) => ({
|
|
105
|
-
include: (
|
|
106
|
-
await SourceFinder.find({
|
|
107
|
-
include,
|
|
108
|
-
exclude,
|
|
109
|
-
filter: filter(config),
|
|
110
|
-
})
|
|
111
|
-
).map((file) => ({
|
|
112
|
-
paths: [""],
|
|
113
|
-
file,
|
|
114
|
-
})),
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
const filter =
|
|
118
|
-
(config: INestiaConfig) =>
|
|
119
|
-
async (location: string): Promise<boolean> =>
|
|
120
|
-
location.endsWith(".ts") &&
|
|
121
|
-
!location.endsWith(".d.ts") &&
|
|
122
|
-
(config.output === undefined ||
|
|
123
|
-
(location.indexOf(path.join(config.output, "functional")) === -1 &&
|
|
124
|
-
(await (
|
|
125
|
-
await bundler.get(config.output)
|
|
126
|
-
)(location))) === false);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const memory = new Map<INestiaConfig, Promise<INormalizedInput>>();
|
|
130
|
-
const bundler = new Singleton(async (output: string) => {
|
|
131
|
-
const assets: string[] = await fs.promises.readdir(SdkGenerator.BUNDLE_PATH);
|
|
132
|
-
const tuples: Pair<string, boolean>[] = await ArrayUtil.asyncMap(
|
|
133
|
-
assets,
|
|
134
|
-
async (file) => {
|
|
135
|
-
const relative: string = path.join(output, file);
|
|
136
|
-
const location: string = path.join(SdkGenerator.BUNDLE_PATH, file);
|
|
137
|
-
const stats: fs.Stats = await fs.promises.stat(location);
|
|
138
|
-
return new Pair(relative, stats.isDirectory());
|
|
139
|
-
},
|
|
140
|
-
);
|
|
141
|
-
return async (file: string): Promise<boolean> => {
|
|
142
|
-
for (const it of tuples)
|
|
143
|
-
if (it.second === false && file === it.first) return true;
|
|
144
|
-
else if (it.second === true && file.indexOf(it.first) === 0) return true;
|
|
145
|
-
return false;
|
|
146
|
-
};
|
|
147
|
-
});
|
|
1
|
+
import { INestApplication, VersioningType } from "@nestjs/common";
|
|
2
|
+
import { MODULE_PATH } from "@nestjs/common/constants";
|
|
3
|
+
import { NestContainer } from "@nestjs/core";
|
|
4
|
+
import { Module } from "@nestjs/core/injector/module";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { HashMap, Pair, Singleton } from "tstl";
|
|
8
|
+
|
|
9
|
+
import { INestiaConfig } from "../INestiaConfig";
|
|
10
|
+
import { SdkGenerator } from "../generates/SdkGenerator";
|
|
11
|
+
import { INormalizedInput } from "../structures/INormalizedInput";
|
|
12
|
+
import { ArrayUtil } from "../utils/ArrayUtil";
|
|
13
|
+
import { MapUtil } from "../utils/MapUtil";
|
|
14
|
+
import { SourceFinder } from "../utils/SourceFinder";
|
|
15
|
+
|
|
16
|
+
export namespace ConfigAnalyzer {
|
|
17
|
+
export const input = (config: INestiaConfig): Promise<INormalizedInput> =>
|
|
18
|
+
MapUtil.take(memory, config, async () => {
|
|
19
|
+
const input = config.input;
|
|
20
|
+
if (Array.isArray(input)) return transform_input(config)(input);
|
|
21
|
+
else if (typeof input === "function")
|
|
22
|
+
return analyze_application(await input());
|
|
23
|
+
else if (typeof input === "object")
|
|
24
|
+
if (input === null)
|
|
25
|
+
throw new Error("Invalid input config. It can't be null.");
|
|
26
|
+
else return transform_input(config)(input.include, input.exclude);
|
|
27
|
+
else if (typeof input === "string")
|
|
28
|
+
return transform_input(config)([input]);
|
|
29
|
+
else throw new Error("Invalid input config.");
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const analyze_application = async (
|
|
33
|
+
app: INestApplication,
|
|
34
|
+
): Promise<INormalizedInput> => {
|
|
35
|
+
const files: HashMap<Pair<Function, string>, Set<string>> = new HashMap();
|
|
36
|
+
const container: NestContainer = (app as any).container as NestContainer;
|
|
37
|
+
const modules: Module[] = [...container.getModules().values()].filter(
|
|
38
|
+
(m) => !!m.controllers.size,
|
|
39
|
+
);
|
|
40
|
+
for (const m of modules) {
|
|
41
|
+
const path: string =
|
|
42
|
+
Reflect.getMetadata(
|
|
43
|
+
MODULE_PATH + container.getModules().applicationId,
|
|
44
|
+
m.metatype,
|
|
45
|
+
) ??
|
|
46
|
+
Reflect.getMetadata(MODULE_PATH, m.metatype) ??
|
|
47
|
+
"";
|
|
48
|
+
for (const controller of [...m.controllers.keys()]) {
|
|
49
|
+
const file: string | null =
|
|
50
|
+
(await require("get-function-location")(controller))?.source ?? null;
|
|
51
|
+
if (file === null) continue;
|
|
52
|
+
|
|
53
|
+
const location: string = normalize_file(file);
|
|
54
|
+
if (location.length === 0) continue;
|
|
55
|
+
|
|
56
|
+
const key: Pair<Function, string> = new Pair(
|
|
57
|
+
controller as Function,
|
|
58
|
+
location,
|
|
59
|
+
);
|
|
60
|
+
files.take(key, () => new Set([])).add(path);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const versioning = (app as any).config?.versioningOptions;
|
|
65
|
+
return {
|
|
66
|
+
include: files.toJSON().map((pair) => ({
|
|
67
|
+
controller: pair.first.first,
|
|
68
|
+
file: decodeURIComponent(pair.first.second),
|
|
69
|
+
paths: [...pair.second.values()],
|
|
70
|
+
})),
|
|
71
|
+
globalPrefix:
|
|
72
|
+
typeof (app as any).config?.globalPrefix === "string"
|
|
73
|
+
? {
|
|
74
|
+
prefix: (app as any).config.globalPrefix,
|
|
75
|
+
exclude: (app as any).config.globalPrefixOptions?.exclude ?? {},
|
|
76
|
+
}
|
|
77
|
+
: undefined,
|
|
78
|
+
versioning:
|
|
79
|
+
versioning === undefined || versioning.type !== VersioningType.URI
|
|
80
|
+
? undefined
|
|
81
|
+
: {
|
|
82
|
+
prefix:
|
|
83
|
+
versioning.prefix === undefined || versioning.prefix === false
|
|
84
|
+
? "v"
|
|
85
|
+
: versioning.prefix,
|
|
86
|
+
defaultVersion: versioning.defaultVersion,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const normalize_file = (str: string) =>
|
|
92
|
+
str.substring(
|
|
93
|
+
str.startsWith("file:///")
|
|
94
|
+
? process.cwd()[0] === "/"
|
|
95
|
+
? 7
|
|
96
|
+
: 8
|
|
97
|
+
: str.startsWith("file://")
|
|
98
|
+
? 7
|
|
99
|
+
: 0,
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const transform_input =
|
|
103
|
+
(config: INestiaConfig) =>
|
|
104
|
+
async (include: string[], exclude?: string[]) => ({
|
|
105
|
+
include: (
|
|
106
|
+
await SourceFinder.find({
|
|
107
|
+
include,
|
|
108
|
+
exclude,
|
|
109
|
+
filter: filter(config),
|
|
110
|
+
})
|
|
111
|
+
).map((file) => ({
|
|
112
|
+
paths: [""],
|
|
113
|
+
file,
|
|
114
|
+
})),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const filter =
|
|
118
|
+
(config: INestiaConfig) =>
|
|
119
|
+
async (location: string): Promise<boolean> =>
|
|
120
|
+
location.endsWith(".ts") &&
|
|
121
|
+
!location.endsWith(".d.ts") &&
|
|
122
|
+
(config.output === undefined ||
|
|
123
|
+
(location.indexOf(path.join(config.output, "functional")) === -1 &&
|
|
124
|
+
(await (
|
|
125
|
+
await bundler.get(config.output)
|
|
126
|
+
)(location))) === false);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const memory = new Map<INestiaConfig, Promise<INormalizedInput>>();
|
|
130
|
+
const bundler = new Singleton(async (output: string) => {
|
|
131
|
+
const assets: string[] = await fs.promises.readdir(SdkGenerator.BUNDLE_PATH);
|
|
132
|
+
const tuples: Pair<string, boolean>[] = await ArrayUtil.asyncMap(
|
|
133
|
+
assets,
|
|
134
|
+
async (file) => {
|
|
135
|
+
const relative: string = path.join(output, file);
|
|
136
|
+
const location: string = path.join(SdkGenerator.BUNDLE_PATH, file);
|
|
137
|
+
const stats: fs.Stats = await fs.promises.stat(location);
|
|
138
|
+
return new Pair(relative, stats.isDirectory());
|
|
139
|
+
},
|
|
140
|
+
);
|
|
141
|
+
return async (file: string): Promise<boolean> => {
|
|
142
|
+
for (const it of tuples)
|
|
143
|
+
if (it.second === false && file === it.first) return true;
|
|
144
|
+
else if (it.second === true && file.indexOf(it.first) === 0) return true;
|
|
145
|
+
return false;
|
|
146
|
+
};
|
|
147
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { VERSION_NEUTRAL, VersionValue } from "@nestjs/common/interfaces";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { HashMap } from "tstl
|
|
3
|
+
import { HashMap } from "tstl";
|
|
4
4
|
import ts from "typescript";
|
|
5
5
|
import { CommentFactory } from "typia/lib/factories/CommentFactory";
|
|
6
6
|
|
|
@@ -132,7 +132,12 @@ export namespace ControllerAnalyzer {
|
|
|
132
132
|
importDict,
|
|
133
133
|
signature.getReturnType(),
|
|
134
134
|
);
|
|
135
|
-
if (
|
|
135
|
+
if (
|
|
136
|
+
outputType === null ||
|
|
137
|
+
(project.config.clone !== true &&
|
|
138
|
+
(outputType.typeName === "__type" ||
|
|
139
|
+
outputType.typeName === "__object"))
|
|
140
|
+
) {
|
|
136
141
|
project.errors.push({
|
|
137
142
|
file: controller.file,
|
|
138
143
|
controller: controller.name,
|
|
@@ -172,16 +177,18 @@ export namespace ControllerAnalyzer {
|
|
|
172
177
|
...jsDocTags
|
|
173
178
|
.filter((tag) => tag.name === "security")
|
|
174
179
|
.map((tag) =>
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
180
|
+
tag.text === undefined
|
|
181
|
+
? [{}]
|
|
182
|
+
: tag.text.map((text) => {
|
|
183
|
+
const line: string[] = text.text
|
|
184
|
+
.split(" ")
|
|
185
|
+
.filter((s) => s.trim())
|
|
186
|
+
.filter((s) => !!s.length);
|
|
187
|
+
if (line.length === 0) return {};
|
|
188
|
+
return {
|
|
189
|
+
[line[0]]: line.slice(1),
|
|
190
|
+
};
|
|
191
|
+
}),
|
|
185
192
|
)
|
|
186
193
|
.flat(),
|
|
187
194
|
);
|
|
@@ -189,6 +196,7 @@ export namespace ControllerAnalyzer {
|
|
|
189
196
|
// CONSTRUCT COMMON DATA
|
|
190
197
|
const common: Omit<IRoute, "path" | "accessors"> = {
|
|
191
198
|
...func,
|
|
199
|
+
controller: controller.target,
|
|
192
200
|
parameters: parameters.filter((p) => p !== null) as IRoute.IParameter[],
|
|
193
201
|
output: {
|
|
194
202
|
type: outputType.type,
|
|
@@ -197,9 +205,9 @@ export namespace ControllerAnalyzer {
|
|
|
197
205
|
},
|
|
198
206
|
imports,
|
|
199
207
|
status: func.status,
|
|
200
|
-
|
|
201
|
-
class: controller.
|
|
202
|
-
function: func.
|
|
208
|
+
target: {
|
|
209
|
+
class: controller.target,
|
|
210
|
+
function: func.target,
|
|
203
211
|
},
|
|
204
212
|
location: (() => {
|
|
205
213
|
const file = declaration.getSourceFile();
|
|
@@ -265,8 +273,8 @@ export namespace ControllerAnalyzer {
|
|
|
265
273
|
v === null
|
|
266
274
|
? null
|
|
267
275
|
: project.input.versioning?.prefix?.length
|
|
268
|
-
|
|
269
|
-
|
|
276
|
+
? `${project.input.versioning.prefix}${v}`
|
|
277
|
+
: v,
|
|
270
278
|
),
|
|
271
279
|
)({
|
|
272
280
|
method: func.method,
|
|
@@ -274,9 +282,20 @@ export namespace ControllerAnalyzer {
|
|
|
274
282
|
}),
|
|
275
283
|
)
|
|
276
284
|
.flat()
|
|
285
|
+
.filter((path) => {
|
|
286
|
+
const escaped: string | null = PathAnalyzer.escape(path);
|
|
287
|
+
if (escaped === null)
|
|
288
|
+
project.errors.push({
|
|
289
|
+
file: controller.file,
|
|
290
|
+
controller: controller.name,
|
|
291
|
+
function: func.name,
|
|
292
|
+
message: `unable to escape the path "${path}".`,
|
|
293
|
+
});
|
|
294
|
+
return escaped !== null;
|
|
295
|
+
})
|
|
277
296
|
.map((path) => ({
|
|
278
297
|
...common,
|
|
279
|
-
path: PathAnalyzer.escape(path
|
|
298
|
+
path: PathAnalyzer.escape(path)!,
|
|
280
299
|
accessors: [...PathUtil.accessors(path), func.name],
|
|
281
300
|
}));
|
|
282
301
|
};
|
|
@@ -348,7 +367,11 @@ export namespace ControllerAnalyzer {
|
|
|
348
367
|
importDict,
|
|
349
368
|
type,
|
|
350
369
|
);
|
|
351
|
-
if (
|
|
370
|
+
if (
|
|
371
|
+
tuple === null ||
|
|
372
|
+
(project.config.clone !== true &&
|
|
373
|
+
(tuple.typeName === "__type" || tuple.typeName === "__object"))
|
|
374
|
+
)
|
|
352
375
|
errors.push({
|
|
353
376
|
file: controller.file,
|
|
354
377
|
controller: controller.name,
|