ts-ioc-container 23.3.7 → 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.
Files changed (68) hide show
  1. package/README.md +186 -80
  2. package/package.json +5 -5
  3. /package/{cjs → cjm}/container/AutoMockedContainer.d.ts +0 -0
  4. /package/{cjs → cjm}/container/AutoMockedContainer.js +0 -0
  5. /package/{cjs → cjm}/container/AutoMockedContainer.js.map +0 -0
  6. /package/{cjs → cjm}/container/Container.d.ts +0 -0
  7. /package/{cjs → cjm}/container/Container.js +0 -0
  8. /package/{cjs → cjm}/container/Container.js.map +0 -0
  9. /package/{cjs → cjm}/container/ContainerDisposedError.d.ts +0 -0
  10. /package/{cjs → cjm}/container/ContainerDisposedError.js +0 -0
  11. /package/{cjs → cjm}/container/ContainerDisposedError.js.map +0 -0
  12. /package/{cjs → cjm}/container/DependencyNotFoundError.d.ts +0 -0
  13. /package/{cjs → cjm}/container/DependencyNotFoundError.js +0 -0
  14. /package/{cjs → cjm}/container/DependencyNotFoundError.js.map +0 -0
  15. /package/{cjs → cjm}/container/EmptyContainer.d.ts +0 -0
  16. /package/{cjs → cjm}/container/EmptyContainer.js +0 -0
  17. /package/{cjs → cjm}/container/EmptyContainer.js.map +0 -0
  18. /package/{cjs → cjm}/container/IContainer.d.ts +0 -0
  19. /package/{cjs → cjm}/container/IContainer.js +0 -0
  20. /package/{cjs → cjm}/container/IContainer.js.map +0 -0
  21. /package/{cjs → cjm}/container/MethodNotImplementedError.d.ts +0 -0
  22. /package/{cjs → cjm}/container/MethodNotImplementedError.js +0 -0
  23. /package/{cjs → cjm}/container/MethodNotImplementedError.js.map +0 -0
  24. /package/{cjs → cjm}/index.d.ts +0 -0
  25. /package/{cjs → cjm}/index.js +0 -0
  26. /package/{cjs → cjm}/index.js.map +0 -0
  27. /package/{cjs → cjm}/injector/IInjector.d.ts +0 -0
  28. /package/{cjs → cjm}/injector/IInjector.js +0 -0
  29. /package/{cjs → cjm}/injector/IInjector.js.map +0 -0
  30. /package/{cjs → cjm}/injector/ProxyInjector.d.ts +0 -0
  31. /package/{cjs → cjm}/injector/ProxyInjector.js +0 -0
  32. /package/{cjs → cjm}/injector/ProxyInjector.js.map +0 -0
  33. /package/{cjs → cjm}/injector/ReflectionInjector.d.ts +0 -0
  34. /package/{cjs → cjm}/injector/ReflectionInjector.js +0 -0
  35. /package/{cjs → cjm}/injector/ReflectionInjector.js.map +0 -0
  36. /package/{cjs → cjm}/injector/SimpleInjector.d.ts +0 -0
  37. /package/{cjs → cjm}/injector/SimpleInjector.js +0 -0
  38. /package/{cjs → cjm}/injector/SimpleInjector.js.map +0 -0
  39. /package/{cjs → cjm}/provider/ArgsProvider.d.ts +0 -0
  40. /package/{cjs → cjm}/provider/ArgsProvider.js +0 -0
  41. /package/{cjs → cjm}/provider/ArgsProvider.js.map +0 -0
  42. /package/{cjs → cjm}/provider/IProvider.d.ts +0 -0
  43. /package/{cjs → cjm}/provider/IProvider.js +0 -0
  44. /package/{cjs → cjm}/provider/IProvider.js.map +0 -0
  45. /package/{cjs → cjm}/provider/Provider.d.ts +0 -0
  46. /package/{cjs → cjm}/provider/Provider.js +0 -0
  47. /package/{cjs → cjm}/provider/Provider.js.map +0 -0
  48. /package/{cjs → cjm}/provider/ProviderDecorator.d.ts +0 -0
  49. /package/{cjs → cjm}/provider/ProviderDecorator.js +0 -0
  50. /package/{cjs → cjm}/provider/ProviderDecorator.js.map +0 -0
  51. /package/{cjs → cjm}/provider/SingletonProvider.d.ts +0 -0
  52. /package/{cjs → cjm}/provider/SingletonProvider.js +0 -0
  53. /package/{cjs → cjm}/provider/SingletonProvider.js.map +0 -0
  54. /package/{cjs → cjm}/provider/TaggedProvider.d.ts +0 -0
  55. /package/{cjs → cjm}/provider/TaggedProvider.js +0 -0
  56. /package/{cjs → cjm}/provider/TaggedProvider.js.map +0 -0
  57. /package/{cjs → cjm}/reflection.d.ts +0 -0
  58. /package/{cjs → cjm}/reflection.js +0 -0
  59. /package/{cjs → cjm}/reflection.js.map +0 -0
  60. /package/{cjs → cjm}/registration/DependencyMissingKeyError.d.ts +0 -0
  61. /package/{cjs → cjm}/registration/DependencyMissingKeyError.js +0 -0
  62. /package/{cjs → cjm}/registration/DependencyMissingKeyError.js.map +0 -0
  63. /package/{cjs → cjm}/registration/Registration.d.ts +0 -0
  64. /package/{cjs → cjm}/registration/Registration.js +0 -0
  65. /package/{cjs → cjm}/registration/Registration.js.map +0 -0
  66. /package/{cjs → cjm}/utils.d.ts +0 -0
  67. /package/{cjs → cjm}/utils.js +0 -0
  68. /package/{cjs → cjm}/utils.js.map +0 -0
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 { Container, IContainer, IInjector, Provider, by, inject, resolve } from "ts-ioc-container";
194
+ import 'reflect-metadata';
195
+ import { by, Container, inject, Provider, ReflectionInjector } from 'ts-ioc-container';
195
196
 
