@travetto/di
Advanced tools
+1
-1
@@ -22,3 +22,3 @@ { | ||
| }, | ||
| "version": "0.0.7" | ||
| "version": "0.0.8" | ||
| } |
@@ -1,1 +0,1 @@ | ||
| export * from './injectable'; | ||
| export * from './injectable'; |
| import { InjectableConfig, Dependency } from '../types'; | ||
| import { DependencyRegistry, DEFAULT_INSTANCE } from '../service'; | ||
| import { Class } from '@travetto/registry'; | ||
| import { InjectableFactoryConfig } from '..'; | ||
@@ -24,9 +25,17 @@ export function Injectable(config: Partial<InjectableConfig<any>> = {}): ClassDecorator { | ||
| export function Inject(config?: InjectConfig): PropertyDecorator { | ||
| return (target: any, propertyKey: string | symbol) => { | ||
| DependencyRegistry.registerProperty( | ||
| target.constructor, | ||
| propertyKey as string, | ||
| config as any as Dependency); | ||
| export function Inject(config?: InjectConfig) { | ||
| return (target: any, propertyKey: string | symbol, idx?: number) => { | ||
| if (typeof idx !== 'number') { // Only register if on property | ||
| DependencyRegistry.registerProperty( | ||
| target.constructor, | ||
| propertyKey as string, | ||
| config as any as Dependency); | ||
| } | ||
| }; | ||
| } | ||
| export function InjectableFactory(config: InjectableFactoryConfig<any>): MethodDecorator { | ||
| return (target: any, property: string | symbol, descriptor: TypedPropertyDescriptor<any>) => { | ||
| DependencyRegistry.registerFactory({ ...config, fn: descriptor.value, id: `${target.__id}#${property}` }); | ||
| }; | ||
| } |
@@ -1,2 +0,2 @@ | ||
| import { Dependency, InjectableConfig, ClassTarget } from '../types'; | ||
| import { Dependency, InjectableConfig, ClassTarget, InjectableFactoryConfig } from '../types'; | ||
| import { InjectionError } from './error'; | ||
@@ -111,3 +111,5 @@ import { MetadataRegistry, Class, RootRegistry, ChangeEvent } from '@travetto/registry'; | ||
| const inst = new managed.class(...consValues); | ||
| const inst = managed.factory ? | ||
| managed.factory(...consValues) : | ||
| new managed.class(...consValues); | ||
@@ -211,2 +213,5 @@ for (let i = 0; i < fieldKeys.length; i++) { | ||
| if (pconfig.factory) { | ||
| config.factory = pconfig.factory; | ||
| } | ||
| if (pconfig.qualifier) { | ||
@@ -224,4 +229,34 @@ config.qualifier = pconfig.qualifier; | ||
| } | ||
| if (pconfig.dependencies) { | ||
| config.dependencies = { fields: {}, ...pconfig.dependencies }; | ||
| } | ||
| } | ||
| registerFactory(config: InjectableFactoryConfig<any> & { fn: (...args: any[]) => any, id?: string }) { | ||
| const finalConfig: InjectableConfig<any> = {} as any; | ||
| if (typeof config.autoCreate === 'boolean') { | ||
| finalConfig.autoCreate = { create: config.autoCreate } as any; | ||
| } | ||
| finalConfig.factory = config.fn; | ||
| finalConfig.target = config.class; | ||
| if (config.qualifier) { | ||
| finalConfig.qualifier = config.qualifier; | ||
| } | ||
| if (config.dependencies) { | ||
| finalConfig.dependencies = { | ||
| cons: config.dependencies, | ||
| fields: {} | ||
| } | ||
| } | ||
| // Create mock cls for DI purposes | ||
| const cls = { __id: config.id || `${config.class.__id}#${config.fn.name}` } as any; | ||
| finalConfig.class = cls; | ||
| this.registerClass(cls, finalConfig); | ||
| } | ||
| onInstallFinalize<T>(cls: Class<T>) { | ||
@@ -238,5 +273,6 @@ const classId = cls.__id; | ||
| if (parentConfig) { | ||
| config.dependencies.fields = Object.assign({}, | ||
| parentConfig.dependencies!.fields, | ||
| config.dependencies.fields); | ||
| config.dependencies.fields = { | ||
| ...parentConfig.dependencies!.fields, | ||
| ...config.dependencies.fields | ||
| }; | ||
@@ -243,0 +279,0 @@ // Inherit cons deps if no constructor defined |
@@ -5,3 +5,5 @@ import * as ts from 'typescript'; | ||
| let INJECTABLES = TransformUtil.buildImportAliasMap({ | ||
| ConfigLoader.initialize(); // Initialize config as we need it here | ||
| const INJECTABLES = TransformUtil.buildImportAliasMap({ | ||
| ...ConfigLoader.get('registry.injectable'), | ||
@@ -33,3 +35,3 @@ '@travetto/di': 'Injectable' | ||
| optional, | ||
| name: TransformUtil.getObjectValue(injectConfig, 'name') | ||
| qualifier: TransformUtil.getObjectValue(injectConfig, 'qualifier') | ||
| }); | ||
@@ -63,5 +65,5 @@ } | ||
| const foundDec = TransformUtil.findAnyDecorator(node, INJECTABLES, state); | ||
| let decls = node.decorators; | ||
| if (foundDec) { | ||
| if (foundDec) { // Constructor | ||
| let decls = node.decorators; | ||
@@ -87,3 +89,3 @@ node = ts.visitEachChild(node, c => visitNode(context, c, state), context); | ||
| // Add injectable to if not there | ||
| // Add injectable decorator if not there (for aliased decorators) | ||
| let injectable = TransformUtil.findAnyDecorator(node, { Injectable: new Set(['@travetto/di']) }, state); | ||
@@ -96,16 +98,15 @@ if (!injectable) { | ||
| decls = ts.createNodeArray(declTemp); | ||
| const cNode = node as any as ts.ClassDeclaration; | ||
| const ret = ts.updateClassDeclaration(cNode, | ||
| decls, | ||
| cNode.modifiers, | ||
| cNode.name, | ||
| cNode.typeParameters, | ||
| ts.createNodeArray(cNode.heritageClauses), | ||
| cNode.members | ||
| ) as any; | ||
| return ret; | ||
| } | ||
| const cNode = node as any as ts.ClassDeclaration; | ||
| const out = ts.updateClassDeclaration(cNode, | ||
| decls, | ||
| cNode.modifiers, | ||
| cNode.name, | ||
| cNode.typeParameters, | ||
| ts.createNodeArray(cNode.heritageClauses), | ||
| cNode.members | ||
| ) as any; | ||
| return out; | ||
| } if (ts.isPropertyDeclaration(node)) { | ||
| } else if (ts.isPropertyDeclaration(node)) { // Property | ||
| const expr = processDeclaration(state, node); | ||
@@ -133,2 +134,53 @@ | ||
| } | ||
| } else if (ts.isMethodDeclaration(node) && (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Static) > 0) { // tslint:disable-line no-bitwise | ||
| // Factory for static methods | ||
| const foundDec = TransformUtil.findAnyDecorator(node, { InjectableFactory: new Set(['@travetto/di']) }, state); | ||
| const decls = node.decorators; | ||
| if (foundDec) { // Constructor | ||
| const declTemp = (node.decorators || []).slice(0); | ||
| let injectArgs: object[] = []; | ||
| try { | ||
| injectArgs = node.parameters.map(x => processDeclaration(state, x)!); | ||
| } catch (e) { | ||
| // If error, skip | ||
| if (e.message !== 'Type information not found') { | ||
| throw e; | ||
| } | ||
| } | ||
| if (injectArgs.length) { | ||
| const foundExpr = (foundDec.expression as ts.CallExpression); | ||
| const args = TransformUtil.extendObjectLiteral({ | ||
| dependencies: injectArgs | ||
| }, foundExpr.arguments[0] as ts.ObjectLiteralExpression); | ||
| node = ts.createMethod( | ||
| decls!.filter(x => x !== foundDec).concat([ | ||
| ts.createDecorator( | ||
| ts.createCall( | ||
| foundExpr.expression, | ||
| foundExpr.typeArguments, | ||
| ts.createNodeArray([args]) | ||
| ) | ||
| ) | ||
| ]), | ||
| node.modifiers, | ||
| node.asteriskToken, | ||
| node.name, | ||
| node.questionToken, | ||
| node.typeParameters, | ||
| node.parameters, | ||
| node.type, | ||
| node.body | ||
| ) as any; | ||
| } | ||
| return node; | ||
| } else { | ||
| return node; | ||
| } | ||
| } | ||
@@ -138,3 +190,2 @@ return ts.visitEachChild(node, c => visitNode(context, c, state), context); | ||
| export const InjectableTransformer = { | ||
@@ -141,0 +192,0 @@ transformer: TransformUtil.importingVisitor<DiState>(() => ({ |
+8
-0
@@ -7,2 +7,3 @@ import { Class } from '@travetto/registry'; | ||
| class: Class<T>; | ||
| factory: (...args: any[]) => T; | ||
| dependencies: { | ||
@@ -19,2 +20,9 @@ cons?: Dependency<any>[], | ||
| optional?: boolean; | ||
| } | ||
| export interface InjectableFactoryConfig<T> { | ||
| class: Class<T>; | ||
| qualifier?: symbol; | ||
| dependencies?: Dependency<any>[] | ||
| autoCreate?: boolean | ||
| } |
+13
-4
@@ -1,2 +0,2 @@ | ||
| import { Injectable, Inject } from '../src'; | ||
| import { Injectable, Inject, InjectableFactory } from '../src'; | ||
| import { DbConfig, AltConfig } from './config'; | ||
@@ -6,4 +6,4 @@ | ||
| export class Database { | ||
| @Inject() dbConfig!: DbConfig<any, any>; | ||
| @Inject({ optional: true }) altConfig!: AltConfig; | ||
| @Inject() dbConfig: DbConfig<any, any>; | ||
| @Inject({ optional: true }) altConfig: AltConfig; | ||
@@ -45,2 +45,11 @@ postConstruct() { | ||
| age = 31; | ||
| } | ||
| } | ||
| export const CUSTOM_SERVICE_INHERIT = Symbol('Custom'); | ||
| class TestConfig { | ||
| @InjectableFactory({ class: ServiceInherit, qualifier: CUSTOM_SERVICE_INHERIT }) | ||
| static getObject(@Inject({ qualifier: SERVICE_INHERIT_2 }) svc: ServiceInherit) { | ||
| return new ServiceInherit2(svc.db); | ||
| } | ||
| } |
+10
-2
| import { DependencyRegistry } from '../src/service'; | ||
| import { ServiceInherit, SERVICE_INHERIT_2 } from './deps'; | ||
| import { ServiceInherit, SERVICE_INHERIT_2, CUSTOM_SERVICE_INHERIT } from './deps'; | ||
| import { Suite, Test } from '@travetto/test'; | ||
@@ -46,3 +46,2 @@ import * as assert from 'assert'; | ||
| @Test('run') | ||
@@ -72,2 +71,11 @@ async run() { | ||
| } | ||
| @Test('factory') | ||
| async factory() { | ||
| await DependencyRegistry.init(); | ||
| const inst = await DependencyRegistry.getInstance(ServiceInherit, CUSTOM_SERVICE_INHERIT); | ||
| assert(inst); | ||
| } | ||
| } |
23822
19.67%644
19.04%