summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamy Pesse <samypesse@gmail.com>2016-02-11 21:44:38 +0100
committerSamy Pesse <samypesse@gmail.com>2016-02-11 21:44:38 +0100
commit669f3b39849890c48171d807225cd6eaa3c9086b (patch)
treebc07fefc4e13ac8f737174166ac1d19512379298
parente7eed2abbe91fa44bd071819123bd9ea04d1702a (diff)
downloadgitbook-669f3b39849890c48171d807225cd6eaa3c9086b.zip
gitbook-669f3b39849890c48171d807225cd6eaa3c9086b.tar.gz
gitbook-669f3b39849890c48171d807225cd6eaa3c9086b.tar.bz2
Add base for normalizing html
-rw-r--r--lib/book.js2
-rw-r--r--lib/generators/website/index.js (renamed from lib/generators/website.js)12
-rw-r--r--lib/generators/website/theme.js6
-rw-r--r--lib/page/html.js57
-rw-r--r--lib/page/index.js (renamed from lib/backbone/page.js)23
-rw-r--r--lib/utils/command.js27
-rw-r--r--lib/utils/images.js27
-rw-r--r--lib/utils/promise.js12
8 files changed, 156 insertions, 10 deletions
diff --git a/lib/book.js b/lib/book.js
index 914b48a..0f73135 100644
--- a/lib/book.js
+++ b/lib/book.js
@@ -9,7 +9,7 @@ var Readme = require('./backbone/readme');
var Glossary = require('./backbone/glossary');
var Summary = require('./backbone/summary');
var Langs = require('./backbone/langs');
-var Page = require('./backbone/page');
+var Page = require('./page');
var TemplateEngine = require('./template');
var pathUtil = require('./utils/path');
var error = require('./utils/error');
diff --git a/lib/generators/website.js b/lib/generators/website/index.js
index a2c3311..f474cbb 100644
--- a/lib/generators/website.js
+++ b/lib/generators/website/index.js
@@ -1,5 +1,5 @@
var util = require('util');
-var Generator = require('./base');
+var Generator = require('../base');
function WebsiteGenerator() {
Generator.apply(this, arguments);
@@ -8,12 +8,10 @@ util.inherits(WebsiteGenerator, Generator);
// Copy an asset file
WebsiteGenerator.prototype.writeAsset = function(filename) {
- var that = this;
-
- return that.book.readFile(filename)
- .then(function(buf) {
- return that.output.writeFile(filename, buf);
- });
+ return this.output.copyFile(
+ this.book.resolve(filename),
+ filename
+ );
};
// Write a page (parsable file)
diff --git a/lib/generators/website/theme.js b/lib/generators/website/theme.js
new file mode 100644
index 0000000..1cc2891
--- /dev/null
+++ b/lib/generators/website/theme.js
@@ -0,0 +1,6 @@
+
+function Theme() {
+
+}
+
+module.exports = Theme;
diff --git a/lib/page/html.js b/lib/page/html.js
new file mode 100644
index 0000000..f828d11
--- /dev/null
+++ b/lib/page/html.js
@@ -0,0 +1,57 @@
+var _ = require('lodash');
+var cheerio = require('cheerio');
+var domSerializer = require('dom-serializer');
+var slug = require('github-slugid');
+
+var Promise = require('../utils/promise');
+
+// Render a cheerio DOM as html
+function renderDOM($, dom, options) {
+ if (!dom && $._root && $._root.children) {
+ dom = $._root.children;
+ }
+ options = options|| dom.options || $._options;
+ return domSerializer(dom, options);
+}
+
+function HTMLPipeline(htmlString, opts) {
+ _.bindAll(this);
+
+ this.opts = _.defaults(opts || {}, {
+ convertImages: true
+ });
+
+ this.$ = cheerio.load(htmlString, {
+ // We should parse html without trying to normalize too much
+ xmlMode: false,
+
+ // SVG need some attributes to use uppercases
+ lowerCaseAttributeNames: false,
+ lowerCaseTags: false
+ });
+}
+
+// Add ID to headings
+HTMLPipeline.prototype.addHeadingIDs = function() {
+ var that = this;
+
+ this.$('h1,h2,h3,h4,h5,h6').each(function() {
+ // Already has an ID?
+ if (that.$(this).attr('id')) return;
+
+ that.$(this).attr('id', slug(that.$(this).text()));
+ });
+};
+
+// Write content to the pipeline
+HTMLPipeline.prototype.output = function() {
+ var that = this;
+
+ return Promise()
+ .then(this.addHeadingIDs)
+ .then(function() {
+ return renderDOM(that.$);
+ });
+};
+
+module.exports = HTMLPipeline;
diff --git a/lib/backbone/page.js b/lib/page/index.js
index 94812d9..8f8819c 100644
--- a/lib/backbone/page.js
+++ b/lib/page/index.js
@@ -3,6 +3,8 @@ var path = require('path');
var parsers = require('gitbook-parsers');
var error = require('../utils/error');
+var Promise = require('../utils/promise');
+var HTMLPipeline = require('./html');
/*
A page represent a parsable file in the book (Markdown, Asciidoc, etc)
@@ -63,9 +65,14 @@ Page.prototype.read = function() {
};
// Parse the page and return its content
-Page.prototype.parse = function() {
+Page.prototype.parse = function(opts) {
var that = this;
+ opts = _.defaults(opts || {}, {
+
+ });
+
+
this.log.debug.ln('start parsing file', this.path);
return this.read()
@@ -93,6 +100,20 @@ Page.prototype.parse = function() {
.then(function() {
return that.parser.page(that.content)
.then(that.update);
+ })
+
+ // Normalize HTML output
+ .then(function() {
+ return Promise.map(that.content.sections, function(section) {
+ var pipeline = new HTMLPipeline(section.content, opts);
+
+ return pipeline.output()
+ .then(function(content) {
+ return {
+ content: content
+ };
+ });
+ });
});
};
diff --git a/lib/utils/command.js b/lib/utils/command.js
index f395fa1..4269d6c 100644
--- a/lib/utils/command.js
+++ b/lib/utils/command.js
@@ -13,7 +13,32 @@ function exec(command, options) {
return Promise.nfcall(childProcess.exec, command, options);
}
+// Spawn an executable
+function spawn(command, args, options) {
+ if (!isAvailable) {
+ return Promise.reject(new Error('Command execution is not possible on this platform'));
+ }
+
+ var d = Promise.deferred();
+ var child = childProcess.spawn(command, args, options);
+
+ child.on('error', function(error) {
+ return d.reject(error);
+ });
+
+ child.on('close', function(code) {
+ if (code === 0) {
+ d.resolve();
+ } else {
+ d.reject(new Error('Error with command "'+command+'"'));
+ }
+ });
+
+ return d.promise;
+}
+
module.exports = {
isAvailable: isAvailable,
- exec: exec
+ exec: exec,
+ spawn: spawn
};
diff --git a/lib/utils/images.js b/lib/utils/images.js
new file mode 100644
index 0000000..3ba0f1f
--- /dev/null
+++ b/lib/utils/images.js
@@ -0,0 +1,27 @@
+var fs = require('fs');
+
+var Promise = require('./promise');
+var command = require('./command');
+var error = require('./error');
+
+// Convert a svg file to a pmg
+function convertSVGToPNG(source, dest, options) {
+ if (!command.isAvailable) return Promise.reject(new Error('Could not convert SVG in this platform'));
+ if (!fs.existsSync(source)) return Promise.reject(new error.FileNotFoundError({ filename: source }));
+
+ return command.spawn('svgexport', [source, dest])
+ .fail(function(err) {
+ if (err.code == 'ENOENT') err = new Error('Need to install "svgexport" using "npm install svgexport -g"');
+ throw err;
+ })
+ .then(function() {
+ if (fs.existsSync(dest)) return;
+
+ throw new Error('Error converting '+source+' into '+dest);
+ });
+}
+
+module.exports = {
+ convertSVGToPNG: convertSVGToPNG,
+ INVALID: ['.svg']
+}; \ No newline at end of file
diff --git a/lib/utils/promise.js b/lib/utils/promise.js
index 82f4a60..adcc1c3 100644
--- a/lib/utils/promise.js
+++ b/lib/utils/promise.js
@@ -32,7 +32,19 @@ function some(arr, iter) {
}, Q());
}
+// Map an array using an async (promised) iterator
+function map(arr, iter) {
+ return reduce(arr, function(prev, entry, i) {
+ return Q(iter(entry, i))
+ .then(function(out) {
+ prev.push(out);
+ return prev;
+ });
+ }, []);
+}
+
module.exports = Q;
module.exports.reduce = reduce;
+module.exports.map = map;
module.exports.serie = serie;
module.exports.some = some;