diff options
Diffstat (limited to 'lib/plugin.js')
-rw-r--r-- | lib/plugin.js | 107 |
1 files changed, 83 insertions, 24 deletions
diff --git a/lib/plugin.js b/lib/plugin.js index 5e1c427..b4d4aa1 100644 --- a/lib/plugin.js +++ b/lib/plugin.js @@ -1,16 +1,28 @@ -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 resolve = require("resolve"); +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 resolve = require('resolve'); +var mergeDefaults = require('merge-defaults'); +var jsonschema = require('jsonschema'); +var jsonSchemaDefaults = require('json-schema-defaults'); + +var pkg = require('../package.json'); + +var PLUGIN_PREFIX = 'gitbook-plugin-'; + +// Return an absolute name for the plugin (the one on NPM) +function absoluteName(name) { + if (name.indexOf(PLUGIN_PREFIX) === 0) return name; + return [PLUGIN_PREFIX, name].join(''); +} -var pkg = require("../package.json"); var Plugin = function(book, name) { this.book = book; - this.name = name; + this.name = absoluteName(name); this.packageInfos = {}; this.infos = {}; @@ -18,9 +30,8 @@ var Plugin = function(book, name) { _.bindAll(this); _.each([ - "gitbook-plugin-"+name, - "gitbook-"+name, - name, + absoluteName(name), + name ], function(_name) { // Load from the book if (this.load(_name, book.root)) return false; @@ -31,20 +42,27 @@ var Plugin = function(book, name) { }; // Type of plugins resources -Plugin.RESOURCES = ["js", "css"]; +Plugin.RESOURCES = ['js', 'css']; Plugin.HOOKS = [ - "init", "finish", "finish:before", "page", "page:before" + 'init', 'finish', 'finish:before', 'config', 'page', 'page:before' ]; +// Return the reduce name for the plugin +// "gitbook-plugin-test" -> "test" +// Return a relative name for the plugin (the one on GitBook) +Plugin.prototype.reducedName = function() { + return this.name.replace(PLUGIN_PREFIX, ''); +}; + // Load from a name Plugin.prototype.load = function(name, baseDir) { try { - var res = resolve.sync(name+"/package.json", { basedir: baseDir }); + 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; + this.name = this.packageInfos.name; return true; } catch (e) { @@ -62,13 +80,13 @@ Plugin.prototype.normalizeResource = function(resource) { // so we will simply link to using it's URL if (parsed.protocol) { return { - "url": resource + 'url': resource }; } // This will be copied over from disk // and shipped with the book's build - return { "path": this.name+"/"+resource }; + return { 'path': this.name+'/'+resource }; }; // Return resources @@ -77,7 +95,7 @@ Plugin.prototype._getResources = function(base) { var book = this.infos[base]; // Compatibility with version 1.x.x - if (base == "website") book = book || this.infos.book; + if (base == 'website') book = book || this.infos.book; // Nothing specified, fallback to default if (!book) { @@ -85,7 +103,7 @@ Plugin.prototype._getResources = function(base) { } // Dynamic function - if(typeof book === "function") { + if(typeof book === 'function') { // Call giving it the context of our book return Q().then(book.bind(this.book)); } @@ -133,12 +151,43 @@ Plugin.prototype.isValid = function() { // Valid hooks _.each(this.infos.hooks, function(hook, hookName) { if (_.contains(Plugin.HOOKS, hookName)) return; - that.book.log.warn.ln("Hook '"+hookName+" 'used by plugin '"+that.packageInfos.name+"' has been removed or is deprecated"); + that.book.log.warn.ln('Hook "'+hookName+'"" used by plugin "'+that.packageInfos.name+'" has been removed or is deprecated'); }); return isValid; }; +// Normalize, validate configuration for this plugin using its schema +// Throw an error when shcema is not respected +Plugin.prototype.validateConfig = function(config) { + var that = this; + + return Q() + .then(function() { + var schema = that.packageInfos.gitbook || {}; + if (!schema) return config; + + // Normalize schema + schema.id = '/pluginsConfig.'+that.reducedName(); + schema.type = 'object'; + + // Validate and throw if invalid + var v = new jsonschema.Validator(); + var result = v.validate(config, schema, { + propertyName: 'pluginsConfig.'+that.reducedName() + }); + + // Throw error + if (result.errors.length > 0) { + throw new Error('Configuration Error: '+result.errors[0].stack); + } + + // Insert default values + var defaults = jsonSchemaDefaults(schema); + return mergeDefaults(config, defaults); + }); +}; + // Resolve file path Plugin.prototype.resolveFile = function(filename) { return path.resolve(this.baseDir, filename); @@ -154,8 +203,8 @@ Plugin.prototype.callHook = function(name, data) { if (!hookFunc) return Q(data); - this.book.log.debug.ln("call hook", name); - if (!_.contains(Plugin.HOOKS, name)) this.book.log.warn.ln("hook '"+name+"' used by plugin '"+this.name+"' is deprecated, and will be removed in the coming versions"); + this.book.log.debug.ln('call hook', name); + if (!_.contains(Plugin.HOOKS, name)) this.book.log.warn.ln('hook "'+name+'" used by plugin "'+this.name+'" is deprecated, and will be removed in the coming versions'); return Q() .then(function() { @@ -168,7 +217,7 @@ Plugin.prototype.copyAssets = function(out, base) { var that = this; return this.getResources(base) - .get("assets") + .get('assets') .then(function(assets) { // Assets are undefined if(!assets) return false; @@ -180,4 +229,14 @@ Plugin.prototype.copyAssets = function(out, base) { }, _.constant(false)); }; +// Get config from book +Plugin.prototype.getConfig = function() { + return this.book.config.get('pluginsConfig.'+this.reducedName(), {}); +}; + +// Set configuration for this plugin +Plugin.prototype.setConfig = function(values) { + return this.book.config.set('pluginsConfig.'+this.reducedName(), values); +}; + module.exports = Plugin; |