ts-ioc-container 45.1.2 → 45.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.hbs.md +276 -0
- package/README.md +1 -0
- package/package.json +2 -2
package/README.hbs.md
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
# Typescript IoC (Inversion Of Control) container
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
[](https://coveralls.io/github/IgorBabkin/ts-ioc-container?branch=master)
|
|
7
|
+

|
|
8
|
+
[](https://github.com/semantic-release/semantic-release)
|
|
9
|
+
|
|
10
|
+
## Advantages
|
|
11
|
+
- battle tested :boom:
|
|
12
|
+
- written on `typescript`
|
|
13
|
+
- simple and lightweight :heart:
|
|
14
|
+
- clean API :green_heart:
|
|
15
|
+
- supports `tagged scopes`
|
|
16
|
+
- fully test covered :100:
|
|
17
|
+
- can be used with decorators `@inject`
|
|
18
|
+
- can [inject properties](#inject-property)
|
|
19
|
+
- can inject [lazy dependencies](#lazy)
|
|
20
|
+
- composable and open to extend
|
|
21
|
+
- awesome for testing (auto mocking)
|
|
22
|
+
|
|
23
|
+
## Content
|
|
24
|
+
|
|
25
|
+
- [Setup](#setup)
|
|
26
|
+
- [Container](#container)
|
|
27
|
+
- [Basic usage](#basic-usage)
|
|
28
|
+
- [Scope](#scope) `tags`
|
|
29
|
+
- [Instances](#instances)
|
|
30
|
+
- [Dispose](#dispose)
|
|
31
|
+
- [Lazy](#lazy) `lazy`
|
|
32
|
+
- [Injector](#injector)
|
|
33
|
+
- [Metadata](#metadata) `@inject`
|
|
34
|
+
- [Simple](#simple)
|
|
35
|
+
- [Proxy](#proxy)
|
|
36
|
+
- [Provider](#provider) `provider`
|
|
37
|
+
- [Singleton](#singleton) `singleton`
|
|
38
|
+
- [Arguments](#arguments) `args` `argsFn`
|
|
39
|
+
- [Visibility](#visibility) `visible`
|
|
40
|
+
- [Alias](#alias) `asAlias`
|
|
41
|
+
- [Decorator](#decorator) `decorate`
|
|
42
|
+
- [Registration](#registration) `@register`
|
|
43
|
+
- [Key](#key) `asKey`
|
|
44
|
+
- [Scope](#scope) `scope`
|
|
45
|
+
- [Module](#module)
|
|
46
|
+
- [Hook](#hook) `@hook`
|
|
47
|
+
- [OnConstruct](#onconstruct) `@onConstruct`
|
|
48
|
+
- [OnDispose](#ondispose) `@onDispose`
|
|
49
|
+
- [Inject Property](#inject-property)
|
|
50
|
+
- [Inject Method](#inject-method)
|
|
51
|
+
- [Mock](#mock)
|
|
52
|
+
- [Error](#error)
|
|
53
|
+
|
|
54
|
+
## Setup
|
|
55
|
+
|
|
56
|
+
```shell script
|
|
57
|
+
npm install ts-ioc-container reflect-metadata
|
|
58
|
+
```
|
|
59
|
+
```shell script
|
|
60
|
+
yarn add ts-ioc-container reflect-metadata
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Just put it in the entrypoint file of your project. It should be the first line of the code.
|
|
64
|
+
```typescript
|
|
65
|
+
import 'reflect-metadata';
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
And `tsconfig.json` should have next options:
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"compilerOptions": {
|
|
72
|
+
"experimentalDecorators": true,
|
|
73
|
+
"emitDecoratorMetadata": true
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Container
|
|
79
|
+
`IContainer` consists of:
|
|
80
|
+
|
|
81
|
+
- Provider is dependency factory which creates dependency
|
|
82
|
+
- Injector describes how to inject dependencies to constructor
|
|
83
|
+
- Registration is provider factory which registers provider in container
|
|
84
|
+
|
|
85
|
+
### Basic usage
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
{{{include_file './__tests__/readme/basic.spec.ts'}}}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Scope
|
|
92
|
+
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.
|
|
93
|
+
|
|
94
|
+
- NOTICE: remember that when scope doesn't have dependency then it will be resolved from parent container
|
|
95
|
+
- NOTICE: when you create a scope of container then all providers are cloned to new scope. For that reason every provider has methods `clone` and `isValid` to clone itself and check if it's valid for certain scope accordingly.
|
|
96
|
+
- NOTICE: when you create a scope then we clone ONLY tags-matched providers.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
{{{include_file './__tests__/readme/scopes.spec.ts'}}}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Instances
|
|
103
|
+
Sometimes you want to get all instances from container and its scopes. For example, when you want to dispose all instances of container.
|
|
104
|
+
|
|
105
|
+
- you can get instances from container and scope which were created by injector
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
{{{include_file './__tests__/readme/instances.spec.ts'}}}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Dispose
|
|
112
|
+
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.
|
|
113
|
+
|
|
114
|
+
- container can be disposed
|
|
115
|
+
- when container is disposed then all scopes are disposed too
|
|
116
|
+
- when container is disposed then it unregisters all providers and remove all instances
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
{{{include_file './__tests__/readme/disposing.spec.ts'}}}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Lazy
|
|
123
|
+
Sometimes you want to create dependency only when somebody want to invoke it's method or property. This is what `lazy` is for.
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
{{{include_file './__tests__/readme/lazy.spec.ts'}}}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Injector
|
|
130
|
+
`IInjector` is used to describe how dependencies should be injected to constructor.
|
|
131
|
+
|
|
132
|
+
- `MetadataInjector` - injects dependencies using `@inject` decorator
|
|
133
|
+
- `ProxyInjector` - injects dependencies as dictionary `Record<string, unknown>`
|
|
134
|
+
- `SimpleInjector` - just passes container to constructor with others arguments
|
|
135
|
+
|
|
136
|
+
### Metadata
|
|
137
|
+
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`.
|
|
138
|
+
Also you can [inject property.](#inject-property)
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
{{{include_file './__tests__/readme/metadataInjector.spec.ts'}}}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Simple
|
|
145
|
+
This type of injector just passes container to constructor with others arguments.
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
{{{include_file './__tests__/injector/SimpleInjector.spec.ts'}}}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Proxy
|
|
152
|
+
This type of injector injects dependencies as dictionary `Record<string, unknown>`.
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
{{{include_file './__tests__/injector/ProxyInjector.spec.ts'}}}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Provider
|
|
159
|
+
Provider is dependency factory which creates dependency.
|
|
160
|
+
|
|
161
|
+
- `Provider.fromClass(Logger)`
|
|
162
|
+
- `Provider.fromValue(logger)`
|
|
163
|
+
- `new Provider((container, ...args) => container.resolve(Logger, {args}))`
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
{{{include_file './__tests__/readme/provider.spec.ts'}}}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Singleton
|
|
170
|
+
Sometimes you need to create only one instance of dependency per scope. For example, you want to create only one logger per scope.
|
|
171
|
+
|
|
172
|
+
- Singleton provider creates only one instance in every scope where it's resolved.
|
|
173
|
+
- NOTICE: if you create a scope 'A' of container 'root' then Logger of A !== Logger of root.
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
{{{include_file './__tests__/provider/Singleton.spec.ts'}}}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Arguments
|
|
180
|
+
Sometimes you want to bind some arguments to provider. This is what `ArgsProvider` is for.
|
|
181
|
+
- `provider(args('someArgument'))`
|
|
182
|
+
- `provider(argsFn((container) => [container.resolve(Logger), 'someValue']))`
|
|
183
|
+
- `Provider.fromClass(Logger).pipe(args('someArgument'))`
|
|
184
|
+
- NOTICE: args from this provider has higher priority than args from `resolve` method.
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
{{{include_file './__tests__/provider/ArgsProvider.spec.ts'}}}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Visibility
|
|
191
|
+
Sometimes you want to hide dependency if somebody wants to resolve it from certain scope
|
|
192
|
+
- `provider(visible(({ isParent, child }) => isParent || child.hasTag('root')))` - dependency will be accessible from scope `root` or from scope where it's registered
|
|
193
|
+
- `Provider.fromClass(Logger).pipe(visible(({ isParent, child }) => isParent || child.hasTag('root')))`
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
{{{include_file './__tests__/readme/visibility.spec.ts'}}}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Alias
|
|
200
|
+
Alias is needed to group keys
|
|
201
|
+
- `@register(asAlias('logger'))` helper assigns `logger` alias to registration.
|
|
202
|
+
- `by.aliases((it) => it.has('logger') || it.has('a'))` resolves dependencies which have `logger` or `a` aliases
|
|
203
|
+
- `Provider.fromClass(Logger).pipe(alias('logger'))`
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
{{{include_file './__tests__/readme/alias.spec.ts'}}}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Decorator
|
|
210
|
+
Sometimes you want to decorate you class with some logic. This is what `DecoratorProvider` is for.
|
|
211
|
+
- `provider(decorate((instance, container) => new LoggerDecorator(instance)))`
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
{{{include_file './__tests__/readme/decorate.spec.ts'}}}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Registration
|
|
218
|
+
Registration is provider factory which registers provider in container.
|
|
219
|
+
- `@register(asKey('logger'))`
|
|
220
|
+
- `Registration.fromClass(Logger).to('logger')`
|
|
221
|
+
- `Registration.fromClass(Logger)`
|
|
222
|
+
- `Registration.fromValue(Logger)`
|
|
223
|
+
- `Registration.fromFn((container, ...args) => container.resolve(Logger, {args}))`
|
|
224
|
+
|
|
225
|
+
### Key
|
|
226
|
+
Sometimes you want to register provider with certain key. This is what `key` is for.
|
|
227
|
+
|
|
228
|
+
- by default, key is class name
|
|
229
|
+
- you can assign the same key to different registrations
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
{{{include_file './__tests__/readme/registration.spec.ts'}}}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Scope
|
|
236
|
+
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`
|
|
237
|
+
- `@register(scope((container) => container.hasTag('root'))` - register provider only in root scope
|
|
238
|
+
- `Registration.fromClass(Logger).when((container) => container.hasTag('root'))`
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
{{{include_file './__tests__/provider/ScopeProvider.spec.ts'}}}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Module
|
|
245
|
+
Sometimes you want to encapsulate registration logic in separate module. This is what `IContainerModule` is for.
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
{{{include_file './__tests__/readme/containerModule.spec.ts'}}}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Hook
|
|
252
|
+
Sometimes you need to invoke methods after construct or dispose of class. This is what hooks are for.
|
|
253
|
+
|
|
254
|
+
### OnConstruct
|
|
255
|
+
```typescript
|
|
256
|
+
{{{include_file '__tests__/hooks/OnConstruct.spec.ts'}}}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### OnDispose
|
|
260
|
+
```typescript
|
|
261
|
+
{{{include_file '__tests__/hooks/OnDispose.spec.ts'}}}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Inject property
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
{{{include_file '__tests__/readme/injectProp.spec.ts'}}}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## Error
|
|
271
|
+
|
|
272
|
+
- [DependencyNotFoundError.ts](..%2F..%2Flib%2Ferrors%2FDependencyNotFoundError.ts)
|
|
273
|
+
- [MethodNotImplementedError.ts](..%2F..%2Flib%2Ferrors%2FMethodNotImplementedError.ts)
|
|
274
|
+
- [DependencyMissingKeyError.ts](..%2F..%2Flib%2Ferrors%2FDependencyMissingKeyError.ts)
|
|
275
|
+
- [ContainerDisposedError.ts](..%2F..%2Flib%2Ferrors%2FContainerDisposedError.ts)
|
|
276
|
+
|
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|

|
|
6
6
|
[](https://coveralls.io/github/IgorBabkin/ts-ioc-container?branch=master)
|
|
7
7
|

|
|
8
|
+
[](https://github.com/semantic-release/semantic-release)
|
|
8
9
|
|
|
9
10
|
## Advantages
|
|
10
11
|
- battle tested :boom:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-ioc-container",
|
|
3
|
-
"version": "45.1.
|
|
3
|
+
"version": "45.1.4",
|
|
4
4
|
"description": "Typescript IoC container",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"generate:docs": "scripts/generate-readme/generate-readme.ts && git add README.md",
|
|
45
45
|
"build": "npm run build:cjm && npm run build:esm && npm run build:types",
|
|
46
46
|
"test": "jest",
|
|
47
|
-
"test:coverage": "jest --coverage",
|
|
47
|
+
"test:coverage": "jest --coverage --coverageReporters=lcov --coverageReporters=text-summary",
|
|
48
48
|
"type-check": "tsc --noEmit",
|
|
49
49
|
"type-check:watch": "tsc --noEmit --watch",
|
|
50
50
|
"commit": "cz",
|