ts-ioc-container 42.0.0 → 43.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +201 -214
- package/cjm/container/AliasMap.js +0 -5
- package/cjm/container/Container.js +18 -27
- package/cjm/container/EmptyContainer.js +2 -11
- package/cjm/container/IContainer.js +0 -18
- package/cjm/errors/UnsupportedTokenTypeError.js +12 -0
- package/cjm/hooks/HookContext.js +2 -4
- package/cjm/hooks/hook.js +10 -49
- package/cjm/hooks/injectProp.js +6 -0
- package/cjm/hooks/onConstruct.js +8 -0
- package/cjm/hooks/onDispose.js +9 -0
- package/cjm/hooks/runner/AsyncHooksRunner.js +23 -0
- package/cjm/hooks/runner/HooksRunner.js +2 -0
- package/cjm/hooks/runner/SyncHooksRunner.js +28 -0
- package/cjm/index.js +35 -14
- package/cjm/injector/ProxyInjector.js +2 -2
- package/cjm/injector/inject.js +11 -23
- package/cjm/provider/IProvider.js +3 -2
- package/cjm/provider/Provider.js +2 -2
- package/cjm/registration/IRegistration.js +9 -6
- package/cjm/select.js +16 -0
- package/cjm/token/AliasToken.js +43 -0
- package/cjm/token/BindToken.js +7 -0
- package/cjm/token/ClassToken.js +32 -0
- package/cjm/token/ConstantToken.js +24 -0
- package/cjm/token/FunctionToken.js +24 -0
- package/cjm/token/InjectionToken.js +8 -0
- package/cjm/token/InstanceListToken.js +37 -0
- package/cjm/token/StringToken.js +41 -0
- package/cjm/token/toToken.js +25 -0
- package/cjm/utils.js +2 -10
- package/esm/container/AliasMap.js +0 -5
- package/esm/container/Container.js +19 -28
- package/esm/container/EmptyContainer.js +2 -11
- package/esm/container/IContainer.js +1 -16
- package/esm/errors/UnsupportedTokenTypeError.js +8 -0
- package/esm/hooks/HookContext.js +1 -2
- package/esm/hooks/hook.js +10 -43
- package/esm/hooks/injectProp.js +2 -0
- package/esm/hooks/onConstruct.js +4 -0
- package/esm/hooks/onDispose.js +6 -0
- package/esm/hooks/runner/AsyncHooksRunner.js +19 -0
- package/esm/hooks/runner/HooksRunner.js +1 -0
- package/esm/hooks/runner/SyncHooksRunner.js +24 -0
- package/esm/index.js +17 -4
- package/esm/injector/ProxyInjector.js +2 -2
- package/esm/injector/inject.js +11 -21
- package/esm/provider/IProvider.js +3 -2
- package/esm/provider/Provider.js +2 -2
- package/esm/registration/IRegistration.js +7 -3
- package/esm/select.js +13 -0
- package/esm/token/AliasToken.js +38 -0
- package/esm/token/BindToken.js +4 -0
- package/esm/token/ClassToken.js +28 -0
- package/esm/token/ConstantToken.js +20 -0
- package/esm/token/FunctionToken.js +20 -0
- package/esm/token/InjectionToken.js +3 -0
- package/esm/token/InstanceListToken.js +33 -0
- package/esm/token/StringToken.js +37 -0
- package/esm/token/toToken.js +21 -0
- package/esm/utils.js +1 -8
- package/package.json +2 -10
- package/typings/container/AliasMap.d.ts +0 -1
- package/typings/container/AutoMockedContainer.d.ts +3 -7
- package/typings/container/Container.d.ts +3 -10
- package/typings/container/EmptyContainer.d.ts +2 -6
- package/typings/container/IContainer.d.ts +7 -9
- package/typings/errors/UnsupportedTokenTypeError.d.ts +5 -0
- package/typings/hooks/HookContext.d.ts +3 -3
- package/typings/hooks/hook.d.ts +2 -19
- package/typings/hooks/injectProp.d.ts +6 -0
- package/typings/hooks/onConstruct.d.ts +4 -0
- package/typings/hooks/onDispose.d.ts +3 -0
- package/typings/hooks/runner/AsyncHooksRunner.d.ts +11 -0
- package/typings/hooks/runner/HooksRunner.d.ts +7 -0
- package/typings/hooks/runner/SyncHooksRunner.d.ts +6 -0
- package/typings/index.d.ts +18 -6
- package/typings/injector/IInjector.d.ts +1 -1
- package/typings/injector/MetadataInjector.d.ts +2 -2
- package/typings/injector/ProxyInjector.d.ts +2 -2
- package/typings/injector/SimpleInjector.d.ts +2 -2
- package/typings/injector/inject.d.ts +3 -5
- package/typings/provider/IProvider.d.ts +1 -1
- package/typings/registration/IRegistration.d.ts +2 -3
- package/typings/select.d.ts +12 -0
- package/typings/token/AliasToken.d.ts +15 -0
- package/typings/token/BindToken.d.ts +5 -0
- package/typings/token/ClassToken.d.ts +12 -0
- package/typings/token/ConstantToken.d.ts +10 -0
- package/typings/token/FunctionToken.d.ts +11 -0
- package/typings/token/InjectionToken.d.ts +14 -0
- package/typings/token/InstanceListToken.d.ts +13 -0
- package/typings/token/StringToken.d.ts +13 -0
- package/typings/token/toToken.d.ts +5 -0
- package/typings/utils.d.ts +1 -8
- package/cjm/DepKey.js +0 -39
- package/cjm/resolve.js +0 -76
- package/esm/DepKey.js +0 -35
- package/esm/resolve.js +0 -71
- package/typings/DepKey.d.ts +0 -15
- package/typings/resolve.d.ts +0 -37
package/README.md
CHANGED
|
@@ -84,7 +84,7 @@ And `tsconfig.json` should have next options:
|
|
|
84
84
|
### Basic usage
|
|
85
85
|
|
|
86
86
|
```typescript
|
|
87
|
-
import { type IContainer,
|
|
87
|
+
import { Container, type IContainer, inject, Registration as R, select } from 'ts-ioc-container';
|
|
88
88
|
|
|
89
89
|
describe('Basic usage', function () {
|
|
90
90
|
class Logger {
|
|
@@ -98,17 +98,17 @@ describe('Basic usage', function () {
|
|
|
98
98
|
|
|
99
99
|
const container = new Container().addRegistration(R.fromClass(Logger).bindToKey('ILogger'));
|
|
100
100
|
|
|
101
|
-
expect(container.
|
|
101
|
+
expect(container.resolve(App).logger.name).toBe('Logger');
|
|
102
102
|
});
|
|
103
103
|
|
|
104
104
|
it('should inject current scope', function () {
|
|
105
105
|
const root = new Container({ tags: ['root'] });
|
|
106
106
|
|
|
107
107
|
class App {
|
|
108
|
-
constructor(@inject(
|
|
108
|
+
constructor(@inject(select.scope.current) public scope: IContainer) {}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
const app = root.
|
|
111
|
+
const app = root.resolve(App);
|
|
112
112
|
|
|
113
113
|
expect(app.scope).toBe(root);
|
|
114
114
|
});
|
|
@@ -125,8 +125,7 @@ Sometimes you need to create a scope of container. For example, when you want to
|
|
|
125
125
|
|
|
126
126
|
```typescript
|
|
127
127
|
import {
|
|
128
|
-
|
|
129
|
-
by,
|
|
128
|
+
bindTo,
|
|
130
129
|
Container,
|
|
131
130
|
DependencyNotFoundError,
|
|
132
131
|
type IContainer,
|
|
@@ -134,10 +133,11 @@ import {
|
|
|
134
133
|
register,
|
|
135
134
|
Registration as R,
|
|
136
135
|
scope,
|
|
136
|
+
select,
|
|
137
137
|
singleton,
|
|
138
138
|
} from 'ts-ioc-container';
|
|
139
139
|
|
|
140
|
-
@register(
|
|
140
|
+
@register(bindTo('ILogger'), scope((s) => s.hasTag('child')), singleton())
|
|
141
141
|
class Logger {}
|
|
142
142
|
|
|
143
143
|
describe('Scopes', function () {
|
|
@@ -145,18 +145,18 @@ describe('Scopes', function () {
|
|
|
145
145
|
const root = new Container({ tags: ['root'] }).addRegistration(R.fromClass(Logger));
|
|
146
146
|
const child = root.createScope({ tags: ['child'] });
|
|
147
147
|
|
|
148
|
-
expect(child.
|
|
149
|
-
expect(() => root.
|
|
148
|
+
expect(child.resolve('ILogger')).toBe(child.resolve('ILogger'));
|
|
149
|
+
expect(() => root.resolve('ILogger')).toThrow(DependencyNotFoundError);
|
|
150
150
|
});
|
|
151
151
|
|
|
152
152
|
it('should inject new scope', function () {
|
|
153
153
|
const root = new Container({ tags: ['root'] });
|
|
154
154
|
|
|
155
155
|
class App {
|
|
156
|
-
constructor(@inject(
|
|
156
|
+
constructor(@inject(select.scope.create({ tags: ['child'] })) public scope: IContainer) {}
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
const app = root.
|
|
159
|
+
const app = root.resolve(App);
|
|
160
160
|
|
|
161
161
|
expect(app.scope).not.toBe(root);
|
|
162
162
|
expect(app.scope.hasTag('child')).toBe(true);
|
|
@@ -171,25 +171,25 @@ Sometimes you want to get all instances from container and its scopes. For examp
|
|
|
171
171
|
- you can get instances from container and scope which were created by injector
|
|
172
172
|
|
|
173
173
|
```typescript
|
|
174
|
-
import {
|
|
174
|
+
import { bindTo, Container, inject, register, Registration as R, select } from 'ts-ioc-container';
|
|
175
175
|
|
|
176
176
|
describe('Instances', function () {
|
|
177
|
-
@register(
|
|
177
|
+
@register(bindTo('ILogger'))
|
|
178
178
|
class Logger {}
|
|
179
179
|
|
|
180
180
|
it('should return injected instances', () => {
|
|
181
181
|
class App {
|
|
182
|
-
constructor(@inject(
|
|
182
|
+
constructor(@inject(select.instances()) public loggers: Logger[]) {}
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
const root = new Container({ tags: ['root'] }).addRegistration(R.fromClass(Logger));
|
|
186
186
|
const child = root.createScope({ tags: ['child'] });
|
|
187
187
|
|
|
188
|
-
root.
|
|
189
|
-
child.
|
|
188
|
+
root.resolve('ILogger');
|
|
189
|
+
child.resolve('ILogger');
|
|
190
190
|
|
|
191
|
-
const rootApp = root.
|
|
192
|
-
const childApp = child.
|
|
191
|
+
const rootApp = root.resolve(App);
|
|
192
|
+
const childApp = child.resolve(App);
|
|
193
193
|
|
|
194
194
|
expect(childApp.loggers.length).toBe(1);
|
|
195
195
|
expect(rootApp.loggers.length).toBe(2);
|
|
@@ -197,16 +197,16 @@ describe('Instances', function () {
|
|
|
197
197
|
|
|
198
198
|
it('should return only current scope instances', () => {
|
|
199
199
|
class App {
|
|
200
|
-
constructor(@inject(
|
|
200
|
+
constructor(@inject(select.instances().cascade(false)) public loggers: Logger[]) {}
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
const root = new Container({ tags: ['root'] }).addRegistration(R.fromClass(Logger));
|
|
204
204
|
const child = root.createScope({ tags: ['child'] });
|
|
205
205
|
|
|
206
|
-
root.
|
|
207
|
-
child.
|
|
206
|
+
root.resolve('ILogger');
|
|
207
|
+
child.resolve('ILogger');
|
|
208
208
|
|
|
209
|
-
const rootApp = root.
|
|
209
|
+
const rootApp = root.resolve(App);
|
|
210
210
|
|
|
211
211
|
expect(rootApp.loggers.length).toBe(1);
|
|
212
212
|
});
|
|
@@ -215,14 +215,14 @@ describe('Instances', function () {
|
|
|
215
215
|
const isLogger = (instance: unknown) => instance instanceof Logger;
|
|
216
216
|
|
|
217
217
|
class App {
|
|
218
|
-
constructor(@inject(
|
|
218
|
+
constructor(@inject(select.instances(isLogger)) public loggers: Logger[]) {}
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
const container = new Container().addRegistration(R.fromClass(Logger));
|
|
222
222
|
|
|
223
|
-
const logger0 = container.
|
|
224
|
-
const logger1 = container.
|
|
225
|
-
const app = container.
|
|
223
|
+
const logger0 = container.resolve('ILogger');
|
|
224
|
+
const logger1 = container.resolve('ILogger');
|
|
225
|
+
const app = container.resolve(App);
|
|
226
226
|
|
|
227
227
|
expect(app.loggers).toHaveLength(2);
|
|
228
228
|
expect(app.loggers[0]).toBe(logger0);
|
|
@@ -240,7 +240,7 @@ Sometimes you want to dispose container and all its scopes. For example, when yo
|
|
|
240
240
|
- when container is disposed then it unregisters all providers and remove all instances
|
|
241
241
|
|
|
242
242
|
```typescript
|
|
243
|
-
import {
|
|
243
|
+
import { Container, ContainerDisposedError, Registration as R, select } from 'ts-ioc-container';
|
|
244
244
|
|
|
245
245
|
class Logger {}
|
|
246
246
|
|
|
@@ -250,8 +250,8 @@ describe('Disposing', function () {
|
|
|
250
250
|
|
|
251
251
|
root.dispose();
|
|
252
252
|
|
|
253
|
-
expect(() => root.
|
|
254
|
-
expect(
|
|
253
|
+
expect(() => root.resolve('ILogger')).toThrow(ContainerDisposedError);
|
|
254
|
+
expect(select.instances().resolve(root).length).toBe(0);
|
|
255
255
|
});
|
|
256
256
|
});
|
|
257
257
|
|
|
@@ -261,7 +261,7 @@ describe('Disposing', function () {
|
|
|
261
261
|
Sometimes you want to create dependency only when somebody want to invoke it's method or property. This is what `lazy` is for.
|
|
262
262
|
|
|
263
263
|
```typescript
|
|
264
|
-
import {
|
|
264
|
+
import { Container, inject, register, Registration as R, singleton, toToken } from 'ts-ioc-container';
|
|
265
265
|
|
|
266
266
|
describe('lazy provider', () => {
|
|
267
267
|
@register(singleton())
|
|
@@ -286,7 +286,7 @@ describe('lazy provider', () => {
|
|
|
286
286
|
}
|
|
287
287
|
|
|
288
288
|
class App {
|
|
289
|
-
constructor(@inject(
|
|
289
|
+
constructor(@inject(toToken('Service').lazy()) public service: Service) {}
|
|
290
290
|
|
|
291
291
|
run() {
|
|
292
292
|
return this.service.greet();
|
|
@@ -304,8 +304,8 @@ describe('lazy provider', () => {
|
|
|
304
304
|
const container = createContainer();
|
|
305
305
|
|
|
306
306
|
// Act
|
|
307
|
-
container.
|
|
308
|
-
const flag = container.
|
|
307
|
+
container.resolve(App);
|
|
308
|
+
const flag = container.resolve<Flag>('Flag');
|
|
309
309
|
|
|
310
310
|
// Assert
|
|
311
311
|
expect(flag.isSet).toBe(false);
|
|
@@ -316,8 +316,8 @@ describe('lazy provider', () => {
|
|
|
316
316
|
const container = createContainer();
|
|
317
317
|
|
|
318
318
|
// Act
|
|
319
|
-
const app = container.
|
|
320
|
-
const flag = container.
|
|
319
|
+
const app = container.resolve(App);
|
|
320
|
+
const flag = container.resolve<Flag>('Flag');
|
|
321
321
|
|
|
322
322
|
// Assert
|
|
323
323
|
expect(app.run()).toBe('Hello');
|
|
@@ -329,7 +329,7 @@ describe('lazy provider', () => {
|
|
|
329
329
|
const container = createContainer();
|
|
330
330
|
|
|
331
331
|
// Act
|
|
332
|
-
const app = container.
|
|
332
|
+
const app = container.resolve(App);
|
|
333
333
|
|
|
334
334
|
// Assert
|
|
335
335
|
expect(app.run()).toBe('Hello');
|
|
@@ -342,8 +342,8 @@ describe('lazy provider', () => {
|
|
|
342
342
|
const container = createContainer();
|
|
343
343
|
|
|
344
344
|
// Act
|
|
345
|
-
const app = container.
|
|
346
|
-
const flag = container.
|
|
345
|
+
const app = container.resolve(App);
|
|
346
|
+
const flag = container.resolve<Flag>('Flag');
|
|
347
347
|
|
|
348
348
|
// Assert
|
|
349
349
|
expect(app.service.name).toBe('Service');
|
|
@@ -387,7 +387,7 @@ describe('Reflection Injector', function () {
|
|
|
387
387
|
it('should inject dependencies by @inject decorator', function () {
|
|
388
388
|
const container = new Container().addRegistration(R.fromClass(Logger).bindToKey('ILogger'));
|
|
389
389
|
|
|
390
|
-
const app = container.
|
|
390
|
+
const app = container.resolve(App);
|
|
391
391
|
|
|
392
392
|
expect(app.getLoggerName()).toBe('Logger');
|
|
393
393
|
});
|
|
@@ -410,7 +410,7 @@ describe('SimpleInjector', function () {
|
|
|
410
410
|
const container = new Container({ injector: new SimpleInjector() }).addRegistration(
|
|
411
411
|
R.fromClass(App).bindToKey('App'),
|
|
412
412
|
);
|
|
413
|
-
const app = container.
|
|
413
|
+
const app = container.resolve<App>('App');
|
|
414
414
|
|
|
415
415
|
expect(app.container).toBeInstanceOf(Container);
|
|
416
416
|
});
|
|
@@ -426,7 +426,7 @@ describe('SimpleInjector', function () {
|
|
|
426
426
|
const container = new Container({ injector: new SimpleInjector() }).addRegistration(
|
|
427
427
|
R.fromClass(App).bindToKey('App'),
|
|
428
428
|
);
|
|
429
|
-
const app = container.
|
|
429
|
+
const app = container.resolve<App>('App', { args: ['Hello world'] });
|
|
430
430
|
|
|
431
431
|
expect(app.greeting).toBe('Hello world');
|
|
432
432
|
});
|
|
@@ -438,7 +438,7 @@ describe('SimpleInjector', function () {
|
|
|
438
438
|
This type of injector injects dependencies as dictionary `Record<string, unknown>`.
|
|
439
439
|
|
|
440
440
|
```typescript
|
|
441
|
-
import { Container, ProxyInjector,
|
|
441
|
+
import { args, Container, ProxyInjector, Registration as R } from 'ts-ioc-container';
|
|
442
442
|
|
|
443
443
|
describe('ProxyInjector', function () {
|
|
444
444
|
it('should pass dependency to constructor as dictionary', function () {
|
|
@@ -456,7 +456,7 @@ describe('ProxyInjector', function () {
|
|
|
456
456
|
R.fromClass(Logger).bindToKey('logger'),
|
|
457
457
|
);
|
|
458
458
|
|
|
459
|
-
const app = container.
|
|
459
|
+
const app = container.resolve(App);
|
|
460
460
|
expect(app.logger).toBeInstanceOf(Logger);
|
|
461
461
|
});
|
|
462
462
|
|
|
@@ -487,7 +487,7 @@ describe('ProxyInjector', function () {
|
|
|
487
487
|
.addRegistration(R.fromClass(App).bindToKey('App').pipe(args({ greetingTemplate })))
|
|
488
488
|
.addRegistration(R.fromClass(Logger).bindToKey('logger'));
|
|
489
489
|
|
|
490
|
-
const app = container.
|
|
490
|
+
const app = container.resolve<App>('App', { args: [{ name: `world` }] });
|
|
491
491
|
expect(app.greeting).toBe('Hello world');
|
|
492
492
|
});
|
|
493
493
|
|
|
@@ -505,22 +505,22 @@ describe('ProxyInjector', function () {
|
|
|
505
505
|
}
|
|
506
506
|
}
|
|
507
507
|
|
|
508
|
-
// Mock container's
|
|
508
|
+
// Mock container's resolveByAlias to return an array with a Logger instance
|
|
509
509
|
const mockLogger = new Logger();
|
|
510
510
|
const mockContainer = new Container({ injector: new ProxyInjector() });
|
|
511
|
-
mockContainer.
|
|
511
|
+
mockContainer.resolveByAlias = jest.fn().mockImplementation((key) => {
|
|
512
512
|
// Always return the mock array for simplicity
|
|
513
513
|
return [mockLogger];
|
|
514
514
|
});
|
|
515
515
|
mockContainer.addRegistration(R.fromClass(Service).bindToKey('service'));
|
|
516
516
|
|
|
517
|
-
const app = mockContainer.
|
|
517
|
+
const app = mockContainer.resolve(App);
|
|
518
518
|
expect(app.loggers).toBeInstanceOf(Array);
|
|
519
519
|
expect(app.loggers.length).toBe(1);
|
|
520
520
|
expect(app.loggers[0]).toBe(mockLogger);
|
|
521
521
|
expect(app.service).toBeInstanceOf(Service);
|
|
522
|
-
// Verify that
|
|
523
|
-
expect(mockContainer.
|
|
522
|
+
// Verify that resolveByAlias was called with the correct key
|
|
523
|
+
expect(mockContainer.resolveByAlias).toHaveBeenCalledWith('loggersArray');
|
|
524
524
|
});
|
|
525
525
|
});
|
|
526
526
|
|
|
@@ -537,16 +537,16 @@ Provider is dependency factory which creates dependency.
|
|
|
537
537
|
import {
|
|
538
538
|
args,
|
|
539
539
|
argsFn,
|
|
540
|
+
bindTo,
|
|
540
541
|
Container,
|
|
541
542
|
inject,
|
|
543
|
+
lazy,
|
|
542
544
|
Provider,
|
|
545
|
+
register,
|
|
546
|
+
Registration as R,
|
|
543
547
|
scopeAccess,
|
|
544
548
|
singleton,
|
|
545
|
-
|
|
546
|
-
register,
|
|
547
|
-
asKey,
|
|
548
|
-
lazy,
|
|
549
|
-
by,
|
|
549
|
+
toToken,
|
|
550
550
|
} from 'ts-ioc-container';
|
|
551
551
|
|
|
552
552
|
class Logger {}
|
|
@@ -568,37 +568,37 @@ class ClassWithoutTransformers {}
|
|
|
568
568
|
describe('Provider', () => {
|
|
569
569
|
it('can be registered as a function', () => {
|
|
570
570
|
const container = new Container().register('ILogger', new Provider(() => new Logger()));
|
|
571
|
-
expect(container.
|
|
571
|
+
expect(container.resolve('ILogger')).not.toBe(container.resolve('ILogger'));
|
|
572
572
|
});
|
|
573
573
|
|
|
574
574
|
it('can be registered as a value', () => {
|
|
575
575
|
const container = new Container().register('ILogger', Provider.fromValue(new Logger()));
|
|
576
|
-
expect(container.
|
|
576
|
+
expect(container.resolve('ILogger')).toBe(container.resolve('ILogger'));
|
|
577
577
|
});
|
|
578
578
|
|
|
579
579
|
it('can be registered as a class', () => {
|
|
580
580
|
const container = new Container().register('ILogger', Provider.fromClass(Logger));
|
|
581
|
-
expect(container.
|
|
581
|
+
expect(container.resolve('ILogger')).not.toBe(container.resolve('ILogger'));
|
|
582
582
|
});
|
|
583
583
|
|
|
584
584
|
it('can be featured by pipe method', () => {
|
|
585
585
|
const root = new Container({ tags: ['root'] }).register('ILogger', Provider.fromClass(Logger).pipe(singleton()));
|
|
586
|
-
expect(root.
|
|
586
|
+
expect(root.resolve('ILogger')).toBe(root.resolve('ILogger'));
|
|
587
587
|
});
|
|
588
588
|
|
|
589
589
|
it('can be created from a dependency key', () => {
|
|
590
590
|
const container = new Container()
|
|
591
591
|
.register('ILogger', Provider.fromClass(Logger))
|
|
592
592
|
.register('LoggerAlias', Provider.fromKey('ILogger'));
|
|
593
|
-
const logger1 = container.
|
|
594
|
-
const logger2 = container.
|
|
593
|
+
const logger1 = container.resolve('ILogger');
|
|
594
|
+
const logger2 = container.resolve('LoggerAlias');
|
|
595
595
|
expect(logger2).toBeInstanceOf(Logger);
|
|
596
596
|
expect(logger2).not.toBe(logger1);
|
|
597
597
|
});
|
|
598
598
|
|
|
599
599
|
it('supports lazy resolution', () => {
|
|
600
600
|
const container = new Container().register('ILogger', Provider.fromClass(Logger));
|
|
601
|
-
const lazyLogger = container.
|
|
601
|
+
const lazyLogger = container.resolve('ILogger', { lazy: true });
|
|
602
602
|
expect(typeof lazyLogger).toBe('object');
|
|
603
603
|
const loggerPrototype = Object.getPrototypeOf(lazyLogger);
|
|
604
604
|
expect(loggerPrototype).toBeDefined();
|
|
@@ -609,7 +609,7 @@ describe('Provider', () => {
|
|
|
609
609
|
'ConfigService',
|
|
610
610
|
Provider.fromClass(ConfigService).pipe(args('/etc/config.json')),
|
|
611
611
|
);
|
|
612
|
-
const config = container.
|
|
612
|
+
const config = container.resolve<ConfigService>('ConfigService');
|
|
613
613
|
expect(config.getPath()).toBe('/etc/config.json');
|
|
614
614
|
});
|
|
615
615
|
|
|
@@ -620,7 +620,7 @@ describe('Provider', () => {
|
|
|
620
620
|
'ConfigService',
|
|
621
621
|
Provider.fromClass(ConfigService).pipe(argsFn((container) => ['/dynamic/config.json'])),
|
|
622
622
|
);
|
|
623
|
-
const config = container.
|
|
623
|
+
const config = container.resolve<ConfigService>('ConfigService');
|
|
624
624
|
expect(config.getPath()).toBe('/dynamic/config.json');
|
|
625
625
|
});
|
|
626
626
|
|
|
@@ -629,10 +629,10 @@ describe('Provider', () => {
|
|
|
629
629
|
.register('Logger', Provider.fromClass(Logger))
|
|
630
630
|
.register(
|
|
631
631
|
'UserService',
|
|
632
|
-
Provider.fromClass(UserService).pipe(argsFn((container) => [container.
|
|
632
|
+
Provider.fromClass(UserService).pipe(argsFn((container) => [container.resolve('Logger')])),
|
|
633
633
|
);
|
|
634
634
|
const configService = new ConfigService('/test/config.json');
|
|
635
|
-
const userService = container.
|
|
635
|
+
const userService = container.resolve<UserService>('UserService', { args: [configService] });
|
|
636
636
|
expect(userService).toBeInstanceOf(UserService);
|
|
637
637
|
});
|
|
638
638
|
|
|
@@ -645,8 +645,8 @@ describe('Provider', () => {
|
|
|
645
645
|
);
|
|
646
646
|
const adminChild = rootContainer.createScope({ tags: ['admin'] });
|
|
647
647
|
const userChild = rootContainer.createScope({ tags: ['user'] });
|
|
648
|
-
expect(() => adminChild.
|
|
649
|
-
expect(() => userChild.
|
|
648
|
+
expect(() => adminChild.resolve('ILogger')).not.toThrow();
|
|
649
|
+
expect(() => userChild.resolve('ILogger')).toThrow();
|
|
650
650
|
});
|
|
651
651
|
|
|
652
652
|
it('supports chaining multiple pipe transformations', () => {
|
|
@@ -654,8 +654,8 @@ describe('Provider', () => {
|
|
|
654
654
|
'ConfigService',
|
|
655
655
|
Provider.fromClass(ConfigService).pipe(args('/default/config.json'), singleton()),
|
|
656
656
|
);
|
|
657
|
-
const config1 = container.
|
|
658
|
-
const config2 = container.
|
|
657
|
+
const config1 = container.resolve<ConfigService>('ConfigService');
|
|
658
|
+
const config2 = container.resolve<ConfigService>('ConfigService');
|
|
659
659
|
expect(config1).toBe(config2);
|
|
660
660
|
expect(config1.getPath()).toBe('/default/config.json');
|
|
661
661
|
});
|
|
@@ -664,9 +664,9 @@ describe('Provider', () => {
|
|
|
664
664
|
const container = new Container()
|
|
665
665
|
.register('ClassConstructor', Provider.fromValue(TestClass))
|
|
666
666
|
.register('ClassInstance', Provider.fromClass(TestClass));
|
|
667
|
-
const instance1 = container.
|
|
668
|
-
const instance2 = container.
|
|
669
|
-
const instance3 = container.
|
|
667
|
+
const instance1 = container.resolve('ClassConstructor');
|
|
668
|
+
const instance2 = container.resolve('ClassConstructor');
|
|
669
|
+
const instance3 = container.resolve('ClassInstance');
|
|
670
670
|
expect(instance1).toBe(TestClass);
|
|
671
671
|
expect(instance2).toBe(TestClass);
|
|
672
672
|
expect(instance3).toBeInstanceOf(TestClass);
|
|
@@ -678,15 +678,15 @@ describe('Provider', () => {
|
|
|
678
678
|
.register('NumberValue', Provider.fromValue(42))
|
|
679
679
|
.register('BooleanValue', Provider.fromValue(true))
|
|
680
680
|
.register('ObjectValue', Provider.fromValue({ key: 'value' }));
|
|
681
|
-
expect(container.
|
|
682
|
-
expect(container.
|
|
683
|
-
expect(container.
|
|
684
|
-
expect(container.
|
|
681
|
+
expect(container.resolve('StringValue')).toBe('test-string');
|
|
682
|
+
expect(container.resolve('NumberValue')).toBe(42);
|
|
683
|
+
expect(container.resolve('BooleanValue')).toBe(true);
|
|
684
|
+
expect(container.resolve('ObjectValue')).toEqual({ key: 'value' });
|
|
685
685
|
});
|
|
686
686
|
|
|
687
687
|
it('resolves dependencies with empty args', () => {
|
|
688
688
|
const container = new Container().register('Logger', Provider.fromClass(Logger));
|
|
689
|
-
const logger = container.
|
|
689
|
+
const logger = container.resolve('Logger', { args: [] });
|
|
690
690
|
expect(logger).toBeInstanceOf(Logger);
|
|
691
691
|
});
|
|
692
692
|
|
|
@@ -696,28 +696,28 @@ describe('Provider', () => {
|
|
|
696
696
|
const container = new Container({ tags: ['root'] }).register('Logger', provider);
|
|
697
697
|
const specialChild = container.createScope({ tags: ['special'] });
|
|
698
698
|
const regularChild = container.createScope({ tags: ['regular'] });
|
|
699
|
-
expect(() => specialChild.
|
|
700
|
-
expect(() => regularChild.
|
|
699
|
+
expect(() => specialChild.resolve('Logger')).not.toThrow();
|
|
700
|
+
expect(() => regularChild.resolve('Logger')).toThrow();
|
|
701
701
|
});
|
|
702
702
|
|
|
703
703
|
it('allows direct manipulation of args function', () => {
|
|
704
704
|
const provider = Provider.fromClass(ConfigService);
|
|
705
705
|
provider.setArgs(() => ['/custom/path.json']);
|
|
706
706
|
const container = new Container().register('ConfigService', provider);
|
|
707
|
-
const config = container.
|
|
707
|
+
const config = container.resolve<ConfigService>('ConfigService');
|
|
708
708
|
expect(config.getPath()).toBe('/custom/path.json');
|
|
709
709
|
});
|
|
710
710
|
|
|
711
711
|
it('handles class constructors when getTransformers returns null', () => {
|
|
712
712
|
const container = new Container().register('NoTransformers', Provider.fromValue(ClassWithoutTransformers));
|
|
713
|
-
const result = container.
|
|
713
|
+
const result = container.resolve('NoTransformers');
|
|
714
714
|
expect(result).toBe(ClassWithoutTransformers);
|
|
715
715
|
});
|
|
716
716
|
|
|
717
717
|
it('allows to register lazy provider', () => {
|
|
718
718
|
let isLoggerCreated = false;
|
|
719
719
|
|
|
720
|
-
@register(
|
|
720
|
+
@register(bindTo('Logger'), lazy())
|
|
721
721
|
class Logger {
|
|
722
722
|
private logs: string[] = [];
|
|
723
723
|
|
|
@@ -743,7 +743,7 @@ describe('Provider', () => {
|
|
|
743
743
|
}
|
|
744
744
|
|
|
745
745
|
const root = new Container({ tags: ['root'] }).addRegistration(R.fromClass(Logger));
|
|
746
|
-
const main = root.
|
|
746
|
+
const main = root.resolve(Main);
|
|
747
747
|
|
|
748
748
|
expect(isLoggerCreated).toBe(false);
|
|
749
749
|
|
|
@@ -753,7 +753,7 @@ describe('Provider', () => {
|
|
|
753
753
|
});
|
|
754
754
|
|
|
755
755
|
it('allows to resolve with args', () => {
|
|
756
|
-
@register(
|
|
756
|
+
@register(bindTo('ILogger'))
|
|
757
757
|
class Logger {
|
|
758
758
|
readonly channel: string;
|
|
759
759
|
|
|
@@ -763,7 +763,7 @@ describe('Provider', () => {
|
|
|
763
763
|
}
|
|
764
764
|
|
|
765
765
|
class Main {
|
|
766
|
-
constructor(@inject(
|
|
766
|
+
constructor(@inject(toToken('ILogger').args({ channel: 'file' })) private logger: Logger) {}
|
|
767
767
|
|
|
768
768
|
getChannel(): string {
|
|
769
769
|
return this.logger.channel;
|
|
@@ -771,13 +771,13 @@ describe('Provider', () => {
|
|
|
771
771
|
}
|
|
772
772
|
|
|
773
773
|
const root = new Container({ tags: ['root'] }).addRegistration(R.fromClass(Logger));
|
|
774
|
-
const main = root.
|
|
774
|
+
const main = root.resolve(Main);
|
|
775
775
|
|
|
776
776
|
expect(main.getChannel()).toBe('file');
|
|
777
777
|
});
|
|
778
778
|
|
|
779
779
|
it('allows to resolve with argsFn', () => {
|
|
780
|
-
@register(
|
|
780
|
+
@register(bindTo('ILogger'))
|
|
781
781
|
class Logger {
|
|
782
782
|
readonly channel: string;
|
|
783
783
|
|
|
@@ -788,7 +788,7 @@ describe('Provider', () => {
|
|
|
788
788
|
|
|
789
789
|
class Main {
|
|
790
790
|
constructor(
|
|
791
|
-
@inject(
|
|
791
|
+
@inject(toToken('ILogger').argsFn((s) => [{ channel: s.resolve('channel') }])) private logger: Logger,
|
|
792
792
|
) {}
|
|
793
793
|
|
|
794
794
|
getChannel(): string {
|
|
@@ -800,7 +800,7 @@ describe('Provider', () => {
|
|
|
800
800
|
.addRegistration(R.fromValue('file').bindToKey('channel'))
|
|
801
801
|
.addRegistration(R.fromClass(Logger));
|
|
802
802
|
|
|
803
|
-
const main = root.
|
|
803
|
+
const main = root.resolve(Main);
|
|
804
804
|
|
|
805
805
|
expect(main.getChannel()).toBe('file');
|
|
806
806
|
});
|
|
@@ -815,9 +815,9 @@ Sometimes you need to create only one instance of dependency per scope. For exam
|
|
|
815
815
|
- NOTICE: if you create a scope 'A' of container 'root' then Logger of A !== Logger of root.
|
|
816
816
|
|
|
817
817
|
```typescript
|
|
818
|
-
import {
|
|
818
|
+
import { bindTo, Container, register, Registration as R, singleton } from 'ts-ioc-container';
|
|
819
819
|
|
|
820
|
-
@register(
|
|
820
|
+
@register(bindTo('logger'), singleton())
|
|
821
821
|
class Logger {}
|
|
822
822
|
|
|
823
823
|
describe('Singleton', function () {
|
|
@@ -828,21 +828,21 @@ describe('Singleton', function () {
|
|
|
828
828
|
it('should resolve the same container per every request', function () {
|
|
829
829
|
const container = createContainer().addRegistration(R.fromClass(Logger));
|
|
830
830
|
|
|
831
|
-
expect(container.
|
|
831
|
+
expect(container.resolve('logger')).toBe(container.resolve('logger'));
|
|
832
832
|
});
|
|
833
833
|
|
|
834
834
|
it('should resolve different dependency per scope', function () {
|
|
835
835
|
const container = createContainer().addRegistration(R.fromClass(Logger));
|
|
836
836
|
const child = container.createScope();
|
|
837
837
|
|
|
838
|
-
expect(container.
|
|
838
|
+
expect(container.resolve('logger')).not.toBe(child.resolve('logger'));
|
|
839
839
|
});
|
|
840
840
|
|
|
841
841
|
it('should resolve the same dependency for scope', function () {
|
|
842
842
|
const container = createContainer().addRegistration(R.fromClass(Logger));
|
|
843
843
|
const child = container.createScope();
|
|
844
844
|
|
|
845
|
-
expect(child.
|
|
845
|
+
expect(child.resolve('logger')).toBe(child.resolve('logger'));
|
|
846
846
|
});
|
|
847
847
|
});
|
|
848
848
|
|
|
@@ -859,19 +859,18 @@ Sometimes you want to bind some arguments to provider. This is what `ArgsProvide
|
|
|
859
859
|
import {
|
|
860
860
|
args,
|
|
861
861
|
argsFn,
|
|
862
|
-
|
|
863
|
-
by,
|
|
862
|
+
bindTo,
|
|
864
863
|
Container,
|
|
865
|
-
depKey,
|
|
866
864
|
inject,
|
|
867
865
|
MultiCache,
|
|
868
866
|
register,
|
|
869
867
|
Registration as R,
|
|
868
|
+
resolveByArgs,
|
|
870
869
|
singleton,
|
|
870
|
+
StringToken,
|
|
871
871
|
} from 'ts-ioc-container';
|
|
872
|
-
import { resolveByArgs } from '../lib/provider/IProvider';
|
|
873
872
|
|
|
874
|
-
@register(
|
|
873
|
+
@register(bindTo('logger'))
|
|
875
874
|
class Logger {
|
|
876
875
|
constructor(
|
|
877
876
|
public name: string,
|
|
@@ -887,21 +886,21 @@ describe('ArgsProvider', function () {
|
|
|
887
886
|
it('can assign argument function to provider', function () {
|
|
888
887
|
const root = createContainer().addRegistration(R.fromClass(Logger).pipe(argsFn(() => ['name'])));
|
|
889
888
|
|
|
890
|
-
const logger = root.createScope().
|
|
889
|
+
const logger = root.createScope().resolve<Logger>('logger');
|
|
891
890
|
expect(logger.name).toBe('name');
|
|
892
891
|
});
|
|
893
892
|
|
|
894
893
|
it('can assign argument to provider', function () {
|
|
895
894
|
const root = createContainer().addRegistration(R.fromClass(Logger).pipe(args('name')));
|
|
896
895
|
|
|
897
|
-
const logger = root.
|
|
896
|
+
const logger = root.resolve<Logger>('logger');
|
|
898
897
|
expect(logger.name).toBe('name');
|
|
899
898
|
});
|
|
900
899
|
|
|
901
900
|
it('should set provider arguments with highest priority in compare to resolve arguments', function () {
|
|
902
901
|
const root = createContainer().addRegistration(R.fromClass(Logger).pipe(args('name')));
|
|
903
902
|
|
|
904
|
-
const logger = root.
|
|
903
|
+
const logger = root.resolve<Logger>('logger', { args: ['file'] });
|
|
905
904
|
|
|
906
905
|
expect(logger.name).toBe('name');
|
|
907
906
|
expect(logger.type).toBe('file');
|
|
@@ -912,15 +911,15 @@ describe('ArgsProvider', function () {
|
|
|
912
911
|
name: string;
|
|
913
912
|
}
|
|
914
913
|
|
|
915
|
-
const IUserRepositoryKey =
|
|
916
|
-
const ITodoRepositoryKey =
|
|
914
|
+
const IUserRepositoryKey = new StringToken<IRepository>('IUserRepository');
|
|
915
|
+
const ITodoRepositoryKey = new StringToken<IRepository>('ITodoRepository');
|
|
917
916
|
|
|
918
|
-
@register(IUserRepositoryKey
|
|
917
|
+
@register(bindTo(IUserRepositoryKey))
|
|
919
918
|
class UserRepository implements IRepository {
|
|
920
919
|
name = 'UserRepository';
|
|
921
920
|
}
|
|
922
921
|
|
|
923
|
-
@register(ITodoRepositoryKey
|
|
922
|
+
@register(bindTo(ITodoRepositoryKey))
|
|
924
923
|
class TodoRepository implements IRepository {
|
|
925
924
|
name = 'TodoRepository';
|
|
926
925
|
}
|
|
@@ -929,17 +928,17 @@ describe('ArgsProvider', function () {
|
|
|
929
928
|
repository: IRepository;
|
|
930
929
|
}
|
|
931
930
|
|
|
932
|
-
const IEntityManagerKey =
|
|
931
|
+
const IEntityManagerKey = new StringToken<IEntityManager>('IEntityManager');
|
|
933
932
|
|
|
934
|
-
@register(IEntityManagerKey
|
|
933
|
+
@register(bindTo(IEntityManagerKey), argsFn(resolveByArgs))
|
|
935
934
|
class EntityManager {
|
|
936
935
|
constructor(public repository: IRepository) {}
|
|
937
936
|
}
|
|
938
937
|
|
|
939
938
|
class Main {
|
|
940
939
|
constructor(
|
|
941
|
-
@inject(
|
|
942
|
-
@inject(
|
|
940
|
+
@inject(IEntityManagerKey.args(IUserRepositoryKey)) public userEntities: EntityManager,
|
|
941
|
+
@inject(IEntityManagerKey.args(ITodoRepositoryKey)) public todoEntities: EntityManager,
|
|
943
942
|
) {}
|
|
944
943
|
}
|
|
945
944
|
|
|
@@ -947,7 +946,7 @@ describe('ArgsProvider', function () {
|
|
|
947
946
|
.addRegistration(R.fromClass(EntityManager))
|
|
948
947
|
.addRegistration(R.fromClass(UserRepository))
|
|
949
948
|
.addRegistration(R.fromClass(TodoRepository));
|
|
950
|
-
const main = root.
|
|
949
|
+
const main = root.resolve(Main);
|
|
951
950
|
|
|
952
951
|
expect(main.userEntities.repository).toBeInstanceOf(UserRepository);
|
|
953
952
|
expect(main.todoEntities.repository).toBeInstanceOf(TodoRepository);
|
|
@@ -958,15 +957,15 @@ describe('ArgsProvider', function () {
|
|
|
958
957
|
name: string;
|
|
959
958
|
}
|
|
960
959
|
|
|
961
|
-
const IUserRepositoryKey =
|
|
962
|
-
const ITodoRepositoryKey =
|
|
960
|
+
const IUserRepositoryKey = new StringToken<IRepository>('IUserRepository');
|
|
961
|
+
const ITodoRepositoryKey = new StringToken<IRepository>('ITodoRepository');
|
|
963
962
|
|
|
964
|
-
@register(IUserRepositoryKey
|
|
963
|
+
@register(bindTo(IUserRepositoryKey))
|
|
965
964
|
class UserRepository implements IRepository {
|
|
966
965
|
name = 'UserRepository';
|
|
967
966
|
}
|
|
968
967
|
|
|
969
|
-
@register(ITodoRepositoryKey
|
|
968
|
+
@register(bindTo(ITodoRepositoryKey))
|
|
970
969
|
class TodoRepository implements IRepository {
|
|
971
970
|
name = 'TodoRepository';
|
|
972
971
|
}
|
|
@@ -975,17 +974,17 @@ describe('ArgsProvider', function () {
|
|
|
975
974
|
repository: IRepository;
|
|
976
975
|
}
|
|
977
976
|
|
|
978
|
-
const IEntityManagerKey =
|
|
977
|
+
const IEntityManagerKey = new StringToken<IEntityManager>('IEntityManager');
|
|
979
978
|
|
|
980
|
-
@register(IEntityManagerKey
|
|
979
|
+
@register(bindTo(IEntityManagerKey), argsFn(resolveByArgs), singleton(MultiCache.fromFirstArg))
|
|
981
980
|
class EntityManager {
|
|
982
981
|
constructor(public repository: IRepository) {}
|
|
983
982
|
}
|
|
984
983
|
|
|
985
984
|
class Main {
|
|
986
985
|
constructor(
|
|
987
|
-
@inject(
|
|
988
|
-
@inject(
|
|
986
|
+
@inject(IEntityManagerKey.args(IUserRepositoryKey)) public userEntities: EntityManager,
|
|
987
|
+
@inject(IEntityManagerKey.args(ITodoRepositoryKey)) public todoEntities: EntityManager,
|
|
989
988
|
) {}
|
|
990
989
|
}
|
|
991
990
|
|
|
@@ -993,13 +992,13 @@ describe('ArgsProvider', function () {
|
|
|
993
992
|
.addRegistration(R.fromClass(EntityManager))
|
|
994
993
|
.addRegistration(R.fromClass(UserRepository))
|
|
995
994
|
.addRegistration(R.fromClass(TodoRepository));
|
|
996
|
-
const main = root.
|
|
995
|
+
const main = root.resolve(Main);
|
|
997
996
|
|
|
998
|
-
const userRepository = IEntityManagerKey.resolve(root
|
|
997
|
+
const userRepository = IEntityManagerKey.args(IUserRepositoryKey).resolve(root).repository;
|
|
999
998
|
expect(userRepository).toBeInstanceOf(UserRepository);
|
|
1000
999
|
expect(main.userEntities.repository).toBe(userRepository);
|
|
1001
1000
|
|
|
1002
|
-
const todoRepository = IEntityManagerKey.resolve(root
|
|
1001
|
+
const todoRepository = IEntityManagerKey.args(ITodoRepositoryKey).resolve(root).repository;
|
|
1003
1002
|
expect(todoRepository).toBeInstanceOf(TodoRepository);
|
|
1004
1003
|
expect(main.todoEntities.repository).toBe(todoRepository);
|
|
1005
1004
|
});
|
|
@@ -1014,20 +1013,20 @@ Sometimes you want to hide dependency if somebody wants to resolve it from certa
|
|
|
1014
1013
|
|
|
1015
1014
|
```typescript
|
|
1016
1015
|
import {
|
|
1017
|
-
|
|
1016
|
+
bindTo,
|
|
1018
1017
|
Container,
|
|
1019
1018
|
DependencyNotFoundError,
|
|
1020
1019
|
register,
|
|
1021
1020
|
Registration as R,
|
|
1022
1021
|
scope,
|
|
1023
|
-
singleton,
|
|
1024
1022
|
scopeAccess,
|
|
1023
|
+
singleton,
|
|
1025
1024
|
} from 'ts-ioc-container';
|
|
1026
1025
|
|
|
1027
1026
|
describe('Visibility', function () {
|
|
1028
1027
|
it('should hide from children', () => {
|
|
1029
1028
|
@register(
|
|
1030
|
-
|
|
1029
|
+
bindTo('logger'),
|
|
1031
1030
|
scope((s) => s.hasTag('root')),
|
|
1032
1031
|
singleton(),
|
|
1033
1032
|
scopeAccess(({ invocationScope, providerScope }) => invocationScope === providerScope),
|
|
@@ -1038,8 +1037,8 @@ describe('Visibility', function () {
|
|
|
1038
1037
|
|
|
1039
1038
|
const child = parent.createScope({ tags: ['child'] });
|
|
1040
1039
|
|
|
1041
|
-
expect(() => child.
|
|
1042
|
-
expect(parent.
|
|
1040
|
+
expect(() => child.resolve('logger')).toThrowError(DependencyNotFoundError);
|
|
1041
|
+
expect(parent.resolve('logger')).toBeInstanceOf(FileLogger);
|
|
1043
1042
|
});
|
|
1044
1043
|
});
|
|
1045
1044
|
|
|
@@ -1052,11 +1051,20 @@ Alias is needed to group keys
|
|
|
1052
1051
|
- `Provider.fromClass(Logger).pipe(alias('logger'))`
|
|
1053
1052
|
|
|
1054
1053
|
```typescript
|
|
1055
|
-
import {
|
|
1054
|
+
import {
|
|
1055
|
+
bindTo,
|
|
1056
|
+
Container,
|
|
1057
|
+
DependencyNotFoundError,
|
|
1058
|
+
inject,
|
|
1059
|
+
register,
|
|
1060
|
+
Registration as R,
|
|
1061
|
+
scope,
|
|
1062
|
+
toAlias,
|
|
1063
|
+
} from 'ts-ioc-container';
|
|
1056
1064
|
|
|
1057
1065
|
describe('alias', () => {
|
|
1058
1066
|
const IMiddlewareKey = 'IMiddleware';
|
|
1059
|
-
const middleware = register(
|
|
1067
|
+
const middleware = register(bindTo(toAlias(IMiddlewareKey)));
|
|
1060
1068
|
|
|
1061
1069
|
interface IMiddleware {
|
|
1062
1070
|
applyTo(application: IApplication): void;
|
|
@@ -1084,7 +1092,7 @@ describe('alias', () => {
|
|
|
1084
1092
|
it('should resolve by some alias', () => {
|
|
1085
1093
|
class App implements IApplication {
|
|
1086
1094
|
private appliedMiddleware: Set<string> = new Set();
|
|
1087
|
-
constructor(@inject(
|
|
1095
|
+
constructor(@inject(toAlias(IMiddlewareKey)) public middleware: IMiddleware[]) {}
|
|
1088
1096
|
|
|
1089
1097
|
markMiddlewareAsApplied(name: string): void {
|
|
1090
1098
|
this.appliedMiddleware.add(name);
|
|
@@ -1109,7 +1117,7 @@ describe('alias', () => {
|
|
|
1109
1117
|
.addRegistration(R.fromClass(LoggerMiddleware))
|
|
1110
1118
|
.addRegistration(R.fromClass(ErrorHandlerMiddleware));
|
|
1111
1119
|
|
|
1112
|
-
const app = container.
|
|
1120
|
+
const app = container.resolve(App);
|
|
1113
1121
|
app.run();
|
|
1114
1122
|
|
|
1115
1123
|
expect(app.isMiddlewareApplied('LoggerMiddleware')).toBe(true);
|
|
@@ -1117,55 +1125,33 @@ describe('alias', () => {
|
|
|
1117
1125
|
});
|
|
1118
1126
|
|
|
1119
1127
|
it('should resolve by some alias', () => {
|
|
1120
|
-
@register(
|
|
1128
|
+
@register(bindTo(toAlias('ILogger')))
|
|
1121
1129
|
class FileLogger {}
|
|
1122
1130
|
|
|
1123
1131
|
const container = new Container().addRegistration(R.fromClass(FileLogger));
|
|
1124
1132
|
|
|
1125
|
-
expect(container.
|
|
1126
|
-
expect(() => container.
|
|
1133
|
+
expect(container.resolveByAlias('ILogger', { takeFirst: 1 })[0]).toBeInstanceOf(FileLogger);
|
|
1134
|
+
expect(() => container.resolve('logger')).toThrowError(DependencyNotFoundError);
|
|
1127
1135
|
});
|
|
1128
1136
|
|
|
1129
1137
|
it('should resolve by alias', () => {
|
|
1130
|
-
@register(
|
|
1138
|
+
@register(bindTo(toAlias('ILogger')), scope((s) => s.hasTag('root')))
|
|
1131
1139
|
class FileLogger {}
|
|
1132
1140
|
|
|
1133
|
-
@register(
|
|
1141
|
+
@register(bindTo(toAlias('ILogger')), scope((s) => s.hasTag('child')))
|
|
1134
1142
|
class DbLogger {}
|
|
1135
1143
|
|
|
1136
1144
|
const container = new Container({ tags: ['root'] })
|
|
1137
1145
|
.addRegistration(R.fromClass(FileLogger))
|
|
1138
1146
|
.addRegistration(R.fromClass(DbLogger));
|
|
1139
1147
|
|
|
1140
|
-
const result1 = container.
|
|
1148
|
+
const [result1] = container.resolveByAlias('ILogger', { takeFirst: 1 });
|
|
1141
1149
|
const child = container.createScope({ tags: ['child'] });
|
|
1142
|
-
const result2 = child.
|
|
1150
|
+
const [result2] = child.resolveByAlias('ILogger', { takeFirst: 1 });
|
|
1143
1151
|
|
|
1144
1152
|
expect(result1).toBeInstanceOf(FileLogger);
|
|
1145
1153
|
expect(result2).toBeInstanceOf(DbLogger);
|
|
1146
1154
|
});
|
|
1147
|
-
|
|
1148
|
-
it('should resolve by aliases', () => {
|
|
1149
|
-
interface ILogger {}
|
|
1150
|
-
|
|
1151
|
-
@register(asAlias('ILogger'))
|
|
1152
|
-
class FileLogger implements ILogger {}
|
|
1153
|
-
|
|
1154
|
-
@register(asAlias('ILogger'))
|
|
1155
|
-
class DbLogger implements ILogger {}
|
|
1156
|
-
|
|
1157
|
-
class App {
|
|
1158
|
-
constructor(@inject(by.aliasOne('ILogger')) public loggers: ILogger[]) {}
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
const container = new Container().addRegistration(R.fromClass(FileLogger));
|
|
1162
|
-
|
|
1163
|
-
const loggers = container.resolveOne(App).loggers;
|
|
1164
|
-
container.addRegistration(R.fromClass(DbLogger));
|
|
1165
|
-
const loggers2 = container.resolveOne(App).loggers;
|
|
1166
|
-
|
|
1167
|
-
expect(loggers).toEqual(loggers2);
|
|
1168
|
-
});
|
|
1169
1155
|
});
|
|
1170
1156
|
|
|
1171
1157
|
```
|
|
@@ -1176,8 +1162,7 @@ Sometimes you want to decorate you class with some logic. This is what `Decorato
|
|
|
1176
1162
|
|
|
1177
1163
|
```typescript
|
|
1178
1164
|
import {
|
|
1179
|
-
|
|
1180
|
-
by,
|
|
1165
|
+
bindTo,
|
|
1181
1166
|
Container,
|
|
1182
1167
|
decorate,
|
|
1183
1168
|
type IContainer,
|
|
@@ -1185,6 +1170,7 @@ import {
|
|
|
1185
1170
|
register,
|
|
1186
1171
|
Registration as R,
|
|
1187
1172
|
singleton,
|
|
1173
|
+
toToken,
|
|
1188
1174
|
} from 'ts-ioc-container';
|
|
1189
1175
|
|
|
1190
1176
|
describe('lazy provider', () => {
|
|
@@ -1213,7 +1199,7 @@ describe('lazy provider', () => {
|
|
|
1213
1199
|
class LogRepository implements IRepository {
|
|
1214
1200
|
constructor(
|
|
1215
1201
|
private repository: IRepository,
|
|
1216
|
-
@inject(
|
|
1202
|
+
@inject(toToken('Logger').lazy()) private logger: Logger,
|
|
1217
1203
|
) {}
|
|
1218
1204
|
|
|
1219
1205
|
async save(item: Todo): Promise<void> {
|
|
@@ -1222,9 +1208,9 @@ describe('lazy provider', () => {
|
|
|
1222
1208
|
}
|
|
1223
1209
|
}
|
|
1224
1210
|
|
|
1225
|
-
const logRepo = (dep: IRepository, scope: IContainer) => scope.
|
|
1211
|
+
const logRepo = (dep: IRepository, scope: IContainer) => scope.resolve(LogRepository, { args: [dep] });
|
|
1226
1212
|
|
|
1227
|
-
@register(
|
|
1213
|
+
@register(bindTo('IRepository'), decorate(logRepo))
|
|
1228
1214
|
class TodoRepository implements IRepository {
|
|
1229
1215
|
async save(item: Todo): Promise<void> {}
|
|
1230
1216
|
}
|
|
@@ -1249,8 +1235,8 @@ describe('lazy provider', () => {
|
|
|
1249
1235
|
const container = createContainer();
|
|
1250
1236
|
|
|
1251
1237
|
// Act
|
|
1252
|
-
const app = container.
|
|
1253
|
-
const logger = container.
|
|
1238
|
+
const app = container.resolve(App);
|
|
1239
|
+
const logger = container.resolve<Logger>('Logger');
|
|
1254
1240
|
await app.run();
|
|
1255
1241
|
|
|
1256
1242
|
// Assert
|
|
@@ -1275,31 +1261,31 @@ Sometimes you want to register provider with certain key. This is what `key` is
|
|
|
1275
1261
|
- you can assign the same key to different registrations
|
|
1276
1262
|
|
|
1277
1263
|
```typescript
|
|
1278
|
-
import {
|
|
1264
|
+
import { bindTo, Container, register, Registration as R, scope, singleton, toAlias } from 'ts-ioc-container';
|
|
1279
1265
|
import { DependencyMissingKeyError } from '../../lib/errors/DependencyMissingKeyError';
|
|
1280
1266
|
|
|
1281
1267
|
describe('Registration module', function () {
|
|
1282
1268
|
const createContainer = () => new Container({ tags: ['root'] });
|
|
1283
1269
|
|
|
1284
1270
|
it('should register class', function () {
|
|
1285
|
-
@register(
|
|
1271
|
+
@register(bindTo('ILogger'), scope((s) => s.hasTag('root')), singleton())
|
|
1286
1272
|
class Logger {}
|
|
1287
1273
|
|
|
1288
1274
|
const root = createContainer().addRegistration(R.fromClass(Logger));
|
|
1289
1275
|
|
|
1290
|
-
expect(root.
|
|
1276
|
+
expect(root.resolve('ILogger')).toBeInstanceOf(Logger);
|
|
1291
1277
|
});
|
|
1292
1278
|
|
|
1293
1279
|
it('should register value', function () {
|
|
1294
1280
|
const root = createContainer().addRegistration(R.fromValue('smth').bindToKey('ISmth'));
|
|
1295
1281
|
|
|
1296
|
-
expect(root.
|
|
1282
|
+
expect(root.resolve('ISmth')).toBe('smth');
|
|
1297
1283
|
});
|
|
1298
1284
|
|
|
1299
1285
|
it('should register fn', function () {
|
|
1300
1286
|
const root = createContainer().addRegistration(R.fromFn(() => 'smth').bindToKey('ISmth'));
|
|
1301
1287
|
|
|
1302
|
-
expect(root.
|
|
1288
|
+
expect(root.resolve('ISmth')).toBe('smth');
|
|
1303
1289
|
});
|
|
1304
1290
|
|
|
1305
1291
|
it('should raise an error if key is not provider', () => {
|
|
@@ -1313,17 +1299,17 @@ describe('Registration module', function () {
|
|
|
1313
1299
|
|
|
1314
1300
|
const root = createContainer().addRegistration(R.fromClass(FileLogger));
|
|
1315
1301
|
|
|
1316
|
-
expect(root.
|
|
1302
|
+
expect(root.resolve('FileLogger')).toBeInstanceOf(FileLogger);
|
|
1317
1303
|
});
|
|
1318
1304
|
|
|
1319
1305
|
it('should assign additional key which redirects to original one', function () {
|
|
1320
|
-
@register(
|
|
1306
|
+
@register(bindTo('ILogger'), bindTo(toAlias('Logger')), singleton())
|
|
1321
1307
|
class Logger {}
|
|
1322
1308
|
|
|
1323
1309
|
const root = createContainer().addRegistration(R.fromClass(Logger));
|
|
1324
1310
|
|
|
1325
|
-
expect(root.
|
|
1326
|
-
expect(root.
|
|
1311
|
+
expect(root.resolveByAlias('Logger')[0]).toBeInstanceOf(Logger);
|
|
1312
|
+
expect(root.resolve('ILogger')).toBeInstanceOf(Logger);
|
|
1327
1313
|
});
|
|
1328
1314
|
});
|
|
1329
1315
|
|
|
@@ -1335,15 +1321,15 @@ Sometimes you need to register provider only in scope which matches to certain c
|
|
|
1335
1321
|
- `Registration.fromClass(Logger).when((container) => container.hasTag('root'))`
|
|
1336
1322
|
|
|
1337
1323
|
```typescript
|
|
1338
|
-
import {
|
|
1324
|
+
import { bindTo, Container, register, Registration as R, scope, singleton } from 'ts-ioc-container';
|
|
1339
1325
|
|
|
1340
|
-
@register(
|
|
1326
|
+
@register(bindTo('ILogger'), scope((s) => s.hasTag('root')), singleton())
|
|
1341
1327
|
class Logger {}
|
|
1342
1328
|
describe('ScopeProvider', function () {
|
|
1343
1329
|
it('should return the same instance', function () {
|
|
1344
1330
|
const root = new Container({ tags: ['root'] }).addRegistration(R.fromClass(Logger));
|
|
1345
1331
|
const child = root.createScope();
|
|
1346
|
-
expect(root.
|
|
1332
|
+
expect(root.resolve('ILogger')).toBe(child.resolve('ILogger'));
|
|
1347
1333
|
});
|
|
1348
1334
|
});
|
|
1349
1335
|
|
|
@@ -1353,12 +1339,12 @@ describe('ScopeProvider', function () {
|
|
|
1353
1339
|
Sometimes you want to encapsulate registration logic in separate module. This is what `IContainerModule` is for.
|
|
1354
1340
|
|
|
1355
1341
|
```typescript
|
|
1356
|
-
import {
|
|
1342
|
+
import { bindTo, Container, type IContainer, type IContainerModule, register, Registration as R } from 'ts-ioc-container';
|
|
1357
1343
|
|
|
1358
|
-
@register(
|
|
1344
|
+
@register(bindTo('ILogger'))
|
|
1359
1345
|
class Logger {}
|
|
1360
1346
|
|
|
1361
|
-
@register(
|
|
1347
|
+
@register(bindTo('ILogger'))
|
|
1362
1348
|
class TestLogger {}
|
|
1363
1349
|
|
|
1364
1350
|
class Production implements IContainerModule {
|
|
@@ -1381,13 +1367,13 @@ describe('Container Modules', function () {
|
|
|
1381
1367
|
it('should register production dependencies', function () {
|
|
1382
1368
|
const container = createContainer(true);
|
|
1383
1369
|
|
|
1384
|
-
expect(container.
|
|
1370
|
+
expect(container.resolve('ILogger')).toBeInstanceOf(Logger);
|
|
1385
1371
|
});
|
|
1386
1372
|
|
|
1387
1373
|
it('should register development dependencies', function () {
|
|
1388
1374
|
const container = createContainer(false);
|
|
1389
1375
|
|
|
1390
|
-
expect(container.
|
|
1376
|
+
expect(container.resolve('ILogger')).toBeInstanceOf(TestLogger);
|
|
1391
1377
|
});
|
|
1392
1378
|
});
|
|
1393
1379
|
|
|
@@ -1399,7 +1385,7 @@ Sometimes you need to invoke methods after construct or dispose of class. This i
|
|
|
1399
1385
|
### OnConstruct
|
|
1400
1386
|
```typescript
|
|
1401
1387
|
import {
|
|
1402
|
-
|
|
1388
|
+
bindTo,
|
|
1403
1389
|
type constructor,
|
|
1404
1390
|
Container,
|
|
1405
1391
|
hook,
|
|
@@ -1409,20 +1395,21 @@ import {
|
|
|
1409
1395
|
MetadataInjector,
|
|
1410
1396
|
register,
|
|
1411
1397
|
Registration as R,
|
|
1412
|
-
|
|
1398
|
+
SyncHooksRunner,
|
|
1413
1399
|
} from 'ts-ioc-container';
|
|
1414
1400
|
|
|
1401
|
+
const onConstructHooksRunner = new SyncHooksRunner('onConstruct');
|
|
1415
1402
|
class MyInjector implements IInjector {
|
|
1416
1403
|
private injector = new MetadataInjector();
|
|
1417
1404
|
|
|
1418
1405
|
resolve<T>(container: IContainer, value: constructor<T>, options: InjectOptions): T {
|
|
1419
1406
|
const instance = this.injector.resolve(container, value, options);
|
|
1420
|
-
|
|
1407
|
+
onConstructHooksRunner.execute(instance as object, { scope: container });
|
|
1421
1408
|
return instance;
|
|
1422
1409
|
}
|
|
1423
1410
|
}
|
|
1424
1411
|
|
|
1425
|
-
@register(
|
|
1412
|
+
@register(bindTo('logger'))
|
|
1426
1413
|
class Logger {
|
|
1427
1414
|
isReady = false;
|
|
1428
1415
|
|
|
@@ -1442,7 +1429,7 @@ describe('onConstruct', function () {
|
|
|
1442
1429
|
it('should make logger be ready on resolve', function () {
|
|
1443
1430
|
const container = new Container({ injector: new MyInjector() }).addRegistration(R.fromClass(Logger));
|
|
1444
1431
|
|
|
1445
|
-
const logger = container.
|
|
1432
|
+
const logger = container.resolve<Logger>('logger');
|
|
1446
1433
|
|
|
1447
1434
|
expect(logger.isReady).toBe(true);
|
|
1448
1435
|
});
|
|
@@ -1452,9 +1439,20 @@ describe('onConstruct', function () {
|
|
|
1452
1439
|
|
|
1453
1440
|
### OnDispose
|
|
1454
1441
|
```typescript
|
|
1455
|
-
import {
|
|
1442
|
+
import {
|
|
1443
|
+
bindTo,
|
|
1444
|
+
Container,
|
|
1445
|
+
hook,
|
|
1446
|
+
inject,
|
|
1447
|
+
register,
|
|
1448
|
+
Registration as R,
|
|
1449
|
+
select,
|
|
1450
|
+
singleton,
|
|
1451
|
+
SyncHooksRunner,
|
|
1452
|
+
} from 'ts-ioc-container';
|
|
1456
1453
|
|
|
1457
|
-
|
|
1454
|
+
const onDisposeHookRunner = new SyncHooksRunner('onDispose');
|
|
1455
|
+
@register(bindTo('logsRepo'), singleton())
|
|
1458
1456
|
class LogsRepo {
|
|
1459
1457
|
savedLogs: string[] = [];
|
|
1460
1458
|
|
|
@@ -1463,7 +1461,7 @@ class LogsRepo {
|
|
|
1463
1461
|
}
|
|
1464
1462
|
}
|
|
1465
1463
|
|
|
1466
|
-
@register(
|
|
1464
|
+
@register(bindTo('logger'))
|
|
1467
1465
|
class Logger {
|
|
1468
1466
|
@hook('onDispose', ({ instance, methodName }) => {
|
|
1469
1467
|
// @ts-ignore
|
|
@@ -1493,14 +1491,14 @@ describe('onDispose', function () {
|
|
|
1493
1491
|
it('should invoke hooks on all instances', async function () {
|
|
1494
1492
|
const container = new Container().addRegistration(R.fromClass(Logger)).addRegistration(R.fromClass(LogsRepo));
|
|
1495
1493
|
|
|
1496
|
-
const logger = container.
|
|
1494
|
+
const logger = container.resolve<Logger>('logger');
|
|
1497
1495
|
logger.log('Hello');
|
|
1498
1496
|
|
|
1499
|
-
for (const instance of
|
|
1500
|
-
|
|
1497
|
+
for (const instance of select.instances().resolve(container)) {
|
|
1498
|
+
onDisposeHookRunner.execute(instance as object, { scope: container });
|
|
1501
1499
|
}
|
|
1502
1500
|
|
|
1503
|
-
expect(container.
|
|
1501
|
+
expect(container.resolve<LogsRepo>('logsRepo').savedLogs.join(',')).toBe('Hello,world');
|
|
1504
1502
|
});
|
|
1505
1503
|
});
|
|
1506
1504
|
|
|
@@ -1509,8 +1507,9 @@ describe('onDispose', function () {
|
|
|
1509
1507
|
### Inject property
|
|
1510
1508
|
|
|
1511
1509
|
```typescript
|
|
1512
|
-
import { Container, hook, injectProp, Registration,
|
|
1510
|
+
import { Container, hook, injectProp, Registration, SyncHooksRunner } from 'ts-ioc-container';
|
|
1513
1511
|
|
|
1512
|
+
const onInitHookRunner = new SyncHooksRunner('onInit');
|
|
1514
1513
|
describe('inject property', () => {
|
|
1515
1514
|
it('should inject property', () => {
|
|
1516
1515
|
class App {
|
|
@@ -1520,8 +1519,8 @@ describe('inject property', () => {
|
|
|
1520
1519
|
const expected = 'Hello world!';
|
|
1521
1520
|
|
|
1522
1521
|
const container = new Container().addRegistration(Registration.fromValue(expected).bindToKey('greeting'));
|
|
1523
|
-
const app = container.
|
|
1524
|
-
|
|
1522
|
+
const app = container.resolve(App);
|
|
1523
|
+
onInitHookRunner.execute(app as object, { scope: container });
|
|
1525
1524
|
|
|
1526
1525
|
expect(app.greeting).toBe(expected);
|
|
1527
1526
|
});
|
|
@@ -1539,7 +1538,7 @@ import { type IMock, Mock } from 'moq.ts';
|
|
|
1539
1538
|
export class MoqContainer extends AutoMockedContainer {
|
|
1540
1539
|
private mocks = new Map<DependencyKey, IMock<any>>();
|
|
1541
1540
|
|
|
1542
|
-
|
|
1541
|
+
resolve<T>(key: DependencyKey): T {
|
|
1543
1542
|
return this.resolveMock<T>(key).object();
|
|
1544
1543
|
}
|
|
1545
1544
|
|
|
@@ -1550,19 +1549,7 @@ export class MoqContainer extends AutoMockedContainer {
|
|
|
1550
1549
|
return this.mocks.get(key) as IMock<T>;
|
|
1551
1550
|
}
|
|
1552
1551
|
|
|
1553
|
-
|
|
1554
|
-
throw new Error('Method not implemented.');
|
|
1555
|
-
}
|
|
1556
|
-
|
|
1557
|
-
resolveOneByKey<T>(key: DependencyKey): T {
|
|
1558
|
-
return this.resolveMock<T>(key).object();
|
|
1559
|
-
}
|
|
1560
|
-
|
|
1561
|
-
resolveMany<T>(alias: DependencyKey): T[] {
|
|
1562
|
-
throw new Error('Method not implemented.');
|
|
1563
|
-
}
|
|
1564
|
-
|
|
1565
|
-
resolveOneByAlias<T>(key: DependencyKey): T {
|
|
1552
|
+
resolveByAlias<T>(alias: DependencyKey): T[] {
|
|
1566
1553
|
throw new Error('Method not implemented.');
|
|
1567
1554
|
}
|
|
1568
1555
|
}
|
|
@@ -1579,7 +1566,7 @@ describe('Mocking', () => {
|
|
|
1579
1566
|
const engineMock = mockContainer.resolveMock<IEngine>('IEngine');
|
|
1580
1567
|
engineMock.setup((i) => i.getRegistrationNumber()).returns('123');
|
|
1581
1568
|
|
|
1582
|
-
const engine = container.
|
|
1569
|
+
const engine = container.resolve<IEngine>('IEngine');
|
|
1583
1570
|
|
|
1584
1571
|
expect(engine.getRegistrationNumber()).toBe('123');
|
|
1585
1572
|
});
|