mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-10-03 20:20:35 +00:00
Add mode TemplateOutput to represent {content + blocks} from template
This commit is contained in:
@@ -107,6 +107,16 @@ function encodeGlobal(output) {
|
||||
return outputFolder;
|
||||
},
|
||||
|
||||
/**
|
||||
Resolve a file from the output root
|
||||
|
||||
@param {String} fileName
|
||||
@return {String}
|
||||
*/
|
||||
resolve: function(fileName) {
|
||||
return path.resolve(outputFolder, fileName);
|
||||
},
|
||||
|
||||
/**
|
||||
Convert a filepath into an url
|
||||
@return {String}
|
||||
|
||||
+12
-42
@@ -8,8 +8,6 @@ var TemplateShortcut = require('./templateShortcut');
|
||||
|
||||
var NODE_ENDARGS = '%%endargs%%';
|
||||
|
||||
var blockBodies = {};
|
||||
|
||||
var TemplateBlock = Immutable.Record({
|
||||
// Name of block, also the start tag
|
||||
name: String(),
|
||||
@@ -26,9 +24,6 @@ var TemplateBlock = Immutable.Record({
|
||||
// List of shortcuts to replace with this block
|
||||
shortcuts: Immutable.Map(),
|
||||
|
||||
// Function to execute in post processing
|
||||
post: null,
|
||||
|
||||
parse: true
|
||||
}, 'TemplateBlock');
|
||||
|
||||
@@ -36,10 +31,6 @@ TemplateBlock.prototype.getName = function() {
|
||||
return this.get('name');
|
||||
};
|
||||
|
||||
TemplateBlock.prototype.getPost = function() {
|
||||
return this.get('post');
|
||||
};
|
||||
|
||||
TemplateBlock.prototype.getParse = function() {
|
||||
return this.get('parse');
|
||||
};
|
||||
@@ -85,7 +76,7 @@ TemplateBlock.prototype.getExtensionName = function() {
|
||||
|
||||
@return {Nunjucks.Extension}
|
||||
*/
|
||||
TemplateBlock.prototype.toNunjucksExt = function(mainContext) {
|
||||
TemplateBlock.prototype.toNunjucksExt = function(mainContext, blocksOutput) {
|
||||
var that = this;
|
||||
var name = this.getName();
|
||||
var endTag = this.getEndTag();
|
||||
@@ -195,7 +186,7 @@ TemplateBlock.prototype.toNunjucksExt = function(mainContext) {
|
||||
return that.applyBlock(mainBlock, ctx);
|
||||
})
|
||||
.then(function(result) {
|
||||
return that.blockResultToHtml(result);
|
||||
return that.blockResultToHtml(result, blocksOutput);
|
||||
})
|
||||
.nodeify(callback);
|
||||
};
|
||||
@@ -221,19 +212,19 @@ TemplateBlock.prototype.applyBlock = function(inner, context) {
|
||||
var r = processFn.call(context, inner);
|
||||
|
||||
if (Promise.isPromiseAlike(r)) {
|
||||
return r.then(this.handleBlockResult.bind(this));
|
||||
return r.then(this.normalizeBlockResult.bind(this));
|
||||
} else {
|
||||
return this.handleBlockResult(r);
|
||||
return this.normalizeBlockResult(r);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
Handle result from a block process function
|
||||
Normalize result from a block process function
|
||||
|
||||
@param {Object} result
|
||||
@param {Object|String} result
|
||||
@return {Object}
|
||||
*/
|
||||
TemplateBlock.prototype.handleBlockResult = function(result) {
|
||||
TemplateBlock.prototype.normalizeBlockResult = function(result) {
|
||||
if (is.string(result)) {
|
||||
result = { body: result };
|
||||
}
|
||||
@@ -246,15 +237,17 @@ TemplateBlock.prototype.handleBlockResult = function(result) {
|
||||
Convert a block result to HTML
|
||||
|
||||
@param {Object} result
|
||||
@param {Object} blocksOutput: stored post processing blocks in this object
|
||||
@return {String}
|
||||
*/
|
||||
TemplateBlock.prototype.blockResultToHtml = function(result) {
|
||||
TemplateBlock.prototype.blockResultToHtml = function(result, blocksOutput) {
|
||||
var parse = this.getParse();
|
||||
var indexedKey;
|
||||
var toIndex = (!parse) || (this.getPost() !== undefined);
|
||||
var toIndex = (!parse) || (result.post !== undefined);
|
||||
|
||||
if (toIndex) {
|
||||
indexedKey = TemplateBlock.indexBlockResult(result);
|
||||
indexedKey = genKey();
|
||||
blocksOutput[indexedKey] = result;
|
||||
}
|
||||
|
||||
// Parsable block, just return it
|
||||
@@ -267,29 +260,6 @@ TemplateBlock.prototype.blockResultToHtml = function(result) {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
Index a block result, and return the indexed key
|
||||
|
||||
@param {Object} blk
|
||||
@return {String}
|
||||
*/
|
||||
TemplateBlock.indexBlockResult = function(blk) {
|
||||
var key = genKey();
|
||||
blockBodies[key] = blk;
|
||||
|
||||
return key;
|
||||
};
|
||||
|
||||
/**
|
||||
Get a block results indexed for a specific key
|
||||
|
||||
@param {String} key
|
||||
@return {Object|undefined}
|
||||
*/
|
||||
TemplateBlock.getBlockResultByKey = function(key) {
|
||||
return blockBodies[key];
|
||||
};
|
||||
|
||||
/**
|
||||
Create a template block from a function or an object
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ TemplateEngine.prototype.getBlock = function(name) {
|
||||
|
||||
@return {Nunjucks.Environment}
|
||||
*/
|
||||
TemplateEngine.prototype.toNunjucks = function() {
|
||||
TemplateEngine.prototype.toNunjucks = function(blocksOutput) {
|
||||
var loader = this.getLoader();
|
||||
var blocks = this.getBlocks();
|
||||
var filters = this.getFilters();
|
||||
@@ -101,7 +101,7 @@ TemplateEngine.prototype.toNunjucks = function() {
|
||||
// Add blocks
|
||||
blocks.forEach(function(block) {
|
||||
var extName = block.getExtensionName();
|
||||
var Ext = block.toNunjucksExt(context);
|
||||
var Ext = block.toNunjucksExt(context, blocksOutput);
|
||||
|
||||
env.addExtension(extName, new Ext());
|
||||
});
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
var Immutable = require('immutable');
|
||||
|
||||
var TemplateOutput = Immutable.Record({
|
||||
// Text content of the template
|
||||
content: String(),
|
||||
|
||||
// Map of blocks to replace / post process
|
||||
blocks: Immutable.Map()
|
||||
}, 'TemplateOutput');
|
||||
|
||||
TemplateOutput.prototype.getContent = function() {
|
||||
return this.get('content');
|
||||
};
|
||||
|
||||
TemplateOutput.prototype.getBlocks = function() {
|
||||
return this.get('blocks');
|
||||
};
|
||||
|
||||
/**
|
||||
Update content of this output
|
||||
|
||||
@param {String} content
|
||||
@return {TemplateContent}
|
||||
*/
|
||||
TemplateOutput.prototype.setContent = function(content) {
|
||||
return this.set('content', content);
|
||||
};
|
||||
|
||||
/**
|
||||
Create a TemplateOutput from a text content
|
||||
and an object containing block definition
|
||||
|
||||
@param {String} content
|
||||
@param {Object} blocks
|
||||
@return {TemplateOutput}
|
||||
*/
|
||||
TemplateOutput.create = function(content, blocks) {
|
||||
return new TemplateOutput({
|
||||
content: content,
|
||||
blocks: Immutable.fromJS(blocks)
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = TemplateOutput;
|
||||
@@ -30,8 +30,8 @@ function getPDFTemplate(output, type) {
|
||||
return Templating.renderFile(engine, 'ebook/' + filePath, context)
|
||||
|
||||
// Inline css and assets
|
||||
.then(function(html) {
|
||||
return Promise.nfcall(juice.juiceResources, html, {
|
||||
.then(function(tplOut) {
|
||||
return Promise.nfcall(juice.juiceResources, tplOut.getContent(), {
|
||||
webResources: {
|
||||
relativeTo: outputRoot
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ function writeSummary(output) {
|
||||
return Templating.renderFile(engine, prefix + '/summary.html', context)
|
||||
|
||||
// Write it to the disk
|
||||
.then(function(html) {
|
||||
return writeFile(output, filePath, html);
|
||||
.then(function(tplOut) {
|
||||
return writeFile(output, filePath, tplOut.getContent());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -47,12 +47,18 @@ function generatePage(output, page) {
|
||||
return Templating.render(engine, filePath, content, context);
|
||||
})
|
||||
|
||||
// Render page using parser (markdown -> HTML)
|
||||
.then(parser.parsePage.bind(parser)).get('content')
|
||||
.then(function(output) {
|
||||
var content = output.getContent();
|
||||
|
||||
return parser.parsePage(content)
|
||||
.then(function(result) {
|
||||
return output.setContent(result.content);
|
||||
});
|
||||
})
|
||||
|
||||
// Post processing for templating syntax
|
||||
.then(function(content) {
|
||||
return Templating.postRender(engine, content);
|
||||
.then(function(output) {
|
||||
return Templating.postRender(engine, output);
|
||||
})
|
||||
|
||||
// Return new page
|
||||
|
||||
@@ -27,8 +27,8 @@ function onFinish(output) {
|
||||
return Templating.renderFile(engine, prefix + '/languages.html', context)
|
||||
|
||||
// Write it to the disk
|
||||
.then(function(html) {
|
||||
return writeFile(output, filePath, html);
|
||||
.then(function(tplOut) {
|
||||
return writeFile(output, filePath, tplOut.getContent());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -67,8 +67,8 @@ function onPage(output, page) {
|
||||
return Templating.renderFile(engine, prefix + '/page.html', context)
|
||||
|
||||
// Write it to the disk
|
||||
.then(function(html) {
|
||||
return writeFile(output, filePath, html);
|
||||
.then(function(tplOut) {
|
||||
return writeFile(output, filePath, tplOut.getContent());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
var Promise = require('../utils/promise');
|
||||
var replaceBlocks = require('./replaceBlocks');
|
||||
|
||||
|
||||
/**
|
||||
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 {Object} {blocks: Set, content: String}
|
||||
*/
|
||||
function replaceBlocks(content, blocks) {
|
||||
var newContent = content.replace(/\{\{\-\%([\s\S]+?)\%\-\}\}/g, function(match, key) {
|
||||
var replacedWith = match;
|
||||
|
||||
var block = blocks.get(key);
|
||||
if (block) {
|
||||
replacedWith = replaceBlocks(block.get('body'), blocks);
|
||||
}
|
||||
|
||||
return replacedWith;
|
||||
});
|
||||
|
||||
return newContent;
|
||||
}
|
||||
|
||||
/**
|
||||
Post render a template:
|
||||
@@ -7,22 +29,25 @@ var replaceBlocks = require('./replaceBlocks');
|
||||
- Replace block content
|
||||
|
||||
@param {TemplateEngine} engine
|
||||
@param {String} content
|
||||
@param {TemplateOutput} content
|
||||
@return {Promise<String>}
|
||||
*/
|
||||
function postRender(engine, content) {
|
||||
function postRender(engine, output) {
|
||||
var content = output.getContent();
|
||||
var blocks = output.getBlocks();
|
||||
|
||||
var result = replaceBlocks(content);
|
||||
|
||||
return Promise.forEach(result.blocks, function(blockType) {
|
||||
var block = engine.getBlock(blockType);
|
||||
var post = block.getPost();
|
||||
return Promise.forEach(blocks, function(block) {
|
||||
var post = block.get('post');
|
||||
|
||||
if (!post) {
|
||||
return;
|
||||
}
|
||||
|
||||
return post();
|
||||
})
|
||||
.thenResolve(result.content);
|
||||
.thenResolve(result);
|
||||
}
|
||||
|
||||
module.exports = postRender;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
var Promise = require('../utils/promise');
|
||||
var timing = require('../utils/timing');
|
||||
|
||||
var TemplateOutput = require('../models/templateOutput');
|
||||
var replaceShortcuts = require('./replaceShortcuts');
|
||||
|
||||
/**
|
||||
@@ -10,16 +10,23 @@ var replaceShortcuts = require('./replaceShortcuts');
|
||||
@param {String} filePath
|
||||
@param {String} content
|
||||
@param {Object} context
|
||||
@return {Promise<String>}
|
||||
@return {Promise<TemplateOutput>}
|
||||
*/
|
||||
function renderTemplate(engine, filePath, content, context) {
|
||||
context = context || {};
|
||||
var env = engine.toNunjucks();
|
||||
|
||||
// Mutable objects to contains all blocks requiring post-processing
|
||||
var blocks = {};
|
||||
|
||||
// Create nunjucks environment
|
||||
var env = engine.toNunjucks(blocks);
|
||||
|
||||
// Replace shortcuts from plugin's blocks
|
||||
content = replaceShortcuts(engine, filePath, content);
|
||||
|
||||
return timing.measure(
|
||||
'template.render',
|
||||
|
||||
Promise.nfcall(
|
||||
env.renderString.bind(env),
|
||||
content,
|
||||
@@ -28,6 +35,9 @@ function renderTemplate(engine, filePath, content, context) {
|
||||
path: filePath
|
||||
}
|
||||
)
|
||||
.then(function(content) {
|
||||
return TemplateOutput.create(content, blocks);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ var render = require('./render');
|
||||
@param {TemplateEngine} engine
|
||||
@param {String} filePath
|
||||
@param {Object} context
|
||||
@return {Promise<String>}
|
||||
@return {Promise<TemplateOutput>}
|
||||
*/
|
||||
function renderTemplateFile(engine, filePath, context) {
|
||||
var loader = engine.getLoader();
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
var Immutable = require('immutable');
|
||||
var TemplateBlock = require('../models/templateBlock');
|
||||
|
||||
/**
|
||||
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 {Object} {blocks: Set, content: String}
|
||||
*/
|
||||
function replaceBlocks(content) {
|
||||
var blockTypes = new Immutable.Set();
|
||||
var newContent = content.replace(/\{\{\-\%([\s\S]+?)\%\-\}\}/g, function(match, key) {
|
||||
var replacedWith = match;
|
||||
|
||||
var block = TemplateBlock.getBlockResultByKey(key);
|
||||
if (block) {
|
||||
var result = replaceBlocks(block.body);
|
||||
|
||||
blockTypes = blockTypes.add(block.name);
|
||||
blockTypes = blockTypes.concat(result.blocks);
|
||||
replacedWith = result.content;
|
||||
}
|
||||
|
||||
return replacedWith;
|
||||
});
|
||||
|
||||
return {
|
||||
content: newContent,
|
||||
blocks: blockTypes
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = replaceBlocks;
|
||||
Reference in New Issue
Block a user