-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathmetho.js
98 lines (90 loc) · 2.72 KB
/
metho.js
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
export const data = Symbol("methoData")
const registry = {}
export function add(
targetOrTargets,
f,
{
outerSyntax = false,
symbolName = f?.name,
register = false,
useSymbol = false,
} = {}
) {
return f.length
? outerSyntax
? addProperty(targetOrTargets, f, { symbolName, register, useSymbol })
: addWithParams(targetOrTargets, f, { symbolName, register })
: addSimple(targetOrTargets, f, { symbolName, register, useSymbol })
}
export function addProperty(
targetOrTargets,
f,
{ symbolName = f?.name, register = false, useSymbol = false } = {}
) {
const s = useSymbol || Symbol(symbolName)
sanitiseTargets(targetOrTargets).forEach(target => (target[s] = f))
register && addToRegister(symbolName, s)
return s
}
export function addWithParams(
targetOrTargets,
f,
{ symbolName = f?.name, register = false } = {}
) {
const methodName = symbolName || "__methoIntermediate"
const buildTempMethod = {
[methodName]: function (...args) {
const s = Symbol(symbolName)
const targets = buildTempMethod[methodName].targets
targets.forEach(target => {
Object.defineProperty(target, s, {
configurable: true,
get: function () {
targets.forEach(target => {
delete target[s]
})
return f.apply(this, args)
},
})
})
return s
},
}
buildTempMethod[methodName].targets = sanitiseTargets(targetOrTargets)
buildTempMethod[methodName].toString = ()=>buildTempMethod[methodName]()
register && addToRegister(symbolName, buildTempMethod[methodName])
return buildTempMethod[methodName]
}
export function addSimple(
targetOrTargets,
f,
{ symbolName = f?.name, register = false, useSymbol = false } = {}
) {
const s = useSymbol || Symbol(symbolName)
sanitiseTargets(targetOrTargets).forEach(target =>
Object.defineProperty(target, s, { configurable: true, get: f })
)
register && addToRegister(symbolName, s)
return s
}
export const getRegistered = name => registry[name]
const sanitiseTargets = targets => (Array.isArray(targets) && targets.prototype) ? targets : [targets]
const addToRegister = (name, item) => (registry[name] = item)
export function addWithSharedSymbolName(target, func, symbolName) {
const isRegistered = getRegistered(symbolName)
let ret
if (isRegistered) {
if (!func.length) {
// if already registerd and no params, re-use symbol
ret = add(target, func, { useSymbol: isRegistered })
} else {
// else if already registered and has params, don't overwrite function, just update targets (function will have to deal with both targets)
ret = isRegistered
ret.targets = [...new Set([...ret.targets, target])]
}
} else {
// if not registered, create a new Symbol and register it
ret = add(target, func, { register: true, symbolName })
}
return ret
}