summaryrefslogtreecommitdiffstats
path: root/lib/pluginslist.js
diff options
context:
space:
mode:
authorSamy Pessé <samypesse@gmail.com>2015-03-09 10:43:12 +0100
committerSamy Pessé <samypesse@gmail.com>2015-03-09 10:43:12 +0100
commit34fc2831e0cf0fed01c71cec28d93472d87f455b (patch)
treea803cc907c20491ba02863b5d3dd5aedf6bfed10 /lib/pluginslist.js
parente1594cde2c32e4ff48f6c4eff3d3d461743d74e1 (diff)
parent1bf68a5aa0703b5a1815cfe4ebb731b5fb6ed9d2 (diff)
downloadgitbook-34fc2831e0cf0fed01c71cec28d93472d87f455b.zip
gitbook-34fc2831e0cf0fed01c71cec28d93472d87f455b.tar.gz
gitbook-34fc2831e0cf0fed01c71cec28d93472d87f455b.tar.bz2
Merge branch 'version/2.0'
Diffstat (limited to 'lib/pluginslist.js')
-rw-r--r--lib/pluginslist.js225
1 files changed, 225 insertions, 0 deletions
diff --git a/lib/pluginslist.js b/lib/pluginslist.js
new file mode 100644
index 0000000..61d52a5
--- /dev/null
+++ b/lib/pluginslist.js
@@ -0,0 +1,225 @@
+var _ = require("lodash");
+var Q = require("q");
+var npmi = require('npmi');
+var npm = require('npm');
+var semver = require('semver');
+
+var Plugin = require("./plugin");
+var pkg = require("../package.json");
+
+var initNPM = _.memoize(function() {
+ return Q.nfcall(npm.load, { silent: true, loglevel: 'silent' });
+});
+
+
+var PluginsList = function(book, plugins) {
+ this.book = book;
+ this.log = this.book.log;
+
+ // List of Plugin objects
+ this.list = [];
+
+ // List of names of failed plugins
+ this.failed = [];
+
+ // Namespaces
+ this.namespaces = _.chain(["website", "ebook"])
+ .map(function(namespace) {
+ return [
+ namespace,
+ {
+ html: {},
+ resources: _.chain(Plugin.RESOURCES)
+ .map(function(type) {
+ return [type, []];
+ })
+ .object()
+ .value()
+ }
+ ];
+ })
+ .object()
+ .value();
+
+ // Bind methods
+ _.bindAll(this);
+
+ if (plugins) this.load(plugins);
+};
+
+// return count of plugins
+PluginsList.prototype.count = function() {
+ return this.list.length;
+};
+
+// Add and load a plugin
+PluginsList.prototype.load = function(plugin, options) {
+ var that = this;
+
+ if (_.isArray(plugin)) {
+ return _.reduce(plugin, function(prev, p) {
+ return prev.then(function() {
+ return that.load(p);
+ });
+ }, Q());
+ }
+ if (_.isObject(plugin) && !(plugin instanceof Plugin)) plugin = plugin.name;
+ if (_.isString(plugin)) plugin = new Plugin(this.book, plugin);
+
+ that.log.info("load plugin", plugin.name, "....");
+ if (!plugin.isValid()) {
+ that.log.info.fail();
+ that.failed.push(plugin.name);
+ return Q();
+ } else {
+ that.log.info.ok();
+
+ // Push in the list
+ that.list.push(plugin);
+ }
+
+ // Extract filters
+ _.each(plugin.getFilters(), function(filterFunc, filterName) {
+ that.book.template.addFilter(filterName, filterFunc);
+ });
+
+ // Extract blocks
+ _.each(plugin.getBlocks(), function(block, blockName) {
+ that.book.template.addBlock(blockName, block);
+ });
+
+ return _.reduce(_.keys(that.namespaces), function(prev, namespaceName) {
+ return prev.then(function() {
+ return plugin.getResources(namespaceName)
+ .then(function(plResources) {
+ var namespace = that.namespaces[namespaceName];
+
+ // Extract js and css
+ _.each(Plugin.RESOURCES, function(resourceType) {
+ namespace.resources[resourceType] = (namespace.resources[resourceType] || []).concat(plResources[resourceType] || []);
+ });
+
+ // Map of html resources by name added by each plugin
+ _.each(plResources.html || {}, function(value, tag) {
+ // Turn into function if not one already
+ if (!_.isFunction(value)) value = _.constant(value);
+
+ namespace.html[tag] = namespace.html[tag] || [];
+ namespace.html[tag].push(value);
+ });
+ })
+ });
+ }, Q());
+};
+
+// Call a hook
+PluginsList.prototype.hook = function(name, data) {
+ return _.reduce(this.list, function(prev, plugin) {
+ return prev.then(function(ret) {
+ return plugin.callHook(name, ret);
+ });
+ }, Q(data));
+};
+
+// Return a template from a plugin
+PluginsList.prototype.template = function(name) {
+ var withTpl = _.find(this.list, function(plugin) {
+ return (
+ plugin.infos.templates &&
+ plugin.infos.templates[name]
+ );
+ });
+
+ if (!withTpl) return null;
+ return withTpl.resolveFile(withTpl.infos.templates[name]);
+};
+
+// Return an html snippet
+PluginsList.prototype.html = function(namespace, tag, context, options) {
+ var htmlSnippets = this.namespaces[namespace].html[tag];
+ return _.map(htmlSnippets || [], function(code) {
+ return code.call(context, options);
+ }).join("\n");
+};
+
+// Return a resources map for a namespace
+PluginsList.prototype.resources = function(namespace) {
+ return this.namespaces[namespace].resources;
+};
+
+// Install plugins from a book
+PluginsList.prototype.install = function() {
+ var that = this;
+ var defaultsPlugins = _.pluck(that.book.options.defaultsPlugins)
+
+ // Remove defaults (no need to install)
+ var plugins = _.filter(that.book.options.plugins, function(plugin) {
+ return !_.contains(defaultsPlugins, plugin.name);
+ });
+
+ // Install plugins one by one
+ that.book.log.info.ln(plugins.length+" plugins to install");
+ return _.reduce(plugins, function(prev, plugin) {
+ return prev.then(function() {
+ var fullname = "gitbook-plugin-"+plugin.name;
+
+ return Q()
+
+ // Resolve version if needed
+ .then(function() {
+ if (plugin.version) return plugin.version;
+
+ that.book.log.info.ln("No version specified, resolve plugin", plugin.name);
+ return initNPM()
+ .then(function() {
+ return Q.nfcall(npm.commands.view, [fullname+"@*", "engines"], true);
+ })
+ .then(function(versions) {
+ return _.chain(versions)
+ .pairs()
+ .map(function(v) {
+ return {
+ version: v[0],
+ gitbook: (v[1].engines || {})["gitbook"]
+ }
+ })
+ .filter(function(v) {
+ return v.gitbook && semver.satisfies(pkg.version, v.gitbook);
+ })
+ .sort(function(v1, v2) {
+ return semver.lt(v1.version, v2.version)? 1 : -1;
+ })
+ .pluck("version")
+ .first()
+ .value();
+ });
+ })
+
+ // Install the plugin with the resolved version
+ .then(function(version) {
+ if (!version) {
+ throw "Found no satisfactory version for plugin "+plugin.name;
+ }
+
+ that.book.log.info.ln("install plugin", plugin.name, "from npm ("+fullname+") with version", version);
+ return Q.nfcall(npmi, {
+ 'name': fullname,
+ 'version': version,
+ 'path': that.book.root,
+ 'npmLoad': {
+ 'loglevel': 'silent',
+ 'loaded': true,
+ 'prefix': that.book.root
+ }
+ });
+ })
+ .then(function() {
+ that.book.log.info.ok("plugin", plugin.name, "installed with success");
+ });
+ });
+ }, Q());
+};
+
+
+
+module.exports = PluginsList;