UNPKG

ember-legacy-class-transform

Version:
1,393 lines (1,289 loc) 53.6 kB
define('@glimmer/syntax', ['exports', 'simple-html-tokenizer', '@glimmer/util', 'handlebars'], function (exports, simpleHtmlTokenizer, _glimmer_util, handlebars) { 'use strict'; function isCall(node) { return node.type === 'SubExpression' || node.type === 'MustacheStatement' && node.path.type === 'PathExpression'; } function isLiteral(input) { return !!(typeof input === 'object' && input.type.match(/Literal$/)); } var nodes = Object.freeze({ isCall: isCall, isLiteral: isLiteral }); function buildMustache(path, params, hash, raw, loc) { if (!isLiteral(path)) { path = buildPath(path); } return { type: "MustacheStatement", path: path, params: params || [], hash: hash || buildHash([]), escaped: !raw, loc: buildLoc(loc || null) }; } function buildBlock(path, params, hash, program, inverse, loc) { return { type: "BlockStatement", path: buildPath(path), params: params || [], hash: hash || buildHash([]), program: program || null, inverse: inverse || null, loc: buildLoc(loc || null) }; } function buildElementModifier(path, params, hash, loc) { return { type: "ElementModifierStatement", path: buildPath(path), params: params || [], hash: hash || buildHash([]), loc: buildLoc(loc || null) }; } function buildPartial(name, params, hash, indent, loc) { return { type: "PartialStatement", name: name, params: params || [], hash: hash || buildHash([]), indent: indent || '', strip: { open: false, close: false }, loc: buildLoc(loc || null) }; } function buildComment(value, loc) { return { type: "CommentStatement", value: value, loc: buildLoc(loc || null) }; } function buildMustacheComment(value, loc) { return { type: "MustacheCommentStatement", value: value, loc: buildLoc(loc || null) }; } function buildConcat(parts, loc) { return { type: "ConcatStatement", parts: parts || [], loc: buildLoc(loc || null) }; } function buildElement(tag, attributes, modifiers, children, comments, loc) { // this is used for backwards compat prior to `comments` being added to the AST if (!Array.isArray(comments)) { loc = comments; comments = []; } return { type: "ElementNode", tag: tag || "", attributes: attributes || [], blockParams: [], modifiers: modifiers || [], comments: comments || [], children: children || [], loc: buildLoc(loc || null) }; } function buildAttr(name, value, loc) { return { type: "AttrNode", name: name, value: value, loc: buildLoc(loc || null) }; } function buildText(chars, loc) { return { type: "TextNode", chars: chars || "", loc: buildLoc(loc || null) }; } // Expressions function buildSexpr(path, params, hash, loc) { return { type: "SubExpression", path: buildPath(path), params: params || [], hash: hash || buildHash([]), loc: buildLoc(loc || null) }; } function buildPath(original, loc) { if (typeof original !== 'string') return original; var parts = original.split('.'); var thisHead = false; if (parts[0] === 'this') { thisHead = true; parts = parts.slice(1); } return { type: "PathExpression", original: original, this: thisHead, parts: parts, data: false, loc: buildLoc(loc || null) }; } function buildLiteral(type, value, loc) { return { type: type, value: value, original: value, loc: buildLoc(loc || null) }; } // Miscellaneous function buildHash(pairs, loc) { return { type: "Hash", pairs: pairs || [], loc: buildLoc(loc || null) }; } function buildPair(key, value, loc) { return { type: "HashPair", key: key, value: value, loc: buildLoc(loc || null) }; } function buildProgram(body, blockParams, loc) { return { type: "Program", body: body || [], blockParams: blockParams || [], loc: buildLoc(loc || null) }; } function buildSource(source) { return source || null; } function buildPosition(line, column) { return { line: line, column: column }; } var SYNTHETIC = { source: '(synthetic)', start: { line: 1, column: 0 }, end: { line: 1, column: 0 } }; function buildLoc() { for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } if (args.length === 1) { var loc = args[0]; if (loc && typeof loc === 'object') { return { source: buildSource(loc.source), start: buildPosition(loc.start.line, loc.start.column), end: buildPosition(loc.end.line, loc.end.column) }; } else { return SYNTHETIC; } } else { var startLine = args[0], startColumn = args[1], endLine = args[2], endColumn = args[3], source = args[4]; return { source: buildSource(source), start: buildPosition(startLine, startColumn), end: buildPosition(endLine, endColumn) }; } } var b = { mustache: buildMustache, block: buildBlock, partial: buildPartial, comment: buildComment, mustacheComment: buildMustacheComment, element: buildElement, elementModifier: buildElementModifier, attr: buildAttr, text: buildText, sexpr: buildSexpr, path: buildPath, concat: buildConcat, hash: buildHash, pair: buildPair, literal: buildLiteral, program: buildProgram, loc: buildLoc, pos: buildPosition, string: literal('StringLiteral'), boolean: literal('BooleanLiteral'), number: literal('NumberLiteral'), undefined: function () { return buildLiteral('UndefinedLiteral', undefined); }, null: function () { return buildLiteral('NullLiteral', null); } }; function literal(type) { return function (value) { return buildLiteral(type, value); }; } /** * Subclass of `Error` with additional information * about location of incorrect markup. */ var SyntaxError = function () { SyntaxError.prototype = Object.create(Error.prototype); SyntaxError.prototype.constructor = SyntaxError; function SyntaxError(message, location) { var error = Error.call(this, message); this.message = message; this.stack = error.stack; this.location = location; } return SyntaxError; }(); // Regex to validate the identifier for block parameters. // Based on the ID validation regex in Handlebars. var ID_INVERSE_PATTERN = /[!"#%-,\.\/;->@\[-\^`\{-~]/; // Checks the element's attributes to see if it uses block params. // If it does, registers the block params with the program and // removes the corresponding attributes from the element. function parseElementBlockParams(element) { var params = parseBlockParams(element); if (params) element.blockParams = params; } function parseBlockParams(element) { var l = element.attributes.length; var attrNames = []; for (var i = 0; i < l; i++) { attrNames.push(element.attributes[i].name); } var asIndex = attrNames.indexOf('as'); if (asIndex !== -1 && l > asIndex && attrNames[asIndex + 1].charAt(0) === '|') { // Some basic validation, since we're doing the parsing ourselves var paramsString = attrNames.slice(asIndex).join(' '); if (paramsString.charAt(paramsString.length - 1) !== '|' || paramsString.match(/\|/g).length !== 2) { throw new SyntaxError('Invalid block parameters syntax: \'' + paramsString + '\'', element.loc); } var params = []; for (var _i = asIndex + 1; _i < l; _i++) { var param = attrNames[_i].replace(/\|/g, ''); if (param !== '') { if (ID_INVERSE_PATTERN.test(param)) { throw new SyntaxError('Invalid identifier for block parameters: \'' + param + '\' in \'' + paramsString + '\'', element.loc); } params.push(param); } } if (params.length === 0) { throw new SyntaxError('Cannot use zero block parameters: \'' + paramsString + '\'', element.loc); } element.attributes = element.attributes.slice(0, asIndex); return params; } return null; } function childrenFor(node) { switch (node.type) { case 'Program': return node.body; case 'ElementNode': return node.children; } } function appendChild(parent, node) { childrenFor(parent).push(node); } function isLiteral$1(path) { return path.type === 'StringLiteral' || path.type === 'BooleanLiteral' || path.type === 'NumberLiteral' || path.type === 'NullLiteral' || path.type === 'UndefinedLiteral'; } function printLiteral(literal) { if (literal.type === 'UndefinedLiteral') { return 'undefined'; } else { return JSON.stringify(literal.value); } } var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck$2(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var entityParser = new simpleHtmlTokenizer.EntityParser(simpleHtmlTokenizer.HTML5NamedCharRefs); var Parser = function () { function Parser(source) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; _classCallCheck$2(this, Parser); this.elementStack = []; this.currentAttribute = null; this.currentNode = null; this.tokenizer = new simpleHtmlTokenizer.EventedTokenizer(this, entityParser); this.options = options; this.source = source.split(/(?:\r\n?|\n)/g); } Parser.prototype.acceptNode = function acceptNode(node) { return this[node.type](node); }; Parser.prototype.currentElement = function currentElement() { return this.elementStack[this.elementStack.length - 1]; }; Parser.prototype.sourceForNode = function sourceForNode(node, endNode) { var firstLine = node.loc.start.line - 1; var currentLine = firstLine - 1; var firstColumn = node.loc.start.column; var string = []; var line = void 0; var lastLine = void 0; var lastColumn = void 0; if (endNode) { lastLine = endNode.loc.end.line - 1; lastColumn = endNode.loc.end.column; } else { lastLine = node.loc.end.line - 1; lastColumn = node.loc.end.column; } while (currentLine < lastLine) { currentLine++; line = this.source[currentLine]; if (currentLine === firstLine) { if (firstLine === lastLine) { string.push(line.slice(firstColumn, lastColumn)); } else { string.push(line.slice(firstColumn)); } } else if (currentLine === lastLine) { string.push(line.slice(0, lastColumn)); } else { string.push(line); } } return string.join('\n'); }; _createClass(Parser, [{ key: 'currentAttr', get: function () { return this.currentAttribute; } }, { key: 'currentTag', get: function () { var node = this.currentNode; _glimmer_util.assert(node && (node.type === 'StartTag' || node.type === 'EndTag'), 'expected tag'); return node; } }, { key: 'currentStartTag', get: function () { var node = this.currentNode; _glimmer_util.assert(node && node.type === 'StartTag', 'expected start tag'); return node; } }, { key: 'currentEndTag', get: function () { var node = this.currentNode; _glimmer_util.assert(node && node.type === 'EndTag', 'expected end tag'); return node; } }, { key: 'currentComment', get: function () { var node = this.currentNode; _glimmer_util.assert(node && node.type === 'CommentStatement', 'expected a comment'); return node; } }, { key: 'currentData', get: function () { var node = this.currentNode; _glimmer_util.assert(node && node.type === 'TextNode', 'expected a text node'); return node; } }]); return Parser; }(); function _defaults$1(obj, defaults) { var keys = Object.getOwnPropertyNames(defaults); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = Object.getOwnPropertyDescriptor(defaults, key); if (value && value.configurable && obj[key] === undefined) { Object.defineProperty(obj, key, value); } } return obj; } function _classCallCheck$1(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn$1(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits$1(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : _defaults$1(subClass, superClass); } var HandlebarsNodeVisitors = function (_Parser) { _inherits$1(HandlebarsNodeVisitors, _Parser); function HandlebarsNodeVisitors() { _classCallCheck$1(this, HandlebarsNodeVisitors); return _possibleConstructorReturn$1(this, _Parser.apply(this, arguments)); } HandlebarsNodeVisitors.prototype.Program = function Program(program) { var body = []; var node = b.program(body, program.blockParams, program.loc); var i = void 0, l = program.body.length; this.elementStack.push(node); if (l === 0) { return this.elementStack.pop(); } for (i = 0; i < l; i++) { this.acceptNode(program.body[i]); } // Ensure that that the element stack is balanced properly. var poppedNode = this.elementStack.pop(); if (poppedNode !== node) { var elementNode = poppedNode; throw new SyntaxError("Unclosed element `" + elementNode.tag + "` (on line " + elementNode.loc.start.line + ").", elementNode.loc); } return node; }; HandlebarsNodeVisitors.prototype.BlockStatement = function BlockStatement(block) { if (this.tokenizer['state'] === 'comment') { this.appendToCommentData(this.sourceForNode(block)); return; } if (this.tokenizer['state'] !== 'comment' && this.tokenizer['state'] !== 'data' && this.tokenizer['state'] !== 'beforeData') { throw new SyntaxError("A block may only be used inside an HTML element or another block.", block.loc); } var _acceptCallNodes = acceptCallNodes(this, block), path = _acceptCallNodes.path, params = _acceptCallNodes.params, hash = _acceptCallNodes.hash; var program = this.Program(block.program); var inverse = block.inverse ? this.Program(block.inverse) : null; var node = b.block(path, params, hash, program, inverse, block.loc); var parentProgram = this.currentElement(); appendChild(parentProgram, node); }; HandlebarsNodeVisitors.prototype.MustacheStatement = function MustacheStatement(rawMustache) { var tokenizer = this.tokenizer; if (tokenizer['state'] === 'comment') { this.appendToCommentData(this.sourceForNode(rawMustache)); return; } var mustache = void 0; var escaped = rawMustache.escaped, loc = rawMustache.loc; if (rawMustache.path.type.match(/Literal$/)) { mustache = { type: 'MustacheStatement', path: this.acceptNode(rawMustache.path), params: [], hash: b.hash(), escaped: escaped, loc: loc }; } else { var _acceptCallNodes2 = acceptCallNodes(this, rawMustache), path = _acceptCallNodes2.path, params = _acceptCallNodes2.params, hash = _acceptCallNodes2.hash; mustache = b.mustache(path, params, hash, !escaped, loc); } switch (tokenizer.state) { // Tag helpers case "tagName": addElementModifier(this.currentStartTag, mustache); tokenizer.state = "beforeAttributeName"; break; case "beforeAttributeName": addElementModifier(this.currentStartTag, mustache); break; case "attributeName": case "afterAttributeName": this.beginAttributeValue(false); this.finishAttributeValue(); addElementModifier(this.currentStartTag, mustache); tokenizer.state = "beforeAttributeName"; break; case "afterAttributeValueQuoted": addElementModifier(this.currentStartTag, mustache); tokenizer.state = "beforeAttributeName"; break; // Attribute values case "beforeAttributeValue": appendDynamicAttributeValuePart(this.currentAttribute, mustache); tokenizer.state = 'attributeValueUnquoted'; break; case "attributeValueDoubleQuoted": case "attributeValueSingleQuoted": case "attributeValueUnquoted": appendDynamicAttributeValuePart(this.currentAttribute, mustache); break; // TODO: Only append child when the tokenizer state makes // sense to do so, otherwise throw an error. default: appendChild(this.currentElement(), mustache); } return mustache; }; HandlebarsNodeVisitors.prototype.ContentStatement = function ContentStatement(content) { updateTokenizerLocation(this.tokenizer, content); this.tokenizer.tokenizePart(content.value); this.tokenizer.flushData(); }; HandlebarsNodeVisitors.prototype.CommentStatement = function CommentStatement(rawComment) { var tokenizer = this.tokenizer; if (tokenizer.state === 'comment') { this.appendToCommentData(this.sourceForNode(rawComment)); return null; } var value = rawComment.value, loc = rawComment.loc; var comment = b.mustacheComment(value, loc); switch (tokenizer.state) { case "beforeAttributeName": this.currentStartTag.comments.push(comment); break; case 'beforeData': case 'data': appendChild(this.currentElement(), comment); break; default: throw new SyntaxError("Using a Handlebars comment when in the `" + tokenizer.state + "` state is not supported: \"" + comment.value + "\" on line " + loc.start.line + ":" + loc.start.column, rawComment.loc); } return comment; }; HandlebarsNodeVisitors.prototype.PartialStatement = function PartialStatement(partial) { var loc = partial.loc; throw new SyntaxError("Handlebars partials are not supported: \"" + this.sourceForNode(partial, partial.name) + "\" at L" + loc.start.line + ":C" + loc.start.column, partial.loc); }; HandlebarsNodeVisitors.prototype.PartialBlockStatement = function PartialBlockStatement(partialBlock) { var loc = partialBlock.loc; throw new SyntaxError("Handlebars partial blocks are not supported: \"" + this.sourceForNode(partialBlock, partialBlock.name) + "\" at L" + loc.start.line + ":C" + loc.start.column, partialBlock.loc); }; HandlebarsNodeVisitors.prototype.Decorator = function Decorator(decorator) { var loc = decorator.loc; throw new SyntaxError("Handlebars decorators are not supported: \"" + this.sourceForNode(decorator, decorator.path) + "\" at L" + loc.start.line + ":C" + loc.start.column, decorator.loc); }; HandlebarsNodeVisitors.prototype.DecoratorBlock = function DecoratorBlock(decoratorBlock) { var loc = decoratorBlock.loc; throw new SyntaxError("Handlebars decorator blocks are not supported: \"" + this.sourceForNode(decoratorBlock, decoratorBlock.path) + "\" at L" + loc.start.line + ":C" + loc.start.column, decoratorBlock.loc); }; HandlebarsNodeVisitors.prototype.SubExpression = function SubExpression(sexpr) { var _acceptCallNodes3 = acceptCallNodes(this, sexpr), path = _acceptCallNodes3.path, params = _acceptCallNodes3.params, hash = _acceptCallNodes3.hash; return b.sexpr(path, params, hash, sexpr.loc); }; HandlebarsNodeVisitors.prototype.PathExpression = function PathExpression(path) { var original = path.original, loc = path.loc; var parts = void 0; if (original.indexOf('/') !== -1) { if (original.slice(0, 2) === './') { throw new SyntaxError("Using \"./\" is not supported in Glimmer and unnecessary: \"" + path.original + "\" on line " + loc.start.line + ".", path.loc); } if (original.slice(0, 3) === '../') { throw new SyntaxError("Changing context using \"../\" is not supported in Glimmer: \"" + path.original + "\" on line " + loc.start.line + ".", path.loc); } if (original.indexOf('.') !== -1) { throw new SyntaxError("Mixing '.' and '/' in paths is not supported in Glimmer; use only '.' to separate property paths: \"" + path.original + "\" on line " + loc.start.line + ".", path.loc); } parts = [path.parts.join('/')]; } else { parts = path.parts; } var thisHead = false; // This is to fix a bug in the Handlebars AST where the path expressions in // `{{this.foo}}` (and similarly `{{foo-bar this.foo named=this.foo}}` etc) // are simply turned into `{{foo}}`. The fix is to push it back onto the // parts array and let the runtime see the difference. However, we cannot // simply use the string `this` as it means literally the property called // "this" in the current context (it can be expressed in the syntax as // `{{[this]}}`, where the square bracket are generally for this kind of // escaping – such as `{{foo.["bar.baz"]}}` would mean lookup a property // named literally "bar.baz" on `this.foo`). By convention, we use `null` // for this purpose. if (original.match(/^this(\..+)?$/)) { thisHead = true; } return { type: 'PathExpression', original: path.original, this: thisHead, parts: parts, data: path.data, loc: path.loc }; }; HandlebarsNodeVisitors.prototype.Hash = function Hash(hash) { var pairs = []; for (var i = 0; i < hash.pairs.length; i++) { var pair = hash.pairs[i]; pairs.push(b.pair(pair.key, this.acceptNode(pair.value), pair.loc)); } return b.hash(pairs, hash.loc); }; HandlebarsNodeVisitors.prototype.StringLiteral = function StringLiteral(string) { return b.literal('StringLiteral', string.value, string.loc); }; HandlebarsNodeVisitors.prototype.BooleanLiteral = function BooleanLiteral(boolean) { return b.literal('BooleanLiteral', boolean.value, boolean.loc); }; HandlebarsNodeVisitors.prototype.NumberLiteral = function NumberLiteral(number) { return b.literal('NumberLiteral', number.value, number.loc); }; HandlebarsNodeVisitors.prototype.UndefinedLiteral = function UndefinedLiteral(undef) { return b.literal('UndefinedLiteral', undefined, undef.loc); }; HandlebarsNodeVisitors.prototype.NullLiteral = function NullLiteral(nul) { return b.literal('NullLiteral', null, nul.loc); }; return HandlebarsNodeVisitors; }(Parser); function calculateRightStrippedOffsets(original, value) { if (value === '') { // if it is empty, just return the count of newlines // in original return { lines: original.split("\n").length - 1, columns: 0 }; } // otherwise, return the number of newlines prior to // `value` var difference = original.split(value)[0]; var lines = difference.split(/\n/); var lineCount = lines.length - 1; return { lines: lineCount, columns: lines[lineCount].length }; } function updateTokenizerLocation(tokenizer, content) { var line = content.loc.start.line; var column = content.loc.start.column; var offsets = calculateRightStrippedOffsets(content.original, content.value); line = line + offsets.lines; if (offsets.lines) { column = offsets.columns; } else { column = column + offsets.columns; } tokenizer.line = line; tokenizer.column = column; } function acceptCallNodes(compiler, node) { var path = compiler.PathExpression(node.path); var params = node.params ? node.params.map(function (e) { return compiler.acceptNode(e); }) : []; var hash = node.hash ? compiler.Hash(node.hash) : b.hash(); return { path: path, params: params, hash: hash }; } function addElementModifier(element, mustache) { var path = mustache.path, params = mustache.params, hash = mustache.hash, loc = mustache.loc; if (isLiteral$1(path)) { var _modifier = "{{" + printLiteral(path) + "}}"; var tag = "<" + element.name + " ... " + _modifier + " ..."; throw new SyntaxError("In " + tag + ", " + _modifier + " is not a valid modifier: \"" + path.original + "\" on line " + (loc && loc.start.line) + ".", mustache.loc); } var modifier = b.elementModifier(path, params, hash, loc); element.modifiers.push(modifier); } function appendDynamicAttributeValuePart(attribute, part) { attribute.isDynamic = true; attribute.parts.push(part); } var visitorKeys = { Program: ['body'], MustacheStatement: ['path', 'params', 'hash'], BlockStatement: ['path', 'params', 'hash', 'program', 'inverse'], ElementModifierStatement: ['path', 'params', 'hash'], PartialStatement: ['name', 'params', 'hash'], CommentStatement: [], MustacheCommentStatement: [], ElementNode: ['attributes', 'modifiers', 'children', 'comments'], AttrNode: ['value'], TextNode: [], ConcatStatement: ['parts'], SubExpression: ['path', 'params', 'hash'], PathExpression: [], StringLiteral: [], BooleanLiteral: [], NumberLiteral: [], NullLiteral: [], UndefinedLiteral: [], Hash: ['pairs'], HashPair: ['value'] }; var TraversalError = function () { TraversalError.prototype = Object.create(Error.prototype); TraversalError.prototype.constructor = TraversalError; function TraversalError(message, node, parent, key) { var error = Error.call(this, message); this.key = key; this.message = message; this.node = node; this.parent = parent; this.stack = error.stack; } return TraversalError; }(); function cannotRemoveNode(node, parent, key) { return new TraversalError("Cannot remove a node unless it is part of an array", node, parent, key); } function cannotReplaceNode(node, parent, key) { return new TraversalError("Cannot replace a node with multiple nodes unless it is part of an array", node, parent, key); } function cannotReplaceOrRemoveInKeyHandlerYet(node, key) { return new TraversalError("Replacing and removing in key handlers is not yet supported.", node, null, key); } function visitNode(visitor, node) { var handler = visitor[node.type] || visitor.All || null; var result = void 0; if (handler && handler['enter']) { result = handler['enter'].call(null, node); } if (result !== undefined && result !== null) { if (JSON.stringify(node) === JSON.stringify(result)) { result = undefined; } else if (Array.isArray(result)) { return visitArray(visitor, result) || result; } else { return visitNode(visitor, result) || result; } } if (result === undefined) { var keys = visitorKeys[node.type]; for (var i = 0; i < keys.length; i++) { visitKey(visitor, handler, node, keys[i]); } if (handler && handler['exit']) { result = handler['exit'].call(null, node); } } return result; } function visitKey(visitor, handler, node, key) { var value = node[key]; if (!value) { return; } var keyHandler = handler && (handler.keys[key] || handler.keys.All); var result = void 0; if (keyHandler && keyHandler.enter) { result = keyHandler.enter.call(null, node, key); if (result !== undefined) { throw cannotReplaceOrRemoveInKeyHandlerYet(node, key); } } if (Array.isArray(value)) { visitArray(visitor, value); } else { var _result = visitNode(visitor, value); if (_result !== undefined) { assignKey(node, key, _result); } } if (keyHandler && keyHandler.exit) { result = keyHandler.exit.call(null, node, key); if (result !== undefined) { throw cannotReplaceOrRemoveInKeyHandlerYet(node, key); } } } function visitArray(visitor, array) { for (var i = 0; i < array.length; i++) { var result = visitNode(visitor, array[i]); if (result !== undefined) { i += spliceArray(array, i, result) - 1; } } } function assignKey(node, key, result) { if (result === null) { throw cannotRemoveNode(node[key], node, key); } else if (Array.isArray(result)) { if (result.length === 1) { node[key] = result[0]; } else { if (result.length === 0) { throw cannotRemoveNode(node[key], node, key); } else { throw cannotReplaceNode(node[key], node, key); } } } else { node[key] = result; } } function spliceArray(array, index, result) { if (result === null) { array.splice(index, 1); return 0; } else if (Array.isArray(result)) { array.splice.apply(array, [index, 1].concat(result)); return result.length; } else { array.splice(index, 1, result); return 1; } } function traverse(node, visitor) { visitNode(normalizeVisitor(visitor), node); } function normalizeVisitor(visitor) { var normalizedVisitor = {}; for (var type in visitor) { var handler = visitor[type] || visitor.All; var normalizedKeys = {}; if (typeof handler === 'object') { var keys = handler.keys; if (keys) { for (var key in keys) { var keyHandler = keys[key]; if (typeof keyHandler === 'object') { normalizedKeys[key] = { enter: typeof keyHandler.enter === 'function' ? keyHandler.enter : null, exit: typeof keyHandler.exit === 'function' ? keyHandler.exit : null }; } else if (typeof keyHandler === 'function') { normalizedKeys[key] = { enter: keyHandler, exit: null }; } } } normalizedVisitor[type] = { enter: typeof handler.enter === 'function' ? handler.enter : null, exit: typeof handler.exit === 'function' ? handler.exit : null, keys: normalizedKeys }; } else if (typeof handler === 'function') { normalizedVisitor[type] = { enter: handler, exit: null, keys: normalizedKeys }; } } return normalizedVisitor; } function unreachable() { throw new Error('unreachable'); } function build(ast) { if (!ast) { return ''; } var output = []; switch (ast.type) { case 'Program': { var chainBlock = ast['chained'] && ast.body[0]; if (chainBlock) { chainBlock['chained'] = true; } var body = buildEach(ast.body).join(''); output.push(body); } break; case 'ElementNode': output.push('<', ast.tag); if (ast.attributes.length) { output.push(' ', buildEach(ast.attributes).join(' ')); } if (ast.modifiers.length) { output.push(' ', buildEach(ast.modifiers).join(' ')); } if (ast.comments.length) { output.push(' ', buildEach(ast.comments).join(' ')); } output.push('>'); output.push.apply(output, buildEach(ast.children)); output.push('</', ast.tag, '>'); break; case 'AttrNode': output.push(ast.name, '='); var value = build(ast.value); if (ast.value.type === 'TextNode') { output.push('"', value, '"'); } else { output.push(value); } break; case 'ConcatStatement': output.push('"'); ast.parts.forEach(function (node) { if (node.type === 'StringLiteral') { output.push(node.original); } else { output.push(build(node)); } }); output.push('"'); break; case 'TextNode': output.push(ast.chars); break; case 'MustacheStatement': { output.push(compactJoin(['{{', pathParams(ast), '}}'])); } break; case 'MustacheCommentStatement': { output.push(compactJoin(['{{!--', ast.value, '--}}'])); } break; case 'ElementModifierStatement': { output.push(compactJoin(['{{', pathParams(ast), '}}'])); } break; case 'PathExpression': output.push(ast.original); break; case 'SubExpression': { output.push('(', pathParams(ast), ')'); } break; case 'BooleanLiteral': output.push(ast.value ? 'true' : 'false'); break; case 'BlockStatement': { var lines = []; if (ast['chained']) { lines.push(['{{else ', pathParams(ast), '}}'].join('')); } else { lines.push(openBlock(ast)); } lines.push(build(ast.program)); if (ast.inverse) { if (!ast.inverse['chained']) { lines.push('{{else}}'); } lines.push(build(ast.inverse)); } if (!ast['chained']) { lines.push(closeBlock(ast)); } output.push(lines.join('')); } break; case 'PartialStatement': { output.push(compactJoin(['{{>', pathParams(ast), '}}'])); } break; case 'CommentStatement': { output.push(compactJoin(['<!--', ast.value, '-->'])); } break; case 'StringLiteral': { output.push('"' + ast.value + '"'); } break; case 'NumberLiteral': { output.push(String(ast.value)); } break; case 'UndefinedLiteral': { output.push('undefined'); } break; case 'NullLiteral': { output.push('null'); } break; case 'Hash': { output.push(ast.pairs.map(function (pair) { return build(pair); }).join(' ')); } break; case 'HashPair': { output.push(ast.key + '=' + build(ast.value)); } break; } return output.join(''); } function compact(array) { var newArray = []; array.forEach(function (a) { if (typeof a !== 'undefined' && a !== null && a !== '') { newArray.push(a); } }); return newArray; } function buildEach(asts) { return asts.map(build); } function pathParams(ast) { var path = void 0; switch (ast.type) { case 'MustacheStatement': case 'SubExpression': case 'ElementModifierStatement': case 'BlockStatement': if (isLiteral(ast.path)) { return String(ast.path.value); } path = build(ast.path); break; case 'PartialStatement': path = build(ast.name); break; default: return unreachable(); } return compactJoin([path, buildEach(ast.params).join(' '), build(ast.hash)], ' '); } function compactJoin(array, delimiter) { return compact(array).join(delimiter || ''); } function blockParams(block) { var params = block.program.blockParams; if (params.length) { return ' as |' + params.join(' ') + '|'; } return null; } function openBlock(block) { return ['{{#', pathParams(block), blockParams(block), '}}'].join(''); } function closeBlock(block) { return ['{{/', build(block.path), '}}'].join(''); } function _classCallCheck$3(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Walker = function () { function Walker(order) { _classCallCheck$3(this, Walker); this.order = order; this.stack = []; } Walker.prototype.visit = function visit(node, callback) { if (!node) { return; } this.stack.push(node); if (this.order === 'post') { this.children(node, callback); callback(node, this); } else { callback(node, this); this.children(node, callback); } this.stack.pop(); }; Walker.prototype.children = function children(node, callback) { var visitor = visitors[node.type]; if (visitor) { visitor(this, node, callback); } }; return Walker; }(); var visitors = { Program: function (walker, node, callback) { for (var i = 0; i < node.body.length; i++) { walker.visit(node.body[i], callback); } }, ElementNode: function (walker, node, callback) { for (var i = 0; i < node.children.length; i++) { walker.visit(node.children[i], callback); } }, BlockStatement: function (walker, node, callback) { walker.visit(node.program, callback); walker.visit(node.inverse || null, callback); } }; function _defaults(obj, defaults) { var keys = Object.getOwnPropertyNames(defaults); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = Object.getOwnPropertyDescriptor(defaults, key); if (value && value.configurable && obj[key] === undefined) { Object.defineProperty(obj, key, value); } } return obj; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : _defaults(subClass, superClass); } var voidMap = Object.create(null); var voidTagNames = "area base br col command embed hr img input keygen link meta param source track wbr"; voidTagNames.split(" ").forEach(function (tagName) { voidMap[tagName] = true; }); var TokenizerEventHandlers = function (_HandlebarsNodeVisito) { _inherits(TokenizerEventHandlers, _HandlebarsNodeVisito); function TokenizerEventHandlers() { _classCallCheck(this, TokenizerEventHandlers); var _this = _possibleConstructorReturn(this, _HandlebarsNodeVisito.apply(this, arguments)); _this.tagOpenLine = 0; _this.tagOpenColumn = 0; return _this; } TokenizerEventHandlers.prototype.reset = function reset() { this.currentNode = null; }; // Comment TokenizerEventHandlers.prototype.beginComment = function beginComment() { this.currentNode = b.comment(""); this.currentNode.loc = { source: null, start: b.pos(this.tagOpenLine, this.tagOpenColumn), end: null }; }; TokenizerEventHandlers.prototype.appendToCommentData = function appendToCommentData(char) { this.currentComment.value += char; }; TokenizerEventHandlers.prototype.finishComment = function finishComment() { this.currentComment.loc.end = b.pos(this.tokenizer.line, this.tokenizer.column); appendChild(this.currentElement(), this.currentComment); }; // Data TokenizerEventHandlers.prototype.beginData = function beginData() { this.currentNode = b.text(); this.currentNode.loc = { source: null, start: b.pos(this.tokenizer.line, this.tokenizer.column), end: null }; }; TokenizerEventHandlers.prototype.appendToData = function appendToData(char) { this.currentData.chars += char; }; TokenizerEventHandlers.prototype.finishData = function finishData() { this.currentData.loc.end = b.pos(this.tokenizer.line, this.tokenizer.column); appendChild(this.currentElement(), this.currentData); }; // Tags - basic TokenizerEventHandlers.prototype.tagOpen = function tagOpen() { this.tagOpenLine = this.tokenizer.line; this.tagOpenColumn = this.tokenizer.column; }; TokenizerEventHandlers.prototype.beginStartTag = function beginStartTag() { this.currentNode = { type: 'StartTag', name: "", attributes: [], modifiers: [], comments: [], selfClosing: false, loc: SYNTHETIC }; }; TokenizerEventHandlers.prototype.beginEndTag = function beginEndTag() { this.currentNode = { type: 'EndTag', name: "", attributes: [], modifiers: [], comments: [], selfClosing: false, loc: SYNTHETIC }; }; TokenizerEventHandlers.prototype.finishTag = function finishTag() { var _tokenizer = this.tokenizer, line = _tokenizer.line, column = _tokenizer.column; var tag = this.currentTag; tag.loc = b.loc(this.tagOpenLine, this.tagOpenColumn, line, column); if (tag.type === 'StartTag') { this.finishStartTag(); if (voidMap[tag.name] || tag.selfClosing) { this.finishEndTag(true); } } else if (tag.type === 'EndTag') { this.finishEndTag(false); } }; TokenizerEventHandlers.prototype.finishStartTag = function finishStartTag() { var _currentStartTag = this.currentStartTag, name = _currentStartTag.name, attributes = _currentStartTag.attributes, modifiers = _currentStartTag.modifiers, comments = _currentStartTag.comments; var loc = b.loc(this.tagOpenLine, this.tagOpenColumn); var element = b.element(name, attributes, modifiers, [], comments, loc); this.elementStack.push(element); }; TokenizerEventHandlers.prototype.finishEndTag = function finishEndTag(isVoid) { var tag = this.currentTag; var element = this.elementStack.pop(); var parent = this.currentElement(); validateEndTag(tag, element, isVoid); element.loc.end.line = this.tokenizer.line; element.loc.end.column = this.tokenizer.column; parseElementBlockParams(element); appendChild(parent, element); }; TokenizerEventHandlers.prototype.markTagAsSelfClosing = function markTagAsSelfClosing() { this.currentTag.selfClosing = true; }; // Tags - name TokenizerEventHandlers.prototype.appendToTagName = function appendToTagName(char) { this.currentTag.name += char; }; // Tags - attributes TokenizerEventHandlers.prototype.beginAttribute = function beginAttribute() { var tag = this.currentTag; if (tag.type === 'EndTag') { throw new SyntaxError("Invalid end tag: closing tag must not have attributes, " + ("in `" + tag.name + "` (on line " + this.tokenizer.line + ")."), tag.loc); } this.currentAttribute = { name: "", parts: [], isQuoted: false, isDynamic: false, start: b.pos(this.tokenizer.line, this.tokenizer.column), valueStartLine: 0, valueStartColumn: 0 }; }; TokenizerEventHandlers.prototype.appendToAttributeName = function appendToAttributeName(char) { this.currentAttr.name += char; }; TokenizerEventHandlers.prototype.beginAttributeValue = function beginAttributeValue(isQuoted) { this.currentAttr.isQuoted = isQuoted; this.currentAttr.valueStartLine = this.tokenizer.line; this.currentAttr.valueStartColumn = this.tokenizer.column; }; TokenizerEventHandlers.prototype.appendToAttributeValue = function appendToAttributeValue(char) { var parts = this.currentAttr.parts; var lastPart = parts[parts.length - 1]; if (lastPart && lastPart.type === 'TextNode') { lastPart.chars += char; // update end location for each added char lastPart.loc.end.line = this.tokenizer.line; lastPart.loc.end.column = this.tokenizer.column; } else { // initially assume the text node is a single char var loc = b.loc(this.tokenizer.line, this.tokenizer.column, this.tokenizer.line, this.tokenizer.column); // correct for `\n` as first char if (char === '\n') { loc.start.line -= 1; loc.start.column = lastPart ? lastPart.loc.end.column : this.currentAttr.valueStartColumn; } var text = b.text(char, loc); parts.push(text); } }; TokenizerEventHandlers.prototype.finishAttributeValue = function finishAttributeValue() { var _currentAttr = this.currentAttr, name = _currentAttr.name, parts = _currentAttr.parts, isQuoted = _currentAttr.isQuoted, isDynamic = _currentAttr.isDynamic, valueStartLine = _currentAttr.valueStartLine, valueStartColumn = _currentAttr.valueStartColumn; var value = assembleAttributeValue(parts, isQuoted, isDynamic, this.tokenizer.line); value.loc = b.loc(valueStartLine, valueStartColumn, this.tokenizer.line, this.tokenizer.column); var loc = b.loc(this.currentAttr.start.line, this.currentAttr.start.column, this.token