Skip to content

Commit

Permalink
feat!: Identify injectables and tokens by reference instead of id to …
Browse files Browse the repository at this point in the history
…permit namespaces later

Note: Ids are still used for stuff like error messages.
  • Loading branch information
Iku-turso committed Feb 10, 2023
1 parent 6885495 commit 72487e2
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 101 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 } };
Expand All @@ -52,15 +48,19 @@ export default containerId => {
injectMany: nonDecoratedPrivateInjectMany,
});

const getSideEffectsArePrevented = injectable =>
sideEffectsArePrevented &&
injectable.causesSideEffects &&
!injectablesWithPermittedSideEffects.has(injectable);

const privateInject = privateInjectFor({
getRelatedInjectables,
alreadyInjected,
overridingInjectables,
instancesByInjectableMap,
injectableAndRegistrationContext,
injectMany: nonDecoratedPrivateInjectMany,
// Todo: get rid of function usage.
getSideEffectsArePrevented: () => sideEffectsArePrevented,
getSideEffectsArePrevented,
getDi: () => privateDi,
});

Expand All @@ -73,9 +73,9 @@ export default containerId => {
);

const registerSingle = registerSingleFor({
injectableMap,
injectableSet,
instancesByInjectableMap,
injectableIdsByInjectionToken,
injectablesByInjectionToken,
});

const purgeInstances = purgeInstancesFor({
Expand All @@ -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.
Expand Down Expand Up @@ -131,7 +131,7 @@ export default containerId => {
},

permitSideEffects: alias => {
getRelatedInjectables(alias)[0].permitSideEffects();
injectablesWithPermittedSideEffects.add(alias);
},

purge: purgeInstances,
Expand Down Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -23,9 +23,9 @@ export const deregisterFor =
const di = getDi();

const deregisterSingle = deregisterSingleFor({
injectableMap,
injectableSet,
injectableAndRegistrationContext,
injectableIdsByInjectionToken,
injectablesByInjectionToken,
overridingInjectables,
purgeInstances,
di,
Expand All @@ -38,43 +38,39 @@ 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 => {
injectableAndRegistrationContext.delete(injectable);
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);
};
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -100,7 +99,8 @@ const getInstance = ({
];

const instanceMap = instancesByInjectableMap.get(
injectableToBeInstantiated.id,
injectableToBeInstantiated.overriddenInjectable ||
injectableToBeInstantiated,
);

const minimalDi = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,24 @@ 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.`,
);
}

const originalInjectable = relatedInjectables[0];

overridingInjectables.set(originalInjectable.id, {
overridingInjectables.set(originalInjectable, {
...originalInjectable,
overriddenInjectable: originalInjectable,
causesSideEffects: false,
instantiate: instantiateStub,
});
};

export const unoverrideFor =
({ overridingInjectables }) =>
alias => {
overridingInjectables.delete(alias.id);
injectable => {
overridingInjectables.delete(injectable);
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ export const purgeInstancesFor =
alias => {
const injectable = getRelatedInjectables(alias)[0];

instancesByInjectableMap.get(injectable.id).clear();
instancesByInjectableMap.get(injectable).clear();
};
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
};

0 comments on commit 72487e2

Please sign in to comment.