summaryrefslogtreecommitdiffstats
path: root/lib/output2/base.js
diff options
context:
space:
mode:
authorSamy Pessé <samypesse@gmail.com>2016-04-22 11:00:21 +0200
committerSamy Pessé <samypesse@gmail.com>2016-04-22 11:00:21 +0200
commit4336fdb2414d460ffee68a0cc87c0cb0c85cf56e (patch)
tree279f711ab98666c892c19a7b9e4073a094f03f98 /lib/output2/base.js
parent87db7cf1d412fa6fbd18e9a7e4f4755f2c0c5547 (diff)
downloadgitbook-4336fdb2414d460ffee68a0cc87c0cb0c85cf56e.zip
gitbook-4336fdb2414d460ffee68a0cc87c0cb0c85cf56e.tar.gz
gitbook-4336fdb2414d460ffee68a0cc87c0cb0c85cf56e.tar.bz2
Base
Diffstat (limited to 'lib/output2/base.js')
-rw-r--r--lib/output2/base.js309
1 files changed, 309 insertions, 0 deletions
diff --git a/lib/output2/base.js b/lib/output2/base.js
new file mode 100644
index 0000000..868b85b
--- /dev/null
+++ b/lib/output2/base.js
@@ -0,0 +1,309 @@
+var _ = require('lodash');
+var Ignore = require('ignore');
+var path = require('path');
+
+var Promise = require('../utils/promise');
+var pathUtil = require('../utils/path');
+var location = require('../utils/location');
+var error = require('../utils/error');
+var PluginsManager = require('../plugins');
+var TemplateEngine = require('../template');
+var gitbook = require('../gitbook');
+
+/*
+Output is like a stream interface for a parsed book
+to output "something".
+
+The process is mostly on the behavior of "onPage" and "onAsset"
+*/
+
+function Output(book, opts, parent) {
+ _.bindAll(this);
+ this.parent = parent;
+
+ this.opts = _.defaults({}, opts || {}, {
+ directoryIndex: true
+ });
+
+ this.book = book;
+ book.output = this;
+ this.log = this.book.log;
+
+ // Create plugins manager
+ this.plugins = new PluginsManager(this.book);
+
+ // Create template engine
+ this.template = new TemplateEngine(this);
+
+ // Files to ignore in output
+ this.ignore = Ignore();
+
+ // Hack to inherits from rules of the book
+ this.ignore.add(this.book.ignore);
+}
+
+// Default name for generator
+Output.prototype.name = 'base';
+
+// Default extension for output
+Output.prototype.defaultExtension = '.html';
+
+// Start the generation, for a parsed book
+Output.prototype.generate = function() {
+ var that = this;
+ var isMultilingual = this.book.isMultilingual();
+
+ return Promise()
+
+ // Load all plugins
+ .then(function() {
+ return that.plugins.loadAll()
+ .then(function() {
+ that.template.addFilters(that.plugins.getFilters());
+ that.template.addBlocks(that.plugins.getBlocks());
+ });
+ })
+
+ // Transform the configuration
+ .then(function() {
+ return that.plugins.hook('config', that.book.config.dump())
+ .then(function(cfg) {
+ that.book.config.replace(cfg);
+ });
+ })
+
+ // Initialize the generation
+ .then(function() {
+ return that.plugins.hook('init');
+ })
+ .then(function() {
+ that.log.info.ln('preparing the generation');
+ return that.prepare();
+ })
+
+ // Process all files
+ .then(function() {
+ that.log.debug.ln('listing files');
+ return that.book.fs.listAllFiles(that.book.root);
+ })
+
+ // We want to process assets first, then pages
+ // Since pages can have logic based on existance of assets
+ .then(function(files) {
+ // Split into pages/assets
+ var byTypes = _.chain(files)
+ .filter(that.ignore.createFilter())
+
+ // Ignore file present in a language book
+ .filter(function(filename) {
+ return !(isMultilingual && that.book.isInLanguageBook(filename));
+ })
+
+ .groupBy(function(filename) {
+ return (that.book.hasPage(filename)? 'page' : 'asset');
+ })
+
+ .value();
+
+ return Promise.serie(byTypes.asset, function(filename) {
+ that.log.debug.ln('copy asset', filename);
+ return that.onAsset(filename);
+ })
+ .then(function() {
+ return Promise.serie(byTypes.page, function(filename) {
+ that.log.debug.ln('process page', filename);
+ return that.onPage(that.book.getPage(filename));
+ });
+ });
+ })
+
+ // Generate sub-books
+ .then(function() {
+ if (!that.book.isMultilingual()) return;
+
+ return Promise.serie(that.book.books, function(subbook) {
+ that.log.info.ln('');
+ that.log.info.ln('start generation of language "' + path.relative(that.book.root, subbook.root) + '"');
+
+ var out = that.onLanguageBook(subbook);
+ return out.generate();
+ });
+ })
+
+ // Finish the generation
+ .then(function() {
+ return that.plugins.hook('finish:before');
+ })
+ .then(function() {
+ that.log.debug.ln('finishing the generation');
+ return that.finish();
+ })
+ .then(function() {
+ return that.plugins.hook('finish');
+ })
+
+ .then(function() {
+ if (!that.book.isLanguageBook()) that.log.info.ln('');
+ that.log.info.ok('generation finished with success!');
+ });
+};
+
+// Prepare the generation
+Output.prototype.prepare = function() {
+ this.ignore.addPattern(_.compact([
+ '.gitignore',
+ '.ignore',
+ '.bookignore',
+ 'node_modules',
+ '_layouts',
+
+ // The configuration file should not be copied in the output
+ '/' + this.book.config.path,
+
+ // Structure file to ignore
+ '/' + this.book.summary.path,
+ '/' + this.book.langs.path
+ ]));
+};
+
+// Write a page (parsable file), ex: markdown, etc
+Output.prototype.onPage = function(page) {
+ return page.toHTML(this);
+};
+
+// Copy an asset file (non-parsable), ex: images, etc
+Output.prototype.onAsset = function(filename) {
+
+};
+
+// Finish the generation
+Output.prototype.finish = function() {
+
+};
+
+// Resolve an HTML link
+Output.prototype.onRelativeLink = function(currentPage, href) {
+ var to = currentPage.followPage(href);
+
+ // Replace by an .html link
+ if (to) {
+ href = to.path;
+
+ // Change README path to be "index.html"
+ if (href == this.book.readme.path) {
+ href = 'index.html';
+ }
+
+ // Recalcul as relative link
+ href = currentPage.relative(href);
+
+ // Replace .md by .html
+ href = this.toURL(href);
+ }
+
+ return href;
+};
+
+// Output a SVG buffer as a file
+Output.prototype.onOutputSVG = function(page, svg) {
+ return null;
+};
+
+// Output an image as a file
+// Normalize the relative link
+Output.prototype.onOutputImage = function(page, imgFile) {
+ if (location.isExternal(imgFile)) {
+ return imgFile;
+ }
+
+ imgFile = page.resolveLocal(imgFile);
+ return page.relative(imgFile);
+};
+
+// Read a template by its source URL
+Output.prototype.onGetTemplate = function(sourceUrl) {
+ throw new Error('template not found '+sourceUrl);
+};
+
+// Generate a source URL for a template
+Output.prototype.onResolveTemplate = function(from, to) {
+ return path.resolve(path.dirname(from), to);
+};
+
+// Prepare output for a language book
+Output.prototype.onLanguageBook = function(book) {
+ return new this.constructor(book, this.opts, this);
+};
+
+
+// ---- Utilities ----
+
+// Return conetxt for the output itself
+Output.prototype.getSelfContext = function() {
+ return {
+ name: this.name
+ };
+};
+
+// Return a default context for templates
+Output.prototype.getContext = function() {
+ var ctx = _.extend(
+ {
+ output: this.getSelfContext()
+ },
+ this.book.getContext(),
+ (this.book.isLanguageBook()? this.book.parent: this.book).langs.getContext(),
+ this.book.readme.getContext(),
+ this.book.summary.getContext(),
+ this.book.glossary.getContext(),
+ this.book.config.getContext(),
+ gitbook.getContext()
+ );
+
+ // Deprecated fields
+ error.deprecateField(ctx.gitbook, 'generator', this.name, '"gitbook.generator" property is deprecated, use "output.name" instead');
+
+ return ctx;
+};
+
+// Resolve a file path in the context of a specific page
+// Result is an "absolute path relative to the output folder"
+Output.prototype.resolveForPage = function(page, href) {
+ if (_.isString(page)) page = this.book.getPage(page);
+
+ href = page.relative(href);
+ return this.onRelativeLink(page, href);
+};
+
+// Filename for output
+// READMEs are replaced by index.html
+// /test/README.md -> /test/index.html
+Output.prototype.outputPath = function(filename, ext) {
+ ext = ext || this.defaultExtension;
+ var output = filename;
+
+ if (
+ path.basename(filename, path.extname(filename)) == 'README' ||
+ output == this.book.readme.path
+ ) {
+ output = path.join(path.dirname(output), 'index'+ext);
+ } else {
+ output = pathUtil.setExtension(output, ext);
+ }
+
+ return output;
+};
+
+// Filename for output
+// /test/index.html -> /test/
+Output.prototype.toURL = function(filename, ext) {
+ var href = this.outputPath(filename, ext);
+
+ if (path.basename(href) == 'index.html' && this.opts.directoryIndex) {
+ href = path.dirname(href) + '/';
+ }
+
+ return location.normalize(href);
+};
+
+module.exports = Output;