UNPKG

handlebars-template-loader

Version:
159 lines (133 loc) 3.84 kB
var Parser = require("fastparse"); /** * MACRO CLASS */ var Macro = function(name, index, length) { this.name = name; this.start = index; this.length = length; this.args = []; }; Macro.prototype.getArguments = function() { var args = []; this.args.forEach(function(arg) { args.push(arg.value); }); return args; }; /** * MACROCONTEXT CLASS */ var MacroContext = function(isMacroAvailable, usid) { this.currentDirective = null; this.matches = []; this.isMacroAvailable = isMacroAvailable; this.usid = usid; this.data = {}; // The ident method builds a unique string expression that replaces an macro. // This value is replaced again for the expected expression once the template // has been transformed to a function this.ident = function() { return "____" + usid + Math.random() + "____"; }; }; MacroContext.prototype.replaceMatches = function(content) { var self = this; content = [content]; this.matches.reverse(); this.matches.forEach(function(match) { do { var ident = self.ident(); } while (self.data[ident]); self.data[ident] = match; var x = content.pop(); content.push(x.substr(match.start + match.length)); content.push(ident); content.push(x.substr(0, match.start)); }); content.reverse(); return content.join(''); }; MacroContext.prototype.resolveMacros = function(content, macros) { var regex = new RegExp('____' + this.usid + '[0-9\\.]+____', 'g'); var self = this; // Replace macro expressions content = content.code || content; content = content.replace(regex, function(match) { if (!self.data[match]) { return match; } // TODO: invoke macros with configuration context var macro = self.data[match]; return '" + ' + macros[macro.name].apply(null, macro.getArguments()) + ' + "'; }); // Replace escaped macros content = content.replace(/\\+(@[\w]+)/, function(match, expr) { return expr; }); return content; }; // Parses a macro string argument var processStringArg = function(match, value, index, length) { if (!this.currentMacro) return; this.currentMacro.args.push({ start: index + value.length, index: index, length: length, value: value }); }; // Parses a macro numeric argument var processNumArg = function(match, value, index, length) { if (!this.currentMacro) return; this.currentMacro.args.push({ start: index + value.length, index: index, length: length, value: parseFloat(value) }); }; // Parses a macro boolean argument var processBooleanArg = function(match, value, index, length) { if (!this.currentMacro) return; this.currentMacro.args.push({ start: index + value.length, index: index, length: length, value: value === 'true' }); }; // Parser configuration var specs = { outside: { "^@(\\w+)\\(|([^\\\\])@(\\w+)\\(": function(match, name, prefix, _name, index, length) { name = name || _name; if (!this.isMacroAvailable(name)) { this.currentMacro = null; return 'inside'; } var macro = new Macro(name, prefix ? index + 1 : index, length); this.matches.push(macro); this.currentMacro = macro; return 'inside'; } }, inside: { "\\)": function(match, index) { if (this.currentMacro !== null) { this.currentMacro.length = 1 + index - this.currentMacro.start; } return 'outside'; }, "\'([^\']*)\'": processStringArg, "\"([^\"]*)\"": processStringArg, "\\s*([\\d|\\.]+)\\s*": processNumArg, "\\s*(true|false)\\s*": processBooleanArg, "\\s+": true } }; var parser = new Parser(specs); module.exports = function parse(html, isMacroAvailable, usid) { var context = new MacroContext(isMacroAvailable, usid); return parser.parse('outside', html, context); };