From 41bce5d8a60a6fde61ff62794612eecff2e260ed Mon Sep 17 00:00:00 2001 From: Harry Chen Date: Tue, 27 Oct 2020 14:46:30 +0800 Subject: [PATCH] fix: configuration inject plugin and more in production environment (#680) --- lerna.json | 2 +- packages/bootstrap/package.json | 6 +- packages/core/package.json | 6 +- packages/core/src/baseFramework.ts | 249 ++++++-- packages/core/src/common/util.ts | 70 --- .../core/src/context/applicationContext.ts | 6 +- packages/core/src/context/configuration.ts | 22 +- packages/core/src/context/container.ts | 196 ------ packages/core/src/context/midwayContainer.ts | 382 ++++++++++-- packages/core/src/context/requestContainer.ts | 21 +- packages/core/src/context/resolverHandler.ts | 5 +- .../core/src/definitions/objectCreator.ts | 6 +- packages/core/src/index.ts | 2 +- packages/core/src/interface.ts | 88 ++- packages/core/src/loader.ts | 80 ++- packages/core/src/service/configService.ts | 2 +- packages/core/src/util/index.ts | 71 +++ packages/core/src/util/staticConfig.ts | 2 +- packages/core/test/baseFramework.test.ts | 558 ++++++++++++++++++ packages/core/test/context/container.test.ts | 13 +- .../core/test/context/midwayContainer.test.ts | 219 +++++-- packages/core/test/fixtures/pipeline.ts | 4 +- packages/core/test/loader.test.ts | 7 +- packages/core/test/util.test.ts | 2 +- packages/decorator/package.json | 4 +- packages/decorator/src/annotation/schedule.ts | 2 +- .../decorator/src/common/decoratorManager.ts | 238 +++++++- packages/decorator/src/common/utils.ts | 232 -------- packages/decorator/src/index.ts | 1 - packages/decorator/src/interface.ts | 2 + packages/decorator/src/util/index.ts | 28 + packages/faas/package.json | 10 +- packages/faas/src/framework.ts | 119 ++-- packages/midway-schedule/package.json | 14 +- packages/midway/package.json | 6 +- packages/mock/package.json | 10 +- packages/mock/src/utils.ts | 2 + packages/rabbitmq/package.json | 10 +- packages/rabbitmq/src/framework.ts | 11 +- packages/rabbitmq/src/interface.ts | 5 +- packages/socketio/package.json | 10 +- packages/socketio/src/framework.ts | 5 +- packages/socketio/src/interface.ts | 6 +- packages/web-express/package.json | 10 +- packages/web-express/src/framework.ts | 59 +- packages/web-koa/package.json | 10 +- packages/web-koa/src/framework.ts | 61 +- packages/web/agent.js | 26 +- packages/web/app.js | 47 +- packages/web/app/extend/application.js | 9 + packages/web/package.json | 21 +- packages/web/src/application.ts | 122 ++-- packages/web/src/devFramework.ts | 67 +++ packages/web/src/framework.ts | 154 +++-- packages/web/src/index.ts | 23 +- packages/web/src/interface.ts | 1 - packages/web/test/enhance.test.ts | 8 +- .../src/app/controller/param.ts | 15 +- .../base-app-decorator/src/config/plugin.ts | 2 +- .../src/component/sql/src/configuration.ts | 11 +- packages/web/test/issue.test.ts | 1 + 61 files changed, 2132 insertions(+), 1249 deletions(-) delete mode 100644 packages/core/src/common/util.ts delete mode 100644 packages/core/src/context/container.ts create mode 100644 packages/core/test/baseFramework.test.ts delete mode 100644 packages/decorator/src/common/utils.ts create mode 100644 packages/web/app/extend/application.js create mode 100644 packages/web/src/devFramework.ts diff --git a/lerna.json b/lerna.json index 29a9c1d704bb..23a1548a7fd5 100644 --- a/lerna.json +++ b/lerna.json @@ -21,5 +21,5 @@ } }, "npmClient": "npm", - "version": "2.3.17" + "version": "2.3.18-beta.6" } diff --git a/packages/bootstrap/package.json b/packages/bootstrap/package.json index e85b397116e5..a17c479176fd 100644 --- a/packages/bootstrap/package.json +++ b/packages/bootstrap/package.json @@ -1,6 +1,6 @@ { "name": "@midwayjs/bootstrap", - "version": "2.3.17", + "version": "2.3.18-beta.6", "description": "midwayjs bootstrap", "main": "dist/index", "typings": "dist/index.d.ts", @@ -22,12 +22,12 @@ "license": "MIT", "devDependencies": { "@midwayjs/cli": "^1.0.0", - "@midwayjs/core": "^2.3.17" + "@midwayjs/core": "^2.3.18-beta.6" }, "author": "Harry Chen ", "repository": { "type": "git", "url": "http://github.com/midwayjs/midway.git" }, - "gitHead": "44c0803552baf265debed8a11a860988b7e07a85" + "gitHead": "a603d2348d6141f8f723901498f03a162a037708" } diff --git a/packages/core/package.json b/packages/core/package.json index 45a1cb338435..88ed0a5be4e0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@midwayjs/core", - "version": "2.3.17", + "version": "2.3.18-beta.6", "description": "midway core", "main": "dist/index", "typings": "dist/index.d.ts", @@ -27,7 +27,7 @@ "sinon": "^7.2.2" }, "dependencies": { - "@midwayjs/decorator": "^2.3.17", + "@midwayjs/decorator": "^2.3.18-beta.6", "@midwayjs/glob": "^1.0.2", "class-transformer": "^0.3.1", "extend2": "^1.0.0", @@ -48,5 +48,5 @@ "engines": { "node": ">= 10.0.0" }, - "gitHead": "44c0803552baf265debed8a11a860988b7e07a85" + "gitHead": "a603d2348d6141f8f723901498f03a162a037708" } diff --git a/packages/core/src/baseFramework.ts b/packages/core/src/baseFramework.ts index abf5f4d45515..7b3d056a4cca 100644 --- a/packages/core/src/baseFramework.ts +++ b/packages/core/src/baseFramework.ts @@ -1,5 +1,6 @@ import { IConfigurationOptions, + ILifeCycle, IMidwayApplication, IMidwayBootstrapOptions, IMidwayContainer, @@ -7,8 +8,25 @@ import { MidwayFrameworkType, MidwayProcessTypeEnum, } from './interface'; -import { ContainerLoader } from './'; -import { APPLICATION_KEY } from '@midwayjs/decorator'; +import { MidwayContainer } from './context/midwayContainer'; +import { + APPLICATION_KEY, + ASPECT_KEY, + CONFIGURATION_KEY, + getClassMetadata, + getProviderId, + IMethodAspect, + listModule, + listPreloadModule, +} from '@midwayjs/decorator'; +import { isAbsolute, join } from 'path'; + +function buildLoadDir(baseDir, dir) { + if (!isAbsolute(dir)) { + return join(baseDir, dir); + } + return dir; +} export abstract class BaseFramework< APP extends IMidwayApplication, @@ -17,7 +35,7 @@ export abstract class BaseFramework< protected isTsMode = true; protected baseDir: string; protected appDir: string; - protected containerLoader: ContainerLoader; + protected applicationContext: IMidwayContainer; public configurationOptions: T; public app: APP; @@ -26,54 +44,113 @@ export abstract class BaseFramework< return this; } - public async initialize( - options: Partial - ): Promise { + public async initialize(options: IMidwayBootstrapOptions): Promise { this.baseDir = options.baseDir; this.appDir = options.appDir; - this.containerLoader = new ContainerLoader({ - baseDir: this.baseDir, - isTsMode: this.isTsMode, - preloadModules: options.preloadModules || [], - }); + /** + * before create MidwayContainer instance,can change init parameters + */ + await this.beforeContainerInitialize(options); /** - * initialize containerLoader and initialize ioc container instance + * initialize MidwayContainer instance */ - await this.beforeInitialize(options); - this.containerLoader.initialize(); + await this.containerInitialize(options); + /** + * before container load directory and bind + */ + await this.afterContainerInitialize(options); + + /** + * run container loadDirectoryLoad method to create object definition + */ + await this.containerDirectoryLoad(options); + + /** + * after container load directory and bind + */ + await this.afterContainerDirectoryLoad(options); + + /** + * Third party application initialization + */ + await this.applicationInitialize(options); + + /** + * start to load configuration and lifeCycle + */ + await this.containerReady(options); + + /** + * after container refresh + */ + await this.afterContainerReady(options); + } + + protected async containerInitialize(options: IMidwayBootstrapOptions) { + this.applicationContext = new MidwayContainer(this.baseDir, undefined); + this.applicationContext.disableConflictCheck = + options.disableConflictCheck || true; + this.applicationContext.registerObject('baseDir', this.baseDir); + this.applicationContext.registerObject('appDir', this.appDir); + this.applicationContext.registerObject('isTsMode', this.isTsMode); + } + + protected async containerDirectoryLoad(options: IMidwayBootstrapOptions) { /** * load directory and bind files to ioc container */ - await this.beforeDirectoryLoad(options); - const applicationContext = this.containerLoader.getApplicationContext(); - applicationContext.registerObject('baseDir', this.baseDir); - applicationContext.registerObject('appDir', this.appDir); - // 如果没有关闭autoLoad 则进行load - this.containerLoader.loadDirectory(options); - - // register app - this.containerLoader.registerHook(APPLICATION_KEY, () => { - return this.getApplication(); + if (!this.isTsMode && options.disableAutoLoad === undefined) { + // disable auto load in js mode by default + options.disableAutoLoad = true; + } + + if (options.disableAutoLoad) return; + + // use baseDir in parameter first + const baseDir = options.baseDir || this.baseDir; + const defaultLoadDir = this.isTsMode ? [baseDir] : []; + this.applicationContext.load({ + loadDir: (options.loadDir || defaultLoadDir).map(dir => { + return buildLoadDir(baseDir, dir); + }), + pattern: options.pattern, + ignore: options.ignore, }); - await this.afterDirectoryLoad(options); + if (options.preloadModules && options.preloadModules.length) { + for (const preloadModule of options.preloadModules) { + this.applicationContext.bindClass(preloadModule); + } + } + + this.applicationContext.registerDataHandler(APPLICATION_KEY, () => { + return this.getApplication(); + }); + } + protected async containerReady(options: IMidwayBootstrapOptions) { if (!this.app.getApplicationContext) { - this.app = this.defineApplicationProperties(this.app); + this.defineApplicationProperties(); } - /** - * start to load configuration and lifeCycle - */ - await this.containerLoader.refresh(); - await this.afterInitialize(options); + await this.applicationContext.ready(); + // lifecycle 支持 + await this.loadLifeCycles(); + // 预加载模块支持 + await this.loadPreloadModule(); + // 切面支持 + await this.loadAspect(); + } + + protected async containerStop() { + await this.applicationContext.stop(); } public getApplicationContext(): IMidwayContainer { - return this.containerLoader.getApplicationContext(); + return this.applicationContext; } public getConfiguration(key?: string): any { @@ -88,6 +165,8 @@ export abstract class BaseFramework< .getCurrentEnvironment(); } + public abstract async applicationInitialize(options: IMidwayBootstrapOptions); + public abstract getFrameworkType(): MidwayFrameworkType; public abstract getApplication(): APP; @@ -96,11 +175,11 @@ export abstract class BaseFramework< public async stop(): Promise { await this.beforeStop(); - await this.containerLoader.stop(); + await this.containerStop(); } - protected defineApplicationProperties(app: APP): APP { - return Object.assign(app, { + protected defineApplicationProperties(applicationProperties: object = {}) { + const defaultApplicationProperties = { getBaseDir: () => { return this.baseDir; }, @@ -115,6 +194,10 @@ export abstract class BaseFramework< .getCurrentEnvironment(); }, + getApplicationContext: () => { + return this.getApplicationContext(); + }, + getConfig: (key?: string) => { return this.getApplicationContext() .getConfigService() @@ -128,24 +211,108 @@ export abstract class BaseFramework< getProcessType: () => { return MidwayProcessTypeEnum.APPLICATION; }, - }); + }; + Object.assign( + this.app, + defaultApplicationProperties, + applicationProperties + ); } protected async beforeStop(): Promise {} - protected async beforeInitialize( + protected async beforeContainerInitialize( options: Partial ): Promise {} - protected async beforeDirectoryLoad( + protected async afterContainerInitialize( options: Partial ): Promise {} - protected async afterDirectoryLoad( + protected async afterContainerDirectoryLoad( options: Partial ): Promise {} - protected abstract async afterInitialize( + protected async afterContainerReady( options: Partial - ): Promise; + ): Promise {} + + public async loadLifeCycles() { + // agent 不加载生命周期 + if (this.app.getProcessType() === MidwayProcessTypeEnum.AGENT) return; + const cycles: Array<{ target: any; namespace: string }> = listModule( + CONFIGURATION_KEY + ); + for (const cycle of cycles) { + const providerId = getProviderId(cycle.target); + const inst = await this.getApplicationContext().getAsync( + providerId + ); + if (typeof inst.onReady === 'function') { + /** + * 让组件能正确获取到 bind 之后 registerObject 的对象有三个方法 + * 1、在 load 之后修改 bind,不太可行 + * 2、每次 getAsync 的时候,去掉 namespace,同时还要查找当前全局的变量,性能差 + * 3、一般只会在 onReady 的地方执行 registerObject(否则没有全局的意义),这个取巧的办法就是 onReady 传入一个代理,其中绑定当前的 namespace + */ + await inst.onReady( + new Proxy(this.getApplicationContext(), { + get: function (target, prop, receiver) { + if (prop === 'getCurrentNamespace' && cycle.namespace) { + return () => { + return cycle.namespace; + }; + } + return Reflect.get(target, prop, receiver); + }, + }) + ); + } + } + } + + /** + * load preload module for container + * @private + */ + private async loadPreloadModule() { + // some common decorator implementation + const modules = listPreloadModule(); + for (const module of modules) { + // preload init context + await this.getApplicationContext().getAsync(module); + } + } + + /** + * load aspect method for container + * @private + */ + private async loadAspect() { + // for aop implementation + const aspectModules = listModule(ASPECT_KEY); + // sort for aspect target + let aspectDataList = []; + for (const module of aspectModules) { + const data = getClassMetadata(ASPECT_KEY, module); + aspectDataList = aspectDataList.concat( + data.map(el => { + el.aspectModule = module; + return el; + }) + ); + } + + // sort priority + aspectDataList.sort((pre, next) => { + return (next.priority || 0) - (pre.priority || 0); + }); + + for (const aspectData of aspectDataList) { + const aspectIns = await this.getApplicationContext().getAsync< + IMethodAspect + >(aspectData.aspectModule); + await this.getApplicationContext().addAspect(aspectIns, aspectData); + } + } } diff --git a/packages/core/src/common/util.ts b/packages/core/src/common/util.ts deleted file mode 100644 index 37184841d46b..000000000000 --- a/packages/core/src/common/util.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { dirname, resolve, sep, extname } from 'path'; - -export const safeRequire = p => { - if (p.startsWith(`.${sep}`) || p.startsWith(`..${sep}`)) { - p = resolve(dirname(module.parent.filename), p); - } - try { - return require(p); - } catch (err) { - return undefined; - } -}; - -export const isPath = (p): boolean => { - // eslint-disable-next-line no-useless-escape - if (/(^[\.\/])|:|\\/.test(p)) { - return true; - } - return false; -}; - -/** - * safelyGet(['a','b'],{a: {b: 2}}) // => 2 - * safelyGet(['a','b'],{c: {b: 2}}) // => undefined - * safelyGet(['a','1'],{a: {"1": 2}}) // => 2 - * safelyGet(['a','1'],{a: {b: 2}}) // => undefined - * safelyGet('a.b',{a: {b: 2}}) // => 2 - * safelyGet('a.b',{c: {b: 2}}) // => undefined - */ -export function safelyGet(list: string | string[], obj?: object): any { - if (arguments.length === 1) { - return (_obj: object) => safelyGet(list, _obj); - } - - if (typeof obj === 'undefined' || typeof obj !== 'object' || obj === null) { - return void 0; - } - const pathArrValue = typeof list === 'string' ? list.split('.') : list; - let willReturn: any = obj; - - for (const key of pathArrValue) { - if (typeof willReturn === 'undefined' || willReturn === null) { - return void 0; - } else if (typeof willReturn !== 'object') { - return void 0; - } - willReturn = willReturn[key]; - } - - return willReturn; -} - -/** - * 剔除 @ 符号 - * @param provideId provideId - */ -export function parsePrefix(provideId: string) { - if (provideId.includes('@')) { - return provideId.substr(1); - } - return provideId; -} - -export function isPathEqual(one: string, two: string) { - if (!one || !two) { - return false; - } - const ext = extname(one); - return one.replace(ext, '') === two; -} diff --git a/packages/core/src/context/applicationContext.ts b/packages/core/src/context/applicationContext.ts index 165ebc130d36..6df96fb45b7c 100644 --- a/packages/core/src/context/applicationContext.ts +++ b/packages/core/src/context/applicationContext.ts @@ -13,7 +13,7 @@ import { import { ObjectProperties } from '../definitions/properties'; import { ManagedResolverFactory } from './managedResolverFactory'; import { NotFoundError } from '../common/notFoundError'; -import { parsePrefix, isPathEqual } from '../common/util'; +import { parsePrefix, isPathEqual } from '../util/'; const PREFIX = '_id_default_'; @@ -143,6 +143,10 @@ export class BaseApplicationContext return this._registry; } + set registry(registry) { + this._registry = registry; + } + protected getManagedResolverFactory() { if (!this._resolverFactory) { this._resolverFactory = new ManagedResolverFactory(this); diff --git a/packages/core/src/context/configuration.ts b/packages/core/src/context/configuration.ts index 93f2deacc4b3..461411e3f2ac 100644 --- a/packages/core/src/context/configuration.ts +++ b/packages/core/src/context/configuration.ts @@ -9,12 +9,13 @@ import { isClass, isFunction, IComponentInfo, + listModule, } from '@midwayjs/decorator'; import { dirname, isAbsolute, join } from 'path'; import { MAIN_MODULE_KEY, generateProvideId } from '@midwayjs/decorator'; import { IContainerConfiguration, IMidwayContainer } from '../interface'; -import { isPath, safeRequire } from '../common/util'; +import { isPath, safeRequire } from '../util/'; import * as util from 'util'; const debug = util.debuglog('midway:container:configuration'); @@ -28,6 +29,8 @@ export class ContainerConfiguration implements IContainerConfiguration { loadDirs: string[] = []; loadModules: any[] = []; importObjects: object = new Map(); + // 新版本 configuration + newVersion = false; constructor(container) { this.container = container; @@ -44,6 +47,7 @@ export class ContainerConfiguration implements IContainerConfiguration { // for package const subContainerConfiguration = this.container.createConfiguration(); if (typeof importPackage === 'string') { + subContainerConfiguration.newVersion = false; const subPackageDir = this.resolvePackageBaseDir( importPackage, baseDir @@ -56,6 +60,7 @@ export class ContainerConfiguration implements IContainerConfiguration { `---------- end load configuration from sub package "${importPackage}" ----------` ); } else if ('Configuration' in importPackage) { + subContainerConfiguration.newVersion = true; // component is object debug( '\n---------- start load configuration from submodule" ----------' @@ -65,6 +70,7 @@ export class ContainerConfiguration implements IContainerConfiguration { `---------- end load configuration from sub package "${importPackage}" ----------` ); } else if ('component' in importPackage) { + subContainerConfiguration.newVersion = true; if ( (importPackage as IComponentInfo)?.enabledEnvironment?.includes( this.container.getCurrentEnv() @@ -283,10 +289,18 @@ export class ContainerConfiguration implements IContainerConfiguration { namespace: this.namespace, srcPath: filePath, }); - saveModule(CONFIGURATION_KEY, { - target: clzz, - namespace: this.namespace, + + // configuration 手动绑定去重 + const configurationMods = listModule(CONFIGURATION_KEY); + const exists = configurationMods.find(mod => { + return mod.target === clzz; }); + if (!exists) { + saveModule(CONFIGURATION_KEY, { + target: clzz, + namespace: this.namespace, + }); + } } getImportDirectory() { diff --git a/packages/core/src/context/container.ts b/packages/core/src/context/container.ts deleted file mode 100644 index baa04ba552fd..000000000000 --- a/packages/core/src/context/container.ts +++ /dev/null @@ -1,196 +0,0 @@ -import 'reflect-metadata'; -import { - ObjectDefinitionOptions, - ObjectIdentifier, - getConstructorInject, - getObjectDefProps, - TAGGED_PROP, - getProviderId, - generateProvideId, - isAsyncFunction, - isClass, - isFunction -} from '@midwayjs/decorator'; -import { IContainer } from '../interface'; -import { ObjectDefinition } from '../definitions/objectDefinition'; -import { ManagedReference, ManagedValue } from './managed'; -import { FunctionDefinition } from '../definitions/functionDefinition'; -import { BaseApplicationContext } from './applicationContext'; -import { recursiveGetMetadata } from '../common/reflectTool'; -import * as util from 'util'; - -const globalDebugLogger = util.debuglog('midway:container'); - -export class Container extends BaseApplicationContext implements IContainer { - id = Math.random().toString(10).slice(-5); - debugLogger = globalDebugLogger; - bind(target: T, options?: ObjectDefinitionOptions): void; - bind( - identifier: ObjectIdentifier, - target: T, - options?: ObjectDefinitionOptions - ): void; - bind( - identifier: ObjectIdentifier, - target: T, - options?: ObjectDefinitionOptions - ): void { - let definition; - - if (isClass(identifier) || isFunction(identifier)) { - options = target; - target = identifier as any; - identifier = this.getIdentifier(target); - options = null; - } - - if (isClass(target)) { - definition = new ObjectDefinition(); - } else { - definition = new FunctionDefinition(); - if (!isAsyncFunction(target)) { - definition.asynchronous = false; - } - } - - definition.path = target; - definition.id = identifier; - definition.srcPath = options ? options.srcPath : null; - definition.namespace = options ? options.namespace : ''; - - this.debugLogger(` bind id => [${definition.id}]`); - - // inject constructArgs - const constructorMetaData = getConstructorInject(target); - if (constructorMetaData) { - this.debugLogger(`inject constructor => length = ${target['length']}`); - const maxLength = Math.max.apply(null, Object.keys(constructorMetaData)); - for (let i = 0; i < maxLength + 1; i++) { - const propertyMeta = constructorMetaData[i]; - if (propertyMeta) { - const refManagedIns = new ManagedReference(); - const name = propertyMeta[0].value; - refManagedIns.args = propertyMeta[0].args; - if (this.midwayIdentifiers.includes(name)) { - refManagedIns.name = name; - } else { - refManagedIns.name = generateProvideId(name, definition.namespace); - } - definition.constructorArgs.push(refManagedIns); - } else { - // inject empty value - const valueManagedIns = new ManagedValue(); - valueManagedIns.value = undefined; - definition.constructorArgs.push(valueManagedIns); - } - } - } - - // inject properties - const metaDatas = recursiveGetMetadata(TAGGED_PROP, target); - for (const metaData of metaDatas) { - this.debugLogger(` inject properties => [${Object.keys(metaData)}]`); - for (const metaKey in metaData) { - for (const propertyMeta of metaData[metaKey]) { - const refManaged = new ManagedReference(); - refManaged.args = propertyMeta.args; - if (this.midwayIdentifiers.includes(propertyMeta.value)) { - refManaged.name = propertyMeta.value; - } else { - refManaged.name = generateProvideId( - propertyMeta.value, - definition.namespace - ); - } - definition.properties.set(metaKey, refManaged); - } - } - } - - this.convertOptionsToDefinition(options, definition); - // 对象自定义的annotations可以覆盖默认的属性 - this.registerCustomBinding(definition, target); - - this.registerDefinition(identifier, definition); - } - - registerCustomBinding(objectDefinition: ObjectDefinition, target: any) { - // @async, @init, @destroy @scope - const objDefOptions: ObjectDefinitionOptions = getObjectDefProps(target); - - this.convertOptionsToDefinition(objDefOptions, objectDefinition); - } - - private convertOptionsToDefinition( - options: ObjectDefinitionOptions, - definition: ObjectDefinition - ): ObjectDefinition { - if (options) { - if (options.isAsync) { - this.debugLogger(' register isAsync = true'); - definition.asynchronous = true; - } - - if (options.initMethod) { - this.debugLogger(` register initMethod = ${options.initMethod}`); - definition.initMethod = options.initMethod; - } - - if (options.destroyMethod) { - this.debugLogger(` register destroyMethod = ${options.destroyMethod}`); - definition.destroyMethod = options.destroyMethod; - } - - if (options.scope) { - this.debugLogger(` register scope = ${options.scope}`); - definition.scope = options.scope; - } - - if (options.constructorArgs) { - this.debugLogger( - ` register constructorArgs = ${options.constructorArgs}` - ); - definition.constructorArgs = options.constructorArgs; - } - - if (options.isAutowire === false) { - this.debugLogger(` register autowire = ${options.isAutowire}`); - definition.autowire = false; - } else if (options.isAutowire === true) { - this.debugLogger(` register autowire = ${options.isAutowire}`); - definition.autowire = true; - } - } - - return definition; - } - - createChild(baseDir?: string): IContainer { - return new Container(baseDir || this.baseDir, this); - } - - resolve(target: T): T { - const tempContainer = new Container(); - tempContainer.bind(target); - tempContainer.parent = this; - return tempContainer.get(target); - } - - get(identifier: any, args?: any): T { - if (typeof identifier !== 'string') { - identifier = this.getIdentifier(identifier); - } - return super.get(identifier, args); - } - - async getAsync(identifier: any, args?: any): Promise { - if (typeof identifier !== 'string') { - identifier = this.getIdentifier(identifier); - } - return super.getAsync(identifier, args); - } - - protected getIdentifier(target: any) { - return getProviderId(target); - } -} diff --git a/packages/core/src/context/midwayContainer.ts b/packages/core/src/context/midwayContainer.ts index dd2ce2ab4da3..1dd750f06926 100644 --- a/packages/core/src/context/midwayContainer.ts +++ b/packages/core/src/context/midwayContainer.ts @@ -1,7 +1,6 @@ import { AspectMetadata, CONFIGURATION_KEY, - getObjectDefinition, getProviderId, IMethodAspect, isProvide, @@ -18,10 +17,13 @@ import { ALL, isAsyncFunction, isClass, - isFunction + isFunction, + getConstructorInject, + TAGGED_PROP, + getObjectDefProps, } from '@midwayjs/decorator'; import { ContainerConfiguration } from './configuration'; -import { FUNCTION_INJECT_KEY } from '..'; +import { FUNCTION_INJECT_KEY } from '../common/constants'; import { IApplicationContext, IConfigService, @@ -29,15 +31,21 @@ import { IEnvironmentService, ILifeCycle, IMidwayContainer, + IObjectDefinitionMetadata, REQUEST_CTX_KEY, } from '../interface'; import { MidwayConfigService } from '../service/configService'; import { MidwayEnvironmentService } from '../service/environmentService'; -import { Container } from './container'; import { pipelineFactory } from '../features/pipeline'; import { ResolverHandler } from './resolverHandler'; import { run } from '@midwayjs/glob'; import * as pm from 'picomatch'; +import { BaseApplicationContext } from './applicationContext'; +import * as util from 'util'; +import { recursiveGetMetadata } from '../common/reflectTool'; +import { ObjectDefinition } from '../definitions/objectDefinition'; +import { FunctionDefinition } from '../definitions/functionDefinition'; +import { ManagedReference, ManagedValue } from './managed'; const DEFAULT_PATTERN = ['**/**.ts', '**/**.tsx', '**/**.js']; const DEFAULT_IGNORE_PATTERN = [ @@ -50,22 +58,36 @@ const DEFAULT_IGNORE_PATTERN = [ '**/app/extend/**', ]; -export class MidwayContainer extends Container implements IMidwayContainer { - resolverHandler: ResolverHandler; +const globalDebugLogger = util.debuglog('midway:container'); +let containerIdx = 0; + +export class MidwayContainer + extends BaseApplicationContext + implements IMidwayContainer { + public id: string; + private debugLogger = globalDebugLogger; + private definitionMetadataList = []; + protected resolverHandler: ResolverHandler; // 仅仅用于兼容requestContainer的ctx - ctx = {}; - readyBindModules: Map> = new Map(); - configurationMap: Map = new Map(); + private ctx = {}; + private configurationMap: Map = new Map(); // 特殊处理,按照 main 加载 - likeMainConfiguration: IContainerConfiguration[] = []; - configService: IConfigService; - environmentService: IEnvironmentService; + private likeMainConfiguration: IContainerConfiguration[] = []; + public configService: IConfigService; + public environmentService: IEnvironmentService; - constructor( - baseDir: string = process.cwd(), - parent: IApplicationContext = undefined - ) { + /** + * 单个进程中上一次的 applicationContext 的 registry + */ + static parentDefinitionMetadata: Map; + + constructor(baseDir: string = process.cwd(), parent?: IApplicationContext) { super(baseDir, parent); + this.id = '00' + this.createContainerIdx(); + } + + protected createContainerIdx() { + return containerIdx++; } init(): void { @@ -89,11 +111,13 @@ export class MidwayContainer extends Container implements IMidwayContainer { * load directory and traverse file to find bind class * @param opts */ - load(opts: { - loadDir: string | string[]; - pattern?: string | string[]; - ignore?: string | string[]; - }) { + load( + opts: { + loadDir: string | string[]; + pattern?: string | string[]; + ignore?: string | string[]; + } = { loadDir: [] } + ) { // 添加全局白名单 this.midwayIdentifiers.push(PIPELINE_IDENTIFIER); @@ -105,22 +129,50 @@ export class MidwayContainer extends Container implements IMidwayContainer { configuration.load(this.baseDir); // loadDir this.debugLogger('main:load directory'); - this.loadDirectory(opts); + + // auto load cache next time when loadDirectory invoked + let loadDirKey = this.baseDir; + const loadDirs = [].concat(opts.loadDir || []); + MidwayContainer.parentDefinitionMetadata = + MidwayContainer.parentDefinitionMetadata || new Map(); + + if (loadDirs.length > 0) { + loadDirKey = loadDirs.join('-'); + } + + if (MidwayContainer.parentDefinitionMetadata.has(loadDirKey)) { + this.restoreDefinitions( + MidwayContainer.parentDefinitionMetadata.get(loadDirKey) + ); + } else { + this.loadDirectory(opts); + // 保存元信息最新的上下文中,供其他容器复用,减少重复扫描 + MidwayContainer.parentDefinitionMetadata.set( + loadDirKey, + this.getDefinitionMetaList() + ); + } this.debugLogger('main:main configuration register import objects'); this.registerImportObjects(configuration.getImportObjects()); // load configuration for (const [packageName, containerConfiguration] of this.configurationMap) { - // main 的需要 skip 掉 - if (containerConfiguration.namespace === MAIN_MODULE_KEY) { - continue; + // 老版本 configuration 才加载 + if (containerConfiguration.newVersion === false) { + // main 的需要 skip 掉 + if (containerConfiguration.namespace === MAIN_MODULE_KEY) { + continue; + } + this.debugLogger(`main:load configuration from ${packageName}`); + this.loadConfiguration(opts, containerConfiguration); } - this.debugLogger(`main:load configuration from ${packageName}`); - this.loadConfiguration(opts, containerConfiguration); } for (const containerConfiguration of this.likeMainConfiguration) { - this.loadConfiguration(opts, containerConfiguration); + // 老版本 configuration 才加载 + if (containerConfiguration.newVersion === false) { + this.loadConfiguration(opts, containerConfiguration); + } } // register ad base config hook @@ -172,6 +224,172 @@ export class MidwayContainer extends Container implements IMidwayContainer { } } + bind(target: T, options?: ObjectDefinitionOptions): void; + bind( + identifier: ObjectIdentifier, + target: T, + options?: ObjectDefinitionOptions + ): void; + bind( + identifier: ObjectIdentifier, + target: T, + options?: ObjectDefinitionOptions + ): void { + const definitionMeta = {} as IObjectDefinitionMetadata; + this.definitionMetadataList.push(definitionMeta); + + if (isClass(identifier) || isFunction(identifier)) { + options = target; + target = identifier as any; + identifier = this.getIdentifier(target); + options = null; + } + + if (isClass(target)) { + definitionMeta.definitionType = 'object'; + } else { + definitionMeta.definitionType = 'function'; + if (!isAsyncFunction(target)) { + definitionMeta.asynchronous = false; + } + } + + definitionMeta.path = target; + definitionMeta.id = identifier; + definitionMeta.srcPath = options?.srcPath || null; + definitionMeta.namespace = options?.namespace || ''; + definitionMeta.scope = options?.scope || ScopeEnum.Singleton; + definitionMeta.autowire = options?.isAutowire !== false; + + this.debugLogger(` bind id => [${definitionMeta.id}]`); + + // inject constructArgs + const constructorMetaData = getConstructorInject(target); + if (constructorMetaData) { + this.debugLogger(`inject constructor => length = ${target['length']}`); + definitionMeta.constructorArgs = []; + const maxLength = Math.max.apply(null, Object.keys(constructorMetaData)); + for (let i = 0; i < maxLength + 1; i++) { + const propertyMeta = constructorMetaData[i]; + if (propertyMeta) { + definitionMeta.constructorArgs.push({ + type: 'ref', + value: propertyMeta[0].value, + args: propertyMeta[0].args, + }); + } else { + definitionMeta.constructorArgs.push({ + type: 'value', + value: propertyMeta?.[0].value, + }); + } + } + } + + // inject properties + const metaDatas = recursiveGetMetadata(TAGGED_PROP, target); + definitionMeta.properties = []; + for (const metaData of metaDatas) { + this.debugLogger(` inject properties => [${Object.keys(metaData)}]`); + for (const metaKey in metaData) { + for (const propertyMeta of metaData[metaKey]) { + definitionMeta.properties.push({ + metaKey, + args: propertyMeta.args, + value: propertyMeta.value, + }); + } + } + } + + this.convertOptionsToDefinition(options, definitionMeta); + // 对象自定义的annotations可以覆盖默认的属性 + this.registerCustomBinding(definitionMeta, target); + + // 把源信息变成真正的对象定义 + this.restoreDefinition(definitionMeta); + } + + protected restoreDefinition(definitionMeta: IObjectDefinitionMetadata) { + let definition; + if (definitionMeta.definitionType === 'object') { + definition = new ObjectDefinition(); + } else { + definition = new FunctionDefinition(); + if (!definitionMeta.asynchronous) { + definition.asynchronous = false; + } + } + + definition.path = definitionMeta.path; + definition.id = definitionMeta.id; + definition.srcPath = definitionMeta.srcPath; + definition.namespace = definitionMeta.namespace; + + this.debugLogger(` bind id => [${definition.id}]`); + + // inject constructArgs + if ( + definitionMeta.constructorArgs && + definitionMeta.constructorArgs.length + ) { + for (const constructorInfo of definitionMeta.constructorArgs) { + if (constructorInfo.type === 'ref') { + const refManagedIns = new ManagedReference(); + const name = constructorInfo.value; + refManagedIns.args = constructorInfo.args; + if (this.midwayIdentifiers.includes(name)) { + refManagedIns.name = name; + } else { + refManagedIns.name = generateProvideId(name, definition.namespace); + } + definition.constructorArgs.push(refManagedIns); + } else { + // inject empty value + const valueManagedIns = new ManagedValue(); + valueManagedIns.valueType = constructorInfo.type; + valueManagedIns.value = constructorInfo.value; + definition.constructorArgs.push(valueManagedIns); + } + } + } + + // inject properties + for (const propertyMeta of definitionMeta.properties) { + const refManaged = new ManagedReference(); + refManaged.args = propertyMeta.args; + if (this.midwayIdentifiers.includes(propertyMeta.value)) { + refManaged.name = propertyMeta.value; + } else { + refManaged.name = generateProvideId( + propertyMeta.value, + definition.namespace + ); + } + definition.properties.set(propertyMeta.metaKey, refManaged); + } + + definition.asynchronous = definitionMeta.asynchronous; + definition.initMethod = definitionMeta.initMethod; + definition.destroyMethod = definitionMeta.destroyMethod; + definition.scope = definitionMeta.scope; + definition.autowire = definitionMeta.autowire; + + this.registerDefinition(definitionMeta.id, definition); + } + + protected restoreDefinitions(definitionMetadataList) { + if (definitionMetadataList && definitionMetadataList.length) { + for (const definitionMeta of definitionMetadataList) { + this.restoreDefinition(definitionMeta); + } + } + } + + protected getDefinitionMetaList() { + return this.definitionMetadataList; + } + protected bindModule(module, namespace = '', filePath?: string) { if (isClass(module)) { const providerId = isProvide(module) ? getProviderId(module) : null; @@ -207,19 +425,19 @@ export class MidwayContainer extends Container implements IMidwayContainer { } } - createChild() { - return new MidwayContainer(this.baseDir, this); + createChild(baseDir?: string): IMidwayContainer { + return new MidwayContainer(baseDir || this.baseDir, this); } - registerDataHandler(handlerType: string, handler: (handlerKey) => any) { + registerDataHandler(handlerType: string, handler: (...args) => any) { this.resolverHandler.registerHandler(handlerType, handler); } registerCustomBinding(objectDefinition, target) { - super.registerCustomBinding(objectDefinition, target); + // @async, @init, @destroy @scope + const objDefOptions: ObjectDefinitionOptions = getObjectDefProps(target); + this.convertOptionsToDefinition(objDefOptions, objectDefinition); - // Override the default scope to request - const objDefOptions: ObjectDefinitionOptions = getObjectDefinition(target); if (objDefOptions && !objDefOptions.scope) { this.debugLogger(' @scope => request'); objectDefinition.scope = ScopeEnum.Request; @@ -374,15 +592,37 @@ export class MidwayContainer extends Container implements IMidwayContainer { } } + resolve(target: T): T { + const tempContainer = new MidwayContainer(); + tempContainer.bind(target); + tempContainer.parent = this; + return tempContainer.get(target); + } + + get(identifier: any, args?: any): T { + if (typeof identifier !== 'string') { + identifier = this.getIdentifier(identifier); + } + return super.get(identifier, args); + } + + async getAsync(identifier: any, args?: any): Promise { + if (typeof identifier !== 'string') { + identifier = this.getIdentifier(identifier); + } + return super.getAsync(identifier, args); + } + + protected getIdentifier(target: any) { + return getProviderId(target); + } + async ready() { await super.ready(); if (this.configService) { // 加载配置 await this.configService.load(); } - - // 增加 lifecycle 支持 - await this.loadAndReadyLifeCycles(); } async stop(): Promise { @@ -451,35 +691,49 @@ export class MidwayContainer extends Container implements IMidwayContainer { ); } - private async loadAndReadyLifeCycles() { - const cycles: Array<{ target: any; namespace: string }> = listModule( - CONFIGURATION_KEY - ); - this.debugLogger('load lifecycle length => %s.', cycles && cycles.length); - for (const cycle of cycles) { - const providerId = getProviderId(cycle.target); - this.debugLogger('ready lifecycle id => %s.', providerId); - const inst = await this.getAsync(providerId); - if (typeof inst.onReady === 'function') { - /** - * 让组件能正确获取到 bind 之后 registerObject 的对象有三个方法 - * 1、在 load 之后修改 bind,不太可行 - * 2、每次 getAsync 的时候,去掉 namespace,同时还要查找当前全局的变量,性能差 - * 3、一般只会在 onReady 的地方执行 registerObject(否则没有全局的意义),这个取巧的办法就是 onReady 传入一个代理,其中绑定当前的 namespace - */ - await inst.onReady( - new Proxy(this, { - get: function (target, prop, receiver) { - if (prop === 'getCurrentNamespace' && cycle.namespace) { - return () => { - return cycle.namespace; - }; - } - return Reflect.get(target, prop, receiver); - }, - }) + private convertOptionsToDefinition( + options: ObjectDefinitionOptions, + definition: IObjectDefinitionMetadata + ) { + if (options) { + if (options.isAsync) { + this.debugLogger(' register isAsync = true'); + definition.asynchronous = true; + } + + if (options.initMethod) { + this.debugLogger(` register initMethod = ${options.initMethod}`); + definition.initMethod = options.initMethod; + } + + if (options.destroyMethod) { + this.debugLogger(` register destroyMethod = ${options.destroyMethod}`); + definition.destroyMethod = options.destroyMethod; + } + + if (options.scope) { + this.debugLogger(` register scope = ${options.scope}`); + definition.scope = options.scope; + } + + if (options.constructorArgs) { + this.debugLogger( + ` register constructorArgs = ${options.constructorArgs}` ); + definition.constructorArgs = options.constructorArgs; + } + + if (options.isAutowire === false) { + this.debugLogger(` register autowire = ${options.isAutowire}`); + definition.autowire = false; + } else if (options.isAutowire === true) { + this.debugLogger(` register autowire = ${options.isAutowire}`); + definition.autowire = true; } } } + + public getResolverHandler() { + return this.resolverHandler; + } } diff --git a/packages/core/src/context/requestContainer.ts b/packages/core/src/context/requestContainer.ts index 5193a10711a4..38744d11c5c2 100644 --- a/packages/core/src/context/requestContainer.ts +++ b/packages/core/src/context/requestContainer.ts @@ -1,14 +1,13 @@ import { MidwayContainer } from './midwayContainer'; -import { REQUEST_CTX_KEY } from '../interface'; -import { parsePrefix } from '../common/util'; +import { REQUEST_CTX_KEY, IMidwayContainer } from '../interface'; +import { parsePrefix } from '../util/'; import { PIPELINE_IDENTIFIER } from '@midwayjs/decorator'; export class MidwayRequestContainer extends MidwayContainer { - private applicationContext: MidwayContainer; + private applicationContext: IMidwayContainer; - constructor(ctx, applicationContext) { - super(); - this.parent = applicationContext; + constructor(ctx, applicationContext: IMidwayContainer) { + super(null, applicationContext); this.applicationContext = applicationContext; // register ctx this.registerObject(REQUEST_CTX_KEY, ctx); @@ -18,7 +17,7 @@ export class MidwayRequestContainer extends MidwayContainer { this.registerObject('logger', ctx.logger); } - const resolverHandler = this.applicationContext.resolverHandler; + const resolverHandler = this.applicationContext.getResolverHandler(); this.beforeEachCreated( resolverHandler.beforeEachCreated.bind(resolverHandler) ); @@ -26,6 +25,10 @@ export class MidwayRequestContainer extends MidwayContainer { resolverHandler.afterEachCreated.bind(resolverHandler) ); } + protected createContainerIdx() { + // requestContainer id = -1; + return -1; + } init() { // do nothing @@ -97,10 +100,10 @@ export class MidwayRequestContainer extends MidwayContainer { } get configService() { - return this.applicationContext.configService; + return this.applicationContext.getConfigService(); } get environmentService() { - return this.applicationContext.environmentService; + return this.applicationContext.getEnvironmentService(); } } diff --git a/packages/core/src/context/resolverHandler.ts b/packages/core/src/context/resolverHandler.ts index 73a2c528d357..90606400c1d0 100644 --- a/packages/core/src/context/resolverHandler.ts +++ b/packages/core/src/context/resolverHandler.ts @@ -2,6 +2,7 @@ import { CLASS_KEY_CONSTRUCTOR, getClassMetadata } from '@midwayjs/decorator'; import { ManagedResolverFactory } from './managedResolverFactory'; import { MidwayContainer } from './midwayContainer'; import * as util from 'util'; +import { HandlerFunction, IResolverHandler } from '../interface'; interface FrameworkDecoratorMetadata { key: string; @@ -10,9 +11,7 @@ interface FrameworkDecoratorMetadata { const debug = util.debuglog('midway:container'); -export type HandlerFunction = (handlerKey: string, instance?: any) => any; - -export class ResolverHandler { +export class ResolverHandler implements IResolverHandler { private handlerMap: Map; private resolverFactory: ManagedResolverFactory; diff --git a/packages/core/src/definitions/objectCreator.ts b/packages/core/src/definitions/objectCreator.ts index 00172d4cda6e..a87f00480a84 100644 --- a/packages/core/src/definitions/objectCreator.ts +++ b/packages/core/src/definitions/objectCreator.ts @@ -1,4 +1,8 @@ -import { isAsyncFunction, isGeneratorFunction, isPromise } from '@midwayjs/decorator'; +import { + isAsyncFunction, + isGeneratorFunction, + isPromise, +} from '@midwayjs/decorator'; import { IObjectCreator, IObjectDefinition } from '../interface'; export class ObjectCreator implements IObjectCreator { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f852907c0c00..cc81be67106e 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -39,7 +39,7 @@ export { MidwayRequestContainer } from './context/requestContainer'; export { BaseFramework } from './baseFramework'; export * from './context/providerWrapper'; export * from './common/constants'; -export { safelyGet, safeRequire, parsePrefix } from './common/util'; +export { safelyGet, safeRequire } from './util/'; export * from './features'; export * from './util/webRouterParam'; export { plainToClass, classToPlain } from 'class-transformer'; diff --git a/packages/core/src/interface.ts b/packages/core/src/interface.ts index 3cc7ed7038a9..51bd0126e46c 100644 --- a/packages/core/src/interface.ts +++ b/packages/core/src/interface.ts @@ -64,6 +64,30 @@ export interface IObjectDefinition { hasAttr(key: ObjectIdentifier): boolean; setAttr(key: ObjectIdentifier, value: any): void; } + +/** + * 对象描述元数据,用于生成对象定义 + */ +export interface IObjectDefinitionMetadata { + namespace?: string; + id: string; + name: string; + initMethod: string; + destroyMethod: string; + constructMethod: string; + scope: ScopeEnum; + autowire: boolean; + srcPath: string; + path: any; + export: string; + dependsOn: ObjectIdentifier[]; + constructorArgs: Array<{ value?: string; args?: any; type: string; } | undefined>; + asynchronous: boolean; + properties: any[]; + definitionType: 'object' | 'function' +} + + export interface IObjectCreator { load(): any; doConstruct(Clzz: any, args?: any, context?: IApplicationContext): any; @@ -165,20 +189,6 @@ export interface IManagedResolverFactoryCreateOptions { args?: any; namespace?: string; } -/** - * 提供简化的容器绑定能力 - */ -export interface IContainer extends IApplicationContext { - bind(target: T, options?: ObjectDefinitionOptions): void; - bind( - identifier: ObjectIdentifier, - target: T, - options?: ObjectDefinitionOptions - ): void; - createChild(): IContainer; - resolve(target: T): T; - registerCustomBinding(objectDefinition: IObjectDefinition, target): void; -} export interface ObjectDependencyTree { scope: ScopeEnum; @@ -193,6 +203,7 @@ export const REQUEST_OBJ_CTX_KEY = '_req_ctx'; export interface IContainerConfiguration { namespace: string; packageName: string; + newVersion: boolean; addLoadDir(dir: string); addImports(imports: string[], baseDir?: string); addImportObjects(importObjects: any[]); @@ -209,7 +220,32 @@ export interface IContainerConfiguration { bindConfigurationClass(clzz: any, filePath?: string); } -export interface IMidwayContainer extends IContainer { + +export type HandlerFunction = (handlerKey: string, instance?: any) => any; + +export interface IResolverHandler { + beforeEachCreated(target, constructorArgs: any[], context); + afterEachCreated(instance, context, definition); + registerHandler(key: string, fn: HandlerFunction); + getHandler(key: string); +} + +export interface IMidwayContainer extends IApplicationContext { + load(opts: { + loadDir: string | string[]; + pattern?: string | string[]; + ignore?: string | string[]; + }); + bind(target: T, options?: ObjectDefinitionOptions): void; + bind( + identifier: ObjectIdentifier, + target: T, + options?: ObjectDefinitionOptions + ): void; + bindClass(exports, namespace?: string, filePath?: string); + registerDataHandler(handlerType: string, handler: (...args) => any); + createChild(): IMidwayContainer; + resolve(target: T): T; /** * 默认不添加创建的 configuration 到 configurations 数组中 */ @@ -219,6 +255,7 @@ export interface IMidwayContainer extends IContainer { getConfigService(): IConfigService; getEnvironmentService(): IEnvironmentService; getCurrentEnv(): string; + getResolverHandler(): IResolverHandler; addAspect( aspectIns: IMethodAspect, aspectData: AspectMetadata @@ -256,7 +293,6 @@ export interface IMidwayLogger { } export interface IMidwayApplication { - applicationContext: IMidwayContainer; getBaseDir(): string; getAppDir(): string; getEnv(): string; @@ -268,7 +304,7 @@ export interface IMidwayApplication { } export interface IMidwayContext { - getRequestContext(): IMidwayContainer; + getRequestContext?(): IMidwayContainer; requestContext: IMidwayContainer; } @@ -278,15 +314,17 @@ export interface IMidwayContext { export interface IMidwayCoreApplication extends IMidwayApplication {} export interface IMidwayBootstrapOptions { - logger: IMidwayLogger; + logger?: IMidwayLogger; baseDir: string; - appDir: string; - preloadModules: string[]; - disableAutoLoad: boolean; - pattern: string[]; - ignore: string[]; - isTsMode: boolean; - middleware: string[]; + appDir?: string; + preloadModules?: any[]; + disableAutoLoad?: boolean; + pattern?: string[]; + ignore?: string[]; + isTsMode?: boolean; + middleware?: string[]; + loadDir?: string[]; + disableConflictCheck?: boolean; } export interface IConfigurationOptions {} diff --git a/packages/core/src/loader.ts b/packages/core/src/loader.ts index b4136aa04ac3..04a3a86185da 100644 --- a/packages/core/src/loader.ts +++ b/packages/core/src/loader.ts @@ -1,14 +1,13 @@ import * as path from 'path'; import { MidwayContainer } from './context/midwayContainer'; -import { Container } from './context/container'; import { - ASPECT_KEY, - getClassMetadata, + ASPECT_KEY, CONFIGURATION_KEY, + getClassMetadata, getProviderId, IMethodAspect, listModule, listPreloadModule, } from '@midwayjs/decorator'; -import { IMidwayContainer } from './interface'; +import { ILifeCycle, IMidwayContainer } from './interface'; function buildLoadDir(baseDir, dir) { if (!path.isAbsolute(dir)) { @@ -17,9 +16,11 @@ function buildLoadDir(baseDir, dir) { return dir; } +/** + * @deprecated + */ export class ContainerLoader { baseDir; - pluginContext; applicationContext: MidwayContainer; isTsMode; preloadModules; @@ -38,7 +39,6 @@ export class ContainerLoader { } initialize() { - this.pluginContext = new Container(this.baseDir); this.applicationContext = new MidwayContainer(this.baseDir, undefined); this.applicationContext.disableConflictCheck = this.disableConflictCheck; this.applicationContext.registerObject('baseDir', this.baseDir); @@ -49,13 +49,6 @@ export class ContainerLoader { return this.applicationContext; } - /** - * @Deprecated - */ - getPluginContext() { - return this.pluginContext; - } - registerHook(hookKey, hookHandler) { this.applicationContext.registerDataHandler(hookKey, hookHandler); } @@ -74,19 +67,18 @@ export class ContainerLoader { loadOpts.disableAutoLoad = true; } - // if not disable auto load - if (!loadOpts.disableAutoLoad) { - // use baseDir in parameter first - const baseDir = loadOpts.baseDir || this.baseDir; - const defaultLoadDir = this.isTsMode ? [baseDir] : []; - this.applicationContext.load({ - loadDir: (loadOpts.loadDir || defaultLoadDir).map(dir => { - return buildLoadDir(baseDir, dir); - }), - pattern: loadOpts.pattern, - ignore: loadOpts.ignore, - }); - } + if (loadOpts.disableAutoLoad) return; + + // use baseDir in parameter first + const baseDir = loadOpts.baseDir || this.baseDir; + const defaultLoadDir = this.isTsMode ? [baseDir] : []; + this.applicationContext.load({ + loadDir: (loadOpts.loadDir || defaultLoadDir).map(dir => { + return buildLoadDir(baseDir, dir); + }), + pattern: loadOpts.pattern, + ignore: loadOpts.ignore, + }); if (this.preloadModules && this.preloadModules.length) { for (const preloadModule of this.preloadModules) { @@ -96,8 +88,8 @@ export class ContainerLoader { } async refresh() { - await this.pluginContext.ready(); await this.applicationContext.ready(); + await this.loadLifeCycles(); // some common decorator implementation const modules = listPreloadModule(); @@ -134,7 +126,39 @@ export class ContainerLoader { } async stop() { - await this.pluginContext.stop(); await this.applicationContext.stop(); } + + async loadLifeCycles() { + // agent 不加载生命周期 + const cycles: Array<{ target: any; namespace: string }> = listModule( + CONFIGURATION_KEY + ); + for (const cycle of cycles) { + const providerId = getProviderId(cycle.target); + const inst = await this.getApplicationContext().getAsync( + providerId + ); + if (typeof inst.onReady === 'function') { + /** + * 让组件能正确获取到 bind 之后 registerObject 的对象有三个方法 + * 1、在 load 之后修改 bind,不太可行 + * 2、每次 getAsync 的时候,去掉 namespace,同时还要查找当前全局的变量,性能差 + * 3、一般只会在 onReady 的地方执行 registerObject(否则没有全局的意义),这个取巧的办法就是 onReady 传入一个代理,其中绑定当前的 namespace + */ + await inst.onReady( + new Proxy(this.getApplicationContext(), { + get: function (target, prop, receiver) { + if (prop === 'getCurrentNamespace' && cycle.namespace) { + return () => { + return cycle.namespace; + }; + } + return Reflect.get(target, prop, receiver); + }, + }) + ); + } + } + } } diff --git a/packages/core/src/service/configService.ts b/packages/core/src/service/configService.ts index b46ca7b994bd..26986653b010 100644 --- a/packages/core/src/service/configService.ts +++ b/packages/core/src/service/configService.ts @@ -1,7 +1,7 @@ import * as extend from 'extend2'; import { basename, join } from 'path'; import { IConfigService, IMidwayContainer } from '../interface'; -import { safelyGet } from '..'; +import { safelyGet } from '../util'; import { readdirSync, statSync } from 'fs'; import { isFunction } from '@midwayjs/decorator'; import * as util from 'util'; diff --git a/packages/core/src/util/index.ts b/packages/core/src/util/index.ts index e368b9a94b20..08f12e51072a 100644 --- a/packages/core/src/util/index.ts +++ b/packages/core/src/util/index.ts @@ -1,3 +1,5 @@ +import { dirname, resolve, sep, extname } from 'path'; + /** * get all method names from obj or it's prototype * @param obj @@ -30,3 +32,72 @@ export function getPrototypeNames(obj) { // leave out those methods on Object's prototype return result.filter(k => ownKeysOnObjectPrototype.indexOf(k) === -1); } + +export const safeRequire = p => { + if (p.startsWith(`.${sep}`) || p.startsWith(`..${sep}`)) { + p = resolve(dirname(module.parent.filename), p); + } + try { + return require(p); + } catch (err) { + return undefined; + } +}; + +export const isPath = (p): boolean => { + // eslint-disable-next-line no-useless-escape + if (/(^[\.\/])|:|\\/.test(p)) { + return true; + } + return false; +}; + +/** + * safelyGet(['a','b'],{a: {b: 2}}) // => 2 + * safelyGet(['a','b'],{c: {b: 2}}) // => undefined + * safelyGet(['a','1'],{a: {"1": 2}}) // => 2 + * safelyGet(['a','1'],{a: {b: 2}}) // => undefined + * safelyGet('a.b',{a: {b: 2}}) // => 2 + * safelyGet('a.b',{c: {b: 2}}) // => undefined + */ +export function safelyGet(list: string | string[], obj?: object): any { + if (arguments.length === 1) { + return (_obj: object) => safelyGet(list, _obj); + } + + if (typeof obj === 'undefined' || typeof obj !== 'object' || obj === null) { + return void 0; + } + const pathArrValue = typeof list === 'string' ? list.split('.') : list; + let willReturn: any = obj; + + for (const key of pathArrValue) { + if (typeof willReturn === 'undefined' || willReturn === null) { + return void 0; + } else if (typeof willReturn !== 'object') { + return void 0; + } + willReturn = willReturn[key]; + } + + return willReturn; +} + +/** + * 剔除 @ 符号 + * @param provideId provideId + */ +export function parsePrefix(provideId: string) { + if (provideId.includes('@')) { + return provideId.substr(1); + } + return provideId; +} + +export function isPathEqual(one: string, two: string) { + if (!one || !two) { + return false; + } + const ext = extname(one); + return one.replace(ext, '') === two; +} diff --git a/packages/core/src/util/staticConfig.ts b/packages/core/src/util/staticConfig.ts index 94b2b27ebbb4..714718e98f1f 100644 --- a/packages/core/src/util/staticConfig.ts +++ b/packages/core/src/util/staticConfig.ts @@ -4,7 +4,7 @@ import { getClassMetadata, InjectionConfigurationOptions, isClass, - isFunction + isFunction, } from '@midwayjs/decorator'; import { IConfigService, safeRequire } from '..'; import { MidwayConfigService } from '../service/configService'; diff --git a/packages/core/test/baseFramework.test.ts b/packages/core/test/baseFramework.test.ts new file mode 100644 index 000000000000..af4e3b536ddf --- /dev/null +++ b/packages/core/test/baseFramework.test.ts @@ -0,0 +1,558 @@ +import { + APPLICATION_KEY, + CONFIGURATION_KEY, + LIFECYCLE_IDENTIFIER_PREFIX, + Provide, + resetModule, +} from '@midwayjs/decorator'; +import * as assert from 'assert'; +import * as path from 'path'; +import { + BaseFramework, + clearAllModule, + MidwayContainer, +} from '../src'; +import * as mm from 'mm'; +import sinon = require('sinon'); +import { IMidwayApplication, IMidwayBootstrapOptions, MidwayFrameworkType } from '../src/interface'; +import { LifeCycleTest, LifeCycleTest1, TestBinding } from "./fixtures/lifecycle"; + +type mockApp = {} & IMidwayApplication; +type mockAppOptions = {}; + +class MockFramework extends BaseFramework { + + getApplication(): mockApp { + return this.app; + } + + getFrameworkType(): MidwayFrameworkType { + return MidwayFrameworkType.CUSTOM; + } + + async run(): Promise { + + } + + async applicationInitialize(options: IMidwayBootstrapOptions) { + this.app = {} as IMidwayApplication; + } + +} + +@Provide() +class TestModule { + test() { + return 'hello'; + } +} + +describe('/test/baseFramework.test.ts', () => { + beforeEach(() => { + clearAllModule(); + MidwayContainer.parentDefinitionMetadata = null; + }); + + it.skip('should load js directory and auto disable', async () => { + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join(__dirname, './fixtures/js-app-loader'), + isTsMode: false, + }); + + const appCtx = framework.getApplicationContext(); + try { + await appCtx.getAsync('app'); + } catch (err) { + assert(err); + } + }); + + it('should load preload module', async () => { + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join(__dirname, './fixtures/base-app/src'), + preloadModules: [TestModule], + }); + + const appCtx = framework.getApplicationContext(); + const module: any = await appCtx.getAsync('testModule'); + assert(module.test() === 'hello'); + }); + + it('should load configuration', async () => { + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration/base-app-decorator/src' + ) + }); + + framework.getApplicationContext().registerDataHandler(APPLICATION_KEY, () => ({ + getBaseDir() { + return 'base dir'; + } + })); + + const appCtx = framework.getApplicationContext(); + + const baseService: any = await appCtx.getAsync('baseService'); + assert((await baseService.getInformation()) === 'harry,one article'); + assert.strictEqual(baseService.getAaa(), 123); + assert.strictEqual(baseService.getCcc(), 'mock'); + }); + + it('should load config.*.ts by default env', async () => { + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration/base-app-decorator/src' + ) + }); + + const appCtx = framework.getApplicationContext(); + + const replaceManager: any = await appCtx.getAsync('@ok:replaceManager'); + expect(await replaceManager.getOne()).toEqual('ok'); + }); + + it('should load config.*.ts by process.env', async () => { + mm(process.env, 'NODE_ENV', 'local'); + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration/base-app-decorator/src' + ), + }); + const appCtx = framework.getApplicationContext(); + const replaceManager: any = await appCtx.getAsync('@ok:replaceManager'); + assert((await replaceManager.getOne()) === 'ok1'); + mm.restore(); + }); + + it('should load config.*.ts by process.env MIDWAY_SERVER_ENV', async () => { + const callback = sinon.spy(); + mm(process.env, 'MIDWAY_SERVER_ENV', 'local'); + mm(console, 'log', m => { + callback(m); + }); + + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration/base-app-decorator/src' + ), + }); + + const appCtx = framework.getApplicationContext(); + const replaceManager: any = await appCtx.getAsync('@ok:replaceManager'); + assert((await replaceManager.getOne()) === 'ok1'); + assert.ok( + callback.withArgs('------auto configuration ready now').calledOnce + ); + mm.restore(); + }); + + it('should load with no package.json', async () => { + mm(process.env, 'MIDWAY_SERVER_ENV', 'local'); + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration/base-app-no-package-json/src' + ), + }); + + const appCtx = framework.getApplicationContext(); + const replaceManager: any = await appCtx.getAsync('@ok:replaceManager'); + assert((await replaceManager.getOne()) === 'oktwo'); + const replaceManagerno: any = await appCtx.getAsync( + '@midway-plugin-no-pkg-json:replaceManager' + ); + assert((await replaceManagerno.getOne()) === 'oktwo'); + + const replaceManagerTwo: any = await appCtx.getAsync('@ok:replaceManagerTwo'); + assert((await replaceManagerTwo.getOne()) === 'oktwo'); + mm.restore(); + }); + + it('should load configuration with namespace', async () => { + mm(process.env, 'MIDWAY_SERVER_ENV', 'local'); + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration-namespace/base-app-decorator/src' + ), + }); + + const appCtx = framework.getApplicationContext(); + // 取默认 namespace + const replaceManager1: any = await appCtx.getAsync( + '@midway-plugin-mock:replaceManager' + ); + assert((await replaceManager1.getOne()) === 'one article'); + // 取自定义 namespace + const replaceManager2: any = await appCtx.getAsync('@ok:replaceManager'); + assert((await replaceManager2.getOne()) === 'ok3'); + // 查看覆盖的情况 + const baseService: any = await appCtx.getAsync('baseService'); + expect(await baseService.getInformation()).toEqual('harryone article atmod,one article,ok3'); + + assert(baseService.helloworld === 234); + + assert(baseService.articleManager1); + assert((await baseService.articleManager1.getOne()) === 'ok3empty'); + + assert(baseService.articleManager2); + assert((await baseService.articleManager2.getOne()) === 'ok3emptytwo'); + + const userManager: any = await appCtx.getAsync('userManager'); + assert((await userManager.getUser()) === 'harryone article atmod'); + assert((await userManager.getTest()) === 'testone article atmod bt'); + + const repm: any = await appCtx.getAsync( + '@midway-plugin-mod:replaceManager' + ); + assert((await repm.getOne()) === 'one article mod'); + mm.restore(); + }); + + + it('should load configuration with object', async () => { + mm(process.env, 'MIDWAY_SERVER_ENV', 'local'); + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration-object/base-app-decorator/src' + ), + }); + + const appCtx = framework.getApplicationContext(); + // 取默认 namespace + const replaceManager1: any = await appCtx.getAsync( + 'replaceManager' + ); + expect(await replaceManager1.getOne()).toEqual('one article'); + // 取自定义 namespace + const replaceManager2: any = await appCtx.getAsync('@ok:replaceManager'); + expect(await replaceManager2.getOne()).toEqual('ok2'); + mm.restore(); + }); + + // it('should load conflict with error', async () => { + // const framework = new MockFramework(); + // await framework.initialize(); + // container.load({ + // loadDir: path.join( + // __dirname, + // './fixtures/app-with-conflict/base-app-decorator/src' + // ), + // disableConflictCheck: false, + // }); + // loader.initialize(); + // const callback = sinon.spy(); + // try { + // loader.loadDirectory(); + // + // } catch (e) { + // callback(e.message); + // } + // const p = path.resolve( + // __dirname, + // './fixtures/app-with-conflict/base-app-decorator/src/lib/' + // ); + // const s = `baseService path = ${p}/userManager.ts is exist (${p}/service.ts)!`; + // assert.ok(callback.withArgs(s).calledOnce); + // }); + + it('should load conflict without error', async () => { + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-conflict/base-app-decorator/src' + ), + }); + const appCtx = framework.getApplicationContext(); + const baseService: any = await appCtx.getAsync('baseService'); + assert.ok((await baseService.getInformation()) === 'this is conflict'); + }); + + describe('test load different env', () => { + afterEach(mm.restore); + + it('load default env', async () => { + mm(process.env, 'NODE_ENV', ''); + mm(process.env, 'MIDWAY_SERVER_ENV', ''); + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration-config/src' + ), + }); + + const applicationContext = framework.getApplicationContext(); + const value = applicationContext.getConfigService().getConfiguration(); + assert(value['env'] === 'prod'); + assert(value['bbb'] === '111'); + }); + + it('load prod env', async () => { + mm(process.env, 'NODE_ENV', 'prod'); + mm(process.env, 'MIDWAY_SERVER_ENV', ''); + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration-config/src' + ), + }); + + const applicationContext = framework.getApplicationContext(); + const value = applicationContext + .getConfigService() + .getConfiguration('env'); + assert(value === 'prod'); + }); + + it('load daily env', async () => { + mm(process.env, 'NODE_ENV', 'daily'); + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration-config/src' + ), + }); + const applicationContext = framework.getApplicationContext(); + + const value = applicationContext + .getConfigService() + .getConfiguration('env'); + assert(value === 'daily'); + }); + + it('load pre env', async () => { + mm(process.env, 'NODE_ENV', 'pre'); + mm(process.env, 'MIDWAY_SERVER_ENV', ''); + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration-config/src' + ), + }); + + const applicationContext = framework.getApplicationContext(); + + const value = applicationContext + .getConfigService() + .getConfiguration('env'); + assert(value === 'pre'); + }); + + it('load local env', async () => { + mm(process.env, 'NODE_ENV', 'local'); + mm(process.env, 'MIDWAY_SERVER_ENV', ''); + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration-config/src' + ), + }); + + const applicationContext = framework.getApplicationContext(); + + const value = applicationContext + .getConfigService() + .getConfiguration('env'); + assert(value === 'local'); + }); + }); + + describe('test load different env by load directory', () => { + afterEach(mm.restore); + + it('load default env', async () => { + mm(process.env, 'NODE_ENV', ''); + mm(process.env, 'MIDWAY_SERVER_ENV', ''); + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration-config-dir/src' + ), + }); + + const applicationContext = framework.getApplicationContext(); + + const value = applicationContext.getConfigService().getConfiguration(); + assert(value['env'] === 'prod'); + assert(value['bbb'] === '222'); + + const configManager = await applicationContext.getAsync<{ + allConfig: any; + bbbConfig: any; + }>('configManager'); + assert(configManager.allConfig['env'] === 'prod'); + assert(configManager.allConfig['bbb'] === '222'); + assert(configManager.bbbConfig === '222'); + }); + + it('load prod env', async () => { + mm(process.env, 'NODE_ENV', 'prod'); + mm(process.env, 'MIDWAY_SERVER_ENV', ''); + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration-config-dir/src' + ), + }); + + const applicationContext = framework.getApplicationContext(); + + const value = applicationContext + .getConfigService() + .getConfiguration('env'); + assert(value === 'prod'); + }); + + it('load daily env', async () => { + mm(process.env, 'NODE_ENV', 'daily'); + mm(process.env, 'MIDWAY_SERVER_ENV', ''); + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration-config-dir/src' + ), + }); + + const applicationContext = framework.getApplicationContext(); + + const value = applicationContext + .getConfigService() + .getConfiguration('env'); + assert(value === 'daily'); + }); + + it('load pre env', async () => { + mm(process.env, 'NODE_ENV', 'pre'); + mm(process.env, 'MIDWAY_SERVER_ENV', ''); + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration-config-dir/src' + ), + }); + + const applicationContext = framework.getApplicationContext(); + + const value = applicationContext + .getConfigService() + .getConfiguration('env'); + assert(value === 'pre'); + }); + + }); + + it('should test aspect decorator', async () => { + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/base-app-aspect/src' + ) + }); + + const home: any = await framework.getApplicationContext().getAsync('home'); + expect(home.hello()).toEqual('hello worlddddfff'); + expect(await home.hello1()).toEqual('hello world 1'); + expect(await home.hello2()).toEqual('hello worldcccppp'); + }); + + it('should inject global value in component', async () => { + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration-global-inject/base-app-decorator/src' + ) + }); + + const home: any = await framework.getApplicationContext().getAsync('SQL:home'); + expect(await home.getData()).toMatch(/base-app-decorator\/src\/bbbb\/dddd/); + }); + + it('should load component in different type and different env', async () => { + mm(process.env, 'NODE_ENV', ''); + mm(process.env, 'MIDWAY_SERVER_ENV', ''); + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/app-with-configuration-load/src' + ), + }); + + const applicationContext = framework.getApplicationContext(); + + const value = applicationContext.getConfigService().getConfiguration(); + expect(value['a']).toEqual(1); + mm.restore(); + }); + + it('lifecycle should be ok', async () => { + const framework = new MockFramework(); + await framework.initialize({ + baseDir: path.join( + __dirname, + './fixtures/base-app/src' + ), + }); + + const container = framework.getApplicationContext(); + container.registerDataHandler(APPLICATION_KEY, () => { + return { hello: 123 }; + }); + const cfg = container.createConfiguration(); + container.bind(TestBinding); + cfg.bindConfigurationClass(LifeCycleTest); + cfg.bindConfigurationClass(LifeCycleTest1); + + await framework.loadLifeCycles(); + + const aa = await container.getAsync(LIFECYCLE_IDENTIFIER_PREFIX + 'lifeCycleTest'); + expect(aa.ts).toEqual('hello'); + expect(aa.ready).toBeTruthy(); + // container.registerObject('hellotest111', '12312312'); + expect(container.get('hellotest111')).toEqual('12312312'); + + const aa1 = await container.getAsync(LIFECYCLE_IDENTIFIER_PREFIX + 'lifeCycleTest1'); + expect(aa1.tts).toEqual('hello'); + expect(aa1.ready).toBeTruthy(); + + const callback = sinon.spy(); + mm(console, 'log', (m) => { + callback(m); + }); + + expect(container.registry.hasObject(LIFECYCLE_IDENTIFIER_PREFIX + 'lifeCycleTest')).toBeTruthy(); + await container.stop(); + expect(container.registry.hasObject(LIFECYCLE_IDENTIFIER_PREFIX + 'lifeCycleTest')).toBeFalsy(); + expect(callback.withArgs('on stop').calledOnce).toBeTruthy(); + + resetModule(CONFIGURATION_KEY); + mm.restore(); + }); +}); diff --git a/packages/core/test/context/container.test.ts b/packages/core/test/context/container.test.ts index 2aba06d9b5dd..a3eaa7df41d4 100644 --- a/packages/core/test/context/container.test.ts +++ b/packages/core/test/context/container.test.ts @@ -1,4 +1,4 @@ -import { Container } from '../../src/context/container'; +import { MidwayContainer as Container } from '../../src/context/midwayContainer'; import { expect } from 'chai'; import { Grandson, @@ -28,7 +28,6 @@ import { childAsyncFunction, import { DieselCar, DieselEngine, engineFactory, PetrolEngine } from '../fixtures/mix_sample'; import { HelloSingleton, HelloErrorInitSingleton, HelloErrorSingleton } from '../fixtures/singleton_sample'; import { CircularOne, CircularTwo, CircularThree, TestOne, TestTwo, TestThree, TestOne1, TestTwo1, TestThree1 } from '../fixtures/circular_dependency'; -import { ManagedValue } from '../../src/context/managed'; import { VALUE_TYPE } from '../../src'; describe('/test/context/container.test.ts', () => { @@ -59,10 +58,12 @@ describe('/test/context/container.test.ts', () => { it('should inject property', () => { const container = new Container(); - const va = new ManagedValue(); - va.value = 123; - va.valueType = VALUE_TYPE.INTEGER; - container.bind('warrior', Samurai as any, { constructorArgs: [ va ]}); + container.bind('warrior', Samurai as any, { + constructorArgs: [{ + value: 123, + type: VALUE_TYPE.INTEGER + }] + }); container.bind('katana1', Katana as any); container.bind('katana2', Katana as any); diff --git a/packages/core/test/context/midwayContainer.test.ts b/packages/core/test/context/midwayContainer.test.ts index 3c203f2d3f5a..f221e5d45e8e 100644 --- a/packages/core/test/context/midwayContainer.test.ts +++ b/packages/core/test/context/midwayContainer.test.ts @@ -1,18 +1,185 @@ import { expect } from 'chai'; import * as path from 'path'; -import { MidwayContainer } from '../../src'; +import { clearAllModule, MidwayContainer, MidwayRequestContainer } from '../../src'; import { App } from '../fixtures/ts-app-inject/app'; import { TestCons } from '../fixtures/ts-app-inject/test'; - -import { TestBinding, LifeCycleTest, LifeCycleTest1 } from '../fixtures/lifecycle'; -import sinon = require('sinon'); -import mm = require('mm'); import * as decs from '@midwayjs/decorator'; -const { LIFECYCLE_IDENTIFIER_PREFIX, APPLICATION_KEY, CONFIGURATION_KEY, resetModule } = decs; +import { CONFIG_KEY, LOGGER_KEY, PLUGIN_KEY } from '@midwayjs/decorator'; +import * as assert from 'assert'; + +const { APPLICATION_KEY } = decs; + +function buildLoadDir(arr, baseDir) { + return arr.map(dir => { + if (!path.isAbsolute(dir)) { + return path.join(baseDir, dir); + } + return dir; + }); +} describe('/test/context/midwayContainer.test.ts', () => { + beforeEach(() => { + clearAllModule(); + MidwayContainer.parentDefinitionMetadata = null; + }); + + it('should create new loader', async () => { + const container = new MidwayContainer(); + container.load({ + loadDir: buildLoadDir(['app', 'lib', '../test_other'], path.join(__dirname, '../fixtures/base-app/src')), + }); + assert.ok(typeof (await container.getAsync('testOther'))); + }); + + it('should load ts file and use config, plugin decorator', async () => { + const container = new MidwayContainer(); + container.load({ + loadDir: path.join(__dirname, '../fixtures/base-app-decorator/src') + }); + // register handler for container + container.registerDataHandler(CONFIG_KEY, (key, target) => { + assert( + target instanceof + require('../fixtures/base-app-decorator/src/lib/service')[ + 'BaseService' + ] + ); + return 'hello'; + }); + + container.registerDataHandler(PLUGIN_KEY, (key, target) => { + return { b: 2 }; + }); + + container.registerDataHandler(LOGGER_KEY, (key, target) => { + return console; + }); + + const appCtx = container; + const baseService: any = await appCtx.getAsync('baseService'); + assert(baseService.config === 'hello'); + assert(baseService.logger === console); + assert(baseService.plugin2.b === 2); + + const context = { logger: console }; + const requestCtx = new MidwayRequestContainer(context, appCtx); + 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); + }); + + it('should load ts file and bindapp success', async () => { + const container = new MidwayContainer(); + container.load({ + loadDir: path.join(__dirname, '../fixtures/base-app-forbindapp/src') + }); + + const tt: any = { + getBaseDir() { + return 'hello this is basedir'; + } + }; + container.registerDataHandler(APPLICATION_KEY, () => tt); + await container.ready(); + // register handler for container + container.registerDataHandler(CONFIG_KEY, (key, target) => { + assert( + target instanceof + require('../fixtures/base-app-forbindapp/src/lib/service')[ + 'BaseService' + ] + ); + return 'hello'; + }); + + container.registerDataHandler(PLUGIN_KEY, (key, target) => { + return { b: 2 }; + }); + + container.registerDataHandler(LOGGER_KEY, (key, target) => { + return console; + }); + + const appCtx = container; + const baseService: any = await appCtx.getAsync('baseService'); + assert(baseService.config === 'hello'); + assert(baseService.logger === console); + assert(baseService.plugin2.b === 2); + assert(baseService.test.getBaseDir() === 'hello this is basedir'); + }); + + it('load ts file support constructor inject', async () => { + const container = new MidwayContainer(); + container.load({ + loadDir: path.join(__dirname, '../fixtures/base-app-constructor/src'), + }); + + await container.ready(); + + // register handler for container + container.registerDataHandler(CONFIG_KEY, key => { + return { c: 60 }; + }); + + container.registerDataHandler(PLUGIN_KEY, key => { + return { text: 2 }; + }); + + container.registerDataHandler(LOGGER_KEY, key => { + return console; + }); + + const context = { logger: console }; + const requestCtx = new MidwayRequestContainer( + context, + container + ); + 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); + }); + + it('should auto load function file and inject by function name', async () => { + const container = new MidwayContainer(); + container.load({ + loadDir: path.join(__dirname, '../fixtures/base-app-function/src'), + }); + + await container.ready(); + + // register handler for container + container.registerDataHandler(CONFIG_KEY, key => { + return { c: 60 }; + }); + + container.registerDataHandler(PLUGIN_KEY, key => { + return { text: 2 }; + }); + + container.registerDataHandler(LOGGER_KEY, key => { + return console; + }); + + const context = { logger: console }; + const requestCtx = new MidwayRequestContainer( + context, + container + ); + const baseServiceCtx = await requestCtx.getAsync('baseService'); + assert(baseServiceCtx.factory('google')); + }); + it('should scan app dir and inject automatic', () => { const container = new MidwayContainer(); container.load({ @@ -41,44 +208,4 @@ describe('/test/context/midwayContainer.test.ts', () => { expect(sapp.getConfig().a).to.equal(3); }); - describe('lifecycle case', () => { - 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); - cfg.bindConfigurationClass(LifeCycleTest1); - - expect(container.isReady).false; - await container.ready(); - expect(container.isReady).true; - - const aa = await container.getAsync(LIFECYCLE_IDENTIFIER_PREFIX + 'lifeCycleTest'); - expect(aa.ts).eq('hello'); - expect(aa.ready).true; - // container.registerObject('hellotest111', '12312312'); - expect(container.get('hellotest111')).eq('12312312'); - - const aa1 = await container.getAsync(LIFECYCLE_IDENTIFIER_PREFIX + 'lifeCycleTest1'); - expect(aa1.tts).eq('hello'); - expect(aa1.ready).true; - - const callback = sinon.spy(); - mm(console, 'log', (m) => { - callback(m); - }); - - expect(container.registry.hasObject(LIFECYCLE_IDENTIFIER_PREFIX + 'lifeCycleTest')).true; - await container.stop(); - 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/core/test/fixtures/pipeline.ts b/packages/core/test/fixtures/pipeline.ts index 1f599c862654..9e29bf4eb8d5 100644 --- a/packages/core/test/fixtures/pipeline.ts +++ b/packages/core/test/fixtures/pipeline.ts @@ -1,6 +1,6 @@ import { IValveHandler, IPipelineContext, IPipelineHandler } from '../../src/features/pipeline'; import { Provide, Inject, Pipeline } from '@midwayjs/decorator'; -import { IContainer } from '../../src/interface'; +import { IMidwayContainer } from '../../src/interface'; class VideoDto { videoId: string; @@ -235,7 +235,7 @@ export class DataMainTest { } } -export default (container: IContainer) => { +export default (container: IMidwayContainer) => { container.bind(TestService); container.bind(VideoFeeds); container.bind(AccountMap); diff --git a/packages/core/test/loader.test.ts b/packages/core/test/loader.test.ts index 9c3393da0285..9d66586f20cc 100644 --- a/packages/core/test/loader.test.ts +++ b/packages/core/test/loader.test.ts @@ -5,6 +5,7 @@ import { ContainerLoader, MidwayRequestContainer, clearAllModule, + MidwayContainer, } from '../src'; import * as mm from 'mm'; import sinon = require('sinon'); @@ -19,6 +20,7 @@ class TestModule { describe('/test/loader.test.ts', () => { beforeEach(() => { clearAllModule(); + MidwayContainer.parentDefinitionMetadata = null; }); it('should create new loader', async () => { const loader = new ContainerLoader({ @@ -29,13 +31,8 @@ describe('/test/loader.test.ts', () => { loadDir: ['app', 'lib', '../test_other'], }); await loader.refresh(); - const appCtx = loader.getApplicationContext(); - const pluginCtx = loader.getPluginContext(); - assert(appCtx); - assert(pluginCtx); - assert.ok(typeof (await appCtx.getAsync('testOther'))); }); diff --git a/packages/core/test/util.test.ts b/packages/core/test/util.test.ts index c2e0bef5f89e..bb47d63747c5 100644 --- a/packages/core/test/util.test.ts +++ b/packages/core/test/util.test.ts @@ -1,6 +1,6 @@ import * as assert from 'assert'; import { join } from 'path'; -import { isPath, safeRequire, safelyGet, isPathEqual } from '../src/common/util'; +import { isPath, safeRequire, safelyGet, isPathEqual } from '../src/util'; import { StaticConfigLoader } from '../src/util/staticConfig'; describe('/test/util.test.ts', () => { diff --git a/packages/decorator/package.json b/packages/decorator/package.json index 53df7f3b15d0..c50560e0fa18 100644 --- a/packages/decorator/package.json +++ b/packages/decorator/package.json @@ -1,6 +1,6 @@ { "name": "@midwayjs/decorator", - "version": "2.3.17", + "version": "2.3.18-beta.6", "description": "definition decorator for midway project", "main": "dist/index", "typings": "dist/index.d.ts", @@ -42,5 +42,5 @@ "publishConfig": { "access": "public" }, - "gitHead": "44c0803552baf265debed8a11a860988b7e07a85" + "gitHead": "a603d2348d6141f8f723901498f03a162a037708" } diff --git a/packages/decorator/src/annotation/schedule.ts b/packages/decorator/src/annotation/schedule.ts index fa46b1cc9c49..f45c26a14e91 100644 --- a/packages/decorator/src/annotation/schedule.ts +++ b/packages/decorator/src/annotation/schedule.ts @@ -11,7 +11,7 @@ export interface ScheduleOpts { interval?: number | string; immediate?: boolean; disable?: boolean; - env?: [string]; + env?: string[]; cronOptions?: { currentDate?: string | number | Date; startDate?: string | number | Date; diff --git a/packages/decorator/src/common/decoratorManager.ts b/packages/decorator/src/common/decoratorManager.ts index 7e00d6440af0..251c5e1eb4c5 100644 --- a/packages/decorator/src/common/decoratorManager.ts +++ b/packages/decorator/src/common/decoratorManager.ts @@ -1,17 +1,31 @@ import 'reflect-metadata'; -import { ObjectDefinitionOptions, TagClsMetadata } from '../interface'; import { + ObjectDefinitionOptions, + ObjectIdentifier, + ReflectResult, + TagClsMetadata, + TagPropsMetadata, +} from '../interface'; +import { + CLASS_KEY_CONSTRUCTOR, + INJECT_TAG, MAIN_MODULE_KEY, OBJ_DEF_CLS, PRIVATE_META_DATA_KEY, + TAGGED, TAGGED_CLS, + TAGGED_PROP, } from './constant'; -import { classNamed } from './utils'; -const debug = require('util').debuglog('decorator:manager'); +import { + DUPLICATED_INJECTABLE_DECORATOR, + DUPLICATED_METADATA, + INVALID_DECORATOR_OPERATION, +} from './errMsg'; +import { Metadata } from './metadata'; +import { getParamNames, classNamed } from '../util'; -const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm; -const ARGUMENT_NAMES = /([^\s,]+)/g; +const debug = require('util').debuglog('decorator:manager'); export type decoratorKey = string | symbol; @@ -720,21 +734,6 @@ export function clearAllModule() { return manager.clear(); } -/** - * get parameter name from function - * @param func - */ -export function getParamNames(func): string[] { - 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; -} - /** * get provider id from module * @param module @@ -799,3 +798,202 @@ export function getPropertyType(target, propertyKey: string | symbol) { export function getMethodReturnTypes(target, propertyKey: string | symbol) { return Reflect.getMetadata('design:returntype', target, propertyKey); } + +function _tagParameterOrProperty( + metadataKey: string, + annotationTarget: any, + propertyName: string, + metadata: TagPropsMetadata, + parameterIndex?: number +) { + let paramsOrPropertiesMetadata: ReflectResult = {}; + const isParameterDecorator = typeof parameterIndex === 'number'; + const key: string = + parameterIndex !== undefined && isParameterDecorator + ? parameterIndex.toString() + : propertyName; + + // if the decorator is used as a parameter decorator, the property name must be provided + if (isParameterDecorator && propertyName !== undefined) { + throw new Error(INVALID_DECORATOR_OPERATION); + } + + // read metadata if available + if (Reflect.hasOwnMetadata(metadataKey, annotationTarget)) { + paramsOrPropertiesMetadata = Reflect.getMetadata( + metadataKey, + annotationTarget + ); + } + + // get metadata for the decorated parameter by its index + let paramOrPropertyMetadata: TagPropsMetadata[] = + paramsOrPropertiesMetadata[key]; + + if (!Array.isArray(paramOrPropertyMetadata)) { + paramOrPropertyMetadata = []; + } else { + for (const m of paramOrPropertyMetadata) { + if (m.key === metadata.key) { + throw new Error(`${DUPLICATED_METADATA} ${m.key.toString()}`); + } + } + } + + // set metadata + paramOrPropertyMetadata.push(metadata); + paramsOrPropertiesMetadata[key] = paramOrPropertyMetadata; + Reflect.defineMetadata( + metadataKey, + paramsOrPropertiesMetadata, + annotationTarget + ); +} + +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); +} + +interface InjectOptions { + identifier: ObjectIdentifier; + target: any; + targetKey: string; + index?: number; + args?: any; +} +/** + * 构造器注入 + * @param opts 参数 + */ +export function saveConstructorInject(opts: InjectOptions) { + let identifier = opts.identifier; + if (!identifier) { + const args = getParamNames(opts.target); + if (opts.target.length === args.length && opts.index < opts.target.length) { + identifier = args[opts.index]; + } + } else if (identifier.includes('@') && !identifier.includes(':')) { + const args = getParamNames(opts.target); + if (opts.target.length === args.length && opts.index < opts.target.length) { + identifier = `${identifier}:${args[opts.index]}`; + } + } + const metadata = new Metadata(INJECT_TAG, identifier); + metadata.args = opts.args; + _tagParameterOrProperty( + TAGGED, + opts.target, + opts.targetKey, + metadata, + opts.index + ); +} + +export function getConstructorInject(target: any): TagPropsMetadata[] { + return Reflect.getMetadata(TAGGED, target); +} +/** + * 属性注入 + * @param opts 参数 + */ +export function savePropertyInject(opts: InjectOptions) { + let identifier = opts.identifier; + if (!identifier) { + identifier = opts.targetKey; + } + if (identifier.includes('@') && !identifier.includes(':')) { + identifier = `${identifier}:${opts.targetKey}`; + } + const metadata = new Metadata(INJECT_TAG, identifier); + metadata.args = opts.args; + _tagParameterOrProperty( + TAGGED_PROP, + opts.target.constructor, + opts.targetKey, + metadata + ); +} + +export function getPropertyInject(target: any): TagPropsMetadata[] { + return Reflect.getMetadata(TAGGED_PROP, target); +} +/** + * class 元数据定义 + * @param target class + * @param props 属性 + */ +export function saveObjectDefProps(target: any, props: object = {}) { + if (Reflect.hasMetadata(OBJ_DEF_CLS, target)) { + const originProps = Reflect.getMetadata(OBJ_DEF_CLS, target); + + Reflect.defineMetadata( + OBJ_DEF_CLS, + Object.assign(originProps, props), + target + ); + } else { + Reflect.defineMetadata(OBJ_DEF_CLS, props, target); + } + return target; +} + +export function getObjectDefProps(target: any): ObjectDefinitionOptions { + return Reflect.getMetadata(OBJ_DEF_CLS, target); +} +/** + * class provider id + * @param identifier id + * @param target class + * @param override 是否覆盖 + */ +export function saveProviderId( + identifier: ObjectIdentifier, + target: any, + override?: boolean +) { + if (Reflect.hasOwnMetadata(TAGGED_CLS, target) && !override) { + throw new Error(DUPLICATED_INJECTABLE_DECORATOR); + } + + if (!identifier) { + identifier = classNamed(target.name); + } + + Reflect.defineMetadata( + TAGGED_CLS, + { + id: identifier, + originName: target.name, + }, + target + ); + + if (!Reflect.hasMetadata(OBJ_DEF_CLS, target)) { + Reflect.defineMetadata(OBJ_DEF_CLS, {}, target); + } + + return target; +} +/** + * 是否使用了 saveProviderId + * @param target class + */ +export function isProvide(target: any): boolean { + return Reflect.hasOwnMetadata(TAGGED_CLS, target); +} diff --git a/packages/decorator/src/common/utils.ts b/packages/decorator/src/common/utils.ts deleted file mode 100644 index 4715ac57d996..000000000000 --- a/packages/decorator/src/common/utils.ts +++ /dev/null @@ -1,232 +0,0 @@ -import { - getParamNames, - getClassMetadata, - saveClassMetadata, -} from './decoratorManager'; -import { - CLASS_KEY_CONSTRUCTOR, - OBJ_DEF_CLS, - TAGGED, - TAGGED_PROP, - TAGGED_CLS, - INJECT_TAG, -} from './constant'; -import { - TagPropsMetadata, - ReflectResult, - ObjectIdentifier, - ObjectDefinitionOptions, -} from '../interface'; -import { - DUPLICATED_METADATA, - INVALID_DECORATOR_OPERATION, - DUPLICATED_INJECTABLE_DECORATOR, -} from './errMsg'; -import camelcase = require('camelcase'); -import { Metadata } from './metadata'; - -function _tagParameterOrProperty( - metadataKey: string, - annotationTarget: any, - propertyName: string, - metadata: TagPropsMetadata, - parameterIndex?: number -) { - let paramsOrPropertiesMetadata: ReflectResult = {}; - const isParameterDecorator = typeof parameterIndex === 'number'; - const key: string = - parameterIndex !== undefined && isParameterDecorator - ? parameterIndex.toString() - : propertyName; - - // if the decorator is used as a parameter decorator, the property name must be provided - if (isParameterDecorator && propertyName !== undefined) { - throw new Error(INVALID_DECORATOR_OPERATION); - } - - // read metadata if available - if (Reflect.hasOwnMetadata(metadataKey, annotationTarget)) { - paramsOrPropertiesMetadata = Reflect.getMetadata( - metadataKey, - annotationTarget - ); - } - - // get metadata for the decorated parameter by its index - let paramOrPropertyMetadata: TagPropsMetadata[] = - paramsOrPropertiesMetadata[key]; - - if (!Array.isArray(paramOrPropertyMetadata)) { - paramOrPropertyMetadata = []; - } else { - for (const m of paramOrPropertyMetadata) { - if (m.key === metadata.key) { - throw new Error(`${DUPLICATED_METADATA} ${m.key.toString()}`); - } - } - } - - // set metadata - paramOrPropertyMetadata.push(metadata); - paramsOrPropertiesMetadata[key] = paramOrPropertyMetadata; - Reflect.defineMetadata( - metadataKey, - paramsOrPropertiesMetadata, - annotationTarget - ); -} - -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); -} -/** - * 按照框架规则返回类名字 - * @param name 类名称 - */ -export function classNamed(name: string) { - return camelcase(name); -} - -interface InjectOptions { - identifier: ObjectIdentifier; - target: any; - targetKey: string; - index?: number; - args?: any; -} -/** - * 构造器注入 - * @param opts 参数 - */ -export function saveConstructorInject(opts: InjectOptions) { - let identifier = opts.identifier; - if (!identifier) { - const args = getParamNames(opts.target); - if (opts.target.length === args.length && opts.index < opts.target.length) { - identifier = args[opts.index]; - } - } else if (identifier.includes('@') && !identifier.includes(':')) { - const args = getParamNames(opts.target); - if (opts.target.length === args.length && opts.index < opts.target.length) { - identifier = `${identifier}:${args[opts.index]}`; - } - } - const metadata = new Metadata(INJECT_TAG, identifier); - metadata.args = opts.args; - _tagParameterOrProperty( - TAGGED, - opts.target, - opts.targetKey, - metadata, - opts.index - ); -} - -export function getConstructorInject(target: any): TagPropsMetadata[] { - return Reflect.getMetadata(TAGGED, target); -} -/** - * 属性注入 - * @param opts 参数 - */ -export function savePropertyInject(opts: InjectOptions) { - let identifier = opts.identifier; - if (!identifier) { - identifier = opts.targetKey; - } - if (identifier.includes('@') && !identifier.includes(':')) { - identifier = `${identifier}:${opts.targetKey}`; - } - const metadata = new Metadata(INJECT_TAG, identifier); - metadata.args = opts.args; - _tagParameterOrProperty( - TAGGED_PROP, - opts.target.constructor, - opts.targetKey, - metadata - ); -} - -export function getPropertyInject(target: any): TagPropsMetadata[] { - return Reflect.getMetadata(TAGGED_PROP, target); -} -/** - * class 元数据定义 - * @param target class - * @param props 属性 - */ -export function saveObjectDefProps(target: any, props: object = {}) { - if (Reflect.hasMetadata(OBJ_DEF_CLS, target)) { - const originProps = Reflect.getMetadata(OBJ_DEF_CLS, target); - - Reflect.defineMetadata( - OBJ_DEF_CLS, - Object.assign(originProps, props), - target - ); - } else { - Reflect.defineMetadata(OBJ_DEF_CLS, props, target); - } - return target; -} - -export function getObjectDefProps(target: any): ObjectDefinitionOptions { - return Reflect.getMetadata(OBJ_DEF_CLS, target); -} -/** - * class provider id - * @param identifier id - * @param target class - * @param override 是否覆盖 - */ -export function saveProviderId( - identifier: ObjectIdentifier, - target: any, - override?: boolean -) { - if (Reflect.hasOwnMetadata(TAGGED_CLS, target) && !override) { - throw new Error(DUPLICATED_INJECTABLE_DECORATOR); - } - - if (!identifier) { - identifier = classNamed(target.name); - } - - Reflect.defineMetadata( - TAGGED_CLS, - { - id: identifier, - originName: target.name, - }, - target - ); - - if (!Reflect.hasMetadata(OBJ_DEF_CLS, target)) { - Reflect.defineMetadata(OBJ_DEF_CLS, {}, target); - } - - return target; -} -/** - * 是否使用了 saveProviderId - * @param target class - */ -export function isProvide(target: any): boolean { - return Reflect.hasOwnMetadata(TAGGED_CLS, target); -} diff --git a/packages/decorator/src/index.ts b/packages/decorator/src/index.ts index 49dbf71fec97..ae16172a1b72 100644 --- a/packages/decorator/src/index.ts +++ b/packages/decorator/src/index.ts @@ -5,7 +5,6 @@ export * from './common/decoratorManager'; export * from './common/errMsg'; export * from './common/metadata'; export * from './common/scopeEnum'; -export * from './common/utils'; export * from './faas/fun'; export * from './faas/handler'; export * from './web/requestMapping'; diff --git a/packages/decorator/src/interface.ts b/packages/decorator/src/interface.ts index c9d256613c1c..4184d9038d39 100644 --- a/packages/decorator/src/interface.ts +++ b/packages/decorator/src/interface.ts @@ -10,6 +10,8 @@ export type ObjectIdentifier = string; */ export interface IManagedInstance { type: string; + value?: any; + args?: any; } export interface ObjectDefinitionOptions { diff --git a/packages/decorator/src/util/index.ts b/packages/decorator/src/util/index.ts index d5ead7aa02cb..4131915a76eb 100644 --- a/packages/decorator/src/util/index.ts +++ b/packages/decorator/src/util/index.ts @@ -1,4 +1,6 @@ import * as util from 'util'; +import * as camelcase from 'camelcase'; + const ToString = Function.prototype.toString; function fnBody(fn) { @@ -55,3 +57,29 @@ export function sleep(sleepTime = 1000) { }, sleepTime); }); } + +const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm; +const ARGUMENT_NAMES = /([^\s,]+)/g; + +/** + * get parameter name from function + * @param func + */ +export function getParamNames(func): string[] { + 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; +} + +/** + * 按照框架规则返回类名字 + * @param name 类名称 + */ +export function classNamed(name: string) { + return camelcase(name); +} diff --git a/packages/faas/package.json b/packages/faas/package.json index 02b0d18b31fc..fd56884929dd 100644 --- a/packages/faas/package.json +++ b/packages/faas/package.json @@ -1,18 +1,18 @@ { "name": "@midwayjs/faas", - "version": "2.3.17", + "version": "2.3.18-beta.6", "main": "dist/index", "typings": "dist/index.d.ts", "dependencies": { - "@midwayjs/core": "^2.3.17", - "@midwayjs/decorator": "^2.3.17", + "@midwayjs/core": "^2.3.18-beta.6", + "@midwayjs/decorator": "^2.3.18-beta.6", "@midwayjs/faas-typings": "^1.1.4", "@midwayjs/simple-lock": "^1.1.4", "koa-compose": "^4.1.0" }, "devDependencies": { "@midwayjs/cli": "^1.0.0", - "@midwayjs/mock": "^2.3.17", + "@midwayjs/mock": "^2.3.18-beta.6", "@midwayjs/serverless-fc-starter": "^1.0.0", "@midwayjs/serverless-scf-starter": "^1.0.0", "mm": "^2.5.0" @@ -45,5 +45,5 @@ "url": "git@github.com:midwayjs/midway.git" }, "license": "MIT", - "gitHead": "44c0803552baf265debed8a11a860988b7e07a85" + "gitHead": "a603d2348d6141f8f723901498f03a162a037708" } diff --git a/packages/faas/src/framework.ts b/packages/faas/src/framework.ts index 44c6fd753a25..f3fc0f598609 100644 --- a/packages/faas/src/framework.ts +++ b/packages/faas/src/framework.ts @@ -13,7 +13,6 @@ import { listModule, listPreloadModule, MidwayFrameworkType, - MidwayProcessTypeEnum, MidwayRequestContainer, REQUEST_OBJ_CTX_KEY, } from '@midwayjs/core'; @@ -39,19 +38,46 @@ export class MidwayFaaSFramework extends BaseFramework< private lock = new SimpleLock(); public app: IMidwayFaaSApplication; - protected async beforeDirectoryLoad( + protected async afterContainerInitialize( options: Partial ) { this.logger = options.logger || console; this.globalMiddleware = this.configurationOptions.middleware || []; - this.app = this.defineApplicationProperties( - this.configurationOptions.applicationAdapter?.getApplication() || {} - ); + this.app = + this.configurationOptions.applicationAdapter?.getApplication() || + ({} as IMidwayFaaSApplication); + + this.defineApplicationProperties({ + getLogger: () => { + return this.logger; + }, + /** + * return init context value such as aliyun fc + */ + getInitializeContext: () => { + return this.configurationOptions.initializeContext; + }, + + useMiddleware: async middlewares => { + if (middlewares.length) { + const newMiddlewares = await this.loadMiddleware(middlewares); + for (const mw of newMiddlewares) { + this.app.use(mw); + } + } + }, + + generateMiddleware: async (middlewareId: string) => { + return this.generateMiddleware(middlewareId); + }, + }); this.prepareConfiguration(); } - protected async afterInitialize(options: Partial) { + protected async afterContainerReady( + options: Partial + ) { this.registerDecorator(); } @@ -229,7 +255,7 @@ export class MidwayFaaSFramework extends BaseFramework< if (!fileDir) { fileDir = dirname(resolve(filePath)); } - const container = this.containerLoader.getApplicationContext(); + const container = this.getApplicationContext(); const cfg = container.createConfiguration(); cfg.namespace = namespace; cfg.loadConfiguration(require(filePath), fileDir); @@ -252,73 +278,20 @@ export class MidwayFaaSFramework extends BaseFramework< // this.initConfiguration('./configuration', __dirname); } - protected defineApplicationProperties(app): IMidwayFaaSApplication { - return Object.assign(app, { - getBaseDir: () => { - return this.baseDir; - }, - - getAppDir: () => { - return this.appDir; - }, - - getEnv: () => { - return this.getApplicationContext() - .getEnvironmentService() - .getCurrentEnvironment(); - }, - - getConfig: (key?: string) => { - return this.getApplicationContext() - .getConfigService() - .getConfiguration(key); - }, - - getLogger: () => { - return this.logger; - }, - - getFrameworkType: () => { - return this.getFrameworkType(); - }, - - getProcessType: () => { - return MidwayProcessTypeEnum.APPLICATION; - }, - /** - * return init context value such as aliyun fc - */ - getInitializeContext: () => { - return this.configurationOptions.initializeContext; - }, - - getApplicationContext: () => { - return this.getApplicationContext(); - }, - - useMiddleware: async middlewares => { - if (middlewares.length) { - const newMiddlewares = await this.loadMiddleware(middlewares); - for (const mw of newMiddlewares) { - this.app.use(mw); - } - } - }, - - generateMiddleware: async (middlewareId: string) => { - return this.generateMiddleware(middlewareId); - }, - }); - } - private registerDecorator() { - this.containerLoader.registerHook(PLUGIN_KEY, (key, target) => { - return target[REQUEST_OBJ_CTX_KEY]?.[key] || this.app[key]; - }); + this.getApplicationContext().registerDataHandler( + PLUGIN_KEY, + (key, target) => { + return target[REQUEST_OBJ_CTX_KEY]?.[key] || this.app[key]; + } + ); - this.containerLoader.registerHook(LOGGER_KEY, (key, target) => { - return target[REQUEST_OBJ_CTX_KEY]?.['logger'] || this.app.getLogger(); - }); + this.getApplicationContext().registerDataHandler( + LOGGER_KEY, + (key, target) => { + return target[REQUEST_OBJ_CTX_KEY]?.['logger'] || this.app.getLogger(); + } + ); } private async loadMiddleware(middlewares) { @@ -338,6 +311,8 @@ export class MidwayFaaSFramework extends BaseFramework< return newMiddlewares; } + + async applicationInitialize(options: IMidwayBootstrapOptions) {} } function covertId(cls, method) { diff --git a/packages/midway-schedule/package.json b/packages/midway-schedule/package.json index 4cf3ccb47ec8..ca90d30ae596 100644 --- a/packages/midway-schedule/package.json +++ b/packages/midway-schedule/package.json @@ -1,11 +1,8 @@ { "name": "midway-schedule", - "version": "2.3.17", + "version": "2.3.18-beta.6", "description": "", "main": "index.js", - "directories": { - "lib": "lib" - }, "eggPlugin": { "name": "schedulePlus", "optionalDependencies": [ @@ -13,17 +10,17 @@ ] }, "scripts": { - "build": "midway-bin build -c", + "build": "../../node_modules/.bin/tsc", "test": "NODE_ENV=unittest midway-bin test --ts" }, "keywords": [], "license": "MIT", "devDependencies": { "@midwayjs/cli": "^1.0.0", - "@midwayjs/mock": "^2.3.17" + "@midwayjs/mock": "^2.3.18-beta.6" }, "dependencies": { - "@midwayjs/decorator": "^2.3.17", + "@midwayjs/decorator": "^2.3.18-beta.6", "is-type-of": "^1.2.1" }, "engines": { @@ -31,9 +28,8 @@ }, "files": [ "*.d.ts", - "*.map", "agent.js", "app.js" ], - "gitHead": "44c0803552baf265debed8a11a860988b7e07a85" + "gitHead": "a603d2348d6141f8f723901498f03a162a037708" } diff --git a/packages/midway/package.json b/packages/midway/package.json index b7f8101cdca5..e9f7b8738f19 100644 --- a/packages/midway/package.json +++ b/packages/midway/package.json @@ -1,6 +1,6 @@ { "name": "midway", - "version": "2.3.17", + "version": "2.3.18-beta.6", "description": "Midway Main Package", "main": "dist/index", "typings": "dist/index.d.ts", @@ -21,7 +21,7 @@ "@midwayjs/cli": "^1.0.0" }, "dependencies": { - "@midwayjs/web": "^2.3.17", + "@midwayjs/web": "^2.3.18-beta.6", "egg-cluster": "^1.23.0" }, "files": [ @@ -40,5 +40,5 @@ "type": "git", "url": "http://github.com/midwayjs/midway.git" }, - "gitHead": "44c0803552baf265debed8a11a860988b7e07a85" + "gitHead": "a603d2348d6141f8f723901498f03a162a037708" } diff --git a/packages/mock/package.json b/packages/mock/package.json index 1bef1b0d8669..ba9b4b8d33c3 100644 --- a/packages/mock/package.json +++ b/packages/mock/package.json @@ -1,6 +1,6 @@ { "name": "@midwayjs/mock", - "version": "2.3.17", + "version": "2.3.18-beta.6", "description": "create your test app from midway framework", "main": "dist/index", "typings": "dist/index.d.ts", @@ -32,13 +32,13 @@ "license": "MIT", "devDependencies": { "@midwayjs/cli": "^1.0.0", - "@midwayjs/core": "^2.3.17", + "@midwayjs/core": "^2.3.18-beta.6", "@types/amqplib": "^0.5.13", "amqplib": "^0.6.0" }, "dependencies": { - "@midwayjs/bootstrap": "^2.3.17", - "@midwayjs/decorator": "^2.3.17", + "@midwayjs/bootstrap": "^2.3.18-beta.6", + "@midwayjs/decorator": "^2.3.18-beta.6", "egg-mock": "^3.21.0", "fs-extra": "^8.0.1", "power-assert": "^1.6.1", @@ -49,5 +49,5 @@ "type": "git", "url": "http://github.com/midwayjs/midway.git" }, - "gitHead": "44c0803552baf265debed8a11a860988b7e07a85" + "gitHead": "a603d2348d6141f8f723901498f03a162a037708" } diff --git a/packages/mock/src/utils.ts b/packages/mock/src/utils.ts index bc8bc9aaf422..7430f873a203 100644 --- a/packages/mock/src/utils.ts +++ b/packages/mock/src/utils.ts @@ -1,5 +1,6 @@ import { BootstrapStarter } from '@midwayjs/bootstrap'; import { + MidwayContainer, IMidwayApplication, IMidwayFramework, MidwayFrameworkType, @@ -32,6 +33,7 @@ export async function create< ): Promise { process.env.MIDWAY_TS_MODE = 'true'; clearAllModule(); + MidwayContainer.parentDefinitionMetadata = null; let framework: T = null; let DefaultFramework = null; diff --git a/packages/rabbitmq/package.json b/packages/rabbitmq/package.json index 59ae8538de25..cedb8e649da4 100644 --- a/packages/rabbitmq/package.json +++ b/packages/rabbitmq/package.json @@ -1,6 +1,6 @@ { "name": "@midwayjs/rabbitmq", - "version": "2.3.17", + "version": "2.3.18-beta.6", "description": "Midway Framework for rabbitmq", "main": "dist/index", "typings": "dist/index.d.ts", @@ -24,12 +24,12 @@ "license": "MIT", "devDependencies": { "@midwayjs/cli": "^1.0.0", - "@midwayjs/mock": "^2.3.17", + "@midwayjs/mock": "^2.3.18-beta.6", "fs-extra": "^8.0.1" }, "dependencies": { - "@midwayjs/core": "^2.3.17", - "@midwayjs/decorator": "^2.3.17", + "@midwayjs/core": "^2.3.18-beta.6", + "@midwayjs/decorator": "^2.3.18-beta.6", "@types/amqplib": "^0.5.13", "amqplib": "^0.6.0" }, @@ -38,5 +38,5 @@ "type": "git", "url": "http://github.com/midwayjs/midway.git" }, - "gitHead": "44c0803552baf265debed8a11a860988b7e07a85" + "gitHead": "a603d2348d6141f8f723901498f03a162a037708" } diff --git a/packages/rabbitmq/src/framework.ts b/packages/rabbitmq/src/framework.ts index a6fbd7c84a2e..c3578c6ee241 100644 --- a/packages/rabbitmq/src/framework.ts +++ b/packages/rabbitmq/src/framework.ts @@ -36,7 +36,7 @@ export class MidwayRabbitMQFramework extends BaseFramework< return this; } - protected async afterDirectoryLoad(options) { + async applicationInitialize(options) { this.app = (new RabbitMQServer( this.configurationOptions ) as unknown) as IMidwayRabbitMQApplication; @@ -44,7 +44,7 @@ export class MidwayRabbitMQFramework extends BaseFramework< await this.app.init(); } - protected async afterInitialize( + protected async afterContainerReady( options: Partial ): Promise { await this.loadSubscriber(); @@ -98,14 +98,17 @@ export class MidwayRabbitMQFramework extends BaseFramework< return this.app.createConsumer( listenerOptions, async (data?: ConsumeMessage) => { - const ctx: IMidwayRabbitMQContext = { + const ctx = { channel: this.app.getChannel(), - }; + } as IMidwayRabbitMQContext; const requestContainer = new MidwayRequestContainer( ctx, this.getApplicationContext() ); ctx.requestContext = requestContainer; + ctx.getRequestContext = () => { + return requestContainer; + }; const ins = await requestContainer.getAsync(providerId); await ins[listenerOptions.propertyKey].call(ins, data); } diff --git a/packages/rabbitmq/src/interface.ts b/packages/rabbitmq/src/interface.ts index 95c593fc96cb..8dec8bdf707a 100644 --- a/packages/rabbitmq/src/interface.ts +++ b/packages/rabbitmq/src/interface.ts @@ -1,4 +1,4 @@ -import { IMidwayApplication, IMidwayContainer } from '@midwayjs/core'; +import { IMidwayApplication, IMidwayContext } from '@midwayjs/core'; import { ConsumeMessage, Options } from 'amqplib/properties'; import { RabbitMQListenerOptions } from '@midwayjs/decorator'; import * as amqp from 'amqplib'; @@ -32,8 +32,7 @@ export type IMidwayRabbitMQConfigurationOptions = { export type IMidwayRabbitMQContext = { channel: amqp.Channel; - requestContext?: IMidwayContainer; -}; +} & IMidwayContext; export enum RabbitMQChannelEvent { CHANNEL_CLOSE = 'ch_close', diff --git a/packages/socketio/package.json b/packages/socketio/package.json index 4ea5aa1f682a..33ad54842b15 100644 --- a/packages/socketio/package.json +++ b/packages/socketio/package.json @@ -1,6 +1,6 @@ { "name": "@midwayjs/socketio", - "version": "2.3.17", + "version": "2.3.18-beta.6", "description": "Midway Web Framework for socket.io", "main": "dist/index", "typings": "dist/index.d.ts", @@ -24,15 +24,15 @@ "license": "MIT", "devDependencies": { "@midwayjs/cli": "^1.0.0", - "@midwayjs/mock": "^2.3.17", + "@midwayjs/mock": "^2.3.18-beta.6", "@types/socket.io": "^2.1.11", "@types/socket.io-client": "^1.4.33", "fs-extra": "^8.0.1", "socket.io-client": "^2.3.0" }, "dependencies": { - "@midwayjs/core": "^2.3.17", - "@midwayjs/decorator": "^2.3.17", + "@midwayjs/core": "^2.3.18-beta.6", + "@midwayjs/decorator": "^2.3.18-beta.6", "socket.io": "^2.3.0" }, "author": "Harry Chen ", @@ -40,5 +40,5 @@ "type": "git", "url": "http://github.com/midwayjs/midway.git" }, - "gitHead": "44c0803552baf265debed8a11a860988b7e07a85" + "gitHead": "a603d2348d6141f8f723901498f03a162a037708" } diff --git a/packages/socketio/src/framework.ts b/packages/socketio/src/framework.ts index 854546cd450e..7fdc432b747f 100644 --- a/packages/socketio/src/framework.ts +++ b/packages/socketio/src/framework.ts @@ -26,6 +26,7 @@ export class MidwaySocketIOFramework extends BaseFramework< IMidwaySocketIOApplication, IMidwaySocketIOConfigurationOptions > { + applicationInitialize(options: IMidwayBootstrapOptions) {} public app: IMidwaySocketIOApplication; public configure( @@ -35,7 +36,7 @@ export class MidwaySocketIOFramework extends BaseFramework< return this; } - protected async afterDirectoryLoad( + protected async afterContainerDirectoryLoad( options: Partial ) { if (this.configurationOptions.webServer) { @@ -59,7 +60,7 @@ export class MidwaySocketIOFramework extends BaseFramework< }); } - protected async afterInitialize( + protected async afterContainerReady( options: Partial ): Promise { await this.loadMidwayController(); diff --git a/packages/socketio/src/interface.ts b/packages/socketio/src/interface.ts index 0b0fe473ecf8..f1d8ed4dce6b 100644 --- a/packages/socketio/src/interface.ts +++ b/packages/socketio/src/interface.ts @@ -1,5 +1,5 @@ import * as SocketIO from 'socket.io'; -import { IMidwayApplication, IMidwayContainer } from '@midwayjs/core'; +import { IMidwayApplication, IMidwayContext } from '@midwayjs/core'; import { Server as HttpServer } from 'http'; import { Server as HttpsServer } from 'https'; @@ -12,6 +12,4 @@ export type IMidwaySocketIOConfigurationOptions = { webServer?: HttpServer | HttpsServer; } & SocketIO.ServerOptions; -export type IMidwaySocketIOContext = SocketIO.Socket & { - requestContext: IMidwayContainer; -}; +export type IMidwaySocketIOContext = SocketIO.Socket & IMidwayContext; diff --git a/packages/web-express/package.json b/packages/web-express/package.json index 8c972488fa82..6fc563884d84 100644 --- a/packages/web-express/package.json +++ b/packages/web-express/package.json @@ -1,6 +1,6 @@ { "name": "@midwayjs/express", - "version": "2.3.17", + "version": "2.3.18-beta.6", "description": "Midway Web Framework for Express", "main": "dist/index", "typings": "dist/index.d.ts", @@ -24,13 +24,13 @@ "license": "MIT", "devDependencies": { "@midwayjs/cli": "^1.0.0", - "@midwayjs/mock": "^2.3.17", + "@midwayjs/mock": "^2.3.18-beta.6", "@types/express": "^4.17.8", "fs-extra": "^8.0.1" }, "dependencies": { - "@midwayjs/core": "^2.3.17", - "@midwayjs/decorator": "^2.3.17", + "@midwayjs/core": "^2.3.18-beta.6", + "@midwayjs/decorator": "^2.3.18-beta.6", "express": "^4.17.1" }, "author": "Harry Chen ", @@ -38,5 +38,5 @@ "type": "git", "url": "http://github.com/midwayjs/midway.git" }, - "gitHead": "44c0803552baf265debed8a11a860988b7e07a85" + "gitHead": "a603d2348d6141f8f723901498f03a162a037708" } diff --git a/packages/web-express/src/framework.ts b/packages/web-express/src/framework.ts index cbce134dc96a..aefc3b23794b 100644 --- a/packages/web-express/src/framework.ts +++ b/packages/web-express/src/framework.ts @@ -8,7 +8,6 @@ import { IMidwayBootstrapOptions, listModule, MidwayFrameworkType, - MidwayProcessTypeEnum, MidwayRequestContainer, } from '@midwayjs/core'; @@ -56,11 +55,17 @@ export class MidwayExpressFramework extends BaseFramework< return this; } - protected async afterDirectoryLoad( - options: Partial - ) { + async applicationInitialize(options: Partial) { this.app = (express() as unknown) as IMidwayExpressApplication; - this.defineApplicationProperties(this.app); + this.defineApplicationProperties({ + generateController: (controllerMapping: string) => { + return this.generateController(controllerMapping); + }, + + generateMiddleware: async (middlewareId: string) => { + return this.generateMiddleware(middlewareId); + }, + }); this.app.use((req, res, next) => { const ctx = { req, res } as IMidwayExpressContext; ctx.requestContext = new MidwayRequestContainer( @@ -75,7 +80,7 @@ export class MidwayExpressFramework extends BaseFramework< }); } - protected async afterInitialize( + protected async afterContainerReady( options: Partial ): Promise { await this.loadMidwayController(); @@ -303,46 +308,4 @@ export class MidwayExpressFramework extends BaseFramework< } } } - - protected defineApplicationProperties( - app: IMidwayExpressApplication - ): IMidwayExpressApplication { - return Object.assign(app, { - getBaseDir: () => { - return this.baseDir; - }, - - getAppDir: () => { - return this.appDir; - }, - - getEnv: () => { - return this.getApplicationContext() - .getEnvironmentService() - .getCurrentEnvironment(); - }, - - getConfig: (key?: string) => { - return this.getApplicationContext() - .getConfigService() - .getConfiguration(key); - }, - - getFrameworkType: () => { - return this.getFrameworkType(); - }, - - getProcessType: () => { - return MidwayProcessTypeEnum.APPLICATION; - }, - - generateController: (controllerMapping: string) => { - return this.generateController(controllerMapping); - }, - - generateMiddleware: async (middlewareId: string) => { - return this.generateMiddleware(middlewareId); - }, - }); - } } diff --git a/packages/web-koa/package.json b/packages/web-koa/package.json index 2226bbbd8f44..efebdf10a2b1 100644 --- a/packages/web-koa/package.json +++ b/packages/web-koa/package.json @@ -1,6 +1,6 @@ { "name": "@midwayjs/koa", - "version": "2.3.17", + "version": "2.3.18-beta.6", "description": "Midway Web Framework for KOA", "main": "dist/index", "typings": "dist/index.d.ts", @@ -24,14 +24,14 @@ "license": "MIT", "devDependencies": { "@midwayjs/cli": "^1.0.0", - "@midwayjs/mock": "^2.3.17", + "@midwayjs/mock": "^2.3.18-beta.6", "@types/koa": "^2.11.4", "@types/koa-router": "^7.4.1", "fs-extra": "^8.0.1" }, "dependencies": { - "@midwayjs/core": "^2.3.17", - "@midwayjs/decorator": "^2.3.17", + "@midwayjs/core": "^2.3.18-beta.6", + "@midwayjs/decorator": "^2.3.18-beta.6", "koa": "^2.13.0", "koa-router": "^9.4.0" }, @@ -40,5 +40,5 @@ "type": "git", "url": "http://github.com/midwayjs/midway.git" }, - "gitHead": "44c0803552baf265debed8a11a860988b7e07a85" + "gitHead": "a603d2348d6141f8f723901498f03a162a037708" } diff --git a/packages/web-koa/src/framework.ts b/packages/web-koa/src/framework.ts index f347dc6652b8..d39c22ebb632 100644 --- a/packages/web-koa/src/framework.ts +++ b/packages/web-koa/src/framework.ts @@ -9,7 +9,6 @@ import { IMidwayBootstrapOptions, listModule, MidwayFrameworkType, - MidwayProcessTypeEnum, MidwayRequestContainer, } from '@midwayjs/core'; @@ -264,8 +263,6 @@ export abstract class MidwayKoaBaseFramework< } } } - - protected abstract defineApplicationProperties(app: U): U; } export class MidwayKoaFramework extends MidwayKoaBaseFramework< @@ -280,9 +277,7 @@ export class MidwayKoaFramework extends MidwayKoaBaseFramework< return this; } - protected async afterDirectoryLoad( - options: Partial - ) { + async applicationInitialize(options: Partial) { this.app = new koa< DefaultState, IMidwayKoaContext @@ -296,10 +291,18 @@ export class MidwayKoaFramework extends MidwayKoaBaseFramework< await next(); }); - this.defineApplicationProperties(this.app); + this.defineApplicationProperties({ + generateController: (controllerMapping: string) => { + return this.generateController(controllerMapping); + }, + + generateMiddleware: async (middlewareId: string) => { + return this.generateMiddleware(middlewareId); + }, + }); } - protected async afterInitialize( + protected async afterContainerReady( options: Partial ): Promise { await this.loadMidwayController(); @@ -318,46 +321,4 @@ export class MidwayKoaFramework extends MidwayKoaBaseFramework< public getFrameworkType(): MidwayFrameworkType { return MidwayFrameworkType.WEB_KOA; } - - protected defineApplicationProperties( - app: IMidwayKoaApplication - ): IMidwayKoaApplication { - return Object.assign(app, { - getBaseDir: () => { - return this.baseDir; - }, - - getAppDir: () => { - return this.appDir; - }, - - getEnv: () => { - return this.getApplicationContext() - .getEnvironmentService() - .getCurrentEnvironment(); - }, - - getConfig: (key?: string) => { - return this.getApplicationContext() - .getConfigService() - .getConfiguration(key); - }, - - getFrameworkType: () => { - return this.getFrameworkType(); - }, - - getProcessType: () => { - return MidwayProcessTypeEnum.APPLICATION; - }, - - generateController: (controllerMapping: string) => { - return this.generateController(controllerMapping); - }, - - generateMiddleware: async (middlewareId: string) => { - return this.generateMiddleware(middlewareId); - }, - }); - } } diff --git a/packages/web/agent.js b/packages/web/agent.js index ede988cfb9b1..69b4d7d525f2 100644 --- a/packages/web/agent.js +++ b/packages/web/agent.js @@ -1,33 +1,13 @@ 'use strict'; -const { BootstrapStarter } = require('@midwayjs/bootstrap'); -const { Framework } = require('./dist/index'); - -class AppBootHook { +class AgentBootHook { constructor(app) { this.app = app; } - async didLoad() { - // 这里的逻辑是为了兼容老 cluster 模式 - if (this.app.options['isClusterMode'] !== false) { - this.framework = new Framework().configure({ - processType: 'agent', - app: this.app, - }); - this.bootstrap = new BootstrapStarter(); - this.bootstrap - .configure({ - baseDir: this.app.appDir, - globalConfig: this.app.config, - }) - .load(this.framework); - await this.bootstrap.init(); - this.app.options['webFramework'] = this.framework; - } - } + async didLoad() {} async willReady() {} } -module.exports = AppBootHook; +module.exports = AgentBootHook; diff --git a/packages/web/app.js b/packages/web/app.js index 709b56f9277d..3913f497e7c1 100644 --- a/packages/web/app.js +++ b/packages/web/app.js @@ -1,11 +1,6 @@ 'use strict'; -const { Bootstrap } = require('@midwayjs/bootstrap'); -const { Framework } = require('./dist/index'); const pathMatching = require('egg-path-matching'); -const { safelyGet } = require('@midwayjs/core'); - -const { CONFIG_KEY, LOGGER_KEY, PLUGIN_KEY } = require('@midwayjs/decorator'); class AppBootHook { constructor(app) { @@ -15,50 +10,16 @@ class AppBootHook { configDidLoad() { // 先清空,防止加载到 midway 中间件出错 - this.appMiddleware = this.app.config.appMiddleware; - this.app.config.appMiddleware = []; + this.appMiddleware = this.app.loader.config.appMiddleware; + this.app.loader.config.appMiddleware = []; } async didLoad() { - // 这里的逻辑是为了兼容老 cluster 模式 - if (this.app.options['isClusterMode'] !== false) { - this.framework = new Framework().configure({ - processType: 'application', - app: this.app, - globalConfig: this.app.config, - }); - Bootstrap.configure({ - baseDir: this.app.appDir, - }).load(this.framework); - await Bootstrap.run(); - this.app.options['webFramework'] = this.framework; - } - - // register plugin - this.app.applicationContext.registerDataHandler( - PLUGIN_KEY, - (key, target) => { - return this.app[key]; - } - ); - - // register config - this.app.applicationContext.registerDataHandler(CONFIG_KEY, key => { - return key ? safelyGet(key, this.app.config) : this.app.config; - }); - - // register logger - this.app.applicationContext.registerDataHandler(LOGGER_KEY, key => { - if (this.app.getLogger) { - return this.app.getLogger(key); - } - return this.app.coreLogger; - }); - // 等 midway 加载完成后,再去 use 中间件 for (const name of this.appMiddleware) { if (this.app.getApplicationContext().registry.hasDefinition(name)) { const mwIns = await this.app.generateMiddleware(name); + mwIns._name = name; this.app.use(mwIns); } else { // egg @@ -81,6 +42,8 @@ class AppBootHook { } } } + + await this.app.webFramework.loadMidwayController(); } async willReady() {} diff --git a/packages/web/app/extend/application.js b/packages/web/app/extend/application.js new file mode 100644 index 000000000000..3a82966e5d81 --- /dev/null +++ b/packages/web/app/extend/application.js @@ -0,0 +1,9 @@ +module.exports = { + get baseDir() { + return this.loader.baseDir; + }, + + get webFramework() { + return this.loader.framework; + }, +}; diff --git a/packages/web/package.json b/packages/web/package.json index 30db1064523a..dc386dc0be2c 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,14 +1,13 @@ { "name": "@midwayjs/web", - "version": "2.3.17", + "version": "2.3.18-beta.6", "description": "Midway Web Scene", "main": "dist/index", "typings": "dist/index.d.ts", "scripts": { "build": "midway-bin build -c", - "test": "midway-bin test --forceExit", - "cov": "midway-bin cov --forceExit", - "ci": "npm run test", + "test": "midway-bin test --ts --forceExit", + "cov": "midway-bin cov --ts --forceExit", "link": "npm link" }, "keywords": [ @@ -29,7 +28,7 @@ "license": "MIT", "devDependencies": { "@midwayjs/cli": "^1.0.0", - "@midwayjs/mock": "^2.3.17", + "@midwayjs/mock": "^2.3.18-beta.6", "egg-view-nunjucks": "^2.2.0", "fs-extra": "^8.0.1", "pedding": "^1.1.0", @@ -38,16 +37,16 @@ }, "dependencies": { "@eggjs/router": "^2.0.0", - "@midwayjs/bootstrap": "^2.3.17", - "@midwayjs/core": "^2.3.17", - "@midwayjs/decorator": "^2.3.17", - "@midwayjs/koa": "^2.3.17", + "@midwayjs/bootstrap": "^2.3.18-beta.6", + "@midwayjs/core": "^2.3.18-beta.6", + "@midwayjs/decorator": "^2.3.18-beta.6", + "@midwayjs/koa": "^2.3.18-beta.6", "debug": "^4.1.1", "egg": "^2.28.0", "egg-logger": "^2.4.2", "egg-path-matching": "^1.0.1", "extend2": "^1.0.0", - "midway-schedule": "^2.3.17", + "midway-schedule": "^2.3.18-beta.6", "mkdirp": "^1.0.4" }, "author": "Harry Chen ", @@ -55,5 +54,5 @@ "type": "git", "url": "http://github.com/midwayjs/midway.git" }, - "gitHead": "44c0803552baf265debed8a11a860988b7e07a85" + "gitHead": "a603d2348d6141f8f723901498f03a162a037708" } diff --git a/packages/web/src/application.ts b/packages/web/src/application.ts index cbe88d8ad2e0..62cc4ba23e50 100644 --- a/packages/web/src/application.ts +++ b/packages/web/src/application.ts @@ -1,10 +1,9 @@ -import type { MidwayWebFramework } from './framework'; -import { RouterParamValue } from '@midwayjs/decorator'; import { parseNormalDir } from './utils'; import * as extend from 'extend2'; import { EggAppInfo } from 'egg'; -import { IMidwayWebApplication } from './interface'; import { join } from 'path'; +import { BootstrapStarter } from '@midwayjs/bootstrap'; +import { MidwayWebFramework } from './framework'; const { AppWorkerLoader, @@ -18,24 +17,19 @@ const EGG_PATH = Symbol.for('egg#eggPath'); export const createAppWorkerLoader = AppWorkerLoader => { class EggAppWorkerLoader extends (AppWorkerLoader as any) { - app: IMidwayWebApplication & { - appOptions: { - typescript?: boolean; - isTsMode?: boolean; - }; - appDir: string; - baseDir: string; - }; + app: any; + framework; + bootstrap; getEggPaths() { if (!this.appDir) { // 这里的逻辑是为了兼容老 cluster 模式 - if (this.app.appOptions.typescript || this.app.appOptions.isTsMode) { + if (this.app.options.typescript || this.app.options.isTsMode) { process.env.EGG_TYPESCRIPT = 'true'; } const result = parseNormalDir( - this.app.appOptions['baseDir'], - this.app.appOptions.isTsMode + this.app.options['baseDir'], + this.app.options.isTsMode ); this.baseDir = result.baseDir; this.options.baseDir = this.baseDir; @@ -63,6 +57,23 @@ export const createAppWorkerLoader = AppWorkerLoader => { } return this.appInfo; } + + load() { + this.framework = new MidwayWebFramework().configure({ + processType: 'application', + app: this.app, + globalConfig: this.app.config, + }); + this.bootstrap = new BootstrapStarter(); + this.bootstrap + .configure({ + baseDir: this.app.appDir, + }) + .load(this.framework); + this.bootstrap.init().then(() => { + super.load(); + }); + } } return EggAppWorkerLoader as any; @@ -72,12 +83,12 @@ export const createAgentWorkerLoader = AppWorkerLoader => { class EggAppWorkerLoader extends (AppWorkerLoader as any) { getEggPaths() { if (!this.appDir) { - if (this.app.appOptions.typescript || this.app.appOptions.isTsMode) { + if (this.app.options.typescript || this.app.options.isTsMode) { process.env.EGG_TYPESCRIPT = 'true'; } const result = parseNormalDir( - this.app.appOptions['baseDir'], - this.app.appOptions.isTsMode + this.app.options['baseDir'], + this.app.options.isTsMode ); this.baseDir = result.baseDir; this.options.baseDir = this.baseDir; @@ -104,6 +115,23 @@ export const createAgentWorkerLoader = AppWorkerLoader => { } return this.appInfo; } + + load() { + this.framework = new MidwayWebFramework().configure({ + processType: 'agent', + app: this.app, + globalConfig: this.app.config, + }); + this.bootstrap = new BootstrapStarter(); + this.bootstrap + .configure({ + baseDir: this.app.appDir, + }) + .load(this.framework); + this.bootstrap.init().then(() => { + super.load(); + }); + } } return EggAppWorkerLoader as any; @@ -123,42 +151,6 @@ export const createEggApplication = Application => { get [EGG_PATH]() { return __dirname; } - - get appOptions() { - return this.options; - } - - get midwayWebFramework(): MidwayWebFramework { - return this.appOptions['webFramework']; - } - - get applicationContext() { - return this.midwayWebFramework.getApplicationContext(); - } - - getApplicationContext() { - return this.applicationContext; - } - - generateController( - controllerMapping: string, - routeArgsInfo?: RouterParamValue[], - routerResponseData?: any[] - ) { - return this.midwayWebFramework.generateController( - controllerMapping, - routeArgsInfo, - routerResponseData - ); - } - - async generateMiddleware(middlewareId: string) { - return this.midwayWebFramework.generateMiddleware(middlewareId); - } - - get baseDir() { - return this.loader.baseDir; - } } return EggApplication as any; @@ -178,26 +170,6 @@ export const createEggAgent = Agent => { get [EGG_PATH]() { return __dirname; } - - get appOptions() { - return this.options; - } - - get midwayWebFramework(): MidwayWebFramework { - return this.appOptions['webFramework']; - } - - get applicationContext() { - return this.midwayWebFramework.getApplicationContext(); - } - - getApplicationContext() { - return this.applicationContext; - } - - get baseDir() { - return this.loader.baseDir; - } } return EggAgent as any; @@ -211,7 +183,7 @@ const EggAgentWorkerLoader = createAgentWorkerLoader(AgentWorkerLoader); const BaseEggAgent = createEggAgent(Agent); -export class EggApplication extends BaseEggApplication { +class EggApplication extends BaseEggApplication { get [EGG_LOADER]() { return EggAppWorkerLoader; } @@ -221,7 +193,7 @@ export class EggApplication extends BaseEggApplication { } } -export class EggAgent extends BaseEggAgent { +class EggAgent extends BaseEggAgent { get [EGG_LOADER]() { return EggAgentWorkerLoader; } diff --git a/packages/web/src/devFramework.ts b/packages/web/src/devFramework.ts new file mode 100644 index 000000000000..06adfac178f0 --- /dev/null +++ b/packages/web/src/devFramework.ts @@ -0,0 +1,67 @@ +import { + IMidwayBootstrapOptions, + IMidwayContainer, + IMidwayFramework, + MidwayFrameworkType, +} from '@midwayjs/core'; +import { IMidwayWebConfigurationOptions } from './interface'; +import { Application } from 'egg'; +import { resolve } from 'path'; + +export class MidwayDevFramework + implements IMidwayFramework { + public app: Application; + public configurationOptions: IMidwayWebConfigurationOptions; + isTsMode: boolean; + + public getApplication(): Application { + return this.app; + } + + public getFrameworkType(): MidwayFrameworkType { + return MidwayFrameworkType.WEB; + } + + public async run(): Promise { + if (this.configurationOptions.port) { + new Promise(resolve => { + this.app.listen(this.configurationOptions.port, () => { + resolve(); + }); + }); + } + } + + configure(options: IMidwayWebConfigurationOptions): MidwayDevFramework { + this.configurationOptions = options; + return this; + } + + getApplicationContext(): IMidwayContainer { + return this.app.getApplicationContext(); + } + + getConfiguration(key?: string): any { + return this.app.getConfig(key); + } + + getCurrentEnvironment(): string { + return this.app.getEnv(); + } + + async initialize(options: Partial) { + const { start } = require('egg'); + this.app = await start({ + baseDir: options.appDir, + ignoreWarning: true, + framework: resolve(__dirname, 'application'), + plugins: this.configurationOptions.plugins, + mode: 'single', + isTsMode: this.isTsMode || true, + }); + } + + async stop(): Promise { + await this.app.close(); + } +} diff --git a/packages/web/src/framework.ts b/packages/web/src/framework.ts index c3f5bbee0671..58dce5add1c5 100644 --- a/packages/web/src/framework.ts +++ b/packages/web/src/framework.ts @@ -2,12 +2,17 @@ import { IMidwayBootstrapOptions, MidwayFrameworkType, MidwayProcessTypeEnum, + safelyGet, } from '@midwayjs/core'; -import { ControllerOption } from '@midwayjs/decorator'; +import { + ControllerOption, + CONFIG_KEY, + LOGGER_KEY, + PLUGIN_KEY, +} from '@midwayjs/decorator'; import { IMidwayWebConfigurationOptions } from './interface'; import { MidwayKoaBaseFramework } from '@midwayjs/koa'; import { EggRouter } from '@eggjs/router'; -import { resolve } from 'path'; import { Application, Context, Router } from 'egg'; export class MidwayWebFramework extends MidwayKoaBaseFramework< @@ -21,74 +26,99 @@ export class MidwayWebFramework extends MidwayKoaBaseFramework< priority: number; router: Router; }> = []; - public isClusterMode = false; public configure( options: IMidwayWebConfigurationOptions ): MidwayWebFramework { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; this.configurationOptions = options; if (options.typescript === false) { this.isTsMode = false; } this.app = options.app; - this.isClusterMode = !!this.app; + + this.defineApplicationProperties({ + generateController: (controllerMapping: string) => { + return this.generateController(controllerMapping); + }, + + generateMiddleware: async (middlewareId: string) => { + return this.generateMiddleware(middlewareId); + }, + + getProcessType: () => { + if (this.configurationOptions.processType === 'application') { + return MidwayProcessTypeEnum.APPLICATION; + } + if (this.configurationOptions.processType === 'agent') { + return MidwayProcessTypeEnum.AGENT; + } + + // TODO 单进程模式下区分进程类型?? + return MidwayProcessTypeEnum.APPLICATION; + }, + }); + + Object.defineProperty(this.app, 'applicationContext', { + get() { + return self.getApplicationContext(); + }, + }); + return this; } - protected async beforeInitialize(options: Partial) { + protected async beforeContainerInitialize( + options: Partial + ) { options.ignore = options.ignore || []; options.ignore.push('**/app/extend/**'); } - protected async afterDirectoryLoad( - options: Partial - ) { + async applicationInitialize(options: Partial) { // eslint-disable-next-line @typescript-eslint/no-this-alias const self = this; if (this.isTsMode) { process.env.EGG_TYPESCRIPT = 'true'; } - - if (!this.app) { - const { start } = require('egg'); - this.app = await start({ - baseDir: options.appDir, - ignoreWarning: true, - framework: resolve(__dirname, 'application'), - plugins: this.configurationOptions.plugins, - webFramework: this, - isClusterMode: this.isClusterMode, - mode: 'single', - isTsMode: this.isTsMode, - }); - - this.configurationOptions.globalConfig = this.app.config; - } - if (this.configurationOptions.globalConfig) { this.getApplicationContext() .getConfigService() .addObject(this.configurationOptions.globalConfig); - Object.defineProperty(this.app, 'config', { get() { return self.getConfiguration(); }, }); - // this.app.config = this.getConfiguration(); } - this.defineApplicationProperties(this.app); + // register plugin + this.getApplicationContext().registerDataHandler( + PLUGIN_KEY, + (key, target) => { + return this.app[key]; + } + ); + + // register config + this.getApplicationContext().registerDataHandler(CONFIG_KEY, key => { + return key ? safelyGet(key, this.app.config) : this.app.config; + }); + + // register logger + this.getApplicationContext().registerDataHandler(LOGGER_KEY, key => { + if (this.app.getLogger) { + return this.app.getLogger(key); + } + return this.app.coreLogger; + }); } - protected async afterInitialize( + protected async afterContainerReady( options: Partial - ): Promise { - if (this.configurationOptions.processType !== 'agent') { - await this.loadMidwayController(); - } - } + ): Promise {} public getApplication(): Application { return this.app; @@ -98,16 +128,14 @@ export class MidwayWebFramework extends MidwayKoaBaseFramework< return MidwayFrameworkType.WEB; } - public async run(): Promise { - if (this.configurationOptions.port) { - new Promise(resolve => { - this.app.listen(this.configurationOptions.port, () => { - resolve(); - }); - }); - } - } + /** + * 这个方法 egg-cluster 不走,只有单进程模式使用 @midwayjs/bootstrap 才会执行 + */ + public async run(): Promise {} + /** + * 这个方法 egg-cluster 不走,只有单进程模式使用 @midwayjs/bootstrap 才会执行 + */ protected async beforeStop(): Promise { await this.app.close(); } @@ -127,44 +155,4 @@ export class MidwayWebFramework extends MidwayKoaBaseFramework< } return null; } - - protected defineApplicationProperties(app): Application { - return Object.assign(app, { - getBaseDir: () => { - return this.baseDir; - }, - - getAppDir: () => { - return this.appDir; - }, - - getEnv: () => { - return this.getApplicationContext() - .getEnvironmentService() - .getCurrentEnvironment(); - }, - - getConfig: (key?: string) => { - return this.getApplicationContext() - .getConfigService() - .getConfiguration(key); - }, - - getFrameworkType: () => { - return this.getFrameworkType(); - }, - - getProcessType: () => { - if (this.configurationOptions.processType === 'application') { - return MidwayProcessTypeEnum.APPLICATION; - } - if (this.configurationOptions.processType === 'agent') { - return MidwayProcessTypeEnum.AGENT; - } - - // TODO 单进程模式下区分进程类型?? - return MidwayProcessTypeEnum.APPLICATION; - }, - }); - } } diff --git a/packages/web/src/index.ts b/packages/web/src/index.ts index 0c15d3d5865a..3a4a4f7d6d5c 100644 --- a/packages/web/src/index.ts +++ b/packages/web/src/index.ts @@ -1,5 +1,5 @@ export * from './interface'; -export { MidwayWebFramework as Framework } from './framework'; +export { MidwayDevFramework as Framework } from './devFramework'; export { createEggApplication, createEggAgent, @@ -9,19 +9,29 @@ export { // must export mock app here export { Application, Agent } from './application'; +import { IWebMiddleware, MidwayWebMiddleware } from './interface'; +import { providerWrapper as OriginProviderWrapper } from '@midwayjs/core'; /** - * @deprecated + * @deprecated Please use IWebMiddleware instead */ -import { IWebMiddleware } from './interface'; export type WebMiddleware = IWebMiddleware; -export { MidwayWebMiddleware as Middleware } from './interface'; +/** + * @deprecated Please use MidwayWebMiddleware instead + */ +export type Middleware = MidwayWebMiddleware; +/** + * @deprecated Please use MidwayWebMiddleware instead + */ export type KoaMiddleware = ( context: T, next: () => Promise ) => void; -export { providerWrapper } from '@midwayjs/core'; /** - * @deprecated + * @deprecated Please import from @midwayjs/core + */ +export const providerWrapper = OriginProviderWrapper; +/** + * @deprecated Please import from @midwayjs/decorator */ export { Provide as provide, @@ -55,6 +65,7 @@ export { ControllerOption, ScheduleOpts, ScopeEnum, + CommonSchedule, } from '@midwayjs/decorator'; /** diff --git a/packages/web/src/interface.ts b/packages/web/src/interface.ts index b6c033754864..a4da0a61abc3 100644 --- a/packages/web/src/interface.ts +++ b/packages/web/src/interface.ts @@ -26,7 +26,6 @@ declare module 'egg' { } interface Context { - getRequestContext(): IMidwayContainer; requestContext: IMidwayContainer; } } diff --git a/packages/web/test/enhance.test.ts b/packages/web/test/enhance.test.ts index cd6e93b060a0..6276053eb22e 100644 --- a/packages/web/test/enhance.test.ts +++ b/packages/web/test/enhance.test.ts @@ -7,7 +7,7 @@ import { creatApp, closeApp } from './utils'; const mm = require('mm'); const pedding = require('pedding'); -describe.skip('/test/enhance.test.ts', () => { +describe('/test/enhance.test.ts', () => { describe('load ts file', () => { let app; beforeAll(async () => { @@ -502,7 +502,7 @@ describe.skip('/test/enhance.test.ts', () => { }); }); - describe('should egg hackernew be ok', () => { + describe.skip('should egg hackernew be ok', () => { let app; beforeAll(async () => { app = await creatApp('enhance/base-app-hackernews', { @@ -539,9 +539,9 @@ describe.skip('/test/enhance.test.ts', () => { }); }); - afterAll(() => { + afterAll(async () => { mm.restore(); - return app.close(); + await closeApp(app); }); it('news should be ok', async () => { diff --git a/packages/web/test/fixtures/enhance/base-app-decorator/src/app/controller/param.ts b/packages/web/test/fixtures/enhance/base-app-decorator/src/app/controller/param.ts index d13d0ef23411..a2e2090ee154 100644 --- a/packages/web/test/fixtures/enhance/base-app-decorator/src/app/controller/param.ts +++ b/packages/web/test/fixtures/enhance/base-app-decorator/src/app/controller/param.ts @@ -1,4 +1,5 @@ import { provide, inject, controller, config, get, post, query, param, files, file, session, body, headers } from '../../../../../../../src'; +import { ALL } from '@midwayjs/decorator'; import * as path from 'path'; import * as fs from 'fs'; @@ -16,12 +17,12 @@ export class ParamController { ctx: any; @get('/query') - async query(@query() query) { - this.ctx.body = query; + async query(@query(ALL) query) { + this.ctx.body = query; } @get('/:id/test') - async test(@query() query, @param('id') id) { + async test(@query(ALL) query, @param('id') id) { const data = { id, ...query @@ -35,7 +36,7 @@ export class ParamController { } @get('/param/:id/test/:userId') - async param(@param() param) { + async param(@param(ALL) param) { // service,hello,a,b this.ctx.body = param; } @@ -46,7 +47,7 @@ export class ParamController { } @post('/body') - async body(@body() body) { + async body(@body(ALL) body) { this.ctx.body = body; } @@ -81,13 +82,13 @@ export class ParamController { } @get('/session') - async session(@session() session) { + async session(@session(ALL) session) { // service,hello,a,b this.ctx.body = session; } @get('/headers') - async header(@headers() headers) { + async header(@headers(ALL) headers) { // service,hello,a,b this.ctx.body = headers.host.substring(0, 3); } diff --git a/packages/web/test/fixtures/enhance/base-app-decorator/src/config/plugin.ts b/packages/web/test/fixtures/enhance/base-app-decorator/src/config/plugin.ts index 077c3c1a4790..b28ead5cc018 100644 --- a/packages/web/test/fixtures/enhance/base-app-decorator/src/config/plugin.ts +++ b/packages/web/test/fixtures/enhance/base-app-decorator/src/config/plugin.ts @@ -2,7 +2,7 @@ import * as path from 'path'; module.exports = { // 默认开启的插件 - + static: true, /** * 支持各个 bu 的健康检查 */ diff --git a/packages/web/test/fixtures/feature/base-app-component-config/src/component/sql/src/configuration.ts b/packages/web/test/fixtures/feature/base-app-component-config/src/component/sql/src/configuration.ts index 281171d0a828..25880a332ff2 100644 --- a/packages/web/test/fixtures/feature/base-app-component-config/src/component/sql/src/configuration.ts +++ b/packages/web/test/fixtures/feature/base-app-component-config/src/component/sql/src/configuration.ts @@ -1,9 +1,10 @@ // src/configuration.ts -import { Configuration, Config } from '@midwayjs/decorator'; +import { Configuration, Config, App } from '@midwayjs/decorator'; +import { join } from 'path'; @Configuration({ importConfigs: [ - './config/' + join(__dirname, './config/') ], }) export class ContainerLifeCycle { @@ -11,7 +12,11 @@ export class ContainerLifeCycle { @Config() mock; + @App() + app; + onReady() { + console.log(this.app); console.log(this.mock); } -} \ No newline at end of file +} diff --git a/packages/web/test/issue.test.ts b/packages/web/test/issue.test.ts index a17a4e8c6a19..9c3b4c16aeb8 100644 --- a/packages/web/test/issue.test.ts +++ b/packages/web/test/issue.test.ts @@ -36,3 +36,4 @@ describe('/test/issue.test.ts', () => { await closeApp(app); }); }); +