summaryrefslogtreecommitdiffstats
path: root/lib/generate/plugin.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/generate/plugin.js')
-rw-r--r--lib/generate/plugin.js180
1 files changed, 180 insertions, 0 deletions
diff --git a/lib/generate/plugin.js b/lib/generate/plugin.js
new file mode 100644
index 0000000..5bd332b
--- /dev/null
+++ b/lib/generate/plugin.js
@@ -0,0 +1,180 @@
+var _ = require("lodash");
+var Q = require("q");
+var semver = require("semver");
+var path = require("path");
+var url = require("url");
+var fs = require("./fs");
+
+var pkg = require("../../package.json");
+
+var RESOURCES = ["js", "css"];
+
+var Plugin = function(name) {
+ this.name = name;
+ this.packageInfos = {};
+ this.infos = {};
+
+ _.each([
+ name,
+ "gitbook-"+name,
+ "gitbook-plugin-"+name,
+ "gitbook-theme-"+name
+ ], function(_name) {
+ if (this.load(_name)) return false;
+ }.bind(this));
+};
+
+// Load from a name
+Plugin.prototype.load = function(name) {
+ try {
+ this.packageInfos = require(name+"/package.json");
+ this.infos = require(name);
+ this.name = name;
+ return true;
+ } catch (e) {
+ return false;
+ }
+};
+
+// Return resources
+Plugin.prototype.getResources = function(resource) {
+ if (!this.infos.book || !this.infos.book[resource]) {
+ return [];
+ }
+ return _.chain(this.infos.book[resource])
+ .map(function(resource) {
+ var parsed = url.parse(resource);
+ if (parsed.protocol) return {"url": resource}
+ else return { "path": this.name+"/"+resource };
+ }.bind(this))
+ .value();
+};
+
+// 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(path.dirname(require.resolve(this.name)), filename);
+};
+
+// Resolve file path
+Plugin.prototype.callHook = function(name, args) {
+ var hookFunc = this.infos.hooks? this.infos.hooks[name] : null;
+ args = _.isArray(args) ? args : [args];
+
+ if (!hookFunc) return Q();
+
+ return Q()
+ .then(function() {
+ return hookFunc.apply(null, args);
+ });
+};
+
+// Copy plugin assets fodler
+Plugin.prototype.copyAssets = function(out) {
+ if (!this.infos.book || !this.infos.book.assets) return Q();
+ return fs.copy(
+ this.resolveFile(this.infos.book.assets),
+ out
+ );
+};
+
+
+
+
+// Normalize a list of plugin name to use
+Plugin.normalizeNames = function(names) {
+ // Normalize list to an array
+ names = _.isString(names) ? names.split(":") : (names || []);
+
+ // List plugins to remove
+ var toremove = _.chain(names)
+ .filter(function(name) {
+ return name.length > 0 && name[0] == "-";
+ })
+ .map(function(name) {
+ return name.slice(1)
+ })
+ .value();
+
+ // Merge with defaults
+ names = _.chain(names)
+ .concat(Plugin.defaults)
+ .uniq()
+ .value();
+
+ // Remove plugins starting with
+ names = _.filter(names, function(name) {
+ return !_.contains(toremove, name) && !(name.length > 0 && name[0] == "-");
+ });
+
+ return names;
+};
+
+// Extract data from a list of plugin
+Plugin.fromList = function(names) {
+ var failed = [];
+
+ // Load plugins
+ var plugins = _.map(names, function(name) {
+ var plugin = new Plugin(name);
+ if (!plugin.isValid()) failed.push(name);
+ return plugin;
+ });
+
+ if (_.size(failed) > 0) return Q.reject(new Error("Error loading plugins: "+failed.join(",")));
+
+ // Get all resources
+ var resources = _.chain(RESOURCES)
+ .map(function(resource) {
+ return [
+ resource,
+ _.chain(plugins)
+ .map(function(plugin) {
+ return plugin.getResources(resource);
+ })
+ .flatten()
+ .value()
+ ];
+ })
+ .object()
+ .value();
+
+ return Q({
+ 'list': plugins,
+ 'resources': resources,
+ 'hook': function(name, args) {
+ return _.reduce(plugins, function(prev, plugin) {
+ return prev.then(function() {
+ return plugin.callHook(name, args);
+ })
+ }, Q());
+ },
+ 'template': function(name) {
+ var withTpl = _.find(plugins, function(plugin) {
+ return (plugin.infos.templates
+ && plugin.infos.templates[name]);
+ });
+
+ if (!withTpl) return null;
+ return withTpl.resolveFile(withTpl.infos.templates[name]);
+ }
+ });
+};
+
+// Default plugins
+Plugin.defaults = [
+ "mixpanel",
+ "mathjax"
+];
+
+module.exports = Plugin; \ No newline at end of file