summaryrefslogtreecommitdiffstats
path: root/lib/generators
diff options
context:
space:
mode:
Diffstat (limited to 'lib/generators')
-rw-r--r--lib/generators/ebook.js139
-rw-r--r--lib/generators/index.js11
-rw-r--r--lib/generators/json.js76
-rw-r--r--lib/generators/website.js279
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;