From b7df00b15e434c533b87db908eda3506c846d0fb Mon Sep 17 00:00:00 2001 From: popomore Date: Tue, 2 Aug 2016 17:52:30 +0800 Subject: [PATCH 01/18] refactor: loadServerEnv -> getServerEnv --- lib/base_loader.js | 88 ++++++++++++++++++------------------- test/egg_loader.test.js | 35 --------------- test/get_server_env.test.js | 42 ++++++++++++++++++ 3 files changed, 85 insertions(+), 80 deletions(-) create mode 100644 test/get_server_env.test.js diff --git a/lib/base_loader.js b/lib/base_loader.js index 9ab75658..30989981 100644 --- a/lib/base_loader.js +++ b/lib/base_loader.js @@ -69,24 +69,52 @@ class EggLoader { this.eggPaths = [ this.eggPath ].concat(this.frameworkPaths); debug('Loaded eggPaths %j', this.eggPaths); - /** - * 获取当前应用所在的机器环境,统一 serverEnv - * ``` - * serverEnv | 说明 - * --- | --- - * default | 默认环境 - * test | 交付测试 - * prod | 主站生产环境,包括预发,线上服务器 - * local | 本地开发环境,就是你的电脑本地启动 - * unittest | 单元测试环境,tnpm test, NODE_ENV=test - * ``` - * - * @member {String} EggLoader#serverEnv - */ - this.serverEnv = this.loadServerEnv(); + this.serverEnv = this.getServerEnv(); debug('Loaded serverEnv %j', this.serverEnv); } + /** + * Get environment of Egg, it's not NODE_ENV + * + * 1. from `$baseDir/config/serverEnv` + * 2. from EGG_SERVER_ENV + * 3. from NODE_ENV + * + * serverEnv | description + * --- | --- + * default | default environment + * test | system integration testing + * prod | production + * local | local on your own computer + * unittest | unit test + * + * @return {String} serverEnv + */ + getServerEnv() { + let serverEnv; + + const envPath = path.join(this.options.baseDir, 'config/serverEnv'); + if (fs.existsSync(envPath)) { + serverEnv = fs.readFileSync(envPath, 'utf8').trim(); + } + + if (!serverEnv) { + serverEnv = process.env.EGG_SERVER_ENV; + } + + if (!serverEnv) { + if (process.env.NODE_ENV === 'test') { + serverEnv = 'unittest'; + } else if (process.env.NODE_ENV === 'production') { + serverEnv = 'default'; + } else { + serverEnv = 'local'; + } + } + + return serverEnv; + } + /** * 加载自定义的 app.js,**在 app.js 可做任何操作,但建议尽量减少此操作,做该做的事**。 * @@ -203,36 +231,6 @@ class EggLoader { return dirs; } - /** - * 获取环境变量 - * - * 1. 从 EGG_SERVER_ENV 获取,一般用于测试 - * 2. 从 `$baseDir/config/serverEnv` 读取,框架可根据实际情况自行设置 - * 3. 默认值 - * - * @return {String} serverEnv - * @see EggLoader#serverEnv - */ - loadServerEnv() { - let serverEnv = process.env.EGG_SERVER_ENV; - - const envPath = path.join(this.options.baseDir, 'config/serverEnv'); - if (fs.existsSync(envPath)) { - serverEnv = fs.readFileSync(envPath, 'utf8').trim(); - } - - if (!serverEnv) { - if (process.env.NODE_ENV === 'test') { - serverEnv = 'unittest'; - } else if (process.env.NODE_ENV === 'production') { - serverEnv = 'default'; - } else { - serverEnv = 'local'; - } - } - - return serverEnv; - } /** * 获取 {@link EggLoader#frameworkPaths} diff --git a/test/egg_loader.test.js b/test/egg_loader.test.js index d1fd5b8a..78a209f3 100644 --- a/test/egg_loader.test.js +++ b/test/egg_loader.test.js @@ -10,41 +10,6 @@ describe('test/egg_loader.test.js', function() { afterEach(mm.restore); - describe('.getServerEnv', function() { - - it('should get from env EGG_SERVER_ENV', function() { - mm(process.env, 'EGG_SERVER_ENV', 'prod'); - const app = utils.createApp('serverenv'); - app.loader.serverEnv.should.equal('prod'); - }); - - it('should use unittest when NODE_ENV = test', function() { - mm(process.env, 'NODE_ENV', 'test'); - const app = utils.createApp('serverenv'); - app.loader.serverEnv.should.equal('unittest'); - }); - - it('should use default when NODE_ENV = production', function() { - mm(process.env, 'NODE_ENV', 'production'); - const app = utils.createApp('serverenv'); - app.loader.serverEnv.should.equal('default'); - }); - - it('should use local when NODE_ENV is other', function() { - mm(process.env, 'NODE_ENV', 'development'); - const app = utils.createApp('serverenv'); - app.loader.serverEnv.should.equal('local'); - }); - - it('should get from config/serverEnv', function() { - mm(process.env, 'NODE_ENV', 'production'); - mm(process.env, 'EGG_SERVER_ENV', 'test'); - const app = utils.createApp('serverenv-file'); - app.loader.serverEnv.should.equal('prod'); - }); - }); - - describe('eggPaths', function() { it('should get from paramter', function() { diff --git a/test/get_server_env.test.js b/test/get_server_env.test.js new file mode 100644 index 00000000..a6c99fc1 --- /dev/null +++ b/test/get_server_env.test.js @@ -0,0 +1,42 @@ +'use strict'; + +require('should'); +const mm = require('mm'); +const utils = require('./utils'); + +describe('test/get_server_env.test.js', function() { + + afterEach(mm.restore); + + it('should get from env EGG_SERVER_ENV', function() { + mm(process.env, 'EGG_SERVER_ENV', 'prod'); + const app = utils.createApp('serverenv'); + app.loader.serverEnv.should.equal('prod'); + }); + + it('should use unittest when NODE_ENV = test', function() { + mm(process.env, 'NODE_ENV', 'test'); + const app = utils.createApp('serverenv'); + app.loader.serverEnv.should.equal('unittest'); + }); + + it('should use default when NODE_ENV = production', function() { + mm(process.env, 'NODE_ENV', 'production'); + const app = utils.createApp('serverenv'); + app.loader.serverEnv.should.equal('default'); + }); + + it('should use local when NODE_ENV is other', function() { + mm(process.env, 'NODE_ENV', 'development'); + const app = utils.createApp('serverenv'); + app.loader.serverEnv.should.equal('local'); + }); + + it('should get from config/serverEnv', function() { + mm(process.env, 'NODE_ENV', 'production'); + mm(process.env, 'EGG_SERVER_ENV', 'test'); + const app = utils.createApp('serverenv-file'); + app.loader.serverEnv.should.equal('prod'); + }); + +}); From 36995624aa1579b19b9702a6ca13c666070bb90e Mon Sep 17 00:00:00 2001 From: popomore Date: Wed, 3 Aug 2016 00:20:56 +0800 Subject: [PATCH 02/18] refactor: loadFrameworkPaths -> getEggPaths Load order has changed from `app > framework > plugin > egg` to `app > framework(contain egg) > plugin` Remove options.eggPath, egg is one of the framework. Remove options.customEgg, every framework should specify `Symbol.for('egg#eggPath')` --- lib/base_loader.js | 147 ++++++++---------- lib/loader.js | 6 +- lib/loader/config.js | 0 test/egg_loader.test.js | 48 ------ test/fixtures/custom-app/app/router.js | 2 +- test/fixtures/custom-framework/index.js | 14 +- test/fixtures/egg/index.js | 12 ++ test/fixtures/egg/package.json | 3 + test/fixtures/framework/index.js | 16 -- .../framework/lib/core/config/config.js | 5 - .../node_modules/framework2/index.js | 16 -- .../framework2/lib/core/config/config.js | 5 - .../node_modules/framework2/package.json | 3 - test/fixtures/framework/package.json | 3 - test/get_framework_paths.test.js | 62 ++++++++ test/load_middleware.test.js | 4 +- test/load_plugin.test.js | 7 +- test/utils.js | 16 +- 18 files changed, 173 insertions(+), 196 deletions(-) create mode 100644 lib/loader/config.js create mode 100644 test/fixtures/egg/index.js create mode 100644 test/fixtures/egg/package.json delete mode 100644 test/fixtures/framework/index.js delete mode 100644 test/fixtures/framework/lib/core/config/config.js delete mode 100644 test/fixtures/framework/node_modules/framework2/index.js delete mode 100644 test/fixtures/framework/node_modules/framework2/lib/core/config/config.js delete mode 100644 test/fixtures/framework/node_modules/framework2/package.json delete mode 100644 test/fixtures/framework/package.json create mode 100644 test/get_framework_paths.test.js diff --git a/lib/base_loader.js b/lib/base_loader.js index 30989981..dbdaab99 100644 --- a/lib/base_loader.js +++ b/lib/base_loader.js @@ -7,6 +7,7 @@ const isFunction = require('is-type-of').function; const interopRequire = require('interop-require'); const debug = require('debug')('egg:loader'); const Loader = require('./loader'); +const Emitter = require('events').EventEmitter; class EggLoader { @@ -14,17 +15,17 @@ class EggLoader { * @constructor * @param {Object} options * - {String} [baseDir] - 应用根目录 - * - {String} [eggPath] - 使用 egg-loader 的框架的路径,如 egg - * - {String} [customEgg] - 自定义入口框架的路径,每个异构技术 bu 都可以自定义自己的插件集合 - * - {Object} [plugins] - 自定义插件配置,测试用 * - {Object} [app] - app 实例,如果是 Agent Worker 则传入 agent 实例,可为空 + * - {Object} [plugins] - 自定义插件配置,测试用 * - {Logger} [logger] - logger 实例,默认是 console */ constructor(options) { - this.options = options || {}; + options = options || {}; + assert(fs.existsSync(options.baseDir), `${options.baseDir} not exists`); + + this.options = options; this.options.logger = this.options.logger || console; this.app = this.options.app || {}; // master 没有 app - assert(fs.existsSync(this.options.baseDir), `${this.options.baseDir} not exists`); /** * 读取 package.json @@ -33,48 +34,22 @@ class EggLoader { this.pkg = require(path.join(this.options.baseDir, 'package.json')); /** - * 初始化时传入,见 {@link EggLoader} - * @member {String} EggLoader#eggPath - */ - this.eggPath = fs.realpathSync(this.options.eggPath); - debug('Loaded eggPath %j', this.eggPath); - - /** - * 框架可以继承,从入口框架(CustomEgg)找到所有的框架的根目录 - * - * 需要通过配置 getter 来指定 eggPath 才能被加载到 - * - * ``` - * // lib/xx.js - * const egg = require('egg'); - * class XxApplication extends egg.Application { - * constructor(options) { - * super(options); - * } - * - * get [Symbol.for('egg#eggPath')]() { - * return path.join(__dirname, '..'); - * } - * } - * ``` - * @member {Array} EggLoader#frameworkPaths - */ - this.frameworkPaths = this.loadFrameworkPaths(); - debug('Loaded frameworkPaths %j', this.frameworkPaths); - - /** - * = this.eggPath + this.frameworkPaths * @member {Array} EggLoader#eggPaths + * @see EggLoader#getEggPaths */ - this.eggPaths = [ this.eggPath ].concat(this.frameworkPaths); + this.eggPaths = this.getEggPaths(); debug('Loaded eggPaths %j', this.eggPaths); + /** + * @member {String} EggLoader#serverEnv + * @see EggLoader#getServerEnv + */ this.serverEnv = this.getServerEnv(); debug('Loaded serverEnv %j', this.serverEnv); } /** - * Get environment of Egg, it's not NODE_ENV + * Get environment of Egg, **it's not NODE_ENV** * * 1. from `$baseDir/config/serverEnv` * 2. from EGG_SERVER_ENV @@ -115,6 +90,53 @@ class EggLoader { return serverEnv; } + /** + * Get all framework directories. + * + * You can extend Application of egg, the extrypoint is options.app, + * + * loader will find all directories from the prototype of Application, + * you should define `Symbol.for('egg#eggPath')` property. + * + * ``` + * // lib/xx.js + * const egg = require('egg'); + * class XxApplication extends egg.Application { + * constructor(options) { + * super(options); + * } + * + * get [Symbol.for('egg#eggPath')]() { + * return path.join(__dirname, '..'); + * } + * } + * ``` + * + * @return {Array} framework directories + */ + getEggPaths() { + const eggPaths = []; + + let proto = this.app; + while (proto) { + proto = Object.getPrototypeOf(proto); + if (proto) { + if (isKoa(proto)) { + break; + } + const eggPath = proto[Symbol.for('egg#eggPath')]; + assert(eggPath, 'Symbol.for(\'egg#eggPath\') is required on Application'); + // 使用 fs.realpathSync 来找到最终路径 + const realpath = fs.realpathSync(eggPath); + if (eggPaths.indexOf(realpath) === -1) { + eggPaths.unshift(realpath); + } + } + } + + return eggPaths; + } + /** * 加载自定义的 app.js,**在 app.js 可做任何操作,但建议尽量减少此操作,做该做的事**。 * @@ -209,9 +231,6 @@ class EggLoader { const dirs = this.dirs = []; - // egg 本身路径,在 lib/core 目录下 - dirs.push(path.join(this.eggPath, 'lib/core')); - // 插件目录,master 没有 plugin if (this.orderPlugins) { for (const plugin of this.orderPlugins) { @@ -220,8 +239,8 @@ class EggLoader { } // egg 框架路径,在 lib/core 目录下 - for (const frameworkPath of this.frameworkPaths) { - dirs.push(path.join(frameworkPath, 'lib/core')); + for (const eggPath of this.eggPaths) { + dirs.push(path.join(eggPath, 'lib/core')); } // 应用目录 @@ -231,42 +250,6 @@ class EggLoader { return dirs; } - - /** - * 获取 {@link EggLoader#frameworkPaths} - * @return {Array} 框架目录 - * @private - */ - loadFrameworkPaths() { - const eggPath = this.eggPath; - const frameworkPaths = []; - - addEggPath(this.options.customEgg); - - // 遍历整个原型链,获取原型链上所有的 eggPath - // 越核心的优先级越高 - let proto = this.app; - while (proto) { - proto = Object.getPrototypeOf(proto); - if (proto) { - const eggPath = proto[Symbol.for('egg#eggPath')]; - addEggPath(eggPath); - } - } - - return frameworkPaths; - - function addEggPath(dirpath) { - if (dirpath) { - // 使用 fs.realpathSync 来找到最终路径 - const realpath = fs.realpathSync(dirpath); - if (frameworkPaths.indexOf(realpath) === -1 && realpath !== eggPath) { - frameworkPaths.unshift(realpath); - } - } - } - } - /** * 返回应用 appname,默认获取 pkg.name * @@ -316,3 +299,9 @@ for (const loader of loaders) { } module.exports = EggLoader; + +function isKoa(app) { + return app.hasOwnProperty('use') && + app.hasOwnProperty('listen') && + Object.getPrototypeOf(app) === Emitter.prototype; +} diff --git a/lib/loader.js b/lib/loader.js index a0ebf96d..1d4b53d3 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -93,13 +93,11 @@ function getProperties(filepath, lowercaseFirst) { return filepath .replace('.js', '') .split('/') - .map(function(property) { + .map(property => { if (!/^[a-z][a-z0-9_-]*$/i.test(property)) { throw new Error(`${property} is not match 'a-z0-9_-' in ${filepath}`); } - let result = property.replace(/[_-][a-z]/ig, function(s) { - return s.substring(1).toUpperCase(); - }); + let result = property.replace(/[_-][a-z]/ig, s => s.substring(1).toUpperCase()); if (lowercaseFirst) { result = result[0].toLowerCase() + result.substring(1); } diff --git a/lib/loader/config.js b/lib/loader/config.js new file mode 100644 index 00000000..e69de29b diff --git a/test/egg_loader.test.js b/test/egg_loader.test.js index 78a209f3..fe578af2 100644 --- a/test/egg_loader.test.js +++ b/test/egg_loader.test.js @@ -1,61 +1,13 @@ 'use strict'; require('should'); -const path = require('path'); const mm = require('mm'); const utils = require('./utils'); -const BaseLoader = require('../index'); describe('test/egg_loader.test.js', function() { afterEach(mm.restore); - describe('eggPaths', function() { - - it('should get from paramter', function() { - const app = utils.createApp('eggpath'); - app.loader.eggPath.should.equal(utils.getFilepath('egg')); - }); - - it('should get from framework', function() { - const Application = require('./fixtures/framework'); - const app = new Application(); - app.coreLogger = console; - app.loader = new utils.Loader('eggpath', { app }); - app.loader.loadConfig(); - app.loader.eggPaths.should.eql([ - utils.getFilepath('egg'), - utils.getFilepath('framework/node_modules/framework2'), - utils.getFilepath('framework'), - ]); - return app; - }); - - it('should get from framework using symbol', function() { - const Application = require('./fixtures/framework-symbol'); - const app = new Application(); - app.coreLogger = console; - app.loader = new utils.Loader('eggpath', { app }); - app.loader.loadConfig(); - app.loader.eggPaths.should.eql([ - utils.getFilepath('egg'), - utils.getFilepath('framework-symbol/node_modules/framework2'), - utils.getFilepath('framework-symbol'), - ]); - return app; - }); - - it('frameworkPaths should not container eggPath', function() { - const eggPath = path.join(__dirname, 'fixtures/egg'); - const loader = new BaseLoader({ - baseDir: path.join(__dirname, 'fixtures/eggpath'), - eggPath, - customEgg: eggPath, - }); - loader.frameworkPaths.should.not.containEql(eggPath); - }); - }); - describe('loadDirs', function() { diff --git a/test/fixtures/custom-app/app/router.js b/test/fixtures/custom-app/app/router.js index 6c206ce5..9faa2f59 100644 --- a/test/fixtures/custom-app/app/router.js +++ b/test/fixtures/custom-app/app/router.js @@ -6,7 +6,7 @@ module.exports = function(app) { customFoo: app.customFoo, env: app.config.env, eggPaths: app.loader.eggPaths, - frameworkPaths: app.loader.frameworkPaths, + eggPaths: app.loader.eggPaths, eggPath: app.loader.eggPath, }; }); diff --git a/test/fixtures/custom-framework/index.js b/test/fixtures/custom-framework/index.js index 174cbddd..7f8e388e 100644 --- a/test/fixtures/custom-framework/index.js +++ b/test/fixtures/custom-framework/index.js @@ -1,9 +1,11 @@ 'use strict'; -module.exports = require('../../../'); -const startCluster = module.exports.startCluster; +const KoaApplication = require('koa'); -module.exports.startCluster = function(options) { - options.customEgg = __dirname; - return startCluster(options); -}; +class Application extends KoaApplication { + get [Symbol.for('egg#eggPath')]() { + return __dirname; + } +} + +module.exports = Application; diff --git a/test/fixtures/egg/index.js b/test/fixtures/egg/index.js new file mode 100644 index 00000000..a08983aa --- /dev/null +++ b/test/fixtures/egg/index.js @@ -0,0 +1,12 @@ +'use strict'; + +const path = require('path'); +const KoaApplication = require('koa'); + +class EggApplication extends KoaApplication { + get [Symbol.for('egg#eggPath')]() { + return __dirname; + } +} + +module.exports = EggApplication; diff --git a/test/fixtures/egg/package.json b/test/fixtures/egg/package.json new file mode 100644 index 00000000..6697ad3f --- /dev/null +++ b/test/fixtures/egg/package.json @@ -0,0 +1,3 @@ +{ + "name": "egg" +} diff --git a/test/fixtures/framework/index.js b/test/fixtures/framework/index.js deleted file mode 100644 index 19d91e50..00000000 --- a/test/fixtures/framework/index.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -const Application = require('framework2'); - -class Framework extends Application { - - constructor(options) { - super(options); - } - - get [Symbol.for('egg#eggPath')]() { - return __dirname; - } -} - -module.exports = Framework; diff --git a/test/fixtures/framework/lib/core/config/config.js b/test/fixtures/framework/lib/core/config/config.js deleted file mode 100644 index 1e2c0832..00000000 --- a/test/fixtures/framework/lib/core/config/config.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict'; - -module.exports = { - framework1: 'framework1', -}; diff --git a/test/fixtures/framework/node_modules/framework2/index.js b/test/fixtures/framework/node_modules/framework2/index.js deleted file mode 100644 index 7a2763c2..00000000 --- a/test/fixtures/framework/node_modules/framework2/index.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -const Application = require('koa'); - -class Framework2 extends Application { - - constructor(options) { - super(options); - } - - get [Symbol.for('egg#eggPath')]() { - return __dirname; - } -} - -module.exports = Framework2; diff --git a/test/fixtures/framework/node_modules/framework2/lib/core/config/config.js b/test/fixtures/framework/node_modules/framework2/lib/core/config/config.js deleted file mode 100644 index b024831a..00000000 --- a/test/fixtures/framework/node_modules/framework2/lib/core/config/config.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict'; - -module.exports = { - framework2: 'framework2', -}; diff --git a/test/fixtures/framework/node_modules/framework2/package.json b/test/fixtures/framework/node_modules/framework2/package.json deleted file mode 100644 index eca4a5fe..00000000 --- a/test/fixtures/framework/node_modules/framework2/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "framework2" -} diff --git a/test/fixtures/framework/package.json b/test/fixtures/framework/package.json deleted file mode 100644 index b5d2dde7..00000000 --- a/test/fixtures/framework/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "framework" -} diff --git a/test/get_framework_paths.test.js b/test/get_framework_paths.test.js new file mode 100644 index 00000000..2022696a --- /dev/null +++ b/test/get_framework_paths.test.js @@ -0,0 +1,62 @@ +'use strict'; + +require('should'); +const mm = require('mm'); +const utils = require('./utils'); +const Loader = require('../lib/base_loader'); +const EggApplication = require('./fixtures/egg'); +const KoaApplication = require('koa'); + +describe('test/get_framework_paths.test.js', function() { + + afterEach(mm.restore); + + it('should get from paramter', function() { + const loader = new Loader({ + baseDir: utils.getFilepath('eggpath'), + app: new EggApplication(), + }); + loader.eggPaths.should.eql([ utils.getFilepath('egg') ]); + }); + + it('should get from framework using symbol', function() { + const Application = require('./fixtures/framework-symbol'); + const loader = new Loader({ + baseDir: utils.getFilepath('eggpath'), + app: new Application(), + }); + loader.eggPaths.should.eql([ + utils.getFilepath('framework-symbol/node_modules/framework2'), + utils.getFilepath('framework-symbol'), + ]); + }); + + it('should throw when Application not extends koa', () => { + class Application { + get [Symbol.for('egg#eggPath')]() { + return __dirname; + } + } + (function() { + new Loader({ + baseDir: utils.getFilepath('eggpath'), + app: new Application(), + }); + }).should.throw('Symbol.for(\'egg#eggPath\') is required on Application'); + }); + + it('should throw when one of the Application do not specify symbol', () => { + class Application extends KoaApplication {} + class Application2 extends Application { + get [Symbol.for('egg#eggPath')]() { + return __dirname; + } + } + (function() { + new Loader({ + baseDir: utils.getFilepath('eggpath'), + app: new Application2(), + }); + }).should.throw('Symbol.for(\'egg#eggPath\') is required on Application'); + }); +}); diff --git a/test/load_middleware.test.js b/test/load_middleware.test.js index cb801241..7a50fc4d 100644 --- a/test/load_middleware.test.js +++ b/test/load_middleware.test.js @@ -19,10 +19,10 @@ describe('test/load_middleware.test.js', function() { app.middlewares.should.not.have.property('a'); }); - it('should override middlewares of egg by plugin', function(done) { + it('should override middlewares of plugin by framework', function(done) { request(app.callback()) .get('/status') - .expect('status') + .expect('egg status') .end(done); }); diff --git a/test/load_plugin.test.js b/test/load_plugin.test.js index 8c159e73..2d31b022 100644 --- a/test/load_plugin.test.js +++ b/test/load_plugin.test.js @@ -310,19 +310,18 @@ describe('test/load_plugin.test.js', function() { }); it('should load multi framework', function() { - const customEgg = utils.getFilepath('custom-framework'); + const Application = require(utils.getFilepath('custom-framework')); const loader = new Loader('custom-app', { - customEgg, + app: new Application(), }); loader.loadConfig(); - console.log(loader.plugins); loader.plugins.foo.should.eql({ name: 'foo', enable: true, dep: [], env: [], - path: path.join(customEgg, 'lib/plugins/foo'), + path: utils.getFilepath('custom-framework/lib/plugins/foo'), }); should.not.exists(loader.plugins.depd); diff --git a/test/utils.js b/test/utils.js index aec7af1b..04c42a9c 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,16 +1,24 @@ 'use strict'; const path = require('path'); -const koa = require('koa'); +const KoaApplication = require('koa'); const Router = require('koa-router'); const BaseLoader = require('..'); +class EggApplication extends KoaApplication { + get [Symbol.for('egg#eggPath')]() { + return path.join(__dirname, 'fixtures/egg'); + } +} + class TestLoader extends BaseLoader { constructor(name, options) { options = options || {}; + if (!options.app) { + options.app = new EggApplication(); + } options.baseDir = path.join(__dirname, 'fixtures', name); - options.eggPath = path.join(__dirname, 'fixtures/egg'); super(options); } @@ -51,7 +59,7 @@ module.exports = { createApp(name, options) { options = options || {}; - const app = koa(); + const app = new EggApplication(); options.app = app; app.coreLogger = console; app.loader = new this.Loader(name, options); @@ -64,7 +72,7 @@ module.exports = { createAgent(name, options) { options = options || {}; - const agent = {}; + const agent = new EggApplication(); options.app = agent; agent.coreLogger = console; agent.loader = new this.Loader(name, options); From dffb7bc42011ca86f8833f82cdcb71bf0efce4d1 Mon Sep 17 00:00:00 2001 From: popomore Date: Wed, 3 Aug 2016 12:11:24 +0800 Subject: [PATCH 03/18] refactor: export loadExtend changes - loadApplication > loadApplicationExtend - loadContext > loadContextExtend - loadRequest > loadRequestExtend - loadResponse > loadResponseExtend - loadHelper > loadHelperExtend --- lib/base_loader.js | 4 +- lib/{extend_loader.js => loader/extend.js} | 44 +++++++++++----------- test/utils.js | 12 +++--- 3 files changed, 29 insertions(+), 31 deletions(-) rename lib/{extend_loader.js => loader/extend.js} (75%) diff --git a/lib/base_loader.js b/lib/base_loader.js index dbdaab99..19138a1a 100644 --- a/lib/base_loader.js +++ b/lib/base_loader.js @@ -5,7 +5,7 @@ const path = require('path'); const assert = require('assert'); const isFunction = require('is-type-of').function; const interopRequire = require('interop-require'); -const debug = require('debug')('egg:loader'); +const debug = require('debug')('egg-loader'); const Loader = require('./loader'); const Emitter = require('events').EventEmitter; @@ -288,7 +288,7 @@ class EggLoader { const loaders = [ require('./plugin_loader'), require('./config_loader'), - require('./extend_loader'), + require('./loader/extend'), require('./proxy_loader'), require('./service_loader'), require('./middleware_loader'), diff --git a/lib/extend_loader.js b/lib/loader/extend.js similarity index 75% rename from lib/extend_loader.js rename to lib/loader/extend.js index 109a4ec5..d2680b24 100644 --- a/lib/extend_loader.js +++ b/lib/loader/extend.js @@ -3,10 +3,8 @@ const fs = require('fs'); const path = require('path'); const interopRequire = require('interop-require'); -const utils = require('./utils'); -const debug = require('debug')('egg:extend:loader'); - -const loadExtend = Symbol('loadExtend'); +const debug = require('debug')('egg-loader:extend'); +const utils = require('../utils'); module.exports = { @@ -14,61 +12,61 @@ module.exports = { * 扩展 Agent.prototype 的属性 * * 可加载路径查看 {@link EggLoader#loadDirs} - * @method EggLoader#loadAgent + * @method EggLoader#loadAgentExtend */ - loadAgent() { - this[loadExtend]('agent', this.app); + loadAgentExtend() { + this.loadExtend('agent', this.app); }, /** * 扩展 Application.prototype 的属性 * * 可加载路径查看 {@link EggLoader#loadDirs} - * @method EggLoader#loadApplication + * @method EggLoader#loadApplicationExtend */ - loadApplication() { - this[loadExtend]('application', this.app); + loadApplicationExtend() { + this.loadExtend('application', this.app); }, /** * 扩展 Request.prototype 的属性 * * 可加载路径查看 {@link EggLoader#loadDirs} - * @method EggLoader#loadRequest + * @method EggLoader#loadRequestExtend */ - loadRequest() { - this[loadExtend]('request', this.app.request); + loadRequestExtend() { + this.loadExtend('request', this.app.request); }, /** * 扩展 Response.prototype 的属性 * * 可加载路径查看 {@link EggLoader#loadDirs} - * @method EggLoader#loadResponse + * @method EggLoader#loadResponseExtend */ - loadResponse() { - this[loadExtend]('response', this.app.response); + loadResponseExtend() { + this.loadExtend('response', this.app.response); }, /** * 扩展 Context.prototype 的属性 * * 可加载路径查看 {@link EggLoader#loadDirs} - * @method EggLoader#loadContext + * @method EggLoader#loadContextExtend */ - loadContext() { - this[loadExtend]('context', this.app.context); + loadContextExtend() { + this.loadExtend('context', this.app.context); }, /** * 扩展 app.Helper.prototype 的属性 * * 可加载路径查看 {@link EggLoader#loadDirs} - * @method EggLoader#loadHelper + * @method EggLoader#loadHelperExtend */ - loadHelper() { + loadHelperExtend() { if (this.app && this.app.Helper) { - this[loadExtend]('helper', this.app.Helper.prototype); + this.loadExtend('helper', this.app.Helper.prototype); } }, @@ -80,7 +78,7 @@ module.exports = { * @param {Object} proto - 最终将属性合并到 proto 上 * @private */ - [loadExtend](name, proto) { + loadExtend(name, proto) { // 获取需要加载的文件 const filepaths = this.loadDirs() .map(dir => { diff --git a/test/utils.js b/test/utils.js index 04c42a9c..bf4a67d5 100644 --- a/test/utils.js +++ b/test/utils.js @@ -28,11 +28,11 @@ class TestLoader extends BaseLoader { } load() { - this.loadApplication(); - this.loadRequest(); - this.loadResponse(); - this.loadContext(); - this.loadHelper(); + this.loadApplicationExtend(); + this.loadRequestExtend(); + this.loadResponseExtend(); + this.loadContextExtend(); + this.loadHelperExtend(); this.loadCustomApp(); this.loadProxy(); @@ -79,7 +79,7 @@ module.exports = { agent.loader.loadConfig(); agent.config = agent.loader.config; agent.antx = agent.loader.antx; - agent.loader.loadAgent(); + agent.loader.loadAgentExtend(); agent.loader.loadCustomAgent(); return agent; }, From 16b04223e6f1b8a800b9504da53a2010e207cdb3 Mon Sep 17 00:00:00 2001 From: popomore Date: Wed, 3 Aug 2016 13:13:52 +0800 Subject: [PATCH 04/18] test: add testcase for getAppname --- lib/base_loader.js | 28 +++++++++++++----------- test/fixtures/app-noname/package.json | 1 + test/fixtures/appname/package.json | 3 +++ test/get_appname.test.js | 31 +++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 13 deletions(-) create mode 100644 test/fixtures/app-noname/package.json create mode 100644 test/fixtures/appname/package.json create mode 100644 test/get_appname.test.js diff --git a/lib/base_loader.js b/lib/base_loader.js index 19138a1a..8e71be9f 100644 --- a/lib/base_loader.js +++ b/lib/base_loader.js @@ -90,6 +90,21 @@ class EggLoader { return serverEnv; } + /** + * 返回应用 appname,默认获取 pkg.name + * + * @return {String} appname + * @private + */ + getAppname() { + if (this.pkg.name) { + debug('Loaded appname(%s) from package.json', this.pkg.name); + return this.pkg.name; + } + const pkg = path.join(this.options.baseDir, 'package.json'); + throw new Error(`name is required from ${pkg}`); + } + /** * Get all framework directories. * @@ -250,19 +265,6 @@ class EggLoader { return dirs; } - /** - * 返回应用 appname,默认获取 pkg.name - * - * @return {String} appname - * @private - */ - getAppname() { - if (this.pkg.name) { - debug('Loaded appname(%s) from package.json', this.pkg.name); - return this.pkg.name; - } - throw new Error('Can not get appname from package.json'); - } loadTo(directory, target, opt) { opt = Object.assign({}, { diff --git a/test/fixtures/app-noname/package.json b/test/fixtures/app-noname/package.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/test/fixtures/app-noname/package.json @@ -0,0 +1 @@ +{} diff --git a/test/fixtures/appname/package.json b/test/fixtures/appname/package.json new file mode 100644 index 00000000..5a642d78 --- /dev/null +++ b/test/fixtures/appname/package.json @@ -0,0 +1,3 @@ +{ + "name": "appname" +} diff --git a/test/get_appname.test.js b/test/get_appname.test.js new file mode 100644 index 00000000..becb86a7 --- /dev/null +++ b/test/get_appname.test.js @@ -0,0 +1,31 @@ +'use strict'; + +require('should'); +const mm = require('mm'); +const utils = require('./utils'); +const Loader = require('../lib/base_loader'); +const EggApplication = require('./fixtures/egg'); + +describe('test/get_appname.test.js', function() { + + afterEach(mm.restore); + + it('should get appname', function() { + const loader = new Loader({ + baseDir: utils.getFilepath('appname'), + app: new EggApplication(), + }); + loader.getAppname().should.eql('appname'); + }); + + it('should throw when appname is not found', function() { + const loader = new Loader({ + baseDir: utils.getFilepath('app-noname'), + app: new EggApplication(), + }); + const pkg = utils.getFilepath('app-noname/package.json'); + (function() { + loader.getAppname(); + }).should.throw(`name is required from ${pkg}`); + }); +}); From f524daec250826878d3ec57766f3aaaf5bc69f73 Mon Sep 17 00:00:00 2001 From: popomore Date: Wed, 3 Aug 2016 13:43:39 +0800 Subject: [PATCH 05/18] refactor: move loadFile and more testcase --- lib/base_loader.js | 59 +++++++++++++--------------- lib/loader.js | 10 +---- lib/utils/index.js | 17 ++++++++ test/egg_loader.test.js | 8 ---- test/fixtures/load_file/function.js | 3 ++ test/fixtures/load_file/obj.js | 3 ++ test/fixtures/load_file/package.json | 3 ++ test/load_file.test.js | 36 +++++++++++++++++ 8 files changed, 91 insertions(+), 48 deletions(-) create mode 100644 test/fixtures/load_file/function.js create mode 100644 test/fixtures/load_file/obj.js create mode 100644 test/fixtures/load_file/package.json create mode 100644 test/load_file.test.js diff --git a/lib/base_loader.js b/lib/base_loader.js index 8e71be9f..782f647f 100644 --- a/lib/base_loader.js +++ b/lib/base_loader.js @@ -4,9 +4,9 @@ const fs = require('fs'); const path = require('path'); const assert = require('assert'); const isFunction = require('is-type-of').function; -const interopRequire = require('interop-require'); const debug = require('debug')('egg-loader'); const Loader = require('./loader'); +const loadFile = require('./utils').loadFile; const Emitter = require('events').EventEmitter; class EggLoader { @@ -91,7 +91,7 @@ class EggLoader { } /** - * 返回应用 appname,默认获取 pkg.name + * Get appname from pkg.name * * @return {String} appname * @private @@ -152,6 +152,31 @@ class EggLoader { return eggPaths; } + // Low Level API + + /** + * Load single file, will invork when export is function + * + * @param {String} filepath - fullpath + * @param {Array} arguments - pass rest arguments into the function when invork + * @return {Object} exports + * @example + * ```js + * app.loader.loadFile(path.join(app.options.baseDir, 'config/router.js')); + * ``` + */ + loadFile(filepath) { + if (!fs.existsSync(filepath)) { + return null; + } + + const ret = loadFile(filepath); + // function(arg1, args, ...) {} + let inject = Array.prototype.slice.call(arguments, 1); + if (inject.length === 0) inject = [ this.app ]; + return isFunction(ret) ? ret.apply(null, inject) : ret; + } + /** * 加载自定义的 app.js,**在 app.js 可做任何操作,但建议尽量减少此操作,做该做的事**。 * @@ -199,36 +224,6 @@ class EggLoader { app.coreLogger.info('[egg:loader] Controller loaded: %s', controllerBase); } - /** - * 加载指定文件,如果文件返回是函数则返回函数的调用结果,否则直接返回。 - * - * @param {String} filepath - 加载的文件路径 - * @param {Object} inject - 调用函数时的第一个参数,默认为 app - * @return {Object} - 返回加载文件的结果 - * @example - * ```js - * app.loader.loadFile(path.join(app.options.baseDir, 'config/router.js')); - * ``` - */ - loadFile(filepath) { - if (!fs.existsSync(filepath)) { - return null; - } - - let ret; - try { - ret = interopRequire(filepath); - } catch (err) { - err.message = `[egg-loader] load file ${filepath} error: ${err.message}`; - throw err; - } - // 可支持传入多个参数 - // function(arg1, args, ...) {} - let inject = Array.prototype.slice.call(arguments, 1); - if (inject.length === 0) inject = [ this.app ]; - return isFunction(ret) ? ret.apply(null, inject) : ret; - } - /** * 返回 egg 需要加载的目录 * diff --git a/lib/loader.js b/lib/loader.js index 1d4b53d3..c63ba6ee 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -5,8 +5,8 @@ const fs = require('fs'); const debug = require('debug')('egg-loader:loader'); const path = require('path'); const globby = require('globby'); -const interopRequire = require('interop-require'); const is = require('is-type-of'); +const loadFile = require('./utils').loadFile; const FULLPATH = Symbol('EGG_LOADER_ITEM_FULLPATH'); const defaults = { @@ -106,13 +106,7 @@ function getProperties(filepath, lowercaseFirst) { } function getExports(fullpath, initializer, isCall, inject) { - let exports; - try { - exports = interopRequire(fullpath); - } catch (err) { - err.message = 'load file: ' + fullpath + ', error: ' + err.message; - throw err; - } + let exports = loadFile(fullpath); if (initializer) { exports = initializer(exports); diff --git a/lib/utils/index.js b/lib/utils/index.js index 4f889165..07647305 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -2,10 +2,27 @@ const is = require('is-type-of'); const deprecate = require('depd')('egg-loader'); +const interopRequire = require('interop-require'); const createClassLoader = require('./class_loader'); module.exports = exports = { + /** + * require a file + * @param {String} filepath fullpath + * @return {Object} exports + */ + loadFile(filepath) { + let exports; + try { + exports = interopRequire(filepath); + } catch (err) { + err.message = 'load file: ' + filepath + ', error: ' + err.message; + throw err; + } + return exports; + }, + /** * 判断模块是否存在 * @method Util#existsModule diff --git a/test/egg_loader.test.js b/test/egg_loader.test.js index fe578af2..2ab8516f 100644 --- a/test/egg_loader.test.js +++ b/test/egg_loader.test.js @@ -24,12 +24,4 @@ describe('test/egg_loader.test.js', function() { }); }); - - describe('loadFile', function() { - it('should throw with filepath when file syntax error', function() { - (function() { - utils.createApp('syntaxerror'); - }).should.throw(/test\/fixtures\/syntaxerror\/app\.js error:/); - }); - }); }); diff --git a/test/fixtures/load_file/function.js b/test/fixtures/load_file/function.js new file mode 100644 index 00000000..b1157b9a --- /dev/null +++ b/test/fixtures/load_file/function.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = (a, b) => [ a, b ]; diff --git a/test/fixtures/load_file/obj.js b/test/fixtures/load_file/obj.js new file mode 100644 index 00000000..d0bcc4db --- /dev/null +++ b/test/fixtures/load_file/obj.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = { a: 1 }; diff --git a/test/fixtures/load_file/package.json b/test/fixtures/load_file/package.json new file mode 100644 index 00000000..f22b07a8 --- /dev/null +++ b/test/fixtures/load_file/package.json @@ -0,0 +1,3 @@ +{ + "name": "load_file" +} diff --git a/test/load_file.test.js b/test/load_file.test.js new file mode 100644 index 00000000..c3eb84a5 --- /dev/null +++ b/test/load_file.test.js @@ -0,0 +1,36 @@ +'use strict'; + +require('should'); +const mm = require('mm'); +const utils = require('./utils'); +const Loader = require('../lib/base_loader'); +const EggApplication = require('./fixtures/egg'); + +describe('test/load_file.test.js', function() { + + afterEach(mm.restore); + + it('should load file', function() { + const loader = new Loader({ + baseDir: utils.getFilepath('load_file'), + app: new EggApplication(), + }); + loader.loadFile(utils.getFilepath('load_file/obj.js')).should.eql({ a: 1 }); + }); + + it('should load file when exports is function', function() { + const loader = new Loader({ + baseDir: utils.getFilepath('load_file'), + app: new EggApplication(), + }); + loader.loadFile(utils.getFilepath('load_file/function.js'), 1, 2).should.eql([ 1, 2 ]); + }); + + it('should throw with filepath when file syntax error', function() { + const filepath = utils.getFilepath('syntaxerror/app.js'); + (function() { + utils.createApp('syntaxerror'); + }).should.throw(`load file: ${filepath}, error: Unexpected token )`); + }); + +}); From be89ac8808d59f66fd8c6a5e62ff624a507899ff Mon Sep 17 00:00:00 2001 From: popomore Date: Wed, 3 Aug 2016 14:30:18 +0800 Subject: [PATCH 06/18] refactor: move load_custom_app/load_custom_agent --- lib/base_loader.js | 33 +---------------- lib/loader/custom.js | 35 +++++++++++++++++++ ...cation.test.js => load_custom_app.test.js} | 0 3 files changed, 36 insertions(+), 32 deletions(-) create mode 100644 lib/loader/custom.js rename test/{load_custom_application.test.js => load_custom_app.test.js} (100%) diff --git a/lib/base_loader.js b/lib/base_loader.js index 782f647f..31eba330 100644 --- a/lib/base_loader.js +++ b/lib/base_loader.js @@ -177,38 +177,6 @@ class EggLoader { return isFunction(ret) ? ret.apply(null, inject) : ret; } - /** - * 加载自定义的 app.js,**在 app.js 可做任何操作,但建议尽量减少此操作,做该做的事**。 - * - * 可加载路径查看 {@link EggLoader#loadDirs} - * @example - * ```js - * module.exports = function(app) { - * // 自定义 - * } - * ``` - */ - loadCustomApp() { - this.loadDirs() - .forEach(dir => this.loadFile(path.join(dir, 'app.js'))); - } - - /** - * 同 {@link EggLoader#loadCustomApp},但加载自定义的 agent.js - * - * 可加载路径查看 {@link EggLoader#loadDirs} - * @example - * ```js - * module.exports = function(agent) { - * // 自定义 - * } - * ``` - */ - loadCustomAgent() { - this.loadDirs() - .forEach(dir => this.loadFile(path.join(dir, 'agent.js'))); - } - /** * 加载 app/controller 目录下的文件 * @@ -286,6 +254,7 @@ const loaders = [ require('./plugin_loader'), require('./config_loader'), require('./loader/extend'), + require('./loader/custom'), require('./proxy_loader'), require('./service_loader'), require('./middleware_loader'), diff --git a/lib/loader/custom.js b/lib/loader/custom.js new file mode 100644 index 00000000..55a2adbf --- /dev/null +++ b/lib/loader/custom.js @@ -0,0 +1,35 @@ +'use strict'; + +const path = require('path'); + +module.exports = { + + /** + * load app.js + * + * @example + * ```js + * module.exports = function(app) { + * // can do everything + * do(); + * + * // if you will invork asynchronous, you can use readyCallback + * const done = app.readyCallback(); + * doAsync(done); + * } + * ``` + */ + loadCustomApp() { + this.loadDirs() + .forEach(dir => this.loadFile(path.join(dir, 'app.js'))); + }, + + /** + * Load agent.js, same as {@link EggLoader#loadCustomApp} + */ + loadCustomAgent() { + this.loadDirs() + .forEach(dir => this.loadFile(path.join(dir, 'agent.js'))); + }, + +}; diff --git a/test/load_custom_application.test.js b/test/load_custom_app.test.js similarity index 100% rename from test/load_custom_application.test.js rename to test/load_custom_app.test.js From 356c5a3a8738e4bde048bbbf4cf49fc171e8fa19 Mon Sep 17 00:00:00 2001 From: popomore Date: Wed, 3 Aug 2016 14:56:14 +0800 Subject: [PATCH 07/18] refactor: move middleware to loader --- lib/base_loader.js | 2 +- .../middleware.js} | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) rename lib/{middleware_loader.js => loader/middleware.js} (79%) diff --git a/lib/base_loader.js b/lib/base_loader.js index 31eba330..a914fde3 100644 --- a/lib/base_loader.js +++ b/lib/base_loader.js @@ -257,7 +257,7 @@ const loaders = [ require('./loader/custom'), require('./proxy_loader'), require('./service_loader'), - require('./middleware_loader'), + require('./loader/middleware'), ]; for (const loader of loaders) { diff --git a/lib/middleware_loader.js b/lib/loader/middleware.js similarity index 79% rename from lib/middleware_loader.js rename to lib/loader/middleware.js index e530c304..daba751e 100644 --- a/lib/middleware_loader.js +++ b/lib/loader/middleware.js @@ -6,12 +6,15 @@ const debug = require('debug')('egg:loader:middleware'); const inspect = require('util').inspect; module.exports = { + /** - * 加载中间件,将中间件加载到 app.middleware,并根据 config 配置载入到上下文中 + * Load middleware * - * 中间件允许覆盖,优先级依次从上到下 + * app.config.xx is the options of the middleware xx that has same name as config * - * 中间件的规范写法为,options 是同名配置获取的 + * @method EggLoader#loadMiddleware + * @param {Object} opt LoaderOptions + * @example * ```js * // app/middleware/status.js * module.exports = function(options, app) { @@ -21,27 +24,23 @@ module.exports = { * } * } * ``` - * @method EggLoader#loadMiddleware - * @param {Object} opt - loading 参数 */ loadMiddleware(opt) { - const app = this.app; + + // load middleware to app.middleware opt = Object.assign({ - // 加载中间件,但是不调用它 call: false, override: true, lowercaseFirst: true, }, opt); const middlewarePaths = this.loadDirs().map(dir => join(dir, 'app/middleware')); - this.loadToApp(middlewarePaths, 'middlewares', opt); app.coreLogger.info('Use coreMiddleware order: %j', this.config.coreMiddleware); app.coreLogger.info('Use appMiddleware order: %j', this.config.appMiddleware); - // 将中间件加载到 koa 中 - // 通过 app.config.coreMiddleware, app.config.appMiddleware 配置的顺序加载 + // use middleware ordered by app.config.coreMiddleware and app.config.appMiddleware const middlewareNames = this.config.coreMiddleware.concat(this.config.appMiddleware); debug('middlewareNames: %j', middlewareNames); for (const name of middlewareNames) { From 73034900b355c0e6b17e26cd45d23ea4c8546326 Mon Sep 17 00:00:00 2001 From: popomore Date: Wed, 3 Aug 2016 15:08:22 +0800 Subject: [PATCH 08/18] refactor: move controller to loader --- lib/base_loader.js | 16 +--------------- lib/loader/controller.js | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 15 deletions(-) create mode 100644 lib/loader/controller.js diff --git a/lib/base_loader.js b/lib/base_loader.js index a914fde3..1b8daadf 100644 --- a/lib/base_loader.js +++ b/lib/base_loader.js @@ -177,21 +177,6 @@ class EggLoader { return isFunction(ret) ? ret.apply(null, inject) : ret; } - /** - * 加载 app/controller 目录下的文件 - * - * @param {Object} opt - loading 参数 - */ - loadController(opt) { - const app = this.app; - opt = Object.assign({ lowercaseFirst: true }, opt); - const controllerBase = path.join(this.options.baseDir, 'app/controller'); - - this.loadToApp(controllerBase, 'controller', opt); - app.controllers = app.controller; - app.coreLogger.info('[egg:loader] Controller loaded: %s', controllerBase); - } - /** * 返回 egg 需要加载的目录 * @@ -258,6 +243,7 @@ const loaders = [ require('./proxy_loader'), require('./service_loader'), require('./loader/middleware'), + require('./loader/controller'), ]; for (const loader of loaders) { diff --git a/lib/loader/controller.js b/lib/loader/controller.js new file mode 100644 index 00000000..72644490 --- /dev/null +++ b/lib/loader/controller.js @@ -0,0 +1,21 @@ +'use strict'; + +const path = require('path'); + +module.exports = { + + /** + * load app/controller + * + * @param {Object} opt LoaderOptions + */ + loadController(opt) { + const app = this.app; + opt = Object.assign({ lowercaseFirst: true }, opt); + const controllerBase = path.join(this.options.baseDir, 'app/controller'); + + this.loadToApp(controllerBase, 'controller', opt); + app.coreLogger.info('[egg:loader] Controller loaded: %s', controllerBase); + }, + +}; From 6734d41f5de83abbdb3c1f4d635b5ab0680c673a Mon Sep 17 00:00:00 2001 From: popomore Date: Wed, 3 Aug 2016 15:09:12 +0800 Subject: [PATCH 09/18] refactor: rename loader to mixin --- lib/base_loader.js | 8 ++++---- lib/{loader => mixin}/config.js | 0 lib/{loader => mixin}/controller.js | 0 lib/{loader => mixin}/custom.js | 0 lib/{loader => mixin}/extend.js | 0 lib/{loader => mixin}/middleware.js | 0 6 files changed, 4 insertions(+), 4 deletions(-) rename lib/{loader => mixin}/config.js (100%) rename lib/{loader => mixin}/controller.js (100%) rename lib/{loader => mixin}/custom.js (100%) rename lib/{loader => mixin}/extend.js (100%) rename lib/{loader => mixin}/middleware.js (100%) diff --git a/lib/base_loader.js b/lib/base_loader.js index 1b8daadf..e9100937 100644 --- a/lib/base_loader.js +++ b/lib/base_loader.js @@ -238,12 +238,12 @@ class EggLoader { const loaders = [ require('./plugin_loader'), require('./config_loader'), - require('./loader/extend'), - require('./loader/custom'), + require('./mixin/extend'), + require('./mixin/custom'), require('./proxy_loader'), require('./service_loader'), - require('./loader/middleware'), - require('./loader/controller'), + require('./mixin/middleware'), + require('./mixin/controller'), ]; for (const loader of loaders) { diff --git a/lib/loader/config.js b/lib/mixin/config.js similarity index 100% rename from lib/loader/config.js rename to lib/mixin/config.js diff --git a/lib/loader/controller.js b/lib/mixin/controller.js similarity index 100% rename from lib/loader/controller.js rename to lib/mixin/controller.js diff --git a/lib/loader/custom.js b/lib/mixin/custom.js similarity index 100% rename from lib/loader/custom.js rename to lib/mixin/custom.js diff --git a/lib/loader/extend.js b/lib/mixin/extend.js similarity index 100% rename from lib/loader/extend.js rename to lib/mixin/extend.js diff --git a/lib/loader/middleware.js b/lib/mixin/middleware.js similarity index 100% rename from lib/loader/middleware.js rename to lib/mixin/middleware.js From 9a174d946870880f41ea6d5dbc13ecb406895ead Mon Sep 17 00:00:00 2001 From: popomore Date: Wed, 3 Aug 2016 15:38:45 +0800 Subject: [PATCH 10/18] refactor: move config to mixin --- lib/base_loader.js | 11 +++- lib/config_loader.js | 116 --------------------------------------- lib/mixin/config.js | 96 ++++++++++++++++++++++++++++++++ lib/mixin/middleware.js | 2 +- test/get_appname.test.js | 9 ++- 5 files changed, 111 insertions(+), 123 deletions(-) delete mode 100644 lib/config_loader.js diff --git a/lib/base_loader.js b/lib/base_loader.js index e9100937..8f864942 100644 --- a/lib/base_loader.js +++ b/lib/base_loader.js @@ -7,6 +7,7 @@ const isFunction = require('is-type-of').function; const debug = require('debug')('egg-loader'); const Loader = require('./loader'); const loadFile = require('./utils').loadFile; +const getHomedir = require('./utils').getHomedir; const Emitter = require('events').EventEmitter; class EggLoader { @@ -46,6 +47,14 @@ class EggLoader { */ this.serverEnv = this.getServerEnv(); debug('Loaded serverEnv %j', this.serverEnv); + + this.appInfo = { + name: this.getAppname(), + baseDir: this.options.baseDir, + env: this.serverEnv, + HOME: getHomedir(), + pkg: this.pkg, + }; } /** @@ -237,7 +246,7 @@ class EggLoader { */ const loaders = [ require('./plugin_loader'), - require('./config_loader'), + require('./mixin/config'), require('./mixin/extend'), require('./mixin/custom'), require('./proxy_loader'), diff --git a/lib/config_loader.js b/lib/config_loader.js deleted file mode 100644 index 593fd815..00000000 --- a/lib/config_loader.js +++ /dev/null @@ -1,116 +0,0 @@ -'use strict'; - -const debug = require('debug')('egg:loader:config'); -const fs = require('fs'); -const path = require('path'); -const extend = require('extend'); -const getHomedir = require('./utils').getHomedir; - -module.exports = { - - /** - * config/config.js 加载类,封装加载逻辑, - * - * 加载时会合并 config.default.js 和 config.${env}.js - * - * config.middleware 和 config.proxy 的配置会被剔除 - * - * 可加载路径查看 {@link EggLoader#loadDirs} - * @method EggLoader#loadConfig - */ - loadConfig() { - const target = {}; - - const names = [ - 'config.default.js', - `config.${this.serverEnv}.js`, - ]; - - // 先加载一次应用配置,传给框架和插件的配置 - const appConfig = this._preloadAppConfig(); - - // egg config.default - // plugin config.default - // framework config.default - // egg config.{env} - // plugin config.{env} - // framework config.{env} - for (const filename of names) { - for (const dirpath of this.loadDirs()) { - const config = this._loadConfig(dirpath, filename, appConfig); - - if (!config) { - continue; - } - - debug('Loaded config %s/%s, %j', dirpath, filename, config); - extend(true, target, config); - } - } - - // 可以在 app.js 中操作 app.config.coreMiddleware 和 app.config.appMiddleware; - target.coreMiddleware = target.coreMiddlewares = target.coreMiddleware || []; - // 记录应用自定义中间件,后续可以根据此配置让插件在将中间件放在应用自定义中间件之前 - target.appMiddleware = target.appMiddlewares = target.middleware || []; - - /** - * 获取 `{baseDir}/config/config.{env}.js` 下的配置。 - * 包含以下配置: - * - * * `baseDir`: 应用文件基础目录, 如 `/home/admin/demoapp` - * * `pkg`: [package.json] 配置 - */ - this.config = target; - }, - - // 提前加载应用配置,可以传给其他配置 - _preloadAppConfig() { - const names = [ - 'config.default.js', - `config.${this.serverEnv}.js`, - ]; - const target = {}; - for (const filename of names) { - const config = this._loadConfig(this.options.baseDir, filename); - extend(true, target, config); - } - return target; - }, - - _loadConfig(dirpath, filename, extraInject) { - const pluginPaths = this.orderPlugins ? this.orderPlugins.map(plugin => plugin.path) : []; - const isPlugin = pluginPaths.indexOf(dirpath) > -1; - const isApp = dirpath === this.options.baseDir; - - let filepath = path.join(dirpath, 'config', filename); - // 兼容 config.js,config.js 和 config.default 是平级的 - if (filename === 'config.default.js' && !fs.existsSync(filepath)) { - filepath = path.join(dirpath, 'config/config.js'); - } - const name = this.getAppname(); - const config = this.loadFile(filepath, { - name, - baseDir: this.options.baseDir, - env: this.serverEnv, - HOME: getHomedir(), - pkg: this.pkg, - }, extraInject); - - if (!config) { - return null; - } - - // 插件和应用不允许配置 coreMiddleware - if (isPlugin || isApp) { - delete config.coreMiddleware; - } - // 框架和插件不运行配置 middleware 和 proxy 的属性,避免覆盖应用的 - if (!isApp) { - delete config.middleware; - delete config.proxy; - } - - return config; - }, - -}; diff --git a/lib/mixin/config.js b/lib/mixin/config.js index e69de29b..55a6d776 100644 --- a/lib/mixin/config.js +++ b/lib/mixin/config.js @@ -0,0 +1,96 @@ +'use strict'; + +const debug = require('debug')('egg-loader:config'); +const fs = require('fs'); +const path = require('path'); +const extend = require('extend'); + +module.exports = { + + /** + * Load config/config.js + * + * Will merge config.default.js 和 config.${env}.js + * + * @method EggLoader#loadConfig + */ + loadConfig() { + const target = {}; + + const names = [ + 'config.default.js', + `config.${this.serverEnv}.js`, + ]; + + // Load Application config first + const appConfig = this._preloadAppConfig(); + + // egg config.default + // plugin config.default + // framework config.default + // egg config.{env} + // plugin config.{env} + // framework config.{env} + for (const filename of names) { + for (const dirpath of this.loadDirs()) { + const config = this._loadConfig(dirpath, filename, appConfig); + + if (!config) { + continue; + } + + debug('Loaded config %s/%s, %j', dirpath, filename, config); + extend(true, target, config); + } + } + + // You can manipulate the order of app.config.coreMiddleware and app.config.appMiddleware in app.js + target.coreMiddleware = target.coreMiddlewares = target.coreMiddleware || []; + target.appMiddleware = target.appMiddlewares = target.middleware || []; + + this.config = target; + }, + + _preloadAppConfig() { + const names = [ + 'config.default.js', + `config.${this.serverEnv}.js`, + ]; + const target = {}; + for (const filename of names) { + const config = this._loadConfig(this.options.baseDir, filename); + extend(true, target, config); + } + return target; + }, + + _loadConfig(dirpath, filename, extraInject) { + const pluginPaths = this.orderPlugins ? this.orderPlugins.map(plugin => plugin.path) : []; + const isPlugin = pluginPaths.indexOf(dirpath) > -1; + const isApp = dirpath === this.options.baseDir; + + let filepath = path.join(dirpath, 'config', filename); + // let config.js compatible + if (filename === 'config.default.js' && !fs.existsSync(filepath)) { + filepath = path.join(dirpath, 'config/config.js'); + } + const config = this.loadFile(filepath, this.appInfo, extraInject); + + if (!config) { + return null; + } + + // delete coreMiddleware when app and plugin + if (isPlugin || isApp) { + delete config.coreMiddleware; + } + // delete middleware and proxy when it's not app + if (!isApp) { + delete config.middleware; + delete config.proxy; + } + + return config; + }, + +}; diff --git a/lib/mixin/middleware.js b/lib/mixin/middleware.js index daba751e..f84dbc2e 100644 --- a/lib/mixin/middleware.js +++ b/lib/mixin/middleware.js @@ -2,7 +2,7 @@ const join = require('path').join; const is = require('is-type-of'); -const debug = require('debug')('egg:loader:middleware'); +const debug = require('debug')('egg-loader:middleware'); const inspect = require('util').inspect; module.exports = { diff --git a/test/get_appname.test.js b/test/get_appname.test.js index becb86a7..fd98e7eb 100644 --- a/test/get_appname.test.js +++ b/test/get_appname.test.js @@ -19,13 +19,12 @@ describe('test/get_appname.test.js', function() { }); it('should throw when appname is not found', function() { - const loader = new Loader({ - baseDir: utils.getFilepath('app-noname'), - app: new EggApplication(), - }); const pkg = utils.getFilepath('app-noname/package.json'); (function() { - loader.getAppname(); + new Loader({ + baseDir: utils.getFilepath('app-noname'), + app: new EggApplication(), + }); }).should.throw(`name is required from ${pkg}`); }); }); From 8c2d1ded1176e443ba2bb08748f667ce66392864 Mon Sep 17 00:00:00 2001 From: popomore Date: Wed, 3 Aug 2016 15:46:41 +0800 Subject: [PATCH 11/18] refactor: move plugin to mixin --- lib/base_loader.js | 2 +- lib/{plugin_loader.js => mixin/plugin.js} | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) rename lib/{plugin_loader.js => mixin/plugin.js} (97%) diff --git a/lib/base_loader.js b/lib/base_loader.js index 8f864942..8e432cee 100644 --- a/lib/base_loader.js +++ b/lib/base_loader.js @@ -245,7 +245,7 @@ class EggLoader { * https://medium.com/@leocavalcante/es6-multiple-inheritance-73a3c66d2b6b */ const loaders = [ - require('./plugin_loader'), + require('./mixin/plugin'), require('./mixin/config'), require('./mixin/extend'), require('./mixin/custom'), diff --git a/lib/plugin_loader.js b/lib/mixin/plugin.js similarity index 97% rename from lib/plugin_loader.js rename to lib/mixin/plugin.js index fe9249a1..12160254 100644 --- a/lib/plugin_loader.js +++ b/lib/mixin/plugin.js @@ -2,15 +2,15 @@ const fs = require('fs'); const path = require('path'); -const debug = require('debug')('egg:loader:plugin'); -const interopRequire = require('interop-require'); - -const sequencify = require('./utils/sequencify'); +const debug = require('debug')('egg-loader:plugin'); +const sequencify = require('../utils/sequencify'); +const loadFile = require('../utils').loadFile; module.exports = { /** - * 根据配置加载插件,实现 loadPlugin() 接口 + * Load plugin.js + * * * 插件配置来自三个地方 * @@ -18,7 +18,7 @@ module.exports = { * 2. egg/lib/core/config/plugin.js,优先级次之。 * 3. 插件本身的 package.json => eggPlugin 配置,优先级最低 * - * 具体的插件配置类似 + * plugin.js is written below * * ```js * { @@ -30,7 +30,6 @@ module.exports = { * }, * // 简写 * 'rds': false, - * // 自定义路径,优先级最高 * 'depd': { * enable: true, * path: 'path/to/depd' @@ -164,7 +163,7 @@ module.exports = { continue; } - const config = interopRequire(configPath); + const config = loadFile(configPath); for (const name in config) { this.normalizePluginConfig(config, name); From 34870041082e3fd752204c182c69a69e3e7a6a97 Mon Sep 17 00:00:00 2001 From: popomore Date: Wed, 3 Aug 2016 16:30:16 +0800 Subject: [PATCH 12/18] refactor: change loadDirs to getLoadUnits loadUnit is a directory that can be loaded by EggLoader --- lib/base_loader.js | 34 +++++++++++++++++++++++----------- lib/mixin/config.js | 6 +++--- lib/mixin/custom.js | 8 ++++---- lib/mixin/extend.js | 20 ++++++++++---------- lib/mixin/middleware.js | 2 +- lib/proxy_loader.js | 2 +- lib/service_loader.js | 4 ++-- test/egg_loader.test.js | 17 ----------------- test/get_load_units.test.js | 36 ++++++++++++++++++++++++++++++++++++ 9 files changed, 80 insertions(+), 49 deletions(-) create mode 100644 test/get_load_units.test.js diff --git a/lib/base_loader.js b/lib/base_loader.js index 8e432cee..e93bad86 100644 --- a/lib/base_loader.js +++ b/lib/base_loader.js @@ -187,16 +187,20 @@ class EggLoader { } /** - * 返回 egg 需要加载的目录 + * Get all loadUnit * - * 1. 核心框架目录,目录为框架根目录下的 lib/core 目录,框架根目录来自 {@link EggLoader#eggPaths} - * 2. 已开启插件的根目录 - * 3. 应用根目录 + * loadUnit is a directory that can be loaded by EggLoader, it has the same structure. + * loadUnit has a path and a type(app, framework, plugin). * - * @return {Array} 返回所有目录 + * The order of the loadUnits: + * + * 1. plugin + * 2. framework + * 3. app + * + * @return {Array} loadUnits */ - loadDirs() { - // 做一层缓存 + getLoadUnits() { if (this.dirs) { return this.dirs; } @@ -206,23 +210,31 @@ class EggLoader { // 插件目录,master 没有 plugin if (this.orderPlugins) { for (const plugin of this.orderPlugins) { - dirs.push(plugin.path); + dirs.push({ + path: plugin.path, + type: 'plugin', + }); } } // egg 框架路径,在 lib/core 目录下 for (const eggPath of this.eggPaths) { - dirs.push(path.join(eggPath, 'lib/core')); + dirs.push({ + path: path.join(eggPath, 'lib/core'), + type: 'framework', + }); } // 应用目录 - dirs.push(this.options.baseDir); + dirs.push({ + path: this.options.baseDir, + type: 'app', + }); debug('Loaded dirs %j', dirs); return dirs; } - loadTo(directory, target, opt) { opt = Object.assign({}, { directory, diff --git a/lib/mixin/config.js b/lib/mixin/config.js index 55a6d776..e05cb978 100644 --- a/lib/mixin/config.js +++ b/lib/mixin/config.js @@ -32,14 +32,14 @@ module.exports = { // plugin config.{env} // framework config.{env} for (const filename of names) { - for (const dirpath of this.loadDirs()) { - const config = this._loadConfig(dirpath, filename, appConfig); + for (const unit of this.getLoadUnits()) { + const config = this._loadConfig(unit.path, filename, appConfig); if (!config) { continue; } - debug('Loaded config %s/%s, %j', dirpath, filename, config); + debug('Loaded config %s/%s, %j', unit.path, filename, config); extend(true, target, config); } } diff --git a/lib/mixin/custom.js b/lib/mixin/custom.js index 55a2adbf..de8968fe 100644 --- a/lib/mixin/custom.js +++ b/lib/mixin/custom.js @@ -20,16 +20,16 @@ module.exports = { * ``` */ loadCustomApp() { - this.loadDirs() - .forEach(dir => this.loadFile(path.join(dir, 'app.js'))); + this.getLoadUnits() + .forEach(unit => this.loadFile(path.join(unit.path, 'app.js'))); }, /** * Load agent.js, same as {@link EggLoader#loadCustomApp} */ loadCustomAgent() { - this.loadDirs() - .forEach(dir => this.loadFile(path.join(dir, 'agent.js'))); + this.getLoadUnits() + .forEach(unit => this.loadFile(path.join(unit.path, 'agent.js'))); }, }; diff --git a/lib/mixin/extend.js b/lib/mixin/extend.js index d2680b24..7732cd3d 100644 --- a/lib/mixin/extend.js +++ b/lib/mixin/extend.js @@ -11,7 +11,7 @@ module.exports = { /** * 扩展 Agent.prototype 的属性 * - * 可加载路径查看 {@link EggLoader#loadDirs} + * 可加载路径查看 {@link EggLoader#getLoadUnits} * @method EggLoader#loadAgentExtend */ loadAgentExtend() { @@ -21,7 +21,7 @@ module.exports = { /** * 扩展 Application.prototype 的属性 * - * 可加载路径查看 {@link EggLoader#loadDirs} + * 可加载路径查看 {@link EggLoader#getLoadUnits} * @method EggLoader#loadApplicationExtend */ loadApplicationExtend() { @@ -31,7 +31,7 @@ module.exports = { /** * 扩展 Request.prototype 的属性 * - * 可加载路径查看 {@link EggLoader#loadDirs} + * 可加载路径查看 {@link EggLoader#getLoadUnits} * @method EggLoader#loadRequestExtend */ loadRequestExtend() { @@ -41,7 +41,7 @@ module.exports = { /** * 扩展 Response.prototype 的属性 * - * 可加载路径查看 {@link EggLoader#loadDirs} + * 可加载路径查看 {@link EggLoader#getLoadUnits} * @method EggLoader#loadResponseExtend */ loadResponseExtend() { @@ -51,7 +51,7 @@ module.exports = { /** * 扩展 Context.prototype 的属性 * - * 可加载路径查看 {@link EggLoader#loadDirs} + * 可加载路径查看 {@link EggLoader#getLoadUnits} * @method EggLoader#loadContextExtend */ loadContextExtend() { @@ -61,7 +61,7 @@ module.exports = { /** * 扩展 app.Helper.prototype 的属性 * - * 可加载路径查看 {@link EggLoader#loadDirs} + * 可加载路径查看 {@link EggLoader#getLoadUnits} * @method EggLoader#loadHelperExtend */ loadHelperExtend() { @@ -80,11 +80,11 @@ module.exports = { */ loadExtend(name, proto) { // 获取需要加载的文件 - const filepaths = this.loadDirs() - .map(dir => { - let pluginExtendsPath = path.join(dir, 'app/extend'); + const filepaths = this.getLoadUnits() + .map(unit => { + let pluginExtendsPath = path.join(unit.path, 'app/extend'); if (!fs.existsSync(pluginExtendsPath)) { - pluginExtendsPath = path.join(dir, 'app'); + pluginExtendsPath = path.join(unit.path, 'app'); } return path.join(pluginExtendsPath, name); }); diff --git a/lib/mixin/middleware.js b/lib/mixin/middleware.js index f84dbc2e..784b2740 100644 --- a/lib/mixin/middleware.js +++ b/lib/mixin/middleware.js @@ -34,7 +34,7 @@ module.exports = { override: true, lowercaseFirst: true, }, opt); - const middlewarePaths = this.loadDirs().map(dir => join(dir, 'app/middleware')); + const middlewarePaths = this.getLoadUnits().map(unit => join(unit.path, 'app/middleware')); this.loadToApp(middlewarePaths, 'middlewares', opt); app.coreLogger.info('Use coreMiddleware order: %j', this.config.coreMiddleware); diff --git a/lib/proxy_loader.js b/lib/proxy_loader.js index c0c07ea7..8016ea81 100644 --- a/lib/proxy_loader.js +++ b/lib/proxy_loader.js @@ -18,7 +18,7 @@ module.exports = { loadProxy(opt) { const app = this.app; opt = Object.assign({ call: true, lowercaseFirst: true }, opt); - const arr = this.loadDirs().map(dir => join(dir, 'app/proxy')); + const arr = this.getLoadUnits().map(unit => join(unit.path, 'app/proxy')); // load proxy classes to app.proxyClasses this.loadToApp(arr, 'proxyClasses', opt); diff --git a/lib/service_loader.js b/lib/service_loader.js index a031de78..0e5574f6 100644 --- a/lib/service_loader.js +++ b/lib/service_loader.js @@ -17,8 +17,8 @@ module.exports = { */ loadService(opt) { const app = this.app; - const servicePaths = this.loadDirs().map(dir => { - const servicePath = path.join(dir, 'app/service'); + const servicePaths = this.getLoadUnits().map(unit => { + const servicePath = path.join(unit.path, 'app/service'); return servicePath; }); diff --git a/test/egg_loader.test.js b/test/egg_loader.test.js index 2ab8516f..61ee6017 100644 --- a/test/egg_loader.test.js +++ b/test/egg_loader.test.js @@ -2,26 +2,9 @@ require('should'); const mm = require('mm'); -const utils = require('./utils'); describe('test/egg_loader.test.js', function() { afterEach(mm.restore); - - describe('loadDirs', function() { - - it('should get plugin dir', function() { - const app = utils.createApp('plugin'); - const dirs = app.loader.loadDirs(); - dirs.length.should.eql(10); - }); - - it('should not get plugin dir', function() { - const loader = new utils.Loader('plugin'); - const dirs = loader.loadDirs(); - dirs.length.should.eql(2); - }); - }); - }); diff --git a/test/get_load_units.test.js b/test/get_load_units.test.js new file mode 100644 index 00000000..d9cd6e79 --- /dev/null +++ b/test/get_load_units.test.js @@ -0,0 +1,36 @@ +'use strict'; + +require('should'); +const mm = require('mm'); +const utils = require('./utils'); +const Loader = require('../lib/base_loader'); +const EggApplication = require('./fixtures/egg'); + +describe('test/get_load_units.test.js', function() { + + afterEach(mm.restore); + + it('should get plugin dir', function() { + const loader = new Loader({ + baseDir: utils.getFilepath('plugin'), + app: new EggApplication(), + }); + loader.loadPlugin(); + const units = loader.getLoadUnits(); + units.length.should.eql(10); + units[8].type.should.eql('framework'); + units[8].path.should.eql(utils.getFilepath('egg/lib/core')); + units[9].type.should.eql('app'); + units[9].path.should.eql(utils.getFilepath('plugin')); + }); + + it('should not get plugin dir', function() { + const loader = new Loader({ + baseDir: utils.getFilepath('plugin'), + app: new EggApplication(), + }); + const units = loader.getLoadUnits(); + units.length.should.eql(2); + }); + +}); From 0b6a4245cbbc795e0cbf0cde025547ab489391a4 Mon Sep 17 00:00:00 2001 From: popomore Date: Wed, 3 Aug 2016 16:39:25 +0800 Subject: [PATCH 13/18] refactor: rename base_loader to egg_loader --- index.js | 2 +- lib/{base_loader.js => egg_loader.js} | 0 test/get_appname.test.js | 2 +- test/get_framework_paths.test.js | 2 +- test/get_load_units.test.js | 2 +- test/load_file.test.js | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename lib/{base_loader.js => egg_loader.js} (100%) diff --git a/index.js b/index.js index b562eb25..a5bba83e 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,3 @@ 'use strict'; -module.exports = require('./lib/base_loader'); +module.exports = require('./lib/egg_loader'); diff --git a/lib/base_loader.js b/lib/egg_loader.js similarity index 100% rename from lib/base_loader.js rename to lib/egg_loader.js diff --git a/test/get_appname.test.js b/test/get_appname.test.js index fd98e7eb..5d96fd25 100644 --- a/test/get_appname.test.js +++ b/test/get_appname.test.js @@ -3,7 +3,7 @@ require('should'); const mm = require('mm'); const utils = require('./utils'); -const Loader = require('../lib/base_loader'); +const Loader = require('../lib/egg_loader'); const EggApplication = require('./fixtures/egg'); describe('test/get_appname.test.js', function() { diff --git a/test/get_framework_paths.test.js b/test/get_framework_paths.test.js index 2022696a..0b68bfa0 100644 --- a/test/get_framework_paths.test.js +++ b/test/get_framework_paths.test.js @@ -3,7 +3,7 @@ require('should'); const mm = require('mm'); const utils = require('./utils'); -const Loader = require('../lib/base_loader'); +const Loader = require('../lib/egg_loader'); const EggApplication = require('./fixtures/egg'); const KoaApplication = require('koa'); diff --git a/test/get_load_units.test.js b/test/get_load_units.test.js index d9cd6e79..8a02e7e6 100644 --- a/test/get_load_units.test.js +++ b/test/get_load_units.test.js @@ -3,7 +3,7 @@ require('should'); const mm = require('mm'); const utils = require('./utils'); -const Loader = require('../lib/base_loader'); +const Loader = require('../lib/egg_loader'); const EggApplication = require('./fixtures/egg'); describe('test/get_load_units.test.js', function() { diff --git a/test/load_file.test.js b/test/load_file.test.js index c3eb84a5..1174ba62 100644 --- a/test/load_file.test.js +++ b/test/load_file.test.js @@ -3,7 +3,7 @@ require('should'); const mm = require('mm'); const utils = require('./utils'); -const Loader = require('../lib/base_loader'); +const Loader = require('../lib/egg_loader'); const EggApplication = require('./fixtures/egg'); describe('test/load_file.test.js', function() { From ea88ab17929152e6b2e8a36e7ee443d3419723f2 Mon Sep 17 00:00:00 2001 From: popomore Date: Wed, 3 Aug 2016 22:10:55 +0800 Subject: [PATCH 14/18] feat: add loader.loadToContext, it can load files that bind ctx move proxy/service to mixin --- lib/context_loader.js | 74 +++++++++++++++++++ lib/egg_loader.js | 18 +++-- lib/loader.js | 23 +++++- lib/mixin/proxy.js | 30 ++++++++ lib/mixin/service.js | 30 ++++++++ lib/proxy_loader.js | 51 ------------- lib/service_loader.js | 58 --------------- test/fixtures/subdir-proxy/app/router.js | 2 +- .../subdir-services/app/controller/home.js | 2 +- test/load_proxy.test.js | 2 +- test/load_service.test.js | 5 +- 11 files changed, 174 insertions(+), 121 deletions(-) create mode 100644 lib/context_loader.js create mode 100644 lib/mixin/proxy.js create mode 100644 lib/mixin/service.js delete mode 100644 lib/proxy_loader.js delete mode 100644 lib/service_loader.js diff --git a/lib/context_loader.js b/lib/context_loader.js new file mode 100644 index 00000000..5069aeff --- /dev/null +++ b/lib/context_loader.js @@ -0,0 +1,74 @@ +'use strict'; + +const assert = require('assert'); +const is = require('is-type-of'); +const Loader = require('./loader'); +const classLoader = Symbol('classLoader'); +const END = Loader.END; + +class ClassLoader { + + constructor(options) { + assert(options.ctx, 'options.ctx is required'); + const properties = options.properties; + this._cache = new Map(); + this._ctx = options.ctx; + + for (const property in properties) { + this.defineProperty(property, properties[property]); + } + } + + defineProperty(property, values) { + Object.defineProperty(this, property, { + get() { + if (!this._cache.has(property)) { + this._cache.set(property, getInstance(values, this._ctx)); + } + return this._cache.get(property); + }, + }); + } +} + +class ContextLoader extends Loader { + + constructor(options) { + assert(options.field, 'options.field is required'); + assert(options.inject, 'options.inject is required'); + const target = options.target = {}; + if (options.fieldClass) { + options.inject[options.fieldClass] = target; + } + super(options); + + const app = this.options.inject; + + Object.defineProperty(app.context, options.field, { + get() { + if (!this[classLoader]) { + this[classLoader] = getInstance(target, this); + } + return this[classLoader]; + }, + }); + } +} + +module.exports = ContextLoader; + + +function getInstance(values, ctx) { + const Class = values[END] ? values : null; + let instance; + if (Class) { + if (is.class(Class)) { + instance = new Class(ctx); + } else { + instance = Class; + } + } else { + instance = new ClassLoader({ ctx, properties: values }); + } + return instance; +} diff --git a/lib/egg_loader.js b/lib/egg_loader.js index e93bad86..56abfb58 100644 --- a/lib/egg_loader.js +++ b/lib/egg_loader.js @@ -6,6 +6,7 @@ const assert = require('assert'); const isFunction = require('is-type-of').function; const debug = require('debug')('egg-loader'); const Loader = require('./loader'); +const ContextLoader = require('./context_loader'); const loadFile = require('./utils').loadFile; const getHomedir = require('./utils').getHomedir; const Emitter = require('events').EventEmitter; @@ -235,7 +236,8 @@ class EggLoader { return dirs; } - loadTo(directory, target, opt) { + loadToApp(directory, field, opt) { + const target = this.app[field] = {}; opt = Object.assign({}, { directory, target, @@ -244,9 +246,13 @@ class EggLoader { new Loader(opt).load(); } - loadToApp(directory, field, opt) { - const target = this.app[field] = {}; - this.loadTo(directory, target, opt); + loadToContext(directory, field, opt) { + opt = Object.assign({}, { + directory, + field, + inject: this.app, + }, opt); + new ContextLoader(opt).load(); } } @@ -261,8 +267,8 @@ const loaders = [ require('./mixin/config'), require('./mixin/extend'), require('./mixin/custom'), - require('./proxy_loader'), - require('./service_loader'), + require('./mixin/proxy'), + require('./mixin/service'), require('./mixin/middleware'), require('./mixin/controller'), ]; diff --git a/lib/loader.js b/lib/loader.js index c63ba6ee..f587e51f 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -8,6 +8,7 @@ const globby = require('globby'); const is = require('is-type-of'); const loadFile = require('./utils').loadFile; const FULLPATH = Symbol('EGG_LOADER_ITEM_FULLPATH'); +const END = Symbol('EGG_LOADER_ITEM_END'); const defaults = { directory: null, @@ -33,6 +34,8 @@ class Loader { const target = this.options.target; for (const item of items) { debug('loading item %j', item); + // item { properties: [ 'a', 'b', 'c'], exports } + // => target.a.b.c = exports item.properties.reduce((target, property, index) => { let obj; const properties = item.properties.slice(0, index + 1).join('.'); @@ -41,7 +44,10 @@ class Loader { if (!this.options.override) throw new Error(`can't overwrite property '${properties}' from ${target[property][FULLPATH]} by ${item.fullpath}`); } obj = item.exports; - if (obj) obj[FULLPATH] = item.fullpath; + if (obj) { + obj[FULLPATH] = item.fullpath; + obj[END] = true; + } } else { obj = target[property] || {}; } @@ -87,6 +93,7 @@ class Loader { } module.exports = Loader; +module.exports.END = END; // a/b/c.js => ['a', 'b', 'c'] function getProperties(filepath, lowercaseFirst) { @@ -105,17 +112,30 @@ function getProperties(filepath, lowercaseFirst) { }); } +// Get exports from filepath +// If exports is null/undefined, it will be ignored function getExports(fullpath, initializer, isCall, inject) { let exports = loadFile(fullpath); + // process exports as you like if (initializer) { exports = initializer(exports); } + // return exports when it's a class or generator + // + // module.exports = class Service {}; + // or + // module.exports = function*() {} if (is.class(exports) || is.generatorFunction(exports)) { return exports; } + // return exports after call when it's a function + // + // module.exports = function(app) { + // return {}; + // } if (isCall && is.function(exports)) { exports = exports(inject); if (exports != null) { @@ -123,5 +143,6 @@ function getExports(fullpath, initializer, isCall, inject) { } } + // return exports what is return exports; } diff --git a/lib/mixin/proxy.js b/lib/mixin/proxy.js new file mode 100644 index 00000000..9929ca97 --- /dev/null +++ b/lib/mixin/proxy.js @@ -0,0 +1,30 @@ +'use strict'; + +const join = require('path').join; + +module.exports = { + + /** + * 加载 app/proxy 目录下的文件 + * + * 1. 加载应用 app/proxy + * 2. 加载插件 app/proxy + * + * @method EggLoader#loadProxy + * @param {Object} opt - loading 参数 + */ + loadProxy(opt) { + const app = this.app; + const arr = this.getLoadUnits().map(unit => join(unit.path, 'app/proxy')); + + opt = Object.assign({ + call: true, + lowercaseFirst: true, + fieldClass: 'proxyClasses', + }, opt); + this.loadToContext(arr, 'proxy', opt); + + app.coreLogger.info('[egg:loader] Proxy loaded from %j', arr); + }, + +}; diff --git a/lib/mixin/service.js b/lib/mixin/service.js new file mode 100644 index 00000000..2bf14b93 --- /dev/null +++ b/lib/mixin/service.js @@ -0,0 +1,30 @@ +'use strict'; + +const path = require('path'); + +module.exports = { + + /** + * 加载 app/service 目录下的文件 + * + * 1. 加载应用 app/service + * 2. 加载插件 app/service + * + * @method EggLoader#loadService + * @param {Object} opt - loading 参数 + */ + loadService(opt) { + const servicePaths = this.getLoadUnits().map(unit => { + return path.join(unit.path, 'app/service'); + }); + + // 载入到 app.serviceClasses + opt = Object.assign({ + call: true, + lowercaseFirst: true, + fieldClass: 'serviceClasses', + }, opt); + this.loadToContext(servicePaths, 'service', opt); + }, + +}; diff --git a/lib/proxy_loader.js b/lib/proxy_loader.js deleted file mode 100644 index 8016ea81..00000000 --- a/lib/proxy_loader.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict'; - -const join = require('path').join; -const classLoader = Symbol('classLoader'); -const utils = require('./utils'); - -module.exports = { - - /** - * 加载 app/proxy 目录下的文件 - * - * 1. 加载应用 app/proxy - * 2. 加载插件 app/proxy - * - * @method EggLoader#loadProxy - * @param {Object} opt - loading 参数 - */ - loadProxy(opt) { - const app = this.app; - opt = Object.assign({ call: true, lowercaseFirst: true }, opt); - const arr = this.getLoadUnits().map(unit => join(unit.path, 'app/proxy')); - // load proxy classes to app.proxyClasses - this.loadToApp(arr, 'proxyClasses', opt); - - // this.proxy.demoQuery.getUser(uid) - Object.defineProperty(app.context, 'proxy', { - get() { - let loader = this[classLoader]; - if (!loader) { - this[classLoader] = loader = new this.app.ProxyClassLoader(this); - } - return loader; - }, - }); - - // { - // key1: { - // subkey1: SubProxy1, - // subkey2: { - // subkey21: SubProxy21, - // subkey22: SubProxy22, - // }, - // subkey3: SubProxy3, - // } - // } - app.ProxyClassLoader = utils.getClassLoader(app, 'proxy'); - - app.coreLogger.info('[egg:loader] Proxy loaded from %j', arr); - }, - -}; diff --git a/lib/service_loader.js b/lib/service_loader.js deleted file mode 100644 index 0e5574f6..00000000 --- a/lib/service_loader.js +++ /dev/null @@ -1,58 +0,0 @@ -'use strict'; - -const path = require('path'); -const utils = require('./utils'); -const classLoader = Symbol('classLoader'); - -module.exports = { - - /** - * 加载 app/service 目录下的文件 - * - * 1. 加载应用 app/service - * 2. 加载插件 app/service - * - * @method EggLoader#loadService - * @param {Object} opt - loading 参数 - */ - loadService(opt) { - const app = this.app; - const servicePaths = this.getLoadUnits().map(unit => { - const servicePath = path.join(unit.path, 'app/service'); - return servicePath; - }); - - // 载入到 app.serviceClasses - opt = Object.assign({ call: false, lowercaseFirst: true }, opt); - this.loadToApp(servicePaths, 'serviceClasses', opt); - - /** - * 可以访问到当前应用配置的所有 service, - * service 目录约定在 `${baseDir}/app/service`。 - * @since 1.0.0 - * @member Context#service - */ - Object.defineProperty(app.context, 'service', { - get() { - let loader = this[classLoader]; - if (!loader) { - this[classLoader] = loader = new this.app.ServiceClassLoader(this); - } - return loader; - }, - }); - - // { - // key1: { - // subkey1: SubService1, - // subkey2: { - // subkey21: SubService21, - // subkey22: SubService22, - // }, - // subkey3: SubService3, - // } - // } - app.ServiceClassLoader = utils.getClassLoader(app, 'service'); - }, - -}; diff --git a/test/fixtures/subdir-proxy/app/router.js b/test/fixtures/subdir-proxy/app/router.js index c0bd5f62..ce1e3339 100644 --- a/test/fixtures/subdir-proxy/app/router.js +++ b/test/fixtures/subdir-proxy/app/router.js @@ -13,4 +13,4 @@ module.exports = function (app) { oldStyle: yield this.proxy.oldStyle.url(this), }; }); -} \ No newline at end of file +} diff --git a/test/fixtures/subdir-services/app/controller/home.js b/test/fixtures/subdir-services/app/controller/home.js index 2c551a74..7a625bf1 100644 --- a/test/fixtures/subdir-services/app/controller/home.js +++ b/test/fixtures/subdir-services/app/controller/home.js @@ -5,7 +5,7 @@ module.exports = function* () { bar1: yield this.service.foo.bar.get('bar1name'), bar2: yield this.service.foo.subdir.bar.get('bar2name'), 'foo.subdir2.sub2': yield this.service.foo.subdir2.sub2.get('bar3name'), - subdir11bar: !!this.service.foo.subdir1, + subdir11bar: yield this.service.foo.subdir1.subdir11.bar.get(), ok: yield this.service.ok.get(), cmd: yield this.service.certifyPersonal.mobileHi.doCertify.exec('hihi'), serviceIsSame: this.service.certifyPersonal === this.service.certifyPersonal, diff --git a/test/load_proxy.test.js b/test/load_proxy.test.js index f1a00e4c..7d0112be 100644 --- a/test/load_proxy.test.js +++ b/test/load_proxy.test.js @@ -58,7 +58,7 @@ describe('test/load_proxy.test.js', function() { name: 'bar3name', bar: 'bar3', }, - subdir11bar: false, + subdir11bar: true, ok: { ok: true, }, diff --git a/test/load_service.test.js b/test/load_service.test.js index a0fa0350..6d55760e 100644 --- a/test/load_service.test.js +++ b/test/load_service.test.js @@ -10,7 +10,6 @@ describe('test/load_service.test.js', function() { it('should load from application and plugin', function(done) { const app = utils.createApp('plugin'); - console.log(app.serviceClasses); should.exists(app.serviceClasses.foo); should.exists(app.serviceClasses.foo2); should.not.exists(app.serviceClasses.bar1); @@ -79,7 +78,9 @@ describe('test/load_service.test.js', function() { name: 'bar3name', bar: 'bar3', }, - subdir11bar: false, + subdir11bar: { + bar: 'bar111', + }, ok: { ok: true, }, From 4b15dee8fbd5b2bef24e00c65a63fd29a3d2e792 Mon Sep 17 00:00:00 2001 From: popomore Date: Thu, 4 Aug 2016 00:15:13 +0800 Subject: [PATCH 15/18] refactor: don't load lib/core and lib/plugins - framework's directory will change from lib/core to framework base - dont' lookup plugin from lib/plugins directory --- lib/egg_loader.js | 4 +-- lib/mixin/plugin.js | 11 ++----- test/fixtures/custom-app/app/router.js | 13 -------- test/fixtures/custom-app/package.json | 3 -- test/fixtures/custom-framework/index.js | 11 ------- .../lib/core/config/plugin.js | 10 ------- .../custom-framework/lib/plugins/foo/app.js | 5 ---- .../lib/plugins/foo/package.json | 3 -- test/fixtures/custom-framework/package.json | 3 -- test/fixtures/egg/app/extend/application.js | 11 +++++++ .../{lib/core => }/app/middleware/status.js | 0 .../{lib/core => }/config/config.default.js | 0 .../{lib/core => }/config/config.unittest.js | 0 test/fixtures/egg/config/plugin.js | 30 +++++++++++++++++++ .../egg/lib/core/app/extend/application.js | 11 ------- test/fixtures/egg/lib/core/config/plugin.js | 30 ------------------- .../{lib/core => }/config/config.js | 0 .../{lib/core => }/config/config.js | 0 test/get_load_units.test.js | 2 +- test/load_plugin.test.js | 25 ---------------- 20 files changed, 47 insertions(+), 125 deletions(-) delete mode 100644 test/fixtures/custom-app/app/router.js delete mode 100644 test/fixtures/custom-app/package.json delete mode 100644 test/fixtures/custom-framework/index.js delete mode 100644 test/fixtures/custom-framework/lib/core/config/plugin.js delete mode 100644 test/fixtures/custom-framework/lib/plugins/foo/app.js delete mode 100644 test/fixtures/custom-framework/lib/plugins/foo/package.json delete mode 100644 test/fixtures/custom-framework/package.json create mode 100644 test/fixtures/egg/app/extend/application.js rename test/fixtures/egg/{lib/core => }/app/middleware/status.js (100%) rename test/fixtures/egg/{lib/core => }/config/config.default.js (100%) rename test/fixtures/egg/{lib/core => }/config/config.unittest.js (100%) create mode 100644 test/fixtures/egg/config/plugin.js delete mode 100644 test/fixtures/egg/lib/core/app/extend/application.js delete mode 100644 test/fixtures/egg/lib/core/config/plugin.js rename test/fixtures/framework-symbol/{lib/core => }/config/config.js (100%) rename test/fixtures/framework-symbol/node_modules/framework2/{lib/core => }/config/config.js (100%) diff --git a/lib/egg_loader.js b/lib/egg_loader.js index 56abfb58..c8794b69 100644 --- a/lib/egg_loader.js +++ b/lib/egg_loader.js @@ -218,10 +218,10 @@ class EggLoader { } } - // egg 框架路径,在 lib/core 目录下 + // egg 框架路径 for (const eggPath of this.eggPaths) { dirs.push({ - path: path.join(eggPath, 'lib/core'), + path: eggPath, type: 'framework', }); } diff --git a/lib/mixin/plugin.js b/lib/mixin/plugin.js index 12160254..472af438 100644 --- a/lib/mixin/plugin.js +++ b/lib/mixin/plugin.js @@ -15,7 +15,7 @@ module.exports = { * 插件配置来自三个地方 * * 1. 应用 config/plugin.js,优先级最高 - * 2. egg/lib/core/config/plugin.js,优先级次之。 + * 2. egg/config/plugin.js,优先级次之。 * 3. 插件本身的 package.json => eggPlugin 配置,优先级最低 * * plugin.js is written below @@ -41,7 +41,6 @@ module.exports = { * * 1. $APP_BASE/node_modules/${package or name} * 2. $EGG_BASE/node_modules/${package or name} - * 3. $EGG_BASE/lib/plugins/${package or name} * * 加载后可通过 `loader.plugins` 访问已开启的插件 * @@ -65,7 +64,7 @@ module.exports = { debug('Loaded app plugins: %j', Object.keys(appPlugins)); // 读取 eggPlugins,为框架和 egg 配置 - const eggPluginConfigPaths = this.eggPaths.map(eggPath => path.join(eggPath, 'lib/core/config/plugin.js')); + const eggPluginConfigPaths = this.eggPaths.map(eggPath => path.join(eggPath, 'config/plugin.js')); const eggPlugins = this.readPluginConfigs(eggPluginConfigPaths); debug('Loaded egg plugins: %j', Object.keys(eggPlugins)); @@ -319,17 +318,13 @@ module.exports = { // 尝试在以下目录找到匹配的插件 // -> {appname}/node_modules // -> {framework}/node_modules - // -> {framework}/lib/plugins (plugin.name) - // -> egg/node_modules - // -> egg/lib/plugins (plugin.name) - // -> $CWD/node_modules + // -> $CWD/node_modules lookupDirs.push(path.join(this.options.baseDir, 'node_modules')); // 到 egg 中查找,优先从外往里查找 for (let i = this.eggPaths.length - 1; i >= 0; i--) { const eggPath = this.eggPaths[i]; lookupDirs.push(path.join(eggPath, 'node_modules')); - lookupDirs.push(path.join(eggPath, 'lib/plugins')); } // npm@3, 插件测试用例,还需要通过 $cwd/node_modules 目录获取 diff --git a/test/fixtures/custom-app/app/router.js b/test/fixtures/custom-app/app/router.js deleted file mode 100644 index 9faa2f59..00000000 --- a/test/fixtures/custom-app/app/router.js +++ /dev/null @@ -1,13 +0,0 @@ -'use strict'; - -module.exports = function(app) { - app.get('/', function*() { - this.body = { - customFoo: app.customFoo, - env: app.config.env, - eggPaths: app.loader.eggPaths, - eggPaths: app.loader.eggPaths, - eggPath: app.loader.eggPath, - }; - }); -}; diff --git a/test/fixtures/custom-app/package.json b/test/fixtures/custom-app/package.json deleted file mode 100644 index 42e91811..00000000 --- a/test/fixtures/custom-app/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "custom-app" -} diff --git a/test/fixtures/custom-framework/index.js b/test/fixtures/custom-framework/index.js deleted file mode 100644 index 7f8e388e..00000000 --- a/test/fixtures/custom-framework/index.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -const KoaApplication = require('koa'); - -class Application extends KoaApplication { - get [Symbol.for('egg#eggPath')]() { - return __dirname; - } -} - -module.exports = Application; diff --git a/test/fixtures/custom-framework/lib/core/config/plugin.js b/test/fixtures/custom-framework/lib/core/config/plugin.js deleted file mode 100644 index 6dc7cea3..00000000 --- a/test/fixtures/custom-framework/lib/core/config/plugin.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -exports.foo = { - enable: true, - name: 'foo', -}; - -exports.depd = { - enable: false, -}; diff --git a/test/fixtures/custom-framework/lib/plugins/foo/app.js b/test/fixtures/custom-framework/lib/plugins/foo/app.js deleted file mode 100644 index 358f65b7..00000000 --- a/test/fixtures/custom-framework/lib/plugins/foo/app.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict'; - -module.exports = function(app) { - app.customFoo = true; -}; diff --git a/test/fixtures/custom-framework/lib/plugins/foo/package.json b/test/fixtures/custom-framework/lib/plugins/foo/package.json deleted file mode 100644 index bde99de9..00000000 --- a/test/fixtures/custom-framework/lib/plugins/foo/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "foo" -} diff --git a/test/fixtures/custom-framework/package.json b/test/fixtures/custom-framework/package.json deleted file mode 100644 index 811cc964..00000000 --- a/test/fixtures/custom-framework/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "custom-framework" -} diff --git a/test/fixtures/egg/app/extend/application.js b/test/fixtures/egg/app/extend/application.js new file mode 100644 index 00000000..70acefc4 --- /dev/null +++ b/test/fixtures/egg/app/extend/application.js @@ -0,0 +1,11 @@ +'use strict'; + +const symbol = require('../../../../utils').symbol; + +module.exports = { + Proxy: require('../../proxy'), + Service: require('../../service'), + get [symbol.view]() { + return 'egg'; + } +}; diff --git a/test/fixtures/egg/lib/core/app/middleware/status.js b/test/fixtures/egg/app/middleware/status.js similarity index 100% rename from test/fixtures/egg/lib/core/app/middleware/status.js rename to test/fixtures/egg/app/middleware/status.js diff --git a/test/fixtures/egg/lib/core/config/config.default.js b/test/fixtures/egg/config/config.default.js similarity index 100% rename from test/fixtures/egg/lib/core/config/config.default.js rename to test/fixtures/egg/config/config.default.js diff --git a/test/fixtures/egg/lib/core/config/config.unittest.js b/test/fixtures/egg/config/config.unittest.js similarity index 100% rename from test/fixtures/egg/lib/core/config/config.unittest.js rename to test/fixtures/egg/config/config.unittest.js diff --git a/test/fixtures/egg/config/plugin.js b/test/fixtures/egg/config/plugin.js new file mode 100644 index 00000000..47ea0a01 --- /dev/null +++ b/test/fixtures/egg/config/plugin.js @@ -0,0 +1,30 @@ +'use strict'; + +const path = require('path'); + +module.exports = { + session: { + enable: true, + path: path.join(__dirname, '../node_modules/session'), + }, + + hsfclient: { + enable: false, + path: path.join(__dirname, '../plugins/hsfclient'), + }, + + configclient: { + enable: false, + path: path.join(__dirname, '../plugins/configclient'), + }, + + eagleeye: { + enable: false, + path: path.join(__dirname, '../plugins/eagleeye'), + }, + + diamond: { + enable: false, + path: path.join(__dirname, '../plugins/diamond'), + }, +}; diff --git a/test/fixtures/egg/lib/core/app/extend/application.js b/test/fixtures/egg/lib/core/app/extend/application.js deleted file mode 100644 index 93b2c6dd..00000000 --- a/test/fixtures/egg/lib/core/app/extend/application.js +++ /dev/null @@ -1,11 +0,0 @@ -'use strict'; - -const symbol = require('../../../../../../utils').symbol; - -module.exports = { - Proxy: require('../../../../proxy'), - Service: require('../../../../service'), - get [symbol.view]() { - return 'egg'; - } -}; diff --git a/test/fixtures/egg/lib/core/config/plugin.js b/test/fixtures/egg/lib/core/config/plugin.js deleted file mode 100644 index 1c0d246c..00000000 --- a/test/fixtures/egg/lib/core/config/plugin.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -const path = require('path'); - -module.exports = { - session: { - enable: true, - path: path.join(__dirname, '../../../node_modules/session'), - }, - - hsfclient: { - enable: false, - path: path.join(__dirname, '../../../plugins/hsfclient'), - }, - - configclient: { - enable: false, - path: path.join(__dirname, '../../../plugins/configclient'), - }, - - eagleeye: { - enable: false, - path: path.join(__dirname, '../../../plugins/eagleeye'), - }, - - diamond: { - enable: false, - path: path.join(__dirname, '../../../plugins/diamond'), - }, -}; diff --git a/test/fixtures/framework-symbol/lib/core/config/config.js b/test/fixtures/framework-symbol/config/config.js similarity index 100% rename from test/fixtures/framework-symbol/lib/core/config/config.js rename to test/fixtures/framework-symbol/config/config.js diff --git a/test/fixtures/framework-symbol/node_modules/framework2/lib/core/config/config.js b/test/fixtures/framework-symbol/node_modules/framework2/config/config.js similarity index 100% rename from test/fixtures/framework-symbol/node_modules/framework2/lib/core/config/config.js rename to test/fixtures/framework-symbol/node_modules/framework2/config/config.js diff --git a/test/get_load_units.test.js b/test/get_load_units.test.js index 8a02e7e6..a132be65 100644 --- a/test/get_load_units.test.js +++ b/test/get_load_units.test.js @@ -19,7 +19,7 @@ describe('test/get_load_units.test.js', function() { const units = loader.getLoadUnits(); units.length.should.eql(10); units[8].type.should.eql('framework'); - units[8].path.should.eql(utils.getFilepath('egg/lib/core')); + units[8].path.should.eql(utils.getFilepath('egg')); units[9].type.should.eql('app'); units[9].path.should.eql(utils.getFilepath('plugin')); }); diff --git a/test/load_plugin.test.js b/test/load_plugin.test.js index 2d31b022..7bc87e63 100644 --- a/test/load_plugin.test.js +++ b/test/load_plugin.test.js @@ -35,13 +35,6 @@ describe('test/load_plugin.test.js', function() { env: [], path: path.join(baseDir, 'plugins/e'), }); - // loader.plugins.onerror.should.eql({ - // enable: true, - // name: 'onerror', - // dep: [], - // env: [], - // path: path.join(utils.eggPath, 'lib/plugins/onerror'), - // }); loader.orderPlugins.should.be.an.Array; }); @@ -309,24 +302,6 @@ describe('test/load_plugin.test.js', function() { }); }); - it('should load multi framework', function() { - const Application = require(utils.getFilepath('custom-framework')); - const loader = new Loader('custom-app', { - app: new Application(), - }); - loader.loadConfig(); - - loader.plugins.foo.should.eql({ - name: 'foo', - enable: true, - dep: [], - env: [], - path: utils.getFilepath('custom-framework/lib/plugins/foo'), - }); - - should.not.exists(loader.plugins.depd); - }); - it('should load when all plugins are disabled', function() { const loader = new Loader('noplugin'); loader.loadConfig(); From 98f7bcd5a5cae8e6cddbbf037796c850ca4db42d Mon Sep 17 00:00:00 2001 From: popomore Date: Thu, 4 Aug 2016 00:23:45 +0800 Subject: [PATCH 16/18] refactor item_end to itesm_exports --- lib/context_loader.js | 4 ++-- lib/loader.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/context_loader.js b/lib/context_loader.js index 5069aeff..36d1e13e 100644 --- a/lib/context_loader.js +++ b/lib/context_loader.js @@ -4,7 +4,7 @@ const assert = require('assert'); const is = require('is-type-of'); const Loader = require('./loader'); const classLoader = Symbol('classLoader'); -const END = Loader.END; +const EXPORTS = Loader.EXPORTS; class ClassLoader { @@ -59,7 +59,7 @@ module.exports = ContextLoader; function getInstance(values, ctx) { - const Class = values[END] ? values : null; + const Class = values[EXPORTS] ? values : null; let instance; if (Class) { if (is.class(Class)) { diff --git a/lib/loader.js b/lib/loader.js index f587e51f..c144d9bc 100644 --- a/lib/loader.js +++ b/lib/loader.js @@ -8,7 +8,7 @@ const globby = require('globby'); const is = require('is-type-of'); const loadFile = require('./utils').loadFile; const FULLPATH = Symbol('EGG_LOADER_ITEM_FULLPATH'); -const END = Symbol('EGG_LOADER_ITEM_END'); +const EXPORTS = Symbol('EGG_LOADER_ITEM_EXPORTS'); const defaults = { directory: null, @@ -46,7 +46,7 @@ class Loader { obj = item.exports; if (obj) { obj[FULLPATH] = item.fullpath; - obj[END] = true; + obj[EXPORTS] = true; } } else { obj = target[property] || {}; @@ -93,7 +93,7 @@ class Loader { } module.exports = Loader; -module.exports.END = END; +module.exports.EXPORTS = EXPORTS; // a/b/c.js => ['a', 'b', 'c'] function getProperties(filepath, lowercaseFirst) { From 52c9c70ddf2fcac38a8e08886b365ede4cc164fb Mon Sep 17 00:00:00 2001 From: popomore Date: Thu, 4 Aug 2016 00:28:23 +0800 Subject: [PATCH 17/18] refactor: remove old class_loader --- lib/utils/class_loader.js | 76 ------------------------------- lib/utils/index.js | 95 --------------------------------------- 2 files changed, 171 deletions(-) delete mode 100644 lib/utils/class_loader.js diff --git a/lib/utils/class_loader.js b/lib/utils/class_loader.js deleted file mode 100644 index 38519568..00000000 --- a/lib/utils/class_loader.js +++ /dev/null @@ -1,76 +0,0 @@ -'use strict'; - -const is = require('is-type-of'); - -module.exports = function createClassLoader(classes, subClasses) { - class ClassLoader { - constructor(ctx) { - this.ctx = ctx; - this._cache = new Map(); - } - - _getInstance(classname) { - let instance = this._cache.get(classname); - if (!instance) { - const Class = classes[classname]; - if (typeof Class === 'function' && !is.generatorFunction(Class)) { - // module.exports = class SubService extends Serivce - instance = new Class(this.ctx); - } else { - // 兼容模式 - // module.exports = { ... } - instance = Class; - } - this._cache.set(classname, instance); - } - return instance; - } - - // 支持子节点类加载,目前只支持最多2级节点 - // 只能一次性将此节点下的类都实例化出来 - _getSubClassInstance(rootName) { - let obj = this._cache.get(rootName); - if (obj) { - return obj; - } - obj = {}; - const map = subClasses[rootName]; - for (const sub1 in map) { - const Class = map[sub1]; - if (typeof Class === 'function') { - obj[sub1] = new Class(this.ctx); - } else { - for (const sub2 in Class) { - const Class2 = Class[sub2]; - if (!obj[sub1]) { - obj[sub1] = {}; - } - obj[sub1][sub2] = new Class2(this.ctx); - } - } - } - this._cache.set(rootName, obj); - return obj; - } - } - - Object.keys(classes).forEach(function(classname) { - Object.defineProperty(ClassLoader.prototype, classname, { - get() { - return this._getInstance(classname); - }, - }); - }); - - if (subClasses) { - Object.keys(subClasses).forEach(function(rootName) { - Object.defineProperty(ClassLoader.prototype, rootName, { - get() { - return this._getSubClassInstance(rootName); - }, - }); - }); - } - - return ClassLoader; -}; diff --git a/lib/utils/index.js b/lib/utils/index.js index 07647305..da0fd335 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -1,9 +1,6 @@ 'use strict'; -const is = require('is-type-of'); -const deprecate = require('depd')('egg-loader'); const interopRequire = require('interop-require'); -const createClassLoader = require('./class_loader'); module.exports = exports = { @@ -47,96 +44,4 @@ module.exports = exports = { return process.env.HOME || '/home/admin'; }, - // 遍历 - walk(app, classes, fn, path) { - path = path || []; - Object.keys(classes).forEach(key => { - let target = classes[key]; - const keys = [].concat(path, key); - - if (target === undefined || target === null) { - return undefined; - } - - // module.exports = class xxService extends Service {} - if (is.class(target)) { - return fn(target, keys); - } - - // 兼容模式: module.exports = function*() {} - if (is.generatorFunction(target)) { - return fn(target, keys); - } - - // 兼容模式: module.exports = function (app) {} - if (typeof target === 'function') { - // 自动调用一次 - target = target(app); - return fn(target, keys); - } - - // 判断是否是 exports.get = function* () {} 结构 - let hasGenerator = false; - for (const fnName in target) { - if (is.generatorFunction(target[fnName])) { - hasGenerator = true; - break; - } - } - - if (hasGenerator) { - return fn(target, keys); - } - - return module.exports.walk(app, target, fn, keys); - }); - }, - - /** - * 获取对应的 classloader - * @param {Object} app - app对象 - * @param {String} type - 要加载的类型, proxy / service - * @return {Function} 返回对应的 classloader - */ - getClassLoader(app, type) { - const targetClasses = app[type + 'Classes']; - const subClasses = {}; - - // hook to subServiceClasses / subProxyClasses, will be used in mm.mockService - const subClassesName = 'sub' + type[0].toUpperCase() + type.substring(1) + 'Classes'; - app[subClassesName] = subClasses; - - exports.walk(app, targetClasses, (target, keys) => { - const first = keys[0]; - if (keys.length === 1) { - targetClasses[first] = target; - return; - } - - if (keys.length > 3) { - deprecate(`不再支持超过 2 级子目录的 ${type} 加载,最长只到 ${first}.${keys[1]}.${keys[2]}`); - return; - } - - // 有两层或者三层的情况 - delete targetClasses[first]; - - // 最后一层的值是target - const last = keys.pop(); - - // keys为对象路径,首先依次查看subClasses下是否有,如果没有赋值空对象 - let classes = subClasses; - for (const key of keys) { - if (!classes[key]) { - classes[key] = {}; - } - classes = classes[key]; - } - - classes[last] = target; - }); - - return createClassLoader(targetClasses, subClasses); - }, - }; From df70e1955ae8185a44f928fceec0c18002ad1ca8 Mon Sep 17 00:00:00 2001 From: popomore Date: Thu, 4 Aug 2016 15:50:49 +0800 Subject: [PATCH 18/18] doc: update README and fix review --- README.md | 227 +++++++++++++++--- lib/egg_loader.js | 2 +- lib/mixin/config.js | 25 +- .../config/config.default.js | 1 + .../fixtures/app-core-middleware/package.json | 3 + .../plugin/node_modules/d/config/config.js | 5 - .../config/config.default.js | 1 + .../plugin/plugin-middleware/package.json | 5 + .../plugin-proxy/config/config.default.js | 1 + .../fixtures/plugin/plugin-proxy/package.json | 5 + test/load_config.test.js | 40 ++- test/load_extend.test.js | 2 +- test/loader.test.js | 2 +- 13 files changed, 256 insertions(+), 63 deletions(-) create mode 100644 test/fixtures/app-core-middleware/config/config.default.js create mode 100644 test/fixtures/app-core-middleware/package.json create mode 100644 test/fixtures/plugin/plugin-middleware/config/config.default.js create mode 100644 test/fixtures/plugin/plugin-middleware/package.json create mode 100644 test/fixtures/plugin/plugin-proxy/config/config.default.js create mode 100644 test/fixtures/plugin/plugin-proxy/package.json diff --git a/README.md b/README.md index 271da401..995df505 100644 --- a/README.md +++ b/README.md @@ -20,53 +20,208 @@ [download-image]: https://img.shields.io/npm/dm/egg-loader.svg?style=flat-square [download-url]: https://npmjs.org/package/egg-loader -egg 文件加载器 +A core Plugable framework based on koa -## 使用说明 +**Don't use it directly, see [egg]** + +## Usage + +Directory structure + +``` +├── package.json +├── app.js (optional) +├── agent.js (optional) +├── app +| ├── router.js +│ ├── controller +│ │ └── home.js +| ├── extend (optional) +│ | ├── helper.js (optional) +│ | ├── filter.js (optional) +│ | ├── request.js (optional) +│ | ├── response.js (optional) +│ | ├── context.js (optional) +│ | ├── application.js (optional) +│ | └── agent.js (optional) +│ ├── service (optional) +│ ├── middleware (optional) +│ │ └── response_time.js +│ └── view (optional) +| ├── layout.html +│ └── home.html +├── config +| ├── config.default.js +│ ├── config.prod.js +| ├── config.test.js (optional) +| ├── config.local.js (optional) +| ├── config.unittest.js (optional) +│ └── plugin.js +``` + +Than you can start with code below ```js -const app = koa(); -const Loader = require('egg-loader'); -const loader = new Loader({ - baseDir: '/path/to/app', - eggPath: '/path/to/framework', - app: app, +const Application = require('egg-core').Application; +const app = new Application({ + baseDir: '/path/to/app' }); -loader.loadPlugin(); -loader.loadConfig(); +app.ready(() => { + app.listen(3000); +}); +``` + +## EggLoader + +EggLoader will load file or directory easily, you can also custom your loader with low level API. + +### constructor + +- {String} baseDir - current directory of application +- {Object} app - instance of egg application +- {Object} plugins - merge plugins for test +- {Logger} logger - logger instance,default is console + +### High Level API + +#### loadPlugin + +Load config/plugin.js + +#### loadConfig + +Load config/config.js and config/{serverEnv}.js + +#### loadController + +Load app/controller + +#### loadMiddleware + +Load app/middleware + +#### loadApplicationExtend + +Load app/extend/application.js + +#### loadContextExtend + +Load app/extend/context.js + +#### loadRequestExtend + +Load app/extend/request.js + +#### loadResponseExtend + +Load app/extend/response.js + +#### loadHelperExtend + +Load app/extend/helper.js + +#### loadCustomApp + +Load app.js + +#### loadCustomAgent + +Load agent.js + +#### loadService + +Load app/service + +### Low Level API + +#### getServerEnv() + +Get serverEnv for application, available serverEnv + +serverEnv | description +--- | --- +default | default environment +test | system integration testing environment +prod | production environment +local | local environment on your own computer +unittest | unit test environment + +You can use this.serverEnv directly after instantiation. + +#### getEggPaths() + +Get the directory of the frameworks, a new framework born by extending egg, then you can use this function to get all frameworks. + +#### getLoadUnits() + +A loadUnit is a directory that can be loaded by EggLoader, it has the same structure. + +This function will get add loadUnits follow the order: + +1. plugin +2. framework +3. app + +loadUnit has a path and a type(app, framework, plugin). + +```js +{ + path: 'path/to/application', + type: 'app', +} ``` -## API +#### getAppname() + +Get appname from package.json + +#### loadFile(filepath) + +Load single file, will invork when export is function. + +#### loadToApp(directory, property, LoaderOptions) + +Load the files in directory to app. -### options +Invoke `this.loadToApp('$baseDir/app/controller', 'controller')`, then you can use it by `app.controller`. -- baseDir: 应用根目录 -- eggPath: egg 本身的路径 -- plugins: 自定义插件配置 -- app: 任何基于 koa 实例化 +#### loadToContext(directory, property, LoaderOptions) -### methods +Load the files in directory to context, it will bind the context. -基础方式 +``` +// define service in app/service/query.js +module.exports = class Query { + constructor(ctx) { + // get the ctx + } + + get() {} +}; + +// use the service in app/controller/home.js +module.exports = function*() { + this.body = this.service.query.get(); +}; +``` -- loadFile: 加载单文件, -- loadDirs: 获取需要加载的所有目录,按照 egg > 插件 > 框架 > 应用的顺序加载。 +#### loadExtend(name, target) + +Loader app/extend/xx.js to target, example + +```js +this.loadExtend('application', app); +``` -业务方法 +### LoaderOptions -- getAppname: 获取应用名 -- loadServerEnv: 加载环境变量 -- loadConfig: 加载: config -- loadPlugin: 加载插件 -- loadApplication: 加载 extend/application.js 到 app -- loadRequest: 加载 extend/request.js 到 app.request -- loadResponse: 加载 extend/response.js 到 app.response -- loadContext: 加载 extend/context.js 到 app.context -- loadHelper: 加载 extend/helper.js,到 app.Helper.prototype,需要定义 app.Helper 才会加载 -- loadService: 加载 app/service 到 app.service -- loadProxy: 加载 app/proxy 到 app.proxy -- loadMiddleware: 加载中间件 -- loadController: 加载 app/controller 到 app.controller -- loadAgent: 加载 agent.js 进行自定义 -- loadApp: 加载 app.js 进行自定义 +- {String|Array} directory - directories to load +- {Object} target: attach object from loaded files, +- {String} ignore - ignore the files when load +- {Function} initializer - custom file exports +- {Boolean} lowercaseFirst - determine whether the fist letter is lowercase +- {Boolean} override: determine whether override the property when get the same name +- {Boolean} call - determine whether invoke when exports is function +- {Object} inject - an object that be the argument when invoke the function +[egg]: https://github.com/eggjs/egg diff --git a/lib/egg_loader.js b/lib/egg_loader.js index c8794b69..27a7c4b5 100644 --- a/lib/egg_loader.js +++ b/lib/egg_loader.js @@ -118,7 +118,7 @@ class EggLoader { /** * Get all framework directories. * - * You can extend Application of egg, the extrypoint is options.app, + * You can extend Application of egg, the entrypoint is options.app, * * loader will find all directories from the prototype of Application, * you should define `Symbol.for('egg#eggPath')` property. diff --git a/lib/mixin/config.js b/lib/mixin/config.js index e05cb978..fe0f8481 100644 --- a/lib/mixin/config.js +++ b/lib/mixin/config.js @@ -4,6 +4,8 @@ const debug = require('debug')('egg-loader:config'); const fs = require('fs'); const path = require('path'); const extend = require('extend'); +const assert = require('assert'); + module.exports = { @@ -25,15 +27,15 @@ module.exports = { // Load Application config first const appConfig = this._preloadAppConfig(); - // egg config.default // plugin config.default // framework config.default - // egg config.{env} + // app config.default // plugin config.{env} // framework config.{env} + // app config.{env} for (const filename of names) { for (const unit of this.getLoadUnits()) { - const config = this._loadConfig(unit.path, filename, appConfig); + const config = this._loadConfig(unit.path, filename, appConfig, unit.type); if (!config) { continue; @@ -58,16 +60,15 @@ module.exports = { ]; const target = {}; for (const filename of names) { - const config = this._loadConfig(this.options.baseDir, filename); + const config = this._loadConfig(this.options.baseDir, filename, undefined, 'app'); extend(true, target, config); } return target; }, - _loadConfig(dirpath, filename, extraInject) { - const pluginPaths = this.orderPlugins ? this.orderPlugins.map(plugin => plugin.path) : []; - const isPlugin = pluginPaths.indexOf(dirpath) > -1; - const isApp = dirpath === this.options.baseDir; + _loadConfig(dirpath, filename, extraInject, type) { + const isPlugin = type === 'plugin'; + const isApp = type === 'app'; let filepath = path.join(dirpath, 'config', filename); // let config.js compatible @@ -80,14 +81,12 @@ module.exports = { return null; } - // delete coreMiddleware when app and plugin if (isPlugin || isApp) { - delete config.coreMiddleware; + assert(!config.coreMiddleware, 'Can not define coreMiddleware in app or plugin'); } - // delete middleware and proxy when it's not app if (!isApp) { - delete config.middleware; - delete config.proxy; + assert(!config.middleware, 'Can not define middleware in framework or plugin'); + assert(!config.proxy, 'Can not define proxy in framework or plugin'); } return config; diff --git a/test/fixtures/app-core-middleware/config/config.default.js b/test/fixtures/app-core-middleware/config/config.default.js new file mode 100644 index 00000000..cfa46aeb --- /dev/null +++ b/test/fixtures/app-core-middleware/config/config.default.js @@ -0,0 +1 @@ +exports.coreMiddleware = []; diff --git a/test/fixtures/app-core-middleware/package.json b/test/fixtures/app-core-middleware/package.json new file mode 100644 index 00000000..0b5f2497 --- /dev/null +++ b/test/fixtures/app-core-middleware/package.json @@ -0,0 +1,3 @@ +{ + "name": "coreMiddleware" +} diff --git a/test/fixtures/plugin/node_modules/d/config/config.js b/test/fixtures/plugin/node_modules/d/config/config.js index 1e2f2ee9..e69de29b 100644 --- a/test/fixtures/plugin/node_modules/d/config/config.js +++ b/test/fixtures/plugin/node_modules/d/config/config.js @@ -1,5 +0,0 @@ -exports.proxy = { - -}; - -exports.middleware = ['d']; diff --git a/test/fixtures/plugin/plugin-middleware/config/config.default.js b/test/fixtures/plugin/plugin-middleware/config/config.default.js new file mode 100644 index 00000000..6a96b0a1 --- /dev/null +++ b/test/fixtures/plugin/plugin-middleware/config/config.default.js @@ -0,0 +1 @@ +exports.middleware = []; diff --git a/test/fixtures/plugin/plugin-middleware/package.json b/test/fixtures/plugin/plugin-middleware/package.json new file mode 100644 index 00000000..421531de --- /dev/null +++ b/test/fixtures/plugin/plugin-middleware/package.json @@ -0,0 +1,5 @@ +{ + "eggPlugin": { + "name": "middleware" + } +} diff --git a/test/fixtures/plugin/plugin-proxy/config/config.default.js b/test/fixtures/plugin/plugin-proxy/config/config.default.js new file mode 100644 index 00000000..547bf0c9 --- /dev/null +++ b/test/fixtures/plugin/plugin-proxy/config/config.default.js @@ -0,0 +1 @@ +exports.proxy = {}; diff --git a/test/fixtures/plugin/plugin-proxy/package.json b/test/fixtures/plugin/plugin-proxy/package.json new file mode 100644 index 00000000..9f9b14e3 --- /dev/null +++ b/test/fixtures/plugin/plugin-proxy/package.json @@ -0,0 +1,5 @@ +{ + "eggPlugin": { + "name": "proxy" + } +} diff --git a/test/load_config.test.js b/test/load_config.test.js index f07b241d..cf7886dc 100644 --- a/test/load_config.test.js +++ b/test/load_config.test.js @@ -1,6 +1,7 @@ 'use strict'; const should = require('should'); +const utils = require('./utils'); const Loader = require('./utils').Loader; describe('test/load_config.test.js', function() { @@ -48,12 +49,39 @@ describe('test/load_config.test.js', function() { should.not.exists(loader.config.pluginA); }); - it('should delete config.middleware and config.proxy', function() { - const loader = new Loader('plugin'); - loader.loadConfig(); - should.not.exists(loader.config.proxy); - loader.config.coreMiddleware.should.not.containEql('d'); - loader.config.appMiddleware.should.not.containEql('d'); + it('should throw when plugin define middleware', function() { + const loader = new Loader('plugin', { + plugins: { + middleware: { + enable: true, + path: utils.getFilepath('plugin/plugin-middleware'), + }, + }, + }); + (function() { + loader.loadConfig(); + }).should.throw('Can not define middleware in framework or plugin'); + }); + + it('should throw when plugin define proxy', function() { + const loader = new Loader('plugin', { + plugins: { + proxy: { + enable: true, + path: utils.getFilepath('plugin/plugin-proxy'), + }, + }, + }); + (function() { + loader.loadConfig(); + }).should.throw('Can not define proxy in framework or plugin'); + }); + + it('should throw when app define coreMiddleware', function() { + const loader = new Loader('app-core-middleware'); + (function() { + loader.loadConfig(); + }).should.throw('Can not define coreMiddleware in app or plugin'); }); it('should read appinfo from the function of config', function() { diff --git a/test/load_extend.test.js b/test/load_extend.test.js index 4b842486..be58a0fd 100644 --- a/test/load_extend.test.js +++ b/test/load_extend.test.js @@ -90,7 +90,7 @@ describe('test/load_extend.test.js', function() { }); loader.loadConfig(); loader.load(); - }).should.throw(/load_context_syntax_error\/app\/extend\/context\.js error: Unexpected token \)/); + }).should.throw(/ error: Unexpected token/); }); it('should extend symbol', function() { diff --git a/test/loader.test.js b/test/loader.test.js index 506fa5df..4a600fc3 100644 --- a/test/loader.test.js +++ b/test/loader.test.js @@ -184,7 +184,7 @@ describe('test/loader.test.js', () => { directory: path.join(dirBase, 'syntax_error'), target: app.model, }).load(); - }).should.throw(/load file: .*?test\/fixtures\/load_dirs\/syntax_error\/error\.js, error:/); + }).should.throw(/ error: Unexpected identifier/); }); it('should throw when directory contains dot', () => {