diff options
Diffstat (limited to 'lib/book.js')
-rw-r--r-- | lib/book.js | 668 |
1 files changed, 334 insertions, 334 deletions
diff --git a/lib/book.js b/lib/book.js index 04b4969..a9bc40c 100644 --- a/lib/book.js +++ b/lib/book.js @@ -22,68 +22,68 @@ var PluginsList = require("./pluginslist"); var generators = require("./generators"); var Book = function(root, context, parent) { - var that = this; + var that = this; - this.context = _.defaults(context || {}, { - // Extend book configuration - config: {}, + this.context = _.defaults(context || {}, { + // Extend book configuration + config: {}, - // Log function - log: function(msg) { - process.stdout.write(msg); - }, + // Log function + log: function(msg) { + process.stdout.write(msg); + }, - // Log level - logLevel: "info" - }); + // Log level + logLevel: "info" + }); - // Log - this.log = logger(this.context.log, this.context.logLevel); + // Log + this.log = logger(this.context.log, this.context.logLevel); - // Root folder of the book - this.root = path.resolve(root); + // Root folder of the book + this.root = path.resolve(root); - // Parent book - this.parent = parent; + // Parent book + this.parent = parent; - // Configuration - this.config = new Configuration(this, this.context.config); - Object.defineProperty(this, "options", { - get: function () { - return this.config.options; - } - }); + // Configuration + this.config = new Configuration(this, this.context.config); + Object.defineProperty(this, "options", { + get: function () { + return this.config.options; + } + }); - // Template - this.template = new TemplateEngine(this); + // Template + this.template = new TemplateEngine(this); - // Summary - this.summary = {}; - this.navigation = []; + // Summary + this.summary = {}; + this.navigation = []; - // Glossary - this.glossary = []; + // Glossary + this.glossary = []; - // Langs - this.langs = []; + // Langs + this.langs = []; - // Sub-books - this.books = []; + // Sub-books + this.books = []; - // Files in the book - this.files = []; + // Files in the book + this.files = []; - // List of plugins - this.plugins = new PluginsList(this); + // List of plugins + this.plugins = new PluginsList(this); - // Structure files + // Structure files this.summaryFile = null; this.glossaryFile = null; - this.readmeFile = null; + this.readmeFile = null; this.langsFile = null; - // Search Index - this.searchIndex = lunr(function () { + // Search Index + this.searchIndex = lunr(function () { this.ref('url'); this.field('title', { boost: 10 }); @@ -91,153 +91,153 @@ var Book = function(root, context, parent) { }); // Bind methods - _.bindAll(this); + _.bindAll(this); }; // Initialize and parse the book: config, summary, glossary Book.prototype.parse = function() { - var that = this; - var multilingual = false; - - return this.parseConfig() - - .then(function() { - return that.parsePlugins(); - }) - - .then(function() { - return that.parseLangs() - .then(function() { - multilingual = that.langs.length > 0; - if (multilingual) that.log.info.ln("Parsing multilingual book, with", that.langs.length, "lanuages"); - - // Sub-books that inherit from the current book configuration - that.books = _.map(that.langs, function(lang) { - that.log.info.ln("Preparing language book", lang.lang); - return new Book( - path.join(that.root, lang.path), - _.merge({}, that.context, { - config: _.extend({}, that.options, { - 'output': path.join(that.options.output, lang.lang), - 'language': lang.lang - }) - }), - that - ) - }); - }); - }) - - .then(function() { - if (multilingual) return; - return that.listAllFiles(); - }) - .then(function() { - if (multilingual) return; - return that.parseReadme(); - }) - .then(function() { - if (multilingual) return; - return that.parseSummary(); - }) - .then(function() { - if (multilingual) return; - return that.parseGlossary(); - }) - - .then(function() { - // Init sub-books - return _.reduce(that.books, function(prev, book) { - return prev.then(function() { - return book.parse(); - }); - }, Q()); - }) - - .thenResolve(this); + var that = this; + var multilingual = false; + + return this.parseConfig() + + .then(function() { + return that.parsePlugins(); + }) + + .then(function() { + return that.parseLangs() + .then(function() { + multilingual = that.langs.length > 0; + if (multilingual) that.log.info.ln("Parsing multilingual book, with", that.langs.length, "lanuages"); + + // Sub-books that inherit from the current book configuration + that.books = _.map(that.langs, function(lang) { + that.log.info.ln("Preparing language book", lang.lang); + return new Book( + path.join(that.root, lang.path), + _.merge({}, that.context, { + config: _.extend({}, that.options, { + 'output': path.join(that.options.output, lang.lang), + 'language': lang.lang + }) + }), + that + ) + }); + }); + }) + + .then(function() { + if (multilingual) return; + return that.listAllFiles(); + }) + .then(function() { + if (multilingual) return; + return that.parseReadme(); + }) + .then(function() { + if (multilingual) return; + return that.parseSummary(); + }) + .then(function() { + if (multilingual) return; + return that.parseGlossary(); + }) + + .then(function() { + // Init sub-books + return _.reduce(that.books, function(prev, book) { + return prev.then(function() { + return book.parse(); + }); + }, Q()); + }) + + .thenResolve(this); }; // Generate the output Book.prototype.generate = function(generator) { - var that = this; - that.options.generator = generator || that.options.generator; + var that = this; + that.options.generator = generator || that.options.generator; - that.log.info.ln("start generation with", that.options.generator, "generator"); - return Q() + that.log.info.ln("start generation with", that.options.generator, "generator"); + return Q() - // Clean output folder - .then(function() { + // Clean output folder + .then(function() { that.log.info("clean", that.options.generator, "generator"); - return fs.clean(that.options.output) + return fs.clean(that.options.output) .progress(function(p) { that.log.debug.ln("remove", p.file, "("+p.i+"/"+p.count+")"); }) .then(function() { that.log.info.ok(); }); - }) - - // Create generator - .then(function() { - var Generator = generators[generator]; - if (!Generator) throw "Generator '"+that.options.generator+"' doesn't exist"; - generator = new Generator(that); - - return generator.prepare(); - }) - - // Generate content - .then(function() { - if (that.isMultilingual()) { - return that.generateMultiLingual(generator); - } else { - // Separate list of files into the different operations needed - var ops = _.groupBy(that.files, function(file) { - if (file[file.length -1] == "/") { - return "directories"; + }) + + // Create generator + .then(function() { + var Generator = generators[generator]; + if (!Generator) throw "Generator '"+that.options.generator+"' doesn't exist"; + generator = new Generator(that); + + return generator.prepare(); + }) + + // Generate content + .then(function() { + if (that.isMultilingual()) { + return that.generateMultiLingual(generator); + } else { + // Separate list of files into the different operations needed + var ops = _.groupBy(that.files, function(file) { + if (file[file.length -1] == "/") { + return "directories"; } else if (_.contains(parsers.extensions, path.extname(file)) && that.navigation[file]) { - return "content"; + return "content"; } else { - return "files"; + return "files"; } - }); + }); - return Q() + return Q() - // First, let's create folder - .then(function() { - return _.reduce(ops["directories"] || [], function(prev, folder) { - return prev.then(function() { - that.log.debug.ln("transferring folder", folder); - return Q(generator.transferFolder(folder)); - }); - }, Q()); - }) + // First, let's create folder + .then(function() { + return _.reduce(ops["directories"] || [], function(prev, folder) { + return prev.then(function() { + that.log.debug.ln("transferring folder", folder); + return Q(generator.transferFolder(folder)); + }); + }, Q()); + }) - // Then, let's copy other files - .then(function() { - return Q.all(_.map(ops["files"] || [], function(file) { - that.log.debug.ln("transferring file", file); + // Then, let's copy other files + .then(function() { + return Q.all(_.map(ops["files"] || [], function(file) { + that.log.debug.ln("transferring file", file); return Q(generator.transferFile(file)); - })); - }) + })); + }) - // Finally let's generate content - .then(function() { + // Finally let's generate content + .then(function() { var nFiles = (ops["content"] || []).length; return _.reduce(ops["content"] || [], function(prev, file, i) { - return prev.then(function() { + return prev.then(function() { var p = ((i*100)/nFiles).toFixed(0)+"%"; - that.log.info.ln("processing", file, p); - return Q(generator.convertFile(file)); - }); - }, Q()); - }); - } - }) - - // Finish generation + that.log.info.ln("processing", file, p); + return Q(generator.convertFile(file)); + }); + }, Q()); + }); + } + }) + + // Finish generation .then(function() { return generator.callHook("finish:before"); }) @@ -248,28 +248,28 @@ Book.prototype.generate = function(generator) { return generator.callHook("finish"); }) .then(function() { - that.log.info.ln("generation is finished"); + that.log.info.ln("generation is finished"); }); }; // Generate the output for a multilingual book Book.prototype.generateMultiLingual = function(generator) { - var that = this; - - return Q() - .then(function() { - // Generate sub-books - return _.reduce(that.books, function(prev, book) { - return prev.then(function() { - return book.generate(that.options.generator); - }); - }, Q()); - }); + var that = this; + + return Q() + .then(function() { + // Generate sub-books + return _.reduce(that.books, function(prev, book) { + return prev.then(function() { + return book.generate(that.options.generator); + }); + }, Q()); + }); }; // Extract files from ebook generated Book.prototype.generateFile = function(output, options) { - var book = this; + var book = this; options = _.defaults(options || {}, { ebookFormat: path.extname(output).slice(1) @@ -278,7 +278,7 @@ Book.prototype.generateFile = function(output, options) { return fs.tmp.dir() .then(function(tmpDir) { - book.setOutput(tmpDir); + book.setOutput(tmpDir); return book.generate(options.ebookFormat) .then(function(_options) { @@ -313,7 +313,7 @@ Book.prototype.generateFile = function(output, options) { } }) .then(function(n) { - book.log.info.ok(n+" file(s) generated"); + book.log.info.ok(n+" file(s) generated"); return fs.remove(tmpDir); }); @@ -323,147 +323,147 @@ Book.prototype.generateFile = function(output, options) { // Parse configuration Book.prototype.parseConfig = function() { - var that = this; + var that = this; - that.log.info("loading book configuration....") - return that.config.load() - .then(function() { - that.log.info.ok(); - }); + that.log.info("loading book configuration....") + return that.config.load() + .then(function() { + that.log.info.ok(); + }); }; // Parse list of plugins Book.prototype.parsePlugins = function() { - var that = this; - var failed = []; + var that = this; + var failed = []; // Load plugins return that.plugins.load(that.options.plugins) .then(function() { - if (_.size(that.plugins.failed) > 0) return Q.reject(new Error("Error loading plugins: "+that.plugins.failed.join(",")+". Run 'gitbook install' to install plugins from NPM.")); + if (_.size(that.plugins.failed) > 0) return Q.reject(new Error("Error loading plugins: "+that.plugins.failed.join(",")+". Run 'gitbook install' to install plugins from NPM.")); - that.log.info.ok(that.plugins.count()+" plugins loaded"); - that.log.debug.ln("normalize plugins list"); + that.log.info.ok(that.plugins.count()+" plugins loaded"); + that.log.debug.ln("normalize plugins list"); }); }; // Parse readme to extract defaults title and description Book.prototype.parseReadme = function() { - var that = this; - var structure = that.config.getStructure("readme"); - that.log.debug.ln("start parsing readme:", structure); + var that = this; + var structure = that.config.getStructure("readme"); + that.log.debug.ln("start parsing readme:", structure); - return that.findFile(structure) - .then(function(readme) { - if (!readme) throw "No README file"; - if (!_.contains(that.files, readme.path)) throw "README file is ignored"; + return that.findFile(structure) + .then(function(readme) { + if (!readme) throw "No README file"; + if (!_.contains(that.files, readme.path)) throw "README file is ignored"; - that.readmeFile = readme.path; + that.readmeFile = readme.path; that._defaultsStructure(that.readmeFile); - that.log.debug.ln("readme located at", that.readmeFile); - return that.template.renderFile(that.readmeFile) - .then(function(content) { - return readme.parser.readme(content); - }); - }) - .then(function(readme) { - that.options.title = that.options.title || readme.title; - that.options.description = that.options.description || readme.description; - }); + that.log.debug.ln("readme located at", that.readmeFile); + return that.template.renderFile(that.readmeFile) + .then(function(content) { + return readme.parser.readme(content); + }); + }) + .then(function(readme) { + that.options.title = that.options.title || readme.title; + that.options.description = that.options.description || readme.description; + }); }; // Parse langs to extract list of sub-books Book.prototype.parseLangs = function() { - var that = this; + var that = this; - var structure = that.config.getStructure("langs"); - that.log.debug.ln("start parsing languages index:", structure); + var structure = that.config.getStructure("langs"); + that.log.debug.ln("start parsing languages index:", structure); - return that.findFile(structure) - .then(function(langs) { - if (!langs) return []; + return that.findFile(structure) + .then(function(langs) { + if (!langs) return []; that.langsFile = langs.path; that._defaultsStructure(that.langsFile); - that.log.debug.ln("languages index located at", that.langsFile); - return that.template.renderFile(that.langsFile) - .then(function(content) { - return langs.parser.langs(content); - }); - }) - .then(function(langs) { - that.langs = langs; - }); + that.log.debug.ln("languages index located at", that.langsFile); + return that.template.renderFile(that.langsFile) + .then(function(content) { + return langs.parser.langs(content); + }); + }) + .then(function(langs) { + that.langs = langs; + }); }; // Parse summary to extract list of chapters Book.prototype.parseSummary = function() { - var that = this; + var that = this; - var structure = that.config.getStructure("summary"); - that.log.debug.ln("start parsing summary:", structure); + var structure = that.config.getStructure("summary"); + that.log.debug.ln("start parsing summary:", structure); - return that.findFile(structure) - .then(function(summary) { - if (!summary) throw "No SUMMARY file"; + return that.findFile(structure) + .then(function(summary) { + if (!summary) throw "No SUMMARY file"; - // Remove the summary from the list of files to parse + // Remove the summary from the list of files to parse that.summaryFile = summary.path; that._defaultsStructure(that.summaryFile); - that.files = _.without(that.files, that.summaryFile); + that.files = _.without(that.files, that.summaryFile); - that.log.debug.ln("summary located at", that.summaryFile); - return that.template.renderFile(that.summaryFile) - .then(function(content) { - return summary.parser.summary(content, { - entryPoint: that.readmeFile, + that.log.debug.ln("summary located at", that.summaryFile); + return that.template.renderFile(that.summaryFile) + .then(function(content) { + return summary.parser.summary(content, { + entryPoint: that.readmeFile, entryPointTitle: "", - files: that.files - }); - }); - }) - .then(function(summary) { - that.summary = summary; - that.navigation = parseNavigation(that.summary, that.files); - }); + files: that.files + }); + }); + }) + .then(function(summary) { + that.summary = summary; + that.navigation = parseNavigation(that.summary, that.files); + }); }; // Parse glossary to extract terms Book.prototype.parseGlossary = function() { - var that = this; + var that = this; - var structure = that.config.getStructure("glossary"); - that.log.debug.ln("start parsing glossary: ", structure); + var structure = that.config.getStructure("glossary"); + that.log.debug.ln("start parsing glossary: ", structure); - return that.findFile(structure) - .then(function(glossary) { - if (!glossary) return []; + return that.findFile(structure) + .then(function(glossary) { + if (!glossary) return []; - // Remove the glossary from the list of files to parse + // Remove the glossary from the list of files to parse that.glossaryFile = glossary.path; that._defaultsStructure(that.glossaryFile); - that.files = _.without(that.files, that.glossaryFile); - - that.log.debug.ln("glossary located at", that.glossaryFile); - return that.template.renderFile(that.glossaryFile) - .then(function(content) { - return glossary.parser.glossary(content); - }); - }) - .then(function(glossary) { - that.glossary = glossary; - }); + that.files = _.without(that.files, that.glossaryFile); + + that.log.debug.ln("glossary located at", that.glossaryFile); + return that.template.renderFile(that.glossaryFile) + .then(function(content) { + return glossary.parser.glossary(content); + }); + }) + .then(function(glossary) { + that.glossary = glossary; + }); }; // Parse a page Book.prototype.parsePage = function(filename, options) { - var that = this, page = {}; + var that = this, page = {}; options = _.defaults(options || {}, { - // Transform svg images - convertImages: false, + // Transform svg images + convertImages: false, // Interpolate before templating interpolateTemplate: _.identity, @@ -479,12 +479,12 @@ Book.prototype.parsePage = function(filename, options) { }); }; - that.log.debug.ln("start parsing file", filename); + that.log.debug.ln("start parsing file", filename); - var extension = path.extname(filename); - var filetype = parsers.get(extension); + var extension = path.extname(filename); + var filetype = parsers.get(extension); - if (!filetype) return Q.reject(new Error("Can't parse file: "+filename)); + if (!filetype) return Q.reject(new Error("Can't parse file: "+filename)); // Type of parser used page.type = filetype.name; @@ -498,7 +498,7 @@ Book.prototype.parsePage = function(filename, options) { // Progress in the book page.progress = parseProgress(that.navigation, filename); - that.log.debug.ln("render template", filename); + that.log.debug.ln("render template", filename); // Read file content return that.readFile(page.path) @@ -531,88 +531,88 @@ Book.prototype.parsePage = function(filename, options) { // Post process sections .then(function(_page) { - return _.reduce(_page.sections, function(prev, section) { - return prev.then(function(_sections) { - return that.template.postProcess(section.content || "") - .then(function(content) { - section.content = content; - return _sections.concat([section]); - }) - }); - }, Q([])); + return _.reduce(_page.sections, function(prev, section) { + return prev.then(function(_sections) { + return that.template.postProcess(section.content || "") + .then(function(content) { + section.content = content; + return _sections.concat([section]); + }) + }); + }, Q([])); }) // Prepare html - .then(function(_sections) { - return pageUtil.normalize(_sections, { - book: that, - convertImages: options.convertImages, - input: filename, + .then(function(_sections) { + return pageUtil.normalize(_sections, { + book: that, + convertImages: options.convertImages, + input: filename, navigation: that.navigation, base: path.dirname(filename) || './', output: path.dirname(filename) || './', glossary: that.glossary }); - }) - - // Interpolate output - .then(function(_sections) { - page.sections = _sections; - return interpolate(options.interpolateContent); - }) - - .then(function() { - that.indexPage(page); - return page; - }); + }) + + // Interpolate output + .then(function(_sections) { + page.sections = _sections; + return interpolate(options.interpolateContent); + }) + + .then(function() { + that.indexPage(page); + return page; + }); }; // Find file that can be parsed with a specific filename Book.prototype.findFile = function(filename) { - var that = this; - - return _.reduce(parsers.extensions, function(prev, ext) { - return prev.then(function(output) { - // Stop if already find a parser - if (output) return output; - - var filepath = filename+ext; - - return that.fileExists(filepath) - .then(function(exists) { - if (!exists) return null; - return { - parser: parsers.get(ext), - path: filepath - }; - }) - }); - }, Q(null)); + var that = this; + + return _.reduce(parsers.extensions, function(prev, ext) { + return prev.then(function(output) { + // Stop if already find a parser + if (output) return output; + + var filepath = filename+ext; + + return that.fileExists(filepath) + .then(function(exists) { + if (!exists) return null; + return { + parser: parsers.get(ext), + path: filepath + }; + }) + }); + }, Q(null)); }; // Check if a file exists in the book Book.prototype.fileExists = function(filename) { - return fs.exists( - path.join(this.root, filename) - ); + return fs.exists( + path.join(this.root, filename) + ); }; // Read a file Book.prototype.readFile = function(filename) { - return fs.readFile( - path.join(this.root, filename), - { encoding: "utf8" } - ); + return fs.readFile( + path.join(this.root, filename), + { encoding: "utf8" } + ); }; // Return stat for a file Book.prototype.statFile = function(filename) { - return fs.stat(path.join(this.root, filename)); + return fs.stat(path.join(this.root, filename)); }; // List all files in the book Book.prototype.listAllFiles = function() { - var that = this; + var that = this; return fs.list(this.root, { ignoreFiles: ['.ignore', '.gitignore', '.bookignore'], @@ -646,18 +646,18 @@ Book.prototype.listAllFiles = function() { // Return true if the book is a multilingual book Book.prototype.isMultilingual = function(filename) { - return this.books.length > 0; + return this.books.length > 0; }; // Return root of the parent Book.prototype.parentRoot = function() { - if (this.parent) return this.parent.parentRoot(); - return this.root; + if (this.parent) return this.parent.parentRoot(); + return this.root; }; // Return true if it's a sub-book Book.prototype.isSubBook = function() { - return !!this.parent; + return !!this.parent; }; // Test if the file is the entry point @@ -685,11 +685,11 @@ Book.prototype.contentLink = function(link) { // Index a page into the search index Book.prototype.indexPage = function(page) { - var nav = this.navigation[page.path]; - if (!nav) return; + var nav = this.navigation[page.path]; + if (!nav) return; - this.log.debug.ln("index page", page.path); - this.searchIndex.add({ + this.log.debug.ln("index page", page.path); + this.searchIndex.add({ url: this.contentLink(page.path), title: nav.title, body: pageUtil.extractText(page.sections), @@ -709,12 +709,12 @@ Book.prototype._defaultsStructure = function(filename) { // Change output path Book.prototype.setOutput = function(p) { - var that = this; - this.options.output = path.resolve(p); + var that = this; + this.options.output = path.resolve(p); - _.each(this.books, function(book) { - book.setOutput(path.join(that.options.output, book.options.language)); - }); + _.each(this.books, function(book) { + book.setOutput(path.join(that.options.output, book.options.language)); + }); }; |