@glimmer/compiler
Version:
1,600 lines (1,320 loc) • 508 kB
JavaScript
define('@glimmer/compiler', ['exports', '@glimmer/syntax', '@glimmer/util'], function (exports, syntax, util) { 'use strict';
function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
var Template = /*#__PURE__*/function (_node$fields) {
_inheritsLoose(Template, _node$fields);
function Template() {
return _node$fields.apply(this, arguments) || this;
}
return Template;
}(syntax.node('Template').fields());
var InElement = /*#__PURE__*/function (_node$fields2) {
_inheritsLoose(InElement, _node$fields2);
function InElement() {
return _node$fields2.apply(this, arguments) || this;
}
return InElement;
}(syntax.node('InElement').fields());
var Not = /*#__PURE__*/function (_node$fields3) {
_inheritsLoose(Not, _node$fields3);
function Not() {
return _node$fields3.apply(this, arguments) || this;
}
return Not;
}(syntax.node('Not').fields());
var If = /*#__PURE__*/function (_node$fields4) {
_inheritsLoose(If, _node$fields4);
function If() {
return _node$fields4.apply(this, arguments) || this;
}
return If;
}(syntax.node('If').fields());
var IfInline = /*#__PURE__*/function (_node$fields5) {
_inheritsLoose(IfInline, _node$fields5);
function IfInline() {
return _node$fields5.apply(this, arguments) || this;
}
return IfInline;
}(syntax.node('IfInline').fields());
var Each = /*#__PURE__*/function (_node$fields6) {
_inheritsLoose(Each, _node$fields6);
function Each() {
return _node$fields6.apply(this, arguments) || this;
}
return Each;
}(syntax.node('Each').fields());
var With = /*#__PURE__*/function (_node$fields7) {
_inheritsLoose(With, _node$fields7);
function With() {
return _node$fields7.apply(this, arguments) || this;
}
return With;
}(syntax.node('With').fields());
var Let = /*#__PURE__*/function (_node$fields8) {
_inheritsLoose(Let, _node$fields8);
function Let() {
return _node$fields8.apply(this, arguments) || this;
}
return Let;
}(syntax.node('Let').fields());
var WithDynamicVars = /*#__PURE__*/function (_node$fields9) {
_inheritsLoose(WithDynamicVars, _node$fields9);
function WithDynamicVars() {
return _node$fields9.apply(this, arguments) || this;
}
return WithDynamicVars;
}(syntax.node('WithDynamicVars').fields());
var GetDynamicVar = /*#__PURE__*/function (_node$fields10) {
_inheritsLoose(GetDynamicVar, _node$fields10);
function GetDynamicVar() {
return _node$fields10.apply(this, arguments) || this;
}
return GetDynamicVar;
}(syntax.node('GetDynamicVar').fields());
var Log = /*#__PURE__*/function (_node$fields11) {
_inheritsLoose(Log, _node$fields11);
function Log() {
return _node$fields11.apply(this, arguments) || this;
}
return Log;
}(syntax.node('Log').fields());
var InvokeComponent = /*#__PURE__*/function (_node$fields12) {
_inheritsLoose(InvokeComponent, _node$fields12);
function InvokeComponent() {
return _node$fields12.apply(this, arguments) || this;
}
return InvokeComponent;
}(syntax.node('InvokeComponent').fields());
var NamedBlocks = /*#__PURE__*/function (_node$fields13) {
_inheritsLoose(NamedBlocks, _node$fields13);
function NamedBlocks() {
return _node$fields13.apply(this, arguments) || this;
}
return NamedBlocks;
}(syntax.node('NamedBlocks').fields());
var NamedBlock = /*#__PURE__*/function (_node$fields14) {
_inheritsLoose(NamedBlock, _node$fields14);
function NamedBlock() {
return _node$fields14.apply(this, arguments) || this;
}
return NamedBlock;
}(syntax.node('NamedBlock').fields());
var EndBlock = /*#__PURE__*/function (_node$fields15) {
_inheritsLoose(EndBlock, _node$fields15);
function EndBlock() {
return _node$fields15.apply(this, arguments) || this;
}
return EndBlock;
}(syntax.node('EndBlock').fields());
var AppendTrustedHTML = /*#__PURE__*/function (_node$fields16) {
_inheritsLoose(AppendTrustedHTML, _node$fields16);
function AppendTrustedHTML() {
return _node$fields16.apply(this, arguments) || this;
}
return AppendTrustedHTML;
}(syntax.node('AppendTrustedHTML').fields());
var AppendTextNode = /*#__PURE__*/function (_node$fields17) {
_inheritsLoose(AppendTextNode, _node$fields17);
function AppendTextNode() {
return _node$fields17.apply(this, arguments) || this;
}
return AppendTextNode;
}(syntax.node('AppendTextNode').fields());
var AppendComment = /*#__PURE__*/function (_node$fields18) {
_inheritsLoose(AppendComment, _node$fields18);
function AppendComment() {
return _node$fields18.apply(this, arguments) || this;
}
return AppendComment;
}(syntax.node('AppendComment').fields());
var Component = /*#__PURE__*/function (_node$fields19) {
_inheritsLoose(Component, _node$fields19);
function Component() {
return _node$fields19.apply(this, arguments) || this;
}
return Component;
}(syntax.node('Component').fields());
var StaticAttr = /*#__PURE__*/function (_node$fields20) {
_inheritsLoose(StaticAttr, _node$fields20);
function StaticAttr() {
return _node$fields20.apply(this, arguments) || this;
}
return StaticAttr;
}(syntax.node('StaticAttr').fields());
var DynamicAttr = /*#__PURE__*/function (_node$fields21) {
_inheritsLoose(DynamicAttr, _node$fields21);
function DynamicAttr() {
return _node$fields21.apply(this, arguments) || this;
}
return DynamicAttr;
}(syntax.node('DynamicAttr').fields());
var SimpleElement = /*#__PURE__*/function (_node$fields22) {
_inheritsLoose(SimpleElement, _node$fields22);
function SimpleElement() {
return _node$fields22.apply(this, arguments) || this;
}
return SimpleElement;
}(syntax.node('SimpleElement').fields());
var ElementParameters = /*#__PURE__*/function (_node$fields23) {
_inheritsLoose(ElementParameters, _node$fields23);
function ElementParameters() {
return _node$fields23.apply(this, arguments) || this;
}
return ElementParameters;
}(syntax.node('ElementParameters').fields());
var Yield = /*#__PURE__*/function (_node$fields24) {
_inheritsLoose(Yield, _node$fields24);
function Yield() {
return _node$fields24.apply(this, arguments) || this;
}
return Yield;
}(syntax.node('Yield').fields());
var Debugger = /*#__PURE__*/function (_node$fields25) {
_inheritsLoose(Debugger, _node$fields25);
function Debugger() {
return _node$fields25.apply(this, arguments) || this;
}
return Debugger;
}(syntax.node('Debugger').fields());
var CallExpression = /*#__PURE__*/function (_node$fields26) {
_inheritsLoose(CallExpression, _node$fields26);
function CallExpression() {
return _node$fields26.apply(this, arguments) || this;
}
return CallExpression;
}(syntax.node('CallExpression').fields());
var DeprecatedCallExpression = /*#__PURE__*/function (_node$fields27) {
_inheritsLoose(DeprecatedCallExpression, _node$fields27);
function DeprecatedCallExpression() {
return _node$fields27.apply(this, arguments) || this;
}
return DeprecatedCallExpression;
}(syntax.node('DeprecatedCallExpression').fields());
var Modifier = /*#__PURE__*/function (_node$fields28) {
_inheritsLoose(Modifier, _node$fields28);
function Modifier() {
return _node$fields28.apply(this, arguments) || this;
}
return Modifier;
}(syntax.node('Modifier').fields());
var InvokeBlock = /*#__PURE__*/function (_node$fields29) {
_inheritsLoose(InvokeBlock, _node$fields29);
function InvokeBlock() {
return _node$fields29.apply(this, arguments) || this;
}
return InvokeBlock;
}(syntax.node('InvokeBlock').fields());
var SplatAttr = /*#__PURE__*/function (_node$fields30) {
_inheritsLoose(SplatAttr, _node$fields30);
function SplatAttr() {
return _node$fields30.apply(this, arguments) || this;
}
return SplatAttr;
}(syntax.node('SplatAttr').fields());
var PathExpression = /*#__PURE__*/function (_node$fields31) {
_inheritsLoose(PathExpression, _node$fields31);
function PathExpression() {
return _node$fields31.apply(this, arguments) || this;
}
return PathExpression;
}(syntax.node('PathExpression').fields());
var GetWithResolver = /*#__PURE__*/function (_node$fields32) {
_inheritsLoose(GetWithResolver, _node$fields32);
function GetWithResolver() {
return _node$fields32.apply(this, arguments) || this;
}
return GetWithResolver;
}(syntax.node('GetWithResolver').fields());
var GetSymbol = /*#__PURE__*/function (_node$fields33) {
_inheritsLoose(GetSymbol, _node$fields33);
function GetSymbol() {
return _node$fields33.apply(this, arguments) || this;
}
return GetSymbol;
}(syntax.node('GetSymbol').fields());
var GetFreeWithContext = /*#__PURE__*/function (_node$fields34) {
_inheritsLoose(GetFreeWithContext, _node$fields34);
function GetFreeWithContext() {
return _node$fields34.apply(this, arguments) || this;
}
return GetFreeWithContext;
}(syntax.node('GetFreeWithContext').fields());
/** strict mode */
var GetFree = /*#__PURE__*/function (_node$fields35) {
_inheritsLoose(GetFree, _node$fields35);
function GetFree() {
return _node$fields35.apply(this, arguments) || this;
}
return GetFree;
}(syntax.node('GetFree').fields());
var Missing = /*#__PURE__*/function (_node$fields36) {
_inheritsLoose(Missing, _node$fields36);
function Missing() {
return _node$fields36.apply(this, arguments) || this;
}
return Missing;
}(syntax.node('Missing').fields());
var InterpolateExpression = /*#__PURE__*/function (_node$fields37) {
_inheritsLoose(InterpolateExpression, _node$fields37);
function InterpolateExpression() {
return _node$fields37.apply(this, arguments) || this;
}
return InterpolateExpression;
}(syntax.node('InterpolateExpression').fields());
var HasBlock = /*#__PURE__*/function (_node$fields38) {
_inheritsLoose(HasBlock, _node$fields38);
function HasBlock() {
return _node$fields38.apply(this, arguments) || this;
}
return HasBlock;
}(syntax.node('HasBlock').fields());
var HasBlockParams = /*#__PURE__*/function (_node$fields39) {
_inheritsLoose(HasBlockParams, _node$fields39);
function HasBlockParams() {
return _node$fields39.apply(this, arguments) || this;
}
return HasBlockParams;
}(syntax.node('HasBlockParams').fields());
var Curry = /*#__PURE__*/function (_node$fields40) {
_inheritsLoose(Curry, _node$fields40);
function Curry() {
return _node$fields40.apply(this, arguments) || this;
}
return Curry;
}(syntax.node('Curry').fields());
var Positional = /*#__PURE__*/function (_node$fields41) {
_inheritsLoose(Positional, _node$fields41);
function Positional() {
return _node$fields41.apply(this, arguments) || this;
}
return Positional;
}(syntax.node('Positional').fields());
var NamedArguments = /*#__PURE__*/function (_node$fields42) {
_inheritsLoose(NamedArguments, _node$fields42);
function NamedArguments() {
return _node$fields42.apply(this, arguments) || this;
}
return NamedArguments;
}(syntax.node('NamedArguments').fields());
var NamedArgument = /*#__PURE__*/function (_node$fields43) {
_inheritsLoose(NamedArgument, _node$fields43);
function NamedArgument() {
return _node$fields43.apply(this, arguments) || this;
}
return NamedArgument;
}(syntax.node('NamedArgument').fields());
var Args = /*#__PURE__*/function (_node$fields44) {
_inheritsLoose(Args, _node$fields44);
function Args() {
return _node$fields44.apply(this, arguments) || this;
}
return Args;
}(syntax.node('Args').fields());
var Tail = /*#__PURE__*/function (_node$fields45) {
_inheritsLoose(Tail, _node$fields45);
function Tail() {
return _node$fields45.apply(this, arguments) || this;
}
return Tail;
}(syntax.node('Tail').fields());
function _createForOfIteratorHelperLoose(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; return function () { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } it = o[Symbol.iterator](); return it.next.bind(it); }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
var PresentList = /*#__PURE__*/function () {
function PresentList(list) {
this.list = list;
}
var _proto = PresentList.prototype;
_proto.toArray = function toArray() {
return this.list;
};
_proto.map = function map(callback) {
var result = util.mapPresent(this.list, callback);
return new PresentList(result);
};
_proto.filter = function filter(predicate) {
var out = [];
for (var _iterator = _createForOfIteratorHelperLoose(this.list), _step; !(_step = _iterator()).done;) {
var item = _step.value;
if (predicate(item)) {
out.push(item);
}
}
return OptionalList(out);
};
_proto.toPresentArray = function toPresentArray() {
return this.list;
};
_proto.into = function into(_ref) {
var ifPresent = _ref.ifPresent;
return ifPresent(this);
};
return PresentList;
}();
var EmptyList = /*#__PURE__*/function () {
function EmptyList() {
this.list = [];
}
var _proto2 = EmptyList.prototype;
_proto2.map = function map(_callback) {
return new EmptyList();
};
_proto2.filter = function filter(_predicate) {
return new EmptyList();
};
_proto2.toArray = function toArray() {
return this.list;
};
_proto2.toPresentArray = function toPresentArray() {
return null;
};
_proto2.into = function into(_ref2) {
var ifEmpty = _ref2.ifEmpty;
return ifEmpty();
};
return EmptyList;
}(); // export type OptionalList<T> = PresentList<T> | EmptyList<T>;
function OptionalList(value) {
if (util.isPresent(value)) {
return new PresentList(value);
} else {
return new EmptyList();
}
}
function _inheritsLoose$1(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; }
var ResultImpl = /*#__PURE__*/function () {
function ResultImpl() {}
ResultImpl.all = function all() {
var out = [];
for (var _len = arguments.length, results = new Array(_len), _key = 0; _key < _len; _key++) {
results[_key] = arguments[_key];
}
for (var _i = 0, _results = results; _i < _results.length; _i++) {
var result = _results[_i];
if (result.isErr) {
return result.cast();
} else {
out.push(result.value);
}
}
return Ok(out);
};
return ResultImpl;
}();
var Result = ResultImpl;
var OkImpl = /*#__PURE__*/function (_ResultImpl) {
_inheritsLoose$1(OkImpl, _ResultImpl);
function OkImpl(value) {
var _this;
_this = _ResultImpl.call(this) || this;
_this.value = value;
_this.isOk = true;
_this.isErr = false;
return _this;
}
var _proto = OkImpl.prototype;
_proto.expect = function expect(_message) {
return this.value;
};
_proto.ifOk = function ifOk(callback) {
callback(this.value);
return this;
};
_proto.andThen = function andThen(callback) {
return callback(this.value);
};
_proto.mapOk = function mapOk(callback) {
return Ok(callback(this.value));
};
_proto.ifErr = function ifErr(_callback) {
return this;
};
_proto.mapErr = function mapErr(_callback) {
return this;
};
return OkImpl;
}(ResultImpl);
var ErrImpl = /*#__PURE__*/function (_ResultImpl2) {
_inheritsLoose$1(ErrImpl, _ResultImpl2);
function ErrImpl(reason) {
var _this2;
_this2 = _ResultImpl2.call(this) || this;
_this2.reason = reason;
_this2.isOk = false;
_this2.isErr = true;
return _this2;
}
var _proto2 = ErrImpl.prototype;
_proto2.expect = function expect(message) {
throw new Error(message || 'expected an Ok, got Err');
};
_proto2.andThen = function andThen(_callback) {
return this.cast();
};
_proto2.mapOk = function mapOk(_callback) {
return this.cast();
};
_proto2.ifOk = function ifOk(_callback) {
return this;
};
_proto2.mapErr = function mapErr(callback) {
return Err(callback(this.reason));
};
_proto2.ifErr = function ifErr(callback) {
callback(this.reason);
return this;
};
_proto2.cast = function cast() {
return this;
};
return ErrImpl;
}(ResultImpl);
function Ok(value) {
return new OkImpl(value);
}
function Err(reason) {
return new ErrImpl(reason);
}
var ResultArray = /*#__PURE__*/function () {
function ResultArray(items) {
if (items === void 0) {
items = [];
}
this.items = items;
}
var _proto4 = ResultArray.prototype;
_proto4.add = function add(item) {
this.items.push(item);
};
_proto4.toArray = function toArray() {
var err = this.items.filter(function (item) {
return item instanceof ErrImpl;
})[0];
if (err !== undefined) {
return err.cast();
} else {
return Ok(this.items.map(function (item) {
return item.value;
}));
}
};
_proto4.toOptionalList = function toOptionalList() {
return this.toArray().mapOk(function (arr) {
return OptionalList(arr);
});
};
return ResultArray;
}();
function _createForOfIteratorHelperLoose$1(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$1(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; return function () { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } it = o[Symbol.iterator](); return it.next.bind(it); }
function _unsupportedIterableToArray$1(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$1(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$1(o, minLen); }
function _arrayLikeToArray$1(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
var KeywordImpl = /*#__PURE__*/function () {
function KeywordImpl(keyword, type, delegate) {
this.keyword = keyword;
this.delegate = delegate;
var nodes = new Set();
for (var _iterator = _createForOfIteratorHelperLoose$1(KEYWORD_NODES[type]), _step; !(_step = _iterator()).done;) {
var nodeType = _step.value;
nodes.add(nodeType);
}
this.types = nodes;
}
var _proto = KeywordImpl.prototype;
_proto.match = function match(node) {
if (!this.types.has(node.type)) {
return false;
}
var path = getCalleeExpression(node);
if (path !== null && path.type === 'Path' && path.ref.type === 'Free') {
if (path.tail.length > 0) {
if (path.ref.resolution.serialize() === 'Loose') {
// cannot be a keyword reference, keywords do not allow paths (must be
// relying on implicit this fallback)
return false;
}
}
return path.ref.name === this.keyword;
} else {
return false;
}
};
_proto.translate = function translate(node, state) {
var _this = this;
if (this.match(node)) {
var path = getCalleeExpression(node);
if (path !== null && path.type === 'Path' && path.tail.length > 0) {
return Err(syntax.generateSyntaxError("The `" + this.keyword + "` keyword was used incorrectly. It was used as `" + path.loc.asString() + "`, but it cannot be used with additional path segments. \n\nError caused by", node.loc));
}
var param = this.delegate.assert(node, state);
return param.andThen(function (param) {
return _this.delegate.translate({
node: node,
state: state
}, param);
});
} else {
return null;
}
};
return KeywordImpl;
}();
var KEYWORD_NODES = {
Call: ['Call'],
Block: ['InvokeBlock'],
Append: ['AppendContent'],
Modifier: ['ElementModifier']
};
function keyword(keyword, type, delegate) {
return new KeywordImpl(keyword, type, delegate);
}
function getCalleeExpression(node) {
switch (node.type) {
// This covers the inside of attributes and expressions, as well as the callee
// of call nodes
case 'Path':
return node;
case 'AppendContent':
return getCalleeExpression(node.value);
case 'Call':
case 'InvokeBlock':
case 'ElementModifier':
return node.callee;
default:
return null;
}
}
var Keywords = /*#__PURE__*/function () {
function Keywords(type) {
this._keywords = [];
this._type = type;
}
var _proto2 = Keywords.prototype;
_proto2.kw = function kw(name, delegate) {
this._keywords.push(keyword(name, this._type, delegate));
return this;
};
_proto2.translate = function translate(node, state) {
for (var _iterator2 = _createForOfIteratorHelperLoose$1(this._keywords), _step2; !(_step2 = _iterator2()).done;) {
var _keyword = _step2.value;
var result = _keyword.translate(node, state);
if (result !== null) {
return result;
}
}
var path = getCalleeExpression(node);
if (path && path.type === 'Path' && path.ref.type === 'Free' && syntax.isKeyword(path.ref.name)) {
var name = path.ref.name;
var usedType = this._type;
var validTypes = syntax.KEYWORDS_TYPES[name];
if (validTypes.indexOf(usedType) === -1) {
return Err(syntax.generateSyntaxError("The `" + name + "` keyword was used incorrectly. It was used as " + typesToReadableName[usedType] + ", but its valid usages are:\n\n" + generateTypesMessage(name, validTypes) + "\n\nError caused by", node.loc));
}
}
return null;
};
return Keywords;
}();
var typesToReadableName = {
Append: 'an append statement',
Block: 'a block statement',
Call: 'a call expression',
Modifier: 'a modifier'
};
function generateTypesMessage(name, types) {
return types.map(function (type) {
switch (type) {
case 'Append':
return "- As an append statement, as in: {{" + name + "}}";
case 'Block':
return "- As a block statement, as in: {{#" + name + "}}{{/" + name + "}}";
case 'Call':
return "- As an expression, as in: (" + name + ")";
case 'Modifier':
return "- As a modifier, as in: <div {{" + name + "}}></div>";
default:
return util.exhausted(type);
}
}).join('\n\n');
}
/**
* This function builds keyword definitions for a particular type of AST node (`KeywordType`).
*
* You can build keyword definitions for:
*
* - `Expr`: A `SubExpression` or `PathExpression`
* - `Block`: A `BlockStatement`
* - A `BlockStatement` is a keyword candidate if its head is a
* `PathExpression`
* - `Append`: An `AppendStatement`
*
* A node is a keyword candidate if:
*
* - A `PathExpression` is a keyword candidate if it has no tail, and its
* head expression is a `LocalVarHead` or `FreeVarHead` whose name is
* the keyword's name.
* - A `SubExpression`, `AppendStatement`, or `BlockStatement` is a keyword
* candidate if its head is a keyword candidate.
*
* The keyword infrastructure guarantees that:
*
* - If a node is not a keyword candidate, it is never passed to any keyword's
* `assert` method.
* - If a node is not the `KeywordType` for a particular keyword, it will not
* be passed to the keyword's `assert` method.
*
* `Expr` keywords are used in expression positions and should return HIR
* expressions. `Block` and `Append` keywords are used in statement
* positions and should return HIR statements.
*
* A keyword definition has two parts:
*
* - `match`, which determines whether an AST node matches the keyword, and can
* optionally return some information extracted from the AST node.
* - `translate`, which takes a matching AST node as well as the extracted
* information and returns an appropriate HIR instruction.
*
* # Example
*
* This keyword:
*
* - turns `(hello)` into `"hello"`
* - as long as `hello` is not in scope
* - makes it an error to pass any arguments (such as `(hello world)`)
*
* ```ts
* keywords('SubExpr').kw('hello', {
* assert(node: ExprKeywordNode): Result<void> | false {
* // we don't want to transform `hello` as a `PathExpression`
* if (node.type !== 'SubExpression') {
* return false;
* }
*
* // node.head would be `LocalVarHead` if `hello` was in scope
* if (node.head.type !== 'FreeVarHead') {
* return false;
* }
*
* if (node.params.length || node.hash) {
* return Err(generateSyntaxError(`(hello) does not take any arguments`), node.loc);
* } else {
* return Ok();
* }
* },
*
* translate(node: ASTv2.SubExpression): hir.Expression {
* return ASTv2.builders.literal("hello", node.loc)
* }
* })
* ```
*
* The keyword infrastructure checks to make sure that the node is the right
* type before calling `assert`, so you only need to consider `SubExpression`
* and `PathExpression` here. It also checks to make sure that the node passed
* to `assert` has the keyword name in the right place.
*
* Note the important difference between returning `false` from `assert`,
* which just means that the node didn't match, and returning `Err`, which
* means that the node matched, but there was a keyword-specific syntax
* error.
*/
function keywords(type) {
return new Keywords(type);
}
function hasPath(node) {
return node.callee.type === 'Path';
}
function isHelperInvocation(node) {
if (!hasPath(node)) {
return false;
}
return !node.args.isEmpty();
}
function isSimplePath(path) {
if (path.type === 'Path') {
var head = path.ref,
parts = path.tail;
return head.type === 'Free' && head.resolution !== syntax.ASTv2.STRICT_RESOLUTION && parts.length === 0;
} else {
return false;
}
}
function isStrictHelper(expr) {
if (expr.callee.type !== 'Path') {
return true;
}
if (expr.callee.ref.type !== 'Free') {
return true;
}
return expr.callee.ref.resolution === syntax.ASTv2.STRICT_RESOLUTION;
}
function assertIsValidModifier(helper) {
if (isStrictHelper(helper) || isSimplePath(helper.callee)) {
return;
}
throw syntax.generateSyntaxError("`" + printPath(helper.callee) + "` is not a valid name for a modifier", helper.loc);
}
function printPath(path) {
switch (path.type) {
case 'Literal':
return JSON.stringify(path.value);
case 'Path':
{
var printedPath = [printPathHead(path.ref)];
printedPath.push.apply(printedPath, path.tail.map(function (t) {
return t.chars;
}));
return printedPath.join('.');
}
case 'Call':
return "(" + printPath(path.callee) + " ...)";
case 'DeprecatedCall':
return "" + path.callee.name;
case 'Interpolate':
throw util.unreachable('a concat statement cannot appear as the head of an expression');
}
}
function printPathHead(head) {
switch (head.type) {
case 'Arg':
return head.name.chars;
case 'Free':
case 'Local':
return head.name;
case 'This':
return 'this';
}
}
var NormalizeExpressions = /*#__PURE__*/function () {
function NormalizeExpressions() {}
var _proto = NormalizeExpressions.prototype;
_proto.visit = function visit(node, state) {
switch (node.type) {
case 'Literal':
return Ok(this.Literal(node));
case 'Interpolate':
return this.Interpolate(node, state);
case 'Path':
return this.PathExpression(node);
case 'Call':
var translated = CALL_KEYWORDS.translate(node, state);
if (translated !== null) {
return translated;
}
return this.CallExpression(node, state);
case 'DeprecatedCall':
return this.DeprecaedCallExpression(node, state);
}
};
_proto.visitList = function visitList(nodes, state) {
return new ResultArray(nodes.map(function (e) {
return VISIT_EXPRS.visit(e, state);
})).toOptionalList();
}
/**
* Normalize paths into `hir.Path` or a `hir.Expr` that corresponds to the ref.
*
* TODO since keywords don't support tails anyway, distinguish PathExpression from
* VariableReference in ASTv2.
*/
;
_proto.PathExpression = function PathExpression$1(path) {
var ref = this.VariableReference(path.ref);
var tail = path.tail;
if (util.isPresent(tail)) {
var tailLoc = tail[0].loc.extend(tail[tail.length - 1].loc);
return Ok(new PathExpression({
loc: path.loc,
head: ref,
tail: new Tail({
loc: tailLoc,
members: tail
})
}));
} else {
return Ok(ref);
}
};
_proto.VariableReference = function VariableReference(ref) {
return ref;
};
_proto.Literal = function Literal(literal) {
return literal;
};
_proto.Interpolate = function Interpolate(expr, state) {
var parts = expr.parts.map(convertPathToCallIfKeyword);
return VISIT_EXPRS.visitList(parts, state).mapOk(function (parts) {
return new InterpolateExpression({
loc: expr.loc,
parts: parts
});
});
};
_proto.CallExpression = function CallExpression$1(expr, state) {
if (!hasPath(expr)) {
throw new Error("unimplemented subexpression at the head of a subexpression");
} else {
return Result.all(VISIT_EXPRS.visit(expr.callee, state), VISIT_EXPRS.Args(expr.args, state)).mapOk(function (_ref) {
var callee = _ref[0],
args = _ref[1];
return new CallExpression({
loc: expr.loc,
callee: callee,
args: args
});
});
}
};
_proto.DeprecaedCallExpression = function DeprecaedCallExpression(_ref2, _state) {
var arg = _ref2.arg,
callee = _ref2.callee,
loc = _ref2.loc;
return Ok(new DeprecatedCallExpression({
loc: loc,
arg: arg,
callee: callee
}));
};
_proto.Args = function Args$1(_ref3, state) {
var positional = _ref3.positional,
named = _ref3.named,
loc = _ref3.loc;
return Result.all(this.Positional(positional, state), this.NamedArguments(named, state)).mapOk(function (_ref4) {
var positional = _ref4[0],
named = _ref4[1];
return new Args({
loc: loc,
positional: positional,
named: named
});
});
};
_proto.Positional = function Positional$1(positional, state) {
return VISIT_EXPRS.visitList(positional.exprs, state).mapOk(function (list) {
return new Positional({
loc: positional.loc,
list: list
});
});
};
_proto.NamedArguments = function NamedArguments$1(named, state) {
var pairs = named.entries.map(function (arg) {
var value = convertPathToCallIfKeyword(arg.value);
return VISIT_EXPRS.visit(value, state).mapOk(function (value) {
return new NamedArgument({
loc: arg.loc,
key: arg.name,
value: value
});
});
});
return new ResultArray(pairs).toOptionalList().mapOk(function (pairs) {
return new NamedArguments({
loc: named.loc,
entries: pairs
});
});
};
return NormalizeExpressions;
}();
function convertPathToCallIfKeyword(path) {
if (path.type === 'Path' && path.ref.type === 'Free' && path.ref.name in syntax.KEYWORDS_TYPES) {
return new syntax.ASTv2.CallExpression({
callee: path,
args: syntax.ASTv2.Args.empty(path.loc),
loc: path.loc
});
}
return path;
}
var VISIT_EXPRS = new NormalizeExpressions();
var _CurriedTypeToReadabl;
var CurriedTypeToReadableType = (_CurriedTypeToReadabl = {}, _CurriedTypeToReadabl[0
/* Component */
] = 'component', _CurriedTypeToReadabl[1
/* Helper */
] = 'helper', _CurriedTypeToReadabl[2
/* Modifier */
] = 'modifier', _CurriedTypeToReadabl);
function assertCurryKeyword(curriedType) {
return function (node, state) {
var readableType = CurriedTypeToReadableType[curriedType];
var stringsAllowed = curriedType === 0
/* Component */
;
var args = node.args;
var definition = args.nth(0);
if (definition === null) {
return Err(syntax.generateSyntaxError("(" + readableType + ") requires a " + readableType + " definition or identifier as its first positional parameter, did not receive any parameters.", args.loc));
}
if (definition.type === 'Literal') {
if (stringsAllowed && state.isStrict) {
return Err(syntax.generateSyntaxError("(" + readableType + ") cannot resolve string values in strict mode templates", node.loc));
} else if (!stringsAllowed) {
return Err(syntax.generateSyntaxError("(" + readableType + ") cannot resolve string values, you must pass a " + readableType + " definition directly", node.loc));
}
}
args = new syntax.ASTv2.Args({
positional: new syntax.ASTv2.PositionalArguments({
exprs: args.positional.exprs.slice(1),
loc: args.positional.loc
}),
named: args.named,
loc: args.loc
});
return Ok({
definition: definition,
args: args
});
};
}
function translateCurryKeyword(curriedType) {
return function (_ref, _ref2) {
var node = _ref.node,
state = _ref.state;
var definition = _ref2.definition,
args = _ref2.args;
var definitionResult = VISIT_EXPRS.visit(definition, state);
var argsResult = VISIT_EXPRS.Args(args, state);
return Result.all(definitionResult, argsResult).mapOk(function (_ref3) {
var definition = _ref3[0],
args = _ref3[1];
return new Curry({
loc: node.loc,
curriedType: curriedType,
definition: definition,
args: args
});
});
};
}
function curryKeyword(curriedType) {
return {
assert: assertCurryKeyword(curriedType),
translate: translateCurryKeyword(curriedType)
};
}
function assertGetDynamicVarKeyword(node) {
var call = node.type === 'AppendContent' ? node.value : node;
var named = call.type === 'Call' ? call.args.named : null;
var positionals = call.type === 'Call' ? call.args.positional : null;
if (named && !named.isEmpty()) {
return Err(syntax.generateSyntaxError("(-get-dynamic-vars) does not take any named arguments", node.loc));
}
var varName = positionals === null || positionals === void 0 ? void 0 : positionals.nth(0);
if (!varName) {
return Err(syntax.generateSyntaxError("(-get-dynamic-vars) requires a var name to get", node.loc));
}
if (positionals && positionals.size > 1) {
return Err(syntax.generateSyntaxError("(-get-dynamic-vars) only receives one positional arg", node.loc));
}
return Ok(varName);
}
function translateGetDynamicVarKeyword(_ref, name) {
var node = _ref.node,
state = _ref.state;
return VISIT_EXPRS.visit(name, state).mapOk(function (name) {
return new GetDynamicVar({
name: name,
loc: node.loc
});
});
}
var getDynamicVarKeyword = {
assert: assertGetDynamicVarKeyword,
translate: translateGetDynamicVarKeyword
};
function assertHasBlockKeyword(type) {
return function (node) {
var call = node.type === 'AppendContent' ? node.value : node;
var named = call.type === 'Call' ? call.args.named : null;
var positionals = call.type === 'Call' ? call.args.positional : null;
if (named && !named.isEmpty()) {
return Err(syntax.generateSyntaxError("(" + type + ") does not take any named arguments", call.loc));
}
if (!positionals || positionals.isEmpty()) {
return Ok(syntax.SourceSlice.synthetic('default'));
} else if (positionals.exprs.length === 1) {
var positional = positionals.exprs[0];
if (syntax.ASTv2.isLiteral(positional, 'string')) {
return Ok(positional.toSlice());
} else {
return Err(syntax.generateSyntaxError("(" + type + ") can only receive a string literal as its first argument", call.loc));
}
} else {
return Err(syntax.generateSyntaxError("(" + type + ") only takes a single positional argument", call.loc));
}
};
}
function translateHasBlockKeyword(type) {
return function (_ref, target) {
var node = _ref.node,
scope = _ref.state.scope;
var block = type === 'has-block' ? new HasBlock({
loc: node.loc,
target: target,
symbol: scope.allocateBlock(target.chars)
}) : new HasBlockParams({
loc: node.loc,
target: target,
symbol: scope.allocateBlock(target.chars)
});
return Ok(block);
};
}
function hasBlockKeyword(type) {
return {
assert: assertHasBlockKeyword(type),
translate: translateHasBlockKeyword(type)
};
}
function assertIfUnlessInlineKeyword(type) {
return function (originalNode) {
var _a;
var inverted = type === 'unless';
var node = originalNode.type === 'AppendContent' ? originalNode.value : originalNode;
var named = node.type === 'Call' ? node.args.named : null;
var positional = node.type === 'Call' ? node.args.positional : null;
if (named && !named.isEmpty()) {
return Err(syntax.generateSyntaxError("(" + type + ") cannot receive named parameters, received " + named.entries.map(function (e) {
return e.name.chars;
}).join(', '), originalNode.loc));
}
var condition = positional === null || positional === void 0 ? void 0 : positional.nth(0);
if (!positional || !condition) {
return Err(syntax.generateSyntaxError("When used inline, (" + type + ") requires at least two parameters 1. the condition that determines the state of the (" + type + "), and 2. the value to return if the condition is " + (inverted ? 'false' : 'true') + ". Did not receive any parameters", originalNode.loc));
}
var truthy = positional.nth(1);
var falsy = positional.nth(2);
if (truthy === null) {
return Err(syntax.generateSyntaxError("When used inline, (" + type + ") requires at least two parameters 1. the condition that determines the state of the (" + type + "), and 2. the value to return if the condition is " + (inverted ? 'false' : 'true') + ". Received only one parameter, the condition", originalNode.loc));
}
if (positional.size > 3) {
return Err(syntax.generateSyntaxError("When used inline, (" + type + ") can receive a maximum of three positional parameters 1. the condition that determines the state of the (" + type + "), 2. the value to return if the condition is " + (inverted ? 'false' : 'true') + ", and 3. the value to return if the condition is " + (inverted ? 'true' : 'false') + ". Received " + ((_a = positional === null || positional === void 0 ? void 0 : positional.size) !== null && _a !== void 0 ? _a : 0) + " parameters", originalNode.loc));
}
return Ok({
condition: condition,
truthy: truthy,
falsy: falsy
});
};
}
function translateIfUnlessInlineKeyword(type) {
var inverted = type === 'unless';
return function (_ref, _ref2) {
var node = _ref.node,
state = _ref.state;
var condition = _ref2.condition,
truthy = _ref2.truthy,
falsy = _ref2.falsy;
var conditionResult = VISIT_EXPRS.visit(condition, state);
var truthyResult = VISIT_EXPRS.visit(truthy, state);
var falsyResult = falsy ? VISIT_EXPRS.visit(falsy, state) : Ok(null);
return Result.all(conditionResult, truthyResult, falsyResult).mapOk(function (_ref3) {
var condition = _ref3[0],
truthy = _ref3[1],
falsy = _ref3[2];
if (inverted) {
condition = new Not({
value: condition,
loc: node.loc
});
}
return new IfInline({
loc: node.loc,
condition: condition,
truthy: truthy,
falsy: falsy
});
});
};
}
function ifUnlessInlineKeyword(type) {
return {
assert: assertIfUnlessInlineKeyword(type),
translate: translateIfUnlessInlineKeyword(type)
};
}
function assertLogKeyword(node) {
var _node$args = node.args,
named = _node$args.named,
positional = _node$args.positional;
if (named && !named.isEmpty()) {
return Err(syntax.generateSyntaxError("(log) does not take any named arguments", node.loc));
}
return Ok(positional);
}
function translateLogKeyword(_ref, positional) {
var node = _ref.node,
state = _ref.state;
return VISIT_EXPRS.Positional(positional, state).mapOk(function (positional) {
return new Log({
positional: positional,
loc: node.loc
});
});
}
var logKeyword = {
assert: assertLogKeyword,
translate: translateLogKeyword
};
var CALL_KEYWORDS = keywords('Call').kw('has-block', hasBlockKeyword('has-block')).kw('has-block-params', hasBlockKeyword('has-block-params')).kw('-get-dynamic-var', getDynamicVarKeyword).kw('log', logKeyword).kw('if', ifUnlessInlineKeyword('if')).kw('unless', ifUnlessInlineKeyword('unless')).kw('component', curryKeyword(0
/* Component */
)).kw('helper', curryKeyword(1
/* Helper */
)).kw('modifier', curryKeyword(2
/* Modifier */
));
function toAppend(_ref) {
var assert = _ref.assert,
_translate = _ref.translate;
return {
assert: assert,
translate: function translate(_ref2, value) {
var node = _ref2.node,
state = _ref2.state;
var result = _translate({
node: node,
state: state
}, value);
return result.mapOk(function (text) {
return new AppendTextNode({
text: text,
loc: node.loc
});
});
}
};
}
var APPEND_KEYWORDS = keywords('Append').kw('has-block', toAppend(hasBlockKeyword('has-block'))).kw('has-block-params', toAppend(hasBlockKeyword('has-block-params'))).kw('-get-dynamic-var', toAppend(getDynamicVarKeyword)).kw('log', toAppend(logKeyword)).kw('if', toAppend(ifUnlessInlineKeyword('if'))).kw('unless', toAppend(ifUnlessInlineKeyword('unless'))).kw('yield', {
assert: function assert(node) {
var args = node.args;
if (args.named.isEmpty()) {
return Ok({
target: syntax.SourceSpan.synthetic('default').toSlice(),
positional: args.positional
});
} else {
var target = args.named.get('to');
if (args.named.size > 1 || target === null) {
return Err(syntax.generateSyntaxError("yield only takes a single named argument: 'to'", args.named.loc));
}
if (syntax.ASTv2.isLiteral(target, 'string')) {
return Ok({
target: target.toSlice(),
positional: args.positional
});
} else {
return Err(syntax.generateSyntaxError("you can only yield to a literal string value", target.loc));
}
}
},
translate: function translate(_ref, _ref2) {
var node = _ref.node,
state = _ref.state;
var target = _ref2.target,
positional = _ref2.positional;
return VISIT_EXPRS.Positional(positional, state).mapOk(function (positional) {
return new Yield({
loc: node.loc,
target: target,
to: state.scope.allocateBlock(target.chars),
positional: positional
});
});
}
}).kw('debugger', {
assert: function assert(node) {
var args = node.args;
var positional = args.positional;
if (args.isEmpty()) {
return Ok(undefined);
} else {
if (positional.isEmpty()) {
return Err(syntax.generateSyntaxError("debugger does not take any named arguments", node.loc));
} else {
return Err(syntax.generateSyntaxError("debugger does not take any positional arguments", node.loc));
}
}
},
translate: function translate(_ref3) {
var node = _ref3.node,
scope = _ref3.state.scope;
scope.setHasEval();
return Ok(new Debugger({
loc: node.loc,
scope: scope
}));
}
}).kw('component', {
assert: assertCurryKeyword(0
/* Component */
),
translate: function translate(_ref4, _ref5) {
var node = _ref4.node,
state = _ref4.state;
var definition = _ref5.definition,
args = _ref5.args;
var definitionResult = VISIT_EXPRS.visit(definition, state);
var argsResult = VISIT_EXPRS.Args(args, state);
return Result.all(definitionResult, argsResult).mapOk(function (_ref6) {
var definition = _ref6[0],
args = _ref6[1];
return new InvokeComponent({
loc: node.loc,
definition: definition,
args: args,
blocks: null
});
});
}
}).kw('helper', {
assert: assertCurryKeyword(1
/* Helper */
),
translate: function translate(_ref7, _ref8) {
var node = _ref7.node,
state = _ref7.state;
var definition = _ref8.definition,
args = _ref8.args;
var definitionResult = VISIT_EXPRS.visit(definition, state);
var argsResult = VISIT_EXPRS.Args(args, state);
return Result.all(definitionResult, argsResult).mapOk(function (_ref9) {
var definition = _ref9[0],
args = _ref9[1];
var text = new CallExpression({
callee: definition,
args: args,
loc: node.loc
});
return new AppendTextNode({
loc: node.loc,
text: text
});
});
}
});
var BLOCK_KEYWORDS = keywords('Block').kw('in-element', {
assert: function assert(node) {
var args = node.args;
var guid = args.get('guid');
if (guid) {
return Err(syntax.generateSyntaxError("Cannot pass `guid` to `{{#in-element}}`", guid.loc));
}
var insertBefore = args.get('insertBefore');
var destination = args.nth(0);
if (destination === null) {
return Err(syntax.generateSyntaxError("{{#in-element}} requires a target element as its first positional parameter", args.loc));
} // TODO Better syntax checks
return Ok({
insertBefore: insertBefore,
destination: destination
});
},
translate: function translate(_ref, _ref2) {
var node = _ref.node,
state = _ref.state;
var insertBefore = _ref2.insertBefore,
destination = _ref2.destination;
var named = node.blocks.get('default');
var body = VISIT_STMTS.NamedBlock(named, state);
var destinationResult = VISIT_EXPRS.visit(destination, state);
return Result.all(body,