nano-injector 1.0.1 → 1.0.4
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/LICENSE +21 -21
- package/README.md +221 -221
- package/lib/Binder.js +8 -3
- package/lib/Injector.js +52 -19
- package/lib/InjectorsStack.js +16 -8
- package/lib/Provider.js +6 -9
- package/lib/index.js +0 -1
- package/package.json +1 -1
- package/typings/Binder.d.ts +4 -1
- package/typings/Injector.d.ts +25 -6
- package/typings/InjectorsStack.d.ts +5 -3
- package/typings/Provider.d.ts +2 -1
- package/typings/index.d.ts +0 -1
- package/lib/InjectingError.js +0 -9
- package/typings/InjectingError.d.ts +0 -5
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2021 Roman Pukhalskyi
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Roman Pukhalskyi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,221 +1,221 @@
|
|
|
1
|
-
# Nano-injector
|
|
2
|
-
|
|
3
|
-
Miniature dependency injection library for TypeScript and JavaScript.
|
|
4
|
-
|
|
5
|
-
# Motivation
|
|
6
|
-
|
|
7
|
-
There is a myriad of dependency injection libraries in typescript ecosystem. Most of them are built around decorators.
|
|
8
|
-
Decorators are an obvious solution for such kinds of tasks. But in terms of dependency injection, they have its
|
|
9
|
-
drawbacks - they are not type safe, which means that theoretically, it is possible to bind any value to the required type,
|
|
10
|
-
and they don't work well with interfaces and primitives, therefore some workaround solutions are required to overcome
|
|
11
|
-
this limitation.
|
|
12
|
-
Typically, injecting of an interface, using these libraries, looks like as follows:
|
|
13
|
-
```typescript
|
|
14
|
-
// binding value
|
|
15
|
-
injector.bind('IStorage').toValue(new DefStorage())
|
|
16
|
-
|
|
17
|
-
// injecting it
|
|
18
|
-
@Inject('IStorage')
|
|
19
|
-
private storage: IStorage
|
|
20
|
-
```
|
|
21
|
-
where string IStorage should be passed to Inject decorator in order to let the injector know which value should be injected
|
|
22
|
-
to the required property. Since it is just a string, any string can be passed, therefore any type can be injected,
|
|
23
|
-
and the compiler won't warn you about that. Also, there is type duplication, therefore the whole usage looks a bit ugly.
|
|
24
|
-
|
|
25
|
-
**Nano-injector** is addressing these issues, by doing dependency injection in a little bit different way - instead of using
|
|
26
|
-
decorators, it uses plain functions as dependencies providers. By doing so, the same workflow, using Nano-injector,
|
|
27
|
-
looks as follows:
|
|
28
|
-
|
|
29
|
-
```typescript
|
|
30
|
-
// defining provider
|
|
31
|
-
const $IStorage = createProvider<IStorage>()
|
|
32
|
-
|
|
33
|
-
// binding it to the value
|
|
34
|
-
injector.bindProvider($IStorage).toValue(new DefStorage())
|
|
35
|
-
|
|
36
|
-
// injecting it
|
|
37
|
-
private storage = $IStorage()
|
|
38
|
-
```
|
|
39
|
-
Here _**$IStorage**_ is the provider, which basically is a plain function, and which should be bound to desired value through
|
|
40
|
-
injector. Type of storage property compiler infers automatically, which reduces unneeded code duplication. Also, you can bind only
|
|
41
|
-
value of the type specified during provider creation, if not to do so, the compiler will warn you about that.
|
|
42
|
-
|
|
43
|
-
Also, unlike other libraries, Nano-injector is very small, with a very concise API.
|
|
44
|
-
More usage details you can find below, and in the example directory.
|
|
45
|
-
|
|
46
|
-
# Features
|
|
47
|
-
|
|
48
|
-
- Very simple
|
|
49
|
-
- Ultra lightweight
|
|
50
|
-
- Zero dependencies
|
|
51
|
-
- No decorators
|
|
52
|
-
- Type safe
|
|
53
|
-
- Injectable interfaces and primitives
|
|
54
|
-
|
|
55
|
-
# Installation
|
|
56
|
-
```bash
|
|
57
|
-
npm i nano-injector
|
|
58
|
-
```
|
|
59
|
-
# Usage
|
|
60
|
-
Importing dependencies:
|
|
61
|
-
```typescript
|
|
62
|
-
import { Injector, createProvider } from 'nano-injector'
|
|
63
|
-
```
|
|
64
|
-
Defining types and providers:
|
|
65
|
-
```typescript
|
|
66
|
-
// to distinguish providers from any other entities $ sign is used
|
|
67
|
-
const $Clock = createProvider<(cb: () => void, time: number) => number>()
|
|
68
|
-
const $ClockRate = createProvider<number>()
|
|
69
|
-
|
|
70
|
-
const $CPU = createProvider<CPU>()
|
|
71
|
-
interface CPU {
|
|
72
|
-
readonly model: string
|
|
73
|
-
readonly numCores: number
|
|
74
|
-
readonly frequency: number
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const $GPU = createProvider<GPU>()
|
|
78
|
-
interface GPU {
|
|
79
|
-
readonly model: string
|
|
80
|
-
readonly numRayTracingCores: number
|
|
81
|
-
readonly memorySize: number
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const $RAM = createProvider<RAM>()
|
|
85
|
-
interface RAM {
|
|
86
|
-
readonly capacity: number
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const $Motherboard = createProvider<Motherboard>()
|
|
90
|
-
interface Motherboard {
|
|
91
|
-
readonly cpu: CPU
|
|
92
|
-
readonly gpu: GPU
|
|
93
|
-
readonly ram: RAM
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
class GeForce911 implements GPU {
|
|
97
|
-
model = 'GeForce911'
|
|
98
|
-
numRayTracingCores = 100
|
|
99
|
-
memorySize = 2048
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
class DefMotherboard implements Motherboard {
|
|
103
|
-
constructor(
|
|
104
|
-
readonly model: string,
|
|
105
|
-
public cpu = $CPU(),
|
|
106
|
-
public gpu = $GPU(),
|
|
107
|
-
public ram = $RAM()
|
|
108
|
-
) {}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
class PC {
|
|
112
|
-
constructor(private name: string, private motherboard = $Motherboard()) {}
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
Defining injector through which injection occurs:
|
|
116
|
-
```typescript
|
|
117
|
-
const injector = new Injector()
|
|
118
|
-
```
|
|
119
|
-
Binding providers:
|
|
120
|
-
```typescript
|
|
121
|
-
// binding $CPU provider to exact value
|
|
122
|
-
injector.bindProvider($CPU).toValue({ numCores: 4, model: 't7', frequency: 2000 })
|
|
123
|
-
|
|
124
|
-
// binding $GPU provider to class which conforms to provider's returning type
|
|
125
|
-
// calling asSingleton specifies that value should be created only once
|
|
126
|
-
injector.bindProvider($GPU).toConstructor(GeForce911).asSingleton()
|
|
127
|
-
|
|
128
|
-
// binding $RAM provider to the factory which creates value conforming to
|
|
129
|
-
// provider's returning type
|
|
130
|
-
injector.bindProvider($RAM).toFactory(() => ({
|
|
131
|
-
capacity: Math.floor(Math.random() * 1024),
|
|
132
|
-
}))
|
|
133
|
-
|
|
134
|
-
injector
|
|
135
|
-
.bindProvider($Motherboard)
|
|
136
|
-
.toFactory(
|
|
137
|
-
() =>
|
|
138
|
-
// only one required parameter is passed, the rest are initialized
|
|
139
|
-
// automatically through providers
|
|
140
|
-
new DefMotherboard('Asus ABC-123')
|
|
141
|
-
)
|
|
142
|
-
.asSingleton()
|
|
143
|
-
|
|
144
|
-
// without ignoring compiler would tell you about wrong value's type
|
|
145
|
-
// @ts-expect-error
|
|
146
|
-
injector.bindProvider($ClockRate).toValue('asd')
|
|
147
|
-
|
|
148
|
-
injector.bindProvider($ClockRate).toValue(1000)
|
|
149
|
-
|
|
150
|
-
injector.bindProvider($Clock).toValue(setTimeout)
|
|
151
|
-
```
|
|
152
|
-
Creating instances with all dependencies injected:
|
|
153
|
-
```typescript
|
|
154
|
-
// the first parameter of PC constructor is required, so it is passed into the
|
|
155
|
-
// construction method
|
|
156
|
-
injector.createInstance(PC, 'My pc')
|
|
157
|
-
```
|
|
158
|
-
Directly getting the bound to the providers values:
|
|
159
|
-
```typescript
|
|
160
|
-
injector.getValue($CPU)
|
|
161
|
-
injector.getValue($GPU)
|
|
162
|
-
injector.getValue($RAM)
|
|
163
|
-
|
|
164
|
-
injector.getValue($Clock)(() => console.log('Tick!'), 1000)
|
|
165
|
-
```
|
|
166
|
-
Manually creating instance with all its dependencies:
|
|
167
|
-
```typescript
|
|
168
|
-
new DefMotherboard(
|
|
169
|
-
'Asus xyz',
|
|
170
|
-
{ frequency: 123, model: 'Intel xyz', numCores: 1 },
|
|
171
|
-
{ model: '', memorySize: 0, numRayTracingCores: 1 },
|
|
172
|
-
{ capacity: 123 },
|
|
173
|
-
)
|
|
174
|
-
```
|
|
175
|
-
Calling function through injector:
|
|
176
|
-
```typescript
|
|
177
|
-
injector.callFunc(() => {
|
|
178
|
-
// inside function all providers return bound to them values
|
|
179
|
-
console.log('Inside function')
|
|
180
|
-
console.log('CPU:', $CPU())
|
|
181
|
-
console.log('GPU:', $GPU())
|
|
182
|
-
console.log('RAM:', $RAM())
|
|
183
|
-
})
|
|
184
|
-
```
|
|
185
|
-
It's also possible to bind few providers to the same value. The bound value should conform to
|
|
186
|
-
intersection of all providers' types:
|
|
187
|
-
```typescript
|
|
188
|
-
injector.bindProvider($RAM, $CPU, $GPU).toValue({
|
|
189
|
-
capacity: 10,
|
|
190
|
-
frequency: 100,
|
|
191
|
-
memorySize: 200,
|
|
192
|
-
model: 'ATB-21',
|
|
193
|
-
numCores: 2,
|
|
194
|
-
numRayTracingCores: 60,
|
|
195
|
-
})
|
|
196
|
-
```
|
|
197
|
-
Creating composition of injectors:
|
|
198
|
-
```typescript
|
|
199
|
-
const childInjector = new Injector({ parent: injector })
|
|
200
|
-
```
|
|
201
|
-
Overriding $CPU binding of parent injector inside child injector:
|
|
202
|
-
```typescript
|
|
203
|
-
childInjector.bindProvider($CPU).toValue({ numCores: 1, model: 'z2', frequency: 999 })
|
|
204
|
-
```
|
|
205
|
-
Injecting value to the existing instance:
|
|
206
|
-
```typescript
|
|
207
|
-
const newMotherboard = new class {
|
|
208
|
-
gpu: GPU
|
|
209
|
-
cpu: CPU
|
|
210
|
-
}
|
|
211
|
-
injector.injectValues(newMotherboard, { cpu: $CPU, gpu: $GPU })
|
|
212
|
-
```
|
|
213
|
-
# [Docs](https://protoukr.github.io/nano-injector/)
|
|
214
|
-
|
|
215
|
-
# Contributing
|
|
216
|
-
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
|
|
217
|
-
|
|
218
|
-
Please make sure to update tests as appropriate.
|
|
219
|
-
|
|
220
|
-
## License
|
|
221
|
-
[MIT](https://choosealicense.com/licenses/mit/)
|
|
1
|
+
# Nano-injector
|
|
2
|
+
|
|
3
|
+
Miniature dependency injection library for TypeScript and JavaScript.
|
|
4
|
+
|
|
5
|
+
# Motivation
|
|
6
|
+
|
|
7
|
+
There is a myriad of dependency injection libraries in typescript ecosystem. Most of them are built around decorators.
|
|
8
|
+
Decorators are an obvious solution for such kinds of tasks. But in terms of dependency injection, they have its
|
|
9
|
+
drawbacks - they are not type safe, which means that theoretically, it is possible to bind any value to the required type,
|
|
10
|
+
and they don't work well with interfaces and primitives, therefore some workaround solutions are required to overcome
|
|
11
|
+
this limitation.
|
|
12
|
+
Typically, injecting of an interface, using these libraries, looks like as follows:
|
|
13
|
+
```typescript
|
|
14
|
+
// binding value
|
|
15
|
+
injector.bind('IStorage').toValue(new DefStorage())
|
|
16
|
+
|
|
17
|
+
// injecting it
|
|
18
|
+
@Inject('IStorage')
|
|
19
|
+
private storage: IStorage
|
|
20
|
+
```
|
|
21
|
+
where string IStorage should be passed to Inject decorator in order to let the injector know which value should be injected
|
|
22
|
+
to the required property. Since it is just a string, any string can be passed, therefore any type can be injected,
|
|
23
|
+
and the compiler won't warn you about that. Also, there is type duplication, therefore the whole usage looks a bit ugly.
|
|
24
|
+
|
|
25
|
+
**Nano-injector** is addressing these issues, by doing dependency injection in a little bit different way - instead of using
|
|
26
|
+
decorators, it uses plain functions as dependencies providers. By doing so, the same workflow, using Nano-injector,
|
|
27
|
+
looks as follows:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
// defining provider
|
|
31
|
+
const $IStorage = createProvider<IStorage>()
|
|
32
|
+
|
|
33
|
+
// binding it to the value
|
|
34
|
+
injector.bindProvider($IStorage).toValue(new DefStorage())
|
|
35
|
+
|
|
36
|
+
// injecting it
|
|
37
|
+
private storage = $IStorage()
|
|
38
|
+
```
|
|
39
|
+
Here _**$IStorage**_ is the provider, which basically is a plain function, and which should be bound to desired value through
|
|
40
|
+
injector. Type of storage property compiler infers automatically, which reduces unneeded code duplication. Also, you can bind only
|
|
41
|
+
value of the type specified during provider creation, if not to do so, the compiler will warn you about that.
|
|
42
|
+
|
|
43
|
+
Also, unlike other libraries, Nano-injector is very small, with a very concise API.
|
|
44
|
+
More usage details you can find below, and in the example directory.
|
|
45
|
+
|
|
46
|
+
# Features
|
|
47
|
+
|
|
48
|
+
- Very simple
|
|
49
|
+
- Ultra lightweight
|
|
50
|
+
- Zero dependencies
|
|
51
|
+
- No decorators
|
|
52
|
+
- Type safe
|
|
53
|
+
- Injectable interfaces and primitives
|
|
54
|
+
|
|
55
|
+
# Installation
|
|
56
|
+
```bash
|
|
57
|
+
npm i nano-injector
|
|
58
|
+
```
|
|
59
|
+
# Usage
|
|
60
|
+
Importing dependencies:
|
|
61
|
+
```typescript
|
|
62
|
+
import { Injector, createProvider } from 'nano-injector'
|
|
63
|
+
```
|
|
64
|
+
Defining types and providers:
|
|
65
|
+
```typescript
|
|
66
|
+
// to distinguish providers from any other entities $ sign is used
|
|
67
|
+
const $Clock = createProvider<(cb: () => void, time: number) => number>()
|
|
68
|
+
const $ClockRate = createProvider<number>()
|
|
69
|
+
|
|
70
|
+
const $CPU = createProvider<CPU>()
|
|
71
|
+
interface CPU {
|
|
72
|
+
readonly model: string
|
|
73
|
+
readonly numCores: number
|
|
74
|
+
readonly frequency: number
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const $GPU = createProvider<GPU>()
|
|
78
|
+
interface GPU {
|
|
79
|
+
readonly model: string
|
|
80
|
+
readonly numRayTracingCores: number
|
|
81
|
+
readonly memorySize: number
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const $RAM = createProvider<RAM>()
|
|
85
|
+
interface RAM {
|
|
86
|
+
readonly capacity: number
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const $Motherboard = createProvider<Motherboard>()
|
|
90
|
+
interface Motherboard {
|
|
91
|
+
readonly cpu: CPU
|
|
92
|
+
readonly gpu: GPU
|
|
93
|
+
readonly ram: RAM
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
class GeForce911 implements GPU {
|
|
97
|
+
model = 'GeForce911'
|
|
98
|
+
numRayTracingCores = 100
|
|
99
|
+
memorySize = 2048
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
class DefMotherboard implements Motherboard {
|
|
103
|
+
constructor(
|
|
104
|
+
readonly model: string,
|
|
105
|
+
public cpu = $CPU(),
|
|
106
|
+
public gpu = $GPU(),
|
|
107
|
+
public ram = $RAM()
|
|
108
|
+
) {}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
class PC {
|
|
112
|
+
constructor(private name: string, private motherboard = $Motherboard()) {}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
Defining injector through which injection occurs:
|
|
116
|
+
```typescript
|
|
117
|
+
const injector = new Injector()
|
|
118
|
+
```
|
|
119
|
+
Binding providers:
|
|
120
|
+
```typescript
|
|
121
|
+
// binding $CPU provider to exact value
|
|
122
|
+
injector.bindProvider($CPU).toValue({ numCores: 4, model: 't7', frequency: 2000 })
|
|
123
|
+
|
|
124
|
+
// binding $GPU provider to class which conforms to provider's returning type
|
|
125
|
+
// calling asSingleton specifies that value should be created only once
|
|
126
|
+
injector.bindProvider($GPU).toConstructor(GeForce911).asSingleton()
|
|
127
|
+
|
|
128
|
+
// binding $RAM provider to the factory which creates value conforming to
|
|
129
|
+
// provider's returning type
|
|
130
|
+
injector.bindProvider($RAM).toFactory(() => ({
|
|
131
|
+
capacity: Math.floor(Math.random() * 1024),
|
|
132
|
+
}))
|
|
133
|
+
|
|
134
|
+
injector
|
|
135
|
+
.bindProvider($Motherboard)
|
|
136
|
+
.toFactory(
|
|
137
|
+
() =>
|
|
138
|
+
// only one required parameter is passed, the rest are initialized
|
|
139
|
+
// automatically through providers
|
|
140
|
+
new DefMotherboard('Asus ABC-123')
|
|
141
|
+
)
|
|
142
|
+
.asSingleton()
|
|
143
|
+
|
|
144
|
+
// without ignoring compiler would tell you about wrong value's type
|
|
145
|
+
// @ts-expect-error
|
|
146
|
+
injector.bindProvider($ClockRate).toValue('asd')
|
|
147
|
+
|
|
148
|
+
injector.bindProvider($ClockRate).toValue(1000)
|
|
149
|
+
|
|
150
|
+
injector.bindProvider($Clock).toValue(setTimeout)
|
|
151
|
+
```
|
|
152
|
+
Creating instances with all dependencies injected:
|
|
153
|
+
```typescript
|
|
154
|
+
// the first parameter of PC constructor is required, so it is passed into the
|
|
155
|
+
// construction method
|
|
156
|
+
injector.createInstance(PC, 'My pc')
|
|
157
|
+
```
|
|
158
|
+
Directly getting the bound to the providers values:
|
|
159
|
+
```typescript
|
|
160
|
+
injector.getValue($CPU)
|
|
161
|
+
injector.getValue($GPU)
|
|
162
|
+
injector.getValue($RAM)
|
|
163
|
+
|
|
164
|
+
injector.getValue($Clock)(() => console.log('Tick!'), 1000)
|
|
165
|
+
```
|
|
166
|
+
Manually creating instance with all its dependencies:
|
|
167
|
+
```typescript
|
|
168
|
+
new DefMotherboard(
|
|
169
|
+
'Asus xyz',
|
|
170
|
+
{ frequency: 123, model: 'Intel xyz', numCores: 1 },
|
|
171
|
+
{ model: '', memorySize: 0, numRayTracingCores: 1 },
|
|
172
|
+
{ capacity: 123 },
|
|
173
|
+
)
|
|
174
|
+
```
|
|
175
|
+
Calling function through injector:
|
|
176
|
+
```typescript
|
|
177
|
+
injector.callFunc(() => {
|
|
178
|
+
// inside function all providers return bound to them values
|
|
179
|
+
console.log('Inside function')
|
|
180
|
+
console.log('CPU:', $CPU())
|
|
181
|
+
console.log('GPU:', $GPU())
|
|
182
|
+
console.log('RAM:', $RAM())
|
|
183
|
+
})
|
|
184
|
+
```
|
|
185
|
+
It's also possible to bind few providers to the same value. The bound value should conform to
|
|
186
|
+
intersection of all providers' types:
|
|
187
|
+
```typescript
|
|
188
|
+
injector.bindProvider($RAM, $CPU, $GPU).toValue({
|
|
189
|
+
capacity: 10,
|
|
190
|
+
frequency: 100,
|
|
191
|
+
memorySize: 200,
|
|
192
|
+
model: 'ATB-21',
|
|
193
|
+
numCores: 2,
|
|
194
|
+
numRayTracingCores: 60,
|
|
195
|
+
})
|
|
196
|
+
```
|
|
197
|
+
Creating composition of injectors:
|
|
198
|
+
```typescript
|
|
199
|
+
const childInjector = new Injector({ parent: injector })
|
|
200
|
+
```
|
|
201
|
+
Overriding $CPU binding of parent injector inside child injector:
|
|
202
|
+
```typescript
|
|
203
|
+
childInjector.bindProvider($CPU).toValue({ numCores: 1, model: 'z2', frequency: 999 })
|
|
204
|
+
```
|
|
205
|
+
Injecting value to the existing instance:
|
|
206
|
+
```typescript
|
|
207
|
+
const newMotherboard = new class {
|
|
208
|
+
gpu: GPU
|
|
209
|
+
cpu: CPU
|
|
210
|
+
}
|
|
211
|
+
injector.injectValues(newMotherboard, { cpu: $CPU, gpu: $GPU })
|
|
212
|
+
```
|
|
213
|
+
# [Docs](https://protoukr.github.io/nano-injector/)
|
|
214
|
+
|
|
215
|
+
# Contributing
|
|
216
|
+
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
|
|
217
|
+
|
|
218
|
+
Please make sure to update tests as appropriate.
|
|
219
|
+
|
|
220
|
+
## License
|
|
221
|
+
[MIT](https://choosealicense.com/licenses/mit/)
|
package/lib/Binder.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Binder = void 0;
|
|
4
|
-
|
|
3
|
+
exports.Binder = exports.NoCreationMethodSpecifiedError = void 0;
|
|
4
|
+
class NoCreationMethodSpecifiedError extends Error {
|
|
5
|
+
constructor() {
|
|
6
|
+
super('No creation method specified');
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
exports.NoCreationMethodSpecifiedError = NoCreationMethodSpecifiedError;
|
|
5
10
|
/**
|
|
6
11
|
* Class through which is defined how to create value for this binder
|
|
7
12
|
*/
|
|
@@ -75,7 +80,7 @@ class Binder {
|
|
|
75
80
|
if (this.factory != null) {
|
|
76
81
|
return this.factory(this.injector);
|
|
77
82
|
}
|
|
78
|
-
throw new
|
|
83
|
+
throw new NoCreationMethodSpecifiedError();
|
|
79
84
|
}
|
|
80
85
|
}
|
|
81
86
|
exports.Binder = Binder;
|
package/lib/Injector.js
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Injector = exports.$Injector = void 0;
|
|
3
|
+
exports.Injector = exports.$Injector = exports.NoBinderError = exports.CircularDependencyError = void 0;
|
|
4
4
|
const Binder_1 = require("./Binder");
|
|
5
5
|
const Provider_1 = require("./Provider");
|
|
6
6
|
const InjectorsStack_1 = require("./InjectorsStack");
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Thrown when circular dependency is detected
|
|
9
|
+
*/
|
|
10
|
+
class CircularDependencyError extends Error {
|
|
11
|
+
constructor(providers) {
|
|
12
|
+
const providerNames = providers.map(Provider_1.getProviderName)
|
|
13
|
+
.map((name) => name !== null && name !== void 0 ? name : '?');
|
|
14
|
+
super(`Circular dependency detected: ${providerNames.join('->')}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.CircularDependencyError = CircularDependencyError;
|
|
18
|
+
class NoBinderError extends Error {
|
|
19
|
+
constructor(provider) {
|
|
20
|
+
var _a;
|
|
21
|
+
const providerName = (_a = (0, Provider_1.getProviderName)(provider)) !== null && _a !== void 0 ? _a : 'unknown';
|
|
22
|
+
super(`Value of ${providerName} provider is not found`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.NoBinderError = NoBinderError;
|
|
8
26
|
/**
|
|
9
27
|
* Provider which every injector binds itself to
|
|
10
28
|
*/
|
|
@@ -70,31 +88,49 @@ class Injector {
|
|
|
70
88
|
}
|
|
71
89
|
/**
|
|
72
90
|
* Returns bound to the specified provider value. If the value is not found
|
|
73
|
-
*
|
|
91
|
+
* exception is thrown
|
|
74
92
|
* @param provider
|
|
75
93
|
*/
|
|
76
94
|
getValue(provider) {
|
|
77
95
|
const binder = this.getBinder(provider);
|
|
78
|
-
|
|
79
|
-
|
|
96
|
+
this.pushResolvingProvider(provider);
|
|
97
|
+
const value = this.activateAndCall(() => binder.getValue());
|
|
98
|
+
this.popResolvingProvder();
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
tryGetValue(provider, defValue) {
|
|
102
|
+
try {
|
|
103
|
+
return this.getValue(provider);
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
if (err instanceof NoBinderError) {
|
|
107
|
+
return defValue;
|
|
108
|
+
}
|
|
109
|
+
throw err;
|
|
80
110
|
}
|
|
111
|
+
}
|
|
112
|
+
pushResolvingProvider(provider) {
|
|
81
113
|
this.checkCircularDependency(provider);
|
|
82
114
|
this.resolvingProviders.push(provider);
|
|
83
|
-
|
|
115
|
+
}
|
|
116
|
+
popResolvingProvder() {
|
|
84
117
|
this.resolvingProviders.pop();
|
|
85
|
-
|
|
118
|
+
}
|
|
119
|
+
getBinder(provider) {
|
|
120
|
+
const binder = this.tryGetBinderRecursively(provider);
|
|
121
|
+
if (binder == null) {
|
|
122
|
+
throw new NoBinderError(provider);
|
|
123
|
+
}
|
|
124
|
+
return binder;
|
|
86
125
|
}
|
|
87
126
|
/**
|
|
88
127
|
* Finds binder for the specified provider recursively up to the root injector
|
|
89
128
|
* @param provider
|
|
90
129
|
* @private
|
|
91
130
|
*/
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return this.parent.getBinder(provider);
|
|
96
|
-
}
|
|
97
|
-
return binder;
|
|
131
|
+
tryGetBinderRecursively(provider) {
|
|
132
|
+
var _a, _b;
|
|
133
|
+
return ((_a = this.binders.get(provider)) !== null && _a !== void 0 ? _a : (_b = this.parent) === null || _b === void 0 ? void 0 : _b.tryGetBinderRecursively(provider));
|
|
98
134
|
}
|
|
99
135
|
/**
|
|
100
136
|
* Checks is there circular dependency and throws error if so
|
|
@@ -103,13 +139,10 @@ class Injector {
|
|
|
103
139
|
*/
|
|
104
140
|
checkCircularDependency(provider) {
|
|
105
141
|
const i = this.resolvingProviders.indexOf(provider);
|
|
106
|
-
if (i
|
|
107
|
-
|
|
142
|
+
if (i !== -1) {
|
|
143
|
+
const providers = [...this.resolvingProviders.slice(i), provider];
|
|
144
|
+
throw new CircularDependencyError(providers);
|
|
108
145
|
}
|
|
109
|
-
const providers = [...this.resolvingProviders.slice(i), provider]
|
|
110
|
-
.map(Provider_1.getProviderName)
|
|
111
|
-
.map((name) => name !== null && name !== void 0 ? name : '?');
|
|
112
|
-
throw new InjectingError_1.InjectingError(`Circular dependency detected: ${providers.join('->')}`);
|
|
113
146
|
}
|
|
114
147
|
/**
|
|
115
148
|
* Temporary activates this injector calls provided function and returns its value
|
package/lib/InjectorsStack.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.InjectorsStack = void 0;
|
|
3
|
+
exports.InjectorsStack = exports.NoActiveInjectorError = void 0;
|
|
4
|
+
class NoActiveInjectorError extends Error {
|
|
5
|
+
constructor() {
|
|
6
|
+
super('No active injector found');
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
exports.NoActiveInjectorError = NoActiveInjectorError;
|
|
4
10
|
/**
|
|
5
11
|
* Private class for holding current active injector
|
|
6
12
|
*/
|
|
@@ -8,18 +14,20 @@ class _InjectorsStack {
|
|
|
8
14
|
constructor() {
|
|
9
15
|
this.injectors = [];
|
|
10
16
|
}
|
|
11
|
-
get(
|
|
12
|
-
|
|
13
|
-
|
|
17
|
+
get activeInjector() {
|
|
18
|
+
if (this._activeInjector == null) {
|
|
19
|
+
throw new NoActiveInjectorError();
|
|
20
|
+
}
|
|
21
|
+
return this._activeInjector;
|
|
14
22
|
}
|
|
15
23
|
push(injector) {
|
|
16
|
-
if (this.
|
|
17
|
-
this.injectors.push(this.
|
|
24
|
+
if (this._activeInjector != null) {
|
|
25
|
+
this.injectors.push(this._activeInjector);
|
|
18
26
|
}
|
|
19
|
-
this.
|
|
27
|
+
this._activeInjector = injector;
|
|
20
28
|
}
|
|
21
29
|
pop() {
|
|
22
|
-
this.
|
|
30
|
+
this._activeInjector = this.injectors.pop();
|
|
23
31
|
}
|
|
24
32
|
}
|
|
25
33
|
exports.InjectorsStack = new _InjectorsStack();
|
package/lib/Provider.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getProviderName = exports.getProviderID = exports.isProvider = exports.createProvider = void 0;
|
|
4
4
|
const InjectorsStack_1 = require("./InjectorsStack");
|
|
5
|
-
const InjectingError_1 = require("./InjectingError");
|
|
6
5
|
/**
|
|
7
6
|
* Symbol used for storing the id of a provider
|
|
8
7
|
*/
|
|
@@ -17,15 +16,13 @@ let PROVIDER_ID = 0;
|
|
|
17
16
|
* @param name name for this provider used mainly for debugging purposes
|
|
18
17
|
*/
|
|
19
18
|
function createProvider(name) {
|
|
20
|
-
function provider(
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
function provider(defValue) {
|
|
20
|
+
const hasDefaultValue = arguments.length > 0;
|
|
21
|
+
const { activeInjector } = InjectorsStack_1.InjectorsStack;
|
|
22
|
+
if (hasDefaultValue) {
|
|
23
|
+
return activeInjector.tryGetValue(provider, defValue);
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
return args[0];
|
|
27
|
-
}
|
|
28
|
-
throw new InjectingError_1.InjectingError(`Value of ${name !== null && name !== void 0 ? name : 'unknown'} provider is not found`);
|
|
25
|
+
return activeInjector.getValue(provider);
|
|
29
26
|
}
|
|
30
27
|
provider[NAME_SYMBOL] = name;
|
|
31
28
|
provider[ID_SYMBOL] = PROVIDER_ID++;
|
package/lib/index.js
CHANGED
|
@@ -12,4 +12,3 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
__exportStar(require("./Injector"), exports);
|
|
14
14
|
__exportStar(require("./Provider"), exports);
|
|
15
|
-
__exportStar(require("./InjectingError"), exports);
|
package/package.json
CHANGED
package/typings/Binder.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { Injector } from './Injector';
|
|
2
|
+
export declare class NoCreationMethodSpecifiedError extends Error {
|
|
3
|
+
constructor();
|
|
4
|
+
}
|
|
2
5
|
/**
|
|
3
6
|
* Class through which is defined how to create value for this binder
|
|
4
7
|
*/
|
|
@@ -29,7 +32,7 @@ export declare class Binder<T> {
|
|
|
29
32
|
* will be ignored
|
|
30
33
|
* @param factory
|
|
31
34
|
*/
|
|
32
|
-
toFactory(factory: (injector
|
|
35
|
+
toFactory(factory: (injector: Injector) => T): Binder<T>;
|
|
33
36
|
/**
|
|
34
37
|
* Defines whether value should be a singleton. If yes the value will be created only once
|
|
35
38
|
* and the same instance will be returned forever
|
package/typings/Injector.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { Binder } from './Binder';
|
|
2
|
-
import { Provider } from './Provider';
|
|
2
|
+
import { Provider, ProviderValueType } from './Provider';
|
|
3
|
+
/**
|
|
4
|
+
* Thrown when circular dependency is detected
|
|
5
|
+
*/
|
|
6
|
+
export declare class CircularDependencyError extends Error {
|
|
7
|
+
constructor(providers: Array<Provider<unknown>>);
|
|
8
|
+
}
|
|
9
|
+
export declare class NoBinderError extends Error {
|
|
10
|
+
constructor(provider: Provider<unknown>);
|
|
11
|
+
}
|
|
3
12
|
declare type UnionToIntersection<T> = (T extends any ? (k: T) => void : never) extends (k: infer R) => void ? R : never;
|
|
4
13
|
/**
|
|
5
14
|
* Provider which every injector binds itself to
|
|
@@ -42,25 +51,35 @@ export declare class Injector {
|
|
|
42
51
|
* @param type
|
|
43
52
|
* @param args
|
|
44
53
|
*/
|
|
45
|
-
createInstance<ClassT extends new (...args:
|
|
54
|
+
createInstance<ClassT extends new (...args: any[]) => unknown>(type: ClassT, ...args: ConstructorParameters<ClassT>): InstanceType<ClassT>;
|
|
46
55
|
/**
|
|
47
56
|
* Activates this injector and calls the function with provided arguments
|
|
48
57
|
* @param func function which should be called
|
|
49
58
|
* @param args args which should be passed to the called function
|
|
50
59
|
*/
|
|
51
|
-
callFunc<FuncT extends (...args:
|
|
60
|
+
callFunc<FuncT extends (...args: any[]) => unknown>(func: FuncT, ...args: Parameters<FuncT>): ReturnType<FuncT>;
|
|
61
|
+
/**
|
|
62
|
+
* Returns bound to the specified provider value. If the value is not found
|
|
63
|
+
* exception is thrown
|
|
64
|
+
* @param provider
|
|
65
|
+
*/
|
|
66
|
+
getValue<ProviderT extends Provider<unknown>>(provider: ProviderT): ProviderValueType<ProviderT>;
|
|
52
67
|
/**
|
|
53
68
|
* Returns bound to the specified provider value. If the value is not found
|
|
54
|
-
*
|
|
69
|
+
* default value is returned
|
|
55
70
|
* @param provider
|
|
56
71
|
*/
|
|
57
|
-
|
|
72
|
+
tryGetValue<ProviderT extends Provider<unknown>>(provider: ProviderT): ProviderValueType<ProviderT> | undefined;
|
|
73
|
+
tryGetValue<ProviderT extends Provider<unknown>, DefValT>(provider: ProviderT, defVal: DefValT): ProviderValueType<ProviderT> | DefValT;
|
|
74
|
+
private pushResolvingProvider;
|
|
75
|
+
private popResolvingProvder;
|
|
76
|
+
private getBinder;
|
|
58
77
|
/**
|
|
59
78
|
* Finds binder for the specified provider recursively up to the root injector
|
|
60
79
|
* @param provider
|
|
61
80
|
* @private
|
|
62
81
|
*/
|
|
63
|
-
private
|
|
82
|
+
private tryGetBinderRecursively;
|
|
64
83
|
/**
|
|
65
84
|
* Checks is there circular dependency and throws error if so
|
|
66
85
|
* @param provider
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { Injector } from './Injector';
|
|
2
|
-
|
|
2
|
+
export declare class NoActiveInjectorError extends Error {
|
|
3
|
+
constructor();
|
|
4
|
+
}
|
|
3
5
|
/**
|
|
4
6
|
* Private class for holding current active injector
|
|
5
7
|
*/
|
|
6
8
|
declare class _InjectorsStack {
|
|
7
9
|
private readonly injectors;
|
|
8
|
-
private
|
|
9
|
-
get
|
|
10
|
+
private _activeInjector?;
|
|
11
|
+
get activeInjector(): Injector;
|
|
10
12
|
push(injector: Injector): void;
|
|
11
13
|
pop(): void;
|
|
12
14
|
}
|
package/typings/Provider.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare type ProviderValueType<T extends Provider<any>> = T extends Provider<infer R> ? R : never;
|
|
1
2
|
/**
|
|
2
3
|
* Symbol used for storing the id of a provider
|
|
3
4
|
*/
|
|
@@ -45,5 +46,5 @@ export declare function getProviderID(provider: Provider<unknown>): number;
|
|
|
45
46
|
* Returns the name of the specified provider
|
|
46
47
|
* @param provider
|
|
47
48
|
*/
|
|
48
|
-
export declare function getProviderName(provider: Provider<unknown>): string;
|
|
49
|
+
export declare function getProviderName(provider: Provider<unknown>): string | undefined;
|
|
49
50
|
export {};
|
package/typings/index.d.ts
CHANGED
package/lib/InjectingError.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.InjectingError = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* Just an error class, an instance of which is fired in some cases
|
|
6
|
-
*/
|
|
7
|
-
class InjectingError extends Error {
|
|
8
|
-
}
|
|
9
|
-
exports.InjectingError = InjectingError;
|