UNPKG

ember-source

Version:

A JavaScript framework for creating ambitious web applications

1,894 lines (1,856 loc) 469 kB
const CURRIED_COMPONENT = 0; const CURRIED_HELPER = 1; const CURRIED_MODIFIER = 2; // import Logger from './logger'; function assert$2(test, msg) { } /** * This constant exists to make it easier to differentiate normal logs from * errant console.logs. LOGGER can be used outside of LOCAL_TRACE_LOGGING checks, * and is meant to be used in the rare situation where a console.* call is * actually appropriate. */ const LOGGER$1 = console; function assertNever$1(value, desc = 'unexpected unreachable branch') { LOGGER$1.log('unreachable', value); LOGGER$1.log(`${desc} :: ${JSON.stringify(value)} (${value})`); throw new Error(`code reached unreachable`); } /** * An object branded with a local debug type has special local trace logging * behavior. * * If `LOCAL_DEBUG` is `false`, this function does nothing (and is removed * by the minifier in builder). */ function setLocalDebugType(type, ...brand) { } function unwrap$1(val) { return val; } function expect(val, message) { return val; } function exhausted(value) { } function isPresentArray(list) { return list ? list.length > 0 : false; } function asPresentArray(list, message = `unexpected empty list`) { return list; } function getLast(list) { return list.length === 0 ? undefined : list[list.length - 1]; } function getFirst(list) { return list.length === 0 ? undefined : list[0]; } function mapPresentArray(list, mapper) { if (list === null) { return null; } let out = []; for (let item of list) { out.push(mapper(item)); } return out; } const SIGN_BIT = -536870913; function encodeNegative(num) { return num & SIGN_BIT; } function decodeNegative(num) { return num | ~SIGN_BIT; } function encodePositive(num) { return ~num; } function decodePositive(num) { return ~num; } function decodeHandle(num) { return num; } function encodeImmediate(num) { num |= 0; return num < 0 ? encodeNegative(num) : encodePositive(num); } function decodeImmediate(num) { num |= 0; return num > SIGN_BIT ? decodePositive(num) : decodeNegative(num); } [1, -1].forEach(x => decodeImmediate(encodeImmediate(x))); const VM_SYSCALL_SIZE = 113; const VM_MACHINE_SIZE = 7; /** * This constant exists to make it easier to differentiate normal logs from * errant console.logs. LOCAL_LOGGER should only be used inside a * LOCAL_TRACE_LOGGING check. * * It does not alleviate the need to check LOCAL_TRACE_LOGGING, which is used * for stripping. */ /** * This constant exists to make it easier to differentiate normal logs from * errant console.logs. LOGGER can be used outside of LOCAL_TRACE_LOGGING checks, * and is meant to be used in the rare situation where a console.* call is * actually appropriate. */ const LOGGER = console; function dict() { return Object.create(null); } const assign = Object.assign; function assertNever(value, desc = 'unexpected unreachable branch') { LOGGER.log('unreachable', value); LOGGER.log(`${desc} :: ${JSON.stringify(value)} (${value})`); throw new Error(`code reached unreachable`); } const opcodes = { Append: 1, TrustingAppend: 2, Comment: 3, Modifier: 4, Block: 6, Component: 8, OpenElement: 10, OpenElementWithSplat: 11, FlushElement: 12, CloseElement: 13, StaticAttr: 14, DynamicAttr: 15, ComponentAttr: 16, AttrSplat: 17, Yield: 18, TrustingDynamicAttr: 22, TrustingComponentAttr: 23, StaticComponentAttr: 24, Debugger: 26, Undefined: 27, Call: 28, Concat: 29, GetSymbol: 30, GetLexicalSymbol: 32, GetStrictKeyword: 31, GetFreeAsComponentOrHelperHead: 35, GetFreeAsHelperHead: 37, GetFreeAsModifierHead: 38, GetFreeAsComponentHead: 39, InElement: 40, If: 41, Each: 42, Let: 44, WithDynamicVars: 45, InvokeComponent: 46, HasBlock: 48, HasBlockParams: 49, Curry: 50, Not: 51, IfInline: 52, GetDynamicVar: 53, Log: 54 }; const WellKnownAttrNames = { class: 0, id: 1, value: 2, name: 3, type: 4, style: 5, href: 6 }; const WellKnownTagNames = { div: 0, span: 1, p: 2, a: 3 }; const Char = { NBSP: 0xa0, QUOT: 0x22, LT: 0x3c, GT: 0x3e, AMP: 0x26 }; // \x26 is ampersand, \xa0 is non-breaking space const ATTR_VALUE_REGEX_TEST = /["\x26\xa0]/u; const ATTR_VALUE_REGEX_REPLACE = new RegExp(ATTR_VALUE_REGEX_TEST.source, 'gu'); const TEXT_REGEX_TEST = /[&<>\xa0]/u; const TEXT_REGEX_REPLACE = new RegExp(TEXT_REGEX_TEST.source, 'gu'); function attrValueReplacer(char) { switch (char.charCodeAt(0)) { case Char.NBSP: return '&nbsp;'; case Char.QUOT: return '&quot;'; case Char.AMP: return '&amp;'; default: return char; } } function textReplacer(char) { switch (char.charCodeAt(0)) { case Char.NBSP: return '&nbsp;'; case Char.AMP: return '&amp;'; case Char.LT: return '&lt;'; case Char.GT: return '&gt;'; default: return char; } } function escapeAttrValue(attrValue) { if (ATTR_VALUE_REGEX_TEST.test(attrValue)) { return attrValue.replace(ATTR_VALUE_REGEX_REPLACE, attrValueReplacer); } return attrValue; } function escapeText(text) { if (TEXT_REGEX_TEST.test(text)) { return text.replace(TEXT_REGEX_REPLACE, textReplacer); } return text; } function sortByLoc(a, b) { // If either is invisible, don't try to order them if (a.loc.isInvisible || b.loc.isInvisible) { return 0; } if (a.loc.startPosition.line < b.loc.startPosition.line) { return -1; } if (a.loc.startPosition.line === b.loc.startPosition.line && a.loc.startPosition.column < b.loc.startPosition.column) { return -1; } if (a.loc.startPosition.line === b.loc.startPosition.line && a.loc.startPosition.column === b.loc.startPosition.column) { return 0; } return 1; } const voidMap = new Set(['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']); const NON_WHITESPACE = /^\S/u; /** * Examples when true: * - link * - liNK * * Examples when false: * - Link (component) */ function isVoidTag(tag) { return voidMap.has(tag.toLowerCase()) && tag[0]?.toLowerCase() === tag[0]; } class Printer { buffer = ''; options; constructor(options) { this.options = options; } /* This is used by _all_ methods on this Printer class that add to `this.buffer`, it allows consumers of the printer to use alternate string representations for a given node. The primary use case for this are things like source -> source codemod utilities. For example, ember-template-recast attempts to always preserve the original string formatting in each AST node if no modifications are made to it. */ handledByOverride(node, ensureLeadingWhitespace = false) { if (this.options.override !== undefined) { let result = this.options.override(node, this.options); if (typeof result === 'string') { if (ensureLeadingWhitespace && NON_WHITESPACE.test(result)) { result = ` ${result}`; } this.buffer += result; return true; } } return false; } Node(node) { switch (node.type) { case 'MustacheStatement': case 'BlockStatement': case 'MustacheCommentStatement': case 'CommentStatement': case 'TextNode': case 'ElementNode': case 'AttrNode': case 'Block': case 'Template': return this.TopLevelStatement(node); case 'StringLiteral': case 'BooleanLiteral': case 'NumberLiteral': case 'UndefinedLiteral': case 'NullLiteral': case 'PathExpression': case 'SubExpression': return this.Expression(node); case 'ConcatStatement': // should have an AttrNode parent return this.ConcatStatement(node); case 'Hash': return this.Hash(node); case 'HashPair': return this.HashPair(node); case 'ElementModifierStatement': return this.ElementModifierStatement(node); } } Expression(expression) { switch (expression.type) { case 'StringLiteral': case 'BooleanLiteral': case 'NumberLiteral': case 'UndefinedLiteral': case 'NullLiteral': return this.Literal(expression); case 'PathExpression': return this.PathExpression(expression); case 'SubExpression': return this.SubExpression(expression); } } Literal(literal) { switch (literal.type) { case 'StringLiteral': return this.StringLiteral(literal); case 'BooleanLiteral': return this.BooleanLiteral(literal); case 'NumberLiteral': return this.NumberLiteral(literal); case 'UndefinedLiteral': return this.UndefinedLiteral(literal); case 'NullLiteral': return this.NullLiteral(literal); } } TopLevelStatement(statement) { switch (statement.type) { case 'MustacheStatement': return this.MustacheStatement(statement); case 'BlockStatement': return this.BlockStatement(statement); case 'MustacheCommentStatement': return this.MustacheCommentStatement(statement); case 'CommentStatement': return this.CommentStatement(statement); case 'TextNode': return this.TextNode(statement); case 'ElementNode': return this.ElementNode(statement); case 'Block': return this.Block(statement); case 'Template': return this.Template(statement); case 'AttrNode': // should have element return this.AttrNode(statement); } } Template(template) { this.TopLevelStatements(template.body); } Block(block) { /* When processing a template like: ```hbs {{#if whatever}} whatever {{else if somethingElse}} something else {{else}} fallback {{/if}} ``` The AST still _effectively_ looks like: ```hbs {{#if whatever}} whatever {{else}}{{#if somethingElse}} something else {{else}} fallback {{/if}}{{/if}} ``` The only way we can tell if that is the case is by checking for `block.chained`, but unfortunately when the actual statements are processed the `block.body[0]` node (which will always be a `BlockStatement`) has no clue that its ancestor `Block` node was chained. This "forwards" the `chained` setting so that we can check it later when processing the `BlockStatement`. */ if (block.chained) { let firstChild = block.body[0]; firstChild.chained = true; } if (this.handledByOverride(block)) { return; } this.TopLevelStatements(block.body); } TopLevelStatements(statements) { statements.forEach(statement => this.TopLevelStatement(statement)); } ElementNode(el) { if (this.handledByOverride(el)) { return; } this.OpenElementNode(el); this.TopLevelStatements(el.children); this.CloseElementNode(el); } OpenElementNode(el) { this.buffer += `<${el.tag}`; const parts = [...el.attributes, ...el.modifiers, ...el.comments].sort(sortByLoc); for (const part of parts) { this.buffer += ' '; switch (part.type) { case 'AttrNode': this.AttrNode(part); break; case 'ElementModifierStatement': this.ElementModifierStatement(part); break; case 'MustacheCommentStatement': this.MustacheCommentStatement(part); break; } } if (el.blockParams.length) { this.BlockParams(el.blockParams); } if (el.selfClosing) { this.buffer += ' /'; } this.buffer += '>'; } CloseElementNode(el) { if (el.selfClosing || isVoidTag(el.tag)) { return; } this.buffer += `</${el.tag}>`; } AttrNode(attr) { if (this.handledByOverride(attr)) { return; } let { name, value } = attr; this.buffer += name; const isAttribute = !name.startsWith('@'); const shouldElideValue = isAttribute && value.type == 'TextNode' && value.chars.length === 0; if (!shouldElideValue) { this.buffer += '='; this.AttrNodeValue(value); } } AttrNodeValue(value) { if (value.type === 'TextNode') { let quote = '"'; if (this.options.entityEncoding === 'raw') { if (value.chars.includes('"') && !value.chars.includes("'")) { quote = "'"; } } this.buffer += quote; this.TextNode(value, quote); this.buffer += quote; } else { this.Node(value); } } TextNode(text, isInAttr) { if (this.handledByOverride(text)) { return; } if (this.options.entityEncoding === 'raw') { if (isInAttr && text.chars.includes(isInAttr)) { this.buffer += escapeAttrValue(text.chars); } else { this.buffer += text.chars; } } else if (isInAttr) { this.buffer += escapeAttrValue(text.chars); } else { this.buffer += escapeText(text.chars); } } MustacheStatement(mustache) { if (this.handledByOverride(mustache)) { return; } this.buffer += mustache.trusting ? '{{{' : '{{'; if (mustache.strip.open) { this.buffer += '~'; } this.Expression(mustache.path); this.Params(mustache.params); this.Hash(mustache.hash); if (mustache.strip.close) { this.buffer += '~'; } this.buffer += mustache.trusting ? '}}}' : '}}'; } BlockStatement(block) { if (this.handledByOverride(block)) { return; } if (block.chained) { this.buffer += block.inverseStrip.open ? '{{~' : '{{'; this.buffer += 'else '; } else { this.buffer += block.openStrip.open ? '{{~#' : '{{#'; } this.Expression(block.path); this.Params(block.params); this.Hash(block.hash); if (block.program.blockParams.length) { this.BlockParams(block.program.blockParams); } if (block.chained) { this.buffer += block.inverseStrip.close ? '~}}' : '}}'; } else { this.buffer += block.openStrip.close ? '~}}' : '}}'; } this.Block(block.program); if (block.inverse) { if (!block.inverse.chained) { this.buffer += block.inverseStrip.open ? '{{~' : '{{'; this.buffer += 'else'; this.buffer += block.inverseStrip.close ? '~}}' : '}}'; } this.Block(block.inverse); } if (!block.chained) { this.buffer += block.closeStrip.open ? '{{~/' : '{{/'; this.Expression(block.path); this.buffer += block.closeStrip.close ? '~}}' : '}}'; } } BlockParams(blockParams) { this.buffer += ` as |${blockParams.join(' ')}|`; } ConcatStatement(concat) { if (this.handledByOverride(concat)) { return; } this.buffer += '"'; concat.parts.forEach(part => { if (part.type === 'TextNode') { this.TextNode(part, '"'); } else { this.Node(part); } }); this.buffer += '"'; } MustacheCommentStatement(comment) { if (this.handledByOverride(comment)) { return; } this.buffer += `{{!--${comment.value}--}}`; } ElementModifierStatement(mod) { if (this.handledByOverride(mod)) { return; } this.buffer += '{{'; this.Expression(mod.path); this.Params(mod.params); this.Hash(mod.hash); this.buffer += '}}'; } CommentStatement(comment) { if (this.handledByOverride(comment)) { return; } this.buffer += `<!--${comment.value}-->`; } PathExpression(path) { if (this.handledByOverride(path)) { return; } this.buffer += path.original; } SubExpression(sexp) { if (this.handledByOverride(sexp)) { return; } this.buffer += '('; this.Expression(sexp.path); this.Params(sexp.params); this.Hash(sexp.hash); this.buffer += ')'; } Params(params) { // TODO: implement a top level Params AST node (just like the Hash object) // so that this can also be overridden if (params.length) { params.forEach(param => { this.buffer += ' '; this.Expression(param); }); } } Hash(hash) { if (this.handledByOverride(hash, true)) { return; } hash.pairs.forEach(pair => { this.buffer += ' '; this.HashPair(pair); }); } HashPair(pair) { if (this.handledByOverride(pair)) { return; } this.buffer += pair.key; this.buffer += '='; this.Node(pair.value); } StringLiteral(str) { if (this.handledByOverride(str)) { return; } this.buffer += JSON.stringify(str.value); } BooleanLiteral(bool) { if (this.handledByOverride(bool)) { return; } this.buffer += String(bool.value); } NumberLiteral(number) { if (this.handledByOverride(number)) { return; } this.buffer += String(number.value); } UndefinedLiteral(node) { if (this.handledByOverride(node)) { return; } this.buffer += 'undefined'; } NullLiteral(node) { if (this.handledByOverride(node)) { return; } this.buffer += 'null'; } print(node) { let { options } = this; if (options.override) { let result = options.override(node, options); if (result !== undefined) { return result; } } this.buffer = ''; this.Node(node); return this.buffer; } } function build(ast, options = { entityEncoding: 'transformed' }) { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- JS users if (!ast) { return ''; } let printer = new Printer(options); return printer.print(ast); } function isKeyword(word, type) { if (word in KEYWORDS_TYPES) { { return true; } } else { return false; } } /** * This includes the full list of keywords currently in use in the template * language, and where their valid usages are. */ const KEYWORDS_TYPES = { action: ['Call', 'Modifier'], component: ['Call', 'Append', 'Block'], debugger: ['Append'], 'each-in': ['Block'], each: ['Block'], 'has-block-params': ['Call', 'Append'], 'has-block': ['Call', 'Append'], helper: ['Call', 'Append'], if: ['Call', 'Append', 'Block'], 'in-element': ['Block'], let: ['Block'], log: ['Call', 'Append'], modifier: ['Call', 'Modifier'], mount: ['Append'], mut: ['Call', 'Append'], outlet: ['Append'], readonly: ['Call', 'Append'], unbound: ['Call', 'Append'], unless: ['Call', 'Append', 'Block'], yield: ['Append'] }; var errorProps = ['description', 'fileName', 'lineNumber', 'endLineNumber', 'message', 'name', 'number', 'stack']; function Exception(message, node) { var loc = node && node.loc, line, endLineNumber, column, endColumn; if (loc) { line = loc.start.line; endLineNumber = loc.end.line; column = loc.start.column; endColumn = loc.end.column; message += ' - ' + line + ':' + column; } var tmp = Error.prototype.constructor.call(this, message); // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. for (var idx = 0; idx < errorProps.length; idx++) { this[errorProps[idx]] = tmp[errorProps[idx]]; } /* istanbul ignore else */ if (Error.captureStackTrace) { Error.captureStackTrace(this, Exception); } try { if (loc) { this.lineNumber = line; this.endLineNumber = endLineNumber; // Work around issue under safari where we can't directly set the column value /* istanbul ignore next */ if (Object.defineProperty) { Object.defineProperty(this, 'column', { value: column, enumerable: true }); Object.defineProperty(this, 'endColumn', { value: endColumn, enumerable: true }); } else { this.column = column; this.endColumn = endColumn; } } } catch (nop) { /* Ignore if the browser is very particular */ } } Exception.prototype = new Error(); function Visitor() { this.parents = []; } Visitor.prototype = { constructor: Visitor, mutating: false, // Visits a given value. If mutating, will replace the value if necessary. acceptKey: function (node, name) { var value = this.accept(node[name]); if (this.mutating) { // Hacky sanity check: This may have a few false positives for type for the helper // methods but will generally do the right thing without a lot of overhead. if (value && !Visitor.prototype[value.type]) { throw new Exception('Unexpected node type "' + value.type + '" found when accepting ' + name + ' on ' + node.type); } node[name] = value; } }, // Performs an accept operation with added sanity check to ensure // required keys are not removed. acceptRequired: function (node, name) { this.acceptKey(node, name); if (!node[name]) { throw new Exception(node.type + ' requires ' + name); } }, // Traverses a given array. If mutating, empty responses will be removed // for child elements. acceptArray: function (array) { for (var i = 0, l = array.length; i < l; i++) { this.acceptKey(array, i); if (!array[i]) { array.splice(i, 1); i--; l--; } } }, accept: function (object) { if (!object) { return; } /* istanbul ignore next: Sanity code */ if (!this[object.type]) { throw new Exception('Unknown type: ' + object.type, object); } if (this.current) { this.parents.unshift(this.current); } this.current = object; var ret = this[object.type](object); this.current = this.parents.shift(); if (!this.mutating || ret) { return ret; } else if (ret !== false) { return object; } }, Program: function (program) { this.acceptArray(program.body); }, MustacheStatement: visitSubExpression, Decorator: visitSubExpression, BlockStatement: visitBlock, DecoratorBlock: visitBlock, PartialStatement: visitPartial, PartialBlockStatement: function (partial) { visitPartial.call(this, partial); this.acceptKey(partial, 'program'); }, ContentStatement: function /* content */ () {}, CommentStatement: function /* comment */ () {}, SubExpression: visitSubExpression, PathExpression: function /* path */ () {}, StringLiteral: function /* string */ () {}, NumberLiteral: function /* number */ () {}, BooleanLiteral: function /* bool */ () {}, UndefinedLiteral: function /* literal */ () {}, NullLiteral: function /* literal */ () {}, Hash: function (hash) { this.acceptArray(hash.pairs); }, HashPair: function (pair) { this.acceptRequired(pair, 'value'); } }; function visitSubExpression(mustache) { this.acceptRequired(mustache, 'path'); this.acceptArray(mustache.params); this.acceptKey(mustache, 'hash'); } function visitBlock(block) { visitSubExpression.call(this, block); this.acceptKey(block, 'program'); this.acceptKey(block, 'inverse'); } function visitPartial(partial) { this.acceptRequired(partial, 'name'); this.acceptArray(partial.params); this.acceptKey(partial, 'hash'); } function WhitespaceControl(options) { if (options === void 0) { options = {}; } this.options = options; } WhitespaceControl.prototype = new Visitor(); WhitespaceControl.prototype.Program = function (program) { var doStandalone = !this.options.ignoreStandalone; var isRoot = !this.isRootSeen; this.isRootSeen = true; var body = program.body; for (var i = 0, l = body.length; i < l; i++) { var current = body[i], strip = this.accept(current); if (!strip) { continue; } var _isPrevWhitespace = isPrevWhitespace(body, i, isRoot), _isNextWhitespace = isNextWhitespace(body, i, isRoot), openStandalone = strip.openStandalone && _isPrevWhitespace, closeStandalone = strip.closeStandalone && _isNextWhitespace, inlineStandalone = strip.inlineStandalone && _isPrevWhitespace && _isNextWhitespace; if (strip.close) { omitRight(body, i, true); } if (strip.open) { omitLeft(body, i, true); } if (doStandalone && inlineStandalone) { omitRight(body, i); if (omitLeft(body, i)) { // If we are on a standalone node, save the indent info for partials if (current.type === 'PartialStatement') { // Pull out the whitespace from the final line current.indent = /([ \t]+$)/.exec(body[i - 1].original)[1]; } } } if (doStandalone && openStandalone) { omitRight((current.program || current.inverse).body); // Strip out the previous content node if it's whitespace only omitLeft(body, i); } if (doStandalone && closeStandalone) { // Always strip the next node omitRight(body, i); omitLeft((current.inverse || current.program).body); } } return program; }; WhitespaceControl.prototype.BlockStatement = WhitespaceControl.prototype.DecoratorBlock = WhitespaceControl.prototype.PartialBlockStatement = function (block) { this.accept(block.program); this.accept(block.inverse); // Find the inverse program that is involved with whitespace stripping. var program = block.program || block.inverse, inverse = block.program && block.inverse, firstInverse = inverse, lastInverse = inverse; if (inverse && inverse.chained) { firstInverse = inverse.body[0].program; // Walk the inverse chain to find the last inverse that is actually in the chain. while (lastInverse.chained) { lastInverse = lastInverse.body[lastInverse.body.length - 1].program; } } var strip = { open: block.openStrip.open, close: block.closeStrip.close, // Determine the standalone candidacy. Basically flag our content as being possibly standalone // so our parent can determine if we actually are standalone openStandalone: isNextWhitespace(program.body), closeStandalone: isPrevWhitespace((firstInverse || program).body) }; if (block.openStrip.close) { omitRight(program.body, null, true); } if (inverse) { var inverseStrip = block.inverseStrip; if (inverseStrip.open) { omitLeft(program.body, null, true); } if (inverseStrip.close) { omitRight(firstInverse.body, null, true); } if (block.closeStrip.open) { omitLeft(lastInverse.body, null, true); } // Find standalone else statements if (!this.options.ignoreStandalone && isPrevWhitespace(program.body) && isNextWhitespace(firstInverse.body)) { omitLeft(program.body); omitRight(firstInverse.body); } } else if (block.closeStrip.open) { omitLeft(program.body, null, true); } return strip; }; WhitespaceControl.prototype.Decorator = WhitespaceControl.prototype.MustacheStatement = function (mustache) { return mustache.strip; }; WhitespaceControl.prototype.PartialStatement = WhitespaceControl.prototype.CommentStatement = function (node) { /* istanbul ignore next */ var strip = node.strip || {}; return { inlineStandalone: true, open: strip.open, close: strip.close }; }; function isPrevWhitespace(body, i, isRoot) { if (i === undefined) { i = body.length; } // Nodes that end with newlines are considered whitespace (but are special // cased for strip operations) var prev = body[i - 1], sibling = body[i - 2]; if (!prev) { return isRoot; } if (prev.type === 'ContentStatement') { return (sibling || !isRoot ? /\r?\n\s*?$/ : /(^|\r?\n)\s*?$/).test(prev.original); } } function isNextWhitespace(body, i, isRoot) { if (i === undefined) { i = -1; } var next = body[i + 1], sibling = body[i + 2]; if (!next) { return isRoot; } if (next.type === 'ContentStatement') { return (sibling || !isRoot ? /^\s*?\r?\n/ : /^\s*?(\r?\n|$)/).test(next.original); } } // Marks the node to the right of the position as omitted. // I.e. {{foo}}' ' will mark the ' ' node as omitted. // // If i is undefined, then the first child will be marked as such. // // If multiple is truthy then all whitespace will be stripped out until non-whitespace // content is met. function omitRight(body, i, multiple) { var current = body[i == null ? 0 : i + 1]; if (!current || current.type !== 'ContentStatement' || !multiple && current.rightStripped) { return; } var original = current.value; current.value = current.value.replace(multiple ? /^\s+/ : /^[ \t]*\r?\n?/, ''); current.rightStripped = current.value !== original; } // Marks the node to the left of the position as omitted. // I.e. ' '{{foo}} will mark the ' ' node as omitted. // // If i is undefined then the last child will be marked as such. // // If multiple is truthy then all whitespace will be stripped out until non-whitespace // content is met. function omitLeft(body, i, multiple) { var current = body[i == null ? body.length - 1 : i - 1]; if (!current || current.type !== 'ContentStatement' || !multiple && current.leftStripped) { return; } // We omit the last node if it's whitespace only and not preceded by a non-content node. var original = current.value; current.value = current.value.replace(multiple ? /\s+$/ : /[ \t]+$/, ''); current.leftStripped = current.value !== original; return current.leftStripped; } /* parser generated by jison 0.4.18 */ /* Returns a Parser object of the following structure: Parser: { yy: {} } Parser.prototype: { yy: {}, trace: function(), symbols_: {associative list: name ==> number}, terminals_: {associative list: number ==> name}, productions_: [...], performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate, $$, _$), table: [...], defaultActions: {...}, parseError: function(str, hash), parse: function(input), lexer: { EOF: 1, parseError: function(str, hash), setInput: function(input), input: function(), unput: function(str), more: function(), less: function(n), pastInput: function(), upcomingInput: function(), showPosition: function(), test_match: function(regex_match_array, rule_index), next: function(), lex: function(), begin: function(condition), popState: function(), _currentRules: function(), topState: function(), pushState: function(condition), options: { ranges: boolean (optional: true ==> token location info will include a .range[] member) flex: boolean (optional: true ==> flex-like lexing behaviour where the rules are tested exhaustively to find the longest match) backtrack_lexer: boolean (optional: true ==> lexer regexes are tested in order and for each matching regex the action code is invoked; the lexer terminates the scan when a token is returned by the action code) }, performAction: function(yy, yy_, $avoiding_name_collisions, YY_START), rules: [...], conditions: {associative list: name ==> set}, } } token location info (@$, _$, etc.): { first_line: n, last_line: n, first_column: n, last_column: n, range: [start_number, end_number] (where the numbers are indexes into the input string, regular zero-based) } the parseError function receives a 'hash' object with these members for lexer and parser errors: { text: (matched text) token: (the produced terminal token, if any) line: (yylineno) } while parser (grammar) errors will also provide these members, i.e. parser errors deliver a superset of attributes: { loc: (yylloc) expected: (string describing the set of expected tokens) recoverable: (boolean: TRUE when the parser has a error recovery rule available for this particular error) } */ var parser = function () { var o = function (k, v, o, l) { for (o = o || {}, l = k.length; l--; o[k[l]] = v); return o; }, $V0 = [2, 52], $V1 = [1, 20], $V2 = [5, 14, 15, 19, 29, 34, 39, 44, 47, 48, 53, 57, 61], $V3 = [1, 44], $V4 = [1, 40], $V5 = [1, 43], $V6 = [1, 33], $V7 = [1, 34], $V8 = [1, 35], $V9 = [1, 36], $Va = [1, 37], $Vb = [1, 42], $Vc = [1, 46], $Vd = [14, 15, 19, 29, 34, 39, 44, 47, 48, 53, 57, 61], $Ve = [14, 15, 19, 29, 34, 44, 47, 48, 53, 57, 61], $Vf = [15, 18], $Vg = [14, 15, 19, 29, 34, 47, 48, 53, 57, 61], $Vh = [33, 67, 73, 75, 84, 85, 86, 87, 88, 89], $Vi = [23, 33, 56, 67, 68, 73, 75, 77, 79, 84, 85, 86, 87, 88, 89], $Vj = [1, 62], $Vk = [1, 63], $Vl = [23, 33, 56, 68, 73, 79], $Vm = [23, 33, 56, 67, 68, 73, 75, 77, 79, 84, 85, 86, 87, 88, 89, 92, 93], $Vn = [2, 51], $Vo = [1, 64], $Vp = [67, 73, 75, 77, 84, 85, 86, 87, 88, 89], $Vq = [56, 67, 73, 75, 84, 85, 86, 87, 88, 89], $Vr = [1, 75], $Vs = [1, 76], $Vt = [1, 83], $Vu = [33, 67, 73, 75, 79, 84, 85, 86, 87, 88, 89], $Vv = [23, 67, 73, 75, 84, 85, 86, 87, 88, 89], $Vw = [67, 68, 73, 75, 84, 85, 86, 87, 88, 89], $Vx = [33, 79], $Vy = [1, 134], $Vz = [73, 81]; var parser = { trace: function trace() {}, yy: {}, symbols_: { "error": 2, "root": 3, "program": 4, "EOF": 5, "program_repetition0": 6, "statement": 7, "mustache": 8, "block": 9, "rawBlock": 10, "partial": 11, "partialBlock": 12, "content": 13, "COMMENT": 14, "CONTENT": 15, "openRawBlock": 16, "rawBlock_repetition0": 17, "END_RAW_BLOCK": 18, "OPEN_RAW_BLOCK": 19, "helperName": 20, "openRawBlock_repetition0": 21, "openRawBlock_option0": 22, "CLOSE_RAW_BLOCK": 23, "openBlock": 24, "block_option0": 25, "closeBlock": 26, "openInverse": 27, "block_option1": 28, "OPEN_BLOCK": 29, "openBlock_repetition0": 30, "openBlock_option0": 31, "openBlock_option1": 32, "CLOSE": 33, "OPEN_INVERSE": 34, "openInverse_repetition0": 35, "openInverse_option0": 36, "openInverse_option1": 37, "openInverseChain": 38, "OPEN_INVERSE_CHAIN": 39, "openInverseChain_repetition0": 40, "openInverseChain_option0": 41, "openInverseChain_option1": 42, "inverseAndProgram": 43, "INVERSE": 44, "inverseChain": 45, "inverseChain_option0": 46, "OPEN_ENDBLOCK": 47, "OPEN": 48, "hash": 49, "expr": 50, "mustache_repetition0": 51, "mustache_option0": 52, "OPEN_UNESCAPED": 53, "mustache_repetition1": 54, "mustache_option1": 55, "CLOSE_UNESCAPED": 56, "OPEN_PARTIAL": 57, "partial_repetition0": 58, "partial_option0": 59, "openPartialBlock": 60, "OPEN_PARTIAL_BLOCK": 61, "openPartialBlock_repetition0": 62, "openPartialBlock_option0": 63, "exprHead": 64, "arrayLiteral": 65, "sexpr": 66, "OPEN_SEXPR": 67, "CLOSE_SEXPR": 68, "sexpr_repetition0": 69, "sexpr_option0": 70, "hash_repetition_plus0": 71, "hashSegment": 72, "ID": 73, "EQUALS": 74, "OPEN_ARRAY": 75, "arrayLiteral_repetition0": 76, "CLOSE_ARRAY": 77, "blockParams": 78, "OPEN_BLOCK_PARAMS": 79, "blockParams_repetition_plus0": 80, "CLOSE_BLOCK_PARAMS": 81, "path": 82, "dataName": 83, "STRING": 84, "NUMBER": 85, "BOOLEAN": 86, "UNDEFINED": 87, "NULL": 88, "DATA": 89, "pathSegments": 90, "sep": 91, "SEP": 92, "PRIVATE_SEP": 93, "$accept": 0, "$end": 1 }, terminals_: { 2: "error", 5: "EOF", 14: "COMMENT", 15: "CONTENT", 18: "END_RAW_BLOCK", 19: "OPEN_RAW_BLOCK", 23: "CLOSE_RAW_BLOCK", 29: "OPEN_BLOCK", 33: "CLOSE", 34: "OPEN_INVERSE", 39: "OPEN_INVERSE_CHAIN", 44: "INVERSE", 47: "OPEN_ENDBLOCK", 48: "OPEN", 53: "OPEN_UNESCAPED", 56: "CLOSE_UNESCAPED", 57: "OPEN_PARTIAL", 61: "OPEN_PARTIAL_BLOCK", 67: "OPEN_SEXPR", 68: "CLOSE_SEXPR", 73: "ID", 74: "EQUALS", 75: "OPEN_ARRAY", 77: "CLOSE_ARRAY", 79: "OPEN_BLOCK_PARAMS", 81: "CLOSE_BLOCK_PARAMS", 84: "STRING", 85: "NUMBER", 86: "BOOLEAN", 87: "UNDEFINED", 88: "NULL", 89: "DATA", 92: "SEP", 93: "PRIVATE_SEP" }, productions_: [0, [3, 2], [4, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [7, 1], [13, 1], [10, 3], [16, 5], [9, 4], [9, 4], [24, 6], [27, 6], [38, 6], [43, 2], [45, 3], [45, 1], [26, 3], [8, 3], [8, 5], [8, 5], [11, 5], [12, 3], [60, 5], [50, 1], [50, 1], [64, 1], [64, 1], [66, 3], [66, 5], [49, 1], [72, 3], [65, 3], [78, 3], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [20, 1], [83, 2], [91, 1], [91, 1], [82, 3], [82, 1], [90, 3], [90, 1], [6, 0], [6, 2], [17, 0], [17, 2], [21, 0], [21, 2], [22, 0], [22, 1], [25, 0], [25, 1], [28, 0], [28, 1], [30, 0], [30, 2], [31, 0], [31, 1], [32, 0], [32, 1], [35, 0], [35, 2], [36, 0], [36, 1], [37, 0], [37, 1], [40, 0], [40, 2], [41, 0], [41, 1], [42, 0], [42, 1], [46, 0], [46, 1], [51, 0], [51, 2], [52, 0], [52, 1], [54, 0], [54, 2], [55, 0], [55, 1], [58, 0], [58, 2], [59, 0], [59, 1], [62, 0], [62, 2], [63, 0], [63, 1], [69, 0], [69, 2], [70, 0], [70, 1], [71, 1], [71, 2], [76, 0], [76, 2], [80, 1], [80, 2]], performAction: function anonymous(yytext, yyleng, yylineno, yy, yystate /* action[1] */, $$ /* vstack */, _$ /* lstack */) { /* this == yyval */ var $0 = $$.length - 1; switch (yystate) { case 1: return $$[$0 - 1]; case 2: this.$ = yy.prepareProgram($$[$0]); break; case 3: case 4: case 5: case 6: case 7: case 8: case 20: case 28: case 29: case 30: case 31: case 38: case 39: case 46: case 47: this.$ = $$[$0]; break; case 9: this.$ = { type: 'CommentStatement', value: yy.stripComment($$[$0]), strip: yy.stripFlags($$[$0], $$[$0]), loc: yy.locInfo(this._$) }; break; case 10: this.$ = { type: 'ContentStatement', original: $$[$0], value: $$[$0], loc: yy.locInfo(this._$) }; break; case 11: this.$ = yy.prepareRawBlock($$[$0 - 2], $$[$0 - 1], $$[$0], this._$); break; case 12: this.$ = { path: $$[$0 - 3], params: $$[$0 - 2], hash: $$[$0 - 1] }; break; case 13: this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], false, this._$); break; case 14: this.$ = yy.prepareBlock($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0], true, this._$); break; case 15: this.$ = { open: $$[$0 - 5], path: $$[$0 - 4], params: $$[$0 - 3], hash: $$[$0 - 2], blockParams: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 5], $$[$0]) }; break; case 16: case 17: this.$ = { path: $$[$0 - 4], params: $$[$0 - 3], hash: $$[$0 - 2], blockParams: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 5], $$[$0]) }; break; case 18: this.$ = { strip: yy.stripFlags($$[$0 - 1], $$[$0 - 1]), program: $$[$0] }; break; case 19: var inverse = yy.prepareBlock($$[$0 - 2], $$[$0 - 1], $$[$0], $$[$0], false, this._$), program = yy.prepareProgram([inverse], $$[$0 - 1].loc); program.chained = true; this.$ = { strip: $$[$0 - 2].strip, program: program, chain: true }; break; case 21: this.$ = { path: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 2], $$[$0]) }; break; case 22: this.$ = yy.prepareMustache(yy.syntax.hash($$[$0 - 1], yy.locInfo(this._$), { yy: yy, syntax: 'expr' }), [], undefined, $$[$0 - 2], yy.stripFlags($$[$0 - 2], $$[$0]), this._$); break; case 23: case 24: this.$ = yy.prepareMustache($$[$0 - 3], $$[$0 - 2], $$[$0 - 1], $$[$0 - 4], yy.stripFlags($$[$0 - 4], $$[$0]), this._$); break; case 25: this.$ = { type: 'PartialStatement', name: $$[$0 - 3], params: $$[$0 - 2], hash: $$[$0 - 1], indent: '', strip: yy.stripFlags($$[$0 - 4], $$[$0]), loc: yy.locInfo(this._$) }; break; case 26: this.$ = yy.preparePartialBlock($$[$0 - 2], $$[$0 - 1], $$[$0], this._$); break; case 27: this.$ = { path: $$[$0 - 3], params: $$[$0 - 2], hash: $$[$0 - 1], strip: yy.stripFlags($$[$0 - 4], $$[$0]) }; break; case 32: this.$ = yy.syntax.hash($$[$0 - 1], yy.locInfo(this._$), { yy: yy, syntax: 'expr' }); break; case 33: this.$ = { type: 'SubExpression', path: $$[$0 - 3], params: $$[$0 - 2], hash: $$[$0 - 1], loc: yy.locInfo(this._$) }; break; case 34: this.$ = { type: 'Hash', pairs: $$[$0], loc: yy.locInfo(this._$) }; break; case 35: this.$ = { type: 'HashPair', key: yy.id($$[$0 - 2]), value: $$[$0], loc: yy.locInfo(this._$) }; break; case 36: this.$ = yy.syntax.square($$[$0 - 1], yy.locInfo(this._$), { yy: yy, syntax: 'expr' }); break; case 37: this.$ = yy.id($$[$0 - 1]); break; case 40: this.$ = { type: 'StringLiteral', value: $$[$0], original: $$[$0], loc: yy.locInfo(this._$) }; break; case 41: this.$ = { type: 'NumberLiteral', value: Number($$[$0]), original: Number($$[$0]), loc: yy.locInfo(this._$) }; break; case 42: this.$ = { type: 'BooleanLiteral', value: $$[$0] === 'true', original: $$[$0] === 'true', loc: yy.locInfo(this._$) }; break; case 43: this.$ = { type: 'UndefinedLiteral', original: undefined, value: undefined, loc: yy.locInfo(this._$) }; break; case 44: this.$ = { type: 'NullLiteral', original: null, value: null, loc: yy.locInfo(this._$) }; break; case 45: this.$ = yy.preparePath(true, false, $$[$0], this._$); break; case 48: this.$ = yy.preparePath(false, $$[$0 - 2], $$[$0], this._$); break; case 49: this.$ = yy.preparePath(false, false, $$[$0], this._$); break; case 50: $$[$0 - 2].push({ part: yy.id($$[$0]), original: $$[$0], separator: $$[$0 - 1] }); this.$ = $$[$0 - 2]; break; case 51: this.$ = [{ part: yy.id($$[$0]), original: $$[$0] }]; break; case 52: case 54: case 56: case 64: case 70: case 76: case 84: case 88: case 92: case 96: case 100: case 106: this.$ = []; break; case 53: case 55: case 57: case 65: case 71: case 77: case 85: case 89: case 93: case 97: case 101: case 105: case 107: case 109: $$[$0 - 1].push($$[$0]); break; case 104: case 108: this.$ = [$$[$0]]; break; } }, table: [o([5, 14, 15, 19, 29, 34, 48, 53, 57, 61], $V0, { 3: 1, 4: 2, 6: 3 }), { 1: [3] }, { 5: [1, 4] }, o([5, 39, 44, 47], [2, 2], { 7: 5, 8: 6, 9: 7, 10: 8, 11: 9, 12: 10, 13: 11, 24: 15, 27: 16, 16: 17, 60: 19, 14: [1, 12], 15: $V1, 19: [1, 23], 29: [1, 21], 34: [1, 22], 48: [1, 13], 53: [1, 14], 57: [1, 18], 61: [1, 24] }), { 1: [2, 1] }, o($V2, [2, 53]), o($V2, [2, 3]), o($V2, [2, 4]), o($V2, [2, 5]), o($V2, [2, 6]), o($V2, [2, 7]), o($V2, [2, 8]), o($V2, [2, 9]), { 20: 28, 49: 25, 50: 26, 64: 29, 65: 38, 66: 39, 67: $V3, 71: 27, 72: 30, 73: $V4, 75: $V5, 82: 31, 83: 32, 84: $V6, 85: $V7, 86: $V8, 87: $V9, 88: $Va, 89: $Vb, 90: 41 }, { 20: 28, 50: 45, 64: 29, 65: 38, 66: 39, 67: $V3, 73: $Vc, 75: $V5, 82: 31, 83: 32, 84: $V6, 85: $V7, 86: $V8, 87: $V9, 88: $Va, 89: $Vb, 90: 41 }, o($Vd, $V0, { 6: 3, 4: 47 }), o($Ve, $V0, { 6: 3, 4: 48 }), o($Vf, [2, 54], { 17: 49 }), { 20: 28, 50: 50, 64: 29, 65: 38, 66: 39, 67: $V3, 73: $Vc, 75: $V5, 82: 31, 83: 32, 84: $V6, 85: $V7, 86: $V8, 87: $V9, 88: $Va, 89: $Vb, 90: 41 }, o($Vg, $V0, { 6: 3, 4: 51 }), o([5, 14, 15, 18, 19, 29, 34, 39, 44, 47, 48, 53, 57, 61], [2, 10]), { 20: 52, 64: 53, 65: 38, 66: 39, 67: $V3, 73: $Vc, 75: $V5, 82: 31, 83: 32, 84: $V6, 85: $V7, 86: $V8, 87: $V9, 88: $Va, 89: $Vb, 90: 41 }, { 20: 54, 64: 53, 65: 38, 66: 39, 67: $V3, 73: $Vc, 75: $V5, 82: 31, 83: 32, 84: $V6, 85: $V7, 86: $V8, 87: $V9, 88: $Va, 89: $Vb, 90: 41 }, { 20: 55, 64: 53, 65: 38, 66: 39, 67: $V3, 73: $Vc, 75: $V5, 82: 31, 83: 32, 84: $V6, 85: $V7, 86: $V8, 87: $V9, 88: $Va, 89: $Vb, 90: 41 }, { 20: 28, 50: 56, 64: 29, 65: 38, 66: 39, 67: $V3, 73: $Vc, 75: $V5, 82: 31, 83: 32, 84: $V6, 85: $V7, 86: $V8, 87: $V9, 88: $Va, 89: $Vb, 90: 41 }, { 33: [1, 57] }, o($Vh, [2, 84], { 51: 58 }), o([23, 33, 56, 68, 79], [2, 34], { 72: 59, 73: [1, 60] }), o($Vi, [2, 28]), o($Vi, [2, 29], { 91: 61, 92: $Vj, 93: $Vk }), o($Vl, [2, 104]), o($Vi, [2, 38]), o($Vi, [2, 39]), o($Vi, [2, 40]), o($Vi, [2, 41]), o($Vi, [2, 42]), o($Vi, [2, 43]), o($Vi, [2, 44]), o($Vm, [2, 30]), o($Vm, [2, 31]), o([23, 33, 56, 67, 68, 73, 75, 79, 84, 85, 86, 87, 88, 89, 92, 93], $Vn, { 74: $Vo }), o($Vi, [2, 49], { 91: 65, 92: $Vj, 93: $Vk }), { 73: $Vc, 90: 66 }, o($Vp, [2, 106], { 76: 67 }), { 20: 28, 49: 68, 50: 69, 64: 29, 65: 38, 66: 39, 67: $V3, 71: 27, 72: 30, 73: $V4, 75: $V5, 82: 31, 83: 32, 84: $V6, 85: $V7, 86: $V8, 87: $V9, 88: $Va, 89: $Vb, 90: 41 }, o($Vq, [2, 88], { 54: 70 }), o($Vm, $Vn), { 25: 71, 38: 73, 39: $Vr, 43: 74, 44: $Vs, 45: 72, 47: [2, 60] }, { 28: 77, 43: 78, 44: $Vs, 47: [2, 62] }, { 13: 80, 15: $V1, 18: [1, 79] }, o($Vh, [2, 92], { 58: 81 }), { 26: 82, 47: $Vt }, o($Vu, [2, 64], { 30: 84 }), { 91: 61, 92: $Vj, 93: $Vk }, o($Vu, [2, 70], { 35: 85 }), o($Vv, [2, 56], { 21: 86 }), o($Vh, [2, 96], { 62: 87 }), o($V2, [2, 22]), { 20: 28, 33: [2, 86], 49: 90, 50: 89, 52: 88, 64: 29, 65: 38, 66: 39, 67: $V3, 71: 27, 72: 30, 73: $V4, 75: $V5, 82: 31, 83: 32, 84: $V6, 85: $V7, 86: $V8, 87: $V9, 88: $Va, 89: $Vb, 90: 41 }, o($Vl, [2, 105]), { 74: $Vo }, { 73: $Vc, 90: 91 }, { 73: [2, 46] }, { 73: [2, 47] }, { 20: 28, 50: 92, 64: 29, 65: 38, 66: 39, 67: $V3, 73: $Vc, 75: $V5, 82: 31, 83: 32, 84: $V6, 85: $V7, 86: $V8, 87: $V9, 88: $Va, 89: $Vb, 90: 41 }, { 73: [1, 93] }, o($Vi, [2, 45], { 91: 65, 92: $Vj, 93: $Vk }), { 20: 28, 50: 95, 64: 29, 65: 38, 66: 39, 67: $V3, 73: $Vc, 75: $V5, 77: [1, 94],