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