Refactor precompiler API into two phase

Load templates and then parse them in a distinct operation. This will allow us to use other input sources such as stdin and strings.
This commit is contained in:
kpdecker
2015-08-04 09:26:19 -05:00
parent ba31ef8ae4
commit a62cbad95a
3 changed files with 155 additions and 123 deletions
+89 -77
View File
@@ -5,28 +5,73 @@ import {basename} from 'path';
import {SourceMapConsumer, SourceNode} from 'source-map';
import uglify from 'uglify-js';
module.exports.loadTemplates = function(opts) {
// Build file extension pattern
let extension = (opts.extension || 'handlebars').replace(/[\\^$*+?.():=!|{}\-\[\]]/g, function(arg) { return '\\' + arg; });
extension = new RegExp('\\.' + extension + '$');
let ret = [];
function processTemplate(template, root) {
let path = template,
stat;
try {
stat = fs.statSync(template);
} catch (err) {
throw new Handlebars.Exception(`Unable to open template file "${template}"`);
}
if (stat.isDirectory()) {
opts.hasDirectory = true;
fs.readdirSync(template).map(function(file) {
let childPath = template + '/' + file;
if (extension.test(childPath) || fs.statSync(childPath).isDirectory()) {
processTemplate(childPath, root || template);
}
});
} else {
let data = fs.readFileSync(path, 'utf8');
if (opts.bom && data.indexOf('\uFEFF') === 0) {
data = data.substring(1);
}
// Clean the template name
if (!root) {
template = basename(template);
} else if (template.indexOf(root) === 0) {
template = template.substring(root.length + 1);
}
template = template.replace(extension, '');
ret.push({
path: path,
name: template,
source: data
});
}
}
opts.templates.forEach(function(template) {
processTemplate(template, opts.root);
});
opts.templates = ret;
};
module.exports.cli = function(opts) {
if (opts.version) {
console.log(Handlebars.VERSION);
return;
}
if (!opts.templates.length) {
if (!opts.templates.length && !opts.hasDirectory) {
throw new Handlebars.Exception('Must define at least one template or directory.');
}
opts.templates.forEach(function(template) {
try {
fs.statSync(template);
} catch (err) {
throw new Handlebars.Exception(`Unable to open template file "${template}"`);
}
});
if (opts.simple && opts.min) {
throw new Handlebars.Exception('Unable to minimize simple output');
}
if (opts.simple && (opts.templates.length !== 1 || fs.statSync(opts.templates[0]).isDirectory())) {
if (opts.simple && (opts.templates.length !== 1 || opts.hasDirectory)) {
throw new Handlebars.Exception('Unable to output multiple templates in simple mode');
}
@@ -41,10 +86,6 @@ module.exports.cli = function(opts) {
}
}
// Build file extension pattern
let extension = opts.extension.replace(/[\\^$*+?.():=!|{}\-\[\]]/g, function(arg) { return '\\' + arg; });
extension = new RegExp('\\.' + extension + '$');
let output = new SourceNode();
if (!opts.simple) {
if (opts.amd) {
@@ -63,76 +104,47 @@ module.exports.cli = function(opts) {
}
output.add('{};\n');
}
function processTemplate(template, root) {
let path = template,
stat = fs.statSync(path);
if (stat.isDirectory()) {
fs.readdirSync(template).map(function(file) {
let childPath = template + '/' + file;
if (extension.test(childPath) || fs.statSync(childPath).isDirectory()) {
processTemplate(childPath, root || template);
}
});
} else {
let data = fs.readFileSync(path, 'utf8');
if (opts.bom && data.indexOf('\uFEFF') === 0) {
data = data.substring(1);
}
let options = {
knownHelpers: known,
knownHelpersOnly: opts.o
};
if (opts.map) {
options.srcName = path;
}
if (opts.data) {
options.data = true;
}
// Clean the template name
if (!root) {
template = basename(template);
} else if (template.indexOf(root) === 0) {
template = template.substring(root.length + 1);
}
template = template.replace(extension, '');
let precompiled = Handlebars.precompile(data, options);
// If we are generating a source map, we have to reconstruct the SourceNode object
if (opts.map) {
let consumer = new SourceMapConsumer(precompiled.map);
precompiled = SourceNode.fromStringWithSourceMap(precompiled.code, consumer);
}
if (opts.simple) {
output.add([precompiled, '\n']);
} else if (opts.partial) {
if (opts.amd && (opts.templates.length == 1 && !fs.statSync(opts.templates[0]).isDirectory())) {
output.add('return ');
}
output.add(['Handlebars.partials[\'', template, '\'] = template(', precompiled, ');\n']);
} else {
if (opts.amd && (opts.templates.length == 1 && !fs.statSync(opts.templates[0]).isDirectory())) {
output.add('return ');
}
output.add(['templates[\'', template, '\'] = template(', precompiled, ');\n']);
}
}
}
opts.templates.forEach(function(template) {
processTemplate(template, opts.root);
let options = {
knownHelpers: known,
knownHelpersOnly: opts.o
};
if (opts.map) {
options.srcName = template.path;
}
if (opts.data) {
options.data = true;
}
let precompiled = Handlebars.precompile(template.source, options);
// If we are generating a source map, we have to reconstruct the SourceNode object
if (opts.map) {
let consumer = new SourceMapConsumer(precompiled.map);
precompiled = SourceNode.fromStringWithSourceMap(precompiled.code, consumer);
}
if (opts.simple) {
output.add([precompiled, '\n']);
} else if (opts.partial) {
if (opts.amd && (opts.templates.length == 1 && !opts.hasDirectory)) {
output.add('return ');
}
output.add(['Handlebars.partials[\'', template.name, '\'] = template(', precompiled, ');\n']);
} else {
if (opts.amd && (opts.templates.length == 1 && !opts.hasDirectory)) {
output.add('return ');
}
output.add(['templates[\'', template.name, '\'] = template(', precompiled, ');\n']);
}
});
// Output the content
if (!opts.simple) {
if (opts.amd) {
if (opts.templates.length > 1 || (opts.templates.length == 1 && fs.statSync(opts.templates[0]).isDirectory())) {
if (opts.templates.length > 1 || (opts.templates.length == 1 && opts.hasDirectory)) {
if (opts.partial) {
output.add('return Handlebars.partials;\n');
} else {