@lppedd/di-wise-neo 0.8.0 → 0.9.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/dist/cjs/index.js CHANGED
@@ -1,5 +1,32 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
2
 
3
+ /**
4
+ * Type API.
5
+ */ /**
6
+ * Creates a type token.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const ISpell = createType<Spell>("Spell");
11
+ * ```
12
+ *
13
+ * @__NO_SIDE_EFFECTS__
14
+ */ function createType(typeName) {
15
+ const type = {
16
+ name: `Type<${typeName}>`,
17
+ inter: createType,
18
+ union: createType,
19
+ toString () {
20
+ return type.name;
21
+ }
22
+ };
23
+ return type;
24
+ }
25
+ // @internal
26
+ function isConstructor(token) {
27
+ return typeof token === "function";
28
+ }
29
+
3
30
  // @internal
4
31
  function assert(condition, message) {
5
32
  if (!condition) {
@@ -11,8 +38,10 @@ function expectNever(value) {
11
38
  throw new TypeError(tag(`unexpected value ${String(value)}`));
12
39
  }
13
40
  // @internal
14
- function throwUnregisteredError(token) {
15
- throw new Error(tag(`unregistered token ${token.name}`));
41
+ function throwUnregisteredError(token, name) {
42
+ const type = isConstructor(token) ? "class" : "token";
43
+ const spec = name !== undefined ? `[name=${name}]` : "";
44
+ throw new Error(tag(`unregistered ${type} ${token.name}${spec}`));
16
45
  }
17
46
  // @internal
18
47
  function throwExistingUnregisteredError(sourceToken, targetTokenOrError) {
@@ -76,6 +105,7 @@ class WeakRefMap {
76
105
  }
77
106
  this.myMap.delete(key);
78
107
  }
108
+ return undefined;
79
109
  }
80
110
  set(key, value) {
81
111
  invariant(!this.get(key));
@@ -290,33 +320,6 @@ const Scope = {
290
320
  Container: "Container"
291
321
  };
292
322
 
293
- /**
294
- * Type API.
295
- */ /**
296
- * Creates a type token.
297
- *
298
- * @example
299
- * ```ts
300
- * const ISpell = createType<Spell>("Spell");
301
- * ```
302
- *
303
- * @__NO_SIDE_EFFECTS__
304
- */ function createType(typeName) {
305
- const type = {
306
- name: `Type<${typeName}>`,
307
- inter: createType,
308
- union: createType,
309
- toString () {
310
- return type.name;
311
- }
312
- };
313
- return type;
314
- }
315
- // @internal
316
- function isConstructor(token) {
317
- return typeof token === "function";
318
- }
319
-
320
323
  // @internal
321
324
  function getTypeName(value) {
322
325
  switch(typeof value){
@@ -614,29 +617,30 @@ function isDisposable(value) {
614
617
  localOptional = optionalOrName;
615
618
  localName = name;
616
619
  }
617
- const registration = this.myTokenRegistry.get(token, localName);
620
+ let registration = this.myTokenRegistry.get(token, localName);
621
+ if (!registration && isConstructor(token)) {
622
+ registration = this.autoRegisterClass(token, localName);
623
+ }
618
624
  if (registration) {
619
625
  return this.resolveRegistration(token, registration, localName);
620
626
  }
621
- if (isConstructor(token)) {
622
- return this.instantiateClass(token, localOptional);
623
- }
624
- return optionalOrName ? undefined : throwUnregisteredError(token);
627
+ return localOptional ? undefined : throwUnregisteredError(token, localName);
625
628
  }
626
629
  resolveAll(token, optional) {
627
630
  this.checkDisposed();
628
- const registrations = this.myTokenRegistry.getAll(token);
631
+ let registrations = this.myTokenRegistry.getAll(token);
632
+ if (registrations.length === 0 && isConstructor(token)) {
633
+ const registration = this.autoRegisterClass(token);
634
+ if (registration) {
635
+ registrations = [
636
+ registration
637
+ ];
638
+ }
639
+ }
629
640
  if (registrations.length > 0) {
630
641
  return registrations //
631
642
  .map((registration)=>this.resolveRegistration(token, registration)).filter((value)=>value != null);
632
643
  }
633
- if (isConstructor(token)) {
634
- const instance = this.instantiateClass(token, optional);
635
- return instance === undefined // = could not resolve, but since it is optional
636
- ? [] : [
637
- instance
638
- ];
639
- }
640
644
  return optional ? [] : throwUnregisteredError(token);
641
645
  }
642
646
  dispose() {
@@ -686,37 +690,22 @@ function isDisposable(value) {
686
690
  throw e;
687
691
  }
688
692
  }
689
- instantiateClass(Class, optional) {
693
+ autoRegisterClass(Class, name) {
690
694
  const metadata = getMetadata(Class);
691
- if (metadata.autoRegister ?? this.myOptions.autoRegister) {
692
- // Temporarily set eagerInstantiate to false to avoid resolving the class two times:
693
- // one inside register(), and the other just below
695
+ const autoRegister = metadata.autoRegister ?? this.myOptions.autoRegister;
696
+ if (autoRegister && (name === undefined || metadata.name === name)) {
697
+ // Temporarily set eagerInstantiate to false to avoid potentially resolving
698
+ // the class inside register()
694
699
  const eagerInstantiate = metadata.eagerInstantiate;
695
700
  metadata.eagerInstantiate = false;
696
701
  try {
697
702
  this.register(Class);
698
- return this.resolve(Class);
703
+ return this.myTokenRegistry.get(Class, name ?? metadata.name);
699
704
  } finally{
700
705
  metadata.eagerInstantiate = eagerInstantiate;
701
706
  }
702
707
  }
703
- const scope = this.resolveScope(metadata.scope?.value);
704
- if (optional && scope === Scope.Container) {
705
- // It would not be possible to resolve the class in container scope,
706
- // as that would require prior registration.
707
- // However, since resolution is marked optional, we simply return undefined.
708
- return undefined;
709
- }
710
- assert(scope !== Scope.Container, `unregistered class ${Class.name} cannot be resolved in container scope`);
711
- const registration = {
712
- provider: metadata.provider,
713
- options: {
714
- scope: scope
715
- },
716
- dependencies: metadata.dependencies
717
- };
718
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
719
- return this.resolveScopedValue(registration, (args)=>new Class(...args));
708
+ return undefined;
720
709
  }
721
710
  resolveProviderValue(registration, provider) {
722
711
  assert(registration.provider === provider, "internal error: mismatching provider");
@@ -988,10 +977,22 @@ function updateParameterMetadata(decorator, target, propertyKey, parameterIndex,
988
977
  updateFn(dependency);
989
978
  }
990
979
  }
980
+ // Checks that a constructor or method parameter has only one injection decorator.
981
+ // For example, if both `@Inject` and `@Optional` are used, it becomes difficult to
982
+ // understand which one "wins", unless the user is aware of the decorator evaluation order.
983
+ //
984
+ // @internal
985
+ function checkSingleDecorator(dependency, target, propertyKey, parameterIndex) {
986
+ assert(!dependency.appliedBy, ()=>{
987
+ const where = propertyKey === undefined ? `${target.name} constructor` : `${target.constructor.name}.${String(propertyKey)}`;
988
+ return `${where} parameter ${parameterIndex} declares multiple injection decorators, but only one is allowed`;
989
+ });
990
+ }
991
991
 
992
992
  function Inject(token) {
993
993
  return function(target, propertyKey, parameterIndex) {
994
994
  updateParameterMetadata("Inject", target, propertyKey, parameterIndex, (dependency)=>{
995
+ checkSingleDecorator(dependency, target, propertyKey, parameterIndex);
995
996
  dependency.appliedBy = "Inject";
996
997
  dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
997
998
  });
@@ -1021,6 +1022,7 @@ function Inject(token) {
1021
1022
  function InjectAll(token) {
1022
1023
  return function(target, propertyKey, parameterIndex) {
1023
1024
  updateParameterMetadata("InjectAll", target, propertyKey, parameterIndex, (dependency)=>{
1025
+ checkSingleDecorator(dependency, target, propertyKey, parameterIndex);
1024
1026
  dependency.appliedBy = "InjectAll";
1025
1027
  dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
1026
1028
  });
@@ -1068,6 +1070,7 @@ function InjectAll(token) {
1068
1070
  function Optional(token) {
1069
1071
  return function(target, propertyKey, parameterIndex) {
1070
1072
  updateParameterMetadata("Optional", target, propertyKey, parameterIndex, (dependency)=>{
1073
+ checkSingleDecorator(dependency, target, propertyKey, parameterIndex);
1071
1074
  dependency.appliedBy = "Optional";
1072
1075
  dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
1073
1076
  });
@@ -1077,6 +1080,7 @@ function Optional(token) {
1077
1080
  function OptionalAll(token) {
1078
1081
  return function(target, propertyKey, parameterIndex) {
1079
1082
  updateParameterMetadata("OptionalAll", target, propertyKey, parameterIndex, (dependency)=>{
1083
+ checkSingleDecorator(dependency, target, propertyKey, parameterIndex);
1080
1084
  dependency.appliedBy = "OptionalAll";
1081
1085
  dependency.tokenRef = isTokenRef(token) ? token : forwardRef(()=>token);
1082
1086
  });
@@ -1142,7 +1146,7 @@ function OptionalAll(token) {
1142
1146
  const resolution = context.resolution;
1143
1147
  const dependentFrame = resolution.stack.peek();
1144
1148
  const dependentRef = dependentFrame && resolution.dependents.get(dependentFrame.provider);
1145
- function withContext(fn) {
1149
+ const runInContext = (fn)=>{
1146
1150
  if (useInjectionContext()) {
1147
1151
  return fn();
1148
1152
  }
@@ -1156,13 +1160,13 @@ function OptionalAll(token) {
1156
1160
  } finally{
1157
1161
  cleanups.forEach((cleanup)=>cleanup?.());
1158
1162
  }
1159
- }
1163
+ };
1160
1164
  return {
1161
- inject: (token, name)=>withContext(()=>inject(token, name)),
1162
- injectAll: (token)=>withContext(()=>injectAll(token)),
1163
- optional: (token, name)=>withContext(()=>optional(token, name)),
1164
- optionalAll: (token)=>withContext(()=>optionalAll(token)),
1165
- runInContext: withContext
1165
+ inject: (token, name)=>runInContext(()=>inject(token, name)),
1166
+ injectAll: (token)=>runInContext(()=>injectAll(token)),
1167
+ optional: (token, name)=>runInContext(()=>optional(token, name)),
1168
+ optionalAll: (token)=>runInContext(()=>optionalAll(token)),
1169
+ runInContext
1166
1170
  };
1167
1171
  }, "Injector");
1168
1172