@ogre-tools/injectable
Advanced tools
Comparing version 1.4.2 to 1.5.0
/// <reference types="jest" /> | ||
declare module '@ogre-tools/injectable' { | ||
type Awaited<TMaybePromise> = TMaybePromise extends PromiseLike<infer TValue> | ||
? TValue | ||
: TMaybePromise; | ||
export interface DependencyInjectionContainer { | ||
@@ -26,2 +22,8 @@ inject: < | ||
: TMaybePromiseInstance; | ||
purge: <TInjectable extends Injectable<any, any, any>>( | ||
injectableKey: TInjectable, | ||
) => void; | ||
runSetups: () => void; | ||
} | ||
@@ -52,2 +54,4 @@ | ||
setup?: (di: DependencyInjectionContainer) => void; | ||
getDependencies: ( | ||
@@ -54,0 +58,0 @@ di?: DependencyInjectionContainer, |
{ | ||
"name": "@ogre-tools/injectable", | ||
"private": false, | ||
"version": "1.4.2", | ||
"version": "1.5.0", | ||
"description": "A brutal dependency injection container", | ||
@@ -25,3 +25,3 @@ "repository": { | ||
}, | ||
"gitHead": "78746cbc745e83c905ec1c20e17988a11c9c5bc4", | ||
"gitHead": "a9ce35b359658eb777270ca8fea2ce435949f6be", | ||
"bugs": { | ||
@@ -28,0 +28,0 @@ "url": "https://github.com/ogre-works/ogre-tools/issues" |
@@ -22,5 +22,10 @@ import conforms from 'lodash/fp/conforms'; | ||
let setupsHaveBeenRan = false; | ||
const singletonInstanceMap = new Map(); | ||
const scopedTransientMap = new Map(); | ||
const getLifecycle = injectableKey => | ||
getInjectable({ | ||
injectables, | ||
alias: injectableKey, | ||
di, | ||
}).lifecycle; | ||
const di = { | ||
@@ -61,4 +66,2 @@ inject: (alias, instantiationParameter) => { | ||
di, | ||
singletonInstanceMap, | ||
scopedTransientMap, | ||
}); | ||
@@ -156,8 +159,13 @@ }, | ||
getLifecycle: injectableKey => | ||
getInjectable({ | ||
getLifecycle, | ||
purge: injectableKey => { | ||
const injectable = getInjectable({ | ||
injectables, | ||
di, | ||
alias: injectableKey, | ||
di, | ||
}).lifecycle, | ||
}); | ||
getLifecycle(injectableKey).purge(injectable); | ||
}, | ||
}; | ||
@@ -164,0 +172,0 @@ |
@@ -878,2 +878,96 @@ import createContainer from './createContainer'; | ||
}); | ||
it('given an injectable is singleton and injected but purged, when injected, injects new instance', () => { | ||
const singletonInjectable = { | ||
instantiate: () => ({}), | ||
lifecycle: lifecycleEnum.singleton, | ||
}; | ||
const di = getDi(singletonInjectable); | ||
const actual1 = di.inject(singletonInjectable); | ||
di.purge(singletonInjectable); | ||
const actual2 = di.inject(singletonInjectable); | ||
expect(actual1).not.toBe(actual2); | ||
}); | ||
it('given an injectable is singleton and injected but unrelated singleton is purged, when injected, injects singleton', () => { | ||
const singletonInjectable = { | ||
instantiate: () => ({}), | ||
lifecycle: lifecycleEnum.singleton, | ||
}; | ||
const unrelatedSingletonInjectable = { | ||
instantiate: () => ({}), | ||
lifecycle: lifecycleEnum.singleton, | ||
}; | ||
const di = getDi(singletonInjectable, unrelatedSingletonInjectable); | ||
const actual1 = di.inject(singletonInjectable); | ||
di.purge(unrelatedSingletonInjectable); | ||
const actual2 = di.inject(singletonInjectable); | ||
expect(actual1).toBe(actual2); | ||
}); | ||
it('given an injectable is transient, when purged, throws', () => { | ||
const transientInjectable = { | ||
instantiate: () => ({}), | ||
lifecycle: lifecycleEnum.transient, | ||
}; | ||
const di = getDi(transientInjectable); | ||
expect(() => { | ||
di.purge(transientInjectable); | ||
}).toThrow('Tried to purge injectable with transient lifecycle.'); | ||
}); | ||
it('given an injectable is scoped transient and injected but purged, when injected, injects new instance', () => { | ||
const scopedTransientInjectable = { | ||
instantiate: () => ({}), | ||
lifecycle: lifecycleEnum.scopedTransient(() => 'some-scope'), | ||
}; | ||
const di = getDi(scopedTransientInjectable); | ||
const actual1 = di.inject(scopedTransientInjectable); | ||
di.purge(scopedTransientInjectable); | ||
const actual2 = di.inject(scopedTransientInjectable); | ||
expect(actual1).not.toBe(actual2); | ||
}); | ||
it('given an injectable is scoped transient and injected but unrelated scoped transient is purged, when injected, injects same instance', () => { | ||
const scopedTransientInjectable = { | ||
instantiate: () => ({}), | ||
lifecycle: lifecycleEnum.scopedTransient(() => 'some-scope'), | ||
}; | ||
const unrelatedScopedTransientInjectable = { | ||
instantiate: () => ({}), | ||
lifecycle: lifecycleEnum.scopedTransient(() => 'some-scope'), | ||
}; | ||
const di = getDi( | ||
scopedTransientInjectable, | ||
unrelatedScopedTransientInjectable, | ||
); | ||
const actual1 = di.inject(scopedTransientInjectable); | ||
di.purge(unrelatedScopedTransientInjectable); | ||
const actual2 = di.inject(scopedTransientInjectable); | ||
expect(actual1).toBe(actual2); | ||
}); | ||
}); | ||
@@ -880,0 +974,0 @@ |
@@ -29,75 +29,84 @@ import toPairs from 'lodash/fp/toPairs'; | ||
const iife = callback => callback(); | ||
export default { | ||
singleton: { | ||
key: 'singleton', | ||
singleton: iife(() => { | ||
const singletonInstanceMap = new Map(); | ||
getInstance: ({ | ||
injectable, | ||
instantiationParameter, | ||
di, | ||
singletonInstanceMap, | ||
}) => { | ||
if (instantiationParameter) { | ||
throw new Error( | ||
`Tried to inject singleton "${injectable.id}" with instantiation parameters.`, | ||
); | ||
} | ||
return { | ||
key: 'singleton', | ||
const existingInstance = singletonInstanceMap.get(injectable); | ||
getInstance: ({ injectable, instantiationParameter, di }) => { | ||
if (instantiationParameter) { | ||
throw new Error( | ||
`Tried to inject singleton "${injectable.id}" with instantiation parameters.`, | ||
); | ||
} | ||
if (existingInstance) { | ||
return existingInstance; | ||
} | ||
const existingInstance = singletonInstanceMap.get(injectable); | ||
const newInstance = getInstance({ | ||
injectable, | ||
instantiationParameter, | ||
di, | ||
}); | ||
if (existingInstance) { | ||
return existingInstance; | ||
} | ||
singletonInstanceMap.set(injectable, newInstance); | ||
const newInstance = getInstance({ | ||
injectable, | ||
instantiationParameter, | ||
di, | ||
}); | ||
return newInstance; | ||
}, | ||
}, | ||
singletonInstanceMap.set(injectable, newInstance); | ||
return newInstance; | ||
}, | ||
purge: injectable => singletonInstanceMap.delete(injectable), | ||
}; | ||
}), | ||
transient: { | ||
key: 'transient', | ||
getInstance, | ||
purge: () => { | ||
throw new Error('Tried to purge injectable with transient lifecycle.'); | ||
}, | ||
}, | ||
scopedTransient: getScope => ({ | ||
key: 'scoped-transient', | ||
scopedTransient: getScope => | ||
iife(() => { | ||
const scopedTransientMap = new Map(); | ||
getInstance: ({ | ||
di, | ||
injectable, | ||
instantiationParameter, | ||
scopedTransientMap, | ||
}) => { | ||
const scope = getScope(di); | ||
return { | ||
key: 'scoped-transient', | ||
const scopesForInjectable = | ||
scopedTransientMap.get(injectable) || new Map(); | ||
getInstance: ({ di, injectable, instantiationParameter }) => { | ||
const scope = getScope(di); | ||
scopedTransientMap.set(injectable, scopesForInjectable); | ||
const scopesForInjectable = | ||
scopedTransientMap.get(injectable) || new Map(); | ||
const existingInstance = scopesForInjectable.get(scope); | ||
scopedTransientMap.set(injectable, scopesForInjectable); | ||
if (existingInstance) { | ||
return existingInstance; | ||
} | ||
const existingInstance = scopesForInjectable.get(scope); | ||
const newInstance = getInstance({ | ||
injectable, | ||
instantiationParameter, | ||
di, | ||
}); | ||
if (existingInstance) { | ||
return existingInstance; | ||
} | ||
scopesForInjectable.clear(); | ||
scopesForInjectable.set(scope, newInstance); | ||
const newInstance = getInstance({ | ||
injectable, | ||
instantiationParameter, | ||
di, | ||
}); | ||
return newInstance; | ||
}, | ||
}), | ||
scopesForInjectable.clear(); | ||
scopesForInjectable.set(scope, newInstance); | ||
return newInstance; | ||
}, | ||
purge: injectable => scopedTransientMap.delete(injectable), | ||
}; | ||
}), | ||
}; | ||
@@ -104,0 +113,0 @@ |
Sorry, the diff of this file is too big to display
207867
1117