mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-20 17:43:24 +00:00
Remove templating blocks post process
This commit is contained in:
@@ -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;
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -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,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;
|
||||
Reference in New Issue
Block a user