summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/generate/ebook/index.js53
-rw-r--r--lib/generate/fs.js13
-rw-r--r--lib/generate/generator.js21
-rw-r--r--lib/generate/index.js76
-rw-r--r--lib/generate/page/index.js25
-rw-r--r--lib/generate/pdf/index.js56
-rw-r--r--lib/generate/plugin.js2
-rw-r--r--lib/generate/site/index.js23
-rw-r--r--lib/generate/template.js52
-rw-r--r--lib/parse/git.js2
-rw-r--r--lib/parse/navigation.js10
-rw-r--r--lib/parse/summary.js27
-rw-r--r--lib/utils/links.js23
-rw-r--r--lib/utils/string.js26
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
+};