/
Store.ts
147 lines (126 loc) · 4.05 KB
/
Store.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import {DecoratorTypes} from "../domain/DecoratorTypes";
import {decoratorTypeOf, deepClone, deepMerge, descriptorOf, isSymbol, nameOf} from "../utils";
import {Metadata} from "./Metadata";
/**
* @ignore
*/
export const CLASS_STORE = "tsed:class:store";
/**
* @ignore
*/
export const METHOD_STORE = "tsed:method:store";
/**
* @ignore
*/
export const PROPERTY_STORE = "tsed:property:store";
/**
* @ignore
*/
export const PARAM_STORE = "tsed:param:store";
const stores = new Map<symbol, Store>();
function storeGet(key: string, ...args: any[]): Store {
if (isSymbol(args[0])) {
if (!stores.has(args[0])) {
stores.set(args[0], new Store());
}
return stores.get(args[0])!;
} else {
const registry = Metadata as any;
if (!registry.hasOwn(key, ...args)) {
registry.set(key, new Store(), ...args);
}
return registry.getOwn(key, ...args);
}
}
function defineStore(args: any[]): Store {
const [target, propertyKey, descriptor] = args;
switch (decoratorTypeOf(args)) {
case DecoratorTypes.PARAM_CTOR:
case DecoratorTypes.PARAM_STC:
case DecoratorTypes.PARAM:
const store = storeGet(PARAM_STORE, target, propertyKey);
if (!store.has("" + descriptor)) {
store.set("" + descriptor, new Store());
}
return store.get("" + descriptor);
case DecoratorTypes.PROP:
case DecoratorTypes.PROP_STC:
return storeGet(PROPERTY_STORE, target, propertyKey);
case DecoratorTypes.METHOD:
case DecoratorTypes.METHOD_STC:
return storeGet(METHOD_STORE, target, propertyKey);
case DecoratorTypes.CLASS:
return storeGet(CLASS_STORE, target);
}
}
export class Store {
#entries = new Map<string, any>();
/**
* Create or get a Store from args {target + methodName + descriptor}
* @param args
* @returns {Store}
*/
static from(...args: any[]): Store {
return defineStore(args);
}
/**
* Create store on the method.
* @param target
* @param {string} propertyKey
* @returns {Store}
*/
static fromMethod(target: any, propertyKey: string | symbol): Store {
return Store.from(target, propertyKey, descriptorOf(target, propertyKey));
}
/**
* The get() method returns a specified element from a Map object.
* @param key Required. The key of the element to return from the Map object.
* @param defaultValue
* @returns {T} Returns the element associated with the specified key or undefined if the key can't be found in the Map object.
*/
get<T = any>(key: any, defaultValue?: any): T {
return this.#entries.get(nameOf(key)) || defaultValue;
}
/**
* The has() method returns a boolean indicating whether an element with the specified key exists or not.
* @param key
* @returns {boolean}
*/
has(key: any): boolean {
return this.#entries.has(nameOf(key));
}
/**
* The set() method adds or updates an element with a specified key and value to a Map object.
* @param key Required. The key of the element to add to the Map object.
* @param metadata Required. The value of the element to add to the Map object.
*/
set(key: any, metadata: any): Store {
this.#entries.set(nameOf(key), metadata);
return this;
}
/**
* The delete() method removes the specified element from a Map object.
* @param key Required. The key of the element to remove from the Map object.
* @returns {boolean} Returns true if an element in the Map object existed and has been removed, or false if the element does not exist.
*/
delete(key: string): boolean {
return this.#entries.delete(nameOf(key));
}
/**
* Merge given value with existing value.
* @param key
* @param value
* @param inverse Change the merge order. Get the existing value and apply over given value
* @returns {Store}
*/
merge(key: any, value: any, inverse: boolean = false): Store {
let _value_ = this.get(key);
if (_value_) {
value = deepClone(value);
_value_ = deepClone(_value_);
value = inverse ? deepMerge(value, _value_) : deepMerge(_value_, value);
}
this.set(key, value);
return this;
}
}