@nestia/sdk 0.2.0 → 1.0.1

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 (38) hide show
  1. package/assets/config/nestia.config.ts +79 -70
  2. package/lib/INestiaConfig.d.ts +18 -14
  3. package/lib/executable/sdk.js +16 -16
  4. package/lib/generates/FunctionGenerator.js +9 -9
  5. package/lib/generates/FunctionGenerator.js.map +1 -1
  6. package/lib/generates/SwaggerGenerator.js +9 -9
  7. package/package.json +3 -3
  8. package/src/INestiaConfig.ts +124 -120
  9. package/src/NestiaSdkApplication.ts +183 -183
  10. package/src/analyses/ControllerAnalyzer.ts +203 -203
  11. package/src/analyses/GenericAnalyzer.ts +53 -53
  12. package/src/analyses/ImportAnalyzer.ts +143 -143
  13. package/src/analyses/PathAnalyzer.ts +58 -58
  14. package/src/analyses/ReflectAnalyzer.ts +279 -279
  15. package/src/analyses/SourceFinder.ts +59 -59
  16. package/src/executable/internal/CommandParser.ts +15 -15
  17. package/src/executable/internal/NestiaConfigCompilerOptions.ts +18 -18
  18. package/src/executable/internal/NestiaSdkCommand.ts +174 -174
  19. package/src/executable/internal/NestiaSdkConfig.ts +35 -35
  20. package/src/executable/internal/nestia.config.getter.ts +12 -12
  21. package/src/executable/sdk.ts +74 -74
  22. package/src/generates/FileGenerator.ts +156 -156
  23. package/src/generates/FunctionGenerator.ts +284 -287
  24. package/src/generates/SdkGenerator.ts +50 -50
  25. package/src/generates/SwaggerGenerator.ts +393 -393
  26. package/src/index.ts +3 -3
  27. package/src/module.ts +2 -2
  28. package/src/structures/IController.ts +27 -27
  29. package/src/structures/IRoute.ts +29 -29
  30. package/src/structures/ISwagger.ts +55 -55
  31. package/src/structures/ITypeTuple.ts +6 -6
  32. package/src/structures/MethodType.ts +11 -11
  33. package/src/structures/ParamCategory.ts +1 -1
  34. package/src/structures/TypeEntry.ts +22 -22
  35. package/src/utils/ArrayUtil.ts +26 -26
  36. package/src/utils/ImportDictionary.ts +56 -56
  37. package/src/utils/MapUtil.ts +14 -14
  38. package/src/utils/StripEnums.ts +10 -10
