@glimmer/compiler
Version:
1,344 lines (1,098 loc) • 350 kB
JavaScript
define('@glimmer/compiler', ['exports', '@glimmer/util', '@glimmer/wire-format', '@glimmer/syntax'], function (exports, util, wireFormat, syntax) { 'use strict';
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 _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 _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); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var SymbolTable = function () {
function SymbolTable() {
_classCallCheck(this, SymbolTable);
}
SymbolTable.top = function top() {
return new ProgramSymbolTable();
};
SymbolTable.prototype.child = function child(locals) {
var _this = this;
var symbols = locals.map(function (name) {
return _this.allocate(name);
});
return new BlockSymbolTable(this, locals, symbols);
};
return SymbolTable;
}();
var ProgramSymbolTable = function (_SymbolTable) {
_inherits(ProgramSymbolTable, _SymbolTable);
function ProgramSymbolTable() {
_classCallCheck(this, ProgramSymbolTable);
var _this2 = _possibleConstructorReturn(this, _SymbolTable.apply(this, arguments));
_this2.symbols = [];
_this2.freeVariables = [];
_this2.size = 1;
_this2.named = util.dict();
_this2.blocks = util.dict();
return _this2;
}
ProgramSymbolTable.prototype.has = function has(_name) {
return false;
};
ProgramSymbolTable.prototype.get = function get(_name) {
throw util.unreachable();
};
ProgramSymbolTable.prototype.getLocalsMap = function getLocalsMap() {
return {};
};
ProgramSymbolTable.prototype.getEvalInfo = function getEvalInfo() {
return [];
};
ProgramSymbolTable.prototype.allocateFree = function allocateFree(name) {
var index = this.freeVariables.indexOf(name);
if (index !== -1) {
return index;
}
index = this.freeVariables.length;
this.freeVariables.push(name);
return index;
};
ProgramSymbolTable.prototype.allocateNamed = function allocateNamed(name) {
var named = this.named[name];
if (!named) {
named = this.named[name] = this.allocate(name);
}
return named;
};
ProgramSymbolTable.prototype.allocateBlock = function allocateBlock(name) {
if (name === 'inverse') {
name = 'else';
}
var block = this.blocks[name];
if (!block) {
block = this.blocks[name] = this.allocate('&' + name);
}
return block;
};
ProgramSymbolTable.prototype.allocate = function allocate(identifier) {
this.symbols.push(identifier);
return this.size++;
};
return ProgramSymbolTable;
}(SymbolTable);
var BlockSymbolTable = function (_SymbolTable2) {
_inherits(BlockSymbolTable, _SymbolTable2);
function BlockSymbolTable(parent, symbols, slots) {
_classCallCheck(this, BlockSymbolTable);
var _this3 = _possibleConstructorReturn(this, _SymbolTable2.call(this));
_this3.parent = parent;
_this3.symbols = symbols;
_this3.slots = slots;
return _this3;
}
BlockSymbolTable.prototype.has = function has(name) {
return this.symbols.indexOf(name) !== -1 || this.parent.has(name);
};
BlockSymbolTable.prototype.get = function get(name) {
var slot = this.symbols.indexOf(name);
return slot === -1 ? this.parent.get(name) : this.slots[slot];
};
BlockSymbolTable.prototype.getLocalsMap = function getLocalsMap() {
var _this4 = this;
var dict = this.parent.getLocalsMap();
this.symbols.forEach(function (symbol) {
return dict[symbol] = _this4.get(symbol);
});
return dict;
};
BlockSymbolTable.prototype.getEvalInfo = function getEvalInfo() {
var locals = this.getLocalsMap();
return Object.keys(locals).map(function (symbol) {
return locals[symbol];
});
};
BlockSymbolTable.prototype.allocateFree = function allocateFree(name) {
return this.parent.allocateFree(name);
};
BlockSymbolTable.prototype.allocateNamed = function allocateNamed(name) {
return this.parent.allocateNamed(name);
};
BlockSymbolTable.prototype.allocateBlock = function allocateBlock(name) {
return this.parent.allocateBlock(name);
};
BlockSymbolTable.prototype.allocate = function allocate(identifier) {
return this.parent.allocate(identifier);
};
return BlockSymbolTable;
}(SymbolTable);
/**
* Takes in an AST and outputs a list of actions to be consumed
* by a compiler. For example, the template
*
* foo{{bar}}<div>baz</div>
*
* produces the actions
*
* [['startProgram', [programNode, 0]],
* ['text', [textNode, 0, 3]],
* ['mustache', [mustacheNode, 1, 3]],
* ['openElement', [elementNode, 2, 3, 0]],
* ['text', [textNode, 0, 1]],
* ['closeElement', [elementNode, 2, 3],
* ['endProgram', [programNode]]]
*
* This visitor walks the AST depth first and backwards. As
* a result the bottom-most child template will appear at the
* top of the actions list whereas the root template will appear
* at the bottom of the list. For example,
*
* <div>{{#if}}foo{{else}}bar<b></b>{{/if}}</div>
*
* produces the actions
*
* [['startProgram', [programNode, 0]],
* ['text', [textNode, 0, 2, 0]],
* ['openElement', [elementNode, 1, 2, 0]],
* ['closeElement', [elementNode, 1, 2]],
* ['endProgram', [programNode]],
* ['startProgram', [programNode, 0]],
* ['text', [textNode, 0, 1]],
* ['endProgram', [programNode]],
* ['startProgram', [programNode, 2]],
* ['openElement', [elementNode, 0, 1, 1]],
* ['block', [blockNode, 0, 1]],
* ['closeElement', [elementNode, 0, 1]],
* ['endProgram', [programNode]]]
*
* The state of the traversal is maintained by a stack of frames.
* Whenever a node with children is entered (either a ProgramNode
* or an ElementNode) a frame is pushed onto the stack. The frame
* contains information about the state of the traversal of that
* node. For example,
*
* - index of the current child node being visited
* - the number of mustaches contained within its child nodes
* - the list of actions generated by its child nodes
*/
var Frame = function Frame() {
_classCallCheck(this, Frame);
this.parentNode = null;
this.children = null;
this.childIndex = null;
this.childCount = null;
this.childTemplateCount = 0;
this.mustacheCount = 0;
this.actions = [];
this.blankChildTextNodes = null;
this.symbols = null;
};
var TemplateVisitor = function () {
function TemplateVisitor() {
_classCallCheck(this, TemplateVisitor);
this.frameStack = [];
this.actions = [];
this.programDepth = -1;
}
TemplateVisitor.prototype.visit = function visit(node) {
this[node.type](node);
};
// Traversal methods
TemplateVisitor.prototype.Block = function Block(program) {
return this.anyBlock(program);
};
TemplateVisitor.prototype.Template = function Template(program) {
return this.anyBlock(program);
};
TemplateVisitor.prototype.anyBlock = function anyBlock(program) {
var _actions;
this.programDepth++;
var parentFrame = this.getCurrentFrame();
var programFrame = this.pushFrame();
if (!parentFrame) {
program.symbols = SymbolTable.top();
} else {
program.symbols = parentFrame.symbols.child(program.blockParams);
}
var startType = void 0,
endType = void 0;
if (this.programDepth === 0) {
startType = 'startProgram';
endType = 'endProgram';
} else {
startType = 'startBlock';
endType = 'endBlock';
}
programFrame.parentNode = program;
programFrame.children = program.body;
programFrame.childCount = program.body.length;
programFrame.blankChildTextNodes = [];
programFrame.actions.push([endType, [program, this.programDepth]]);
programFrame.symbols = program['symbols'];
for (var i = program.body.length - 1; i >= 0; i--) {
programFrame.childIndex = i;
this.visit(program.body[i]);
}
programFrame.actions.push([startType, [program, programFrame.childTemplateCount, programFrame.blankChildTextNodes.reverse()]]);
this.popFrame();
this.programDepth--;
// Push the completed template into the global actions list
if (parentFrame) {
parentFrame.childTemplateCount++;
}
(_actions = this.actions).push.apply(_actions, programFrame.actions.reverse());
};
TemplateVisitor.prototype.ElementNode = function ElementNode(element) {
var _parentFrame$actions;
var parentFrame = this.currentFrame;
var elementFrame = this.pushFrame();
elementFrame.parentNode = element;
elementFrame.children = element.children;
elementFrame.childCount = element.children.length;
elementFrame.mustacheCount += element.modifiers.length;
elementFrame.blankChildTextNodes = [];
elementFrame.symbols = element.symbols = parentFrame.symbols.child(element.blockParams);
var actionArgs = [element, parentFrame.childIndex, parentFrame.childCount];
elementFrame.actions.push(['closeElement', actionArgs]);
for (var i = element.attributes.length - 1; i >= 0; i--) {
this.visit(element.attributes[i]);
}
for (var _i = element.children.length - 1; _i >= 0; _i--) {
elementFrame.childIndex = _i;
this.visit(element.children[_i]);
}
var open = ['openElement', [].concat(actionArgs, [elementFrame.mustacheCount, elementFrame.blankChildTextNodes.reverse()])];
elementFrame.actions.push(open);
this.popFrame();
// Propagate the element's frame state to the parent frame
if (elementFrame.mustacheCount > 0) {
parentFrame.mustacheCount++;
}
parentFrame.childTemplateCount += elementFrame.childTemplateCount;
(_parentFrame$actions = parentFrame.actions).push.apply(_parentFrame$actions, elementFrame.actions);
};
TemplateVisitor.prototype.AttrNode = function AttrNode(attr) {
if (attr.value.type !== 'TextNode') {
this.currentFrame.mustacheCount++;
}
};
TemplateVisitor.prototype.TextNode = function TextNode(text) {
var frame = this.currentFrame;
if (text.chars === '') {
frame.blankChildTextNodes.push(domIndexOf(frame.children, text));
}
frame.actions.push(['text', [text, frame.childIndex, frame.childCount]]);
};
TemplateVisitor.prototype.BlockStatement = function BlockStatement(node) {
var frame = this.currentFrame;
frame.mustacheCount++;
frame.actions.push(['block', [node, frame.childIndex, frame.childCount]]);
if (node.inverse) {
this.visit(node.inverse);
}
if (node.program) {
this.visit(node.program);
}
};
TemplateVisitor.prototype.PartialStatement = function PartialStatement(node) {
var frame = this.currentFrame;
frame.mustacheCount++;
frame.actions.push(['mustache', [node, frame.childIndex, frame.childCount]]);
};
TemplateVisitor.prototype.CommentStatement = function CommentStatement(text) {
var frame = this.currentFrame;
frame.actions.push(['comment', [text, frame.childIndex, frame.childCount]]);
};
TemplateVisitor.prototype.MustacheCommentStatement = function MustacheCommentStatement() {
// Intentional empty: Handlebars comments should not affect output.
};
TemplateVisitor.prototype.MustacheStatement = function MustacheStatement(mustache) {
var frame = this.currentFrame;
frame.mustacheCount++;
frame.actions.push(['mustache', [mustache, frame.childIndex, frame.childCount]]);
};
// Frame helpers
TemplateVisitor.prototype.getCurrentFrame = function getCurrentFrame() {
return this.frameStack[this.frameStack.length - 1];
};
TemplateVisitor.prototype.pushFrame = function pushFrame() {
var frame = new Frame();
this.frameStack.push(frame);
return frame;
};
TemplateVisitor.prototype.popFrame = function popFrame() {
return this.frameStack.pop();
};
_createClass(TemplateVisitor, [{
key: 'currentFrame',
get: function get() {
return this.getCurrentFrame();
}
}]);
return TemplateVisitor;
}();
function domIndexOf(nodes, domNode) {
var index = -1;
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
if (node.type !== 'TextNode' && node.type !== 'ElementNode') {
continue;
} else {
index++;
}
if (node === domNode) {
return index;
}
}
return -1;
}
var _createClass$1 = 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 _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 _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); }
function _classCallCheck$1(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Block = function () {
function Block() {
_classCallCheck$1(this, Block);
this.statements = [];
}
Block.prototype.push = function push(statement) {
this.statements.push(statement);
};
return Block;
}();
var InlineBlock = function (_Block) {
_inherits$1(InlineBlock, _Block);
function InlineBlock(table) {
_classCallCheck$1(this, InlineBlock);
var _this = _possibleConstructorReturn$1(this, _Block.call(this));
_this.table = table;
return _this;
}
InlineBlock.prototype.toJSON = function toJSON() {
return {
statements: this.statements,
parameters: this.table.slots
};
};
return InlineBlock;
}(Block);
var NamedBlock = function (_InlineBlock) {
_inherits$1(NamedBlock, _InlineBlock);
function NamedBlock(name, table) {
_classCallCheck$1(this, NamedBlock);
var _this2 = _possibleConstructorReturn$1(this, _InlineBlock.call(this, table));
_this2.name = name;
return _this2;
}
return NamedBlock;
}(InlineBlock);
var TemplateBlock = function (_Block2) {
_inherits$1(TemplateBlock, _Block2);
function TemplateBlock(symbolTable) {
_classCallCheck$1(this, TemplateBlock);
var _this3 = _possibleConstructorReturn$1(this, _Block2.call(this));
_this3.symbolTable = symbolTable;
_this3.type = 'template';
_this3.yields = new util.DictSet();
_this3.named = new util.DictSet();
_this3.blocks = [];
_this3.hasEval = false;
return _this3;
}
TemplateBlock.prototype.push = function push(statement) {
this.statements.push(statement);
};
TemplateBlock.prototype.toJSON = function toJSON() {
return {
symbols: this.symbolTable.symbols,
statements: this.statements,
hasEval: this.hasEval,
upvars: this.symbolTable.freeVariables
};
};
return TemplateBlock;
}(Block);
var ComponentBlock = function (_Block3) {
_inherits$1(ComponentBlock, _Block3);
function ComponentBlock(tag, table, selfClosing) {
_classCallCheck$1(this, ComponentBlock);
var _this4 = _possibleConstructorReturn$1(this, _Block3.call(this));
_this4.tag = tag;
_this4.table = table;
_this4.selfClosing = selfClosing;
_this4.attributes = [];
_this4.arguments = [];
_this4.inParams = true;
_this4.positionals = [];
_this4.blocks = [];
return _this4;
}
ComponentBlock.prototype.push = function push(statement) {
if (this.inParams) {
if (wireFormat.isFlushElement(statement)) {
this.inParams = false;
} else if (wireFormat.isArgument(statement)) {
this.arguments.push(statement);
} else if (wireFormat.isAttribute(statement)) {
this.attributes.push(statement);
} else {
throw new Error('Compile Error: only parameters allowed before flush-element');
}
} else {
this.statements.push(statement);
}
};
ComponentBlock.prototype.pushBlock = function pushBlock(name, block) {
this.blocks.push([name, block]);
};
ComponentBlock.prototype.toJSON = function toJSON() {
var blocks = void 0;
var args = this.arguments;
var keys = args.map(function (arg) {
return arg[1];
});
var values = args.map(function (arg) {
return arg[2];
});
if (this.selfClosing) {
blocks = null;
} else if (this.blocks.length > 0) {
var _keys = [];
var _values = [];
for (var i = 0; i < this.blocks.length; i++) {
var _blocks$i = this.blocks[i],
key = _blocks$i[0],
value = _blocks$i[1];
_keys.push(key.slice(1));
_values.push(value);
}
blocks = [_keys, _values];
} else {
blocks = [['default'], [{
statements: this.statements,
parameters: this.table.slots
}]];
}
return [this.tag, this.attributes, [keys, values], blocks];
};
return ComponentBlock;
}(Block);
var Template = function () {
function Template(symbols) {
_classCallCheck$1(this, Template);
this.block = new TemplateBlock(symbols);
}
Template.prototype.toJSON = function toJSON() {
return this.block.toJSON();
};
return Template;
}();
var JavaScriptCompiler = function () {
function JavaScriptCompiler(opcodes, symbols, locations, options) {
_classCallCheck$1(this, JavaScriptCompiler);
this.locations = locations;
this.blocks = new util.Stack();
this.values = [];
this.location = null;
this.locationStack = [];
this.opcodes = opcodes;
this.template = new Template(symbols);
this.options = options;
}
JavaScriptCompiler.process = function process(opcodes, locations, symbols, options) {
var compiler = new JavaScriptCompiler(opcodes, symbols, locations, options);
return compiler.process();
};
JavaScriptCompiler.prototype.process = function process() {
var _this5 = this;
this.opcodes.forEach(function (op, i) {
var opcode = op[0];
_this5.location = _this5.locations[i];
var arg = op[1];
if (!_this5[opcode]) {
throw new Error('unimplemented ' + opcode + ' on JavaScriptCompiler');
}
_this5[opcode](arg);
});
return this.template;
};
/// Nesting
JavaScriptCompiler.prototype.startBlock = function startBlock(program) {
this.startInlineBlock(program.symbols);
};
JavaScriptCompiler.prototype.endBlock = function endBlock() {
var block = this.endInlineBlock();
this.template.block.blocks.push(block);
};
JavaScriptCompiler.prototype.startProgram = function startProgram() {
this.blocks.push(this.template.block);
};
JavaScriptCompiler.prototype.endProgram = function endProgram() {};
/// Statements
JavaScriptCompiler.prototype.text = function text(content) {
this.push([1 /* Append */, 1, 0, 0, content]);
};
JavaScriptCompiler.prototype.append = function append(trusted) {
this.push([1 /* Append */, +trusted, 0, 0, this.popValue()]);
};
JavaScriptCompiler.prototype.comment = function comment(value) {
this.push([2 /* Comment */, value]);
};
JavaScriptCompiler.prototype.modifier = function modifier() {
var name = this.popValue();
var params = this.popValue();
var hash = this.popValue();
this.push([3 /* Modifier */, 0, 0, name, params, hash]);
};
JavaScriptCompiler.prototype.block = function block(_ref) {
var template = _ref[0],
inverse = _ref[1];
var head = this.popValue();
var params = this.popValue();
var hash = this.popValue();
var blocks = this.template.block.blocks;
var namedBlocks = void 0;
if (template === null && inverse === null) {
namedBlocks = null;
} else if (inverse === null) {
namedBlocks = [['default'], [blocks[template]]];
} else {
namedBlocks = [['default', 'else'], [blocks[template], blocks[inverse]]];
}
// assert(head[]);
this.push([5 /* Block */, head, params, hash, namedBlocks]);
};
JavaScriptCompiler.prototype.openComponent = function openComponent(element) {
var tag = this.options && this.options.customizeComponentName ? this.options.customizeComponentName(element.tag) : element.tag;
var component = new ComponentBlock(tag, element.symbols, element.selfClosing);
this.blocks.push(component);
};
JavaScriptCompiler.prototype.openNamedBlock = function openNamedBlock(element) {
var block = new NamedBlock(element.tag, element.symbols);
this.blocks.push(block);
};
JavaScriptCompiler.prototype.openElement = function openElement(_ref2) {
var element = _ref2[0],
simple = _ref2[1];
var tag = element.tag;
if (element.blockParams.length > 0) {
throw new Error('Compile Error: <' + element.tag + '> is not a component and doesn\'t support block parameters');
} else {
this.push([9 /* OpenElement */, tag, simple]);
}
};
JavaScriptCompiler.prototype.flushElement = function flushElement() {
this.push([10 /* FlushElement */]);
};
JavaScriptCompiler.prototype.closeComponent = function closeComponent(_element) {
var _endComponent = this.endComponent(),
tag = _endComponent[0],
attrs = _endComponent[1],
args = _endComponent[2],
blocks = _endComponent[3];
this.push([7 /* Component */, tag, attrs, args, blocks]);
};
JavaScriptCompiler.prototype.closeNamedBlock = function closeNamedBlock(_element) {
var blocks = this.blocks;
var block = blocks.pop();
this.currentComponent.pushBlock(block.name, block.toJSON());
};
JavaScriptCompiler.prototype.closeDynamicComponent = function closeDynamicComponent(_element) {
var _endComponent2 = this.endComponent(),
attrs = _endComponent2[1],
args = _endComponent2[2],
block = _endComponent2[3];
this.push([7 /* Component */, this.popValue(), attrs, args, block]);
};
JavaScriptCompiler.prototype.closeElement = function closeElement(_element) {
this.push([11 /* CloseElement */]);
};
JavaScriptCompiler.prototype.staticAttr = function staticAttr(_ref3) {
var name = _ref3[0],
namespace = _ref3[1];
var value = this.popValue();
this.push([12 /* StaticAttr */, name, value, namespace]);
};
JavaScriptCompiler.prototype.dynamicAttr = function dynamicAttr(_ref4) {
var name = _ref4[0],
namespace = _ref4[1];
var value = this.popValue();
this.push([13 /* DynamicAttr */, name, value, namespace]);
};
JavaScriptCompiler.prototype.componentAttr = function componentAttr(_ref5) {
var name = _ref5[0],
namespace = _ref5[1];
var value = this.popValue();
this.push([14 /* ComponentAttr */, name, value, namespace]);
};
JavaScriptCompiler.prototype.trustingAttr = function trustingAttr(_ref6) {
var name = _ref6[0],
namespace = _ref6[1];
var value = this.popValue();
this.push([20 /* TrustingDynamicAttr */, name, value, namespace]);
};
JavaScriptCompiler.prototype.trustingComponentAttr = function trustingComponentAttr(_ref7) {
var name = _ref7[0],
namespace = _ref7[1];
var value = this.popValue();
this.push([21 /* TrustingComponentAttr */, name, value, namespace]);
};
JavaScriptCompiler.prototype.staticArg = function staticArg(name) {
var value = this.popValue();
this.push([19 /* StaticArg */, name, value]);
};
JavaScriptCompiler.prototype.dynamicArg = function dynamicArg(name) {
var value = this.popValue();
this.push([18 /* DynamicArg */, name, value]);
};
JavaScriptCompiler.prototype.yield = function _yield(to) {
var params = this.popValue();
this.push([16 /* Yield */, to, params]);
};
JavaScriptCompiler.prototype.attrSplat = function attrSplat(to) {
// consume (and disregard) the value pushed for the
// ...attributes attribute
this.popValue();
this.push([15 /* AttrSplat */, to]);
};
JavaScriptCompiler.prototype.debugger = function _debugger(evalInfo) {
this.push([22 /* Debugger */, evalInfo]);
this.template.block.hasEval = true;
};
JavaScriptCompiler.prototype.hasBlock = function hasBlock(name) {
this.pushValue([28 /* HasBlock */, [24 /* GetSymbol */, name]]);
};
JavaScriptCompiler.prototype.hasBlockParams = function hasBlockParams(name) {
this.pushValue([29 /* HasBlockParams */
, [24 /* GetSymbol */, name]]);
};
JavaScriptCompiler.prototype.partial = function partial(evalInfo) {
var params = this.popValue();
this.push([17 /* Partial */, params[0], evalInfo]);
this.template.block.hasEval = true;
};
/// Expressions
JavaScriptCompiler.prototype.literal = function literal(value) {
if (value === undefined) {
this.pushValue([30 /* Undefined */]);
} else {
this.pushValue(value);
}
};
JavaScriptCompiler.prototype.getPath = function getPath(rest) {
var head = this.popValue();
this.pushValue([27 /* GetPath */, head, rest]);
};
JavaScriptCompiler.prototype.getSymbol = function getSymbol(head) {
this.pushValue([24 /* GetSymbol */, head]);
};
JavaScriptCompiler.prototype.getFree = function getFree(head) {
this.pushValue([25 /* GetFree */, head]);
};
JavaScriptCompiler.prototype.getFreeWithContext = function getFreeWithContext(_ref8) {
var head = _ref8[0],
context = _ref8[1];
this.pushValue([26 /* GetContextualFree */, head, context]);
};
JavaScriptCompiler.prototype.concat = function concat() {
this.pushValue([32 /* Concat */, this.popValue()]);
};
JavaScriptCompiler.prototype.helper = function helper() {
var _popLocatedValue = this.popLocatedValue(),
head = _popLocatedValue.value,
location = _popLocatedValue.location;
var params = this.popValue();
var hash = this.popValue();
this.pushValue([31 /* Call */
, start(location), end(location), head, params, hash]);
};
/// Stack Management Opcodes
JavaScriptCompiler.prototype.prepareArray = function prepareArray(size) {
var values = [];
for (var i = 0; i < size; i++) {
values.push(this.popValue());
}
this.pushValue(values);
};
JavaScriptCompiler.prototype.prepareObject = function prepareObject(size) {
var keys = new Array(size);
var values = new Array(size);
for (var i = 0; i < size; i++) {
keys[i] = this.popValue();
values[i] = this.popValue();
}
this.pushValue([keys, values]);
};
/// Utilities
JavaScriptCompiler.prototype.endComponent = function endComponent() {
var component = this.blocks.pop();
return component.toJSON();
};
JavaScriptCompiler.prototype.startInlineBlock = function startInlineBlock(symbols) {
var block = new InlineBlock(symbols);
this.blocks.push(block);
};
JavaScriptCompiler.prototype.endInlineBlock = function endInlineBlock() {
var blocks = this.blocks;
var block = blocks.pop();
return block.toJSON();
};
JavaScriptCompiler.prototype.push = function push(args) {
this.currentBlock.push(args);
};
JavaScriptCompiler.prototype.pushValue = function pushValue(val) {
this.values.push(val);
this.locationStack.push(this.location);
};
JavaScriptCompiler.prototype.popLocatedValue = function popLocatedValue() {
var value = this.values.pop();
var location = this.locationStack.pop();
if (location === undefined) {
throw new Error('Unbalanced location push and pop');
}
return { value: value, location: location };
};
JavaScriptCompiler.prototype.popValue = function popValue() {
return this.popLocatedValue().value;
};
_createClass$1(JavaScriptCompiler, [{
key: 'currentBlock',
get: function get() {
return this.blocks.current;
}
}, {
key: 'currentComponent',
get: function get() {
var block = this.currentBlock;
if (block instanceof ComponentBlock) {
return block;
} else {
throw new Error('Expected ComponentBlock on stack, found ' + block.constructor.name);
}
}
}]);
return JavaScriptCompiler;
}();
function start(location) {
if (location) {
return location.start;
} else {
return -1;
}
}
function end(location) {
if (location) {
return location.end - location.start;
} else {
return -1;
}
}
// There is a small whitelist of namespaced attributes specially
// enumerated in
// https://www.w3.org/TR/html/syntax.html#attributes-0
//
// > When a foreign element has one of the namespaced attributes given by
// > the local name and namespace of the first and second cells of a row
// > from the following table, it must be written using the name given by
// > the third cell from the same row.
//
// In all other cases, colons are interpreted as a regular character
// with no special meaning:
//
// > No other namespaced attribute can be expressed in the HTML syntax.
var XLINK = 'http://www.w3.org/1999/xlink';
var XML = 'http://www.w3.org/XML/1998/namespace';
var XMLNS = 'http://www.w3.org/2000/xmlns/';
var WHITELIST = {
'xlink:actuate': XLINK,
'xlink:arcrole': XLINK,
'xlink:href': XLINK,
'xlink:role': XLINK,
'xlink:show': XLINK,
'xlink:title': XLINK,
'xlink:type': XLINK,
'xml:base': XML,
'xml:lang': XML,
'xml:space': XML,
xmlns: XMLNS,
'xmlns:xlink': XMLNS
};
function getAttrNamespace(attrName) {
return WHITELIST[attrName] || null;
}
var _createClass$2 = 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 SymbolAllocator = function () {
function SymbolAllocator(ops, locations) {
_classCallCheck$2(this, SymbolAllocator);
this.ops = ops;
this.locations = locations;
this.symbolStack = new util.Stack();
}
SymbolAllocator.prototype.process = function process() {
var out = [];
var locations = [];
var ops = this.ops;
for (var i = 0; i < ops.length; i++) {
var op = ops[i];
var location = this.locations[i];
var result = this.dispatch(op);
out.push(result);
locations.push(location);
}
return { ops: out, locations: locations };
};
SymbolAllocator.prototype.dispatch = function dispatch(op) {
var name = op[0];
var operand = op[1];
return this[name](operand) || op;
};
SymbolAllocator.prototype.startProgram = function startProgram(op) {
this.symbolStack.push(op.symbols);
};
SymbolAllocator.prototype.endProgram = function endProgram() {
this.symbolStack.pop();
};
SymbolAllocator.prototype.startBlock = function startBlock(op) {
this.symbolStack.push(op.symbols);
};
SymbolAllocator.prototype.endBlock = function endBlock() {
this.symbolStack.pop();
};
SymbolAllocator.prototype.openNamedBlock = function openNamedBlock(op) {
this.symbolStack.push(op.symbols);
};
SymbolAllocator.prototype.closeNamedBlock = function closeNamedBlock(_op) {
this.symbolStack.pop();
};
SymbolAllocator.prototype.flushElement = function flushElement(op) {
this.symbolStack.push(op.symbols);
};
SymbolAllocator.prototype.closeElement = function closeElement(_op) {
this.symbolStack.pop();
};
SymbolAllocator.prototype.closeComponent = function closeComponent(_op) {
this.symbolStack.pop();
};
SymbolAllocator.prototype.closeDynamicComponent = function closeDynamicComponent(_op) {
this.symbolStack.pop();
};
SymbolAllocator.prototype.attrSplat = function attrSplat() {
return ['attrSplat', this.symbols.allocateBlock('attrs')];
};
SymbolAllocator.prototype.getFree = function getFree(name) {
var symbol = this.symbols.allocateFree(name);
return ['getFree', symbol];
};
SymbolAllocator.prototype.getArg = function getArg(name) {
var symbol = this.symbols.allocateNamed(name);
return ['getSymbol', symbol];
};
SymbolAllocator.prototype.getThis = function getThis() {
return ['getSymbol', 0];
};
SymbolAllocator.prototype.getVar = function getVar(_ref) {
var name = _ref[0],
context = _ref[1];
if (this.symbols.has(name)) {
var symbol = this.symbols.get(name);
return ['getSymbol', symbol];
} else {
var _symbol = this.symbols.allocateFree(name);
return ['getFreeWithContext', [_symbol, context]];
}
};
SymbolAllocator.prototype.getPath = function getPath(rest) {
return ['getPath', rest];
};
SymbolAllocator.prototype.yield = function _yield(op) {
return ['yield', this.symbols.allocateBlock(op)];
};
SymbolAllocator.prototype.debugger = function _debugger(_op) {
return ['debugger', this.symbols.getEvalInfo()];
};
SymbolAllocator.prototype.hasBlock = function hasBlock(op) {
if (op === 0) {
throw new Error('Cannot hasBlock this');
}
return ['hasBlock', this.symbols.allocateBlock(op)];
};
SymbolAllocator.prototype.hasBlockParams = function hasBlockParams(op) {
if (op === 0) {
throw new Error('Cannot hasBlockParams this');
}
return ['hasBlockParams', this.symbols.allocateBlock(op)];
};
SymbolAllocator.prototype.partial = function partial() {
return ['partial', this.symbols.getEvalInfo()];
};
SymbolAllocator.prototype.block = function block(_ref2) {
var template = _ref2[0],
inverse = _ref2[1];
return ['block', [template, inverse]];
};
SymbolAllocator.prototype.modifier = function modifier() {
return ['modifier'];
};
SymbolAllocator.prototype.helper = function helper() {
return ['helper'];
};
SymbolAllocator.prototype.text = function text(content) {
return ['text', content];
};
SymbolAllocator.prototype.comment = function comment(_comment) {
return ['comment', _comment];
};
SymbolAllocator.prototype.openComponent = function openComponent(element) {
return ['openComponent', element];
};
SymbolAllocator.prototype.openElement = function openElement(_ref3) {
var element = _ref3[0],
simple = _ref3[1];
return ['openElement', [element, simple]];
};
SymbolAllocator.prototype.staticArg = function staticArg(name) {
return ['staticArg', name];
};
SymbolAllocator.prototype.dynamicArg = function dynamicArg(name) {
return ['dynamicArg', name];
};
SymbolAllocator.prototype.staticAttr = function staticAttr(_ref4) {
var name = _ref4[0],
ns = _ref4[1];
return ['staticAttr', [name, ns]];
};
SymbolAllocator.prototype.trustingAttr = function trustingAttr(_ref5) {
var name = _ref5[0],
ns = _ref5[1];
return ['trustingAttr', [name, ns]];
};
SymbolAllocator.prototype.dynamicAttr = function dynamicAttr(_ref6) {
var name = _ref6[0],
ns = _ref6[1];
return ['dynamicAttr', [name, ns]];
};
SymbolAllocator.prototype.componentAttr = function componentAttr(_ref7) {
var name = _ref7[0],
ns = _ref7[1];
return ['componentAttr', [name, ns]];
};
SymbolAllocator.prototype.trustingComponentAttr = function trustingComponentAttr(_ref8) {
var name = _ref8[0],
ns = _ref8[1];
return ['trustedComponentAttr', [name, ns]];
};
SymbolAllocator.prototype.append = function append(trusted) {
return ['append', trusted];
};
SymbolAllocator.prototype.literal = function literal(value) {
return ['literal', value];
};
SymbolAllocator.prototype.prepareArray = function prepareArray(count) {
return ['prepareArray', count];
};
SymbolAllocator.prototype.prepareObject = function prepareObject(count) {
return ['prepareObject', count];
};
SymbolAllocator.prototype.concat = function concat() {
return ['concat'];
};
_createClass$2(SymbolAllocator, [{
key: 'symbols',
get: function get() {
return this.symbolStack.current;
}
}]);
return SymbolAllocator;
}();
function locationToOffset(source, line, column) {
var seenLines = 0;
var seenChars = 0;
while (true) {
if (seenChars === source.length) return null;
var nextLine = source.indexOf('\n', seenChars);
if (nextLine === -1) nextLine = source.length;
if (seenLines === line) {
if (seenChars + column > nextLine) return null;
return seenChars + column;
} else if (nextLine === -1) {
return null;
} else {
seenLines += 1;
seenChars = nextLine + 1;
}
}
}
function offsetToLocation(source, offset) {
var seenLines = 0;
var seenChars = 0;
if (offset > source.length) {
return null;
}
while (true) {
var nextLine = source.indexOf('\n', seenChars);
if (offset <= nextLine || nextLine === -1) {
return {
line: seenLines,
column: offset - seenChars
};
} else {
seenLines += 1;
seenChars = nextLine + 1;
}
}
}
function _classCallCheck$3(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function isTrustedValue(value) {
return value.escaped !== undefined && !value.escaped;
}
var TemplateCompiler = function () {
function TemplateCompiler(source) {
_classCallCheck$3(this, TemplateCompiler);
this.source = source;
this.templateId = 0;
this.templateIds = [];
this.opcodes = [];
this.locations = [];
this.includeMeta = true;
}
TemplateCompiler.compile = function compile(ast, source, options) {
var templateVisitor = new TemplateVisitor();
templateVisitor.visit(ast);
var compiler = new TemplateCompiler(source);
var _compiler$process = compiler.process(templateVisitor.actions),
opcodes = _compiler$process.opcodes,
templateLocations = _compiler$process.locations;
var _process = new SymbolAllocator(opcodes, templateLocations).process(),
ops = _process.ops,
allocationLocations = _process.locations;
var out = JavaScriptCompiler.process(ops, allocationLocations, ast.symbols, options);
return out;
};
TemplateCompiler.prototype.process = function process(actions) {
var _this = this;
actions.forEach(function (_ref) {
var name = _ref[0],
args = _ref[1];
if (!_this[name]) {
throw new Error('Unimplemented ' + name + ' on TemplateCompiler');
}
_this[name](args);
});
return { opcodes: this.opcodes, locations: this.locations };
};
TemplateCompiler.prototype.startProgram = function startProgram(_ref2) {
var program = _ref2[0];
this.opcode(['startProgram', program], program);
};
TemplateCompiler.prototype.endProgram = function endProgram() {
this.opcode(['endProgram'], null);
};
TemplateCompiler.prototype.startBlock = function startBlock(_ref3) {
var program = _ref3[0];
this.templateId++;
this.opcode(['startBlock', program], program);
};
TemplateCompiler.prototype.endBlock = function endBlock() {
this.templateIds.push(this.templateId - 1);
this.opcode(['endBlock'], null);
};
TemplateCompiler.prototype.text = function text(_ref4) {
var action = _ref4[0];
this.opcode(['tex