@settlin/spacebars-loader
Version:
blaze templates loader for meteor-webpack
240 lines (207 loc) • 7.67 kB
JavaScript
/**
* Copied from Meteor v1.1.0.2 and exports html_scanner
* https://github.com/meteor/meteor/tree/release/METEOR%401.1.0.2/packages/templating/plugin
*
* See modifications under heading
* ADD, REMOVE, MODIFY
*/
// ADD LINES {
var lodash = require("lodash");
var Meteor = require("meteor-core")(lodash);
require("meteor-htmljs")(Meteor);
require("meteor-html-tools")(Meteor);
require("meteor-blaze-tools")(Meteor);
require("meteor-spacebars-compiler")(Meteor);
var SpacebarsCompiler = Meteor.SpacebarsCompiler;
// }
html_scanner = {
// Scan a template file for <head>, <body>, and <template>
// tags and extract their contents.
//
// This is a primitive, regex-based scanner. It scans
// top-level tags, which are allowed to have attributes,
// and ignores top-level HTML comments.
// Has fields 'message', 'line', 'file'
ParseError: function () {
},
bodyAttributes : [],
scan: function (contents, source_name, options) {
var rest = contents;
var index = 0;
var advance = function(amount) {
rest = rest.substring(amount);
index += amount;
};
var throwParseError = function (msg, overrideIndex) {
var ret = new html_scanner.ParseError;
ret.message = msg || "bad formatting in HTML template";
ret.file = source_name;
var theIndex = (typeof overrideIndex === 'number' ? overrideIndex : index);
ret.line = contents.substring(0, theIndex).split('\n').length;
throw ret;
};
var results = html_scanner._initResults();
var rOpenTag = /^((<(template|head|body)\b)|(<!--)|(<!DOCTYPE|{{!)|$)/i;
while (rest) {
// skip whitespace first (for better line numbers)
advance(rest.match(/^\s*/)[0].length);
var match = rOpenTag.exec(rest);
if (! match)
throwParseError(); // unknown text encountered
var matchToken = match[1];
var matchTokenTagName = match[3];
var matchTokenComment = match[4];
var matchTokenUnsupported = match[5];
var tagStartIndex = index;
advance(match.index + match[0].length);
if (! matchToken)
break; // matched $ (end of file)
if (matchTokenComment === '<!--') {
// top-level HTML comment
var commentEnd = /--\s*>/.exec(rest);
if (! commentEnd)
throwParseError("unclosed HTML comment");
advance(commentEnd.index + commentEnd[0].length);
continue;
}
if (matchTokenUnsupported) {
switch (matchTokenUnsupported.toLowerCase()) {
case '<!doctype':
throwParseError(
"Can't set DOCTYPE here. (Meteor sets <!DOCTYPE html> for you)");
case '{{!':
throwParseError(
"Can't use '{{! }}' outside a template. Use '<!-- -->'.");
}
throwParseError();
}
// otherwise, a <tag>
var tagName = matchTokenTagName.toLowerCase();
var tagAttribs = {}; // bare name -> value dict
var rTagPart = /^\s*((([a-zA-Z0-9:_-]+)\s*=\s*(["'])(.*?)\4)|(>))/;
var attr;
// read attributes
while ((attr = rTagPart.exec(rest))) {
var attrToken = attr[1];
var attrKey = attr[3];
var attrValue = attr[5];
advance(attr.index + attr[0].length);
if (attrToken === '>')
break;
// XXX we don't HTML unescape the attribute value
// (e.g. to allow "abcd"efg") or protect against
// collisions with methods of tagAttribs (e.g. for
// a property named toString)
attrValue = attrValue.match(/^\s*([\s\S]*?)\s*$/)[1]; // trim
tagAttribs[attrKey] = attrValue;
}
if (! attr) // didn't end on '>'
throwParseError("Parse error in tag");
// find </tag>
var end = (new RegExp('</'+tagName+'\\s*>', 'i')).exec(rest);
if (! end)
throwParseError("unclosed <"+tagName+">");
var tagContents = rest.slice(0, end.index);
var contentsStartIndex = index;
// act on the tag
html_scanner._handleTag(results, tagName, tagAttribs, tagContents,
throwParseError, contentsStartIndex,
tagStartIndex, options);
// advance afterwards, so that line numbers in errors are correct
advance(end.index + end[0].length);
}
return results;
},
_initResults: function() {
var results = {};
results.head = '';
results.body = '';
results.js = '';
return results;
},
_handleTag: function (results, tag, attribs, contents, throwParseError,
// contentsStartIndex, tagStartIndex) {
contentsStartIndex, tagStartIndex, options) {
// ADD LINES }}}
options = options || {};
// }}}
// trim the tag contents.
// this is a courtesy and is also relied on by some unit tests.
var m = contents.match(/^([ \t\r\n]*)([\s\S]*?)[ \t\r\n]*$/);
contentsStartIndex += m[1].length;
contents = m[2];
// do we have 1 or more attribs?
var hasAttribs = false;
for(var k in attribs) {
if (attribs.hasOwnProperty(k)) {
hasAttribs = true;
break;
}
}
if (tag === "head") {
if (hasAttribs)
throwParseError("Attributes on <head> not supported");
results.head += contents;
return;
}
// <body> or <template>
try {
if (tag === "template") {
var name = attribs.name;
if (! name)
throwParseError("Template has no 'name' attribute");
if (SpacebarsCompiler.isReservedName(name))
throwParseError("Template can't be named \"" + name + "\"");
var renderFuncCode = SpacebarsCompiler.compile(
contents, {
isTemplate: true,
sourceName: 'Template "' + name + '"'
});
var nameLiteral = JSON.stringify(name);
var templateDotNameLiteral = JSON.stringify("Template." + name);
// MODIFY {{{
// results.js += "\nTemplate.__checkName(" + nameLiteral + ");\n" +
// "Template[" + nameLiteral + "] = new Template(" +
// templateDotNameLiteral + ", " + renderFuncCode + ");\n";
if (!('attachGlobal' in options)) options.attachGlobal = true;
if (options.attachGlobal) {
}
results.js +=
'\nmodule.exports.template = new Blaze.Template(' +
(options.templateName || templateDotNameLiteral) + ", " +
renderFuncCode + ");\n";
if (options.attachGlobal) {
results.js +=
"Blaze.Template.__checkName(" + nameLiteral + ");\n" +
'Blaze.Template[' + nameLiteral + '] = module.exports.template;\n';
}
// }}}
} 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";
}
var renderFuncCode = SpacebarsCompiler.compile(
contents, {
isBody: true,
sourceName: "<body>"
});
// We may be one of many `<body>` tags.
results.js += "\nBlaze.Template.body.addContent(" + renderFuncCode + ");\nMeteor.startup(Blaze.Template.body.renderToDocument);\n";
}
} catch (e) {
if (e.scanner) {
// The error came from Spacebars
throwParseError(e.message, contentsStartIndex + e.offset);
} else {
throw e;
}
}
}
};
// ADD LINE {
module.exports = html_scanner;
// }