@nestia/sdk 11.0.0-dev.20260316 → 11.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.
- package/LICENSE +21 -21
- package/README.md +93 -93
- package/assets/bundle/api/HttpError.ts +1 -1
- package/assets/bundle/api/IConnection.ts +1 -1
- package/assets/bundle/api/Primitive.ts +1 -1
- package/assets/bundle/api/Resolved.ts +1 -1
- package/assets/bundle/api/index.ts +4 -4
- package/assets/bundle/api/module.ts +6 -6
- package/assets/bundle/distribute/README.md +37 -37
- package/assets/bundle/distribute/package.json +28 -28
- package/assets/bundle/distribute/tsconfig.json +109 -109
- package/assets/bundle/e2e/index.ts +42 -42
- package/assets/config/nestia.config.ts +97 -97
- package/lib/executable/internal/NestiaConfigLoader.js +5 -5
- package/lib/executable/internal/NestiaConfigLoader.js.map +1 -1
- package/package.json +8 -8
- package/src/INestiaConfig.ts +267 -267
- package/src/NestiaSdkApplication.ts +307 -307
- package/src/NestiaSwaggerComposer.ts +143 -143
- package/src/analyses/AccessorAnalyzer.ts +67 -67
- package/src/analyses/DtoAnalyzer.ts +260 -260
- package/src/analyses/ImportAnalyzer.ts +126 -126
- package/src/analyses/ReflectHttpOperationAnalyzer.ts +183 -183
- package/src/analyses/ReflectHttpOperationExceptionAnalyzer.ts +72 -72
- package/src/analyses/ReflectHttpOperationParameterAnalyzer.ts +350 -350
- package/src/analyses/ReflectHttpOperationResponseAnalyzer.ts +126 -126
- package/src/analyses/TypedHttpRouteAnalyzer.ts +208 -208
- package/src/executable/internal/NestiaConfigLoader.ts +85 -85
- package/src/executable/internal/NestiaSdkCommand.ts +107 -107
- package/src/generates/SwaggerGenerator.ts +291 -291
- package/src/generates/internal/E2eFileProgrammer.ts +196 -196
- package/src/generates/internal/FilePrinter.ts +64 -64
- package/src/generates/internal/ImportDictionary.ts +192 -192
- package/src/generates/internal/SdkAliasCollection.ts +260 -260
- package/src/generates/internal/SdkFileProgrammer.ts +110 -110
- package/src/generates/internal/SdkHttpCloneProgrammer.ts +126 -126
- package/src/generates/internal/SdkHttpCloneReferencer.ts +77 -77
- package/src/generates/internal/SdkHttpFunctionProgrammer.ts +278 -278
- package/src/generates/internal/SdkHttpNamespaceProgrammer.ts +502 -502
- package/src/generates/internal/SdkHttpRouteProgrammer.ts +109 -109
- package/src/generates/internal/SdkHttpSimulationProgrammer.ts +312 -312
- package/src/generates/internal/SdkImportWizard.ts +62 -62
- package/src/generates/internal/SdkTypeProgrammer.ts +388 -388
- package/src/generates/internal/SdkTypeTagProgrammer.ts +114 -114
- package/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts +379 -379
- package/src/generates/internal/SdkWebSocketRouteProgrammer.ts +302 -302
- package/src/generates/internal/SwaggerOperationComposer.ts +119 -119
- package/src/generates/internal/SwaggerOperationParameterComposer.ts +161 -161
- package/src/generates/internal/SwaggerOperationResponseComposer.ts +110 -110
- package/src/module.ts +4 -4
- package/src/structures/IReflectHttpOperationException.ts +18 -18
- package/src/structures/IReflectHttpOperationParameter.ts +79 -79
- package/src/structures/IReflectHttpOperationSuccess.ts +21 -21
- package/src/structures/ITypedApplication.ts +11 -11
- package/src/structures/ITypedHttpRouteException.ts +15 -15
- package/src/structures/ITypedHttpRouteParameter.ts +41 -41
- package/src/structures/ITypedHttpRouteSuccess.ts +22 -22
- package/src/transformers/IOperationMetadata.ts +46 -46
- package/src/transformers/ISdkOperationTransformerContext.ts +8 -8
- package/src/transformers/SdkOperationProgrammer.ts +240 -240
- package/src/transformers/SdkOperationTransformer.ts +248 -248
- package/src/transformers/TextPlainValidator.ts +17 -17
- package/src/utils/MetadataUtil.ts +26 -26
- package/src/validators/HttpHeadersValidator.ts +40 -40
- package/src/validators/HttpQueryValidator.ts +40 -40
|
@@ -1,143 +1,143 @@
|
|
|
1
|
-
import { INestApplication } from "@nestjs/common";
|
|
2
|
-
import { IMetadataDictionary } from "@typia/core";
|
|
3
|
-
import { OpenApiV3, OpenApiV3_1, SwaggerV2 } from "@typia/interface";
|
|
4
|
-
import { OpenApiConverter } from "@typia/utils";
|
|
5
|
-
import path from "path";
|
|
6
|
-
import { TreeMap } from "tstl";
|
|
7
|
-
import { OpenApi } from "typia";
|
|
8
|
-
|
|
9
|
-
import { INestiaConfig } from "./INestiaConfig";
|
|
10
|
-
import { AccessorAnalyzer } from "./analyses/AccessorAnalyzer";
|
|
11
|
-
import { ConfigAnalyzer } from "./analyses/ConfigAnalyzer";
|
|
12
|
-
import { PathAnalyzer } from "./analyses/PathAnalyzer";
|
|
13
|
-
import { ReflectControllerAnalyzer } from "./analyses/ReflectControllerAnalyzer";
|
|
14
|
-
import { TypedHttpRouteAnalyzer } from "./analyses/TypedHttpRouteAnalyzer";
|
|
15
|
-
import { SwaggerGenerator } from "./generates/SwaggerGenerator";
|
|
16
|
-
import { INestiaProject } from "./structures/INestiaProject";
|
|
17
|
-
import { INestiaSdkInput } from "./structures/INestiaSdkInput";
|
|
18
|
-
import { IReflectController } from "./structures/IReflectController";
|
|
19
|
-
import { IReflectOperationError } from "./structures/IReflectOperationError";
|
|
20
|
-
import { ITypedHttpRoute } from "./structures/ITypedHttpRoute";
|
|
21
|
-
import { IOperationMetadata } from "./transformers/IOperationMetadata";
|
|
22
|
-
import { VersioningStrategy } from "./utils/VersioningStrategy";
|
|
23
|
-
|
|
24
|
-
export namespace NestiaSwaggerComposer {
|
|
25
|
-
export const document = async (
|
|
26
|
-
app: INestApplication,
|
|
27
|
-
config: Omit<INestiaConfig.ISwaggerConfig, "output">,
|
|
28
|
-
): Promise<
|
|
29
|
-
| OpenApi.IDocument
|
|
30
|
-
| OpenApiV3_1.IDocument
|
|
31
|
-
| OpenApiV3.IDocument
|
|
32
|
-
| SwaggerV2.IDocument
|
|
33
|
-
> => {
|
|
34
|
-
const input: INestiaSdkInput = await ConfigAnalyzer.application(app);
|
|
35
|
-
const document: OpenApi.IDocument = await SwaggerGenerator.compose({
|
|
36
|
-
config,
|
|
37
|
-
routes: analyze(input),
|
|
38
|
-
document: await SwaggerGenerator.initialize(config),
|
|
39
|
-
});
|
|
40
|
-
return (config.openapi ?? "3.2") === "3.2"
|
|
41
|
-
? document
|
|
42
|
-
: OpenApiConverter.downgradeDocument(document, config.openapi as "2.0");
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const analyze = (input: INestiaSdkInput): ITypedHttpRoute[] => {
|
|
46
|
-
// GET REFLECT CONTROLLERS
|
|
47
|
-
const unique: WeakSet<any> = new WeakSet();
|
|
48
|
-
const project: Omit<INestiaProject, "config"> = {
|
|
49
|
-
input,
|
|
50
|
-
checker: null!,
|
|
51
|
-
errors: [],
|
|
52
|
-
warnings: [],
|
|
53
|
-
};
|
|
54
|
-
const controllers: IReflectController[] = project.input.controllers
|
|
55
|
-
.map((c) =>
|
|
56
|
-
ReflectControllerAnalyzer.analyze({ project, controller: c, unique }),
|
|
57
|
-
)
|
|
58
|
-
.filter((c): c is IReflectController => c !== null);
|
|
59
|
-
if (project.errors.length)
|
|
60
|
-
throw report({ type: "error", errors: project.errors });
|
|
61
|
-
|
|
62
|
-
// METADATA COMPONENTS
|
|
63
|
-
const collection: IMetadataDictionary =
|
|
64
|
-
TypedHttpRouteAnalyzer.dictionary(controllers);
|
|
65
|
-
|
|
66
|
-
// CONVERT TO TYPED OPERATIONS
|
|
67
|
-
const globalPrefix: string = project.input.globalPrefix?.prefix ?? "";
|
|
68
|
-
const routes: ITypedHttpRoute[] = [];
|
|
69
|
-
for (const c of controllers)
|
|
70
|
-
for (const o of c.operations) {
|
|
71
|
-
const pathList: Set<string> = new Set();
|
|
72
|
-
const versions: string[] = VersioningStrategy.merge(project)([
|
|
73
|
-
...(c.versions ?? []),
|
|
74
|
-
...(o.versions ?? []),
|
|
75
|
-
]);
|
|
76
|
-
for (const v of versions)
|
|
77
|
-
for (const prefix of wrapPaths(c.prefixes))
|
|
78
|
-
for (const cPath of wrapPaths(c.paths))
|
|
79
|
-
for (const filePath of wrapPaths(o.paths))
|
|
80
|
-
pathList.add(
|
|
81
|
-
PathAnalyzer.join(globalPrefix, v, prefix, cPath, filePath),
|
|
82
|
-
);
|
|
83
|
-
if (o.protocol === "http")
|
|
84
|
-
routes.push(
|
|
85
|
-
...TypedHttpRouteAnalyzer.analyze({
|
|
86
|
-
controller: c,
|
|
87
|
-
errors: project.errors,
|
|
88
|
-
dictionary: collection,
|
|
89
|
-
operation: o,
|
|
90
|
-
paths: Array.from(pathList),
|
|
91
|
-
}),
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
AccessorAnalyzer.analyze(routes);
|
|
95
|
-
return routes;
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const report = (props: {
|
|
100
|
-
type: "error" | "warning";
|
|
101
|
-
errors: IReflectOperationError[];
|
|
102
|
-
}): void => {
|
|
103
|
-
const map: TreeMap<
|
|
104
|
-
IReflectOperationError.Key,
|
|
105
|
-
Array<string | IOperationMetadata.IError>
|
|
106
|
-
> = new TreeMap();
|
|
107
|
-
for (const e of props.errors)
|
|
108
|
-
map.take(new IReflectOperationError.Key(e), () => []).push(...e.contents);
|
|
109
|
-
|
|
110
|
-
const messages: string[] = [];
|
|
111
|
-
for (const {
|
|
112
|
-
first: { error },
|
|
113
|
-
second: contents,
|
|
114
|
-
} of map) {
|
|
115
|
-
if (error.contents.length === 0) continue;
|
|
116
|
-
const location: string = path.relative(process.cwd(), error.file);
|
|
117
|
-
messages.push(
|
|
118
|
-
[
|
|
119
|
-
`${location} - `,
|
|
120
|
-
error.class,
|
|
121
|
-
...(error.function !== null ? [`.${error.function}()`] : [""]),
|
|
122
|
-
...(error.from !== null ? [` from ${error.from}`] : [""]),
|
|
123
|
-
":\n",
|
|
124
|
-
contents
|
|
125
|
-
.map((c) => {
|
|
126
|
-
if (typeof c === "string") return ` - ${c}`;
|
|
127
|
-
else
|
|
128
|
-
return [
|
|
129
|
-
c.accessor
|
|
130
|
-
? ` - ${c.name}: `
|
|
131
|
-
: ` - ${c.name} (${c.accessor}): `,
|
|
132
|
-
...c.messages.map((msg) => ` - ${msg}`),
|
|
133
|
-
].join("\n");
|
|
134
|
-
})
|
|
135
|
-
.join("\n"),
|
|
136
|
-
].join(""),
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
throw new Error(`Error on NestiaSwaggerComposer.compose():\n${messages}`);
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
const wrapPaths = (paths: string[]): string[] =>
|
|
143
|
-
paths.length === 0 ? [""] : paths;
|
|
1
|
+
import { INestApplication } from "@nestjs/common";
|
|
2
|
+
import { IMetadataDictionary } from "@typia/core";
|
|
3
|
+
import { OpenApiV3, OpenApiV3_1, SwaggerV2 } from "@typia/interface";
|
|
4
|
+
import { OpenApiConverter } from "@typia/utils";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { TreeMap } from "tstl";
|
|
7
|
+
import { OpenApi } from "typia";
|
|
8
|
+
|
|
9
|
+
import { INestiaConfig } from "./INestiaConfig";
|
|
10
|
+
import { AccessorAnalyzer } from "./analyses/AccessorAnalyzer";
|
|
11
|
+
import { ConfigAnalyzer } from "./analyses/ConfigAnalyzer";
|
|
12
|
+
import { PathAnalyzer } from "./analyses/PathAnalyzer";
|
|
13
|
+
import { ReflectControllerAnalyzer } from "./analyses/ReflectControllerAnalyzer";
|
|
14
|
+
import { TypedHttpRouteAnalyzer } from "./analyses/TypedHttpRouteAnalyzer";
|
|
15
|
+
import { SwaggerGenerator } from "./generates/SwaggerGenerator";
|
|
16
|
+
import { INestiaProject } from "./structures/INestiaProject";
|
|
17
|
+
import { INestiaSdkInput } from "./structures/INestiaSdkInput";
|
|
18
|
+
import { IReflectController } from "./structures/IReflectController";
|
|
19
|
+
import { IReflectOperationError } from "./structures/IReflectOperationError";
|
|
20
|
+
import { ITypedHttpRoute } from "./structures/ITypedHttpRoute";
|
|
21
|
+
import { IOperationMetadata } from "./transformers/IOperationMetadata";
|
|
22
|
+
import { VersioningStrategy } from "./utils/VersioningStrategy";
|
|
23
|
+
|
|
24
|
+
export namespace NestiaSwaggerComposer {
|
|
25
|
+
export const document = async (
|
|
26
|
+
app: INestApplication,
|
|
27
|
+
config: Omit<INestiaConfig.ISwaggerConfig, "output">,
|
|
28
|
+
): Promise<
|
|
29
|
+
| OpenApi.IDocument
|
|
30
|
+
| OpenApiV3_1.IDocument
|
|
31
|
+
| OpenApiV3.IDocument
|
|
32
|
+
| SwaggerV2.IDocument
|
|
33
|
+
> => {
|
|
34
|
+
const input: INestiaSdkInput = await ConfigAnalyzer.application(app);
|
|
35
|
+
const document: OpenApi.IDocument = await SwaggerGenerator.compose({
|
|
36
|
+
config,
|
|
37
|
+
routes: analyze(input),
|
|
38
|
+
document: await SwaggerGenerator.initialize(config),
|
|
39
|
+
});
|
|
40
|
+
return (config.openapi ?? "3.2") === "3.2"
|
|
41
|
+
? document
|
|
42
|
+
: OpenApiConverter.downgradeDocument(document, config.openapi as "2.0");
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const analyze = (input: INestiaSdkInput): ITypedHttpRoute[] => {
|
|
46
|
+
// GET REFLECT CONTROLLERS
|
|
47
|
+
const unique: WeakSet<any> = new WeakSet();
|
|
48
|
+
const project: Omit<INestiaProject, "config"> = {
|
|
49
|
+
input,
|
|
50
|
+
checker: null!,
|
|
51
|
+
errors: [],
|
|
52
|
+
warnings: [],
|
|
53
|
+
};
|
|
54
|
+
const controllers: IReflectController[] = project.input.controllers
|
|
55
|
+
.map((c) =>
|
|
56
|
+
ReflectControllerAnalyzer.analyze({ project, controller: c, unique }),
|
|
57
|
+
)
|
|
58
|
+
.filter((c): c is IReflectController => c !== null);
|
|
59
|
+
if (project.errors.length)
|
|
60
|
+
throw report({ type: "error", errors: project.errors });
|
|
61
|
+
|
|
62
|
+
// METADATA COMPONENTS
|
|
63
|
+
const collection: IMetadataDictionary =
|
|
64
|
+
TypedHttpRouteAnalyzer.dictionary(controllers);
|
|
65
|
+
|
|
66
|
+
// CONVERT TO TYPED OPERATIONS
|
|
67
|
+
const globalPrefix: string = project.input.globalPrefix?.prefix ?? "";
|
|
68
|
+
const routes: ITypedHttpRoute[] = [];
|
|
69
|
+
for (const c of controllers)
|
|
70
|
+
for (const o of c.operations) {
|
|
71
|
+
const pathList: Set<string> = new Set();
|
|
72
|
+
const versions: string[] = VersioningStrategy.merge(project)([
|
|
73
|
+
...(c.versions ?? []),
|
|
74
|
+
...(o.versions ?? []),
|
|
75
|
+
]);
|
|
76
|
+
for (const v of versions)
|
|
77
|
+
for (const prefix of wrapPaths(c.prefixes))
|
|
78
|
+
for (const cPath of wrapPaths(c.paths))
|
|
79
|
+
for (const filePath of wrapPaths(o.paths))
|
|
80
|
+
pathList.add(
|
|
81
|
+
PathAnalyzer.join(globalPrefix, v, prefix, cPath, filePath),
|
|
82
|
+
);
|
|
83
|
+
if (o.protocol === "http")
|
|
84
|
+
routes.push(
|
|
85
|
+
...TypedHttpRouteAnalyzer.analyze({
|
|
86
|
+
controller: c,
|
|
87
|
+
errors: project.errors,
|
|
88
|
+
dictionary: collection,
|
|
89
|
+
operation: o,
|
|
90
|
+
paths: Array.from(pathList),
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
AccessorAnalyzer.analyze(routes);
|
|
95
|
+
return routes;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const report = (props: {
|
|
100
|
+
type: "error" | "warning";
|
|
101
|
+
errors: IReflectOperationError[];
|
|
102
|
+
}): void => {
|
|
103
|
+
const map: TreeMap<
|
|
104
|
+
IReflectOperationError.Key,
|
|
105
|
+
Array<string | IOperationMetadata.IError>
|
|
106
|
+
> = new TreeMap();
|
|
107
|
+
for (const e of props.errors)
|
|
108
|
+
map.take(new IReflectOperationError.Key(e), () => []).push(...e.contents);
|
|
109
|
+
|
|
110
|
+
const messages: string[] = [];
|
|
111
|
+
for (const {
|
|
112
|
+
first: { error },
|
|
113
|
+
second: contents,
|
|
114
|
+
} of map) {
|
|
115
|
+
if (error.contents.length === 0) continue;
|
|
116
|
+
const location: string = path.relative(process.cwd(), error.file);
|
|
117
|
+
messages.push(
|
|
118
|
+
[
|
|
119
|
+
`${location} - `,
|
|
120
|
+
error.class,
|
|
121
|
+
...(error.function !== null ? [`.${error.function}()`] : [""]),
|
|
122
|
+
...(error.from !== null ? [` from ${error.from}`] : [""]),
|
|
123
|
+
":\n",
|
|
124
|
+
contents
|
|
125
|
+
.map((c) => {
|
|
126
|
+
if (typeof c === "string") return ` - ${c}`;
|
|
127
|
+
else
|
|
128
|
+
return [
|
|
129
|
+
c.accessor
|
|
130
|
+
? ` - ${c.name}: `
|
|
131
|
+
: ` - ${c.name} (${c.accessor}): `,
|
|
132
|
+
...c.messages.map((msg) => ` - ${msg}`),
|
|
133
|
+
].join("\n");
|
|
134
|
+
})
|
|
135
|
+
.join("\n"),
|
|
136
|
+
].join(""),
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
throw new Error(`Error on NestiaSwaggerComposer.compose():\n${messages}`);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const wrapPaths = (paths: string[]): string[] =>
|
|
143
|
+
paths.length === 0 ? [""] : paths;
|
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
import { NamingConvention } from "@typia/utils";
|
|
2
|
-
|
|
3
|
-
import { ITypedHttpRoute } from "../structures/ITypedHttpRoute";
|
|
4
|
-
import { ITypedWebSocketRoute } from "../structures/ITypedWebSocketRoute";
|
|
5
|
-
|
|
6
|
-
export namespace AccessorAnalyzer {
|
|
7
|
-
export const analyze = (
|
|
8
|
-
routes: Array<ITypedHttpRoute | ITypedWebSocketRoute>,
|
|
9
|
-
) => {
|
|
10
|
-
shrink(routes);
|
|
11
|
-
variable(routes);
|
|
12
|
-
shrink(routes);
|
|
13
|
-
for (const r of routes) r.name = r.accessor.at(-1) ?? r.name;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const prepare = (
|
|
17
|
-
routeList: Array<ITypedHttpRoute | ITypedWebSocketRoute>,
|
|
18
|
-
): Map<string, number> => {
|
|
19
|
-
const dict: Map<string, number> = new Map();
|
|
20
|
-
for (const route of routeList)
|
|
21
|
-
route.accessor.forEach((_a, i) => {
|
|
22
|
-
const key: string = route.accessor.slice(0, i + 1).join(".");
|
|
23
|
-
dict.set(key, (dict.get(key) ?? 0) + 1);
|
|
24
|
-
});
|
|
25
|
-
return dict;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const variable = (
|
|
29
|
-
routeList: Array<ITypedHttpRoute | ITypedWebSocketRoute>,
|
|
30
|
-
) => {
|
|
31
|
-
const dict: Map<string, number> = prepare(routeList);
|
|
32
|
-
for (const route of routeList) {
|
|
33
|
-
const emended: string[] = route.accessor.slice();
|
|
34
|
-
route.accessor.forEach((accessor, i) => {
|
|
35
|
-
if (NamingConvention.variable(accessor)) return;
|
|
36
|
-
while (true) {
|
|
37
|
-
accessor = "_" + accessor;
|
|
38
|
-
const partial: string = [
|
|
39
|
-
...route.accessor.slice(0, i),
|
|
40
|
-
accessor,
|
|
41
|
-
].join(".");
|
|
42
|
-
if (dict.has(partial) === false) {
|
|
43
|
-
emended[i] = accessor;
|
|
44
|
-
break;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
route.accessor.splice(0, route.accessor.length, ...emended);
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const shrink = (routeList: Array<ITypedHttpRoute | ITypedWebSocketRoute>) => {
|
|
53
|
-
const dict: Map<string, number> = prepare(routeList);
|
|
54
|
-
for (const route of routeList) {
|
|
55
|
-
if (
|
|
56
|
-
route.accessor.length < 2 ||
|
|
57
|
-
route.accessor.at(-1) !== route.accessor.at(-2)
|
|
58
|
-
)
|
|
59
|
-
continue;
|
|
60
|
-
|
|
61
|
-
const cut: string[] = route.accessor.slice(0, -1);
|
|
62
|
-
if ((dict.get(cut.join(".")) ?? 0) > 1) continue;
|
|
63
|
-
|
|
64
|
-
route.accessor = cut;
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
}
|
|
1
|
+
import { NamingConvention } from "@typia/utils";
|
|
2
|
+
|
|
3
|
+
import { ITypedHttpRoute } from "../structures/ITypedHttpRoute";
|
|
4
|
+
import { ITypedWebSocketRoute } from "../structures/ITypedWebSocketRoute";
|
|
5
|
+
|
|
6
|
+
export namespace AccessorAnalyzer {
|
|
7
|
+
export const analyze = (
|
|
8
|
+
routes: Array<ITypedHttpRoute | ITypedWebSocketRoute>,
|
|
9
|
+
) => {
|
|
10
|
+
shrink(routes);
|
|
11
|
+
variable(routes);
|
|
12
|
+
shrink(routes);
|
|
13
|
+
for (const r of routes) r.name = r.accessor.at(-1) ?? r.name;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const prepare = (
|
|
17
|
+
routeList: Array<ITypedHttpRoute | ITypedWebSocketRoute>,
|
|
18
|
+
): Map<string, number> => {
|
|
19
|
+
const dict: Map<string, number> = new Map();
|
|
20
|
+
for (const route of routeList)
|
|
21
|
+
route.accessor.forEach((_a, i) => {
|
|
22
|
+
const key: string = route.accessor.slice(0, i + 1).join(".");
|
|
23
|
+
dict.set(key, (dict.get(key) ?? 0) + 1);
|
|
24
|
+
});
|
|
25
|
+
return dict;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const variable = (
|
|
29
|
+
routeList: Array<ITypedHttpRoute | ITypedWebSocketRoute>,
|
|
30
|
+
) => {
|
|
31
|
+
const dict: Map<string, number> = prepare(routeList);
|
|
32
|
+
for (const route of routeList) {
|
|
33
|
+
const emended: string[] = route.accessor.slice();
|
|
34
|
+
route.accessor.forEach((accessor, i) => {
|
|
35
|
+
if (NamingConvention.variable(accessor)) return;
|
|
36
|
+
while (true) {
|
|
37
|
+
accessor = "_" + accessor;
|
|
38
|
+
const partial: string = [
|
|
39
|
+
...route.accessor.slice(0, i),
|
|
40
|
+
accessor,
|
|
41
|
+
].join(".");
|
|
42
|
+
if (dict.has(partial) === false) {
|
|
43
|
+
emended[i] = accessor;
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
route.accessor.splice(0, route.accessor.length, ...emended);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const shrink = (routeList: Array<ITypedHttpRoute | ITypedWebSocketRoute>) => {
|
|
53
|
+
const dict: Map<string, number> = prepare(routeList);
|
|
54
|
+
for (const route of routeList) {
|
|
55
|
+
if (
|
|
56
|
+
route.accessor.length < 2 ||
|
|
57
|
+
route.accessor.at(-1) !== route.accessor.at(-2)
|
|
58
|
+
)
|
|
59
|
+
continue;
|
|
60
|
+
|
|
61
|
+
const cut: string[] = route.accessor.slice(0, -1);
|
|
62
|
+
if ((dict.get(cut.join(".")) ?? 0) > 1) continue;
|
|
63
|
+
|
|
64
|
+
route.accessor = cut;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|