-
Notifications
You must be signed in to change notification settings - Fork 0
/
Container.ts
119 lines (98 loc) · 3.17 KB
/
Container.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
/* eslint-disable no-dupe-class-members */
import Binding from './Binding'
import type { Key, Resolvable } from '.'
export default class Container {
protected bindings = new Map()
protected instances = new Map()
protected aliases = new Map()
/**
* Checks if anything has been bound or aliased into the Container
*/
has(key: Key) {
const alias = this.getAlias(key)
return this.bindings.has(alias) || this.instances.has(alias)
}
/**
* Binds a resolvable function into the Container
*
* If it is the only param, the object reference works as the reference key
*/
bind(key: Key | Resolvable<unknown>, resolvable?: unknown | Resolvable<unknown>, shared = false) {
if (!key) {
throw new TypeError('First parameter is required')
} else if (!resolvable) resolvable = key as Resolvable<unknown>
if (Binding.isResolvable(resolvable)) {
this.bindings.set(key, new Binding(resolvable as Resolvable<unknown>, shared))
return this
}
throw new TypeError(`Only functions are bindable, got [${typeof resolvable}]`)
}
private resolve(key: Key, params?: unknown[]) {
const alias = this.getAlias(key)
const instance = this.getInstance(alias)
if (instance) return instance
if (this.bindings.has(alias)) {
const binding = this.bindings.get(alias)
const result = binding.resolve(this, params)
if (binding.shared) {
this.instances.set(alias, result)
}
return result
}
}
/**
* Resolves a Binding reference from the Container
*
* If the given key is a Resolvable function,
* it is resolved from the Container on the fly
*/
make(key: unknown, ...params: unknown[]) {
const binding = this.resolve(key, params)
if (binding) {
return binding
} else if (Binding.isResolvable(key)) {
return Binding.once(key as Resolvable<unknown>, this, params)
}
throw new TypeError(`Can not resolve anything for ${key}`)
}
/**
* Resolves a Binding reference from the Container
*/
get<T extends new (...p: any[]) => InstanceType<T>>(key: T): InstanceType<T>
get<T>(key: T): T extends new () => infer I ? I : unknown
get(key: unknown): unknown {
const binding = this.resolve(key)
if (binding) return binding
throw new TypeError(`${key} is not bound`)
}
/**
* Set an object or primitive value into the Container
*
* When resolved, it returns the same value and reference
*/
instance(key: Key, value: unknown) {
this.instances.set(key, value)
}
/**
* Bind a Resolvable function into the Container.
*
* The function is resolved once. Its result is stored
* and returned on subsequent calls to `get` or `make`.
*/
singleton(key: Key, resolvable: unknown) {
return this.bind(key, resolvable, true)
}
private getInstance(key: Key) {
return this.instances.has(key) ? this.instances.get(key) : null
}
/**
* Apply an alias to an already-bound binding
*/
alias(alias: unknown, key: Key | Resolvable<unknown>) {
this.aliases.set(alias, key)
}
private getAlias(alias: unknown) {
const key = this.aliases.has(alias) ? this.aliases.get(alias) : null
return key || alias
}
}