/*
* liquidjs@10.14.0, https://github.com/harttle/liquidjs
* (c) 2016-2024 harttle
* Released under the MIT License.
*/
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var stream = require('stream');
var path = require('path');
var fs$1 = require('fs');
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 promisify(fn) {
return function (...args) {
return new Promise((resolve, reject) => {
fn(...args, (err, result) => {
err ? reject(err) : resolve(result);
});
});
};
}
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?.[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;
}
// convert an async iterator to a Promise
async function toPromise(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';
try {
if (isIterator(value))
value = toPromise(value);
if (isPromise(value))
value = await 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?.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(exports.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(exports.TokenKind.Output, valueRange, input, begin, end, trimOutputLeft, trimOutputRight, file);
}
}
class HTMLToken extends Token {
constructor(input, begin, end, file) {
super(exports.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(exports.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(exports.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(exports.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(exports.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(exports.TokenKind.PropertyAccess, input, begin, end, file);
this.variable = variable;
this.props = props;
}
}
class FilterToken extends Token {
constructor(name, args, input, begin, end, file) {
super(exports.TokenKind.Filter, input, begin, end, file);
this.name = name;
this.args = args;
}
}
class HashToken extends Token {
constructor(input, begin, end, name, value, file) {
super(exports.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(exports.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(exports.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(exports.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(exports.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 = new stream.PassThrough();
}
write(html) {
this.stream.write(stringify(html));
}
error(err) {
this.stream.emit('error', err);
}
end() {
this.stream.end();
}
}
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?.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);
}
}
const requireResolve = require.resolve;
const statAsync = promisify(fs$1.stat);
const readFileAsync = promisify(fs$1.readFile);
async function exists(filepath) {
try {
await statAsync(filepath);
return true;
}
catch (err) {
return false;
}
}
function readFile(filepath) {
return readFileAsync(filepath, 'utf8');
}
function existsSync(filepath) {
try {
fs$1.statSync(filepath);
return true;
}
catch (err) {
return false;
}
}
function readFileSync(filepath) {
return fs$1.readFileSync(filepath, 'utf8');
}
function resolve(root, file, ext) {
if (!path.extname(file))
file += ext;
return path.resolve(root, file);
}
function fallback(file) {
try {
return requireResolve(file);
}
catch (e) { }
}
function dirname(filepath) {
return path.dirname(filepath);
}
function contains(root, file) {
root = path.resolve(root);
root = root.endsWith(path.sep) ? root : root + path.sep;
return file.startsWith(root);
}
var fs = /*#__PURE__*/Object.freeze({
__proto__: null,
exists: exists,
readFile: readFile,
existsSync: existsSync,
readFileSync: readFileSync,
resolve: resolve,
fallback: fallback,
dirname: dirname,
contains: contains,
sep: path.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(/