Improve error context for DI

This commit is contained in:
Garrett Mills 2022-08-10 21:53:56 -05:00
parent 9b47d2ac99
commit 3d836afa59
4 changed files with 60 additions and 20 deletions

View File

@ -1,6 +1,6 @@
{
"name": "@extollo/lib",
"version": "0.13.9",
"version": "0.13.10",
"description": "The framework library that lifts up your code.",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@ -8,7 +8,16 @@ import {
TypedDependencyKey,
} from './types'
import {AbstractFactory} from './factory/AbstractFactory'
import {Awaitable, collect, Collection, ErrorWithContext, globalRegistry, hasOwnProperty, logIfDebugging} from '../util'
import {
Awaitable,
collect,
Collection,
ErrorWithContext,
globalRegistry,
hasOwnProperty,
logIfDebugging,
withErrorContext,
} from '../util'
import {Factory} from './factory/Factory'
import {DuplicateFactoryKeyError} from './error/DuplicateFactoryKeyError'
import {ClosureFactory} from './factory/ClosureFactory'
@ -376,7 +385,7 @@ export class Container {
/**
* Resolve the dependency key. If a singleton value for that key already exists in this container,
* return that value. Otherwise, use the factory an given parameters to produce and return the value.
* return that value. Otherwise, use the factory and given parameters to produce and return the value.
* @param {DependencyKey} key
* @param {...any} parameters
*/
@ -432,11 +441,16 @@ export class Container {
// Create the dependencies for the factory
const keys = factory.getDependencyKeys().filter(req => this.hasKey(req.key))
const dependencies = keys.map<ResolvedDependency>(req => {
return withErrorContext(() => {
return {
paramIndex: req.paramIndex,
key: req.key,
resolved: this.resolveAndCreate(req.key),
}
}, {
producingToken: factory.getTokenName(),
constructorDependency: req,
})
}).sortBy('paramIndex')
// Build the arguments for the factory, using dependencies in the
@ -460,7 +474,12 @@ export class Container {
factory.getInjectedProperties().each(dependency => {
logIfDebugging('extollo.di.injector', 'Resolving injected dependency:', dependency)
if ( dependency.key && inst ) {
withErrorContext(() => {
(inst as any)[dependency.property] = this.resolveAndCreate(dependency.key)
}, {
producingToken: factory.getTokenName(),
propertyDependency: dependency,
})
}
})
@ -499,15 +518,23 @@ export class Container {
this.checkForMakeCycles()
try {
const result = withErrorContext(() => {
if (this.hasKey(target)) {
const realized = this.resolveAndCreate(target, ...parameters)
Container.makeStack.pop()
Container.makeStack?.pop()
return realized
} else if (typeof target !== 'string' && isInstantiable(target)) {
const realized = this.produceFactory(new Factory(target), parameters)
Container.makeStack.pop()
Container.makeStack?.pop()
return realized
}
}, {
makeStack: Container.makeStack,
})
if ( result ) {
return result
}
} catch (e: unknown) {
Container.makeStack.pop()
throw e

View File

@ -1,11 +1,12 @@
import {DependencyKey} from '../types'
import {ErrorWithContext} from '../../util'
/**
* Error thrown when a dependency key that has not been registered is passed to a resolver.
* @extends Error
*/
export class InvalidDependencyKeyError extends Error {
constructor(key: DependencyKey) {
super(`No such dependency is registered with this container: ${key}`)
export class InvalidDependencyKeyError extends ErrorWithContext {
constructor(key: DependencyKey, context: {[key: string]: any} = {}) {
super(`No such dependency is registered with this container: ${key}`, context)
}
}

View File

@ -41,4 +41,16 @@ export abstract class AbstractFactory<T> {
* @return Collection<PropertyDependency>
*/
abstract getInjectedProperties(): Collection<PropertyDependency>
/**
* Get a human-readable name of the token this factory produces.
* This is meant for debugging output only.
*/
public getTokenName(): string {
if ( typeof this.token === 'string' ) {
return this.token
}
return this.token.name ?? '(unknown token)'
}
}