summaryrefslogtreecommitdiffstats
path: root/lib/plugins
diff options
context:
space:
mode:
authorSamy Pessé <samypesse@gmail.com>2016-04-22 11:33:25 +0200
committerSamy Pessé <samypesse@gmail.com>2016-04-22 11:33:25 +0200
commitcc78b880cef726201f1670b5c9f7f6f3512c2dbe (patch)
tree2e41e8bf387b1f1efc5a78aa71e0369550cad495 /lib/plugins
parent4336fdb2414d460ffee68a0cc87c0cb0c85cf56e (diff)
downloadgitbook-cc78b880cef726201f1670b5c9f7f6f3512c2dbe.zip
gitbook-cc78b880cef726201f1670b5c9f7f6f3512c2dbe.tar.gz
gitbook-cc78b880cef726201f1670b5c9f7f6f3512c2dbe.tar.bz2
Add utils to install plugins
Diffstat (limited to 'lib/plugins')
-rw-r--r--lib/plugins/index.js5
-rw-r--r--lib/plugins/installPlugins.js125
-rw-r--r--lib/plugins/loadPlugin.js13
-rw-r--r--lib/plugins/validateConfig.js70
4 files changed, 200 insertions, 13 deletions
diff --git a/lib/plugins/index.js b/lib/plugins/index.js
index bee8ac6..622f00c 100644
--- a/lib/plugins/index.js
+++ b/lib/plugins/index.js
@@ -1,6 +1,7 @@
module.exports = {
-
-
+ loadForBook: require('./loadForBook'),
+ validateConfig: require('./validateConfig'),
+ installPlugins: require('./installPlugins')
};
diff --git a/lib/plugins/installPlugins.js b/lib/plugins/installPlugins.js
new file mode 100644
index 0000000..77ecfce
--- /dev/null
+++ b/lib/plugins/installPlugins.js
@@ -0,0 +1,125 @@
+var npm = require('npm');
+var npmi = require('npmi');
+var semver = require('semver');
+var Immutable = require('immutable');
+
+var Promise = require('../utils/promise');
+var Plugin = require('../models/plugin');
+var gitbook = require('../gitbook');
+
+var npmIsReady;
+
+/**
+ Initialize and prepare NPM
+
+ @return {Promise}
+*/
+function initNPM() {
+ if (npmIsReady) return npmIsReady;
+
+ npmIsReady = Promise.nfcall(npm.load, {
+ silent: true,
+ loglevel: 'silent'
+ });
+
+ return npmIsReady;
+}
+
+
+
+/**
+ Resolve a plugin to a version
+
+ @param {Plugin}
+ @return {Promise<String>}
+*/
+function resolveVersion(plugin) {
+ var npmId = Plugin.nameToNpmID(plugin.getName());
+ var requiredVersion = plugin.getVersion();
+
+ return initNPM()
+ .then(function() {
+ return Promise.nfcall(npm.commands.view, [npmId + '@' + requiredVersion, 'engines'], true);
+ })
+ .then(function(versions) {
+ versions = Immutable.Map(versions).entries().toList();
+
+ var result = versions
+ .map(function(entry) {
+ return {
+ version: entry[0],
+ gitbook: (entry[1].engines || {}).gitbook
+ };
+ })
+ .filter(function(v) {
+ return v.gitbook && gitbook.satisfies(v.gitbook);
+ })
+ .sort(function(v1, v2) {
+ return semver.lt(v1.version, v2.version)? 1 : -1;
+ })
+ .get(0);
+
+ if (!result) {
+ return undefined;
+ } else {
+ return result.get('version');
+ }
+ });
+}
+
+
+/**
+ Install a plugin for a book
+
+ @param {Book}
+ @param {Plugin}
+ @return {Promise}
+*/
+function installPlugin(book, plugin) {
+ var logger = book.getLogger();
+
+ var installFolder = book.getRoot();
+ var name = plugin.getName();
+ var requirement = plugin.getVersion();
+
+ logger.info.ln('installing plugin', name);
+
+ // Find a version to install
+ return resolveVersion(plugin)
+ .then(function(version) {
+ if (!version) {
+ throw new Error('Found no satisfactory version for plugin "' + name + '" with requirement "' + requirement + '"');
+ }
+
+ logger.info.ln('install plugin "' + plugin +'" from NPM with version', requirement);
+ return Promise.nfcall(npmi, {
+ 'name': plugin.getNpmID(),
+ 'version': version,
+ 'path': installFolder,
+ 'npmLoad': {
+ 'loglevel': 'silent',
+ 'loaded': true,
+ 'prefix': installFolder
+ }
+ });
+ })
+ .then(function() {
+ logger.info.ok('plugin "' + plugin + '" installed with success');
+ });
+}
+
+
+/**
+ Install plugin requirements for a book
+
+ @param {Book}
+ @param {OrderedMap<String:Plugin>}
+ @return {Promise}
+*/
+function installPlugins(book, plugins) {
+ return Promise.forEach(plugins, function(plugin) {
+ return installPlugin(book, plugin);
+ });
+}
+
+module.exports = installPlugins;
diff --git a/lib/plugins/loadPlugin.js b/lib/plugins/loadPlugin.js
index a0dac5f..375329e 100644
--- a/lib/plugins/loadPlugin.js
+++ b/lib/plugins/loadPlugin.js
@@ -33,7 +33,7 @@ function loadPlugin(book, plugin) {
var packageContent;
var content;
- // Locate plugin and load pacjage.json
+ // Locate plugin and load package.json
try {
var res = resolve.sync('./package.json', { basedir: pkgPath });
@@ -70,16 +70,7 @@ function loadPlugin(book, plugin) {
});
})
- .then(validatePlugin)
-
- // Validate the configuration and update it
- .then(function() {
- var config = that.book.config.get(that.getConfigKey(), {});
- return that.validateConfig(config);
- })
- .then(function(config) {
- that.book.config.set(that.getConfigKey(), config);
- });
+ .then(validatePlugin);
logger.info('loading plugin "' + name + '"... ');
return logger.info.promise(p);
diff --git a/lib/plugins/validateConfig.js b/lib/plugins/validateConfig.js
new file mode 100644
index 0000000..fc32344
--- /dev/null
+++ b/lib/plugins/validateConfig.js
@@ -0,0 +1,70 @@
+var jsonschema = require('jsonschema');
+var jsonSchemaDefaults = require('json-schema-defaults');
+var mergeDefaults = require('merge-defaults');
+
+var Promise = require('../utils/promise');
+var error = require('../utils/error');
+
+/**
+ Validate one plugin for a book and update book's confiration
+
+ @param {Book}
+ @param {Plugin}
+ @return {Book}
+*/
+function validatePluginConfig(book, plugin) {
+ var config = book.getConfig();
+ var packageInfos = plugin.getPackage();
+
+ var configKey = [
+ 'pluginsConfig',
+ plugin.getName()
+ ].join('.');
+
+ var pluginConfig = config.getValue(configKey, {});
+
+ var schema = packageInfos.gitbook || {};
+ if (!schema) return book;
+
+ // Normalize schema
+ schema.id = '/' + configKey;
+ schema.type = 'object';
+
+ // Validate and throw if invalid
+ var v = new jsonschema.Validator();
+ var result = v.validate(config, schema, {
+ propertyName: configKey
+ });
+
+ // Throw error
+ if (result.errors.length > 0) {
+ throw new error.ConfigurationError(new Error(result.errors[0].stack));
+ }
+
+ // Insert default values
+ var defaults = jsonSchemaDefaults(schema);
+ pluginConfig = mergeDefaults(config, defaults);
+
+
+ // Update configuration
+ config = config.setValue(configKey, pluginConfig);
+
+ // Return new book
+ return book.set('config', config);
+}
+
+/**
+ Validate a book configuration for plugins and
+ returns an update configuration with default values.
+
+ @param {Book}
+ @param {OrderedMap<String:Plugin>}
+ @return {Promise<Book>}
+*/
+function validateConfig(book, plugins) {
+ return Promise.reduce(plugins, function(newBook, plugin) {
+ return validatePluginConfig(newBook, plugin);
+ }, book);
+}
+
+module.exports = validateConfig;