proxydi 0.0.9 → 0.0.11
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/CHANGELOG.md +85 -0
- package/README.md +64 -3
- package/dist/ProxyDiContainer.d.ts +7 -1
- package/dist/index.cjs +128 -56
- package/dist/index.d.ts +2 -2
- package/dist/index.js +127 -55
- package/dist/index.umd.js +128 -56
- package/dist/injectable.d.ts +17 -0
- package/dist/proxy.constuctor.d.ts +2 -0
- package/dist/types.d.ts +15 -5
- package/package.json +1 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [[0.0.10](https://www.npmjs.com/package/proxydi/v/0.0.11)] - 2025-03-03
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- resolveInjectables() method [#12](https://github.com/proxy-di/proxydi/pull/13)
|
|
13
|
+
|
|
14
|
+
## [[0.0.10](https://www.npmjs.com/package/proxydi/v/0.0.10)] - 2025-03-02
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Renamed @autoInjectable to @injectable
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Experimental constructor injections via @injectable decorator [#12](https://github.com/proxy-di/proxydi/pull/12)
|
|
23
|
+
|
|
24
|
+
## [[0.0.9](https://www.npmjs.com/package/proxydi/v/0.0.9)] - 2025-02-27
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- resolveAll() function to find all dependencies of a given kind [#11](https://github.com/proxy-di/proxydi/pull/11)
|
|
29
|
+
|
|
30
|
+
## [[0.0.8](https://www.npmjs.com/package/proxydi/v/0.0.8)] - 2025-02-26
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- API improvements [#10](https://github.com/proxy-di/proxydi/pull/10)
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- Added access to child containers
|
|
39
|
+
- PROXYDI_CONTAINER and DEPENDENCY_ID now public
|
|
40
|
+
- Enhanced hierarchy documentation
|
|
41
|
+
|
|
42
|
+
## [[0.0.7](https://www.npmjs.com/package/proxydi/v/0.0.7)] - 2025-02-21
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
- TypeDoc integration [#7](https://github.com/proxy-di/proxydi/pull/7)
|
|
47
|
+
- Action to publish documentation on GitHub Pages
|
|
48
|
+
|
|
49
|
+
## [[0.0.6](https://www.npmjs.com/package/proxydi/v/0.0.6)] - 2025-02-21
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
|
|
53
|
+
- Improved API [#6](https://github.com/proxy-di/proxydi/pull/6)
|
|
54
|
+
|
|
55
|
+
## [[0.0.5](https://www.npmjs.com/package/proxydi/v/0.0.5)] - 2025-02-20
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- Baking dependencies [#5](https://github.com/proxy-di/proxydi/pull/5)
|
|
60
|
+
- 100% test coverage
|
|
61
|
+
- Changed API with new terminology
|
|
62
|
+
|
|
63
|
+
## [[0.0.4](https://www.npmjs.com/package/proxydi/v/0.0.4)] - 2025-02-11
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
- Implemented proxy per instance instead of per container [#2](https://github.com/proxy-di/proxydi/pull/2)
|
|
68
|
+
- Ability to register anything as an instance
|
|
69
|
+
- Changed settings to object format
|
|
70
|
+
- Added settings presets
|
|
71
|
+
|
|
72
|
+
### Fixed
|
|
73
|
+
|
|
74
|
+
- Made injectables known
|
|
75
|
+
|
|
76
|
+
## [[0.0.3](https://www.npmjs.com/package/proxydi/v/0.0.3)] - 2025-02-07
|
|
77
|
+
|
|
78
|
+
### Added
|
|
79
|
+
|
|
80
|
+
- Draft DI-container implementation
|
|
81
|
+
- @injectable decorator
|
|
82
|
+
- Basic project setup with CI/CD
|
|
83
|
+
- Testing framework integration
|
|
84
|
+
- Basic package configuration
|
|
85
|
+
- Repository initialization
|
package/README.md
CHANGED
|
@@ -12,9 +12,13 @@ Core features:
|
|
|
12
12
|
- Automatically resolves circular dependencies with no performance impact
|
|
13
13
|
- Resolves dependencies in the context of a particular container
|
|
14
14
|
- Supports hierarchical containers with the ability to resolve dependencies in both directions
|
|
15
|
-
- Matches dependencies by unique identifiers or automatically using class names and property names
|
|
16
15
|
- Currently under active development, the API may change until version 0.1.0
|
|
17
16
|
|
|
17
|
+
Eperimemntal features:
|
|
18
|
+
|
|
19
|
+
- Construtor injections (see unit tests for examples)
|
|
20
|
+
- Matches dependencies by unique identifiers or automatically using class names and property names
|
|
21
|
+
|
|
18
22
|
## Quick start
|
|
19
23
|
|
|
20
24
|
> If you are using React, you should use the React wrapper for ProxyDi instead of this library directly: [@proxydi/react](https://github.com/proxy-di/proxydi-react)
|
|
@@ -218,7 +222,7 @@ class UnderwaterShield {
|
|
|
218
222
|
|
|
219
223
|
In this example, the shield perk increases character health if it is underwater. The perk receives both level and character using the @inject() decorator, the same way as we have seen so far.
|
|
220
224
|
|
|
221
|
-
|
|
225
|
+
### Resolving dependencies from children
|
|
222
226
|
|
|
223
227
|
Backward bonus of containers hierarchy, each top level dependency is free to use all dependency from the bottom:
|
|
224
228
|
|
|
@@ -239,10 +243,67 @@ In this example, the character activates all its perks, which are registered in
|
|
|
239
243
|
|
|
240
244
|
### Reference to the container
|
|
241
245
|
|
|
242
|
-
Here you should be wondering, how [resolveAll()](https://proxy-di.github.io/proxydi/functions/resolveAll.html
|
|
246
|
+
Here you should be wondering, how [resolveAll()](https://proxy-di.github.io/proxydi/functions/resolveAll.html) function knows about the container, to which character belongs. The answer - each time when dependency is registered in the ProxyDiContainer, it saves a reference to itself in this dependency instance. So, when you call resolveAll() function, it just takes this reference from the instance and then recursively resolves all asked dependencies from this container and all its children and children of children and so on.
|
|
243
247
|
|
|
244
248
|
Despite this explanation is a little bit complicated, the example is still simple, the character just activates all its perks.
|
|
245
249
|
|
|
250
|
+
## Injectable classes
|
|
251
|
+
|
|
252
|
+
Look at the following example:
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
@injectable()
|
|
256
|
+
class GameEngine {
|
|
257
|
+
start = () => console.log('Game started');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const container = new ProxyDiContainer();
|
|
261
|
+
|
|
262
|
+
const gameEngine = container.resolve(GameEngine);
|
|
263
|
+
gameEngine.start();
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
```shell
|
|
267
|
+
> Game started
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Two things happen here:
|
|
271
|
+
|
|
272
|
+
1. We use the [@injectable()](https://proxy-di.github.io/proxydi/functions/injectable.html) decorator to mark the class as injectable. This allows us to resolve the dependency without registering it in the container.
|
|
273
|
+
|
|
274
|
+
2. To resolve the dependency instance, we pass the injectable class itself as the dependency identifier. This is possible because ProxyDi automatically uses the class name as the dependency identifier when none is explicitly provided. As an additional bonus, the `gameEngine` has a type `GameEngine`.
|
|
275
|
+
|
|
276
|
+
### Implicitly resolving injectable() dependencies
|
|
277
|
+
|
|
278
|
+
There are a few important nuances about the `@injectable()` decorator to keep in mind. The first is how these dependencies are resolved. Consider this example:
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
const parent = new ProxyDiContainer();
|
|
282
|
+
const child = parent.createChildContainer();
|
|
283
|
+
|
|
284
|
+
const engine1 = child.resolve(GameEngine);
|
|
285
|
+
const engine2 = parent.resolve(GameEngine);
|
|
286
|
+
|
|
287
|
+
engine1 === engine2; // false
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Here we create a hierarchy of containers and resolve the same dependency from the child container first and then from the parent container. The result is two different instances of the GameEngine class. This happens because during the first resolution, the child container doesn't find GameEngine in either itself or its parent container, so it creates a new instance and stores it in itself. When the parent container resolves GameEngine, it creates another instance since he knowns nothing about child depenencies
|
|
291
|
+
|
|
292
|
+
There are two ways to avoid this behavior:
|
|
293
|
+
|
|
294
|
+
1. Don't use the `@injectable()` decorator and always register dependencies explicitly
|
|
295
|
+
2. Use the `registerInjectables()` method to create instances for all injectable dependencies in the container. In this case every time when you resolve injectable dependency, you will get the same instance:
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
const parent = new ProxyDiContainer().registerInjectables();
|
|
299
|
+
const child = parent.createChildContainer();
|
|
300
|
+
|
|
301
|
+
const engine1 = child.resolve(GameEngine);
|
|
302
|
+
const engine2 = parent.resolve(GameEngine);
|
|
303
|
+
|
|
304
|
+
engine1 === engine2; // true now
|
|
305
|
+
```
|
|
306
|
+
|
|
246
307
|
## Rewriting dependencies
|
|
247
308
|
|
|
248
309
|
By default, ProxyDi doesn't allow rewriting dependencies in the container. After a dependency becomes known to the container, any attempt to register a new dependency with the same dependency ID will throw an Error:
|
|
@@ -46,6 +46,7 @@ export declare class ProxyDiContainer implements IProxyDiContainer {
|
|
|
46
46
|
*/
|
|
47
47
|
register<T>(DependencyClass: DependencyClass<T>, dependencyId: DependencyId): T & ContainerizedDependency;
|
|
48
48
|
register<T>(dependency: T extends new (...args: any[]) => any ? never : T, dependencyId: DependencyId): T & ContainerizedDependency;
|
|
49
|
+
private createInstance;
|
|
49
50
|
/**
|
|
50
51
|
* Internal method that implements registeration of dependency and prepare it for injection.
|
|
51
52
|
* @param dependencyId The unique identifier of the dependency.
|
|
@@ -65,12 +66,17 @@ export declare class ProxyDiContainer implements IProxyDiContainer {
|
|
|
65
66
|
* @throws Error if the dependency cannot be found or is not auto injectable.
|
|
66
67
|
*/
|
|
67
68
|
resolve<T>(dependencyId: DependencyId): T & ContainerizedDependency;
|
|
68
|
-
resolve<T extends
|
|
69
|
+
resolve<T extends DependencyClass<any>>(SomeClass: T): InstanceType<T> & ContainerizedDependency;
|
|
69
70
|
/**
|
|
70
71
|
* Injects dependencies to the given object based on its defined injections metadata. Does not affect the container.
|
|
71
72
|
* @param injectionsOwner The object to inject dependencies into.
|
|
72
73
|
*/
|
|
73
74
|
injectDependenciesTo(injectionsOwner: any): void;
|
|
75
|
+
/**
|
|
76
|
+
* Creates instances for all injectable classes and registers them in this container.
|
|
77
|
+
* @returns This container to allow use along with constructor.
|
|
78
|
+
*/
|
|
79
|
+
registerInjectables(): this;
|
|
74
80
|
/**
|
|
75
81
|
* Finalizes dependency injections, prevents further rewriting of dependencies,
|
|
76
82
|
* and recursively bakes injections for child containers.
|
package/dist/index.cjs
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const INJECTIONS = Symbol('injections');
|
|
4
|
+
/**
|
|
5
|
+
* This symbol constant defines a property name.
|
|
6
|
+
* This property is present in each dependency instance that was registered in ProxyDiContainer.
|
|
7
|
+
* The property stores the dependency identifier that should be used to resolve dependency from the container where it was registered.
|
|
8
|
+
*/
|
|
4
9
|
const DEPENDENCY_ID = Symbol('DependencyId');
|
|
10
|
+
/**
|
|
11
|
+
* This symbol constant defines a property name.
|
|
12
|
+
* This property is present in each dependency instance that was registered in ProxyDiContainer.
|
|
13
|
+
* The property stores a reference to the ProxyDiContainer in which the dependency was registered.
|
|
14
|
+
*/
|
|
15
|
+
const PROXYDI_CONTAINER = Symbol('proxyDiContainer');
|
|
5
16
|
const IS_INJECTION_PROXY = Symbol('isInjectionProxy');
|
|
6
17
|
const INJECTION_OWNER = Symbol('injectionOwner');
|
|
7
|
-
const PROXYDY_CONTAINER = Symbol('proxyDiContainer');
|
|
8
18
|
const IS_INSTANCE_PROXY = Symbol('isInstanceProxy');
|
|
9
19
|
|
|
10
20
|
/**
|
|
@@ -37,31 +47,40 @@ const inject = (dependencyId) => {
|
|
|
37
47
|
};
|
|
38
48
|
};
|
|
39
49
|
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
*
|
|
44
|
-
* @param dependencyId - Optional dependency identifier. If omitted, the class name is used.
|
|
45
|
-
* @returns A class decorator function.
|
|
46
|
-
*
|
|
47
|
-
* Note: During dependency resolution, any container that does not have an instance for the specified dependency identifier
|
|
48
|
-
* will create an instance of the decorated class. However, if a container already has an instance with that identifier
|
|
49
|
-
* prior to resolution, the decorated class will be ignored by that container.
|
|
50
|
-
*/
|
|
51
|
-
const autoInjectable = (dependencyId) => {
|
|
50
|
+
const injectableClasses = {};
|
|
51
|
+
const constructorInjections = {};
|
|
52
|
+
function injectable(dependencyOrDependencies, autoInjecions) {
|
|
52
53
|
return function (value, context) {
|
|
53
|
-
if ((context === null || context === undefined ? undefined : context.kind)
|
|
54
|
-
|
|
55
|
-
if (autoInjectableClasses[name]) {
|
|
56
|
-
throw new Error(`ProxyDi autoInjectable already has dependency ID: ${String(name)}`);
|
|
57
|
-
}
|
|
58
|
-
autoInjectableClasses[name] = value;
|
|
54
|
+
if ((context === null || context === undefined ? undefined : context.kind) !== 'class') {
|
|
55
|
+
throw new Error('@injectable decorator should decorate classes');
|
|
59
56
|
}
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
const name = dependencyOrDependencies
|
|
58
|
+
? Array.isArray(dependencyOrDependencies)
|
|
59
|
+
? context.name
|
|
60
|
+
: dependencyOrDependencies
|
|
61
|
+
: context.name;
|
|
62
|
+
const injectToConstructor = dependencyOrDependencies
|
|
63
|
+
? Array.isArray(dependencyOrDependencies)
|
|
64
|
+
? dependencyOrDependencies
|
|
65
|
+
: autoInjecions
|
|
66
|
+
: autoInjecions;
|
|
67
|
+
if (injectableClasses[name]) {
|
|
68
|
+
throw new Error(`ProxyDi has already regisered dependency ${String(name)} by @injectable`);
|
|
69
|
+
}
|
|
70
|
+
injectableClasses[name] = value;
|
|
71
|
+
if (injectToConstructor) {
|
|
72
|
+
constructorInjections[name] = injectToConstructor;
|
|
62
73
|
}
|
|
63
74
|
};
|
|
64
|
-
}
|
|
75
|
+
}
|
|
76
|
+
function findInjectableId(injectable) {
|
|
77
|
+
for (const [id, DependencyClass] of Object.entries(injectableClasses)) {
|
|
78
|
+
if (DependencyClass === injectable) {
|
|
79
|
+
return id;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
throw new Error(`Class is not @injectable: ${injectable.name}`);
|
|
83
|
+
}
|
|
65
84
|
|
|
66
85
|
const DEFAULT_SETTINGS = {
|
|
67
86
|
allowRegisterAnything: false,
|
|
@@ -74,7 +93,7 @@ class InjectionProxy {
|
|
|
74
93
|
constructor(onwer, container) {
|
|
75
94
|
this[_a] = true;
|
|
76
95
|
this[INJECTION_OWNER] = onwer;
|
|
77
|
-
this[
|
|
96
|
+
this[PROXYDI_CONTAINER] = container;
|
|
78
97
|
}
|
|
79
98
|
}
|
|
80
99
|
_a = IS_INJECTION_PROXY;
|
|
@@ -128,6 +147,49 @@ function makeDependencyProxy(dependency) {
|
|
|
128
147
|
});
|
|
129
148
|
}
|
|
130
149
|
|
|
150
|
+
function makeConstructorDependencyProxy(container, dependencyId) {
|
|
151
|
+
// let values: Record<string | symbol, any> = {};
|
|
152
|
+
// let wasResolved = false;
|
|
153
|
+
function getDependency() {
|
|
154
|
+
if (container.isKnown(dependencyId)) {
|
|
155
|
+
const dependency = container.resolve(dependencyId);
|
|
156
|
+
// if (!wasResolved) {
|
|
157
|
+
// for (const key in values) {
|
|
158
|
+
// dependency[key] = values[key];
|
|
159
|
+
// }
|
|
160
|
+
// values = {};
|
|
161
|
+
// wasResolved = true;
|
|
162
|
+
// }
|
|
163
|
+
return dependency;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
throw new Error(`Unknown dependency: ${String(dependencyId)}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return new Proxy({}, {
|
|
170
|
+
get: function (target, prop, receiver) {
|
|
171
|
+
// if (values[prop]) {
|
|
172
|
+
// return values[prop];
|
|
173
|
+
// }
|
|
174
|
+
const dependency = getDependency();
|
|
175
|
+
return Reflect.get(dependency, prop, receiver);
|
|
176
|
+
},
|
|
177
|
+
set: function (_target, prop, value) {
|
|
178
|
+
// if (!container.isKnown(dependencyId)) {
|
|
179
|
+
// values[prop] = value;
|
|
180
|
+
// return true;
|
|
181
|
+
// } else {
|
|
182
|
+
const dependency = getDependency();
|
|
183
|
+
return Reflect.set(dependency, prop, value);
|
|
184
|
+
//}
|
|
185
|
+
},
|
|
186
|
+
has: function (_target, prop) {
|
|
187
|
+
const dependency = getDependency();
|
|
188
|
+
return Reflect.has(dependency, prop);
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
131
193
|
/**
|
|
132
194
|
* A dependency injection container
|
|
133
195
|
*/
|
|
@@ -163,7 +225,7 @@ class ProxyDiContainer {
|
|
|
163
225
|
let dependencyInstance;
|
|
164
226
|
const isClass = typeof dependency === 'function';
|
|
165
227
|
if (isClass) {
|
|
166
|
-
dependencyInstance =
|
|
228
|
+
dependencyInstance = this.createInstance(dependency, dependencyId);
|
|
167
229
|
}
|
|
168
230
|
else {
|
|
169
231
|
dependencyInstance = dependency;
|
|
@@ -173,11 +235,20 @@ class ProxyDiContainer {
|
|
|
173
235
|
}
|
|
174
236
|
}
|
|
175
237
|
if (typeof dependencyInstance === 'object') {
|
|
176
|
-
dependencyInstance[
|
|
238
|
+
dependencyInstance[PROXYDI_CONTAINER] = this;
|
|
177
239
|
}
|
|
178
240
|
this.registerImpl(dependencyInstance, dependencyId);
|
|
179
241
|
return dependencyInstance;
|
|
180
242
|
}
|
|
243
|
+
createInstance(Dependency, dependencyId) {
|
|
244
|
+
const paramIds = constructorInjections[dependencyId] || [];
|
|
245
|
+
const params = [];
|
|
246
|
+
for (const id of paramIds) {
|
|
247
|
+
const param = makeConstructorDependencyProxy(this, id);
|
|
248
|
+
params.push(param);
|
|
249
|
+
}
|
|
250
|
+
return new Dependency(...params);
|
|
251
|
+
}
|
|
181
252
|
/**
|
|
182
253
|
* Internal method that implements registeration of dependency and prepare it for injection.
|
|
183
254
|
* @param dependencyId The unique identifier of the dependency.
|
|
@@ -199,39 +270,34 @@ class ProxyDiContainer {
|
|
|
199
270
|
return !!(this.parentDependencyProxies[dependencyId] ||
|
|
200
271
|
this.dependencies[dependencyId] ||
|
|
201
272
|
(this.parent && this.parent.isKnown(dependencyId)) ||
|
|
202
|
-
|
|
273
|
+
injectableClasses[dependencyId]);
|
|
203
274
|
}
|
|
204
|
-
resolve(
|
|
205
|
-
if (typeof
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
return this.resolve(dependencyId);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
throw new Error(`Class is not auto injectable: ${param.name}`);
|
|
275
|
+
resolve(dependency) {
|
|
276
|
+
if (typeof dependency === 'function') {
|
|
277
|
+
const id = findInjectableId(dependency);
|
|
278
|
+
return this.resolve(id);
|
|
212
279
|
}
|
|
213
|
-
if (!this.isKnown(
|
|
214
|
-
throw new Error(`Can't resolve unknown dependency: ${String(
|
|
280
|
+
if (!this.isKnown(dependency)) {
|
|
281
|
+
throw new Error(`Can't resolve unknown dependency: ${String(dependency)}`);
|
|
215
282
|
}
|
|
216
|
-
const proxy = this.parentDependencyProxies[
|
|
283
|
+
const proxy = this.parentDependencyProxies[dependency];
|
|
217
284
|
if (proxy) {
|
|
218
285
|
return proxy;
|
|
219
286
|
}
|
|
220
|
-
const
|
|
221
|
-
if (
|
|
222
|
-
if (
|
|
223
|
-
typeof
|
|
287
|
+
const instance = this.findDependency(dependency);
|
|
288
|
+
if (instance) {
|
|
289
|
+
if (instance[PROXYDI_CONTAINER] !== this &&
|
|
290
|
+
typeof instance === 'object' &&
|
|
224
291
|
this.settings.resolveInContainerContext) {
|
|
225
|
-
const proxy = makeDependencyProxy(
|
|
292
|
+
const proxy = makeDependencyProxy(instance);
|
|
226
293
|
this.injectDependenciesTo(proxy);
|
|
227
|
-
this.parentDependencyProxies[
|
|
294
|
+
this.parentDependencyProxies[dependency] = proxy;
|
|
228
295
|
return proxy;
|
|
229
296
|
}
|
|
230
|
-
return
|
|
297
|
+
return instance;
|
|
231
298
|
}
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
return this.register(autoDependency, param);
|
|
299
|
+
const InjectableClass = injectableClasses[dependency];
|
|
300
|
+
return this.register(InjectableClass, dependency);
|
|
235
301
|
}
|
|
236
302
|
/**
|
|
237
303
|
* Injects dependencies to the given object based on its defined injections metadata. Does not affect the container.
|
|
@@ -244,6 +310,16 @@ class ProxyDiContainer {
|
|
|
244
310
|
injection.set(injectionsOwner, dependencyProxy);
|
|
245
311
|
});
|
|
246
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* Creates instances for all injectable classes and registers them in this container.
|
|
315
|
+
* @returns This container to allow use along with constructor.
|
|
316
|
+
*/
|
|
317
|
+
registerInjectables() {
|
|
318
|
+
for (const [dependencyId, InjectableClass] of Object.entries(injectableClasses)) {
|
|
319
|
+
this.register(InjectableClass, dependencyId);
|
|
320
|
+
}
|
|
321
|
+
return this;
|
|
322
|
+
}
|
|
247
323
|
/**
|
|
248
324
|
* Finalizes dependency injections, prevents further rewriting of dependencies,
|
|
249
325
|
* and recursively bakes injections for child containers.
|
|
@@ -376,14 +452,10 @@ function isDependency(dependencyOrId) {
|
|
|
376
452
|
|
|
377
453
|
function resolveAll(instance, dependencyId) {
|
|
378
454
|
if (typeof dependencyId === 'function') {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
return resolveAll(instance, id);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
throw new Error(`Class is not auto injectable: ${dependencyId.name}`);
|
|
455
|
+
const id = findInjectableId(dependencyId);
|
|
456
|
+
return resolveAll(instance, id);
|
|
385
457
|
}
|
|
386
|
-
const container = instance[
|
|
458
|
+
const container = instance[PROXYDI_CONTAINER];
|
|
387
459
|
if (!container) {
|
|
388
460
|
throw new Error('Instance is not registered in any container');
|
|
389
461
|
}
|
|
@@ -401,8 +473,8 @@ function recursiveResolveAll(container, dependencyId) {
|
|
|
401
473
|
}
|
|
402
474
|
|
|
403
475
|
exports.DEPENDENCY_ID = DEPENDENCY_ID;
|
|
404
|
-
exports.
|
|
476
|
+
exports.PROXYDI_CONTAINER = PROXYDI_CONTAINER;
|
|
405
477
|
exports.ProxyDiContainer = ProxyDiContainer;
|
|
406
|
-
exports.autoInjectable = autoInjectable;
|
|
407
478
|
exports.inject = inject;
|
|
479
|
+
exports.injectable = injectable;
|
|
408
480
|
exports.resolveAll = resolveAll;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { inject } from './inject';
|
|
2
2
|
export { ProxyDiContainer } from './ProxyDiContainer';
|
|
3
|
-
export {
|
|
4
|
-
export { Injection, DependencyId, DependencyClass, ContainerSettings,
|
|
3
|
+
export { injectable } from './injectable';
|
|
4
|
+
export { Injection, DependencyId, DependencyClass, ContainerSettings, PROXYDI_CONTAINER, DEPENDENCY_ID, } from './types';
|
|
5
5
|
export { resolveAll } from './resolveAll';
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
const INJECTIONS = Symbol('injections');
|
|
2
|
+
/**
|
|
3
|
+
* This symbol constant defines a property name.
|
|
4
|
+
* This property is present in each dependency instance that was registered in ProxyDiContainer.
|
|
5
|
+
* The property stores the dependency identifier that should be used to resolve dependency from the container where it was registered.
|
|
6
|
+
*/
|
|
2
7
|
const DEPENDENCY_ID = Symbol('DependencyId');
|
|
8
|
+
/**
|
|
9
|
+
* This symbol constant defines a property name.
|
|
10
|
+
* This property is present in each dependency instance that was registered in ProxyDiContainer.
|
|
11
|
+
* The property stores a reference to the ProxyDiContainer in which the dependency was registered.
|
|
12
|
+
*/
|
|
13
|
+
const PROXYDI_CONTAINER = Symbol('proxyDiContainer');
|
|
3
14
|
const IS_INJECTION_PROXY = Symbol('isInjectionProxy');
|
|
4
15
|
const INJECTION_OWNER = Symbol('injectionOwner');
|
|
5
|
-
const PROXYDY_CONTAINER = Symbol('proxyDiContainer');
|
|
6
16
|
const IS_INSTANCE_PROXY = Symbol('isInstanceProxy');
|
|
7
17
|
|
|
8
18
|
/**
|
|
@@ -35,31 +45,40 @@ const inject = (dependencyId) => {
|
|
|
35
45
|
};
|
|
36
46
|
};
|
|
37
47
|
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
*
|
|
42
|
-
* @param dependencyId - Optional dependency identifier. If omitted, the class name is used.
|
|
43
|
-
* @returns A class decorator function.
|
|
44
|
-
*
|
|
45
|
-
* Note: During dependency resolution, any container that does not have an instance for the specified dependency identifier
|
|
46
|
-
* will create an instance of the decorated class. However, if a container already has an instance with that identifier
|
|
47
|
-
* prior to resolution, the decorated class will be ignored by that container.
|
|
48
|
-
*/
|
|
49
|
-
const autoInjectable = (dependencyId) => {
|
|
48
|
+
const injectableClasses = {};
|
|
49
|
+
const constructorInjections = {};
|
|
50
|
+
function injectable(dependencyOrDependencies, autoInjecions) {
|
|
50
51
|
return function (value, context) {
|
|
51
|
-
if ((context === null || context === undefined ? undefined : context.kind)
|
|
52
|
-
|
|
53
|
-
if (autoInjectableClasses[name]) {
|
|
54
|
-
throw new Error(`ProxyDi autoInjectable already has dependency ID: ${String(name)}`);
|
|
55
|
-
}
|
|
56
|
-
autoInjectableClasses[name] = value;
|
|
52
|
+
if ((context === null || context === undefined ? undefined : context.kind) !== 'class') {
|
|
53
|
+
throw new Error('@injectable decorator should decorate classes');
|
|
57
54
|
}
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
const name = dependencyOrDependencies
|
|
56
|
+
? Array.isArray(dependencyOrDependencies)
|
|
57
|
+
? context.name
|
|
58
|
+
: dependencyOrDependencies
|
|
59
|
+
: context.name;
|
|
60
|
+
const injectToConstructor = dependencyOrDependencies
|
|
61
|
+
? Array.isArray(dependencyOrDependencies)
|
|
62
|
+
? dependencyOrDependencies
|
|
63
|
+
: autoInjecions
|
|
64
|
+
: autoInjecions;
|
|
65
|
+
if (injectableClasses[name]) {
|
|
66
|
+
throw new Error(`ProxyDi has already regisered dependency ${String(name)} by @injectable`);
|
|
67
|
+
}
|
|
68
|
+
injectableClasses[name] = value;
|
|
69
|
+
if (injectToConstructor) {
|
|
70
|
+
constructorInjections[name] = injectToConstructor;
|
|
60
71
|
}
|
|
61
72
|
};
|
|
62
|
-
}
|
|
73
|
+
}
|
|
74
|
+
function findInjectableId(injectable) {
|
|
75
|
+
for (const [id, DependencyClass] of Object.entries(injectableClasses)) {
|
|
76
|
+
if (DependencyClass === injectable) {
|
|
77
|
+
return id;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
throw new Error(`Class is not @injectable: ${injectable.name}`);
|
|
81
|
+
}
|
|
63
82
|
|
|
64
83
|
const DEFAULT_SETTINGS = {
|
|
65
84
|
allowRegisterAnything: false,
|
|
@@ -72,7 +91,7 @@ class InjectionProxy {
|
|
|
72
91
|
constructor(onwer, container) {
|
|
73
92
|
this[_a] = true;
|
|
74
93
|
this[INJECTION_OWNER] = onwer;
|
|
75
|
-
this[
|
|
94
|
+
this[PROXYDI_CONTAINER] = container;
|
|
76
95
|
}
|
|
77
96
|
}
|
|
78
97
|
_a = IS_INJECTION_PROXY;
|
|
@@ -126,6 +145,49 @@ function makeDependencyProxy(dependency) {
|
|
|
126
145
|
});
|
|
127
146
|
}
|
|
128
147
|
|
|
148
|
+
function makeConstructorDependencyProxy(container, dependencyId) {
|
|
149
|
+
// let values: Record<string | symbol, any> = {};
|
|
150
|
+
// let wasResolved = false;
|
|
151
|
+
function getDependency() {
|
|
152
|
+
if (container.isKnown(dependencyId)) {
|
|
153
|
+
const dependency = container.resolve(dependencyId);
|
|
154
|
+
// if (!wasResolved) {
|
|
155
|
+
// for (const key in values) {
|
|
156
|
+
// dependency[key] = values[key];
|
|
157
|
+
// }
|
|
158
|
+
// values = {};
|
|
159
|
+
// wasResolved = true;
|
|
160
|
+
// }
|
|
161
|
+
return dependency;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
throw new Error(`Unknown dependency: ${String(dependencyId)}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return new Proxy({}, {
|
|
168
|
+
get: function (target, prop, receiver) {
|
|
169
|
+
// if (values[prop]) {
|
|
170
|
+
// return values[prop];
|
|
171
|
+
// }
|
|
172
|
+
const dependency = getDependency();
|
|
173
|
+
return Reflect.get(dependency, prop, receiver);
|
|
174
|
+
},
|
|
175
|
+
set: function (_target, prop, value) {
|
|
176
|
+
// if (!container.isKnown(dependencyId)) {
|
|
177
|
+
// values[prop] = value;
|
|
178
|
+
// return true;
|
|
179
|
+
// } else {
|
|
180
|
+
const dependency = getDependency();
|
|
181
|
+
return Reflect.set(dependency, prop, value);
|
|
182
|
+
//}
|
|
183
|
+
},
|
|
184
|
+
has: function (_target, prop) {
|
|
185
|
+
const dependency = getDependency();
|
|
186
|
+
return Reflect.has(dependency, prop);
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
129
191
|
/**
|
|
130
192
|
* A dependency injection container
|
|
131
193
|
*/
|
|
@@ -161,7 +223,7 @@ class ProxyDiContainer {
|
|
|
161
223
|
let dependencyInstance;
|
|
162
224
|
const isClass = typeof dependency === 'function';
|
|
163
225
|
if (isClass) {
|
|
164
|
-
dependencyInstance =
|
|
226
|
+
dependencyInstance = this.createInstance(dependency, dependencyId);
|
|
165
227
|
}
|
|
166
228
|
else {
|
|
167
229
|
dependencyInstance = dependency;
|
|
@@ -171,11 +233,20 @@ class ProxyDiContainer {
|
|
|
171
233
|
}
|
|
172
234
|
}
|
|
173
235
|
if (typeof dependencyInstance === 'object') {
|
|
174
|
-
dependencyInstance[
|
|
236
|
+
dependencyInstance[PROXYDI_CONTAINER] = this;
|
|
175
237
|
}
|
|
176
238
|
this.registerImpl(dependencyInstance, dependencyId);
|
|
177
239
|
return dependencyInstance;
|
|
178
240
|
}
|
|
241
|
+
createInstance(Dependency, dependencyId) {
|
|
242
|
+
const paramIds = constructorInjections[dependencyId] || [];
|
|
243
|
+
const params = [];
|
|
244
|
+
for (const id of paramIds) {
|
|
245
|
+
const param = makeConstructorDependencyProxy(this, id);
|
|
246
|
+
params.push(param);
|
|
247
|
+
}
|
|
248
|
+
return new Dependency(...params);
|
|
249
|
+
}
|
|
179
250
|
/**
|
|
180
251
|
* Internal method that implements registeration of dependency and prepare it for injection.
|
|
181
252
|
* @param dependencyId The unique identifier of the dependency.
|
|
@@ -197,39 +268,34 @@ class ProxyDiContainer {
|
|
|
197
268
|
return !!(this.parentDependencyProxies[dependencyId] ||
|
|
198
269
|
this.dependencies[dependencyId] ||
|
|
199
270
|
(this.parent && this.parent.isKnown(dependencyId)) ||
|
|
200
|
-
|
|
271
|
+
injectableClasses[dependencyId]);
|
|
201
272
|
}
|
|
202
|
-
resolve(
|
|
203
|
-
if (typeof
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
return this.resolve(dependencyId);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
throw new Error(`Class is not auto injectable: ${param.name}`);
|
|
273
|
+
resolve(dependency) {
|
|
274
|
+
if (typeof dependency === 'function') {
|
|
275
|
+
const id = findInjectableId(dependency);
|
|
276
|
+
return this.resolve(id);
|
|
210
277
|
}
|
|
211
|
-
if (!this.isKnown(
|
|
212
|
-
throw new Error(`Can't resolve unknown dependency: ${String(
|
|
278
|
+
if (!this.isKnown(dependency)) {
|
|
279
|
+
throw new Error(`Can't resolve unknown dependency: ${String(dependency)}`);
|
|
213
280
|
}
|
|
214
|
-
const proxy = this.parentDependencyProxies[
|
|
281
|
+
const proxy = this.parentDependencyProxies[dependency];
|
|
215
282
|
if (proxy) {
|
|
216
283
|
return proxy;
|
|
217
284
|
}
|
|
218
|
-
const
|
|
219
|
-
if (
|
|
220
|
-
if (
|
|
221
|
-
typeof
|
|
285
|
+
const instance = this.findDependency(dependency);
|
|
286
|
+
if (instance) {
|
|
287
|
+
if (instance[PROXYDI_CONTAINER] !== this &&
|
|
288
|
+
typeof instance === 'object' &&
|
|
222
289
|
this.settings.resolveInContainerContext) {
|
|
223
|
-
const proxy = makeDependencyProxy(
|
|
290
|
+
const proxy = makeDependencyProxy(instance);
|
|
224
291
|
this.injectDependenciesTo(proxy);
|
|
225
|
-
this.parentDependencyProxies[
|
|
292
|
+
this.parentDependencyProxies[dependency] = proxy;
|
|
226
293
|
return proxy;
|
|
227
294
|
}
|
|
228
|
-
return
|
|
295
|
+
return instance;
|
|
229
296
|
}
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
return this.register(autoDependency, param);
|
|
297
|
+
const InjectableClass = injectableClasses[dependency];
|
|
298
|
+
return this.register(InjectableClass, dependency);
|
|
233
299
|
}
|
|
234
300
|
/**
|
|
235
301
|
* Injects dependencies to the given object based on its defined injections metadata. Does not affect the container.
|
|
@@ -242,6 +308,16 @@ class ProxyDiContainer {
|
|
|
242
308
|
injection.set(injectionsOwner, dependencyProxy);
|
|
243
309
|
});
|
|
244
310
|
}
|
|
311
|
+
/**
|
|
312
|
+
* Creates instances for all injectable classes and registers them in this container.
|
|
313
|
+
* @returns This container to allow use along with constructor.
|
|
314
|
+
*/
|
|
315
|
+
registerInjectables() {
|
|
316
|
+
for (const [dependencyId, InjectableClass] of Object.entries(injectableClasses)) {
|
|
317
|
+
this.register(InjectableClass, dependencyId);
|
|
318
|
+
}
|
|
319
|
+
return this;
|
|
320
|
+
}
|
|
245
321
|
/**
|
|
246
322
|
* Finalizes dependency injections, prevents further rewriting of dependencies,
|
|
247
323
|
* and recursively bakes injections for child containers.
|
|
@@ -374,14 +450,10 @@ function isDependency(dependencyOrId) {
|
|
|
374
450
|
|
|
375
451
|
function resolveAll(instance, dependencyId) {
|
|
376
452
|
if (typeof dependencyId === 'function') {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
return resolveAll(instance, id);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
throw new Error(`Class is not auto injectable: ${dependencyId.name}`);
|
|
453
|
+
const id = findInjectableId(dependencyId);
|
|
454
|
+
return resolveAll(instance, id);
|
|
383
455
|
}
|
|
384
|
-
const container = instance[
|
|
456
|
+
const container = instance[PROXYDI_CONTAINER];
|
|
385
457
|
if (!container) {
|
|
386
458
|
throw new Error('Instance is not registered in any container');
|
|
387
459
|
}
|
|
@@ -398,4 +470,4 @@ function recursiveResolveAll(container, dependencyId) {
|
|
|
398
470
|
return all;
|
|
399
471
|
}
|
|
400
472
|
|
|
401
|
-
export { DEPENDENCY_ID,
|
|
473
|
+
export { DEPENDENCY_ID, PROXYDI_CONTAINER, ProxyDiContainer, inject, injectable, resolveAll };
|
package/dist/index.umd.js
CHANGED
|
@@ -5,10 +5,20 @@
|
|
|
5
5
|
})(this, (function (exports) { 'use strict';
|
|
6
6
|
|
|
7
7
|
const INJECTIONS = Symbol('injections');
|
|
8
|
+
/**
|
|
9
|
+
* This symbol constant defines a property name.
|
|
10
|
+
* This property is present in each dependency instance that was registered in ProxyDiContainer.
|
|
11
|
+
* The property stores the dependency identifier that should be used to resolve dependency from the container where it was registered.
|
|
12
|
+
*/
|
|
8
13
|
const DEPENDENCY_ID = Symbol('DependencyId');
|
|
14
|
+
/**
|
|
15
|
+
* This symbol constant defines a property name.
|
|
16
|
+
* This property is present in each dependency instance that was registered in ProxyDiContainer.
|
|
17
|
+
* The property stores a reference to the ProxyDiContainer in which the dependency was registered.
|
|
18
|
+
*/
|
|
19
|
+
const PROXYDI_CONTAINER = Symbol('proxyDiContainer');
|
|
9
20
|
const IS_INJECTION_PROXY = Symbol('isInjectionProxy');
|
|
10
21
|
const INJECTION_OWNER = Symbol('injectionOwner');
|
|
11
|
-
const PROXYDY_CONTAINER = Symbol('proxyDiContainer');
|
|
12
22
|
const IS_INSTANCE_PROXY = Symbol('isInstanceProxy');
|
|
13
23
|
|
|
14
24
|
/**
|
|
@@ -41,31 +51,40 @@
|
|
|
41
51
|
};
|
|
42
52
|
};
|
|
43
53
|
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
*
|
|
48
|
-
* @param dependencyId - Optional dependency identifier. If omitted, the class name is used.
|
|
49
|
-
* @returns A class decorator function.
|
|
50
|
-
*
|
|
51
|
-
* Note: During dependency resolution, any container that does not have an instance for the specified dependency identifier
|
|
52
|
-
* will create an instance of the decorated class. However, if a container already has an instance with that identifier
|
|
53
|
-
* prior to resolution, the decorated class will be ignored by that container.
|
|
54
|
-
*/
|
|
55
|
-
const autoInjectable = (dependencyId) => {
|
|
54
|
+
const injectableClasses = {};
|
|
55
|
+
const constructorInjections = {};
|
|
56
|
+
function injectable(dependencyOrDependencies, autoInjecions) {
|
|
56
57
|
return function (value, context) {
|
|
57
|
-
if ((context === null || context === undefined ? undefined : context.kind)
|
|
58
|
-
|
|
59
|
-
if (autoInjectableClasses[name]) {
|
|
60
|
-
throw new Error(`ProxyDi autoInjectable already has dependency ID: ${String(name)}`);
|
|
61
|
-
}
|
|
62
|
-
autoInjectableClasses[name] = value;
|
|
58
|
+
if ((context === null || context === undefined ? undefined : context.kind) !== 'class') {
|
|
59
|
+
throw new Error('@injectable decorator should decorate classes');
|
|
63
60
|
}
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
const name = dependencyOrDependencies
|
|
62
|
+
? Array.isArray(dependencyOrDependencies)
|
|
63
|
+
? context.name
|
|
64
|
+
: dependencyOrDependencies
|
|
65
|
+
: context.name;
|
|
66
|
+
const injectToConstructor = dependencyOrDependencies
|
|
67
|
+
? Array.isArray(dependencyOrDependencies)
|
|
68
|
+
? dependencyOrDependencies
|
|
69
|
+
: autoInjecions
|
|
70
|
+
: autoInjecions;
|
|
71
|
+
if (injectableClasses[name]) {
|
|
72
|
+
throw new Error(`ProxyDi has already regisered dependency ${String(name)} by @injectable`);
|
|
73
|
+
}
|
|
74
|
+
injectableClasses[name] = value;
|
|
75
|
+
if (injectToConstructor) {
|
|
76
|
+
constructorInjections[name] = injectToConstructor;
|
|
66
77
|
}
|
|
67
78
|
};
|
|
68
|
-
}
|
|
79
|
+
}
|
|
80
|
+
function findInjectableId(injectable) {
|
|
81
|
+
for (const [id, DependencyClass] of Object.entries(injectableClasses)) {
|
|
82
|
+
if (DependencyClass === injectable) {
|
|
83
|
+
return id;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
throw new Error(`Class is not @injectable: ${injectable.name}`);
|
|
87
|
+
}
|
|
69
88
|
|
|
70
89
|
const DEFAULT_SETTINGS = {
|
|
71
90
|
allowRegisterAnything: false,
|
|
@@ -78,7 +97,7 @@
|
|
|
78
97
|
constructor(onwer, container) {
|
|
79
98
|
this[_a] = true;
|
|
80
99
|
this[INJECTION_OWNER] = onwer;
|
|
81
|
-
this[
|
|
100
|
+
this[PROXYDI_CONTAINER] = container;
|
|
82
101
|
}
|
|
83
102
|
}
|
|
84
103
|
_a = IS_INJECTION_PROXY;
|
|
@@ -132,6 +151,49 @@
|
|
|
132
151
|
});
|
|
133
152
|
}
|
|
134
153
|
|
|
154
|
+
function makeConstructorDependencyProxy(container, dependencyId) {
|
|
155
|
+
// let values: Record<string | symbol, any> = {};
|
|
156
|
+
// let wasResolved = false;
|
|
157
|
+
function getDependency() {
|
|
158
|
+
if (container.isKnown(dependencyId)) {
|
|
159
|
+
const dependency = container.resolve(dependencyId);
|
|
160
|
+
// if (!wasResolved) {
|
|
161
|
+
// for (const key in values) {
|
|
162
|
+
// dependency[key] = values[key];
|
|
163
|
+
// }
|
|
164
|
+
// values = {};
|
|
165
|
+
// wasResolved = true;
|
|
166
|
+
// }
|
|
167
|
+
return dependency;
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
throw new Error(`Unknown dependency: ${String(dependencyId)}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return new Proxy({}, {
|
|
174
|
+
get: function (target, prop, receiver) {
|
|
175
|
+
// if (values[prop]) {
|
|
176
|
+
// return values[prop];
|
|
177
|
+
// }
|
|
178
|
+
const dependency = getDependency();
|
|
179
|
+
return Reflect.get(dependency, prop, receiver);
|
|
180
|
+
},
|
|
181
|
+
set: function (_target, prop, value) {
|
|
182
|
+
// if (!container.isKnown(dependencyId)) {
|
|
183
|
+
// values[prop] = value;
|
|
184
|
+
// return true;
|
|
185
|
+
// } else {
|
|
186
|
+
const dependency = getDependency();
|
|
187
|
+
return Reflect.set(dependency, prop, value);
|
|
188
|
+
//}
|
|
189
|
+
},
|
|
190
|
+
has: function (_target, prop) {
|
|
191
|
+
const dependency = getDependency();
|
|
192
|
+
return Reflect.has(dependency, prop);
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
135
197
|
/**
|
|
136
198
|
* A dependency injection container
|
|
137
199
|
*/
|
|
@@ -167,7 +229,7 @@
|
|
|
167
229
|
let dependencyInstance;
|
|
168
230
|
const isClass = typeof dependency === 'function';
|
|
169
231
|
if (isClass) {
|
|
170
|
-
dependencyInstance =
|
|
232
|
+
dependencyInstance = this.createInstance(dependency, dependencyId);
|
|
171
233
|
}
|
|
172
234
|
else {
|
|
173
235
|
dependencyInstance = dependency;
|
|
@@ -177,11 +239,20 @@
|
|
|
177
239
|
}
|
|
178
240
|
}
|
|
179
241
|
if (typeof dependencyInstance === 'object') {
|
|
180
|
-
dependencyInstance[
|
|
242
|
+
dependencyInstance[PROXYDI_CONTAINER] = this;
|
|
181
243
|
}
|
|
182
244
|
this.registerImpl(dependencyInstance, dependencyId);
|
|
183
245
|
return dependencyInstance;
|
|
184
246
|
}
|
|
247
|
+
createInstance(Dependency, dependencyId) {
|
|
248
|
+
const paramIds = constructorInjections[dependencyId] || [];
|
|
249
|
+
const params = [];
|
|
250
|
+
for (const id of paramIds) {
|
|
251
|
+
const param = makeConstructorDependencyProxy(this, id);
|
|
252
|
+
params.push(param);
|
|
253
|
+
}
|
|
254
|
+
return new Dependency(...params);
|
|
255
|
+
}
|
|
185
256
|
/**
|
|
186
257
|
* Internal method that implements registeration of dependency and prepare it for injection.
|
|
187
258
|
* @param dependencyId The unique identifier of the dependency.
|
|
@@ -203,39 +274,34 @@
|
|
|
203
274
|
return !!(this.parentDependencyProxies[dependencyId] ||
|
|
204
275
|
this.dependencies[dependencyId] ||
|
|
205
276
|
(this.parent && this.parent.isKnown(dependencyId)) ||
|
|
206
|
-
|
|
277
|
+
injectableClasses[dependencyId]);
|
|
207
278
|
}
|
|
208
|
-
resolve(
|
|
209
|
-
if (typeof
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
return this.resolve(dependencyId);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
throw new Error(`Class is not auto injectable: ${param.name}`);
|
|
279
|
+
resolve(dependency) {
|
|
280
|
+
if (typeof dependency === 'function') {
|
|
281
|
+
const id = findInjectableId(dependency);
|
|
282
|
+
return this.resolve(id);
|
|
216
283
|
}
|
|
217
|
-
if (!this.isKnown(
|
|
218
|
-
throw new Error(`Can't resolve unknown dependency: ${String(
|
|
284
|
+
if (!this.isKnown(dependency)) {
|
|
285
|
+
throw new Error(`Can't resolve unknown dependency: ${String(dependency)}`);
|
|
219
286
|
}
|
|
220
|
-
const proxy = this.parentDependencyProxies[
|
|
287
|
+
const proxy = this.parentDependencyProxies[dependency];
|
|
221
288
|
if (proxy) {
|
|
222
289
|
return proxy;
|
|
223
290
|
}
|
|
224
|
-
const
|
|
225
|
-
if (
|
|
226
|
-
if (
|
|
227
|
-
typeof
|
|
291
|
+
const instance = this.findDependency(dependency);
|
|
292
|
+
if (instance) {
|
|
293
|
+
if (instance[PROXYDI_CONTAINER] !== this &&
|
|
294
|
+
typeof instance === 'object' &&
|
|
228
295
|
this.settings.resolveInContainerContext) {
|
|
229
|
-
const proxy = makeDependencyProxy(
|
|
296
|
+
const proxy = makeDependencyProxy(instance);
|
|
230
297
|
this.injectDependenciesTo(proxy);
|
|
231
|
-
this.parentDependencyProxies[
|
|
298
|
+
this.parentDependencyProxies[dependency] = proxy;
|
|
232
299
|
return proxy;
|
|
233
300
|
}
|
|
234
|
-
return
|
|
301
|
+
return instance;
|
|
235
302
|
}
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
return this.register(autoDependency, param);
|
|
303
|
+
const InjectableClass = injectableClasses[dependency];
|
|
304
|
+
return this.register(InjectableClass, dependency);
|
|
239
305
|
}
|
|
240
306
|
/**
|
|
241
307
|
* Injects dependencies to the given object based on its defined injections metadata. Does not affect the container.
|
|
@@ -248,6 +314,16 @@
|
|
|
248
314
|
injection.set(injectionsOwner, dependencyProxy);
|
|
249
315
|
});
|
|
250
316
|
}
|
|
317
|
+
/**
|
|
318
|
+
* Creates instances for all injectable classes and registers them in this container.
|
|
319
|
+
* @returns This container to allow use along with constructor.
|
|
320
|
+
*/
|
|
321
|
+
registerInjectables() {
|
|
322
|
+
for (const [dependencyId, InjectableClass] of Object.entries(injectableClasses)) {
|
|
323
|
+
this.register(InjectableClass, dependencyId);
|
|
324
|
+
}
|
|
325
|
+
return this;
|
|
326
|
+
}
|
|
251
327
|
/**
|
|
252
328
|
* Finalizes dependency injections, prevents further rewriting of dependencies,
|
|
253
329
|
* and recursively bakes injections for child containers.
|
|
@@ -380,14 +456,10 @@
|
|
|
380
456
|
|
|
381
457
|
function resolveAll(instance, dependencyId) {
|
|
382
458
|
if (typeof dependencyId === 'function') {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
return resolveAll(instance, id);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
throw new Error(`Class is not auto injectable: ${dependencyId.name}`);
|
|
459
|
+
const id = findInjectableId(dependencyId);
|
|
460
|
+
return resolveAll(instance, id);
|
|
389
461
|
}
|
|
390
|
-
const container = instance[
|
|
462
|
+
const container = instance[PROXYDI_CONTAINER];
|
|
391
463
|
if (!container) {
|
|
392
464
|
throw new Error('Instance is not registered in any container');
|
|
393
465
|
}
|
|
@@ -405,10 +477,10 @@
|
|
|
405
477
|
}
|
|
406
478
|
|
|
407
479
|
exports.DEPENDENCY_ID = DEPENDENCY_ID;
|
|
408
|
-
exports.
|
|
480
|
+
exports.PROXYDI_CONTAINER = PROXYDI_CONTAINER;
|
|
409
481
|
exports.ProxyDiContainer = ProxyDiContainer;
|
|
410
|
-
exports.autoInjectable = autoInjectable;
|
|
411
482
|
exports.inject = inject;
|
|
483
|
+
exports.injectable = injectable;
|
|
412
484
|
exports.resolveAll = resolveAll;
|
|
413
485
|
|
|
414
486
|
}));
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DependencyClass, DependencyId } from './types';
|
|
2
|
+
export declare const injectableClasses: Record<DependencyId, DependencyClass<any>>;
|
|
3
|
+
export declare const constructorInjections: Record<DependencyId, DependencyId[]>;
|
|
4
|
+
/**
|
|
5
|
+
* Registers a class as an automatically injectable for dependency injection container.
|
|
6
|
+
*
|
|
7
|
+
* @param dependencyId - Optional dependency identifier. If omitted, the class name is used.
|
|
8
|
+
* @param autoInjecions - injections that should be injected to dependency constructor while it creates
|
|
9
|
+
* @returns A class decorator function.
|
|
10
|
+
*
|
|
11
|
+
* Note: During dependency resolution, any container that does not have an instance for the specified dependency identifier
|
|
12
|
+
* will create an instance of the decorated class. However, if a container already has an instance with that identifier
|
|
13
|
+
* prior to resolution, the decorated class will be ignored by that container.
|
|
14
|
+
*/
|
|
15
|
+
export declare function injectable(autoInjecions: DependencyId[]): any;
|
|
16
|
+
export declare function injectable(dependencyId?: DependencyId, autoInjecions?: DependencyId[]): any;
|
|
17
|
+
export declare function findInjectableId(injectable: DependencyClass<any>): DependencyId;
|
package/dist/types.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export type IProxyDiContainer = {
|
|
|
12
12
|
isKnown: (dependencyId: DependencyId) => boolean;
|
|
13
13
|
injectDependenciesTo: (dependency: any) => void;
|
|
14
14
|
register: (dependency: any, dependencyId: DependencyId) => any;
|
|
15
|
-
resolve: <T>(dependencyId: DependencyId) => T & ContainerizedDependency;
|
|
15
|
+
resolve: <T>(dependencyId: DependencyId | DependencyClass<any>) => T & ContainerizedDependency;
|
|
16
16
|
createChildContainer: () => IProxyDiContainer;
|
|
17
17
|
children: IProxyDiContainer[];
|
|
18
18
|
getChild(id: number): IProxyDiContainer;
|
|
@@ -21,7 +21,18 @@ export type IProxyDiContainer = {
|
|
|
21
21
|
destroy: () => void;
|
|
22
22
|
};
|
|
23
23
|
export declare const INJECTIONS: unique symbol;
|
|
24
|
+
/**
|
|
25
|
+
* This symbol constant defines a property name.
|
|
26
|
+
* This property is present in each dependency instance that was registered in ProxyDiContainer.
|
|
27
|
+
* The property stores the dependency identifier that should be used to resolve dependency from the container where it was registered.
|
|
28
|
+
*/
|
|
24
29
|
export declare const DEPENDENCY_ID: unique symbol;
|
|
30
|
+
/**
|
|
31
|
+
* This symbol constant defines a property name.
|
|
32
|
+
* This property is present in each dependency instance that was registered in ProxyDiContainer.
|
|
33
|
+
* The property stores a reference to the ProxyDiContainer in which the dependency was registered.
|
|
34
|
+
*/
|
|
35
|
+
export declare const PROXYDI_CONTAINER: unique symbol;
|
|
25
36
|
export type Injections = Record<string | symbol, Injection>;
|
|
26
37
|
export type Dependency = {
|
|
27
38
|
[INJECTIONS]: Injections;
|
|
@@ -31,13 +42,13 @@ export type Dependency = {
|
|
|
31
42
|
*/
|
|
32
43
|
export type ContainerizedDependency = Dependency & {
|
|
33
44
|
/**
|
|
34
|
-
* Unique identifier that could use to resolve this instance
|
|
45
|
+
* Unique identifier that could use to resolve this instance from container where it was registered
|
|
35
46
|
*/
|
|
36
47
|
[DEPENDENCY_ID]: DependencyId;
|
|
37
48
|
/**
|
|
38
49
|
* ProxyDi container in which this instance was registered
|
|
39
50
|
*/
|
|
40
|
-
[
|
|
51
|
+
[PROXYDI_CONTAINER]: IProxyDiContainer;
|
|
41
52
|
};
|
|
42
53
|
export type ContainerSettings = {
|
|
43
54
|
allowRegisterAnything?: boolean;
|
|
@@ -46,10 +57,9 @@ export type ContainerSettings = {
|
|
|
46
57
|
};
|
|
47
58
|
export declare const IS_INJECTION_PROXY: unique symbol;
|
|
48
59
|
export declare const INJECTION_OWNER: unique symbol;
|
|
49
|
-
export declare const PROXYDY_CONTAINER: unique symbol;
|
|
50
60
|
export declare const IS_INSTANCE_PROXY: unique symbol;
|
|
51
61
|
export type InjectionProxy = {
|
|
52
62
|
[IS_INJECTION_PROXY]: true;
|
|
53
63
|
[INJECTION_OWNER]: ContainerizedDependency;
|
|
54
|
-
[
|
|
64
|
+
[PROXYDI_CONTAINER]: IProxyDiContainer;
|
|
55
65
|
};
|