-
-
Notifications
You must be signed in to change notification settings - Fork 197
/
Copy pathdecorators.ts
132 lines (115 loc) · 4.34 KB
/
decorators.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import { AnalyticsEventLabelDelimiter } from "../constants";
/**
* Caches the result of the first execution of the method and returns it whenever it is called instead of executing it again.
* Works with methods and getters.
* @example
* ```
* class CacheDecoratorsTest {
*
* @cache()
* public method(num: number): number {
* return num;
* }
*
* @cache()
* public get property(): any {
* // execute some heavy operation.
* return result;
* }
* }
*
* const instance = new CacheDecoratorsTest();
* const result = instance.method(1); // returns 1;
*
* // all consecutive calls to instance.method will return 1.
* const result2 = instance.method(2); // returns 1;
* ```
*/
export function cache(): any {
return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>): TypedPropertyDescriptor<any> => {
let result: any;
const propName: string = descriptor.value ? "value" : "get";
const originalValue = (<any>descriptor)[propName];
(<any>descriptor)[propName] = function (...args: any[]) {
const propertyName = `__isCalled_${propertyKey}__`;
if (this && !this[propertyName]) {
this[propertyName] = true;
result = originalValue.apply(this, args);
}
return result;
};
return descriptor;
};
}
/**
* Calls specific method of the instance before executing the decorated method.
* This is usable when some of your methods depend on initialize async method, that cannot be invoked in constructor of the class.
* IMPORTANT: The decorated method must be async.
* @param {string} methodName The name of the method that will be invoked before calling the decorated method.
* @param {any[]} methodArgs Args that will be passed to the method that will be invoked before calling the decorated one.
* @return {any} Result of the decorated method.
*/
export function invokeBefore(methodName: string, methodArgs?: any[]): any {
return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>): TypedPropertyDescriptor<any> => {
const originalValue = descriptor.value;
descriptor.value = async function (...args: any[]) {
await target[methodName].apply(this, methodArgs);
return originalValue.apply(this, args);
};
return descriptor;
};
}
export function invokeInit(): any {
return invokeBefore("init");
}
export function exported(moduleName: string): any {
return (target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>): TypedPropertyDescriptor<any> => {
$injector.publicApi.__modules__[moduleName] = $injector.publicApi.__modules__[moduleName] || {};
$injector.publicApi.__modules__[moduleName][propertyKey] = (...args: any[]): any => {
const originalModule = $injector.resolve(moduleName),
originalMethod: any = originalModule[propertyKey],
result = originalMethod.apply(originalModule, args);
return result;
};
return descriptor;
};
}
export function performanceLog(injector?: IInjector): any {
injector = injector || $injector;
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor): any {
const originalMethod = descriptor.value;
const className = target.constructor.name;
const trackName = `${className}${AnalyticsEventLabelDelimiter}${propertyKey}`;
const performanceService: IPerformanceService = injector.resolve("performanceService");
//needed for the returned function to have the same name as the original - used in hooks decorator
const functionWrapper = {
[originalMethod.name]: function (...args: Array<any>) {
const start = performanceService.now();
const result = originalMethod.apply(this, args);
const resolvedPromise = Promise.resolve(result);
let end;
if (resolvedPromise !== result) {
end = performanceService.now();
performanceService.processExecutionData(trackName, start, end, args);
} else {
resolvedPromise
.then(() => {
end = performanceService.now();
performanceService.processExecutionData(trackName, start, end, args);
})
.catch((err) => {
end = performanceService.now();
performanceService.processExecutionData(trackName, start, end, args);
});
}
return result;
}
};
descriptor.value = functionWrapper[originalMethod.name];
// used to get parameter names in hooks decorator
descriptor.value.toString = () => {
return originalMethod.toString();
};
return descriptor;
};
}