summaryrefslogtreecommitdiffstats
path: root/lib/plugin.js
diff options
context:
space:
mode:
authorSamy Pessé <samypesse@gmail.com>2015-01-19 16:23:44 +0100
committerSamy Pessé <samypesse@gmail.com>2015-01-19 16:23:44 +0100
commit7f01f4b048faf5ce5e1815c3676916ac7859279f (patch)
tree98a58abd2c18b3c46fea9987eb3316c1f2c80236 /lib/plugin.js
parent5ed584449007d9c6c816e765ce3e999e14abbf97 (diff)
downloadgitbook-7f01f4b048faf5ce5e1815c3676916ac7859279f.zip
gitbook-7f01f4b048faf5ce5e1815c3676916ac7859279f.tar.gz
gitbook-7f01f4b048faf5ce5e1815c3676916ac7859279f.tar.bz2
Add plugin object
Diffstat (limited to 'lib/plugin.js')
-rw-r--r--lib/plugin.js227
1 files changed, 227 insertions, 0 deletions
diff --git a/lib/plugin.js b/lib/plugin.js
new file mode 100644
index 0000000..c2cc668
--- /dev/null
+++ b/lib/plugin.js
@@ -0,0 +1,227 @@
+var _ = require("lodash");
+var Q = require("q");
+var semver = require("semver");
+var path = require("path");
+var url = require("url");
+var fs = require("./utils/fs");
+var npmi = require('npmi');
+var resolve = require('resolve');
+
+var pkg = require("../package.json");
+
+var Plugin = function(book, name) {
+ this.book = book;
+ this.name = name;
+ this.packageInfos = {};
+ this.infos = {};
+
+ // Bind methods
+ _.bindAll(this);
+
+ _.each([
+ "gitbook-plugin-"+name,
+ "gitbook-"+name,
+ name,
+ ], function(_name) {
+ if (this.load(_name, __dirname)) return false;
+ if (this.load(_name, book.root)) return false;
+ }, this);
+};
+
+// Type of plugins resources
+Plugin.RESOURCES = ["js", "css"];
+
+// Default plugins added to each books
+Plugin.defaults = ["mathjax"];
+
+// Load from a name
+Plugin.prototype.load = function(name, baseDir) {
+ try {
+ var res = resolve.sync(name+"/package.json", { basedir: baseDir });
+
+ this.baseDir = path.dirname(res);
+ this.packageInfos = require(res);
+ this.infos = require(resolve.sync(name, { basedir: baseDir }));
+ this.name = name;
+
+ return true;
+ } catch (e) {
+ return false;
+ }
+};
+
+Plugin.prototype.normalizeResource = function(resource) {
+ // Parse the resource path
+ var parsed = url.parse(resource);
+
+ // This is a remote resource
+ // so we will simply link to using it's URL
+ if (parsed.protocol) {
+ return {
+ "url": resource
+ };
+ }
+
+ // This will be copied over from disk
+ // and shipped with the book's build
+ return { "path": this.name+"/"+resource };
+};
+
+// Return resources
+Plugin.prototype._getResources = function(base) {
+ base = base || "book";
+ var book = this.infos[base];
+
+ // Nothing specified, fallback to default
+ if (!book) {
+ return Q({});
+ }
+
+ // Dynamic function
+ if(typeof book === "function") {
+ // Call giving it the context of our generator
+ return Q().then(book.bind(this.book));
+ }
+
+ // Plain data object
+ return Q(_.cloneDeep(book));
+};
+
+// Normalize resources and return them
+Plugin.prototype.getResources = function(base) {
+ var that = this;
+
+ return this._getResources(base)
+ .then(function(resources) {
+
+ _.each(RESOURCES, function(resourceType) {
+ resources[resourceType] = (resources[resourceType] || []).map(that.normalizeResource);
+ });
+
+ return resources;
+ });
+};
+
+// Test if it's a valid plugin
+Plugin.prototype.isValid = function() {
+ return (
+ this.packageInfos &&
+ this.packageInfos.name &&
+ this.packageInfos.engines &&
+ this.packageInfos.engines.gitbook &&
+ semver.satisfies(pkg.version, this.packageInfos.engines.gitbook)
+ );
+};
+
+// Resolve file path
+Plugin.prototype.resolveFile = function(filename) {
+ return path.resolve(this.baseDir, filename);
+};
+
+// Resolve file path
+Plugin.prototype.callHook = function(name, data) {
+ // Our generator will be the context to apply
+ var context = this.book;
+
+ var hookFunc = this.infos.hooks? this.infos.hooks[name] : null;
+ data = data || {};
+
+ if (!hookFunc) return Q(data);
+
+ return Q()
+ .then(function() {
+ return hookFunc.apply(context, [data]);
+ });
+};
+
+// Copy plugin assets fodler
+Plugin.prototype.copyAssets = function(out, options) {
+ var that = this;
+ options = _.defaults(options || {}, {
+ base: "book"
+ });
+
+ return this.getResources(options.base)
+ .get('assets')
+ .then(function(assets) {
+ // Assets are undefined
+ if(!assets) return false;
+
+ return fs.copy(
+ that.resolveFile(assets),
+ out
+ ).then(_.constant(true));
+ }, _.constant(false));
+};
+
+
+// Install a list of plugin
+Plugin.install = function(options) {
+ // Normalize list of plugins
+ var plugins = Plugin.normalizeList(options.plugins);
+
+ // Install plugins one by one
+ return _.reduce(plugins, function(prev, plugin) {
+ return prev.then(function() {
+ var fullname = "gitbook-plugin-"+plugin.name;
+ console.log("Install plugin", plugin.name, "from npm ("+fullname+") with version", (plugin.version || "*"));
+ return Q.nfcall(npmi, {
+ 'name': fullname,
+ 'version': plugin.version,
+ 'path': options.input,
+ 'npmLoad': {
+ 'loglevel': 'silent',
+ 'loaded': false,
+ 'prefix': options.input
+ }
+ });
+ });
+ }, Q());
+};
+
+// Normalize a list of plugins to use
+Plugin.normalizeList = function(plugins) {
+ // Normalize list to an array
+ plugins = _.isString(plugins) ? plugins.split(",") : (plugins || []);
+
+ // Divide as {name, version} to handle format like "myplugin@1.0.0"
+ plugins = _.map(plugins, function(plugin) {
+ var parts = plugin.split("@");
+ return {
+ 'name': parts[0],
+ 'version': parts[1] // optional
+ }
+ });
+
+ // List plugins to remove
+ var toremove = _.chain(plugins)
+ .filter(function(plugin) {
+ return plugin.name.length > 0 && plugin.name[0] == "-";
+ })
+ .map(function(plugin) {
+ return plugin.name.slice(1);
+ })
+ .value();
+
+ // Merge with defaults
+ plugins = _.chain(plugins)
+ .concat(_.map(Plugin.defaults, function(plugin) {
+ return { 'name': plugin }
+ }))
+ .uniq()
+ .value();
+
+ // Build final list
+ plugins = _.filter(plugins, function(plugin) {
+ return !_.contains(toremove, plugin.name) && !(plugin.name.length > 0 && plugin.name[0] == "-");
+ });
+
+ return plugins;
+};
+
+// Normalize a list of plugin name to use
+Plugin.normalizeNames = function(plugins) {
+ return _.pluck(Plugin.normalizeList(plugins), "name");
+};
+
+module.exports = Plugin;