diff options
author | Shaform <shaform@gmail.com> | 2014-06-17 22:08:46 +0800 |
---|---|---|
committer | Shaform <shaform@gmail.com> | 2014-06-17 22:08:46 +0800 |
commit | 6bbefad24e6aa4a5b51b4b21bdf30b73f8cb4d41 (patch) | |
tree | d2efbbb62f9746b19b683c11a302c9afe385a2be /lib | |
parent | 1f4cf33dd08b439cadb5c649654ecd6d3f4b399d (diff) | |
parent | d6eb8e4c6042262408c3c0b8d67bccecfa4bf882 (diff) | |
download | gitbook-6bbefad24e6aa4a5b51b4b21bdf30b73f8cb4d41.zip gitbook-6bbefad24e6aa4a5b51b4b21bdf30b73f8cb4d41.tar.gz gitbook-6bbefad24e6aa4a5b51b4b21bdf30b73f8cb4d41.tar.bz2 |
Merge remote-tracking branch 'upstream/master' into ref_links
Diffstat (limited to 'lib')
-rw-r--r-- | lib/generate/ebook/index.js | 53 | ||||
-rw-r--r-- | lib/generate/fs.js | 13 | ||||
-rw-r--r-- | lib/generate/generator.js | 21 | ||||
-rw-r--r-- | lib/generate/index.js | 76 | ||||
-rw-r--r-- | lib/generate/page/index.js | 25 | ||||
-rw-r--r-- | lib/generate/pdf/index.js | 56 | ||||
-rw-r--r-- | lib/generate/plugin.js | 2 | ||||
-rw-r--r-- | lib/generate/site/index.js | 23 | ||||
-rw-r--r-- | lib/generate/template.js | 52 | ||||
-rw-r--r-- | lib/parse/git.js | 2 | ||||
-rw-r--r-- | lib/parse/navigation.js | 10 | ||||
-rw-r--r-- | lib/parse/summary.js | 27 | ||||
-rw-r--r-- | lib/utils/links.js | 23 | ||||
-rw-r--r-- | lib/utils/string.js | 26 |
14 files changed, 268 insertions, 141 deletions
diff --git a/lib/generate/ebook/index.js b/lib/generate/ebook/index.js index 7416860..e3d3db4 100644 --- a/lib/generate/ebook/index.js +++ b/lib/generate/ebook/index.js @@ -7,6 +7,7 @@ var exec = require('child_process').exec; var fs = require("fs"); var parse = require("../../parse"); var BaseGenerator = require("../page"); +var stringUtils = require("../../utils/string"); /* * This generator inherits from the single page generator @@ -14,11 +15,6 @@ var BaseGenerator = require("../page"); */ var Generator = function() { BaseGenerator.apply(this, arguments); - - // Options for eBook generation - this.options = _.defaults(this.options, { - extension: "epub" - }); }; util.inherits(Generator, BaseGenerator); @@ -28,27 +24,54 @@ Generator.prototype.finish = function() { return BaseGenerator.prototype.finish.apply(this) .then(function() { var d = Q.defer(); + var format = that.options.extension || path.extname(that.options.output).replace("\.", "") || "pdf"; 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 + "--cover": that.options.cover, + "--title": that.options.title, + "--comments": that.options.description, + "--authors": that.options.author, + "--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 ')]" }; + if (format == "pdf") { + var pdfOptions = _.defaults(that.options.pdf || {}, { + "fontSize": 12, + "toc": true, + "pageNumbers": false, + "paperSize": "a4", + "margin": { + "right": 62, + "left": 62, + "top": 36, + "bottom": 36 + } + }); + + _.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-add-toc": Boolean(pdfOptions.toc), + "--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) + }); + } + var command = [ "ebook-convert", path.join(that.options.output, "index.html"), - path.join(that.options.output, "index."+that.options.extension), - _.chain(_options) - .map(function(value, key) { - if (value == null) return null; - return key+"="+value; - }) - .compact() - .value() - .join(" ") + path.join(that.options.output, "index."+format), + stringUtils.optionsToShellArgs(_options) ].join(" "); exec(command, function (error, stdout, stderr) { diff --git a/lib/generate/fs.js b/lib/generate/fs.js index 12252e3..66530f3 100644 --- a/lib/generate/fs.js +++ b/lib/generate/fs.js @@ -16,9 +16,20 @@ var getFiles = function(path) { // Add extra rules to ignore common folders ig.addIgnoreRules([ + // Skip Git stuff '.git/', '.gitignore', - '.DS_Store' + + // Skip OS X meta data + '.DS_Store', + + // Skip stuff installed by plugins + 'node_modules', + + // Skip book outputs + '*.pdf', + '*.epub', + '*.mobi', ], '__custom_stuff'); // Push each file to our list diff --git a/lib/generate/generator.js b/lib/generate/generator.js index 2c4cb44..d664eef 100644 --- a/lib/generate/generator.js +++ b/lib/generate/generator.js @@ -45,6 +45,27 @@ BaseGenerator.prototype.transferFolder = function(input) { ); }; +BaseGenerator.prototype.copyCover = function() { + var that = this; + + return Q.all([ + fs.copy(path.join(this.options.input, "cover.jpg"), path.join(this.options.output, "cover.jpg")), + fs.copy(path.join(this.options.input, "cover_small.jpg"), path.join(this.options.output, "cover_small.jpg")) + ]) + .fail(function() { + // If orignally from multi-lang, try copy from originalInput + if (!that.options.originalInput) return; + + return Q.all([ + fs.copy(path.join(that.options.originalInput, "cover.jpg"), path.join(that.options.output, "cover.jpg")), + fs.copy(path.join(that.options.originalInput, "cover_small.jpg"), path.join(that.options.output, "cover_small.jpg")) + ]); + }) + .fail(function(err) { + return Q(); + }); +}; + BaseGenerator.prototype.langsIndex = function(langs) { return Q.reject(new Error("Langs index is not supported in this generator")); }; diff --git a/lib/generate/index.js b/lib/generate/index.js index 6b75dea..444f75f 100644 --- a/lib/generate/index.js +++ b/lib/generate/index.js @@ -1,9 +1,9 @@ var Q = require("q"); var _ = require("lodash"); var path = require("path"); -var swig = require('swig'); var tmp = require('tmp'); +var swig = require('./template'); var fs = require("./fs"); var parse = require("../parse"); var Plugin = require("./plugin"); @@ -11,7 +11,6 @@ var Plugin = require("./plugin"); var generators = { "site": require("./site"), "page": require("./page"), - "pdf": require("./pdf"), "ebook": require("./ebook"), "json": require("./json") }; @@ -26,13 +25,23 @@ var containsFiles = function(dir, files) { .then(_.all); }; +// TEst if generator exists +var checkGenerator = function(options) { + if (!generators[options.generator]) { + return Q.reject(new Error("Invalid generator (availables are: "+_.keys(generators).join(", ")+")")); + } + return Q(); +}; // Create the generator and load plugins var loadGenerator = function(options) { - var generator = new generators[options.generator](options); + return checkGenerator(options) + .then(function() { + var generator = new generators[options.generator](options); - return generator.loadPlugins() - .then(_.constant(generator)); + return generator.loadPlugins() + .then(_.constant(generator)); + }); }; @@ -45,7 +54,7 @@ var generate = function(options) { "output": null, // Config file (relative to input) - "configFile": "book.json", + "configFile": "book", // Output generator "generator": "site", @@ -79,27 +88,20 @@ var generate = function(options) { return Q.reject(new Error("Need option input (book input directory)")); } - // Ensure generator exists - if (!generators[options.generator]) { - return Q.reject(new Error("Invalid generator (availables are: "+_.keys(generators).join(", "))); - } - // Check files to get folder type (book, multilanguage book or neither) - return Q() + return checkGenerator(options) // Read config file .then(function() { - return fs.readFile(path.resolve(options.input, options.configFile)) - .then(function(_config) { - // Extend current config - _config = JSON.parse(_config); - _config = _.omit(_config, 'input', 'configFile', 'defaultsPlugins'); - - _.extend(options, _config); - }, function() { + try { + var _config = require(path.resolve(options.input, options.configFile)); + + _.extend(options, _.omit(_config, 'input', 'configFile', 'defaultsPlugins')); + } + catch(err) { // No config file: not a big deal return Q(); - }); + } }) // Read readme @@ -134,8 +136,12 @@ var generateMultiLang = function(options) { var langsSummary; options.output = options.output || path.join(options.input, "_book"); + return checkGenerator(options) + // Multi-languages book - return fs.readFile(path.join(options.input, "LANGS.md"), "utf-8") + .then(function() { + return fs.readFile(path.join(options.input, "LANGS.md"), "utf-8") + }) // Clean output folder .then(function(_langsSummary) { @@ -155,7 +161,9 @@ var generateMultiLang = function(options) { return prev.then(function() { return generate(_.extend({}, options, { input: path.join(options.input, entry.path), - output: path.join(options.output, entry.path) + output: path.join(options.output, entry.path), + originalInput: options.input, + originalOutput: options.output })); }) }, Q()); @@ -170,6 +178,17 @@ var generateMultiLang = function(options) { return generator.langsIndex(options.langsSummary); }) + // Copy cover file + .then(function() { + return Q.all([ + fs.copy(path.join(options.input, "cover.jpg"), path.join(options.output, "cover.jpg")), + fs.copy(path.join(options.input, "cover_small.jpg"), path.join(options.output, "cover_small.jpg")) + ]) + .fail(function() { + return Q(); + }) + }) + // Return options to caller .then(_.constant(options)); }; @@ -205,8 +224,15 @@ var generateBook = function(options) { options.github = null; return null; } else if(options.github) { - // Git already specified in options - return options.github; + // Git already specified in options + options.github = ( + // Support URLs in "github" entry of book.json + parse.git.githubID(options.github) || + + // Fallback + options.github + ); + return; } // Try auto detecting diff --git a/lib/generate/page/index.js b/lib/generate/page/index.js index c2f4484..bd9b233 100644 --- a/lib/generate/page/index.js +++ b/lib/generate/page/index.js @@ -2,22 +2,12 @@ var _ = require("lodash"); var util = require("util"); var path = require("path"); var Q = require("q"); -var swig = require('swig'); -var hljs = require('highlight.js'); +var swig = require("../template"); var fs = require("../fs"); var parse = require("../../parse"); var BaseGenerator = require("../site"); -// Swig filter: highlight coloration -swig.setFilter('code', function(code, lang) { - try { - return hljs.highlight(lang, code).value; - } catch(e) { - return hljs.highlightAuto(code).value; - } -}); - /* * This generator will generate a simple index.html which can be converted as a PDF @@ -61,22 +51,35 @@ Generator.prototype.convertFile = function(content, input) { }); }; +// Generate languages index +Generator.prototype.langsIndex = function(langs) { + return Q(); +}; + Generator.prototype.finish = function() { var that = this; var basePath = "."; var output = path.join(this.options.output, "index.html"); + var progress = parse.progress(this.options.navigation, "README.md"); + return Q() // Generate html .then(function(pages) { return that._writeTemplate(that.template, { pages: that.pages, + progress: progress, basePath: basePath, staticBase: path.join(basePath, "gitbook"), }, output); }) + // Copy cover + .then(function() { + return that.copyCover(); + }) + // Copy assets .then(function() { return fs.copy( diff --git a/lib/generate/pdf/index.js b/lib/generate/pdf/index.js deleted file mode 100644 index 185eabd..0000000 --- a/lib/generate/pdf/index.js +++ /dev/null @@ -1,56 +0,0 @@ -var util = require("util"); -var path = require("path"); -var Q = require("q"); -var _ = require("lodash"); -var exec = require('child_process').exec; - -var fs = require("../fs"); -var parse = require("../../parse"); -var BaseGenerator = require("../page"); - -/* - * This generator inherits from the single page generator - * and convert the page output to pdf using gitbook-pdf - */ -var Generator = function() { - BaseGenerator.apply(this, arguments); - - // Options for PDF generation - this.options = _.defaults(this.options, { - paperformat: "A4" - }); -}; -util.inherits(Generator, BaseGenerator); - -Generator.prototype.finish = function() { - var that = this; - - return BaseGenerator.prototype.finish.apply(this) - .then(function() { - var d = Q.defer(); - - var command = [ - "gitbook-pdf", - "generate", - path.join(that.options.output, "index.html"), - path.join(that.options.output, "index.pdf"), - "--format="+that.options.paperformat - ].join(" "); - - exec(command, function (error, stdout, stderr) { - if (error) { - if (error.code == 127) { - error.message = "Need to install gitbook-pdf using: npm install gitbook-pdf -g"; - } else { - error.message = error.message + " "+stdout; - } - return d.reject(error); - } - d.resolve(); - }); - - return d.promise; - }); -}; - -module.exports = Generator; diff --git a/lib/generate/plugin.js b/lib/generate/plugin.js index 19eec86..3eacc19 100644 --- a/lib/generate/plugin.js +++ b/lib/generate/plugin.js @@ -163,6 +163,7 @@ Plugin.fromList = function(names, root) { resources.html = {} _.each(plugins, function(plugin) { + if (!plugin.infos.book || !plugin.infos.book.html) return; var html = plugin.infos.book.html || {}; _.each(html, function(code, key) { if (!_.isFunction(code)) code = _.constant(code); @@ -200,7 +201,6 @@ Plugin.fromList = function(names, root) { // Default plugins Plugin.defaults = [ - "mixpanel", "mathjax" ]; diff --git a/lib/generate/site/index.js b/lib/generate/site/index.js index da65152..b59c01c 100644 --- a/lib/generate/site/index.js +++ b/lib/generate/site/index.js @@ -2,26 +2,16 @@ var util = require("util"); var path = require("path"); var Q = require("q"); var _ = require("lodash"); -var swig = require('swig'); +var swig = require("../template"); var fs = require("../fs"); var parse = require("../../parse"); var BaseGenerator = require("../generator"); - +var links = require("../../utils/links"); var indexer = require('./search_indexer'); var Manifest = require('../manifest'); -// Swig filter for returning the count of lines in a code section -swig.setFilter('lines', function(content) { - return content.split('\n').length; -}); -// Swig filter for returning a link to the associated html file of a markdown file -swig.setFilter('mdLink', function(link) { - var link = link.replace(".md", ".html"); - if (link == "README.html") link = "index.html"; - return link; -}); var Generator = function() { BaseGenerator.apply(this, arguments); @@ -63,7 +53,6 @@ Generator.prototype._writeTemplate = function(tpl, options, output, interpolate) var that = this; interpolate = interpolate || _.identity; - return Q() .then(function(sections) { return tpl(_.extend({ @@ -163,7 +152,7 @@ Generator.prototype.convertFile = function(content, _input) { content: page.sections, basePath: basePath, - staticBase: path.join(basePath, "gitbook"), + staticBase: links.join(basePath, "gitbook"), }, output, function(html) { page.content = html; @@ -199,7 +188,7 @@ Generator.prototype.copyAssets = function() { path.join(that.options.output, "gitbook") ) - // Add to cach manifest + // Add to cache manifest .then(function() { return that.manifest.addFolder(path.join(that.options.output, "gitbook"), "gitbook"); }) @@ -238,7 +227,9 @@ Generator.prototype.writeCacheManifest = function() { }; Generator.prototype.finish = function() { - var deferred = this.copyAssets().then(this.writeSearchIndex); + var deferred = this.copyAssets() + .then(this.copyCover) + .then(this.writeSearchIndex); if (this.options.cache !== false) { deferred = deferred.then(this.writeCacheManifest); diff --git a/lib/generate/template.js b/lib/generate/template.js new file mode 100644 index 0000000..9b85228 --- /dev/null +++ b/lib/generate/template.js @@ -0,0 +1,52 @@ +var path = require("path"); +var swig = require('swig'); +var hljs = require('highlight.js'); + +var links = require('../utils/').links; +var pkg = require('../../package.json'); + +swig.setDefaults({ + locals: { + gitbook: { + version: pkg.version + } + } +}); + +// Swig filter for returning the count of lines in a code section +swig.setFilter('lines', function(content) { + return content.split('\n').length; +}); + +// Swig filter for returning a link to the associated html file of a markdown file +swig.setFilter('mdLink', function(link) { + var link = link.replace(".md", ".html"); + if (link == "README.html") link = "index.html"; + return link; +}); + +// Swig filter: highlight coloration +swig.setFilter('code', function(code, lang) { + try { + return hljs.highlight(lang, code).value; + } catch(e) { + return hljs.highlightAuto(code).value; + } +}); + +// Convert a level into a deep level +swig.setFilter('lvl', function(lvl) { + return lvl.split(".").length; +}); + +// Join path +swig.setFilter('pathJoin', function(base, _path) { + return path.join(base, _path); +}); + +// Is a link an absolute link +swig.setFilter('isExternalLink', function(link) { + return links.isExternal(link); +}); + +module.exports = swig; diff --git a/lib/parse/git.js b/lib/parse/git.js index 4478f20..18a7cd3 100644 --- a/lib/parse/git.js +++ b/lib/parse/git.js @@ -28,7 +28,7 @@ function gitURL(path) { // Parse a git URL to a github ID : username/reponame function githubID(_url) { // Remove .git if it's in _url - var sliceEnd = _url.slice(-4) === '.git' ? -4 : _url.length; + var sliceEnd = _url.slice(-4) === '.git' ? -4 : undefined; // Detect HTTPS repos var parsed = url.parse(_url); diff --git a/lib/parse/navigation.js b/lib/parse/navigation.js index 2c783e4..ae4eb9d 100644 --- a/lib/parse/navigation.js +++ b/lib/parse/navigation.js @@ -26,15 +26,9 @@ function navigation(summary, files) { // Support single files as well as list files = _.isArray(files) ? files : (_.isString(files) ? [files] : null); - // Special README nav - var README_NAV = { - path: 'README.md', - title: 'Introduction', - level: '0', - }; - // List of all navNodes - var navNodes = [README_NAV].concat(flattenChapters(summary.chapters)); + // Flatten chapters, then add in default README node if ndeeded etc ... + var navNodes = flattenChapters(summary.chapters); var prevNodes = [null].concat(navNodes.slice(0, -1)); var nextNodes = navNodes.slice(1).concat([null]); diff --git a/lib/parse/summary.js b/lib/parse/summary.js index 1fd5676..7e54df0 100644 --- a/lib/parse/summary.js +++ b/lib/parse/summary.js @@ -94,18 +94,35 @@ function parseChapter(nodes, nums) { }); } +function defaultChapterList(chapterList) { + var first = _.first(chapterList); + + var chapter = parseChapter(first, [0]); + + // Already have README node, we're good to go + if(chapter.path === 'README.md') { + return chapterList; + } + + return [ + [ { type: 'text', text: '[Introduction](README.md)' } ] + ].concat(chapterList); +} + function parseSummary(src) { var nodes = marked.lexer(src); // Get out list of chapters - var chapterList = filterList(nodes); + var chapterList = listSplit( + filterList(nodes), + 'list_item_start', 'list_item_end' + ); // Split out chapter sections - var chapters = _.chain(listSplit(chapterList, 'list_item_start', 'list_item_end')) + var chapters = defaultChapterList(chapterList) .map(function(nodes, i) { - return parseChapter(nodes, [i + 1]); - }) - .value(); + return parseChapter(nodes, [i]); + }); return { chapters: chapters diff --git a/lib/utils/links.js b/lib/utils/links.js index 808d711..6606bbf 100644 --- a/lib/utils/links.js +++ b/lib/utils/links.js @@ -1,6 +1,11 @@ var url = require('url'); var path = require('path'); +// Is the link an external link +var isExternal = function(href) { + return Boolean(url.parse(href).protocol); +}; + // Return true if the link is relative var isRelative = function(href) { var parsed = url.parse(href); @@ -26,8 +31,22 @@ var toAbsolute = function(_href, dir, outdir) { return _href; }; +// Join links + +var join = function() { + var _href = path.join.apply(path, arguments); + + if (process.platform === 'win32') { + _href = _href.replace(/\\/g, '/'); + } + + return _href; +}; + module.exports = { isRelative: isRelative, - toAbsolute: toAbsolute -};
\ No newline at end of file + isExternal: isExternal, + toAbsolute: toAbsolute, + join: join +}; diff --git a/lib/utils/string.js b/lib/utils/string.js new file mode 100644 index 0000000..417d7af --- /dev/null +++ b/lib/utils/string.js @@ -0,0 +1,26 @@ +var _ = require("lodash"); + +function escapeShellArg(arg) { + var ret = ''; + + ret = arg.replace(/"/g, '\\"'); + + return "\"" + ret + "\""; +} + +function optionsToShellArgs(options) { + return _.chain(options) + .map(function(value, key) { + if (value == null || value == false) return null; + if (value == true) return key; + return key+"="+escapeShellArg(value); + }) + .compact() + .value() + .join(" "); +} + +module.exports = { + escapeShellArg: escapeShellArg, + optionsToShellArgs: optionsToShellArgs +}; |