summaryrefslogtreecommitdiffstats
path: root/lib/output
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/output
parent87db7cf1d412fa6fbd18e9a7e4f4755f2c0c5547 (diff)
downloadgitbook-4336fdb2414d460ffee68a0cc87c0cb0c85cf56e.zip
gitbook-4336fdb2414d460ffee68a0cc87c0cb0c85cf56e.tar.gz
gitbook-4336fdb2414d460ffee68a0cc87c0cb0c85cf56e.tar.bz2
Base
Diffstat (limited to 'lib/output')
-rw-r--r--lib/output/assets-inliner.js140
-rw-r--r--lib/output/base.js309
-rw-r--r--lib/output/conrefs.js67
-rw-r--r--lib/output/ebook.js193
-rw-r--r--lib/output/folder.js152
-rw-r--r--lib/output/generateBook.js42
-rw-r--r--lib/output/generatePage.js9
-rw-r--r--lib/output/generators/json.js26
-rw-r--r--lib/output/index.js11
-rw-r--r--lib/output/json.js47
-rw-r--r--lib/output/modifiers/addHeadingId.js9
-rw-r--r--lib/output/modifiers/htmlTransform.js16
-rw-r--r--lib/output/modifiers/index.js9
-rw-r--r--lib/output/modifiers/inlineAssets.js11
-rw-r--r--lib/output/modifiers/svgToImg.js28
-rw-r--r--lib/output/website/index.js225
-rw-r--r--lib/output/website/templateEnv.js95
-rw-r--r--lib/output/website/themeLoader.js127
18 files changed, 161 insertions, 1355 deletions
diff --git a/lib/output/assets-inliner.js b/lib/output/assets-inliner.js
deleted file mode 100644
index 6f1f02d..0000000
--- a/lib/output/assets-inliner.js
+++ /dev/null
@@ -1,140 +0,0 @@
-var util = require('util');
-var path = require('path');
-var crc = require('crc');
-
-var FolderOutput = require('./folder')();
-var Promise = require('../utils/promise');
-var fs = require('../utils/fs');
-var imagesUtil = require('../utils/images');
-var location = require('../utils/location');
-
-var DEFAULT_ASSETS_FOLDER = 'assets';
-
-/*
-Mixin to inline all the assets in a book:
- - Outline <svg> tags
- - Download remote images
- - Convert .svg images as png
-*/
-
-module.exports = function assetsInliner(Base) {
- Base = Base || FolderOutput;
-
- function AssetsInliner() {
- Base.apply(this, arguments);
-
- // Map of svg already converted
- this.svgs = {};
- this.inlineSvgs = {};
-
- // Map of images already downloaded
- this.downloaded = {};
- }
- util.inherits(AssetsInliner, Base);
-
- // Output a SVG buffer as a file
- AssetsInliner.prototype.onOutputSVG = function(page, svg) {
- this.log.debug.ln('output svg from', page.path);
-
- // Convert svg buffer to a png file
- return this.convertSVGBuffer(svg)
-
- // Return relative path from the page
- .then(function(filename) {
- return page.relative('/' + filename);
- });
- };
-
-
- // Output an image as a file
- AssetsInliner.prototype.onOutputImage = function(page, src) {
- var that = this;
-
- return Promise()
-
- // Download file if external
- .then(function() {
- if (!location.isExternal(src)) return;
-
- return that.downloadAsset(src)
- .then(function(_asset) {
- src = '/' + _asset;
- });
-
- })
- .then(function() {
- // Resolve src to a relative filepath to the book's root
- src = page.resolveLocal(src);
-
- // Already a PNG/JPG/.. ?
- if (path.extname(src).toLowerCase() != '.svg') {
- return src;
- }
-
- // Convert SVG to PNG
- return that.convertSVGFile(that.resolve(src));
- })
-
- // Return relative path from the page
- .then(function(filename) {
- return page.relative(filename);
- });
- };
-
- // Download an asset if not already download; returns the output file
- AssetsInliner.prototype.downloadAsset = function(src) {
- if (this.downloaded[src]) return Promise(this.downloaded[src]);
-
- var that = this;
- var ext = path.extname(src);
- var hash = crc.crc32(src).toString(16);
-
- // Create new file
- return this.createNewFile(DEFAULT_ASSETS_FOLDER, hash + ext)
- .then(function(filename) {
- that.downloaded[src] = filename;
-
- that.log.debug.ln('downloading asset', src);
- return fs.download(src, that.resolve(filename))
- .thenResolve(filename);
- });
- };
-
- // Convert a .svg into an .png
- // Return the output filename for the .png
- AssetsInliner.prototype.convertSVGFile = function(src) {
- if (this.svgs[src]) return Promise(this.svgs[src]);
-
- var that = this;
- var hash = crc.crc32(src).toString(16);
-
- // Create new file
- return this.createNewFile(DEFAULT_ASSETS_FOLDER, hash + '.png')
- .then(function(filename) {
- that.svgs[src] = filename;
-
- return imagesUtil.convertSVGToPNG(src, that.resolve(filename))
- .thenResolve(filename);
- });
- };
-
- // Convert an inline svg into an .png
- // Return the output filename for the .png
- AssetsInliner.prototype.convertSVGBuffer = function(buf) {
- var that = this;
- var hash = crc.crc32(buf).toString(16);
-
- // Already converted?
- if (this.inlineSvgs[hash]) return Promise(this.inlineSvgs[hash]);
-
- return this.createNewFile(DEFAULT_ASSETS_FOLDER, hash + '.png')
- .then(function(filename) {
- that.inlineSvgs[hash] = filename;
-
- return imagesUtil.convertSVGBufferToPNG(buf, that.resolve(filename))
- .thenResolve(filename);
- });
- };
-
- return AssetsInliner;
-};
diff --git a/lib/output/base.js b/lib/output/base.js
deleted file mode 100644
index 868b85b..0000000
--- a/lib/output/base.js
+++ /dev/null
@@ -1,309 +0,0 @@
-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;
diff --git a/lib/output/conrefs.js b/lib/output/conrefs.js
deleted file mode 100644
index e58f836..0000000
--- a/lib/output/conrefs.js
+++ /dev/null
@@ -1,67 +0,0 @@
-var path = require('path');
-var util = require('util');
-
-var folderOutput = require('./folder');
-var Git = require('../utils/git');
-var fs = require('../utils/fs');
-var pathUtil = require('../utils/path');
-var location = require('../utils/location');
-
-/*
-Mixin for output to resolve git conrefs
-*/
-
-module.exports = function conrefsLoader(Base) {
- Base = folderOutput(Base);
-
- function ConrefsLoader() {
- Base.apply(this, arguments);
-
- this.git = new Git();
- }
- util.inherits(ConrefsLoader, Base);
-
- // Read a template by its source URL
- ConrefsLoader.prototype.onGetTemplate = function(sourceURL) {
- var that = this;
-
- return this.git.resolve(sourceURL)
- .then(function(filepath) {
- // Is local file
- if (!filepath) {
- filepath = that.book.resolve(sourceURL);
- } else {
- that.book.log.debug.ln('resolve from git', sourceURL, 'to', filepath);
- }
-
- // Read file from absolute path
- return fs.readFile(filepath)
- .then(function(source) {
- return {
- src: source.toString('utf8'),
- path: filepath
- };
- });
- });
- };
-
- // Generate a source URL for a template
- ConrefsLoader.prototype.onResolveTemplate = function(from, to) {
- // If origin is in the book, we enforce result file to be in the book
- if (this.book.isInBook(from)) {
- var href = location.toAbsolute(to, path.dirname(from), '');
- return this.book.resolve(href);
- }
-
- // If origin is in a git repository, we resolve file in the git repository
- var gitRoot = this.git.resolveRoot(from);
- if (gitRoot) {
- return pathUtil.resolveInRoot(gitRoot, to);
- }
-
- // If origin is not in the book (include from a git content ref)
- return path.resolve(path.dirname(from), to);
- };
-
- return ConrefsLoader;
-};
diff --git a/lib/output/ebook.js b/lib/output/ebook.js
deleted file mode 100644
index 2b8fac9..0000000
--- a/lib/output/ebook.js
+++ /dev/null
@@ -1,193 +0,0 @@
-var _ = require('lodash');
-var util = require('util');
-var juice = require('juice');
-
-var command = require('../utils/command');
-var fs = require('../utils/fs');
-var Promise = require('../utils/promise');
-var error = require('../utils/error');
-var WebsiteOutput = require('./website');
-var assetsInliner = require('./assets-inliner');
-
-function _EbookOutput() {
- WebsiteOutput.apply(this, arguments);
-
- // ebook-convert does not support link like "./"
- this.opts.directoryIndex = false;
-}
-util.inherits(_EbookOutput, WebsiteOutput);
-
-var EbookOutput = assetsInliner(_EbookOutput);
-
-EbookOutput.prototype.name = 'ebook';
-
-// Return context for templating
-// Incldue type of ebbook generated
-EbookOutput.prototype.getSelfContext = function() {
- var ctx = EbookOutput.super_.prototype.getSelfContext.apply(this);
- ctx.format = this.opts.format;
-
- return ctx;
-};
-
-// Finish generation, create ebook using ebook-convert
-EbookOutput.prototype.finish = function() {
- var that = this;
- if (that.book.isMultilingual()) {
- return EbookOutput.super_.prototype.finish.apply(that);
- }
-
- return Promise()
- .then(function() {
- return EbookOutput.super_.prototype.finish.apply(that);
- })
-
- // Generate SUMMARY.html
- .then(function() {
- return that.render('summary', 'SUMMARY.html', that.getContext());
- })
-
- // Start ebook-convert
- .then(function() {
- return that.ebookConvertOption();
- })
-
- .then(function(options) {
- if (!that.opts.format) return;
-
- var cmd = [
- 'ebook-convert',
- that.resolve('SUMMARY.html'),
- that.resolve('index.'+that.opts.format),
- command.optionsToShellArgs(options)
- ].join(' ');
-
- return command.exec(cmd)
- .progress(function(data) {
- that.book.log.debug(data);
- })
- .fail(function(err) {
- if (err.code == 127) {
- throw error.RequireInstallError({
- cmd: 'ebook-convert',
- install: 'Install it from Calibre: https://calibre-ebook.com'
- });
- }
-
- throw error.EbookError(err);
- });
- });
-};
-
-// Generate header/footer for PDF
-EbookOutput.prototype.getPDFTemplate = function(tpl) {
- var that = this;
- var context = _.extend(
- {
- // Nunjucks context mapping to ebook-convert templating
- page: {
- num: '_PAGENUM_',
- title: '_TITLE_',
- section: '_SECTION_'
- }
- },
- this.getContext()
- );
-
- return this.renderAsString('pdf_'+tpl, context)
-
- // Inline css, include css relative to the output folder
- .then(function(output) {
- return Promise.nfcall(juice.juiceResources, output, {
- webResources: {
- relativeTo: that.root()
- }
- });
- });
-};
-
-// Locate the cover file to use
-// Use configuration or search a "cover.jpg" file
-// For multi-lingual book, it can use the one from the main book
-EbookOutput.prototype.locateCover = function() {
- var cover = this.book.config.get('cover', 'cover.jpg');
-
- // Resolve to absolute
- cover = this.resolve(cover);
-
- // Cover doesn't exist and multilingual?
- if (!fs.existsSync(cover)) {
- if (this.parent) return this.parent.locateCover();
- else return undefined;
- }
-
- return cover;
-};
-
-// Generate options for ebook-convert
-EbookOutput.prototype.ebookConvertOption = function() {
- var that = this;
-
- var options = {
- '--cover': this.locateCover(),
- '--title': that.book.config.get('title'),
- '--comments': that.book.config.get('description'),
- '--isbn': that.book.config.get('isbn'),
- '--authors': that.book.config.get('author'),
- '--language': that.book.config.get('language'),
- '--book-producer': 'GitBook',
- '--publisher': 'GitBook',
- '--chapter': 'descendant-or-self::*[contains(concat(\' \', normalize-space(@class), \' \'), \' book-chapter \')]',
- '--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.opts.format == 'epub') {
- options = _.extend(options, {
- '--dont-split-on-page-breaks': true
- });
- }
-
- if (that.opts.format != 'pdf') return Promise(options);
-
- var pdfOptions = that.book.config.get('pdf');
-
- options = _.extend(options, {
- '--chapter-mark': String(pdfOptions.chapterMark),
- '--page-breaks-before': String(pdfOptions.pageBreaksBefore),
- '--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': that.getPDFTemplate('header'),
- '--pdf-footer-template': that.getPDFTemplate('footer'),
- '--pdf-sans-family': String(pdfOptions.fontFamily)
- });
-
- return that.getPDFTemplate('header')
- .then(function(tpl) {
- options['--pdf-header-template'] = tpl;
-
- return that.getPDFTemplate('footer');
- })
- .then(function(tpl) {
- options['--pdf-footer-template'] = tpl;
-
- return options;
- });
-};
-
-// Don't write multi-lingual index for wbook
-EbookOutput.prototype.outputMultilingualIndex = function() {
-
-};
-
-module.exports = EbookOutput;
diff --git a/lib/output/folder.js b/lib/output/folder.js
deleted file mode 100644
index 8303ed2..0000000
--- a/lib/output/folder.js
+++ /dev/null
@@ -1,152 +0,0 @@
-var _ = require('lodash');
-var util = require('util');
-var path = require('path');
-
-var Output = require('./base');
-var fs = require('../utils/fs');
-var pathUtil = require('../utils/path');
-var Promise = require('../utils/promise');
-
-/*
-This output requires the native fs module to output
-book as a directory (mapping assets and pages)
-*/
-
-module.exports = function folderOutput(Base) {
- Base = Base || Output;
-
- function FolderOutput() {
- Base.apply(this, arguments);
-
- this.opts.root = path.resolve(this.opts.root || this.book.resolve('_book'));
- }
- util.inherits(FolderOutput, Base);
-
- // Copy an asset file (non-parsable), ex: images, etc
- FolderOutput.prototype.onAsset = function(filename) {
- return this.copyFile(
- this.book.resolve(filename),
- filename
- );
- };
-
- // Prepare the generation by creating the output folder
- FolderOutput.prototype.prepare = function() {
- var that = this;
-
- return Promise()
- .then(function() {
- return FolderOutput.super_.prototype.prepare.apply(that);
- })
-
- // Cleanup output folder
- .then(function() {
- that.log.debug.ln('removing previous output directory');
- return fs.rmDir(that.root())
- .fail(function() {
- return Promise();
- });
- })
-
- // Create output folder
- .then(function() {
- that.log.debug.ln('creating output directory');
- return fs.mkdirp(that.root());
- })
-
- // Add output folder to ignored files
- .then(function() {
- that.ignore.addPattern([
- path.relative(that.book.root, that.root())
- ]);
- });
- };
-
- // Prepare output for a language book
- FolderOutput.prototype.onLanguageBook = function(book) {
- return new this.constructor(book, _.extend({}, this.opts, {
-
- // Language output should be output in sub-directory of output
- root: path.resolve(this.root(), book.language)
- }), this);
- };
-
- // ----- Utility methods -----
-
- // Return path to the root folder
- FolderOutput.prototype.root = function() {
- return this.opts.root;
- };
-
- // Resolve a file in the output directory
- FolderOutput.prototype.resolve = function(filename) {
- return pathUtil.resolveInRoot.apply(null, [this.root()].concat(_.toArray(arguments)));
- };
-
- // Copy a file to the output
- FolderOutput.prototype.copyFile = function(from, to) {
- var that = this;
-
- return Promise()
- .then(function() {
- to = that.resolve(to);
- var folder = path.dirname(to);
-
- // Ensure folder exists
- return fs.mkdirp(folder);
- })
- .then(function() {
- return fs.copy(from, to);
- });
- };
-
- // Write a file/buffer to the output folder
- FolderOutput.prototype.writeFile = function(filename, buf) {
- var that = this;
-
- return Promise()
- .then(function() {
- filename = that.resolve(filename);
- var folder = path.dirname(filename);
-
- // Ensure folder exists
- return fs.mkdirp(folder);
- })
-
- // Write the file
- .then(function() {
- return fs.writeFile(filename, buf);
- });
- };
-
- // Return true if a file exists in the output folder
- FolderOutput.prototype.hasFile = function(filename) {
- var that = this;
-
- return Promise()
- .then(function() {
- return fs.exists(that.resolve(filename));
- });
- };
-
- // Create a new unique file
- // Returns its filename
- FolderOutput.prototype.createNewFile = function(base, filename) {
- var that = this;
-
- if (!filename) {
- filename = path.basename(filename);
- base = path.dirname(base);
- }
-
- return fs.uniqueFilename(this.resolve(base), filename)
- .then(function(out) {
- out = path.join(base, out);
-
- return fs.ensure(that.resolve(out))
- .thenResolve(out);
- });
- };
-
- return FolderOutput;
-};
diff --git a/lib/output/generateBook.js b/lib/output/generateBook.js
new file mode 100644
index 0000000..3f04875
--- /dev/null
+++ b/lib/output/generateBook.js
@@ -0,0 +1,42 @@
+var Parse = require('../parse');
+
+/**
+ List all assets for a book
+
+ @param {Book} book
+ @param {Map<String:Page>} pages
+ @param
+*/
+function listAssets(book, pages) {
+ var fs = book.getContentFS();
+
+ return fs.listAllFiles()
+ .then(function(files) {
+ return files.filterNot(function(file) {
+ return (
+ book.isContentFileIgnored(file) ||
+ pages.has(file)
+ );
+ });
+ });
+}
+
+
+/**
+ Generate a book using a generator
+
+ @param {Generator} generator
+ @param {Book} book
+
+ @return {Promise}
+*/
+function generateBook(generator, book) {
+ // List all parsable pages
+ return Parse.parsePagesList(book)
+ .then(function(pages) {
+ return listAssets(book, pages);
+ });
+}
+
+
+module.exports = generateBook;
diff --git a/lib/output/generatePage.js b/lib/output/generatePage.js
new file mode 100644
index 0000000..9afb50a
--- /dev/null
+++ b/lib/output/generatePage.js
@@ -0,0 +1,9 @@
+/**
+ Generate a page using a generator
+*/
+function generatePage(generator, book, page) {
+
+}
+
+
+module.exports = generatePage;
diff --git a/lib/output/generators/json.js b/lib/output/generators/json.js
new file mode 100644
index 0000000..e75ae7c
--- /dev/null
+++ b/lib/output/generators/json.js
@@ -0,0 +1,26 @@
+var Promise = require('../../utils/promise');
+
+var Modifier = require('../');
+
+
+function JSONGenerator(book) {
+ this.book = book;
+}
+
+
+JSONGenerator.prototype.onPage = function(page) {
+ return Modifier.HTMLTransformations(page, [
+ Modifier.svgToImg(),
+ Modifier.svgToPng()
+ ])
+ .then(function() {
+
+
+ });
+};
+
+JSONGenerator.prototype.onAsset = function(file) {
+
+};
+
+module.exports = JSONGenerator; \ No newline at end of file
diff --git a/lib/output/index.js b/lib/output/index.js
new file mode 100644
index 0000000..67ca5ee
--- /dev/null
+++ b/lib/output/index.js
@@ -0,0 +1,11 @@
+
+
+function generate(book, output) {
+
+}
+
+
+
+module.exports = {
+ generate: generate
+};
diff --git a/lib/output/json.js b/lib/output/json.js
deleted file mode 100644
index 7061141..0000000
--- a/lib/output/json.js
+++ /dev/null
@@ -1,47 +0,0 @@
-var conrefsLoader = require('./conrefs');
-
-var JSONOutput = conrefsLoader();
-
-JSONOutput.prototype.name = 'json';
-
-// Don't copy asset on JSON output
-JSONOutput.prototype.onAsset = function(filename) {};
-
-// Write a page (parsable file)
-JSONOutput.prototype.onPage = function(page) {
- var that = this;
-
- // Parse the page
- return page.toHTML(this)
-
- // Write as json
- .then(function() {
- var json = page.getOutputContext(that);
-
- // Delete some private properties
- delete json.config;
-
- // Specify JSON output version
- json.version = '3';
-
- return that.writeFile(
- page.withExtension('.json'),
- JSON.stringify(json, null, 4)
- );
- });
-};
-
-// At the end of generation, generate README.json for multilingual books
-JSONOutput.prototype.finish = function() {
- if (!this.book.isMultilingual()) return;
-
- // Copy README.json from main book
- var mainLanguage = this.book.langs.getDefault().id;
- return this.copyFile(
- this.resolve(mainLanguage, 'README.json'),
- 'README.json'
- );
-};
-
-
-module.exports = JSONOutput;
diff --git a/lib/output/modifiers/addHeadingId.js b/lib/output/modifiers/addHeadingId.js
new file mode 100644
index 0000000..751f4b8
--- /dev/null
+++ b/lib/output/modifiers/addHeadingId.js
@@ -0,0 +1,9 @@
+var slug = require('github-slugid');
+var HTMLModifier = require('./html');
+
+var addHeadingID = HTMLModifier('h1,h2,h3,h4,h5,h6', function(heading) {
+ if (heading.attr('id')) return;
+ heading.attr('id', slug(heading.text()));
+});
+
+module.exports = addHeadingID;
diff --git a/lib/output/modifiers/htmlTransform.js b/lib/output/modifiers/htmlTransform.js
new file mode 100644
index 0000000..528b08d
--- /dev/null
+++ b/lib/output/modifiers/htmlTransform.js
@@ -0,0 +1,16 @@
+var Promise = require('../../utils/promise');
+
+/**
+
+
+*/
+function transformTags() {
+ var $elements = $(query);
+
+ return Promise.serie($elements, function(el) {
+ var $el = that.$(el);
+ return fn.call(that, $el);
+ });
+}
+
+module.exports = transformTags;
diff --git a/lib/output/modifiers/index.js b/lib/output/modifiers/index.js
new file mode 100644
index 0000000..76ce3c2
--- /dev/null
+++ b/lib/output/modifiers/index.js
@@ -0,0 +1,9 @@
+
+
+function modifyPage() {
+
+
+}
+
+
+module.exports = modifyPage;
diff --git a/lib/output/modifiers/inlineAssets.js b/lib/output/modifiers/inlineAssets.js
new file mode 100644
index 0000000..190a945
--- /dev/null
+++ b/lib/output/modifiers/inlineAssets.js
@@ -0,0 +1,11 @@
+
+
+/**
+
+*/
+function inlineAssets() {
+
+
+}
+
+module.exports = inlineAssets;
diff --git a/lib/output/modifiers/svgToImg.js b/lib/output/modifiers/svgToImg.js
new file mode 100644
index 0000000..b36770a
--- /dev/null
+++ b/lib/output/modifiers/svgToImg.js
@@ -0,0 +1,28 @@
+var cheerio = require('cheerio');
+var domSerializer = require('dom-serializer');
+
+// 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);
+}
+
+/**
+
+*/
+var svgToImg = HTMLModifier('svg', function($svg, $) {
+ var content = '<?xml version="1.0" encoding="UTF-8"?>' + renderDOM($, $svg);
+
+
+
+});
+
+function svgToImg(page) {
+ var $ = cheerio.load(page.content);
+
+}
+
+module.exports = svgToImg;
diff --git a/lib/output/website/index.js b/lib/output/website/index.js
deleted file mode 100644
index 0a8618c..0000000
--- a/lib/output/website/index.js
+++ /dev/null
@@ -1,225 +0,0 @@
-var _ = require('lodash');
-var path = require('path');
-var util = require('util');
-var I18n = require('i18n-t');
-
-var Promise = require('../../utils/promise');
-var location = require('../../utils/location');
-var fs = require('../../utils/fs');
-var conrefsLoader = require('../conrefs');
-var Output = require('../base');
-var setupTemplateEnv = require('./templateEnv');
-
-function _WebsiteOutput() {
- Output.apply(this, arguments);
-
- // Nunjucks environment
- this.env;
-
- // Plugin instance for the main theme
- this.theme;
-
- // Plugin instance for the default theme
- this.defaultTheme;
-
- // Resources loaded from plugins
- this.resources;
-
- // i18n for themes
- this.i18n = new I18n();
-}
-util.inherits(_WebsiteOutput, Output);
-
-var WebsiteOutput = conrefsLoader(_WebsiteOutput);
-
-// Name of the generator
-// It's being used as a prefix for templates
-WebsiteOutput.prototype.name = 'website';
-
-// Load and setup the theme
-WebsiteOutput.prototype.prepare = function() {
- var that = this;
-
- return Promise()
- .then(function() {
- return WebsiteOutput.super_.prototype.prepare.apply(that);
- })
-
- .then(function() {
- // This list is ordered to give priority to templates in the book
- var searchPaths = _.pluck(that.plugins.list(), 'root');
-
- // The book itself can contains a "_layouts" folder
- searchPaths.unshift(that.book.root);
-
- // Load i18n
- _.each(searchPaths.concat().reverse(), function(searchPath) {
- var i18nRoot = path.resolve(searchPath, '_i18n');
-
- if (!fs.existsSync(i18nRoot)) return;
- that.i18n.load(i18nRoot);
- });
-
- that.searchPaths = searchPaths;
- })
-
- // Copy assets from themes before copying files from book
- .then(function() {
- if (that.book.isLanguageBook()) return;
-
- // Assets from the book are already copied
- // Copy assets from plugins (start with default plugins)
- return Promise.serie(that.plugins.list().reverse(), function(plugin) {
- // Copy assets only if exists (don't fail otherwise)
- var assetFolder = path.join(plugin.root, '_assets', that.name);
- if (!fs.existsSync(assetFolder)) return;
-
- that.log.debug.ln('copy assets from theme', assetFolder);
- return fs.copyDir(
- assetFolder,
- that.resolve('gitbook'),
- {
- deleteFirst: false,
- overwrite: true,
- confirm: true
- }
- );
- });
- })
-
- // Load resources for plugins
- .then(function() {
- return that.plugins.getResources(that.name)
- .then(function(resources) {
- that.resources = resources;
- });
- });
-};
-
-// Write a page (parsable file)
-WebsiteOutput.prototype.onPage = function(page) {
- var that = this;
-
- // Parse the page
- return page.toHTML(this)
-
- // Render the page template with the same context as the json output
- .then(function() {
- return that.render('page', that.outputPath(page.path), page.getOutputContext(that));
- });
-};
-
-// Finish generation, create ebook using ebook-convert
-WebsiteOutput.prototype.finish = function() {
- var that = this;
-
- return Promise()
- .then(function() {
- return WebsiteOutput.super_.prototype.finish.apply(that);
- })
-
- // Copy assets from plugins
- .then(function() {
- if (that.book.isLanguageBook()) return;
- return that.plugins.copyResources(that.name, that.resolve('gitbook'));
- })
-
- // Generate homepage to select languages
- .then(function() {
- if (!that.book.isMultilingual()) return;
- return that.outputMultilingualIndex();
- });
-};
-
-// ----- Utilities ----
-
-// Write multi-languages index
-WebsiteOutput.prototype.outputMultilingualIndex = function() {
- var that = this;
-
- return that.render('languages', 'index.html', that.getContext());
-};
-
-/*
- Render a template as an HTML string
- Templates are stored in `_layouts` folders
-
-
- @param {String} tpl: template name (ex: "page")
- @param {String} outputFile: filename to write, relative to the output
- @param {Object} context: context for the page
- @return {Promise}
-*/
-WebsiteOutput.prototype.renderAsString = function(tpl, context) {
- // Calcul template name
- var filename = this.templateName(tpl);
-
- context = _.extend(context, {
- plugins: {
- resources: this.resources
- },
-
- options: this.opts
- });
-
- // Create environment
- var env = setupTemplateEnv(this, context);
-
- return Promise.nfcall(env.render.bind(env), filename, context);
-};
-
-/*
- Render a template using nunjucks
- Templates are stored in `_layouts` folders
-
-
- @param {String} tpl: template name (ex: "page")
- @param {String} outputFile: filename to write, relative to the output
- @param {Object} context: context for the page
- @return {Promise}
-*/
-WebsiteOutput.prototype.render = function(tpl, outputFile, context) {
- var that = this;
-
- // Calcul relative path to the root
- var outputDirName = path.dirname(outputFile);
- var basePath = location.normalize(path.relative(outputDirName, './'));
-
- // Setup complete context
- context = _.extend(context, {
- basePath: basePath,
-
- template: {
- getJSContext: function() {
- return {
- page: _.omit(context.page, 'content'),
- config: context.config,
- file: context.file,
- gitbook: context.gitbook,
- basePath: basePath,
- book: {
- language: context.book.language
- }
- };
- }
- }
- });
-
- return this.renderAsString(tpl, context)
- .then(function(html) {
- return that.writeFile(
- outputFile,
- html
- );
- });
-};
-
-// Return a complete name for a template
-WebsiteOutput.prototype.templateName = function(name) {
- return path.join(this.name, name+'.html');
-};
-
-module.exports = WebsiteOutput;
-
-
-
diff --git a/lib/output/website/templateEnv.js b/lib/output/website/templateEnv.js
deleted file mode 100644
index d385108..0000000
--- a/lib/output/website/templateEnv.js
+++ /dev/null
@@ -1,95 +0,0 @@
-var _ = require('lodash');
-var nunjucks = require('nunjucks');
-var path = require('path');
-var fs = require('fs');
-var DoExtension = require('nunjucks-do')(nunjucks);
-
-
-var location = require('../../utils/location');
-var defaultFilters = require('../../template/filters');
-
-var ThemeLoader = require('./themeLoader');
-
-// Directory for a theme with the templates
-function templatesPath(dir) {
- return path.join(dir, '_layouts');
-}
-
-/*
- Create and setup at Nunjucks template environment
-
- @return {Nunjucks.Environment}
-*/
-function setupTemplateEnv(output, context) {
- context = _.defaults(context || {}, {
- // Required by ThemeLoader
- template: {}
- });
-
- var loader = new ThemeLoader(
- _.map(output.searchPaths, templatesPath)
- );
- var env = new nunjucks.Environment(loader);
-
- env.addExtension('DoExtension', new DoExtension());
-
- // Add context as global
- _.each(context, function(value, key) {
- env.addGlobal(key, value);
- });
-
- // Add GitBook default filters
- _.each(defaultFilters, function(fn, filter) {
- env.addFilter(filter, fn);
- });
-
- // Translate using _i18n locales
- env.addFilter('t', function t(s) {
- return output.i18n.t(output.book.config.get('language'), s);
- });
-
- // Transform an absolute path into a relative path
- // using this.ctx.page.path
- env.addFilter('resolveFile', function resolveFile(href) {
- return location.normalize(output.resolveForPage(context.file.path, href));
- });
-
- // Test if a file exists
- env.addFilter('fileExists', function fileExists(href) {
- return fs.existsSync(output.resolve(href));
- });
-
- // Transform a '.md' into a '.html' (README -> index)
- env.addFilter('contentURL', function contentURL(s) {
- return output.toURL(s);
- });
-
- // Get an article using its path
- env.addFilter('getArticleByPath', function getArticleByPath(s) {
- var article = output.book.summary.getArticle(s);
- if (!article) return undefined;
-
- return article.getContext();
- });
-
- // Relase path to an asset
- env.addFilter('resolveAsset', function resolveAsset(href) {
- href = path.join('gitbook', href);
-
- // Resolve for current file
- if (context.file) {
- href = output.resolveForPage(context.file.path, '/' + href);
- }
-
- // Use assets from parent
- if (output.book.isLanguageBook()) {
- href = path.join('../', href);
- }
-
- return location.normalize(href);
- });
-
- return env;
-}
-
-module.exports = setupTemplateEnv;
diff --git a/lib/output/website/themeLoader.js b/lib/output/website/themeLoader.js
deleted file mode 100644
index 774a39e..0000000
--- a/lib/output/website/themeLoader.js
+++ /dev/null
@@ -1,127 +0,0 @@
-var _ = require('lodash');
-var fs = require('fs');
-var path = require('path');
-var nunjucks = require('nunjucks');
-
-/*
- Nunjucks loader similar to FileSystemLoader, but avoid infinite looping
-*/
-
-/*
- Return true if a filename is relative.
-*/
-function isRelative(filename) {
- return (filename.indexOf('./') === 0 || filename.indexOf('../') === 0);
-}
-
-var ThemeLoader = nunjucks.Loader.extend({
- init: function(searchPaths) {
- this.searchPaths = _.map(searchPaths, path.normalize);
- },
-
- /*
- Read source of a resolved filepath
-
- @param {String}
- @return {Object}
- */
- getSource: function(fullpath) {
- if (!fullpath) return null;
-
- fullpath = this.resolve(null, fullpath);
- var templateName = this.getTemplateName(fullpath);
-
- if(!fullpath) {
- return null;
- }
-
- var src = fs.readFileSync(fullpath, 'utf-8');
-
- src = '{% do %}var template = template || {}; template.stack = template.stack || []; template.stack.push(template.self); template.self = ' + JSON.stringify(templateName) + '{% enddo %}\n' +
- src +
- '\n{% do %}template.self = template.stack.pop();{% enddo %}';
-
- return {
- src: src,
- path: fullpath,
- noCache: true
- };
- },
-
- /*
- Nunjucks calls "isRelative" to determine when to call "resolve".
- We handle absolute paths ourselves in ".resolve" so we always return true
- */
- isRelative: function() {
- return true;
- },
-
- /*
- Get original search path containing a template
-
- @param {String} filepath
- @return {String} searchPath
- */
- getSearchPath: function(filepath) {
- return _.chain(this.searchPaths)
- .sortBy(function(s) {
- return -s.length;
- })
- .find(function(basePath) {
- return (filepath && filepath.indexOf(basePath) === 0);
- })
- .value();
- },
-
- /*
- Get template name from a filepath
-
- @param {String} filepath
- @return {String} name
- */
- getTemplateName: function(filepath) {
- var originalSearchPath = this.getSearchPath(filepath);
- return originalSearchPath? path.relative(originalSearchPath, filepath) : null;
- },
-
- /*
- Resolve a template from a current template
-
- @param {String|null} from
- @param {String} to
- @return {String|null}
- */
- resolve: function(from, to) {
- var searchPaths = this.searchPaths;
-
- // Relative template like "./test.html"
- if (isRelative(to) && from) {
- return path.resolve(path.dirname(from), to);
- }
-
- // Determine in which search folder we currently are
- var originalSearchPath = this.getSearchPath(from);
- var originalFilename = this.getTemplateName(from);
-
- // If we are including same file from a different search path
- // Slice the search paths to avoid including from previous ones
- if (originalFilename == to) {
- var currentIndex = searchPaths.indexOf(originalSearchPath);
- searchPaths = searchPaths.slice(currentIndex + 1);
- }
-
- // Absolute template to resolve in root folder
- var resultFolder = _.find(searchPaths, function(basePath) {
- var p = path.resolve(basePath, to);
-
- return (
- p.indexOf(basePath) === 0
- && fs.existsSync(p)
- );
- });
- if (!resultFolder) return null;
- return path.resolve(resultFolder, to);
- }
-});
-
-module.exports = ThemeLoader;