@adimm/x-injection 1.2.1 → 2.0.3
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 +459 -270
- package/dist/index.cjs +672 -529
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +534 -640
- package/dist/index.d.ts +534 -640
- package/dist/index.js +635 -491
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<h1 align="center">
|
|
2
2
|
xInjection <a href="https://www.npmjs.com/package/@adimm/x-injection" target="__blank"><img src="https://badgen.net/npm/v/@adimm/x-injection"></a>
|
|
3
|
-
<img src="https://badgen.net/npm/license/@adimm/x-injection">
|
|
4
3
|
<a href="https://app.codecov.io/gh/AdiMarianMutu/x-injection" target="__blank"><img src="https://badgen.net/codecov/c/github/AdiMarianMutu/x-injection"></a>
|
|
4
|
+
<img src="https://badgen.net/npm/license/@adimm/x-injection">
|
|
5
5
|
</h1>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
@@ -20,36 +20,47 @@ xInjection <a href="https://www.npmjs.com/package/@adimm/x-injection" targe
|
|
|
20
20
|
- [Installation](#installation)
|
|
21
21
|
- [TypeScript Configuration](#typescript-configuration)
|
|
22
22
|
- [Getting Started](#getting-started)
|
|
23
|
-
- [
|
|
24
|
-
- [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
- [
|
|
28
|
-
- [
|
|
29
|
-
|
|
23
|
+
- [Quick Start](#quick-start)
|
|
24
|
+
- [Glossary](#glossary)
|
|
25
|
+
- [ProviderModule](#providermodule)
|
|
26
|
+
- [AppModule](#appmodule)
|
|
27
|
+
- [Blueprint](#blueprint)
|
|
28
|
+
- [Definition](#definition)
|
|
29
|
+
- [Conventions](#conventions)
|
|
30
|
+
- [ProviderModule](#providermodule-1)
|
|
31
|
+
- [Blueprints](#blueprints)
|
|
32
|
+
- [ProviderToken](#providertoken)
|
|
33
|
+
- [AppModule](#appmodule-1)
|
|
34
|
+
- [ProviderModule API](#providermodule-api)
|
|
35
|
+
- [Injection Scope](#injection-scope)
|
|
36
|
+
- [Singleton](#singleton)
|
|
37
|
+
- [Transient](#transient)
|
|
38
|
+
- [Request](#request)
|
|
39
|
+
- [Provider Tokens](#provider-tokens)
|
|
30
40
|
- [Provider Modules](#provider-modules)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- [Lazy `imports` and `exports`](#lazy-imports-and-exports)
|
|
35
|
-
- [Imports](#imports)
|
|
36
|
-
- [Exports](#exports)
|
|
41
|
+
- [Blueprints](#blueprints-1)
|
|
42
|
+
- [Import Behavior](#import-behavior)
|
|
43
|
+
- [isGlobal](#isglobal)
|
|
37
44
|
- [Advanced Usage](#advanced-usage)
|
|
38
|
-
- [
|
|
39
|
-
- [
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
- [Events](#events)
|
|
46
|
+
- [Middlewares](#middlewares)
|
|
47
|
+
- [Internals](#internals)
|
|
48
|
+
- [ProviderModule](#providermodule-2)
|
|
49
|
+
- [MiddlewaresManager](#middlewaresmanager)
|
|
50
|
+
- [ModuleContainer](#modulecontainer)
|
|
51
|
+
- [ImportedModuleContainer](#importedmodulecontainer)
|
|
52
|
+
- [DynamicModuleDefinition](#dynamicmoduledefinition)
|
|
53
|
+
- [ProviderModuleBlueprint](#providermoduleblueprint)
|
|
54
|
+
- [Set of Helpers](#set-of-helpers)
|
|
42
55
|
- [Unit Tests](#unit-tests)
|
|
43
56
|
- [Documentation](#documentation)
|
|
44
|
-
- [Conventions](#conventions)
|
|
45
|
-
- [ProviderModule](#providermodule)
|
|
46
|
-
- [ProviderModuleDefinition](#providermoduledefinition-1)
|
|
47
57
|
- [ReactJS Implementation](#reactjs-implementation)
|
|
48
58
|
- [Contributing](#contributing)
|
|
59
|
+
- [Credits](#credits)
|
|
49
60
|
|
|
50
61
|
## Overview
|
|
51
62
|
|
|
52
|
-
**xInjection** is a robust Inversion of Control [(IoC)](https://en.wikipedia.org/wiki/Inversion_of_control) library that extends [InversifyJS](https://github.com/inversify/InversifyJS) with a modular, [NestJS](https://github.com/nestjs/nest)-inspired Dependency Injection [(DI)](https://en.wikipedia.org/wiki/Dependency_injection) system. It enables you to **encapsulate** dependencies with fine-grained control using **[ProviderModule](https://adimarianmutu.github.io/x-injection/classes/
|
|
63
|
+
**xInjection** is a robust Inversion of Control [(IoC)](https://en.wikipedia.org/wiki/Inversion_of_control) library that extends [InversifyJS](https://github.com/inversify/InversifyJS) with a modular, [NestJS](https://github.com/nestjs/nest)-inspired Dependency Injection [(DI)](https://en.wikipedia.org/wiki/Dependency_injection) system. It enables you to **encapsulate** dependencies with fine-grained control using **[ProviderModule](https://adimarianmutu.github.io/x-injection/classes/IProviderModule.html)** classes, allowing for clean **separation** of concerns and **scalable** architecture.
|
|
53
64
|
|
|
54
65
|
Each `ProviderModule` manages its _own_ container, supporting easy **decoupling** and _explicit_ control over which providers are **exported** and **imported** across modules. The global **[AppModule](https://adimarianmutu.github.io/x-injection/variables/AppModule.html)** is always available, ensuring a seamless foundation for your application's DI needs.
|
|
55
66
|
|
|
@@ -58,8 +69,11 @@ Each `ProviderModule` manages its _own_ container, supporting easy **decoupling*
|
|
|
58
69
|
- **NestJS-inspired module system:** Import and export providers between modules.
|
|
59
70
|
- **Granular dependency encapsulation:** Each module manages its own container.
|
|
60
71
|
- **Flexible provider scopes:** [Singleton](https://adimarianmutu.github.io/x-injection/enums/InjectionScope.html#singleton), [Request](https://adimarianmutu.github.io/x-injection/enums/InjectionScope.html#request), and [Transient](https://adimarianmutu.github.io/x-injection/enums/InjectionScope.html#transient) lifecycles.
|
|
61
|
-
- **Lifecycle hooks:** [onReady](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#onready) and [onDispose](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#ondispose) for _module_ initialization and cleanup.
|
|
62
|
-
- **
|
|
72
|
+
- **Lifecycle hooks:** [onReady](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#onready), [onReset](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#onreset) and [onDispose](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#ondispose) for _module_ initialization and cleanup.
|
|
73
|
+
- **Middlewares:** Tap into the low-level implementation without any effort by just adding new `middlewares`.
|
|
74
|
+
- **Events:** Subscribe to internal events for maximum control.
|
|
75
|
+
- **Blueprints:** Plan ahead your `modules` without eagerly instantiating them.
|
|
76
|
+
- **Fully Agnostic:** It doesn't rely on any framework, just on [InversifyJS](https://inversify.io/) as it uses it under-the-hood to build the containers. It works the same both client side and server side.
|
|
63
77
|
|
|
64
78
|
## Installation
|
|
65
79
|
|
|
@@ -69,6 +83,10 @@ First, ensure you have [`reflect-metadata`](https://www.npmjs.com/package/reflec
|
|
|
69
83
|
npm i reflect-metadata
|
|
70
84
|
```
|
|
71
85
|
|
|
86
|
+
> [!NOTE]
|
|
87
|
+
>
|
|
88
|
+
> You may have to add `import 'reflect-metadata'` at the entry point of your application.
|
|
89
|
+
|
|
72
90
|
Then install `xInjection`:
|
|
73
91
|
|
|
74
92
|
```sh
|
|
@@ -90,113 +108,189 @@ Add the following options to your `tsconfig.json` to enable decorator metadata:
|
|
|
90
108
|
|
|
91
109
|
## Getting Started
|
|
92
110
|
|
|
93
|
-
###
|
|
111
|
+
### Quick Start
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { Injectable, ProviderModule } from '@adimm/x-injection';
|
|
115
|
+
|
|
116
|
+
@Injectable()
|
|
117
|
+
class HelloService {
|
|
118
|
+
sayHello() {
|
|
119
|
+
return 'Hello, world!';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const HelloModule = ProviderModule.create({
|
|
124
|
+
id: 'HelloModule',
|
|
125
|
+
providers: [HelloService],
|
|
126
|
+
exports: [HelloService],
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const helloService = HelloModule.get(HelloService);
|
|
130
|
+
|
|
131
|
+
console.log(helloService.sayHello());
|
|
132
|
+
// => 'Hello, world!'
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Glossary
|
|
94
136
|
|
|
95
|
-
|
|
137
|
+
#### ProviderModule
|
|
138
|
+
|
|
139
|
+
The core class of `xInjection`, if you ever worked with [NestJS](https://nestjs.com/) _(or [Angular](https://angular.dev/))_, you'll find it very familiar.
|
|
96
140
|
|
|
97
141
|
```ts
|
|
98
|
-
|
|
142
|
+
const GarageModule = ProviderModule.create({ id: 'GarageModule', imports: [], providers: [], exports: [] });
|
|
143
|
+
```
|
|
99
144
|
|
|
100
|
-
AppModule
|
|
145
|
+
#### AppModule
|
|
146
|
+
|
|
147
|
+
It is a special instance of the `ProviderModule` class which acts as the `root` of your `modules` graph, all _global_ modules will be automatically imported into the `AppModule` and shared across all your modules.
|
|
148
|
+
|
|
149
|
+
#### Blueprint
|
|
150
|
+
|
|
151
|
+
Another core class which most probably you'll end using a lot too, to keep it short, it allows you to plan ahead the `modules` without instantiating them.
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
const CarModuleBlueprint = ProviderModule.blueprint({ id: 'CarModule', imports: [], providers: [], exports: [] });
|
|
101
155
|
```
|
|
102
156
|
|
|
103
|
-
|
|
104
|
-
>
|
|
105
|
-
> _You must invoke `AppModule.register()` even if you have no global providers. Passing an empty object `{}` to the method is valid._
|
|
157
|
+
#### Definition
|
|
106
158
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
159
|
+
It is used to refer to the three main blocks of a module:
|
|
160
|
+
|
|
161
|
+
- [imports](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#imports)
|
|
162
|
+
- [providers](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#providers)
|
|
163
|
+
- [exports](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#exports)
|
|
110
164
|
|
|
111
|
-
###
|
|
165
|
+
### Conventions
|
|
112
166
|
|
|
113
|
-
|
|
167
|
+
The library has some opinionated _naming_ conventions which you should adopt too
|
|
168
|
+
|
|
169
|
+
#### ProviderModule
|
|
170
|
+
|
|
171
|
+
All variables holding an _instance_ of a `ProviderModule` should be written in [PascalCase](https://www.wikidata.org/wiki/Q9761807) and _suffixed_ with `Module`, like this:
|
|
114
172
|
|
|
115
173
|
```ts
|
|
116
|
-
|
|
174
|
+
const DatabaseModule = ProviderModule.create({...});
|
|
175
|
+
const UserModule = ProviderModule.create({...});
|
|
176
|
+
const CarPartsModule = ProviderModule.create({...});
|
|
177
|
+
```
|
|
117
178
|
|
|
118
|
-
|
|
119
|
-
class LoggerService {}
|
|
179
|
+
The `id` property of the `ProviderModule.options` should be the same as the `module` variable name.
|
|
120
180
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
181
|
+
```ts
|
|
182
|
+
const DatabaseModule = ProviderModule.create({ id: 'DatabaseModule' });
|
|
183
|
+
const UserModule = ProviderModule.create({ id: 'UserModule' });
|
|
184
|
+
const CarPartsModule = ProviderModule.create({ id: 'CarPartsModule' });
|
|
185
|
+
```
|
|
125
186
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
187
|
+
If you are exporting a `module` from a designated file, then you should name that file as following:
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
database.module.ts
|
|
191
|
+
user.module.ts
|
|
192
|
+
car-parts.module.ts
|
|
129
193
|
```
|
|
130
194
|
|
|
131
|
-
|
|
195
|
+
> [!TIP]
|
|
196
|
+
>
|
|
197
|
+
> If you install/use the [Material Icon Theme](https://marketplace.visualstudio.com/items?itemName=PKief.material-icon-theme) VS Code extension, you'll see the `*.module.ts` files with a specific icon.
|
|
132
198
|
|
|
133
|
-
|
|
199
|
+
#### Blueprints
|
|
134
200
|
|
|
135
|
-
|
|
201
|
+
All variables holding an _instance_ of a `ProviderModuleBlueprint` should be written in [PascalCase](https://www.wikidata.org/wiki/Q9761807) too and _suffixed_ with `ModuleBp`, like this:
|
|
136
202
|
|
|
137
203
|
```ts
|
|
138
|
-
const
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
identifier: 'ConfigModule',
|
|
143
|
-
isGlobal: true,
|
|
144
|
-
providers: [SECRET_TOKEN_PROVIDER, SECRET_TOKEN_2_PROVIDER],
|
|
145
|
-
exports: [SECRET_TOKEN_PROVIDER, SECRET_TOKEN_2_PROVIDER],
|
|
146
|
-
});
|
|
204
|
+
const DatabaseModuleBp = ProviderModule.blueprint({...});
|
|
205
|
+
const UserModuleBp = ProviderModule.blueprint({...});
|
|
206
|
+
const CarPartsModuleBp = ProviderModule.blueprint({...});
|
|
207
|
+
```
|
|
147
208
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
209
|
+
The `id` property of the `ProviderModuleBlueprint.options` should **not** end with `Bp` because when you'll import that `blueprint` into a `module`, the exact provided `id` will be used!
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
const DatabaseModuleBp = ProviderModule.create({ id: 'DatabaseModule' });
|
|
213
|
+
const UserModuleBp = ProviderModule.create({ id: 'UserModule' });
|
|
214
|
+
const CarPartsModuleBp = ProviderModule.create({ id: 'CarPartsModule' });
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
If you are exporting a `blueprint` from a designated file, then you should name that file as following:
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
database.module.bp.ts
|
|
221
|
+
user.module.bp.ts
|
|
222
|
+
car-parts.module.bp.ts
|
|
151
223
|
```
|
|
152
224
|
|
|
225
|
+
#### ProviderToken
|
|
226
|
+
|
|
227
|
+
All variables holding an _object_ representing a [ProviderToken](https://adimarianmutu.github.io/x-injection/types/ProviderToken.html) should be written in [SCREAMING_SNAKE_CASE](https://en.wikipedia.org/wiki/Snake_case) and _suffixed_ with `_PROVIDER`, like this:
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
const USER_SERVICE_PROVIDER = UserService;
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
If you are exporting a `provider token` from a designated file, then you should name that file as following:
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
user-service.provider.ts
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### AppModule
|
|
240
|
+
|
|
241
|
+
As explained above, it is the `root` module of your application, it is always available and eagerly bootstrapped.
|
|
242
|
+
|
|
243
|
+
Usually you'll not interact much with it as any `module` which is defined as _global_ will be automatically imported into it, therefore having its `exports` definition available across all your modules _out-of-the-box_. However, you can use it like any `ProviderModule` instance.
|
|
244
|
+
|
|
153
245
|
> [!WARNING]
|
|
154
246
|
>
|
|
155
|
-
>
|
|
247
|
+
> Importing the `AppModule` into any `module` will throw an error!
|
|
156
248
|
|
|
157
|
-
|
|
249
|
+
You have 2 options to access it:
|
|
158
250
|
|
|
159
251
|
```ts
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
isGlobal: true,
|
|
163
|
-
providers: [FancyService],
|
|
164
|
-
exports: [FancyService],
|
|
165
|
-
});
|
|
252
|
+
import { AppModule } from '@adimm/x-injection';
|
|
253
|
+
```
|
|
166
254
|
|
|
167
|
-
|
|
168
|
-
identifier: 'ScopedModule',
|
|
169
|
-
// This `ScopedModule` will automatically "forward" the global `FancyModuleDefinition`
|
|
170
|
-
// exports to the `AppModule`.
|
|
171
|
-
imports: [FancyModuleDefinition],
|
|
172
|
-
});
|
|
255
|
+
or
|
|
173
256
|
|
|
174
|
-
|
|
175
|
-
|
|
257
|
+
```ts
|
|
258
|
+
import { ProviderModule } from '@adimm/x-injection';
|
|
259
|
+
|
|
260
|
+
ProviderModule.APP_MODULE_REF;
|
|
261
|
+
|
|
262
|
+
// This option is mostly used internally, but you can 100% safely use it as well.
|
|
176
263
|
```
|
|
177
264
|
|
|
178
|
-
|
|
265
|
+
Providing global services to the `AppModule`:
|
|
179
266
|
|
|
180
267
|
```ts
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
isGlobal: true,
|
|
184
|
-
providers: [FancyService],
|
|
185
|
-
exports: [FancyService],
|
|
186
|
-
});
|
|
268
|
+
@Injectable()
|
|
269
|
+
class UserService {}
|
|
187
270
|
|
|
188
|
-
AppModule.
|
|
271
|
+
AppModule.update.addProvider(UserService);
|
|
189
272
|
```
|
|
190
273
|
|
|
191
274
|
> [!NOTE]
|
|
192
275
|
>
|
|
193
|
-
>
|
|
194
|
-
|
|
195
|
-
|
|
276
|
+
> All `providers` scope is set to [Singleton](https://adimarianmutu.github.io/x-injection/enums/InjectionScope.html#singleton) by default if not provided.
|
|
277
|
+
|
|
278
|
+
Yes, that's it, now you have access to the `UserService` anywhere in your app across your `modules`, meaning that you can now do:
|
|
196
279
|
|
|
197
|
-
|
|
280
|
+
```ts
|
|
281
|
+
const UnrelatedModule = ProviderModule.create({ id: 'UnrelatedModule' });
|
|
282
|
+
|
|
283
|
+
const userService = UnrelatedModule.get(UserService);
|
|
284
|
+
// returns the `userService` singleton instance.
|
|
285
|
+
```
|
|
198
286
|
|
|
199
|
-
|
|
287
|
+
## ProviderModule API
|
|
288
|
+
|
|
289
|
+
You can see all the available `properties` and `methods` of the `ProviderModule` [here](https://adimarianmutu.github.io/x-injection/classes/IProviderModule.html).
|
|
290
|
+
|
|
291
|
+
## Injection Scope
|
|
292
|
+
|
|
293
|
+
There are mainly 3 first-class ways to set the [InjectionScope](https://adimarianmutu.github.io/x-injection/enums/InjectionScope.html) of a `provider`, and each one has an order priority.
|
|
200
294
|
The below list shows them in order of priority _(highest to lowest)_, meaning that if 2 _(or more)_ ways are used, the method with the highest priority will take precedence.
|
|
201
295
|
|
|
202
296
|
1. By providing the [scope](https://adimarianmutu.github.io/x-injection/interfaces/ProviderScopeOption.html) property to the [ProviderToken](https://adimarianmutu.github.io/x-injection/types/ProviderToken.html):
|
|
@@ -224,7 +318,7 @@ The below list shows them in order of priority _(highest to lowest)_, meaning th
|
|
|
224
318
|
>
|
|
225
319
|
> _Imported modules/providers retain their original `InjectionScope`!_
|
|
226
320
|
|
|
227
|
-
|
|
321
|
+
### Singleton
|
|
228
322
|
|
|
229
323
|
The [Singleton](https://adimarianmutu.github.io/x-injection/enums/InjectionScope.html#singleton) injection scope means that once a dependency has been resolved from within a module will be cached and further resolutions will use the value from the cache.
|
|
230
324
|
|
|
@@ -235,7 +329,7 @@ expect(MyModule.get(MyProvider)).toBe(MyModule.get(MyProvider));
|
|
|
235
329
|
// true
|
|
236
330
|
```
|
|
237
331
|
|
|
238
|
-
|
|
332
|
+
### Transient
|
|
239
333
|
|
|
240
334
|
The [Transient](https://adimarianmutu.github.io/x-injection/enums/InjectionScope.html#transient) injection scope means that a _new_ instance of the dependency will be used whenever a resolution occurs.
|
|
241
335
|
|
|
@@ -246,7 +340,7 @@ expect(MyModule.get(MyProvider)).toBe(MyModule.get(MyProvider));
|
|
|
246
340
|
// false
|
|
247
341
|
```
|
|
248
342
|
|
|
249
|
-
|
|
343
|
+
### Request
|
|
250
344
|
|
|
251
345
|
The [Request](https://adimarianmutu.github.io/x-injection/enums/InjectionScope.html#request) injection scope means that the _same_ instance will be used when a resolution happens in the _same_ request scope.
|
|
252
346
|
|
|
@@ -282,235 +376,352 @@ expect(winstonLibrary.metro2033).toBe(londonLibrary.metro2033);
|
|
|
282
376
|
// false
|
|
283
377
|
```
|
|
284
378
|
|
|
285
|
-
## Provider
|
|
379
|
+
## Provider Tokens
|
|
286
380
|
|
|
287
|
-
|
|
381
|
+
A [ProviderToken](https://adimarianmutu.github.io/x-injection/types/ProviderToken.html) is another core block of `xInjection` _(and also many other IoC/DI libs)_ which is used to define a `token` which can then be used to resolve a `provider`.
|
|
288
382
|
|
|
289
|
-
|
|
290
|
-
import { Injectable, InjectionScope, ProviderModule } from '@adimm/x-injection';
|
|
383
|
+
`xInjection` offers _4_ types of tokens:
|
|
291
384
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
385
|
+
- [ProviderIdentifier](https://adimarianmutu.github.io/x-injection/types/ProviderIdentifier.html)
|
|
386
|
+
- It allows you to bind a `value` to a specific _transparent_ token, like a `Class`, `Function`, `symbol` or `string`:
|
|
387
|
+
```ts
|
|
388
|
+
const API_SERVICE_PROVIDER = ApiService;
|
|
389
|
+
// or
|
|
390
|
+
const CONSTANT_SECRET_PROVIDER = 'Shh';
|
|
391
|
+
```
|
|
392
|
+
- [ProviderClassToken](https://adimarianmutu.github.io/x-injection/types/ProviderClassToken.html)
|
|
296
393
|
|
|
297
|
-
|
|
298
|
-
export class SessionService {
|
|
299
|
-
constructor(public readonly userService: UserService) {}
|
|
394
|
+
- It can be used define the token _and_ the provider:
|
|
300
395
|
|
|
301
|
-
|
|
302
|
-
}
|
|
396
|
+
```ts
|
|
397
|
+
const HUMAN_SERVICE_PROVIDER = { provide: HumanService, useClass: FemaleService };
|
|
303
398
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
providers: [DatabaseService],
|
|
307
|
-
exports: [DatabaseService],
|
|
308
|
-
onReady: async (module) => {
|
|
309
|
-
const databaseService = module.get(DatabaseService);
|
|
399
|
+
// This will bind `HumanService` as the `token` and will resolve `FemaleService` from the container.
|
|
400
|
+
```
|
|
310
401
|
|
|
311
|
-
|
|
312
|
-
},
|
|
313
|
-
onDispose: () => {
|
|
314
|
-
return {
|
|
315
|
-
before: async (module) => {
|
|
316
|
-
// It is invoked right before the dispose process begins.
|
|
317
|
-
// This means that the `module` container is still available to be used.
|
|
402
|
+
- [ProviderValueToken](https://adimarianmutu.github.io/x-injection/types/ProviderValueToken.html)
|
|
318
403
|
|
|
319
|
-
|
|
404
|
+
- It can be used to easily bind _constant_ values, it can be anything, but once resolved it'll be cached and re-used upon further resolutions
|
|
320
405
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
// It is invoked right after the dispose process ended.
|
|
325
|
-
// This means that the `module` container is not available anymore.
|
|
326
|
-
},
|
|
327
|
-
};
|
|
328
|
-
},
|
|
329
|
-
});
|
|
406
|
+
```ts
|
|
407
|
+
const THEY_DONT_KNOW_PROVIDER = { provide: CONSTANT_SECRET_PROVIDER, useValue: `They'll never know` };
|
|
408
|
+
const THEY_MAY_KNOW_PROVIDER = { provide: CONSTANT_SECRET_PROVIDER, useValue: 'Maybe they know?' };
|
|
330
409
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
defaultScope: InjectionScope.Request,
|
|
334
|
-
providers: [SessionService],
|
|
335
|
-
exports: [SessionService],
|
|
336
|
-
});
|
|
337
|
-
```
|
|
410
|
+
// As you can see we now have 2 different ProviderTokens which use the same `provide` key.
|
|
411
|
+
```
|
|
338
412
|
|
|
339
|
-
|
|
413
|
+
- [ProviderFactoryToken](https://adimarianmutu.github.io/x-injection/types/ProviderFactoryToken.html)
|
|
340
414
|
|
|
341
|
-
|
|
342
|
-
AppModule.register({
|
|
343
|
-
imports: [DatabaseModuleDef, SessionModuleDef],
|
|
344
|
-
});
|
|
345
|
-
```
|
|
415
|
+
- It can be used to bind a `factory` which is intended for more complex scenarios:
|
|
346
416
|
|
|
347
|
-
|
|
417
|
+
```ts
|
|
418
|
+
const MAKE_PIZZA_PROVIDER = {
|
|
419
|
+
provide: 'MAKE_PIZZA',
|
|
420
|
+
useFactory: async (apiService: ApiService, pizzaService: PizzaService) => {
|
|
421
|
+
const typeOfPizza = await apiService.getTypeOfPizza();
|
|
422
|
+
|
|
423
|
+
if (typeOfPizza === 'margherita') return pizzaService.make.margherita;
|
|
424
|
+
if (typeOfPizza === 'quattro_stagioni') return pizzaService.make.quattroStagioni;
|
|
425
|
+
// and so on
|
|
426
|
+
},
|
|
427
|
+
// optional
|
|
428
|
+
inject: [API_SERVICE_PROVIDER, PizzaService],
|
|
429
|
+
};
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
These are all the available `ProviderToken` you can use.
|
|
433
|
+
|
|
434
|
+
> [!NOTE]
|
|
348
435
|
>
|
|
349
|
-
>
|
|
436
|
+
> In `NestJS` and `Angular` you can't use a `ProviderToken` to _get_ a value, `xInjection` allows this pattern, but you must understand that what it actually does, is to use the _value_ from the `provide` property.
|
|
437
|
+
|
|
438
|
+
## Provider Modules
|
|
350
439
|
|
|
351
|
-
|
|
440
|
+
As you already saw till here, everything relies around the `ProviderModule` class, so let's dive a little more deep into understanding it.
|
|
352
441
|
|
|
353
|
-
|
|
442
|
+
The most straight forward way to _create/instantiate_ a new `module` is:
|
|
354
443
|
|
|
355
444
|
```ts
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
445
|
+
const MyModule = ProviderModule.create({
|
|
446
|
+
id: 'MyModule',
|
|
447
|
+
imports: [AnotherModule, SecondModule, ThirdModule],
|
|
448
|
+
providers: [
|
|
449
|
+
{ provide: CONSTANT_SECRET_PROVIDER, useValue: 'ultra secret' },
|
|
450
|
+
PizzaService,
|
|
451
|
+
{ provide: HumanService, useClass: FemaleService },
|
|
452
|
+
],
|
|
453
|
+
exports: [SecondModule, ThirdModule, PizzaService],
|
|
454
|
+
});
|
|
359
455
|
```
|
|
360
456
|
|
|
361
|
-
|
|
457
|
+
From what we can see, the `MyModule` is importing into it 3 more modules, each of them may export one or more _(maybe nothing, that's valid too)_ providers, or even other `modules`.
|
|
458
|
+
Because we imported them into the `MyModule`, now we have access to any providers they may have chosen to export, and the same is true also for _their exported_ modules.
|
|
362
459
|
|
|
363
|
-
|
|
460
|
+
Then, we've chosen to _re-export_ from the `MyModule` the `SecondModule` and `ThirdModule`, meaning that if a different `module` imports `MyModule`, it'll automatically get access to those 2 modules as well. And in the end we also exported our own `PizzaService`, while the remaining other 2 providers, `CONSTANT_SECRET_PROVIDER` and `HumanService` can't be accessed when importing `MyModule`.
|
|
364
461
|
|
|
365
|
-
|
|
366
|
-
const MyModule = new ProviderModule({...});
|
|
367
|
-
```
|
|
462
|
+
This is the _core_ feature of `xInjection` _(and `Angular`/`NestJS` DI system)_, being able to encapsulate the providers, so nothing can spill out without our explicit consent.
|
|
368
463
|
|
|
369
|
-
|
|
464
|
+
---
|
|
370
465
|
|
|
371
|
-
|
|
466
|
+
We could also achieve the above by using the `ProviderModule` API like this:
|
|
372
467
|
|
|
373
468
|
```ts
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
469
|
+
MyModule.update.addImport(AnotherModule);
|
|
470
|
+
MyModule.update.addImport(SecondModule, true); // `true` means "also add to the `exports` definition"
|
|
471
|
+
MyModule.update.addImport(ThirdModule, true);
|
|
377
472
|
|
|
378
|
-
|
|
473
|
+
MyModule.update.addProvider({ provide: CONSTANT_SECRET_PROVIDER, useValue: 'ultra secret' });
|
|
474
|
+
MyModule.update.addProvider(PizzaService, true);
|
|
475
|
+
MyModule.update.addProvider({ provide: HumanService, useClass: FemaleService });
|
|
379
476
|
```
|
|
380
477
|
|
|
381
|
-
|
|
478
|
+
Sometimes you may actually want to _lazy_ import a `module` from a _file_, this can be done very easily with `xInjection`:
|
|
382
479
|
|
|
383
480
|
```ts
|
|
384
|
-
|
|
481
|
+
(async () => {
|
|
482
|
+
await MyModule.update.addImportLazy(async () => (await import('./lazy.module')).LazyModule);
|
|
483
|
+
|
|
484
|
+
MyModule.isImportingModule('LazyModule');
|
|
485
|
+
// => true
|
|
486
|
+
})();
|
|
385
487
|
```
|
|
386
488
|
|
|
387
|
-
|
|
489
|
+
> [!TIP]
|
|
490
|
+
>
|
|
491
|
+
> This design pattern is _extremely_ powerful and useful when you may have a lot of `modules` initializing during the app bootstrap process as you can defer their initialization, or even never load them if the user never needs those specific `modules` _(this is mostly applicable on the client-side rather than the server-side)_
|
|
388
492
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
493
|
+
Keep reading to understand how you can defer initialization of the modules _both_ `client-side` and `server-side`.
|
|
494
|
+
|
|
495
|
+
## Blueprints
|
|
496
|
+
|
|
497
|
+
The [ProviderModuleBlueprint](https://adimarianmutu.github.io/x-injection/classes/ProviderModuleBlueprint.html) `class` main purpose is to encapsulate the `definitions` of a `Module`, when you do `ProviderModule.blueprint({...})` you are _not_ actually creating an instance of the `ProviderModule` class, but an instance of the `ProviderModuleBlueprint` class.
|
|
498
|
+
|
|
499
|
+
Before diving into some examples, let's first clarify some important aspects about the behavior of the `blueprint` class.
|
|
500
|
+
|
|
501
|
+
### Import Behavior
|
|
502
|
+
|
|
503
|
+
Whenever you _import_ a `blueprint` into a `module`, it'll automatically be "transformed" to a `ProviderModule` instance by the engine, this step is crucial as a `blueprint` per se does not contain a _container_, just its _definitions_.
|
|
504
|
+
|
|
505
|
+
This has a "gotcha", because the conversion happens internally, you'll "not" be able to get a reference to that imported module, not directly at least _(I may change this in a future patch)_, this means that if you don't have the reference of the `ProviderModule`, you can't remove it from the _imported_ module _(if you'll ever need to remove it)_.
|
|
392
506
|
|
|
393
507
|
> [!NOTE]
|
|
394
508
|
>
|
|
395
|
-
>
|
|
509
|
+
> There's currently an workaround for this, you can `subscribe` to the `Import` event and get from there the `ProviderModule` instance, just make sure to check the `id` of the module via `module.toString()` when doing so.
|
|
396
510
|
|
|
397
|
-
|
|
511
|
+
It is also important, to understand the _injection_ `scope` of an imported `blueprint`; we previously learned that when we import a `blueprint` into a `module` it automatically creates an instance of the `ProviderModule` from it, this means that all the `singleton` providers of the `blueprint` definition are now _scoped singleton_, where _scoped_ means _singleton in relation to their imported module_.
|
|
398
512
|
|
|
399
|
-
|
|
513
|
+
### isGlobal
|
|
400
514
|
|
|
401
|
-
|
|
515
|
+
When you initialize a `blueprint` with the [isGlobal](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#isglobal) property set to `true`, the out-of-the-box behavior is to _automatically_ import the `blueprint` into the `AppModule`. You can disable this behavior by setting the [autoImportIntoAppModuleWhenGlobal](https://adimarianmutu.github.io/x-injectioninterfaces/ModuleBlueprintOptions.html#autoimportintoappmodulewhenglobal) property to `false`
|
|
516
|
+
|
|
517
|
+
```ts
|
|
518
|
+
const GlobalModuleBp = ProviderModule.blueprint({..., isGlobal: true }, { autoImportIntoAppModuleWhenGlobal: false });
|
|
519
|
+
```
|
|
402
520
|
|
|
403
|
-
|
|
521
|
+
Now you can decide when to import it into the `AppModule` by doing `AppModule.addImport(GlobalModuleBp)`.
|
|
404
522
|
|
|
405
|
-
|
|
523
|
+
---
|
|
524
|
+
|
|
525
|
+
I highly recommend to take advantage of the `blueprints` nature in order to plan-ahead your `modules` and import them wherever you have to import only when needed;
|
|
526
|
+
|
|
527
|
+
Why?
|
|
528
|
+
|
|
529
|
+
- To _define module configurations upfront_ without incurring the cost of immediate initialization _(even if negligible)_.
|
|
530
|
+
- To reuse module _definitions across_ different parts of your application while maintaining isolated instances. _(when possible/applicable)_
|
|
531
|
+
- To _compose modules flexibly_, allowing you to adjust module dependencies dynamically before instantiation.
|
|
532
|
+
|
|
533
|
+
## Advanced Usage
|
|
534
|
+
|
|
535
|
+
> [!WARNING]
|
|
536
|
+
>
|
|
537
|
+
> This section covers advanced features which may add additional complexity _(or even bugs)_ to your application if you misuse them, use these features only if truly needed and after evaluating the _pros_ and _cons_ of each.
|
|
538
|
+
|
|
539
|
+
### Events
|
|
540
|
+
|
|
541
|
+
Each `module` will emit specific events through its life-cycle and you can intercept them by using the `Module.update.subscribe` method.
|
|
542
|
+
|
|
543
|
+
> [!TIP]
|
|
544
|
+
>
|
|
545
|
+
> [Here](https://adimarianmutu.github.io/x-injection/enums/DefinitionEventType.html) you can see all the available `events`
|
|
546
|
+
|
|
547
|
+
If you'd need to intercept a `get` request, you can achieve that by doing:
|
|
406
548
|
|
|
407
549
|
```ts
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
imports: [FerrariModule, PorscheModule, ...]
|
|
550
|
+
const CarModule = ProviderModule.create({
|
|
551
|
+
id: 'CarModule',
|
|
552
|
+
providers: [CarService],
|
|
412
553
|
});
|
|
413
554
|
|
|
414
|
-
|
|
555
|
+
CarModule.update.subscribe(({ type, change }) => {
|
|
556
|
+
// We are interested only in the `GetProvider` event.
|
|
557
|
+
if (type !== DefinitionEventType.GetProvider) return;
|
|
558
|
+
|
|
559
|
+
// As our `CarModule` has only one provider, it is safe to assume
|
|
560
|
+
// that the `change` will always be the `CarService` instance.
|
|
561
|
+
const carService = change as CarService;
|
|
562
|
+
|
|
563
|
+
console.log('CarService: ', carService);
|
|
564
|
+
});
|
|
415
565
|
|
|
416
|
-
|
|
566
|
+
const carService = CarModule.get(CarService);
|
|
567
|
+
// logs => CarService: <instance_of_car_service_here>
|
|
417
568
|
```
|
|
418
569
|
|
|
419
|
-
|
|
570
|
+
> [!WARNING]
|
|
571
|
+
>
|
|
572
|
+
> After subscribing to a `ProviderModule` signal emission, you should make sure to also `unsubscribe` if you don't need anymore to intercept the changes, not doing
|
|
573
|
+
> so may cause memory leaks if you have lots of `subscriptions` which do heavy computations!
|
|
420
574
|
|
|
421
|
-
|
|
575
|
+
The `subscribe` method will _always_ return a `method` having the signature `() => void`, when you invoke it, it'll close the pipe which intercepts the signal emitted by the `module`:
|
|
422
576
|
|
|
423
577
|
```ts
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
providers: [BankBranchService],
|
|
427
|
-
exports: [BankBranchService],
|
|
578
|
+
const unsubscribe = CarModule.update.subscribe(({ type, change }) => {
|
|
579
|
+
/* heavy computation here */
|
|
428
580
|
});
|
|
429
581
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
exports: [..., (importerModule) => {
|
|
434
|
-
// When the module having the identifier `UnknownBankModule` imports the `BankModule`
|
|
435
|
-
// it'll not be able to also import the `SecureBankBranchModule` as we are not returning it here.
|
|
436
|
-
if (importerModule.toString() === 'UnknownBankModule') return;
|
|
437
|
-
|
|
438
|
-
// Otherwise we safely export it
|
|
439
|
-
return SecureBankBranchModule;
|
|
440
|
-
}]
|
|
441
|
-
});
|
|
582
|
+
// later in your code
|
|
583
|
+
|
|
584
|
+
unsubscribe();
|
|
442
585
|
```
|
|
443
586
|
|
|
444
|
-
|
|
587
|
+
> [!NOTE]
|
|
588
|
+
>
|
|
589
|
+
> Events are _always_ invoked _after_ middlewares
|
|
590
|
+
|
|
591
|
+
### Middlewares
|
|
445
592
|
|
|
446
|
-
|
|
593
|
+
Using middlewares is not encouraged as it allows you to tap into very deep low-level code which can cause unexpected bugs if not implemented carefully, however, `middlewares` are the perfect choice if you want to extend/alter the standard behavior of `module` as it allows you to decide what should happen with a resolved value _before_ it is returned to the `consumer`.
|
|
447
594
|
|
|
448
|
-
|
|
595
|
+
> [!TIP]
|
|
596
|
+
>
|
|
597
|
+
> [Here](https://adimarianmutu.github.io/x-injection/enums/MiddlewareType.html) you can see all the available `middlewares`
|
|
598
|
+
|
|
599
|
+
Let's say that you want to wrap all the returned values of a specific `module` within an object having this signature `{ timestamp: number; value: any }`. By using the `GetProvider` event will not do the trick because it _doesn't_ allow you to alter/change the actual returned value to the `consumer`, you can indeed alter the _content_ via reference, but not the _actual_ result.
|
|
600
|
+
|
|
601
|
+
So the easiest way to achieve that is by using the `BeforeGet` middleware as shown below:
|
|
449
602
|
|
|
450
603
|
```ts
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
604
|
+
const TransactionModule = ProviderModule.create(TransactionModuleBp);
|
|
605
|
+
|
|
606
|
+
TransactionModule.middlewares.add(MiddlewareType.BeforeGet, (provider, providerToken, inject) => {
|
|
607
|
+
// We are interested only in the `providers` instances which are from the `Payment` class
|
|
608
|
+
if (!(provider instanceof Payment)) return true;
|
|
609
|
+
// or
|
|
610
|
+
if (providerToken !== 'LAST_TRANSACTION') return true;
|
|
611
|
+
|
|
612
|
+
// DON'T do this as you'll encounter an infinite loop
|
|
613
|
+
const transactionService = TransactionModule.get(TransactionService);
|
|
614
|
+
// If you have to inject into the middleware `context` from the `module`
|
|
615
|
+
// use the `inject` parameter
|
|
616
|
+
const transactionService = inject(TransactionService);
|
|
617
|
+
|
|
618
|
+
return {
|
|
619
|
+
timestamp: transactionService.getTimestamp(),
|
|
620
|
+
value: provider,
|
|
621
|
+
};
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
const transaction = TransactionModule.get('LAST_TRANSACTION');
|
|
625
|
+
// transaction => { timestamp: 1363952948, value: <Payment_instance> }
|
|
454
626
|
```
|
|
455
627
|
|
|
456
|
-
|
|
628
|
+
One more example is to add a `middleware` in order to _dynamically_ control which `modules` can import a specific `module` by using the [OnExportAccess](https://adimarianmutu.github.io/x-injection/enums/MiddlewareType.html#onexportaccess) flag.
|
|
457
629
|
|
|
458
630
|
```ts
|
|
459
|
-
|
|
631
|
+
const UnauthorizedBranchBankModule = ProviderModule.create({ id: 'UnauthorizedBranchBankModule' });
|
|
632
|
+
const SensitiveBankDataModule = ProviderModule.create({
|
|
633
|
+
id: 'SensitiveBankDataModule',
|
|
634
|
+
providers: [SensitiveBankDataService, NonSensitiveBankDataService],
|
|
635
|
+
exports: [SensitiveBankDataService, NonSensitiveBankDataService],
|
|
636
|
+
});
|
|
460
637
|
|
|
461
|
-
|
|
462
|
-
|
|
638
|
+
SensitiveBankDataModule.middlewares.add(MiddlewareType.OnExportAccess, (importerModule, currentExport) => {
|
|
639
|
+
// We want to deny access to our `SensitiveBankDataService` from the `exports` definition if the importer module is `UnauthorizedBranchBankModule`
|
|
640
|
+
if (importerModule.toString() === 'UnauthorizedBranchBankModule' && currentExport === SensitiveBankDataService)
|
|
641
|
+
return false;
|
|
463
642
|
|
|
464
|
-
|
|
643
|
+
// Remaining module are able to import all our `export` definition
|
|
644
|
+
// The `UnauthorizedBranchBankModule` is unable to import the `SensitiveBankDataService`
|
|
645
|
+
return true;
|
|
646
|
+
});
|
|
647
|
+
```
|
|
465
648
|
|
|
466
|
-
|
|
649
|
+
> [!CAUTION]
|
|
650
|
+
>
|
|
651
|
+
> Returning `false` in a `middleware` will abort the chain, meaning that for the above example, no value would be returned.
|
|
652
|
+
> If you have to explicitly return a `false` boolean value, you may have to wrap your provider value as an workaround. _(`null` is accepted as a return value)_
|
|
653
|
+
>
|
|
654
|
+
> Meanwhile returning `true` means _"return the value without changing it"_.
|
|
655
|
+
>
|
|
656
|
+
> In the future this behavior may change, so if your business logic relies a lot on `middlewares` make sure to stay up-to-date with the latest changes.
|
|
467
657
|
|
|
468
|
-
|
|
658
|
+
It is also worth mentioning that you can apply _multiple_ middlewares by just invoking the `middlewares.add` method multiple times, they are executed in the same exact order as you applied them, meaning that the first invokation to `middlewares.add` will actually be the `root` of the chain.
|
|
469
659
|
|
|
470
|
-
|
|
660
|
+
If no error is thrown down the chain, all the registered middleware `callback` will be supplied with the necessary values.
|
|
471
661
|
|
|
472
662
|
> [!WARNING]
|
|
473
663
|
>
|
|
474
|
-
>
|
|
664
|
+
> It is the _developer_ responsability to catch any error down the `chain`!
|
|
475
665
|
|
|
476
|
-
|
|
666
|
+
### Internals
|
|
477
667
|
|
|
478
|
-
|
|
668
|
+
If you are not interested in understanding how `xInjection` works under the hood, you can skip this section 😌
|
|
479
669
|
|
|
480
|
-
|
|
670
|
+
#### ProviderModule
|
|
481
671
|
|
|
482
|
-
|
|
672
|
+
It is the head of everything, a `ProviderModule` is actually composed by several classes, each with its own purpose.
|
|
483
673
|
|
|
484
|
-
|
|
674
|
+
> [!TIP]
|
|
675
|
+
>
|
|
676
|
+
> You can get access to _all_ the internal instances by doing `new ProviderModule({...})` instead of `ProviderModule.create({...})`
|
|
485
677
|
|
|
486
|
-
|
|
487
|
-
const ScopedModule = new ProviderModule({
|
|
488
|
-
identifier: 'ScopedModule',
|
|
489
|
-
providers: [...],
|
|
490
|
-
exports: [...],
|
|
491
|
-
});
|
|
678
|
+
#### MiddlewaresManager
|
|
492
679
|
|
|
493
|
-
|
|
494
|
-
identifier: 'AnotherScopedModule',
|
|
495
|
-
imports: [ScopedModule],
|
|
496
|
-
providers: [...],
|
|
497
|
-
exports: [...],
|
|
498
|
-
});
|
|
680
|
+
It is the `class` which takes care of managing the registered `middlewares`, check it out [here](https://adimarianmutu.github.io/x-injection/classes/MiddlewaresManager.html).
|
|
499
681
|
|
|
500
|
-
|
|
501
|
-
identifier: 'GlobalModule',
|
|
502
|
-
isGlobal: true,
|
|
503
|
-
imports: [AnotherScopedModule],
|
|
504
|
-
});
|
|
682
|
+
Not much to say about it as its main role is to _register_ and _build_ the `middleware` chain.
|
|
505
683
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
684
|
+
#### ModuleContainer
|
|
685
|
+
|
|
686
|
+
It is the `class` which takes care of managing the `inversify` container, check it out [here](https://adimarianmutu.github.io/x-injection/classes/ModuleContainer.html).
|
|
687
|
+
|
|
688
|
+
Its main purpose is to initialize the module raw _([InversifyJS Container](https://inversify.io/docs/api/container/))_ `class` and to _bind_ the providers to it.
|
|
689
|
+
|
|
690
|
+
#### ImportedModuleContainer
|
|
691
|
+
|
|
692
|
+
It is the `class` which takes care of managing the _imported_ modules, check it out [here](https://adimarianmutu.github.io/x-injection/classes/ImportedModuleContainer.html).
|
|
693
|
+
|
|
694
|
+
Because `modules` can be imported into other modules, therefore creating a _complex_ `graph` of modules, the purpose of this class is to keep track and sync the changes of the `exports` definition of the _imported_ module.
|
|
695
|
+
|
|
696
|
+
The `ProviderModule` API is simple yet very powerful, you may not realize that doing `addImport` will cause _(based on how deep is the imported module)_ a chain reaction which the `ImportedModuleContainer` must keep track of in order to make sure that the _consumer_ `module` which imported the _consumed_ `module` has access only to the `providers`/`modules` explicitly exported by the _consumed_ `module`.
|
|
697
|
+
|
|
698
|
+
Therefore it is encouraged to keep things mostly static, as each `addProvider`, `addImport`, `removeImport` and so on have a penality cost on your application performance. This cost in most cases is negligible, however it highly depends on how the _developer_ uses the feature `xInjection` offers.
|
|
699
|
+
|
|
700
|
+
> "With great power comes great responsibility."
|
|
701
|
+
|
|
702
|
+
#### DynamicModuleDefinition
|
|
703
|
+
|
|
704
|
+
It is the `class` which takes care of managing the _updates_ and _event_ emissions of the `module`, check it out [here](https://adimarianmutu.github.io/x-injection/classes/DynamicModuleDefinition.html).
|
|
705
|
+
|
|
706
|
+
This class is actually the "parent" of the `ImportedModuleContainer` instances, its purpose is to _build_ the _initial_ definition graph, and while doing so it also instantiate _for each_ imported module a new `ImportedModuleContainer`.
|
|
707
|
+
|
|
708
|
+
It also take care of managing the `events` bubbling by checking cirular references and so on.
|
|
709
|
+
|
|
710
|
+
#### ProviderModuleBlueprint
|
|
711
|
+
|
|
712
|
+
It's the "metadata" counterpart of the `ProviderModule` class, as its only purpose is to carry the definitions. Check it out [here](https://adimarianmutu.github.io/x-injection/classes/ProviderModuleBlueprint.html).
|
|
713
|
+
|
|
714
|
+
#### Set of Helpers
|
|
715
|
+
|
|
716
|
+
The library does also export a set of useful helpers in the case you may need it:
|
|
717
|
+
|
|
718
|
+
```ts
|
|
719
|
+
import { ProviderModuleHelpers, ProviderTokenHelpers } from '@adimm/x-injection';
|
|
509
720
|
```
|
|
510
721
|
|
|
511
|
-
|
|
722
|
+
---
|
|
512
723
|
|
|
513
|
-
|
|
724
|
+
This covers pretty much everything about how `xInjection` is built and how it works.
|
|
514
725
|
|
|
515
726
|
## Unit Tests
|
|
516
727
|
|
|
@@ -528,12 +739,13 @@ class ApiService {
|
|
|
528
739
|
private async sendToLocation(user: User, location: any): Promise<any> {}
|
|
529
740
|
}
|
|
530
741
|
|
|
531
|
-
const
|
|
532
|
-
|
|
742
|
+
const ApiModuleBp = new ProviderModule.blueprint({
|
|
743
|
+
id: 'ApiModule',
|
|
533
744
|
providers: [UserService, ApiService],
|
|
534
745
|
});
|
|
535
746
|
|
|
536
|
-
|
|
747
|
+
// Clone returns a `deep` clone and wraps all the `methods` to break their reference!
|
|
748
|
+
const ApiModuleBpMocked = ApiModuleBp.clone().updateDefinition({
|
|
537
749
|
identifier: 'ApiModuleMocked',
|
|
538
750
|
providers: [
|
|
539
751
|
{
|
|
@@ -552,7 +764,7 @@ const ApiModuleDefinitionMocked = ApiModule.clone({
|
|
|
552
764
|
});
|
|
553
765
|
```
|
|
554
766
|
|
|
555
|
-
Now what you have to do is just to provide the `
|
|
767
|
+
Now what you have to do is just to provide the `ApiModuleBpMocked` instead of the `ApiModuleBp` 😎
|
|
556
768
|
|
|
557
769
|
## Documentation
|
|
558
770
|
|
|
@@ -560,34 +772,6 @@ Comprehensive, auto-generated documentation is available at:
|
|
|
560
772
|
|
|
561
773
|
👉 [https://adimarianmutu.github.io/x-injection/index.html](https://adimarianmutu.github.io/x-injection/index.html)
|
|
562
774
|
|
|
563
|
-
## Conventions
|
|
564
|
-
|
|
565
|
-
To create a stable experience across different projects, the following _conventions_ should be followed.
|
|
566
|
-
|
|
567
|
-
### ProviderModule
|
|
568
|
-
|
|
569
|
-
When instantiating a new `ProviderModule` class you should append `Module` to the `variable` name and its `identifier`:
|
|
570
|
-
|
|
571
|
-
```ts
|
|
572
|
-
const DatabaseModule = new ProviderModule({
|
|
573
|
-
identifier: 'DatabaseModule',
|
|
574
|
-
});
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
### ProviderModuleDefinition
|
|
578
|
-
|
|
579
|
-
When instantiating a new `ProviderModuleDefinition` class you should append `ModuleDef` to the `variable` name:
|
|
580
|
-
|
|
581
|
-
> [!NOTE]
|
|
582
|
-
>
|
|
583
|
-
> _Do not append `Def` to the `identifier` as that will be inherit by the actual `ProviderModule`!_
|
|
584
|
-
|
|
585
|
-
```ts
|
|
586
|
-
const DatabaseModuleDef = new ProviderModuleDefinition({
|
|
587
|
-
identifier: 'DatabaseModule', // No `Def` here!
|
|
588
|
-
});
|
|
589
|
-
```
|
|
590
|
-
|
|
591
775
|
## ReactJS Implementation
|
|
592
776
|
|
|
593
777
|
You want to use it within a [ReactJS](https://react.dev/) project? Don't worry, the library does already have an official implementation for React ⚛️
|
|
@@ -600,6 +784,11 @@ Pull requests are warmly welcomed! 😃
|
|
|
600
784
|
|
|
601
785
|
Please ensure your contributions adhere to the project's code style. See the repository for more details.
|
|
602
786
|
|
|
787
|
+
## Credits
|
|
788
|
+
|
|
789
|
+
- [Adi-Marian Mutu](https://www.linkedin.com/in/mutu-adi-marian/) - Author of `xInjection`
|
|
790
|
+
- [InversifyJS](https://github.com/inversify/monorepo) - Base lib
|
|
791
|
+
|
|
603
792
|
---
|
|
604
793
|
|
|
605
794
|
> [!NOTE]
|