diff --git a/packages/midway-core/src/common/constants.ts b/packages/midway-core/src/common/constants.ts index 95f95dbad8ca..7eea0aa949f6 100644 --- a/packages/midway-core/src/common/constants.ts +++ b/packages/midway-core/src/common/constants.ts @@ -1,9 +1,3 @@ -export const MidwayHandlerKey = { - CONFIG: 'config', - PLUGIN: 'plugin', - LOGGER: 'logger', -}; - export const FUNCTION_INJECT_KEY = 'midway:function_inject_key'; export const MIDWAY_ALL_CONFIG = 'midway:all_config_inject_key'; diff --git a/packages/midway-core/src/context/midwayContainer.ts b/packages/midway-core/src/context/midwayContainer.ts index c3ec67db2224..20a21e73a24a 100644 --- a/packages/midway-core/src/context/midwayContainer.ts +++ b/packages/midway-core/src/context/midwayContainer.ts @@ -1,10 +1,5 @@ import * as globby from 'globby'; import { - CLASS_KEY_CONSTRUCTOR, - CONFIG_KEY, - LOGGER_KEY, - PLUGIN_KEY, - getClassMetadata, getObjectDefinition, getProviderId, ObjectDefinitionOptions, @@ -18,7 +13,7 @@ import { import * as is from 'is-type-of'; import { join } from 'path'; import { ContainerConfiguration } from './configuration'; -import { FUNCTION_INJECT_KEY, MidwayHandlerKey, MIDWAY_ALL_CONFIG } from '../common/constants'; +import { FUNCTION_INJECT_KEY } from '../common/constants'; import { IConfigService, IEnvironmentService, @@ -27,12 +22,14 @@ import { MAIN_MODULE_KEY, IContainerConfiguration, ILifeCycle, + REQUEST_CTX_KEY, } from '../interface'; import { MidwayConfigService } from '../service/configService'; import { MidwayEnvironmentService } from '../service/environmentService'; import { Container } from './container'; import { generateProvideId } from '../common/util'; import { pipelineFactory } from '../features/pipeline'; +import { ResolverHandler } from './resolverHandler'; const DEFAULT_PATTERN = ['**/**.ts', '**/**.tsx', '**/**.js', '!**/**.d.ts']; const DEFAULT_IGNORE_PATTERN = [ @@ -46,13 +43,8 @@ const DEFAULT_IGNORE_PATTERN = [ const debug = require('debug')('midway:container'); -interface FrameworkDecoratorMetadata { - key: string; - propertyName: string; -} - export class MidwayContainer extends Container implements IMidwayContainer { - handlerMap: Map any>; + resolverHandler: ResolverHandler; // 仅仅用于兼容requestContainer的ctx ctx = {}; readyBindModules: Map> = new Map(); @@ -70,13 +62,12 @@ export class MidwayContainer extends Container implements IMidwayContainer { } init(): void { - this.handlerMap = new Map(); this.initService(); - this.registerEachCreatedHook(); + this.resolverHandler = new ResolverHandler(this, this.getManagedResolverFactory()); // 防止直接从applicationContext.getAsync or get对象实例时依赖当前上下文信息出错 // ctx is in requestContainer - this.registerObject('ctx', this.ctx); + this.registerObject(REQUEST_CTX_KEY, this.ctx); } initService() { @@ -200,106 +191,8 @@ export class MidwayContainer extends Container implements IMidwayContainer { return child; } - protected registerEachCreatedHook() { - // register constructor inject - this.beforeEachCreated((target, constructorArgs, context) => { - let constructorMetaData; - try { - constructorMetaData = getClassMetadata(CLASS_KEY_CONSTRUCTOR, target); - } catch (e) { - debug(`beforeEachCreated error ${e.stack}`); - } - // lack of field - if (constructorMetaData && constructorArgs) { - for (const idx in constructorMetaData) { - const index = parseInt(idx, 10); - const propertyMeta = constructorMetaData[index]; - let result; - - switch (propertyMeta.type) { - case 'config': - result = this.findHandlerHook(MidwayHandlerKey.CONFIG)( - propertyMeta.key - ); - break; - case 'logger': - result = this.findHandlerHook(MidwayHandlerKey.LOGGER)( - propertyMeta.key - ); - break; - case 'plugin': - result = this.findHandlerHook(MidwayHandlerKey.PLUGIN)( - propertyMeta.key - ); - break; - } - constructorArgs[index] = result; - } - } - }); - - // register property inject - this.afterEachCreated((instance, context, definition) => { - // 处理配置装饰器 - const configSetterProps: FrameworkDecoratorMetadata[] = getClassMetadata( - CONFIG_KEY, - instance - ); - this.defineGetterPropertyValue( - configSetterProps, - instance, - this.findHandlerHook(MidwayHandlerKey.CONFIG) - ); - // 处理插件装饰器 - const pluginSetterProps: FrameworkDecoratorMetadata[] = getClassMetadata( - PLUGIN_KEY, - instance - ); - this.defineGetterPropertyValue( - pluginSetterProps, - instance, - this.findHandlerHook(MidwayHandlerKey.PLUGIN) - ); - // 处理日志装饰器 - const loggerSetterProps: FrameworkDecoratorMetadata[] = getClassMetadata( - LOGGER_KEY, - instance - ); - this.defineGetterPropertyValue( - loggerSetterProps, - instance, - this.findHandlerHook(MidwayHandlerKey.LOGGER) - ); - }); - } - - /** - * binding getter method for decorator - * - * @param setterProps - * @param instance - * @param getterHandler - */ - private defineGetterPropertyValue( - setterProps: FrameworkDecoratorMetadata[], - instance, - getterHandler - ) { - if (setterProps && getterHandler) { - for (const prop of setterProps) { - if (prop.propertyName) { - Object.defineProperty(instance, prop.propertyName, { - get: () => getterHandler(prop.key, instance), - configurable: true, // 继承对象有可能会有相同属性,这里需要配置成 true - enumerable: true, - }); - } - } - } - } - registerDataHandler(handlerType: string, handler: (handlerKey) => any) { - this.handlerMap.set(handlerType, handler); + this.resolverHandler.registerHandler(handlerType, handler); } registerCustomBinding(objectDefinition, target) { @@ -315,20 +208,6 @@ export class MidwayContainer extends Container implements IMidwayContainer { } } - /** - * get hook from current map or parent map - * @param hookKey - */ - findHandlerHook(hookKey: string) { - if (this.handlerMap.has(hookKey)) { - return this.handlerMap.get(hookKey); - } - - if (this.parent) { - return (this.parent as MidwayContainer).findHandlerHook(hookKey); - } - } - createConfiguration(): IContainerConfiguration { const containerConfiguration = new ContainerConfiguration(this); return containerConfiguration; @@ -361,18 +240,6 @@ export class MidwayContainer extends Container implements IMidwayContainer { async ready() { super.ready(); if (this.configService) { - // register handler for container - this.registerDataHandler(MidwayHandlerKey.CONFIG, (key: string) => { - if (key) { - if (key === MIDWAY_ALL_CONFIG) { - return this.configService.getConfiguration(); - } else { - const val = this.configService.getConfiguration(key); - debug('@config key => %s value => %j.', key, val); - return val; - } - } - }); // 加载配置 await this.configService.load(); } diff --git a/packages/midway-core/src/context/requestContainer.ts b/packages/midway-core/src/context/requestContainer.ts index 85c3ea7feeea..53aed5ce87f3 100644 --- a/packages/midway-core/src/context/requestContainer.ts +++ b/packages/midway-core/src/context/requestContainer.ts @@ -14,6 +14,14 @@ export class MidwayRequestContainer extends MidwayContainer { this.registerObject(REQUEST_CTX_KEY, ctx); // register contextLogger this.registerObject('logger', ctx.logger); + + const resolverHandler = this.applicationContext.resolverHandler; + this.beforeEachCreated(resolverHandler.beforeEachCreated.bind(resolverHandler)); + this.afterEachCreated(resolverHandler.afterEachCreated.bind(resolverHandler)); + } + + init() { + // do nothing } get(identifier: any, args?: any): T { @@ -76,10 +84,6 @@ export class MidwayRequestContainer extends MidwayContainer { } } - initService() { - // do nothing - } - async ready() { this.readied = true; // ignore other things diff --git a/packages/midway-core/src/context/resolverHandler.ts b/packages/midway-core/src/context/resolverHandler.ts new file mode 100644 index 000000000000..d78049ccffd2 --- /dev/null +++ b/packages/midway-core/src/context/resolverHandler.ts @@ -0,0 +1,132 @@ +import { + CLASS_KEY_CONSTRUCTOR, + getClassMetadata, + CONFIG_KEY, +} from '@midwayjs/decorator'; +import { ManagedResolverFactory } from './managedResolverFactory'; +import { MidwayContainer } from './midwayContainer'; +import { MIDWAY_ALL_CONFIG } from '../common/constants'; + +interface FrameworkDecoratorMetadata { + key: string; + propertyName: string; +} + +const debug = require('debug')('midway:container'); + +export type HandlerFunction = (handlerKey: string, instance?: any) => any; + +export class ResolverHandler { + private handlerMap: Map; + private resolverFactory: ManagedResolverFactory; + private container: MidwayContainer; + + constructor(container: MidwayContainer, factory: ManagedResolverFactory) { + this.container = container; + this.resolverFactory = factory; + this.handlerMap = new Map(); + this.bindCreatedHook(); + } + + bindCreatedHook() { + this.resolverFactory.beforeEachCreated(this.beforeEachCreated.bind(this)); + this.resolverFactory.afterEachCreated(this.afterEachCreated.bind(this)); + + if (this.container.configService) { + // register handler for container + this.registerHandler(CONFIG_KEY, (key: string) => { + if (key) { + if (key === MIDWAY_ALL_CONFIG) { + return this.container.configService.getConfiguration(); + } else { + const val = this.container.configService.getConfiguration(key); + debug('@config key => %s value => %j.', key, val); + return val; + } + } + }); + } + } + /** + * 创建对象前 + * @param target 当前对象 + * @param constructorArgs 构造参数 + * @param context 上下文 + */ + beforeEachCreated(target, constructorArgs: any[], context) { + let constructorMetaData; + try { + constructorMetaData = getClassMetadata(CLASS_KEY_CONSTRUCTOR, target); + } catch (e) { + debug(`beforeEachCreated error ${e.stack}`); + } + // lack of field + if (constructorMetaData && constructorArgs) { + for (const idx in constructorMetaData) { + const index = parseInt(idx, 10); + const propertyMeta = constructorMetaData[index]; + const hook = this.getHandler(propertyMeta.type); + if (hook) { + constructorArgs[index] = hook( + propertyMeta.key + ); + } + } + } + } + /** + * 创建对象后 + * @param instance 对象 + * @param context 上下文 + * @param definition 定义 + */ + afterEachCreated(instance, context, definition) { + const iter = this.handlerMap.keys(); + for (const key of iter) { + // 处理配置装饰器 + const setterProps: FrameworkDecoratorMetadata[] = getClassMetadata( + key, + instance + ); + this.defineGetterPropertyValue( + setterProps, + instance, + this.getHandler(key) + ); + } + } + /** + * binding getter method for decorator + * + * @param setterProps + * @param instance + * @param getterHandler + */ + private defineGetterPropertyValue( + setterProps: FrameworkDecoratorMetadata[], + instance, + getterHandler + ) { + if (setterProps && getterHandler) { + for (const prop of setterProps) { + if (prop.propertyName) { + Object.defineProperty(instance, prop.propertyName, { + get: () => getterHandler(prop.key, instance), + configurable: true, // 继承对象有可能会有相同属性,这里需要配置成 true + enumerable: true, + }); + } + } + } + } + + registerHandler(key: string, fn: HandlerFunction) { + this.handlerMap.set(key, fn); + } + + getHandler(key: string) { + if (this.handlerMap.has(key)) { + return this.handlerMap.get(key); + } + } +} diff --git a/packages/midway-core/src/interface.ts b/packages/midway-core/src/interface.ts index 1c236bdecbef..682d5c62ad97 100644 --- a/packages/midway-core/src/interface.ts +++ b/packages/midway-core/src/interface.ts @@ -225,3 +225,21 @@ export interface IEnvironmentService { export interface IMiddleware { resolve: () => (context: T, next: () => Promise) => any; } + +export interface IMidwayCoreApplication { + isTsMode: boolean; + baseDir: string; + appDir: string; + env?: string; + /** + * application/agent + */ + type?: string; + applicationContext: IMidwayContainer; + /** + * middlewares + */ + middleware: any[]; + + getConfig(key?: string): any; +} diff --git a/packages/midway-core/src/loader.ts b/packages/midway-core/src/loader.ts index 34f4ba0e4301..a0c5bb4ed99d 100644 --- a/packages/midway-core/src/loader.ts +++ b/packages/midway-core/src/loader.ts @@ -1,6 +1,8 @@ import * as path from 'path'; import { MidwayContainer } from './context/midwayContainer'; import { Container } from './context/container'; +import { IMidwayCoreApplication } from './interface'; +import { APPLICATION_KEY } from '@midwayjs/decorator'; function buildLoadDir(baseDir, dir) { if (!path.isAbsolute(dir)) { @@ -45,6 +47,12 @@ export class ContainerLoader { this.applicationContext.registerDataHandler(hookKey, hookHandler); } + bindApp(app: IMidwayCoreApplication) { + this.applicationContext.registerDataHandler(APPLICATION_KEY, () => { + return app; + }); + } + loadDirectory(loadOpts: { baseDir?: string; loadDir?: string[]; diff --git a/packages/midway-core/test/context/midwayContainer.test.ts b/packages/midway-core/test/context/midwayContainer.test.ts index d062f538f86a..b8369a88705d 100644 --- a/packages/midway-core/test/context/midwayContainer.test.ts +++ b/packages/midway-core/test/context/midwayContainer.test.ts @@ -9,7 +9,7 @@ import { TestBinding, LifeCycleTest, LifeCycleTest1 } from '../fixtures/lifecycl import sinon = require('sinon'); import mm = require('mm'); import * as decs from '@midwayjs/decorator'; -const { LIFECYCLE_IDENTIFIER_PREFIX } = decs; +const { LIFECYCLE_IDENTIFIER_PREFIX, APPLICATION_KEY, CONFIGURATION_KEY, resetModule } = decs; describe('/test/context/midwayContainer.test.ts', () => { @@ -51,6 +51,9 @@ describe('/test/context/midwayContainer.test.ts', () => { const container = new MidwayContainer(); it('lifecycle should be ok', async () => { + container.registerDataHandler(APPLICATION_KEY, () => { + return { hello: 123}; + }); const cfg = container.createConfiguration(); container.bind(TestBinding); cfg.bindConfigurationClass(LifeCycleTest); @@ -80,6 +83,7 @@ describe('/test/context/midwayContainer.test.ts', () => { expect(container.registry.hasObject(LIFECYCLE_IDENTIFIER_PREFIX + 'lifeCycleTest')).false; expect(callback.withArgs('on stop').calledOnce).true; + resetModule(CONFIGURATION_KEY); mm.restore(); }); }); diff --git a/packages/midway-core/test/fixtures/app-with-configuration/midway-plugin-no-pkg-json/dist/configuration.ts b/packages/midway-core/test/fixtures/app-with-configuration/midway-plugin-no-pkg-json/dist/configuration.ts index 178ff169da26..707ed3e7e154 100644 --- a/packages/midway-core/test/fixtures/app-with-configuration/midway-plugin-no-pkg-json/dist/configuration.ts +++ b/packages/midway-core/test/fixtures/app-with-configuration/midway-plugin-no-pkg-json/dist/configuration.ts @@ -1,9 +1,10 @@ import { Configuration } from '@midwayjs/decorator'; - +import path = require('path'); +const abPath = path.resolve(path.join(__dirname, './config/config.default')); @Configuration({ namespace: 'midway-plugin-no-pkg-json', importConfigs: [ - './config/config.default', + abPath, './config/config.local' ] }) diff --git a/packages/midway-core/test/fixtures/base-app-forbindapp/package.json b/packages/midway-core/test/fixtures/base-app-forbindapp/package.json new file mode 100644 index 000000000000..621cdc6a4174 --- /dev/null +++ b/packages/midway-core/test/fixtures/base-app-forbindapp/package.json @@ -0,0 +1,3 @@ +{ + "name": "ali-demo" +} diff --git a/packages/midway-core/test/fixtures/base-app-forbindapp/src/app/controller/api.ts b/packages/midway-core/test/fixtures/base-app-forbindapp/src/app/controller/api.ts new file mode 100644 index 000000000000..9d115de6624d --- /dev/null +++ b/packages/midway-core/test/fixtures/base-app-forbindapp/src/app/controller/api.ts @@ -0,0 +1,13 @@ +import {BaseService} from '../../lib/service'; + +exports.index = async (ctx, next) => { + const context = ctx.app.applicationContext; + const baseService = await context.getAsync('baseService'); + ctx.body = baseService.config.c + baseService.plugin2.text; +}; + +exports.baseService = async (ctx, next) => { + const context = ctx.app.applicationContext; + const baseService = await context.getAsync(BaseService); + ctx.body = baseService.config.c + baseService.plugin2.text; +}; diff --git a/packages/midway-core/test/fixtures/base-app-forbindapp/src/app/router.ts b/packages/midway-core/test/fixtures/base-app-forbindapp/src/app/router.ts new file mode 100644 index 000000000000..a791556bf3ae --- /dev/null +++ b/packages/midway-core/test/fixtures/base-app-forbindapp/src/app/router.ts @@ -0,0 +1,5 @@ +module.exports = function(app) { + app.get('/api/index', app.controller.api); + app.get('/api/baseService', app.controller.api.baseService); + app.get('/api', app.controller.api.index); +}; diff --git a/packages/midway-core/test/fixtures/base-app-forbindapp/src/config/config.default.ts b/packages/midway-core/test/fixtures/base-app-forbindapp/src/config/config.default.ts new file mode 100644 index 000000000000..182fd4cc1002 --- /dev/null +++ b/packages/midway-core/test/fixtures/base-app-forbindapp/src/config/config.default.ts @@ -0,0 +1,12 @@ +export const keys = 'key'; + +export const hello = { + a: 1, + b: 2, + d: [1, 2, 3], +}; + +export const plugins = { + bucLogin: false, + plugin2: true +}; diff --git a/packages/midway-core/test/fixtures/base-app-forbindapp/src/config/config.unittest.ts b/packages/midway-core/test/fixtures/base-app-forbindapp/src/config/config.unittest.ts new file mode 100644 index 000000000000..764021a2a8b6 --- /dev/null +++ b/packages/midway-core/test/fixtures/base-app-forbindapp/src/config/config.unittest.ts @@ -0,0 +1,5 @@ + +exports.hello = { + b: 4, + c: 3, +}; diff --git a/packages/midway-core/test/fixtures/base-app-forbindapp/src/config/plugin.ts b/packages/midway-core/test/fixtures/base-app-forbindapp/src/config/plugin.ts new file mode 100644 index 000000000000..077c3c1a4790 --- /dev/null +++ b/packages/midway-core/test/fixtures/base-app-forbindapp/src/config/plugin.ts @@ -0,0 +1,13 @@ +import * as path from 'path'; + +module.exports = { + // 默认开启的插件 + + /** + * 支持各个 bu 的健康检查 + */ + plugin2: { + enable: true, + path: path.join(__dirname, '../plugins/plugin2'), + } +}; diff --git a/packages/midway-core/test/fixtures/base-app-forbindapp/src/configuration.ts b/packages/midway-core/test/fixtures/base-app-forbindapp/src/configuration.ts new file mode 100644 index 000000000000..37b6d547df72 --- /dev/null +++ b/packages/midway-core/test/fixtures/base-app-forbindapp/src/configuration.ts @@ -0,0 +1,22 @@ +import { ILifeCycle, IMidwayCoreApplication } from '../../../../src'; +import { Configuration, App } from '@midwayjs/decorator'; + +@Configuration({ + imports: [ + ], + importObjects: { + aa: 123 + } +}) +class AutoConfiguraion implements ILifeCycle { + @App() + test: IMidwayCoreApplication; + + async onReady() { + if (this.test.baseDir !== 'hello this is basedir') { + throw new Error('midway core application error'); + } + } +} + +module.exports = AutoConfiguraion; diff --git a/packages/midway-core/test/fixtures/base-app-forbindapp/src/lib/service.ts b/packages/midway-core/test/fixtures/base-app-forbindapp/src/lib/service.ts new file mode 100644 index 000000000000..048d858fbe5e --- /dev/null +++ b/packages/midway-core/test/fixtures/base-app-forbindapp/src/lib/service.ts @@ -0,0 +1,17 @@ +import { Config, Plugin, Logger, Provide, Async, App } from '@midwayjs/decorator'; +import { IMidwayCoreApplication } from '../../../../../src'; +@Async() +@Provide() +export class BaseService { + @Config('hello') + config; + + @Plugin('plugin2') + plugin2; + + @Logger() + logger; + + @App() + test: IMidwayCoreApplication; +} diff --git a/packages/midway-core/test/fixtures/base-app-forbindapp/src/plugins/plugin2/README.md b/packages/midway-core/test/fixtures/base-app-forbindapp/src/plugins/plugin2/README.md new file mode 100644 index 000000000000..ca7efa171998 --- /dev/null +++ b/packages/midway-core/test/fixtures/base-app-forbindapp/src/plugins/plugin2/README.md @@ -0,0 +1 @@ +# loader diff --git a/packages/midway-core/test/fixtures/base-app-forbindapp/src/plugins/plugin2/app.js b/packages/midway-core/test/fixtures/base-app-forbindapp/src/plugins/plugin2/app.js new file mode 100644 index 000000000000..e1b415b2549e --- /dev/null +++ b/packages/midway-core/test/fixtures/base-app-forbindapp/src/plugins/plugin2/app.js @@ -0,0 +1,15 @@ +module.exports = function(app) { + + let plugin2 = {}; + + app.beforeStart(async () => { + await new Promise((resolve) => { + setTimeout(() => { + plugin2.text = 't'; + resolve(); + }, 10); + }) + }); + + app.plugin2 = plugin2; +}; diff --git a/packages/midway-core/test/fixtures/base-app-forbindapp/src/plugins/plugin2/config/config.js b/packages/midway-core/test/fixtures/base-app-forbindapp/src/plugins/plugin2/config/config.js new file mode 100644 index 000000000000..702e391dd40c --- /dev/null +++ b/packages/midway-core/test/fixtures/base-app-forbindapp/src/plugins/plugin2/config/config.js @@ -0,0 +1,8 @@ + +'use strict'; + +module.exports = { + a: 1, + b: 2, + text: 'plugin2', +}; diff --git a/packages/midway-core/test/fixtures/base-app-forbindapp/src/plugins/plugin2/package.json b/packages/midway-core/test/fixtures/base-app-forbindapp/src/plugins/plugin2/package.json new file mode 100644 index 000000000000..ce905deeb33b --- /dev/null +++ b/packages/midway-core/test/fixtures/base-app-forbindapp/src/plugins/plugin2/package.json @@ -0,0 +1,7 @@ +{ + "name":"plugin2", + "eggPlugin": { + "name": "plugin2", + "dep": [] + } +} diff --git a/packages/midway-core/test/fixtures/lifecycle.ts b/packages/midway-core/test/fixtures/lifecycle.ts index 511a7571fc2d..9ca65174e34c 100644 --- a/packages/midway-core/test/fixtures/lifecycle.ts +++ b/packages/midway-core/test/fixtures/lifecycle.ts @@ -1,5 +1,6 @@ import { ILifeCycle, IMidwayContainer } from '../../src'; import { Provide, Inject, Configuration } from '@midwayjs/decorator'; +import { App } from '@midwayjs/decorator/src'; @Provide() export class TestBinding { @@ -40,6 +41,14 @@ export class LifeCycleTest1 implements ILifeCycle { @Inject() testBinding: TestBinding; + test: any; + constructor(@App() ttt: any) { + this.test = ttt; + } + + @App() + ttt: any; + async onReady() { this.tts = await this.testBinding.doReady(); @@ -49,5 +58,12 @@ export class LifeCycleTest1 implements ILifeCycle { resolve(); }, 500); }); + + if (this.test.hello !== 123) { + throw new Error('test.hello !== 123'); + } + if (this.ttt.hello !== 123) { + throw new Error('ttt.hello !== 123'); + } } } diff --git a/packages/midway-core/test/loader.test.ts b/packages/midway-core/test/loader.test.ts index 040faa9b2958..8e8c59d63318 100644 --- a/packages/midway-core/test/loader.test.ts +++ b/packages/midway-core/test/loader.test.ts @@ -87,6 +87,44 @@ describe('/test/loader.test.ts', () => { assert(baseServiceCtx.plugin2.b === 2); }); + it('should load ts file and bindapp success', async () => { + const loader = new ContainerLoader({ + baseDir: path.join(__dirname, './fixtures/base-app-forbindapp/src'), + }); + loader.initialize(); + loader.loadDirectory(); + const tt: any = { + baseDir: 'hello this is basedir' + }; + loader.bindApp(tt); + await loader.refresh(); + // register handler for container + loader.registerHook(CONFIG_KEY, (key, target) => { + assert( + target instanceof + require('./fixtures/base-app-forbindapp/src/lib/service')[ + 'BaseService' + ] + ); + return 'hello'; + }); + + loader.registerHook(PLUGIN_KEY, (key, target) => { + return { b: 2 }; + }); + + loader.registerHook(LOGGER_KEY, (key, target) => { + return console; + }); + + const appCtx = loader.getApplicationContext(); + const baseService: any = await appCtx.getAsync('baseService'); + assert(baseService.config === 'hello'); + assert(baseService.logger === console); + assert(baseService.plugin2.b === 2); + assert(baseService.test.baseDir === 'hello this is basedir'); + }); + it('load ts file support constructor inject', async () => { const loader = new ContainerLoader({ baseDir: path.join(__dirname, './fixtures/base-app-constructor/src'), diff --git a/packages/midway-decorator/src/common/constant.ts b/packages/midway-decorator/src/common/constant.ts index 34e9e622a7e0..7d495e30817c 100644 --- a/packages/midway-decorator/src/common/constant.ts +++ b/packages/midway-decorator/src/common/constant.ts @@ -19,6 +19,7 @@ export const HSF_KEY = 'rpc:hsf'; export const CONFIG_KEY = 'config'; export const PLUGIN_KEY = 'plugin'; export const LOGGER_KEY = 'logger'; +export const APPLICATION_KEY = '__midway_framework_app__'; ////////////////////////////////////////// inject keys // constructor key diff --git a/packages/midway-decorator/src/common/decoratorManager.ts b/packages/midway-decorator/src/common/decoratorManager.ts index 87dbb1b1608d..33175245ee09 100644 --- a/packages/midway-decorator/src/common/decoratorManager.ts +++ b/packages/midway-decorator/src/common/decoratorManager.ts @@ -33,6 +33,10 @@ export class DecoratorManager extends Map { this.get(key).add(module); } + resetModule(key) { + this.set(key, new Set()); + } + static getDecoratorClassKey(decoratorNameKey: decoratorKey) { return decoratorNameKey.toString() + '_CLS'; } @@ -400,6 +404,13 @@ export function saveModule(decoratorNameKey: decoratorKey, target) { export function listModule(decoratorNameKey: decoratorKey): any[] { return manager.listModule(decoratorNameKey); } +/** + * reset module + * @param decoratorNameKey + */ +export function resetModule(decoratorNameKey: decoratorKey): void { + return manager.resetModule(decoratorNameKey); +} /** * clear all module diff --git a/packages/midway-decorator/src/framework/app.ts b/packages/midway-decorator/src/framework/app.ts new file mode 100644 index 000000000000..f42c1e6cbf16 --- /dev/null +++ b/packages/midway-decorator/src/framework/app.ts @@ -0,0 +1,22 @@ +import { + attachClassMetadata, + APPLICATION_KEY, + attachConstructorDataOnClass, +} from '../common'; + +export function App() { + return function(target: any, targetKey: string, index?: number): void { + if (typeof index === 'number') { + attachConstructorDataOnClass(targetKey, target, APPLICATION_KEY, index); + } else { + attachClassMetadata( + APPLICATION_KEY, + { + key: APPLICATION_KEY, + propertyName: targetKey, + }, + target + ); + } + }; +} diff --git a/packages/midway-decorator/src/index.ts b/packages/midway-decorator/src/index.ts index 9d0743a62893..f7941c0a58a7 100644 --- a/packages/midway-decorator/src/index.ts +++ b/packages/midway-decorator/src/index.ts @@ -10,3 +10,4 @@ export * from './rpc/hsf'; export * from './framework/config'; export * from './framework/logger'; export * from './framework/plugin'; +export * from './framework/app'; diff --git a/packages/midway-decorator/test/common/decoratorManager.test.ts b/packages/midway-decorator/test/common/decoratorManager.test.ts index 447399711a56..65c1cff7f659 100644 --- a/packages/midway-decorator/test/common/decoratorManager.test.ts +++ b/packages/midway-decorator/test/common/decoratorManager.test.ts @@ -10,12 +10,14 @@ import { getProviderId, listMethodDataFromClass, listModule, + resetModule, listPreloadModule, getObjectDefinition, savePropertyDataToClass, listPropertyDataFromClass, attachPropertyMetadata } from '../../src'; import * as assert from 'assert'; import { expect } from 'chai'; import { ManagerTest as module } from '../fixtures/decorator/customClass'; import mm = require('mm'); +import { PRELOAD_MODULE_KEY } from '../../dist'; describe('/test/common/decoratorManager.test.ts', () => { it('should save data on class and get it', () => { @@ -39,8 +41,12 @@ describe('/test/common/decoratorManager.test.ts', () => { }); it('should list preload module', () => { - const modules = listPreloadModule(); + let modules = listPreloadModule(); assert(modules.length === 1); + + resetModule(PRELOAD_MODULE_KEY); + modules = listPreloadModule(); + assert(modules.length === 0); }); it('should list module', () => { diff --git a/packages/midway-decorator/test/faas/handler.test.ts b/packages/midway-decorator/test/faas/handler.test.ts index 778607698f83..505c33955110 100644 --- a/packages/midway-decorator/test/faas/handler.test.ts +++ b/packages/midway-decorator/test/faas/handler.test.ts @@ -5,16 +5,26 @@ import { Handler, FUNC_KEY, getClassMetadata } from '../../src'; class Test { @Handler('index.handler', { middleware: ['hello'] }) greeting() {} + + @Handler({ funHandler: 'index.handler1', middleware: ['hello'] }) + test() {} } describe('/test/faas/handler.test.ts', () => { it('handler decorator should be ok', () => { const meta = getClassMetadata(FUNC_KEY, Test); delete meta[0].descriptor; + delete meta[1].descriptor; expect(meta).deep.eq([{ funHandler: 'index.handler', middleware: ['hello'], key: 'greeting', + }, { + funHandler: 'index.handler1', + key: 'test', + middleware: [ + 'hello' + ] }]); }); }); diff --git a/packages/midway-decorator/test/framework/app.test.ts b/packages/midway-decorator/test/framework/app.test.ts new file mode 100644 index 000000000000..81137c948c37 --- /dev/null +++ b/packages/midway-decorator/test/framework/app.test.ts @@ -0,0 +1,29 @@ + +import { expect } from 'chai'; +import { App, getClassMetadata, CLASS_KEY_CONSTRUCTOR, APPLICATION_KEY } from '../../src'; + +class Test { + constructor(@App() aaa: any) { + // ignore + } + + @App() + hhh: any; +} + +describe('/test/framework/config.test.ts', () => { + it('config decorator should be ok', () => { + let data = getClassMetadata(CLASS_KEY_CONSTRUCTOR, Test); + expect(data).deep.eq({ + 0: { + key: 'aaa', + type: APPLICATION_KEY + } + }); + + data = getClassMetadata(APPLICATION_KEY, Test); + expect(data).deep.eq( + [ {key: APPLICATION_KEY, propertyName: 'hhh'} ] + ); + }); +}); diff --git a/packages/midway-web/src/index.ts b/packages/midway-web/src/index.ts index 74e917aafa14..7e63876a27b2 100644 --- a/packages/midway-web/src/index.ts +++ b/packages/midway-web/src/index.ts @@ -29,6 +29,7 @@ export { Options as options, Head as head, All as all, + App as app, KoaMiddleware, KoaMiddlewareParamArray, ControllerOption, diff --git a/packages/midway-web/src/loader/webLoader.ts b/packages/midway-web/src/loader/webLoader.ts index 16a2a3bed578..f7f4456dce2e 100644 --- a/packages/midway-web/src/loader/webLoader.ts +++ b/packages/midway-web/src/loader/webLoader.ts @@ -7,12 +7,12 @@ import { RouterParamValue, WEB_ROUTER_KEY, WEB_ROUTER_PARAM_KEY, - getClassMetadata, getPropertyDataFromClass, getProviderId, listModule + getClassMetadata, getPropertyDataFromClass, getProviderId, listModule, PLUGIN_KEY, LOGGER_KEY, APPLICATION_KEY } from '@midwayjs/decorator'; import { EggAppInfo } from 'egg'; import * as extend from 'extend2'; import * as fs from 'fs'; -import { ContainerLoader, MidwayContainer, MidwayHandlerKey } from '@midwayjs/core'; +import { ContainerLoader, MidwayContainer } from '@midwayjs/core'; import * as path from 'path'; import { Middleware, MiddlewareParamArray, MidwayLoaderOptions, WebMiddleware } from '../interface'; import { isTypeScriptEnvironment } from '../utils'; @@ -81,16 +81,20 @@ export class MidwayWebLoader extends EggLoader { // 如果没有关闭autoLoad 则进行load this.containerLoader.loadDirectory(containerConfig); - this.containerLoader.registerHook(MidwayHandlerKey.PLUGIN, (key: string) => { + this.containerLoader.registerHook(PLUGIN_KEY, (key: string) => { return this.app[key] || this.pluginContext.get(key); }); - this.containerLoader.registerHook(MidwayHandlerKey.LOGGER, (key: string) => { + this.containerLoader.registerHook(LOGGER_KEY, (key: string) => { if (this.app.getLogger) { return this.app.getLogger(key); } return this.options.logger; }); + // register app + this.containerLoader.registerHook(APPLICATION_KEY, (key: string) => { + return this.app; + }); } // loadPlugin -> loadConfig -> afterLoadConfig diff --git a/packages/midway-web/test/fixtures/enhance/base-app-decorator/src/configuration.ts b/packages/midway-web/test/fixtures/enhance/base-app-decorator/src/configuration.ts index 1d097d0207b7..4446900d954b 100644 --- a/packages/midway-web/test/fixtures/enhance/base-app-decorator/src/configuration.ts +++ b/packages/midway-web/test/fixtures/enhance/base-app-decorator/src/configuration.ts @@ -1,13 +1,24 @@ -import { configuration, logger } from '../../../../../src'; -import { ILifeCycle, IMidwayContainer } from '@midwayjs/core'; +import { configuration, logger, app } from '../../../../../src'; +import { ILifeCycle, IMidwayContainer, IMidwayCoreApplication } from '@midwayjs/core'; @configuration({}) export class LifeCycleTest implements ILifeCycle { @logger() logger: any; + + @app() + appx: IMidwayCoreApplication; + async onReady(container: IMidwayContainer): Promise { console.log('this is lifecycle test1'); this.logger.debug('this is a lifecycle test1'); + + if (this.appx) { + throw new Error('app is empty!'); + } + if (!this.appx.baseDir) { + throw new Error('app.baseDir is empty!'); + } } async onStop(container: IMidwayContainer): Promise { diff --git a/packages/midway-web/test/midway.test.ts b/packages/midway-web/test/midway.test.ts new file mode 100644 index 000000000000..ea7ca196f093 --- /dev/null +++ b/packages/midway-web/test/midway.test.ts @@ -0,0 +1,46 @@ +import { Application, Agent } from '../src/midway'; +import * as assert from 'assert'; + +describe('/test/midway.test.ts', () => { + it('MidwayApplication should be ok', () => { + const app = new Application(); + assert(app.enablePlugins); + + assert(!app.getConfig('test')); + (app as any).config['test'] = 'hello world'; + assert(app.getConfig('test') === 'hello world'); + + assert(app.getConfig()['test'] === 'hello world'); + assert(app.getConfig()['session']); + + assert(app.getPluginContext()); + assert(app.pluginContext); + try { + assert(app.getPlugin('schedulePlus')); + } catch (e) { + assert(e.message === 'schedulePlus is not valid in current context'); + } + }); + + it('MidwayAgent should be ok', () => { + const app = new Agent(); + assert(!app.getConfig('test')); + (app as any).config['test'] = 'hello world'; + assert(app.getConfig('test') === 'hello world'); + + assert(app.getConfig()['test'] === 'hello world'); + assert(app.getConfig()['session']); + + assert(app.getPluginContext()); + assert(app.pluginContext); + assert(app.getApplicationContext()); + assert(app.applicationContext); + try { + assert(app.getPlugin('schedulePlus')); + } catch (e) { + assert(e.message === 'schedulePlus is not valid in current context'); + } + + assert(app.appDir); + }); +});