@adimm/x-injection 1.1.6 → 1.2.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 +133 -43
- package/dist/index.cjs +242 -220
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +44 -17
- package/dist/index.d.ts +44 -17
- package/dist/index.js +138 -116
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,7 +31,6 @@ xInjection <a href="https://www.npmjs.com/package/@adimm/x-injection" targe
|
|
|
31
31
|
- [ProviderModuleDefinition](#providermoduledefinition)
|
|
32
32
|
- [Feed to `new ProviderModule`](#feed-to-new-providermodule)
|
|
33
33
|
- [Feed to `lazyImport`](#feed-to-lazyimport)
|
|
34
|
-
- [Why not just use the `ProviderModuleOptions` interface?](#why-not-just-use-the-providermoduleoptions-interface)
|
|
35
34
|
- [Lazy `imports` and `exports`](#lazy-imports-and-exports)
|
|
36
35
|
- [Imports](#imports)
|
|
37
36
|
- [Exports](#exports)
|
|
@@ -39,9 +38,12 @@ xInjection <a href="https://www.npmjs.com/package/@adimm/x-injection" targe
|
|
|
39
38
|
- [ProviderModuleNaked Interface](#providermodulenaked-interface)
|
|
40
39
|
- [Strict Mode](#strict-mode)
|
|
41
40
|
- [Why you should not turn it off:](#why-you-should-not-turn-it-off)
|
|
42
|
-
- [
|
|
41
|
+
- [isGlobal](#isglobal)
|
|
43
42
|
- [Unit Tests](#unit-tests)
|
|
44
43
|
- [Documentation](#documentation)
|
|
44
|
+
- [Conventions](#conventions)
|
|
45
|
+
- [ProviderModule](#providermodule)
|
|
46
|
+
- [ProviderModuleDefinition](#providermoduledefinition-1)
|
|
45
47
|
- [ReactJS Implementation](#reactjs-implementation)
|
|
46
48
|
- [Contributing](#contributing)
|
|
47
49
|
|
|
@@ -98,7 +100,13 @@ import { AppModule } from '@adimm/x-injection';
|
|
|
98
100
|
AppModule.register({});
|
|
99
101
|
```
|
|
100
102
|
|
|
101
|
-
>
|
|
103
|
+
> [!WARNING]
|
|
104
|
+
>
|
|
105
|
+
> _You must invoke `AppModule.register()` even if you have no global providers. Passing an empty object `{}` to the method is valid._
|
|
106
|
+
|
|
107
|
+
> [!NOTE]
|
|
108
|
+
>
|
|
109
|
+
> _You can import additional modules (later at run-time) into the `AppModule` by using the [lazyImport](https://adimarianmutu.github.io/x-injection/interfaces/IAppModule.html#lazyimport-1) `method`._
|
|
102
110
|
|
|
103
111
|
### Registering Global Providers
|
|
104
112
|
|
|
@@ -130,21 +138,61 @@ You can also import entire modules into the `AppModule` like so:
|
|
|
130
138
|
const SECRET_TOKEN_PROVIDER = { provide: 'SECRET_TOKEN', useValue: '123' };
|
|
131
139
|
const SECRET_TOKEN_2_PROVIDER = { provide: 'SECRET_TOKEN_2', useValue: 123 };
|
|
132
140
|
|
|
133
|
-
const
|
|
134
|
-
identifier:
|
|
135
|
-
|
|
141
|
+
const ConfigModuleDef = new ProviderModuleDefinition({
|
|
142
|
+
identifier: 'ConfigModule',
|
|
143
|
+
isGlobal: true,
|
|
136
144
|
providers: [SECRET_TOKEN_PROVIDER, SECRET_TOKEN_2_PROVIDER],
|
|
137
145
|
exports: [SECRET_TOKEN_PROVIDER, SECRET_TOKEN_2_PROVIDER],
|
|
138
146
|
});
|
|
139
147
|
|
|
140
148
|
AppModule.register({
|
|
141
|
-
imports: [
|
|
149
|
+
imports: [ConfigModuleDef],
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
> [!WARNING]
|
|
154
|
+
>
|
|
155
|
+
> _Importing a module marked as global into a scoped module will automatically import it into the `AppModule` rather than the scoped module itself!_
|
|
156
|
+
|
|
157
|
+
This means that you can also do the following:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
const FancyModuleDefinition = new ProviderModuleDefinition({
|
|
161
|
+
identifier: 'FancyModule',
|
|
162
|
+
isGlobal: true,
|
|
163
|
+
providers: [FancyService],
|
|
164
|
+
exports: [FancyService],
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const ScopedModule = new ProviderModule({
|
|
168
|
+
identifier: 'ScopedModule',
|
|
169
|
+
// This `ScopedModule` will automatically "forward" the global `FancyModuleDefinition`
|
|
170
|
+
// exports to the `AppModule`.
|
|
171
|
+
imports: [FancyModuleDefinition],
|
|
142
172
|
});
|
|
173
|
+
|
|
174
|
+
AppModule.get(FancyService);
|
|
175
|
+
// returns the global instance of the `FancyService` class
|
|
143
176
|
```
|
|
144
177
|
|
|
145
|
-
|
|
178
|
+
The above can also be written as:
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
const FancyModuleDefinition = new ProviderModuleDefinition({
|
|
182
|
+
identifier: 'FancyModule',
|
|
183
|
+
isGlobal: true,
|
|
184
|
+
providers: [FancyService],
|
|
185
|
+
exports: [FancyService],
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
AppModule.lazyImport(FancyModuleDefinition);
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
> [!NOTE]
|
|
192
|
+
>
|
|
193
|
+
> _All modules which are imported into the `AppModule` must have the [isGlobal](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#isGlobal) option set to `true`, otherwise the [InjectionProviderModuleGlobalMarkError](https://adimarianmutu.github.io/x-injection/classes/InjectionProviderModuleGlobalMarkError.html) exception will be thrown!_
|
|
146
194
|
>
|
|
147
|
-
>
|
|
195
|
+
> _An [InjectionProviderModuleGlobalMarkError](https://adimarianmutu.github.io/x-injection/classes/InjectionProviderModuleGlobalMarkError.html) exception will be thrown also when importing into the `AppModule` a module which does **not** have the [isGlobal](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#isGlobal) flag option!_
|
|
148
196
|
|
|
149
197
|
### Injection Scope
|
|
150
198
|
|
|
@@ -166,13 +214,15 @@ The below list shows them in order of priority _(highest to lowest)_, meaning th
|
|
|
166
214
|
```
|
|
167
215
|
3. By providing the [defaultScope](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#defaultscope) property when initializing a `ProviderModule`:
|
|
168
216
|
```ts
|
|
169
|
-
const
|
|
170
|
-
identifier:
|
|
217
|
+
const RainModuleDef = new ProviderModuleDef({
|
|
218
|
+
identifier: 'RainModule',
|
|
171
219
|
defaultScope: InjectionScope.Transient,
|
|
172
220
|
});
|
|
173
221
|
```
|
|
174
222
|
|
|
175
|
-
>
|
|
223
|
+
> [!NOTE]
|
|
224
|
+
>
|
|
225
|
+
> _Imported modules/providers retain their original `InjectionScope`!_
|
|
176
226
|
|
|
177
227
|
#### Singleton
|
|
178
228
|
|
|
@@ -251,9 +301,8 @@ export class SessionService {
|
|
|
251
301
|
// Implementation...
|
|
252
302
|
}
|
|
253
303
|
|
|
254
|
-
export const
|
|
255
|
-
identifier:
|
|
256
|
-
// or: identifier: 'DatabaseModule',
|
|
304
|
+
export const DatabaseModuleDef = new ProviderModuleDefinitionDef({
|
|
305
|
+
identifier: 'DatabaseModule',
|
|
257
306
|
providers: [DatabaseService],
|
|
258
307
|
exports: [DatabaseService],
|
|
259
308
|
onReady: async (module) => {
|
|
@@ -261,15 +310,26 @@ export const DatabaseModule = new ProviderModule({
|
|
|
261
310
|
|
|
262
311
|
// Additional initialization...
|
|
263
312
|
},
|
|
264
|
-
onDispose:
|
|
265
|
-
|
|
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.
|
|
318
|
+
|
|
319
|
+
const databaseService = module.get(DatabaseService);
|
|
266
320
|
|
|
267
|
-
|
|
321
|
+
databaseService.closeConnection();
|
|
322
|
+
},
|
|
323
|
+
after: async (module) => {
|
|
324
|
+
// It is invoked right after the dispose process ended.
|
|
325
|
+
// This means that the `module` container is not available anymore.
|
|
326
|
+
},
|
|
327
|
+
};
|
|
268
328
|
},
|
|
269
329
|
});
|
|
270
330
|
|
|
271
|
-
export const
|
|
272
|
-
identifier:
|
|
331
|
+
export const SessionModuleDef = new ProviderModuleDefinition({
|
|
332
|
+
identifier: 'SessionModule',
|
|
273
333
|
defaultScope: InjectionScope.Request,
|
|
274
334
|
providers: [SessionService],
|
|
275
335
|
exports: [SessionService],
|
|
@@ -280,11 +340,13 @@ Register these modules in your `AppModule`:
|
|
|
280
340
|
|
|
281
341
|
```ts
|
|
282
342
|
AppModule.register({
|
|
283
|
-
imports: [
|
|
343
|
+
imports: [DatabaseModuleDef, SessionModuleDef],
|
|
284
344
|
});
|
|
285
345
|
```
|
|
286
346
|
|
|
287
|
-
>
|
|
347
|
+
> [!WARNING]
|
|
348
|
+
>
|
|
349
|
+
> The `AppModule.register` method can be invoked only _once_, preferably during your application bootstrapping process. _(you may re-invoke it only after the module has been disposed)_
|
|
288
350
|
|
|
289
351
|
From now on, the `AppModule` container has the references of the `DatabaseService` and the `SessionService`.
|
|
290
352
|
|
|
@@ -328,13 +390,9 @@ const GarageModule = new ProviderModule(GarageModuleDefinition);
|
|
|
328
390
|
ExistingModule.lazyImport(GarageModuleDefinition);
|
|
329
391
|
```
|
|
330
392
|
|
|
331
|
-
>
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
That's a very good question! It means that you understood that the `ProviderModuleDefinition` is actually a `class` wrapper of the `ProviderModuleOptions`.
|
|
336
|
-
|
|
337
|
-
Theoretically you _can_ use a _plain_ `object` having the `ProviderModuleOptions` interface, however, the `ProviderModuleOptions` interface purpose is solely to _expose/shape_ the options with which a module can be instantiated, while the `ProviderModuleDefinition` purpose is to _define_ the actual `ProviderModule` _blueprint_.
|
|
393
|
+
> [!NOTE]
|
|
394
|
+
>
|
|
395
|
+
> _Providing it to the `lazyImport` method will automatically instantiate a new `ProviderModule` on-the-fly!_
|
|
338
396
|
|
|
339
397
|
### Lazy `imports` and `exports`
|
|
340
398
|
|
|
@@ -363,15 +421,15 @@ GarageModule.lazyImport(LamborghiniModule, BugattiModule, ...);
|
|
|
363
421
|
You can lazily `export` a `provider` or `module` by providing a `callback` _(it can also be an `async` callback)_ as shown below:
|
|
364
422
|
|
|
365
423
|
```ts
|
|
366
|
-
const
|
|
424
|
+
const SecureBankBranchModuleDef = new ProviderModuleDef({
|
|
367
425
|
identifier: 'SecureBankBranchModule',
|
|
368
426
|
providers: [BankBranchService],
|
|
369
427
|
exports: [BankBranchService],
|
|
370
428
|
});
|
|
371
429
|
|
|
372
|
-
const
|
|
430
|
+
const BankModuleDef = new ProviderModuleDef({
|
|
373
431
|
identifier: 'BankModule',
|
|
374
|
-
imports: [
|
|
432
|
+
imports: [SecureBankBranchModuleDef],
|
|
375
433
|
exports: [..., (importerModule) => {
|
|
376
434
|
// When the module having the identifier `UnknownBankModule` imports the `BankModule`
|
|
377
435
|
// it'll not be able to also import the `SecureBankBranchModule` as we are not returning it here.
|
|
@@ -411,13 +469,15 @@ By default the `AppModule` runs in "strict mode", a built-in mode which enforces
|
|
|
411
469
|
|
|
412
470
|
When invoking the [AppModule.register](https://adimarianmutu.github.io/x-injection/interfaces/IAppModule.html#register-1) `method` you can set the [\_strict](https://adimarianmutu.github.io/x-injection/interfaces/AppModuleOptions.html#_strict) property to `false` in order to permanentely disable those set of built-in rules.
|
|
413
471
|
|
|
414
|
-
>
|
|
472
|
+
> [!WARNING]
|
|
473
|
+
>
|
|
474
|
+
> _Do not open an `issue` if a bug or edge-case is caused by having the `strict` property disabled!_
|
|
415
475
|
|
|
416
476
|
#### Why you should not turn it off:
|
|
417
477
|
|
|
418
|
-
#####
|
|
478
|
+
##### isGlobal
|
|
419
479
|
|
|
420
|
-
|
|
480
|
+
When using the [isGlobal](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#isGlobal) property if a `module` is imported into the `AppModule` without having the `isGlobal` flag property set, it'll throw an error.
|
|
421
481
|
|
|
422
482
|
This may look redundant, but it may save you _(and your team)_ some hours of debugging in understanding why some `providers` are able to make their way into other `modules`. As those `providers` are now acting as _global_ `providers`.
|
|
423
483
|
|
|
@@ -439,7 +499,7 @@ const AnotherScopedModule = new ProviderModule({
|
|
|
439
499
|
|
|
440
500
|
const GlobalModule = new ProviderModule({
|
|
441
501
|
identifier: 'GlobalModule',
|
|
442
|
-
|
|
502
|
+
isGlobal: true,
|
|
443
503
|
imports: [AnotherScopedModule],
|
|
444
504
|
});
|
|
445
505
|
|
|
@@ -450,7 +510,7 @@ AppModule.register({
|
|
|
450
510
|
|
|
451
511
|
At first glance you may not spot/understand the issue there, but because the `GlobalModule` _(which is then imported into the `AppModule`)_ is _directly_ importing the `AnotherScopedModule`, it means that _all_ the `providers` of the `AnotherScopedModule` and `ScopedModule` _(because `AnotherScopedModule` also imports `ScopedModule`)_ will become accessible through your entire app!
|
|
452
512
|
|
|
453
|
-
Disabling `strict` mode removes this safeguard, allowing any module to be imported into the `AppModule` regardless of `
|
|
513
|
+
Disabling `strict` mode removes this safeguard, allowing any module to be imported into the `AppModule` regardless of `isGlobal`, increasing risk of bugs by exposing yourself to the above example.
|
|
454
514
|
|
|
455
515
|
## Unit Tests
|
|
456
516
|
|
|
@@ -468,13 +528,13 @@ class ApiService {
|
|
|
468
528
|
private async sendToLocation(user: User, location: any): Promise<any> {}
|
|
469
529
|
}
|
|
470
530
|
|
|
471
|
-
const
|
|
472
|
-
identifier:
|
|
531
|
+
const ApiModuleDefinition = new ProviderModuleDefinition({
|
|
532
|
+
identifier: 'ApiModule',
|
|
473
533
|
providers: [UserService, ApiService],
|
|
474
534
|
});
|
|
475
535
|
|
|
476
|
-
const
|
|
477
|
-
identifier:
|
|
536
|
+
const ApiModuleDefinitionMocked = ApiModule.clone({
|
|
537
|
+
identifier: 'ApiModuleMocked',
|
|
478
538
|
providers: [
|
|
479
539
|
{
|
|
480
540
|
provide: UserService,
|
|
@@ -492,7 +552,7 @@ const ApiModuleMocked = new ProviderModule({
|
|
|
492
552
|
});
|
|
493
553
|
```
|
|
494
554
|
|
|
495
|
-
Now what you have to do is just to provide the `
|
|
555
|
+
Now what you have to do is just to provide the `ApiModuleDefinitionMocked` instead of the `ApiModuleDefinition` 😎
|
|
496
556
|
|
|
497
557
|
## Documentation
|
|
498
558
|
|
|
@@ -500,6 +560,34 @@ Comprehensive, auto-generated documentation is available at:
|
|
|
500
560
|
|
|
501
561
|
👉 [https://adimarianmutu.github.io/x-injection/index.html](https://adimarianmutu.github.io/x-injection/index.html)
|
|
502
562
|
|
|
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
|
+
|
|
503
591
|
## ReactJS Implementation
|
|
504
592
|
|
|
505
593
|
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 ⚛️
|
|
@@ -514,4 +602,6 @@ Please ensure your contributions adhere to the project's code style. See the rep
|
|
|
514
602
|
|
|
515
603
|
---
|
|
516
604
|
|
|
517
|
-
>
|
|
605
|
+
> [!NOTE]
|
|
606
|
+
>
|
|
607
|
+
> **For questions, feature requests, or bug reports, feel free to open an [issue](https://github.com/AdiMarianMutu/x-injection/issues) on GitHub.**
|