ts-ioc-container 27.4.0 → 27.4.2
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 +95 -130
- package/cjm/container/Container.js +4 -2
- package/cjm/index.js +2 -1
- package/cjm/injector/ReflectionInjector.js +3 -7
- package/cjm/metadata.js +8 -1
- package/cjm/utils.js +10 -13
- package/esm/container/Container.js +4 -2
- package/esm/index.js +1 -1
- package/esm/injector/ReflectionInjector.js +4 -8
- package/esm/metadata.js +6 -0
- package/esm/utils.js +8 -11
- package/package.json +4 -4
- package/typings/container/Container.d.ts +1 -1
- package/typings/container/IContainer.d.ts +1 -1
- package/typings/index.d.ts +1 -1
- package/typings/metadata.d.ts +1 -0
- package/typings/utils.d.ts +1 -1
package/README.md
CHANGED
|
@@ -30,21 +30,20 @@
|
|
|
30
30
|
- [Simple injector](#simple-injector)
|
|
31
31
|
- [Proxy injector](#proxy-injector)
|
|
32
32
|
- [Providers](#providers)
|
|
33
|
+
- [Registration module (Provider + DependencyKey)](#registration-and-providers) `@key`
|
|
33
34
|
- [Provider](#provider) `@provider`
|
|
34
35
|
- [Singleton provider](#singleton-provider)
|
|
35
36
|
- [Tagged provider](#tagged-provider)
|
|
36
37
|
- [Args provider](#args-provider)
|
|
37
38
|
- [Aliases](#aliases) `alias`
|
|
38
39
|
- [Container modules](#container-modules)
|
|
39
|
-
- [Basic usage](#basic-usage-1)
|
|
40
|
-
- [Registration module (Provider + DependencyKey)](#registration-module-provider--dependencykey) `@key`
|
|
41
40
|
- [Hooks](#hooks) `@hook`
|
|
42
41
|
- [OnConstruct](#onconstruct) `@onConstruct`
|
|
43
42
|
- [OnDispose](#ondispose) `@onDispose`
|
|
44
43
|
- [Tests and Mocks](#tests-and-mocks)
|
|
45
44
|
- [Errors](#errors)
|
|
46
45
|
|
|
47
|
-
##
|
|
46
|
+
## Setu
|
|
48
47
|
|
|
49
48
|
```shell script
|
|
50
49
|
npm install ts-ioc-container reflect-metadata
|
|
@@ -78,16 +77,7 @@ And `tsconfig.json` should have next options:
|
|
|
78
77
|
|
|
79
78
|
```typescript
|
|
80
79
|
import 'reflect-metadata';
|
|
81
|
-
import {
|
|
82
|
-
IContainer,
|
|
83
|
-
by,
|
|
84
|
-
Container,
|
|
85
|
-
inject,
|
|
86
|
-
ReflectionInjector,
|
|
87
|
-
RegistrationConflictError,
|
|
88
|
-
Registration,
|
|
89
|
-
key,
|
|
90
|
-
} from 'ts-ioc-container';
|
|
80
|
+
import { IContainer, by, Container, inject, ReflectionInjector, Registration as R } from 'ts-ioc-container';
|
|
91
81
|
|
|
92
82
|
describe('Basic usage', function () {
|
|
93
83
|
class Logger {
|
|
@@ -99,7 +89,7 @@ describe('Basic usage', function () {
|
|
|
99
89
|
constructor(@inject(by.key('ILogger')) public logger: Logger) {}
|
|
100
90
|
}
|
|
101
91
|
|
|
102
|
-
const container = new Container(new ReflectionInjector()).use(
|
|
92
|
+
const container = new Container(new ReflectionInjector()).use(R.fromClass(Logger).to('ILogger'));
|
|
103
93
|
|
|
104
94
|
expect(container.resolve(App).logger.name).toBe('Logger');
|
|
105
95
|
});
|
|
@@ -110,8 +100,8 @@ describe('Basic usage', function () {
|
|
|
110
100
|
}
|
|
111
101
|
|
|
112
102
|
const container = new Container(new ReflectionInjector())
|
|
113
|
-
.use(
|
|
114
|
-
.use(
|
|
103
|
+
.use(R.fromClass(Logger).to('ILogger1'))
|
|
104
|
+
.use(R.fromClass(Logger).to('ILogger2'));
|
|
115
105
|
|
|
116
106
|
expect(container.resolve(App).loggers).toHaveLength(2);
|
|
117
107
|
});
|
|
@@ -127,46 +117,6 @@ describe('Basic usage', function () {
|
|
|
127
117
|
|
|
128
118
|
expect(app.scope).toBe(root);
|
|
129
119
|
});
|
|
130
|
-
|
|
131
|
-
it('should not raise an error when key is busy', () => {
|
|
132
|
-
expect(() => {
|
|
133
|
-
new Container(new ReflectionInjector()).use(Registration.fromClass(Logger)).use(Registration.fromClass(Logger));
|
|
134
|
-
}).not.toThrowError(RegistrationConflictError);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it('registration -> should raise an error when key is busy', () => {
|
|
138
|
-
expect(() => {
|
|
139
|
-
new Container(new ReflectionInjector())
|
|
140
|
-
.use(Registration.fromClass(Logger))
|
|
141
|
-
.use(Registration.fromClass(Logger).throwErrorOnConflict());
|
|
142
|
-
}).toThrowError(RegistrationConflictError);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it('registration -> should not raise an error when key is busy', () => {
|
|
146
|
-
expect(() => {
|
|
147
|
-
new Container(new ReflectionInjector()).use(Registration.fromClass(Logger)).use(Registration.fromClass(Logger));
|
|
148
|
-
}).not.toThrowError(RegistrationConflictError);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('@key -> should raise an error when key is busy', () => {
|
|
152
|
-
@key('Logger')
|
|
153
|
-
class Logger1 {}
|
|
154
|
-
|
|
155
|
-
@key('Logger')
|
|
156
|
-
class Logger2 {}
|
|
157
|
-
|
|
158
|
-
expect(() => {
|
|
159
|
-
new Container(new ReflectionInjector())
|
|
160
|
-
.use(Registration.fromClass(Logger1))
|
|
161
|
-
.use(Registration.fromClass(Logger2).throwErrorOnConflict());
|
|
162
|
-
}).toThrowError(RegistrationConflictError);
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('registration -> should not raise an error when key is busy', () => {
|
|
166
|
-
expect(() => {
|
|
167
|
-
new Container(new ReflectionInjector()).use(Registration.fromClass(Logger)).use(Registration.fromClass(Logger));
|
|
168
|
-
}).not.toThrowError(RegistrationConflictError);
|
|
169
|
-
});
|
|
170
120
|
});
|
|
171
121
|
|
|
172
122
|
```
|
|
@@ -190,7 +140,7 @@ import {
|
|
|
190
140
|
tags,
|
|
191
141
|
provider,
|
|
192
142
|
ReflectionInjector,
|
|
193
|
-
Registration,
|
|
143
|
+
Registration as R,
|
|
194
144
|
by,
|
|
195
145
|
} from 'ts-ioc-container';
|
|
196
146
|
|
|
@@ -200,7 +150,7 @@ class Logger {}
|
|
|
200
150
|
|
|
201
151
|
describe('Scopes', function () {
|
|
202
152
|
it('should resolve dependencies from scope', function () {
|
|
203
|
-
const root = new Container(new ReflectionInjector(), { tags: ['root'] }).use(
|
|
153
|
+
const root = new Container(new ReflectionInjector(), { tags: ['root'] }).use(R.fromClass(Logger));
|
|
204
154
|
const child = root.createScope('child');
|
|
205
155
|
|
|
206
156
|
expect(child.resolve('ILogger')).toBe(child.resolve('ILogger'));
|
|
@@ -230,14 +180,14 @@ Sometimes you want to get all instances from container and its scopes. For examp
|
|
|
230
180
|
|
|
231
181
|
```typescript
|
|
232
182
|
import 'reflect-metadata';
|
|
233
|
-
import { inject, key, Registration, Container, ReflectionInjector, by } from 'ts-ioc-container';
|
|
183
|
+
import { inject, key, Registration as R, Container, ReflectionInjector, by } from 'ts-ioc-container';
|
|
234
184
|
|
|
235
185
|
describe('Instances', function () {
|
|
236
186
|
@key('ILogger')
|
|
237
187
|
class Logger {}
|
|
238
188
|
|
|
239
189
|
it('should return injected instances', () => {
|
|
240
|
-
const container = new Container(new ReflectionInjector()).use(
|
|
190
|
+
const container = new Container(new ReflectionInjector()).use(R.fromClass(Logger));
|
|
241
191
|
const scope = container.createScope();
|
|
242
192
|
|
|
243
193
|
const logger1 = container.resolve('ILogger');
|
|
@@ -254,7 +204,7 @@ describe('Instances', function () {
|
|
|
254
204
|
constructor(@inject(by.instances(isLogger)) public loggers: Logger[]) {}
|
|
255
205
|
}
|
|
256
206
|
|
|
257
|
-
const container = new Container(new ReflectionInjector()).use(
|
|
207
|
+
const container = new Container(new ReflectionInjector()).use(R.fromClass(Logger));
|
|
258
208
|
|
|
259
209
|
const logger0 = container.resolve('ILogger');
|
|
260
210
|
const logger1 = container.resolve('ILogger');
|
|
@@ -277,15 +227,13 @@ Sometimes you want to dispose container and all its scopes. For example, when yo
|
|
|
277
227
|
|
|
278
228
|
```typescript
|
|
279
229
|
import 'reflect-metadata';
|
|
280
|
-
import { Container, ContainerDisposedError, ReflectionInjector, Registration } from 'ts-ioc-container';
|
|
230
|
+
import { Container, ContainerDisposedError, ReflectionInjector, Registration as R } from 'ts-ioc-container';
|
|
281
231
|
|
|
282
232
|
class Logger {}
|
|
283
233
|
|
|
284
234
|
describe('Disposing', function () {
|
|
285
235
|
it('should container and make it unavailable for the further usage', function () {
|
|
286
|
-
const root = new Container(new ReflectionInjector(), { tags: ['root'] }).use(
|
|
287
|
-
Registration.fromClass(Logger).to('ILogger'),
|
|
288
|
-
);
|
|
236
|
+
const root = new Container(new ReflectionInjector(), { tags: ['root'] }).use(R.fromClass(Logger).to('ILogger'));
|
|
289
237
|
const child = root.createScope('child');
|
|
290
238
|
|
|
291
239
|
const logger = child.resolve('ILogger');
|
|
@@ -311,7 +259,7 @@ This type of injector uses `@inject` decorator to mark where dependencies should
|
|
|
311
259
|
|
|
312
260
|
```typescript
|
|
313
261
|
import 'reflect-metadata';
|
|
314
|
-
import { by, Container, inject, ReflectionInjector, Registration } from 'ts-ioc-container';
|
|
262
|
+
import { by, Container, inject, ReflectionInjector, Registration as R } from 'ts-ioc-container';
|
|
315
263
|
|
|
316
264
|
class Logger {
|
|
317
265
|
name = 'Logger';
|
|
@@ -331,7 +279,7 @@ class App {
|
|
|
331
279
|
|
|
332
280
|
describe('Reflection Injector', function () {
|
|
333
281
|
it('should inject dependencies by @inject decorator', function () {
|
|
334
|
-
const container = new Container(new ReflectionInjector()).use(
|
|
282
|
+
const container = new Container(new ReflectionInjector()).use(R.fromClass(Logger).to('ILogger'));
|
|
335
283
|
|
|
336
284
|
const app = container.resolve(App);
|
|
337
285
|
|
|
@@ -346,7 +294,7 @@ This type of injector just passes container to constructor with others arguments
|
|
|
346
294
|
|
|
347
295
|
```typescript
|
|
348
296
|
import 'reflect-metadata';
|
|
349
|
-
import { Container, IContainer, Registration, SimpleInjector } from 'ts-ioc-container';
|
|
297
|
+
import { Container, IContainer, Registration as R, SimpleInjector } from 'ts-ioc-container';
|
|
350
298
|
|
|
351
299
|
describe('SimpleInjector', function () {
|
|
352
300
|
it('should pass container as first parameter', function () {
|
|
@@ -354,7 +302,7 @@ describe('SimpleInjector', function () {
|
|
|
354
302
|
constructor(public container: IContainer) {}
|
|
355
303
|
}
|
|
356
304
|
|
|
357
|
-
const container = new Container(new SimpleInjector()).use(
|
|
305
|
+
const container = new Container(new SimpleInjector()).use(R.fromClass(App).to('App'));
|
|
358
306
|
const app = container.resolve<App>('App');
|
|
359
307
|
|
|
360
308
|
expect(app.container).toBeInstanceOf(Container);
|
|
@@ -365,7 +313,7 @@ describe('SimpleInjector', function () {
|
|
|
365
313
|
constructor(container: IContainer, public greeting: string) {}
|
|
366
314
|
}
|
|
367
315
|
|
|
368
|
-
const container = new Container(new SimpleInjector()).use(
|
|
316
|
+
const container = new Container(new SimpleInjector()).use(R.fromClass(App).to('App'));
|
|
369
317
|
const app = container.resolve<App>('App', 'Hello world');
|
|
370
318
|
|
|
371
319
|
expect(app.greeting).toBe('Hello world');
|
|
@@ -379,7 +327,7 @@ This type of injector injects dependencies as dictionary `Record<string, unknown
|
|
|
379
327
|
|
|
380
328
|
```typescript
|
|
381
329
|
import 'reflect-metadata';
|
|
382
|
-
import { Container, ProxyInjector, args, Registration } from 'ts-ioc-container';
|
|
330
|
+
import { Container, ProxyInjector, args, Registration as R } from 'ts-ioc-container';
|
|
383
331
|
|
|
384
332
|
describe('ProxyInjector', function () {
|
|
385
333
|
it('should pass dependency to constructor as dictionary', function () {
|
|
@@ -393,7 +341,7 @@ describe('ProxyInjector', function () {
|
|
|
393
341
|
}
|
|
394
342
|
}
|
|
395
343
|
|
|
396
|
-
const container = new Container(new ProxyInjector()).use(
|
|
344
|
+
const container = new Container(new ProxyInjector()).use(R.fromClass(Logger).to('logger'));
|
|
397
345
|
|
|
398
346
|
const app = container.resolve(App);
|
|
399
347
|
expect(app.logger).toBeInstanceOf(Logger);
|
|
@@ -423,8 +371,8 @@ describe('ProxyInjector', function () {
|
|
|
423
371
|
const greetingTemplate = (name: string) => `Hello ${name}`;
|
|
424
372
|
|
|
425
373
|
const container = new Container(new ProxyInjector())
|
|
426
|
-
.use(
|
|
427
|
-
.use(
|
|
374
|
+
.use(R.fromClass(App).to('App').pipe(args({ greetingTemplate })))
|
|
375
|
+
.use(R.fromClass(Logger).to('logger'));
|
|
428
376
|
|
|
429
377
|
const app = container.resolve<App>('App', { name: `world` });
|
|
430
378
|
expect(app.greeting).toBe('Hello world');
|
|
@@ -433,7 +381,7 @@ describe('ProxyInjector', function () {
|
|
|
433
381
|
|
|
434
382
|
```
|
|
435
383
|
|
|
436
|
-
## Providers
|
|
384
|
+
## Registration and Providers
|
|
437
385
|
`IProvider<T>` is used to describe how instances should be created. It has next basic methods:
|
|
438
386
|
- `resolve` - creates instance with passed arguments
|
|
439
387
|
- `clone` - we invoke it when we create a scope. It clones provider to new scope.
|
|
@@ -445,6 +393,58 @@ There are next types of providers:
|
|
|
445
393
|
- `TaggedProvider` - provider that can be resolved only from container with certain tags and their sub scopes
|
|
446
394
|
- `ArgsProvider` - provider that encapsulates arguments to pass it to constructor.
|
|
447
395
|
|
|
396
|
+
`Registration` - just a helper to register provider with certain key. `(preferrably to use)`
|
|
397
|
+
|
|
398
|
+
### Registration (Provider + DependencyKey)
|
|
399
|
+
Sometimes you need to keep dependency key with class together. For example, you want to register class with key 'ILogger' and you want to keep this key with class. This is what `Registration` is for.
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
import 'reflect-metadata';
|
|
403
|
+
import { singleton, Container, tags, provider, ReflectionInjector, Registration as R, key } from 'ts-ioc-container';
|
|
404
|
+
import { DependencyMissingKeyError } from '../../lib/errors/DependencyMissingKeyError';
|
|
405
|
+
|
|
406
|
+
describe('Registration module', function () {
|
|
407
|
+
const createContainer = () => new Container(new ReflectionInjector(), { tags: ['root'] });
|
|
408
|
+
|
|
409
|
+
it('should register class', function () {
|
|
410
|
+
@key('ILogger')
|
|
411
|
+
@provider(singleton(), tags('root'))
|
|
412
|
+
class Logger {}
|
|
413
|
+
|
|
414
|
+
const root = createContainer().use(R.fromClass(Logger));
|
|
415
|
+
|
|
416
|
+
expect(root.resolve('ILogger')).toBeInstanceOf(Logger);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('should register value', function () {
|
|
420
|
+
const root = createContainer().use(R.fromValue('smth').to('ISmth'));
|
|
421
|
+
|
|
422
|
+
expect(root.resolve('ISmth')).toBe('smth');
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('should register fn', function () {
|
|
426
|
+
const root = createContainer().use(R.fromFn(() => 'smth').to('ISmth'));
|
|
427
|
+
|
|
428
|
+
expect(root.resolve('ISmth')).toBe('smth');
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it('should raise an error if key is not provider', () => {
|
|
432
|
+
expect(() => {
|
|
433
|
+
createContainer().use(R.fromValue('smth'));
|
|
434
|
+
}).toThrowError(DependencyMissingKeyError);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it('should register dependency by class name if @key is not provided', function () {
|
|
438
|
+
class FileLogger {}
|
|
439
|
+
|
|
440
|
+
const root = createContainer().use(R.fromClass(FileLogger));
|
|
441
|
+
|
|
442
|
+
expect(root.resolve('FileLogger')).toBeInstanceOf(FileLogger);
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
```
|
|
447
|
+
|
|
448
448
|
### Provider
|
|
449
449
|
|
|
450
450
|
```typescript
|
|
@@ -492,7 +492,7 @@ Sometimes you need to create only one instance of dependency per scope. For exam
|
|
|
492
492
|
|
|
493
493
|
```typescript
|
|
494
494
|
import 'reflect-metadata';
|
|
495
|
-
import { singleton, Container, key, provider, ReflectionInjector, Registration } from 'ts-ioc-container';
|
|
495
|
+
import { singleton, Container, key, provider, ReflectionInjector, Registration as R } from 'ts-ioc-container';
|
|
496
496
|
|
|
497
497
|
@key('logger')
|
|
498
498
|
@provider(singleton())
|
|
@@ -504,20 +504,20 @@ describe('Singleton', function () {
|
|
|
504
504
|
}
|
|
505
505
|
|
|
506
506
|
it('should resolve the same container per every request', function () {
|
|
507
|
-
const container = createContainer().use(
|
|
507
|
+
const container = createContainer().use(R.fromClass(Logger));
|
|
508
508
|
|
|
509
509
|
expect(container.resolve('logger')).toBe(container.resolve('logger'));
|
|
510
510
|
});
|
|
511
511
|
|
|
512
512
|
it('should resolve different dependency per scope', function () {
|
|
513
|
-
const container = createContainer().use(
|
|
513
|
+
const container = createContainer().use(R.fromClass(Logger));
|
|
514
514
|
const child = container.createScope();
|
|
515
515
|
|
|
516
516
|
expect(container.resolve('logger')).not.toBe(child.resolve('logger'));
|
|
517
517
|
});
|
|
518
518
|
|
|
519
519
|
it('should resolve the same dependency for scope', function () {
|
|
520
|
-
const container = createContainer().use(
|
|
520
|
+
const container = createContainer().use(R.fromClass(Logger));
|
|
521
521
|
const child = container.createScope();
|
|
522
522
|
|
|
523
523
|
expect(child.resolve('logger')).toBe(child.resolve('logger'));
|
|
@@ -532,14 +532,14 @@ Sometimes you need to resolve provider only from container with certain tags and
|
|
|
532
532
|
|
|
533
533
|
```typescript
|
|
534
534
|
import 'reflect-metadata';
|
|
535
|
-
import { singleton, Container, key, tags, provider, ReflectionInjector, Registration } from 'ts-ioc-container';
|
|
535
|
+
import { singleton, Container, key, tags, provider, ReflectionInjector, Registration as R } from 'ts-ioc-container';
|
|
536
536
|
|
|
537
537
|
@key('ILogger')
|
|
538
538
|
@provider(singleton(), tags('root')) // the same as .pipe(singleton(), tags('root'))
|
|
539
539
|
class Logger {}
|
|
540
540
|
describe('TaggedProvider', function () {
|
|
541
541
|
it('should return the same instance', function () {
|
|
542
|
-
const root = new Container(new ReflectionInjector(), { tags: ['root'] }).use(
|
|
542
|
+
const root = new Container(new ReflectionInjector(), { tags: ['root'] }).use(R.fromClass(Logger));
|
|
543
543
|
const child = root.createScope();
|
|
544
544
|
expect(root.resolve('ILogger')).toBe(child.resolve('ILogger'));
|
|
545
545
|
});
|
|
@@ -553,7 +553,7 @@ Sometimes you want to bind some arguments to provider. This is what `ArgsProvide
|
|
|
553
553
|
|
|
554
554
|
```typescript
|
|
555
555
|
import 'reflect-metadata';
|
|
556
|
-
import { Container, key, argsFn, args, ReflectionInjector, Registration } from 'ts-ioc-container';
|
|
556
|
+
import { Container, key, argsFn, args, ReflectionInjector, Registration as R } from 'ts-ioc-container';
|
|
557
557
|
|
|
558
558
|
@key('logger')
|
|
559
559
|
class Logger {
|
|
@@ -566,21 +566,21 @@ describe('ArgsProvider', function () {
|
|
|
566
566
|
}
|
|
567
567
|
|
|
568
568
|
it('can assign argument function to provider', function () {
|
|
569
|
-
const root = createContainer().use(
|
|
569
|
+
const root = createContainer().use(R.fromClass(Logger).pipe(argsFn((container, ...args) => ['name'])));
|
|
570
570
|
|
|
571
571
|
const logger = root.createScope().resolve<Logger>('logger');
|
|
572
572
|
expect(logger.name).toBe('name');
|
|
573
573
|
});
|
|
574
574
|
|
|
575
575
|
it('can assign argument to provider', function () {
|
|
576
|
-
const root = createContainer().use(
|
|
576
|
+
const root = createContainer().use(R.fromClass(Logger).pipe(args('name')));
|
|
577
577
|
|
|
578
578
|
const logger = root.resolve<Logger>('logger');
|
|
579
579
|
expect(logger.name).toBe('name');
|
|
580
580
|
});
|
|
581
581
|
|
|
582
582
|
it('should set provider arguments with highest priority in compare to resolve arguments', function () {
|
|
583
|
-
const root = createContainer().use(
|
|
583
|
+
const root = createContainer().use(R.fromClass(Logger).pipe(args('name')));
|
|
584
584
|
|
|
585
585
|
const logger = root.resolve<Logger>('logger', 'file');
|
|
586
586
|
|
|
@@ -599,7 +599,7 @@ Sometimes you want to register the same provider with different keys. This is wh
|
|
|
599
599
|
|
|
600
600
|
```typescript
|
|
601
601
|
import 'reflect-metadata';
|
|
602
|
-
import { alias, by, Container, inject, provider, ReflectionInjector, Registration } from 'ts-ioc-container';
|
|
602
|
+
import { alias, by, Container, inject, provider, ReflectionInjector, Registration as R } from 'ts-ioc-container';
|
|
603
603
|
|
|
604
604
|
describe('alias', () => {
|
|
605
605
|
const IMiddlewareKey = Symbol('IMiddleware');
|
|
@@ -653,8 +653,8 @@ describe('alias', () => {
|
|
|
653
653
|
}
|
|
654
654
|
|
|
655
655
|
const container = new Container(new ReflectionInjector())
|
|
656
|
-
.use(
|
|
657
|
-
.use(
|
|
656
|
+
.use(R.fromClass(LoggerMiddleware))
|
|
657
|
+
.use(R.fromClass(ErrorHandlerMiddleware));
|
|
658
658
|
|
|
659
659
|
const app = container.resolve(App);
|
|
660
660
|
app.run();
|
|
@@ -669,11 +669,9 @@ describe('alias', () => {
|
|
|
669
669
|
## Container modules
|
|
670
670
|
Sometimes you want to encapsulate registration logic in separate module. This is what `IContainerModule` is for.
|
|
671
671
|
|
|
672
|
-
### Basic usage
|
|
673
|
-
|
|
674
672
|
```typescript
|
|
675
673
|
import 'reflect-metadata';
|
|
676
|
-
import { IContainerModule, Registration, IContainer, key, Container, ReflectionInjector } from 'ts-ioc-container';
|
|
674
|
+
import { IContainerModule, Registration as R, IContainer, key, Container, ReflectionInjector } from 'ts-ioc-container';
|
|
677
675
|
|
|
678
676
|
@key('ILogger')
|
|
679
677
|
class Logger {}
|
|
@@ -683,13 +681,13 @@ class TestLogger {}
|
|
|
683
681
|
|
|
684
682
|
class Production implements IContainerModule {
|
|
685
683
|
applyTo(container: IContainer): void {
|
|
686
|
-
container.use(
|
|
684
|
+
container.use(R.fromClass(Logger));
|
|
687
685
|
}
|
|
688
686
|
}
|
|
689
687
|
|
|
690
688
|
class Development implements IContainerModule {
|
|
691
689
|
applyTo(container: IContainer): void {
|
|
692
|
-
container.use(
|
|
690
|
+
container.use(R.fromClass(TestLogger));
|
|
693
691
|
}
|
|
694
692
|
}
|
|
695
693
|
|
|
@@ -713,37 +711,6 @@ describe('Container Modules', function () {
|
|
|
713
711
|
|
|
714
712
|
```
|
|
715
713
|
|
|
716
|
-
### Registration module (Provider + DependencyKey)
|
|
717
|
-
Sometimes you need to keep dependency key with class together. For example, you want to register class with key 'ILogger' and you want to keep this key with class. This is what `Registration` is for.
|
|
718
|
-
|
|
719
|
-
```typescript
|
|
720
|
-
import 'reflect-metadata';
|
|
721
|
-
import { singleton, Container, tags, provider, ReflectionInjector, Registration, key } from 'ts-ioc-container';
|
|
722
|
-
|
|
723
|
-
describe('Registration module', function () {
|
|
724
|
-
const createContainer = () => new Container(new ReflectionInjector(), { tags: ['root'] });
|
|
725
|
-
|
|
726
|
-
it('should register dependency by @key', function () {
|
|
727
|
-
@key('ILogger')
|
|
728
|
-
@provider(singleton(), tags('root'))
|
|
729
|
-
class Logger {}
|
|
730
|
-
|
|
731
|
-
const root = createContainer().use(Registration.fromClass(Logger));
|
|
732
|
-
|
|
733
|
-
expect(root.resolve('ILogger')).toBeInstanceOf(Logger);
|
|
734
|
-
});
|
|
735
|
-
|
|
736
|
-
it('should register dependency by class name if @key is not provided', function () {
|
|
737
|
-
class FileLogger {}
|
|
738
|
-
|
|
739
|
-
const root = createContainer().use(Registration.fromClass(FileLogger));
|
|
740
|
-
|
|
741
|
-
expect(root.resolve('FileLogger')).toBeInstanceOf(FileLogger);
|
|
742
|
-
});
|
|
743
|
-
});
|
|
744
|
-
|
|
745
|
-
```
|
|
746
|
-
|
|
747
714
|
## Hooks
|
|
748
715
|
Sometimes you need to invoke methods after construct or dispose of class. This is what hooks are for.
|
|
749
716
|
|
|
@@ -759,7 +726,7 @@ import {
|
|
|
759
726
|
IContainer,
|
|
760
727
|
IInjector,
|
|
761
728
|
ReflectionInjector,
|
|
762
|
-
Registration,
|
|
729
|
+
Registration as R,
|
|
763
730
|
} from 'ts-ioc-container';
|
|
764
731
|
|
|
765
732
|
class MyInjector implements IInjector {
|
|
@@ -792,7 +759,7 @@ class Logger {
|
|
|
792
759
|
|
|
793
760
|
describe('onConstruct', function () {
|
|
794
761
|
it('should make logger be ready on resolve', function () {
|
|
795
|
-
const container = new Container(new MyInjector()).use(
|
|
762
|
+
const container = new Container(new MyInjector()).use(R.fromClass(Logger));
|
|
796
763
|
|
|
797
764
|
const logger = container.resolve<Logger>('logger');
|
|
798
765
|
|
|
@@ -814,7 +781,7 @@ import {
|
|
|
814
781
|
hook,
|
|
815
782
|
inject,
|
|
816
783
|
provider,
|
|
817
|
-
Registration,
|
|
784
|
+
Registration as R,
|
|
818
785
|
ReflectionInjector,
|
|
819
786
|
} from 'ts-ioc-container';
|
|
820
787
|
|
|
@@ -847,9 +814,7 @@ class Logger {
|
|
|
847
814
|
|
|
848
815
|
describe('onDispose', function () {
|
|
849
816
|
it('should invoke hooks on all instances', async function () {
|
|
850
|
-
const container = new Container(new ReflectionInjector())
|
|
851
|
-
.use(Registration.fromClass(Logger))
|
|
852
|
-
.use(Registration.fromClass(LogsRepo));
|
|
817
|
+
const container = new Container(new ReflectionInjector()).use(R.fromClass(Logger)).use(R.fromClass(LogsRepo));
|
|
853
818
|
|
|
854
819
|
const logger = container.resolve<Logger>('logger');
|
|
855
820
|
logger.log('Hello');
|
package/cjm/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getMetadata = exports.setMetadata = exports.by = exports.hook = exports.getHooks = exports.ProxyInjector = exports.SimpleInjector = exports.inject = exports.ReflectionInjector = exports.Registration = exports.key = exports.AutoMockedContainer = exports.TaggedProvider = exports.tags = exports.SingletonProvider = exports.singleton = exports.ArgsProvider = exports.args = exports.argsFn = exports.provider = exports.Provider = exports.ContainerDisposedError = exports.MethodNotImplementedError = exports.DependencyNotFoundError = exports.alias = exports.EmptyContainer = exports.Container = exports.isDependencyKey = void 0;
|
|
3
|
+
exports.setParameterMetadata = exports.getMetadata = exports.setMetadata = exports.by = exports.hook = exports.getHooks = exports.ProxyInjector = exports.SimpleInjector = exports.inject = exports.ReflectionInjector = exports.Registration = exports.key = exports.AutoMockedContainer = exports.TaggedProvider = exports.tags = exports.SingletonProvider = exports.singleton = exports.ArgsProvider = exports.args = exports.argsFn = exports.provider = exports.Provider = exports.ContainerDisposedError = exports.MethodNotImplementedError = exports.DependencyNotFoundError = exports.alias = exports.EmptyContainer = exports.Container = exports.isDependencyKey = void 0;
|
|
4
4
|
var IContainer_1 = require("./container/IContainer");
|
|
5
5
|
Object.defineProperty(exports, "isDependencyKey", { enumerable: true, get: function () { return IContainer_1.isDependencyKey; } });
|
|
6
6
|
var Container_1 = require("./container/Container");
|
|
@@ -48,3 +48,4 @@ Object.defineProperty(exports, "by", { enumerable: true, get: function () { retu
|
|
|
48
48
|
var metadata_1 = require("./metadata");
|
|
49
49
|
Object.defineProperty(exports, "setMetadata", { enumerable: true, get: function () { return metadata_1.setMetadata; } });
|
|
50
50
|
Object.defineProperty(exports, "getMetadata", { enumerable: true, get: function () { return metadata_1.getMetadata; } });
|
|
51
|
+
Object.defineProperty(exports, "setParameterMetadata", { enumerable: true, get: function () { return metadata_1.setParameterMetadata; } });
|
|
@@ -2,19 +2,15 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ReflectionInjector = exports.inject = void 0;
|
|
4
4
|
const utils_1 = require("../utils");
|
|
5
|
+
const metadata_1 = require("../metadata");
|
|
5
6
|
const INJECT_KEY = 'INJECT_FN_LIST';
|
|
6
|
-
const inject = (value) => (
|
|
7
|
-
var _a;
|
|
8
|
-
const metadata = (_a = Reflect.getOwnMetadata(INJECT_KEY, target)) !== null && _a !== void 0 ? _a : [];
|
|
9
|
-
metadata[parameterIndex] = value;
|
|
10
|
-
Reflect.defineMetadata(INJECT_KEY, metadata, target);
|
|
11
|
-
};
|
|
7
|
+
const inject = (value) => (0, metadata_1.setParameterMetadata)(INJECT_KEY, value);
|
|
12
8
|
exports.inject = inject;
|
|
13
9
|
class ReflectionInjector {
|
|
14
10
|
resolve(container, Target, ...deps) {
|
|
15
11
|
var _a;
|
|
16
12
|
const injectionFns = (_a = Reflect.getOwnMetadata(INJECT_KEY, Target)) !== null && _a !== void 0 ? _a : [];
|
|
17
|
-
const args = (0, utils_1.
|
|
13
|
+
const args = (0, utils_1.fillEmptyIndexes)(injectionFns, deps.map(utils_1.constant)).map((fn) => fn(container));
|
|
18
14
|
return new Target(...args);
|
|
19
15
|
}
|
|
20
16
|
}
|
package/cjm/metadata.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getMetadata = exports.setMetadata = void 0;
|
|
3
|
+
exports.setParameterMetadata = exports.getMetadata = exports.setMetadata = void 0;
|
|
4
4
|
const setMetadata = (key, value) => (target) => {
|
|
5
5
|
Reflect.defineMetadata(key, value, target);
|
|
6
6
|
};
|
|
@@ -10,3 +10,10 @@ function getMetadata(target, key) {
|
|
|
10
10
|
return Reflect.getOwnMetadata(key, target);
|
|
11
11
|
}
|
|
12
12
|
exports.getMetadata = getMetadata;
|
|
13
|
+
const setParameterMetadata = (key, value) => (target, propertyKey, parameterIndex) => {
|
|
14
|
+
var _a;
|
|
15
|
+
const metadata = (_a = Reflect.getOwnMetadata(key, target)) !== null && _a !== void 0 ? _a : [];
|
|
16
|
+
metadata[parameterIndex] = value;
|
|
17
|
+
Reflect.defineMetadata(key, metadata, target);
|
|
18
|
+
};
|
|
19
|
+
exports.setParameterMetadata = setParameterMetadata;
|
package/cjm/utils.js
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.constant = exports.
|
|
3
|
+
exports.constant = exports.fillEmptyIndexes = exports.pipe = void 0;
|
|
4
4
|
function pipe(...mappers) {
|
|
5
5
|
return (value) => mappers.reduce((acc, current) => current(acc), value);
|
|
6
6
|
}
|
|
7
7
|
exports.pipe = pipe;
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
function fillEmptyIndexes(baseArr, insertArr) {
|
|
9
|
+
const a = [...baseArr];
|
|
10
|
+
const b = [...insertArr];
|
|
11
|
+
for (let i = 0; i < a.length; i++) {
|
|
12
|
+
if (a[i] === undefined) {
|
|
13
|
+
a[i] = b.shift();
|
|
14
|
+
}
|
|
11
15
|
}
|
|
12
|
-
|
|
13
|
-
return baseArr;
|
|
14
|
-
}
|
|
15
|
-
const [b1, ...restBaseArr] = baseArr;
|
|
16
|
-
const [i1, ...restInsertArr] = insertArr;
|
|
17
|
-
return b1 === undefined
|
|
18
|
-
? [i1].concat(merge(restBaseArr, restInsertArr))
|
|
19
|
-
: [b1].concat(merge(restBaseArr, insertArr));
|
|
16
|
+
return a.concat(b);
|
|
20
17
|
}
|
|
21
|
-
exports.
|
|
18
|
+
exports.fillEmptyIndexes = fillEmptyIndexes;
|
|
22
19
|
const constant = (value) => () => value;
|
|
23
20
|
exports.constant = constant;
|
|
@@ -64,8 +64,10 @@ export class Container {
|
|
|
64
64
|
hasTag(tag) {
|
|
65
65
|
return this.tags.includes(tag);
|
|
66
66
|
}
|
|
67
|
-
use(
|
|
68
|
-
module
|
|
67
|
+
use(...modules) {
|
|
68
|
+
for (const module of modules) {
|
|
69
|
+
module.applyTo(this);
|
|
70
|
+
}
|
|
69
71
|
return this;
|
|
70
72
|
}
|
|
71
73
|
hasDependency(key) {
|
package/esm/index.js
CHANGED
|
@@ -16,4 +16,4 @@ export { SimpleInjector } from './injector/SimpleInjector';
|
|
|
16
16
|
export { ProxyInjector } from './injector/ProxyInjector';
|
|
17
17
|
export { getHooks, hook } from './hook';
|
|
18
18
|
export { by } from './by';
|
|
19
|
-
export { setMetadata, getMetadata } from './metadata';
|
|
19
|
+
export { setMetadata, getMetadata, setParameterMetadata } from './metadata';
|
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import { constant,
|
|
1
|
+
import { constant, fillEmptyIndexes } from '../utils';
|
|
2
|
+
import { setParameterMetadata } from '../metadata';
|
|
2
3
|
const INJECT_KEY = 'INJECT_FN_LIST';
|
|
3
|
-
export const inject = (value) => (
|
|
4
|
-
var _a;
|
|
5
|
-
const metadata = (_a = Reflect.getOwnMetadata(INJECT_KEY, target)) !== null && _a !== void 0 ? _a : [];
|
|
6
|
-
metadata[parameterIndex] = value;
|
|
7
|
-
Reflect.defineMetadata(INJECT_KEY, metadata, target);
|
|
8
|
-
};
|
|
4
|
+
export const inject = (value) => setParameterMetadata(INJECT_KEY, value);
|
|
9
5
|
export class ReflectionInjector {
|
|
10
6
|
resolve(container, Target, ...deps) {
|
|
11
7
|
var _a;
|
|
12
8
|
const injectionFns = (_a = Reflect.getOwnMetadata(INJECT_KEY, Target)) !== null && _a !== void 0 ? _a : [];
|
|
13
|
-
const args =
|
|
9
|
+
const args = fillEmptyIndexes(injectionFns, deps.map(constant)).map((fn) => fn(container));
|
|
14
10
|
return new Target(...args);
|
|
15
11
|
}
|
|
16
12
|
}
|
package/esm/metadata.js
CHANGED
|
@@ -5,3 +5,9 @@ export const setMetadata = (key, value) => (target) => {
|
|
|
5
5
|
export function getMetadata(target, key) {
|
|
6
6
|
return Reflect.getOwnMetadata(key, target);
|
|
7
7
|
}
|
|
8
|
+
export const setParameterMetadata = (key, value) => (target, propertyKey, parameterIndex) => {
|
|
9
|
+
var _a;
|
|
10
|
+
const metadata = (_a = Reflect.getOwnMetadata(key, target)) !== null && _a !== void 0 ? _a : [];
|
|
11
|
+
metadata[parameterIndex] = value;
|
|
12
|
+
Reflect.defineMetadata(key, metadata, target);
|
|
13
|
+
};
|
package/esm/utils.js
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
export function pipe(...mappers) {
|
|
2
2
|
return (value) => mappers.reduce((acc, current) => current(acc), value);
|
|
3
3
|
}
|
|
4
|
-
export function
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
export function fillEmptyIndexes(baseArr, insertArr) {
|
|
5
|
+
const a = [...baseArr];
|
|
6
|
+
const b = [...insertArr];
|
|
7
|
+
for (let i = 0; i < a.length; i++) {
|
|
8
|
+
if (a[i] === undefined) {
|
|
9
|
+
a[i] = b.shift();
|
|
10
|
+
}
|
|
7
11
|
}
|
|
8
|
-
|
|
9
|
-
return baseArr;
|
|
10
|
-
}
|
|
11
|
-
const [b1, ...restBaseArr] = baseArr;
|
|
12
|
-
const [i1, ...restInsertArr] = insertArr;
|
|
13
|
-
return b1 === undefined
|
|
14
|
-
? [i1].concat(merge(restBaseArr, restInsertArr))
|
|
15
|
-
: [b1].concat(merge(restBaseArr, insertArr));
|
|
12
|
+
return a.concat(b);
|
|
16
13
|
}
|
|
17
14
|
export const constant = (value) => () => value;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-ioc-container",
|
|
3
|
-
"version": "27.4.
|
|
3
|
+
"version": "27.4.2",
|
|
4
4
|
"description": "Typescript IoC container",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"build:cjm": "rimraf cjm && tsc -p tsconfig.production.json --outDir cjm --module CommonJS",
|
|
42
42
|
"build:esm": "rimraf esm && tsc -p tsconfig.production.json --outDir esm",
|
|
43
43
|
"build:types": "rimraf typings && tsc -p tsconfig.production.json --outDir typings --emitDeclarationOnly --declaration",
|
|
44
|
-
"
|
|
45
|
-
"build": "npm run build:cjm && npm run build:esm && npm run build:types
|
|
44
|
+
"generate:docs": "ts-node scripts/generateReadme/generateReadme.ts && git add README.md",
|
|
45
|
+
"build": "npm run build:cjm && npm run build:esm && npm run build:types",
|
|
46
46
|
"coverage": "coveralls",
|
|
47
47
|
"watch": "nodemon --watch ./lib --exec npm run build",
|
|
48
48
|
"test": "jest --coverage"
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"ts-node": "^10.9.1",
|
|
61
61
|
"typescript": "4.4.3"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "f3cf3333af2a2182dcce507a564d30c9cbee2ace"
|
|
64
64
|
}
|
|
@@ -20,7 +20,7 @@ export declare class Container implements IContainer, Tagged {
|
|
|
20
20
|
dispose(): void;
|
|
21
21
|
getInstances(): unknown[];
|
|
22
22
|
hasTag(tag: Tag): boolean;
|
|
23
|
-
use(
|
|
23
|
+
use(...modules: IContainerModule[]): this;
|
|
24
24
|
hasDependency(key: DependencyKey): boolean;
|
|
25
25
|
/**
|
|
26
26
|
* @private
|
|
@@ -23,7 +23,7 @@ export interface IContainer extends Resolvable {
|
|
|
23
23
|
removeScope(child: IContainer): void;
|
|
24
24
|
getInstances(): unknown[];
|
|
25
25
|
dispose(): void;
|
|
26
|
-
use(
|
|
26
|
+
use(...modules: IContainerModule[]): this;
|
|
27
27
|
hasTag(tag: Tag): boolean;
|
|
28
28
|
getAllProviders(): Map<DependencyKey, IProvider>;
|
|
29
29
|
getTokensByProvider(predicate: (provider: IProvider) => boolean): DependencyKey[];
|
package/typings/index.d.ts
CHANGED
|
@@ -18,4 +18,4 @@ export { SimpleInjector } from './injector/SimpleInjector';
|
|
|
18
18
|
export { ProxyInjector } from './injector/ProxyInjector';
|
|
19
19
|
export { getHooks, hook } from './hook';
|
|
20
20
|
export { by, InstancePredicate } from './by';
|
|
21
|
-
export { setMetadata, getMetadata } from './metadata';
|
|
21
|
+
export { setMetadata, getMetadata, setParameterMetadata } from './metadata';
|
package/typings/metadata.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export declare const setMetadata: <T>(key: string | symbol, value: T) => ClassDecorator;
|
|
2
2
|
export declare function getMetadata<T>(target: Object, key: string | symbol): T | undefined;
|
|
3
|
+
export declare const setParameterMetadata: <T>(key: string | symbol, value: T) => ParameterDecorator;
|
package/typings/utils.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare type constructor<T> = new (...args: any[]) => T;
|
|
2
2
|
export declare type MapFn<T> = (value: T) => T;
|
|
3
3
|
export declare function pipe<T>(...mappers: MapFn<T>[]): MapFn<T>;
|
|
4
|
-
export declare function
|
|
4
|
+
export declare function fillEmptyIndexes<T>(baseArr: (T | undefined)[], insertArr: T[]): T[];
|
|
5
5
|
export declare const constant: <T>(value: T) => () => T;
|