nestia 2.0.6 → 2.0.7-dev.20220414
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/{src/IConfiguration.ts → bin/IConfiguration.d.ts} +5 -8
- package/bin/IConfiguration.d.ts.map +1 -0
- package/bin/IConfiguration.js +3 -0
- package/bin/NestiaApplication.d.ts +10 -0
- package/bin/NestiaApplication.d.ts.map +1 -0
- package/bin/NestiaApplication.js +281 -0
- package/bin/analyses/ControllerAnalyzer.d.ts +7 -0
- package/bin/analyses/ControllerAnalyzer.d.ts.map +1 -0
- package/bin/analyses/ControllerAnalyzer.js +186 -0
- package/bin/analyses/GenericAnalyzer.d.ts +6 -0
- package/bin/analyses/GenericAnalyzer.d.ts.map +1 -0
- package/bin/analyses/GenericAnalyzer.js +100 -0
- package/bin/analyses/ImportAnalyzer.d.ts +13 -0
- package/bin/analyses/ImportAnalyzer.d.ts.map +1 -0
- package/bin/analyses/ImportAnalyzer.js +82 -0
- package/bin/analyses/ReflectAnalyzer.d.ts +5 -0
- package/bin/analyses/ReflectAnalyzer.d.ts.map +1 -0
- package/bin/analyses/ReflectAnalyzer.js +307 -0
- package/bin/analyses/SourceFinder.d.ts +5 -0
- package/bin/analyses/SourceFinder.d.ts.map +1 -0
- package/bin/analyses/SourceFinder.js +260 -0
- package/bin/bundle/HttpError.d.ts +2 -0
- package/bin/bundle/HttpError.d.ts.map +1 -0
- package/bin/bundle/HttpError.js +6 -0
- package/bin/bundle/IConnection.d.ts +2 -0
- package/bin/bundle/IConnection.d.ts.map +1 -0
- package/bin/bundle/IConnection.js +3 -0
- package/bin/bundle/Primitive.d.ts +2 -0
- package/bin/bundle/Primitive.d.ts.map +1 -0
- package/bin/bundle/Primitive.js +6 -0
- package/bin/bundle/__internal/AesPkcs5.d.ts +2 -0
- package/bin/bundle/__internal/AesPkcs5.d.ts.map +1 -0
- package/bin/bundle/__internal/AesPkcs5.js +6 -0
- package/bin/bundle/__internal/Fetcher.d.ts +2 -0
- package/bin/bundle/__internal/Fetcher.d.ts.map +1 -0
- package/bin/bundle/__internal/Fetcher.js +6 -0
- package/bin/executable/internal/CompilerOptions.d.ts +13 -0
- package/bin/executable/internal/CompilerOptions.d.ts.map +1 -0
- package/bin/executable/internal/CompilerOptions.js +132 -0
- package/bin/executable/internal/NestiaConfig.d.ts +5 -0
- package/bin/executable/internal/NestiaConfig.d.ts.map +1 -0
- package/bin/executable/internal/NestiaConfig.js +196 -0
- package/bin/executable/internal/nestia.config.getter.d.ts +2 -0
- package/bin/executable/internal/nestia.config.getter.d.ts.map +1 -0
- package/bin/executable/internal/nestia.config.getter.js +60 -0
- package/bin/executable/nestia.d.ts +3 -0
- package/bin/executable/nestia.d.ts.map +1 -0
- package/bin/executable/nestia.js +136 -0
- package/bin/executable/sdk.d.ts +2 -0
- package/bin/executable/sdk.d.ts.map +1 -0
- package/bin/executable/sdk.js +206 -0
- package/bin/generates/FileGenerator.d.ts +6 -0
- package/bin/generates/FileGenerator.d.ts.map +1 -0
- package/bin/generates/FileGenerator.js +302 -0
- package/bin/generates/FunctionGenerator.d.ts +5 -0
- package/bin/generates/FunctionGenerator.d.ts.map +1 -0
- package/bin/generates/FunctionGenerator.js +207 -0
- package/bin/generates/SdkGenerator.d.ts +6 -0
- package/bin/generates/SdkGenerator.d.ts.map +1 -0
- package/bin/generates/SdkGenerator.js +147 -0
- package/bin/index.d.ts +3 -0
- package/bin/index.d.ts.map +1 -0
- package/bin/index.js +28 -0
- package/bin/module.d.ts +3 -0
- package/bin/module.d.ts.map +1 -0
- package/bin/module.js +19 -0
- package/{src/structures/IController.ts → bin/structures/IController.d.ts} +6 -13
- package/bin/structures/IController.d.ts.map +1 -0
- package/bin/structures/IController.js +3 -0
- package/{src/structures/IRoute.ts → bin/structures/IRoute.d.ts} +5 -11
- package/bin/structures/IRoute.d.ts.map +1 -0
- package/bin/structures/IRoute.js +3 -0
- package/bin/structures/MethodType.d.ts +5 -0
- package/bin/structures/MethodType.d.ts.map +1 -0
- package/bin/structures/MethodType.js +8 -0
- package/bin/structures/ParamCategory.d.ts +2 -0
- package/bin/structures/ParamCategory.d.ts.map +1 -0
- package/bin/structures/ParamCategory.js +3 -0
- package/bin/utils/ArrayUtil.d.ts +6 -0
- package/bin/utils/ArrayUtil.d.ts.map +1 -0
- package/bin/utils/ArrayUtil.js +144 -0
- package/bin/utils/DirectoryUtil.d.ts +6 -0
- package/bin/utils/DirectoryUtil.d.ts.map +1 -0
- package/bin/utils/DirectoryUtil.js +190 -0
- package/bin/utils/ImportDictionary.d.ts +7 -0
- package/bin/utils/ImportDictionary.d.ts.map +1 -0
- package/bin/utils/ImportDictionary.js +85 -0
- package/bin/utils/StringUtil.d.ts +4 -0
- package/bin/utils/StringUtil.d.ts.map +1 -0
- package/bin/utils/StringUtil.js +13 -0
- package/bin/utils/stripJsonComments.d.ts +4 -0
- package/bin/utils/stripJsonComments.d.ts.map +1 -0
- package/bin/utils/stripJsonComments.js +76 -0
- package/package.json +14 -10
- package/tsconfig.json +8 -7
- package/src/NestiaApplication.ts +0 -96
- package/src/analyses/ControllerAnalyzer.ts +0 -154
- package/src/analyses/GenericAnalyzer.ts +0 -52
- package/src/analyses/ImportAnalyzer.ts +0 -93
- package/src/analyses/ReflectAnalyzer.ts +0 -221
- package/src/analyses/SourceFinder.ts +0 -73
- package/src/bin/nestia.ts +0 -59
- package/src/bundle/HttpError.ts +0 -1
- package/src/bundle/IConnection.ts +0 -1
- package/src/bundle/Primitive.ts +0 -1
- package/src/bundle/__internal/AesPkcs5.ts +0 -1
- package/src/bundle/__internal/Fetcher.ts +0 -1
- package/src/executable/sdk.ts +0 -84
- package/src/generates/FileGenerator.ts +0 -150
- package/src/generates/FunctionGenerator.ts +0 -201
- package/src/generates/SdkGenerator.ts +0 -39
- package/src/internal/NestiaConfig.ts +0 -36
- package/src/internal/TsConfig.ts +0 -123
- package/src/internal/tsconfig.default.json +0 -81
- package/src/structures/MethodType.ts +0 -6
- package/src/structures/ParamCategory.ts +0 -1
- package/src/utils/ArrayUtil.ts +0 -26
- package/src/utils/DirectoryUtil.ts +0 -48
- package/src/utils/ImportDictionary.ts +0 -44
- package/src/utils/StringUtil.ts +0 -10
- package/src/utils/stripJsonComments.ts +0 -79
package/src/NestiaApplication.ts
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import * as path from "path";
|
|
3
|
-
import * as tsc from "typescript";
|
|
4
|
-
import { Pair } from "tstl/utility/Pair";
|
|
5
|
-
import { Singleton } from "tstl/thread/Singleton";
|
|
6
|
-
|
|
7
|
-
import { ControllerAnalyzer } from "./analyses/ControllerAnalyzer";
|
|
8
|
-
import { ReflectAnalyzer } from "./analyses/ReflectAnalyzer";
|
|
9
|
-
import { SourceFinder } from "./analyses/SourceFinder";
|
|
10
|
-
import { SdkGenerator } from "./generates/SdkGenerator";
|
|
11
|
-
|
|
12
|
-
import { IConfiguration } from "./IConfiguration";
|
|
13
|
-
import { IController } from "./structures/IController";
|
|
14
|
-
import { IRoute } from "./structures/IRoute";
|
|
15
|
-
import { ArrayUtil } from "./utils/ArrayUtil";
|
|
16
|
-
|
|
17
|
-
export class NestiaApplication
|
|
18
|
-
{
|
|
19
|
-
private readonly config_: IConfiguration;
|
|
20
|
-
private readonly bundle_checker_: Singleton<Promise<(str: string) => boolean>>;
|
|
21
|
-
|
|
22
|
-
public constructor(config: IConfiguration)
|
|
23
|
-
{
|
|
24
|
-
this.config_ = config;
|
|
25
|
-
this.bundle_checker_ = new Singleton(async () =>
|
|
26
|
-
{
|
|
27
|
-
const bundles: string[] = await fs.promises.readdir(`${__dirname}${path.sep}bundle`);
|
|
28
|
-
const tuples: Pair<string, boolean>[] = await ArrayUtil.asyncMap(bundles, async file =>
|
|
29
|
-
{
|
|
30
|
-
const relative: string = `${this.config_.output}${path.sep}${file}`;
|
|
31
|
-
const stats: fs.Stats = await fs.promises.stat(`${__dirname}${path.sep}bundle${path.sep}${file}`);
|
|
32
|
-
|
|
33
|
-
return new Pair(relative, stats.isDirectory());
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
return (file: string): boolean =>
|
|
37
|
-
{
|
|
38
|
-
for (const it of tuples)
|
|
39
|
-
if (it.second === false && file === it.first)
|
|
40
|
-
return true;
|
|
41
|
-
else if (it.second === true && file.indexOf(it.first) === 0)
|
|
42
|
-
return true;
|
|
43
|
-
return false;
|
|
44
|
-
};
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
public async generate(): Promise<void>
|
|
49
|
-
{
|
|
50
|
-
// LOAD CONTROLLER FILES
|
|
51
|
-
const input: IConfiguration.IInput = this.config_.input instanceof Array
|
|
52
|
-
? { include: this.config_.input }
|
|
53
|
-
: typeof this.config_.input === "string"
|
|
54
|
-
? { include: [ this.config_.input ] }
|
|
55
|
-
: this.config_.input;
|
|
56
|
-
const fileList: string[] = await ArrayUtil.asyncFilter
|
|
57
|
-
(
|
|
58
|
-
await SourceFinder.find(input),
|
|
59
|
-
file => this.is_not_excluded(file)
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
// ANALYZE REFLECTS
|
|
63
|
-
const unique: WeakSet<any> = new WeakSet();
|
|
64
|
-
const controllerList: IController[] = [];
|
|
65
|
-
|
|
66
|
-
for (const file of fileList)
|
|
67
|
-
controllerList.push(...await ReflectAnalyzer.analyze(unique, file));
|
|
68
|
-
|
|
69
|
-
// ANALYZE TYPESCRIPT CODE
|
|
70
|
-
const program: tsc.Program = tsc.createProgram
|
|
71
|
-
(
|
|
72
|
-
controllerList.map(c => c.file),
|
|
73
|
-
this.config_.compilerOptions || {}
|
|
74
|
-
);
|
|
75
|
-
const checker: tsc.TypeChecker = program.getTypeChecker();
|
|
76
|
-
|
|
77
|
-
const routeList: IRoute[] = [];
|
|
78
|
-
for (const controller of controllerList)
|
|
79
|
-
{
|
|
80
|
-
const sourceFile: tsc.SourceFile | undefined = program.getSourceFile(controller.file);
|
|
81
|
-
if (sourceFile === undefined)
|
|
82
|
-
continue;
|
|
83
|
-
|
|
84
|
-
routeList.push(...ControllerAnalyzer.analyze(checker, sourceFile, controller));
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// DO GENERATE
|
|
88
|
-
await SdkGenerator.generate(this.config_, routeList);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
private async is_not_excluded(file: string): Promise<boolean>
|
|
92
|
-
{
|
|
93
|
-
return file.indexOf(`${this.config_.output}${path.sep}functional`) === -1
|
|
94
|
-
&& (await this.bundle_checker_.get())(file) === false;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import * as NodePath from "path";
|
|
2
|
-
import * as tsc from "typescript";
|
|
3
|
-
import { HashMap } from "tstl/container/HashMap";
|
|
4
|
-
|
|
5
|
-
import { IController } from "../structures/IController";
|
|
6
|
-
import { IRoute } from "../structures/IRoute";
|
|
7
|
-
|
|
8
|
-
import { GenericAnalyzer } from "./GenericAnalyzer";
|
|
9
|
-
import { ImportAnalyzer } from "./ImportAnalyzer";
|
|
10
|
-
|
|
11
|
-
export namespace ControllerAnalyzer
|
|
12
|
-
{
|
|
13
|
-
export function analyze(checker: tsc.TypeChecker, sourceFile: tsc.SourceFile, controller: IController): IRoute[]
|
|
14
|
-
{
|
|
15
|
-
// FIND CONTROLLER CLASS
|
|
16
|
-
const ret: IRoute[] = [];
|
|
17
|
-
tsc.forEachChild(sourceFile, node =>
|
|
18
|
-
{
|
|
19
|
-
if (tsc.isClassDeclaration(node) && node.name?.escapedText === controller.name)
|
|
20
|
-
{
|
|
21
|
-
// ANALYZE THE CONTROLLER
|
|
22
|
-
ret.push(..._Analyze_controller(checker, controller, node));
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
return ret;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/* ---------------------------------------------------------
|
|
30
|
-
CLASS
|
|
31
|
-
--------------------------------------------------------- */
|
|
32
|
-
function _Analyze_controller
|
|
33
|
-
(
|
|
34
|
-
checker: tsc.TypeChecker,
|
|
35
|
-
controller: IController,
|
|
36
|
-
classNode: tsc.ClassDeclaration
|
|
37
|
-
): IRoute[]
|
|
38
|
-
{
|
|
39
|
-
const ret: IRoute[] = [];
|
|
40
|
-
const classType: tsc.InterfaceType = checker.getTypeAtLocation(classNode) as tsc.InterfaceType;
|
|
41
|
-
const genericDict: GenericAnalyzer.Dictionary = GenericAnalyzer.analyze(checker, classNode);
|
|
42
|
-
|
|
43
|
-
for (const property of classType.getProperties())
|
|
44
|
-
if (property.declarations)
|
|
45
|
-
for (const declaration of property.declarations)
|
|
46
|
-
{
|
|
47
|
-
// TARGET ONLY METHOD
|
|
48
|
-
if (!tsc.isMethodDeclaration(declaration))
|
|
49
|
-
continue;
|
|
50
|
-
|
|
51
|
-
// IT MUST BE
|
|
52
|
-
const identifier = declaration.name;
|
|
53
|
-
if (!tsc.isIdentifier(identifier))
|
|
54
|
-
continue;
|
|
55
|
-
|
|
56
|
-
// ANALYZED WITH THE REFLECTED-FUNCTION
|
|
57
|
-
const func: IController.IFunction | undefined = controller.functions.find(f => f.name === identifier.escapedText);
|
|
58
|
-
if (func !== undefined)
|
|
59
|
-
ret.push(_Analyze_function(checker, controller, genericDict, func, declaration));
|
|
60
|
-
}
|
|
61
|
-
return ret;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/* ---------------------------------------------------------
|
|
65
|
-
FUNCTION
|
|
66
|
-
--------------------------------------------------------- */
|
|
67
|
-
function _Analyze_function
|
|
68
|
-
(
|
|
69
|
-
checker: tsc.TypeChecker,
|
|
70
|
-
controller: IController,
|
|
71
|
-
genericDict: GenericAnalyzer.Dictionary,
|
|
72
|
-
func: IController.IFunction,
|
|
73
|
-
declaration: tsc.MethodDeclaration
|
|
74
|
-
): IRoute
|
|
75
|
-
{
|
|
76
|
-
// PREPARE ASSETS
|
|
77
|
-
const signature: tsc.Signature | undefined = checker.getSignatureFromDeclaration(declaration);
|
|
78
|
-
if (signature === undefined)
|
|
79
|
-
throw new Error(`Error on ControllerAnalyzer._Analyze_function(): unable to get the ignature from the ${controller.name}.${func.name}().`);
|
|
80
|
-
|
|
81
|
-
const importDict: ImportAnalyzer.Dictionary = new HashMap();
|
|
82
|
-
|
|
83
|
-
// EXPLORE CHILDREN TYPES
|
|
84
|
-
const parameters: IRoute.IParameter[] = func.parameters.map(param => _Analyze_parameter
|
|
85
|
-
(
|
|
86
|
-
checker,
|
|
87
|
-
genericDict,
|
|
88
|
-
importDict,
|
|
89
|
-
controller,
|
|
90
|
-
func.name,
|
|
91
|
-
param,
|
|
92
|
-
declaration.parameters[param.index]
|
|
93
|
-
));
|
|
94
|
-
const output: string = ImportAnalyzer.analyze
|
|
95
|
-
(
|
|
96
|
-
checker,
|
|
97
|
-
genericDict,
|
|
98
|
-
importDict,
|
|
99
|
-
checker.getReturnTypeOfSignature(signature),
|
|
100
|
-
);
|
|
101
|
-
const imports: [string, string[]][] = importDict.toJSON().map(pair => [pair.first, pair.second.toJSON()]);
|
|
102
|
-
|
|
103
|
-
// CONFIGURE PATH
|
|
104
|
-
let path: string = NodePath.join(controller.path, func.path).split("\\").join("/");
|
|
105
|
-
if (path[0] !== "/")
|
|
106
|
-
path = "/" + path;
|
|
107
|
-
if (path[path.length - 1] === "/" && path !== "/")
|
|
108
|
-
path = path.substr(0, path.length - 1);
|
|
109
|
-
|
|
110
|
-
// RETURNS
|
|
111
|
-
return {
|
|
112
|
-
...func,
|
|
113
|
-
path,
|
|
114
|
-
parameters,
|
|
115
|
-
output,
|
|
116
|
-
imports,
|
|
117
|
-
|
|
118
|
-
symbol: `${controller.name}.${func.name}()`,
|
|
119
|
-
comments: signature.getDocumentationComment(undefined),
|
|
120
|
-
tags: signature.getJsDocTags()
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/* ---------------------------------------------------------
|
|
125
|
-
PARAMETER
|
|
126
|
-
--------------------------------------------------------- */
|
|
127
|
-
function _Analyze_parameter
|
|
128
|
-
(
|
|
129
|
-
checker: tsc.TypeChecker,
|
|
130
|
-
genericDict: GenericAnalyzer.Dictionary,
|
|
131
|
-
importDict: ImportAnalyzer.Dictionary,
|
|
132
|
-
controller: IController,
|
|
133
|
-
funcName: string,
|
|
134
|
-
param: IController.IParameter,
|
|
135
|
-
declaration: tsc.ParameterDeclaration
|
|
136
|
-
): IRoute.IParameter
|
|
137
|
-
{
|
|
138
|
-
const symbol: tsc.Symbol = checker.getSymbolAtLocation(declaration.name)!;
|
|
139
|
-
const type: tsc.Type = checker.getTypeOfSymbolAtLocation(symbol, declaration);
|
|
140
|
-
const name: string = symbol.getEscapedName().toString();
|
|
141
|
-
|
|
142
|
-
// VALIDATE PARAMETERS
|
|
143
|
-
if ((param.category === "query" || param.category === "body") && param.field !== undefined)
|
|
144
|
-
throw new Error(`Error on ${controller.name}.${funcName}(): parameter ${name} is specifying a field ${param.field} of the request ${param.category} message, however, Nestia does not support the field specialization for the request ${param.category} message. Erase the ${controller.name}.${funcName}()#${name} parameter and re-define a new decorator accepting full structured message.`);
|
|
145
|
-
|
|
146
|
-
return {
|
|
147
|
-
name,
|
|
148
|
-
category: param.category,
|
|
149
|
-
field: param.field,
|
|
150
|
-
encrypted: param.encrypted,
|
|
151
|
-
type: ImportAnalyzer.analyze(checker, genericDict, importDict, type)
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import * as tsc from "typescript";
|
|
2
|
-
|
|
3
|
-
export namespace GenericAnalyzer
|
|
4
|
-
{
|
|
5
|
-
export type Dictionary = WeakMap<tsc.Type, tsc.Type>;
|
|
6
|
-
|
|
7
|
-
export function analyze(checker: tsc.TypeChecker, classNode: tsc.ClassDeclaration): Dictionary
|
|
8
|
-
{
|
|
9
|
-
const dict: Dictionary = new WeakMap();
|
|
10
|
-
explore(checker, dict, classNode);
|
|
11
|
-
return dict;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function explore(checker: tsc.TypeChecker, dict: Dictionary, classNode: tsc.ClassDeclaration): void
|
|
15
|
-
{
|
|
16
|
-
if (classNode.heritageClauses === undefined)
|
|
17
|
-
return;
|
|
18
|
-
|
|
19
|
-
for (const heritage of classNode.heritageClauses)
|
|
20
|
-
for (const hType of heritage.types)
|
|
21
|
-
{
|
|
22
|
-
// MUST BE CLASS
|
|
23
|
-
const expression: tsc.Type = checker.getTypeAtLocation(hType.expression);
|
|
24
|
-
const superNode: tsc.Declaration = expression.symbol.getDeclarations()![0];
|
|
25
|
-
|
|
26
|
-
if (!tsc.isClassDeclaration(superNode))
|
|
27
|
-
continue;
|
|
28
|
-
|
|
29
|
-
// SPECIFY GENERICS
|
|
30
|
-
const usages: ReadonlyArray<tsc.TypeNode> = if_undefined_array(hType.typeArguments);
|
|
31
|
-
const parameters: ReadonlyArray<tsc.TypeParameterDeclaration> = if_undefined_array(superNode.typeParameters);
|
|
32
|
-
|
|
33
|
-
parameters.forEach((param, index) =>
|
|
34
|
-
{
|
|
35
|
-
const paramType: tsc.Type = checker.getTypeAtLocation(param);
|
|
36
|
-
const usageType: tsc.Type = (usages[index] !== undefined)
|
|
37
|
-
? checker.getTypeAtLocation(usages[index])
|
|
38
|
-
: checker.getTypeAtLocation(param.default!);
|
|
39
|
-
|
|
40
|
-
dict.set(paramType, usageType);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
// RECUSRIVE EXPLORATION
|
|
44
|
-
explore(checker, dict, superNode);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function if_undefined_array<T>(array: ReadonlyArray<T> | undefined): ReadonlyArray<T>
|
|
49
|
-
{
|
|
50
|
-
return array !== undefined ? array : [];
|
|
51
|
-
}
|
|
52
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import * as tsc from "typescript";
|
|
2
|
-
|
|
3
|
-
import { HashMap } from "tstl/container/HashMap";
|
|
4
|
-
import { HashSet } from "tstl/container/HashSet";
|
|
5
|
-
import { GenericAnalyzer } from "./GenericAnalyzer";
|
|
6
|
-
|
|
7
|
-
export namespace ImportAnalyzer
|
|
8
|
-
{
|
|
9
|
-
export interface IOutput
|
|
10
|
-
{
|
|
11
|
-
features: [string, string[]][];
|
|
12
|
-
alias: string;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export type Dictionary = HashMap<string, HashSet<string>>;
|
|
16
|
-
|
|
17
|
-
export function analyze
|
|
18
|
-
(
|
|
19
|
-
checker: tsc.TypeChecker,
|
|
20
|
-
genericDict: GenericAnalyzer.Dictionary,
|
|
21
|
-
importDict: Dictionary,
|
|
22
|
-
type: tsc.Type
|
|
23
|
-
): string
|
|
24
|
-
{
|
|
25
|
-
return explore(checker, genericDict, importDict, type);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function explore
|
|
29
|
-
(
|
|
30
|
-
checker: tsc.TypeChecker,
|
|
31
|
-
genericDict: GenericAnalyzer.Dictionary,
|
|
32
|
-
importDict: Dictionary,
|
|
33
|
-
type: tsc.Type
|
|
34
|
-
): string
|
|
35
|
-
{
|
|
36
|
-
//----
|
|
37
|
-
// CONDITIONAL BRANCHES
|
|
38
|
-
//----
|
|
39
|
-
// DECOMPOSE GENERIC ARGUMENT
|
|
40
|
-
while (genericDict.has(type) === true)
|
|
41
|
-
type = genericDict.get(type)!;
|
|
42
|
-
|
|
43
|
-
// PRIMITIVE
|
|
44
|
-
const symbol: tsc.Symbol | undefined = type.getSymbol() || type.aliasSymbol;
|
|
45
|
-
if (symbol === undefined)
|
|
46
|
-
return checker.typeToString(type, undefined, undefined);
|
|
47
|
-
|
|
48
|
-
// UNION OR INTERSECT
|
|
49
|
-
else if (type.aliasSymbol === undefined && type.isUnionOrIntersection())
|
|
50
|
-
{
|
|
51
|
-
const joiner: string = type.isIntersection() ? " & " : " | ";
|
|
52
|
-
return type.types.map(child => explore(checker, genericDict, importDict, child)).join(joiner);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
//----
|
|
56
|
-
// SPECIALIZATION
|
|
57
|
-
//----
|
|
58
|
-
const name: string = get_name(symbol);
|
|
59
|
-
const sourceFile: tsc.SourceFile = symbol.declarations![0].getSourceFile();
|
|
60
|
-
|
|
61
|
-
if (sourceFile.fileName.indexOf("typescript/lib") === -1)
|
|
62
|
-
{
|
|
63
|
-
let it: HashMap.Iterator<string, HashSet<string>> = importDict.find(sourceFile.fileName);
|
|
64
|
-
if (it.equals(importDict.end()) === true)
|
|
65
|
-
it = importDict.emplace(sourceFile.fileName, new HashSet()).first;
|
|
66
|
-
it.second.insert(name.split(".")[0]);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// CHECK GENERIC
|
|
70
|
-
const generic: readonly tsc.Type[] = checker.getTypeArguments(type as tsc.TypeReference);
|
|
71
|
-
if (generic.length)
|
|
72
|
-
{
|
|
73
|
-
return name === "Promise"
|
|
74
|
-
? explore(checker, genericDict, importDict, generic[0])
|
|
75
|
-
: `${name}<${generic.map(child => explore(checker, genericDict, importDict, child)).join(", ")}>`;
|
|
76
|
-
}
|
|
77
|
-
else
|
|
78
|
-
return name;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function get_name(symbol: tsc.Symbol): string
|
|
82
|
-
{
|
|
83
|
-
let name: string = symbol.escapedName.toString();
|
|
84
|
-
let decl: tsc.Node = symbol.getDeclarations()![0].parent;
|
|
85
|
-
|
|
86
|
-
while (tsc.isModuleBlock(decl))
|
|
87
|
-
{
|
|
88
|
-
name = `${decl.parent.name.getText()}.${name}`;
|
|
89
|
-
decl = decl.parent.parent;
|
|
90
|
-
}
|
|
91
|
-
return name;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
import * as NodePath from "path";
|
|
2
|
-
import { equal } from "tstl/ranges/module";
|
|
3
|
-
|
|
4
|
-
import { ArrayUtil } from "../utils/ArrayUtil";
|
|
5
|
-
import { StringUtil } from "../utils/StringUtil";
|
|
6
|
-
|
|
7
|
-
import { IController } from "../structures/IController";
|
|
8
|
-
import { ParamCategory } from "../structures/ParamCategory";
|
|
9
|
-
|
|
10
|
-
type IModule =
|
|
11
|
-
{
|
|
12
|
-
[key: string]: any;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export namespace ReflectAnalyzer
|
|
16
|
-
{
|
|
17
|
-
export async function analyze(unique: WeakSet<any>, file: string): Promise<IController[]>
|
|
18
|
-
{
|
|
19
|
-
const module: IModule = await import(file);
|
|
20
|
-
const ret: IController[] = [];
|
|
21
|
-
|
|
22
|
-
for (const tuple of Object.entries(module))
|
|
23
|
-
{
|
|
24
|
-
if (unique.has(tuple[1]))
|
|
25
|
-
continue;
|
|
26
|
-
else
|
|
27
|
-
unique.add(tuple[1]);
|
|
28
|
-
|
|
29
|
-
const controller: IController | null = _Analyze_controller(file, ...tuple);
|
|
30
|
-
if (controller !== null)
|
|
31
|
-
ret.push(controller);
|
|
32
|
-
}
|
|
33
|
-
return ret;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/* ---------------------------------------------------------
|
|
37
|
-
CONTROLLER
|
|
38
|
-
--------------------------------------------------------- */
|
|
39
|
-
function _Analyze_controller(file: string, name: string, creator: any): IController | null
|
|
40
|
-
{
|
|
41
|
-
//----
|
|
42
|
-
// VALIDATIONS
|
|
43
|
-
//----
|
|
44
|
-
// MUST BE TYPE OF A CREATOR WHO HAS THE CONSTRUCTOR
|
|
45
|
-
if (!(creator instanceof Function && creator.constructor instanceof Function))
|
|
46
|
-
return null;
|
|
47
|
-
|
|
48
|
-
// MUST HAVE THOSE MATADATA
|
|
49
|
-
else if (ArrayUtil.has(Reflect.getMetadataKeys(creator), "path", "host", "scope:options") === false)
|
|
50
|
-
return null;
|
|
51
|
-
|
|
52
|
-
//----
|
|
53
|
-
// CONSTRUCTION
|
|
54
|
-
//----
|
|
55
|
-
// BASIC INFO
|
|
56
|
-
const meta: IController = {
|
|
57
|
-
file,
|
|
58
|
-
name,
|
|
59
|
-
path: Reflect.getMetadata("path", creator),
|
|
60
|
-
functions: []
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
// PARSE CHILDREN DATA
|
|
64
|
-
for (const tuple of _Get_prototype_entries(creator))
|
|
65
|
-
{
|
|
66
|
-
const child: IController.IFunction | null = _Analyze_function(creator.prototype, meta, ...tuple);
|
|
67
|
-
if (child !== null)
|
|
68
|
-
meta.functions.push(child);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// RETURNS
|
|
72
|
-
return meta;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function _Get_prototype_entries(creator: any): Array<[string, unknown]>
|
|
76
|
-
{
|
|
77
|
-
const tuple = Object.entries(creator.prototype);
|
|
78
|
-
const parent = Object.getPrototypeOf(creator);
|
|
79
|
-
|
|
80
|
-
if (parent.prototype !== undefined)
|
|
81
|
-
tuple.push(..._Get_prototype_entries(parent));
|
|
82
|
-
|
|
83
|
-
return tuple;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/* ---------------------------------------------------------
|
|
87
|
-
FUNCTION
|
|
88
|
-
--------------------------------------------------------- */
|
|
89
|
-
function _Analyze_function(classProto: any, controller: IController, name: string, proto: any): IController.IFunction | null
|
|
90
|
-
{
|
|
91
|
-
//----
|
|
92
|
-
// VALIDATIONS
|
|
93
|
-
//----
|
|
94
|
-
// MUST BE TYPE OF A FUNCTION
|
|
95
|
-
if (!(proto instanceof Function))
|
|
96
|
-
return null;
|
|
97
|
-
|
|
98
|
-
// MUST HAVE THOSE METADATE
|
|
99
|
-
else if (ArrayUtil.has(Reflect.getMetadataKeys(proto), "path", "method") === false)
|
|
100
|
-
return null;
|
|
101
|
-
|
|
102
|
-
//----
|
|
103
|
-
// CONSTRUCTION
|
|
104
|
-
//----
|
|
105
|
-
// BASIC INFO
|
|
106
|
-
const meta: IController.IFunction = {
|
|
107
|
-
name,
|
|
108
|
-
method: METHODS[Reflect.getMetadata("method", proto)],
|
|
109
|
-
path: Reflect.getMetadata("path", proto),
|
|
110
|
-
parameters: [],
|
|
111
|
-
encrypted: Reflect.hasMetadata("__interceptors__", proto)
|
|
112
|
-
&& Reflect.getMetadata("__interceptors__", proto)[0]?.constructor?.name === "EncryptedRouteInterceptor"
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
// PARSE CHILDREN DATA
|
|
116
|
-
const nestParameters: NestParameters | undefined = Reflect.getMetadata("__routeArguments__", classProto.constructor, name);
|
|
117
|
-
if (nestParameters === undefined)
|
|
118
|
-
meta.parameters = [];
|
|
119
|
-
else
|
|
120
|
-
{
|
|
121
|
-
for (const tuple of Object.entries(nestParameters))
|
|
122
|
-
{
|
|
123
|
-
const child: IController.IParameter | null = _Analyze_parameter(...tuple);
|
|
124
|
-
if (child !== null)
|
|
125
|
-
meta.parameters.push(child);
|
|
126
|
-
}
|
|
127
|
-
meta.parameters = meta.parameters.sort((x, y) => x.index - y.index);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// VALIDATE PATH ARGUMENTS
|
|
131
|
-
const funcPathArguments: string[] = StringUtil.betweens(NodePath.join(controller.path, meta.path).split("\\").join("/"), ":", "/").sort();
|
|
132
|
-
const paramPathArguments: string[] = meta.parameters.filter(param => param.category === "param").map(param => param.field!).sort();
|
|
133
|
-
|
|
134
|
-
if (equal(funcPathArguments, paramPathArguments) === false)
|
|
135
|
-
throw new Error(`Error on ${controller.name}.${name}(): binded arguments in the "path" between function's decorator and parameters' decorators are different (function: [${funcPathArguments.join(", ")}], parameters: [${paramPathArguments.join(", ")}])`);
|
|
136
|
-
|
|
137
|
-
// RETURNS
|
|
138
|
-
return meta;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH"];
|
|
142
|
-
|
|
143
|
-
/* ---------------------------------------------------------
|
|
144
|
-
PARAMETER
|
|
145
|
-
--------------------------------------------------------- */
|
|
146
|
-
function _Analyze_parameter(key: string, param: INestParam): IController.IParameter | null
|
|
147
|
-
{
|
|
148
|
-
const symbol: string = key.split(":")[0];
|
|
149
|
-
if (symbol.indexOf("__custom") !== -1)
|
|
150
|
-
return _Analyze_custom_parameter(param);
|
|
151
|
-
|
|
152
|
-
const typeIndex: number = Number(symbol[0]);
|
|
153
|
-
if (isNaN(typeIndex) === true)
|
|
154
|
-
return null;
|
|
155
|
-
|
|
156
|
-
const type: ParamCategory | undefined = NEST_PARAMETER_TYPES[typeIndex];
|
|
157
|
-
if (type === undefined)
|
|
158
|
-
return null;
|
|
159
|
-
|
|
160
|
-
return {
|
|
161
|
-
name: key,
|
|
162
|
-
category: type,
|
|
163
|
-
index: param.index,
|
|
164
|
-
field: param.data,
|
|
165
|
-
encrypted: false
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function _Analyze_custom_parameter(param: INestParam): IController.IParameter | null
|
|
170
|
-
{
|
|
171
|
-
if (param.factory === undefined)
|
|
172
|
-
return null;
|
|
173
|
-
else if (param.factory.name === "EncryptedBody" || param.factory.name === "PlainBody")
|
|
174
|
-
{
|
|
175
|
-
return {
|
|
176
|
-
category: "body",
|
|
177
|
-
index: param.index,
|
|
178
|
-
name: param.name,
|
|
179
|
-
field: param.data,
|
|
180
|
-
encrypted: param.factory.name === "EncryptedBody"
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
else if (param.factory.name === "TypedParam")
|
|
184
|
-
return {
|
|
185
|
-
name: param.name,
|
|
186
|
-
category: "param",
|
|
187
|
-
index: param.index,
|
|
188
|
-
field: param.data,
|
|
189
|
-
encrypted: false
|
|
190
|
-
};
|
|
191
|
-
else
|
|
192
|
-
return null;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
type NestParameters = {
|
|
196
|
-
[key: string]: INestParam;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
interface INestParam
|
|
200
|
-
{
|
|
201
|
-
name: string;
|
|
202
|
-
index: number;
|
|
203
|
-
factory?: Function;
|
|
204
|
-
data: string | undefined;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const NEST_PARAMETER_TYPES = [
|
|
208
|
-
undefined,
|
|
209
|
-
undefined,
|
|
210
|
-
undefined,
|
|
211
|
-
"body",
|
|
212
|
-
"query",
|
|
213
|
-
"param",
|
|
214
|
-
undefined,
|
|
215
|
-
undefined,
|
|
216
|
-
undefined,
|
|
217
|
-
undefined,
|
|
218
|
-
undefined,
|
|
219
|
-
undefined
|
|
220
|
-
] as const;
|
|
221
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
import glob from "glob";
|
|
3
|
-
import * as path from "path";
|
|
4
|
-
|
|
5
|
-
import { IConfiguration } from "../IConfiguration";
|
|
6
|
-
|
|
7
|
-
export namespace SourceFinder
|
|
8
|
-
{
|
|
9
|
-
export async function find(input: IConfiguration.IInput): Promise<string[]>
|
|
10
|
-
{
|
|
11
|
-
const dict: Set<string> = new Set();
|
|
12
|
-
await decode(input.include, str => dict.add(str));
|
|
13
|
-
if (input.exclude)
|
|
14
|
-
await decode(input.exclude, str => dict.delete(str));
|
|
15
|
-
|
|
16
|
-
return [...dict];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async function decode
|
|
20
|
-
(
|
|
21
|
-
input: string[],
|
|
22
|
-
closure: (location: string) => void,
|
|
23
|
-
): Promise<void>
|
|
24
|
-
{
|
|
25
|
-
for (const pattern of input)
|
|
26
|
-
for (const location of await _Glob(path.resolve(pattern)))
|
|
27
|
-
{
|
|
28
|
-
const stats: fs.Stats = await fs.promises.stat(location);
|
|
29
|
-
if (stats.isDirectory() === true)
|
|
30
|
-
await iterate(closure, location);
|
|
31
|
-
else if (stats.isFile() && _Is_ts_file(location))
|
|
32
|
-
closure(location);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async function iterate
|
|
37
|
-
(
|
|
38
|
-
closure: (location: string) => void,
|
|
39
|
-
location: string
|
|
40
|
-
): Promise<void>
|
|
41
|
-
{
|
|
42
|
-
const directory: string[] = await fs.promises.readdir(location);
|
|
43
|
-
for (const file of directory)
|
|
44
|
-
{
|
|
45
|
-
const next: string = path.resolve(`${location}/${file}`);
|
|
46
|
-
const stats: fs.Stats = await fs.promises.stat(next);
|
|
47
|
-
|
|
48
|
-
if (stats.isDirectory() === true)
|
|
49
|
-
await iterate(closure, next);
|
|
50
|
-
else if (stats.isFile() && _Is_ts_file(file))
|
|
51
|
-
closure(next);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function _Glob(pattern: string): Promise<string[]>
|
|
56
|
-
{
|
|
57
|
-
return new Promise((resolve, reject) =>
|
|
58
|
-
{
|
|
59
|
-
glob(pattern, (err, matches) =>
|
|
60
|
-
{
|
|
61
|
-
if (err)
|
|
62
|
-
reject(err);
|
|
63
|
-
else
|
|
64
|
-
resolve(matches.map(str => path.resolve(str)));
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function _Is_ts_file(file: string): boolean
|
|
70
|
-
{
|
|
71
|
-
return file.substr(-3) === ".ts" && file.substr(-5) !== ".d.ts";
|
|
72
|
-
}
|
|
73
|
-
}
|