Load all plugins, even dependencies of plugins

This commit is contained in:
Samy Pessé
2016-03-15 12:37:25 +01:00
parent 4d19a33af2
commit fbffd54aa2
4 changed files with 67 additions and 49 deletions
+36 -13
View File
@@ -38,24 +38,21 @@ PluginsManager.prototype.get = function(name) {
});
};
// Load a plugin, or a list of plugins
PluginsManager.prototype.load = function(name) {
// Load a plugin (could be a BookPlugin or {name,path})
PluginsManager.prototype.load = function(plugin) {
var that = this;
if (_.isArray(name)) {
return Promise.serie(name, function(_name) {
return that.load(_name);
});
if (_.isArray(plugin)) {
return Promise.serie(plugin, that.load);
}
return Promise()
// Initiate and load the plugin
.then(function() {
var plugin;
if (!_.isString(name)) plugin = name;
else plugin = new BookPlugin(that.book, name);
if (!(plugin instanceof BookPlugin)) {
plugin = new BookPlugin(that.book, plugin.name, plugin.path);
}
if (that.get(plugin.id)) {
throw new Error('Plugin "'+plugin.id+'" is already loaded');
@@ -73,10 +70,36 @@ PluginsManager.prototype.load = function(name) {
// Load all plugins from the book's configuration
PluginsManager.prototype.loadAll = function() {
var plugins = _.pluck(this.book.config.get('plugins'), 'name');
var that = this;
var pluginNames = _.pluck(this.book.config.get('plugins'), 'name');
this.log.info.ln('loading', plugins.length, 'plugins');
return this.load(plugins);
return registry.list(this.book)
.then(function(plugins) {
// Filter out plugins not listed of first level
// (aka pre-installed plugins)
plugins = _.filter(plugins, function(plugin) {
return (
plugin.depth > 1 ||
_.contains(pluginNames, plugin.name)
);
});
// Log state
that.log.info.ln(_.size(plugins) + ' are installed');
if (_.size(pluginNames) != _.size(plugins)) that.log.info.ln(_.size(pluginNames) + ' explicitly listed');
// Verify that all plugins are present
var notInstalled = _.filter(pluginNames, function(name) {
return !_.find(plugins, { name: name });
});
if (_.size(notInstalled) > 0) {
throw new Error('Couldn\'t locate plugins "' + notInstalled.join(', ') + '", Run \'gitbook install\' to install plugins from registry.');
}
// Load plugins
return that.load(plugins);
});
};
// Setup a plugin
+11 -22
View File
@@ -24,13 +24,14 @@ function isModuleNotFound(err) {
return err.message.indexOf('Cannot find module') >= 0;
}
function BookPlugin(book, pluginId) {
function BookPlugin(book, pluginId, pluginFolder) {
this.book = book;
this.log = this.book.log.prefix(pluginId);
this.id = pluginId;
this.npmId = registry.npmId(pluginId);
this.root;
this.root = pluginFolder;
this.packageInfos = undefined;
this.content = undefined;
@@ -51,8 +52,7 @@ BookPlugin.prototype.bind = function(fn) {
return fn.bind(compatibility.pluginCtx(this));
};
// Load this plugin
// An optional folder to search in can be passed
// Load this plugin from its root folder
BookPlugin.prototype.load = function(folder) {
var that = this;
@@ -60,18 +60,12 @@ BookPlugin.prototype.load = function(folder) {
return Promise.reject(new Error('Plugin "' + this.id + '" is already loaded'));
}
// Fodlers to search plugins in
var searchPaths = _.compact([
folder,
this.book.resolve('node_modules'),
__dirname
]);
// Try loading plugins from different location
var p = Promise.some(searchPaths, function(baseDir) {
var p = Promise()
.then(function() {
// Locate plugin and load pacjage.json
try {
var res = resolve.sync(that.npmId + '/package.json', { basedir: baseDir });
var res = resolve.sync('./package.json', { basedir: that.root });
that.root = path.dirname(res);
that.packageInfos = require(res);
@@ -81,12 +75,12 @@ BookPlugin.prototype.load = function(folder) {
that.packageInfos = undefined;
that.content = undefined;
return false;
return;
}
// Load plugin JS content
try {
that.content = require(resolve.sync(that.npmId, { basedir: baseDir }));
that.content = require(that.root);
} catch(err) {
// It's no big deal if the plugin doesn't have an "index.js"
// (For example: themes)
@@ -98,8 +92,6 @@ BookPlugin.prototype.load = function(folder) {
});
}
}
return true;
})
.then(that.validate)
@@ -122,18 +114,15 @@ BookPlugin.prototype.load = function(folder) {
// This method throws erros if plugin is invalid
BookPlugin.prototype.validate = function() {
var isValid = (
this.isLoaded() &&
this.packageInfos &&
this.packageInfos.name &&
this.packageInfos.engines &&
this.packageInfos.engines.gitbook
);
if (!this.isLoaded()) {
throw new Error('Couldn\'t locate plugin "' + this.id + '", Run \'gitbook install\' to install plugins from registry.');
}
if (!isValid) {
throw new Error('Invalid plugin "' + this.id + '"');
throw new Error('Error loading plugin "' + this.id + '" at "' + this.root + '"');
}
if (!gitbook.satisfies(this.packageInfos.engines.gitbook)) {
+10 -8
View File
@@ -23,7 +23,7 @@ function pluginId(name) {
// Validate an NPM plugin ID
function validateId(name) {
return name.indexOf(PLUGIN_PREFIX) === 0;
return name && name.indexOf(PLUGIN_PREFIX) === 0;
}
// Initialize NPM for operations
@@ -107,24 +107,25 @@ function installPlugin(book, plugin, version) {
}
// List all packages installed inside a folder
// Returns a map { pluginName -> folder }
// Returns an ordered list of plugins
function listInstalled(folder) {
var options = {
dev: false,
log: function() {},
depth: 4
};
var result = {};
var results = [];
function onPackage(pkg, isRoot) {
if (!validateId(pkg.name)){
if (!isRoot) return;
} else {
result[pluginId(pkg.name)] = {
results.push({
name: pluginId(pkg.name),
version: pkg.version,
path: pkg.realPath,
depth: pkg.depth
};
});
}
_.each(pkg.dependencies, function(dep) {
@@ -135,18 +136,19 @@ function listInstalled(folder) {
return Promise.nfcall(readInstalled, folder, options)
.then(function(data) {
onPackage(data, true);
return result;
return _.uniq(results, 'name');
});
}
// List installed plugins for a book (defaults and installed)
function listPlugins(book) {
return Promise.nfcall([
return Promise.all([
listInstalled(path.resolve(__dirname, '../..')),
listInstalled(book.root)
])
.spread(function(defaultPlugins, plugins) {
return _.extend(defaultPlugins, plugins);
var results = plugins.concat(defaultPlugins);
return _.uniq(results, 'name');
});
}
+10 -6
View File
@@ -9,6 +9,10 @@ var BookPlugin = require('../lib/plugins/plugin');
var PLUGINS_ROOT = path.resolve(__dirname, 'node_modules');
function TestPlugin(book, name) {
return new BookPlugin(book, name, path.resolve(PLUGINS_ROOT, 'gitbook-plugin-'+name));
}
describe('Plugins', function() {
var book;
@@ -90,7 +94,7 @@ describe('Plugins', function() {
describe('Configuration', function() {
it('should fail loading a plugin with an invalid configuration', function() {
var plugin = new BookPlugin(book, 'test-config');
var plugin = TestPlugin(book, 'test-config');
return plugin.load(PLUGINS_ROOT)
.should.be.rejectedWith('Error with book\'s configuration: pluginsConfig.test-config.myProperty is required');
});
@@ -108,7 +112,7 @@ describe('Plugins', function() {
.then(function(book2) {
return book2.prepareConfig()
.then(function() {
var plugin = new BookPlugin(book2, 'test-config');
var plugin = TestPlugin(book2, 'test-config');
return plugin.load(PLUGINS_ROOT);
})
.then(function() {
@@ -122,7 +126,7 @@ describe('Plugins', function() {
var plugin;
before(function() {
plugin = new BookPlugin(book, 'test-resources');
plugin = TestPlugin(book, 'test-resources');
return plugin.load(PLUGINS_ROOT);
});
@@ -146,7 +150,7 @@ describe('Plugins', function() {
var plugin, filters;
before(function() {
plugin = new BookPlugin(book, 'test-filters');
plugin = TestPlugin(book, 'test-filters');
return plugin.load(PLUGINS_ROOT)
.then(function() {
@@ -171,7 +175,7 @@ describe('Plugins', function() {
var plugin, blocks;
before(function() {
plugin = new BookPlugin(book, 'test-blocks');
plugin = TestPlugin(book, 'test-blocks');
return plugin.load(PLUGINS_ROOT)
.then(function() {
@@ -196,7 +200,7 @@ describe('Plugins', function() {
var plugin;
before(function() {
plugin = new BookPlugin(book, 'test-hooks');
plugin = TestPlugin(book, 'test-hooks');
return plugin.load(PLUGINS_ROOT);
});