diff options
author | Samy Pessé <samypesse@gmail.com> | 2015-03-09 10:43:12 +0100 |
---|---|---|
committer | Samy Pessé <samypesse@gmail.com> | 2015-03-09 10:43:12 +0100 |
commit | 34fc2831e0cf0fed01c71cec28d93472d87f455b (patch) | |
tree | a803cc907c20491ba02863b5d3dd5aedf6bfed10 /lib/generators | |
parent | e1594cde2c32e4ff48f6c4eff3d3d461743d74e1 (diff) | |
parent | 1bf68a5aa0703b5a1815cfe4ebb731b5fb6ed9d2 (diff) | |
download | gitbook-34fc2831e0cf0fed01c71cec28d93472d87f455b.zip gitbook-34fc2831e0cf0fed01c71cec28d93472d87f455b.tar.gz gitbook-34fc2831e0cf0fed01c71cec28d93472d87f455b.tar.bz2 |
Merge branch 'version/2.0'
Diffstat (limited to 'lib/generators')
-rw-r--r-- | lib/generators/ebook.js | 139 | ||||
-rw-r--r-- | lib/generators/index.js | 11 | ||||
-rw-r--r-- | lib/generators/json.js | 76 | ||||
-rw-r--r-- | lib/generators/website.js | 279 |
4 files changed, 505 insertions, 0 deletions
diff --git a/lib/generators/ebook.js b/lib/generators/ebook.js new file mode 100644 index 0000000..29b1966 --- /dev/null +++ b/lib/generators/ebook.js @@ -0,0 +1,139 @@ +var util = require("util"); +var path = require("path"); +var Q = require("q"); +var _ = require("lodash"); +var exec = require('child_process').exec; + +var fs = require("../utils/fs"); +var stringUtils = require("../utils/string"); +var BaseGenerator = require("./website"); + +var Generator = function(book, format) { + BaseGenerator.apply(this, arguments); + + // eBook format + this.ebookFormat = format; + + // Resources namespace + this.namespace = "ebook"; + + // Styles to use + this.styles = _.compact(["ebook", this.ebookFormat]); + + // Convert images (svg -> png) + this.convertImages = true; +}; +util.inherits(Generator, BaseGenerator); + +Generator.prototype.prepareTemplates = function() { + this.templates["page"] = this.book.plugins.template("ebook:page") || path.resolve(this.options.theme, 'templates/ebook/page.html'); + this.templates["summary"] = this.book.plugins.template("ebook:summary") || path.resolve(this.options.theme, 'templates/ebook/summary.html'); + this.templates["glossary"] = this.book.plugins.template("ebook:glossary") || path.resolve(this.options.theme, 'templates/ebook/glossary.html'); + + return Q(); +}; + +// Generate table of contents +Generator.prototype.writeSummary = function() { + var that = this; + + that.book.log.info.ln("write SUMMARY.html"); + return this._writeTemplate(this.templates["summary"], {}, path.join(this.options.output, "SUMMARY.html")); +}; + +Generator.prototype.finish = function() { + var that = this; + + return Q() + .then(this.copyAssets) + .then(this.copyCover) + .then(this.writeGlossary) + .then(this.writeSummary) + .then(function() { + if (!that.ebookFormat) return Q(); + + var d = Q.defer(); + + if (!that.options.cover && fs.existsSync(path.join(that.options.output, "cover.jpg"))) { + that.options.cover = path.join(that.options.output, "cover.jpg"); + } + + var _options = { + "--cover": that.options.cover, + "--title": that.options.title, + "--comments": that.options.description, + "--isbn": that.options.isbn, + "--authors": that.options.author, + "--language": that.options.language, + "--book-producer": "GitBook", + "--publisher": "GitBook", + "--chapter": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter ')]", + "--chapter-mark": "pagebreak", + "--page-breaks-before": "/", + "--level1-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-1 ')]", + "--level2-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-2 ')]", + "--level3-toc": "descendant-or-self::*[contains(concat(' ', normalize-space(@class), ' '), ' book-chapter-3 ')]", + "--no-chapters-in-toc": true, + "--max-levels": "1", + "--breadth-first": true + }; + + if (that.ebookFormat == "pdf") { + var pdfOptions = that.options.pdf; + + _.extend(_options, { + "--margin-left": String(pdfOptions.margin.left), + "--margin-right": String(pdfOptions.margin.right), + "--margin-top": String(pdfOptions.margin.top), + "--margin-bottom": String(pdfOptions.margin.bottom), + "--pdf-default-font-size": String(pdfOptions.fontSize), + "--pdf-mono-font-size": String(pdfOptions.fontSize), + "--paper-size": String(pdfOptions.paperSize), + "--pdf-page-numbers": Boolean(pdfOptions.pageNumbers), + "--pdf-header-template": String(pdfOptions.headerTemplate) || "<p class='header'><span>"+that.options.title+"</span></p>", + "--pdf-footer-template": String(pdfOptions.footerTemplate) || "<p class='footer'><span>_SECTION_</span> <span style='float:right;'>_PAGENUM_</span></p>" + }); + } else if (that.ebookFormat == "epub") { + _.extend(_options, { + "--dont-split-on-page-breaks": true + }); + } + + var command = [ + "ebook-convert", + path.join(that.options.output, "SUMMARY.html"), + path.join(that.options.output, "index."+that.ebookFormat), + stringUtils.optionsToShellArgs(_options) + ].join(" "); + + that.book.log.info("start conversion to", that.ebookFormat, "...."); + + var child = exec(command, function (error, stdout, stderr) { + if (error) { + that.book.log.info.fail(); + + if (error.code == 127) { + error.message = "Need to install ebook-convert from Calibre"; + } else { + error.message = error.message + " "+stdout; + } + return d.reject(error); + } + + that.book.log.info.ok(); + d.resolve(); + }); + + child.stdout.on('data', function (data) { + that.book.log.debug(data); + }); + + child.stderr.on('data', function (data) { + that.book.log.debug(data); + }); + + return d.promise; + }); +}; + +module.exports = Generator; diff --git a/lib/generators/index.js b/lib/generators/index.js new file mode 100644 index 0000000..e619188 --- /dev/null +++ b/lib/generators/index.js @@ -0,0 +1,11 @@ +var _ = require("lodash"); +var EbookGenerator = require("./ebook"); + +module.exports = { + json: require("./json"), + website: require("./website"), + ebook: EbookGenerator, + pdf: _.partialRight(EbookGenerator, "pdf"), + mobi: _.partialRight(EbookGenerator, "mobi"), + epub: _.partialRight(EbookGenerator, "epub") +}; diff --git a/lib/generators/json.js b/lib/generators/json.js new file mode 100644 index 0000000..6c9439d --- /dev/null +++ b/lib/generators/json.js @@ -0,0 +1,76 @@ +var util = require("util"); +var path = require("path"); +var Q = require("q"); +var _ = require("lodash"); + +var fs = require("../utils/fs"); +var BaseGenerator = require("../generator"); +var links = require("../utils/links"); + +var Generator = function() { + BaseGenerator.apply(this, arguments); +}; +util.inherits(Generator, BaseGenerator); + +// Ignore some methods +Generator.prototype.transferFile = function(input) { }; + +// Convert an input file +Generator.prototype.convertFile = function(input) { + var that = this; + + return that.book.parsePage(input) + .then(function(page) { + var json = { + progress: page.progress, + sections: page.sections + }; + + var output = links.changeExtension(page.path, ".json"); + output = path.join(that.options.output, output); + + return fs.writeFile( + output, + JSON.stringify(json, null, 4) + ); + }); +}; + +// Finish generation +Generator.prototype.finish = function() { + return this.writeReadme(); +}; + +// Write README.json +Generator.prototype.writeReadme = function() { + var that = this; + var mainlang, langs; + + return Q() + .then(function() { + langs = that.book.langs; + mainLang = langs.length > 0? _.first(langs).lang : null; + + readme = links.changeExtension(that.book.readmeFile, ".json"); + + // Read readme from main language + return fs.readFile( + mainLang? path.join(that.options.output, mainLang, readme) : path.join(that.options.output, readme) + ); + }) + .then(function(content) { + // Extend it with infos about the languages + var json = JSON.parse(content); + _.extend(json, { + langs: langs + }); + + // Write it as README.json + return fs.writeFile( + path.join(that.options.output, "README.json"), + JSON.stringify(json, null, 4) + ); + }); +}; + +module.exports = Generator; diff --git a/lib/generators/website.js b/lib/generators/website.js new file mode 100644 index 0000000..a58e3c4 --- /dev/null +++ b/lib/generators/website.js @@ -0,0 +1,279 @@ +var util = require("util"); +var path = require("path"); +var Q = require("q"); +var _ = require("lodash"); + +var nunjucks = require("nunjucks"); +var AutoEscapeExtension = require("nunjucks-autoescape"); +var FilterExtension = require("nunjucks-filter"); +var I18nExtension = require("nunjucks-i18n"); + +var fs = require("../utils/fs"); +var BaseGenerator = require("../generator"); +var links = require("../utils/links"); +var pageUtil = require("../utils/page"); +var i18n = require("../utils/i18n"); + +var pkg = require("../../package.json"); + +var Generator = function() { + BaseGenerator.apply(this, arguments); + + // Revision + this.revision = new Date(); + + // Resources namespace + this.namespace = "website"; + + // Style to integrates in the output + this.styles = ["website"]; + + // Convert images (svg -> png) + this.convertImages = false; + + // Templates + this.templates = {}; +}; +util.inherits(Generator, BaseGenerator); + +// Prepare the genertor +Generator.prototype.prepare = function() { + return BaseGenerator.prototype.prepare.apply(this) + .then(this.prepareStyles) + .then(this.prepareTemplates) + .then(this.prepareTemplateEngine); +}; + +// Prepare all styles +Generator.prototype.prepareStyles = function() { + var that = this; + + this.styles = _.chain(this.styles) + .map(function(style) { + var stylePath = that.options.styles[style]; + if (fs.existsSync(path.resolve(that.book.root, stylePath))) { + return stylePath; + } + return null; + }) + .compact() + .value(); + + return Q(); +}; + +// Prepare templates +Generator.prototype.prepareTemplates = function() { + this.templates["page"] = this.book.plugins.template("site:page") || path.resolve(this.options.theme, 'templates/website/page.html'); + this.templates["langs"] = this.book.plugins.template("site:langs") || path.resolve(this.options.theme, 'templates/website/langs.html'); + this.templates["glossary"] = this.book.plugins.template("site:glossary") || path.resolve(this.options.theme, 'templates/website/glossary.html'); + + return Q(); +}; + +// Prepare template engine +Generator.prototype.prepareTemplateEngine = function() { + var that = this; + + return Q() + .then(function() { + var language = that.book.config.normalizeLanguage(); + + if (!i18n.getByLanguage(language) && language != "en") { + that.book.log.warn.ln("Language '"+language+"' is not available as a layout locales (en, "+i18n.getLanguages().join(", ")+")"); + } + + var folders = _.chain(that.templates) + .values() + .map(path.dirname) + .uniq() + .value(); + + that.env = new nunjucks.Environment( + new nunjucks.FileSystemLoader(folders), + { + autoescape: true + } + ); + + // Add filter + that.env.addFilter("contentLink", that.book.contentLink.bind(that.book)); + that.env.addFilter('lvl', function(lvl) { + return lvl.split(".").length; + }); + + // Add extension + that.env.addExtension('AutoEscapeExtension', new AutoEscapeExtension(that.env)); + that.env.addExtension('FilterExtension', new FilterExtension(that.env)); + that.env.addExtension('I18nExtension', new I18nExtension({ + env: that.env, + translations: i18n.getLocales(), + locale: "language" + })); + }); +}; + +// Finis generation +Generator.prototype.finish = function() { + return this.copyAssets() + .then(this.copyCover) + .then(this.writeGlossary) + .then(this.writeSearchIndex) + .then(this.writeLangsIndex) +}; + +// Convert an input file +Generator.prototype.convertFile = function(input) { + var that = this; + + return that.book.parsePage(input, { + convertImages: that.convertImages, + interpolateTemplate: function(page) { + return that.callHook("page:before", page); + }, + interpolateContent: function(page) { + return that.callHook("page", page); + } + }) + .then(function(page) { + var relativeOutput = that.book.contentLink(page.path); + var output = path.join(that.options.output, relativeOutput); + + var basePath = path.relative(path.dirname(output), that.options.output) || "."; + if (process.platform === 'win32') basePath = basePath.replace(/\\/g, '/'); + + that.book.log.debug.ln("write parsed file", page.path, "to", relativeOutput); + + return that._writeTemplate(that.templates["page"], { + progress: page.progress, + + _input: page.path, + content: page.sections, + + basePath: basePath, + staticBase: links.join(basePath, "gitbook") + }, output); + }); +}; + +// Write the index for langs +Generator.prototype.writeLangsIndex = function() { + var that = this; + if (!this.book.langs.length) return Q(); + return this._writeTemplate(this.templates["langs"], { + langs: this.book.langs + }, path.join(this.options.output, "index.html")); +}; + +// Write glossary +Generator.prototype.writeGlossary = function() { + var that = this; + + // No glossary + if (this.book.glossary.length == 0) return Q(); + + return this._writeTemplate(this.templates["glossary"], {}, path.join(this.options.output, "GLOSSARY.html")); +}; + +// Write the search index +Generator.prototype.writeSearchIndex = function() { + var that = this; + + return fs.writeFile( + path.join(this.options.output, "search_index.json"), + JSON.stringify(this.book.searchIndex) + ); +}; + +// Convert a page into a normalized data set +Generator.prototype.normalizePage = function(page) { + var that = this; + + var _callHook = function(name) { + return that.callHook(name, page) + .then(function(_page) { + page = _page; + return page; + }); + }; + + return Q() + .then(function() { + return _callHook("page"); + }) + .then(function() { + return page; + }); +}; + +// Generate a template +Generator.prototype._writeTemplate = function(tpl, options, output, interpolate) { + var that = this; + + interpolate = interpolate || _.identity; + return Q() + .then(function(sections) { + return that.env.render( + tpl, + _.extend({ + gitbook: { + version: pkg.version + }, + + styles: that.styles, + + revision: that.revision, + + title: that.options.title, + description: that.options.description, + language: that.book.config.normalizeLanguage(), + + glossary: that.book.glossary, + + summary: that.book.summary, + allNavigation: that.book.navigation, + + plugins: { + resources: that.book.plugins.resources(that.namespace) + }, + pluginsConfig: JSON.stringify(that.options.pluginsConfig), + htmlSnippet: _.partial(_.partialRight(that.book.plugins.html, that, options), that.namespace), + + options: that.options, + + basePath: ".", + staticBase: path.join(".", "gitbook"), + }, options) + ); + }) + .then(interpolate) + .then(function(html) { + return fs.writeFile( + output, + html + ); + }); +}; + +// Copy assets +Generator.prototype.copyAssets = function() { + var that = this; + + // Copy gitbook assets + return fs.copy( + path.join(that.options.theme, "assets"), + path.join(that.options.output, "gitbook") + ) + + // Copy plugins assets + .then(function() { + return Q.all( + _.map(that.book.plugins.list, function(plugin) { + var pluginAssets = path.join(that.options.output, "gitbook/plugins/", plugin.name); + return plugin.copyAssets(pluginAssets, that.namespace); + }) + ); + }); +}; + +module.exports = Generator; |