summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/api/index.js5
-rw-r--r--lib/json/encodeBook.js21
-rw-r--r--lib/json/encodeBookWithPage.js22
-rw-r--r--lib/json/encodeConfig.js12
-rw-r--r--lib/json/encodeFile.js16
-rw-r--r--lib/json/encodeGlossary.js21
-rw-r--r--lib/json/encodeGlossaryEntry.js16
-rw-r--r--lib/json/encodePage.js30
-rw-r--r--lib/json/encodeReadme.js17
-rw-r--r--lib/json/encodeSummary.js20
-rw-r--r--lib/json/encodeSummaryArticle.js18
-rw-r--r--lib/json/encodeSummaryPart.js17
-rw-r--r--lib/json/index.js10
-rw-r--r--lib/models/file.js14
-rw-r--r--lib/models/page.js9
-rw-r--r--lib/models/summaryArticle.js9
-rw-r--r--lib/output/json/onPage.js19
-rw-r--r--lib/output/modifiers/index.js11
-rw-r--r--lib/output/modifiers/modifyHTML.js15
-rw-r--r--lib/output/writer/index.js4
-rw-r--r--lib/output/writer/writePage.js33
-rw-r--r--lib/output2/assets-inliner.js140
-rw-r--r--lib/output2/base.js309
-rw-r--r--lib/output2/conrefs.js67
-rw-r--r--lib/output2/ebook.js193
-rw-r--r--lib/output2/folder.js152
-rw-r--r--lib/output2/json.js47
-rw-r--r--lib/output2/website/index.js225
-rw-r--r--lib/output2/website/templateEnv.js95
-rw-r--r--lib/output2/website/themeLoader.js127
-rw-r--r--lib/page/html.js290
-rw-r--r--lib/page/index.js246
-rw-r--r--lib/parse/parsePage.js11
-rw-r--r--lib/plugins2/compatibility.js61
-rw-r--r--lib/plugins2/index.js188
-rw-r--r--lib/plugins2/plugin.js288
-rw-r--r--lib/plugins2/registry.js172
-rw-r--r--lib/template/blocks.js36
-rw-r--r--lib/template/filters.js15
-rw-r--r--lib/template/index.js552
-rw-r--r--lib/template/loader.js42
41 files changed, 328 insertions, 3267 deletions
diff --git a/lib/api/index.js b/lib/api/index.js
deleted file mode 100644
index 213ef1a..0000000
--- a/lib/api/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-
-module.exports = {
- encodePage: require('./encodePage'),
- decodePage: require('./decodePage')
-};
diff --git a/lib/json/encodeBook.js b/lib/json/encodeBook.js
new file mode 100644
index 0000000..eed5e5f
--- /dev/null
+++ b/lib/json/encodeBook.js
@@ -0,0 +1,21 @@
+var encodeSummary = require('./encodeSummary');
+var encodeGlossary = require('./encodeGlossary');
+var encodeReadme = require('./encodeReadme');
+var encodeConfig = require('./encodeConfig');
+
+/**
+ Encode a book to JSON
+
+ @param {Book}
+ @return {Object}
+*/
+function encodeBookToJson(book) {
+ return {
+ summary: encodeSummary(book.getSummary()),
+ glossary: encodeGlossary(book.getGlossary()),
+ readme: encodeReadme(book.getReadme()),
+ config: encodeConfig(book.getConfig())
+ };
+}
+
+module.exports = encodeBookToJson;
diff --git a/lib/json/encodeBookWithPage.js b/lib/json/encodeBookWithPage.js
new file mode 100644
index 0000000..26ab174
--- /dev/null
+++ b/lib/json/encodeBookWithPage.js
@@ -0,0 +1,22 @@
+var encodeBook = require('./encodeBook');
+var encodePage = require('./encodePage');
+var encodeFile = require('./encodeFile');
+
+/**
+ Return a JSON representation of a book with a specific file
+
+ @param {Book} output
+ @param {Page} page
+ @return {Object}
+*/
+function encodeBookWithPage(book, page) {
+ var file = page.getFile();
+
+ var result = encodeBook(book);
+ result.page = encodePage(page);
+ result.file = encodeFile(file);
+
+ return result;
+}
+
+module.exports = encodeBookWithPage;
diff --git a/lib/json/encodeConfig.js b/lib/json/encodeConfig.js
new file mode 100644
index 0000000..195d43b
--- /dev/null
+++ b/lib/json/encodeConfig.js
@@ -0,0 +1,12 @@
+/**
+ Encode configuration to JSON
+
+ @param {Config}
+ @return {Object}
+*/
+function encodeConfig(config) {
+ var values = config.getValues();
+ return values.toJS();
+}
+
+module.exports = encodeConfig;
diff --git a/lib/json/encodeFile.js b/lib/json/encodeFile.js
new file mode 100644
index 0000000..03b3450
--- /dev/null
+++ b/lib/json/encodeFile.js
@@ -0,0 +1,16 @@
+
+/**
+ Return a JSON representation of a file
+
+ @param {File} file
+ @return {Object}
+*/
+function encodeFileToJson(file) {
+ return {
+ path: file.getPath(),
+ mtime: file.getMTime(),
+ type: file.getType()
+ };
+}
+
+module.exports = encodeFileToJson;
diff --git a/lib/json/encodeGlossary.js b/lib/json/encodeGlossary.js
new file mode 100644
index 0000000..fcaf4d2
--- /dev/null
+++ b/lib/json/encodeGlossary.js
@@ -0,0 +1,21 @@
+var encodeFile = require('./encodeFileToJson');
+var encodeGlossaryEntry = require('./encodeGlossaryEntry');
+
+/**
+ Encode a glossary to JSON
+
+ @param {Glossary}
+ @return {Object}
+*/
+function encodeGlossary(glossary) {
+ var file = glossary.getFile();
+ var entries = glossary.getEntries();
+
+ return {
+ file: encodeFile(file),
+ entries: entries
+ .map(encodeGlossaryEntry).toJS()
+ };
+}
+
+module.exports = encodeGlossary;
diff --git a/lib/json/encodeGlossaryEntry.js b/lib/json/encodeGlossaryEntry.js
new file mode 100644
index 0000000..d163f45
--- /dev/null
+++ b/lib/json/encodeGlossaryEntry.js
@@ -0,0 +1,16 @@
+
+/**
+ Encode a SummaryArticle to JSON
+
+ @param {GlossaryEntry}
+ @return {Object}
+*/
+function encodeGlossaryEntry(entry) {
+ return {
+ id: entry.getID(),
+ name: entry.getName(),
+ description: entry.getDescription()
+ };
+}
+
+module.exports = encodeGlossaryEntry;
diff --git a/lib/json/encodePage.js b/lib/json/encodePage.js
new file mode 100644
index 0000000..d876c78
--- /dev/null
+++ b/lib/json/encodePage.js
@@ -0,0 +1,30 @@
+/**
+ Return a JSON representation of a page
+
+ @param {Page} page
+ @param {Summary} summary
+ @return {Object}
+*/
+function encodePage(page, summary) {
+ var file = page.getFile();
+ var attributes = page.getAttributes();
+ var article = summary.getByPath(file.getPath());
+
+
+ var result = attributes.toJS();
+
+ if (article) {
+ result.title = article.getTitle();
+ result.level = article.getLevel();
+ result.depth = article.getDepth();
+
+ // todo: next and prev
+ }
+
+ result.content = page.getContent();
+ result.dir = page.getDir();
+
+ return result;
+}
+
+module.exports = encodePage;
diff --git a/lib/json/encodeReadme.js b/lib/json/encodeReadme.js
new file mode 100644
index 0000000..0127788
--- /dev/null
+++ b/lib/json/encodeReadme.js
@@ -0,0 +1,17 @@
+var encodeFile = require('./encodeFileToJson');
+
+/**
+ Encode a readme to JSON
+
+ @param {Readme}
+ @return {Object}
+*/
+function encodeReadme(readme) {
+ var file = readme.getFile();
+
+ return {
+ file: encodeFile(file)
+ };
+}
+
+module.exports = encodeReadme;
diff --git a/lib/json/encodeSummary.js b/lib/json/encodeSummary.js
new file mode 100644
index 0000000..97db910
--- /dev/null
+++ b/lib/json/encodeSummary.js
@@ -0,0 +1,20 @@
+var encodeFile = require('./encodeFile');
+var encodeSummaryPart = require('./encodeSummaryPart');
+
+/**
+ Encode a summary to JSON
+
+ @param {Summary}
+ @return {Object}
+*/
+function encodeSummary(summary) {
+ var file = summary.getFile();
+ var parts = summary.getParts();
+
+ return {
+ file: encodeFile(file),
+ parts: parts.map(encodeSummaryPart).toJS()
+ };
+}
+
+module.exports = encodeSummary;
diff --git a/lib/json/encodeSummaryArticle.js b/lib/json/encodeSummaryArticle.js
new file mode 100644
index 0000000..485d209
--- /dev/null
+++ b/lib/json/encodeSummaryArticle.js
@@ -0,0 +1,18 @@
+
+/**
+ Encode a SummaryArticle to JSON
+
+ @param {SummaryArticle}
+ @return {Object}
+*/
+function encodeSummaryArticle(article) {
+ return {
+ title: article.getTitle(),
+ level: article.getLevel(),
+ depth: article.getDepth(),
+ articles: article.getArticles()
+ .map(encodeSummaryArticle).toJS()
+ };
+}
+
+module.exports = encodeSummaryArticle;
diff --git a/lib/json/encodeSummaryPart.js b/lib/json/encodeSummaryPart.js
new file mode 100644
index 0000000..a5e7218
--- /dev/null
+++ b/lib/json/encodeSummaryPart.js
@@ -0,0 +1,17 @@
+var encodeSummaryArticle = require('./encodeSummaryArticle');
+
+/**
+ Encode a SummaryPart to JSON
+
+ @param {SummaryPart}
+ @return {Object}
+*/
+function encodeSummaryPart(part) {
+ return {
+ title: part.getTitle(),
+ articles: part.getArticles()
+ .map(encodeSummaryArticle).toJS()
+ };
+}
+
+module.exports = encodeSummaryPart;
diff --git a/lib/json/index.js b/lib/json/index.js
new file mode 100644
index 0000000..a026a7f
--- /dev/null
+++ b/lib/json/index.js
@@ -0,0 +1,10 @@
+
+module.exports = {
+ encodeBookWithPage: require('./encodeBookWithPage'),
+ encodeBook: require('./encodeBook'),
+ encodeFile: require('./encodeFile'),
+ encodePage: require('./encodePage'),
+ encodeSummary: require('./encodeSummary'),
+ encodeReadme: require('./encodeReadme'),
+ encodeConfig: require('./encodeConfig')
+};
diff --git a/lib/models/file.js b/lib/models/file.js
index ebfe629..b6b06ee 100644
--- a/lib/models/file.js
+++ b/lib/models/file.js
@@ -20,6 +20,20 @@ File.prototype.getMTime = function() {
};
/**
+ Return type of file ('markdown' or 'asciidoc')
+
+ @return {String}
+*/
+File.prototype.getType = function() {
+ var parser = this.getParser();
+ if (parser) {
+ return parser.name;
+ } else {
+ return undefined;
+ }
+};
+
+/**
Return extension of this file (lowercased)
@return {String}
diff --git a/lib/models/page.js b/lib/models/page.js
index 1ac0d50..0a9cf38 100644
--- a/lib/models/page.js
+++ b/lib/models/page.js
@@ -9,7 +9,10 @@ var Page = Immutable.Record({
attributes: Immutable.Map(),
// Content of the page
- content: String()
+ content: String(),
+
+ // Direction of the text
+ dir: String('ltr')
});
Page.prototype.getFile = function() {
@@ -24,6 +27,10 @@ Page.prototype.getContent = function() {
return this.get('content');
};
+Page.prototype.getDir = function() {
+ return this.get('dir');
+};
+
/**
Create a page for a file
diff --git a/lib/models/summaryArticle.js b/lib/models/summaryArticle.js
index 4a448c8..e3d85ef 100644
--- a/lib/models/summaryArticle.js
+++ b/lib/models/summaryArticle.js
@@ -30,6 +30,15 @@ SummaryArticle.prototype.getArticles = function() {
};
/**
+ Return how deep the article is
+
+ @return {Number}
+*/
+SummaryArticle.prototype.getDepth = function() {
+ return this.getLevel().split('.').length;
+};
+
+/**
Return an article by its level
@param {String} level
diff --git a/lib/output/json/onPage.js b/lib/output/json/onPage.js
index 3ef5c74..a84e297 100644
--- a/lib/output/json/onPage.js
+++ b/lib/output/json/onPage.js
@@ -1,4 +1,6 @@
+var JSONUtils = require('../../json');
var Modifier = require('../modifier');
+var Writer = require('../writer');
/**
Write a page as a json file
@@ -7,13 +9,22 @@ var Modifier = require('../modifier');
@param {Page} page
*/
function onPage(output, page) {
- var options = output.
-
- return Modifier.applyHTMLTransformations(page, [
+ return Modifier.modifyHTML(page, [
Modifier.HTML.resolveLinks()
])
- .then(function(newPage) {
+ .then(function(resultPage) {
+ // Generate the JSON
+ var json = JSONUtils.encodeBookWithPage(output.getBook(), resultPage);
+ // Write it to the disk
+ return Writer.writePage(
+ output,
+ page,
+ JSON.stringify(json, null, 4),
+ {
+ extension: '.json'
+ }
+ );
});
}
diff --git a/lib/output/modifiers/index.js b/lib/output/modifiers/index.js
index 76ce3c2..ced8716 100644
--- a/lib/output/modifiers/index.js
+++ b/lib/output/modifiers/index.js
@@ -1,9 +1,4 @@
-
-function modifyPage() {
-
-
-}
-
-
-module.exports = modifyPage;
+module.exports = {
+ modifyHTML: require('./modifyHTML')
+};
diff --git a/lib/output/modifiers/modifyHTML.js b/lib/output/modifiers/modifyHTML.js
new file mode 100644
index 0000000..c1dad74
--- /dev/null
+++ b/lib/output/modifiers/modifyHTML.js
@@ -0,0 +1,15 @@
+
+
+/**
+ Apply a list of operations to a page and
+ output the new page.
+
+ @param {Page}
+ @param {List<Transformation>}
+*/
+function modifyHTML(page, operations) {
+
+}
+
+
+module.exports = modifyHTML;
diff --git a/lib/output/writer/index.js b/lib/output/writer/index.js
new file mode 100644
index 0000000..2ef3364
--- /dev/null
+++ b/lib/output/writer/index.js
@@ -0,0 +1,4 @@
+
+module.exports = {
+ writePage: require('./writePage')
+};
diff --git a/lib/output/writer/writePage.js b/lib/output/writer/writePage.js
new file mode 100644
index 0000000..23e37d0
--- /dev/null
+++ b/lib/output/writer/writePage.js
@@ -0,0 +1,33 @@
+var path = require('path');
+var Immutable = require('immutable');
+
+var fs = require('../../utils/fs');
+var PathUtils = require('../../utils/path');
+
+var WriteOptions = Immutable.Record({
+ extension: '.html'
+});
+
+/**
+ Write a file to the output folder
+
+ @param {Output} output
+ @param {Page} page
+ @param {Buffer|String} content
+ @return {Promise}
+*/
+function writePage(output, page, content, options) {
+ var file = page.getFile();
+ var outputOpts = output.getOptions();
+ var rootFolder = outputOpts.get('root');
+
+ options = WriteOptions(options);
+
+ // Get filename for file to write
+ var fileName = PathUtils.setExtension(file.getPath(), options.get('extension'));
+ var filePath = path.join(rootFolder, fileName);
+
+ return fs.writeFile(filePath, content);
+}
+
+module.exports = writePage;
diff --git a/lib/output2/assets-inliner.js b/lib/output2/assets-inliner.js
deleted file mode 100644
index 6f1f02d..0000000
--- a/lib/output2/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/output2/base.js b/lib/output2/base.js
deleted file mode 100644
index 868b85b..0000000
--- a/lib/output2/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/output2/conrefs.js b/lib/output2/conrefs.js
deleted file mode 100644
index e58f836..0000000
--- a/lib/output2/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/output2/ebook.js b/lib/output2/ebook.js
deleted file mode 100644
index 2b8fac9..0000000
--- a/lib/output2/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/output2/folder.js b/lib/output2/folder.js
deleted file mode 100644
index 8303ed2..0000000
--- a/lib/output2/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/output2/json.js b/lib/output2/json.js
deleted file mode 100644
index 7061141..0000000
--- a/lib/output2/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/output2/website/index.js b/lib/output2/website/index.js
deleted file mode 100644
index 0a8618c..0000000
--- a/lib/output2/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/output2/website/templateEnv.js b/lib/output2/website/templateEnv.js
deleted file mode 100644
index d385108..0000000
--- a/lib/output2/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/output2/website/themeLoader.js b/lib/output2/website/themeLoader.js
deleted file mode 100644
index 774a39e..0000000
--- a/lib/output2/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;
diff --git a/lib/page/html.js b/lib/page/html.js
deleted file mode 100644
index e8d3a85..0000000
--- a/lib/page/html.js
+++ /dev/null
@@ -1,290 +0,0 @@
-var _ = require('lodash');
-var url = require('url');
-var cheerio = require('cheerio');
-var domSerializer = require('dom-serializer');
-var slug = require('github-slugid');
-
-var Promise = require('../utils/promise');
-var location = require('../utils/location');
-
-// Selector to ignore
-var ANNOTATION_IGNORE = '.no-glossary,code,pre,a,script,h1,h2,h3,h4,h5,h6';
-
-function HTMLPipeline(htmlString, opts) {
- _.bindAll(this);
-
- this.opts = _.defaults(opts || {}, {
- // Called once the description has been found
- onDescription: function(description) { },
-
- // Calcul new href for a relative link
- onRelativeLink: _.identity,
-
- // Output an image
- onImage: _.identity,
-
- // Syntax highlighting
- onCodeBlock: _.identity,
-
- // Output a svg, if returns null the svg is kept inlined
- onOutputSVG: _.constant(null),
-
- // Words to annotate
- annotations: [],
-
- // When an annotation is applied
- onAnnotation: function () { }
- });
-
- 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
- });
-}
-
-// Transform a query of elements in the page
-HTMLPipeline.prototype._transform = function(query, fn) {
- var that = this;
-
- var $elements = this.$(query);
-
- return Promise.serie($elements, function(el) {
- var $el = that.$(el);
- return fn.call(that, $el);
- });
-};
-
-// Normalize links
-HTMLPipeline.prototype.transformLinks = function() {
- return this._transform('a', function($a) {
- var href = $a.attr('href');
- if (!href) return;
-
- if (location.isAnchor(href)) {
- // Don't "change" anchor links
- } else if (location.isRelative(href)) {
- // Preserve anchor
- var parsed = url.parse(href);
- var filename = this.opts.onRelativeLink(parsed.pathname);
-
- $a.attr('href', filename + (parsed.hash || ''));
- } else {
- // External links
- $a.attr('target', '_blank');
- }
- });
-};
-
-// Normalize images
-HTMLPipeline.prototype.transformImages = function() {
- return this._transform('img', function($img) {
- return Promise(this.opts.onImage($img.attr('src')))
- .then(function(filename) {
- $img.attr('src', filename);
- });
- });
-};
-
-// Normalize code blocks
-HTMLPipeline.prototype.transformCodeBlocks = function() {
- return this._transform('code', function($code) {
- // Extract language
- var lang = _.chain(
- ($code.attr('class') || '').split(' ')
- )
- .map(function(cl) {
- // Markdown
- if (cl.search('lang-') === 0) return cl.slice('lang-'.length);
-
- // Asciidoc
- if (cl.search('language-') === 0) return cl.slice('language-'.length);
-
- return null;
- })
- .compact()
- .first()
- .value();
-
- var source = $code.text();
-
- return Promise(this.opts.onCodeBlock(source, lang))
- .then(function(blk) {
- if (blk.html === false) {
- $code.text(blk.body);
- } else {
- $code.html(blk.body);
- }
- });
- });
-};
-
-// Add ID to headings
-HTMLPipeline.prototype.transformHeadings = function() {
- var that = this;
-
- this.$('h1,h2,h3,h4,h5,h6').each(function() {
- var $h = that.$(this);
-
- // Already has an ID?
- if ($h.attr('id')) return;
- $h.attr('id', slug($h.text()));
- });
-};
-
-// Outline SVG from the HML
-HTMLPipeline.prototype.transformSvgs = function() {
- var that = this;
-
- return this._transform('svg', function($svg) {
- var content = [
- '<?xml version="1.0" encoding="UTF-8"?>',
- renderDOM(that.$, $svg)
- ].join('\n');
-
- return Promise(that.opts.onOutputSVG(content))
- .then(function(filename) {
- if (!filename) return;
-
- $svg.replaceWith(that.$('<img>').attr('src', filename));
- });
- });
-};
-
-// Annotate the content
-HTMLPipeline.prototype.applyAnnotations = function() {
- var that = this;
-
- _.each(this.opts.annotations, function(annotation) {
- var searchRegex = new RegExp( '\\b(' + pregQuote(annotation.name.toLowerCase()) + ')\\b' , 'gi' );
-
- that.$('*').each(function() {
- var $this = that.$(this);
-
- if (
- $this.is(ANNOTATION_IGNORE) ||
- $this.parents(ANNOTATION_IGNORE).length > 0
- ) return;
-
- replaceText(that.$, this, searchRegex, function(match) {
- that.opts.onAnnotation(annotation);
-
- return '<a href="' + that.opts.onRelativeLink(annotation.href) + '" '
- + 'class="glossary-term" title="'+_.escape(annotation.description)+'">'
- + match
- + '</a>';
- });
- });
- });
-};
-
-// Extract page description from html
-// This can totally be improved
-HTMLPipeline.prototype.extractDescription = function() {
- var $ = this.$;
- var $p = $('p').first();
- var $next = $p.nextUntil('h1,h2,h3,h4,h5,h6,pre,blockquote,ul,ol,div');
-
- var description = $p.text().trim();
-
- $next.each(function() {
- description += ' ' + $(this).text().trim();
- });
-
- // Truncate description
- description = _.trunc(description, 300);
-
- this.opts.onDescription(description);
-};
-
-// Write content to the pipeline
-HTMLPipeline.prototype.output = function() {
- var that = this;
-
- return Promise()
- .then(this.extractDescription)
- .then(this.transformImages)
- .then(this.transformHeadings)
- .then(this.transformCodeBlocks)
- .then(this.transformSvgs)
- .then(this.applyAnnotations)
-
- // Transform of links should be applied after annotations
- // because annotations are created as links
- .then(this.transformLinks)
-
- .then(function() {
- return renderDOM(that.$);
- });
-};
-
-
-// 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);
-}
-
-// Replace text in an element
-function replaceText($, el, search, replace, text_only ) {
- return $(el).each(function(){
- var node = this.firstChild,
- val,
- new_val,
-
- // Elements to be removed at the end.
- remove = [];
-
- // Only continue if firstChild exists.
- if ( node ) {
-
- // Loop over all childNodes.
- while (node) {
-
- // Only process text nodes.
- if ( node.nodeType === 3 ) {
-
- // The original node value.
- val = node.nodeValue;
-
- // The new value.
- new_val = val.replace( search, replace );
-
- // Only replace text if the new value is actually different!
- if ( new_val !== val ) {
-
- if ( !text_only && /</.test( new_val ) ) {
- // The new value contains HTML, set it in a slower but far more
- // robust way.
- $(node).before( new_val );
-
- // Don't remove the node yet, or the loop will lose its place.
- remove.push( node );
- } else {
- // The new value contains no HTML, so it can be set in this
- // very fast, simple way.
- node.nodeValue = new_val;
- }
- }
- }
-
- node = node.nextSibling;
- }
- }
-
- // Time to remove those elements!
- if (remove.length) $(remove).remove();
- });
-}
-
-function pregQuote( str ) {
- return (str+'').replace(/([\\\.\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, '\\$1');
-}
-
-module.exports = HTMLPipeline;
diff --git a/lib/page/index.js b/lib/page/index.js
deleted file mode 100644
index f0d7f57..0000000
--- a/lib/page/index.js
+++ /dev/null
@@ -1,246 +0,0 @@
-var _ = require('lodash');
-var path = require('path');
-var direction = require('direction');
-var fm = require('front-matter');
-
-var error = require('../utils/error');
-var pathUtil = require('../utils/path');
-var location = require('../utils/location');
-var parsers = require('../parsers');
-var pluginCompatibility = require('../plugins/compatibility');
-var HTMLPipeline = require('./html');
-
-/*
-A page represent a parsable file in the book (Markdown, Asciidoc, etc)
-*/
-
-function Page(book, filename) {
- if (!(this instanceof Page)) return new Page(book, filename);
- var extension;
- _.bindAll(this);
-
- this.book = book;
- this.log = this.book.log;
-
- // Map of attributes from YAML frontmatter
- // Description is also extracted by default from content
- this.attributes = {};
-
- // Current content
- this.content = '';
-
- // Relative path to the page
- this.path = location.normalize(filename);
-
- // Absolute path to the page
- this.rawPath = this.book.resolve(filename);
-
- // Last modification date
- this.mtime = 0;
-
- // Can we parse it?
- extension = path.extname(this.path);
- this.parser = parsers.getByExt(extension);
- if (!this.parser) throw error.ParsingError(new Error('Can\'t parse file "'+this.path+'"'));
-
- this.type = this.parser.name;
-}
-
-// Return the filename of the page with another extension
-// "README.md" -> "README.html"
-Page.prototype.withExtension = function(ext) {
- return pathUtil.setExtension(this.path, ext);
-};
-
-// Resolve a filename relative to this page
-// It returns a path relative to the book root folder
-Page.prototype.resolveLocal = function() {
- var dir = path.dirname(this.path);
- var file = path.join.apply(path, _.toArray(arguments));
-
- return location.toAbsolute(file, dir, '');
-};
-
-// Resolve a filename relative to this page
-// It returns an absolute path for the FS
-Page.prototype.resolve = function() {
- return this.book.resolve(this.resolveLocal.apply(this, arguments));
-};
-
-// Convert an absolute path (in the book) to a relative path from this page
-Page.prototype.relative = function(name) {
- // Convert /test.png -> test.png
- name = location.toAbsolute(name, '', '');
-
- return location.relative(
- this.resolve('.') + '/',
- this.book.resolve(name)
- );
-};
-
-// Return a page result of a relative page from this page
-Page.prototype.followPage = function(filename) {
- var absPath = this.resolveLocal(filename);
- return this.book.getPage(absPath);
-};
-
-// Update content of the page
-Page.prototype.update = function(content) {
- this.content = content;
-};
-
-// Read the page as a string
-Page.prototype.read = function() {
- var that = this;
-
- return this.book.statFile(this.path)
- .then(function(stat) {
- that.mtime = stat.mtime;
- return that.book.readFile(that.path);
- })
- .then(this.update);
-};
-
-// Return templating context for this page
-// This is used both for themes and page parsing
-Page.prototype.getContext = function() {
- var article = this.book.summary.getArticle(this);
- var next = article? article.next() : null;
- var prev = article? article.prev() : null;
-
- // Detect text direction in this page
- var dir = this.book.config.get('direction');
- if (!dir) {
- dir = direction(this.content);
- if (dir == 'neutral') dir = null;
- }
-
- return {
- file: {
- path: this.path,
- mtime: this.mtime,
- type: this.type
- },
- page: _.extend({}, this.attributes, {
- title: article? article.title : null,
- next: next? next.getContext() : null,
- previous: prev? prev.getContext() : null,
- level: article? article.level : null,
- depth: article? article.depth() : 0,
- content: this.content,
- dir: dir
- })
- };
-};
-
-// Return complete context for templating (page + book + summary + ...)
-Page.prototype.getOutputContext = function(output) {
- return _.extend({}, this.getContext(), output.getContext());
-};
-
-// Parse the page and return its content
-Page.prototype.toHTML = function(output) {
- var that = this;
-
- this.log.debug.ln('start parsing file', this.path);
-
- // Call a hook in the output
- // using an utility to "keep" compatibility with gitbook 2
- function hook(name) {
- return pluginCompatibility.pageHook(that, function(ctx) {
- return output.plugins.hook(name, ctx);
- })
- .then(function(result) {
- if(_.isString(result)) that.update(result);
- });
- }
-
- return this.read()
-
- // Parse yaml front matter
- .then(function() {
- var parsed = fm(that.content);
-
- // Extract attributes
- that.attributes = parsed.attributes;
-
- // Keep only the body
- that.update(parsed.body);
- })
-
- .then(function() {
- return hook('page:before');
- })
-
- // Pre-process page with parser
- .then(function() {
- return that.parser.page.prepare(that.content)
- .then(that.update);
- })
-
- // Render template
- .then(function() {
- return output.template.render(that.content, that.getOutputContext(output), {
- path: that.path
- })
- .then(that.update);
- })
-
- // Render markup using the parser
- .then(function() {
- return that.parser.page(that.content)
- .then(function(out) {
- that.update(out.content);
- });
- })
-
- // Post process templating
- .then(function() {
- return output.template.postProcess(that.content)
- .then(that.update);
- })
-
- // Normalize HTML output
- .then(function() {
- var pipelineOpts = {
- onRelativeLink: _.partial(output.onRelativeLink, that),
- onImage: _.partial(output.onOutputImage, that),
- onOutputSVG: _.partial(output.onOutputSVG, that),
-
- // Use 'code' template block
- onCodeBlock: function(source, lang) {
- return output.template.applyBlock('code', {
- body: source,
- kwargs: {
- language: lang
- }
- });
- },
-
- // Extract description from page's content if no frontmatter
- onDescription: function(description) {
- if (that.attributes.description) return;
- that.attributes.description = description;
- },
-
- // Convert glossary entries to annotations
- annotations: that.book.glossary.annotations()
- };
- var pipeline = new HTMLPipeline(that.content, pipelineOpts);
-
- return pipeline.output()
- .then(that.update);
- })
-
- .then(function() {
- return hook('page');
- })
-
- // Return content itself
- .then(function() {
- return that.content;
- });
-};
-
-
-module.exports = Page;
diff --git a/lib/parse/parsePage.js b/lib/parse/parsePage.js
index 75bcf61..1d515d6 100644
--- a/lib/parse/parsePage.js
+++ b/lib/parse/parsePage.js
@@ -1,4 +1,6 @@
+var Immutable = require('immutable');
var fm = require('front-matter');
+var direction = require('direction');
/**
Parse a page, read its content and parse the YAMl header
@@ -15,10 +17,11 @@ function parsePage(book, page) {
.then(function(content) {
var parsed = fm(content);
- page = page.set('content', parsed.body);
- page = page.set('attributes', parsed.attributes);
-
- return page;
+ return page.merge({
+ content: parsed.body,
+ attributes: Immutable.fromJS(parsed.attributes),
+ dir: direction(parsed.body)
+ });
});
}
diff --git a/lib/plugins2/compatibility.js b/lib/plugins2/compatibility.js
deleted file mode 100644
index 77f4be2..0000000
--- a/lib/plugins2/compatibility.js
+++ /dev/null
@@ -1,61 +0,0 @@
-var _ = require('lodash');
-var error = require('../utils/error');
-
-/*
- Return the context for a plugin.
- It tries to keep compatibilities with GitBook v2
-*/
-function pluginCtx(plugin) {
- var book = plugin.book;
- var ctx = book;
-
- return ctx;
-}
-
-/*
- Call a function "fn" with a context of page similar to the one in GitBook v2
-
- @params {Page}
- @returns {String|undefined} new content of the page
-*/
-function pageHook(page, fn) {
- // Get page context
- var ctx = page.getContext().page;
-
- // Add other informations
- ctx.type = page.type;
- ctx.rawPath = page.rawPath;
- ctx.path = page.path;
-
- // Deprecate sections
- error.deprecateField(ctx, 'sections', [
- { content: ctx.content, type: 'normal' }
- ], '"sections" property is deprecated, use page.content instead');
-
- // Keep reference of original content for compatibility
- var originalContent = ctx.content;
-
- return fn(ctx)
- .then(function(result) {
- // No returned value
- // Existing content will be used
- if (!result) return undefined;
-
- // GitBook 3
- // Use returned page.content if different from original content
- if (result.content != originalContent) {
- return result.content;
- }
-
- // GitBook 2 compatibility
- // Finally, use page.sections
- if (result.sections) {
- return _.pluck(result.sections, 'content').join('\n');
- }
- });
-}
-
-module.exports = {
- pluginCtx: pluginCtx,
- pageHook: pageHook
-};
diff --git a/lib/plugins2/index.js b/lib/plugins2/index.js
deleted file mode 100644
index c6f1686..0000000
--- a/lib/plugins2/index.js
+++ /dev/null
@@ -1,188 +0,0 @@
-var _ = require('lodash');
-var path = require('path');
-
-var Promise = require('../utils/promise');
-var fs = require('../utils/fs');
-var BookPlugin = require('./plugin');
-var registry = require('./registry');
-var pluginsConfig = require('../config/plugins');
-
-/*
-PluginsManager is an interface to work with multiple plugins at once:
-- Extract assets from plugins
-- Call hooks for all plugins, etc
-*/
-
-function PluginsManager(book) {
- this.book = book;
- this.log = this.book.log;
- this.plugins = [];
-
- _.bindAll(this);
-}
-
-// Returns the list of plugins
-PluginsManager.prototype.list = function() {
- return this.plugins;
-};
-
-// Return count of plugins loaded
-PluginsManager.prototype.count = function() {
- return _.size(this.plugins);
-};
-
-// Returns a plugin by its name
-PluginsManager.prototype.get = function(name) {
- return _.find(this.plugins, {
- id: name
- });
-};
-
-// Load a plugin (could be a BookPlugin or {name,path})
-PluginsManager.prototype.load = function(plugin) {
- var that = this;
-
- if (_.isArray(plugin)) {
- return Promise.serie(plugin, that.load);
- }
-
- return Promise()
-
- // Initiate and load the plugin
- .then(function() {
- if (!(plugin instanceof BookPlugin)) {
- plugin = new BookPlugin(that.book, plugin.name, plugin.path);
- }
-
- if (that.get(plugin.id)) {
- throw new Error('Plugin "'+plugin.id+'" is already loaded');
- }
-
-
- if (plugin.isLoaded()) return plugin;
- else return plugin.load()
- .thenResolve(plugin);
- })
-
- // Setup the plugin
- .then(this._setup);
-};
-
-// Load all plugins from the book's configuration
-PluginsManager.prototype.loadAll = function() {
- var that = this;
- var pluginNames = _.pluck(this.book.config.get('plugins'), 'name');
-
- return registry.list(this.book)
- .then(function(plugins) {
- // Filter out plugins not listed of first level
- // (aka pre-installed plugins)
- plugins = _.filter(plugins, function(plugin) {
- return (
- plugin.depth > 1 ||
- _.contains(pluginNames, plugin.name)
- );
- });
-
- // Sort plugins to match list in book.json
- plugins.sort(function(a, b){
- return pluginNames.indexOf(a.name) < pluginNames.indexOf(b.name) ? -1 : 1;
- });
-
- // Log state
- that.log.info.ln(_.size(plugins) + ' are installed');
- if (_.size(pluginNames) != _.size(plugins)) that.log.info.ln(_.size(pluginNames) + ' explicitly listed');
-
- // Verify that all plugins are present
- var notInstalled = _.filter(pluginNames, function(name) {
- return !_.find(plugins, { name: name });
- });
-
- if (_.size(notInstalled) > 0) {
- throw new Error('Couldn\'t locate plugins "' + notInstalled.join(', ') + '", Run \'gitbook install\' to install plugins from registry.');
- }
-
- // Load plugins
- return that.load(plugins);
- });
-};
-
-// Setup a plugin
-// Register its filter, blocks, etc
-PluginsManager.prototype._setup = function(plugin) {
- this.plugins.push(plugin);
-};
-
-// Install all plugins for the book
-PluginsManager.prototype.install = function() {
- var that = this;
- var plugins = _.filter(this.book.config.get('plugins'), function(plugin) {
- return !pluginsConfig.isDefaultPlugin(plugin.name);
- });
-
- if (plugins.length == 0) {
- this.log.info.ln('nothing to install!');
- return Promise(0);
- }
-
- this.log.info.ln('installing', plugins.length, 'plugins');
-
- return Promise.serie(plugins, function(plugin) {
- return registry.install(that.book, plugin.name, plugin.version);
- })
- .thenResolve(plugins.length);
-};
-
-// Call a hook on all plugins to transform an input
-PluginsManager.prototype.hook = function(name, input) {
- return Promise.reduce(this.plugins, function(current, plugin) {
- return plugin.hook(name, current);
- }, input);
-};
-
-// Extract all resources for a namespace
-PluginsManager.prototype.getResources = function(namespace) {
- return Promise.reduce(this.plugins, function(out, plugin) {
- return plugin.getResources(namespace)
- .then(function(pluginResources) {
- _.each(BookPlugin.RESOURCES, function(resourceType) {
- out[resourceType] = (out[resourceType] || []).concat(pluginResources[resourceType] || []);
- });
-
- return out;
- });
- }, {});
-};
-
-// Copy all resources for a plugin
-PluginsManager.prototype.copyResources = function(namespace, outputRoot) {
- return Promise.serie(this.plugins, function(plugin) {
- return plugin.getResources(namespace)
- .then(function(resources) {
- if (!resources.assets) return;
-
- var input = path.resolve(plugin.root, resources.assets);
- var output = path.resolve(outputRoot, plugin.npmId);
-
- return fs.copyDir(input, output);
- });
- });
-};
-
-// Get all filters and blocks
-PluginsManager.prototype.getFilters = function() {
- return _.reduce(this.plugins, function(out, plugin) {
- var filters = plugin.getFilters();
-
- return _.extend(out, filters);
- }, {});
-};
-PluginsManager.prototype.getBlocks = function() {
- return _.reduce(this.plugins, function(out, plugin) {
- var blocks = plugin.getBlocks();
-
- return _.extend(out, blocks);
- }, {});
-};
-
-module.exports = PluginsManager;
diff --git a/lib/plugins2/plugin.js b/lib/plugins2/plugin.js
deleted file mode 100644
index d1c00d8..0000000
--- a/lib/plugins2/plugin.js
+++ /dev/null
@@ -1,288 +0,0 @@
-var _ = require('lodash');
-var path = require('path');
-var url = require('url');
-var resolve = require('resolve');
-var mergeDefaults = require('merge-defaults');
-var jsonschema = require('jsonschema');
-var jsonSchemaDefaults = require('json-schema-defaults');
-
-var Promise = require('../utils/promise');
-var error = require('../utils/error');
-var gitbook = require('../gitbook');
-var registry = require('./registry');
-var compatibility = require('./compatibility');
-
-var HOOKS = [
- 'init', 'finish', 'finish:before', 'config', 'page', 'page:before'
-];
-
-var RESOURCES = ['js', 'css'];
-
-// Return true if an error is a "module not found"
-// Wait on https://github.com/substack/node-resolve/pull/81 to be merged
-function isModuleNotFound(err) {
- return err.message.indexOf('Cannot find module') >= 0;
-}
-
-function BookPlugin(book, pluginId, pluginFolder) {
- this.book = book;
- this.log = this.book.log.prefix(pluginId);
-
-
- this.id = pluginId;
- this.npmId = registry.npmId(pluginId);
- this.root = pluginFolder;
-
- this.packageInfos = undefined;
- this.content = undefined;
-
- // Cache for resources
- this._resources = {};
-
- _.bindAll(this);
-}
-
-// Return true if plugin has been loaded correctly
-BookPlugin.prototype.isLoaded = function() {
- return Boolean(this.packageInfos && this.content);
-};
-
-// Bind a function to the plugin's context
-BookPlugin.prototype.bind = function(fn) {
- return fn.bind(compatibility.pluginCtx(this));
-};
-
-// Load this plugin from its root folder
-BookPlugin.prototype.load = function(folder) {
- var that = this;
-
- if (this.isLoaded()) {
- return Promise.reject(new Error('Plugin "' + this.id + '" is already loaded'));
- }
-
- // Try loading plugins from different location
- var p = Promise()
- .then(function() {
- // Locate plugin and load pacjage.json
- try {
- var res = resolve.sync('./package.json', { basedir: that.root });
-
- that.root = path.dirname(res);
- that.packageInfos = require(res);
- } catch (err) {
- if (!isModuleNotFound(err)) throw err;
-
- that.packageInfos = undefined;
- that.content = undefined;
-
- return;
- }
-
- // Load plugin JS content
- try {
- that.content = require(that.root);
- } catch(err) {
- // It's no big deal if the plugin doesn't have an "index.js"
- // (For example: themes)
- if (isModuleNotFound(err)) {
- that.content = {};
- } else {
- throw new error.PluginError(err, {
- plugin: that.id
- });
- }
- }
- })
-
- .then(that.validate)
-
- // Validate the configuration and update it
- .then(function() {
- var config = that.book.config.get(that.getConfigKey(), {});
- return that.validateConfig(config);
- })
- .then(function(config) {
- that.book.config.set(that.getConfigKey(), config);
- });
-
- this.log.info('loading plugin "' + this.id + '"... ');
- return this.log.info.promise(p);
-};
-
-// Verify the definition of a plugin
-// Also verify that the plugin accepts the current gitbook version
-// This method throws erros if plugin is invalid
-BookPlugin.prototype.validate = function() {
- var isValid = (
- this.isLoaded() &&
- this.packageInfos &&
- this.packageInfos.name &&
- this.packageInfos.engines &&
- this.packageInfos.engines.gitbook
- );
-
- if (!isValid) {
- throw new Error('Error loading plugin "' + this.id + '" at "' + this.root + '"');
- }
-
- if (!gitbook.satisfies(this.packageInfos.engines.gitbook)) {
- throw new Error('GitBook doesn\'t satisfy the requirements of this plugin: '+this.packageInfos.engines.gitbook);
- }
-};
-
-// Normalize, validate configuration for this plugin using its schema
-// Throw an error when shcema is not respected
-BookPlugin.prototype.validateConfig = function(config) {
- var that = this;
-
- return Promise()
- .then(function() {
- var schema = that.packageInfos.gitbook || {};
- if (!schema) return config;
-
- // Normalize schema
- schema.id = '/'+that.getConfigKey();
- schema.type = 'object';
-
- // Validate and throw if invalid
- var v = new jsonschema.Validator();
- var result = v.validate(config, schema, {
- propertyName: that.getConfigKey()
- });
-
- // Throw error
- if (result.errors.length > 0) {
- throw new error.ConfigurationError(new Error(result.errors[0].stack));
- }
-
- // Insert default values
- var defaults = jsonSchemaDefaults(schema);
- return mergeDefaults(config, defaults);
- });
-};
-
-// Return key for configuration
-BookPlugin.prototype.getConfigKey = function() {
- return 'pluginsConfig.'+this.id;
-};
-
-// Call a hook and returns its result
-BookPlugin.prototype.hook = function(name, input) {
- var that = this;
- var hookFunc = this.content.hooks? this.content.hooks[name] : null;
- input = input || {};
-
- if (!hookFunc) return Promise(input);
-
- this.book.log.debug.ln('call hook "' + name + '" for plugin "' + this.id + '"');
- if (!_.contains(HOOKS, name)) {
- this.book.log.warn.ln('hook "'+name+'" used by plugin "'+this.name+'" is deprecated, and will be removed in the coming versions');
- }
-
- return Promise()
- .then(function() {
- return that.bind(hookFunc)(input);
- });
-};
-
-// Return resources without normalization
-BookPlugin.prototype._getResources = function(base) {
- var that = this;
-
- return Promise()
- .then(function() {
- if (that._resources[base]) return that._resources[base];
-
- var book = that.content[base];
-
- // Compatibility with version 1.x.x
- if (base == 'website') book = book || that.content.book;
-
- // Nothing specified, fallback to default
- if (!book) {
- return Promise({});
- }
-
- // Dynamic function
- if(typeof book === 'function') {
- // Call giving it the context of our book
- return that.bind(book)();
- }
-
- // Plain data object
- return book;
- })
-
- .then(function(resources) {
- that._resources[base] = resources;
- return _.cloneDeep(resources);
- });
-};
-
-// Normalize a specific resource
-BookPlugin.prototype.normalizeResource = function(resource) {
- // Parse the resource path
- var parsed = url.parse(resource);
-
- // This is a remote resource
- // so we will simply link to using it's URL
- if (parsed.protocol) {
- return {
- 'url': resource
- };
- }
-
- // This will be copied over from disk
- // and shipped with the book's build
- return { 'path': this.npmId+'/'+resource };
-};
-
-
-// Normalize resources and return them
-BookPlugin.prototype.getResources = function(base) {
- var that = this;
-
- return this._getResources(base)
- .then(function(resources) {
- _.each(RESOURCES, function(resourceType) {
- resources[resourceType] = _.map(resources[resourceType] || [], that.normalizeResource);
- });
-
- return resources;
- });
-};
-
-// Normalize filters and return them
-BookPlugin.prototype.getFilters = function() {
- var that = this;
-
- return _.mapValues(this.content.filters || {}, function(fn, filter) {
- return function() {
- var ctx = _.extend(compatibility.pluginCtx(that), this);
-
- return fn.apply(ctx, arguments);
- };
- });
-};
-
-// Normalize blocks and return them
-BookPlugin.prototype.getBlocks = function() {
- var that = this;
-
- return _.mapValues(this.content.blocks || {}, function(block, blockName) {
- block = _.isFunction(block)? { process: block } : block;
-
- var fn = block.process;
- block.process = function() {
- var ctx = _.extend(compatibility.pluginCtx(that), this);
-
- return fn.apply(ctx, arguments);
- };
-
- return block;
- });
-};
-
-module.exports = BookPlugin;
-module.exports.RESOURCES = RESOURCES;
-
diff --git a/lib/plugins2/registry.js b/lib/plugins2/registry.js
deleted file mode 100644
index fe9406d..0000000
--- a/lib/plugins2/registry.js
+++ /dev/null
@@ -1,172 +0,0 @@
-var npm = require('npm');
-var npmi = require('npmi');
-var path = require('path');
-var semver = require('semver');
-var _ = require('lodash');
-var readInstalled = require('read-installed');
-
-var Promise = require('../utils/promise');
-var gitbook = require('../gitbook');
-
-var PLUGIN_PREFIX = 'gitbook-plugin-';
-
-// Return an absolute name for the plugin (the one on NPM)
-function npmId(name) {
- if (name.indexOf(PLUGIN_PREFIX) === 0) return name;
- return [PLUGIN_PREFIX, name].join('');
-}
-
-// Return a plugin ID 9the one on GitBook
-function pluginId(name) {
- return name.replace(PLUGIN_PREFIX, '');
-}
-
-// Validate an NPM plugin ID
-function validateId(name) {
- return name && name.indexOf(PLUGIN_PREFIX) === 0;
-}
-
-// Initialize NPM for operations
-var initNPM = _.memoize(function() {
- return Promise.nfcall(npm.load, {
- silent: true,
- loglevel: 'silent'
- });
-});
-
-// Link a plugin for use in a specific book
-function linkPlugin(book, pluginPath) {
- book.log('linking', pluginPath);
-}
-
-// Resolve the latest version for a plugin
-function resolveVersion(plugin) {
- var npnName = npmId(plugin);
-
- return initNPM()
- .then(function() {
- return Promise.nfcall(npm.commands.view, [npnName+'@*', 'engines'], true);
- })
- .then(function(versions) {
- return _.chain(versions)
- .pairs()
- .map(function(v) {
- return {
- version: v[0],
- gitbook: (v[1].engines || {}).gitbook
- };
- })
- .filter(function(v) {
- return v.gitbook && gitbook.satisfies(v.gitbook);
- })
- .sort(function(v1, v2) {
- return semver.lt(v1.version, v2.version)? 1 : -1;
- })
- .pluck('version')
- .first()
- .value();
- });
-}
-
-
-// Install a plugin in a book
-function installPlugin(book, plugin, version) {
- book.log.info.ln('installing plugin', plugin);
-
- var npnName = npmId(plugin);
-
- return Promise()
- .then(function() {
- if (version) return version;
-
- book.log.info.ln('No version specified, resolve plugin "' + plugin + '"');
- return resolveVersion(plugin);
- })
-
- // Install the plugin with the resolved version
- .then(function(version) {
- if (!version) {
- throw new Error('Found no satisfactory version for plugin "' + plugin + '"');
- }
-
- book.log.info.ln('install plugin "' + plugin +'" from npm ('+npnName+') with version', version);
- return Promise.nfcall(npmi, {
- 'name': npnName,
- 'version': version,
- 'path': book.root,
- 'npmLoad': {
- 'loglevel': 'silent',
- 'loaded': true,
- 'prefix': book.root
- }
- });
- })
- .then(function() {
- book.log.info.ok('plugin "' + plugin + '" installed with success');
- });
-}
-
-// List all packages installed inside a folder
-// Returns an ordered list of plugins
-function listInstalled(folder) {
- var options = {
- dev: false,
- log: function() {},
- depth: 4
- };
- var results = [];
-
- function onPackage(pkg, isRoot) {
- if (!validateId(pkg.name)){
- if (!isRoot) return;
- } else {
- results.push({
- name: pluginId(pkg.name),
- version: pkg.version,
- path: pkg.realPath,
- depth: pkg.depth
- });
- }
-
- _.each(pkg.dependencies, function(dep) {
- onPackage(dep);
- });
- }
-
- return Promise.nfcall(readInstalled, folder, options)
- .then(function(data) {
- onPackage(data, true);
- return _.uniq(results, 'name');
- });
-}
-
-// List installed plugins for a book (defaults and installed)
-function listPlugins(book) {
- return Promise.all([
- listInstalled(path.resolve(__dirname, '../..')),
- listInstalled(book.root),
- book.originalRoot? listInstalled(book.originalRoot) : Promise([]),
- book.isLanguageBook()? listInstalled(book.parent.root) : Promise([])
- ])
- .spread(function() {
- var args = _.toArray(arguments);
-
- var results = _.reduce(args, function(out, a) {
- return out.concat(a);
- }, []);
-
- return _.uniq(results, 'name');
- });
-}
-
-module.exports = {
- npmId: npmId,
- pluginId: pluginId,
- validateId: validateId,
-
- resolve: resolveVersion,
- link: linkPlugin,
- install: installPlugin,
- list: listPlugins,
- listInstalled: listInstalled
-};
diff --git a/lib/template/blocks.js b/lib/template/blocks.js
deleted file mode 100644
index 5dfb0c8..0000000
--- a/lib/template/blocks.js
+++ /dev/null
@@ -1,36 +0,0 @@
-var _ = require('lodash');
-
-module.exports = {
- // Return non-parsed html
- // since blocks are by default non-parsable, a simple identity method works fine
- html: _.identity,
-
- // Highlight a code block
- // This block can be replaced by plugins
- code: function(blk) {
- return {
- html: false,
- body: blk.body
- };
- },
-
- // Render some markdown to HTML
- markdown: function(blk) {
- return this.book.renderInline('markdown', blk.body)
- .then(function(out) {
- return { body: out };
- });
- },
- asciidoc: function(blk) {
- return this.book.renderInline('asciidoc', blk.body)
- .then(function(out) {
- return { body: out };
- });
- },
- markup: function(blk) {
- return this.book.renderInline(this.ctx.file.type, blk.body)
- .then(function(out) {
- return { body: out };
- });
- }
-};
diff --git a/lib/template/filters.js b/lib/template/filters.js
deleted file mode 100644
index ac68b82..0000000
--- a/lib/template/filters.js
+++ /dev/null
@@ -1,15 +0,0 @@
-var moment = require('moment');
-
-
-module.exports = {
- // Format a date
- // ex: 'MMMM Do YYYY, h:mm:ss a
- date: function(time, format) {
- return moment(time).format(format);
- },
-
- // Relative Time
- dateFromNow: function(time) {
- return moment(time).fromNow();
- }
-};
diff --git a/lib/template/index.js b/lib/template/index.js
deleted file mode 100644
index ae11bc9..0000000
--- a/lib/template/index.js
+++ /dev/null
@@ -1,552 +0,0 @@
-var _ = require('lodash');
-var path = require('path');
-var nunjucks = require('nunjucks');
-var escapeStringRegexp = require('escape-string-regexp');
-
-var Promise = require('../utils/promise');
-var error = require('../utils/error');
-var parsers = require('../parsers');
-var defaultBlocks = require('./blocks');
-var defaultFilters = require('./filters');
-var Loader = require('./loader');
-
-var NODE_ENDARGS = '%%endargs%%';
-
-// Return extension name for a specific block
-function blockExtName(name) {
- return 'Block'+name+'Extension';
-}
-
-// Normalize the result of block process function
-function normBlockResult(blk) {
- if (_.isString(blk)) blk = { body: blk };
- return blk;
-}
-
-// Extract kwargs from an arguments array
-function extractKwargs(args) {
- var last = _.last(args);
- return (_.isObject(last) && last.__keywords)? args.pop() : {};
-}
-
-function TemplateEngine(output) {
- this.output = output;
- this.book = output.book;
- this.log = this.book.log;
-
- // Create file loader
- this.loader = new Loader(this);
-
- // Create nunjucks instance
- this.env = new nunjucks.Environment(
- this.loader,
- {
- // Escaping is done after by the asciidoc/markdown parser
- autoescape: false,
-
- // Syntax
- tags: {
- blockStart: '{%',
- blockEnd: '%}',
- variableStart: '{{',
- variableEnd: '}}',
- commentStart: '{###',
- commentEnd: '###}'
- }
- }
- );
-
- // List of tags shortcuts
- this.shortcuts = [];
-
- // Map of blocks bodies (that requires post-processing)
- this.blockBodies = {};
-
- // Map of added blocks
- this.blocks = {};
-
- // Bind methods
- _.bindAll(this);
-
- // Add default blocks and filters
- this.addBlocks(defaultBlocks);
- this.addFilters(defaultFilters);
-
- // Build context for this book with depreacted fields
- this.ctx = {
- template: this,
- book: this.book,
- output: this.output
- };
- error.deprecateField(this.ctx, 'generator', this.output.name, '"generator" property is deprecated, use "output.generator" instead');
-}
-
-/*
- Bind a function to a context
- Filters and blocks are binded to this context.
-
- @param {Function}
- @param {Function}
-*/
-TemplateEngine.prototype.bindContext = function(func) {
- var that = this;
-
- return function() {
- var ctx = _.extend({
- ctx: this.ctx
- }, that.ctx);
-
- return func.apply(ctx, arguments);
- };
-};
-
-/*
- Interpolate a string content to replace shortcuts according to the filetype.
-
- @param {String} filepath
- @param {String} source
- @param {String}
-*/
-TemplateEngine.prototype.interpolate = function(filepath, source) {
- var parser = parsers.getByExt(path.extname(filepath));
- var type = parser? parser.name : null;
-
- return this.applyShortcuts(type, source);
-};
-
-/*
- Add a new custom filter, it bind to the right context
-
- @param {String}
- @param {Function}
-*/
-TemplateEngine.prototype.addFilter = function(filterName, func) {
- try {
- this.env.getFilter(filterName);
- this.log.error.ln('conflict in filters, "'+filterName+'" is already set');
- return false;
- } catch(e) {
- // Filter doesn't exist
- }
-
- this.log.debug.ln('add filter "'+filterName+'"');
- this.env.addFilter(filterName, this.bindContext(function() {
- var ctx = this;
- var args = Array.prototype.slice.apply(arguments);
- var callback = _.last(args);
-
- Promise()
- .then(function() {
- return func.apply(ctx, args.slice(0, -1));
- })
- .nodeify(callback);
- }), true);
- return true;
-};
-
-/*
- Add multiple filters at once
-
- @param {Map<String:Function>}
-*/
-TemplateEngine.prototype.addFilters = function(filters) {
- _.each(filters, function(filter, name) {
- this.addFilter(name, filter);
- }, this);
-};
-
-/*
- Return true if a block is defined
-
- @param {String}
-*/
-TemplateEngine.prototype.hasBlock = function(name) {
- return this.env.hasExtension(blockExtName(name));
-};
-
-/*
- Remove/Disable a block
-
- @param {String}
-*/
-TemplateEngine.prototype.removeBlock = function(name) {
- if (!this.hasBlock(name)) return;
-
- // Remove nunjucks extension
- this.env.removeExtension(blockExtName(name));
-
- // Cleanup shortcuts
- this.shortcuts = _.reject(this.shortcuts, {
- block: name
- });
-};
-
-/*
- Add a block.
- Using the extensions of nunjucks: https://mozilla.github.io/nunjucks/api.html#addextension
-
- @param {String} name
- @param {BlockDescriptor|Function} block
- @param {Function} block.process: function to be called to render the block
- @param {String} block.end: name of the end tag of this block (default to "end<name>")
- @param {Array<String>} block.blocks: list of inner blocks to parse
- @param {Array<Shortcut>} block.shortcuts: list of shortcuts to parse this block
-*/
-TemplateEngine.prototype.addBlock = function(name, block) {
- var that = this, Ext, extName;
-
- // Block can be a simple function
- if (_.isFunction(block)) block = { process: block };
-
- block = _.defaults(block || {}, {
- shortcuts: [],
- end: 'end'+name,
- blocks: []
- });
-
- extName = blockExtName(name);
-
- if (!block.process) {
- throw new Error('Invalid block "' + name + '", it should have a "process" method');
- }
-
- if (this.hasBlock(name) && !defaultBlocks[name]) {
- this.log.warn.ln('conflict in blocks, "'+name+'" is already defined');
- }
-
- // Cleanup previous block
- this.removeBlock(name);
-
- this.log.debug.ln('add block \''+name+'\'');
- this.blocks[name] = block;
-
- Ext = function () {
- this.tags = [name];
-
- this.parse = function(parser, nodes) {
- var lastBlockName = null;
- var lastBlockArgs = null;
- var allBlocks = block.blocks.concat([block.end]);
-
- // Parse first block
- var tok = parser.nextToken();
- lastBlockArgs = parser.parseSignature(null, true);
- parser.advanceAfterBlockEnd(tok.value);
-
- var args = new nodes.NodeList();
- var bodies = [];
- var blockNamesNode = new nodes.Array(tok.lineno, tok.colno);
- var blockArgCounts = new nodes.Array(tok.lineno, tok.colno);
-
- // Parse while we found "end<block>"
- do {
- // Read body
- var currentBody = parser.parseUntilBlocks.apply(parser, allBlocks);
-
- // Handle body with previous block name and args
- blockNamesNode.addChild(new nodes.Literal(args.lineno, args.colno, lastBlockName));
- blockArgCounts.addChild(new nodes.Literal(args.lineno, args.colno, lastBlockArgs.children.length));
- bodies.push(currentBody);
-
- // Append arguments of this block as arguments of the run function
- _.each(lastBlockArgs.children, function(child) {
- args.addChild(child);
- });
-
- // Read new block
- lastBlockName = parser.nextToken().value;
-
- // Parse signature and move to the end of the block
- if (lastBlockName != block.end) {
- lastBlockArgs = parser.parseSignature(null, true);
- }
-
- parser.advanceAfterBlockEnd(lastBlockName);
- } while (lastBlockName != block.end);
-
- args.addChild(blockNamesNode);
- args.addChild(blockArgCounts);
- args.addChild(new nodes.Literal(args.lineno, args.colno, NODE_ENDARGS));
-
- return new nodes.CallExtensionAsync(this, 'run', args, bodies);
- };
-
- this.run = function(context) {
- var fnArgs = Array.prototype.slice.call(arguments, 1);
-
- var args;
- var blocks = [];
- var bodies = [];
- var blockNames;
- var blockArgCounts;
- var callback;
-
- // Extract callback
- callback = fnArgs.pop();
-
- // Detect end of arguments
- var endArgIndex = fnArgs.indexOf(NODE_ENDARGS);
-
- // Extract arguments and bodies
- args = fnArgs.slice(0, endArgIndex);
- bodies = fnArgs.slice(endArgIndex + 1);
-
- // Extract block counts
- blockArgCounts = args.pop();
- blockNames = args.pop();
-
- // Recreate list of blocks
- _.each(blockNames, function(name, i) {
- var countArgs = blockArgCounts[i];
- var blockBody = bodies.shift();
-
- var blockArgs = countArgs > 0? args.slice(0, countArgs) : [];
- args = args.slice(countArgs);
- var blockKwargs = extractKwargs(blockArgs);
-
- blocks.push({
- name: name,
- body: blockBody(),
- args: blockArgs,
- kwargs: blockKwargs
- });
- });
-
- var mainBlock = blocks.shift();
- mainBlock.blocks = blocks;
-
- Promise()
- .then(function() {
- return that.applyBlock(name, mainBlock, context);
- })
-
- // Process the block returned
- .then(that.processBlock)
- .nodeify(callback);
- };
- };
-
- // Add the Extension
- this.env.addExtension(extName, new Ext());
-
- // Add shortcuts if any
- if (!_.isArray(block.shortcuts)) {
- block.shortcuts = [block.shortcuts];
- }
-
- _.each(block.shortcuts, function(shortcut) {
- this.log.debug.ln('add template shortcut from "'+shortcut.start+'" to block "'+name+'" for parsers ', shortcut.parsers);
- this.shortcuts.push({
- block: name,
- parsers: shortcut.parsers,
- start: shortcut.start,
- end: shortcut.end,
- tag: {
- start: name,
- end: block.end
- }
- });
- }, this);
-};
-
-/*
- Add multiple blocks at once
-
- @param {Array<BlockDescriptor>}
-*/
-TemplateEngine.prototype.addBlocks = function(blocks) {
- _.each(blocks, function(block, name) {
- this.addBlock(name, block);
- }, this);
-};
-
-/*
- Apply a block to some content
- This method result depends on the type of block (async or sync)
-
-
- @param {String} name: name of the block type to apply
- @param {Block} blk: content of the block
- @param {Object} ctx: context of execution of the block
- @return {Block|Promise<Block>}
-*/
-TemplateEngine.prototype.applyBlock = function(name, blk, ctx) {
- var func, block, r;
-
- block = this.blocks[name];
- if (!block) throw new Error('Block not found "'+name+'"');
- if (_.isString(blk)) {
- blk = {
- body: blk
- };
- }
-
- blk = _.defaults(blk, {
- args: [],
- kwargs: {},
- blocks: []
- });
-
- // Bind and call block processor
- func = this.bindContext(block.process);
- r = func.call(ctx || {}, blk);
-
- if (Promise.isPromiseAlike(r)) return Promise(r).then(normBlockResult);
- else return normBlockResult(r);
-};
-
-/*
- Process the result of block in a context. It returns the content to append to the output.
- It can return an "anchor" that will be replaced by "replaceBlocks" in "postProcess"
-
- @param {Block}
- @return {String}
-*/
-TemplateEngine.prototype.processBlock = function(blk) {
- blk = _.defaults(blk, {
- parse: false,
- post: undefined
- });
- blk.id = _.uniqueId('blk');
-
- var toAdd = (!blk.parse) || (blk.post !== undefined);
-
- // Add to global map
- if (toAdd) this.blockBodies[blk.id] = blk;
-
- // Parsable block, just return it
- if (blk.parse) {
- return blk.body;
- }
-
- // Return it as a position marker
- return '{{-%'+blk.id+'%-}}';
-};
-
-/*
- Render a string (without post processing)
-
- @param {String} content: template's content to render
- @param {Object} context
- @param {Object} options
- @param {String} options.path: pathname to the template
- @return {Promise<String>}
-*/
-TemplateEngine.prototype.render = function(content, context, options) {
- options = _.defaults(options || {}, {
- path: null
- });
- var filename = options.path;
-
- // Setup path and type
- if (options.path) {
- options.path = this.book.resolve(options.path);
- }
-
- // Replace shortcuts
- content = this.applyShortcuts(options.type, content);
-
- return Promise.nfcall(this.env.renderString.bind(this.env), content, context, options)
- .fail(function(err) {
- throw error.TemplateError(err, {
- filename: filename || '<inline>'
- });
- });
-};
-
-/*
- Render a string (with post processing)
-
- @param {String} content: template's content to render
- @param {Object} context
- @param {Object} options
- @return {Promise<String>}
-*/
-TemplateEngine.prototype.renderString = function(content, context, options) {
- return this.render(content, context, options)
- .then(this.postProcess);
-};
-
-/*
- Apply a shortcut of block to a template
-
- @param {String} content
- @param {Shortcut} shortcut
- @return {String}
-*/
-TemplateEngine.prototype.applyShortcut = function(content, shortcut) {
- var regex = new RegExp(
- escapeStringRegexp(shortcut.start) + '([\\s\\S]*?[^\\$])' + escapeStringRegexp(shortcut.end),
- 'g'
- );
- return content.replace(regex, function(all, match) {
- return '{% '+shortcut.tag.start+' %}'+ match + '{% '+shortcut.tag.end+' %}';
- });
-};
-
-
-/*
- Apply all shortcut of blocks to a template
-
- @param {String} type: type of template ("markdown", "asciidoc")
- @param {String} content
- @return {String}
-*/
-TemplateEngine.prototype.applyShortcuts = function(type, content) {
- return _.chain(this.shortcuts)
- .filter(function(shortcut) {
- return _.contains(shortcut.parsers, type);
- })
- .reduce(this.applyShortcut, content)
- .value();
-};
-
-/*
- Replace position markers of blocks by body after processing
- This is done to avoid that markdown/asciidoc processer parse the block content
-
- @param {String} content
- @return {String}
-*/
-TemplateEngine.prototype.replaceBlocks = function(content) {
- var that = this;
-
- return content.replace(/\{\{\-\%([\s\S]+?)\%\-\}\}/g, function(match, key) {
- var blk = that.blockBodies[key];
- if (!blk) return match;
-
- var body = blk.body;
-
- return body;
- });
-};
-
-
-
-/*
- Post process templating result: remplace block's anchors and apply "post"
-
- @param {String} content
- @return {Promise<String>}
-*/
-TemplateEngine.prototype.postProcess = function(content) {
- var that = this;
-
- return Promise(content)
- .then(that.replaceBlocks)
- .then(function(_content) {
- return Promise.serie(that.blockBodies, function(blk, blkId) {
- return Promise()
- .then(function() {
- if (!blk.post) return;
- return blk.post();
- })
- .then(function() {
- delete that.blockBodies[blkId];
- });
- })
- .thenResolve(_content);
- });
-};
-
-module.exports = TemplateEngine;
diff --git a/lib/template/loader.js b/lib/template/loader.js
deleted file mode 100644
index 23d179a..0000000
--- a/lib/template/loader.js
+++ /dev/null
@@ -1,42 +0,0 @@
-var nunjucks = require('nunjucks');
-var location = require('../utils/location');
-
-/*
-Simple nunjucks loader which is passing the reponsability to the Output
-*/
-
-var Loader = nunjucks.Loader.extend({
- async: true,
-
- init: function(engine, opts) {
- this.engine = engine;
- this.output = engine.output;
- },
-
- getSource: function(sourceURL, callback) {
- var that = this;
-
- this.output.onGetTemplate(sourceURL)
- .then(function(out) {
- // We disable cache since content is modified (shortcuts, ...)
- out.noCache = true;
-
- // Transform template before runnign it
- out.source = that.engine.interpolate(out.path, out.source);
-
- return out;
- })
- .nodeify(callback);
- },
-
- resolve: function(from, to) {
- return this.output.onResolveTemplate(from, to);
- },
-
- // Handle all files as relative, so that nunjucks pass responsability to 'resolve'
- isRelative: function(filename) {
- return location.isRelative(filename);
- }
-});
-
-module.exports = Loader;