/*
* liquidjs@10.14.0, https://github.com/harttle/liquidjs
* (c) 2016-2024 harttle
* Released under the MIT License.
*/
class Token {
constructor(kind, input, begin, end, file) {
this.kind = kind;
this.input = input;
this.begin = begin;
this.end = end;
this.file = file;
}
getText() {
return this.input.slice(this.begin, this.end);
}
getPosition() {
let [row, col] = [1, 1];
for (let i = 0; i < this.begin; i++) {
if (this.input[i] === '\n') {
row++;
col = 1;
}
else
col++;
}
return [row, col];
}
size() {
return this.end - this.begin;
}
}
class Drop {
liquidMethodMissing(key) {
return undefined;
}
}
const toString$1 = Object.prototype.toString;
const toLowerCase = String.prototype.toLowerCase;
const hasOwnProperty = Object.hasOwnProperty;
function isString(value) {
return typeof value === 'string';
}
// eslint-disable-next-line @typescript-eslint/ban-types
function isFunction(value) {
return typeof value === 'function';
}
function isPromise(val) {
return val && isFunction(val.then);
}
function isIterator(val) {
return val && isFunction(val.next) && isFunction(val.throw) && isFunction(val.return);
}
function escapeRegex(str) {
return str.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
}
function stringify(value) {
value = toValue(value);
if (isString(value))
return value;
if (isNil(value))
return '';
if (isArray(value))
return value.map(x => stringify(x)).join('');
return String(value);
}
function toEnumerable(val) {
val = toValue(val);
if (isArray(val))
return val;
if (isString(val) && val.length > 0)
return [val];
if (isIterable(val))
return Array.from(val);
if (isObject(val))
return Object.keys(val).map((key) => [key, val[key]]);
return [];
}
function toArray(val) {
val = toValue(val);
if (isNil(val))
return [];
if (isArray(val))
return val;
return [val];
}
function toValue(value) {
return (value instanceof Drop && isFunction(value.valueOf)) ? value.valueOf() : value;
}
function isNumber(value) {
return typeof value === 'number';
}
function toLiquid(value) {
if (value && isFunction(value.toLiquid))
return toLiquid(value.toLiquid());
return value;
}
function isNil(value) {
return value == null;
}
function isUndefined(value) {
return value === undefined;
}
function isArray(value) {
// be compatible with IE 8
return toString$1.call(value) === '[object Array]';
}
function isIterable(value) {
return isObject(value) && Symbol.iterator in value;
}
/*
* 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(obj, iteratee) {
obj = obj || {};
for (const k in obj) {
if (hasOwnProperty.call(obj, k)) {
if (iteratee(obj[k], k, obj) === false)
break;
}
}
return obj;
}
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) {
const type = typeof value;
return value !== null && (type === 'object' || type === 'function');
}
function range(start, stop, step = 1) {
const arr = [];
for (let i = start; i < stop; i += step) {
arr.push(i);
}
return arr;
}
function padStart(str, length, ch = ' ') {
return pad(str, length, ch, (str, ch) => ch + str);
}
function padEnd(str, length, ch = ' ') {
return pad(str, length, ch, (str, ch) => str + ch);
}
function pad(str, length, ch, add) {
str = String(str);
let n = length - str.length;
while (n-- > 0)
str = add(str, ch);
return str;
}
function identify(val) {
return val;
}
function changeCase(str) {
const hasLowerCase = [...str].some(ch => ch >= 'a' && ch <= 'z');
return hasLowerCase ? str.toUpperCase() : str.toLowerCase();
}
function ellipsis(str, N) {
return str.length > N ? str.slice(0, N - 3) + '...' : str;
}
// compare string in case-insensitive way, undefined values to the tail
function caseInsensitiveCompare(a, b) {
if (a == null && b == null)
return 0;
if (a == null)
return 1;
if (b == null)
return -1;
a = toLowerCase.call(a);
b = toLowerCase.call(b);
if (a < b)
return -1;
if (a > b)
return 1;
return 0;
}
function argumentsToValue(fn) {
return (...args) => fn(...args.map(toValue));
}
function escapeRegExp(text) {
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}
/**
* targeting ES5, extends Error won't create a proper prototype chain, need a trait to keep track of classes
*/
const TRAIT = '__liquidClass__';
class LiquidError extends Error {
constructor(err, token) {
/**
* note: for ES5 targeting, `this` will be replaced by return value of Error(),
* thus everything on `this` will be lost, avoid calling `LiquidError` methods here
*/
super(typeof err === 'string' ? err : err.message);
this.context = '';
if (typeof err !== 'string')
Object.defineProperty(this, 'originalError', { value: err, enumerable: false });
Object.defineProperty(this, 'token', { value: token, enumerable: false });
Object.defineProperty(this, TRAIT, { value: 'LiquidError', enumerable: false });
}
update() {
Object.defineProperty(this, 'context', { value: mkContext(this.token), enumerable: false });
this.message = mkMessage(this.message, this.token);
this.stack = this.message + '\n' + this.context +
'\n' + this.stack;
if (this.originalError)
this.stack += '\nFrom ' + this.originalError.stack;
}
static is(obj) {
return (obj === null || obj === void 0 ? void 0 : obj[TRAIT]) === 'LiquidError';
}
}
class TokenizationError extends LiquidError {
constructor(message, token) {
super(message, token);
this.name = 'TokenizationError';
super.update();
}
}
class ParseError extends LiquidError {
constructor(err, token) {
super(err, token);
this.name = 'ParseError';
this.message = err.message;
super.update();
}
}
class RenderError extends LiquidError {
constructor(err, tpl) {
super(err, tpl.token);
this.name = 'RenderError';
this.message = err.message;
super.update();
}
static is(obj) {
return obj.name === 'RenderError';
}
}
class LiquidErrors extends LiquidError {
constructor(errors) {
super(errors[0], errors[0].token);
this.errors = errors;
this.name = 'LiquidErrors';
const s = errors.length > 1 ? 's' : '';
this.message = `${errors.length} error${s} found`;
super.update();
}
static is(obj) {
return obj.name === 'LiquidErrors';
}
}
class UndefinedVariableError extends LiquidError {
constructor(err, token) {
super(err, token);
this.name = 'UndefinedVariableError';
this.message = err.message;
super.update();
}
}
// only used internally; raised where we don't have token information,
// so it can't be an UndefinedVariableError.
class InternalUndefinedVariableError extends Error {
constructor(variableName) {
super(`undefined variable: ${variableName}`);
this.name = 'InternalUndefinedVariableError';
this.variableName = variableName;
}
}
class AssertionError extends Error {
constructor(message) {
super(message);
this.name = 'AssertionError';
this.message = message + '';
}
}
function mkContext(token) {
const [line, col] = token.getPosition();
const lines = token.input.split('\n');
const begin = Math.max(line - 2, 1);
const end = Math.min(line + 3, lines.length);
const context = range(begin, end + 1)
.map(lineNumber => {
const rowIndicator = (lineNumber === line) ? '>> ' : ' ';
const num = padStart(String(lineNumber), String(end).length);
let text = `${rowIndicator}${num}| `;
const colIndicator = lineNumber === line
? '\n' + padStart('^', col + text.length)
: '';
text += lines[lineNumber - 1];
text += colIndicator;
return text;
})
.join('\n');
return context;
}
function mkMessage(msg, token) {
if (token.file)
msg += `, file:${token.file}`;
const [line, col] = token.getPosition();
msg += `, line:${line}, col:${col}`;
return msg;
}
// **DO NOT CHANGE THIS FILE**
//
// This file is generated by bin/character-gen.js
// bitmask character types to boost performance
const TYPES = [0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 4, 4, 4, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 2, 8, 0, 0, 0, 0, 8, 0, 0, 0, 64, 0, 65, 0, 0, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 0, 0, 2, 2, 2, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0];
const WORD = 1;
const BLANK = 4;
const QUOTE = 8;
const INLINE_BLANK = 16;
const NUMBER = 32;
const SIGN = 64;
const PUNCTUATION = 128;
function isWord(char) {
const code = char.charCodeAt(0);
return code >= 128 ? !TYPES[code] : !!(TYPES[code] & WORD);
}
TYPES[160] = TYPES[5760] = TYPES[6158] = TYPES[8192] = TYPES[8193] = TYPES[8194] = TYPES[8195] = TYPES[8196] = TYPES[8197] = TYPES[8198] = TYPES[8199] = TYPES[8200] = TYPES[8201] = TYPES[8202] = TYPES[8232] = TYPES[8233] = TYPES[8239] = TYPES[8287] = TYPES[12288] = BLANK;
TYPES[8220] = TYPES[8221] = PUNCTUATION;
function assert(predicate, message) {
if (!predicate) {
const msg = typeof message === 'function'
? message()
: (message || `expect ${predicate} to be true`);
throw new AssertionError(msg);
}
}
class NullDrop extends Drop {
equals(value) {
return isNil(toValue(value));
}
gt() {
return false;
}
geq() {
return false;
}
lt() {
return false;
}
leq() {
return false;
}
valueOf() {
return null;
}
}
class EmptyDrop extends Drop {
equals(value) {
if (value instanceof EmptyDrop)
return false;
value = toValue(value);
if (isString(value) || isArray(value))
return value.length === 0;
if (isObject(value))
return Object.keys(value).length === 0;
return false;
}
gt() {
return false;
}
geq() {
return false;
}
lt() {
return false;
}
leq() {
return false;
}
valueOf() {
return '';
}
}
class BlankDrop extends EmptyDrop {
equals(value) {
if (value === false)
return true;
if (isNil(toValue(value)))
return true;
if (isString(value))
return /^\s*$/.test(value);
return super.equals(value);
}
}
class ForloopDrop extends Drop {
constructor(length, collection, variable) {
super();
this.i = 0;
this.length = length;
this.name = `${variable}-${collection}`;
}
next() {
this.i++;
}
index0() {
return this.i;
}
index() {
return this.i + 1;
}
first() {
return this.i === 0;
}
last() {
return this.i === this.length - 1;
}
rindex() {
return this.length - this.i;
}
rindex0() {
return this.length - this.i - 1;
}
valueOf() {
return JSON.stringify(this);
}
}
class BlockDrop extends Drop {
constructor(
// the block render from layout template
superBlockRender = () => '') {
super();
this.superBlockRender = superBlockRender;
}
/**
* Provide parent access in child block by
* {{ block.super }}
*/
super() {
return this.superBlockRender();
}
}
function isComparable(arg) {
return (arg &&
isFunction(arg.equals) &&
isFunction(arg.gt) &&
isFunction(arg.geq) &&
isFunction(arg.lt) &&
isFunction(arg.leq));
}
const nil = new NullDrop();
const literalValues = {
'true': true,
'false': false,
'nil': nil,
'null': nil,
'empty': new EmptyDrop(),
'blank': new BlankDrop()
};
function createTrie(input) {
const trie = {};
for (const [name, data] of Object.entries(input)) {
let node = trie;
for (let i = 0; i < name.length; i++) {
const c = name[i];
node[c] = node[c] || {};
if (i === name.length - 1 && isWord(name[i])) {
node[c].needBoundary = true;
}
node = node[c];
}
node.data = data;
node.end = true;
}
return trie;
}
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
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) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
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) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
// convert an async iterator to a Promise
function toPromise(val) {
return __awaiter(this, void 0, void 0, function* () {
if (!isIterator(val))
return val;
let value;
let done = false;
let next = 'next';
do {
const state = val[next](value);
done = state.done;
value = state.value;
next = 'next';
try {
if (isIterator(value))
value = toPromise(value);
if (isPromise(value))
value = yield value;
}
catch (err) {
next = 'throw';
value = err;
}
} while (!done);
return value;
});
}
// convert an async iterator to a value in a synchronous manner
function toValueSync(val) {
if (!isIterator(val))
return val;
let value;
let done = false;
let next = 'next';
do {
const state = val[next](value);
done = state.done;
value = state.value;
next = 'next';
if (isIterator(value)) {
try {
value = toValueSync(value);
}
catch (err) {
next = 'throw';
value = err;
}
}
} while (!done);
return value;
}
const rFormat = /%([-_0^#:]+)?(\d+)?([EO])?(.)/;
const monthNames = [
'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
'September', 'October', 'November', 'December'
];
const dayNames = [
'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'
];
const monthNamesShort = monthNames.map(abbr);
const dayNamesShort = dayNames.map(abbr);
function abbr(str) {
return str.slice(0, 3);
}
// prototype extensions
function daysInMonth(d) {
const feb = isLeapYear(d) ? 29 : 28;
return [31, feb, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
}
function getDayOfYear(d) {
let num = 0;
for (let i = 0; i < d.getMonth(); ++i) {
num += daysInMonth(d)[i];
}
return num + d.getDate();
}
function getWeekOfYear(d, startDay) {
// Skip to startDay of this week
const now = getDayOfYear(d) + (startDay - d.getDay());
// Find the first startDay of the year
const jan1 = new Date(d.getFullYear(), 0, 1);
const then = (7 - jan1.getDay() + startDay);
return String(Math.floor((now - then) / 7) + 1);
}
function isLeapYear(d) {
const year = d.getFullYear();
return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year)));
}
function ordinal(d) {
const date = d.getDate();
if ([11, 12, 13].includes(date))
return 'th';
switch (date % 10) {
case 1: return 'st';
case 2: return 'nd';
case 3: return 'rd';
default: return 'th';
}
}
function century(d) {
return parseInt(d.getFullYear().toString().substring(0, 2), 10);
}
// default to 0
const padWidths = {
d: 2,
e: 2,
H: 2,
I: 2,
j: 3,
k: 2,
l: 2,
L: 3,
m: 2,
M: 2,
S: 2,
U: 2,
W: 2
};
// default to '0'
const padChars = {
a: ' ',
A: ' ',
b: ' ',
B: ' ',
c: ' ',
e: ' ',
k: ' ',
l: ' ',
p: ' ',
P: ' '
};
function getTimezoneOffset(d, opts) {
const nOffset = Math.abs(d.getTimezoneOffset());
const h = Math.floor(nOffset / 60);
const m = nOffset % 60;
return (d.getTimezoneOffset() > 0 ? '-' : '+') +
padStart(h, 2, '0') +
(opts.flags[':'] ? ':' : '') +
padStart(m, 2, '0');
}
const formatCodes = {
a: (d) => dayNamesShort[d.getDay()],
A: (d) => dayNames[d.getDay()],
b: (d) => monthNamesShort[d.getMonth()],
B: (d) => monthNames[d.getMonth()],
c: (d) => d.toLocaleString(),
C: (d) => century(d),
d: (d) => d.getDate(),
e: (d) => d.getDate(),
H: (d) => d.getHours(),
I: (d) => String(d.getHours() % 12 || 12),
j: (d) => getDayOfYear(d),
k: (d) => d.getHours(),
l: (d) => String(d.getHours() % 12 || 12),
L: (d) => d.getMilliseconds(),
m: (d) => d.getMonth() + 1,
M: (d) => d.getMinutes(),
N: (d, opts) => {
const width = Number(opts.width) || 9;
const str = String(d.getMilliseconds()).slice(0, width);
return padEnd(str, width, '0');
},
p: (d) => (d.getHours() < 12 ? 'AM' : 'PM'),
P: (d) => (d.getHours() < 12 ? 'am' : 'pm'),
q: (d) => ordinal(d),
s: (d) => Math.round(d.getTime() / 1000),
S: (d) => d.getSeconds(),
u: (d) => d.getDay() || 7,
U: (d) => getWeekOfYear(d, 0),
w: (d) => d.getDay(),
W: (d) => getWeekOfYear(d, 1),
x: (d) => d.toLocaleDateString(),
X: (d) => d.toLocaleTimeString(),
y: (d) => d.getFullYear().toString().slice(2, 4),
Y: (d) => d.getFullYear(),
z: getTimezoneOffset,
Z: (d, opts) => {
if (d.getTimezoneName) {
return d.getTimezoneName() || getTimezoneOffset(d, opts);
}
return (typeof Intl !== 'undefined' ? Intl.DateTimeFormat().resolvedOptions().timeZone : '');
},
't': () => '\t',
'n': () => '\n',
'%': () => '%'
};
formatCodes.h = formatCodes.b;
function strftime(d, formatStr) {
let output = '';
let remaining = formatStr;
let match;
while ((match = rFormat.exec(remaining))) {
output += remaining.slice(0, match.index);
remaining = remaining.slice(match.index + match[0].length);
output += format(d, match);
}
return output + remaining;
}
function format(d, match) {
const [input, flagStr = '', width, modifier, conversion] = match;
const convert = formatCodes[conversion];
if (!convert)
return input;
const flags = {};
for (const flag of flagStr)
flags[flag] = true;
let ret = String(convert(d, { flags, width, modifier }));
let padChar = padChars[conversion] || '0';
let padWidth = width || padWidths[conversion] || 0;
if (flags['^'])
ret = ret.toUpperCase();
else if (flags['#'])
ret = changeCase(ret);
if (flags['_'])
padChar = ' ';
else if (flags['0'])
padChar = '0';
if (flags['-'])
padWidth = 0;
return padStart(ret, padWidth, padChar);
}
// one minute in milliseconds
const OneMinute = 60000;
const ISO8601_TIMEZONE_PATTERN = /([zZ]|([+-])(\d{2}):(\d{2}))$/;
/**
* A date implementation with timezone info, just like Ruby date
*
* Implementation:
* - create a Date offset by it's timezone difference, avoiding overriding a bunch of methods
* - rewrite getTimezoneOffset() to trick strftime
*/
class TimezoneDate {
constructor(init, timezone) {
this.date = init instanceof TimezoneDate
? init.date
: new Date(init);
this.timezoneOffset = isString(timezone) ? TimezoneDate.getTimezoneOffset(timezone, this.date) : timezone;
this.timezoneName = isString(timezone) ? timezone : '';
const diff = (this.date.getTimezoneOffset() - this.timezoneOffset) * OneMinute;
const time = this.date.getTime() + diff;
this.displayDate = new Date(time);
}
getTime() {
return this.displayDate.getTime();
}
getMilliseconds() {
return this.displayDate.getMilliseconds();
}
getSeconds() {
return this.displayDate.getSeconds();
}
getMinutes() {
return this.displayDate.getMinutes();
}
getHours() {
return this.displayDate.getHours();
}
getDay() {
return this.displayDate.getDay();
}
getDate() {
return this.displayDate.getDate();
}
getMonth() {
return this.displayDate.getMonth();
}
getFullYear() {
return this.displayDate.getFullYear();
}
toLocaleString(locale, init) {
if (init === null || init === void 0 ? void 0 : init.timeZone) {
return this.date.toLocaleString(locale, init);
}
return this.displayDate.toLocaleString(locale, init);
}
toLocaleTimeString(locale) {
return this.displayDate.toLocaleTimeString(locale);
}
toLocaleDateString(locale) {
return this.displayDate.toLocaleDateString(locale);
}
getTimezoneOffset() {
return this.timezoneOffset;
}
getTimezoneName() {
return this.timezoneName;
}
/**
* Create a Date object fixed to it's declared Timezone. Both
* - 2021-08-06T02:29:00.000Z and
* - 2021-08-06T02:29:00.000+08:00
* will always be displayed as
* - 2021-08-06 02:29:00
* regardless timezoneOffset in JavaScript realm
*
* The implementation hack:
* Instead of calling `.getMonth()`/`.getUTCMonth()` respect to `preserveTimezones`,
* we create a different Date to trick strftime, it's both simpler and more performant.
* Given that a template is expected to be parsed fewer times than rendered.
*/
static createDateFixedToTimezone(dateString) {
const m = dateString.match(ISO8601_TIMEZONE_PATTERN);
// representing a UTC timestamp
if (m && m[1] === 'Z') {
return new TimezoneDate(+new Date(dateString), 0);
}
// has a timezone specified
if (m && m[2] && m[3] && m[4]) {
const [, , sign, hours, minutes] = m;
const offset = (sign === '+' ? -1 : 1) * (parseInt(hours, 10) * 60 + parseInt(minutes, 10));
return new TimezoneDate(+new Date(dateString), offset);
}
return new Date(dateString);
}
static getTimezoneOffset(timezoneName, date = new Date()) {
const localDateString = date.toLocaleString('en-US', { timeZone: timezoneName });
const utcDateString = date.toLocaleString('en-US', { timeZone: 'UTC' });
const localDate = new Date(localDateString);
const utcDate = new Date(utcDateString);
return (+utcDate - +localDate) / (60 * 1000);
}
}
class DelimitedToken extends Token {
constructor(kind, [contentBegin, contentEnd], input, begin, end, trimLeft, trimRight, file) {
super(kind, input, begin, end, file);
this.trimLeft = false;
this.trimRight = false;
const tl = input[contentBegin] === '-';
const tr = input[contentEnd - 1] === '-';
let l = tl ? contentBegin + 1 : contentBegin;
let r = tr ? contentEnd - 1 : contentEnd;
while (l < r && (TYPES[input.charCodeAt(l)] & BLANK))
l++;
while (r > l && (TYPES[input.charCodeAt(r - 1)] & BLANK))
r--;
this.contentRange = [l, r];
this.trimLeft = tl || trimLeft;
this.trimRight = tr || trimRight;
}
get content() {
return this.input.slice(this.contentRange[0], this.contentRange[1]);
}
}
class TagToken extends DelimitedToken {
constructor(input, begin, end, options, file) {
const { trimTagLeft, trimTagRight, tagDelimiterLeft, tagDelimiterRight } = options;
const [valueBegin, valueEnd] = [begin + tagDelimiterLeft.length, end - tagDelimiterRight.length];
super(TokenKind.Tag, [valueBegin, valueEnd], input, begin, end, trimTagLeft, trimTagRight, file);
this.tokenizer = new Tokenizer(input, options.operators, file, this.contentRange);
this.name = this.tokenizer.readTagName();
this.tokenizer.assert(this.name, `illegal tag syntax, tag name expected`);
this.tokenizer.skipBlank();
}
get args() {
return this.tokenizer.input.slice(this.tokenizer.p, this.contentRange[1]);
}
}
class OutputToken extends DelimitedToken {
constructor(input, begin, end, options, file) {
const { trimOutputLeft, trimOutputRight, outputDelimiterLeft, outputDelimiterRight } = options;
const valueRange = [begin + outputDelimiterLeft.length, end - outputDelimiterRight.length];
super(TokenKind.Output, valueRange, input, begin, end, trimOutputLeft, trimOutputRight, file);
}
}
class HTMLToken extends Token {
constructor(input, begin, end, file) {
super(TokenKind.HTML, input, begin, end, file);
this.input = input;
this.begin = begin;
this.end = end;
this.file = file;
this.trimLeft = 0;
this.trimRight = 0;
}
getContent() {
return this.input.slice(this.begin + this.trimLeft, this.end - this.trimRight);
}
}
class NumberToken extends Token {
constructor(input, begin, end, file) {
super(TokenKind.Number, input, begin, end, file);
this.input = input;
this.begin = begin;
this.end = end;
this.file = file;
this.content = Number(this.getText());
}
}
class IdentifierToken extends Token {
constructor(input, begin, end, file) {
super(TokenKind.Word, input, begin, end, file);
this.input = input;
this.begin = begin;
this.end = end;
this.file = file;
this.content = this.getText();
}
isNumber(allowSign = false) {
const begin = allowSign && TYPES[this.input.charCodeAt(this.begin)] & SIGN
? this.begin + 1
: this.begin;
for (let i = begin; i < this.end; i++) {
if (!(TYPES[this.input.charCodeAt(i)] & NUMBER))
return false;
}
return true;
}
}
class LiteralToken extends Token {
constructor(input, begin, end, file) {
super(TokenKind.Literal, input, begin, end, file);
this.input = input;
this.begin = begin;
this.end = end;
this.file = file;
this.literal = this.getText();
this.content = literalValues[this.literal];
}
}
const operatorPrecedences = {
'==': 2,
'!=': 2,
'>': 2,
'<': 2,
'>=': 2,
'<=': 2,
'contains': 2,
'not': 1,
'and': 0,
'or': 0
};
const operatorTypes = {
'==': 0 /* OperatorType.Binary */,
'!=': 0 /* OperatorType.Binary */,
'>': 0 /* OperatorType.Binary */,
'<': 0 /* OperatorType.Binary */,
'>=': 0 /* OperatorType.Binary */,
'<=': 0 /* OperatorType.Binary */,
'contains': 0 /* OperatorType.Binary */,
'not': 1 /* OperatorType.Unary */,
'and': 0 /* OperatorType.Binary */,
'or': 0 /* OperatorType.Binary */
};
class OperatorToken extends Token {
constructor(input, begin, end, file) {
super(TokenKind.Operator, input, begin, end, file);
this.input = input;
this.begin = begin;
this.end = end;
this.file = file;
this.operator = this.getText();
}
getPrecedence() {
const key = this.getText();
return key in operatorPrecedences ? operatorPrecedences[key] : 1;
}
}
class PropertyAccessToken extends Token {
constructor(variable, props, input, begin, end, file) {
super(TokenKind.PropertyAccess, input, begin, end, file);
this.variable = variable;
this.props = props;
}
}
class FilterToken extends Token {
constructor(name, args, input, begin, end, file) {
super(TokenKind.Filter, input, begin, end, file);
this.name = name;
this.args = args;
}
}
class HashToken extends Token {
constructor(input, begin, end, name, value, file) {
super(TokenKind.Hash, input, begin, end, file);
this.input = input;
this.begin = begin;
this.end = end;
this.name = name;
this.value = value;
this.file = file;
}
}
const rHex = /[\da-fA-F]/;
const rOct = /[0-7]/;
const escapeChar = {
b: '\b',
f: '\f',
n: '\n',
r: '\r',
t: '\t',
v: '\x0B'
};
function hexVal(c) {
const code = c.charCodeAt(0);
if (code >= 97)
return code - 87;
if (code >= 65)
return code - 55;
return code - 48;
}
function parseStringLiteral(str) {
let ret = '';
for (let i = 1; i < str.length - 1; i++) {
if (str[i] !== '\\') {
ret += str[i];
continue;
}
if (escapeChar[str[i + 1]] !== undefined) {
ret += escapeChar[str[++i]];
}
else if (str[i + 1] === 'u') {
let val = 0;
let j = i + 2;
while (j <= i + 5 && rHex.test(str[j])) {
val = val * 16 + hexVal(str[j++]);
}
i = j - 1;
ret += String.fromCharCode(val);
}
else if (!rOct.test(str[i + 1])) {
ret += str[++i];
}
else {
let j = i + 1;
let val = 0;
while (j <= i + 3 && rOct.test(str[j])) {
val = val * 8 + hexVal(str[j++]);
}
i = j - 1;
ret += String.fromCharCode(val);
}
}
return ret;
}
class QuotedToken extends Token {
constructor(input, begin, end, file) {
super(TokenKind.Quoted, input, begin, end, file);
this.input = input;
this.begin = begin;
this.end = end;
this.file = file;
this.content = parseStringLiteral(this.getText());
}
}
class RangeToken extends Token {
constructor(input, begin, end, lhs, rhs, file) {
super(TokenKind.Range, input, begin, end, file);
this.input = input;
this.begin = begin;
this.end = end;
this.lhs = lhs;
this.rhs = rhs;
this.file = file;
}
}
/**
* LiquidTagToken is different from TagToken by not having delimiters `{%` or `%}`
*/
class LiquidTagToken extends DelimitedToken {
constructor(input, begin, end, options, file) {
super(TokenKind.Tag, [begin, end], input, begin, end, false, false, file);
this.tokenizer = new Tokenizer(input, options.operators, file, this.contentRange);
this.name = this.tokenizer.readTagName();
this.tokenizer.assert(this.name, 'illegal liquid tag syntax');
this.tokenizer.skipBlank();
this.args = this.tokenizer.remaining();
}
}
/**
* value expression with optional filters
* e.g.
* {% assign foo="bar" | append: "coo" %}
*/
class FilteredValueToken extends Token {
constructor(initial, filters, input, begin, end, file) {
super(TokenKind.FilteredValue, input, begin, end, file);
this.initial = initial;
this.filters = filters;
this.input = input;
this.begin = begin;
this.end = end;
this.file = file;
}
}
class SimpleEmitter {
constructor() {
this.buffer = '';
}
write(html) {
this.buffer += stringify(html);
}
}
class StreamedEmitter {
constructor() {
this.buffer = '';
this.stream = null;
throw new Error('streaming not supported in browser');
}
}
class KeepingTypeEmitter {
constructor() {
this.buffer = '';
}
write(html) {
html = toValue(html);
// This will only preserve the type if the value is isolated.
// I.E:
// {{ my-port }} -> 42
// {{ my-host }}:{{ my-port }} -> 'host:42'
if (typeof html !== 'string' && this.buffer === '') {
this.buffer = html;
}
else {
this.buffer = stringify(this.buffer) + stringify(html);
}
}
}
class Render {
renderTemplatesToNodeStream(templates, ctx) {
const emitter = new StreamedEmitter();
Promise.resolve().then(() => toPromise(this.renderTemplates(templates, ctx, emitter)))
.then(() => emitter.end(), err => emitter.error(err));
return emitter.stream;
}
*renderTemplates(templates, ctx, emitter) {
if (!emitter) {
emitter = ctx.opts.keepOutputType ? new KeepingTypeEmitter() : new SimpleEmitter();
}
const errors = [];
for (const tpl of templates) {
try {
// if tpl.render supports emitter, it'll return empty `html`
const html = yield tpl.render(ctx, emitter);
// if not, it'll return an `html`, write to the emitter for it
html && emitter.write(html);
if (emitter['break'] || emitter['continue'])
break;
}
catch (e) {
const err = LiquidError.is(e) ? e : new RenderError(e, tpl);
if (ctx.opts.catchAllErrors)
errors.push(err);
else
throw err;
}
}
if (errors.length) {
throw new LiquidErrors(errors);
}
return emitter.buffer;
}
}
class Expression {
constructor(tokens) {
this.postfix = [...toPostfix(tokens)];
}
*evaluate(ctx, lenient) {
assert(ctx, 'unable to evaluate: context not defined');
const operands = [];
for (const token of this.postfix) {
if (isOperatorToken(token)) {
const r = operands.pop();
let result;
if (operatorTypes[token.operator] === 1 /* OperatorType.Unary */) {
result = yield ctx.opts.operators[token.operator](r, ctx);
}
else {
const l = operands.pop();
result = yield ctx.opts.operators[token.operator](l, r, ctx);
}
operands.push(result);
}
else {
operands.push(yield evalToken(token, ctx, lenient));
}
}
return operands[0];
}
valid() {
return !!this.postfix.length;
}
}
function* evalToken(token, ctx, lenient = false) {
if (!token)
return;
if ('content' in token)
return token.content;
if (isPropertyAccessToken(token))
return yield evalPropertyAccessToken(token, ctx, lenient);
if (isRangeToken(token))
return yield evalRangeToken(token, ctx);
}
function* evalPropertyAccessToken(token, ctx, lenient) {
const props = [];
for (const prop of token.props) {
props.push((yield evalToken(prop, ctx, false)));
}
try {
if (token.variable) {
const variable = yield evalToken(token.variable, ctx, lenient);
return yield ctx._getFromScope(variable, props);
}
else {
return yield ctx._get(props);
}
}
catch (e) {
if (lenient && e.name === 'InternalUndefinedVariableError')
return null;
throw (new UndefinedVariableError(e, token));
}
}
function evalQuotedToken(token) {
return token.content;
}
function* evalRangeToken(token, ctx) {
const low = yield evalToken(token.lhs, ctx);
const high = yield evalToken(token.rhs, ctx);
return range(+low, +high + 1);
}
function* toPostfix(tokens) {
const ops = [];
for (const token of tokens) {
if (isOperatorToken(token)) {
while (ops.length && ops[ops.length - 1].getPrecedence() > token.getPrecedence()) {
yield ops.pop();
}
ops.push(token);
}
else
yield token;
}
while (ops.length) {
yield ops.pop();
}
}
function isTruthy(val, ctx) {
return !isFalsy(val, ctx);
}
function isFalsy(val, ctx) {
val = toValue(val);
if (ctx.opts.jsTruthy) {
return !val;
}
else {
return val === false || undefined === val || val === null;
}
}
const defaultOperators = {
'==': equals,
'!=': (l, r) => !equals(l, r),
'>': (l, r) => {
if (isComparable(l))
return l.gt(r);
if (isComparable(r))
return r.lt(l);
return toValue(l) > toValue(r);
},
'<': (l, r) => {
if (isComparable(l))
return l.lt(r);
if (isComparable(r))
return r.gt(l);
return toValue(l) < toValue(r);
},
'>=': (l, r) => {
if (isComparable(l))
return l.geq(r);
if (isComparable(r))
return r.leq(l);
return toValue(l) >= toValue(r);
},
'<=': (l, r) => {
if (isComparable(l))
return l.leq(r);
if (isComparable(r))
return r.geq(l);
return toValue(l) <= toValue(r);
},
'contains': (l, r) => {
l = toValue(l);
if (isArray(l))
return l.some((i) => equals(i, r));
if (isFunction(l === null || l === void 0 ? void 0 : l.indexOf))
return l.indexOf(toValue(r)) > -1;
return false;
},
'not': (v, ctx) => isFalsy(toValue(v), ctx),
'and': (l, r, ctx) => isTruthy(toValue(l), ctx) && isTruthy(toValue(r), ctx),
'or': (l, r, ctx) => isTruthy(toValue(l), ctx) || isTruthy(toValue(r), ctx)
};
function equals(lhs, rhs) {
if (isComparable(lhs))
return lhs.equals(rhs);
if (isComparable(rhs))
return rhs.equals(lhs);
lhs = toValue(lhs);
rhs = toValue(rhs);
if (isArray(lhs)) {
return isArray(rhs) && arrayEquals(lhs, rhs);
}
return lhs === rhs;
}
function arrayEquals(lhs, rhs) {
if (lhs.length !== rhs.length)
return false;
return !lhs.some((value, i) => !equals(value, rhs[i]));
}
class Node {
constructor(key, value, next, prev) {
this.key = key;
this.value = value;
this.next = next;
this.prev = prev;
}
}
class LRU {
constructor(limit, size = 0) {
this.limit = limit;
this.size = size;
this.cache = {};
this.head = new Node('HEAD', null, null, null);
this.tail = new Node('TAIL', null, null, null);
this.head.next = this.tail;
this.tail.prev = this.head;
}
write(key, value) {
if (this.cache[key]) {
this.cache[key].value = value;
}
else {
const node = new Node(key, value, this.head.next, this.head);
this.head.next.prev = node;
this.head.next = node;
this.cache[key] = node;
this.size++;
this.ensureLimit();
}
}
read(key) {
if (!this.cache[key])
return;
const { value } = this.cache[key];
this.remove(key);
this.write(key, value);
return value;
}
remove(key) {
const node = this.cache[key];
node.prev.next = node.next;
node.next.prev = node.prev;
delete this.cache[key];
this.size--;
}
clear() {
this.head.next = this.tail;
this.tail.prev = this.head;
this.size = 0;
this.cache = {};
}
ensureLimit() {
if (this.size > this.limit)
this.remove(this.tail.prev.key);
}
}
function domResolve(root, path) {
const base = document.createElement('base');
base.href = root;
const head = document.getElementsByTagName('head')[0];
head.insertBefore(base, head.firstChild);
const a = document.createElement('a');
a.href = path;
const resolved = a.href;
head.removeChild(base);
return resolved;
}
function resolve(root, filepath, ext) {
if (root.length && last(root) !== '/')
root += '/';
const url = domResolve(root, filepath);
return url.replace(/^(\w+:\/\/[^/]+)(\/[^?]+)/, (str, origin, path) => {
const last = path.split('/').pop();
if (/\.\w+$/.test(last))
return str;
return origin + path + ext;
});
}
function readFile(url) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.responseText);
}
else {
reject(new Error(xhr.statusText));
}
};
xhr.onerror = () => {
reject(new Error('An error occurred whilst receiving the response.'));
};
xhr.open('GET', url);
xhr.send();
});
});
}
function readFileSync(url) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
xhr.send();
if (xhr.status < 200 || xhr.status >= 300) {
throw new Error(xhr.statusText);
}
return xhr.responseText;
}
function exists(filepath) {
return __awaiter(this, void 0, void 0, function* () {
return true;
});
}
function existsSync(filepath) {
return true;
}
function dirname(filepath) {
return domResolve(filepath, '.');
}
const sep = '/';
var fs = /*#__PURE__*/Object.freeze({
__proto__: null,
resolve: resolve,
readFile: readFile,
readFileSync: readFileSync,
exists: exists,
existsSync: existsSync,
dirname: dirname,
sep: sep
});
function defaultFilter(value, defaultValue, ...args) {
value = toValue(value);
if (isArray(value) || isString(value))
return value.length ? value : defaultValue;
if (value === false && (new Map(args)).get('allow_false'))
return false;
return isFalsy(value, this.context) ? defaultValue : value;
}
function json(value, space = 0) {
return JSON.stringify(value, null, space);
}
function inspect(value, space = 0) {
const ancestors = [];
return JSON.stringify(value, function (_key, value) {
if (typeof value !== 'object' || value === null)
return value;
// `this` is the object that value is contained in, i.e., its direct parent.
while (ancestors.length > 0 && ancestors[ancestors.length - 1] !== this)
ancestors.pop();
if (ancestors.includes(value))
return '[Circular]';
ancestors.push(value);
return value;
}, space);
}
function to_integer(value) {
return Number(value);
}
const raw = {
raw: true,
handler: identify
};
var misc = {
default: defaultFilter,
raw,
jsonify: json,
to_integer,
json,
inspect
};
const escapeMap = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
const unescapeMap = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
''': "'"
};
function escape(str) {
return stringify(str).replace(/&|<|>|"|'/g, m => escapeMap[m]);
}
function xml_escape(str) {
return escape(str);
}
function unescape(str) {
return stringify(str).replace(/&(amp|lt|gt|#34|#39);/g, m => unescapeMap[m]);
}
function escape_once(str) {
return escape(unescape(stringify(str)));
}
function newline_to_br(v) {
return stringify(v).replace(/\r?\n/gm, '
\n');
}
function strip_html(v) {
return stringify(v).replace(/