diff --git a/packages/injectable/core/src/dependency-injection-container/createContainer.js b/packages/injectable/core/src/dependency-injection-container/createContainer.js index 1e25b9ab..afbef733 100644 --- a/packages/injectable/core/src/dependency-injection-container/createContainer.js +++ b/packages/injectable/core/src/dependency-injection-container/createContainer.js @@ -7,26 +7,22 @@ import { purgeInstancesFor } from './purgeInstances'; import { deregisterFor } from './deregister'; import { overrideFor, unoverrideFor } from './override'; import { decorateFor, decorateFunctionFor } from './decorate'; +import isInjectable from '../getInjectable/isInjectable'; export default containerId => { - let injectableMap = new Map(); + let injectableSet = new Set(); let overridingInjectables = new Map(); let sideEffectsArePrevented = false; let alreadyInjected = new Set(); + let injectablesWithPermittedSideEffects = new Set(); const injectableAndRegistrationContext = new Map(); const instancesByInjectableMap = new Map(); - const injectableIdsByInjectionToken = new Map(); - - const getInjectablesHavingInjectionToken = - getInjectablesHavingInjectionTokenFor({ - injectableMap, - injectableIdsByInjectionToken, - }); + const injectablesByInjectionToken = new Map(); const getRelatedInjectables = getRelatedInjectablesFor({ - injectableMap, - getInjectablesHavingInjectionToken, + injectablesByInjectionToken, + injectableSet, }); const containerRootContextItem = { injectable: { id: containerId } }; @@ -52,6 +48,11 @@ export default containerId => { injectMany: nonDecoratedPrivateInjectMany, }); + const getSideEffectsArePrevented = injectable => + sideEffectsArePrevented && + injectable.causesSideEffects && + !injectablesWithPermittedSideEffects.has(injectable); + const privateInject = privateInjectFor({ getRelatedInjectables, alreadyInjected, @@ -59,8 +60,7 @@ export default containerId => { instancesByInjectableMap, injectableAndRegistrationContext, injectMany: nonDecoratedPrivateInjectMany, - // Todo: get rid of function usage. - getSideEffectsArePrevented: () => sideEffectsArePrevented, + getSideEffectsArePrevented, getDi: () => privateDi, }); @@ -73,9 +73,9 @@ export default containerId => { ); const registerSingle = registerSingleFor({ - injectableMap, + injectableSet, instancesByInjectableMap, - injectableIdsByInjectionToken, + injectablesByInjectionToken, }); const purgeInstances = purgeInstancesFor({ @@ -87,9 +87,9 @@ export default containerId => { const deregister = deregisterFor({ injectMany: nonDecoratedPrivateInjectMany, - injectableMap, + injectableSet, injectableAndRegistrationContext, - injectableIdsByInjectionToken, + injectablesByInjectionToken, overridingInjectables, purgeInstances, // Todo: get rid of function usage. @@ -131,7 +131,7 @@ export default containerId => { }, permitSideEffects: alias => { - getRelatedInjectables(alias)[0].permitSideEffects(); + injectablesWithPermittedSideEffects.add(alias); }, purge: purgeInstances, @@ -171,35 +171,14 @@ export default containerId => { return publicDi; }; -const getInjectablesHavingInjectionTokenFor = - ({ injectableMap, injectableIdsByInjectionToken }) => - alias => { - const idSetForInjectablesHavingInjectionToken = - injectableIdsByInjectionToken.get(alias.id); - - const idsForInjectablesHavingInjectionToken = - idSetForInjectablesHavingInjectionToken - ? [...idSetForInjectablesHavingInjectionToken.values()] - : []; - - return idsForInjectablesHavingInjectionToken.map(injectableId => - injectableMap.get(injectableId), - ); - }; - const getRelatedInjectablesFor = - ({ injectableMap, getInjectablesHavingInjectionToken }) => - alias => { - const injectable = injectableMap.get(alias.id); - - const injectablesHavingInjectionToken = getInjectablesHavingInjectionToken( - alias, - ).filter(x => x.id !== alias.id); - - return injectable - ? [injectable, ...injectablesHavingInjectionToken] - : injectablesHavingInjectionToken; - }; + ({ injectablesByInjectionToken, injectableSet }) => + alias => + isInjectable(alias) + ? injectableSet.has(alias) + ? [alias] + : [] + : [...(injectablesByInjectionToken.get(alias)?.values() || [])]; export const registrationCallbackToken = getInjectionToken({ id: 'registration-callback-token', diff --git a/packages/injectable/core/src/dependency-injection-container/deregister.js b/packages/injectable/core/src/dependency-injection-container/deregister.js index 844579c3..eb24745c 100644 --- a/packages/injectable/core/src/dependency-injection-container/deregister.js +++ b/packages/injectable/core/src/dependency-injection-container/deregister.js @@ -3,9 +3,9 @@ import { deregistrationCallbackToken } from './createContainer'; export const deregisterFor = ({ injectMany, - injectableMap, + injectableSet, injectableAndRegistrationContext, - injectableIdsByInjectionToken, + injectablesByInjectionToken, overridingInjectables, purgeInstances, // Todo: get rid of function usage. @@ -23,9 +23,9 @@ export const deregisterFor = const di = getDi(); const deregisterSingle = deregisterSingleFor({ - injectableMap, + injectableSet, injectableAndRegistrationContext, - injectableIdsByInjectionToken, + injectablesByInjectionToken, overridingInjectables, purgeInstances, di, @@ -38,25 +38,23 @@ export const deregisterFor = export const deregisterSingleFor = ({ - injectableMap, + injectableSet, injectableAndRegistrationContext, - injectableIdsByInjectionToken, + injectablesByInjectionToken, overridingInjectables, purgeInstances, di, }) => - alias => { - const relatedInjectable = injectableMap.get(alias.id); - - if (!relatedInjectable) { + injectable => { + if (!injectableSet.has(injectable)) { throw new Error( - `Tried to deregister non-registered injectable "${alias.id}".`, + `Tried to deregister non-registered injectable "${injectable.id}".`, ); } [...injectableAndRegistrationContext.entries()] .filter(([, context]) => - context.find(contextItem => contextItem.injectable.id === alias.id), + context.find(contextItem => contextItem.injectable === injectable), ) .map(x => x[0]) .forEach(injectable => { @@ -64,17 +62,15 @@ export const deregisterSingleFor = di.deregister(injectable); }); - purgeInstances(alias); - - injectableMap.delete(alias.id); - - if (alias.injectionToken) { - const tokenId = alias.injectionToken.id; + purgeInstances(injectable); - const injectableIdSet = injectableIdsByInjectionToken.get(tokenId); + injectableSet.delete(injectable); - injectableIdSet.delete(alias.id); + if (injectable.injectionToken) { + injectablesByInjectionToken + .get(injectable.injectionToken) + .delete(injectable); } - overridingInjectables.delete(alias.id); + overridingInjectables.delete(injectable); }; diff --git a/packages/injectable/core/src/dependency-injection-container/inject.js b/packages/injectable/core/src/dependency-injection-container/inject.js index 5f2dcb70..d34e539d 100644 --- a/packages/injectable/core/src/dependency-injection-container/inject.js +++ b/packages/injectable/core/src/dependency-injection-container/inject.js @@ -31,15 +31,14 @@ export const privateInjectFor = ({ const originalInjectable = getRelatedInjectables(alias)[0]; - alreadyInjected.add(originalInjectable.id); + alreadyInjected.add(originalInjectable); - const overriddenInjectable = overridingInjectables.get( - originalInjectable.id, - ); + const overriddenInjectable = + overridingInjectables.get(originalInjectable); const injectable = overriddenInjectable || originalInjectable; - if (getSideEffectsArePrevented() && injectable.causesSideEffects) { + if (getSideEffectsArePrevented(injectable)) { throw new Error( `Tried to inject "${[...context, { injectable }] .map(item => item.injectable.id) @@ -100,7 +99,8 @@ const getInstance = ({ ]; const instanceMap = instancesByInjectableMap.get( - injectableToBeInstantiated.id, + injectableToBeInstantiated.overriddenInjectable || + injectableToBeInstantiated, ); const minimalDi = { diff --git a/packages/injectable/core/src/dependency-injection-container/override.js b/packages/injectable/core/src/dependency-injection-container/override.js index 90cdbac6..61f9dd5b 100644 --- a/packages/injectable/core/src/dependency-injection-container/override.js +++ b/packages/injectable/core/src/dependency-injection-container/override.js @@ -25,7 +25,7 @@ export const overrideFor = ); } - if (alreadyInjected.has(alias.id)) { + if (alreadyInjected.has(alias)) { throw new Error( `Tried to override injectable "${alias.id}", but it was already injected.`, ); @@ -33,8 +33,9 @@ export const overrideFor = const originalInjectable = relatedInjectables[0]; - overridingInjectables.set(originalInjectable.id, { + overridingInjectables.set(originalInjectable, { ...originalInjectable, + overriddenInjectable: originalInjectable, causesSideEffects: false, instantiate: instantiateStub, }); @@ -42,6 +43,6 @@ export const overrideFor = export const unoverrideFor = ({ overridingInjectables }) => - alias => { - overridingInjectables.delete(alias.id); + injectable => { + overridingInjectables.delete(injectable); }; diff --git a/packages/injectable/core/src/dependency-injection-container/purgeInstances.js b/packages/injectable/core/src/dependency-injection-container/purgeInstances.js index 2ca77c1b..a6ae5788 100644 --- a/packages/injectable/core/src/dependency-injection-container/purgeInstances.js +++ b/packages/injectable/core/src/dependency-injection-container/purgeInstances.js @@ -3,5 +3,5 @@ export const purgeInstancesFor = alias => { const injectable = getRelatedInjectables(alias)[0]; - instancesByInjectableMap.get(injectable.id).clear(); + instancesByInjectableMap.get(injectable).clear(); }; diff --git a/packages/injectable/core/src/dependency-injection-container/register.js b/packages/injectable/core/src/dependency-injection-container/register.js index f9e6e78e..3a12daa4 100644 --- a/packages/injectable/core/src/dependency-injection-container/register.js +++ b/packages/injectable/core/src/dependency-injection-container/register.js @@ -17,43 +17,31 @@ export const registerFor = }; export const registerSingleFor = - ({ - injectableMap, - instancesByInjectableMap, - injectableIdsByInjectionToken, - }) => - externalInjectable => { - let injectableId = externalInjectable.id; + ({ injectableSet, instancesByInjectableMap, injectablesByInjectionToken }) => + injectable => { + let injectableId = injectable.id; if (!injectableId) { throw new Error('Tried to register injectable without ID.'); } - if (injectableMap.has(injectableId)) { + if ([...injectableSet.values()].find(x => x.id === injectableId)) { throw new Error( `Tried to register multiple injectables for ID "${injectableId}"`, ); } - const internalInjectable = { - ...externalInjectable, + injectableSet.add(injectable); + instancesByInjectableMap.set(injectable, new Map()); - permitSideEffects: function () { - this.causesSideEffects = false; - }, - }; + if (injectable.injectionToken) { + const token = injectable.injectionToken; - injectableMap.set(internalInjectable.id, internalInjectable); - instancesByInjectableMap.set(internalInjectable.id, new Map()); + const injectablesSet = + injectablesByInjectionToken.get(token) || new Set(); - if (externalInjectable.injectionToken) { - const tokenId = externalInjectable.injectionToken.id; + injectablesSet.add(injectable); - const injectableIdsSet = - injectableIdsByInjectionToken.get(tokenId) || new Set(); - - injectableIdsSet.add(injectableId); - - injectableIdsByInjectionToken.set(tokenId, injectableIdsSet); + injectablesByInjectionToken.set(token, injectablesSet); } };