diff --git a/lib/egg.js b/lib/egg.js index a32fa55a..8a2c2f6b 100644 --- a/lib/egg.js +++ b/lib/egg.js @@ -4,6 +4,7 @@ const assert = require('assert'); const fs = require('fs'); const KoaApplication = require('koa'); const EggConsoleLogger = require('egg-logger').EggConsoleLogger; +const debug = require('debug')('egg-core'); const DEPRECATE = Symbol('EggCore#deprecate'); @@ -165,6 +166,8 @@ class EggCore extends KoaApplication { }).on('ready_timeout', id => { this.console.warn('[egg:core:ready_timeout] 10 seconds later %s was still unable to finish.', id); }); + + this.ready(() => debug('egg emit ready, application started')); } } diff --git a/lib/loader/mixin/custom.js b/lib/loader/mixin/custom.js index e4d70b1a..ab64ba7b 100644 --- a/lib/loader/mixin/custom.js +++ b/lib/loader/mixin/custom.js @@ -2,6 +2,8 @@ const path = require('path'); +const LOAD_CUSTOM_FILE = Symbol('EggCore#loadCustomFile'); + module.exports = { /** @@ -18,19 +20,50 @@ module.exports = { * doAsync(done); * } * ``` + * + * it's better to use async function + * + * ```js + * module.exports = async function(app) { + * await doAsync(); + * } + * ``` + * + * Or use generator wrapped by co.wrap + * + * ```js + * const co = require('co'); + * module.exports = co.wrap(function*(app) { + * yield doAsync(); + * }); + * ``` * @since 1.0.0 */ loadCustomApp() { - this.getLoadUnits() - .forEach(unit => this.loadFile(path.join(unit.path, 'app.js'))); + this[LOAD_CUSTOM_FILE]('app.js'); }, /** * Load agent.js, same as {@link EggLoader#loadCustomApp} */ loadCustomAgent() { - this.getLoadUnits() - .forEach(unit => this.loadFile(path.join(unit.path, 'agent.js'))); + this[LOAD_CUSTOM_FILE]('agent.js'); }, + [LOAD_CUSTOM_FILE](filename) { + this.getLoadUnits() + .forEach(unit => { + const filepath = path.join(unit.path, filename); + const ret = this.loadFile(filepath); + registerCallback(ret, this.app, filepath); + }); + }, }; + +function registerCallback(ret, app, filepath) { + // register readyCallback if custom file export async function + if (ret instanceof Promise) { + const done = app.readyCallback(filepath); + ret.then(() => done()).catch(done); + } +} diff --git a/package.json b/package.json index dff6810b..f6066daa 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ }, "devDependencies": { "autod": "^2.7.0", + "co": "^4.6.0", "egg-bin": "1", "egg-ci": "1", "eslint": "~3.1.0", diff --git a/test/fixtures/custom-app-async-function/app.js b/test/fixtures/custom-app-async-function/app.js new file mode 100644 index 00000000..0c814d7c --- /dev/null +++ b/test/fixtures/custom-app-async-function/app.js @@ -0,0 +1,20 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments)).next()); + }); +}; +function default_1(app) { + return __awaiter(this, void 0, void 0, function* () { + yield wait(1000); + app.app = true; + }); +} +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = default_1; +function wait(timeout) { + return new Promise(resolve => setTimeout(resolve, timeout)); +} diff --git a/test/fixtures/custom-app-async-function/app.ts b/test/fixtures/custom-app-async-function/app.ts new file mode 100644 index 00000000..4eaec8dc --- /dev/null +++ b/test/fixtures/custom-app-async-function/app.ts @@ -0,0 +1,8 @@ +export default async function(app) { + await wait(1000); + app.app = true; +} + +function wait(timeout) { + return new Promise(resolve => setTimeout(resolve, timeout)); +} diff --git a/test/fixtures/custom-app-async-function/package.json b/test/fixtures/custom-app-async-function/package.json new file mode 100644 index 00000000..3d3a03f9 --- /dev/null +++ b/test/fixtures/custom-app-async-function/package.json @@ -0,0 +1,3 @@ +{ + "name": "custom-app-async-function" +} diff --git a/test/fixtures/custom-app-error/app.js b/test/fixtures/custom-app-error/app.js new file mode 100644 index 00000000..e6c6d580 --- /dev/null +++ b/test/fixtures/custom-app-error/app.js @@ -0,0 +1,12 @@ +'use strict'; + +const co = require('co'); + +module.exports = co.wrap(function*(app) { + yield wait(1000); + throw new Error('load async error'); +}); + +function wait(timeout) { + return new Promise(resolve => setTimeout(resolve, timeout)); +} diff --git a/test/fixtures/custom-app-error/package.json b/test/fixtures/custom-app-error/package.json new file mode 100644 index 00000000..858197f6 --- /dev/null +++ b/test/fixtures/custom-app-error/package.json @@ -0,0 +1,3 @@ +{ + "name": "custom-app-error" +} diff --git a/test/fixtures/custom-app-promise/app.js b/test/fixtures/custom-app-promise/app.js new file mode 100644 index 00000000..9a80b6cb --- /dev/null +++ b/test/fixtures/custom-app-promise/app.js @@ -0,0 +1,12 @@ +'use strict'; + +const co = require('co'); + +module.exports = co.wrap(function*(app) { + yield wait(1000); + app.app = true; +}); + +function wait(timeout) { + return new Promise(resolve => setTimeout(resolve, timeout)); +} diff --git a/test/fixtures/custom-app-promise/package.json b/test/fixtures/custom-app-promise/package.json new file mode 100644 index 00000000..32abf9cb --- /dev/null +++ b/test/fixtures/custom-app-promise/package.json @@ -0,0 +1,3 @@ +{ + "name": "custom-app-promise" +} diff --git a/test/loader/mixin/load_custom_app.test.js b/test/loader/mixin/load_custom_app.test.js index c334a879..536b0f60 100644 --- a/test/loader/mixin/load_custom_app.test.js +++ b/test/loader/mixin/load_custom_app.test.js @@ -3,29 +3,83 @@ const should = require('should'); const utils = require('../../utils'); -describe('test/loader/mixin/load_custom_app.test.js', function() { - - let app; - before(function() { - app = utils.createApp('plugin'); - app.loader.loadPlugin(); - app.loader.loadConfig(); - app.loader.loadCustomApp(); +describe('test/loader/mixin/load_custom_app.test.js', () => { + + describe('app.js as function', () => { + let app; + before(() => { + app = utils.createApp('plugin'); + app.loader.loadPlugin(); + app.loader.loadConfig(); + app.loader.loadCustomApp(); + }); + after(() => app.close()); + + it('should load app.js', () => { + app.b.should.equal('plugin b'); + app.c.should.equal('plugin c'); + app.app.should.equal('app'); + }); + + it('should app.js of plugin before application\'s', () => { + (app.dateB <= app.date).should.equal(true); + (app.dateC <= app.date).should.equal(true); + }); + + it('should not load plugin that is disabled', () => { + should.not.exists(app.a); + }); }); - after(() => app.close()); - it('should load app.js', function() { - app.b.should.equal('plugin b'); - app.c.should.equal('plugin c'); - app.app.should.equal('app'); + describe('app.js as function return promise', () => { + let app; + before(done => { + app = utils.createApp('custom-app-promise'); + app.loader.loadPlugin(); + app.loader.loadConfig(); + app.loader.loadCustomApp(); + app.ready(done); + }); + after(() => app.close()); + + it('should load app.js success', () => { + app.app.should.be.true(); + }); }); - it('should app.js of plugin before application\'s', function() { - (app.dateB <= app.date).should.equal(true); - (app.dateC <= app.date).should.equal(true); + describe('app.js as async function', () => { + let app; + before(done => { + app = utils.createApp('custom-app-async-function'); + app.loader.loadPlugin(); + app.loader.loadConfig(); + app.loader.loadCustomApp(); + app.ready(done); + }); + after(() => app.close()); + + it('should load app.js success', () => { + app.app.should.be.true(); + }); }); - it('should not load plugin that is disabled', function() { - should.not.exists(app.a); + + describe('app.js load async error', () => { + let app; + after(() => app.close()); + + it('should load app.js success', done => { + app = utils.createApp('custom-app-error'); + app.on('error', err => { + err.message.should.eql('load async error'); + done(); + }); + app.loader.loadPlugin(); + app.loader.loadConfig(); + app.loader.loadCustomApp(); + app.ready(() => { + throw new Error('should not call'); + }); + }); }); });