var _ = require('lodash'); var semver = require('semver'); var gitbook = require('../gitbook'); var Promise = require('../utils/promise'); var validator = require('./validator'); var plugins = require('./plugins'); // Config files to tested (sorted) var CONFIG_FILES = [ 'book.js', 'book.json' ]; /* Config is an interface for the book's configuration stored in "book.json" (or "book.js") */ function Config(book, baseConfig) { this.book = book; this.fs = book.fs; this.log = book.log; this.path = ''; this.baseConfig = baseConfig || {}; this.replace({}); } // Load configuration of the book // and verify that the configuration is satisfying Config.prototype.load = function() { var that = this; var isLanguageBook = this.book.isLanguageBook(); // Try all potential configuration file return Promise.some(CONFIG_FILES, function(filename) { that.log.debug.ln('try loading configuration from', filename); return that.fs.loadAsObject(that.book.resolve(filename)) .then(function(_config) { that.log.debug.ln('configuration loaded from', filename); that.path = filename; return that.replace(_config); }) .fail(function(err) { if (err.code != 'MODULE_NOT_FOUND') throw(err); else return Promise(false); }); }) .then(function() { if (!isLanguageBook) { if (!gitbook.satisfies(that.options.gitbook)) { throw new Error('GitBook version doesn\'t satisfy version required by the book: '+that.options.gitbook); } if (that.options.gitbook != '*' && !semver.satisfies(semver.inc(gitbook.version, 'patch'), that.options.gitbook)) { that.log.warn.ln('gitbook version specified in your book.json might be too strict for future patches, \''+(_.first(gitbook.version.split('.'))+'.x.x')+'\' is more adequate'); } that.options.plugins = plugins.toList(that.options.plugins); } else { // Multilingual book should inherits the plugins list from parent that.options.plugins = that.book.parent.config.get('plugins'); } that.options.gitbook = gitbook.version; }); }; // Replace the whole configuration Config.prototype.replace = function(options) { var that = this; // Extend base config options = _.defaults(_.cloneDeep(options), this.baseConfig); // Validate the config this.options = validator.validate(options); // options.input == book.root Object.defineProperty(this.options, 'input', { get: function () { return that.book.root; } }); // options.originalInput == book.parent.root Object.defineProperty(this.options, 'originalInput', { get: function () { return that.book.parent? that.book.parent.root : undefined; } }); }; // Return true if book has a configuration file Config.prototype.exists = function() { return Boolean(this.path); }; // Return path to a structure file // Strip the extension by default Config.prototype.getStructure = function(name, dontStripExt) { var filename = this.options.structure[name]; if (dontStripExt) return filename; filename = filename.split('.').slice(0, -1).join('.'); return filename; }; // Return a configuration using a key and a default value Config.prototype.get = function(key, def) { return _.get(this.options, key, def); }; // Update a configuration Config.prototype.set = function(key, value) { return _.set(this.options, key, value); }; // Return a dump of the configuration Config.prototype.dump = function() { return _.cloneDeep(this.options); }; // Return templating context Config.prototype.getContext = function() { return { config: this.book.config.dump() }; }; module.exports = Config;