ts-ioc-container 23.3.8 → 23.3.9
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 +186 -80
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -191,94 +191,127 @@ Injectors are used to describe how dependencies should be injected to constructo
|
|
|
191
191
|
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 `ReflectionInjector`.
|
|
192
192
|
|
|
193
193
|
```typescript
|
|
194
|
-
import
|
|
194
|
+
import 'reflect-metadata';
|
|
195
|
+
import { by, Container, inject, Provider, ReflectionInjector } from 'ts-ioc-container';
|
|
195
196
|
|
|
196
|
-
class Logger
|
|
197
|
-
|
|
198
|
-
console.log(message);
|
|
199
|
-
}
|
|
197
|
+
class Logger {
|
|
198
|
+
name = 'Logger';
|
|
200
199
|
}
|
|
201
200
|
|
|
202
201
|
class App {
|
|
203
|
-
constructor(@inject((
|
|
204
|
-
}
|
|
202
|
+
constructor(@inject(by('ILogger')) private logger: Logger) {}
|
|
205
203
|
|
|
206
204
|
// OR
|
|
207
|
-
// constructor(@inject(
|
|
205
|
+
// constructor(@inject((container, ...args) => container.resolve('ILogger', ...args)) private logger: ILogger) {
|
|
208
206
|
// }
|
|
209
207
|
|
|
210
|
-
|
|
211
|
-
this.logger.
|
|
208
|
+
getLoggerName(): string {
|
|
209
|
+
return this.logger.name;
|
|
212
210
|
}
|
|
213
211
|
}
|
|
214
212
|
|
|
215
|
-
|
|
216
|
-
|
|
213
|
+
describe('Reflection Injector', function () {
|
|
214
|
+
it('should inject dependencies by @inject decorator', function () {
|
|
215
|
+
const container = new Container(new ReflectionInjector()).register('ILogger', Provider.fromClass(Logger));
|
|
216
|
+
|
|
217
|
+
const app = container.resolve(App);
|
|
218
|
+
|
|
219
|
+
expect(app.getLoggerName()).toBe('Logger');
|
|
220
|
+
});
|
|
221
|
+
});
|
|
217
222
|
|
|
218
|
-
const app = container.resolve(App);
|
|
219
|
-
app.run();
|
|
220
223
|
```
|
|
221
224
|
|
|
222
225
|
### Simple injector
|
|
223
226
|
This type of injector just passes container to constructor with others arguments.
|
|
224
227
|
|
|
225
228
|
```typescript
|
|
226
|
-
import
|
|
229
|
+
import 'reflect-metadata';
|
|
230
|
+
import { Container, IContainer, Provider, SimpleInjector } from '../lib';
|
|
227
231
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
232
|
+
describe('SimpleInjector', function () {
|
|
233
|
+
it('should pass container as first parameter', function () {
|
|
234
|
+
class App {
|
|
235
|
+
constructor(public container: IContainer) {}
|
|
236
|
+
}
|
|
233
237
|
|
|
234
|
-
|
|
235
|
-
|
|
238
|
+
const container = new Container(new SimpleInjector()).register('App', Provider.fromClass(App));
|
|
239
|
+
const app = container.resolve<App>('App');
|
|
236
240
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
241
|
+
expect(app.container).toBeInstanceOf(Container);
|
|
242
|
+
});
|
|
240
243
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
244
|
+
it('should pass parameters alongside with container', function () {
|
|
245
|
+
class App {
|
|
246
|
+
constructor(container: IContainer, public greeting: string) {}
|
|
247
|
+
}
|
|
245
248
|
|
|
246
|
-
const container = new Container(new SimpleInjector())
|
|
247
|
-
|
|
249
|
+
const container = new Container(new SimpleInjector()).register('App', Provider.fromClass(App));
|
|
250
|
+
const app = container.resolve<App>('App', 'Hello world');
|
|
251
|
+
|
|
252
|
+
expect(app.greeting).toBe('Hello world');
|
|
253
|
+
});
|
|
254
|
+
});
|
|
248
255
|
|
|
249
|
-
const app = container.resolve(App);
|
|
250
|
-
app.run();
|
|
251
256
|
```
|
|
252
257
|
|
|
253
258
|
### Proxy injector
|
|
254
259
|
This type of injector injects dependencies as dictionary `Record<string, unknown>`.
|
|
255
260
|
|
|
256
261
|
```typescript
|
|
257
|
-
import
|
|
262
|
+
import 'reflect-metadata';
|
|
263
|
+
import { Container, Provider, ProxyInjector, withArgs } from '../lib';
|
|
258
264
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
}
|
|
265
|
+
describe('ProxyInjector', function () {
|
|
266
|
+
it('should pass dependency to constructor as dictionary', function () {
|
|
267
|
+
class Logger {}
|
|
264
268
|
|
|
265
|
-
class App {
|
|
266
|
-
|
|
269
|
+
class App {
|
|
270
|
+
logger: Logger;
|
|
267
271
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
272
|
+
constructor({ logger }: { logger: Logger }) {
|
|
273
|
+
this.logger = logger;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
271
276
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
277
|
+
const container = new Container(new ProxyInjector()).register('logger', Provider.fromClass(Logger));
|
|
278
|
+
|
|
279
|
+
const app = container.resolve(App);
|
|
280
|
+
expect(app.logger).toBeInstanceOf(Logger);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('should pass arguments as objects', function () {
|
|
284
|
+
class Logger {}
|
|
276
285
|
|
|
277
|
-
|
|
278
|
-
|
|
286
|
+
class App {
|
|
287
|
+
logger: Logger;
|
|
288
|
+
greeting: string;
|
|
289
|
+
|
|
290
|
+
constructor({
|
|
291
|
+
logger,
|
|
292
|
+
greetingTemplate,
|
|
293
|
+
name,
|
|
294
|
+
}: {
|
|
295
|
+
logger: Logger;
|
|
296
|
+
greetingTemplate: (name: string) => string;
|
|
297
|
+
name: string;
|
|
298
|
+
}) {
|
|
299
|
+
this.logger = logger;
|
|
300
|
+
this.greeting = greetingTemplate(name);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const greetingTemplate = (name: string) => `Hello ${name}`;
|
|
305
|
+
|
|
306
|
+
const container = new Container(new ProxyInjector())
|
|
307
|
+
.register('App', Provider.fromClass(App).pipe(withArgs({ greetingTemplate })))
|
|
308
|
+
.register('logger', Provider.fromClass(Logger));
|
|
309
|
+
|
|
310
|
+
const app = container.resolve<App>('App', { name: `world` });
|
|
311
|
+
expect(app.greeting).toBe('Hello world');
|
|
312
|
+
});
|
|
313
|
+
});
|
|
279
314
|
|
|
280
|
-
const app = container.resolve(App);
|
|
281
|
-
app.run();
|
|
282
315
|
```
|
|
283
316
|
|
|
284
317
|
## Providers `IProvider<T>`
|
|
@@ -344,17 +377,39 @@ Sometimes you need to create only one instance of dependency per scope. For exam
|
|
|
344
377
|
- NOTICE: if you create a scope 'A' of container 'root' then Logger of A !== Logger of root.
|
|
345
378
|
|
|
346
379
|
```typescript
|
|
347
|
-
import
|
|
380
|
+
import 'reflect-metadata';
|
|
381
|
+
import { asSingleton, Container, forKey, provider, ReflectionInjector, Registration } from '../lib';
|
|
348
382
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
383
|
+
@forKey('logger')
|
|
384
|
+
@provider(asSingleton())
|
|
385
|
+
class Logger {}
|
|
352
386
|
|
|
353
|
-
|
|
387
|
+
describe('Singleton', function () {
|
|
388
|
+
function createContainer() {
|
|
389
|
+
return new Container(new ReflectionInjector());
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
it('should resolve the same container per every request', function () {
|
|
393
|
+
const container = createContainer().add(Registration.fromClass(Logger));
|
|
394
|
+
|
|
395
|
+
expect(container.resolve('logger')).toBe(container.resolve('logger'));
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it('should resolve different dependency per scope', function () {
|
|
399
|
+
const container = createContainer().add(Registration.fromClass(Logger));
|
|
400
|
+
const child = container.createScope();
|
|
401
|
+
|
|
402
|
+
expect(container.resolve('logger')).not.toBe(child.resolve('logger'));
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('should resolve the same dependency for scope', function () {
|
|
406
|
+
const container = createContainer().add(Registration.fromClass(Logger));
|
|
407
|
+
const child = container.createScope();
|
|
408
|
+
|
|
409
|
+
expect(child.resolve('logger')).toBe(child.resolve('logger'));
|
|
410
|
+
});
|
|
411
|
+
});
|
|
354
412
|
|
|
355
|
-
const scope = container.createScope();
|
|
356
|
-
scope.resolve('ILogger') === scope.resolve('ILogger'); // true
|
|
357
|
-
container.resolve('ILogger') !== scope.resolve('ILogger'); // true. NOTICE: because every provider is cloned for every child scope from parent one
|
|
358
413
|
```
|
|
359
414
|
|
|
360
415
|
### Tagged provider
|
|
@@ -455,15 +510,25 @@ container.register('ILogger', Provider.fromClass(Logger));
|
|
|
455
510
|
Sometimes you need to invoke methods after construct or dispose of class. This is what hooks are for.
|
|
456
511
|
|
|
457
512
|
```typescript
|
|
513
|
+
import 'reflect-metadata';
|
|
458
514
|
import {
|
|
515
|
+
asSingleton,
|
|
516
|
+
by,
|
|
517
|
+
constructor,
|
|
459
518
|
Container,
|
|
460
|
-
|
|
461
|
-
ContainerHook,
|
|
462
|
-
Injector,
|
|
463
|
-
Registration,
|
|
519
|
+
forKey,
|
|
464
520
|
getHooks,
|
|
465
521
|
hook,
|
|
466
|
-
|
|
522
|
+
IContainer,
|
|
523
|
+
IInjector,
|
|
524
|
+
inject,
|
|
525
|
+
provider,
|
|
526
|
+
ReflectionInjector,
|
|
527
|
+
Registration,
|
|
528
|
+
} from '../lib';
|
|
529
|
+
|
|
530
|
+
const onConstruct = hook('onConstruct');
|
|
531
|
+
const onDispose = hook('onDispose');
|
|
467
532
|
|
|
468
533
|
class MyInjector implements IInjector {
|
|
469
534
|
private injector = new ReflectionInjector();
|
|
@@ -478,29 +543,70 @@ class MyInjector implements IInjector {
|
|
|
478
543
|
}
|
|
479
544
|
}
|
|
480
545
|
|
|
481
|
-
@forKey('
|
|
546
|
+
@forKey('logsRepo')
|
|
547
|
+
@provider(asSingleton())
|
|
548
|
+
class LogsRepo {
|
|
549
|
+
savedLogs: string[] = [];
|
|
550
|
+
|
|
551
|
+
saveLogs(messages: string[]) {
|
|
552
|
+
this.savedLogs.push(...messages);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
@forKey('logger')
|
|
482
557
|
class Logger {
|
|
483
|
-
|
|
558
|
+
isReady = false;
|
|
559
|
+
private messages: string[] = [];
|
|
560
|
+
|
|
561
|
+
constructor(@inject(by('logsRepo')) private logsRepo: LogsRepo) {}
|
|
562
|
+
|
|
563
|
+
@onConstruct
|
|
484
564
|
initialize() {
|
|
485
|
-
|
|
565
|
+
this.isReady = true;
|
|
486
566
|
}
|
|
487
567
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
console.log('disposed');
|
|
568
|
+
log(message: string): void {
|
|
569
|
+
this.messages.push(message);
|
|
491
570
|
}
|
|
492
|
-
}
|
|
493
571
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
for (const instance of container.getInstances()) {
|
|
499
|
-
for (const h of getHooks(instance, 'onDispose')) {
|
|
500
|
-
// @ts-ignore
|
|
501
|
-
instance[h](); // disposed
|
|
572
|
+
@onDispose
|
|
573
|
+
async save(): Promise<void> {
|
|
574
|
+
this.logsRepo.saveLogs(this.messages);
|
|
575
|
+
this.messages = [];
|
|
502
576
|
}
|
|
503
577
|
}
|
|
578
|
+
|
|
579
|
+
describe('Hooks', function () {
|
|
580
|
+
function createContainer() {
|
|
581
|
+
return new Container(new MyInjector());
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
it('should invoke hooks on all instances', async function () {
|
|
585
|
+
const container = createContainer().add(Registration.fromClass(Logger)).add(Registration.fromClass(LogsRepo));
|
|
586
|
+
|
|
587
|
+
const logger = container.resolve<Logger>('logger');
|
|
588
|
+
logger.log('Hello');
|
|
589
|
+
|
|
590
|
+
for (const instance of container.getInstances()) {
|
|
591
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
592
|
+
for (const h of getHooks(instance as object, 'onDispose')) {
|
|
593
|
+
// @ts-ignore
|
|
594
|
+
await instance[h]();
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
expect(container.resolve<LogsRepo>('logsRepo').savedLogs).toContain('Hello');
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
it('should make logger be ready on resolve', function () {
|
|
602
|
+
const container = createContainer().add(Registration.fromClass(Logger)).add(Registration.fromClass(LogsRepo));
|
|
603
|
+
|
|
604
|
+
const logger = container.resolve<Logger>('logger');
|
|
605
|
+
|
|
606
|
+
expect(logger.isReady).toBe(true);
|
|
607
|
+
});
|
|
608
|
+
});
|
|
609
|
+
|
|
504
610
|
```
|
|
505
611
|
|
|
506
612
|
## Mocking / Tests `AutoMockedContainer`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-ioc-container",
|
|
3
|
-
"version": "23.3.
|
|
3
|
+
"version": "23.3.9",
|
|
4
4
|
"description": "Typescript IoC container",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"ts-node": "^10.9.1",
|
|
57
57
|
"typescript": "4.4.3"
|
|
58
58
|
},
|
|
59
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "f764eba0952e959a0415b24e27c40fa774023473"
|
|
60
60
|
}
|