@travetto/di
Advanced tools
Comparing version 0.0.6 to 0.0.7
@@ -22,3 +22,3 @@ { | ||
}, | ||
"version": "0.0.6" | ||
"version": "0.0.7" | ||
} |
@@ -16,3 +16,3 @@ import { InjectableConfig, Dependency } from '../types'; | ||
export type InjectConfig = { name?: string, optional?: boolean }; | ||
export type InjectConfig = { qualifier?: symbol, optional?: boolean }; | ||
@@ -26,5 +26,8 @@ export function InjectArgs(configs?: InjectConfig[]): ClassDecorator { | ||
export function Inject(config?: InjectConfig): PropertyDecorator { | ||
return (target: any, propertyKey: string) => { | ||
DependencyRegistry.registerProperty(target.constructor, propertyKey, config as any as Dependency); | ||
return (target: any, propertyKey: string | symbol) => { | ||
DependencyRegistry.registerProperty( | ||
target.constructor, | ||
propertyKey as string, | ||
config as any as Dependency); | ||
}; | ||
} |
@@ -7,3 +7,3 @@ import { Dependency, InjectableConfig, ClassTarget } from '../types'; | ||
export const DEFAULT_INSTANCE = '__default'; | ||
export const DEFAULT_INSTANCE = Symbol('__default'); | ||
@@ -17,13 +17,17 @@ export interface ManagedExtra { | ||
function getName(symbol: symbol) { | ||
return symbol.toString().split(/[()]/g)[1]; | ||
} | ||
export class $DependencyRegistry extends MetadataRegistry<InjectableConfig> { | ||
private pendingFinalize: Class[] = []; | ||
private instances = new Map<TargetId, Map<string, any>>(); | ||
private instancePromises = new Map<TargetId, Map<string, Promise<any>>>(); | ||
private instances = new Map<TargetId, Map<Symbol, any>>(); | ||
private instancePromises = new Map<TargetId, Map<Symbol, Promise<any>>>(); | ||
private aliases = new Map<TargetId, Map<string, string>>(); | ||
private targets = new Map<ClassId, Map<string, TargetId>>(); | ||
private aliases = new Map<TargetId, Map<Symbol, string>>(); | ||
private targets = new Map<ClassId, Map<Symbol, TargetId>>(); | ||
private proxies = new Map<TargetId, Map<string, Proxy<RetargettingHandler<any>>>>(); | ||
private proxyHandlers = new Map<TargetId, Map<string, RetargettingHandler<any>>>(); | ||
private proxies = new Map<TargetId, Map<Symbol, Proxy<RetargettingHandler<any>>>>(); | ||
private proxyHandlers = new Map<TargetId, Map<Symbol, RetargettingHandler<any>>>(); | ||
@@ -37,6 +41,6 @@ private autoCreate: (Dependency<any> & { priority: number })[] = []; | ||
async initialInstall() { | ||
let finalizing = this.pendingFinalize; | ||
const finalizing = this.pendingFinalize; | ||
this.pendingFinalize = []; | ||
for (let cls of finalizing) { | ||
for (const cls of finalizing) { | ||
this.install(cls, { type: 'added', curr: cls }); | ||
@@ -48,5 +52,5 @@ } | ||
console.debug('Auto-creating', this.autoCreate.map(x => x.target.name)); | ||
let items = this.autoCreate.slice(0).sort((a, b) => a.priority - b.priority); | ||
for (let i of items) { | ||
await this.getInstance(i.target, i.name); | ||
const items = this.autoCreate.slice(0).sort((a, b) => a.priority - b.priority); | ||
for (const i of items) { | ||
await this.getInstance(i.target, i.qualifier); | ||
} | ||
@@ -62,3 +66,3 @@ } | ||
return { | ||
name: DEFAULT_INSTANCE, | ||
qualifier: DEFAULT_INSTANCE, | ||
class: cls, | ||
@@ -77,18 +81,18 @@ target: cls, | ||
async construct<T>(target: ClassTarget<T & ManagedExtra>, name: string = DEFAULT_INSTANCE): Promise<T> { | ||
let targetId = target.__id; | ||
async construct<T>(target: ClassTarget<T & ManagedExtra>, qualifier: symbol = DEFAULT_INSTANCE): Promise<T> { | ||
const targetId = target.__id; | ||
let aliasMap = this.aliases.get(targetId); | ||
const aliasMap = this.aliases.get(targetId); | ||
if (!aliasMap || !aliasMap.has(name)) { | ||
throw new InjectionError(`Dependency not found: ${targetId}[${name}]`); | ||
if (!aliasMap || !aliasMap.has(qualifier)) { | ||
throw new InjectionError(`Dependency not found: ${targetId}[${getName(qualifier)}]`); | ||
} | ||
let clz = aliasMap.get(name)!; | ||
let managed = this.get(clz)!; | ||
const clz = aliasMap.get(qualifier)!; | ||
const managed = this.get(clz)!; | ||
const fieldKeys = Object.keys(managed.dependencies.fields!); | ||
let consDeps = managed.dependencies.cons || []; | ||
let allDeps = consDeps.concat(fieldKeys.map(x => managed.dependencies.fields[x])) | ||
const consDeps = managed.dependencies.cons || []; | ||
const allDeps = consDeps.concat(fieldKeys.map(x => managed.dependencies.fields[x])) | ||
@@ -98,3 +102,3 @@ const promises = allDeps | ||
try { | ||
return await this.getInstance(x.target, x.name); | ||
return await this.getInstance(x.target, x.qualifier); | ||
} catch (e) { | ||
@@ -126,4 +130,4 @@ if (x.optional && e instanceof InjectionError) { | ||
private async createInstance<T>(target: ClassTarget<T>, name: string = DEFAULT_INSTANCE) { | ||
let targetId = target.__id; | ||
private async createInstance<T>(target: ClassTarget<T>, qualifier: symbol = DEFAULT_INSTANCE) { | ||
const targetId = target.__id; | ||
@@ -135,10 +139,10 @@ if (!this.instances.has(targetId)) { | ||
if (this.instancePromises.get(targetId)!.has(name)) { | ||
return this.instancePromises.get(targetId)!.get(name); | ||
if (this.instancePromises.get(targetId)!.has(qualifier)) { | ||
return this.instancePromises.get(targetId)!.get(qualifier); | ||
} | ||
let instancePromise = this.construct(target, name); | ||
this.instancePromises.get(targetId)!.set(name, instancePromise); | ||
const instancePromise = this.construct(target, qualifier); | ||
this.instancePromises.get(targetId)!.set(qualifier, instancePromise); | ||
let instance = await instancePromise; | ||
const instance = await instancePromise; | ||
@@ -154,37 +158,39 @@ if (AppEnv.watch) { | ||
console.debug('Creating Instance', targetId, AppEnv.watch, !this.proxyHandlers.has(targetId), this.proxyHandlers.has(targetId) && !this.proxyHandlers.get(targetId)!.has(name)) | ||
console.debug('Creating Instance', targetId, AppEnv.watch, | ||
!this.proxyHandlers.has(targetId), | ||
this.proxyHandlers.has(targetId) && !this.proxyHandlers.get(targetId)!.has(qualifier)) | ||
// if in watch mode, create proxies | ||
if (AppEnv.watch) { | ||
if (!this.proxies.get(targetId)!.has(name)) { | ||
let handler = new RetargettingHandler(out); | ||
let proxy = new Proxy({}, handler); | ||
this.proxyHandlers.get(targetId)!.set(name, handler); | ||
this.proxies.get(targetId)!.set(name, proxy); | ||
if (!this.proxies.get(targetId)!.has(qualifier)) { | ||
const handler = new RetargettingHandler(out); | ||
const proxy = new Proxy({}, handler); | ||
this.proxyHandlers.get(targetId)!.set(qualifier, handler); | ||
this.proxies.get(targetId)!.set(qualifier, proxy); | ||
out = proxy; | ||
console.debug('Registering proxy', target.__id, name); | ||
console.debug('Registering proxy', target.__id, qualifier); | ||
} else { | ||
let handler = this.proxyHandlers.get(targetId)!.get(name)!; | ||
console.debug('Updating target', target.__id, name, out); | ||
const handler = this.proxyHandlers.get(targetId)!.get(qualifier)!; | ||
console.debug('Updating target', target.__id, qualifier, out); | ||
handler.target = out; | ||
out = this.proxies.get(targetId)!.get(name); | ||
out = this.proxies.get(targetId)!.get(qualifier); | ||
} | ||
} | ||
this.instances.get(targetId)!.set(name, out); | ||
this.instances.get(targetId)!.set(qualifier, out); | ||
} | ||
async getInstance<T>(target: ClassTarget<T>, name: string = DEFAULT_INSTANCE): Promise<T> { | ||
let targetId = target.__id; | ||
if (!this.instances.has(targetId) || !this.instances.get(targetId)!.has(name)) { | ||
console.debug('Getting Intance', targetId, name); | ||
await this.createInstance(target, name); | ||
async getInstance<T>(target: ClassTarget<T>, qualifier: symbol = DEFAULT_INSTANCE): Promise<T> { | ||
const targetId = target.__id; | ||
if (!this.instances.has(targetId) || !this.instances.get(targetId)!.has(qualifier)) { | ||
console.debug('Getting Intance', targetId, getName(qualifier)); | ||
await this.createInstance(target, qualifier); | ||
} | ||
return this.instances.get(targetId)!.get(name)!; | ||
return this.instances.get(targetId)!.get(qualifier)!; | ||
} | ||
getCandidateTypes<T>(target: Class<T>) { | ||
let targetId = target.__id; | ||
let aliasMap = this.aliases.get(targetId)!; | ||
let aliasedIds = aliasMap ? Array.from(aliasMap.values()) : []; | ||
const targetId = target.__id; | ||
const aliasMap = this.aliases.get(targetId)!; | ||
const aliasedIds = aliasMap ? Array.from(aliasMap.values()) : []; | ||
return aliasedIds.map(id => this.get(id)!) | ||
@@ -195,7 +201,7 @@ } | ||
registerConstructor<T>(cls: Class<T>, dependencies?: Dependency<any>[]) { | ||
let conf = this.getOrCreatePending(cls); | ||
const conf = this.getOrCreatePending(cls); | ||
conf.dependencies!.cons = dependencies; | ||
if (dependencies) { | ||
for (let dependency of dependencies) { | ||
dependency.name = dependency.name || DEFAULT_INSTANCE; | ||
for (const dependency of dependencies) { | ||
dependency.qualifier = dependency.qualifier || DEFAULT_INSTANCE; | ||
} | ||
@@ -206,13 +212,13 @@ } | ||
registerProperty<T>(cls: Class<T>, field: string, dependency: Dependency<any>) { | ||
let conf = this.getOrCreatePending(cls); | ||
const conf = this.getOrCreatePending(cls); | ||
conf.dependencies!.fields[field] = dependency; | ||
dependency.name = dependency.name || DEFAULT_INSTANCE; | ||
dependency.qualifier = dependency.qualifier || DEFAULT_INSTANCE; | ||
} | ||
registerClass<T>(cls: Class<T>, pconfig: Partial<InjectableConfig<T>>) { | ||
let classId = pconfig.class!.__id; | ||
let config = this.getOrCreatePending(pconfig.class!); | ||
const classId = pconfig.class!.__id; | ||
const config = this.getOrCreatePending(pconfig.class!); | ||
if (pconfig.name) { | ||
config.name = pconfig.name; | ||
if (pconfig.qualifier) { | ||
config.qualifier = pconfig.qualifier; | ||
} | ||
@@ -231,10 +237,10 @@ if (pconfig.target) { | ||
onInstallFinalize<T>(cls: Class<T>) { | ||
let classId = cls.__id; | ||
const classId = cls.__id; | ||
console.debug('Finalized', classId); | ||
let config = this.getOrCreatePending(cls) as InjectableConfig<T>; | ||
const config = this.getOrCreatePending(cls) as InjectableConfig<T>; | ||
let parentClass = Object.getPrototypeOf(cls); | ||
let parentConfig = this.get(parentClass.__id); | ||
const parentClass = Object.getPrototypeOf(cls); | ||
const parentConfig = this.get(parentClass.__id); | ||
@@ -256,3 +262,3 @@ if (parentConfig) { | ||
let targetId = config.target.__id; | ||
const targetId = config.target.__id; | ||
@@ -263,10 +269,10 @@ if (!this.aliases.has(targetId)) { | ||
this.aliases.get(targetId)!.set(config.name, classId); | ||
this.targets.get(classId)!.set(config.name, targetId); | ||
this.aliases.get(targetId)!.set(config.qualifier, classId); | ||
this.targets.get(classId)!.set(config.qualifier, targetId); | ||
// TODO: Auto alias parent class if framework managed | ||
if (parentClass.__id && config.name !== DEFAULT_INSTANCE) { | ||
let parentId = parentClass.__id; | ||
this.aliases.get(parentId)!.set(config.name, classId); | ||
this.targets.get(classId)!.set(config.name, parentId); | ||
if (parentClass.__id && config.qualifier !== DEFAULT_INSTANCE) { | ||
const parentId = parentClass.__id; | ||
this.aliases.get(parentId)!.set(config.qualifier, classId); | ||
this.targets.get(classId)!.set(config.qualifier, parentId); | ||
} | ||
@@ -277,7 +283,7 @@ | ||
this.proxies.has(targetId) && | ||
this.proxies.get(targetId)!.has(config.name) | ||
this.proxies.get(targetId)!.has(config.qualifier) | ||
) { | ||
console.debug('Reloading on next tick'); | ||
// Timing matters b/c of create instance | ||
process.nextTick(() => this.createInstance(config.target, config.name)); | ||
process.nextTick(() => this.createInstance(config.target, config.qualifier)); | ||
} else if (config.autoCreate.create) { | ||
@@ -287,3 +293,3 @@ // If not loaded, and autocreate | ||
target: config.target, | ||
name: config.name, | ||
qualifier: config.qualifier, | ||
priority: config.autoCreate.priority! | ||
@@ -302,3 +308,3 @@ }) | ||
// Remove current instance | ||
for (let [config, targetId] of this.targets.get(cls.__id)!.entries()) { | ||
for (const [config, targetId] of this.targets.get(cls.__id)!.entries()) { | ||
if (this.instances.has(targetId) && | ||
@@ -308,3 +314,3 @@ this.instances.get(targetId)!.has(config) && | ||
) { | ||
let handler = this.proxyHandlers.get(targetId)!.get(config) | ||
const handler = this.proxyHandlers.get(targetId)!.get(config) | ||
if (handler) { | ||
@@ -311,0 +317,0 @@ handler.target = null; |
@@ -17,7 +17,7 @@ import * as ts from 'typescript'; | ||
function processDeclaration(state: State, param: ts.ParameterDeclaration | ts.PropertyDeclaration) { | ||
let injection = TransformUtil.findAnyDecorator(param, { Inject: new Set(['@travetto/di']) }, state); | ||
const injection = TransformUtil.findAnyDecorator(param, { Inject: new Set(['@travetto/di']) }, state); | ||
if (injection || ts.isParameter(param)) { | ||
let finalTarget = TransformUtil.importIfExternal(param.type!, state); | ||
let injectConfig = TransformUtil.getPrimaryArgument<ts.ObjectLiteralExpression>(injection); | ||
const finalTarget = TransformUtil.importIfExternal(param.type!, state); | ||
const injectConfig = TransformUtil.getPrimaryArgument<ts.ObjectLiteralExpression>(injection); | ||
@@ -47,3 +47,3 @@ let optional = TransformUtil.getObjectValue(injectConfig, 'optional'); | ||
} | ||
let ident = ts.createIdentifier(name); | ||
const ident = ts.createIdentifier(name); | ||
state.decorators[name] = ts.createPropertyAccess(state.import, ident); | ||
@@ -62,3 +62,3 @@ } | ||
if (ts.isClassDeclaration(node)) { | ||
let foundDec = TransformUtil.findAnyDecorator(node, INJECTABLES, state); | ||
const foundDec = TransformUtil.findAnyDecorator(node, INJECTABLES, state); | ||
let decls = node.decorators; | ||
@@ -70,4 +70,4 @@ | ||
let declTemp = (node.decorators || []).slice(0); | ||
let cons = (node as any as ts.ClassDeclaration).members.find(x => ts.isConstructorDeclaration(x)) as ts.ConstructorDeclaration; | ||
const declTemp = (node.decorators || []).slice(0); | ||
const cons = (node as any as ts.ClassDeclaration).members.find(x => ts.isConstructorDeclaration(x)) as ts.ConstructorDeclaration; | ||
let injectArgs = undefined; | ||
@@ -98,4 +98,4 @@ | ||
let cNode = node as any as ts.ClassDeclaration; | ||
let out = ts.updateClassDeclaration(cNode, | ||
const cNode = node as any as ts.ClassDeclaration; | ||
const out = ts.updateClassDeclaration(cNode, | ||
decls, | ||
@@ -111,11 +111,11 @@ cNode.modifiers, | ||
} if (ts.isPropertyDeclaration(node)) { | ||
let expr = processDeclaration(state, node); | ||
const expr = processDeclaration(state, node); | ||
if (expr) { | ||
let final = createInjectDecorator(state, 'Inject', expr); | ||
let finalDecs = ((node.decorators as any as ts.Decorator[]) || []) | ||
const final = createInjectDecorator(state, 'Inject', expr); | ||
const finalDecs = ((node.decorators as any as ts.Decorator[]) || []) | ||
.filter(x => TransformUtil.getDecoratorIdent(x).text !== 'Inject'); | ||
// Doing decls | ||
let ret = ts.updateProperty( | ||
const ret = ts.updateProperty( | ||
node, | ||
@@ -122,0 +122,0 @@ ts.createNodeArray([final, ...finalDecs]), |
@@ -16,4 +16,4 @@ import { Class } from '@travetto/registry'; | ||
target: ClassTarget<T>; | ||
name: string; | ||
qualifier: symbol; | ||
optional?: boolean; | ||
} |
@@ -1,46 +0,10 @@ | ||
import { Injectable, Inject } from '../src/decorator/injectable'; | ||
import { DbConfig, AltConfig } from './config'; | ||
import { DependencyRegistry } from '../src/service'; | ||
import { ServiceInherit, SERVICE_INHERIT_2 } from './deps'; | ||
import { Suite, Test } from '@travetto/test'; | ||
import * as assert from 'assert'; | ||
@Injectable() | ||
class Database { | ||
@Inject() dbConfig: DbConfig<any, any>; | ||
@Inject({ optional: true }) altConfig: AltConfig; | ||
postConstruct() { | ||
console.log('Creating database', this.dbConfig.getUrl()); | ||
} | ||
query() { | ||
console.log('Getting 350', this.dbConfig.getUrl()); | ||
} | ||
} | ||
@Injectable() | ||
class Service { | ||
constructor(public db: Database) { | ||
console.log('Creating service', db); | ||
} | ||
doWork() { | ||
this.db.query(); | ||
} | ||
} | ||
@Injectable() | ||
class ServiceInherit extends Service { | ||
name = 'bob'; | ||
age = 30; | ||
doWork() { | ||
this.db.query(); | ||
} | ||
} | ||
const FOUR = 4; | ||
function doWork() { | ||
//throw new Error('ahhh'); | ||
// throw new Error('ahhh'); | ||
} | ||
@@ -60,2 +24,7 @@ | ||
inst = await DependencyRegistry.getInstance(ServiceInherit, SERVICE_INHERIT_2); | ||
inst.doWork(); | ||
assert.ok(inst.db); | ||
assert(inst.age === 31); | ||
assert.equal(inst.db.altConfig, undefined); | ||
@@ -67,2 +36,3 @@ assert(inst.db.dbConfig.getUrl() === 'mongodb://oscar'); | ||
async runner() { | ||
assert(1 === 1); | ||
@@ -78,2 +48,3 @@ assert(2 + 2 === FOUR); | ||
@Test('run') | ||
@@ -83,3 +54,5 @@ async run() { | ||
await DependencyRegistry.init(); | ||
let inst = await DependencyRegistry.getInstance(ServiceInherit); | ||
assert(30 === 30); | ||
const inst = await DependencyRegistry.getInstance(ServiceInherit); | ||
inst.doWork(); | ||
@@ -90,4 +63,4 @@ | ||
assert.equal(inst.db.altConfig, undefined); | ||
assert.equal(inst.db.altConfig, undefined); | ||
assert(inst.db.dbConfig.getUrl() === 'mongodb://oscar'); | ||
@@ -99,4 +72,6 @@ } | ||
assert(1 === 1); | ||
console.log('hi') | ||
assert(2 + 2 === FOUR); | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
19907
541