From 834eb8148531e415a44436e16074ded88de29836 Mon Sep 17 00:00:00 2001 From: Harry Chen Date: Tue, 5 Mar 2019 16:06:32 +0800 Subject: [PATCH] refactor: remove egg-core from midway-core --- packages/midway-core/.autod.conf.js | 1 - packages/midway-core/package.json | 9 +- .../src/{constants.ts => constant.ts} | 2 + packages/midway-core/src/container.ts | 69 ++--- .../midway-core/src/decorators/application.ts | 52 ---- packages/midway-core/src/decorators/index.ts | 3 - .../midway-core/src/decorators/metaKeys.ts | 8 - packages/midway-core/src/index.ts | 7 +- packages/midway-core/src/loader.ts | 244 ++++------------- .../src/{decorators => }/providerWrapper.ts | 2 +- packages/midway-core/src/requestContainer.ts | 18 -- packages/midway-core/src/utils.ts | 54 ---- .../base-app-constructor/src/lib/service.ts | 7 +- .../src/app/controller/api.ts | 1 - .../base-app-decorator/src/lib/service.ts | 4 +- .../base-app-function/src/lib/factory.ts | 2 +- .../base-app-function/src/lib/otherFactory.ts | 2 +- .../base-app-function/src/lib/service.ts | 2 +- .../midway-core/test/fixtures/midway/index.ts | 91 ------- .../test/fixtures/midway/package.json | 7 - packages/midway-core/test/loader.test.ts | 250 ++++++------------ packages/midway-core/test/utils.ts | 38 --- packages/midway-decorator/package.json | 2 +- .../midway-decorator/src/common/priority.ts | 4 +- .../midway-decorator/src/common/schedule.ts | 4 +- packages/midway-decorator/src/constant.ts | 8 + packages/midway-decorator/src/faas/handler.ts | 4 +- .../midway-decorator/src/framework/config.ts | 19 ++ .../midway-decorator/src/framework/logger.ts | 19 ++ .../midway-decorator/src/framework/plugin.ts | 19 ++ packages/midway-decorator/src/index.ts | 3 + packages/midway-decorator/src/utils.ts | 23 ++ .../midway-decorator/src/web/controller.ts | 4 +- .../src/web/requestMapping.ts | 4 +- packages/midway-mock/package.json | 2 +- packages/midway-schedule/agent.ts | 4 +- packages/midway-schedule/app.ts | 4 +- packages/midway-schedule/package.json | 2 +- packages/midway-web/.autod.conf.js | 1 - packages/midway-web/package.json | 4 +- packages/midway-web/src/index.ts | 27 ++ .../src/interface.ts | 0 packages/midway-web/src/loader/webLoader.ts | 229 +++++++++++++++- packages/midway-web/src/utils.ts | 8 + .../enhance/base-app-async/src/lib/service.ts | 2 +- .../base-app-constructor/src/lib/service.ts | 2 +- .../base-app-decorator/src/lib/service.ts | 2 +- .../base-app-function/src/lib/service.ts | 2 +- .../src/web/middleware/api.ts | 3 +- .../base-app-utils/src/app/controller/api.ts | 3 +- packages/midway/package.json | 3 - packages/midway/src/index.ts | 26 -- 52 files changed, 548 insertions(+), 762 deletions(-) rename packages/midway-core/src/{constants.ts => constant.ts} (59%) delete mode 100644 packages/midway-core/src/decorators/application.ts delete mode 100644 packages/midway-core/src/decorators/index.ts delete mode 100644 packages/midway-core/src/decorators/metaKeys.ts rename packages/midway-core/src/{decorators => }/providerWrapper.ts (89%) delete mode 100644 packages/midway-core/src/utils.ts delete mode 100644 packages/midway-core/test/fixtures/midway/index.ts delete mode 100644 packages/midway-core/test/fixtures/midway/package.json delete mode 100644 packages/midway-core/test/utils.ts create mode 100644 packages/midway-decorator/src/framework/config.ts create mode 100644 packages/midway-decorator/src/framework/logger.ts create mode 100644 packages/midway-decorator/src/framework/plugin.ts create mode 100644 packages/midway-decorator/src/utils.ts rename packages/{midway-core => midway-web}/src/interface.ts (100%) diff --git a/packages/midway-core/.autod.conf.js b/packages/midway-core/.autod.conf.js index add647a176a2..d7c3cb24536a 100644 --- a/packages/midway-core/.autod.conf.js +++ b/packages/midway-core/.autod.conf.js @@ -11,7 +11,6 @@ module.exports = { 'run', ], dep: [ - "inflection" ], devdep: [ ], diff --git a/packages/midway-core/package.json b/packages/midway-core/package.json index f30055fc8cba..00e843d152cf 100644 --- a/packages/midway-core/package.json +++ b/packages/midway-core/package.json @@ -27,15 +27,12 @@ "midway-bin": "^1.4.3" }, "dependencies": { + "@midwayjs/decorator": "^1.4.3", "camelcase": "^5.0.0", "debug": "^4.1.1", - "egg-core": "^4.14.1", - "extend2": "^1.0.0", "globby": "^9.0.0", - "inflection": "^1.12.0", - "injection": "^1.3.2", - "is-type-of": "^1.2.1", - "reflect-metadata": "^0.1.13" + "injection": "^1.4.1", + "is-type-of": "^1.2.1" }, "author": "Harry Chen ", "repository": { diff --git a/packages/midway-core/src/constants.ts b/packages/midway-core/src/constant.ts similarity index 59% rename from packages/midway-core/src/constants.ts rename to packages/midway-core/src/constant.ts index 1cd735b7fffa..c87c5c0f7505 100644 --- a/packages/midway-core/src/constants.ts +++ b/packages/midway-core/src/constant.ts @@ -3,3 +3,5 @@ export const MidwayHandlerKey = { PLUGIN: 'plugin', LOGGER: 'logger', }; + +export const FUNCTION_INJECT_KEY = 'midway:function_inject_key'; diff --git a/packages/midway-core/src/container.ts b/packages/midway-core/src/container.ts index debed0c2da70..1a53b0e607b2 100644 --- a/packages/midway-core/src/container.ts +++ b/packages/midway-core/src/container.ts @@ -1,7 +1,10 @@ -import 'reflect-metadata'; +import { CLASS_KEY_CONSTRUCTOR, CONFIG_KEY, LOGGER_KEY, PLUGIN_KEY } from '@midwayjs/decorator'; import { Autowire, Container, + getClassMetadata, + getObjectDefinition, + getProviderId, IApplicationContext, IContainer, IManagedInstance, @@ -10,26 +13,13 @@ import { IObjectDefinition, IObjectDefinitionParser, IParserContext, - OBJ_DEF_CLS, ObjectDefinitionOptions, ObjectIdentifier, Scope, ScopeEnum, - TagClsMetadata, - TAGGED_CLS, XmlObjectDefinition } from 'injection'; -import { - CLASS_KEY_CONSTRUCTOR, - CONFIG_KEY_CLZ, - CONFIG_KEY_PROP, - FUNCTION_INJECT_KEY, - LOGGER_KEY_CLZ, - LOGGER_KEY_PROP, - PLUGIN_KEY_CLZ, - PLUGIN_KEY_PROP -} from './decorators'; -import { MidwayHandlerKey } from './constants'; +import { FUNCTION_INJECT_KEY, MidwayHandlerKey } from './constant'; const globby = require('globby'); const path = require('path'); @@ -41,6 +31,11 @@ const MIDDLEWARES = 'middlewares'; const TYPE_LOGGER = 'logger'; const TYPE_PLUGIN = 'plugin'; +interface FrameworkDecoratorMetadata { + key: string; + propertyName: string; +} + class BaseParser { container: MidwayContainer; @@ -186,6 +181,7 @@ export class MidwayContainer extends Container implements IContainer { // ctx is in requestContainer this.registerObject('ctx', this.ctx); } + /** * update current context in applicationContext * for mock and other case @@ -240,9 +236,9 @@ export class MidwayContainer extends Container implements IContainer { protected bindClass(module) { if (is.class(module)) { - const metaData = Reflect.getMetadata(TAGGED_CLS, module) as TagClsMetadata; - if (metaData) { - this.bind(metaData.id, module); + const providerId = getProviderId(module); + if (providerId) { + this.bind(providerId, module); } else { // inject by name in js this.bind(camelcase(module.name), module); @@ -277,7 +273,7 @@ export class MidwayContainer extends Container implements IContainer { this.beforeEachCreated((target, constructorArgs, context) => { let constructorMetaData; try { - constructorMetaData = Reflect.getOwnMetadata(CLASS_KEY_CONSTRUCTOR, target); + constructorMetaData = getClassMetadata(CLASS_KEY_CONSTRUCTOR, target); } catch (e) { debug(`beforeEachCreated error ${e.stack}`); } @@ -308,14 +304,14 @@ export class MidwayContainer extends Container implements IContainer { this.afterEachCreated((instance, context, definition) => { // 处理配置装饰器 - const configSetterProps = this.getClzSetterProps(CONFIG_KEY_CLZ, instance); - this.defineGetterPropertyValue(configSetterProps, CONFIG_KEY_PROP, instance, this.handlerMap.get(MidwayHandlerKey.CONFIG)); + const configSetterProps: FrameworkDecoratorMetadata[] = getClassMetadata(CONFIG_KEY, instance); + this.defineGetterPropertyValue(configSetterProps, instance, this.handlerMap.get(MidwayHandlerKey.CONFIG)); // 处理插件装饰器 - const pluginSetterProps = this.getClzSetterProps(PLUGIN_KEY_CLZ, instance); - this.defineGetterPropertyValue(pluginSetterProps, PLUGIN_KEY_PROP, instance, this.handlerMap.get(MidwayHandlerKey.PLUGIN)); + const pluginSetterProps: FrameworkDecoratorMetadata[] = getClassMetadata(PLUGIN_KEY, instance); + this.defineGetterPropertyValue(pluginSetterProps, instance, this.handlerMap.get(MidwayHandlerKey.PLUGIN)); // 处理日志装饰器 - const loggerSetterProps = this.getClzSetterProps(LOGGER_KEY_CLZ, instance); - this.defineGetterPropertyValue(loggerSetterProps, LOGGER_KEY_PROP, instance, this.handlerMap.get(MidwayHandlerKey.LOGGER)); + const loggerSetterProps: FrameworkDecoratorMetadata[] = getClassMetadata(LOGGER_KEY, instance); + this.defineGetterPropertyValue(loggerSetterProps, instance, this.handlerMap.get(MidwayHandlerKey.LOGGER)); // 表示非ts annotation模式 if (!pluginSetterProps && !loggerSetterProps && definition.isAutowire()) { @@ -338,32 +334,19 @@ export class MidwayContainer extends Container implements IContainer { }); } - /** - * get method name for decorator - * - * @param setterClzKey - * @param target - * @returns {Array} - */ - private getClzSetterProps(setterClzKey, target): string[] { - return Reflect.getMetadata(setterClzKey, target); - } - /** * binding getter method for decorator * * @param setterProps - * @param metadataKey * @param instance * @param getterHandler */ - private defineGetterPropertyValue(setterProps, metadataKey, instance, getterHandler) { + private defineGetterPropertyValue(setterProps: FrameworkDecoratorMetadata[], instance, getterHandler) { if (setterProps && getterHandler) { for (const prop of setterProps) { - const propertyKey = Reflect.getMetadata(metadataKey, instance, prop); - if (propertyKey) { - Object.defineProperty(instance, prop, { - get: () => getterHandler(propertyKey), + if (prop.propertyName) { + Object.defineProperty(instance, prop.propertyName, { + get: () => getterHandler(prop.key), configurable: false, enumerable: true }); @@ -380,7 +363,7 @@ export class MidwayContainer extends Container implements IContainer { super.registerCustomBinding(objectDefinition, target); // Override the default scope to request - const objDefOptions: ObjectDefinitionOptions = Reflect.getMetadata(OBJ_DEF_CLS, target); + const objDefOptions: ObjectDefinitionOptions = getObjectDefinition(target); if (objDefOptions && !objDefOptions.scope) { debug(`register @scope to default value(request), id=${objectDefinition.id}`); objectDefinition.scope = ScopeEnum.Request; diff --git a/packages/midway-core/src/decorators/application.ts b/packages/midway-core/src/decorators/application.ts deleted file mode 100644 index 9126064704bb..000000000000 --- a/packages/midway-core/src/decorators/application.ts +++ /dev/null @@ -1,52 +0,0 @@ -import 'reflect-metadata'; -import { attachConstructorDataOnClass, attachMetaDataOnClass } from '../utils'; -import { - CONFIG_KEY_CLZ, - CONFIG_KEY_PROP, - LOGGER_KEY_CLZ, - LOGGER_KEY_PROP, - PLUGIN_KEY_CLZ, - PLUGIN_KEY_PROP, -} from './metaKeys'; - -export function config(identifier?: string) { - return function (target: any, targetKey: string, index?: number): void { - if (typeof index === 'number') { - attachConstructorDataOnClass(identifier, target, 'config', index); - } else { - if (!identifier) { - identifier = targetKey; - } - attachMetaDataOnClass(target, CONFIG_KEY_CLZ, targetKey); - Reflect.defineMetadata(CONFIG_KEY_PROP, identifier, target, targetKey); - } - }; -} - -export function plugin(identifier?: string) { - return function (target: any, targetKey: string, index?: number): void { - if (typeof index === 'number') { - attachConstructorDataOnClass(identifier, target, 'plugin', index); - } else { - if (!identifier) { - identifier = targetKey; - } - attachMetaDataOnClass(target, PLUGIN_KEY_CLZ, targetKey); - Reflect.defineMetadata(PLUGIN_KEY_PROP, identifier, target, targetKey); - } - }; -} - -export function logger(identifier?: string) { - return function (target: any, targetKey: string, index?: number): void { - if (typeof index === 'number') { - attachConstructorDataOnClass(identifier, target, 'logger', index); - } else { - if (!identifier) { - identifier = targetKey; - } - attachMetaDataOnClass(target, LOGGER_KEY_CLZ, targetKey); - Reflect.defineMetadata(LOGGER_KEY_PROP, identifier, target, targetKey); - } - }; -} diff --git a/packages/midway-core/src/decorators/index.ts b/packages/midway-core/src/decorators/index.ts deleted file mode 100644 index 852aae5f02fd..000000000000 --- a/packages/midway-core/src/decorators/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './application'; -export * from './providerWrapper'; -export * from './metaKeys'; diff --git a/packages/midway-core/src/decorators/metaKeys.ts b/packages/midway-core/src/decorators/metaKeys.ts deleted file mode 100644 index f10de18bf766..000000000000 --- a/packages/midway-core/src/decorators/metaKeys.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const CONFIG_KEY_CLZ = 'midway:config_key_class'; -export const CONFIG_KEY_PROP = 'midway:config_key_props'; -export const PLUGIN_KEY_CLZ = 'midway:plugin_key_class'; -export const PLUGIN_KEY_PROP = 'midway:plugin_key_props'; -export const LOGGER_KEY_CLZ = 'midway:logger_key_class'; -export const LOGGER_KEY_PROP = 'midway:logger_key_props'; -export const CLASS_KEY_CONSTRUCTOR = 'midway:class_key_constructor'; -export const FUNCTION_INJECT_KEY = 'midway:function_inject_key'; diff --git a/packages/midway-core/src/index.ts b/packages/midway-core/src/index.ts index 3fd4fe97c06b..8c2310f9dfb4 100644 --- a/packages/midway-core/src/index.ts +++ b/packages/midway-core/src/index.ts @@ -1,6 +1,5 @@ -export { MidwayLoader } from './loader'; +export { ContainerLoader } from './loader'; export { MidwayContainer } from './container'; -export * from './decorators'; -export * from './interface'; -export * from './constants'; export { MidwayRequestContainer } from './requestContainer'; +export * from './providerWrapper'; +export * from './constant'; diff --git a/packages/midway-core/src/loader.ts b/packages/midway-core/src/loader.ts index a80a735848a3..1d19ce48f3e6 100644 --- a/packages/midway-core/src/loader.ts +++ b/packages/midway-core/src/loader.ts @@ -1,232 +1,86 @@ -import { isPluginName, isTypeScriptEnvironment } from './utils'; import * as path from 'path'; -import * as fs from 'fs'; import { MidwayContainer } from './container'; -import { MidwayHandlerKey } from './constants'; -import { MidwayLoaderOptions } from './interface'; import { MidwayRequestContainer } from './requestContainer'; -import * as extend from 'extend2'; -const EggLoader = require('egg-core').EggLoader; -const TS_SRC_DIR = 'src'; -const TS_TARGET_DIR = 'dist'; -const debug = require('debug')(`midway:loader:${process.pid}`); +export class ContainerLoader { -export class MidwayLoader extends EggLoader { - - protected pluginLoaded = false; - applicationContext; - pluginContext; baseDir; - appDir; - options; + pluginContext; + applicationContext; + requestContext; + isTsMode; - constructor(options: MidwayLoaderOptions) { - super(options); - this.pluginContext = new MidwayContainer(); + constructor({baseDir, isTsMode = true}) { + this.baseDir = baseDir; + this.isTsMode = isTsMode; } - /** - * 判断是否是 ts 模式,在构造器内就会被执行 - */ - get isTsMode() { - return this.app.options.typescript; + initialize() { + this.pluginContext = new MidwayContainer(); + this.applicationContext = new MidwayContainer(this.baseDir); + this.requestContext = new MidwayRequestContainer(this.applicationContext); + // put requestContext to applicationContext + this.applicationContext.registerObject('requestContext', this.requestContext); + this.applicationContext.registerObject('baseDir', this.baseDir); + this.applicationContext.registerObject('isTsMode', this.isTsMode); } - // loadPlugin -> loadConfig -> afterLoadConfig - loadConfig() { - this.loadPlugin(); - super.loadConfig(); + getApplicationContext() { + return this.applicationContext; } - async refreshContext(): Promise { - // 虽然有点hack,但是将就着用吧 - if (Array.isArray(this.config.configLocations)) { - this.applicationContext.configLocations = this.config.configLocations; - this.applicationContext.props.putObject(this.config); - } - - await this.pluginContext.ready(); - await this.applicationContext.ready(); + getPluginContext() { + return this.pluginContext; } - // Get the real plugin path - protected getPluginPath(plugin) { - if (plugin.path) { - return plugin.path; - } - - const name = plugin.package || plugin.name; - const lookupDirs = []; - - // 尝试在以下目录找到匹配的插件 - // -> {APP_PATH}/node_modules - // -> {EGG_PATH}/node_modules - // -> $CWD/node_modules - lookupDirs.push(path.join(this.appDir, 'node_modules')); - - // 到 egg 中查找,优先从外往里查找 - for (let i = this.eggPaths.length - 1; i >= 0; i--) { - const eggPath = this.eggPaths[i]; - lookupDirs.push(path.join(eggPath, 'node_modules')); - } - - // should find the $cwd/node_modules when test the plugins under npm3 - lookupDirs.push(path.join(process.cwd(), 'node_modules')); - - if (process.env.PLUGIN_PATH) { - lookupDirs.push(path.join(process.env.PLUGIN_PATH, 'node_modules')); - } - - for (let dir of lookupDirs) { - dir = path.join(dir, name); - if (fs.existsSync(dir)) { - return fs.realpathSync(dir); - } - } - - throw new Error(`Can not find plugin ${name} in "${lookupDirs.join(', ')}"`); + getRequestContext() { + return this.requestContext; } - private registerTypescriptDirectory() { - const app = this.app; - // 处理 ts 的初始路径 - this.appDir = this.baseDir = app.options.baseDir; - if (this.isTsMode) { - let dirSuffix = app.options.targetDir || TS_TARGET_DIR; - if (isTypeScriptEnvironment()) { - dirSuffix = app.options.srcDir || TS_SRC_DIR; - // 打开 egg 加载 ts 的开关 - process.env.EGG_TYPESCRIPT = 'true'; - debug(`typescript mode = true`); - } - - const dir = path.join(app.options.baseDir, dirSuffix); - this.baseDir = app.options.baseDir = this.options.baseDir = dir; - this.options.logger.info(`in typescript current dir change to ${dir}`); - debug(`in typescript current dir change to ${dir}`); - } + registerAllHook(hookKey, hookHandler) { + this.registerApplicationHook(hookKey, hookHandler); + this.registerRequeastHook(hookKey, hookHandler); } - getEggPaths() { - if (!this.appDir) { - // register appDir here - this.registerTypescriptDirectory(); - } - return super.getEggPaths(); + registerApplicationHook(hookKey, hookHandler) { + this.applicationContext.registerDataHandler(hookKey, hookHandler); } - getServerEnv() { - let serverEnv; - - const envPath = path.join(this.appDir, 'config/env'); - if (fs.existsSync(envPath)) { - serverEnv = fs.readFileSync(envPath, 'utf8').trim(); - } - - if (!serverEnv) { - serverEnv = process.env.EGG_SERVER_ENV || process.env.MIDWAY_SERVER_ENV; - } - - if (!serverEnv) { - serverEnv = super.getServerEnv(); - } - - return serverEnv; + registerRequeastHook(hookKey, hookHandler) { + this.requestContext.registerDataHandler(hookKey, hookHandler); } - private buildLoadDir(dir) { - if (!path.isAbsolute(dir)) { - return path.join(this.appDir, dir); - } - return dir; - } - - protected loadApplicationContext() { - // this.app.options.container 测试用例编写方便点 - const containerConfig = this.config.container || this.app.options.container || {}; - // 在 super contructor 中会调用到getAppInfo,之后会被赋值 - // 如果是typescript会加上 dist 或者 src 目录 - this.applicationContext = new MidwayContainer(this.baseDir); - const requestContext = new MidwayRequestContainer(this.applicationContext); - // put requestContext to applicationContext - this.applicationContext.registerObject('requestContext', requestContext); - this.applicationContext.registerObject('baseDir', this.baseDir); - this.applicationContext.registerObject('appDir', this.appDir); - this.applicationContext.registerObject('isTsMode', this.isTsMode); + loadDirectory(loadOpts: { + loadDir?: string[]; + disableAutoLoad?: boolean; + pattern?: string; + ignore?: string; + configLocations?: string[]; + } = {}) { // 如果没有关闭autoLoad 则进行load - if (!containerConfig.disableAutoLoad) { + if (!loadOpts.disableAutoLoad) { const defaultLoadDir = this.isTsMode ? [this.baseDir] : ['app', 'lib']; this.applicationContext.load({ - loadDir: (containerConfig.loadDir || defaultLoadDir).map(dir => { + loadDir: (loadOpts.loadDir || defaultLoadDir).map(dir => { return this.buildLoadDir(dir); }), - pattern: containerConfig.pattern, - ignore: containerConfig.ignore + pattern: loadOpts.pattern, + ignore: loadOpts.ignore }); } - - // register handler for container - this.applicationContext.registerDataHandler(MidwayHandlerKey.CONFIG, (key) => { - return this.config[key]; - }); - - this.applicationContext.registerDataHandler(MidwayHandlerKey.PLUGIN, (key) => { - return this.pluginContext.get(key); - }); - - this.applicationContext.registerDataHandler(MidwayHandlerKey.LOGGER, (key) => { - if (this.app.getLogger) { - return this.app.getLogger(key); - } - return this.options.logger; - }); } - /** - * intercept plugin when it set value to app - * @param fileName - * @returns {boolean} - */ - protected interceptLoadCustomApplication(fileName) { - const self = this; - const pluginContainerProps = Object.getOwnPropertyNames(this); - this.app = new Proxy(this.app, { - defineProperty(target, prop, attributes) { - if (!self.pluginLoaded && isPluginName(prop) && !(prop in pluginContainerProps)) { - // save to context when called app.xxx = xxx - // now we can get plugin from context - debug(`pluginContext register [${prop as string}]`); - self.pluginContext.registerObject(prop, attributes.value); - } - return Object.defineProperty(target, prop, attributes); - } - }); - - this.getLoadUnits() - .forEach(unit => { - // 兼容旧插件加载方式 - const ret = this.loadFile(this.resolveModule(path.join(unit.path, fileName))); - if (ret) { - // midway 的插件会返回对象 - debug(`pluginContext register [${unit.name}]`); - this.pluginContext.registerObject(unit.name, ret); - } - }); - - // 插件加载完毕 - this.pluginLoaded = true; + async refresh() { + await this.pluginContext.ready(); + await this.applicationContext.ready(); + await this.requestContext.ready(); } - getAppInfo() { - if (!this.appInfo) { - const appInfo = super.getAppInfo(); - // ROOT == HOME in prod env - this.appInfo = extend(true, appInfo, { - root: appInfo.env === 'local' || appInfo.env === 'unittest' ? this.appDir : appInfo.root, - appDir: this.appDir, - }); + private buildLoadDir(dir) { + if (!path.isAbsolute(dir)) { + return path.join(this.baseDir, dir); } - return this.appInfo; + return dir; } + } diff --git a/packages/midway-core/src/decorators/providerWrapper.ts b/packages/midway-core/src/providerWrapper.ts similarity index 89% rename from packages/midway-core/src/decorators/providerWrapper.ts rename to packages/midway-core/src/providerWrapper.ts index 4bb65ba526ce..4037eeb4165c 100644 --- a/packages/midway-core/src/decorators/providerWrapper.ts +++ b/packages/midway-core/src/providerWrapper.ts @@ -1,5 +1,5 @@ import { IApplicationContext, ObjectIdentifier, Scope } from 'injection'; -import { FUNCTION_INJECT_KEY } from './metaKeys'; +import { FUNCTION_INJECT_KEY } from './constant'; export function providerWrapper(wrapperInfo: Array<{ id: ObjectIdentifier; diff --git a/packages/midway-core/src/requestContainer.ts b/packages/midway-core/src/requestContainer.ts index c488eb13803f..355ead61e644 100644 --- a/packages/midway-core/src/requestContainer.ts +++ b/packages/midway-core/src/requestContainer.ts @@ -1,4 +1,3 @@ -import { MidwayHandlerKey } from './constants'; import { MidwayContainer } from './container'; import { ManagedValue, VALUE_TYPE } from 'injection'; @@ -22,23 +21,6 @@ export class MidwayRequestContainer extends MidwayContainer { this.registerObject('logger', ctx.logger); } - registerEachCreatedHook() { - // register handler for container - this.registerDataHandler(MidwayHandlerKey.CONFIG, (key) => { - return this.ctx.app.config[key]; - }); - - this.registerDataHandler(MidwayHandlerKey.PLUGIN, (key) => { - return this.ctx.app.pluginContext.get(key); - }); - - this.registerDataHandler(MidwayHandlerKey.LOGGER, (key) => { - return this.ctx.app.getLogger(key); - }); - - super.registerEachCreatedHook(); - } - get(identifier: any, args?: any) { if (typeof identifier !== 'string') { identifier = this.getIdentifier(identifier); diff --git a/packages/midway-core/src/utils.ts b/packages/midway-core/src/utils.ts deleted file mode 100644 index e80f084221cb..000000000000 --- a/packages/midway-core/src/utils.ts +++ /dev/null @@ -1,54 +0,0 @@ -import 'reflect-metadata'; -import {CLASS_KEY_CONSTRUCTOR} from './decorators/metaKeys'; - -export function attachMetaDataOnClass(clz, key, value) { - // save method name on class - let classMetaValue = Reflect.getMetadata(key, clz); - if (classMetaValue) { - classMetaValue = classMetaValue.concat(value); - } else { - classMetaValue = [value]; - } - Reflect.defineMetadata(key, classMetaValue, clz); -} - -export function attachConstructorDataOnClass(identifier, clz, type, index) { - - if (!identifier) { - const args = getParamNames(clz); - if (clz.length === args.length && index < clz.length) { - identifier = args[index]; - } - } - - // save constructor index on class - let constructorMetaValue = Reflect.getOwnMetadata(CLASS_KEY_CONSTRUCTOR, clz); - if (!constructorMetaValue) { - constructorMetaValue = {}; - } - constructorMetaValue[index] = { - key: identifier, - type - }; - Reflect.defineMetadata(CLASS_KEY_CONSTRUCTOR, constructorMetaValue, clz); -} - -const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; -const ARGUMENT_NAMES = /([^\s,]+)/g; - -export function getParamNames(func) { - const fnStr = func.toString().replace(STRIP_COMMENTS, ''); - let result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES); - if (result === null) { - result = []; - } - return result; -} - -export function isTypeScriptEnvironment() { - return !!require.extensions['.ts']; -} - -export function isPluginName(name) { - return typeof name === 'string' && !/^_/.test(name); -} diff --git a/packages/midway-core/test/fixtures/base-app-constructor/src/lib/service.ts b/packages/midway-core/test/fixtures/base-app-constructor/src/lib/service.ts index e784a0861389..ed527832407d 100644 --- a/packages/midway-core/test/fixtures/base-app-constructor/src/lib/service.ts +++ b/packages/midway-core/test/fixtures/base-app-constructor/src/lib/service.ts @@ -1,4 +1,4 @@ -import {config, plugin} from '../../../../../src/decorators'; +import {config, plugin, logger} from '@midwayjs/decorator'; import {provide, async, init, inject} from 'injection'; @provide() @@ -21,17 +21,20 @@ export class BaseService { config; plugin2; + logger; constructor( @inject() a, @config('hello') config, @inject() b, - @plugin('plugin2') plugin2 + @plugin('plugin2') plugin2, + @logger() logger ) { this.config = Object.assign(config, { c: a.config.c + b.config.c + config.c }); this.plugin2 = plugin2; + this.logger = logger; } @init() diff --git a/packages/midway-core/test/fixtures/base-app-decorator/src/app/controller/api.ts b/packages/midway-core/test/fixtures/base-app-decorator/src/app/controller/api.ts index 16044ac3b7b2..9d115de6624d 100644 --- a/packages/midway-core/test/fixtures/base-app-decorator/src/app/controller/api.ts +++ b/packages/midway-core/test/fixtures/base-app-decorator/src/app/controller/api.ts @@ -1,4 +1,3 @@ -'use strict'; import {BaseService} from '../../lib/service'; exports.index = async (ctx, next) => { diff --git a/packages/midway-core/test/fixtures/base-app-decorator/src/lib/service.ts b/packages/midway-core/test/fixtures/base-app-decorator/src/lib/service.ts index 4cf8cca4016c..2a23f5c5ad28 100644 --- a/packages/midway-core/test/fixtures/base-app-decorator/src/lib/service.ts +++ b/packages/midway-core/test/fixtures/base-app-decorator/src/lib/service.ts @@ -1,4 +1,4 @@ -import {config, plugin} from '../../../../../src/decorators'; +import {config, plugin, logger} from '@midwayjs/decorator'; import {provide, async} from 'injection'; @async() @provide() @@ -10,4 +10,6 @@ export class BaseService { @plugin('plugin2') plugin2; + @logger() + logger; } diff --git a/packages/midway-core/test/fixtures/base-app-function/src/lib/factory.ts b/packages/midway-core/test/fixtures/base-app-function/src/lib/factory.ts index 24eb4d8461b2..bbefcfc08f6d 100644 --- a/packages/midway-core/test/fixtures/base-app-function/src/lib/factory.ts +++ b/packages/midway-core/test/fixtures/base-app-function/src/lib/factory.ts @@ -1,4 +1,4 @@ -import {providerWrapper} from '../../../../../src/decorators'; +import {providerWrapper} from '../../../../../src/'; import {IApplicationContext} from 'injection'; export function adapterFactory(context: IApplicationContext) { diff --git a/packages/midway-core/test/fixtures/base-app-function/src/lib/otherFactory.ts b/packages/midway-core/test/fixtures/base-app-function/src/lib/otherFactory.ts index ffdc8802770b..1ae28e8b9c16 100644 --- a/packages/midway-core/test/fixtures/base-app-function/src/lib/otherFactory.ts +++ b/packages/midway-core/test/fixtures/base-app-function/src/lib/otherFactory.ts @@ -1,4 +1,4 @@ -import {providerWrapper} from '../../../../../src/decorators'; +import {providerWrapper} from '../../../../../src/'; import {IApplicationContext} from 'injection'; class MyTestObj { diff --git a/packages/midway-core/test/fixtures/base-app-function/src/lib/service.ts b/packages/midway-core/test/fixtures/base-app-function/src/lib/service.ts index c2fa6f4ed0c1..fddd47fde8f9 100644 --- a/packages/midway-core/test/fixtures/base-app-function/src/lib/service.ts +++ b/packages/midway-core/test/fixtures/base-app-function/src/lib/service.ts @@ -1,4 +1,4 @@ -import {config, plugin} from '../../../../../src/decorators'; +import {config, plugin} from '@midwayjs/decorator'; import {provide, async, init, inject} from 'injection'; @provide() diff --git a/packages/midway-core/test/fixtures/midway/index.ts b/packages/midway-core/test/fixtures/midway/index.ts deleted file mode 100644 index dcd1d994e532..000000000000 --- a/packages/midway-core/test/fixtures/midway/index.ts +++ /dev/null @@ -1,91 +0,0 @@ -import {Agent, Application} from 'egg'; -import {MidwayLoader} from '../../../src'; - -export class AppWorkerLoader extends MidwayLoader { - - loadCustomApp() { - this.interceptLoadCustomApplication('app'); - } - - load() { - // app > plugin > core - this.loadApplicationExtend(); - this.loadRequestExtend(); - this.loadResponseExtend(); - this.loadContextExtend(); - this.loadHelperExtend(); - - this.loadApplicationContext(); - // app > plugin - this.loadCustomApp(); - // app > plugin - this.loadService(); - // app > plugin > core - this.loadMiddleware(); - - this.app.beforeStart(async () => { - await this.refreshContext(); - // app - this.loadController(); - // app - this.loadRouter(); // 依赖 controller - }); - } - -} - -export class AgentWorkerLoader extends MidwayLoader { - - loadCustomAgent() { - this.interceptLoadCustomApplication('agent'); - } - - load() { - this.loadAgentExtend(); - this.loadApplicationContext(); - this.loadCustomAgent(); - this.app.beforeStart(async () => { - await this.refreshContext(); - }); - } - -} - -class MidwayApplication extends (Application as { - new(...x) -}) { - - get [Symbol.for('egg#loader')]() { - return AppWorkerLoader; - } - - get [Symbol.for('egg#eggPath')]() { - return __dirname; - } - - get applicationContext() { - return this.loader.applicationContext; - } -} - -class MidwayAgent extends (Agent as { - new(...x) -}) { - - get [Symbol.for('egg#loader')]() { - return AgentWorkerLoader; - } - - get [Symbol.for('egg#eggPath')]() { - return __dirname; - } - - get applicationContext() { - return this.loader.applicationContext; - } -} - -export { - MidwayApplication as Application, - MidwayAgent as Agent, -}; diff --git a/packages/midway-core/test/fixtures/midway/package.json b/packages/midway-core/test/fixtures/midway/package.json deleted file mode 100644 index 61afcd9ff3b9..000000000000 --- a/packages/midway-core/test/fixtures/midway/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "midway", - "version": "0.0.1", - "dependencies": { - - } -} diff --git a/packages/midway-core/test/loader.test.ts b/packages/midway-core/test/loader.test.ts index dc775e20f9ad..0bc6d1ac6f80 100644 --- a/packages/midway-core/test/loader.test.ts +++ b/packages/midway-core/test/loader.test.ts @@ -1,209 +1,121 @@ -const assert = require('assert'); -const request = require('supertest'); -const utils = require('./utils'); +import { CONFIG_KEY, LOGGER_KEY, PLUGIN_KEY } from '@midwayjs/decorator'; +import * as assert from 'assert'; import * as path from 'path'; +import { ContainerLoader } from '../src'; describe('/test/loader.test.ts', () => { - describe('load ts file', () => { - let app; - before(() => { - app = utils.app('base-app', { - typescript: true - }); - return app.ready(); + it('should create new loader', async () => { + const loader = new ContainerLoader({ + baseDir: path.join(__dirname, './fixtures/base-app/src') }); + loader.initialize(); + loader.loadDirectory(); + await loader.refresh(); - after(() => app.close()); + const appCtx = loader.getApplicationContext(); + const pluginCtx = loader.getPluginContext(); + const requestCtx = loader.getRequestContext(); - it('should get config merge', () => { - assert(app.config.rundir, path.join(__dirname, './fixtures/enhance/base-app/run')); - }); - - it('should load ts directory', (done) => { - request(app.callback()) - .get('/api') - .expect(200) - .expect('hello', done); - }); + assert(pluginCtx); + assert(requestCtx === await appCtx.getAsync('requestContext')); }); - describe('load ts file and use config, plugin decorator', () => { - let app; - before(() => { - app = utils.app('base-app-decorator', { - typescript: true - }); - return app.ready(); + it('should load ts file and use config, plugin decorator', async () => { + const loader = new ContainerLoader({ + baseDir: path.join(__dirname, './fixtures/base-app-decorator/src') }); + loader.initialize(); + loader.loadDirectory(); + await loader.refresh(); - after(() => app.close()); - - it('should load ts directory', (done) => { - request(app.callback()) - .get('/api') - .expect(200) - .expect(/3t/, done); + // register handler for container + loader.registerAllHook(CONFIG_KEY, (key) => { + return 'hello'; }); - it('should load ts directory 2', done => { - request(app.callback()) - .get('/api/baseService') - .expect(200) - .expect(/3t/, done); + loader.registerAllHook(PLUGIN_KEY, (key) => { + return {b: 2}; }); - }); - describe('load ts file and use third party module', () => { - let app; - before(() => { - app = utils.app('base-app-utils', { - typescript: true - }); - return app.ready(); + loader.registerAllHook(LOGGER_KEY, (key) => { + return console; }); - after(() => app.close()); - - it('should inject ctx be ok', async () => { - assert.ok(await app.applicationContext.getAsync('baseService')); - }); + const appCtx = loader.getApplicationContext(); + const baseService = await appCtx.getAsync('baseService'); + assert(baseService.config === 'hello'); + assert(baseService.logger === console); + assert(baseService.plugin2.b === 2); - it('should load ts directory and inject module', (done) => { - request(app.callback()) - .get('/api/test') - .expect(200) - .expect('false3', done); - }); + const context = {logger: console}; + const requestCtx = loader.getRequestContext(); + requestCtx.updateContext(context); + const baseServiceCtx = await requestCtx.getAsync('baseService'); + const baseServiceCtx1 = await requestCtx.getAsync('baseService'); + assert(baseServiceCtx === baseServiceCtx1); + assert(baseServiceCtx.config === 'hello'); + assert(baseServiceCtx.logger === console); + assert(baseServiceCtx.plugin2.b === 2); }); - describe('load ts file and use async init', () => { - let app; - before(() => { - app = utils.app('base-app-async', { - typescript: true - }); - return app.ready(); + it('load ts file support constructor inject', async () => { + const loader = new ContainerLoader({ + baseDir: path.join(__dirname, './fixtures/base-app-constructor/src') }); + loader.initialize(); + loader.loadDirectory(); + await loader.refresh(); - after(() => app.close()); - - it('should load ts directory and inject module', (done) => { - request(app.callback()) - .get('/api') - .expect(200) - .expect('10t', done); + // register handler for container + loader.registerAllHook(CONFIG_KEY, (key) => { + return {c: 60}; }); - }); - describe('load ts file support constructor inject', () => { - let app; - before(() => { - app = utils.app('base-app-constructor', { - typescript: true - }); - return app.ready(); + loader.registerAllHook(PLUGIN_KEY, (key) => { + return {text: 2}; }); - after(() => app.close()); - - it('should load ts directory and inject in constructor', (done) => { - request(app.callback()) - .get('/api/test') - .expect(200) - .expect('63t', done); + loader.registerAllHook(LOGGER_KEY, (key) => { + return console; }); - }); - - describe('auto load function file and inject by function name', () => { - let app; - before(() => { - app = utils.app('base-app-function', { - typescript: true - }); - return app.ready(); - }); - - after(() => app.close()); - it('should load ts directory and inject in constructor', (done) => { - request(app.callback()) - .get('/api') - .expect(200) - .expect('63t', done); - }); - - it('should load ts directory and inject in constructor with error', done => { - request(app.callback()) - .get('/api/error') - .expect(200) - .expect('error', done); - }); + const context = {logger: console}; + const requestCtx = loader.getRequestContext(); + requestCtx.updateContext(context); + const module = require(path.join(__dirname, './fixtures/base-app-constructor/src/lib/service')); + const baseServiceCtx = await requestCtx.getAsync(module['BaseService']); + assert(baseServiceCtx.config.c === 120); + assert(baseServiceCtx.plugin2.text === 2); + assert(baseServiceCtx.logger === console); }); - describe('auto js app loader', () => { - let app; - before(() => { - app = utils.app('js-app-loader', {}); - return app.ready(); - }); - - after(() => app.close()); - - it('should js app loader should be ok', async () => { - const a = await app.applicationContext.getAsync('app'); - const c = a.loader.getConfig(); - - const cc = {a: 1, b: 2}; - assert.deepEqual(c, cc); + it('should auto load function file and inject by function name', async () => { + const loader = new ContainerLoader({ + baseDir: path.join(__dirname, './fixtures/base-app-function/src') }); - }); + loader.initialize(); + loader.loadDirectory(); + await loader.refresh(); - describe('disable auto app loader', () => { - let app; - before(() => { - app = utils.app('js-app-loader', {container: {disableAutoLoad: true}}); - return app.ready(); + // register handler for container + loader.registerAllHook(CONFIG_KEY, (key) => { + return {c: 60}; }); - after(() => app.close()); - - it('disable js app loader should be ok', async () => { - let called = false; - try { - const a = await app.applicationContext.getAsync('app'); - const c = a.loader.getConfig(); - - const cc = {a: 1, b: 2}; - assert.deepEqual(c, cc); - console.log('------', c, a); - } catch (e) { - console.error(e); - called = true; - } - - assert.ok(called); + loader.registerAllHook(PLUGIN_KEY, (key) => { + return {text: 2}; }); - }); - describe('disable auto app loader', () => { - let app; - before(() => { - app = utils.app('base-app', {container: {disableAutoLoad: true}}); - return app.ready(); + loader.registerAllHook(LOGGER_KEY, (key) => { + return console; }); - after(() => app.close()); - - it('disable js app loader should be ok', async () => { - try { - const reqCtx = await app.applicationContext.getAsync('requestContext'); - reqCtx.updateContext({ logger: console }); - reqCtx.get('ctx'); - reqCtx.getAsync('logger'); - } catch (e) { - console.error(e); - } - }); + const context = {logger: console}; + const requestCtx = loader.getRequestContext(); + requestCtx.updateContext(context); + const baseServiceCtx = await requestCtx.getAsync('baseService'); + assert(baseServiceCtx.factory('google')); }); + }); diff --git a/packages/midway-core/test/utils.ts b/packages/midway-core/test/utils.ts deleted file mode 100644 index b92b02a60b3a..000000000000 --- a/packages/midway-core/test/utils.ts +++ /dev/null @@ -1,38 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const mm = require('egg-mock'); -const logDir = path.join(__dirname, '../logs'); - -process.setMaxListeners(0); - -if (!fs.existsSync(logDir)) { - fs.mkdirSync(logDir); -} - -export function app(name, options) { - options = formatOptions(name, options); - // mm.consoleLevel(options.consoleLevel || 'NONE'); - const app = mm.app(options); - app.close = () => { - fs.rmdirSync(path.join(app.baseDir, 'run')); - return app.close; - }; - return app; -} - -export function cluster(name, options) { - options = formatOptions(name, options); - return mm.cluster(options); -} - -export function formatOptions(name, options = {}) { - return Object.assign({}, { - baseDir: name, - framework: path.join(__dirname, './fixtures/midway'), - // 默认关闭覆盖率,太慢 - coverage: false, - cache: false, - }, options); -} diff --git a/packages/midway-decorator/package.json b/packages/midway-decorator/package.json index b794f03a16bc..31cb33ff1193 100644 --- a/packages/midway-decorator/package.json +++ b/packages/midway-decorator/package.json @@ -13,7 +13,7 @@ "autod": "midway-bin autod" }, "dependencies": { - "injection": "^1.3.2" + "injection": "^1.4.1" }, "devDependencies": { "midway-bin": "^1.4.3" diff --git a/packages/midway-decorator/src/common/priority.ts b/packages/midway-decorator/src/common/priority.ts index fb8159dfc4f7..f86e232c4b95 100644 --- a/packages/midway-decorator/src/common/priority.ts +++ b/packages/midway-decorator/src/common/priority.ts @@ -1,8 +1,8 @@ -import { saveClassMetaData } from 'injection'; +import { saveClassMetadata } from 'injection'; import { PRIORITY_KEY } from '../constant'; export function priority(priority: number): ClassDecorator { return (target: any) => { - saveClassMetaData(PRIORITY_KEY, priority, target); + saveClassMetadata(PRIORITY_KEY, priority, target); }; } diff --git a/packages/midway-decorator/src/common/schedule.ts b/packages/midway-decorator/src/common/schedule.ts index d1e1fefd41be..319c15e10285 100644 --- a/packages/midway-decorator/src/common/schedule.ts +++ b/packages/midway-decorator/src/common/schedule.ts @@ -1,4 +1,4 @@ -import { saveClassMetaData, saveModule, scope, ScopeEnum } from 'injection'; +import { saveClassMetadata, saveModule, scope, ScopeEnum } from 'injection'; import { SCHEDULE_KEY } from '../constant'; export interface CommonSchedule { @@ -21,7 +21,7 @@ export interface ScheduleOpts { export function schedule(scheduleOpts: ScheduleOpts | string) { return function (target: any): void { saveModule(SCHEDULE_KEY, target); - saveClassMetaData(SCHEDULE_KEY, scheduleOpts, target); + saveClassMetadata(SCHEDULE_KEY, scheduleOpts, target); scope(ScopeEnum.Request)(target); }; } diff --git a/packages/midway-decorator/src/constant.ts b/packages/midway-decorator/src/constant.ts index 43d117fc2882..a770dfc02a3c 100644 --- a/packages/midway-decorator/src/constant.ts +++ b/packages/midway-decorator/src/constant.ts @@ -9,3 +9,11 @@ export const HANDLER_KEY = 'handler'; // web export const CONTROLLER_KEY = 'controller'; export const WEB_ROUTER_KEY = 'web_router'; + +// framework +export const CONFIG_KEY = 'config'; +export const PLUGIN_KEY = 'plugin'; +export const LOGGER_KEY = 'logger'; + +// constructor key +export const CLASS_KEY_CONSTRUCTOR = 'midway:class_key_constructor'; diff --git a/packages/midway-decorator/src/faas/handler.ts b/packages/midway-decorator/src/faas/handler.ts index 9b9c2fc90001..70cdd5d24a35 100644 --- a/packages/midway-decorator/src/faas/handler.ts +++ b/packages/midway-decorator/src/faas/handler.ts @@ -1,9 +1,9 @@ -import { saveMethodDataToClass } from 'injection'; +import { savePropertyDataToClass } from 'injection'; import { HANDLER_KEY } from '../constant'; export function handler(handlerName): MethodDecorator { return (target: object, propertykey: string, descriptor: PropertyDescriptor) => { - saveMethodDataToClass(HANDLER_KEY, { + savePropertyDataToClass(HANDLER_KEY, { method: propertykey, data: handlerName, }, target, propertykey); diff --git a/packages/midway-decorator/src/framework/config.ts b/packages/midway-decorator/src/framework/config.ts new file mode 100644 index 000000000000..17ec6e5eb7ab --- /dev/null +++ b/packages/midway-decorator/src/framework/config.ts @@ -0,0 +1,19 @@ +import { attachClassMetadata } from 'injection'; +import { CONFIG_KEY } from '../constant'; +import { attachConstructorDataOnClass } from '../utils'; + +export function config(identifier?: string) { + return function (target: any, targetKey: string, index?: number): void { + if (typeof index === 'number') { + attachConstructorDataOnClass(identifier, target, CONFIG_KEY, index); + } else { + if (!identifier) { + identifier = targetKey; + } + attachClassMetadata(CONFIG_KEY, { + key: identifier, + propertyName: targetKey + }, target); + } + }; +} diff --git a/packages/midway-decorator/src/framework/logger.ts b/packages/midway-decorator/src/framework/logger.ts new file mode 100644 index 000000000000..e861dc1a1b8f --- /dev/null +++ b/packages/midway-decorator/src/framework/logger.ts @@ -0,0 +1,19 @@ +import { attachClassMetadata } from 'injection'; +import { LOGGER_KEY } from '../constant'; +import { attachConstructorDataOnClass } from '../utils'; + +export function logger(identifier?: string) { + return function (target: any, targetKey: string, index?: number): void { + if (typeof index === 'number') { + attachConstructorDataOnClass(identifier, target, LOGGER_KEY, index); + } else { + if (!identifier) { + identifier = targetKey; + } + attachClassMetadata(LOGGER_KEY, { + key: identifier, + propertyName: targetKey + }, target); + } + }; +} diff --git a/packages/midway-decorator/src/framework/plugin.ts b/packages/midway-decorator/src/framework/plugin.ts new file mode 100644 index 000000000000..2b9105146863 --- /dev/null +++ b/packages/midway-decorator/src/framework/plugin.ts @@ -0,0 +1,19 @@ +import { attachClassMetadata } from 'injection'; +import { PLUGIN_KEY } from '../constant'; +import { attachConstructorDataOnClass } from '../utils'; + +export function plugin(identifier?: string) { + return function (target: any, targetKey: string, index?: number): void { + if (typeof index === 'number') { + attachConstructorDataOnClass(identifier, target, PLUGIN_KEY, index); + } else { + if (!identifier) { + identifier = targetKey; + } + attachClassMetadata(PLUGIN_KEY, { + key: identifier, + propertyName: targetKey + }, target); + } + }; +} diff --git a/packages/midway-decorator/src/index.ts b/packages/midway-decorator/src/index.ts index 07d31836a886..921686d50a29 100644 --- a/packages/midway-decorator/src/index.ts +++ b/packages/midway-decorator/src/index.ts @@ -5,3 +5,6 @@ export * from './common/priority'; export * from './faas/fun'; export * from './web/requestMapping'; export * from './web/controller'; +export * from './framework/config'; +export * from './framework/logger'; +export * from './framework/plugin'; diff --git a/packages/midway-decorator/src/utils.ts b/packages/midway-decorator/src/utils.ts new file mode 100644 index 000000000000..376a342b71bd --- /dev/null +++ b/packages/midway-decorator/src/utils.ts @@ -0,0 +1,23 @@ +import { getParamNames, getClassMetadata, saveClassMetadata } from 'injection'; +import { CLASS_KEY_CONSTRUCTOR } from './constant'; + +export function attachConstructorDataOnClass(identifier, clz, type, index) { + + if (!identifier) { + const args = getParamNames(clz); + if (clz.length === args.length && index < clz.length) { + identifier = args[index]; + } + } + + // save constructor index on class + let constructorMetaValue = getClassMetadata(CLASS_KEY_CONSTRUCTOR, clz); + if (!constructorMetaValue) { + constructorMetaValue = {}; + } + constructorMetaValue[index] = { + key: identifier, + type + }; + saveClassMetadata(CLASS_KEY_CONSTRUCTOR, constructorMetaValue, clz); +} diff --git a/packages/midway-decorator/src/web/controller.ts b/packages/midway-decorator/src/web/controller.ts index d2c30ede7fa3..7114d1517a96 100644 --- a/packages/midway-decorator/src/web/controller.ts +++ b/packages/midway-decorator/src/web/controller.ts @@ -1,4 +1,4 @@ -import { saveClassMetaData, saveModule, scope, ScopeEnum } from 'injection'; +import { saveClassMetadata, saveModule, scope, ScopeEnum } from 'injection'; import { CONTROLLER_KEY } from '../constant'; import { WebMiddleware } from '../interface'; @@ -10,7 +10,7 @@ export interface ControllerOption { export function controller(prefix: string, routerOptions: { middleware: Array } = {middleware: []}): ClassDecorator { return (target: any) => { saveModule(CONTROLLER_KEY, target); - saveClassMetaData(CONTROLLER_KEY, { + saveClassMetadata(CONTROLLER_KEY, { prefix, routerOptions } as ControllerOption, target); diff --git a/packages/midway-decorator/src/web/requestMapping.ts b/packages/midway-decorator/src/web/requestMapping.ts index b3ec1c2bac0c..878bcbf0d264 100644 --- a/packages/midway-decorator/src/web/requestMapping.ts +++ b/packages/midway-decorator/src/web/requestMapping.ts @@ -1,7 +1,7 @@ /** * 'HEAD', 'OPTIONS', 'GET', 'PUT', 'PATCH', 'POST', 'DELETE' 封装 */ -import { attachClassMetaData } from 'injection'; +import { attachClassMetadata } from 'injection'; import { WEB_ROUTER_KEY } from '../constant'; import { WebMiddleware } from '../interface'; @@ -52,7 +52,7 @@ export const RequestMapping = ( const middleware = metadata[ROUTER_MIDDLEWARE]; return (target, key, descriptor: PropertyDescriptor) => { - attachClassMetaData(WEB_ROUTER_KEY, { + attachClassMetadata(WEB_ROUTER_KEY, { path, requestMethod, routerName, diff --git a/packages/midway-mock/package.json b/packages/midway-mock/package.json index e00d5628a7cd..1f40066d435c 100644 --- a/packages/midway-mock/package.json +++ b/packages/midway-mock/package.json @@ -31,7 +31,7 @@ ], "license": "MIT", "devDependencies": { - "injection": "^1.3.2", + "injection": "^1.4.1", "midway-bin": "^1.4.3", "midway-core": "^1.4.3" }, diff --git a/packages/midway-schedule/agent.ts b/packages/midway-schedule/agent.ts index 86661438e960..6ab86362fb66 100644 --- a/packages/midway-schedule/agent.ts +++ b/packages/midway-schedule/agent.ts @@ -1,5 +1,5 @@ import { SCHEDULE_KEY, ScheduleOpts } from '@midwayjs/decorator'; -import { getClassMetaData, getProviderId, listModule } from 'injection'; +import { getClassMetadata, getProviderId, listModule } from 'injection'; export = (agent) => { @@ -28,7 +28,7 @@ export = (agent) => { const schedules: any[] = listModule(SCHEDULE_KEY); for (const scheduleModule of schedules) { const provideId = getProviderId(scheduleModule); - const opts: ScheduleOpts = getClassMetaData(SCHEDULE_KEY, scheduleModule); + const opts: ScheduleOpts = getClassMetadata(SCHEDULE_KEY, scheduleModule); const type = opts.type; if (opts.disable) { continue; diff --git a/packages/midway-schedule/app.ts b/packages/midway-schedule/app.ts index 802e88c50333..c1c7028ca75c 100644 --- a/packages/midway-schedule/app.ts +++ b/packages/midway-schedule/app.ts @@ -1,5 +1,5 @@ import { ScheduleOpts, SCHEDULE_KEY } from '@midwayjs/decorator'; -import { getClassMetaData, listModule, getProviderId } from 'injection'; +import { getClassMetadata, listModule, getProviderId } from 'injection'; import * as is from 'is-type-of'; export = (app) => { @@ -14,7 +14,7 @@ export = (app) => { const providerId = getProviderId(scheduleModule); if (providerId) { const key = providerId + '#' + scheduleModule.name; - const opts: ScheduleOpts = getClassMetaData(SCHEDULE_KEY, scheduleModule); + const opts: ScheduleOpts = getClassMetadata(SCHEDULE_KEY, scheduleModule); const task = async (ctx, data) => { const ins = await ctx.requestContext.getAsync(scheduleModule); ins.exec = app.toAsyncFunction(ins.exec); diff --git a/packages/midway-schedule/package.json b/packages/midway-schedule/package.json index 30705df69245..8ea2279441c6 100644 --- a/packages/midway-schedule/package.json +++ b/packages/midway-schedule/package.json @@ -26,7 +26,7 @@ }, "dependencies": { "@midwayjs/decorator": "^1.4.3", - "injection": "^1.3.2", + "injection": "^1.4.1", "is-type-of": "^1.2.1" } } diff --git a/packages/midway-web/.autod.conf.js b/packages/midway-web/.autod.conf.js index 4a2f3b2a26ad..7464e262aca5 100644 --- a/packages/midway-web/.autod.conf.js +++ b/packages/midway-web/.autod.conf.js @@ -11,7 +11,6 @@ module.exports = { 'run', ], dep: [ - "inflection", "midway-schedule" ], devdep: [ diff --git a/packages/midway-web/package.json b/packages/midway-web/package.json index 871735c05af0..8ac0c542518a 100644 --- a/packages/midway-web/package.json +++ b/packages/midway-web/package.json @@ -40,10 +40,10 @@ "@midwayjs/decorator": "^1.4.3", "debug": "^4.1.1", "egg": "^2.17.0", + "egg-core": "^4.14.1", "extend2": "^1.0.0", "globby": "^9.0.0", - "inflection": "^1.12.0", - "injection": "^1.3.2", + "injection": "^1.4.1", "is-type-of": "^1.2.1", "midway-core": "^1.4.3", "midway-schedule": "^1.4.3", diff --git a/packages/midway-web/src/index.ts b/packages/midway-web/src/index.ts index 004577a36613..9a49b89b3d7e 100644 --- a/packages/midway-web/src/index.ts +++ b/packages/midway-web/src/index.ts @@ -1,6 +1,33 @@ +export * from 'injection'; +export * from 'midway-core'; export * from '@midwayjs/decorator'; export {AgentWorkerLoader, AppWorkerLoader} from './loader/loader'; export {Application, Agent} from './midway'; export {BaseController} from './baseController'; export {MidwayWebLoader} from './loader/webLoader'; export {loading} from './loading'; + +export { + Context, + IContextLocals, + EggEnvType, + IEggPluginItem, + EggPlugin, + PowerPartial, + EggAppConfig, + FileStream, + IApplicationLocals, + EggApplication, + EggAppInfo, + EggHttpClient, + EggContextHttpClient, + Request, + Response, + Router, +} from 'egg'; +export { + LoggerLevel as EggLoggerLevel, + EggLogger, + EggLoggers, + EggContextLogger, +} from 'egg-logger'; diff --git a/packages/midway-core/src/interface.ts b/packages/midway-web/src/interface.ts similarity index 100% rename from packages/midway-core/src/interface.ts rename to packages/midway-web/src/interface.ts diff --git a/packages/midway-web/src/loader/webLoader.ts b/packages/midway-web/src/loader/webLoader.ts index 8a48d7867a39..64bcee030234 100644 --- a/packages/midway-web/src/loader/webLoader.ts +++ b/packages/midway-web/src/loader/webLoader.ts @@ -1,14 +1,50 @@ import { EggRouter as Router } from '@eggjs/router'; -import { CONTROLLER_KEY, RouterOption, PRIORITY_KEY, WEB_ROUTER_KEY, ControllerOption, WebMiddleware } from '@midwayjs/decorator'; -import { getClassMetaData, listModule, getProviderId } from 'injection'; -import { MidwayLoader } from 'midway-core'; +import { + CONTROLLER_KEY, + ControllerOption, + PRIORITY_KEY, + RouterOption, + WEB_ROUTER_KEY, + WebMiddleware +} from '@midwayjs/decorator'; +import * as extend from 'extend2'; +import * as fs from 'fs'; +import { getClassMetadata, getProviderId, listModule } from 'injection'; +import { ContainerLoader, MidwayHandlerKey } from 'midway-core'; +import * as path from 'path'; +import { MidwayLoaderOptions } from '../interface'; +import { isPluginName, isTypeScriptEnvironment } from '../utils'; -export class MidwayWebLoader extends MidwayLoader { +const debug = require('debug')(`midway:loader:${process.pid}`); +const EggLoader = require('egg-core').EggLoader; + +const TS_SRC_DIR = 'src'; +const TS_TARGET_DIR = 'dist'; + +export class MidwayWebLoader extends EggLoader { private controllerIds: string[] = []; private prioritySortRouters: Array<{ priority: number, router: Router, }> = []; + private containerLoader; + + constructor(options: MidwayLoaderOptions) { + super(options); + } + + /** + * 判断是否是 ts 模式,在构造器内就会被执行 + */ + get isTsMode() { + return this.app.options.typescript; + } + + // loadPlugin -> loadConfig -> afterLoadConfig + loadConfig() { + this.loadPlugin(); + super.loadConfig(); + } protected async loadMidwayController(): Promise { const controllerModules = listModule(CONTROLLER_KEY); @@ -62,7 +98,7 @@ export class MidwayWebLoader extends MidwayLoader { protected async preRegisterRouter(target, controllerId) { const app = this.app; - const controllerOption: ControllerOption = getClassMetaData(CONTROLLER_KEY, target); + const controllerOption: ControllerOption = getClassMetadata(CONTROLLER_KEY, target); let newRouter; if (controllerOption.prefix) { newRouter = new Router({ @@ -81,7 +117,7 @@ export class MidwayWebLoader extends MidwayLoader { } // implement @get @post - const webRouterInfo: RouterOption[] = getClassMetaData(WEB_ROUTER_KEY, target); + const webRouterInfo: RouterOption[] = getClassMetadata(WEB_ROUTER_KEY, target); if (webRouterInfo && typeof webRouterInfo[Symbol.iterator] === 'function') { for (const webRouter of webRouterInfo) { // get middleware @@ -111,7 +147,7 @@ export class MidwayWebLoader extends MidwayLoader { // sort for priority if (newRouter) { - const priority = getClassMetaData(PRIORITY_KEY, target); + const priority = getClassMetadata(PRIORITY_KEY, target); this.prioritySortRouters.push({ priority: priority || 0, router: newRouter, @@ -134,7 +170,184 @@ export class MidwayWebLoader extends MidwayLoader { } public async refreshContext(): Promise { - await super.refreshContext(); + // 虽然有点hack,但是将就着用吧 + if (Array.isArray(this.config.configLocations)) { + this.applicationContext.configLocations = this.config.configLocations; + this.applicationContext.props.putObject(this.config); + } + + await this.containerLoader.refresh(); await this.preloadControllerFromXml(); } + + // Get the real plugin path + protected getPluginPath(plugin) { + if (plugin.path) { + return plugin.path; + } + + const name = plugin.package || plugin.name; + const lookupDirs = []; + + // 尝试在以下目录找到匹配的插件 + // -> {APP_PATH}/node_modules + // -> {EGG_PATH}/node_modules + // -> $CWD/node_modules + lookupDirs.push(path.join(this.appDir, 'node_modules')); + + // 到 egg 中查找,优先从外往里查找 + for (let i = this.eggPaths.length - 1; i >= 0; i--) { + const eggPath = this.eggPaths[i]; + lookupDirs.push(path.join(eggPath, 'node_modules')); + } + + // should find the $cwd/node_modules when test the plugins under npm3 + lookupDirs.push(path.join(process.cwd(), 'node_modules')); + + if (process.env.PLUGIN_PATH) { + lookupDirs.push(path.join(process.env.PLUGIN_PATH, 'node_modules')); + } + + for (let dir of lookupDirs) { + dir = path.join(dir, name); + if (fs.existsSync(dir)) { + return fs.realpathSync(dir); + } + } + + throw new Error(`Can not find plugin ${name} in "${lookupDirs.join(', ')}"`); + } + + private registerTypescriptDirectory() { + const app = this.app; + // 处理 ts 的初始路径 + this.appDir = this.baseDir = app.options.baseDir; + if (this.isTsMode) { + let dirSuffix = app.options.targetDir || TS_TARGET_DIR; + if (isTypeScriptEnvironment()) { + dirSuffix = app.options.srcDir || TS_SRC_DIR; + // 打开 egg 加载 ts 的开关 + process.env.EGG_TYPESCRIPT = 'true'; + debug(`typescript mode = true`); + } + + const dir = path.join(app.options.baseDir, dirSuffix); + this.baseDir = app.options.baseDir = this.options.baseDir = dir; + this.options.logger.info(`in typescript current dir change to ${dir}`); + debug(`in typescript current dir change to ${dir}`); + } + } + + getEggPaths() { + if (!this.appDir) { + // register appDir here + this.registerTypescriptDirectory(); + } + return super.getEggPaths(); + } + + getServerEnv() { + let serverEnv; + + const envPath = path.join(this.appDir, 'config/env'); + if (fs.existsSync(envPath)) { + serverEnv = fs.readFileSync(envPath, 'utf8').trim(); + } + + if (!serverEnv) { + serverEnv = process.env.EGG_SERVER_ENV || process.env.MIDWAY_SERVER_ENV; + } + + if (!serverEnv) { + serverEnv = super.getServerEnv(); + } + + return serverEnv; + } + + /** + * intercept plugin when it set value to app + * @param fileName + * @returns {boolean} + */ + protected interceptLoadCustomApplication(fileName) { + const self = this; + const pluginContainerProps = Object.getOwnPropertyNames(this); + this.app = new Proxy(this.app, { + defineProperty(target, prop, attributes) { + if (!self.pluginLoaded && isPluginName(prop) && !(prop in pluginContainerProps)) { + // save to context when called app.xxx = xxx + // now we can get plugin from context + debug(`pluginContext register [${prop as string}]`); + self.pluginContext.registerObject(prop, attributes.value); + } + return Object.defineProperty(target, prop, attributes); + } + }); + + this.getLoadUnits() + .forEach(unit => { + // 兼容旧插件加载方式 + const ret = this.loadFile(this.resolveModule(path.join(unit.path, fileName))); + if (ret) { + // midway 的插件会返回对象 + debug(`pluginContext register [${unit.name}]`); + this.pluginContext.registerObject(unit.name, ret); + } + }); + + // 插件加载完毕 + this.pluginLoaded = true; + } + + getAppInfo() { + if (!this.appInfo) { + const appInfo = super.getAppInfo(); + // ROOT == HOME in prod env + this.appInfo = extend(true, appInfo, { + root: appInfo.env === 'local' || appInfo.env === 'unittest' ? this.appDir : appInfo.root, + appDir: this.appDir, + }); + } + return this.appInfo; + } + + get applicationContext() { + return this.containerLoader.getApplicationContext(); + } + + get pluginContext() { + return this.containerLoader.getPluginContext(); + } + + loadApplicationContext() { + // this.app.options.container 测试用例编写方便点 + const containerConfig = this.config.container || this.app.options.container || {}; + // 在 super constructor 中会调用到getAppInfo,之后会被赋值 + // 如果是typescript会加上 dist 或者 src 目录 + this.containerLoader = new ContainerLoader({ + baseDir: this.baseDir, + isTsMode: this.isTsMode + }); + this.containerLoader.initialize(); + this.applicationContext.registerObject('appDir', this.appDir); + // 如果没有关闭autoLoad 则进行load + this.containerLoader.loadDirectory(containerConfig); + + // register handler for container + this.containerLoader.registerAllHook(MidwayHandlerKey.CONFIG, (key) => { + return this.config[key]; + }); + + this.containerLoader.registerAllHook(MidwayHandlerKey.PLUGIN, (key) => { + return this.pluginContext.get(key); + }); + + this.containerLoader.registerAllHook(MidwayHandlerKey.LOGGER, (key) => { + if (this.app.getLogger) { + return this.app.getLogger(key); + } + return this.options.logger; + }); + } } diff --git a/packages/midway-web/src/utils.ts b/packages/midway-web/src/utils.ts index 6397d7fa512f..a4908ee410c1 100644 --- a/packages/midway-web/src/utils.ts +++ b/packages/midway-web/src/utils.ts @@ -41,3 +41,11 @@ export function getMethodNames(obj) { return ownKeysOnObjectPrototype.indexOf(k) === -1; }); } + +export function isTypeScriptEnvironment() { + return !!require.extensions['.ts']; +} + +export function isPluginName(name) { + return typeof name === 'string' && !/^_/.test(name); +} diff --git a/packages/midway-web/test/fixtures/enhance/base-app-async/src/lib/service.ts b/packages/midway-web/test/fixtures/enhance/base-app-async/src/lib/service.ts index 510a0dbf2aa6..13f109f70276 100644 --- a/packages/midway-web/test/fixtures/enhance/base-app-async/src/lib/service.ts +++ b/packages/midway-web/test/fixtures/enhance/base-app-async/src/lib/service.ts @@ -1,4 +1,4 @@ -import {config, plugin} from 'midway-core'; +import {config, plugin} from '@midwayjs/decorator'; import {provide, async, init} from 'injection'; @async() diff --git a/packages/midway-web/test/fixtures/enhance/base-app-constructor/src/lib/service.ts b/packages/midway-web/test/fixtures/enhance/base-app-constructor/src/lib/service.ts index 6c95dff00dfd..de00623b2541 100644 --- a/packages/midway-web/test/fixtures/enhance/base-app-constructor/src/lib/service.ts +++ b/packages/midway-web/test/fixtures/enhance/base-app-constructor/src/lib/service.ts @@ -1,4 +1,4 @@ -import {config, plugin} from 'midway-core'; +import {config, plugin} from '@midwayjs/decorator'; import {provide, async, init, inject} from 'injection'; @provide() diff --git a/packages/midway-web/test/fixtures/enhance/base-app-decorator/src/lib/service.ts b/packages/midway-web/test/fixtures/enhance/base-app-decorator/src/lib/service.ts index df9d4474497b..cf01caada024 100644 --- a/packages/midway-web/test/fixtures/enhance/base-app-decorator/src/lib/service.ts +++ b/packages/midway-web/test/fixtures/enhance/base-app-decorator/src/lib/service.ts @@ -1,4 +1,4 @@ -import {config, plugin} from 'midway-core'; +import {config, plugin} from '@midwayjs/decorator'; import {provide} from 'injection'; @provide() diff --git a/packages/midway-web/test/fixtures/enhance/base-app-function/src/lib/service.ts b/packages/midway-web/test/fixtures/enhance/base-app-function/src/lib/service.ts index c8a1e31c636e..868a5d590056 100644 --- a/packages/midway-web/test/fixtures/enhance/base-app-function/src/lib/service.ts +++ b/packages/midway-web/test/fixtures/enhance/base-app-function/src/lib/service.ts @@ -1,4 +1,4 @@ -import { config, plugin } from 'midway-core'; +import { config, plugin } from '@midwayjs/decorator'; import { async, init, inject, provide } from 'injection'; @provide() diff --git a/packages/midway-web/test/fixtures/enhance/base-app-middleware/src/web/middleware/api.ts b/packages/midway-web/test/fixtures/enhance/base-app-middleware/src/web/middleware/api.ts index e5a51d6a0037..6012eb87df8b 100644 --- a/packages/midway-web/test/fixtures/enhance/base-app-middleware/src/web/middleware/api.ts +++ b/packages/midway-web/test/fixtures/enhance/base-app-middleware/src/web/middleware/api.ts @@ -1,6 +1,5 @@ -import { WebMiddleware } from '@midwayjs/decorator'; +import { WebMiddleware, config } from '@midwayjs/decorator'; import { provide } from 'injection'; -import { config } from 'midway-core'; @provide() export class ApiMiddleware implements WebMiddleware { diff --git a/packages/midway-web/test/fixtures/enhance/base-app-utils/src/app/controller/api.ts b/packages/midway-web/test/fixtures/enhance/base-app-utils/src/app/controller/api.ts index 0c8bd46d6003..67fc5385be9e 100644 --- a/packages/midway-web/test/fixtures/enhance/base-app-utils/src/app/controller/api.ts +++ b/packages/midway-web/test/fixtures/enhance/base-app-utils/src/app/controller/api.ts @@ -1,8 +1,7 @@ 'use strict'; import {inject, provide} from 'injection'; -import {controller, get} from '../../../../../../../src/'; -import {config} from 'midway-core'; +import {controller, get, config} from '@midwayjs/decorator'; @provide() export class BaseApi { diff --git a/packages/midway/package.json b/packages/midway/package.json index d4b40f7d9161..4993934b1b15 100644 --- a/packages/midway/package.json +++ b/packages/midway/package.json @@ -22,10 +22,7 @@ "midway-bin": "^1.4.3" }, "dependencies": { - "egg": "^2.17.0", "egg-cluster": "^1.22.2", - "egg-core": "^4.14.1", - "injection": "^1.3.2", "midway-core": "^1.4.3", "midway-web": "^1.4.3", "ts-node": "^8.0.2" diff --git a/packages/midway/src/index.ts b/packages/midway/src/index.ts index fe7ae1d682d1..dea4523e051c 100644 --- a/packages/midway/src/index.ts +++ b/packages/midway/src/index.ts @@ -1,30 +1,4 @@ -export * from 'injection'; -export * from 'midway-core'; export * from 'midway-web'; -export { - Context, - IContextLocals, - EggEnvType, - IEggPluginItem, - EggPlugin, - PowerPartial, - EggAppConfig, - FileStream, - IApplicationLocals, - EggApplication, - EggAppInfo, - EggHttpClient, - EggContextHttpClient, - Request, - Response, - Router, -} from 'egg'; -export { - LoggerLevel as EggLoggerLevel, - EggLogger, - EggLoggers, - EggContextLogger, -} from 'egg-logger'; const Master = require('../cluster/master'); /**