@lppedd/di-wise-neo 0.5.3 → 0.6.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 CHANGED
@@ -3,7 +3,8 @@
3
3
  <p align="center">Lightweight, type-safe, flexible dependency injection library for TypeScript and JavaScript</p>
4
4
  <div align="center">
5
5
 
6
- [![test](https://img.shields.io/github/actions/workflow/status/lppedd/di-wise-neo/test.yml.svg?branch=main)](https://github.com/lppedd/di-wise-neo/actions/workflows/test.yml)
6
+ [![build](https://img.shields.io/github/actions/workflow/status/lppedd/di-wise-neo/test.yml.svg?branch=main)](https://github.com/lppedd/di-wise-neo/actions/workflows/test.yml)
7
+ [![coverage](https://img.shields.io/codecov/c/github/lppedd/di-wise-neo/main?token=R9XZFTQ0BA)](https://app.codecov.io/gh/lppedd/di-wise-neo/tree/main/src)
7
8
  [![npm](https://img.shields.io/npm/v/@lppedd/di-wise-neo?color=%23de1f1f&logo=npm)](https://www.npmjs.com/package/@lppedd/di-wise-neo)
8
9
  [![npm gzipped size](https://img.shields.io/bundlejs/size/@lppedd/di-wise-neo)](https://bundlejs.com/?q=@lppedd/di-wise-neo)
9
10
  [![license](https://img.shields.io/github/license/lppedd/di-wise-neo?color=blue)](https://github.com/lppedd/di-wise-neo/blob/main/LICENSE)
@@ -19,7 +20,6 @@
19
20
 
20
21
  ## Table of Contents
21
22
 
22
- - [Why yet another library](#why-yet-another-library)
23
23
  - [Installation](#installation)
24
24
  - [API reference](#api-reference)
25
25
  - [Ergonomics & Requirements](#ergonomics)
@@ -79,7 +79,7 @@ the use of ECMAScript Stage 3 decorators, which do not support decorating method
79
79
  So what's the right move? Forking the best pick and refactoring it to suite my
80
80
  production needs.
81
81
 
82
- ## Installation
82
+ ### Installation
83
83
 
84
84
  ```sh
85
85
  npm i @lppedd/di-wise-neo
@@ -93,11 +93,11 @@ pnpm add @lppedd/di-wise-neo
93
93
  yarn add @lppedd/di-wise-neo
94
94
  ```
95
95
 
96
- ## API reference
96
+ ### API reference
97
97
 
98
98
  You can find the complete API reference at [lppedd.github.io/di-wise-neo](https://lppedd.github.io/di-wise-neo)
99
99
 
100
- ## Ergonomics
100
+ ### Ergonomics
101
101
 
102
102
  - Does **not** depend on other libraries
103
103
  - Does **not** use [reflect-metadata](https://www.npmjs.com/package/reflect-metadata) to drive decorators
@@ -281,7 +281,9 @@ The container will translate `TaskID` to `PID` before resolving the value.
281
281
 
282
282
  The primary way to perform dependency injection in **di-wise-neo** is through
283
283
  functions like `inject(T)`, `injectAll(T)`, `optional(T)`, and `optionalAll(T)`.
284
- This approach is recommended because it preserves full type safety.
284
+
285
+ > [!TIP]
286
+ > Using injection functions is recommended because it preserves type safety.
285
287
 
286
288
  ### Injection context
287
289
 
@@ -473,10 +475,11 @@ In this example, `ExtensionContext` will be registered with **Resolution** scope
473
475
 
474
476
  ### `@AutoRegister`
475
477
 
476
- Enables automatic registration of the decorated class if it has not been registered explicitly.
478
+ Enables automatic registration of the decorated class when it is resolved,
479
+ if it has not been registered beforehand.
477
480
 
478
481
  ```ts
479
- @AutoRegister
482
+ @AutoRegister()
480
483
  export class ExtensionContext {
481
484
  /* ... */
482
485
  }
@@ -487,19 +490,20 @@ container.resolve(ExtensionContext);
487
490
 
488
491
  ### `@EagerInstantiate`
489
492
 
490
- Marks a class for eager instantiation when registered with **Container** scope.
493
+ Sets the default class scope to **Container** and marks the class for eager instantiation
494
+ upon registration.
491
495
 
492
496
  This causes the container to immediately create and cache the instance of the class
493
497
  at registration time, instead of deferring instantiation until the first resolution.
494
498
 
495
499
  ```ts
496
- @EagerInstantiate
497
- @Scoped(Scope.Container)
500
+ @EagerInstantiate()
498
501
  export class ExtensionContext {
499
502
  /* ... */
500
503
  }
501
504
 
502
- // The container immediately creates and caches the instance
505
+ // ExtensionContext is registered with Container scope,
506
+ // and an instance is immediately created and cached by the container
503
507
  container.register(ExtensionContext);
504
508
  ```
505
509
 
@@ -422,7 +422,7 @@ declare function createContainer(options?: Partial<ContainerOptions>): Container
422
422
  *
423
423
  * @example
424
424
  * ```ts
425
- * @AutoRegister
425
+ * @AutoRegister()
426
426
  * class Wizard {}
427
427
  *
428
428
  * const wizard = container.resolve(Wizard);
@@ -431,28 +431,28 @@ declare function createContainer(options?: Partial<ContainerOptions>): Container
431
431
  *
432
432
  * @__NO_SIDE_EFFECTS__
433
433
  */
434
- declare function AutoRegister<Ctor extends Constructor<any>>(Class: Ctor): void;
434
+ declare function AutoRegister(): ClassDecorator;
435
435
 
436
436
  /**
437
- * Class decorator that enables eager instantiation of a class when it is registered
438
- * in the container with `Scope.Container`.
437
+ * Class decorator that sets the class scope to **Container** and enables
438
+ * eager instantiation when the class is registered in the container.
439
439
  *
440
440
  * This causes the container to immediately create and cache the instance of the class,
441
441
  * instead of deferring instantiation until the first resolution.
442
442
  *
443
443
  * @example
444
444
  * ```ts
445
- * @EagerInstantiate
446
- * @Scoped(Scope.Container)
445
+ * @EagerInstantiate()
447
446
  * class Wizard {}
448
447
  *
449
- * // A Wizard instance is immediately created and cached by the container
448
+ * // Wizard is registered with Container scope, and an instance
449
+ * // is immediately created and cached by the container
450
450
  * const wizard = container.register(Wizard);
451
451
  * ```
452
452
  *
453
453
  * @__NO_SIDE_EFFECTS__
454
454
  */
455
- declare function EagerInstantiate<Ctor extends Constructor<any>>(Class: Ctor): void;
455
+ declare function EagerInstantiate(): ClassDecorator;
456
456
 
457
457
  interface TokensRef<Value = any> {
458
458
  readonly getRefTokens: () => Set<Token<Value>>;
package/dist/cjs/index.js CHANGED
@@ -536,7 +536,7 @@ function isDisposable(value) {
536
536
  // The provider is of type ClassProvider, initialized by getMetadata
537
537
  provider: metadata.provider,
538
538
  options: {
539
- scope: metadata.scope ?? this.myOptions.defaultScope
539
+ scope: metadata.scope?.value ?? this.myOptions.defaultScope
540
540
  },
541
541
  dependencies: metadata.dependencies
542
542
  };
@@ -553,7 +553,7 @@ function isDisposable(value) {
553
553
  }
554
554
  // Eager-instantiate only if the class is container-scoped
555
555
  if (metadata.eagerInstantiate && registration.options?.scope === Scope.Container) {
556
- this.resolve(Class);
556
+ this.resolveProviderValue(registration, registration.provider);
557
557
  }
558
558
  } else {
559
559
  const [token, provider, options] = args;
@@ -564,7 +564,7 @@ function isDisposable(value) {
564
564
  options: {
565
565
  // The explicit registration options override what is specified
566
566
  // via class decorators (e.g., @Scoped)
567
- scope: metadata.scope ?? this.myOptions.defaultScope,
567
+ scope: metadata.scope?.value ?? this.myOptions.defaultScope,
568
568
  ...options
569
569
  },
570
570
  dependencies: metadata.dependencies
@@ -572,7 +572,7 @@ function isDisposable(value) {
572
572
  this.myTokenRegistry.set(token, registration);
573
573
  // Eager-instantiate only if the provided class is container-scoped
574
574
  if (metadata.eagerInstantiate && registration.options?.scope === Scope.Container) {
575
- this.resolve(token);
575
+ this.resolveProviderValue(registration, registration.provider);
576
576
  }
577
577
  } else {
578
578
  if (isExistingProvider(provider)) {
@@ -688,7 +688,7 @@ function isDisposable(value) {
688
688
  metadata.eagerInstantiate = eagerInstantiate;
689
689
  }
690
690
  }
691
- const scope = this.resolveScope(metadata.scope);
691
+ const scope = this.resolveScope(metadata.scope?.value);
692
692
  if (optional && scope === Scope.Container) {
693
693
  // It would not be possible to resolve the class in container scope,
694
694
  // as that would require prior registration.
@@ -878,7 +878,7 @@ function isDisposable(value) {
878
878
  *
879
879
  * @example
880
880
  * ```ts
881
- * @AutoRegister
881
+ * @AutoRegister()
882
882
  * class Wizard {}
883
883
  *
884
884
  * const wizard = container.resolve(Wizard);
@@ -886,32 +886,45 @@ function isDisposable(value) {
886
886
  * ```
887
887
  *
888
888
  * @__NO_SIDE_EFFECTS__
889
- */ function AutoRegister(Class) {
890
- const metadata = getMetadata(Class);
891
- metadata.autoRegister = true;
889
+ */ function AutoRegister() {
890
+ return function(Class) {
891
+ const metadata = getMetadata(Class);
892
+ metadata.autoRegister = true;
893
+ };
892
894
  }
893
895
 
894
896
  /**
895
- * Class decorator that enables eager instantiation of a class when it is registered
896
- * in the container with `Scope.Container`.
897
+ * Class decorator that sets the class scope to **Container** and enables
898
+ * eager instantiation when the class is registered in the container.
897
899
  *
898
900
  * This causes the container to immediately create and cache the instance of the class,
899
901
  * instead of deferring instantiation until the first resolution.
900
902
  *
901
903
  * @example
902
904
  * ```ts
903
- * @EagerInstantiate
904
- * @Scoped(Scope.Container)
905
+ * @EagerInstantiate()
905
906
  * class Wizard {}
906
907
  *
907
- * // A Wizard instance is immediately created and cached by the container
908
+ * // Wizard is registered with Container scope, and an instance
909
+ * // is immediately created and cached by the container
908
910
  * const wizard = container.register(Wizard);
909
911
  * ```
910
912
  *
911
913
  * @__NO_SIDE_EFFECTS__
912
- */ function EagerInstantiate(Class) {
913
- const metadata = getMetadata(Class);
914
- metadata.eagerInstantiate = true;
914
+ */ function EagerInstantiate() {
915
+ return function(Class) {
916
+ const metadata = getMetadata(Class);
917
+ const currentScope = metadata.scope;
918
+ assert(!currentScope || currentScope.value === Scope.Container, ()=>{
919
+ const { value, appliedBy } = currentScope;
920
+ return `class ${Class.name}: Scope.${value} was already set by @${appliedBy},\n ` + `but @EagerInstantiate is trying to set a conflicting Scope.Container.\n ` + `Only one decorator should set the class scope, or all must agree on the same value.`;
921
+ });
922
+ metadata.eagerInstantiate = true;
923
+ metadata.scope = {
924
+ value: Scope.Container,
925
+ appliedBy: "EagerInstantiate"
926
+ };
927
+ };
915
928
  }
916
929
 
917
930
  function forwardRef(token) {
@@ -1043,7 +1056,16 @@ function OptionalAll(token) {
1043
1056
  */ function Scoped(scope) {
1044
1057
  return function(Class) {
1045
1058
  const metadata = getMetadata(Class);
1046
- metadata.scope = scope;
1059
+ const currentScope = metadata.scope;
1060
+ assert(!currentScope || currentScope.value === scope, ()=>{
1061
+ const { value, appliedBy } = currentScope;
1062
+ const by = appliedBy === "Scoped" ? `another @${appliedBy} decorator` : `@${appliedBy}`;
1063
+ return `class ${Class.name}: Scope.${value} was already set by ${by},\n ` + `but @Scoped is trying to set a conflicting Scope.${scope}.\n ` + `Only one decorator should set the class scope, or all must agree on the same value.`;
1064
+ });
1065
+ metadata.scope = {
1066
+ value: scope,
1067
+ appliedBy: "Scoped"
1068
+ };
1047
1069
  };
1048
1070
  }
1049
1071