@@ -1,287 +1,284 @@
1
- import { Vector } from "tstl/container/Vector";
2
- import { Pair } from "tstl/utility/Pair";
3
- import ts from "typescript";
4
- import { Escaper } from "typia/lib/utils/Escaper";
5
-
6
- import { INestiaConfig } from "../INestiaConfig";
7
- import { IRoute } from "../structures/IRoute";
8
-
9
- export namespace FunctionGenerator {
10
- export function generate(config: INestiaConfig, route: IRoute): string {
11
- const query: IRoute.IParameter | undefined = route.parameters.find(
12
- (param) => param.category === "query" && param.field === undefined,
13
- );
14
- const input: IRoute.IParameter | undefined = route.parameters.find(
15
- (param) => param.category === "body",
16
- );
17
-
18
- return [head, body, tail]
19
- .map((closure) => closure(route, query, input, config))
20
- .filter((str) => !!str)
21
- .join("\n");
22
- }
23
-
24
- /* ---------------------------------------------------------
25
- BODY
26
- --------------------------------------------------------- */
27
- function body(
28
- route: IRoute,
29
- query: IRoute.IParameter | undefined,
30
- input: IRoute.IParameter | undefined,
31
- config: INestiaConfig,
32
- ): string {
33
- // FETCH ARGUMENTS WITH REQUST BODY
34
- const parameters = filter_parameters(route, query);
35
- const fetchArguments: string[] = [
36
- "connection",
37
- `${route.name}.ENCRYPTED`,
38
- `${route.name}.METHOD`,
39
- `${route.name}.path(${parameters.map((p) => p.name).join(", ")})`,
40
- ];
41
- if (input !== undefined) {
42
- fetchArguments.push(input.name);
43
- if (config.json === true)
44
- fetchArguments.push(`${route.name}.stringify`);
45
- }
46
-
47
- const assertions: string =
48
- config.assert === true && route.parameters.length !== 0
49
- ? route.parameters
50
- .map(
51
- (param) =>
52
- ` typia.assert<typeof ${param.name}>(${param.name});`,
53
- )
54
- .join("\n") + "\n\n"
55
- : "";
56
-
57
- // RETURNS WITH FINALIZATION
58
- return (
59
- "{\n" +
60
- assertions +
61
- " return Fetcher.fetch\n" +
62
- " (\n" +
63
- fetchArguments.map((param) => ` ${param}`).join(",\n") +
64
- "\n" +
65
- " );\n" +
66
- "}"
67
- );
68
- }
69
-
70
- function filter_parameters(
71
- route: IRoute,
72
- query: IRoute.IParameter | undefined,
73
- ): IRoute.IParameter[] {
74
- const parameters: IRoute.IParameter[] = route.parameters.filter(
75
- (param) =>
76
- param.category === "param" ||
77
- (param.category === "query" && param.field !== undefined),
78
- );
79
- if (query) parameters.push(query);
80
- return parameters;
81
- }
82
-
83
- /* ---------------------------------------------------------
84
- HEAD & TAIL
85
- --------------------------------------------------------- */
86
- function head(
87
- route: IRoute,
88
- query: IRoute.IParameter | undefined,
89
- input: IRoute.IParameter | undefined,
90
- config: INestiaConfig,
91
- ): string {
92
- //----
93
- // CONSTRUCT COMMENT
94
- //----
95
- // MAIN DESCRIPTION
96
- const comments: string[] = route.comments
97
- .map((part) => `${part.kind === "linkText" ? " " : ""}${part.text}`)
98
- .map((str) => str.split("\r\n").join("\n"))
99
- .join("")
100
- .split("\n")
101
- .filter((str, i, array) => str !== "" || i !== array.length - 1);
102
- if (comments.length) comments.push("");
103
-
104
- // FILTER TAGS (VULNERABLE PARAMETERS WOULD BE REMOVED)
105
- const tagList: ts.JSDocTagInfo[] = route.tags.filter(
106
- (tag) => tag.text !== undefined,
107
- );
108
- if (tagList.length !== 0) {
109
- const index: number = tagList.findIndex((t) => t.name === "param");
110
- if (index !== -1) {
111
- const capsule: Vector<ts.JSDocTagInfo> = Vector.wrap(tagList);
112
- capsule.insert(capsule.nth(index), {
113
- name: "param",
114
- text: [
115
- {
116
- kind: "parameterName",
117
- text: "connection",
118
- },
119
- {
120
- kind: "space",
121
- text: " ",
122
- },
123
- {
124
- kind: "text",
125
- text: "connection Information of the remote HTTP(s) server with headers (+encryption password)",
126
- },
127
- ],
128
- });
129
- }
130
- comments.push(
131
- ...tagList.map(
132
- (tag) =>
133
- `@${tag.name} ${tag
134
- .text!.map((elem) => elem.text)
135
- .join("")}`,
136
- ),
137
- "",
138
- );
139
- }
140
-
141
- // COMPLETE THE COMMENT
142
- comments.push(
143
- `@controller ${route.symbol}`,
144
- `@path ${route.method} ${route.path}`,
145
- `@nestia Generated by Nestia - https://github.com/samchon/nestia`,
146
- );
147
-
148
- //----
149
- // FINALIZATION
150
- //----
151
- // REFORM PARAMETERS TEXT
152
- const parameters: string[] = [
153
- "connection: IConnection",
154
- ...route.parameters.map((param) => {
155
- const type: string =
156
- config.primitive !== false &&
157
- (param === query || param === input)
158
- ? `Primitive<${route.name}.${
159
- param === query ? "Query" : "Input"
160
- }>`
161
- : param.type.name;
162
- return `${param.name}: ${type}`;
163
- }),
164
- ];
165
-
166
- // OUTPUT TYPE
167
- const output: string =
168
- route.output.name === "void" ? "void" : `${route.name}.Output`;
169
-
170
- // RETURNS WITH CONSTRUCTION
171
- return (
172
- "" +
173
- "/**\n" +
174
- comments.map((str) => ` * ${str}`).join("\n") +
175
- "\n" +
176
- " */\n" +
177
- `export function ${route.name}\n` +
178
- ` (\n` +
179
- `${parameters.map((str) => ` ${str}`).join(",\n")}\n` +
180
- ` ): Promise<${output}>`
181
- );
182
- }
183
-
184
- function tail(
185
- route: IRoute,
186
- query: IRoute.IParameter | undefined,
187
- input: IRoute.IParameter | undefined,
188
- config: INestiaConfig,
189
- ): string | null {
190
- // LIST UP TYPES
191
- const types: Pair<string, string>[] = [];
192
- if (query !== undefined) types.push(new Pair("Query", query.type.name));
193
- if (input !== undefined) types.push(new Pair("Input", input.type.name));
194
- if (route.output.name !== "void")
195
- types.push(new Pair("Output", route.output.name));
196
-
197
- // PATH WITH PARAMETERS
198
- const parameters: IRoute.IParameter[] = filter_parameters(route, query);
199
- const path: string = compute_path(query, parameters, route.path);
200
-
201
- return (
202
- `export namespace ${route.name}\n` +
203
- "{\n" +
204
- (types.length !== 0
205
- ? types
206
- .map(
207
- (tuple) =>
208
- ` export type ${tuple.first} = ${
209
- config.primitive !== false
210
- ? `Primitive<${tuple.second}>`
211
- : tuple.second
212
- };`,
213
- )
214
- .join("\n") + "\n"
215
- : "") +
216
- "\n" +
217
- ` export const METHOD = "${route.method}" as const;\n` +
218
- ` export const PATH: string = "${route.path}";\n` +
219
- ` export const ENCRYPTED: Fetcher.IEncrypted = {\n` +
220
- ` request: ${input !== undefined && input.encrypted},\n` +
221
- ` response: ${route.encrypted},\n` +
222
- ` };\n` +
223
- "\n" +
224
- ` export function path(${parameters
225
- .map((param) => `${param.name}: ${param.type.name}`)
226
- .join(", ")}): string\n` +
227
- ` {\n` +
228
- ` return ${path};\n` +
229
- ` }\n` +
230
- (config.json === true &&
231
- (route.method === "POST" ||
232
- route.method === "PUT" ||
233
- route.method === "PATCH")
234
- ? ` export const stringify = (input: Input) => typia.stringify(input);\n`
235
- : "") +
236
- "}"
237
- );
238
- }
239
-
240
- function compute_path(
241
- query: IRoute.IParameter | undefined,
242
- parameters: IRoute.IParameter[],
243
- path: string,
244
- ): string {
245
- for (const param of parameters)
246
- if (param.category === "param")
247
- path = path.replace(
248
- `:${param.field}`,
249
- `\${encodeURIComponent(${param.name})}`,
250
- );
251
- const queryParams: IRoute.IParameter[] = parameters.filter(
252
- (param) => param.category === "query" && param.field !== undefined,
253
- );
254
- if (query === undefined && queryParams.length === 0)
255
- return `\`${path}\``;
256
-
257
- const wrapper = (str: string) =>
258
- `\`${path}?\${new URLSearchParams(${str}).toString()}\``;
259
- if (query !== undefined && queryParams.length === 0)
260
- return wrapper(`${query.name} as any`);
261
- else if (query === undefined)
262
- return wrapper(`
263
- {
264
- ${rest_query_parameters(queryParams)}
265
- } as any`);
266
-
267
- return wrapper(`
268
- {
269
- ...${query.name},
270
- ${rest_query_parameters(queryParams)},
271
- } as any`);
272
- }
273
-
274
- function rest_query_parameters(parameters: IRoute.IParameter[]): string {
275
- return parameters
276
- .map((param) =>
277
- param.name === param.field
278
- ? param.name
279
- : `${
280
- Escaper.variable(param.field!)
281
- ? param.field
282
- : JSON.stringify(param.field)
283
- }: ${param.name}`,
284
- )
285
- .join(`,\n${" ".repeat(12)}`);
286
- }
287
- }
1
+ import { Vector } from "tstl/container/Vector";
2
+ import { Pair } from "tstl/utility/Pair";
3
+ import ts from "typescript";
4
+ import { Escaper } from "typia/lib/utils/Escaper";
5
+
6
+ import { INestiaConfig } from "../INestiaConfig";
7
+ import { IRoute } from "../structures/IRoute";
8
+
9
+ export namespace FunctionGenerator {
10
+ export function generate(config: INestiaConfig, route: IRoute): string {
11
+ const query: IRoute.IParameter | undefined = route.parameters.find(
12
+ (param) => param.category === "query" && param.field === undefined,
13
+ );
14
+ const input: IRoute.IParameter | undefined = route.parameters.find(
15
+ (param) => param.category === "body",
16
+ );
17
+
18
+ return [head, body, tail]
19
+ .map((closure) => closure(route, query, input, config))
20
+ .filter((str) => !!str)
21
+ .join("\n");
22
+ }
23
+
24
+ /* ---------------------------------------------------------
25
+ BODY
26
+ --------------------------------------------------------- */
27
+ function body(
28
+ route: IRoute,
29
+ query: IRoute.IParameter | undefined,
30
+ input: IRoute.IParameter | undefined,
31
+ config: INestiaConfig,
32
+ ): string {
33
+ // FETCH ARGUMENTS WITH REQUST BODY
34
+ const parameters = filter_parameters(route, query);
35
+ const fetchArguments: string[] = [
36
+ "connection",
37
+ `${route.name}.ENCRYPTED`,
38
+ `${route.name}.METHOD`,
39
+ `${route.name}.path(${parameters.map((p) => p.name).join(", ")})`,
40
+ ];
41
+ if (input !== undefined) {
42
+ fetchArguments.push(input.name);
43
+ if (config.json === true)
44
+ fetchArguments.push(`${route.name}.stringify`);
45
+ }
46
+
47
+ const assertions: string =
48
+ config.assert === true && route.parameters.length !== 0
49
+ ? route.parameters
50
+ .map((param) => ` typia.assert(${param.name});`)
51
+ .join("\n") + "\n\n"
52
+ : "";
53
+
54
+ // RETURNS WITH FINALIZATION
55
+ return (
56
+ "{\n" +
57
+ assertions +
58
+ " return Fetcher.fetch\n" +
59
+ " (\n" +
60
+ fetchArguments.map((param) => ` ${param}`).join(",\n") +
61
+ "\n" +
62
+ " );\n" +
63
+ "}"
64
+ );
65
+ }
66
+
67
+ function filter_parameters(
68
+ route: IRoute,
69
+ query: IRoute.IParameter | undefined,
70
+ ): IRoute.IParameter[] {
71
+ const parameters: IRoute.IParameter[] = route.parameters.filter(
72
+ (param) =>
73
+ param.category === "param" ||
74
+ (param.category === "query" && param.field !== undefined),
75
+ );
76
+ if (query) parameters.push(query);
77
+ return parameters;
78
+ }
79
+
80
+ /* ---------------------------------------------------------
81
+ HEAD & TAIL
82
+ --------------------------------------------------------- */
83
+ function head(
84
+ route: IRoute,
85
+ query: IRoute.IParameter | undefined,
86
+ input: IRoute.IParameter | undefined,
87
+ config: INestiaConfig,
88
+ ): string {
89
+ //----
90
+ // CONSTRUCT COMMENT
91
+ //----
92
+ // MAIN DESCRIPTION
93
+ const comments: string[] = route.comments
94
+ .map((part) => `${part.kind === "linkText" ? " " : ""}${part.text}`)
95
+ .map((str) => str.split("\r\n").join("\n"))
96
+ .join("")
97
+ .split("\n")
98
+ .filter((str, i, array) => str !== "" || i !== array.length - 1);
99
+ if (comments.length) comments.push("");
100
+
101
+ // FILTER TAGS (VULNERABLE PARAMETERS WOULD BE REMOVED)
102
+ const tagList: ts.JSDocTagInfo[] = route.tags.filter(
103
+ (tag) => tag.text !== undefined,
104
+ );
105
+ if (tagList.length !== 0) {
106
+ const index: number = tagList.findIndex((t) => t.name === "param");
107
+ if (index !== -1) {
108
+ const capsule: Vector<ts.JSDocTagInfo> = Vector.wrap(tagList);
109
+ capsule.insert(capsule.nth(index), {
110
+ name: "param",
111
+ text: [
112
+ {
113
+ kind: "parameterName",
114
+ text: "connection",
115
+ },
116
+ {
117
+ kind: "space",
118
+ text: " ",
119
+ },
120
+ {
121
+ kind: "text",
122
+ text: "connection Information of the remote HTTP(s) server with headers (+encryption password)",
123
+ },
124
+ ],
125
+ });
126
+ }
127
+ comments.push(
128
+ ...tagList.map(
129
+ (tag) =>
130
+ `@${tag.name} ${tag
131
+ .text!.map((elem) => elem.text)
132
+ .join("")}`,
133
+ ),
134
+ "",
135
+ );
136
+ }
137
+
138
+ // COMPLETE THE COMMENT
139
+ comments.push(
140
+ `@controller ${route.symbol}`,
141
+ `@path ${route.method} ${route.path}`,
142
+ `@nestia Generated by Nestia - https://github.com/samchon/nestia`,
143
+ );
144
+
145
+ //----
146
+ // FINALIZATION
147
+ //----
148
+ // REFORM PARAMETERS TEXT
149
+ const parameters: string[] = [
150
+ "connection: IConnection",
151
+ ...route.parameters.map((param) => {
152
+ const type: string =
153
+ config.primitive !== false &&
154
+ (param === query || param === input)
155
+ ? `Primitive<${route.name}.${
156
+ param === query ? "Query" : "Input"
157
+ }>`
158
+ : param.type.name;
159
+ return `${param.name}: ${type}`;
160
+ }),
161
+ ];
162
+
163
+ // OUTPUT TYPE
164
+ const output: string =
165
+ route.output.name === "void" ? "void" : `${route.name}.Output`;
166
+
167
+ // RETURNS WITH CONSTRUCTION
168
+ return (
169
+ "" +
170
+ "/**\n" +
171
+ comments.map((str) => ` * ${str}`).join("\n") +
172
+ "\n" +
173
+ " */\n" +
174
+ `export function ${route.name}\n` +
175
+ ` (\n` +
176
+ `${parameters.map((str) => ` ${str}`).join(",\n")}\n` +
177
+ ` ): Promise<${output}>`
178
+ );
179
+ }
180
+
181
+ function tail(
182
+ route: IRoute,
183
+ query: IRoute.IParameter | undefined,
184
+ input: IRoute.IParameter | undefined,
185
+ config: INestiaConfig,
186
+ ): string | null {
187
+ // LIST UP TYPES
188
+ const types: Pair<string, string>[] = [];
189
+ if (query !== undefined) types.push(new Pair("Query", query.type.name));
190
+ if (input !== undefined) types.push(new Pair("Input", input.type.name));
191
+ if (route.output.name !== "void")
192
+ types.push(new Pair("Output", route.output.name));
193
+
194
+ // PATH WITH PARAMETERS
195
+ const parameters: IRoute.IParameter[] = filter_parameters(route, query);
196
+ const path: string = compute_path(query, parameters, route.path);
197
+
198
+ return (
199
+ `export namespace ${route.name}\n` +
200
+ "{\n" +
201
+ (types.length !== 0
202
+ ? types
203
+ .map(
204
+ (tuple) =>
205
+ ` export type ${tuple.first} = ${
206
+ config.primitive !== false
207
+ ? `Primitive<${tuple.second}>`
208
+ : tuple.second
209
+ };`,
210
+ )
211
+ .join("\n") + "\n"
212
+ : "") +
213
+ "\n" +
214
+ ` export const METHOD = "${route.method}" as const;\n` +
215
+ ` export const PATH: string = "${route.path}";\n` +
216
+ ` export const ENCRYPTED: Fetcher.IEncrypted = {\n` +
217
+ ` request: ${input !== undefined && input.encrypted},\n` +
218
+ ` response: ${route.encrypted},\n` +
219
+ ` };\n` +
220
+ "\n" +
221
+ ` export function path(${parameters
222
+ .map((param) => `${param.name}: ${param.type.name}`)
223
+ .join(", ")}): string\n` +
224
+ ` {\n` +
225
+ ` return ${path};\n` +
226
+ ` }\n` +
227
+ (config.json === true &&
228
+ (route.method === "POST" ||
229
+ route.method === "PUT" ||
230
+ route.method === "PATCH")
231
+ ? ` export const stringify = typia.createAssertStringify<Input>();\n`
232
+ : "") +
233
+ "}"
234
+ );
235
+ }
236
+
237
+ function compute_path(
238
+ query: IRoute.IParameter | undefined,
239
+ parameters: IRoute.IParameter[],
240
+ path: string,
241
+ ): string {
242
+ for (const param of parameters)
243
+ if (param.category === "param")
244
+ path = path.replace(
245
+ `:${param.field}`,
246
+ `\${encodeURIComponent(${param.name})}`,
247
+ );
248
+ const queryParams: IRoute.IParameter[] = parameters.filter(
249
+ (param) => param.category === "query" && param.field !== undefined,
250
+ );
251
+ if (query === undefined && queryParams.length === 0)
252
+ return `\`${path}\``;
253
+
254
+ const wrapper = (str: string) =>
255
+ `\`${path}?\${new URLSearchParams(${str}).toString()}\``;
256
+ if (query !== undefined && queryParams.length === 0)
257
+ return wrapper(`${query.name} as any`);
258
+ else if (query === undefined)
259
+ return wrapper(`
260
+ {
261
+ ${rest_query_parameters(queryParams)}
262
+ } as any`);
263
+
264
+ return wrapper(`
265
+ {
266
+ ...${query.name},
267
+ ${rest_query_parameters(queryParams)},
268
+ } as any`);
269
+ }
270
+
271
+ function rest_query_parameters(parameters: IRoute.IParameter[]): string {
272
+ return parameters
273
+ .map((param) =>
274
+ param.name === param.field
275
+ ? param.name
276
+ : `${
277
+ Escaper.variable(param.field!)
278
+ ? param.field
279
+ : JSON.stringify(param.field)
280
+ }: ${param.name}`,
281
+ )
282
+ .join(`,\n${" ".repeat(12)}`);
283
+ }
284
+ }