@nestia/sdk 1.1.1 → 1.2.0-dev.20230504
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/assets/bundle/api/index.ts +4 -0
- package/assets/bundle/api/module.ts +5 -0
- package/assets/bundle/e2e/index.ts +41 -0
- package/lib/INestiaConfig.d.ts +6 -0
- package/lib/NestiaSdkApplication.d.ts +1 -0
- package/lib/NestiaSdkApplication.js +35 -14
- package/lib/NestiaSdkApplication.js.map +1 -1
- package/lib/analyses/ControllerAnalyzer.js +1 -0
- package/lib/analyses/ControllerAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectAnalyzer.js +12 -1
- package/lib/analyses/ReflectAnalyzer.js.map +1 -1
- package/lib/executable/internal/NestiaSdkCommand.d.ts +3 -2
- package/lib/executable/internal/NestiaSdkCommand.js +52 -56
- package/lib/executable/internal/NestiaSdkCommand.js.map +1 -1
- package/lib/executable/internal/NestiaSdkConfig.js +5 -1
- package/lib/executable/internal/NestiaSdkConfig.js.map +1 -1
- package/lib/executable/sdk.js +5 -8
- package/lib/executable/sdk.js.map +1 -1
- package/lib/generates/E2eGenerator.d.ts +5 -0
- package/lib/generates/E2eGenerator.js +52 -0
- package/lib/generates/E2eGenerator.js.map +1 -0
- package/lib/generates/SdkGenerator.d.ts +1 -2
- package/lib/generates/SdkGenerator.js +20 -22
- package/lib/generates/SdkGenerator.js.map +1 -1
- package/lib/generates/SwaggerGenerator.d.ts +1 -1
- package/lib/generates/SwaggerGenerator.js +40 -43
- package/lib/generates/SwaggerGenerator.js.map +1 -1
- package/lib/generates/internal/E2eFileProgrammer.d.ts +8 -0
- package/lib/generates/internal/E2eFileProgrammer.js +101 -0
- package/lib/generates/internal/E2eFileProgrammer.js.map +1 -0
- package/lib/generates/internal/SdkFileProgrammer.d.ts +5 -0
- package/lib/generates/internal/SdkFileProgrammer.js +121 -0
- package/lib/generates/internal/SdkFileProgrammer.js.map +1 -0
- package/lib/generates/internal/SdkFunctionProgrammer.d.ts +5 -0
- package/lib/generates/{FunctionGenerator.js → internal/SdkFunctionProgrammer.js} +58 -57
- package/lib/generates/internal/SdkFunctionProgrammer.js.map +1 -0
- package/lib/generates/internal/SdkRouteDirectory.d.ts +10 -0
- package/lib/generates/internal/SdkRouteDirectory.js +18 -0
- package/lib/generates/internal/SdkRouteDirectory.js.map +1 -0
- package/lib/structures/IController.d.ts +4 -0
- package/lib/structures/IRoute.d.ts +4 -0
- package/lib/utils/NestiaConfigUtil.d.ts +4 -0
- package/lib/utils/NestiaConfigUtil.js +24 -0
- package/lib/utils/NestiaConfigUtil.js.map +1 -0
- package/lib/utils/SourceFinder.d.ts +9 -0
- package/lib/utils/SourceFinder.js +60 -0
- package/lib/utils/SourceFinder.js.map +1 -0
- package/package.json +2 -3
- package/src/INestiaConfig.ts +7 -0
- package/src/NestiaSdkApplication.ts +54 -17
- package/src/analyses/ControllerAnalyzer.ts +1 -0
- package/src/analyses/ReflectAnalyzer.ts +12 -2
- package/src/executable/internal/NestiaSdkCommand.ts +87 -105
- package/src/executable/sdk.ts +4 -8
- package/src/generates/E2eGenerator.ts +65 -0
- package/src/generates/SdkGenerator.ts +29 -30
- package/src/generates/SwaggerGenerator.ts +66 -64
- package/src/generates/internal/E2eFileProgrammer.ts +119 -0
- package/src/generates/internal/SdkFileProgrammer.ts +144 -0
- package/src/generates/internal/SdkFunctionProgrammer.ts +371 -0
- package/src/generates/internal/SdkRouteDirectory.ts +21 -0
- package/src/structures/IController.ts +4 -0
- package/src/structures/IRoute.ts +4 -0
- package/src/utils/NestiaConfigUtil.ts +21 -0
- package/src/utils/SourceFinder.ts +60 -0
- package/lib/analyses/SourceFinder.d.ts +0 -4
- package/lib/analyses/SourceFinder.js +0 -71
- package/lib/analyses/SourceFinder.js.map +0 -1
- package/lib/generates/FileGenerator.d.ts +0 -5
- package/lib/generates/FileGenerator.js +0 -138
- package/lib/generates/FileGenerator.js.map +0 -1
- package/lib/generates/FunctionGenerator.d.ts +0 -5
- package/lib/generates/FunctionGenerator.js.map +0 -1
- package/src/analyses/SourceFinder.ts +0 -59
- package/src/generates/FileGenerator.ts +0 -156
- package/src/generates/FunctionGenerator.ts +0 -348
- /package/assets/bundle/{HttpError.ts → api/HttpError.ts} +0 -0
- /package/assets/bundle/{IConnection.ts → api/IConnection.ts} +0 -0
- /package/assets/bundle/{Primitive.ts → api/Primitive.ts} +0 -0
|
@@ -11,99 +11,83 @@ import { NestiaSdkConfig } from "./NestiaSdkConfig";
|
|
|
11
11
|
interface ICommand {
|
|
12
12
|
exclude: string | null;
|
|
13
13
|
out: string | null;
|
|
14
|
+
e2e: string | null;
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
interface
|
|
17
|
+
interface IProps {
|
|
17
18
|
assign: (config: INestiaConfig, output: string) => void;
|
|
18
19
|
validate: (config: INestiaConfig) => boolean;
|
|
19
20
|
location: (config: INestiaConfig) => string;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export namespace NestiaSdkCommand {
|
|
23
|
-
export
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
export const sdk = (argv: string[]) =>
|
|
25
|
+
main({
|
|
26
|
+
assign: (config, output) => (config.output = output),
|
|
27
|
+
validate: (config) => !!config.output,
|
|
28
|
+
location: (config) => config.output!,
|
|
29
|
+
})(argv)((app) => app.sdk());
|
|
30
|
+
|
|
31
|
+
export const swagger = (argv: string[]) =>
|
|
32
|
+
main({
|
|
33
|
+
assign: (config, output) => {
|
|
34
|
+
if (!config.swagger) config.swagger = { output };
|
|
35
|
+
else config.swagger.output = output;
|
|
33
36
|
},
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
cli.setArgv([
|
|
67
|
-
process.argv[0],
|
|
68
|
-
process.argv[1],
|
|
69
|
-
"nestia",
|
|
70
|
-
...elements,
|
|
71
|
-
]);
|
|
72
|
-
const command: ICommand = cli.parse({
|
|
73
|
-
exclude: ["e", "Something to exclude", "string", null],
|
|
74
|
-
out: ["o", "Output path of the SDK files", "string", null],
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
const inputs: string[] = [];
|
|
78
|
-
for (const arg of elements) {
|
|
79
|
-
if (arg[0] === "-") break;
|
|
80
|
-
inputs.push(arg);
|
|
81
|
-
}
|
|
82
|
-
await generate(task, inputs, command, output);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async function generate(
|
|
86
|
-
task: (app: NestiaSdkApplication) => Promise<void>,
|
|
87
|
-
include: string[],
|
|
88
|
-
command: ICommand,
|
|
89
|
-
output: IOutput,
|
|
90
|
-
): Promise<void> {
|
|
91
|
-
// CONFIGURATION
|
|
92
|
-
const config: INestiaConfig =
|
|
93
|
-
(await get_nestia_config(output.validate)) ??
|
|
94
|
-
parse_cli(include, command, output);
|
|
95
|
-
|
|
96
|
-
const options = await get_typescript_options();
|
|
97
|
-
|
|
98
|
-
config.compilerOptions = {
|
|
99
|
-
...options,
|
|
100
|
-
...(config.compilerOptions || {}),
|
|
37
|
+
validate: (config) => !!config.swagger && !!config.swagger.output,
|
|
38
|
+
location: (config) => config.swagger!.output!,
|
|
39
|
+
})(argv)((app) => app.swagger());
|
|
40
|
+
|
|
41
|
+
export const e2e = (argv: string[]) =>
|
|
42
|
+
main({
|
|
43
|
+
assign: (config, output) => (config.output = output),
|
|
44
|
+
validate: (config) => !!config.output,
|
|
45
|
+
location: (config) => config.output!,
|
|
46
|
+
})(argv)((app) => app.sdk());
|
|
47
|
+
|
|
48
|
+
const main =
|
|
49
|
+
(props: IProps) =>
|
|
50
|
+
(argv: string[]) =>
|
|
51
|
+
async (task: (app: NestiaSdkApplication) => Promise<void>) => {
|
|
52
|
+
const command: ICommand = cli.parse({
|
|
53
|
+
exclude: ["e", "Something to exclude", "string", null],
|
|
54
|
+
out: ["o", "Output path of the SDK files", "string", null],
|
|
55
|
+
e2e: [
|
|
56
|
+
"e",
|
|
57
|
+
"Output path of e2e test function files",
|
|
58
|
+
"string",
|
|
59
|
+
null,
|
|
60
|
+
],
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const inputs: string[] = [];
|
|
64
|
+
for (const r of argv) {
|
|
65
|
+
if (r[0] === "-") break;
|
|
66
|
+
inputs.push(r);
|
|
67
|
+
}
|
|
68
|
+
await generate(props)(command)(inputs)(task);
|
|
101
69
|
};
|
|
102
70
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
71
|
+
const generate =
|
|
72
|
+
(props: IProps) =>
|
|
73
|
+
(command: ICommand) =>
|
|
74
|
+
(include: string[]) =>
|
|
75
|
+
async (task: (app: NestiaSdkApplication) => Promise<void>) => {
|
|
76
|
+
// CONFIGURATION
|
|
77
|
+
const config: INestiaConfig =
|
|
78
|
+
(await get_nestia_config(props.validate)) ??
|
|
79
|
+
parse_cli(props)(command)(include);
|
|
80
|
+
|
|
81
|
+
const options = await get_typescript_options();
|
|
82
|
+
config.compilerOptions = {
|
|
83
|
+
...options,
|
|
84
|
+
...(config.compilerOptions || {}),
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// CALL THE APP.GENERATE()
|
|
88
|
+
const app: NestiaSdkApplication = new NestiaSdkApplication(config);
|
|
89
|
+
await task(app);
|
|
90
|
+
};
|
|
107
91
|
|
|
108
92
|
async function get_typescript_options(): Promise<ts.CompilerOptions | null> {
|
|
109
93
|
const configFileName = ts.findConfigFile(
|
|
@@ -111,18 +95,14 @@ export namespace NestiaSdkCommand {
|
|
|
111
95
|
ts.sys.fileExists,
|
|
112
96
|
"tsconfig.json",
|
|
113
97
|
);
|
|
114
|
-
|
|
115
98
|
if (!configFileName) return null;
|
|
116
99
|
|
|
117
100
|
const { tsconfig } = await parseNative(configFileName);
|
|
118
|
-
|
|
119
101
|
const configFileText = JSON.stringify(tsconfig);
|
|
120
|
-
|
|
121
102
|
const { config } = ts.parseConfigFileTextToJson(
|
|
122
103
|
configFileName,
|
|
123
104
|
configFileText,
|
|
124
105
|
);
|
|
125
|
-
|
|
126
106
|
const configParseResult = ts.parseJsonConfigFileContent(
|
|
127
107
|
config,
|
|
128
108
|
ts.sys,
|
|
@@ -138,7 +118,9 @@ export namespace NestiaSdkCommand {
|
|
|
138
118
|
validate: (config: INestiaConfig) => boolean,
|
|
139
119
|
): Promise<INestiaConfig | null> {
|
|
140
120
|
const connector = new WorkerConnector(null, null, "process");
|
|
141
|
-
await connector.connect(
|
|
121
|
+
await connector.connect(
|
|
122
|
+
`${__dirname}/nestia.config.getter.${__filename.substr(-2)}`,
|
|
123
|
+
);
|
|
142
124
|
|
|
143
125
|
const driver = await connector.getDriver<typeof NestiaSdkConfig>();
|
|
144
126
|
const config: INestiaConfig | null = await driver.get();
|
|
@@ -152,23 +134,23 @@ export namespace NestiaSdkCommand {
|
|
|
152
134
|
return config;
|
|
153
135
|
}
|
|
154
136
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
command: ICommand
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
137
|
+
const parse_cli =
|
|
138
|
+
(props: IProps) =>
|
|
139
|
+
(command: ICommand) =>
|
|
140
|
+
(include: string[]): INestiaConfig => {
|
|
141
|
+
if (command.out === null)
|
|
142
|
+
throw new Error(
|
|
143
|
+
`Error on NestiaCommand.main(): output directory is not specified. Add the "--out <output_directory>" option.`,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const config: INestiaConfig = {
|
|
147
|
+
input: {
|
|
148
|
+
include,
|
|
149
|
+
exclude: command.exclude ? [command.exclude] : undefined,
|
|
150
|
+
},
|
|
151
|
+
e2e: command.e2e ?? undefined,
|
|
152
|
+
};
|
|
153
|
+
props.assign(config, command.out);
|
|
154
|
+
return config;
|
|
170
155
|
};
|
|
171
|
-
output.assign(config, command.out);
|
|
172
|
-
return config;
|
|
173
|
-
}
|
|
174
156
|
}
|
package/src/executable/sdk.ts
CHANGED
|
@@ -14,14 +14,9 @@ npx @nestia/sdk [command] [options?]
|
|
|
14
14
|
- npx @nestia/sdk dependencies
|
|
15
15
|
- npx @nestia/sdk dependencies --manager pnpm
|
|
16
16
|
2. npx @nestia/sdk init
|
|
17
|
-
3. npx @nestia/sdk sdk
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
- npx @nestia/sdk sdk src/**/*.controller.ts --out src/api
|
|
21
|
-
4. npx @nestia/sdk swagger <input> --out <output>
|
|
22
|
-
- npx @nestia/sdk swagger # when "nestia.config.ts" be configured
|
|
23
|
-
- npx @nestia/sdk swagger src/controllers --out src/api
|
|
24
|
-
- npx @nestia/sdk swagger src/**/*.controller.ts --out src/api
|
|
17
|
+
3. npx @nestia/sdk sdk
|
|
18
|
+
4. npx @nestia/sdk swagger
|
|
19
|
+
5. npx @nestia/sdk e2e
|
|
25
20
|
`;
|
|
26
21
|
|
|
27
22
|
function halt(desc: string): never {
|
|
@@ -66,6 +61,7 @@ async function main() {
|
|
|
66
61
|
else if (type === "init") await initialize();
|
|
67
62
|
else if (type === "sdk") await execute((c) => c.sdk(argv));
|
|
68
63
|
else if (type === "swagger") await execute((c) => c.swagger(argv));
|
|
64
|
+
else if (type === "e2e") await execute((c) => c.e2e(argv));
|
|
69
65
|
else halt(USAGE);
|
|
70
66
|
}
|
|
71
67
|
main().catch((exp) => {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
import { INestiaConfig } from "../INestiaConfig";
|
|
5
|
+
import { IRoute } from "../structures/IRoute";
|
|
6
|
+
import { NestiaConfigUtil } from "../utils/NestiaConfigUtil";
|
|
7
|
+
import { E2eFileProgrammer } from "./internal/E2eFileProgrammer";
|
|
8
|
+
|
|
9
|
+
export namespace E2eGenerator {
|
|
10
|
+
export const generate =
|
|
11
|
+
(config: INestiaConfig) =>
|
|
12
|
+
async (routeList: IRoute[]): Promise<void> => {
|
|
13
|
+
// PREPARE DIRECTORIES
|
|
14
|
+
const output: string = path.resolve(config.e2e!);
|
|
15
|
+
await mkdir(output);
|
|
16
|
+
await mkdir(path.join(output, "features"));
|
|
17
|
+
await mkdir(path.join(output, "features", "api"));
|
|
18
|
+
await mkdir(path.join(output, "features", "api", "automated"));
|
|
19
|
+
|
|
20
|
+
// GENERATE TEST INDEX FILE
|
|
21
|
+
await index(config)(path.join(config.e2e!, "index.ts"));
|
|
22
|
+
|
|
23
|
+
// GENERATE EACH TEST FILES
|
|
24
|
+
for (const route of routeList)
|
|
25
|
+
await E2eFileProgrammer.generate(config)({
|
|
26
|
+
api: path.resolve(config.output!),
|
|
27
|
+
current: path.join(output, "features", "api", "automated"),
|
|
28
|
+
})(route);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const index =
|
|
32
|
+
(config: INestiaConfig) =>
|
|
33
|
+
async (output: string): Promise<void> => {
|
|
34
|
+
if (fs.existsSync(output)) return;
|
|
35
|
+
|
|
36
|
+
const location: string = path.join(
|
|
37
|
+
__dirname,
|
|
38
|
+
"..",
|
|
39
|
+
"..",
|
|
40
|
+
"assets",
|
|
41
|
+
"bundle",
|
|
42
|
+
"e2e",
|
|
43
|
+
"index.ts",
|
|
44
|
+
);
|
|
45
|
+
const content: string = await fs.promises.readFile(
|
|
46
|
+
location,
|
|
47
|
+
"utf8",
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
await fs.promises.writeFile(
|
|
51
|
+
output,
|
|
52
|
+
content.replace(
|
|
53
|
+
"${input}",
|
|
54
|
+
JSON.stringify(NestiaConfigUtil.input(config.input)),
|
|
55
|
+
),
|
|
56
|
+
"utf8",
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const mkdir = async (location: string): Promise<void> => {
|
|
62
|
+
try {
|
|
63
|
+
await fs.promises.mkdir(location);
|
|
64
|
+
} catch {}
|
|
65
|
+
};
|
|
@@ -1,44 +1,42 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import NodePath from "path";
|
|
3
|
-
import ts from "typescript";
|
|
4
3
|
|
|
5
4
|
import { INestiaConfig } from "../INestiaConfig";
|
|
6
5
|
import { IRoute } from "../structures/IRoute";
|
|
7
|
-
import {
|
|
6
|
+
import { SdkFileProgrammer } from "./internal/SdkFileProgrammer";
|
|
8
7
|
|
|
9
8
|
export namespace SdkGenerator {
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
await fs.promises.mkdir(config.output!);
|
|
18
|
-
} catch {}
|
|
9
|
+
export const generate =
|
|
10
|
+
(config: INestiaConfig) =>
|
|
11
|
+
async (routes: IRoute[]): Promise<void> => {
|
|
12
|
+
// PREPARE NEW DIRECTORIES
|
|
13
|
+
try {
|
|
14
|
+
await fs.promises.mkdir(config.output!);
|
|
15
|
+
} catch {}
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
// BUNDLING
|
|
18
|
+
const bundle: string[] = await fs.promises.readdir(BUNDLE_PATH);
|
|
19
|
+
for (const file of bundle) {
|
|
20
|
+
const current: string = `${BUNDLE_PATH}/${file}`;
|
|
21
|
+
const stats: fs.Stats = await fs.promises.stat(current);
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
23
|
+
if (stats.isFile() === true) {
|
|
24
|
+
const content: string = await fs.promises.readFile(
|
|
25
|
+
current,
|
|
26
|
+
"utf8",
|
|
27
|
+
);
|
|
28
|
+
if (fs.existsSync(`${config.output}/${file}`) === false)
|
|
29
|
+
await fs.promises.writeFile(
|
|
30
|
+
`${config.output}/${file}`,
|
|
31
|
+
content,
|
|
32
|
+
"utf8",
|
|
33
|
+
);
|
|
34
|
+
}
|
|
36
35
|
}
|
|
37
|
-
}
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
// FUNCTIONAL
|
|
38
|
+
await SdkFileProgrammer.generate(config)(routes);
|
|
39
|
+
};
|
|
42
40
|
|
|
43
41
|
export const BUNDLE_PATH = NodePath.join(
|
|
44
42
|
__dirname,
|
|
@@ -46,5 +44,6 @@ export namespace SdkGenerator {
|
|
|
46
44
|
"..",
|
|
47
45
|
"assets",
|
|
48
46
|
"bundle",
|
|
47
|
+
"api",
|
|
49
48
|
);
|
|
50
49
|
}
|
|
@@ -17,72 +17,74 @@ import { ISwaggerDocument } from "../structures/ISwaggerDocument";
|
|
|
17
17
|
import { MapUtil } from "../utils/MapUtil";
|
|
18
18
|
|
|
19
19
|
export namespace SwaggerGenerator {
|
|
20
|
-
export
|
|
21
|
-
checker: ts.TypeChecker
|
|
22
|
-
config: INestiaConfig.ISwaggerConfig
|
|
23
|
-
routeList: IRoute[]
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
20
|
+
export const generate =
|
|
21
|
+
(checker: ts.TypeChecker) =>
|
|
22
|
+
(config: INestiaConfig.ISwaggerConfig) =>
|
|
23
|
+
async (routeList: IRoute[]): Promise<void> => {
|
|
24
|
+
// PREPARE ASSETS
|
|
25
|
+
const parsed: NodePath.ParsedPath = NodePath.parse(config.output);
|
|
26
|
+
const location: string = !!parsed.ext
|
|
27
|
+
? NodePath.resolve(config.output)
|
|
28
|
+
: NodePath.join(
|
|
29
|
+
NodePath.resolve(config.output),
|
|
30
|
+
"swagger.json",
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const collection: MetadataCollection = new MetadataCollection({
|
|
34
|
+
replace: MetadataCollection.replace,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// CONSTRUCT SWAGGER DOCUMENTS
|
|
38
|
+
const tupleList: Array<ISchemaTuple> = [];
|
|
39
|
+
const swagger: ISwaggerDocument = await initialize(location);
|
|
40
|
+
const pathDict: Map<string, ISwaggerDocument.IPath> = new Map();
|
|
41
|
+
|
|
42
|
+
for (const route of routeList) {
|
|
43
|
+
if (route.tags.find((tag) => tag.name === "internal")) continue;
|
|
44
|
+
|
|
45
|
+
const path: ISwaggerDocument.IPath = MapUtil.take(
|
|
46
|
+
pathDict,
|
|
47
|
+
get_path(route.path, route.parameters),
|
|
48
|
+
() => ({}),
|
|
49
|
+
);
|
|
50
|
+
path[route.method.toLowerCase()] = generate_route(
|
|
51
|
+
checker,
|
|
52
|
+
collection,
|
|
53
|
+
tupleList,
|
|
54
|
+
route,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
swagger.paths = {};
|
|
58
|
+
for (const [path, routes] of pathDict) {
|
|
59
|
+
swagger.paths[path] = routes;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// FILL JSON-SCHEMAS
|
|
63
|
+
const application: IJsonApplication = ApplicationProgrammer.write({
|
|
64
|
+
purpose: "swagger",
|
|
65
|
+
})(tupleList.map(({ metadata }) => metadata));
|
|
66
|
+
swagger.components = {
|
|
67
|
+
...(swagger.components ?? {}),
|
|
68
|
+
schemas: application.components.schemas,
|
|
69
|
+
};
|
|
70
|
+
tupleList.forEach(({ schema }, index) => {
|
|
71
|
+
Object.assign(schema, application.schemas[index]!);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// CONFIGURE SECURITY
|
|
75
|
+
if (config.security) fill_security(config.security, swagger);
|
|
76
|
+
|
|
77
|
+
// ERASE IJsonComponents.IObject.$id
|
|
78
|
+
for (const obj of Object.values(swagger.components.schemas))
|
|
79
|
+
if (obj.$id) delete obj.$id;
|
|
80
|
+
|
|
81
|
+
// DO GENERATE
|
|
82
|
+
await fs.promises.writeFile(
|
|
83
|
+
location,
|
|
84
|
+
JSON.stringify(swagger, null, 2),
|
|
85
|
+
"utf8",
|
|
53
86
|
);
|
|
54
|
-
}
|
|
55
|
-
swagger.paths = {};
|
|
56
|
-
for (const [path, routes] of pathDict) {
|
|
57
|
-
swagger.paths[path] = routes;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// FILL JSON-SCHEMAS
|
|
61
|
-
const application: IJsonApplication = ApplicationProgrammer.write({
|
|
62
|
-
purpose: "swagger",
|
|
63
|
-
})(tupleList.map(({ metadata }) => metadata));
|
|
64
|
-
swagger.components = {
|
|
65
|
-
...(swagger.components ?? {}),
|
|
66
|
-
schemas: application.components.schemas,
|
|
67
87
|
};
|
|
68
|
-
tupleList.forEach(({ schema }, index) => {
|
|
69
|
-
Object.assign(schema, application.schemas[index]!);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// CONFIGURE SECURITY
|
|
73
|
-
if (config.security) fill_security(config.security, swagger);
|
|
74
|
-
|
|
75
|
-
// ERASE IJsonComponents.IObject.$id
|
|
76
|
-
for (const obj of Object.values(swagger.components.schemas))
|
|
77
|
-
if (obj.$id) delete obj.$id;
|
|
78
|
-
|
|
79
|
-
// DO GENERATE
|
|
80
|
-
await fs.promises.writeFile(
|
|
81
|
-
location,
|
|
82
|
-
JSON.stringify(swagger, null, 2),
|
|
83
|
-
"utf8",
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
88
|
|
|
87
89
|
/* ---------------------------------------------------------
|
|
88
90
|
INITIALIZERS
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
import { INestiaConfig } from "../../INestiaConfig";
|
|
5
|
+
import { IRoute } from "../../structures/IRoute";
|
|
6
|
+
import { ImportDictionary } from "../../utils/ImportDictionary";
|
|
7
|
+
|
|
8
|
+
export namespace E2eFileProgrammer {
|
|
9
|
+
export const generate =
|
|
10
|
+
(config: INestiaConfig) =>
|
|
11
|
+
(props: { api: string; current: string }) =>
|
|
12
|
+
async (route: IRoute): Promise<void> => {
|
|
13
|
+
const importDict: ImportDictionary = new ImportDictionary();
|
|
14
|
+
for (const tuple of route.imports)
|
|
15
|
+
for (const instance of tuple[1])
|
|
16
|
+
importDict.emplace(tuple[0], false, instance);
|
|
17
|
+
|
|
18
|
+
const uuid: boolean = route.parameters.some(
|
|
19
|
+
(p) => p.category === "param" && p.meta?.type === "uuid",
|
|
20
|
+
);
|
|
21
|
+
const content: string = [
|
|
22
|
+
config.primitive === false
|
|
23
|
+
? `import typia from "typia";`
|
|
24
|
+
: `import typia, { Primitive } from "typia";`,
|
|
25
|
+
"",
|
|
26
|
+
`import api from "./${path
|
|
27
|
+
.relative(props.current, props.api)
|
|
28
|
+
.split("\\")
|
|
29
|
+
.join("/")}";`,
|
|
30
|
+
...(importDict.empty()
|
|
31
|
+
? []
|
|
32
|
+
: [importDict.toScript(props.current)]),
|
|
33
|
+
"",
|
|
34
|
+
arrow(config)(route),
|
|
35
|
+
...(uuid ? ["", UUID] : []),
|
|
36
|
+
].join("\n");
|
|
37
|
+
|
|
38
|
+
await fs.promises.writeFile(
|
|
39
|
+
`${props.current}/${name(route)}.ts`,
|
|
40
|
+
content,
|
|
41
|
+
"utf8",
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const arrow =
|
|
46
|
+
(config: INestiaConfig) =>
|
|
47
|
+
(route: IRoute): string => {
|
|
48
|
+
const tab: number = route.output.name === "void" ? 2 : 3;
|
|
49
|
+
const output = [
|
|
50
|
+
`await ${accessor(route)}(`,
|
|
51
|
+
`${" ".repeat(tab * 4)}connection,`,
|
|
52
|
+
...route.parameters.map(parameter(config)(tab)),
|
|
53
|
+
`${" ".repeat((tab - 1) * 4)});`,
|
|
54
|
+
].join("\n");
|
|
55
|
+
return [
|
|
56
|
+
`export const ${name(route)} = async (`,
|
|
57
|
+
` connection: api.IConnection`,
|
|
58
|
+
`): Promise<void> => {`,
|
|
59
|
+
...(route.output.name === "void"
|
|
60
|
+
? [` ${output}`]
|
|
61
|
+
: [
|
|
62
|
+
` const output: ${primitive(config)(
|
|
63
|
+
route.output.name,
|
|
64
|
+
)} = `,
|
|
65
|
+
` ${output}`,
|
|
66
|
+
` typia.assert(output);`,
|
|
67
|
+
]),
|
|
68
|
+
`};`,
|
|
69
|
+
].join("\n");
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const parameter =
|
|
73
|
+
(config: INestiaConfig) =>
|
|
74
|
+
(tab: number) =>
|
|
75
|
+
(param: IRoute.IParameter): string => {
|
|
76
|
+
const middle: string =
|
|
77
|
+
param.category === "param" && param.meta?.type === "uuid"
|
|
78
|
+
? param.meta.nullable
|
|
79
|
+
? `Math.random() < .2 ? null : uuid()`
|
|
80
|
+
: `uuid()`
|
|
81
|
+
: `typia.random<${primitive(config)(param.type.name)}>()`;
|
|
82
|
+
return `${" ".repeat(4 * tab)}${middle},`;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const name = (route: IRoute): string =>
|
|
86
|
+
[
|
|
87
|
+
"test_api",
|
|
88
|
+
...route.path
|
|
89
|
+
.split("/")
|
|
90
|
+
.filter((str) => str.length && str[0] !== ":")
|
|
91
|
+
.map(normalize),
|
|
92
|
+
route.name,
|
|
93
|
+
].join("_");
|
|
94
|
+
|
|
95
|
+
const accessor = (route: IRoute): string =>
|
|
96
|
+
[
|
|
97
|
+
"api.functional",
|
|
98
|
+
...route.path
|
|
99
|
+
.split("/")
|
|
100
|
+
.filter((str) => str.length && str[0] !== ":")
|
|
101
|
+
.map(normalize),
|
|
102
|
+
route.name,
|
|
103
|
+
].join(".");
|
|
104
|
+
|
|
105
|
+
const normalize = (str: string) =>
|
|
106
|
+
str.split("-").join("_").split(".").join("_");
|
|
107
|
+
|
|
108
|
+
const primitive =
|
|
109
|
+
(config: INestiaConfig) =>
|
|
110
|
+
(name: string): string =>
|
|
111
|
+
config.primitive !== false ? `Primitive<${name}>` : name;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const UUID = `const uuid = (): string =>
|
|
115
|
+
"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
|
|
116
|
+
const r = (Math.random() * 16) | 0;
|
|
117
|
+
const v = c === "x" ? r : (r & 0x3) | 0x8;
|
|
118
|
+
return v.toString(16);
|
|
119
|
+
});`;
|