From fb03aff43a87723eee387f98c341e0b9fdc316e4 Mon Sep 17 00:00:00 2001 From: Raymond Feng Date: Thu, 5 May 2016 15:48:02 -0700 Subject: [PATCH] Add ability to load plugins from app --- lib/bootstrapper.js | 19 +++++++ lib/bundler.js | 6 +++ lib/plugin-loader.js | 59 +++++++++++++++++++++ test/bootstrapper.test.js | 3 ++ test/fixtures/simple-app/plugins/tracker.js | 23 ++++++++ 5 files changed, 110 insertions(+) create mode 100644 lib/plugin-loader.js create mode 100644 test/fixtures/simple-app/plugins/tracker.js diff --git a/lib/bootstrapper.js b/lib/bootstrapper.js index 7029476..cc5f200 100644 --- a/lib/bootstrapper.js +++ b/lib/bootstrapper.js @@ -1,6 +1,8 @@ var assert = require('assert'); var async = require('async'); var utils = require('./utils'); +var path = require('path'); +var pluginLoader = require('./plugin-loader'); var debug = require('debug')('loopback:boot:bootstrapper'); var Promise = global.Promise || require('bluebird'); @@ -31,6 +33,17 @@ var builtinPhases = [ 'load', 'compile', 'starting', 'start', 'started', ]; +function loadAndRegisterPlugins(bootstrapper, options) { + var loader = pluginLoader(options); + var loaderContext = {}; + loader.load(loaderContext); + loader.compile(loaderContext); + + for (var i in loaderContext.instructions.pluginScripts) { + bootstrapper.use('/boot/' + i, loaderContext.instructions.pluginScripts[i]); + } +} + /** * Create a new Bootstrapper with options * @param options @@ -64,6 +77,12 @@ function Bootstrapper(options) { var factory = require('./plugins/' + p); self.use('/boot/' + p, factory(options)); }); + + try { + loadAndRegisterPlugins(self, options); + } catch (err) { + debug(err); + } } /** diff --git a/lib/bundler.js b/lib/bundler.js index 34ccca4..d638fd7 100644 --- a/lib/bundler.js +++ b/lib/bundler.js @@ -15,6 +15,7 @@ var g = require('strong-globalize')(); * @param {Object} bundler A browserify object created by `browserify()`. */ module.exports = function addInstructionsToBrowserify(context, bundler) { + // bundlePluginScripts(context, bundler); bundleModelScripts(context, bundler); bundleMixinScripts(context, bundler); bundleComponentScripts(context, bundler); @@ -27,6 +28,11 @@ function bundleOtherScripts(context, bundler) { addScriptsToBundle('boot', list, bundler); } +function bundlePluginScripts(context, bundler) { + var list = context.instructions.pluginScripts; + addScriptsToBundle('plugins', list, bundler); +} + function bundleModelScripts(context, bundler) { bundleSourceFiles(context, 'models', bundler); } diff --git a/lib/plugin-loader.js b/lib/plugin-loader.js new file mode 100644 index 0000000..c093e96 --- /dev/null +++ b/lib/plugin-loader.js @@ -0,0 +1,59 @@ +var util = require('util'); +var utils = require('./utils'); +var path = require('path'); +var async = require('async'); +var debug = require('debug')('loopback:boot:plugin-loader'); +var PluginBase = require('./plugin-base'); +var _ = require('lodash'); + +module.exports = function(options) { + return new PluginScript(options); +}; + +function PluginScript(options) { + PluginBase.call(this, options, 'pluginScripts', null); +} + +util.inherits(PluginScript, PluginBase); + +PluginScript.prototype.load = function(context) { + var options = this.options; + var appRootDir = options.rootDir; + // require directories + var pluginDirs = options.pluginDirs || []; // precedence + pluginDirs = pluginDirs.concat(path.join(appRootDir, 'plugins')); + utils.resolveRelativePaths(pluginDirs, appRootDir); + + var pluginScripts = options.pluginScripts || []; + utils.resolveRelativePaths(pluginScripts, appRootDir); + + pluginDirs.forEach(function(dir) { + pluginScripts = pluginScripts.concat(utils.findScripts(dir)); + var envdir = dir + '/' + options.env; + pluginScripts = pluginScripts.concat(utils.findScripts(envdir)); + }); + + // de-dedup boot scripts -ERS + // https://github.com/strongloop/loopback-boot/issues/64 + pluginScripts = _.uniq(pluginScripts); + debug('Plugin scripts: %j', pluginScripts); + this.configure(context, pluginScripts); + return pluginScripts; +}; + +PluginScript.prototype.compile = function(context) { + var pluginScripts = context.configurations.pluginScripts; + context.instructions = context.instructions || {}; + var plugins = context.instructions.pluginScripts = {}; + var self = this; + pluginScripts.forEach(function(ps) { + debug('Loading %s', ps); + var factory = require(ps); + var handler = factory(self.options); + var name = handler.name || path.basename(ps, '.js'); + debug('Loaded plugin name: %s', name); + plugins[name] = handler; + }); +}; + + diff --git a/test/bootstrapper.test.js b/test/bootstrapper.test.js index 0f1bea7..c649d38 100644 --- a/test/bootstrapper.test.js +++ b/test/bootstrapper.test.js @@ -29,6 +29,7 @@ describe('Bootstrapper', function() { expect(context.configurations.bootScripts).to.be.object; expect(context.configurations.middleware).to.be.object; expect(context.configurations.models).to.be.object; + expect(context.configurations.tracker).to.eql('load'); expect(context.instructions).to.be.undefined; expect(process.bootFlags.length).to.eql(0); done(); @@ -55,6 +56,8 @@ describe('Bootstrapper', function() { expect(context.configurations.models).to.be.undefined; expect(context.configurations.bootScripts).to.be.object; expect(context.instructions.application).to.be.object; + expect(context.instructions.tracker).to.eql('compile'); + expect(context.executions.tracker).to.eql('start'); expect(process.bootFlags).to.eql(['barLoaded', 'barSyncLoaded', 'fooLoaded', diff --git a/test/fixtures/simple-app/plugins/tracker.js b/test/fixtures/simple-app/plugins/tracker.js new file mode 100644 index 0000000..8df632a --- /dev/null +++ b/test/fixtures/simple-app/plugins/tracker.js @@ -0,0 +1,23 @@ +module.exports = function(opitions) { + return new Tracker(opitions); +}; + +function Tracker(options) { + this.name = 'tracker'; + this.options = options || {}; +} + +Tracker.prototype.load = function(context) { + context.configurations.tracker = 'load'; +}; + +Tracker.prototype.compile = function(context, done) { + context.instructions.tracker = 'compile'; + process.nextTick(done); +}; + +Tracker.prototype.start = function(context, done) { + context.executions = context.executions || {}; + context.executions.tracker = 'start'; + process.nextTick(done); +};