Port Spacebars' templating package to batch plugins

This commit is contained in:
Slava Kim
2015-06-16 14:55:22 -07:00
parent b59b73d34f
commit 3c7ccbf169
3 changed files with 104 additions and 41 deletions

View File

@@ -9,14 +9,18 @@ Package.describe({
// registry and a default templating system, ideally per-package.
Package.registerBuildPlugin({
name: "compileTemplates",
name: "compileTemplatesBatch",
// minifiers is a weak dependency of spacebars-compiler; adding it here
// ensures that the output is minified. (Having it as a weak dependency means
// that we don't ship uglify etc with built apps just because
// boilerplate-generator uses spacebars-compiler.)
// XXX maybe uglify should be applied by this plugin instead of via magic
// weak dependency.
use: ['minifiers', 'spacebars-compiler'],
use: [
'minifiers',
'spacebars-compiler',
'compiler-plugin'
],
sources: [
'plugin/html_scanner.js',
'plugin/compile-templates.js'

View File

@@ -1,52 +1,89 @@
var path = Npm.require('path');
var doHTMLScanning = function (compileStep, htmlScanner) {
// XXX the way we deal with encodings here is sloppy .. should get
// religion on that
var contents = compileStep.read().toString('utf8');
function TemplateCompiler () {}
TemplateCompiler.prototype.processFilesForTarget = function (files) {
var bodyAttrs = {};
var bodyAttrsOrigin = {};
files.forEach(function (file) {
var scanned = doHTMLScanning(file, html_scanner);
Object.keys(scanned.bodyAttrs).forEach(function (attr) {
var val = scanned.bodyAttrs[attr];
if (bodyAttrs.hasOwnProperty(attr) && bodyAttrs[attr] !== val) {
// two conflicting attributes on <body> tags in two different template
// files
var conflictingFilesStr = [bodyAttrsOrigin[attr], file].map(function (f) {
return f.getPathInPackage();
}).join(', ');
file.error({
message: [
"<body> declarations have conflicting values for the '",
attr,
"' attribute in the following files: ",
conflictingFilesStr,
"."
].join('')
});
return;
}
bodyAttrs[attr] = val;
bodyAttrsOrigin[attr] = file;
});
});
};
var doHTMLScanning = function (inputFile, htmlScanner) {
var contents = inputFile.getContentsAsString();
try {
var results = htmlScanner.scan(contents, compileStep.inputPath);
var results = htmlScanner.scan(contents, inputFile.getPathInPackage());
} catch (e) {
if (e instanceof htmlScanner.ParseError) {
compileStep.error({
if ((e instanceof htmlScanner.ParseError) || (e instanceof htmlScanner.BodyAttrsError)) {
inputFile.error({
message: e.message,
sourcePath: compileStep.inputPath,
line: e.line
});
return;
} else
return null;
} else {
throw e;
}
}
if (results.head)
compileStep.appendDocument({ section: "head", data: results.head });
inputFile.addHtml({ section: "head", data: results.head });
if (results.body)
compileStep.appendDocument({ section: "body", data: results.body });
inputFile.addHtml({ section: "body", data: results.body });
if (results.js) {
var path_part = path.dirname(compileStep.inputPath);
if (path_part === '.')
path_part = '';
if (path_part.length && path_part !== path.sep)
path_part = path_part + path.sep;
var ext = path.extname(compileStep.inputPath);
var basename = path.basename(compileStep.inputPath, ext);
var filePath = inputFile.getPathInPackage();
var pathPart = path.dirname(filePath);
if (pathPart === '.')
pathPart = '';
if (pathPart.length && pathPart !== path.sep)
pathPart = pathPart + path.sep;
var ext = path.extname(filePath);
var basename = path.basename(filePath, ext);
// XXX generate a source map
compileStep.addJavaScript({
path: path.join(path_part, "template." + basename + ".js"),
sourcePath: compileStep.inputPath,
inputFile.addJavaScript({
path: path.join(pathPart, "template." + basename + ".js"),
data: results.js
});
}
return {
bodyAttrs: results.bodyAttrs
};
};
// XXX BBP rewrite to registerCompiler
Plugin.registerSourceHandler(
"html", {isTemplate: true, archMatching: 'web'},
function (compileStep) {
doHTMLScanning(compileStep, html_scanner);
}
);
Plugin.registerCompiler({
extensions: ['html'],
archMatching: 'web',
isTemplate: true
}, function () {
return new TemplateCompiler();
});

View File

@@ -7,10 +7,8 @@ html_scanner = {
// and ignores top-level HTML comments.
// Has fields 'message', 'line', 'file'
ParseError: function () {
},
bodyAttributes : [],
ParseError: function () {},
BodyAttrsError: function () {},
scan: function (contents, source_name) {
var rest = contents;
@@ -21,14 +19,23 @@ html_scanner = {
index += amount;
};
var throwParseError = function (msg, overrideIndex) {
var ret = new html_scanner.ParseError;
ret.message = msg || "bad formatting in template file";
var throwSpecialError = function (msg, errorClass, overrideIndex) {
var ret = new errorClass;
ret.message = msg;
ret.file = source_name;
var theIndex = (typeof overrideIndex === 'number' ? overrideIndex : index);
ret.line = contents.substring(0, theIndex).split('\n').length;
throw ret;
};
var throwParseError = function (msg, overrideIndex) {
throwSpecialError(
msg || "bad formatting in template file",
html_scanner.ParseError,
overrideIndex);
};
var throwBodyAttrsError = function (msg) {
throwSpecialError(msg, html_scanner.BodyAttrsError);
};
var results = html_scanner._initResults();
var rOpenTag = /^((<(template|head|body)\b)|(<!--)|(<!DOCTYPE|{{!)|$)/i;
@@ -101,6 +108,10 @@ html_scanner = {
var tagContents = rest.slice(0, end.index);
var contentsStartIndex = index;
if (tagName === 'body') {
this._addBodyAttrs(results, tagAttribs, throwBodyAttrsError);
}
// act on the tag
html_scanner._handleTag(results, tagName, tagAttribs, tagContents,
throwParseError, contentsStartIndex,
@@ -118,9 +129,23 @@ html_scanner = {
results.head = '';
results.body = '';
results.js = '';
results.bodyAttrs = {};
return results;
},
_addBodyAttrs: function (results, attrs, throwBodyAttrsError) {
Object.keys(attrs).forEach(function (attr) {
var val = attrs[attr];
if (results.bodyAttrs.hasOwnProperty(attr) && results.bodyAttrs[attr] !== val) {
throwBodyAttrsError(
"<body> declarations have conflicting values for the '" + attr + "' attribute.");
}
results.bodyAttrs[attr] = val;
});
},
_handleTag: function (results, tag, attribs, contents, throwParseError,
contentsStartIndex, tagStartIndex) {
@@ -173,9 +198,6 @@ html_scanner = {
} else {
// <body>
if (hasAttribs) {
// XXX we would want to throw an error here if we have duplicate
// attributes, but this is complex to do with the current build system
// so we won't.
results.js += "\nMeteor.startup(function() { $('body').attr(" + JSON.stringify(attribs) + "); });\n";
}