Remove templating blocks post process

This commit is contained in:
Samy Pesse
2016-10-07 11:53:02 +02:00
parent 9cedcc6b6e
commit 47a27fc12d
8 changed files with 13 additions and 288 deletions
@@ -1,42 +0,0 @@
const Immutable = require('immutable');
const 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,
blocks: Immutable.fromJS(blocks)
});
};
module.exports = TemplateOutput;
+5 -12
View File
@@ -39,37 +39,30 @@ function generatePage(output, page) {
return callPageHook('page:before', output, resultPage)
// Escape code blocks with raw tags
.then(function(currentPage) {
.then((currentPage) => {
return parser.preparePage(currentPage.getContent());
})
// Render templating syntax
.then(function(content) {
.then((content) => {
const absoluteFilePath = path.join(book.getContentRoot(), filePath);
return Templating.render(engine, absoluteFilePath, content, context);
})
.then(function(output) {
const content = output.getContent();
.then((content) => {
return parser.parsePage(content)
.then(function(result) {
return output.setContent(result.content);
});
})
// Post processing for templating syntax
.then(function(output) {
return Templating.postRender(engine, output);
})
// Return new page
.then(function(content) {
.then((content) => {
return resultPage.set('content', content);
})
// Call final hook
.then(function(currentPage) {
.then((currentPage) => {
return callPageHook('page', output, currentPage);
});
})
@@ -1,51 +0,0 @@
const TemplateEngine = require('../../models/templateEngine');
const TemplateBlock = require('../../models/templateBlock');
const renderTemplate = require('../render');
const postRender = require('../postRender');
describe('postRender', function() {
let testPost;
const engine = TemplateEngine.create({
blocks: [
TemplateBlock.create('lower', function(blk) {
return blk.body.toLowerCase();
}),
TemplateBlock.create('prefix', function(blk) {
return {
body: '_' + blk.body + '_',
post() {
testPost = true;
}
};
})
]
});
it('should correctly replace block', function() {
return renderTemplate(engine, 'README.md', 'Hello {% lower %}Samy{% endlower %}')
.then(function(output) {
expect(output.getContent()).toMatch(/Hello \{\{\-([\S]+)\-\}\}/);
expect(output.getBlocks().size).toBe(1);
return postRender(engine, output);
})
.then(function(result) {
expect(result).toBe('Hello samy');
});
});
it('should correctly replace blocks', function() {
return renderTemplate(engine, 'README.md', 'Hello {% lower %}Samy{% endlower %}{% prefix %}Pesse{% endprefix %}')
.then(function(output) {
expect(output.getContent()).toMatch(/Hello \{\{\-([\S]+)\-\}\}\{\{\-([\S]+)\-\}\}/);
expect(output.getBlocks().size).toBe(2);
return postRender(engine, output);
})
.then(function(result) {
expect(result).toBe('Hello samy_Pesse_');
expect(testPost).toBe(true);
});
});
});
+4 -7
View File
@@ -1,10 +1,7 @@
module.exports = {
render: require('./render'),
renderFile: require('./renderFile'),
postRender: require('./postRender'),
replaceShortcuts: require('./replaceShortcuts'),
ConrefsLoader: require('./conrefsLoader'),
ThemesLoader: require('./themesLoader')
render: require('./render'),
renderFile: require('./renderFile'),
replaceShortcuts: require('./replaceShortcuts'),
ConrefsLoader: require('./conrefsLoader')
};
@@ -1,4 +1,4 @@
const Immutable = require('immutable');
const { List } = require('immutable');
const parsers = require('../parsers');
/**
@@ -7,13 +7,13 @@ const parsers = require('../parsers');
*
* @param {List<TemplateBlock>} engine
* @param {String} filePath
* @return {List<TemplateShortcut>}
* @return {List<TemplateShortcut>} shortcuts
*/
function listShortcuts(blocks, filePath) {
const parser = parsers.getForFile(filePath);
if (!parser) {
return Immutable.List();
return List();
}
return blocks
@@ -1,53 +0,0 @@
const Promise = require('../utils/promise');
/**
* 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) {
const newContent = content.replace(/\{\{\-\%([\s\S]+?)\%\-\}\}/g, function(match, key) {
let replacedWith = match;
const block = blocks.get(key);
if (block) {
replacedWith = replaceBlocks(block.get('body'), blocks);
}
return replacedWith;
});
return newContent;
}
/**
* Post render a template:
* - Execute "post" for blocks
* - Replace block content
*
* @param {TemplateEngine} engine
* @param {TemplateOutput} content
* @return {Promise<String>}
*/
function postRender(engine, output) {
const content = output.getContent();
const blocks = output.getBlocks();
const result = replaceBlocks(content, blocks);
return Promise.forEach(blocks, function(block) {
const post = block.get('post');
if (!post) {
return;
}
return post();
})
.thenResolve(result);
}
module.exports = postRender;
+1 -5
View File
@@ -1,6 +1,5 @@
const Promise = require('../utils/promise');
const timing = require('../utils/timing');
const TemplateOutput = require('../models/templateOutput');
const replaceShortcuts = require('./replaceShortcuts');
/**
@@ -10,7 +9,7 @@ const replaceShortcuts = require('./replaceShortcuts');
* @param {String} filePath: absolute path for the loader
* @param {String} content
* @param {Object} context (optional)
* @return {Promise<TemplateOutput>}
* @return {Promise<String>}
*/
function renderTemplate(engine, filePath, content, context) {
context = context || {};
@@ -35,9 +34,6 @@ function renderTemplate(engine, filePath, content, context) {
path: filePath
}
)
.then(function(content) {
return TemplateOutput.create(content, blocks);
})
);
}
@@ -1,115 +0,0 @@
const Immutable = require('immutable');
const nunjucks = require('nunjucks');
const fs = require('fs');
const path = require('path');
const PathUtils = require('../utils/path');
const ThemesLoader = nunjucks.Loader.extend({
init(searchPaths) {
this.searchPaths = Immutable.List(searchPaths)
.map(path.normalize);
},
/**
* Read source of a resolved filepath
* @param {String}
* @return {Object}
*/
getSource(fullpath) {
if (!fullpath) return null;
fullpath = this.resolve(null, fullpath);
const templateName = this.getTemplateName(fullpath);
if (!fullpath) {
return null;
}
let 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,
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() {
return true;
},
/**
* Get original search path containing a template
* @param {String} filepath
* @return {String} searchPath
*/
getSearchPath(filepath) {
return this.searchPaths
.sortBy(function(s) {
return -s.length;
})
.find(function(basePath) {
return (filepath && filepath.indexOf(basePath) === 0);
});
},
/**
* Get template name from a filepath
* @param {String} filepath
* @return {String} name
*/
getTemplateName(filepath) {
const 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(from, to) {
let searchPaths = this.searchPaths;
// Relative template like "./test.html"
if (PathUtils.isPureRelative(to) && from) {
return path.resolve(path.dirname(from), to);
}
// Determine in which search folder we currently are
const originalSearchPath = this.getSearchPath(from);
const 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) {
const currentIndex = searchPaths.indexOf(originalSearchPath);
searchPaths = searchPaths.slice(currentIndex + 1);
}
// Absolute template to resolve in root folder
const resultFolder = searchPaths.find(function(basePath) {
const p = path.resolve(basePath, to);
return (
p.indexOf(basePath) === 0
&& fs.existsSync(p)
);
});
if (!resultFolder) return null;
return path.resolve(resultFolder, to);
}
});
module.exports = ThemesLoader;