@adimm/x-injection 2.0.3 → 2.1.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 +72 -10
- package/dist/index.cjs +28 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -8
- package/dist/index.d.ts +12 -8
- package/dist/index.js +20 -12
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -41,6 +41,7 @@ xInjection <a href="https://www.npmjs.com/package/@adimm/x-injection" targe
|
|
|
41
41
|
- [Blueprints](#blueprints-1)
|
|
42
42
|
- [Import Behavior](#import-behavior)
|
|
43
43
|
- [isGlobal](#isglobal)
|
|
44
|
+
- [Definitions](#definitions)
|
|
44
45
|
- [Advanced Usage](#advanced-usage)
|
|
45
46
|
- [Events](#events)
|
|
46
47
|
- [Middlewares](#middlewares)
|
|
@@ -309,7 +310,7 @@ The below list shows them in order of priority _(highest to lowest)_, meaning th
|
|
|
309
310
|
3. By providing the [defaultScope](https://adimarianmutu.github.io/x-injection/interfaces/ProviderModuleOptions.html#defaultscope) property when initializing a `ProviderModule`:
|
|
310
311
|
```ts
|
|
311
312
|
const RainModuleDef = new ProviderModuleDef({
|
|
312
|
-
|
|
313
|
+
id: 'RainModule',
|
|
313
314
|
defaultScope: InjectionScope.Transient,
|
|
314
315
|
});
|
|
315
316
|
```
|
|
@@ -408,6 +409,7 @@ A [ProviderToken](https://adimarianmutu.github.io/x-injection/types/ProviderToke
|
|
|
408
409
|
const THEY_MAY_KNOW_PROVIDER = { provide: CONSTANT_SECRET_PROVIDER, useValue: 'Maybe they know?' };
|
|
409
410
|
|
|
410
411
|
// As you can see we now have 2 different ProviderTokens which use the same `provide` key.
|
|
412
|
+
// This means that resolving the `CONSTANT_SECRET_PROVIDER` will return an array of strings.
|
|
411
413
|
```
|
|
412
414
|
|
|
413
415
|
- [ProviderFactoryToken](https://adimarianmutu.github.io/x-injection/types/ProviderFactoryToken.html)
|
|
@@ -475,6 +477,45 @@ MyModule.update.addProvider(PizzaService, true);
|
|
|
475
477
|
MyModule.update.addProvider({ provide: HumanService, useClass: FemaleService });
|
|
476
478
|
```
|
|
477
479
|
|
|
480
|
+
Now you may probably ask yourself `If we import with the 'addImport' method a new module into an already imported module, will we have access to the providers of that newly imported module?`
|
|
481
|
+
|
|
482
|
+
The ansuwer is `yes`, we do have access thanks to the _dynamic_ nature of the `ProviderModule` class. Meaning that doing the following will work as expected:
|
|
483
|
+
|
|
484
|
+
```ts
|
|
485
|
+
const InnerModule = ProviderModule.create({
|
|
486
|
+
id: 'InnerModule',
|
|
487
|
+
providers: [FirstService],
|
|
488
|
+
exports: [FirstService],
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
const OuterModule = ProviderModule.create({
|
|
492
|
+
id: 'OuterModule',
|
|
493
|
+
imports: [InnerModule],
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
const UnknownModule = ProviderModule.create({
|
|
497
|
+
id: 'UnknownModule',
|
|
498
|
+
providers: [SecondService],
|
|
499
|
+
exports: [SecondService],
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
InnerModule.update.addImport(UnknownModule, true); // Don't forget to provide `true` to the `addToExports` optional parameter!
|
|
503
|
+
|
|
504
|
+
const secondService = OuterModule.get(SecondService);
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
The `OuterModule` has now access to the `UnknownModule` exports because it has been _dynamically_ imported _(later at run-time)_ into the `InnerModule` _(which has been imported into `OuterModule` during the `bootstrap` phase)_
|
|
508
|
+
|
|
509
|
+
Basically what happens is that when a `module` is imported, it takes care of _notify_ the `host` module if its _definiton_ changed.
|
|
510
|
+
|
|
511
|
+
> [!WARNING]
|
|
512
|
+
>
|
|
513
|
+
> This is a very powerful feature which comes in with some costs, _most of the time negligible_, but if you have an app which has thousand and thousand of `modules` doing this type of _dynamic_ behavior, you may incur in some performance issues which will require proper design to keep under control.
|
|
514
|
+
>
|
|
515
|
+
> _Most of the times the best solution is to leverage the nature of `blueprints`._
|
|
516
|
+
|
|
517
|
+
---
|
|
518
|
+
|
|
478
519
|
Sometimes you may actually want to _lazy_ import a `module` from a _file_, this can be done very easily with `xInjection`:
|
|
479
520
|
|
|
480
521
|
```ts
|
|
@@ -490,25 +531,21 @@ Sometimes you may actually want to _lazy_ import a `module` from a _file_, this
|
|
|
490
531
|
>
|
|
491
532
|
> 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)_
|
|
492
533
|
|
|
493
|
-
Keep reading to understand how you can defer initialization of the modules
|
|
534
|
+
Keep reading to understand how you can defer initialization of the `modules` by using `blueprints`.
|
|
494
535
|
|
|
495
536
|
## Blueprints
|
|
496
537
|
|
|
497
538
|
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
539
|
|
|
499
|
-
|
|
540
|
+
> To better understand the above concept; imagine the `blueprint` as being a _dormant_ _(static)_ `module` which is not fully awake _(dynamic)_ till it is actually _imported_ into a `module`.
|
|
500
541
|
|
|
501
542
|
### Import Behavior
|
|
502
543
|
|
|
503
544
|
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
545
|
|
|
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)_.
|
|
506
|
-
|
|
507
546
|
> [!NOTE]
|
|
508
547
|
>
|
|
509
|
-
>
|
|
510
|
-
|
|
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_.
|
|
548
|
+
> Therefore it is 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_.
|
|
512
549
|
|
|
513
550
|
### isGlobal
|
|
514
551
|
|
|
@@ -522,7 +559,7 @@ Now you can decide when to import it into the `AppModule` by doing `AppModule.ad
|
|
|
522
559
|
|
|
523
560
|
---
|
|
524
561
|
|
|
525
|
-
I highly recommend to take advantage of the `blueprints` nature in order to plan-ahead your `modules
|
|
562
|
+
I highly recommend to take advantage of the `blueprints` nature in order to plan-ahead your `modules`;
|
|
526
563
|
|
|
527
564
|
Why?
|
|
528
565
|
|
|
@@ -530,6 +567,31 @@ Why?
|
|
|
530
567
|
- To reuse module _definitions across_ different parts of your application while maintaining isolated instances. _(when possible/applicable)_
|
|
531
568
|
- To _compose modules flexibly_, allowing you to adjust module dependencies dynamically before instantiation.
|
|
532
569
|
|
|
570
|
+
### Definitions
|
|
571
|
+
|
|
572
|
+
After you have provided the _initial_ `definitons` of a `blueprint`, you can always modify them with the [updateDefinition](https://adimarianmutu.github.io/x-injection/classes/ProviderModuleBlueprint.html#updatedefinition) `method`.
|
|
573
|
+
|
|
574
|
+
> [!NOTE]
|
|
575
|
+
>
|
|
576
|
+
> Updating the `definitions` of a `blueprint` after has been _imported_ into a `module` will **_not_** propagate those changes to the `module` where it has been imported.
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
This means that we can actually _leverage_ the `blueprints` nature to _defer_ the actual initialization of a `module` by doing so:
|
|
581
|
+
|
|
582
|
+
```ts
|
|
583
|
+
const UserModuleBp = ProviderModule.blueprint({
|
|
584
|
+
id: 'UserModule',
|
|
585
|
+
...
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
// Later in your code
|
|
589
|
+
|
|
590
|
+
const UserModule = ProviderModule.create(UserModuleBp);
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
The `UserModule` will be created only when _necessary_ and it'll use the same exact definitons which are available into the `UserModuleBp` at the time of the `create` invokation.
|
|
594
|
+
|
|
533
595
|
## Advanced Usage
|
|
534
596
|
|
|
535
597
|
> [!WARNING]
|
|
@@ -746,7 +808,7 @@ const ApiModuleBp = new ProviderModule.blueprint({
|
|
|
746
808
|
|
|
747
809
|
// Clone returns a `deep` clone and wraps all the `methods` to break their reference!
|
|
748
810
|
const ApiModuleBpMocked = ApiModuleBp.clone().updateDefinition({
|
|
749
|
-
|
|
811
|
+
id: 'ApiModuleMocked',
|
|
750
812
|
providers: [
|
|
751
813
|
{
|
|
752
814
|
provide: UserService,
|
package/dist/index.cjs
CHANGED
|
@@ -17,11 +17,11 @@ var e, t = Object.defineProperty, i = Object.getOwnPropertyDescriptor, o = Objec
|
|
|
17
17
|
InjectFromBase: () => N,
|
|
18
18
|
Injectable: () => q,
|
|
19
19
|
InjectionError: () => I,
|
|
20
|
-
InjectionProviderModuleDisposedError: () =>
|
|
20
|
+
InjectionProviderModuleDisposedError: () => w,
|
|
21
21
|
InjectionProviderModuleError: () => b,
|
|
22
22
|
InjectionProviderModuleMissingIdentifierError: () => x,
|
|
23
23
|
InjectionProviderModuleMissingProviderError: () => E,
|
|
24
|
-
InjectionProviderModuleUnknownProviderError: () =>
|
|
24
|
+
InjectionProviderModuleUnknownProviderError: () => P,
|
|
25
25
|
InjectionScope: () => u,
|
|
26
26
|
MiddlewareType: () => a,
|
|
27
27
|
MultiInject: () => U,
|
|
@@ -194,7 +194,7 @@ var g, I = class e extends Error {
|
|
|
194
194
|
} catch {}
|
|
195
195
|
super(`{ProviderModule.${i}} => ${t}`);
|
|
196
196
|
}
|
|
197
|
-
},
|
|
197
|
+
}, P = class e extends b {
|
|
198
198
|
static {
|
|
199
199
|
n(this, "InjectionProviderModuleUnknownProviderError");
|
|
200
200
|
}
|
|
@@ -202,7 +202,7 @@ var g, I = class e extends Error {
|
|
|
202
202
|
constructor(e, t) {
|
|
203
203
|
super(e, `The [${h.providerTokenToString(t)}] provider is of an unknown type!`);
|
|
204
204
|
}
|
|
205
|
-
},
|
|
205
|
+
}, w = class e extends b {
|
|
206
206
|
static {
|
|
207
207
|
n(this, "InjectionProviderModuleDisposedError");
|
|
208
208
|
}
|
|
@@ -270,7 +270,7 @@ var g, I = class e extends Error {
|
|
|
270
270
|
return t.useFactory(...i);
|
|
271
271
|
}))), "bind")
|
|
272
272
|
} ], {bind: o} = i.find((({providerTypeMatches: t}) => t(e))) ?? {};
|
|
273
|
-
if (!o) throw new
|
|
273
|
+
if (!o) throw new P(this.providerModule, e);
|
|
274
274
|
const r = h.isProviderIdentifier(e);
|
|
275
275
|
let s = o();
|
|
276
276
|
if (h.isValueToken(e) || r || (s = this.setBindingScope(e, s)), r) return;
|
|
@@ -388,7 +388,7 @@ var g, I = class e extends Error {
|
|
|
388
388
|
return this.providerModule.moduleContainer;
|
|
389
389
|
}
|
|
390
390
|
get subscribe() {
|
|
391
|
-
if (null === this.event$) throw new
|
|
391
|
+
if (null === this.event$) throw new w(this.providerModule);
|
|
392
392
|
return this.event$.subscribe.bind(this.event$);
|
|
393
393
|
}
|
|
394
394
|
moduleDef;
|
|
@@ -444,22 +444,24 @@ var g, I = class e extends Error {
|
|
|
444
444
|
this.addProvider(i, t);
|
|
445
445
|
}
|
|
446
446
|
removeImport(e) {
|
|
447
|
-
|
|
448
|
-
if (!
|
|
449
|
-
this.
|
|
450
|
-
|
|
451
|
-
this.
|
|
447
|
+
const t = g.isModule(e) ? e : this.getImportedModuleById(e);
|
|
448
|
+
if (!t) return !1;
|
|
449
|
+
if (!1 === this.providerModule.middlewaresManager.applyMiddlewares(a.BeforeRemoveImport, t)) return !1;
|
|
450
|
+
this.unsubscribeFromImportedModuleEvents(t);
|
|
451
|
+
return this.providerModule.importedModuleContainers.get(t).dispose(), this.providerModule.importedModuleContainers.delete(t),
|
|
452
|
+
this.moduleDef.imports.delete(t), this.emitEventSafely({
|
|
452
453
|
type: p.ImportRemoved,
|
|
453
|
-
change:
|
|
454
|
-
}), this.removeFromExports(
|
|
454
|
+
change: t
|
|
455
|
+
}), this.removeFromExports(t), !0;
|
|
455
456
|
}
|
|
456
457
|
removeProvider(e) {
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
this.
|
|
458
|
+
const t = h.isProviderIdentifier(e) ? this.getProviderByIdentifier(e) : e;
|
|
459
|
+
if (!t) return !1;
|
|
460
|
+
return !1 !== this.providerModule.middlewaresManager.applyMiddlewares(a.BeforeRemoveProvider, t) && (this.moduleDef.providers.delete(t),
|
|
461
|
+
this.moduleContainer.container.unbindSync(h.toProviderIdentifier(t)), this.emitEventSafely({
|
|
460
462
|
type: p.ProviderRemoved,
|
|
461
|
-
change:
|
|
462
|
-
}), this.removeFromExports(
|
|
463
|
+
change: t
|
|
464
|
+
}), this.removeFromExports(t), !0);
|
|
463
465
|
}
|
|
464
466
|
removeFromExports(e) {
|
|
465
467
|
if (!this.moduleDef.exports.has(e)) return !1;
|
|
@@ -475,6 +477,12 @@ var g, I = class e extends Error {
|
|
|
475
477
|
change: e
|
|
476
478
|
}), !0);
|
|
477
479
|
}
|
|
480
|
+
getImportedModuleById(e) {
|
|
481
|
+
return this.moduleDef.imports.values().find((t => t.id === e));
|
|
482
|
+
}
|
|
483
|
+
getProviderByIdentifier(e) {
|
|
484
|
+
return this.moduleDef.providers.values().find((t => h.toProviderIdentifier(t) === e));
|
|
485
|
+
}
|
|
478
486
|
emitEventSafely(e) {
|
|
479
487
|
if (!this.emittingModules.has(this.providerModule)) try {
|
|
480
488
|
this.emittingModules.add(this.providerModule), this.event$.emit(e);
|
|
@@ -535,7 +543,7 @@ var g, I = class e extends Error {
|
|
|
535
543
|
i ? i.push(t) : this.middlewaresMap.set(e, [ t ]);
|
|
536
544
|
}
|
|
537
545
|
applyMiddlewares(e, ...t) {
|
|
538
|
-
if (null === this.middlewaresMap) throw new
|
|
546
|
+
if (null === this.middlewaresMap) throw new w(this.providerModule);
|
|
539
547
|
const i = this.middlewaresMap.get(e);
|
|
540
548
|
switch (e) {
|
|
541
549
|
case a.BeforeAddImport:
|
|
@@ -709,7 +717,7 @@ var g, I = class e extends Error {
|
|
|
709
717
|
if (!this.options.id || 0 === this.options.id.toString().trim().length) throw new x(this);
|
|
710
718
|
}
|
|
711
719
|
throwIfDisposed() {
|
|
712
|
-
if (this.isDisposed) throw new
|
|
720
|
+
if (this.isDisposed) throw new w(this);
|
|
713
721
|
}
|
|
714
722
|
};
|
|
715
723
|
|