awilix 8.0.1 → 10.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 +165 -97
- package/lib/awilix.browser.js +171 -93
- package/lib/awilix.d.ts +6 -6
- package/lib/awilix.js +19 -20
- package/lib/awilix.js.map +1 -1
- package/lib/awilix.module.mjs +162 -97
- package/lib/awilix.umd.js +171 -96
- package/lib/container.d.ts +17 -15
- package/lib/container.js +56 -32
- package/lib/container.js.map +1 -1
- package/lib/errors.d.ts +13 -0
- package/lib/errors.js +27 -8
- package/lib/errors.js.map +1 -1
- package/lib/function-tokenizer.js +5 -3
- package/lib/function-tokenizer.js.map +1 -1
- package/lib/lifetime.d.ts +4 -0
- package/lib/lifetime.js +9 -1
- package/lib/lifetime.js.map +1 -1
- package/lib/list-modules.js.map +1 -1
- package/lib/load-module-native.js +2 -0
- package/lib/load-modules.d.ts +2 -2
- package/lib/load-modules.js +4 -3
- package/lib/load-modules.js.map +1 -1
- package/lib/param-parser.js +4 -2
- package/lib/param-parser.js.map +1 -1
- package/lib/resolvers.d.ts +17 -29
- package/lib/resolvers.js +19 -17
- package/lib/resolvers.js.map +1 -1
- package/lib/utils.d.ts +2 -2
- package/lib/utils.js +5 -3
- package/lib/utils.js.map +1 -1
- package/package.json +26 -25
package/README.md
CHANGED
|
@@ -8,10 +8,12 @@
|
|
|
8
8
|
[](https://www.npmjs.com/package/awilix)
|
|
9
9
|
[](http://standardjs.com/)
|
|
10
10
|
|
|
11
|
-
Extremely powerful **Dependency Injection** (DI) container for JavaScript/Node,
|
|
12
|
-
written in [TypeScript](http://typescriptlang.org).
|
|
11
|
+
Extremely powerful, performant, & battle-tested **Dependency Injection** (DI) container for JavaScript/Node,
|
|
12
|
+
written in [TypeScript](http://typescriptlang.org).
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Awilix enables you to write **composable, testable software** using dependency injection **without special annotations**, which in turn decouples your core application code from the intricacies of the DI mechanism.
|
|
15
|
+
|
|
16
|
+
> 💡 Check out this
|
|
15
17
|
> [intro to Dependency Injection with Awilix](https://medium.com/@Jeffijoe/dependency-injection-in-node-js-2016-edition-f2a88efdd427)
|
|
16
18
|
|
|
17
19
|
# Table of Contents
|
|
@@ -22,6 +24,7 @@ written in [TypeScript](http://typescriptlang.org). **Make IoC great again!**
|
|
|
22
24
|
- [Usage](#usage)
|
|
23
25
|
- [Lifetime management](#lifetime-management)
|
|
24
26
|
- [Scoped lifetime](#scoped-lifetime)
|
|
27
|
+
- [Strict mode](#strict-mode)
|
|
25
28
|
- [Injection modes](#injection-modes)
|
|
26
29
|
- [Auto-loading modules](#auto-loading-modules)
|
|
27
30
|
- [Per-module local injections](#per-module-local-injections)
|
|
@@ -37,6 +40,7 @@ written in [TypeScript](http://typescriptlang.org). **Make IoC great again!**
|
|
|
37
40
|
- [`aliasTo()`](#aliasto)
|
|
38
41
|
- [`listModules()`](#listmodules)
|
|
39
42
|
- [`AwilixResolutionError`](#awilixresolutionerror)
|
|
43
|
+
- [`AwilixRegistrationError`](#awilixregistrationerror)
|
|
40
44
|
- [The `AwilixContainer` object](#the-awilixcontainer-object)
|
|
41
45
|
- [`container.cradle`](#containercradle)
|
|
42
46
|
- [`container.registrations`](#containerregistrations)
|
|
@@ -50,7 +54,8 @@ written in [TypeScript](http://typescriptlang.org). **Make IoC great again!**
|
|
|
50
54
|
- [`container.build()`](#containerbuild)
|
|
51
55
|
- [`container.dispose()`](#containerdispose)
|
|
52
56
|
- [Universal Module (Browser Support)](#universal-module-browser-support)
|
|
53
|
-
|
|
57
|
+
- [Ecosystem](#ecosystem)
|
|
58
|
+
- [Contributing](#contributing)
|
|
54
59
|
- [What's in a name?](#whats-in-a-name)
|
|
55
60
|
- [Author](#author)
|
|
56
61
|
|
|
@@ -71,9 +76,9 @@ yarn add awilix
|
|
|
71
76
|
You can also use the [UMD](https://github.com/umdjs/umd) build from `unpkg`
|
|
72
77
|
|
|
73
78
|
```html
|
|
74
|
-
<script src="https://unpkg.com/awilix/lib/awilix.umd.js"/>
|
|
79
|
+
<script src="https://unpkg.com/awilix/lib/awilix.umd.js" />
|
|
75
80
|
<script>
|
|
76
|
-
const container = Awilix.createContainer()
|
|
81
|
+
const container = Awilix.createContainer()
|
|
77
82
|
</script>
|
|
78
83
|
```
|
|
79
84
|
|
|
@@ -92,8 +97,10 @@ minimum, you need to do 3 things:
|
|
|
92
97
|
const awilix = require('awilix')
|
|
93
98
|
|
|
94
99
|
// Create the container and set the injectionMode to PROXY (which is also the default).
|
|
100
|
+
// Enable strict mode for extra correctness checks (highly recommended).
|
|
95
101
|
const container = awilix.createContainer({
|
|
96
|
-
injectionMode: awilix.InjectionMode.PROXY
|
|
102
|
+
injectionMode: awilix.InjectionMode.PROXY,
|
|
103
|
+
strict: true,
|
|
97
104
|
})
|
|
98
105
|
|
|
99
106
|
// This is our app code... We can use
|
|
@@ -115,7 +122,7 @@ class UserController {
|
|
|
115
122
|
container.register({
|
|
116
123
|
// Here we are telling Awilix how to resolve a
|
|
117
124
|
// userController: by instantiating a class.
|
|
118
|
-
userController: awilix.asClass(UserController)
|
|
125
|
+
userController: awilix.asClass(UserController),
|
|
119
126
|
})
|
|
120
127
|
|
|
121
128
|
// Let's try with a factory function.
|
|
@@ -123,16 +130,16 @@ const makeUserService = ({ db }) => {
|
|
|
123
130
|
// Notice how we can use destructuring
|
|
124
131
|
// to access dependencies
|
|
125
132
|
return {
|
|
126
|
-
getUser: id => {
|
|
133
|
+
getUser: (id) => {
|
|
127
134
|
return db.query(`select * from users where id=${id}`)
|
|
128
|
-
}
|
|
135
|
+
},
|
|
129
136
|
}
|
|
130
137
|
}
|
|
131
138
|
|
|
132
139
|
container.register({
|
|
133
140
|
// the `userService` is resolved by
|
|
134
141
|
// invoking the function.
|
|
135
|
-
userService: awilix.asFunction(makeUserService)
|
|
142
|
+
userService: awilix.asFunction(makeUserService),
|
|
136
143
|
})
|
|
137
144
|
|
|
138
145
|
// Alright, now we need a database.
|
|
@@ -146,7 +153,7 @@ function Database(connectionString, timeout) {
|
|
|
146
153
|
this.conn = connectToYourDatabaseSomehow(connectionString, timeout)
|
|
147
154
|
}
|
|
148
155
|
|
|
149
|
-
Database.prototype.query = function(sql) {
|
|
156
|
+
Database.prototype.query = function (sql) {
|
|
150
157
|
// blah....
|
|
151
158
|
return this.conn.rawSql(sql)
|
|
152
159
|
}
|
|
@@ -156,7 +163,7 @@ Database.prototype.query = function(sql) {
|
|
|
156
163
|
// We also want to use `CLASSIC` injection mode for this
|
|
157
164
|
// registration. Read more about injection modes below.
|
|
158
165
|
container.register({
|
|
159
|
-
db: awilix.asClass(Database).classic()
|
|
166
|
+
db: awilix.asClass(Database).classic(),
|
|
160
167
|
})
|
|
161
168
|
|
|
162
169
|
// Lastly we register the connection string and timeout values
|
|
@@ -166,7 +173,7 @@ container.register({
|
|
|
166
173
|
// limited to strings and numbers, it can be anything,
|
|
167
174
|
// really - they will be passed through directly.
|
|
168
175
|
connectionString: awilix.asValue(process.env.CONN_STR),
|
|
169
|
-
timeout: awilix.asValue(1000)
|
|
176
|
+
timeout: awilix.asValue(1000),
|
|
170
177
|
})
|
|
171
178
|
|
|
172
179
|
// We have now wired everything up!
|
|
@@ -216,17 +223,17 @@ const { asClass, asFunction, asValue } = awilix
|
|
|
216
223
|
class MailService {}
|
|
217
224
|
|
|
218
225
|
container.register({
|
|
219
|
-
mailService: asClass(MailService, { lifetime: Lifetime.SINGLETON })
|
|
226
|
+
mailService: asClass(MailService, { lifetime: Lifetime.SINGLETON }),
|
|
220
227
|
})
|
|
221
228
|
|
|
222
229
|
// or using the chaining configuration API..
|
|
223
230
|
container.register({
|
|
224
|
-
mailService: asClass(MailService).setLifetime(Lifetime.SINGLETON)
|
|
231
|
+
mailService: asClass(MailService).setLifetime(Lifetime.SINGLETON),
|
|
225
232
|
})
|
|
226
233
|
|
|
227
234
|
// or..
|
|
228
235
|
container.register({
|
|
229
|
-
mailService: asClass(MailService).singleton()
|
|
236
|
+
mailService: asClass(MailService).singleton(),
|
|
230
237
|
})
|
|
231
238
|
|
|
232
239
|
// or.......
|
|
@@ -257,7 +264,7 @@ class MessageService {
|
|
|
257
264
|
}
|
|
258
265
|
|
|
259
266
|
container.register({
|
|
260
|
-
messageService: asClass(MessageService).scoped()
|
|
267
|
+
messageService: asClass(MessageService).scoped(),
|
|
261
268
|
})
|
|
262
269
|
|
|
263
270
|
// imagine middleware in some web framework..
|
|
@@ -267,7 +274,7 @@ app.use((req, res, next) => {
|
|
|
267
274
|
|
|
268
275
|
// register some request-specific data..
|
|
269
276
|
req.scope.register({
|
|
270
|
-
currentUser: asValue(req.user)
|
|
277
|
+
currentUser: asValue(req.user),
|
|
271
278
|
})
|
|
272
279
|
|
|
273
280
|
next()
|
|
@@ -276,7 +283,7 @@ app.use((req, res, next) => {
|
|
|
276
283
|
app.get('/messages', (req, res) => {
|
|
277
284
|
// for each request we get a new message service!
|
|
278
285
|
const messageService = req.scope.resolve('messageService')
|
|
279
|
-
messageService.getMessages().then(messages => {
|
|
286
|
+
messageService.getMessages().then((messages) => {
|
|
280
287
|
res.send(200, messages)
|
|
281
288
|
})
|
|
282
289
|
})
|
|
@@ -286,18 +293,26 @@ app.get('/messages', (req, res) => {
|
|
|
286
293
|
```
|
|
287
294
|
|
|
288
295
|
**IMPORTANT!** If a singleton is resolved, and it depends on a scoped or
|
|
289
|
-
transient registration, those will remain in the singleton for
|
|
296
|
+
transient registration, those will remain in the singleton for its lifetime!
|
|
297
|
+
Similarly, if a scoped module is resolved, and it depends on a transient
|
|
298
|
+
registration, that remains in the scoped module for its lifetime.
|
|
299
|
+
In the example above, if `messageService` was a singleton, it would be cached
|
|
300
|
+
in the root container, and would always have the `currentUser` from the first
|
|
301
|
+
request. Modules should generally not have a longer lifetime than their
|
|
302
|
+
dependencies, as this can cause issues of stale data.
|
|
290
303
|
|
|
291
304
|
```js
|
|
292
|
-
const makePrintTime =
|
|
293
|
-
|
|
294
|
-
|
|
305
|
+
const makePrintTime =
|
|
306
|
+
({ time }) =>
|
|
307
|
+
() => {
|
|
308
|
+
console.log('Time:', time)
|
|
309
|
+
}
|
|
295
310
|
|
|
296
311
|
const getTime = () => new Date().toString()
|
|
297
312
|
|
|
298
313
|
container.register({
|
|
299
314
|
printTime: asFunction(makePrintTime).singleton(),
|
|
300
|
-
time: asFunction(getTime).transient()
|
|
315
|
+
time: asFunction(getTime).transient(),
|
|
301
316
|
})
|
|
302
317
|
|
|
303
318
|
// Resolving `time` 2 times will
|
|
@@ -312,9 +327,34 @@ container.resolve('printTime')()
|
|
|
312
327
|
container.resolve('printTime')()
|
|
313
328
|
```
|
|
314
329
|
|
|
330
|
+
If you want a mismatched configuration like this to error, set
|
|
331
|
+
`strict` in the container options. This will trigger
|
|
332
|
+
the following error at runtime when the singleton `printTime` is resolved:
|
|
333
|
+
`AwilixResolutionError: Could not resolve 'time'. Dependency 'time' has a shorter lifetime than its ancestor: 'printTime'`
|
|
334
|
+
|
|
335
|
+
In addition, registering a singleton on a scope other than the root container results in
|
|
336
|
+
unpredictable behavior. In particular, if two different singletons are registered on two different
|
|
337
|
+
scopes, they will share a cache entry and collide with each other. To throw a runtime error when a
|
|
338
|
+
singleton is registered on a scope other than the root container, enable [strict mode](#strict-mode).
|
|
339
|
+
|
|
315
340
|
Read the documentation for [`container.createScope()`](#containercreatescope)
|
|
316
341
|
for more examples.
|
|
317
342
|
|
|
343
|
+
# Strict mode
|
|
344
|
+
|
|
345
|
+
Strict mode is a new feature in Awilix 10. It enables additional correctness checks that can help
|
|
346
|
+
you catch bugs early.
|
|
347
|
+
|
|
348
|
+
In particular, strict mode enables the following checks:
|
|
349
|
+
|
|
350
|
+
- When a singleton or scoped registration depends on a transient non-value registration, an error is
|
|
351
|
+
thrown. This detects and prevents the issue where a shorter lifetime dependency can leak outside
|
|
352
|
+
its intended lifetime due to its preservation in a longer lifetime module.
|
|
353
|
+
- Singleton registrations on any scopes are disabled. This prevents the issue where a singleton is
|
|
354
|
+
registered on a scope other than the root container, which results in unpredictable behavior.
|
|
355
|
+
- Singleton resolution is performed using registrations from the root container only, which prevents
|
|
356
|
+
potential leaks in which scoped registrations are preserved in singletons.
|
|
357
|
+
|
|
318
358
|
# Injection modes
|
|
319
359
|
|
|
320
360
|
The injection mode determines how a function/constructor receives its
|
|
@@ -348,11 +388,11 @@ modes are available on `awilix.InjectionMode`
|
|
|
348
388
|
```
|
|
349
389
|
|
|
350
390
|
- `InjectionMode.CLASSIC`: Parses the function/constructor parameters, and
|
|
351
|
-
matches them with registrations in the container. `CLASSIC` mode has a
|
|
352
|
-
slightly higher initialization cost as it has to parse the function/class
|
|
353
|
-
to figure out the dependencies at the time of registration, however resolving
|
|
354
|
-
them will be **much faster** than when using `PROXY`. _Don't use `CLASSIC` if
|
|
355
|
-
you minify your code!_ We recommend using `CLASSIC` in Node and `PROXY` in
|
|
391
|
+
matches them with registrations in the container. `CLASSIC` mode has a
|
|
392
|
+
slightly higher initialization cost as it has to parse the function/class
|
|
393
|
+
to figure out the dependencies at the time of registration, however resolving
|
|
394
|
+
them will be **much faster** than when using `PROXY`. _Don't use `CLASSIC` if
|
|
395
|
+
you minify your code!_ We recommend using `CLASSIC` in Node and `PROXY` in
|
|
356
396
|
environments where minification is needed.
|
|
357
397
|
|
|
358
398
|
```js
|
|
@@ -420,8 +460,8 @@ container.register({
|
|
|
420
460
|
const container = createContainer()
|
|
421
461
|
container.loadModules(['services/**/*.js', 'repositories/**/*.js'], {
|
|
422
462
|
resolverOptions: {
|
|
423
|
-
injectionMode: InjectionMode.CLASSIC
|
|
424
|
-
}
|
|
463
|
+
injectionMode: InjectionMode.CLASSIC,
|
|
464
|
+
},
|
|
425
465
|
})
|
|
426
466
|
```
|
|
427
467
|
|
|
@@ -461,7 +501,7 @@ function database({ connectionString, timeout, logger }) {
|
|
|
461
501
|
const db = database({
|
|
462
502
|
logger: new LoggerMock(),
|
|
463
503
|
timeout: 4000,
|
|
464
|
-
connectionString: 'localhost:1337;user=123...'
|
|
504
|
+
connectionString: 'localhost:1337;user=123...',
|
|
465
505
|
})
|
|
466
506
|
```
|
|
467
507
|
|
|
@@ -518,34 +558,37 @@ const awilix = require('awilix')
|
|
|
518
558
|
const container = awilix.createContainer()
|
|
519
559
|
|
|
520
560
|
// Load our modules!
|
|
521
|
-
container.loadModules(
|
|
522
|
-
// Globs!
|
|
561
|
+
container.loadModules(
|
|
523
562
|
[
|
|
524
|
-
//
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
563
|
+
// Globs!
|
|
564
|
+
[
|
|
565
|
+
// To have different resolverOptions for specific modules.
|
|
566
|
+
'models/**/*.js',
|
|
567
|
+
{
|
|
568
|
+
register: awilix.asValue,
|
|
569
|
+
lifetime: Lifetime.SINGLETON,
|
|
570
|
+
},
|
|
571
|
+
],
|
|
572
|
+
'services/**/*.js',
|
|
573
|
+
'repositories/**/*.js',
|
|
530
574
|
],
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
}
|
|
575
|
+
{
|
|
576
|
+
// We want to register `UserService` as `userService` -
|
|
577
|
+
// by default loaded modules are registered with the
|
|
578
|
+
// name of the file (minus the extension)
|
|
579
|
+
formatName: 'camelCase',
|
|
580
|
+
// Apply resolver options to all modules.
|
|
581
|
+
resolverOptions: {
|
|
582
|
+
// We can give these auto-loaded modules
|
|
583
|
+
// the deal of a lifetime! (see what I did there?)
|
|
584
|
+
// By default it's `TRANSIENT`.
|
|
585
|
+
lifetime: Lifetime.SINGLETON,
|
|
586
|
+
// We can tell Awilix what to register everything as,
|
|
587
|
+
// instead of guessing. If omitted, will inspect the
|
|
588
|
+
// module to determine what to register as.
|
|
589
|
+
register: awilix.asClass,
|
|
590
|
+
},
|
|
591
|
+
},
|
|
549
592
|
)
|
|
550
593
|
|
|
551
594
|
// We are now ready! We now have a userService, userRepository and emailService!
|
|
@@ -572,10 +615,10 @@ export default function userRepository({ db, timeout }) {
|
|
|
572
615
|
return Promise.race([
|
|
573
616
|
db.query('select * from users'),
|
|
574
617
|
Promise.delay(timeout).then(() =>
|
|
575
|
-
Promise.reject(new Error('Timed out'))
|
|
576
|
-
)
|
|
618
|
+
Promise.reject(new Error('Timed out')),
|
|
619
|
+
),
|
|
577
620
|
])
|
|
578
|
-
}
|
|
621
|
+
},
|
|
579
622
|
}
|
|
580
623
|
}
|
|
581
624
|
```
|
|
@@ -594,22 +637,22 @@ const container = createContainer()
|
|
|
594
637
|
// Provide an injection function that returns an object with locals.
|
|
595
638
|
// The function is called once per resolve of the registration
|
|
596
639
|
// it is attached to.
|
|
597
|
-
.inject(() => ({ timeout: 2000 }))
|
|
640
|
+
.inject(() => ({ timeout: 2000 })),
|
|
598
641
|
})
|
|
599
642
|
|
|
600
643
|
// Shorthand variants
|
|
601
644
|
.register({
|
|
602
645
|
userRepository: asFunction(createUserRepository, {
|
|
603
|
-
injector: () => ({ timeout: 2000 })
|
|
604
|
-
})
|
|
646
|
+
injector: () => ({ timeout: 2000 }),
|
|
647
|
+
}),
|
|
605
648
|
})
|
|
606
649
|
|
|
607
650
|
// Stringly-typed shorthand
|
|
608
651
|
.register(
|
|
609
652
|
'userRepository',
|
|
610
653
|
asFunction(createUserRepository, {
|
|
611
|
-
injector: () => ({ timeout: 2000 })
|
|
612
|
-
})
|
|
654
|
+
injector: () => ({ timeout: 2000 }),
|
|
655
|
+
}),
|
|
613
656
|
)
|
|
614
657
|
|
|
615
658
|
// with `loadModules`
|
|
@@ -642,7 +685,7 @@ export default class AwesomeService {
|
|
|
642
685
|
// `RESOLVER` is a Symbol.
|
|
643
686
|
AwesomeService[RESOLVER] = {
|
|
644
687
|
lifetime: Lifetime.SCOPED,
|
|
645
|
-
injectionMode: InjectionMode.CLASSIC
|
|
688
|
+
injectionMode: InjectionMode.CLASSIC,
|
|
646
689
|
}
|
|
647
690
|
```
|
|
648
691
|
|
|
@@ -653,7 +696,7 @@ import { createContainer, asClass } from 'awilix'
|
|
|
653
696
|
import AwesomeService from './services/awesome-service.js'
|
|
654
697
|
|
|
655
698
|
const container = createContainer().register({
|
|
656
|
-
awesomeService: asClass(AwesomeService)
|
|
699
|
+
awesomeService: asClass(AwesomeService),
|
|
657
700
|
})
|
|
658
701
|
|
|
659
702
|
console.log(container.registrations.awesomeService.lifetime) // 'SCOPED'
|
|
@@ -717,7 +760,7 @@ function configureContainer() {
|
|
|
717
760
|
.singleton()
|
|
718
761
|
// This is called when the pool is going to be disposed.
|
|
719
762
|
// If it returns a Promise, it will be awaited by `dispose`.
|
|
720
|
-
.disposer(pool => pool.end())
|
|
763
|
+
.disposer((pool) => pool.end()),
|
|
721
764
|
})
|
|
722
765
|
}
|
|
723
766
|
|
|
@@ -793,18 +836,20 @@ pass in an object with the following props:
|
|
|
793
836
|
[Per-module local injections](#per-module-local-injections)
|
|
794
837
|
- `register`: Only used in `loadModules`, determines how to register a loaded
|
|
795
838
|
module explicitly
|
|
839
|
+
- `isLeakSafe`: true if this resolver should be excluded from lifetime-leak checking performed in
|
|
840
|
+
[strict mode](#strict-mode). Defaults to false.
|
|
796
841
|
|
|
797
842
|
**Examples of usage:**
|
|
798
843
|
|
|
799
844
|
```js
|
|
800
845
|
container.register({
|
|
801
|
-
stuff: asClass(MyClass, { injectionMode: InjectionMode.CLASSIC })
|
|
846
|
+
stuff: asClass(MyClass, { injectionMode: InjectionMode.CLASSIC }),
|
|
802
847
|
})
|
|
803
848
|
|
|
804
849
|
container.loadModules([['some/path/to/*.js', { register: asClass }]], {
|
|
805
850
|
resolverOptions: {
|
|
806
|
-
lifetime: Lifetime.SCOPED
|
|
807
|
-
}
|
|
851
|
+
lifetime: Lifetime.SCOPED,
|
|
852
|
+
},
|
|
808
853
|
})
|
|
809
854
|
```
|
|
810
855
|
|
|
@@ -827,6 +872,7 @@ Args:
|
|
|
827
872
|
**_must_** be named exactly like they are in the registration. For
|
|
828
873
|
example, a dependency registered as `repository` cannot be referenced in a
|
|
829
874
|
class constructor as `repo`.
|
|
875
|
+
- `options.strict`: Enables [strict mode](#strict-mode). Defaults to `false`.
|
|
830
876
|
|
|
831
877
|
## `asFunction()`
|
|
832
878
|
|
|
@@ -866,7 +912,7 @@ Resolves the dependency specified.
|
|
|
866
912
|
```js
|
|
867
913
|
container.register({
|
|
868
914
|
val: asValue(123),
|
|
869
|
-
aliasVal: aliasTo('val')
|
|
915
|
+
aliasVal: aliasTo('val'),
|
|
870
916
|
})
|
|
871
917
|
|
|
872
918
|
container.resolve('aliasVal') === container.resolve('val')
|
|
@@ -910,6 +956,11 @@ This is a special error thrown when Awilix is unable to resolve all dependencies
|
|
|
910
956
|
`err instanceof AwilixResolutionError` if you wish. It will tell you what
|
|
911
957
|
dependencies it could not find or which ones caused a cycle.
|
|
912
958
|
|
|
959
|
+
## `AwilixRegistrationError`
|
|
960
|
+
|
|
961
|
+
This is a special error thrown when Awilix is unable to register a dependency due to a strict mode
|
|
962
|
+
violation. You can catch this error and use `err instanceof AwilixRegistrationError` if you wish.
|
|
963
|
+
|
|
913
964
|
## The `AwilixContainer` object
|
|
914
965
|
|
|
915
966
|
The container returned from `createContainer` has some methods and properties.
|
|
@@ -939,7 +990,7 @@ Each scope has it's own cache, and checks the cache of it's ancestors.
|
|
|
939
990
|
```js
|
|
940
991
|
let counter = 1
|
|
941
992
|
container.register({
|
|
942
|
-
count: asFunction(() => counter++).singleton()
|
|
993
|
+
count: asFunction(() => counter++).singleton(),
|
|
943
994
|
})
|
|
944
995
|
|
|
945
996
|
container.cradle.count === 1
|
|
@@ -955,7 +1006,7 @@ Options passed to `createContainer` are stored here.
|
|
|
955
1006
|
|
|
956
1007
|
```js
|
|
957
1008
|
const container = createContainer({
|
|
958
|
-
injectionMode: InjectionMode.CLASSIC
|
|
1009
|
+
injectionMode: InjectionMode.CLASSIC,
|
|
959
1010
|
})
|
|
960
1011
|
|
|
961
1012
|
console.log(container.options.injectionMode) // 'CLASSIC'
|
|
@@ -971,7 +1022,7 @@ Resolves the registration with the given name. Used by the cradle.
|
|
|
971
1022
|
|
|
972
1023
|
```js
|
|
973
1024
|
container.register({
|
|
974
|
-
leet: asFunction(() => 1337)
|
|
1025
|
+
leet: asFunction(() => 1337),
|
|
975
1026
|
})
|
|
976
1027
|
|
|
977
1028
|
container.resolve('leet') === 1337
|
|
@@ -1019,14 +1070,14 @@ container.register('context', asClass(SessionContext))
|
|
|
1019
1070
|
container.register({
|
|
1020
1071
|
connectionString: asValue('localhost:1433;user=...'),
|
|
1021
1072
|
mailService: asFunction(makeMailService, { lifetime: Lifetime.SINGLETON }),
|
|
1022
|
-
context: asClass(SessionContext, { lifetime: Lifetime.SCOPED })
|
|
1073
|
+
context: asClass(SessionContext, { lifetime: Lifetime.SCOPED }),
|
|
1023
1074
|
})
|
|
1024
1075
|
|
|
1025
1076
|
// `asClass` and `asFunction` also supports a fluid syntax.
|
|
1026
1077
|
// This...
|
|
1027
1078
|
container.register(
|
|
1028
1079
|
'mailService',
|
|
1029
|
-
asFunction(makeMailService).setLifetime(Lifetime.SINGLETON)
|
|
1080
|
+
asFunction(makeMailService).setLifetime(Lifetime.SINGLETON),
|
|
1030
1081
|
)
|
|
1031
1082
|
// .. is the same as this:
|
|
1032
1083
|
container.register('context', asClass(SessionContext).singleton())
|
|
@@ -1067,9 +1118,9 @@ Args:
|
|
|
1067
1118
|
pass the name through as-is. The 2nd parameter is a full module descriptor.
|
|
1068
1119
|
- `opts.resolverOptions`: An `object` passed to the resolvers. Used to configure
|
|
1069
1120
|
the lifetime, injection mode and more of the loaded modules.
|
|
1070
|
-
- `opts.esModules`: Loads modules using Node's native ES modules.
|
|
1071
|
-
**This makes `container.loadModules` asynchronous, and will therefore return a `Promise`!**
|
|
1072
|
-
This is only
|
|
1121
|
+
- `opts.esModules`: Loads modules using Node's native ES modules.
|
|
1122
|
+
**This makes `container.loadModules` asynchronous, and will therefore return a `Promise`!**
|
|
1123
|
+
This is only supported on Node 14.0+ and should only be used if you're using
|
|
1073
1124
|
the [Native Node ES modules](https://nodejs.org/api/esm.html)
|
|
1074
1125
|
|
|
1075
1126
|
Example:
|
|
@@ -1146,7 +1197,7 @@ inside a scope. A scope is basically a "child" container.
|
|
|
1146
1197
|
// Increments the counter every time it is resolved.
|
|
1147
1198
|
let counter = 1
|
|
1148
1199
|
container.register({
|
|
1149
|
-
counterValue: asFunction(() => counter++).scoped()
|
|
1200
|
+
counterValue: asFunction(() => counter++).scoped(),
|
|
1150
1201
|
})
|
|
1151
1202
|
const scope1 = container.createScope()
|
|
1152
1203
|
const scope2 = container.createScope()
|
|
@@ -1166,7 +1217,7 @@ A _Scope_ maintains it's own cache of `Lifetime.SCOPED` registrations, meaning i
|
|
|
1166
1217
|
```js
|
|
1167
1218
|
let counter = 1
|
|
1168
1219
|
container.register({
|
|
1169
|
-
counterValue: asFunction(() => counter++).scoped()
|
|
1220
|
+
counterValue: asFunction(() => counter++).scoped(),
|
|
1170
1221
|
})
|
|
1171
1222
|
const scope1 = container.createScope()
|
|
1172
1223
|
const scope2 = container.createScope()
|
|
@@ -1192,13 +1243,13 @@ that scope and it's children.
|
|
|
1192
1243
|
// that returns the value of the scope-provided dependency.
|
|
1193
1244
|
// For this example we could also use scoped lifetime.
|
|
1194
1245
|
container.register({
|
|
1195
|
-
scopedValue: asFunction(cradle => 'Hello ' + cradle.someValue)
|
|
1246
|
+
scopedValue: asFunction((cradle) => 'Hello ' + cradle.someValue),
|
|
1196
1247
|
})
|
|
1197
1248
|
|
|
1198
1249
|
// Create a scope and register a value.
|
|
1199
1250
|
const scope = container.createScope()
|
|
1200
1251
|
scope.register({
|
|
1201
|
-
someValue: asValue('scope')
|
|
1252
|
+
someValue: asValue('scope'),
|
|
1202
1253
|
})
|
|
1203
1254
|
|
|
1204
1255
|
scope.cradle.scopedValue === 'Hello scope'
|
|
@@ -1208,27 +1259,36 @@ container.cradle.someValue
|
|
|
1208
1259
|
// of the resolver.
|
|
1209
1260
|
```
|
|
1210
1261
|
|
|
1211
|
-
Things registered in the scope take precedence over
|
|
1262
|
+
Things registered in the scope take precedence over registrations in the parent scope(s). This
|
|
1263
|
+
applies to both the registration directly requested from the scope container, and any dependencies
|
|
1264
|
+
that the registration uses.
|
|
1212
1265
|
|
|
1213
1266
|
```js
|
|
1214
1267
|
// It does not matter when the scope is created,
|
|
1215
1268
|
// it will still have anything that is registered
|
|
1216
|
-
// in
|
|
1269
|
+
// in its parent.
|
|
1217
1270
|
const scope = container.createScope()
|
|
1218
1271
|
|
|
1219
1272
|
container.register({
|
|
1220
1273
|
value: asValue('root'),
|
|
1221
|
-
usedValue: asFunction(cradle => cradle.value)
|
|
1274
|
+
usedValue: asFunction((cradle) => `hello from ${cradle.value}`),
|
|
1222
1275
|
})
|
|
1223
1276
|
|
|
1224
1277
|
scope.register({
|
|
1225
|
-
value: asValue('scope')
|
|
1278
|
+
value: asValue('scope'),
|
|
1226
1279
|
})
|
|
1227
1280
|
|
|
1228
|
-
container.cradle.
|
|
1229
|
-
scope.cradle.
|
|
1281
|
+
container.cradle.value === 'root'
|
|
1282
|
+
scope.cradle.value === 'scope'
|
|
1283
|
+
container.cradle.usedValue === 'hello from root'
|
|
1284
|
+
scope.cradle.usedValue === 'hello from scope'
|
|
1230
1285
|
```
|
|
1231
1286
|
|
|
1287
|
+
Registering singletons in a scope results in unpredictable behavior and should be avoided. Having
|
|
1288
|
+
more than one singleton with the same name in different scopes will result in them sharing a cache
|
|
1289
|
+
entry and colliding with each other. To disallow such registrations, enable
|
|
1290
|
+
[strict mode](#strict-mode) in the container options.
|
|
1291
|
+
|
|
1232
1292
|
### `container.build()`
|
|
1233
1293
|
|
|
1234
1294
|
Builds an instance of a class (or a function) by injecting dependencies, but
|
|
@@ -1263,11 +1323,11 @@ class MyClass {
|
|
|
1263
1323
|
}
|
|
1264
1324
|
|
|
1265
1325
|
const createMyFunc = ({ ping }) => ({
|
|
1266
|
-
pong: () => ping
|
|
1326
|
+
pong: () => ping,
|
|
1267
1327
|
})
|
|
1268
1328
|
|
|
1269
1329
|
container.register({
|
|
1270
|
-
ping: asValue('pong')
|
|
1330
|
+
ping: asValue('pong'),
|
|
1271
1331
|
})
|
|
1272
1332
|
|
|
1273
1333
|
// Shorthand
|
|
@@ -1301,9 +1361,9 @@ const pg = require('pg')
|
|
|
1301
1361
|
|
|
1302
1362
|
container.register({
|
|
1303
1363
|
pool: asFunction(() => new pg.Pool())
|
|
1304
|
-
.disposer(pool => pool.end())
|
|
1364
|
+
.disposer((pool) => pool.end())
|
|
1305
1365
|
// IMPORTANT! Must be either singleton or scoped!
|
|
1306
|
-
.singleton()
|
|
1366
|
+
.singleton(),
|
|
1307
1367
|
})
|
|
1308
1368
|
|
|
1309
1369
|
const pool = container.resolve('pool')
|
|
@@ -1343,7 +1403,15 @@ because they depend on Node-specific packages.
|
|
|
1343
1403
|
- Safari >= 10
|
|
1344
1404
|
- Internet Explorer is not supported
|
|
1345
1405
|
|
|
1346
|
-
|
|
1406
|
+
# Ecosystem
|
|
1407
|
+
|
|
1408
|
+
- [`awilix-manager`](https://github.com/kibertoad/awilix-manager): Wrapper that allows eager injection, asynchronous init methods and dependency lookup by tags.
|
|
1409
|
+
- [`awilix-express`](https://github.com/jeffijoe/awilix-express): Bindings for the Express HTTP library.
|
|
1410
|
+
- [`awilix-koa`](https://github.com/jeffijoe/awilix-koa): Bindings for the Koa HTTP library.
|
|
1411
|
+
- [`awilix-router-core`](https://github.com/jeffijoe/awilix-router-core): Library for building HTTP bindings for Awilix with routing.
|
|
1412
|
+
- [`fastify-awilix`](https://github.com/fastify/fastify-awilix): Bindings for the Fastify framework.
|
|
1413
|
+
|
|
1414
|
+
# Contributing
|
|
1347
1415
|
|
|
1348
1416
|
Please see our [contributing.md](./CONTRIBUTING.md)
|
|
1349
1417
|
|