Add support for async boot scripts
This commit is contained in:
parent
e974033395
commit
94cb4d6342
|
@ -12,6 +12,7 @@
|
||||||
"newcap": true,
|
"newcap": true,
|
||||||
"nonew": true,
|
"nonew": true,
|
||||||
"sub": true,
|
"sub": true,
|
||||||
|
"unused": "vars",
|
||||||
"globals": {
|
"globals": {
|
||||||
"describe": true,
|
"describe": true,
|
||||||
"it": true,
|
"it": true,
|
||||||
|
|
4
index.js
4
index.js
|
@ -75,12 +75,12 @@ var addInstructionsToBrowserify = require('./lib/bundler');
|
||||||
* @header boot(app, [options])
|
* @header boot(app, [options])
|
||||||
*/
|
*/
|
||||||
|
|
||||||
exports = module.exports = function bootLoopBackApp(app, options) {
|
exports = module.exports = function bootLoopBackApp(app, options, callback) {
|
||||||
// backwards compatibility with loopback's app.boot
|
// backwards compatibility with loopback's app.boot
|
||||||
options.env = options.env || app.get('env');
|
options.env = options.env || app.get('env');
|
||||||
|
|
||||||
var instructions = compile(options);
|
var instructions = compile(options);
|
||||||
execute(app, instructions);
|
execute(app, instructions, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,6 +2,7 @@ var assert = require('assert');
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
var semver = require('semver');
|
var semver = require('semver');
|
||||||
var debug = require('debug')('loopback:boot:executor');
|
var debug = require('debug')('loopback:boot:executor');
|
||||||
|
var async = require('async');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute bootstrap instructions gathered by `boot.compile`.
|
* Execute bootstrap instructions gathered by `boot.compile`.
|
||||||
|
@ -12,7 +13,7 @@ var debug = require('debug')('loopback:boot:executor');
|
||||||
* @header boot.execute(instructions)
|
* @header boot.execute(instructions)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = function execute(app, instructions) {
|
module.exports = function execute(app, instructions, callback) {
|
||||||
patchAppLoopback(app);
|
patchAppLoopback(app);
|
||||||
assertLoopBackVersion(app);
|
assertLoopBackVersion(app);
|
||||||
|
|
||||||
|
@ -24,9 +25,16 @@ module.exports = function execute(app, instructions) {
|
||||||
setupDataSources(app, instructions);
|
setupDataSources(app, instructions);
|
||||||
setupModels(app, instructions);
|
setupModels(app, instructions);
|
||||||
|
|
||||||
runBootScripts(app, instructions);
|
// Run the boot scripts in series synchronously or asynchronously
|
||||||
|
// Please note async supports both styles
|
||||||
enableAnonymousSwagger(app, instructions);
|
async.series([
|
||||||
|
function(done) {
|
||||||
|
runBootScripts(app, instructions, done);
|
||||||
|
},
|
||||||
|
function(done) {
|
||||||
|
enableAnonymousSwagger(app, instructions);
|
||||||
|
done();
|
||||||
|
}], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
function patchAppLoopback(app) {
|
function patchAppLoopback(app) {
|
||||||
|
@ -176,13 +184,36 @@ function forEachKeyedObject(obj, fn) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function runScripts(app, list) {
|
function runScripts(app, list, callback) {
|
||||||
if (!list || !list.length) return;
|
list = list || [];
|
||||||
|
var functions = [];
|
||||||
list.forEach(function(filepath) {
|
list.forEach(function(filepath) {
|
||||||
|
debug('Requiring script %s', filepath);
|
||||||
var exports = tryRequire(filepath);
|
var exports = tryRequire(filepath);
|
||||||
if (typeof exports === 'function')
|
if (typeof exports === 'function') {
|
||||||
exports(app);
|
debug('Exported function detected %s', filepath);
|
||||||
|
functions.push({
|
||||||
|
path: filepath,
|
||||||
|
func: exports
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async.eachSeries(functions, function(f, done) {
|
||||||
|
debug('Running script %s', f.path);
|
||||||
|
if (f.func.length >= 2) {
|
||||||
|
debug('Starting async function %s', f.path);
|
||||||
|
f.func(app, function(err) {
|
||||||
|
debug('Async function finished %s', f.path);
|
||||||
|
done(err);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
debug('Starting sync function %s', f.path);
|
||||||
|
f.func(app);
|
||||||
|
debug('Sync function finished %s', f.path);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryRequire(modulePath) {
|
function tryRequire(modulePath) {
|
||||||
|
@ -198,8 +229,8 @@ function tryRequire(modulePath) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function runBootScripts(app, instructions) {
|
function runBootScripts(app, instructions, callback) {
|
||||||
runScripts(app, instructions.files.boot);
|
runScripts(app, instructions.files.boot, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableAnonymousSwagger(app, instructions) {
|
function enableAnonymousSwagger(app, instructions) {
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
"url": "https://github.com/strongloop/loopback-boot/blob/master/LICENSE"
|
"url": "https://github.com/strongloop/loopback-boot/blob/master/LICENSE"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"async": "~0.9.0",
|
||||||
"commondir": "0.0.1",
|
"commondir": "0.0.1",
|
||||||
"debug": "^1.0.4",
|
"debug": "^1.0.4",
|
||||||
"lodash.clonedeep": "^2.4.1",
|
"lodash.clonedeep": "^2.4.1",
|
||||||
|
|
|
@ -3,6 +3,7 @@ var path = require('path');
|
||||||
var loopback = require('loopback');
|
var loopback = require('loopback');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var expect = require('must');
|
var expect = require('must');
|
||||||
|
var fs = require('fs-extra');
|
||||||
var sandbox = require('./helpers/sandbox');
|
var sandbox = require('./helpers/sandbox');
|
||||||
var appdir = require('./helpers/appdir');
|
var appdir = require('./helpers/appdir');
|
||||||
|
|
||||||
|
@ -161,13 +162,62 @@ describe('executor', function() {
|
||||||
|
|
||||||
describe('with boot and models files', function() {
|
describe('with boot and models files', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
|
process.bootFlags = process.bootFlags || [];
|
||||||
boot.execute(app, simpleAppInstructions());
|
boot.execute(app, simpleAppInstructions());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should run `boot/*` files', function() {
|
afterEach(function() {
|
||||||
assert(process.loadedFooJS);
|
delete process.bootFlags;
|
||||||
delete process.loadedFooJS;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should run `boot/*` files', function(done) {
|
||||||
|
// scripts are loaded by the order of file names
|
||||||
|
expect(process.bootFlags).to.eql([
|
||||||
|
'barLoaded',
|
||||||
|
'barSyncLoaded',
|
||||||
|
'fooLoaded',
|
||||||
|
'barStarted'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// bar finished happens in the next tick
|
||||||
|
// barSync executed after bar finished
|
||||||
|
setTimeout(function() {
|
||||||
|
expect(process.bootFlags).to.eql([
|
||||||
|
'barLoaded',
|
||||||
|
'barSyncLoaded',
|
||||||
|
'fooLoaded',
|
||||||
|
'barStarted',
|
||||||
|
'barFinished',
|
||||||
|
'barSyncExecuted'
|
||||||
|
]);
|
||||||
|
done();
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with boot with callback', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
process.bootFlags = process.bootFlags || [];
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
delete process.bootFlags;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should run `boot/*` files asynchronously', function(done) {
|
||||||
|
boot.execute(app, simpleAppInstructions(), function() {
|
||||||
|
expect(process.bootFlags).to.eql([
|
||||||
|
'barLoaded',
|
||||||
|
'barSyncLoaded',
|
||||||
|
'fooLoaded',
|
||||||
|
'barStarted',
|
||||||
|
'barFinished',
|
||||||
|
'barSyncExecuted'
|
||||||
|
]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('with PaaS and npm env variables', function() {
|
describe('with PaaS and npm env variables', function() {
|
||||||
|
@ -299,5 +349,7 @@ function someInstructions(values) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function simpleAppInstructions() {
|
function simpleAppInstructions() {
|
||||||
return boot.compile(SIMPLE_APP);
|
// Copy it so that require will happend again
|
||||||
|
fs.copySync(SIMPLE_APP, appdir.PATH);
|
||||||
|
return boot.compile(appdir.PATH);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
process.bootFlags.push('barLoaded');
|
||||||
|
module.exports = function(app, callback) {
|
||||||
|
process.bootFlags.push('barStarted');
|
||||||
|
process.nextTick(function() {
|
||||||
|
process.bootFlags.push('barFinished');
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
};
|
|
@ -0,0 +1,5 @@
|
||||||
|
process.bootFlags.push('barSyncLoaded');
|
||||||
|
module.exports = function(app) {
|
||||||
|
process.bootFlags.push('barSyncExecuted');
|
||||||
|
};
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
process.loadedFooJS = true;
|
process.bootFlags.push('fooLoaded');
|
Loading…
Reference in New Issue