ts-ioc-container 31.1.2 → 31.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +198 -160
- package/cjm/container/AutoMockedContainer.js +1 -1
- package/cjm/container/Container.js +6 -9
- package/cjm/container/EmptyContainer.js +1 -1
- package/cjm/hook.js +12 -6
- package/cjm/index.js +5 -1
- package/cjm/metadata.js +7 -1
- package/cjm/registration/IRegistration.js +1 -1
- package/cjm/registration/Registration.js +1 -1
- package/esm/container/AutoMockedContainer.js +1 -1
- package/esm/container/Container.js +6 -9
- package/esm/container/EmptyContainer.js +1 -1
- package/esm/hook.js +10 -5
- package/esm/index.js +2 -2
- package/esm/metadata.js +4 -0
- package/esm/registration/IRegistration.js +1 -1
- package/esm/registration/Registration.js +1 -1
- package/package.json +2 -2
- package/typings/container/AutoMockedContainer.d.ts +1 -1
- package/typings/container/Container.d.ts +2 -2
- package/typings/container/EmptyContainer.d.ts +1 -1
- package/typings/container/IContainer.d.ts +3 -3
- package/typings/hook.d.ts +4 -2
- package/typings/index.d.ts +2 -2
- package/typings/metadata.d.ts +2 -0
- package/typings/registration/IRegistration.d.ts +1 -1
- package/typings/registration/Registration.d.ts +1 -1
package/README.md
CHANGED
|
@@ -22,26 +22,26 @@
|
|
|
22
22
|
- [Setup](#setup)
|
|
23
23
|
- [Container](#container)
|
|
24
24
|
- [Basic usage](#basic-usage)
|
|
25
|
-
- [
|
|
25
|
+
- [Scope](#scope) `tags`
|
|
26
26
|
- [Instances](#instances)
|
|
27
|
-
- [
|
|
28
|
-
- [
|
|
29
|
-
- [Metadata
|
|
30
|
-
- [Simple
|
|
31
|
-
- [Proxy
|
|
32
|
-
- [
|
|
33
|
-
|
|
34
|
-
- [
|
|
35
|
-
- [
|
|
36
|
-
|
|
37
|
-
- [
|
|
38
|
-
- [
|
|
39
|
-
- [
|
|
40
|
-
- [
|
|
27
|
+
- [Dispose](#dispose)
|
|
28
|
+
- [Injector](#injector)
|
|
29
|
+
- [Metadata](#metadata) `@inject`
|
|
30
|
+
- [Simple](#simple)
|
|
31
|
+
- [Proxy](#proxy)
|
|
32
|
+
- [Visibility](#visibility) `visible`
|
|
33
|
+
- [Provider](#provider) `@provider`
|
|
34
|
+
- [Singleton](#singleton) `singleton`
|
|
35
|
+
- [Arguments](#arguments) `args`
|
|
36
|
+
- [Registration](#registration) `@registration`
|
|
37
|
+
- [Scope](#scope) `scope`
|
|
38
|
+
- [Alias](#alias) `alias`
|
|
39
|
+
- [Module](#module)
|
|
40
|
+
- [Hook](#hook) `@hook`
|
|
41
41
|
- [OnConstruct](#onconstruct) `@onConstruct`
|
|
42
42
|
- [OnDispose](#ondispose) `@onDispose`
|
|
43
|
-
- [
|
|
44
|
-
- [
|
|
43
|
+
- [Mock](#mock)
|
|
44
|
+
- [Error](#error)
|
|
45
45
|
|
|
46
46
|
## Setup
|
|
47
47
|
|
|
@@ -68,10 +68,11 @@ And `tsconfig.json` should have next options:
|
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
## Container
|
|
71
|
-
`IContainer` consists of
|
|
71
|
+
`IContainer` consists of:
|
|
72
72
|
|
|
73
|
-
-
|
|
74
|
-
- Injector
|
|
73
|
+
- Provider is dependency factory which creates dependency
|
|
74
|
+
- Injector describes how to inject dependencies to constructor
|
|
75
|
+
- Registration is provider factory which registers provider in container
|
|
75
76
|
|
|
76
77
|
### Basic usage
|
|
77
78
|
|
|
@@ -89,7 +90,7 @@ describe('Basic usage', function () {
|
|
|
89
90
|
constructor(@inject(by.key('ILogger')) public logger: Logger) {}
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
const container = new Container(new MetadataInjector()).
|
|
93
|
+
const container = new Container(new MetadataInjector()).add(R.fromClass(Logger).to('ILogger'));
|
|
93
94
|
|
|
94
95
|
expect(container.resolve(App).logger.name).toBe('Logger');
|
|
95
96
|
});
|
|
@@ -100,8 +101,8 @@ describe('Basic usage', function () {
|
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
const container = new Container(new MetadataInjector())
|
|
103
|
-
.
|
|
104
|
-
.
|
|
104
|
+
.add(R.fromClass(Logger).to('ILogger1'))
|
|
105
|
+
.add(R.fromClass(Logger).to('ILogger2'));
|
|
105
106
|
|
|
106
107
|
expect(container.resolve(App).loggers).toHaveLength(2);
|
|
107
108
|
});
|
|
@@ -121,7 +122,7 @@ describe('Basic usage', function () {
|
|
|
121
122
|
|
|
122
123
|
```
|
|
123
124
|
|
|
124
|
-
###
|
|
125
|
+
### Scope
|
|
125
126
|
Sometimes you need to create a scope of container. For example, when you want to create a scope per request in web application. You can assign tags to scope and provider and resolve dependencies only from certain scope.
|
|
126
127
|
|
|
127
128
|
- NOTICE: remember that when scope doesn't have dependency then it will be resolved from parent container
|
|
@@ -151,7 +152,7 @@ class Logger {}
|
|
|
151
152
|
|
|
152
153
|
describe('Scopes', function () {
|
|
153
154
|
it('should resolve dependencies from scope', function () {
|
|
154
|
-
const root = new Container(new MetadataInjector(), { tags: ['root'] }).
|
|
155
|
+
const root = new Container(new MetadataInjector(), { tags: ['root'] }).add(R.fromClass(Logger));
|
|
155
156
|
const child = root.createScope('child');
|
|
156
157
|
|
|
157
158
|
expect(child.resolve('ILogger')).toBe(child.resolve('ILogger'));
|
|
@@ -188,7 +189,7 @@ describe('Instances', function () {
|
|
|
188
189
|
class Logger {}
|
|
189
190
|
|
|
190
191
|
it('should return injected instances', () => {
|
|
191
|
-
const container = new Container(new MetadataInjector()).
|
|
192
|
+
const container = new Container(new MetadataInjector()).add(R.fromClass(Logger));
|
|
192
193
|
const scope = container.createScope();
|
|
193
194
|
|
|
194
195
|
const logger1 = container.resolve('ILogger');
|
|
@@ -205,7 +206,7 @@ describe('Instances', function () {
|
|
|
205
206
|
constructor(@inject(by.instances(isLogger)) public loggers: Logger[]) {}
|
|
206
207
|
}
|
|
207
208
|
|
|
208
|
-
const container = new Container(new MetadataInjector()).
|
|
209
|
+
const container = new Container(new MetadataInjector()).add(R.fromClass(Logger));
|
|
209
210
|
|
|
210
211
|
const logger0 = container.resolve('ILogger');
|
|
211
212
|
const logger1 = container.resolve('ILogger');
|
|
@@ -219,7 +220,7 @@ describe('Instances', function () {
|
|
|
219
220
|
|
|
220
221
|
```
|
|
221
222
|
|
|
222
|
-
###
|
|
223
|
+
### Dispose
|
|
223
224
|
Sometimes you want to dispose container and all its scopes. For example, when you want to prevent memory leaks. Or you want to ensure that nobody can use container after it was disposed.
|
|
224
225
|
|
|
225
226
|
- container can be disposed
|
|
@@ -234,9 +235,7 @@ class Logger {}
|
|
|
234
235
|
|
|
235
236
|
describe('Disposing', function () {
|
|
236
237
|
it('should container and make it unavailable for the further usage', function () {
|
|
237
|
-
const root = new Container(new MetadataInjector(), { tags: ['root'] }).
|
|
238
|
-
R.fromClass(Logger).to('ILogger'),
|
|
239
|
-
);
|
|
238
|
+
const root = new Container(new MetadataInjector(), { tags: ['root'] }).add(R.fromClass(Logger).to('ILogger'));
|
|
240
239
|
const child = root.createScope('child');
|
|
241
240
|
|
|
242
241
|
const logger = child.resolve('ILogger');
|
|
@@ -250,14 +249,14 @@ describe('Disposing', function () {
|
|
|
250
249
|
|
|
251
250
|
```
|
|
252
251
|
|
|
253
|
-
##
|
|
252
|
+
## Injector
|
|
254
253
|
`IInjector` is used to describe how dependencies should be injected to constructor.
|
|
255
254
|
|
|
256
255
|
- `MetadataInjector` - injects dependencies using `@inject` decorator
|
|
257
256
|
- `ProxyInjector` - injects dependencies as dictionary `Record<string, unknown>`
|
|
258
257
|
- `SimpleInjector` - just passes container to constructor with others arguments
|
|
259
258
|
|
|
260
|
-
### Metadata
|
|
259
|
+
### Metadata
|
|
261
260
|
This type of injector uses `@inject` decorator to mark where dependencies should be injected. It's bases on `reflect-metadata` package. That's why I call it `MetadataInjector`.
|
|
262
261
|
|
|
263
262
|
```typescript
|
|
@@ -282,7 +281,7 @@ class App {
|
|
|
282
281
|
|
|
283
282
|
describe('Reflection Injector', function () {
|
|
284
283
|
it('should inject dependencies by @inject decorator', function () {
|
|
285
|
-
const container = new Container(new MetadataInjector()).
|
|
284
|
+
const container = new Container(new MetadataInjector()).add(R.fromClass(Logger).to('ILogger'));
|
|
286
285
|
|
|
287
286
|
const app = container.resolve(App);
|
|
288
287
|
|
|
@@ -292,7 +291,7 @@ describe('Reflection Injector', function () {
|
|
|
292
291
|
|
|
293
292
|
```
|
|
294
293
|
|
|
295
|
-
### Simple
|
|
294
|
+
### Simple
|
|
296
295
|
This type of injector just passes container to constructor with others arguments.
|
|
297
296
|
|
|
298
297
|
```typescript
|
|
@@ -305,7 +304,7 @@ describe('SimpleInjector', function () {
|
|
|
305
304
|
constructor(public container: IContainer) {}
|
|
306
305
|
}
|
|
307
306
|
|
|
308
|
-
const container = new Container(new SimpleInjector()).
|
|
307
|
+
const container = new Container(new SimpleInjector()).add(R.fromClass(App).to('App'));
|
|
309
308
|
const app = container.resolve<App>('App');
|
|
310
309
|
|
|
311
310
|
expect(app.container).toBeInstanceOf(Container);
|
|
@@ -319,7 +318,7 @@ describe('SimpleInjector', function () {
|
|
|
319
318
|
) {}
|
|
320
319
|
}
|
|
321
320
|
|
|
322
|
-
const container = new Container(new SimpleInjector()).
|
|
321
|
+
const container = new Container(new SimpleInjector()).add(R.fromClass(App).to('App'));
|
|
323
322
|
const app = container.resolve<App>('App', { args: ['Hello world'] });
|
|
324
323
|
|
|
325
324
|
expect(app.greeting).toBe('Hello world');
|
|
@@ -328,7 +327,7 @@ describe('SimpleInjector', function () {
|
|
|
328
327
|
|
|
329
328
|
```
|
|
330
329
|
|
|
331
|
-
### Proxy
|
|
330
|
+
### Proxy
|
|
332
331
|
This type of injector injects dependencies as dictionary `Record<string, unknown>`.
|
|
333
332
|
|
|
334
333
|
```typescript
|
|
@@ -347,7 +346,7 @@ describe('ProxyInjector', function () {
|
|
|
347
346
|
}
|
|
348
347
|
}
|
|
349
348
|
|
|
350
|
-
const container = new Container(new ProxyInjector()).
|
|
349
|
+
const container = new Container(new ProxyInjector()).add(R.fromClass(Logger).to('logger'));
|
|
351
350
|
|
|
352
351
|
const app = container.resolve(App);
|
|
353
352
|
expect(app.logger).toBeInstanceOf(Logger);
|
|
@@ -377,8 +376,8 @@ describe('ProxyInjector', function () {
|
|
|
377
376
|
const greetingTemplate = (name: string) => `Hello ${name}`;
|
|
378
377
|
|
|
379
378
|
const container = new Container(new ProxyInjector())
|
|
380
|
-
.
|
|
381
|
-
.
|
|
379
|
+
.add(R.fromClass(App).to('App').pipe(args({ greetingTemplate })))
|
|
380
|
+
.add(R.fromClass(Logger).to('logger'));
|
|
382
381
|
|
|
383
382
|
const app = container.resolve<App>('App', { args: [{ name: `world` }] });
|
|
384
383
|
expect(app.greeting).toBe('Hello world');
|
|
@@ -387,71 +386,13 @@ describe('ProxyInjector', function () {
|
|
|
387
386
|
|
|
388
387
|
```
|
|
389
388
|
|
|
390
|
-
##
|
|
391
|
-
|
|
392
|
-
- `resolve` - creates instance with passed arguments
|
|
393
|
-
- `clone` - we invoke it when we create a scope. It clones provider to new scope.
|
|
394
|
-
- `isValid` - checks if provider can be resolved from container or cloned to container with certain tags
|
|
395
|
-
|
|
396
|
-
There are next types of providers:
|
|
397
|
-
- `Provider` - basic provider. It can be used with `@provider` decorator
|
|
398
|
-
- `SingletonProvider` - provider that creates only one instance in every scope where it's resolved
|
|
399
|
-
- `ScopeProvider` - provider that can be resolved only from container with certain tags and their sub scopes
|
|
400
|
-
- `ArgsProvider` - provider that encapsulates arguments to pass it to constructor.
|
|
401
|
-
|
|
402
|
-
`Registration` - just a helper to register provider with certain key. `(preferrably to use)`
|
|
389
|
+
## Provider
|
|
390
|
+
Provider is dependency factory which creates dependency.
|
|
403
391
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
import 'reflect-metadata';
|
|
409
|
-
import { Container, key, MetadataInjector, provider, register, Registration as R, scope, singleton } from 'ts-ioc-container';
|
|
410
|
-
import { DependencyMissingKeyError } from '../../lib/errors/DependencyMissingKeyError';
|
|
411
|
-
|
|
412
|
-
describe('Registration module', function () {
|
|
413
|
-
const createContainer = () => new Container(new MetadataInjector(), { tags: ['root'] });
|
|
414
|
-
|
|
415
|
-
it('should register class', function () {
|
|
416
|
-
@register(key('ILogger'), scope((s) => s.hasTag('root')))
|
|
417
|
-
@provider(singleton())
|
|
418
|
-
class Logger {}
|
|
419
|
-
|
|
420
|
-
const root = createContainer().addRegistration(R.fromClass(Logger));
|
|
421
|
-
|
|
422
|
-
expect(root.resolve('ILogger')).toBeInstanceOf(Logger);
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
it('should register value', function () {
|
|
426
|
-
const root = createContainer().addRegistration(R.fromValue('smth').to('ISmth'));
|
|
427
|
-
|
|
428
|
-
expect(root.resolve('ISmth')).toBe('smth');
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
it('should register fn', function () {
|
|
432
|
-
const root = createContainer().addRegistration(R.fromFn(() => 'smth').to('ISmth'));
|
|
433
|
-
|
|
434
|
-
expect(root.resolve('ISmth')).toBe('smth');
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
it('should raise an error if key is not provider', () => {
|
|
438
|
-
expect(() => {
|
|
439
|
-
createContainer().addRegistration(R.fromValue('smth'));
|
|
440
|
-
}).toThrowError(DependencyMissingKeyError);
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
it('should register dependency by class name if @key is not provided', function () {
|
|
444
|
-
class FileLogger {}
|
|
445
|
-
|
|
446
|
-
const root = createContainer().addRegistration(R.fromClass(FileLogger));
|
|
447
|
-
|
|
448
|
-
expect(root.resolve('FileLogger')).toBeInstanceOf(FileLogger);
|
|
449
|
-
});
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
### Provider
|
|
392
|
+
- `@provider()`
|
|
393
|
+
- `Provider.fromClass(Logger)`
|
|
394
|
+
- `Provider.fromValue(logger)`
|
|
395
|
+
- `new Provider((container, ...args) => container.resolve(Logger, {args}))`
|
|
455
396
|
|
|
456
397
|
```typescript
|
|
457
398
|
import 'reflect-metadata';
|
|
@@ -490,7 +431,7 @@ describe('Provider', function () {
|
|
|
490
431
|
|
|
491
432
|
```
|
|
492
433
|
|
|
493
|
-
### Singleton
|
|
434
|
+
### Singleton
|
|
494
435
|
Sometimes you need to create only one instance of dependency per scope. For example, you want to create only one logger per scope.
|
|
495
436
|
|
|
496
437
|
- Singleton provider creates only one instance in every scope where it's resolved.
|
|
@@ -510,20 +451,20 @@ describe('Singleton', function () {
|
|
|
510
451
|
}
|
|
511
452
|
|
|
512
453
|
it('should resolve the same container per every request', function () {
|
|
513
|
-
const container = createContainer().
|
|
454
|
+
const container = createContainer().add(R.fromClass(Logger));
|
|
514
455
|
|
|
515
456
|
expect(container.resolve('logger')).toBe(container.resolve('logger'));
|
|
516
457
|
});
|
|
517
458
|
|
|
518
459
|
it('should resolve different dependency per scope', function () {
|
|
519
|
-
const container = createContainer().
|
|
460
|
+
const container = createContainer().add(R.fromClass(Logger));
|
|
520
461
|
const child = container.createScope();
|
|
521
462
|
|
|
522
463
|
expect(container.resolve('logger')).not.toBe(child.resolve('logger'));
|
|
523
464
|
});
|
|
524
465
|
|
|
525
466
|
it('should resolve the same dependency for scope', function () {
|
|
526
|
-
const container = createContainer().
|
|
467
|
+
const container = createContainer().add(R.fromClass(Logger));
|
|
527
468
|
const child = container.createScope();
|
|
528
469
|
|
|
529
470
|
expect(child.resolve('logger')).toBe(child.resolve('logger'));
|
|
@@ -532,29 +473,11 @@ describe('Singleton', function () {
|
|
|
532
473
|
|
|
533
474
|
```
|
|
534
475
|
|
|
535
|
-
###
|
|
536
|
-
Sometimes you need to resolve provider only from scope which matches to certain condition and their sub scopes. Especially if you want to register dependency as singleton for some tags, for example `root`
|
|
537
|
-
- NOTICE: It doesn't make clones in not predicate-matched scopes. Usually it's used with `SingletonProvider`.
|
|
538
|
-
|
|
539
|
-
```typescript
|
|
540
|
-
import 'reflect-metadata';
|
|
541
|
-
import { singleton, Container, key, provider, MetadataInjector, Registration as R, scope, register } from 'ts-ioc-container';
|
|
542
|
-
|
|
543
|
-
@register(key('ILogger'), scope((s) => s.hasTag('root')))
|
|
544
|
-
@provider(singleton()) // the same as .pipe(singleton(), scope((s) => s.hasTag('root')))
|
|
545
|
-
class Logger {}
|
|
546
|
-
describe('ScopeProvider', function () {
|
|
547
|
-
it('should return the same instance', function () {
|
|
548
|
-
const root = new Container(new MetadataInjector(), { tags: ['root'] }).addRegistration(R.fromClass(Logger));
|
|
549
|
-
const child = root.createScope();
|
|
550
|
-
expect(root.resolve('ILogger')).toBe(child.resolve('ILogger'));
|
|
551
|
-
});
|
|
552
|
-
});
|
|
553
|
-
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
### Args provider
|
|
476
|
+
### Arguments
|
|
557
477
|
Sometimes you want to bind some arguments to provider. This is what `ArgsProvider` is for.
|
|
478
|
+
- `@provider(args('someArgument'))`
|
|
479
|
+
- `@provider(argsFn((container) => [container.resolve(Logger), 'someValue']))`
|
|
480
|
+
- `Provider.fromClass(Logger).pipe(args('someArgument'))`
|
|
558
481
|
- NOTICE: args from this provider has higher priority than args from `resolve` method.
|
|
559
482
|
|
|
560
483
|
```typescript
|
|
@@ -575,21 +498,21 @@ describe('ArgsProvider', function () {
|
|
|
575
498
|
}
|
|
576
499
|
|
|
577
500
|
it('can assign argument function to provider', function () {
|
|
578
|
-
const root = createContainer().
|
|
501
|
+
const root = createContainer().add(R.fromClass(Logger).pipe(argsFn((container, ...args) => ['name'])));
|
|
579
502
|
|
|
580
503
|
const logger = root.createScope().resolve<Logger>('logger');
|
|
581
504
|
expect(logger.name).toBe('name');
|
|
582
505
|
});
|
|
583
506
|
|
|
584
507
|
it('can assign argument to provider', function () {
|
|
585
|
-
const root = createContainer().
|
|
508
|
+
const root = createContainer().add(R.fromClass(Logger).pipe(args('name')));
|
|
586
509
|
|
|
587
510
|
const logger = root.resolve<Logger>('logger');
|
|
588
511
|
expect(logger.name).toBe('name');
|
|
589
512
|
});
|
|
590
513
|
|
|
591
514
|
it('should set provider arguments with highest priority in compare to resolve arguments', function () {
|
|
592
|
-
const root = createContainer().
|
|
515
|
+
const root = createContainer().add(R.fromClass(Logger).pipe(args('name')));
|
|
593
516
|
|
|
594
517
|
const logger = root.resolve<Logger>('logger', { args: ['file'] });
|
|
595
518
|
|
|
@@ -600,11 +523,125 @@ describe('ArgsProvider', function () {
|
|
|
600
523
|
|
|
601
524
|
```
|
|
602
525
|
|
|
603
|
-
###
|
|
604
|
-
Sometimes you want to
|
|
605
|
-
- `@provider(
|
|
606
|
-
- `
|
|
607
|
-
|
|
526
|
+
### Visibility
|
|
527
|
+
Sometimes you want to hide dependency if somebody wants to resolve it from certain scope
|
|
528
|
+
- `@provider(visible(({ isParent, child }) => isParent || child.hasTag('root')))` - dependency will be accessible from scope `root` or from scope where it's registered
|
|
529
|
+
- `Provider.fromClass(Logger).pipe(visible(({ isParent, child }) => isParent || child.hasTag('root')))`
|
|
530
|
+
|
|
531
|
+
```typescript
|
|
532
|
+
import 'reflect-metadata';
|
|
533
|
+
import {
|
|
534
|
+
Container,
|
|
535
|
+
DependencyNotFoundError,
|
|
536
|
+
key,
|
|
537
|
+
MetadataInjector,
|
|
538
|
+
provider,
|
|
539
|
+
register,
|
|
540
|
+
Registration as R,
|
|
541
|
+
scope,
|
|
542
|
+
singleton,
|
|
543
|
+
visible,
|
|
544
|
+
} from 'ts-ioc-container';
|
|
545
|
+
|
|
546
|
+
describe('Visibility', function () {
|
|
547
|
+
it('should hide from children', () => {
|
|
548
|
+
@register(key('logger'), scope((s) => s.hasTag('root')))
|
|
549
|
+
@provider(singleton(), visible(({ isParent }) => isParent))
|
|
550
|
+
class FileLogger {}
|
|
551
|
+
|
|
552
|
+
const parent = new Container(new MetadataInjector(), { tags: ['root'] }).add(R.fromClass(FileLogger));
|
|
553
|
+
|
|
554
|
+
const child = parent.createScope('child');
|
|
555
|
+
|
|
556
|
+
expect(() => child.resolve('logger')).toThrowError(DependencyNotFoundError);
|
|
557
|
+
expect(parent.resolve('logger')).toBeInstanceOf(FileLogger);
|
|
558
|
+
});
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
## Registration
|
|
564
|
+
Registration is provider factory which registers provider in container.
|
|
565
|
+
- `@registration(key('logger'))`
|
|
566
|
+
- `Registration.fromClass(Logger).to('logger')`
|
|
567
|
+
- `Registration.fromClass(Logger)`
|
|
568
|
+
- `Registration.fromValue(Logger)`
|
|
569
|
+
- `Registration.fromFn((container, ...args) => container.resolve(Logger, {args}))`
|
|
570
|
+
|
|
571
|
+
```typescript
|
|
572
|
+
import 'reflect-metadata';
|
|
573
|
+
import { Container, key, MetadataInjector, provider, register, Registration as R, scope, singleton } from 'ts-ioc-container';
|
|
574
|
+
import { DependencyMissingKeyError } from '../../lib/errors/DependencyMissingKeyError';
|
|
575
|
+
|
|
576
|
+
describe('Registration module', function () {
|
|
577
|
+
const createContainer = () => new Container(new MetadataInjector(), { tags: ['root'] });
|
|
578
|
+
|
|
579
|
+
it('should register class', function () {
|
|
580
|
+
@register(key('ILogger'), scope((s) => s.hasTag('root')))
|
|
581
|
+
@provider(singleton())
|
|
582
|
+
class Logger {}
|
|
583
|
+
|
|
584
|
+
const root = createContainer().add(R.fromClass(Logger));
|
|
585
|
+
|
|
586
|
+
expect(root.resolve('ILogger')).toBeInstanceOf(Logger);
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
it('should register value', function () {
|
|
590
|
+
const root = createContainer().add(R.fromValue('smth').to('ISmth'));
|
|
591
|
+
|
|
592
|
+
expect(root.resolve('ISmth')).toBe('smth');
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
it('should register fn', function () {
|
|
596
|
+
const root = createContainer().add(R.fromFn(() => 'smth').to('ISmth'));
|
|
597
|
+
|
|
598
|
+
expect(root.resolve('ISmth')).toBe('smth');
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
it('should raise an error if key is not provider', () => {
|
|
602
|
+
expect(() => {
|
|
603
|
+
createContainer().add(R.fromValue('smth'));
|
|
604
|
+
}).toThrowError(DependencyMissingKeyError);
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
it('should register dependency by class name if @key is not provided', function () {
|
|
608
|
+
class FileLogger {}
|
|
609
|
+
|
|
610
|
+
const root = createContainer().add(R.fromClass(FileLogger));
|
|
611
|
+
|
|
612
|
+
expect(root.resolve('FileLogger')).toBeInstanceOf(FileLogger);
|
|
613
|
+
});
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
### Scope
|
|
619
|
+
Sometimes you need to register provider only in scope which matches to certain condition and their sub scopes. Especially if you want to register dependency as singleton for some tags, for example `root`
|
|
620
|
+
- `@registration(scope((container) => container.hasTag('root'))` - register provider only in root scope
|
|
621
|
+
- `Registration.fromClass(Logger).when((container) => container.hasTag('root'))`
|
|
622
|
+
|
|
623
|
+
```typescript
|
|
624
|
+
import 'reflect-metadata';
|
|
625
|
+
import { singleton, Container, key, provider, MetadataInjector, Registration as R, scope, register } from 'ts-ioc-container';
|
|
626
|
+
|
|
627
|
+
@register(key('ILogger'), scope((s) => s.hasTag('root')))
|
|
628
|
+
@provider(singleton()) // the same as .pipe(singleton(), scope((s) => s.hasTag('root')))
|
|
629
|
+
class Logger {}
|
|
630
|
+
describe('ScopeProvider', function () {
|
|
631
|
+
it('should return the same instance', function () {
|
|
632
|
+
const root = new Container(new MetadataInjector(), { tags: ['root'] }).add(R.fromClass(Logger));
|
|
633
|
+
const child = root.createScope();
|
|
634
|
+
expect(root.resolve('ILogger')).toBe(child.resolve('ILogger'));
|
|
635
|
+
});
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### Alias
|
|
641
|
+
Alias is needed to group keys
|
|
642
|
+
- `@registration(alias('logger'))` helper assigns `logger` alias to registration.
|
|
643
|
+
- `by.aliases((it) => it.has('logger') || it.has('a'))` resolves dependencies which have `logger` or `a` aliases
|
|
644
|
+
- `Registration.fromClass(Logger).addAliases('logger')`
|
|
608
645
|
|
|
609
646
|
```typescript
|
|
610
647
|
import 'reflect-metadata';
|
|
@@ -649,7 +686,7 @@ describe('alias', () => {
|
|
|
649
686
|
it('should resolve by some alias', () => {
|
|
650
687
|
class App implements IApplication {
|
|
651
688
|
private appliedMiddleware: Set<string> = new Set();
|
|
652
|
-
constructor(@inject(by.aliases((it) => it.
|
|
689
|
+
constructor(@inject(by.aliases((it) => it.has(IMiddlewareKey))) public middleware: IMiddleware[]) {}
|
|
653
690
|
|
|
654
691
|
markMiddlewareAsApplied(name: string): void {
|
|
655
692
|
this.appliedMiddleware.add(name);
|
|
@@ -671,8 +708,8 @@ describe('alias', () => {
|
|
|
671
708
|
}
|
|
672
709
|
|
|
673
710
|
const container = new Container(new MetadataInjector())
|
|
674
|
-
.
|
|
675
|
-
.
|
|
711
|
+
.add(R.fromClass(LoggerMiddleware))
|
|
712
|
+
.add(R.fromClass(ErrorHandlerMiddleware));
|
|
676
713
|
|
|
677
714
|
const app = container.resolve(App);
|
|
678
715
|
app.run();
|
|
@@ -685,16 +722,17 @@ describe('alias', () => {
|
|
|
685
722
|
@register(alias('ILogger'))
|
|
686
723
|
class FileLogger {}
|
|
687
724
|
|
|
688
|
-
const container = new Container(new MetadataInjector()).
|
|
725
|
+
const container = new Container(new MetadataInjector()).add(R.fromClass(FileLogger));
|
|
689
726
|
|
|
690
|
-
expect(by.alias((aliases) => aliases.
|
|
691
|
-
expect(() => by.alias((aliases) => aliases.
|
|
727
|
+
expect(by.alias((aliases) => aliases.has('ILogger'))(container)).toBeInstanceOf(FileLogger);
|
|
728
|
+
expect(() => by.alias((aliases) => aliases.has('logger'))(container)).toThrowError(DependencyNotFoundError);
|
|
692
729
|
});
|
|
693
730
|
});
|
|
694
731
|
|
|
695
732
|
```
|
|
696
733
|
|
|
697
|
-
|
|
734
|
+
|
|
735
|
+
## Module
|
|
698
736
|
Sometimes you want to encapsulate registration logic in separate module. This is what `IContainerModule` is for.
|
|
699
737
|
|
|
700
738
|
```typescript
|
|
@@ -709,13 +747,13 @@ class TestLogger {}
|
|
|
709
747
|
|
|
710
748
|
class Production implements IContainerModule {
|
|
711
749
|
applyTo(container: IContainer): void {
|
|
712
|
-
container.
|
|
750
|
+
container.add(R.fromClass(Logger));
|
|
713
751
|
}
|
|
714
752
|
}
|
|
715
753
|
|
|
716
754
|
class Development implements IContainerModule {
|
|
717
755
|
applyTo(container: IContainer): void {
|
|
718
|
-
container.
|
|
756
|
+
container.add(R.fromClass(TestLogger));
|
|
719
757
|
}
|
|
720
758
|
}
|
|
721
759
|
|
|
@@ -739,7 +777,7 @@ describe('Container Modules', function () {
|
|
|
739
777
|
|
|
740
778
|
```
|
|
741
779
|
|
|
742
|
-
##
|
|
780
|
+
## Hook
|
|
743
781
|
Sometimes you need to invoke methods after construct or dispose of class. This is what hooks are for.
|
|
744
782
|
|
|
745
783
|
### OnConstruct
|
|
@@ -764,7 +802,7 @@ class MyInjector implements IInjector {
|
|
|
764
802
|
resolve<T>(container: IContainer, value: constructor<T>, ...deps: unknown[]): T {
|
|
765
803
|
const instance = this.injector.resolve(container, value, ...deps);
|
|
766
804
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
767
|
-
for (const h of getHooks(instance as object, 'onConstruct')) {
|
|
805
|
+
for (const [h] of getHooks(instance as object, 'onConstruct')) {
|
|
768
806
|
// @ts-ignore
|
|
769
807
|
instance[h]();
|
|
770
808
|
}
|
|
@@ -788,7 +826,7 @@ class Logger {
|
|
|
788
826
|
|
|
789
827
|
describe('onConstruct', function () {
|
|
790
828
|
it('should make logger be ready on resolve', function () {
|
|
791
|
-
const container = new Container(new MyInjector()).
|
|
829
|
+
const container = new Container(new MyInjector()).add(R.fromClass(Logger));
|
|
792
830
|
|
|
793
831
|
const logger = container.resolve<Logger>('logger');
|
|
794
832
|
|
|
@@ -814,6 +852,7 @@ import {
|
|
|
814
852
|
MetadataInjector,
|
|
815
853
|
register,
|
|
816
854
|
} from 'ts-ioc-container';
|
|
855
|
+
import * as console from 'node:console';
|
|
817
856
|
|
|
818
857
|
@register(key('logsRepo'))
|
|
819
858
|
@provider(singleton())
|
|
@@ -844,16 +883,14 @@ class Logger {
|
|
|
844
883
|
|
|
845
884
|
describe('onDispose', function () {
|
|
846
885
|
it('should invoke hooks on all instances', async function () {
|
|
847
|
-
const container = new Container(new MetadataInjector())
|
|
848
|
-
.addRegistration(R.fromClass(Logger))
|
|
849
|
-
.addRegistration(R.fromClass(LogsRepo));
|
|
886
|
+
const container = new Container(new MetadataInjector()).add(R.fromClass(Logger)).add(R.fromClass(LogsRepo));
|
|
850
887
|
|
|
851
888
|
const logger = container.resolve<Logger>('logger');
|
|
852
889
|
logger.log('Hello');
|
|
853
890
|
|
|
854
891
|
for (const instance of container.getInstances()) {
|
|
855
892
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
856
|
-
for (const h of getHooks(instance as object, 'onDispose')) {
|
|
893
|
+
for (const [h] of getHooks(instance as object, 'onDispose')) {
|
|
857
894
|
// @ts-ignore
|
|
858
895
|
await instance[h]();
|
|
859
896
|
}
|
|
@@ -865,7 +902,7 @@ describe('onDispose', function () {
|
|
|
865
902
|
|
|
866
903
|
```
|
|
867
904
|
|
|
868
|
-
##
|
|
905
|
+
## Mock
|
|
869
906
|
Sometimes you need to automatically mock all dependencies in container. This is what `AutoMockedContainer` is for.
|
|
870
907
|
|
|
871
908
|
```typescript
|
|
@@ -907,9 +944,10 @@ describe('Mocking', () => {
|
|
|
907
944
|
|
|
908
945
|
```
|
|
909
946
|
|
|
910
|
-
##
|
|
947
|
+
## Error
|
|
911
948
|
|
|
912
|
-
- [DependencyNotFoundError.ts](
|
|
913
|
-
- [MethodNotImplementedError.ts](
|
|
914
|
-
- [
|
|
949
|
+
- [DependencyNotFoundError.ts](..%2F..%2Flib%2Ferrors%2FDependencyNotFoundError.ts)
|
|
950
|
+
- [MethodNotImplementedError.ts](..%2F..%2Flib%2Ferrors%2FMethodNotImplementedError.ts)
|
|
951
|
+
- [DependencyMissingKeyError.ts](..%2F..%2Flib%2Ferrors%2FDependencyMissingKeyError.ts)
|
|
952
|
+
- [ContainerDisposedError.ts](..%2F..%2Flib%2Ferrors%2FContainerDisposedError.ts)
|
|
915
953
|
|
|
@@ -16,7 +16,7 @@ class Container {
|
|
|
16
16
|
this.parent = options.parent ?? new EmptyContainer_1.EmptyContainer();
|
|
17
17
|
this.tags = new Set(options.tags ?? []);
|
|
18
18
|
}
|
|
19
|
-
|
|
19
|
+
add(registration) {
|
|
20
20
|
this.registrations.push(registration);
|
|
21
21
|
registration.applyTo(this);
|
|
22
22
|
return this;
|
|
@@ -25,7 +25,7 @@ class Container {
|
|
|
25
25
|
this.validateContainer();
|
|
26
26
|
this.providers.set(key, provider);
|
|
27
27
|
if (aliases && aliases.length > 0) {
|
|
28
|
-
this.aliases.set(key, aliases);
|
|
28
|
+
this.aliases.set(key, new Set(aliases));
|
|
29
29
|
}
|
|
30
30
|
return this;
|
|
31
31
|
}
|
|
@@ -70,10 +70,8 @@ class Container {
|
|
|
70
70
|
hasTag(tag) {
|
|
71
71
|
return this.tags.has(tag);
|
|
72
72
|
}
|
|
73
|
-
use(
|
|
74
|
-
|
|
75
|
-
module.applyTo(this);
|
|
76
|
-
}
|
|
73
|
+
use(module) {
|
|
74
|
+
module.applyTo(this);
|
|
77
75
|
return this;
|
|
78
76
|
}
|
|
79
77
|
hasDependency(key) {
|
|
@@ -108,9 +106,8 @@ class Container {
|
|
|
108
106
|
* @private
|
|
109
107
|
*/
|
|
110
108
|
getRegistrations() {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
: this.parent.getRegistrations();
|
|
109
|
+
const registrations = this.parent.getRegistrations();
|
|
110
|
+
return this.registrations.length > 0 ? registrations.concat(this.registrations) : registrations;
|
|
114
111
|
}
|
|
115
112
|
/**
|
|
116
113
|
* @private
|
package/cjm/hook.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getHooks = exports.hook = void 0;
|
|
4
|
-
const hook = (key) => (target, propertyKey) => {
|
|
5
|
-
const hooks = Reflect.hasMetadata(key, target.constructor)
|
|
6
|
-
|
|
3
|
+
exports.hasHooks = exports.getHooks = exports.hook = void 0;
|
|
4
|
+
const hook = (key, fn = () => []) => (target, propertyKey) => {
|
|
5
|
+
const hooks = Reflect.hasMetadata(key, target.constructor)
|
|
6
|
+
? Reflect.getMetadata(key, target.constructor)
|
|
7
|
+
: new Map();
|
|
8
|
+
hooks.set(propertyKey, fn);
|
|
9
|
+
Reflect.defineMetadata(key, hooks, target.constructor); // eslint-disable-line @typescript-eslint/ban-types
|
|
7
10
|
};
|
|
8
11
|
exports.hook = hook;
|
|
9
|
-
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
10
12
|
function getHooks(target, key) {
|
|
11
|
-
return Reflect.hasMetadata(key, target.constructor) ? Reflect.getMetadata(key, target.constructor) :
|
|
13
|
+
return Reflect.hasMetadata(key, target.constructor) ? Reflect.getMetadata(key, target.constructor) : new Map();
|
|
12
14
|
}
|
|
13
15
|
exports.getHooks = getHooks;
|
|
16
|
+
function hasHooks(target, key) {
|
|
17
|
+
return Reflect.hasMetadata(key, target.constructor);
|
|
18
|
+
}
|
|
19
|
+
exports.hasHooks = hasHooks;
|
package/cjm/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.setParameterMetadata = exports.getMetadata = exports.setMetadata = exports.by = exports.hook = exports.getHooks = exports.ProxyInjector = exports.SimpleInjector = exports.inject = exports.MetadataInjector = exports.register = exports.scope = exports.alias = exports.key = exports.Registration = exports.AutoMockedContainer = exports.SingletonProvider = exports.singleton = exports.ArgsProvider = exports.args = exports.argsFn = exports.ProviderDecorator = exports.Provider = exports.ContainerDisposedError = exports.MethodNotImplementedError = exports.DependencyNotFoundError = exports.visible = exports.provider = exports.EmptyContainer = exports.Container = exports.isDependencyKey = void 0;
|
|
3
|
+
exports.getParameterMetadata = exports.getMethodMetadata = exports.setMethodMetadata = exports.setParameterMetadata = exports.getMetadata = exports.setMetadata = exports.by = exports.hasHooks = exports.hook = exports.getHooks = exports.ProxyInjector = exports.SimpleInjector = exports.inject = exports.MetadataInjector = exports.register = exports.scope = exports.alias = exports.key = exports.Registration = exports.AutoMockedContainer = exports.SingletonProvider = exports.singleton = exports.ArgsProvider = exports.args = exports.argsFn = exports.ProviderDecorator = exports.Provider = exports.ContainerDisposedError = exports.MethodNotImplementedError = exports.DependencyNotFoundError = exports.visible = exports.provider = 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");
|
|
@@ -46,9 +46,13 @@ Object.defineProperty(exports, "ProxyInjector", { enumerable: true, get: functio
|
|
|
46
46
|
var hook_1 = require("./hook");
|
|
47
47
|
Object.defineProperty(exports, "getHooks", { enumerable: true, get: function () { return hook_1.getHooks; } });
|
|
48
48
|
Object.defineProperty(exports, "hook", { enumerable: true, get: function () { return hook_1.hook; } });
|
|
49
|
+
Object.defineProperty(exports, "hasHooks", { enumerable: true, get: function () { return hook_1.hasHooks; } });
|
|
49
50
|
var by_1 = require("./by");
|
|
50
51
|
Object.defineProperty(exports, "by", { enumerable: true, get: function () { return by_1.by; } });
|
|
51
52
|
var metadata_1 = require("./metadata");
|
|
52
53
|
Object.defineProperty(exports, "setMetadata", { enumerable: true, get: function () { return metadata_1.setMetadata; } });
|
|
53
54
|
Object.defineProperty(exports, "getMetadata", { enumerable: true, get: function () { return metadata_1.getMetadata; } });
|
|
54
55
|
Object.defineProperty(exports, "setParameterMetadata", { enumerable: true, get: function () { return metadata_1.setParameterMetadata; } });
|
|
56
|
+
Object.defineProperty(exports, "setMethodMetadata", { enumerable: true, get: function () { return metadata_1.setMethodMetadata; } });
|
|
57
|
+
Object.defineProperty(exports, "getMethodMetadata", { enumerable: true, get: function () { return metadata_1.getMethodMetadata; } });
|
|
58
|
+
Object.defineProperty(exports, "getParameterMetadata", { enumerable: true, get: function () { return metadata_1.getParameterMetadata; } });
|
package/cjm/metadata.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getParameterMetadata = exports.setParameterMetadata = exports.getMetadata = exports.setMetadata = void 0;
|
|
3
|
+
exports.getMethodMetadata = exports.setMethodMetadata = exports.getParameterMetadata = exports.setParameterMetadata = exports.getMetadata = exports.setMetadata = void 0;
|
|
4
4
|
const setMetadata = (key, value) => (target) => {
|
|
5
5
|
Reflect.defineMetadata(key, value, target);
|
|
6
6
|
};
|
|
@@ -19,3 +19,9 @@ const getParameterMetadata = (key, target) => {
|
|
|
19
19
|
return Reflect.getOwnMetadata(key, target) ?? [];
|
|
20
20
|
};
|
|
21
21
|
exports.getParameterMetadata = getParameterMetadata;
|
|
22
|
+
const setMethodMetadata = (key, value) => (target, propertyKey) => {
|
|
23
|
+
Reflect.defineMetadata(key, value, target.constructor, propertyKey);
|
|
24
|
+
};
|
|
25
|
+
exports.setMethodMetadata = setMethodMetadata;
|
|
26
|
+
const getMethodMetadata = (key, target, propertyKey) => Reflect.getMetadata(key, target.constructor, propertyKey);
|
|
27
|
+
exports.getMethodMetadata = getMethodMetadata;
|
|
@@ -6,7 +6,7 @@ const key = (key) => (r) => r.to(key);
|
|
|
6
6
|
exports.key = key;
|
|
7
7
|
const alias = (...aliases) => (r) => r.addAliases(...aliases);
|
|
8
8
|
exports.alias = alias;
|
|
9
|
-
const scope = (predicate) => (r) => r.
|
|
9
|
+
const scope = (predicate) => (r) => r.when(predicate);
|
|
10
10
|
exports.scope = scope;
|
|
11
11
|
const METADATA_KEY = 'registration';
|
|
12
12
|
const getTransformers = (Target) => (0, metadata_1.getMetadata)(Target, METADATA_KEY) ?? [];
|
|
@@ -13,7 +13,7 @@ export class Container {
|
|
|
13
13
|
this.parent = options.parent ?? new EmptyContainer();
|
|
14
14
|
this.tags = new Set(options.tags ?? []);
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
add(registration) {
|
|
17
17
|
this.registrations.push(registration);
|
|
18
18
|
registration.applyTo(this);
|
|
19
19
|
return this;
|
|
@@ -22,7 +22,7 @@ export class Container {
|
|
|
22
22
|
this.validateContainer();
|
|
23
23
|
this.providers.set(key, provider);
|
|
24
24
|
if (aliases && aliases.length > 0) {
|
|
25
|
-
this.aliases.set(key, aliases);
|
|
25
|
+
this.aliases.set(key, new Set(aliases));
|
|
26
26
|
}
|
|
27
27
|
return this;
|
|
28
28
|
}
|
|
@@ -67,10 +67,8 @@ export class Container {
|
|
|
67
67
|
hasTag(tag) {
|
|
68
68
|
return this.tags.has(tag);
|
|
69
69
|
}
|
|
70
|
-
use(
|
|
71
|
-
|
|
72
|
-
module.applyTo(this);
|
|
73
|
-
}
|
|
70
|
+
use(module) {
|
|
71
|
+
module.applyTo(this);
|
|
74
72
|
return this;
|
|
75
73
|
}
|
|
76
74
|
hasDependency(key) {
|
|
@@ -105,9 +103,8 @@ export class Container {
|
|
|
105
103
|
* @private
|
|
106
104
|
*/
|
|
107
105
|
getRegistrations() {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
: this.parent.getRegistrations();
|
|
106
|
+
const registrations = this.parent.getRegistrations();
|
|
107
|
+
return this.registrations.length > 0 ? registrations.concat(this.registrations) : registrations;
|
|
111
108
|
}
|
|
112
109
|
/**
|
|
113
110
|
* @private
|
package/esm/hook.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
export const hook = (key) => (target, propertyKey) => {
|
|
2
|
-
const hooks = Reflect.hasMetadata(key, target.constructor)
|
|
3
|
-
|
|
1
|
+
export const hook = (key, fn = () => []) => (target, propertyKey) => {
|
|
2
|
+
const hooks = Reflect.hasMetadata(key, target.constructor)
|
|
3
|
+
? Reflect.getMetadata(key, target.constructor)
|
|
4
|
+
: new Map();
|
|
5
|
+
hooks.set(propertyKey, fn);
|
|
6
|
+
Reflect.defineMetadata(key, hooks, target.constructor); // eslint-disable-line @typescript-eslint/ban-types
|
|
4
7
|
};
|
|
5
|
-
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
6
8
|
export function getHooks(target, key) {
|
|
7
|
-
return Reflect.hasMetadata(key, target.constructor) ? Reflect.getMetadata(key, target.constructor) :
|
|
9
|
+
return Reflect.hasMetadata(key, target.constructor) ? Reflect.getMetadata(key, target.constructor) : new Map();
|
|
10
|
+
}
|
|
11
|
+
export function hasHooks(target, key) {
|
|
12
|
+
return Reflect.hasMetadata(key, target.constructor);
|
|
8
13
|
}
|
package/esm/index.js
CHANGED
|
@@ -15,6 +15,6 @@ export { key, alias, scope, register } from './registration/IRegistration';
|
|
|
15
15
|
export { MetadataInjector, inject } from './injector/MetadataInjector';
|
|
16
16
|
export { SimpleInjector } from './injector/SimpleInjector';
|
|
17
17
|
export { ProxyInjector } from './injector/ProxyInjector';
|
|
18
|
-
export { getHooks, hook } from './hook';
|
|
18
|
+
export { getHooks, hook, hasHooks } from './hook';
|
|
19
19
|
export { by } from './by';
|
|
20
|
-
export { setMetadata, getMetadata, setParameterMetadata } from './metadata';
|
|
20
|
+
export { setMetadata, getMetadata, setParameterMetadata, setMethodMetadata, getMethodMetadata, getParameterMetadata, } from './metadata';
|
package/esm/metadata.js
CHANGED
|
@@ -12,3 +12,7 @@ export const setParameterMetadata = (key, value) => (target, propertyKey, parame
|
|
|
12
12
|
export const getParameterMetadata = (key, target) => {
|
|
13
13
|
return Reflect.getOwnMetadata(key, target) ?? [];
|
|
14
14
|
};
|
|
15
|
+
export const setMethodMetadata = (key, value) => (target, propertyKey) => {
|
|
16
|
+
Reflect.defineMetadata(key, value, target.constructor, propertyKey);
|
|
17
|
+
};
|
|
18
|
+
export const getMethodMetadata = (key, target, propertyKey) => Reflect.getMetadata(key, target.constructor, propertyKey);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getMetadata, setMetadata } from '../metadata';
|
|
2
2
|
export const key = (key) => (r) => r.to(key);
|
|
3
3
|
export const alias = (...aliases) => (r) => r.addAliases(...aliases);
|
|
4
|
-
export const scope = (predicate) => (r) => r.
|
|
4
|
+
export const scope = (predicate) => (r) => r.when(predicate);
|
|
5
5
|
const METADATA_KEY = 'registration';
|
|
6
6
|
export const getTransformers = (Target) => getMetadata(Target, METADATA_KEY) ?? [];
|
|
7
7
|
export const register = (...mappers) => setMetadata(METADATA_KEY, mappers);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-ioc-container",
|
|
3
|
-
"version": "31.1.
|
|
3
|
+
"version": "31.1.4",
|
|
4
4
|
"description": "Typescript IoC container",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"ts-node": "^10.9.1",
|
|
60
60
|
"typescript": "5.4.3"
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "78877437c1aa25256dd20042e106d787db5f0701"
|
|
63
63
|
}
|
|
@@ -13,6 +13,6 @@ export declare abstract class AutoMockedContainer implements IContainer {
|
|
|
13
13
|
use(): this;
|
|
14
14
|
getRegistrations(): never[];
|
|
15
15
|
hasTag(): boolean;
|
|
16
|
-
|
|
16
|
+
add(registration: IRegistration): this;
|
|
17
17
|
getKeyByAlias(alias: AliasPredicate): DependencyKey;
|
|
18
18
|
}
|
|
@@ -16,14 +16,14 @@ export declare class Container implements IContainer {
|
|
|
16
16
|
parent?: IContainer;
|
|
17
17
|
tags?: Tag[];
|
|
18
18
|
});
|
|
19
|
-
|
|
19
|
+
add(registration: IRegistration): this;
|
|
20
20
|
register(key: DependencyKey, provider: IProvider, aliases?: Alias[]): this;
|
|
21
21
|
resolve<T>(token: InjectionToken<T>, { args, child }?: ResolveOptions): T;
|
|
22
22
|
createScope(...tags: Tag[]): Container;
|
|
23
23
|
dispose(): void;
|
|
24
24
|
getInstances(): unknown[];
|
|
25
25
|
hasTag(tag: Tag): boolean;
|
|
26
|
-
use(
|
|
26
|
+
use(module: IContainerModule): this;
|
|
27
27
|
hasDependency(key: DependencyKey): boolean;
|
|
28
28
|
getKeysByAlias(predicate: AliasPredicate): DependencyKey[];
|
|
29
29
|
getKeyByAlias(predicate: AliasPredicate): DependencyKey;
|
|
@@ -14,6 +14,6 @@ export declare class EmptyContainer implements IContainer {
|
|
|
14
14
|
getInstances(): unknown[];
|
|
15
15
|
removeScope(): void;
|
|
16
16
|
use(module: IContainerModule): this;
|
|
17
|
-
|
|
17
|
+
add(registration: IRegistration): this;
|
|
18
18
|
getKeyByAlias(alias: AliasPredicate): DependencyKey;
|
|
19
19
|
}
|
|
@@ -20,16 +20,16 @@ export interface Tagged {
|
|
|
20
20
|
hasTag(tag: Tag): boolean;
|
|
21
21
|
}
|
|
22
22
|
export type Alias = string;
|
|
23
|
-
export type AliasPredicate = (aliases: Alias
|
|
23
|
+
export type AliasPredicate = (aliases: Set<Alias>) => boolean;
|
|
24
24
|
export interface IContainer extends Resolvable, Tagged {
|
|
25
25
|
readonly isDisposed: boolean;
|
|
26
26
|
createScope(...tags: Tag[]): IContainer;
|
|
27
27
|
register(key: DependencyKey, value: IProvider, aliases?: Alias[]): this;
|
|
28
|
-
|
|
28
|
+
add(registration: IRegistration): this;
|
|
29
29
|
removeScope(child: IContainer): void;
|
|
30
30
|
getInstances(): unknown[];
|
|
31
31
|
dispose(): void;
|
|
32
|
-
use(
|
|
32
|
+
use(module: IContainerModule): this;
|
|
33
33
|
getRegistrations(): IRegistration[];
|
|
34
34
|
hasDependency(key: DependencyKey): boolean;
|
|
35
35
|
getKeysByAlias(alias: AliasPredicate): DependencyKey[];
|
package/typings/hook.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export declare
|
|
1
|
+
import { ArgsFn } from './provider/ArgsProvider';
|
|
2
|
+
export declare const hook: (key: string | symbol, fn?: ArgsFn) => MethodDecorator;
|
|
3
|
+
export declare function getHooks(target: object, key: string | symbol): Map<string, ArgsFn>;
|
|
4
|
+
export declare function hasHooks(target: object, key: string | symbol): boolean;
|
package/typings/index.d.ts
CHANGED
|
@@ -17,6 +17,6 @@ export { key, alias, IRegistration, scope, register } from './registration/IRegi
|
|
|
17
17
|
export { MetadataInjector, inject } from './injector/MetadataInjector';
|
|
18
18
|
export { SimpleInjector } from './injector/SimpleInjector';
|
|
19
19
|
export { ProxyInjector } from './injector/ProxyInjector';
|
|
20
|
-
export { getHooks, hook } from './hook';
|
|
20
|
+
export { getHooks, hook, hasHooks } from './hook';
|
|
21
21
|
export { by, InstancePredicate } from './by';
|
|
22
|
-
export { setMetadata, getMetadata, setParameterMetadata } from './metadata';
|
|
22
|
+
export { setMetadata, getMetadata, setParameterMetadata, setMethodMetadata, getMethodMetadata, getParameterMetadata, } from './metadata';
|
package/typings/metadata.d.ts
CHANGED
|
@@ -3,3 +3,5 @@ export declare const setMetadata: <T>(key: string | symbol, value: T) => ClassDe
|
|
|
3
3
|
export declare function getMetadata<T>(target: object, key: string | symbol): T | undefined;
|
|
4
4
|
export declare const setParameterMetadata: (key: string | symbol, value: unknown) => ParameterDecorator;
|
|
5
5
|
export declare const getParameterMetadata: (key: string | symbol, target: constructor<unknown>) => unknown[];
|
|
6
|
+
export declare const setMethodMetadata: (key: string, value: unknown) => MethodDecorator;
|
|
7
|
+
export declare const getMethodMetadata: (key: string, target: object, propertyKey: string) => unknown;
|
|
@@ -3,7 +3,7 @@ import { constructor, MapFn } from '../utils';
|
|
|
3
3
|
export type ScopePredicate = (c: Tagged) => boolean;
|
|
4
4
|
export interface IRegistration extends IContainerModule {
|
|
5
5
|
addAliases(...aliases: Alias[]): this;
|
|
6
|
-
|
|
6
|
+
when(isValidWhen: ScopePredicate): this;
|
|
7
7
|
to(key: DependencyKey): this;
|
|
8
8
|
}
|
|
9
9
|
export declare const key: (key: DependencyKey) => MapFn<IRegistration>;
|
|
@@ -15,5 +15,5 @@ export declare class Registration<T = unknown> implements IRegistration {
|
|
|
15
15
|
addAliases(...aliases: Alias[]): this;
|
|
16
16
|
pipe(...mappers: MapFn<IProvider<T>>[]): this;
|
|
17
17
|
applyTo(container: IContainer): void;
|
|
18
|
-
|
|
18
|
+
when(isValidWhen: ScopePredicate): this;
|
|
19
19
|
}
|