ditox 3.1.0 → 3.3.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 +135 -32
- package/dist/index.cjs +518 -0
- package/dist/index.cjs.map +1 -0
- package/dist/{umd/index.d.ts → index.d.cts} +195 -197
- package/dist/{browser/index.d.ts → index.d.ts} +195 -197
- package/dist/index.js +501 -0
- package/dist/index.js.map +1 -0
- package/dist/index.umd.js +524 -0
- package/dist/index.umd.js.map +1 -0
- package/package.json +22 -19
- package/src/container.test.ts +34 -33
- package/src/container.ts +12 -6
- package/src/index.ts +26 -23
- package/src/modules.test.ts +172 -37
- package/src/modules.ts +22 -5
- package/src/tokens.test.ts +9 -8
- package/src/tokens.ts +2 -2
- package/src/utils.test.ts +18 -17
- package/src/utils.ts +21 -21
- package/dist/browser/index.js +0 -2
- package/dist/browser/index.js.map +0 -1
- package/dist/cjs/container.d.ts +0 -88
- package/dist/cjs/container.js +0 -216
- package/dist/cjs/container.js.map +0 -1
- package/dist/cjs/index.d.ts +0 -7
- package/dist/cjs/index.js +0 -24
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/modules.d.ts +0 -121
- package/dist/cjs/modules.js +0 -143
- package/dist/cjs/modules.js.map +0 -1
- package/dist/cjs/tokens.d.ts +0 -56
- package/dist/cjs/tokens.js +0 -20
- package/dist/cjs/tokens.js.map +0 -1
- package/dist/cjs/utils.d.ts +0 -137
- package/dist/cjs/utils.js +0 -156
- package/dist/cjs/utils.js.map +0 -1
- package/dist/esm/container.d.ts +0 -88
- package/dist/esm/container.js +0 -211
- package/dist/esm/container.js.map +0 -1
- package/dist/esm/index.d.ts +0 -7
- package/dist/esm/index.js +0 -5
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/modules.d.ts +0 -121
- package/dist/esm/modules.js +0 -136
- package/dist/esm/modules.js.map +0 -1
- package/dist/esm/tokens.d.ts +0 -56
- package/dist/esm/tokens.js +0 -15
- package/dist/esm/tokens.js.map +0 -1
- package/dist/esm/utils.d.ts +0 -137
- package/dist/esm/utils.js +0 -145
- package/dist/esm/utils.js.map +0 -1
- package/dist/umd/index.js +0 -2
- package/dist/umd/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Please see the documentation at [ditox.js.org](https://ditox.js.org)
|
|
|
9
9
|
[](https://www.npmjs.com/package/ditox)
|
|
10
10
|
[](https://github.com/mnasyrov/ditox/stargazers)
|
|
11
11
|
[](https://www.npmjs.com/package/ditox)
|
|
12
|
-
[](https://github.com/mnasyrov/ditox/blob/master/LICENSE)
|
|
13
13
|
[](https://coveralls.io/github/mnasyrov/ditox)
|
|
14
14
|
|
|
15
15
|
## Installation
|
|
@@ -20,12 +20,13 @@ You can use the following command to install this package:
|
|
|
20
20
|
npm install --save ditox
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
The package can be used as [UMD](https://github.com/umdjs/umd) module. Use
|
|
23
|
+
The package can be used as an [UMD](https://github.com/umdjs/umd) module. Use
|
|
24
24
|
[jsdelivr.com](https://jsdelivr.com) CDN site to load
|
|
25
25
|
[ditox](https://www.jsdelivr.com/package/npm/ditox):
|
|
26
26
|
|
|
27
27
|
```html
|
|
28
|
-
|
|
28
|
+
|
|
29
|
+
<script src="//cdn.jsdelivr.net/npm/ditox/dist/umd/index.js"></script>
|
|
29
30
|
<script>
|
|
30
31
|
const container = Ditox.createContainer();
|
|
31
32
|
</script>
|
|
@@ -33,8 +34,8 @@ The package can be used as [UMD](https://github.com/umdjs/umd) module. Use
|
|
|
33
34
|
|
|
34
35
|
## General description
|
|
35
36
|
|
|
36
|
-
DI pattern in general allows
|
|
37
|
-
application. It can be described by following phases:
|
|
37
|
+
DI pattern in general allows declaring and construct a code graph of an
|
|
38
|
+
application. It can be described by the following phases:
|
|
38
39
|
|
|
39
40
|
1. Code declaration phase:
|
|
40
41
|
|
|
@@ -59,30 +60,33 @@ Diagram:
|
|
|
59
60
|
## Usage Example
|
|
60
61
|
|
|
61
62
|
```js
|
|
62
|
-
import {createContainer, injectable, optional, token} from 'ditox';
|
|
63
|
+
import { createContainer, injectable, optional, token } from 'ditox';
|
|
63
64
|
|
|
64
65
|
// This is app code, some factory functions and classes:
|
|
65
|
-
function createStorage(config) {
|
|
66
|
+
function createStorage(config) {
|
|
67
|
+
}
|
|
66
68
|
|
|
67
|
-
function createLogger(config) {
|
|
69
|
+
function createLogger(config) {
|
|
70
|
+
}
|
|
68
71
|
|
|
69
72
|
class UserService {
|
|
70
|
-
constructor(storage, logger) {
|
|
73
|
+
constructor(storage, logger) {
|
|
74
|
+
}
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
// Define tokens for injections.
|
|
74
|
-
const STORAGE_TOKEN = token
|
|
78
|
+
const STORAGE_TOKEN = token('Token description for debugging');
|
|
75
79
|
const LOGGER_TOKEN = token();
|
|
76
80
|
const USER_SERVICE_TOKEN = token();
|
|
77
81
|
|
|
78
82
|
// Token can be optional with a default value.
|
|
79
|
-
const STORAGE_CONFIG_TOKEN = optional(token(), {name: 'default storage'});
|
|
83
|
+
const STORAGE_CONFIG_TOKEN = optional(token(), { name: 'default storage' });
|
|
80
84
|
|
|
81
85
|
// Create the container.
|
|
82
86
|
const container = createContainer();
|
|
83
87
|
|
|
84
88
|
// Provide a value to the container.
|
|
85
|
-
container.bindValue(STORAGE_CONFIG_TOKEN, {name: 'custom storage'});
|
|
89
|
+
container.bindValue(STORAGE_CONFIG_TOKEN, { name: 'custom storage' });
|
|
86
90
|
|
|
87
91
|
// Dynamic values are provided by factories.
|
|
88
92
|
|
|
@@ -94,7 +98,7 @@ container.bindFactory(
|
|
|
94
98
|
);
|
|
95
99
|
|
|
96
100
|
// A factory can have `transient` lifetime to create a value on each resolving.
|
|
97
|
-
container.bindFactory(LOGGER_TOKEN, createLogger, {scope: 'transient'});
|
|
101
|
+
container.bindFactory(LOGGER_TOKEN, createLogger, { scope: 'transient' });
|
|
98
102
|
|
|
99
103
|
// A class can be injected by `injectableClass()` which calls its constructor
|
|
100
104
|
// with injected dependencies as arguments.
|
|
@@ -130,11 +134,11 @@ container.removeAll();
|
|
|
130
134
|
|
|
131
135
|
## Container Hierarchy
|
|
132
136
|
|
|
133
|
-
Ditox.js supports "parent-child" hierarchy. If the child container cannot
|
|
137
|
+
Ditox.js supports a "parent-child" hierarchy. If the child container cannot
|
|
134
138
|
resolve a token, it asks the parent container to resolve it:
|
|
135
139
|
|
|
136
140
|
```js
|
|
137
|
-
import {
|
|
141
|
+
import { createContainer, token } from 'ditox';
|
|
138
142
|
|
|
139
143
|
const V1_TOKEN = token();
|
|
140
144
|
const V2_TOKEN = token();
|
|
@@ -157,7 +161,7 @@ creating a container. During resolution, parents are queried from left to right,
|
|
|
157
161
|
and the first parent that provides the token wins.
|
|
158
162
|
|
|
159
163
|
```js
|
|
160
|
-
import {createContainer, token} from 'ditox';
|
|
164
|
+
import { createContainer, token } from 'ditox';
|
|
161
165
|
|
|
162
166
|
const VALUE = token();
|
|
163
167
|
|
|
@@ -182,8 +186,8 @@ child2.resolve(VALUE); // 'from-child'
|
|
|
182
186
|
|
|
183
187
|
## Factory Lifetimes
|
|
184
188
|
|
|
185
|
-
Ditox.js supports managing the lifetime of values which
|
|
186
|
-
|
|
189
|
+
Ditox.js supports managing the lifetime of values which factories produce. There
|
|
190
|
+
are the following types:
|
|
187
191
|
|
|
188
192
|
- `singleton` - **This is the default**. The value is created and cached by the
|
|
189
193
|
most distant parent container which owns the factory function.
|
|
@@ -193,11 +197,11 @@ factories. There are the following types:
|
|
|
193
197
|
|
|
194
198
|
### `singleton`
|
|
195
199
|
|
|
196
|
-
**This is the default scope**. "Singleton" allows
|
|
200
|
+
**This is the default scope**. "Singleton" allows caching a produced value by a
|
|
197
201
|
most distant parent container which registered the factory function:
|
|
198
202
|
|
|
199
203
|
```js
|
|
200
|
-
import {
|
|
204
|
+
import { createContainer, token } from 'ditox';
|
|
201
205
|
|
|
202
206
|
const TAG_TOKEN = token();
|
|
203
207
|
const LOGGER_TOKEN = token();
|
|
@@ -223,12 +227,12 @@ container2.resolve(LOGGER_TOKEN)('bar'); // [parent] bar
|
|
|
223
227
|
|
|
224
228
|
### `scoped`
|
|
225
229
|
|
|
226
|
-
"Scoped" lifetime allows
|
|
230
|
+
"Scoped" lifetime allows having sub-containers with own instances of some
|
|
227
231
|
services which can be disposed. For example, a context during HTTP request
|
|
228
|
-
handling, or
|
|
232
|
+
handling, or another unit of work:
|
|
229
233
|
|
|
230
234
|
```js
|
|
231
|
-
import {
|
|
235
|
+
import { createContainer, token } from 'ditox';
|
|
232
236
|
|
|
233
237
|
const TAG_TOKEN = token();
|
|
234
238
|
const LOGGER_TOKEN = token();
|
|
@@ -257,10 +261,10 @@ container1.removeAll();
|
|
|
257
261
|
|
|
258
262
|
### `transient`
|
|
259
263
|
|
|
260
|
-
"Transient" makes
|
|
264
|
+
"Transient" makes the factory produce a new value for each resolution:
|
|
261
265
|
|
|
262
266
|
```js
|
|
263
|
-
import {createContainer, token} from 'ditox';
|
|
267
|
+
import { createContainer, token } from 'ditox';
|
|
264
268
|
|
|
265
269
|
const TAG_TOKEN = token();
|
|
266
270
|
const LOGGER_TOKEN = token();
|
|
@@ -293,14 +297,22 @@ Dependencies can be organized as modules in declarative way with
|
|
|
293
297
|
`ModuleDeclaration`. It is useful for providing pieces of functionality from
|
|
294
298
|
libraries to an app which depends on them.
|
|
295
299
|
|
|
300
|
+
The `strategy` field determines when the module's factory is executed:
|
|
301
|
+
|
|
302
|
+
- `lazy` (**default**): The factory is called only when the module or its
|
|
303
|
+
exported tokens are first resolved.
|
|
304
|
+
- `eager`: The factory is called immediately after the module is bound to the
|
|
305
|
+
container. This is useful for modules that need to initialize listeners or
|
|
306
|
+
start background processes right away.
|
|
307
|
+
|
|
296
308
|
```typescript
|
|
297
|
-
import {Module, ModuleDeclaration, token} from 'ditox';
|
|
298
|
-
import {TRANSPORT_TOKEN} from './transport';
|
|
309
|
+
import { Module, ModuleDeclaration, token } from 'ditox';
|
|
310
|
+
import { TRANSPORT_TOKEN } from './transport';
|
|
299
311
|
|
|
300
|
-
export type Logger = {log: (message: string) => void};
|
|
312
|
+
export type Logger = { log: (message: string) => void };
|
|
301
313
|
export const LOGGER_TOKEN = token<Logger>();
|
|
302
314
|
|
|
303
|
-
type LoggerModule = Module<{logger: Logger}>;
|
|
315
|
+
type LoggerModule = Module<{ logger: Logger }>;
|
|
304
316
|
|
|
305
317
|
const LOGGER_MODULE_TOKEN = token<LoggerModule>();
|
|
306
318
|
|
|
@@ -308,10 +320,13 @@ const LOGGER_MODULE: ModuleDeclaration<LoggerModule> = {
|
|
|
308
320
|
// An optional explicit token for a module itself
|
|
309
321
|
token: LOGGER_MODULE_TOKEN,
|
|
310
322
|
|
|
323
|
+
// Use "eager" strategy to initialize the module immediately after binding
|
|
324
|
+
strategy: 'eager',
|
|
325
|
+
|
|
311
326
|
factory: (container) => {
|
|
312
327
|
const transport = container.resolve(TRANSPORT_TOKEN).open();
|
|
313
328
|
return {
|
|
314
|
-
logger: {log: (message) => transport.write(message)},
|
|
329
|
+
logger: { log: (message) => transport.write(message) },
|
|
315
330
|
destroy: () => transport.close(),
|
|
316
331
|
};
|
|
317
332
|
},
|
|
@@ -329,7 +344,7 @@ const container = createContainer();
|
|
|
329
344
|
// bind a single module
|
|
330
345
|
bindModule(container, LOGGER_MODULE);
|
|
331
346
|
|
|
332
|
-
// or bind multiple
|
|
347
|
+
// or bind multiple dependency modules
|
|
333
348
|
bindModules(container, [DATABASE_MODULE, CONFIG_MODULE, API_MODULE]);
|
|
334
349
|
```
|
|
335
350
|
|
|
@@ -350,9 +365,97 @@ const LOGGER_MODULE = declareModule<LoggerModule>({
|
|
|
350
365
|
},
|
|
351
366
|
});
|
|
352
367
|
|
|
353
|
-
const APP_MODULE =
|
|
368
|
+
const APP_MODULE = declareModuleBindings([LOGGER_MODULE, DATABASE_MODULE]);
|
|
354
369
|
```
|
|
355
370
|
|
|
371
|
+
## API Reference
|
|
372
|
+
|
|
373
|
+
- Tokens
|
|
374
|
+
|
|
375
|
+
- token<T>(options?: { key?: string; description?: string } | string):
|
|
376
|
+
Token<T>
|
|
377
|
+
- Creates a new binding token. Use `key` to create shareable tokens via
|
|
378
|
+
`Symbol.for(key)`; otherwise a descriptive `Symbol(description)` is
|
|
379
|
+
created
|
|
380
|
+
- optional<T>(token: Token<T>, optionalValue?: T): OptionalToken<T |
|
|
381
|
+
undefined>
|
|
382
|
+
- Marks the token as optional and provides a default value for resolution
|
|
383
|
+
when not bound
|
|
384
|
+
- Types: Token<T>, RequiredToken<T>, OptionalToken<T>
|
|
385
|
+
|
|
386
|
+
- Container
|
|
387
|
+
|
|
388
|
+
- createContainer(parent?: Container | ReadonlyArray<Container>): Container
|
|
389
|
+
- Creates a new container optionally linked to one or more parents; parents
|
|
390
|
+
are queried left-to-right
|
|
391
|
+
- class ResolverError extends Error
|
|
392
|
+
- Thrown by `resolve()` when a token is not found and no optional default
|
|
393
|
+
exists
|
|
394
|
+
- type FactoryScope = 'scoped' | 'singleton' | 'transient'
|
|
395
|
+
- type FactoryOptions<T>
|
|
396
|
+
- For 'scoped' | 'singleton': { scope?: 'scoped' | 'singleton'; onRemoved?:
|
|
397
|
+
(value: T) => void }
|
|
398
|
+
- For 'transient': { scope: 'transient' }
|
|
399
|
+
- type Container API
|
|
400
|
+
- bindValue<T>(token: Token<T>, value: T): void — bind a concrete value
|
|
401
|
+
- bindFactory<T>(token: Token<T>, factory: (container: Container) => T,
|
|
402
|
+
options?: FactoryOptions<T>): void — bind a factory with lifetime options
|
|
403
|
+
- hasToken(token: Token<unknown>): boolean — check token presence in
|
|
404
|
+
hierarchy
|
|
405
|
+
- get<T>(token: Token<T>): T | undefined — try resolve or return undefined;
|
|
406
|
+
optional tokens return their default
|
|
407
|
+
- resolve<T>(token: Token<T>): T — resolve or throw ResolverError; optional
|
|
408
|
+
tokens return their default
|
|
409
|
+
- remove<T>(token: Token<T>): void — remove binding; for scoped/singleton
|
|
410
|
+
factories calls `onRemoved` if provided
|
|
411
|
+
- removeAll(): void — remove all bindings and call `onRemoved` for
|
|
412
|
+
applicable factories
|
|
413
|
+
|
|
414
|
+
- Utilities
|
|
415
|
+
|
|
416
|
+
- isToken<T>(value: unknown): value is Token<T> — checks if a value looks like
|
|
417
|
+
a token
|
|
418
|
+
- bindMultiValue<T>(container: Container, token: Token<ReadonlyArray<T>>,
|
|
419
|
+
value: T): void — appends a value to an array token
|
|
420
|
+
- tryResolveValue(container: Container, tokenOrMap): value | object — resolves
|
|
421
|
+
or returns undefined for missing tokens; supports an object map of tokens
|
|
422
|
+
- tryResolveValues(container: Container, ...tokenOrMaps): array — batched
|
|
423
|
+
variant of tryResolveValue
|
|
424
|
+
- resolveValue(container: Container, tokenOrMap): value | object — resolves or
|
|
425
|
+
throws on missing tokens; supports an object map of tokens
|
|
426
|
+
- resolveValues(container: Container, ...tokenOrMaps): array — batched variant
|
|
427
|
+
of resolveValue
|
|
428
|
+
- injectable(factory, ...tokens): (container: Container) => Result — decorates
|
|
429
|
+
a factory to receive resolved values as arguments
|
|
430
|
+
- injectableClass(Class, ...tokens): (container: Container) => Instance —
|
|
431
|
+
decorates a class constructor to receive resolved values
|
|
432
|
+
|
|
433
|
+
- Modules
|
|
434
|
+
- Types
|
|
435
|
+
- Module<Props = {}> — a module object: exported values plus optional
|
|
436
|
+
destroy(): void
|
|
437
|
+
- ModuleDeclaration<T extends Module> — declarative description of a module:
|
|
438
|
+
- token: Token<T> — required token
|
|
439
|
+
- imports?: ReadonlyArray<ModuleBindingEntry> — optional imports
|
|
440
|
+
- factory: (container: Container) => T — factory function
|
|
441
|
+
- exports?: Dictionary — optional exported tokens
|
|
442
|
+
- beforeBinding?: (container: Container) => void — optional callback
|
|
443
|
+
- afterBinding?: (container: Container) => void — optional callback
|
|
444
|
+
- strategy?: 'eager' | 'lazy' — execution strategy (default: 'lazy')
|
|
445
|
+
- BindModuleOptions — { scope?: 'scoped' | 'singleton' }
|
|
446
|
+
- ModuleBindingEntry — ModuleDeclaration or { module, options }
|
|
447
|
+
- bindModule(container: Container, module: ModuleDeclaration<T>, options?:
|
|
448
|
+
BindModuleOptions): void
|
|
449
|
+
- Binds a module; exported values are bound via factories; module factory
|
|
450
|
+
respects scope; calls destroy() and removes exports on unbinding
|
|
451
|
+
- bindModules(container: Container, modules:
|
|
452
|
+
ReadonlyArray<ModuleBindingEntry>): void — binds multiple modules
|
|
453
|
+
- declareModule(declaration): ModuleDeclaration<T>
|
|
454
|
+
- Creates a module declaration; generates a token if not provided
|
|
455
|
+
- declareModuleBindings(modules: ReadonlyArray<ModuleBindingEntry>):
|
|
456
|
+
ModuleDeclaration<Module>
|
|
457
|
+
- Declares an anonymous module that binds the provided modules
|
|
458
|
+
|
|
356
459
|
---
|
|
357
460
|
|
|
358
461
|
This project is licensed under the
|