196
- class Logger implements ILogger {
197
- info(message: string) {
198
- console.log(message);
199
- }
197
+ class Logger {
198
+ name = 'Logger';
200
199
  }
201
200
 
202
201
  class App {
203
- constructor(@inject((container, ...args) => container.resolve('ILogger', ...args)) private logger: ILogger) {
204
- }
202
+ constructor(@inject(by('ILogger')) private logger: Logger) {}
205
203
 
206
204
  // OR
207
- // constructor(@inject(by('ILogger')) private logger: ILogger) {
205
+ // constructor(@inject((container, ...args) => container.resolve('ILogger', ...args)) private logger: ILogger) {
208
206
  // }
209
207
 
210
- run() {
211
- this.logger.info('Hello world');
208
+ getLoggerName(): string {
209
+ return this.logger.name;
212
210
  }
213
211
  }
214
212
 
215
- const container = new Container(new ReflectionInjector())
216
- .register('ILogger', Provider.fromClass(Logger));
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 { SimpleInjector, IContainer } from "ts-ioc-container";
229
+ import 'reflect-metadata';
230
+ import { Container, IContainer, Provider, SimpleInjector } from '../lib';
227
231
 
228
- class Logger implements ILogger {
229
- info(message: string) {
230
- console.log(message);
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
- class App {
235
- private logger: ILogger;
238
+ const container = new Container(new SimpleInjector()).register('App', Provider.fromClass(App));
239
+ const app = container.resolve<App>('App');
236
240
 
237
- constructor(private container: IContainer) {
238
- this.logger = container.resolve('ILogger');
239
- }
241
+ expect(app.container).toBeInstanceOf(Container);
242
+ });
240
243
 
241
- run() {
242
- this.logger.info('Hello world');
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
- .register('ILogger', Provider.fromClass(Logger));
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 { ProxyInjector, IContainer } from "ts-ioc-container";
262
+ import 'reflect-metadata';
263
+ import { Container, Provider, ProxyInjector, withArgs } from '../lib';
258
264
 
259
- class Logger implements ILogger {
260
- info(message: string) {
261
- console.log(message);
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
- private logger: ILogger;
269
+ class App {
270
+ logger: Logger;
267
271
 
268
- constructor({ logger }: { logger: ILogger }) {
269
- this.logger = logger;
270
- }
272
+ constructor({ logger }: { logger: Logger }) {
273
+ this.logger = logger;
274
+ }
275
+ }
271
276
 
272
- run() {
273
- this.logger.info('Hello world');
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
- const container = new Container(new ProxyInjector())
278
- .register('logger', Provider.fromClass(Logger));
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 { Provider, SingletonProvider, asSingleton } from "ts-ioc-container";
380
+ import 'reflect-metadata';
381
+ import { asSingleton, Container, forKey, provider, ReflectionInjector, Registration } from '../lib';
348
382
 
349
- container.register('ILogger', Provider.fromClass(Logger).pipe((provider) => new SingletonProvider(provider)));
350
- // OR
351
- container.register('ILogger', Provider.fromClass(Logger).pipe(asSingleton()));
383
+ @forKey('logger')
384
+ @provider(asSingleton())
385
+ class Logger {}
352
386
 
353
- container.resolve('ILogger') === container.resolve('ILogger'); // true
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
- IInjector,
461
- ContainerHook,
462
- Injector,
463
- Registration,
519
+ forKey,
464
520
  getHooks,
465
521
  hook,
466
- } from "ts-ioc-container";
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('ILogger')
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
- @hook('onConstruct')
558
+ isReady = false;
559
+ private messages: string[] = [];
560
+
561
+ constructor(@inject(by('logsRepo')) private logsRepo: LogsRepo) {}
562
+
563
+ @onConstruct
484
564
  initialize() {
485
- console.log('initialized');
565
+ this.isReady = true;
486
566
  }
487
567
 
488
- @hook('onDispose')
489
- dispose() {
490
- console.log('disposed');
568
+ log(message: string): void {
569
+ this.messages.push(message);
491
570
  }
492
- }
493
571
 
494
- const container = new Container(new MyInjector())
495
- .add(Registration.fromClass(Logger));
496
- const logger = container.resolve<ILogger>('ILogger'); // initialized
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.7",
3
+ "version": "23.3.9",
4
4
  "description": "Typescript IoC container",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -11,7 +11,7 @@
11
11
  "license": "ISC",
12
12
  "sideEffects": false,
13
13
  "type": "module",
14
- "main": "cjs/index.js",
14
+ "main": "cjm/index.js",
15
15
  "types": "esm/index.d.ts",
16
16
  "module": "esm/index.js",
17
17
  "keywords": [
@@ -27,7 +27,7 @@
27
27
  "test": "__tests__"
28
28
  },
29
29
  "files": [
30
- "cjs/**/*",
30
+ "cjm/**/*",
31
31
  "esm/**/*"
32
32
  ],
33
33
  "repository": {
@@ -35,7 +35,7 @@
35
35
  "url": "git+https://github.com/IgorBabkin/ts-ioc-container/tree/master/packages/ts-ioc-container"
36
36
  },
37
37
  "scripts": {
38
- "build:cjs": "rimraf cjs && tsc -p tsconfig.production.json --outDir cjs --module CommonJS",
38
+ "build:cjm": "rimraf cjm && tsc -p tsconfig.production.json --outDir cjm --module CommonJS",
39
39
  "build:esm": "rimraf esm && tsc -p tsconfig.production.json --outDir esm",
40
40
  "build:readme": "ts-node --esm ./scripts/compileReadme.ts",
41
41
  "build": "npm-run-all -p build:**",
@@ -56,5 +56,5 @@
56
56
  "ts-node": "^10.9.1",
57
57
  "typescript": "4.4.3"
58
58
  },
59
- "gitHead": "f19623a70ba6c19aaaec090048724984f9ba999a"
59
+ "gitHead": "f764eba0952e959a0415b24e27c40fa774023473"
60
60
  }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes