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 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
- const InjectingError_1 = require("./InjectingError");
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 InjectingError_1.InjectingError('Creation method isn\'t specified');
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
- const InjectingError_1 = require("./InjectingError");
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
- * undefined is returned
91
+ * exception is thrown
74
92
  * @param provider
75
93
  */
76
94
  getValue(provider) {
77
95
  const binder = this.getBinder(provider);
78
- if (binder == null) {
79
- return undefined;
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
- const value = this.activateAndCall(() => binder.getValue());
115
+ }
116
+ popResolvingProvder() {
84
117
  this.resolvingProviders.pop();
85
- return value;
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
- getBinder(provider) {
93
- const binder = this.binders.get(provider);
94
- if (binder == null && this.parent != null) {
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 === -1) {
107
- return;
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
@@ -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(provider) {
12
- var _a;
13
- return (_a = this.activeInjector) === null || _a === void 0 ? void 0 : _a.getValue(provider);
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.activeInjector != null) {
17
- this.injectors.push(this.activeInjector);
24
+ if (this._activeInjector != null) {
25
+ this.injectors.push(this._activeInjector);
18
26
  }
19
- this.activeInjector = injector;
27
+ this._activeInjector = injector;
20
28
  }
21
29
  pop() {
22
- this.activeInjector = this.injectors.pop();
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(...args) {
21
- const value = InjectorsStack_1.InjectorsStack.get(provider);
22
- if (value !== undefined) {
23
- return value;
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
- if (args.length !== 0) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nano-injector",
3
- "version": "1.0.1",
3
+ "version": "1.0.4",
4
4
  "description": "Miniature dependency injection library for TypeScript and JavaScript",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./typings/index.d.ts",
@@ -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?: Injector) => T): Binder<T>;
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
@@ -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: unknown[]) => unknown, CtorParamsT extends ConstructorParameters<ClassT>, InstT extends InstanceType<ClassT>>(type: ClassT, ...args: CtorParamsT): InstT;
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: unknown[]) => unknown, ParamsT extends Parameters<FuncT>, ReturnT extends ReturnType<FuncT>>(func: FuncT, ...args: ParamsT): ReturnT;
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
- * undefined is returned
69
+ * default value is returned
55
70
  * @param provider
56
71
  */
57
- getValue<ProviderT extends Provider<unknown>, ValueT extends ProviderT extends Provider<infer R> ? R : never>(provider: ProviderT): ValueT | undefined;
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 getBinder;
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
- import { Provider } from './Provider';
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 activeInjector;
9
- get<ProviderT extends Provider<unknown>, ValueT extends ProviderT extends Provider<infer R> ? R : never>(provider: ProviderT): ValueT | undefined;
10
+ private _activeInjector?;
11
+ get activeInjector(): Injector;
10
12
  push(injector: Injector): void;
11
13
  pop(): void;
12
14
  }
@@ -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 {};
@@ -1,3 +1,2 @@
1
1
  export * from './Injector';
2
2
  export * from './Provider';
3
- export * from './InjectingError';
@@ -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;
@@ -1,5 +0,0 @@
1
- /**
2
- * Just an error class, an instance of which is fired in some cases
3
- */
4
- export declare class InjectingError extends Error {
5
- }