mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-23 02:53:29 +00:00
Add plugin object
This commit is contained in:
+29
-3
@@ -5,7 +5,8 @@ var path = require("path");
|
||||
var fs = require("./utils/fs");
|
||||
var Configuration = require("./configuration");
|
||||
var TemplateEngine = require("./template");
|
||||
var parser = require("./parser");
|
||||
var Plugin = require("./plugin");
|
||||
var parsers = require("./parsers");
|
||||
|
||||
var Book = function(root, options, parent) {
|
||||
// Root folder of the book
|
||||
@@ -39,6 +40,9 @@ var Book = function(root, options, parent) {
|
||||
|
||||
// Files in the book
|
||||
this.files = [];
|
||||
|
||||
// List of plugins
|
||||
this.plugins = {};
|
||||
};
|
||||
|
||||
// Initialize and parse the book: config, summary, glossary
|
||||
@@ -48,6 +52,10 @@ Book.prototype.parse = function() {
|
||||
|
||||
return this.config.load()
|
||||
|
||||
.then(function() {
|
||||
return that.parsePlugins();
|
||||
})
|
||||
|
||||
.then(function() {
|
||||
return that.parseLangs()
|
||||
.then(function() {
|
||||
@@ -124,6 +132,24 @@ Book.prototype.generateMultiLingual = function() {
|
||||
});
|
||||
};
|
||||
|
||||
// Parse list of plugins
|
||||
Book.prototype.parsePlugins = function() {
|
||||
var that = this;
|
||||
var names = Plugin.normalizeNames(this.options.plugins);
|
||||
|
||||
var failed = [];
|
||||
|
||||
// Load plugins
|
||||
that.plugins = _.map(names, function(name) {
|
||||
var plugin = new Plugin(that, name);
|
||||
if (!plugin.isValid()) failed.push(name);
|
||||
return plugin;
|
||||
});
|
||||
|
||||
if (_.size(failed) > 0) return Q.reject(new Error("Error loading plugins: "+failed.join(",")+". Run 'gitbook install' to install plugins from NPM."));
|
||||
return Q();
|
||||
};
|
||||
|
||||
// Parse readme to extract defaults title and description
|
||||
Book.prototype.parseReadme = function() {
|
||||
var that = this;
|
||||
@@ -205,7 +231,7 @@ Book.prototype.parseGlossary = function() {
|
||||
Book.prototype.findFile = function(filename) {
|
||||
var that = this;
|
||||
|
||||
return _.reduce(parser.extensions, function(prev, ext) {
|
||||
return _.reduce(parsers.extensions, function(prev, ext) {
|
||||
return prev.then(function(output) {
|
||||
// Stop if already find a parser
|
||||
if (output) return output;
|
||||
@@ -216,7 +242,7 @@ Book.prototype.findFile = function(filename) {
|
||||
.then(function(exists) {
|
||||
if (!exists) return null;
|
||||
return {
|
||||
parser: parser.get(ext).parser,
|
||||
parser: parsers.get(ext).parser,
|
||||
path: filepath
|
||||
};
|
||||
})
|
||||
|
||||
+227
@@ -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;
|
||||
+7
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gitbook",
|
||||
"version": "1.5.0",
|
||||
"version": "2.0.0-alpha",
|
||||
"homepage": "https://www.gitbook.com",
|
||||
"description": "Library and cmd utility to generate GitBooks",
|
||||
"main": "lib/index.js",
|
||||
@@ -8,10 +8,15 @@
|
||||
"q": "1.0.1",
|
||||
"lodash": "2.4.1",
|
||||
"graceful-fs": "3.0.5",
|
||||
"resolve": "0.6.3",
|
||||
"fs-extra": "0.14.0",
|
||||
"fstream-ignore": "1.0.2",
|
||||
"gitbook-markdown": "1.0.0",
|
||||
"nunjucks": "git+https://github.com/SamyPesse/nunjucks.git#4019d1b7379372336b86ce1b0bf84352a2029747"
|
||||
"nunjucks": "git+https://github.com/SamyPesse/nunjucks.git#4019d1b7379372336b86ce1b0bf84352a2029747",
|
||||
"semver": "2.2.1",
|
||||
"npmi": "0.1.1",
|
||||
"gitbook-plugin-mathjax": "0.0.6",
|
||||
"gitbook-plugin-livereload": "0.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "1.18.2"
|
||||
|
||||
Reference in New Issue
Block a user