summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamy Pessé <samypesse@gmail.com>2015-10-13 15:14:26 +0200
committerSamy Pessé <samypesse@gmail.com>2015-10-13 15:14:26 +0200
commit8ba18cd587de8f073c9818acbdeaeaafe9a478c1 (patch)
treee83f6a9b6e4a37d2058d11653db73123a799aef0
parent4ae5b2e05001df9bdf8c5b64f8a82725e14d6414 (diff)
parent54c12fea89198ad6582529cf7bfd6c7ff6f92ed2 (diff)
downloadgitbook-8ba18cd587de8f073c9818acbdeaeaafe9a478c1.zip
gitbook-8ba18cd587de8f073c9818acbdeaeaafe9a478c1.tar.gz
gitbook-8ba18cd587de8f073c9818acbdeaeaafe9a478c1.tar.bz2
Merge pull request #979 from GitbookIO/feature/plugins_config
Improvements for plugin configurations
-rw-r--r--lib/book.js17
-rw-r--r--lib/config_default.js108
-rw-r--r--lib/configuration.js130
-rw-r--r--lib/generator.js26
-rw-r--r--lib/plugin.js107
-rw-r--r--lib/pluginslist.js56
-rw-r--r--package.json12
-rw-r--r--test/assertions.js34
-rw-r--r--test/plugins.js33
-rw-r--r--test/plugins/config/index.js1
-rw-r--r--test/plugins/config/package.json21
11 files changed, 353 insertions, 192 deletions
diff --git a/lib/book.js b/lib/book.js
index 72f0ec2..39d4719 100644
--- a/lib/book.js
+++ b/lib/book.js
@@ -178,6 +178,14 @@ Book.prototype.generate = function(generator) {
return generator.prepare();
})
+ // Transform configuration
+ .then(function() {
+ return that.callHook('config', that.config.dump())
+ .then(function(newConfig) {
+ that.config.replace(newConfig);
+ });
+ })
+
// Generate content
.then(function() {
if (that.isMultilingual()) {
@@ -238,13 +246,13 @@ Book.prototype.generate = function(generator) {
// Finish generation
.then(function() {
- return generator.callHook('finish:before');
+ return that.callHook('finish:before');
})
.then(function() {
return generator.finish();
})
.then(function() {
- return generator.callHook('finish');
+ return that.callHook('finish');
})
.then(function() {
that.log.info.ln('generation is finished');
@@ -793,4 +801,9 @@ Book.prototype.normError = function(err, opts, defs) {
return err;
};
+// Call a hook in plugins
+Book.prototype.callHook = function(name, data) {
+ return this.plugins.hook(name, data);
+};
+
module.exports= Book;
diff --git a/lib/config_default.js b/lib/config_default.js
new file mode 100644
index 0000000..328531c
--- /dev/null
+++ b/lib/config_default.js
@@ -0,0 +1,108 @@
+var path = require('path');
+
+module.exports = {
+ // Options that can't be extend
+ 'configFile': 'book',
+ 'generator': 'website',
+ 'extension': null,
+
+ // Book metadats (somes are extracted from the README by default)
+ 'title': null,
+ 'description': null,
+ 'isbn': null,
+ 'language': 'en',
+ 'direction': null,
+ 'author': null,
+
+ // version of gitbook to use
+ 'gitbook': '*',
+
+ // Structure
+ 'structure': {
+ 'langs': 'LANGS.md',
+ 'readme': 'README.md',
+ 'glossary': 'GLOSSARY.md',
+ 'summary': 'SUMMARY.md'
+ },
+
+ // CSS Styles
+ 'styles': {
+ 'website': 'styles/website.css',
+ 'print': 'styles/print.css',
+ 'ebook': 'styles/ebook.css',
+ 'pdf': 'styles/pdf.css',
+ 'mobi': 'styles/mobi.css',
+ 'epub': 'styles/epub.css'
+ },
+
+ // Plugins list, can contain '-name' for removing default plugins
+ 'plugins': [],
+
+ // Global configuration for plugins
+ 'pluginsConfig': {},
+
+ // Variables for templating
+ 'variables': {},
+
+ // Set another theme with your own layout
+ // It's recommended to use plugins or add more options for default theme, though
+ // See https://github.com/GitbookIO/gitbook/issues/209
+ 'theme': path.resolve(__dirname, '../theme'),
+
+ // Links in template (null: default, false: remove, string: new value)
+ 'links': {
+ // Custom links at top of sidebar
+ 'sidebar': {
+ // 'Custom link name': 'https://customlink.com'
+ },
+
+ // Sharing links
+ 'sharing': {
+ 'google': null,
+ 'facebook': null,
+ 'twitter': null,
+ 'weibo': null,
+ 'all': null
+ }
+ },
+
+
+ // Options for PDF generation
+ 'pdf': {
+ // Add toc at the end of the file
+ 'toc': true,
+
+ // Add page numbers to the bottom of every page
+ 'pageNumbers': false,
+
+ // Font size for the file content
+ 'fontSize': 12,
+
+ // Paper size for the pdf
+ // Choices are [u’a0’, u’a1’, u’a2’, u’a3’, u’a4’, u’a5’, u’a6’, u’b0’, u’b1’, u’b2’, u’b3’, u’b4’, u’b5’, u’b6’, u’legal’, u’letter’]
+ 'paperSize': 'a4',
+
+ // How to mark detected chapters.
+ // Choices are “pagebreak”, “rule”, 'both' or “none”.
+ 'chapterMark' : 'pagebreak',
+
+ // An XPath expression. Page breaks are inserted before the specified elements.
+ // To disable use the expression: '/'
+ 'pageBreaksBefore': '/',
+
+ // Margin (in pts)
+ // Note: 72 pts equals 1 inch
+ 'margin': {
+ 'right': 62,
+ 'left': 62,
+ 'top': 56,
+ 'bottom': 56
+ },
+
+ // Header HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
+ 'headerTemplate': null,
+
+ // Footer HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
+ 'footerTemplate': null
+ }
+};
diff --git a/lib/configuration.js b/lib/configuration.js
index 7488fae..d514720 100644
--- a/lib/configuration.js
+++ b/lib/configuration.js
@@ -1,12 +1,13 @@
var _ = require('lodash');
var Q = require('q');
-var fs = require('fs');
var path = require('path');
var semver = require('semver');
var pkg = require('../package.json');
var i18n = require('./utils/i18n');
+var DEFAULT_CONFIG = require('./config_default');
+
// Default plugins added to each books
var DEFAULT_PLUGINS = ['highlight', 'search', 'sharing', 'fontsettings'];
@@ -85,9 +86,7 @@ var Configuration = function(book, options) {
var that = this;
this.book = book;
-
- this.options = _.cloneDeep(Configuration.DEFAULT);
- this.options = _.merge(this.options, options || {});
+ this.replace(options);
// options.input == book.root
Object.defineProperty(this.options, 'input', {
@@ -169,6 +168,17 @@ Configuration.prototype.extend = function(options) {
_.extend(this.options, options);
};
+// Replace the whole configuration
+Configuration.prototype.replace = function(options) {
+ this.options = _.cloneDeep(DEFAULT_CONFIG);
+ this.options = _.merge(this.options, options || {});
+};
+
+// Dump configuration as json object
+Configuration.prototype.dump = function() {
+ return _.cloneDeep(this.options);
+};
+
// Get structure file
Configuration.prototype.getStructure = function(name) {
return this.options.structure[name].split('.').slice(0, -1).join('.');
@@ -184,112 +194,12 @@ Configuration.prototype.get = function(key, def) {
return _.get(this.options, key, def);
};
-// Default configuration
-Configuration.DEFAULT = {
- // Options that can't be extend
- 'configFile': 'book',
- 'generator': 'website',
- 'extension': null,
-
- // Book metadats (somes are extracted from the README by default)
- 'title': null,
- 'description': null,
- 'isbn': null,
- 'language': 'en',
- 'direction': null,
- 'author': null,
-
- // version of gitbook to use
- 'gitbook': '*',
-
- // Structure
- 'structure': {
- 'langs': 'LANGS.md',
- 'readme': 'README.md',
- 'glossary': 'GLOSSARY.md',
- 'summary': 'SUMMARY.md'
- },
-
- // CSS Styles
- 'styles': {
- 'website': 'styles/website.css',
- 'print': 'styles/print.css',
- 'ebook': 'styles/ebook.css',
- 'pdf': 'styles/pdf.css',
- 'mobi': 'styles/mobi.css',
- 'epub': 'styles/epub.css'
- },
-
- // Plugins list, can contain '-name' for removing default plugins
- 'plugins': [],
-
- // Global configuration for plugins
- 'pluginsConfig': {},
-
- // Variables for templating
- 'variables': {},
-
- // Set another theme with your own layout
- // It's recommended to use plugins or add more options for default theme, though
- // See https://github.com/GitbookIO/gitbook/issues/209
- 'theme': path.resolve(__dirname, '../theme'),
-
- // Links in template (null: default, false: remove, string: new value)
- 'links': {
- // Custom links at top of sidebar
- 'sidebar': {
- // 'Custom link name': 'https://customlink.com'
- },
-
- // Sharing links
- 'sharing': {
- 'google': null,
- 'facebook': null,
- 'twitter': null,
- 'weibo': null,
- 'all': null
- }
- },
-
-
- // Options for PDF generation
- 'pdf': {
- // Add toc at the end of the file
- 'toc': true,
-
- // Add page numbers to the bottom of every page
- 'pageNumbers': false,
-
- // Font size for the file content
- 'fontSize': 12,
-
- // Paper size for the pdf
- // Choices are [u’a0’, u’a1’, u’a2’, u’a3’, u’a4’, u’a5’, u’a6’, u’b0’, u’b1’, u’b2’, u’b3’, u’b4’, u’b5’, u’b6’, u’legal’, u’letter’]
- 'paperSize': 'a4',
-
- // How to mark detected chapters.
- // Choices are “pagebreak”, “rule”, 'both' or “none”.
- 'chapterMark' : 'pagebreak',
-
- // An XPath expression. Page breaks are inserted before the specified elements.
- // To disable use the expression: '/'
- 'pageBreaksBefore': '/',
-
- // Margin (in pts)
- // Note: 72 pts equals 1 inch
- 'margin': {
- 'right': 62,
- 'left': 62,
- 'top': 56,
- 'bottom': 56
- },
-
- // Header HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
- 'headerTemplate': null,
-
- // Footer HTML template. Available variables: _PAGENUM_, _TITLE_, _AUTHOR_ and _SECTION_.
- 'footerTemplate': null
- }
+// Update a configuration
+Configuration.prototype.set = function(key, value) {
+ return _.set(this.options, key, value);
};
+// Default configuration
+Configuration.DEFAULT = DEFAULT_CONFIG;
+
module.exports= Configuration;
diff --git a/lib/generator.js b/lib/generator.js
index fca5b3c..4e280d8 100644
--- a/lib/generator.js
+++ b/lib/generator.js
@@ -1,12 +1,12 @@
-var _ = require("lodash");
-var path = require("path");
-var Q = require("q");
-var fs = require("./utils/fs");
+var _ = require('lodash');
+var path = require('path');
+var Q = require('q');
+var fs = require('./utils/fs');
var BaseGenerator = function(book) {
this.book = book;
- Object.defineProperty(this, "options", {
+ Object.defineProperty(this, 'options', {
get: function () {
return this.book.options;
}
@@ -16,19 +16,19 @@ var BaseGenerator = function(book) {
};
BaseGenerator.prototype.callHook = function(name, data) {
- return this.book.plugins.hook(name, data);
+ return this.book.callHook(name, data);
};
// Prepare the genertor
BaseGenerator.prototype.prepare = function() {
var that = this;
- return that.callHook("init");
+ return that.callHook('init');
};
// Write a parsed file to the output
BaseGenerator.prototype.convertFile = function(input) {
- return Q.reject(new Error("Could not convert "+input));
+ return Q.reject(new Error('Could not convert '+input));
};
// Copy file to the output (non parsable)
@@ -51,16 +51,16 @@ BaseGenerator.prototype.copyCover = function() {
var that = this;
return Q.all([
- fs.copy(that.book.resolve("cover.jpg"), path.join(that.options.output, "cover.jpg")),
- fs.copy(that.book.resolve("cover_small.jpg"), path.join(that.options.output, "cover_small.jpg"))
+ fs.copy(that.book.resolve('cover.jpg'), path.join(that.options.output, 'cover.jpg')),
+ fs.copy(that.book.resolve('cover_small.jpg'), path.join(that.options.output, 'cover_small.jpg'))
])
.fail(function() {
// If orignaly from multi-lang, try copy from parent
if (!that.book.isSubBook()) return;
return Q.all([
- fs.copy(path.join(that.book.parentRoot(), "cover.jpg"), path.join(that.options.output, "cover.jpg")),
- fs.copy(path.join(that.book.parentRoot(), "cover_small.jpg"), path.join(that.options.output, "cover_small.jpg"))
+ fs.copy(path.join(that.book.parentRoot(), 'cover.jpg'), path.join(that.options.output, 'cover.jpg')),
+ fs.copy(path.join(that.book.parentRoot(), 'cover_small.jpg'), path.join(that.options.output, 'cover_small.jpg'))
]);
})
.fail(function() {
@@ -70,7 +70,7 @@ BaseGenerator.prototype.copyCover = function() {
// At teh end of the generation
BaseGenerator.prototype.finish = function() {
- return Q.reject(new Error("Could not finish generation"));
+ return Q.reject(new Error('Could not finish generation'));
};
module.exports = BaseGenerator;
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;
diff --git a/lib/pluginslist.js b/lib/pluginslist.js
index e4594d6..8830950 100644
--- a/lib/pluginslist.js
+++ b/lib/pluginslist.js
@@ -78,34 +78,46 @@ PluginsList.prototype.load = function(plugin) {
that.list.push(plugin);
}
- // Extract filters
- that.book.template.addFilters(plugin.getFilters());
+ return Q()
- // Extract blocks
- that.book.template.addBlocks(plugin.getBlocks());
+ // Validate and normalize configuration
+ .then(function() {
+ var config = plugin.getConfig();
+ return plugin.validateConfig(config);
+ })
+ .then(function(config) {
+ // Update configuration
+ plugin.setConfig(config);
- return _.reduce(_.keys(that.namespaces), function(prev, namespaceName) {
- return prev.then(function() {
- return plugin.getResources(namespaceName)
- .then(function(plResources) {
- var namespace = that.namespaces[namespaceName];
-
- // Extract js and css
- _.each(Plugin.RESOURCES, function(resourceType) {
- namespace.resources[resourceType] = (namespace.resources[resourceType] || []).concat(plResources[resourceType] || []);
- });
+ // Extract filters
+ that.book.template.addFilters(plugin.getFilters());
- // Map of html resources by name added by each plugin
- _.each(plResources.html || {}, function(value, tag) {
- // Turn into function if not one already
- if (!_.isFunction(value)) value = _.constant(value);
+ // Extract blocks
+ that.book.template.addBlocks(plugin.getBlocks());
- namespace.html[tag] = namespace.html[tag] || [];
- namespace.html[tag].push(value);
+ return _.reduce(_.keys(that.namespaces), function(prev, namespaceName) {
+ return prev.then(function() {
+ return plugin.getResources(namespaceName)
+ .then(function(plResources) {
+ var namespace = that.namespaces[namespaceName];
+
+ // Extract js and css
+ _.each(Plugin.RESOURCES, function(resourceType) {
+ namespace.resources[resourceType] = (namespace.resources[resourceType] || []).concat(plResources[resourceType] || []);
+ });
+
+ // Map of html resources by name added by each plugin
+ _.each(plResources.html || {}, function(value, tag) {
+ // Turn into function if not one already
+ if (!_.isFunction(value)) value = _.constant(value);
+
+ namespace.html[tag] = namespace.html[tag] || [];
+ namespace.html[tag].push(value);
+ });
});
});
- });
- }, Q());
+ }, Q());
+ });
};
// Call a hook
diff --git a/package.json b/package.json
index 3eeec2e..a1e1667 100644
--- a/package.json
+++ b/package.json
@@ -13,9 +13,9 @@
"fstream-ignore": "1.0.2",
"gitbook-parsers": "0.8.4",
"gitbook-plugin-highlight": "1.0.3",
- "gitbook-plugin-sharing": "1.0.0",
- "gitbook-plugin-search": "1.0.0",
- "gitbook-plugin-fontsettings": "1.0.0",
+ "gitbook-plugin-sharing": "1.0.1",
+ "gitbook-plugin-search": "1.0.2",
+ "gitbook-plugin-fontsettings": "1.0.2",
"nunjucks": "2.1.0",
"nunjucks-autoescape": "1.0.0",
"nunjucks-filter": "1.0.0",
@@ -36,12 +36,16 @@
"dom-serializer": "0.1.0",
"spawn-cmd": "0.0.2",
"escape-string-regexp": "1.0.3",
- "juice": "1.5.0"
+ "juice": "1.5.0",
+ "jsonschema": "1.0.2",
+ "json-schema-defaults": "0.1.1",
+ "merge-defaults": "0.2.1"
},
"devDependencies": {
"eslint": "1.5.0",
"mocha": "2.3.2",
"should": "7.1.0",
+ "should-promised": "0.3.1",
"gulp": "^3.8.11",
"gulp-rename": "^1.2.2",
"gulp-uglify": "1.1.0",
diff --git a/test/assertions.js b/test/assertions.js
index 7e14ecb..f9c4ba3 100644
--- a/test/assertions.js
+++ b/test/assertions.js
@@ -1,27 +1,29 @@
-var _ = require("lodash");
-var fs = require("fs");
-var path = require("path");
-var should = require("should");
-var cheerio = require("cheerio");
+var _ = require('lodash');
+var fs = require('fs');
+var path = require('path');
+var should = require('should');
+var cheerio = require('cheerio');
-should.Assertion.add("file", function(file, description) {
- this.params = { actual: this.obj.toString(), operator: "have file " + file, message: description };
+require('should-promised');
- this.obj.should.have.property("options").which.is.an.Object();
- this.obj.options.should.have.property("output").which.is.a.String();
+should.Assertion.add('file', function(file, description) {
+ this.params = { actual: this.obj.toString(), operator: 'have file ' + file, message: description };
+
+ this.obj.should.have.property('options').which.is.an.Object();
+ this.obj.options.should.have.property('output').which.is.a.String();
this.assert(fs.existsSync(path.resolve(this.obj.options.output, file)));
});
-should.Assertion.add("jsonfile", function(file, description) {
- this.params = { actual: this.obj.toString(), operator: "have valid jsonfile " + file, message: description };
+should.Assertion.add('jsonfile', function(file, description) {
+ this.params = { actual: this.obj.toString(), operator: 'have valid jsonfile ' + file, message: description };
- this.obj.should.have.property("options").which.is.an.Object();
- this.obj.options.should.have.property("output").which.is.a.String();
- this.assert(JSON.parse(fs.readFileSync(path.resolve(this.obj.options.output, file), { encoding: "utf-8" })));
+ this.obj.should.have.property('options').which.is.an.Object();
+ this.obj.options.should.have.property('output').which.is.a.String();
+ this.assert(JSON.parse(fs.readFileSync(path.resolve(this.obj.options.output, file), { encoding: 'utf-8' })));
});
-should.Assertion.add("html", function(rules, description) {
- this.params = { actual: "HTML string", operator: "valid html", message: description };
+should.Assertion.add('html', function(rules, description) {
+ this.params = { actual: 'HTML string', operator: 'valid html', message: description };
var $ = cheerio.load(this.obj);
_.each(rules, function(validations, query) {
diff --git a/test/plugins.js b/test/plugins.js
index c0f2373..db2d225 100644
--- a/test/plugins.js
+++ b/test/plugins.js
@@ -51,6 +51,37 @@ describe('Plugins', function () {
});
});
+ describe('Configuration', function() {
+ var plugin;
+
+ before(function() {
+ plugin = new Plugin(book, 'testconfig');
+ plugin.load('./config', PLUGINS_ROOT);
+ });
+
+ it('should throw error for invalid configuration', function() {
+ return plugin.validateConfig({})
+ .should.be.rejectedWith('Configuration Error: pluginsConfig.testconfig.testRequired is required');
+ });
+
+ it('should throw error for invalid types', function() {
+ return plugin.validateConfig({
+ testRequired: 'hello'
+ })
+ .should.be.rejectedWith('Configuration Error: pluginsConfig.testconfig.testRequired is not of a type(s) number');
+ });
+
+ it('should extend with default values', function() {
+ return plugin.validateConfig({
+ testRequired: 12
+ })
+ .should.be.fulfilledWith({
+ hello: 'world',
+ testRequired: 12
+ });
+ });
+ });
+
describe('Resources', function() {
var plugin;
@@ -93,7 +124,7 @@ describe('Plugins', function () {
// There is resources from highlight plugin and this plugin
resources.css.should.have.lengthOf(2);
should.exist(_.find(resources.css, {
- path: './resources/test'
+ path: 'gitbook-plugin-resources/test'
}));
});
});
diff --git a/test/plugins/config/index.js b/test/plugins/config/index.js
new file mode 100644
index 0000000..f053ebf
--- /dev/null
+++ b/test/plugins/config/index.js
@@ -0,0 +1 @@
+module.exports = {};
diff --git a/test/plugins/config/package.json b/test/plugins/config/package.json
new file mode 100644
index 0000000..03ef744
--- /dev/null
+++ b/test/plugins/config/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "gitbook-plugin-testconfig",
+ "description": "Test plugin configuration",
+ "main": "index.js",
+ "version": "0.0.1",
+ "engines": {
+ "gitbook": "*"
+ },
+ "gitbook": {
+ "properties": {
+ "hello": {
+ "type": "string",
+ "default": "world"
+ },
+ "testRequired": {
+ "type": "number",
+ "required": true
+ }
+ }
+ }
+} \ No newline at end of file