ts-ioc-container 23.2.11 → 23.3.0
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/README.md +269 -159
- package/cjs/container/Container.d.ts +1 -1
- package/cjs/index.d.ts +5 -3
- package/cjs/index.js +12 -36
- package/cjs/index.js.map +1 -1
- package/cjs/{IInjector.d.ts → injector/IInjector.d.ts} +2 -2
- package/{esm → cjs/injector}/IInjector.js.map +1 -1
- package/cjs/injector/ProxyInjector.d.ts +6 -0
- package/cjs/injector/ProxyInjector.js +42 -0
- package/cjs/injector/ProxyInjector.js.map +1 -0
- package/cjs/injector/ReflectionInjector.d.ts +10 -0
- package/cjs/injector/ReflectionInjector.js +68 -0
- package/cjs/injector/ReflectionInjector.js.map +1 -0
- package/cjs/injector/SimpleInjector.d.ts +6 -0
- package/cjs/injector/SimpleInjector.js +42 -0
- package/cjs/injector/SimpleInjector.js.map +1 -0
- package/cjs/provider/ArgsProvider.d.ts +2 -2
- package/cjs/provider/ArgsProvider.js.map +1 -1
- package/cjs/reflection.d.ts +2 -0
- package/cjs/reflection.js +38 -1
- package/cjs/reflection.js.map +1 -1
- package/cjs/utils.d.ts +2 -0
- package/cjs/utils.js +37 -1
- package/cjs/utils.js.map +1 -1
- package/esm/container/Container.d.ts +1 -1
- package/esm/index.d.ts +5 -3
- package/esm/index.js +4 -1
- package/esm/index.js.map +1 -1
- package/esm/{IInjector.d.ts → injector/IInjector.d.ts} +2 -2
- package/{cjs → esm/injector}/IInjector.js.map +1 -1
- package/esm/injector/ProxyInjector.d.ts +6 -0
- package/esm/injector/ProxyInjector.js +20 -0
- package/esm/injector/ProxyInjector.js.map +1 -0
- package/esm/injector/ReflectionInjector.d.ts +10 -0
- package/esm/injector/ReflectionInjector.js +19 -0
- package/esm/injector/ReflectionInjector.js.map +1 -0
- package/esm/injector/SimpleInjector.d.ts +6 -0
- package/esm/injector/SimpleInjector.js +6 -0
- package/esm/injector/SimpleInjector.js.map +1 -0
- package/esm/provider/ArgsProvider.d.ts +2 -2
- package/esm/provider/ArgsProvider.js.map +1 -1
- package/esm/reflection.d.ts +2 -0
- package/esm/reflection.js +8 -0
- package/esm/reflection.js.map +1 -1
- package/esm/utils.d.ts +2 -0
- package/esm/utils.js +14 -0
- package/esm/utils.js.map +1 -1
- package/package.json +2 -3
- /package/cjs/{IInjector.js → injector/IInjector.js} +0 -0
- /package/esm/{IInjector.js → injector/IInjector.js} +0 -0
package/README.md
CHANGED
|
@@ -19,13 +19,20 @@
|
|
|
19
19
|
|
|
20
20
|
## Install
|
|
21
21
|
```shell script
|
|
22
|
-
npm install ts-ioc-container
|
|
22
|
+
npm install ts-ioc-container reflect-metadata
|
|
23
23
|
```
|
|
24
24
|
```shell script
|
|
25
|
-
yarn add ts-ioc-container
|
|
25
|
+
yarn add ts-ioc-container reflect-metadata
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
##
|
|
28
|
+
## Setup
|
|
29
|
+
### reflect-metadata
|
|
30
|
+
Add it in main file of your project. It should be first line of code.
|
|
31
|
+
```typescript
|
|
32
|
+
import 'reflect-metadata';
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### tsconfig.json
|
|
29
36
|
```json
|
|
30
37
|
{
|
|
31
38
|
"compilerOptions": {
|
|
@@ -35,21 +42,103 @@ yarn add ts-ioc-container ts-constructor-injector reflect-metadata
|
|
|
35
42
|
}
|
|
36
43
|
```
|
|
37
44
|
|
|
45
|
+
## Container
|
|
46
|
+
Container consists of:
|
|
47
|
+
|
|
48
|
+
- Injector - injects dependencies to constructor
|
|
49
|
+
- Providers - templates that create instances of dependencies
|
|
50
|
+
|
|
51
|
+
### Basic usage
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import 'reflect-metadata';
|
|
55
|
+
import { Container } from "ts-ioc-container";
|
|
56
|
+
|
|
57
|
+
class Logger {
|
|
58
|
+
public name = 'Logger';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
class App {
|
|
62
|
+
constructor(@inject(by('ILogger')) public logger: ILogger) {
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const container = new Container(new ReflectionInjector()).register('ILogger', Provider.fromClass(Logger));
|
|
67
|
+
container.resolve(App).resolve.logger.name === 'Logger'; // true
|
|
68
|
+
|
|
69
|
+
container.dispose();
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Scopes
|
|
73
|
+
- scope - is a container that can be created from another container. It's a sub-container.
|
|
74
|
+
- NOTICE: when you create a scope of container then all providers are cloned to new scope. Every provider has method `clone` that clones itself.
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { Container, ReflectionInjector } from "ts-ioc-container";
|
|
78
|
+
|
|
79
|
+
const container = new Container(new ReflectionInjector());
|
|
80
|
+
const scope = container.createScope();
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Tags
|
|
38
84
|
|
|
39
|
-
|
|
40
|
-
|
|
85
|
+
- tag - is a string that can be used mark each container and scope
|
|
86
|
+
- every provider can be registered per certain tags and cannot be resolved from container with other tags. Only from parent one with certain tags.
|
|
87
|
+
- NOTICE: when you create a scope of container with tags which not includes in provider tags then this provider will not be cloned to new scope.
|
|
41
88
|
|
|
42
|
-
|
|
89
|
+
```typescript
|
|
90
|
+
import { Container, perTags, ReflectionInjector } from "ts-ioc-container";
|
|
91
|
+
|
|
92
|
+
const container = new Container(new ReflectionInjector(), { tags: ['root'] }).register('ILogger', Provider.fromClass(Logger).pipe(perTags('root')));
|
|
93
|
+
const scope = container.createScope(['child']);
|
|
94
|
+
|
|
95
|
+
scope.resolve('ILogger'); // it will be resolved from container, not from scope
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Instances
|
|
99
|
+
- you can get instances from container and scope which were created by injector
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
import { Container, ReflectionInjector } from "ts-ioc-container";
|
|
103
|
+
|
|
104
|
+
const container = new Container(new ReflectionInjector()).register('ILogger', Provider.fromClass(Logger));
|
|
105
|
+
const scope = container.createScope();
|
|
106
|
+
|
|
107
|
+
const logger1 = container.resolve('ILogger');
|
|
108
|
+
const logger2 = scope.resolve('ILogger');
|
|
109
|
+
|
|
110
|
+
expect(scope.getInstances().length).toBe(1);
|
|
111
|
+
expect(container.getInstances().length).toBe(2);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Disposing
|
|
115
|
+
- container can be disposed
|
|
116
|
+
- when container is disposed then all scopes are disposed too
|
|
117
|
+
- when container is disposed then it unregisters all providers and remove all instances
|
|
43
118
|
|
|
44
119
|
```typescript
|
|
45
|
-
import {
|
|
46
|
-
import { inject, resolve } from "ts-constructor-injector";
|
|
120
|
+
import { asSingleton, Container, perTags, ReflectionInjector, ContainerDisposedError } from "ts-ioc-container";
|
|
47
121
|
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
122
|
+
const container = new Container(new ReflectionInjector(), { tags: ['root'] }).register('ILogger', Provider.fromClass(Logger));
|
|
123
|
+
const scope = container.createScope(['child']);
|
|
124
|
+
|
|
125
|
+
const logger = scope.resolve('ILogger');
|
|
126
|
+
container.dispose();
|
|
127
|
+
|
|
128
|
+
expect(() => scope.resolve('ILogger')).toThrow(ContainerDisposedError);
|
|
129
|
+
expect(() => container.resolve('ILogger')).toThrow(ContainerDisposedError);
|
|
130
|
+
expect(container.getInstances().length).toBe(0);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Injectors
|
|
134
|
+
- `ReflectionInjector` - injects dependencies using `@inject` decorator
|
|
135
|
+
- `ProxyInjector` - injects dependencies as dictionary `Record<string, unknown>`
|
|
136
|
+
- `SimpleInjector` - just passes container to constructor with others arguments
|
|
137
|
+
|
|
138
|
+
### Reflection injector
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
import { Container, IContainer, IInjector, Provider, by, inject, resolve } from "ts-ioc-container";
|
|
53
142
|
|
|
54
143
|
class Logger implements ILogger {
|
|
55
144
|
info(message: string) {
|
|
@@ -58,15 +147,19 @@ class Logger implements ILogger {
|
|
|
58
147
|
}
|
|
59
148
|
|
|
60
149
|
class App {
|
|
61
|
-
constructor(@inject(
|
|
150
|
+
constructor(@inject((container, ...args) => container.resolve('ILogger', ...args)) private logger: ILogger) {
|
|
62
151
|
}
|
|
63
152
|
|
|
153
|
+
// OR
|
|
154
|
+
// constructor(@inject(by('ILogger')) private logger: ILogger) {
|
|
155
|
+
// }
|
|
156
|
+
|
|
64
157
|
run() {
|
|
65
158
|
this.logger.info('Hello world');
|
|
66
159
|
}
|
|
67
160
|
}
|
|
68
161
|
|
|
69
|
-
const container = new Container(
|
|
162
|
+
const container = new Container(new ReflectionInjector())
|
|
70
163
|
.register('ILogger', Provider.fromClass(Logger));
|
|
71
164
|
|
|
72
165
|
const app = container.resolve(App);
|
|
@@ -76,13 +169,7 @@ app.run();
|
|
|
76
169
|
### Simple injector
|
|
77
170
|
|
|
78
171
|
```typescript
|
|
79
|
-
import { IContainer } from "ts-
|
|
80
|
-
|
|
81
|
-
const injector: IInjector = {
|
|
82
|
-
resolve<T>(container: IContainer, Target: constructor<T>, ...deps: unknown[]): T {
|
|
83
|
-
return new Target(container, ...deps);
|
|
84
|
-
},
|
|
85
|
-
};
|
|
172
|
+
import { SimpleInjector, IContainer } from "ts-ioc-container";
|
|
86
173
|
|
|
87
174
|
class Logger implements ILogger {
|
|
88
175
|
info(message: string) {
|
|
@@ -96,13 +183,13 @@ class App {
|
|
|
96
183
|
constructor(private container: IContainer) {
|
|
97
184
|
this.logger = container.resolve('ILogger');
|
|
98
185
|
}
|
|
99
|
-
|
|
186
|
+
|
|
100
187
|
run() {
|
|
101
188
|
this.logger.info('Hello world');
|
|
102
189
|
}
|
|
103
190
|
}
|
|
104
191
|
|
|
105
|
-
const container = new Container(
|
|
192
|
+
const container = new Container(new SimpleInjector())
|
|
106
193
|
.register('ILogger', Provider.fromClass(Logger));
|
|
107
194
|
|
|
108
195
|
const app = container.resolve(App);
|
|
@@ -112,24 +199,7 @@ app.run();
|
|
|
112
199
|
### Proxy injector
|
|
113
200
|
|
|
114
201
|
```typescript
|
|
115
|
-
import { IContainer } from "ts-
|
|
116
|
-
|
|
117
|
-
const injector: IInjector = {
|
|
118
|
-
resolve<T>(container: IContainer, Target: constructor<T>, ...deps: unknown[]): T {
|
|
119
|
-
const args = deps.reduce((acc, it) => ({ ...acc, ...it }), {});
|
|
120
|
-
const proxy = new Proxy(
|
|
121
|
-
{},
|
|
122
|
-
{
|
|
123
|
-
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
124
|
-
get(target: {}, prop: string | symbol): any {
|
|
125
|
-
// eslint-disable-next-line no-prototype-builtins
|
|
126
|
-
return args.hasOwnProperty(prop) ? args[prop] : container.resolve(prop);
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
);
|
|
130
|
-
return new Target(proxy);
|
|
131
|
-
},
|
|
132
|
-
};
|
|
202
|
+
import { ProxyInjector, IContainer } from "ts-ioc-container";
|
|
133
203
|
|
|
134
204
|
class Logger implements ILogger {
|
|
135
205
|
info(message: string) {
|
|
@@ -140,103 +210,190 @@ class Logger implements ILogger {
|
|
|
140
210
|
class App {
|
|
141
211
|
private logger: ILogger;
|
|
142
212
|
|
|
143
|
-
constructor({logger}: {logger: ILogger}) {
|
|
213
|
+
constructor({ logger }: { logger: ILogger }) {
|
|
144
214
|
this.logger = logger;
|
|
145
215
|
}
|
|
146
|
-
|
|
216
|
+
|
|
147
217
|
run() {
|
|
148
218
|
this.logger.info('Hello world');
|
|
149
219
|
}
|
|
150
220
|
}
|
|
151
221
|
|
|
152
|
-
const container = new Container(
|
|
222
|
+
const container = new Container(new ProxyInjector())
|
|
153
223
|
.register('logger', Provider.fromClass(Logger));
|
|
154
224
|
|
|
155
225
|
const app = container.resolve(App);
|
|
156
226
|
app.run();
|
|
157
227
|
```
|
|
158
228
|
|
|
159
|
-
##
|
|
229
|
+
## Providers
|
|
230
|
+
All providers are registered in container and cloned for every sub-scope.
|
|
231
|
+
|
|
232
|
+
- `Provider` - basic provider
|
|
233
|
+
- `SingletonProvider` - provider that creates only one instance in every scope where it's resolved
|
|
234
|
+
- `TaggedProvider` - provider that can be resolved only from container with certain tags and their sub scopes
|
|
235
|
+
- `ArgsProvider` - provider that encapsulates arguments to pass it to constructor.
|
|
236
|
+
|
|
237
|
+
### Provider
|
|
160
238
|
|
|
161
|
-
|
|
162
|
-
- `.pipe` - decorates provider by features and returns new provider
|
|
163
|
-
- `asSingleton()` - makes provider singleton (singleton in every scope)
|
|
164
|
-
- `perTags(...tags: string[])` - makes provider available only in scope with certain tags and their sub scopes
|
|
165
|
-
- `withArgs(...args: unknown[])` - passes arguments to constructor
|
|
166
|
-
- `withArgsFn(fn: (scope: IContainer) => unknown[])` - passes arguments to constructor as function result
|
|
239
|
+
From function
|
|
167
240
|
|
|
168
241
|
```typescript
|
|
169
|
-
import { Provider
|
|
242
|
+
import { Provider } from "ts-ioc-container";
|
|
170
243
|
|
|
171
|
-
|
|
172
|
-
|
|
244
|
+
container.register('ILogger', new Provider((container, ...args) => new Logger(container, ...args)));
|
|
245
|
+
```
|
|
173
246
|
|
|
174
|
-
|
|
175
|
-
container.register('ILogger', Provider.fromClass(Logger).pipe(perTags('root')));
|
|
247
|
+
From class
|
|
176
248
|
|
|
177
|
-
|
|
178
|
-
|
|
249
|
+
```typescript
|
|
250
|
+
import { Provider } from "ts-ioc-container";
|
|
179
251
|
|
|
180
|
-
|
|
181
|
-
|
|
252
|
+
container.register('ILogger', Provider.fromClass(Logger));
|
|
253
|
+
```
|
|
182
254
|
|
|
183
|
-
|
|
184
|
-
container.register('ILogger', Provider.fromClass(Logger).pipe(withArgs('dev'), asSingleton()));
|
|
255
|
+
From value
|
|
185
256
|
|
|
186
|
-
|
|
187
|
-
|
|
257
|
+
```typescript
|
|
258
|
+
import { Provider } from "ts-ioc-container";
|
|
188
259
|
|
|
189
260
|
container.register('ILogger', Provider.fromValue(new Logger()));
|
|
190
261
|
```
|
|
191
262
|
|
|
192
|
-
|
|
263
|
+
`pipe` - decorates provider by other providers
|
|
193
264
|
|
|
194
265
|
```typescript
|
|
195
|
-
import { asSingleton, perTags,
|
|
266
|
+
import { asSingleton, perTags, Provider, SingletonProvider, TaggedProvider } from "ts-ioc-container";
|
|
196
267
|
|
|
197
|
-
|
|
268
|
+
container.register('ILogger', Provider.fromClass(Logger).pipe((provider) => new SingletonProvider(provider)), (provider) => new TaggedProvider(provider, ['root']));
|
|
269
|
+
|
|
270
|
+
// OR
|
|
271
|
+
container.register('ILogger', Provider.fromClass(Logger).pipe(asSingleton(), perTags('root')));
|
|
272
|
+
|
|
273
|
+
// OR
|
|
198
274
|
@provider(asSingleton(), perTags('root'))
|
|
199
275
|
class Logger {
|
|
200
|
-
|
|
201
|
-
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
container.register('ILogger', Provider.fromClass(Logger));
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Singleton provider
|
|
282
|
+
|
|
283
|
+
- Singleton provider creates only one instance in every scope where it's resolved.
|
|
284
|
+
- NOTICE: if you create a scope 'A' of container 'root' then Logger of A !== Logger of root.
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import { Provider, SingletonProvider, asSingleton } from "ts-ioc-container";
|
|
288
|
+
|
|
289
|
+
container.register('ILogger', Provider.fromClass(Logger).pipe((provider) => new SingletonProvider(provider)));
|
|
290
|
+
// OR
|
|
291
|
+
container.register('ILogger', Provider.fromClass(Logger).pipe(asSingleton()));
|
|
292
|
+
|
|
293
|
+
container.resolve('ILogger') === container.resolve('ILogger'); // true
|
|
294
|
+
|
|
295
|
+
const scope = container.createScope();
|
|
296
|
+
scope.resolve('ILogger') === scope.resolve('ILogger'); // true
|
|
297
|
+
container.resolve('ILogger') !== scope.resolve('ILogger'); // true. NOTICE: because every provider is cloned for every child scope from parent one
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Tagged provider
|
|
301
|
+
You need tagged provider when you want to resolve provider only from container with certain tags and their sub scopes.
|
|
302
|
+
It doesn't make a clones in scopes with tags that are not in provider's tags. Usually it's used with `SingletonProvider`.
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
import { Provider, TaggedProvider, asSingleton, perTags } from "ts-ioc-container";
|
|
306
|
+
|
|
307
|
+
container.register('ILogger', Provider.fromClass(Logger).pipe((provider) => new TaggedProvider(provider, ['root'])));
|
|
308
|
+
// OR
|
|
309
|
+
container.register('ILogger', Provider.fromClass(Logger).pipe(perTags('root', 'parent')));
|
|
310
|
+
|
|
311
|
+
// with sigleton
|
|
312
|
+
container.register('ILogger', Provider.fromClass(Logger).pipe(perTags('root', 'parent')).pipe(asSingleton()));
|
|
313
|
+
container.resolve('ILogger') === container.resolve('ILogger'); // true
|
|
314
|
+
|
|
315
|
+
const scope = container.createScope();
|
|
316
|
+
scope.resolve('ILogger') === scope.resolve('ILogger'); // true
|
|
317
|
+
container.resolve('ILogger') === scope.resolve('ILogger'); // true
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Args provider
|
|
321
|
+
- You need args provider when you want to pass arguments to constructor on step when you compose container.
|
|
322
|
+
- NOTICE: args from this provider has higher priority than args from `resolve` method.
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
import { Provider, ArgsProvider, withArgs, withArgsFn } from "ts-ioc-container";
|
|
326
|
+
|
|
327
|
+
class Logger {
|
|
328
|
+
constructor(public type: string, public name: string) {
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
container.register('ILogger', Provider.fromClass(Logger).pipe((provider) => new ArgsProvider(provider, () => ['FileLogger'])));
|
|
333
|
+
|
|
334
|
+
// OR
|
|
335
|
+
container.register('ILogger', Provider.fromClass(Logger).pipe(withArgsFn(() => ['FileLogger'])));
|
|
336
|
+
// OR
|
|
337
|
+
container.register('ILogger', Provider.fromClass(Logger).pipe(withArgs('FileLogger')));
|
|
338
|
+
|
|
339
|
+
container.resolve('ILogger', 'Main').type === 'FileLogger'; // true
|
|
340
|
+
container.resolve('ILogger', 'Main').name === 'Main'; // true
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Container modules
|
|
344
|
+
|
|
345
|
+
if you want to encapsulate some logic to enrich container you can use `IContainerModule`.
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
import { Registration } from "ts-ioc-container";
|
|
349
|
+
|
|
350
|
+
class Development implements IContainerModule {
|
|
351
|
+
applyTo(container: IContainer): void {
|
|
352
|
+
container.add(Registration.fromClass(DevLogger));
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
class Production implements IContainerModule {
|
|
357
|
+
applyTo(container: IContainer): void {
|
|
358
|
+
container.add(Registration.fromClass(ProdLogger));
|
|
202
359
|
}
|
|
203
360
|
}
|
|
204
361
|
|
|
205
362
|
const container = new Container(injector, { tags: ['root'] })
|
|
206
|
-
.add(Registration.fromClass(Logger))
|
|
207
|
-
|
|
208
|
-
logger.info('Hello world');
|
|
363
|
+
.add(Registration.fromClass(Logger))
|
|
364
|
+
.add(process.env.NODE_ENV === 'production' ? new Production() : new Development());
|
|
209
365
|
```
|
|
210
366
|
|
|
211
|
-
##
|
|
367
|
+
## Registration module (Provider + DependencyKey)
|
|
368
|
+
It's built-in module that encapsulates logic of registration provider by dependency key `forKey`. Just a sugar
|
|
212
369
|
|
|
213
370
|
```typescript
|
|
214
|
-
import { asSingleton, perTags, forKey,
|
|
215
|
-
import { inject } from "ts-constructor-injector";
|
|
371
|
+
import { asSingleton, perTags, forKey, Registration, Provider } from "ts-ioc-container";
|
|
216
372
|
|
|
217
|
-
@forKey('
|
|
373
|
+
@forKey('ILogger')
|
|
218
374
|
@provider(asSingleton(), perTags('root'))
|
|
219
|
-
class
|
|
220
|
-
|
|
375
|
+
class Logger {
|
|
376
|
+
info(message: string) {
|
|
377
|
+
console.log(message);
|
|
221
378
|
}
|
|
222
379
|
}
|
|
223
380
|
|
|
224
|
-
|
|
381
|
+
container.register(Registration.fromClass(Logger));
|
|
225
382
|
|
|
226
|
-
|
|
383
|
+
// OR
|
|
227
384
|
|
|
228
|
-
@
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
385
|
+
@provider(asSingleton(), perTags('root'))
|
|
386
|
+
class Logger {
|
|
387
|
+
info(message: string) {
|
|
388
|
+
console.log(message);
|
|
232
389
|
}
|
|
233
390
|
}
|
|
234
391
|
|
|
235
|
-
|
|
236
|
-
.add(Registration.fromClass(Engine));
|
|
392
|
+
container.register('ILogger', Provider.fromClass(Logger));
|
|
237
393
|
```
|
|
238
394
|
|
|
239
395
|
## Hooks
|
|
396
|
+
You can mark methods of your classes as hooks. It's useful when you want to do something after construct of dispose classes.
|
|
240
397
|
|
|
241
398
|
```typescript
|
|
242
399
|
import {
|
|
@@ -245,8 +402,25 @@ import {
|
|
|
245
402
|
ContainerHook,
|
|
246
403
|
Injector,
|
|
247
404
|
Registration,
|
|
248
|
-
|
|
249
|
-
|
|
405
|
+
getHooks,
|
|
406
|
+
hook,
|
|
407
|
+
} from "ts-ioc-container";
|
|
408
|
+
|
|
409
|
+
const onConstruct = hook('onConstruct');
|
|
410
|
+
const onDispose = hook('onDispose');
|
|
411
|
+
|
|
412
|
+
class MyInjector implements IInjector {
|
|
413
|
+
private injector = new ReflectionInjector();
|
|
414
|
+
|
|
415
|
+
resolve<T>(container: IContainer, value: constructor<T>, ...deps: unknown[]): T {
|
|
416
|
+
const instance = this.injector.resolve(container, value, ...deps);
|
|
417
|
+
for (const h of getHooks(instance, 'onConstruct')) {
|
|
418
|
+
// @ts-ignore
|
|
419
|
+
instance[h]();
|
|
420
|
+
}
|
|
421
|
+
return instance;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
250
424
|
|
|
251
425
|
@forKey('ILogger')
|
|
252
426
|
class Logger {
|
|
@@ -261,87 +435,27 @@ class Logger {
|
|
|
261
435
|
}
|
|
262
436
|
}
|
|
263
437
|
|
|
264
|
-
const
|
|
265
|
-
resolve<T>(container: IContainer, value: constructor<T>, ...deps: unknown[]): T {
|
|
266
|
-
const instance = resolve(container)(value, ...deps);
|
|
267
|
-
for (const h of getHooks(instance, 'OnConstruct')) {
|
|
268
|
-
// @ts-ignore
|
|
269
|
-
instance[h]();
|
|
270
|
-
}
|
|
271
|
-
return instance;
|
|
272
|
-
},
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const container = new Container(injector)
|
|
438
|
+
const container = new Container(new MyInjector())
|
|
276
439
|
.add(Registration.fromClass(Logger));
|
|
277
440
|
const logger = container.resolve<ILogger>('ILogger'); // initialized
|
|
441
|
+
|
|
278
442
|
for (const instance of container.getInstances()) {
|
|
279
|
-
for (const h of getHooks(instance, '
|
|
443
|
+
for (const h of getHooks(instance, 'onDispose')) {
|
|
280
444
|
// @ts-ignore
|
|
281
|
-
instance[h]();
|
|
445
|
+
instance[h](); // disposed
|
|
282
446
|
}
|
|
283
447
|
}
|
|
284
448
|
```
|
|
285
449
|
|
|
286
|
-
## Scopes (child containers)
|
|
287
|
-
|
|
288
|
-
- tags - you can add tag to scope and root container. And register provider per tag.
|
|
289
|
-
|
|
290
|
-
```typescript
|
|
291
|
-
import { composeDecorators } from "ts-constructor-injector";
|
|
292
|
-
import { forKey, provider, Registration, asSingleton, perTags } from "ts-request-mediator";
|
|
293
|
-
|
|
294
|
-
@forKey('IEngine')
|
|
295
|
-
@provider(perTags('root'), asSingleton())
|
|
296
|
-
class Logger {
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
@forKey('IEngine')
|
|
300
|
-
@provider(perTags('home'), asSingleton())
|
|
301
|
-
class Engine {
|
|
302
|
-
constructor(@inject(by('ILogger')) private logger: ILogger) {
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
const container = new Container(injector, { tags: ['root'] })
|
|
307
|
-
.add(Registration.fromClass(Logger))
|
|
308
|
-
.add(Registration.fromClass(Engine));
|
|
309
|
-
|
|
310
|
-
const scope = container.createScope(['home', 'child']);
|
|
311
|
-
const logger = scope.resolve('ILogger');
|
|
312
|
-
scope.dispose();
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
## Container modules
|
|
316
|
-
|
|
317
|
-
```typescript
|
|
318
|
-
import { Registration } from "ts-request-mediator";
|
|
319
|
-
|
|
320
|
-
class Development implements IContainerModule {
|
|
321
|
-
applyTo(container: IContainer): void {
|
|
322
|
-
container.add(Registration.fromClass(DevLogger));
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
class Production implements IContainerModule {
|
|
327
|
-
applyTo(container: IContainer): void {
|
|
328
|
-
container.add(Registration.fromClass(ProdLogger));
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
const container = new Container(injector, { tags: ['root'] })
|
|
333
|
-
.add(Registration.fromClass(Logger))
|
|
334
|
-
.add(process.env.NODE_ENV === 'production' ? new Production() : new Development());
|
|
335
|
-
```
|
|
336
|
-
|
|
337
450
|
## Mocking / Tests
|
|
451
|
+
`AutoMockedContainer`. It will generate mocks for every dependency that you didn't define.
|
|
338
452
|
|
|
339
453
|
```typescript
|
|
340
454
|
import {
|
|
341
455
|
AutoMockedContainer,
|
|
342
456
|
Container,
|
|
343
457
|
DependencyKey,
|
|
344
|
-
} from "ts-
|
|
458
|
+
} from "ts-ioc-container";
|
|
345
459
|
import { Mock } from "moq.ts";
|
|
346
460
|
|
|
347
461
|
export class MoqContainer extends AutoMockedContainer {
|
|
@@ -351,10 +465,6 @@ export class MoqContainer extends AutoMockedContainer {
|
|
|
351
465
|
return this.resolveMock<T>(key).object();
|
|
352
466
|
}
|
|
353
467
|
|
|
354
|
-
dispose(): void {
|
|
355
|
-
this.mocks.clear();
|
|
356
|
-
}
|
|
357
|
-
|
|
358
468
|
resolveMock<T>(key: DependencyKey): IMock<T> {
|
|
359
469
|
if (!this.mocks.has(key)) {
|
|
360
470
|
this.mocks.set(key, new Mock());
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DependencyKey, IContainer, IContainerModule, InjectionToken, Tag, Tagged } from './IContainer';
|
|
2
|
-
import { IInjector } from '../IInjector';
|
|
2
|
+
import { IInjector } from '../injector/IInjector';
|
|
3
3
|
import { IProvider } from '../provider/IProvider';
|
|
4
4
|
export declare class Container implements IContainer, Tagged {
|
|
5
5
|
private readonly injector;
|
package/cjs/index.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { IContainer, InjectionToken } from './container/IContainer';
|
|
2
1
|
export { IContainer, Resolvable, IContainerModule, isDependencyKey, DependencyKey, InjectionToken, Tag, } from './container/IContainer';
|
|
3
2
|
export { constructor } from './utils';
|
|
4
3
|
export { Container } from './container/Container';
|
|
5
4
|
export { ResolveDependency, IProvider } from './provider/IProvider';
|
|
6
|
-
export { IInjector } from './IInjector';
|
|
5
|
+
export { IInjector } from './injector/IInjector';
|
|
7
6
|
export { DependencyNotFoundError } from './container/DependencyNotFoundError';
|
|
8
7
|
export { MethodNotImplementedError } from './container/MethodNotImplementedError';
|
|
9
8
|
export { ContainerDisposedError } from './container/ContainerDisposedError';
|
|
@@ -14,4 +13,7 @@ export { perTags } from './provider/TaggedProvider';
|
|
|
14
13
|
export { AutoMockedContainer } from './container/AutoMockedContainer';
|
|
15
14
|
export { forKey, Registration } from './registration/Registration';
|
|
16
15
|
export { DependencyMissingKeyError } from './registration/DependencyMissingKeyError';
|
|
17
|
-
export
|
|
16
|
+
export { ReflectionInjector, inject, by } from './injector/ReflectionInjector';
|
|
17
|
+
export { SimpleInjector } from './injector/SimpleInjector';
|
|
18
|
+
export { ProxyInjector } from './injector/ProxyInjector';
|
|
19
|
+
export { hook, getHooks } from './reflection';
|
package/cjs/index.js
CHANGED
|
@@ -1,31 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __read = (this && this.__read) || function (o, n) {
|
|
3
|
-
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
4
|
-
if (!m) return o;
|
|
5
|
-
var i = m.call(o), r, ar = [], e;
|
|
6
|
-
try {
|
|
7
|
-
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
8
|
-
}
|
|
9
|
-
catch (error) { e = { error: error }; }
|
|
10
|
-
finally {
|
|
11
|
-
try {
|
|
12
|
-
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
13
|
-
}
|
|
14
|
-
finally { if (e) throw e.error; }
|
|
15
|
-
}
|
|
16
|
-
return ar;
|
|
17
|
-
};
|
|
18
|
-
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
19
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
20
|
-
if (ar || !(i in from)) {
|
|
21
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
22
|
-
ar[i] = from[i];
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
26
|
-
};
|
|
27
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
-
exports.by = exports.DependencyMissingKeyError = exports.Registration = exports.forKey = exports.AutoMockedContainer = exports.perTags = exports.asSingleton = exports.withArgs = exports.withArgsFn = exports.provider = exports.Provider = exports.ContainerDisposedError = exports.MethodNotImplementedError = exports.DependencyNotFoundError = exports.Container = exports.isDependencyKey = void 0;
|
|
3
|
+
exports.getHooks = exports.hook = exports.ProxyInjector = exports.SimpleInjector = exports.by = exports.inject = exports.ReflectionInjector = exports.DependencyMissingKeyError = exports.Registration = exports.forKey = exports.AutoMockedContainer = exports.perTags = exports.asSingleton = exports.withArgs = exports.withArgsFn = exports.provider = exports.Provider = exports.ContainerDisposedError = exports.MethodNotImplementedError = exports.DependencyNotFoundError = exports.Container = exports.isDependencyKey = void 0;
|
|
29
4
|
var IContainer_1 = require("./container/IContainer");
|
|
30
5
|
Object.defineProperty(exports, "isDependencyKey", { enumerable: true, get: function () { return IContainer_1.isDependencyKey; } });
|
|
31
6
|
var Container_1 = require("./container/Container");
|
|
@@ -53,14 +28,15 @@ Object.defineProperty(exports, "forKey", { enumerable: true, get: function () {
|
|
|
53
28
|
Object.defineProperty(exports, "Registration", { enumerable: true, get: function () { return Registration_1.Registration; } });
|
|
54
29
|
var DependencyMissingKeyError_1 = require("./registration/DependencyMissingKeyError");
|
|
55
30
|
Object.defineProperty(exports, "DependencyMissingKeyError", { enumerable: true, get: function () { return DependencyMissingKeyError_1.DependencyMissingKeyError; } });
|
|
56
|
-
var
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
exports.
|
|
31
|
+
var ReflectionInjector_1 = require("./injector/ReflectionInjector");
|
|
32
|
+
Object.defineProperty(exports, "ReflectionInjector", { enumerable: true, get: function () { return ReflectionInjector_1.ReflectionInjector; } });
|
|
33
|
+
Object.defineProperty(exports, "inject", { enumerable: true, get: function () { return ReflectionInjector_1.inject; } });
|
|
34
|
+
Object.defineProperty(exports, "by", { enumerable: true, get: function () { return ReflectionInjector_1.by; } });
|
|
35
|
+
var SimpleInjector_1 = require("./injector/SimpleInjector");
|
|
36
|
+
Object.defineProperty(exports, "SimpleInjector", { enumerable: true, get: function () { return SimpleInjector_1.SimpleInjector; } });
|
|
37
|
+
var ProxyInjector_1 = require("./injector/ProxyInjector");
|
|
38
|
+
Object.defineProperty(exports, "ProxyInjector", { enumerable: true, get: function () { return ProxyInjector_1.ProxyInjector; } });
|
|
39
|
+
var reflection_1 = require("./reflection");
|
|
40
|
+
Object.defineProperty(exports, "hook", { enumerable: true, get: function () { return reflection_1.hook; } });
|
|
41
|
+
Object.defineProperty(exports, "getHooks", { enumerable: true, get: function () { return reflection_1.getHooks; } });
|
|
66
42
|
//# sourceMappingURL=index.js.map
|
package/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":";;;AAAA,qDAQgC;AAJ5B,6GAAA,eAAe,OAAA;AAMnB,mDAAkD;AAAzC,sGAAA,SAAS,OAAA;AAGlB,+EAA8E;AAArE,kIAAA,uBAAuB,OAAA;AAChC,mFAAkF;AAAzE,sIAAA,yBAAyB,OAAA;AAClC,6EAA4E;AAAnE,gIAAA,sBAAsB,OAAA;AAC/B,gDAAyD;AAAhD,oGAAA,QAAQ,OAAA;AAAE,oGAAA,QAAQ,OAAA;AAC3B,wDAAuE;AAAtD,0GAAA,UAAU,OAAA;AAAE,wGAAA,QAAQ,OAAA;AACrC,kEAA2D;AAAlD,gHAAA,WAAW,OAAA;AACpB,4DAAoD;AAA3C,yGAAA,OAAO,OAAA;AAChB,uEAAsE;AAA7D,0HAAA,mBAAmB,OAAA;AAC5B,4DAAmE;AAA1D,sGAAA,MAAM,OAAA;AAAE,4GAAA,YAAY,OAAA;AAC7B,sFAAqF;AAA5E,sIAAA,yBAAyB,OAAA;AAClC,oEAA+E;AAAtE,wHAAA,kBAAkB,OAAA;AAAE,4GAAA,MAAM,OAAA;AAAE,wGAAA,EAAE,OAAA;AACvC,4DAA2D;AAAlD,gHAAA,cAAc,OAAA;AACvB,0DAAyD;AAAhD,8GAAA,aAAa,OAAA;AACtB,2CAA8C;AAArC,kGAAA,IAAI,OAAA;AAAE,sGAAA,QAAQ,OAAA"}
|