@travetto/di
Advanced tools
Comparing version 3.0.0-rc.4 to 3.0.0-rc.6
{ | ||
"name": "@travetto/di", | ||
"displayName": "Dependency Injection", | ||
"version": "3.0.0-rc.4", | ||
"version": "3.0.0-rc.6", | ||
"description": "Dependency registration/management and injection support.", | ||
@@ -21,8 +20,7 @@ "keywords": [ | ||
"files": [ | ||
"index.ts", | ||
"__index__.ts", | ||
"src", | ||
"support", | ||
"test-support" | ||
"support" | ||
], | ||
"main": "index.ts", | ||
"main": "__index__.ts", | ||
"repository": { | ||
@@ -33,13 +31,15 @@ "url": "https://github.com/travetto/travetto.git", | ||
"dependencies": { | ||
"@travetto/transformer": "^3.0.0-rc.4", | ||
"@travetto/registry": "^3.0.0-rc.4" | ||
"@travetto/registry": "^3.0.0-rc.6" | ||
}, | ||
"devDependencies": { | ||
"@travetto/config": "^3.0.0-rc.4", | ||
"@travetto/schema": "^3.0.0-rc.4" | ||
"peerDependencies": { | ||
"@travetto/transformer": "^3.0.0-rc.6" | ||
}, | ||
"docDependencies": { | ||
"@travetto/model-mongo": true, | ||
"@travetto/rest": true | ||
"peerDependenciesMeta": { | ||
"@travetto/transformer": { | ||
"optional": true | ||
} | ||
}, | ||
"travetto": { | ||
"displayName": "Dependency Injection" | ||
}, | ||
"publishConfig": { | ||
@@ -46,0 +46,0 @@ "access": "public" |
<!-- This file was generated by @travetto/doc and should not be modified directly --> | ||
<!-- Please modify https://github.com/travetto/travetto/tree/main/module/di/doc.ts and execute "npx trv doc" to rebuild --> | ||
<!-- Please modify https://github.com/travetto/travetto/tree/main/module/di/DOC.ts and execute "npx trv doc" to rebuild --> | ||
# Dependency Injection | ||
@@ -14,3 +14,3 @@ ## Dependency registration/management and injection support. | ||
## Declaration | ||
The [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L32) and [@InjectableFactory](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L75) decorators provide the registration of dependencies. Dependency declaration revolves around exposing `class`es and subtypes thereof to provide necessary functionality. Additionally, the framework will utilize dependencies to satisfy contracts with various implementations (e.g. [MongoModelService](https://github.com/travetto/travetto/tree/main/module/model-mongo/src/service.ts#L49) provides itself as an injectable candidate for [ModelCrudSupport](https://github.com/travetto/travetto/tree/main/module/model/src/service/crud.ts). | ||
The [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L31) and [@InjectableFactory](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L74) decorators provide the registration of dependencies. Dependency declaration revolves around exposing `class`es and subtypes thereof to provide necessary functionality. Additionally, the framework will utilize dependencies to satisfy contracts with various implementation. | ||
@@ -76,3 +76,3 @@ **Code: Example Injectable** | ||
In this scenario, `SpecificService` is a valid candidate for `BaseService` due to the abstract inheritance. Sometimes, you may want to provide a slight variation to a dependency without extending a class. To this end, the [@InjectableFactory](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L75) decorator denotes a `static` class method that produces an [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L32). | ||
In this scenario, `SpecificService` is a valid candidate for `BaseService` due to the abstract inheritance. Sometimes, you may want to provide a slight variation to a dependency without extending a class. To this end, the [@InjectableFactory](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L74) decorator denotes a `static` class method that produces an [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L31). | ||
@@ -98,11 +98,11 @@ **Code: Example InjectableFactory** | ||
**Note**: Other modules are able to provide aliases to [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L32) that also provide additional functionality. For example, the [@Config](https://github.com/travetto/travetto/tree/main/module/config/src/decorator.ts#L9) or the [@Controller](https://github.com/travetto/travetto/tree/main/module/rest/src/decorator/controller.ts#L9) decorator registers the associated class as an injectable element. | ||
**Note**: Other modules are able to provide aliases to [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L31) that also provide additional functionality. For example, the [Config](https://github.com/travetto/travetto/tree/main/module/config/src/decorator.js#L10) or the [RESTful API](https://github.com/travetto/travetto/tree/main/module/rest#readme "Declarative api for RESTful APIs with support for the dependency injection module.") module @Controller decorator registers the associated class as an injectable element. | ||
## Injection | ||
Once all of your necessary dependencies are defined, now is the time to provide those [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L32) instances to your code. There are three primary methods for injection: | ||
Once all of your necessary dependencies are defined, now is the time to provide those [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L31) instances to your code. There are three primary methods for injection: | ||
The [@Inject](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L32) decorator, which denotes a desire to inject a value directly. These will be set post construction. | ||
The [@Inject](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L31) decorator, which denotes a desire to inject a value directly. These will be set post construction. | ||
**Code: Example Injectable with dependencies as [@Inject](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L32) fields** | ||
**Code: Example Injectable with dependencies as [@Inject](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L31) fields** | ||
```typescript | ||
@@ -123,3 +123,3 @@ import { Injectable, Inject } from '@travetto/di'; | ||
The [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L32) constructor params, which will be provided as the instance is being constructed. | ||
The [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L31) constructor params, which will be provided as the instance is being constructed. | ||
@@ -141,3 +141,3 @@ **Code: Example Injectable with dependencies in constructor** | ||
Via [@InjectableFactory](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L75) params, which are comparable to constructor params | ||
Via [@InjectableFactory](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L74) params, which are comparable to constructor params | ||
@@ -218,1 +218,37 @@ **Code: Example InjectableFactory with parameters as dependencies** | ||
``` | ||
Additionally, support for interfaces (over class inheritance) is provided, but requires binding the interface to a concrete class as the interface does not exist at runtime. | ||
**Code: Example Interface Injection** | ||
```typescript | ||
import { DependencyRegistry, Inject, Injectable, InjectableFactory } from '@travetto/di'; | ||
class TargetConcrete { } | ||
/** | ||
* @concrete .:TargetConcrete | ||
*/ | ||
export interface ServiceContract { | ||
deleteUser(userId: string): Promise<void>; | ||
} | ||
class MyCustomService implements ServiceContract { | ||
async deleteUser(userId: string): Promise<void> { | ||
// Do something | ||
} | ||
} | ||
@Injectable() | ||
class SpecificService { | ||
@Inject() | ||
service: ServiceContract; | ||
} | ||
class ManualInvocationOfInterface { | ||
@InjectableFactory() | ||
static getCustomService(): Promise<ServiceContract> { | ||
return DependencyRegistry.getInstance<ServiceContract>(TargetConcrete); | ||
} | ||
} | ||
``` |
@@ -1,3 +0,2 @@ | ||
import { Class, ClassInstance } from '@travetto/base'; | ||
import { MethodDescriptor } from '@travetto/base/src/internal/types'; | ||
import type { MethodDescriptor, Class, ClassInstance } from '@travetto/base'; | ||
@@ -30,3 +29,3 @@ import { InjectableFactoryConfig, InjectableConfig, Dependency } from './types'; | ||
* | ||
* @augments `@trv:di/Injectable` | ||
* @augments `@travetto/di:Injectable` | ||
*/ | ||
@@ -56,3 +55,3 @@ export function Injectable(first?: Partial<InjectableConfig> | symbol, ...args: (Partial<InjectableConfig> | undefined)[]) { | ||
* | ||
* @augments `@trv:di/Inject` | ||
* @augments `@travetto/di:Inject` | ||
*/ | ||
@@ -75,3 +74,3 @@ export function Inject(first?: InjectConfig | symbol, ...args: (InjectConfig | undefined)[]) { | ||
* | ||
* @augments `@trv:di/InjectableFactory` | ||
* @augments `@travetto/di:InjectableFactory` | ||
*/ | ||
@@ -85,5 +84,5 @@ export function InjectableFactory(first?: Partial<InjectableFactoryConfig> | symbol, ...args: (Partial<InjectableFactoryConfig> | undefined)[]) { | ||
fn: descriptor.value!, | ||
id: `${target.ᚕid}#${property.toString()}` | ||
id: `${target.Ⲑid}#${property.toString()}` | ||
}); | ||
}; | ||
} |
@@ -10,4 +10,4 @@ import { AppError } from '@travetto/base'; | ||
constructor(message: string, target: ClassTarget, qualifiers?: symbol[]) { | ||
super(`${message}: [${target.ᚕid}]${qualifiers ? `[${qualifiers.map(getName)}]` : ''}`, 'notfound'); | ||
super(`${message}: [${target.Ⲑid}]${qualifiers ? `[${qualifiers.map(getName)}]` : ''}`, 'notfound'); | ||
} | ||
} |
@@ -1,15 +0,16 @@ | ||
import { Class, ClassInstance, ConcreteClass } from '@travetto/base'; | ||
import { Class, ClassInstance, ConcreteClass, GlobalEnv } from '@travetto/base'; | ||
import { MetadataRegistry, RootRegistry, ChangeEvent } from '@travetto/registry'; | ||
import { Dynamic } from '@travetto/base/src/internal/dynamic'; | ||
import { RootIndex } from '@travetto/manifest'; | ||
import { Dependency, InjectableConfig, ClassTarget, InjectableFactoryConfig } from './types'; | ||
import { InjectionError } from './error'; | ||
import { AutoCreateTarget } from './internal/types'; | ||
type TargetId = string; | ||
type ClassId = string; | ||
type Resolved<T> = { config: InjectableConfig<T>, qualifier: symbol, id: string }; | ||
export type Resolved<T> = { config: InjectableConfig<T>, qualifier: symbol, id: string }; | ||
export type ResolutionType = 'strict' | 'loose' | 'any'; | ||
const PrimaryCandidateⲐ = Symbol.for('@trv:di/primary'); | ||
const PrimaryCandidateⲐ = Symbol.for('@travetto/di:primary'); | ||
@@ -29,3 +30,2 @@ function hasPostConstruct(o: unknown): o is { postConstruct: () => Promise<unknown> } { | ||
*/ | ||
@Dynamic('@travetto/di/support/dynamic.injection') | ||
class $DependencyRegistry extends MetadataRegistry<InjectableConfig> { | ||
@@ -54,3 +54,3 @@ protected pendingFinalize: Class[] = []; | ||
protected resolveTarget<T>(target: ClassTarget<T>, qualifier?: symbol, resolution?: ResolutionType): Resolved<T> { | ||
const qualifiers = this.targetToClass.get(target.ᚕid) ?? new Map<symbol, string>(); | ||
const qualifiers = this.targetToClass.get(target.Ⲑid) ?? new Map<symbol, string>(); | ||
@@ -95,3 +95,3 @@ let cls: string | undefined; | ||
if (!this.defaultSymbols.has(qualifier) && resolution === 'loose') { | ||
console.debug('Unable to find specific dependency, falling back to general instance', { qualifier, target: target.ᚕid }); | ||
console.debug('Unable to find specific dependency, falling back to general instance', { qualifier, target: target.Ⲑid }); | ||
return this.resolveTarget(target); | ||
@@ -110,3 +110,3 @@ } | ||
config, | ||
id: (config.factory ? config.target : config.class).ᚕid | ||
id: (config.factory ? config.target : config.class).Ⲑid | ||
}; | ||
@@ -131,3 +131,3 @@ } | ||
if (err && err instanceof Error) { | ||
err.message = `${err.message} via=${managed.class.ᚕid}`; | ||
err.message = `${err.message} via=${managed.class.Ⲑid}`; | ||
} | ||
@@ -229,3 +229,3 @@ throw err; | ||
protected destroyInstance(cls: Class, qualifier: symbol): void { | ||
const classId = cls.ᚕid; | ||
const classId = cls.Ⲑid; | ||
@@ -240,6 +240,18 @@ const activeInstance = this.instances.get(classId)!.get(qualifier); | ||
this.instancePromises.get(classId)!.delete(qualifier); | ||
this.classToTarget.get(cls.ᚕid)!.delete(qualifier); | ||
console.debug('On uninstall', { id: cls.ᚕid, qualifier: qualifier.toString(), classId }); | ||
this.classToTarget.get(cls.Ⲑid)!.delete(qualifier); | ||
console.debug('On uninstall', { id: cls.Ⲑid, qualifier: qualifier.toString(), classId }); | ||
} | ||
override async init(): Promise<void> { | ||
await super.init(); | ||
if (GlobalEnv.dynamic) { | ||
const { DependencyRegistration } = await import('../support/dynamic.injection.js'); | ||
DependencyRegistration.init(this); | ||
} | ||
// Allow for auto-creation | ||
for (const cfg of await this.getCandidateTypes(AutoCreateTarget)) { | ||
await this.getInstance(cfg.class, cfg.qualifier); | ||
} | ||
} | ||
/** | ||
@@ -296,3 +308,3 @@ * Handle initial installation for the entire registry | ||
getCandidateTypes<T>(target: Class<T>): InjectableConfig[] { | ||
const targetId = target.ᚕid; | ||
const targetId = target.Ⲑid; | ||
const qualifiers = this.targetToClass.get(targetId)!; | ||
@@ -327,3 +339,3 @@ const uniqueQualifiers = qualifiers ? Array.from(new Set(qualifiers.values())) : []; | ||
config.class = cls; | ||
config.qualifier = pConfig.qualifier ?? config.qualifier ?? Symbol.for(cls.ᚕid); | ||
config.qualifier = pConfig.qualifier ?? config.qualifier ?? Symbol.for(cls.Ⲑid); | ||
if (pConfig.interfaces) { | ||
@@ -378,3 +390,3 @@ config.interfaces?.push(...pConfig.interfaces); | ||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions | ||
const cls = { ᚕid: config.id } as Class; | ||
const cls = { Ⲑid: config.id } as Class; | ||
@@ -385,8 +397,8 @@ finalConfig.class = cls; | ||
if (!this.factories.has(config.src.ᚕid)) { | ||
this.factories.set(config.src.ᚕid, new Map()); | ||
if (!this.factories.has(config.src.Ⲑid)) { | ||
this.factories.set(config.src.Ⲑid, new Map()); | ||
} | ||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions | ||
this.factories.get(config.src.ᚕid)!.set(cls, finalConfig as InjectableConfig); | ||
this.factories.get(config.src.Ⲑid)!.set(cls, finalConfig as InjectableConfig); | ||
} | ||
@@ -401,4 +413,4 @@ | ||
// Install factories separate from classes | ||
if (this.factories.has(cls.ᚕid)) { | ||
for (const fact of this.factories.get(cls.ᚕid)!.keys()) { | ||
if (this.factories.has(cls.Ⲑid)) { | ||
for (const fact of this.factories.get(cls.Ⲑid)!.keys()) { | ||
this.onInstall(fact, e); | ||
@@ -413,3 +425,3 @@ } | ||
onInstallFinalize<T>(cls: Class<T>): InjectableConfig<T> { | ||
const classId = cls.ᚕid; | ||
const classId = cls.Ⲑid; | ||
@@ -423,8 +435,13 @@ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions | ||
if (config.factory) { | ||
while (Object.getPrototypeOf(parentClass).ᚕabstract) { | ||
while (RootIndex.getFunctionMetadata(Object.getPrototypeOf(parentClass))?.abstract) { | ||
parentClass = Object.getPrototypeOf(parentClass); | ||
} | ||
if (!this.targetToClass.has(classId)) { | ||
this.targetToClass.set(classId, new Map()); | ||
} | ||
// Make explicitly discoverable as self | ||
this.targetToClass.get(classId)?.set(config.qualifier, classId); | ||
} | ||
const parentConfig = this.get(parentClass.ᚕid); | ||
const parentConfig = this.get(parentClass.Ⲑid); | ||
@@ -449,3 +466,3 @@ if (parentConfig) { | ||
if (cls.ᚕabstract) { // Skip out early, only needed to inherit | ||
if (RootIndex.getFunctionMetadata(cls)?.abstract) { // Skip out early, only needed to inherit | ||
return config; | ||
@@ -458,3 +475,3 @@ } | ||
const targetId = config.target.ᚕid; | ||
const targetId = config.target.Ⲑid; | ||
@@ -465,3 +482,3 @@ if (!this.targetToClass.has(targetId)) { | ||
if (config.qualifier === Symbol.for(cls.ᚕid)) { | ||
if (config.qualifier === Symbol.for(cls.Ⲑid)) { | ||
this.defaultSymbols.add(config.qualifier); | ||
@@ -475,10 +492,10 @@ } | ||
for (const el of config.interfaces) { | ||
if (!this.targetToClass.has(el.ᚕid)) { | ||
this.targetToClass.set(el.ᚕid, new Map()); | ||
if (!this.targetToClass.has(el.Ⲑid)) { | ||
this.targetToClass.set(el.Ⲑid, new Map()); | ||
} | ||
this.targetToClass.get(el.ᚕid)!.set(config.qualifier, classId); | ||
this.classToTarget.get(classId)!.set(Symbol.for(el.ᚕid), el.ᚕid); | ||
this.targetToClass.get(el.Ⲑid)!.set(config.qualifier, classId); | ||
this.classToTarget.get(classId)!.set(Symbol.for(el.Ⲑid), el.Ⲑid); | ||
if (config.primary && (classId === targetId || config.factory)) { | ||
this.targetToClass.get(el.ᚕid)!.set(PrimaryCandidateⲐ, classId); | ||
this.targetToClass.get(el.Ⲑid)!.set(PrimaryCandidateⲐ, classId); | ||
} | ||
@@ -488,4 +505,4 @@ } | ||
// If targeting self (default @Injectable behavior) | ||
if ((classId === targetId || config.factory) && (parentConfig || parentClass.ᚕabstract)) { | ||
const parentId = parentClass.ᚕid; | ||
if ((classId === targetId || config.factory) && (parentConfig || RootIndex.getFunctionMetadata(parentClass)?.abstract)) { | ||
const parentId = parentClass.Ⲑid; | ||
@@ -517,6 +534,6 @@ if (!this.targetToClass.has(parentId)) { | ||
const [primaryInterface] = config.interfaces; | ||
if (!this.targetToClass.has(primaryInterface.ᚕid)) { | ||
this.targetToClass.set(primaryInterface.ᚕid, new Map()); | ||
if (!this.targetToClass.has(primaryInterface.Ⲑid)) { | ||
this.targetToClass.set(primaryInterface.Ⲑid, new Map()); | ||
} | ||
this.targetToClass.get(primaryInterface.ᚕid)!.set(PrimaryCandidateⲐ, classId); | ||
this.targetToClass.get(primaryInterface.Ⲑid)!.set(PrimaryCandidateⲐ, classId); | ||
} | ||
@@ -532,5 +549,5 @@ } | ||
override onUninstallFinalize(cls: Class): void { | ||
const classId = cls.ᚕid; | ||
const classId = cls.Ⲑid; | ||
if (!this.classToTarget.has(cls.ᚕid)) { | ||
if (!this.classToTarget.has(cls.Ⲑid)) { | ||
return; | ||
@@ -537,0 +554,0 @@ } |
@@ -80,2 +80,7 @@ import { Class } from '@travetto/base'; | ||
dependencies?: Dependency[]; | ||
} | ||
} | ||
/** | ||
* @concrete ./internal/types:AutoCreateTarget | ||
*/ | ||
export interface AutoCreate { } |
@@ -1,5 +0,5 @@ | ||
import { RetargettingProxy } from '@travetto/base/src/internal/proxy'; | ||
import { Class, ClassInstance } from '@travetto/base'; | ||
import { RetargettingProxy, Class, ClassInstance } from '@travetto/base'; | ||
import { RootIndex } from '@travetto/manifest'; | ||
import type { DependencyRegistry } from '../src/registry'; | ||
import type { DependencyRegistry, ResolutionType, Resolved } from '../src/registry'; | ||
import type { ClassTarget, InjectableConfig } from '../src/types'; | ||
@@ -10,87 +10,106 @@ | ||
*/ | ||
export function init($DependencyRegistry: Class<typeof DependencyRegistry>): typeof $DependencyRegistry { | ||
class $DynamicDependencyRegistry { | ||
#proxies = new Map<string, Map<symbol | undefined, RetargettingProxy<unknown>>>(); | ||
#registry: typeof DependencyRegistry; | ||
#registryCreateInstance: <T>(target: ClassTarget<T>, qualifier: symbol) => Promise<T>; | ||
#registryResolveTarget: <T>(target: ClassTarget<T>, qualifier?: symbol, resolution?: ResolutionType) => Resolved<T>; | ||
#registryOnInstallFinalize: <T>(target: Class<T>) => InjectableConfig<T>; | ||
#registryDestroyInstance: <T>(target: Class<T>, qualifier: symbol) => void; | ||
#registryOnReset: () => void; | ||
/** | ||
* Extending the $DependencyRegistry class to add some functionality for watching | ||
* Proxy the created instance | ||
*/ | ||
const Cls = class extends $DependencyRegistry { | ||
#proxies = new Map<string, Map<symbol | undefined, RetargettingProxy<unknown>>>(); | ||
proxyInstance<T>(target: ClassTarget<T>, qual: symbol | undefined, instance: T): T { | ||
const { qualifier, id: classId } = this.#registryResolveTarget(target, qual); | ||
let proxy: RetargettingProxy<T>; | ||
/** | ||
* Proxy the created instance | ||
*/ | ||
protected proxyInstance<T>(target: ClassTarget<T>, qual: symbol | undefined, instance: T): T { | ||
const { qualifier, id: classId } = this.resolveTarget(target, qual); | ||
let proxy: RetargettingProxy<T>; | ||
if (!this.#proxies.has(classId)) { | ||
this.#proxies.set(classId, new Map()); | ||
} | ||
if (!this.#proxies.has(classId)) { | ||
this.#proxies.set(classId, new Map()); | ||
} | ||
if (!this.#proxies.get(classId)!.has(qualifier)) { | ||
proxy = new RetargettingProxy<T>(instance); | ||
this.#proxies.get(classId)!.set(qualifier, proxy); | ||
console.debug('Registering proxy', { id: target.ᚕid, qualifier: qualifier.toString() }); | ||
} else { | ||
if (!this.#proxies.get(classId)!.has(qualifier)) { | ||
proxy = new RetargettingProxy<T>(instance); | ||
this.#proxies.get(classId)!.set(qualifier, proxy); | ||
console.debug('Registering proxy', { id: target.Ⲑid, qualifier: qualifier.toString() }); | ||
} else { | ||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions | ||
proxy = this.#proxies.get(classId)!.get(qualifier) as RetargettingProxy<T>; | ||
proxy.setTarget(instance); | ||
console.debug('Updating target', { | ||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions | ||
proxy = this.#proxies.get(classId)!.get(qualifier) as RetargettingProxy<T>; | ||
proxy.setTarget(instance); | ||
console.debug('Updating target', { | ||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions | ||
id: target.ᚕid, qualifier: qualifier.toString(), instanceType: (instance as unknown as ClassInstance<T>).constructor.name as string | ||
}); | ||
} | ||
return proxy.get(); | ||
id: target.Ⲑid, qualifier: qualifier.toString(), instanceType: (instance as unknown as ClassInstance<T>).constructor.name as string | ||
}); | ||
} | ||
/** | ||
* Create instance and wrap in a proxy | ||
*/ | ||
protected async createInstance<T>(target: ClassTarget<T>, qualifier: symbol): Promise<T> { | ||
const instance = await super.createInstance(target, qualifier); | ||
const classId = this.resolveTarget(target, qualifier).id; | ||
// Reset as proxy instance | ||
const proxied = this.proxyInstance<T>(target, qualifier, instance); | ||
this.instances.get(classId)!.set(qualifier, proxied); | ||
return proxied; | ||
} | ||
return proxy.get(); | ||
} | ||
/** | ||
* Reload proxy if in watch mode | ||
*/ | ||
onInstallFinalize<T>(cls: Class<T>): InjectableConfig<T> { | ||
const config = super.onInstallFinalize(cls); | ||
// If already loaded, reload | ||
const classId = cls.ᚕid; | ||
/** | ||
* Create instance and wrap in a proxy | ||
*/ | ||
async createInstance<T>(target: ClassTarget<T>, qualifier: symbol): Promise<T> { | ||
const instance = await this.#registryCreateInstance(target, qualifier); | ||
const classId = this.#registryResolveTarget(target, qualifier).id; | ||
// Reset as proxy instance | ||
const proxied = this.proxyInstance<T>(target, qualifier, instance); | ||
this.#registry['instances'].get(classId)!.set(qualifier, proxied); | ||
return proxied; | ||
} | ||
if ( | ||
!cls.ᚕabstract && | ||
this.#proxies.has(classId) && | ||
this.#proxies.get(classId)!.has(config.qualifier) | ||
) { | ||
console.debug('Reloading on next tick'); | ||
// Timing matters due to create instance being asynchronous | ||
process.nextTick(() => this.createInstance(config.target, config.qualifier)); | ||
} | ||
/** | ||
* Reload proxy if in watch mode | ||
*/ | ||
onInstallFinalize<T>(cls: Class<T>): InjectableConfig<T> { | ||
const config = this.#registryOnInstallFinalize(cls); | ||
// If already loaded, reload | ||
const classId = cls.Ⲑid; | ||
return config; | ||
if ( | ||
!RootIndex.getFunctionMetadata(cls)?.abstract && | ||
this.#proxies.has(classId) && | ||
this.#proxies.get(classId)!.has(config.qualifier) | ||
) { | ||
console.debug('Reloading on next tick'); | ||
// Timing matters due to create instance being asynchronous | ||
process.nextTick(() => this.createInstance(config.target, config.qualifier)); | ||
} | ||
destroyInstance(cls: Class, qualifier: symbol): void { | ||
const classId = cls.ᚕid; | ||
const proxy = this.#proxies.get(classId)!.get(qualifier); | ||
super.destroyInstance(cls, qualifier); | ||
if (proxy) { | ||
proxy.setTarget(null); | ||
} | ||
} | ||
return config; | ||
} | ||
onReset(): void { | ||
super.onReset(); | ||
this.#proxies.clear(); | ||
destroyInstance(cls: Class, qualifier: symbol): void { | ||
const classId = cls.Ⲑid; | ||
const proxy = this.#proxies.get(classId)!.get(qualifier); | ||
this.#registryDestroyInstance(cls, qualifier); | ||
if (proxy) { | ||
proxy.setTarget(null); | ||
} | ||
}; | ||
} | ||
return Cls; | ||
} | ||
onReset(): void { | ||
this.#registryOnReset(); | ||
this.#proxies.clear(); | ||
} | ||
register(registry: typeof DependencyRegistry): void { | ||
this.#registry = registry; | ||
this.#registryCreateInstance = registry['createInstance'].bind(registry); | ||
this.#registryResolveTarget = registry['resolveTarget'].bind(registry); | ||
this.#registryOnInstallFinalize = registry['onInstallFinalize'].bind(registry); | ||
this.#registryDestroyInstance = registry['destroyInstance'].bind(registry); | ||
this.#registryOnReset = registry['onReset'].bind(registry); | ||
this.#registry['createInstance'] = this.createInstance.bind(this); | ||
this.#registry['destroyInstance'] = this.destroyInstance.bind(this); | ||
this.#registry['onReset'] = this.onReset.bind(this); | ||
this.#registry['onInstallFinalize'] = this.onInstallFinalize.bind(this); | ||
} | ||
} | ||
export const DependencyRegistration = { | ||
init(registry: typeof DependencyRegistry): void { | ||
const dynamic = new $DynamicDependencyRegistry(); | ||
dynamic.register(registry); | ||
} | ||
}; |
@@ -1,5 +0,5 @@ | ||
import * as ts from 'typescript'; | ||
import ts from 'typescript'; | ||
import { | ||
TransformerState, DecoratorMeta, OnClass, OnProperty, OnStaticMethod, DecoratorUtil, LiteralUtil, TransformerId, OnSetter | ||
TransformerState, DecoratorMeta, OnClass, OnProperty, OnStaticMethod, DecoratorUtil, LiteralUtil, OnSetter | ||
} from '@travetto/transformer'; | ||
@@ -14,4 +14,2 @@ | ||
static [TransformerId] = '@trv:di'; | ||
/** | ||
@@ -31,3 +29,3 @@ * Handle a specific declaration param/property | ||
let optional = undefined; | ||
let optional: ts.Expression | undefined = undefined; | ||
if (optional === undefined && !!param.questionToken) { | ||
@@ -37,6 +35,5 @@ optional = state.fromLiteral(true); | ||
args.unshift(state.fromLiteral({ | ||
target: state.getOrImport(state.resolveExternalType(ts.isSetAccessorDeclaration(param) ? param.parameters[0] : param)), | ||
optional | ||
})); | ||
const keyParam = ts.isSetAccessorDeclaration(param) ? param.parameters[0] : param; | ||
const target = state.getOrImport(state.resolveExternalType(keyParam)); | ||
args.unshift(state.fromLiteral({ target, optional })); | ||
@@ -114,8 +111,10 @@ return args; | ||
const modifiers = DecoratorUtil.spliceDecorators(node, decl, [ | ||
state.createDecorator(INJECTABLE_MOD, 'Inject', ...this.processDeclaration(state, node)), | ||
], 0); | ||
// Doing decls | ||
return state.factory.updateSetAccessorDeclaration( | ||
node, | ||
DecoratorUtil.spliceDecorators(node, decl, [ | ||
state.createDecorator(INJECTABLE_MOD, 'Inject', ...this.processDeclaration(state, node)), | ||
], 0), | ||
modifiers, | ||
node.name, | ||
@@ -122,0 +121,0 @@ node.parameters, |
45754
0
13
907
249
- Removed@travetto/transformer@^3.0.0-rc.4