UNPKG

brazejs

Version:

Liquid template engine for the Braze variant by pure JavaScript: compatible to Braze, easy to extend.

1,366 lines (1,327 loc) 114 kB
/* * brazejs@1.7.1, https://github.com/yq314/brazejs * (c) 2019-2020 QingYE * Released under the MIT License. */ 'use strict'; var path = require('path'); var fs = require('fs'); var rp_ = require('request-promise-cache'); var crypto = require('crypto'); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __awaiter(thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } var toStr = Object.prototype.toString; var specials = [ '-', '[', ']', '/', '{', '}', '(', ')', '*', '+', '?', '.', '\\', '^', '$', '|' ]; var regex = RegExp('[' + specials.join('\\') + ']', 'g'); function escapeRegExp(str) { return str.replace(regex, '\\$&'); } /* * Checks if value is classified as a String primitive or object. * @param {any} value The value to check. * @return {Boolean} Returns true if value is a string, else false. */ function isString(value) { return toStr.call(value) === '[object String]'; } function isFunction(value) { return typeof value === 'function'; } function promisify(fn) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return new Promise(function (resolve, reject) { fn.apply(void 0, args.concat([function (err, result) { err ? reject(err) : resolve(result); }])); }); }; } function stringify(value) { if (isNil(value)) return ''; value = toLiquid(value); if (isArray(value)) { var str = value.map(function (v) { return toValueStr(v); }).join(', '); return "[" + str + "]"; } if (isObject(value)) { var str = Object.keys(value).map(function (k) { return "\"" + k + "\"=>" + toValueStr(value[k]); }).join(', '); return "{" + str + "}"; } return String(value); } function toValueStr(value) { if (isString(value)) return "\"" + value + "\""; return stringify(value); } function toLiquid(value) { if (isFunction(value.toLiquid)) return toLiquid(value.toLiquid()); return value; } function isNil(value) { return value === null || value === undefined; } function isArray(value) { // be compatible with IE 8 return toStr.call(value) === '[object Array]'; } /* * Iterates over own enumerable string keyed properties of an object and invokes iteratee for each property. * The iteratee is invoked with three arguments: (value, key, object). * Iteratee functions may exit iteration early by explicitly returning false. * @param {Object} object The object to iterate over. * @param {Function} iteratee The function invoked per iteration. * @return {Object} Returns object. */ function forOwn(object, iteratee) { object = object || {}; for (var k in object) { if (object.hasOwnProperty(k)) { if (iteratee(object[k], k, object) === false) break; } } return object; } function last(arr) { return arr[arr.length - 1]; } /* * Checks if value is the language type of Object. * (e.g. arrays, functions, objects, regexes, new Number(0), and new String('')) * @param {any} value The value to check. * @return {Boolean} Returns true if value is an object, else false. */ function isObject(value) { var type = typeof value; return value !== null && (type === 'object' || type === 'function'); } function range(start, stop, step) { if (step === void 0) { step = 1; } var arr = []; for (var i = start; i < stop; i += step) { arr.push(i); } return arr; } function padStart(str, length, ch) { if (ch === void 0) { ch = ' '; } str = String(str); var n = length - str.length; while (n-- > 0) str = ch + str; return str; } var Drop = /** @class */ (function () { function Drop() { } Drop.prototype.valueOf = function () { return undefined; }; Drop.prototype.liquidMethodMissing = function (key) { return undefined; }; return Drop; }()); var LiquidError = /** @class */ (function (_super) { __extends(LiquidError, _super); function LiquidError(err, token) { var _this = _super.call(this, err.message) || this; _this.originalError = err; _this.token = token; return _this; } LiquidError.prototype.update = function () { var err = this.originalError; var context = mkContext(this.token); this.message = mkMessage(err.message, this.token); this.stack = this.message + '\n' + context + '\n' + this.stack + '\nFrom ' + err.stack; }; return LiquidError; }(Error)); var TokenizationError = /** @class */ (function (_super) { __extends(TokenizationError, _super); function TokenizationError(message, token) { var _this = _super.call(this, new Error(message), token) || this; _this.name = 'TokenizationError'; _super.prototype.update.call(_this); return _this; } return TokenizationError; }(LiquidError)); var ParseError = /** @class */ (function (_super) { __extends(ParseError, _super); function ParseError(err, token) { var _this = _super.call(this, err, token) || this; _this.name = 'ParseError'; _this.message = err.message; _super.prototype.update.call(_this); return _this; } return ParseError; }(LiquidError)); var RenderError = /** @class */ (function (_super) { __extends(RenderError, _super); function RenderError(err, tpl) { var _this = _super.call(this, err, tpl.token) || this; _this.name = 'RenderError'; _this.message = err.message; _super.prototype.update.call(_this); return _this; } return RenderError; }(LiquidError)); var RenderBreakError = /** @class */ (function (_super) { __extends(RenderBreakError, _super); function RenderBreakError(message) { var _this = _super.call(this, message) || this; _this.resolvedHTML = ''; _this.name = 'RenderBreakError'; _this.message = message + ''; return _this; } return RenderBreakError; }(Error)); var AssertionError = /** @class */ (function (_super) { __extends(AssertionError, _super); function AssertionError(message) { var _this = _super.call(this, message) || this; _this.name = 'AssertionError'; _this.message = message + ''; return _this; } return AssertionError; }(Error)); function mkContext(token) { var lines = token.input.split('\n'); var begin = Math.max(token.line - 2, 1); var end = Math.min(token.line + 3, lines.length); var context = range(begin, end + 1) .map(function (lineNumber) { var indicator = (lineNumber === token.line) ? '>> ' : ' '; var num = padStart(String(lineNumber), String(end).length); var text = lines[lineNumber - 1]; return "" + indicator + num + "| " + text; }) .join('\n'); return context; } function mkMessage(msg, token) { if (token.file) msg += ", file:" + token.file; msg += ", line:" + token.line + ", col:" + token.col; return msg; } function assert (predicate, message) { if (!predicate) { message = message || "expect " + predicate + " to be true"; throw new AssertionError(message); } } var defaultOptions = { root: ['.'], cache: false, extname: '', dynamicPartials: true, trimTagRight: false, trimTagLeft: false, trimOutputRight: false, trimOutputLeft: false, greedy: true, tagDelimiterLeft: '{%', tagDelimiterRight: '%}', outputDelimiterLeft: '{{', outputDelimiterRight: '}}', strictFilters: false, strictVariables: false }; function normalize(options) { options = options || {}; if (options.hasOwnProperty('root')) { options.root = normalizeStringArray(options.root); } return options; } function applyDefault(options) { return __assign({}, defaultOptions, options); } function normalizeStringArray(value) { if (isArray(value)) return value; if (isString(value)) return [value]; return []; } var Context = /** @class */ (function () { function Context(ctx, opts) { if (ctx === void 0) { ctx = {}; } this.scopes = [{}]; this.registers = {}; this.opts = applyDefault(opts); this.environments = ctx; } Context.prototype.getRegister = function (key, defaultValue) { if (defaultValue === void 0) { defaultValue = {}; } return (this.registers[key] = this.registers[key] || defaultValue); }; Context.prototype.setRegister = function (key, value) { return (this.registers[key] = value); }; Context.prototype.getAll = function () { return [this.environments].concat(this.scopes).reduce(function (ctx, val) { return __assign(ctx, val); }, {}); }; Context.prototype.get = function (path$$1) { return __awaiter(this, void 0, void 0, function () { var paths, ctx, _i, paths_1, path_1; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.parseProp(path$$1)]; case 1: paths = _a.sent(); ctx = this.findScope(paths[0]) || this.environments; for (_i = 0, paths_1 = paths; _i < paths_1.length; _i++) { path_1 = paths_1[_i]; ctx = readProperty(ctx, path_1); if (isNil(ctx) && this.opts.strictVariables) { throw new TypeError("undefined variable: " + path_1); } } return [2 /*return*/, ctx]; } }); }); }; Context.prototype.push = function (ctx) { return this.scopes.push(ctx); }; Context.prototype.pop = function () { return this.scopes.pop(); }; Context.prototype.front = function () { return this.scopes[0]; }; Context.prototype.findScope = function (key) { for (var i = this.scopes.length - 1; i >= 0; i--) { var candidate = this.scopes[i]; if (key in candidate) { return candidate; } } return null; }; /* * Parse property access sequence from access string * @example * accessSeq("foo.bar") // ['foo', 'bar'] * accessSeq("foo['bar']") // ['foo', 'bar'] * accessSeq("foo['b]r']") // ['foo', 'b]r'] * accessSeq("foo[bar.coo]") // ['foo', 'bar'], for bar.coo == 'bar' */ Context.prototype.parseProp = function (str) { return __awaiter(this, void 0, void 0, function () { function push() { if (name.length) seq.push(name); name = ''; } var seq, name, j, i, _a, delemiter, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: str = String(str); seq = []; name = ''; i = 0; _c.label = 1; case 1: if (!(i < str.length)) return [3 /*break*/, 10]; _a = str[i]; switch (_a) { case '[': return [3 /*break*/, 2]; case '.': return [3 /*break*/, 7]; } return [3 /*break*/, 8]; case 2: push(); delemiter = str[i + 1]; if (!/['"]/.test(delemiter)) return [3 /*break*/, 3]; j = str.indexOf(delemiter, i + 2); assert(j !== -1, "unbalanced " + delemiter + ": " + str); name = str.slice(i + 2, j); push(); i = j + 2; return [3 /*break*/, 6]; case 3: j = matchRightBracket(str, i + 1); assert(j !== -1, "unbalanced []: " + str); name = str.slice(i + 1, j); if (!!/^[+-]?\d+$/.test(name)) return [3 /*break*/, 5]; _b = String; return [4 /*yield*/, this.get(name)]; case 4: name = _b.apply(void 0, [_c.sent()]); _c.label = 5; case 5: push(); i = j + 1; _c.label = 6; case 6: return [3 /*break*/, 9]; case 7: push(); i++; return [3 /*break*/, 9]; case 8: name += str[i++]; _c.label = 9; case 9: return [3 /*break*/, 1]; case 10: push(); if (!seq.length) { throw new TypeError("invalid path:\"" + str + "\""); } return [2 /*return*/, seq]; } }); }); }; return Context; }()); function readProperty(obj, key) { if (isNil(obj)) return obj; obj = toLiquid(obj); if (obj instanceof Drop) { if (isFunction(obj[key])) return obj[key](); if (obj.hasOwnProperty(key)) return obj[key]; return obj.liquidMethodMissing(key); } // Support Braze's operation if (key === 'first' && isArray(obj)) { return obj[0]; } else if (key === 'last' && isArray(obj)) { return obj[obj.length - 1]; } return key === 'size' ? readSize(obj) : obj[key]; } function readSize(obj) { if (!isNil(obj['size'])) return obj['size']; if (isArray(obj) || isString(obj)) return obj.length; return obj['size']; } function matchRightBracket(str, begin) { var stack = 1; // count of '[' - count of ']' for (var i = begin; i < str.length; i++) { if (str[i] === '[') { stack++; } if (str[i] === ']') { stack--; if (stack === 0) { return i; } } } return -1; } var Types = /*#__PURE__*/Object.freeze({ ParseError: ParseError, TokenizationError: TokenizationError, RenderBreakError: RenderBreakError, AssertionError: AssertionError, Drop: Drop }); var statAsync = promisify(fs.stat); var readFileAsync = promisify(fs.readFile); var fs$1 = { exists: function (filepath) { return statAsync(filepath).then(function () { return true; }).catch(function () { return false; }); }, readFile: function (filepath) { return readFileAsync(filepath, 'utf8'); }, resolve: function (root, file, ext) { if (!path.extname(file)) file += ext; return path.resolve(root, file); } }; var Token = /** @class */ (function () { function Token(raw, input, line, col, file) { this.trimLeft = false; this.trimRight = false; this.type = 'notset'; this.col = col; this.line = line; this.raw = raw; this.value = raw; this.input = input; this.file = file; } return Token; }()); var DelimitedToken = /** @class */ (function (_super) { __extends(DelimitedToken, _super); function DelimitedToken(raw, value, input, line, pos, trimLeft, trimRight, file) { var _this = _super.call(this, raw, input, line, pos, file) || this; var tl = value[0] === '-'; var tr = last(value) === '-'; _this.value = value .slice(tl ? 1 : 0, tr ? -1 : value.length) .trim(); _this.trimLeft = tl || trimLeft; _this.trimRight = tr || trimRight; return _this; } return DelimitedToken; }(Token)); // quote related var singleQuoted = /'[^']*'/; var doubleQuoted = /"[^"]*"/; var quoted = new RegExp(singleQuoted.source + "|" + doubleQuoted.source); var quoteBalanced = new RegExp("(?:" + quoted.source + "|[^'\"])*"); // basic types var number = /[+-]?(?:\d+\.?\d*|\.?\d+)/; var bool = /true|false/; // property access var identifier = /[\w-]+[?]?/; var subscript = new RegExp("\\[(?:" + quoted.source + "|[\\w-\\.]+)\\]"); var literal = new RegExp("(?:" + quoted.source + "|" + bool.source + "|" + number.source + ")"); var variable = new RegExp(identifier.source + "(?:\\." + identifier.source + "|" + subscript.source + ")*"); // range related var rangeLimit = new RegExp("(?:" + variable.source + "|" + number.source + ")"); var range$1 = new RegExp("\\(" + rangeLimit.source + "\\.\\." + rangeLimit.source + "\\)"); var rangeCapture = new RegExp("\\((" + rangeLimit.source + ")\\.\\.(" + rangeLimit.source + ")\\)"); var value = new RegExp("(?:" + variable.source + "|" + literal.source + "|" + range$1.source + ")"); // hash related var hash = new RegExp("(?:" + identifier.source + ")\\s*:\\s*(?:" + value.source + ")"); var hashCapture = new RegExp("(" + identifier.source + ")\\s*:\\s*(" + value.source + ")", 'g'); // full match var tagLine = new RegExp("^\\s*(" + identifier.source + ")\\s*([\\s\\S]*?)\\s*$"); var quotedLine = new RegExp("^" + quoted.source + "$"); var rangeLine = new RegExp("^" + rangeCapture.source + "$"); var attribute = new RegExp("^\\s*(?:(" + identifier.source + ")\\.)?\\$\\{\\s*([\\s\\S]+?)\\s*\\}\\s*$"); var operators = [ /\s+or\s+/, /\s+and\s+/, /==|!=|<=|>=|<|>|\s+contains\s+/ ]; var TagToken = /** @class */ (function (_super) { __extends(TagToken, _super); function TagToken(raw, value$$1, input, line, pos, options, file) { var _this = _super.call(this, raw, value$$1, input, line, pos, options.trimTagLeft, options.trimTagRight, file) || this; _this.type = 'tag'; var match = _this.value.match(tagLine); if (!match) { throw new TokenizationError("illegal tag syntax", _this); } _this.name = match[1]; _this.args = match[2]; return _this; } TagToken.is = function (token) { return token.type === 'tag'; }; return TagToken; }(DelimitedToken)); var HTMLToken = /** @class */ (function (_super) { __extends(HTMLToken, _super); function HTMLToken(str, input, line, col, file) { var _this = _super.call(this, str, input, line, col, file) || this; _this.type = 'html'; _this.value = str; return _this; } HTMLToken.is = function (token) { return token.type === 'html'; }; return HTMLToken; }(Token)); function whiteSpaceCtrl(tokens, options) { options = __assign({ greedy: true }, options); var inRaw = false; for (var i = 0; i < tokens.length; i++) { var token = tokens[i]; if (!inRaw && token.trimLeft) { trimLeft(tokens[i - 1], options.greedy); } if (TagToken.is(token)) { if (token.name === 'raw') inRaw = true; else if (token.name === 'endraw') inRaw = false; } if (!inRaw && token.trimRight) { trimRight(tokens[i + 1], options.greedy); } } } function trimLeft(token, greedy) { if (!token || !HTMLToken.is(token)) return; var rLeft = greedy ? /\s+$/g : /[\t\r ]*$/g; token.value = token.value.replace(rLeft, ''); } function trimRight(token, greedy) { if (!token || !HTMLToken.is(token)) return; var rRight = greedy ? /^\s+/g : /^[\t\r ]*\n?/g; token.value = token.value.replace(rRight, ''); } var OutputToken = /** @class */ (function (_super) { __extends(OutputToken, _super); function OutputToken(raw, value, input, line, pos, options, file) { var _this = _super.call(this, raw, value, input, line, pos, options.trimOutputLeft, options.trimOutputRight, file) || this; _this.type = 'output'; return _this; } OutputToken.is = function (token) { return token.type === 'output'; }; return OutputToken; }(DelimitedToken)); var ParseState; (function (ParseState) { ParseState[ParseState["HTML"] = 0] = "HTML"; ParseState[ParseState["OUTPUT"] = 1] = "OUTPUT"; ParseState[ParseState["TAG"] = 2] = "TAG"; ParseState[ParseState["ATTRIBUTE"] = 3] = "ATTRIBUTE"; ParseState[ParseState["CONTENT_BLOCKS"] = 4] = "CONTENT_BLOCKS"; })(ParseState || (ParseState = {})); var Tokenizer = /** @class */ (function () { function Tokenizer(options) { this.options = applyDefault(options); } Tokenizer.prototype.tokenize = function (input, file) { var tokens = []; var _a = this.options, tagDelimiterLeft = _a.tagDelimiterLeft, tagDelimiterRight = _a.tagDelimiterRight, outputDelimiterLeft = _a.outputDelimiterLeft, outputDelimiterRight = _a.outputDelimiterRight; var attributeLeft = '${'; var attributeRight = '}'; var contentBlocksTag = 'content_blocks'; var p = 0; var curLine = 1; var state = ParseState.HTML; var buffer = ''; var lineBegin = 0; var line = 1; var col = 1; var originalState = ParseState.HTML; while (p < input.length) { if (input[p] === '\n') { curLine++; lineBegin = p + 1; } if (state === ParseState.HTML) { if (input.substr(p, outputDelimiterLeft.length) === outputDelimiterLeft) { if (buffer) tokens.push(new HTMLToken(buffer, input, line, col, file)); line = curLine; col = p - lineBegin + 1; buffer = outputDelimiterLeft; p += outputDelimiterLeft.length; // handle content blocks while (input.substr(p, 1) === ' ') { buffer += ' '; p += 1; } if (input.substr(p, contentBlocksTag.length) === contentBlocksTag) { buffer += contentBlocksTag; p += contentBlocksTag.length; state = ParseState.CONTENT_BLOCKS; } else { state = ParseState.OUTPUT; } continue; } else if (input.substr(p, tagDelimiterLeft.length) === tagDelimiterLeft) { if (buffer) tokens.push(new HTMLToken(buffer, input, line, col, file)); buffer = tagDelimiterLeft; line = curLine; col = p - lineBegin + 1; p += tagDelimiterLeft.length; state = ParseState.TAG; continue; } } else if ((state === ParseState.OUTPUT || state === ParseState.CONTENT_BLOCKS) && input.substr(p, attributeLeft.length) === attributeLeft) { originalState = state; buffer += attributeLeft; line = curLine; col = p - lineBegin + 1; p += attributeLeft.length; state = ParseState.ATTRIBUTE; continue; } else if (state === ParseState.ATTRIBUTE && input[p] === attributeRight) { buffer += attributeRight; line = curLine; col = p - lineBegin + 1; p += attributeRight.length; state = originalState; originalState = ParseState.HTML; continue; } else if ((state === ParseState.OUTPUT || state === ParseState.CONTENT_BLOCKS) && input.substr(p, outputDelimiterRight.length) === outputDelimiterRight) { buffer += outputDelimiterRight; var TokenType = state === ParseState.OUTPUT ? OutputToken : TagToken; tokens.push(new TokenType(buffer, buffer.slice(outputDelimiterLeft.length, -outputDelimiterRight.length), input, line, col, this.options, file)); p += outputDelimiterRight.length; buffer = ''; line = curLine; col = p - lineBegin + 1; state = ParseState.HTML; continue; } else if (input.substr(p, tagDelimiterRight.length) === tagDelimiterRight) { buffer += tagDelimiterRight; tokens.push(new TagToken(buffer, buffer.slice(tagDelimiterLeft.length, -tagDelimiterRight.length), input, line, col, this.options, file)); p += tagDelimiterRight.length; buffer = ''; line = curLine; col = p - lineBegin + 1; state = ParseState.HTML; continue; } buffer += input[p++]; } if (state !== ParseState.HTML) { var t = state === ParseState.OUTPUT ? 'output' : 'tag'; var str = buffer.length > 16 ? buffer.slice(0, 13) + '...' : buffer; throw new TokenizationError(t + " \"" + str + "\" not closed", new Token(buffer, input, line, col, file)); } if (buffer) tokens.push(new HTMLToken(buffer, input, line, col, file)); whiteSpaceCtrl(tokens, this.options); return tokens; }; return Tokenizer; }()); var Render = /** @class */ (function () { function Render() { } Render.prototype.renderTemplates = function (templates, ctx) { return __awaiter(this, void 0, void 0, function () { var html, _i, templates_1, tpl, _a, e_1; return __generator(this, function (_b) { switch (_b.label) { case 0: assert(ctx, 'unable to evalTemplates: context undefined'); html = ''; _i = 0, templates_1 = templates; _b.label = 1; case 1: if (!(_i < templates_1.length)) return [3 /*break*/, 6]; tpl = templates_1[_i]; _b.label = 2; case 2: _b.trys.push([2, 4, , 5]); _a = html; return [4 /*yield*/, tpl.render(ctx)]; case 3: html = _a + _b.sent(); return [3 /*break*/, 5]; case 4: e_1 = _b.sent(); if (e_1.name === 'AbortError') { e_1.resolvedHTML = ''; throw e_1; } if (e_1.name === 'RenderBreakError') { e_1.resolvedHTML = html; throw e_1; } throw e_1.name === 'RenderError' ? e_1 : new RenderError(e_1, tpl); case 5: _i++; return [3 /*break*/, 1]; case 6: return [2 /*return*/, html]; } }); }); }; return Render; }()); function isComparable(arg) { return arg && isFunction(arg.equals); } var EmptyDrop = /** @class */ (function (_super) { __extends(EmptyDrop, _super); function EmptyDrop() { return _super !== null && _super.apply(this, arguments) || this; } EmptyDrop.prototype.equals = function (value) { if (isString(value) || isArray(value)) return value.length === 0; if (isObject(value)) return Object.keys(value).length === 0; return false; }; EmptyDrop.prototype.gt = function () { return false; }; EmptyDrop.prototype.geq = function () { return false; }; EmptyDrop.prototype.lt = function () { return false; }; EmptyDrop.prototype.leq = function () { return false; }; EmptyDrop.prototype.valueOf = function () { return ''; }; return EmptyDrop; }(Drop)); var BlankDrop = /** @class */ (function (_super) { __extends(BlankDrop, _super); function BlankDrop() { return _super !== null && _super.apply(this, arguments) || this; } BlankDrop.prototype.equals = function (value) { if (value === false) return true; if (isNil(value instanceof Drop ? value.valueOf() : value)) return true; if (isString(value)) return /^\s*$/.test(value); return _super.prototype.equals.call(this, value); }; return BlankDrop; }(EmptyDrop)); var NullDrop = /** @class */ (function (_super) { __extends(NullDrop, _super); function NullDrop() { return _super !== null && _super.apply(this, arguments) || this; } NullDrop.prototype.equals = function (value) { return isNil(value instanceof Drop ? value.valueOf() : value) || value instanceof BlankDrop; }; NullDrop.prototype.gt = function () { return false; }; NullDrop.prototype.geq = function () { return false; }; NullDrop.prototype.lt = function () { return false; }; NullDrop.prototype.leq = function () { return false; }; NullDrop.prototype.valueOf = function () { return null; }; return NullDrop; }(Drop)); var binaryOperators = { '==': function (l, r) { if (isComparable(l)) return l.equals(r); if (isComparable(r)) return r.equals(l); return l === r; }, '!=': function (l, r) { if (isComparable(l)) return !l.equals(r); if (isComparable(r)) return !r.equals(l); return l !== r; }, '>': function (l, r) { if (isComparable(l)) return l.gt(r); if (isComparable(r)) return r.lt(l); return l > r; }, '<': function (l, r) { if (isComparable(l)) return l.lt(r); if (isComparable(r)) return r.gt(l); return l < r; }, '>=': function (l, r) { if (isComparable(l)) return l.geq(r); if (isComparable(r)) return r.leq(l); return l >= r; }, '<=': function (l, r) { if (isComparable(l)) return l.leq(r); if (isComparable(r)) return r.geq(l); return l <= r; }, 'contains': function (l, r) { return l && isFunction(l.indexOf) ? l.indexOf(r) > -1 : false; }, 'and': function (l, r) { return isTruthy(l) && isTruthy(r); }, 'or': function (l, r) { return isTruthy(l) || isTruthy(r); } }; function parseExp(exp, ctx) { return __awaiter(this, void 0, void 0, function () { var operatorREs, match, i, operatorRE, expRE, l, op, r, low, high; return __generator(this, function (_a) { switch (_a.label) { case 0: assert(ctx, 'unable to parseExp: scope undefined'); operatorREs = operators; i = 0; _a.label = 1; case 1: if (!(i < operatorREs.length)) return [3 /*break*/, 5]; operatorRE = operatorREs[i]; expRE = new RegExp("^(" + quoteBalanced.source + ")(" + operatorRE.source + ")(" + quoteBalanced.source + ")$"); if (!(match = exp.match(expRE))) return [3 /*break*/, 4]; return [4 /*yield*/, parseExp(match[1], ctx)]; case 2: l = _a.sent(); op = binaryOperators[match[2].trim()]; return [4 /*yield*/, parseExp(match[3], ctx)]; case 3: r = _a.sent(); return [2 /*return*/, op(l, r)]; case 4: i++; return [3 /*break*/, 1]; case 5: if (!(match = exp.match(rangeLine))) return [3 /*break*/, 8]; return [4 /*yield*/, evalValue(match[1], ctx)]; case 6: low = _a.sent(); return [4 /*yield*/, evalValue(match[2], ctx)]; case 7: high = _a.sent(); return [2 /*return*/, range(+low, +high + 1)]; case 8: return [2 /*return*/, parseValue(exp, ctx)]; } }); }); } function evalExp(str, ctx) { return __awaiter(this, void 0, void 0, function () { var value$$1; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, parseExp(str, ctx)]; case 1: value$$1 = _a.sent(); return [2 /*return*/, value$$1 instanceof Drop ? value$$1.valueOf() : value$$1]; } }); }); } function parseValue(str, ctx) { return __awaiter(this, void 0, void 0, function () { var re, match; return __generator(this, function (_a) { if (!str) return [2 /*return*/, null]; str = str.trim(); if (str === 'true') return [2 /*return*/, true]; if (str === 'false') return [2 /*return*/, false]; if (str === 'nil' || str === 'null') return [2 /*return*/, new NullDrop()]; if (str === 'empty') return [2 /*return*/, new EmptyDrop()]; if (str === 'blank') return [2 /*return*/, new BlankDrop()]; if (!isNaN(Number(str))) return [2 /*return*/, Number(str)]; if ((str[0] === '"' || str[0] === "'") && str[0] === last(str)) return [2 /*return*/, str.slice(1, -1) // for Braze, strip {{ and }} ]; re = new RegExp(escapeRegExp(ctx.opts.outputDelimiterLeft) + "\\s*(.*?)\\s*" + escapeRegExp(ctx.opts.outputDelimiterRight)); str = str.replace(re, '$1'); if ((match = str.match(attribute))) { if (match[1]) return [2 /*return*/, ctx.get(match[1] + "['" + match[2] + "']")]; return [2 /*return*/, ctx.get(match[2])]; } return [2 /*return*/, ctx.get(str)]; }); }); } function evalValue(str, ctx) { return __awaiter(this, void 0, void 0, function () { var value$$1; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, parseValue(str, ctx)]; case 1: value$$1 = _a.sent(); return [2 /*return*/, value$$1 instanceof Drop ? value$$1.valueOf() : value$$1]; } }); }); } function isTruthy(val) { return !isFalsy(val); } function isFalsy(val) { return val === false || undefined === val || val === null; } /** * Key-Value Pairs Representing Tag Arguments * Example: * For the markup `{% include 'head.html' foo='bar' %}`, * hash['foo'] === 'bar' */ var Hash = /** @class */ (function () { function Hash() { } Hash.create = function (markup, ctx) { return __awaiter(this, void 0, void 0, function () { var instance, match, k, v, _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: instance = new Hash(); hashCapture.lastIndex = 0; _c.label = 1; case 1: if (!(match = hashCapture.exec(markup))) return [3 /*break*/, 3]; k = match[1]; v = match[2]; _a = instance; _b = k; return [4 /*yield*/, evalValue(v, ctx)]; case 2: _a[_b] = _c.sent(); return [3 /*break*/, 1]; case 3: return [2 /*return*/, instance]; } }); }); }; return Hash; }()); var Template = /** @class */ (function () { function Template(token) { this.token = token; } return Template; }()); var Tag = /** @class */ (function (_super) { __extends(Tag, _super); function Tag(token, tokens, liquid) { var _this = _super.call(this, token) || this; _this.name = token.name; var impl = Tag.impls[token.name]; assert(impl, "tag " + token.name + " not found"); _this.impl = Object.create(impl); _this.impl.liquid = liquid; if (_this.impl.parse) { _this.impl.parse(token, tokens); } return _this; } Tag.prototype.render = function (ctx) { return __awaiter(this, void 0, void 0, function () { var hash, impl, _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: return [4 /*yield*/, Hash.create(this.token.args, ctx)]; case 1: hash = _c.sent(); impl = this.impl; if (!isFunction(impl.render)) return [3 /*break*/, 3]; _b = stringify; return [4 /*yield*/, impl.render(ctx, hash)]; case 2: _a = _b.apply(void 0, [_c.sent()]); return [3 /*break*/, 4]; case 3: _a = ''; _c.label = 4; case 4: return [2 /*return*/, _a]; } }); }); }; Tag.register = function (name, tag) { Tag.impls[name] = tag; }; Tag.clear = function () { Tag.impls = {}; }; Tag.impls = {}; return Tag; }(Template)); var Filter = /** @class */ (function () { function Filter(name, args, strictFilters) { var impl = Filter.impls[name]; if (!impl && strictFilters) throw new TypeError("undefined filter: " + name); this.name = name; this.impl = impl || (function (x) { return x; }); this.args = args; } Filter.prototype.render = function (value, context) { return __awaiter(this, void 0, void 0, function () { var argv, _i, _a, arg, _b, _c, _d, _e, _f; return __generator(this, function (_g) { switch (_g.label) { case 0: argv = []; _i = 0, _a = this.args; _g.label = 1; case 1: if (!(_i < _a.length)) return [3 /*break*/, 6]; arg = _a[_i]; if (!isArray(arg)) return [3 /*break*/, 3]; _c = (_b = argv).push; _d = [arg[0]]; return [4 /*yield*/, evalValue(arg[1], context)]; case 2: _c.apply(_b, [_d.concat([_g.sent()])]); return [3 /*break*/, 5]; case 3: _f = (_e = argv).push; return [4 /*yield*/, evalValue(arg, context)]; case 4: _f.apply(_e, [_g.sent()]); _g.label = 5; case 5: _i++; return [3 /*break*/, 1]; case 6: return [2 /*return*/, this.impl.apply({ context: context }, [value].concat(argv))]; } }); }); }; Filter.register = function (name, filter) { Filter.impls[name] = filter; }; Filter.clear = function () { Filter.impls = {}; }; Filter.impls = {}; return Filter; }()); var ParseStream = /** @class */ (function () { function ParseStream(tokens, parseToken) { this.handlers = {}; this.stopRequested = false; this.tokens = tokens; this.parseToken = parseToken; } ParseStream.prototype.on = function (name, cb) { this.handlers[name] = cb; return this; }; ParseStream.prototype.trigger = function (event, arg) { var h = this.handlers[event]; return h ? (h(arg), true) : false; }; ParseStream.prototype.start = function () { this.trigger('start'); var token; while (!this.stopRequested && (token = this.tokens.shift())) { if (this.trigger('token', token)) continue; if (TagToken.is(token) && this.trigger("tag:" + token.name, token)) { continue; } var template = this.parseToken(token, this.tokens); this.trigger('template', template); } if (!this.stopRequested) this.trigger('end'); return this; }; ParseStream.prototype.stop = function () { this.stopRequested = true; return this; }; return ParseStream; }()); var Value = /** @class */ (function () { /** * @param str value string, like: "i have a dream | truncate: 3 */ function Value(str, strictFilters) { this.filters = []; var tokens = Value.tokenize(str); this.strictFilters = strictFilters; this.initial = tokens[0]; this.parseFilters(tokens, 1); } Value.prototype.parseFilters = function (tokens, begin) { var i = begin; while (i < tokens.length) { if (tokens[i] !== '|') { i++; continue; } var j = ++i; while (i < tokens.length && tokens[i] !== '|') i++; this.parseFilter(tokens, j, i); } }; Value.prototype.parseFilter = function (tokens, begin, end) { var name = tokens[begin]; var args = []; var argName, argValue; for (var i = begin + 1; i < end + 1; i++) { if (i === end || tokens[i] === ',') { if (argName || argValue) { args.push(argName ? [argName, argValue] : argValue); } argValue = argName = undefined; } else if (tokens[i] === ':') { argName = argValue; argValue = undefined; } else if (argValue === undefined) { argValue = tokens[i]; } } this.filters.push(new Filter(name, args, this.strictFilters)); }; Value.prototype.value = function (ctx) { return __awaiter(this, void 0, void 0, function () { var val, _i, _a, filter; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, evalExp(this.initial, ctx)]; case 1: val = _b.sent(); _i = 0, _a = this.filters; _b.label = 2; case 2: if (!(_i < _a.length)) return [3 /*break*/, 5]; filter = _a[_i]; return [4 /*yield*/, filter.render(val, ctx)]; case 3: val = _b.sent(); _b.label = 4; case 4: _i++; return [3 /*break*/, 2]; case 5: return [2 /*return*/, val]; } }); }); }; Value.tokenize = function (str) { var tokens = []; var i = 0; while (i < str.length) { var ch = str[i]; if (ch === '"' || ch === "'") { var j = i; for (i += 2; i < str.length && str[i - 1] !== ch; ++i) ; tokens.push(str.slice(j, i)); } else if