directus-extension-texttoanything 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/dist/api.js +1877 -587
  2. package/dist/app.js +5194 -69
  3. package/package.json +6 -1
  4. package/readme.md +7 -1
package/dist/api.js CHANGED
@@ -3,6 +3,7 @@
3
3
  var stream = require('stream');
4
4
  var require$$1$1 = require('path');
5
5
  var require$$6 = require('fs');
6
+ var module$1 = require('module');
6
7
  var require$$1 = require('util');
7
8
  var require$$3 = require('http');
8
9
  var require$$4 = require('https');
@@ -107,7 +108,7 @@ const {isArray: isArray$1} = Array;
107
108
  *
108
109
  * @returns {boolean} True if the value is undefined, otherwise false
109
110
  */
110
- const isUndefined = typeOfTest('undefined');
111
+ const isUndefined$1 = typeOfTest('undefined');
111
112
 
112
113
  /**
113
114
  * Determine if a value is a Buffer
@@ -117,7 +118,7 @@ const isUndefined = typeOfTest('undefined');
117
118
  * @returns {boolean} True if value is a Buffer, otherwise false
118
119
  */
119
120
  function isBuffer$1(val) {
120
- return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor)
121
+ return val !== null && !isUndefined$1(val) && val.constructor !== null && !isUndefined$1(val.constructor)
121
122
  && isFunction$2(val.constructor.isBuffer) && val.constructor.isBuffer(val);
122
123
  }
123
124
 
@@ -650,7 +651,7 @@ var utils = {
650
651
  isBoolean,
651
652
  isObject: isObject$1,
652
653
  isPlainObject,
653
- isUndefined,
654
+ isUndefined: isUndefined$1,
654
655
  isDate,
655
656
  isFile,
656
657
  isBlob,
@@ -17544,8 +17545,8 @@ axios.formToJSON = thing => {
17544
17545
  };
17545
17546
 
17546
17547
  /*
17547
- * liquidjs@10.7.1, https://github.com/harttle/liquidjs
17548
- * (c) 2016-2023 harttle
17548
+ * liquidjs@10.20.1, https://github.com/harttle/liquidjs
17549
+ * (c) 2016-2025 harttle
17549
17550
  * Released under the MIT License.
17550
17551
  */
17551
17552
 
@@ -17621,9 +17622,33 @@ function stringify(value) {
17621
17622
  return value.map(x => stringify(x)).join('');
17622
17623
  return String(value);
17623
17624
  }
17625
+ function toEnumerable(val) {
17626
+ val = toValue(val);
17627
+ if (isArray(val))
17628
+ return val;
17629
+ if (isString(val) && val.length > 0)
17630
+ return [val];
17631
+ if (isIterable(val))
17632
+ return Array.from(val);
17633
+ if (isObject(val))
17634
+ return Object.keys(val).map((key) => [key, val[key]]);
17635
+ return [];
17636
+ }
17637
+ function toArray(val) {
17638
+ val = toValue(val);
17639
+ if (isNil(val))
17640
+ return [];
17641
+ if (isArray(val))
17642
+ return val;
17643
+ return [val];
17644
+ }
17624
17645
  function toValue(value) {
17625
17646
  return (value instanceof Drop && isFunction(value.valueOf)) ? value.valueOf() : value;
17626
17647
  }
17648
+ function toNumber(value) {
17649
+ value = Number(value);
17650
+ return isNaN(value) ? 0 : value;
17651
+ }
17627
17652
  function isNumber(value) {
17628
17653
  return typeof value === 'number';
17629
17654
  }
@@ -17635,10 +17660,16 @@ function toLiquid(value) {
17635
17660
  function isNil(value) {
17636
17661
  return value == null;
17637
17662
  }
17663
+ function isUndefined(value) {
17664
+ return value === undefined;
17665
+ }
17638
17666
  function isArray(value) {
17639
17667
  // be compatible with IE 8
17640
17668
  return toString$1.call(value) === '[object Array]';
17641
17669
  }
17670
+ function isArrayLike(value) {
17671
+ return value && isNumber(value.length);
17672
+ }
17642
17673
  function isIterable(value) {
17643
17674
  return isObject(value) && Symbol.iterator in value;
17644
17675
  }
@@ -17720,30 +17751,55 @@ function caseInsensitiveCompare(a, b) {
17720
17751
  return 0;
17721
17752
  }
17722
17753
  function argumentsToValue(fn) {
17723
- return (...args) => fn(...args.map(toValue));
17754
+ return function (...args) { return fn.call(this, ...args.map(toValue)); };
17724
17755
  }
17725
17756
  function escapeRegExp(text) {
17726
17757
  return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
17758
+ }
17759
+ /** Return an array containing unique elements from _array_. Works with nested arrays and objects. */
17760
+ function* strictUniq(array) {
17761
+ const seen = new Set();
17762
+ for (const element of array) {
17763
+ const key = JSON.stringify(element);
17764
+ if (!seen.has(key)) {
17765
+ seen.add(key);
17766
+ yield element;
17767
+ }
17768
+ }
17727
17769
  }
17728
17770
 
17771
+ /**
17772
+ * targeting ES5, extends Error won't create a proper prototype chain, need a trait to keep track of classes
17773
+ */
17774
+ const TRAIT = '__liquidClass__';
17729
17775
  class LiquidError extends Error {
17730
17776
  constructor(err, token) {
17731
- super(err.message);
17732
- this.originalError = err;
17733
- this.token = token;
17777
+ /**
17778
+ * note: for ES5 targeting, `this` will be replaced by return value of Error(),
17779
+ * thus everything on `this` will be lost, avoid calling `LiquidError` methods here
17780
+ */
17781
+ super(typeof err === 'string' ? err : err.message);
17734
17782
  this.context = '';
17783
+ if (typeof err !== 'string')
17784
+ Object.defineProperty(this, 'originalError', { value: err, enumerable: false });
17785
+ Object.defineProperty(this, 'token', { value: token, enumerable: false });
17786
+ Object.defineProperty(this, TRAIT, { value: 'LiquidError', enumerable: false });
17735
17787
  }
17736
17788
  update() {
17737
- const err = this.originalError;
17738
- this.context = mkContext(this.token);
17739
- this.message = mkMessage(err.message, this.token);
17789
+ Object.defineProperty(this, 'context', { value: mkContext(this.token), enumerable: false });
17790
+ this.message = mkMessage(this.message, this.token);
17740
17791
  this.stack = this.message + '\n' + this.context +
17741
- '\n' + this.stack + '\nFrom ' + err.stack;
17792
+ '\n' + this.stack;
17793
+ if (this.originalError)
17794
+ this.stack += '\nFrom ' + this.originalError.stack;
17795
+ }
17796
+ static is(obj) {
17797
+ return (obj === null || obj === void 0 ? void 0 : obj[TRAIT]) === 'LiquidError';
17742
17798
  }
17743
17799
  }
17744
17800
  class TokenizationError extends LiquidError {
17745
17801
  constructor(message, token) {
17746
- super(new Error(message), token);
17802
+ super(message, token);
17747
17803
  this.name = 'TokenizationError';
17748
17804
  super.update();
17749
17805
  }
@@ -17767,6 +17823,19 @@ class RenderError extends LiquidError {
17767
17823
  return obj.name === 'RenderError';
17768
17824
  }
17769
17825
  }
17826
+ class LiquidErrors extends LiquidError {
17827
+ constructor(errors) {
17828
+ super(errors[0], errors[0].token);
17829
+ this.errors = errors;
17830
+ this.name = 'LiquidErrors';
17831
+ const s = errors.length > 1 ? 's' : '';
17832
+ this.message = `${errors.length} error${s} found`;
17833
+ super.update();
17834
+ }
17835
+ static is(obj) {
17836
+ return obj.name === 'LiquidErrors';
17837
+ }
17838
+ }
17770
17839
  class UndefinedVariableError extends LiquidError {
17771
17840
  constructor(err, token) {
17772
17841
  super(err, token);
@@ -17792,16 +17861,21 @@ class AssertionError extends Error {
17792
17861
  }
17793
17862
  }
17794
17863
  function mkContext(token) {
17795
- const [line] = token.getPosition();
17864
+ const [line, col] = token.getPosition();
17796
17865
  const lines = token.input.split('\n');
17797
17866
  const begin = Math.max(line - 2, 1);
17798
17867
  const end = Math.min(line + 3, lines.length);
17799
17868
  const context = range(begin, end + 1)
17800
17869
  .map(lineNumber => {
17801
- const indicator = (lineNumber === line) ? '>> ' : ' ';
17870
+ const rowIndicator = (lineNumber === line) ? '>> ' : ' ';
17802
17871
  const num = padStart(String(lineNumber), String(end).length);
17803
- const text = lines[lineNumber - 1];
17804
- return `${indicator}${num}| ${text}`;
17872
+ let text = `${rowIndicator}${num}| `;
17873
+ const colIndicator = lineNumber === line
17874
+ ? '\n' + padStart('^', col + text.length)
17875
+ : '';
17876
+ text += lines[lineNumber - 1];
17877
+ text += colIndicator;
17878
+ return text;
17805
17879
  })
17806
17880
  .join('\n');
17807
17881
  return context;
@@ -17819,13 +17893,19 @@ function mkMessage(msg, token) {
17819
17893
  // This file is generated by bin/character-gen.js
17820
17894
  // bitmask character types to boost performance
17821
17895
  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];
17822
- const IDENTIFIER = 1;
17896
+ const WORD = 1;
17823
17897
  const BLANK = 4;
17824
17898
  const QUOTE = 8;
17825
17899
  const INLINE_BLANK = 16;
17826
17900
  const NUMBER = 32;
17827
17901
  const SIGN = 64;
17828
- 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;
17902
+ const PUNCTUATION = 128;
17903
+ function isWord(char) {
17904
+ const code = char.charCodeAt(0);
17905
+ return code >= 128 ? !TYPES[code] : !!(TYPES[code] & WORD);
17906
+ }
17907
+ 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;
17908
+ TYPES[8220] = TYPES[8221] = PUNCTUATION;
17829
17909
 
17830
17910
  function assert(predicate, message) {
17831
17911
  if (!predicate) {
@@ -17834,6 +17914,9 @@ function assert(predicate, message) {
17834
17914
  : (message || `expect ${predicate} to be true`);
17835
17915
  throw new AssertionError(msg);
17836
17916
  }
17917
+ }
17918
+ function assertEmpty(predicate, message = `unexpected ${JSON.stringify(predicate)}`) {
17919
+ assert(!predicate, message);
17837
17920
  }
17838
17921
 
17839
17922
  class NullDrop extends Drop {
@@ -17883,6 +17966,9 @@ class EmptyDrop extends Drop {
17883
17966
  valueOf() {
17884
17967
  return '';
17885
17968
  }
17969
+ static is(value) {
17970
+ return value instanceof EmptyDrop;
17971
+ }
17886
17972
  }
17887
17973
 
17888
17974
  class BlankDrop extends EmptyDrop {
@@ -17895,6 +17981,9 @@ class BlankDrop extends EmptyDrop {
17895
17981
  return /^\s*$/.test(value);
17896
17982
  return super.equals(value);
17897
17983
  }
17984
+ static is(value) {
17985
+ return value instanceof BlankDrop;
17986
+ }
17898
17987
  }
17899
17988
 
17900
17989
  class ForloopDrop extends Drop {
@@ -17947,7 +18036,12 @@ class BlockDrop extends Drop {
17947
18036
  }
17948
18037
 
17949
18038
  function isComparable(arg) {
17950
- return arg && isFunction(arg.equals);
18039
+ return (arg &&
18040
+ isFunction(arg.equals) &&
18041
+ isFunction(arg.gt) &&
18042
+ isFunction(arg.geq) &&
18043
+ isFunction(arg.lt) &&
18044
+ isFunction(arg.leq));
17951
18045
  }
17952
18046
 
17953
18047
  const nil = new NullDrop();
@@ -17960,19 +18054,19 @@ const literalValues = {
17960
18054
  'blank': new BlankDrop()
17961
18055
  };
17962
18056
 
17963
- function createTrie(operators) {
18057
+ function createTrie(input) {
17964
18058
  const trie = {};
17965
- for (const [name, handler] of Object.entries(operators)) {
18059
+ for (const [name, data] of Object.entries(input)) {
17966
18060
  let node = trie;
17967
18061
  for (let i = 0; i < name.length; i++) {
17968
18062
  const c = name[i];
17969
18063
  node[c] = node[c] || {};
17970
- if (i === name.length - 1 && (TYPES[name.charCodeAt(i)] & IDENTIFIER)) {
18064
+ if (i === name.length - 1 && isWord(name[i])) {
17971
18065
  node[c].needBoundary = true;
17972
18066
  }
17973
18067
  node = node[c];
17974
18068
  }
17975
- node.handler = handler;
18069
+ node.data = data;
17976
18070
  node.end = true;
17977
18071
  }
17978
18072
  return trie;
@@ -18066,45 +18160,7 @@ function toValueSync(val) {
18066
18160
  return value;
18067
18161
  }
18068
18162
 
18069
- function toEnumerable(val) {
18070
- val = toValue(val);
18071
- if (isArray(val))
18072
- return val;
18073
- if (isString(val) && val.length > 0)
18074
- return [val];
18075
- if (isIterable(val))
18076
- return Array.from(val);
18077
- if (isObject(val))
18078
- return Object.keys(val).map((key) => [key, val[key]]);
18079
- return [];
18080
- }
18081
- function toArray(val) {
18082
- if (isNil(val))
18083
- return [];
18084
- if (isArray(val))
18085
- return val;
18086
- return [val];
18087
- }
18088
-
18089
18163
  const rFormat = /%([-_0^#:]+)?(\d+)?([EO])?(.)/;
18090
- const monthNames = [
18091
- 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
18092
- 'September', 'October', 'November', 'December'
18093
- ];
18094
- const dayNames = [
18095
- 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'
18096
- ];
18097
- const monthNamesShort = monthNames.map(abbr);
18098
- const dayNamesShort = dayNames.map(abbr);
18099
- const suffixes = {
18100
- 1: 'st',
18101
- 2: 'nd',
18102
- 3: 'rd',
18103
- 'default': 'th'
18104
- };
18105
- function abbr(str) {
18106
- return str.slice(0, 3);
18107
- }
18108
18164
  // prototype extensions
18109
18165
  function daysInMonth(d) {
18110
18166
  const feb = isLeapYear(d) ? 29 : 28;
@@ -18129,10 +18185,16 @@ function isLeapYear(d) {
18129
18185
  const year = d.getFullYear();
18130
18186
  return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year)));
18131
18187
  }
18132
- function getSuffix(d) {
18133
- const str = d.getDate().toString();
18134
- const index = parseInt(str.slice(-1));
18135
- return suffixes[index] || suffixes['default'];
18188
+ function ordinal(d) {
18189
+ const date = d.getDate();
18190
+ if ([11, 12, 13].includes(date))
18191
+ return 'th';
18192
+ switch (date % 10) {
18193
+ case 1: return 'st';
18194
+ case 2: return 'nd';
18195
+ case 3: return 'rd';
18196
+ default: return 'th';
18197
+ }
18136
18198
  }
18137
18199
  function century(d) {
18138
18200
  return parseInt(d.getFullYear().toString().substring(0, 2), 10);
@@ -18153,24 +18215,21 @@ const padWidths = {
18153
18215
  U: 2,
18154
18216
  W: 2
18155
18217
  };
18156
- // default to '0'
18157
- const padChars = {
18158
- a: ' ',
18159
- A: ' ',
18160
- b: ' ',
18161
- B: ' ',
18162
- c: ' ',
18163
- e: ' ',
18164
- k: ' ',
18165
- l: ' ',
18166
- p: ' ',
18167
- P: ' '
18168
- };
18218
+ const padSpaceChars = new Set('aAbBceklpP');
18219
+ function getTimezoneOffset(d, opts) {
18220
+ const nOffset = Math.abs(d.getTimezoneOffset());
18221
+ const h = Math.floor(nOffset / 60);
18222
+ const m = nOffset % 60;
18223
+ return (d.getTimezoneOffset() > 0 ? '-' : '+') +
18224
+ padStart(h, 2, '0') +
18225
+ (opts.flags[':'] ? ':' : '') +
18226
+ padStart(m, 2, '0');
18227
+ }
18169
18228
  const formatCodes = {
18170
- a: (d) => dayNamesShort[d.getDay()],
18171
- A: (d) => dayNames[d.getDay()],
18172
- b: (d) => monthNamesShort[d.getMonth()],
18173
- B: (d) => monthNames[d.getMonth()],
18229
+ a: (d) => d.getShortWeekdayName(),
18230
+ A: (d) => d.getLongWeekdayName(),
18231
+ b: (d) => d.getShortMonthName(),
18232
+ B: (d) => d.getLongMonthName(),
18174
18233
  c: (d) => d.toLocaleString(),
18175
18234
  C: (d) => century(d),
18176
18235
  d: (d) => d.getDate(),
@@ -18190,7 +18249,7 @@ const formatCodes = {
18190
18249
  },
18191
18250
  p: (d) => (d.getHours() < 12 ? 'AM' : 'PM'),
18192
18251
  P: (d) => (d.getHours() < 12 ? 'am' : 'pm'),
18193
- q: (d) => getSuffix(d),
18252
+ q: (d) => ordinal(d),
18194
18253
  s: (d) => Math.round(d.getTime() / 1000),
18195
18254
  S: (d) => d.getSeconds(),
18196
18255
  u: (d) => d.getDay() || 7,
@@ -18201,15 +18260,8 @@ const formatCodes = {
18201
18260
  X: (d) => d.toLocaleTimeString(),
18202
18261
  y: (d) => d.getFullYear().toString().slice(2, 4),
18203
18262
  Y: (d) => d.getFullYear(),
18204
- z: (d, opts) => {
18205
- const nOffset = Math.abs(d.getTimezoneOffset());
18206
- const h = Math.floor(nOffset / 60);
18207
- const m = nOffset % 60;
18208
- return (d.getTimezoneOffset() > 0 ? '-' : '+') +
18209
- padStart(h, 2, '0') +
18210
- (opts.flags[':'] ? ':' : '') +
18211
- padStart(m, 2, '0');
18212
- },
18263
+ z: getTimezoneOffset,
18264
+ Z: (d, opts) => d.getTimeZoneName() || getTimezoneOffset(d, opts),
18213
18265
  't': () => '\t',
18214
18266
  'n': () => '\n',
18215
18267
  '%': () => '%'
@@ -18235,7 +18287,7 @@ function format(d, match) {
18235
18287
  for (const flag of flagStr)
18236
18288
  flags[flag] = true;
18237
18289
  let ret = String(convert(d, { flags, width, modifier }));
18238
- let padChar = padChars[conversion] || '0';
18290
+ let padChar = padSpaceChars.has(conversion) ? ' ' : '0';
18239
18291
  let padWidth = width || padWidths[conversion] || 0;
18240
18292
  if (flags['^'])
18241
18293
  ret = ret.toUpperCase();
@@ -18250,9 +18302,26 @@ function format(d, match) {
18250
18302
  return padStart(ret, padWidth, padChar);
18251
18303
  }
18252
18304
 
18305
+ function getDateTimeFormat() {
18306
+ return (typeof Intl !== 'undefined' ? Intl.DateTimeFormat : undefined);
18307
+ }
18308
+
18253
18309
  // one minute in milliseconds
18254
18310
  const OneMinute = 60000;
18255
- const ISO8601_TIMEZONE_PATTERN = /([zZ]|([+-])(\d{2}):(\d{2}))$/;
18311
+ /**
18312
+ * Need support both ISO8601 and RFC2822 as in major browsers & NodeJS
18313
+ * RFC2822: https://datatracker.ietf.org/doc/html/rfc2822#section-3.3
18314
+ */
18315
+ const TIMEZONE_PATTERN = /([zZ]|([+-])(\d{2}):?(\d{2}))$/;
18316
+ const monthNames = [
18317
+ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
18318
+ 'September', 'October', 'November', 'December'
18319
+ ];
18320
+ const monthNamesShort = monthNames.map(name => name.slice(0, 3));
18321
+ const dayNames = [
18322
+ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'
18323
+ ];
18324
+ const dayNamesShort = dayNames.map(name => name.slice(0, 3));
18256
18325
  /**
18257
18326
  * A date implementation with timezone info, just like Ruby date
18258
18327
  *
@@ -18260,12 +18329,17 @@ const ISO8601_TIMEZONE_PATTERN = /([zZ]|([+-])(\d{2}):(\d{2}))$/;
18260
18329
  * - create a Date offset by it's timezone difference, avoiding overriding a bunch of methods
18261
18330
  * - rewrite getTimezoneOffset() to trick strftime
18262
18331
  */
18263
- class TimezoneDate {
18264
- constructor(init, timezoneOffset) {
18265
- this.date = init instanceof TimezoneDate
18266
- ? init.date
18267
- : new Date(init);
18268
- this.timezoneOffset = timezoneOffset;
18332
+ class LiquidDate {
18333
+ constructor(init, locale, timezone) {
18334
+ this.locale = locale;
18335
+ this.DateTimeFormat = getDateTimeFormat();
18336
+ this.date = new Date(init);
18337
+ this.timezoneFixed = timezone !== undefined;
18338
+ if (timezone === undefined) {
18339
+ timezone = this.date.getTimezoneOffset();
18340
+ }
18341
+ this.timezoneOffset = isString(timezone) ? LiquidDate.getTimezoneOffset(timezone, this.date) : timezone;
18342
+ this.timezoneName = isString(timezone) ? timezone : '';
18269
18343
  const diff = (this.date.getTimezoneOffset() - this.timezoneOffset) * OneMinute;
18270
18344
  const time = this.date.getTime() + diff;
18271
18345
  this.displayDate = new Date(time);
@@ -18312,6 +18386,35 @@ class TimezoneDate {
18312
18386
  getTimezoneOffset() {
18313
18387
  return this.timezoneOffset;
18314
18388
  }
18389
+ getTimeZoneName() {
18390
+ if (this.timezoneFixed)
18391
+ return this.timezoneName;
18392
+ if (!this.DateTimeFormat)
18393
+ return;
18394
+ return this.DateTimeFormat().resolvedOptions().timeZone;
18395
+ }
18396
+ getLongMonthName() {
18397
+ var _a;
18398
+ return (_a = this.format({ month: 'long' })) !== null && _a !== void 0 ? _a : monthNames[this.getMonth()];
18399
+ }
18400
+ getShortMonthName() {
18401
+ var _a;
18402
+ return (_a = this.format({ month: 'short' })) !== null && _a !== void 0 ? _a : monthNamesShort[this.getMonth()];
18403
+ }
18404
+ getLongWeekdayName() {
18405
+ var _a;
18406
+ return (_a = this.format({ weekday: 'long' })) !== null && _a !== void 0 ? _a : dayNames[this.displayDate.getDay()];
18407
+ }
18408
+ getShortWeekdayName() {
18409
+ var _a;
18410
+ return (_a = this.format({ weekday: 'short' })) !== null && _a !== void 0 ? _a : dayNamesShort[this.displayDate.getDay()];
18411
+ }
18412
+ valid() {
18413
+ return !isNaN(this.getTime());
18414
+ }
18415
+ format(options) {
18416
+ return this.DateTimeFormat && this.DateTimeFormat(this.locale, options).format(this.displayDate);
18417
+ }
18315
18418
  /**
18316
18419
  * Create a Date object fixed to it's declared Timezone. Both
18317
18420
  * - 2021-08-06T02:29:00.000Z and
@@ -18325,57 +18428,88 @@ class TimezoneDate {
18325
18428
  * we create a different Date to trick strftime, it's both simpler and more performant.
18326
18429
  * Given that a template is expected to be parsed fewer times than rendered.
18327
18430
  */
18328
- static createDateFixedToTimezone(dateString) {
18329
- const m = dateString.match(ISO8601_TIMEZONE_PATTERN);
18431
+ static createDateFixedToTimezone(dateString, locale) {
18432
+ const m = dateString.match(TIMEZONE_PATTERN);
18330
18433
  // representing a UTC timestamp
18331
18434
  if (m && m[1] === 'Z') {
18332
- return new TimezoneDate(+new Date(dateString), 0);
18435
+ return new LiquidDate(+new Date(dateString), locale, 0);
18333
18436
  }
18334
18437
  // has a timezone specified
18335
18438
  if (m && m[2] && m[3] && m[4]) {
18336
18439
  const [, , sign, hours, minutes] = m;
18337
18440
  const offset = (sign === '+' ? -1 : 1) * (parseInt(hours, 10) * 60 + parseInt(minutes, 10));
18338
- return new TimezoneDate(+new Date(dateString), offset);
18441
+ return new LiquidDate(+new Date(dateString), locale, offset);
18339
18442
  }
18340
- return new Date(dateString);
18443
+ return new LiquidDate(dateString, locale);
18444
+ }
18445
+ static getTimezoneOffset(timezoneName, date) {
18446
+ const localDateString = date.toLocaleString('en-US', { timeZone: timezoneName });
18447
+ const utcDateString = date.toLocaleString('en-US', { timeZone: 'UTC' });
18448
+ const localDate = new Date(localDateString);
18449
+ const utcDate = new Date(utcDateString);
18450
+ return (+utcDate - +localDate) / (60 * 1000);
18451
+ }
18452
+ }
18453
+
18454
+ class Limiter {
18455
+ constructor(resource, limit) {
18456
+ this.base = 0;
18457
+ this.message = `${resource} limit exceeded`;
18458
+ this.limit = limit;
18459
+ }
18460
+ use(count) {
18461
+ count = toNumber(count);
18462
+ assert(this.base + count <= this.limit, this.message);
18463
+ this.base += count;
18464
+ }
18465
+ check(count) {
18466
+ count = toNumber(count);
18467
+ assert(count <= this.limit, this.message);
18341
18468
  }
18342
18469
  }
18343
18470
 
18344
18471
  class DelimitedToken extends Token {
18345
- constructor(kind, content, input, begin, end, trimLeft, trimRight, file) {
18472
+ constructor(kind, [contentBegin, contentEnd], input, begin, end, trimLeft, trimRight, file) {
18346
18473
  super(kind, input, begin, end, file);
18347
18474
  this.trimLeft = false;
18348
18475
  this.trimRight = false;
18349
- this.content = this.getText();
18350
- const tl = content[0] === '-';
18351
- const tr = last(content) === '-';
18352
- this.content = content
18353
- .slice(tl ? 1 : 0, tr ? -1 : content.length)
18354
- .trim();
18476
+ const tl = input[contentBegin] === '-';
18477
+ const tr = input[contentEnd - 1] === '-';
18478
+ let l = tl ? contentBegin + 1 : contentBegin;
18479
+ let r = tr ? contentEnd - 1 : contentEnd;
18480
+ while (l < r && (TYPES[input.charCodeAt(l)] & BLANK))
18481
+ l++;
18482
+ while (r > l && (TYPES[input.charCodeAt(r - 1)] & BLANK))
18483
+ r--;
18484
+ this.contentRange = [l, r];
18355
18485
  this.trimLeft = tl || trimLeft;
18356
18486
  this.trimRight = tr || trimRight;
18357
18487
  }
18488
+ get content() {
18489
+ return this.input.slice(this.contentRange[0], this.contentRange[1]);
18490
+ }
18358
18491
  }
18359
18492
 
18360
18493
  class TagToken extends DelimitedToken {
18361
18494
  constructor(input, begin, end, options, file) {
18362
18495
  const { trimTagLeft, trimTagRight, tagDelimiterLeft, tagDelimiterRight } = options;
18363
- const value = input.slice(begin + tagDelimiterLeft.length, end - tagDelimiterRight.length);
18364
- super(TokenKind.Tag, value, input, begin, end, trimTagLeft, trimTagRight, file);
18365
- const tokenizer = new Tokenizer(this.content, options.operators);
18366
- this.name = tokenizer.readTagName();
18367
- if (!this.name)
18368
- throw new TokenizationError(`illegal tag syntax`, this);
18369
- tokenizer.skipBlank();
18370
- this.args = tokenizer.remaining();
18496
+ const [valueBegin, valueEnd] = [begin + tagDelimiterLeft.length, end - tagDelimiterRight.length];
18497
+ super(TokenKind.Tag, [valueBegin, valueEnd], input, begin, end, trimTagLeft, trimTagRight, file);
18498
+ this.tokenizer = new Tokenizer(input, options.operators, file, this.contentRange);
18499
+ this.name = this.tokenizer.readTagName();
18500
+ this.tokenizer.assert(this.name, `illegal tag syntax, tag name expected`);
18501
+ this.tokenizer.skipBlank();
18502
+ }
18503
+ get args() {
18504
+ return this.tokenizer.input.slice(this.tokenizer.p, this.contentRange[1]);
18371
18505
  }
18372
18506
  }
18373
18507
 
18374
18508
  class OutputToken extends DelimitedToken {
18375
18509
  constructor(input, begin, end, options, file) {
18376
18510
  const { trimOutputLeft, trimOutputRight, outputDelimiterLeft, outputDelimiterRight } = options;
18377
- const value = input.slice(begin + outputDelimiterLeft.length, end - outputDelimiterRight.length);
18378
- super(TokenKind.Output, value, input, begin, end, trimOutputLeft, trimOutputRight, file);
18511
+ const valueRange = [begin + outputDelimiterLeft.length, end - outputDelimiterRight.length];
18512
+ super(TokenKind.Output, valueRange, input, begin, end, trimOutputLeft, trimOutputRight, file);
18379
18513
  }
18380
18514
  }
18381
18515
 
@@ -18395,10 +18529,13 @@ class HTMLToken extends Token {
18395
18529
  }
18396
18530
 
18397
18531
  class NumberToken extends Token {
18398
- constructor(whole, decimal) {
18399
- super(TokenKind.Number, whole.input, whole.begin, decimal ? decimal.end : whole.end, whole.file);
18400
- this.whole = whole;
18401
- this.decimal = decimal;
18532
+ constructor(input, begin, end, file) {
18533
+ super(TokenKind.Number, input, begin, end, file);
18534
+ this.input = input;
18535
+ this.begin = begin;
18536
+ this.end = end;
18537
+ this.file = file;
18538
+ this.content = Number(this.getText());
18402
18539
  }
18403
18540
  }
18404
18541
 
@@ -18411,16 +18548,6 @@ class IdentifierToken extends Token {
18411
18548
  this.file = file;
18412
18549
  this.content = this.getText();
18413
18550
  }
18414
- isNumber(allowSign = false) {
18415
- const begin = allowSign && TYPES[this.input.charCodeAt(this.begin)] & SIGN
18416
- ? this.begin + 1
18417
- : this.begin;
18418
- for (let i = begin; i < this.end; i++) {
18419
- if (!(TYPES[this.input.charCodeAt(i)] & NUMBER))
18420
- return false;
18421
- }
18422
- return true;
18423
- }
18424
18551
  }
18425
18552
 
18426
18553
  class LiteralToken extends Token {
@@ -18431,6 +18558,7 @@ class LiteralToken extends Token {
18431
18558
  this.end = end;
18432
18559
  this.file = file;
18433
18560
  this.literal = this.getText();
18561
+ this.content = literalValues[this.literal];
18434
18562
  }
18435
18563
  }
18436
18564
 
@@ -18474,13 +18602,10 @@ class OperatorToken extends Token {
18474
18602
  }
18475
18603
 
18476
18604
  class PropertyAccessToken extends Token {
18477
- constructor(variable, props, end) {
18478
- super(TokenKind.PropertyAccess, variable.input, variable.begin, end, variable.file);
18605
+ constructor(variable, props, input, begin, end, file) {
18606
+ super(TokenKind.PropertyAccess, input, begin, end, file);
18479
18607
  this.variable = variable;
18480
18608
  this.props = props;
18481
- this.propertyName = this.variable instanceof IdentifierToken
18482
- ? this.variable.getText()
18483
- : parseStringLiteral(this.variable.getText());
18484
18609
  }
18485
18610
  }
18486
18611
 
@@ -18504,6 +18629,59 @@ class HashToken extends Token {
18504
18629
  }
18505
18630
  }
18506
18631
 
18632
+ const rHex = /[\da-fA-F]/;
18633
+ const rOct = /[0-7]/;
18634
+ const escapeChar = {
18635
+ b: '\b',
18636
+ f: '\f',
18637
+ n: '\n',
18638
+ r: '\r',
18639
+ t: '\t',
18640
+ v: '\x0B'
18641
+ };
18642
+ function hexVal(c) {
18643
+ const code = c.charCodeAt(0);
18644
+ if (code >= 97)
18645
+ return code - 87;
18646
+ if (code >= 65)
18647
+ return code - 55;
18648
+ return code - 48;
18649
+ }
18650
+ function parseStringLiteral(str) {
18651
+ let ret = '';
18652
+ for (let i = 1; i < str.length - 1; i++) {
18653
+ if (str[i] !== '\\') {
18654
+ ret += str[i];
18655
+ continue;
18656
+ }
18657
+ if (escapeChar[str[i + 1]] !== undefined) {
18658
+ ret += escapeChar[str[++i]];
18659
+ }
18660
+ else if (str[i + 1] === 'u') {
18661
+ let val = 0;
18662
+ let j = i + 2;
18663
+ while (j <= i + 5 && rHex.test(str[j])) {
18664
+ val = val * 16 + hexVal(str[j++]);
18665
+ }
18666
+ i = j - 1;
18667
+ ret += String.fromCharCode(val);
18668
+ }
18669
+ else if (!rOct.test(str[i + 1])) {
18670
+ ret += str[++i];
18671
+ }
18672
+ else {
18673
+ let j = i + 1;
18674
+ let val = 0;
18675
+ while (j <= i + 3 && rOct.test(str[j])) {
18676
+ val = val * 8 + hexVal(str[j++]);
18677
+ }
18678
+ i = j - 1;
18679
+ ret += String.fromCharCode(val);
18680
+ }
18681
+ }
18682
+ return ret;
18683
+ }
18684
+
18507
18685
  class QuotedToken extends Token {
18508
18686
  constructor(input, begin, end, file) {
18509
18687
  super(TokenKind.Quoted, input, begin, end, file);
@@ -18511,6 +18689,7 @@ class QuotedToken extends Token {
18511
18689
  this.begin = begin;
18512
18690
  this.end = end;
18513
18691
  this.file = file;
18692
+ this.content = parseStringLiteral(this.getText());
18514
18693
  }
18515
18694
  }
18516
18695
 
@@ -18526,26 +18705,48 @@ class RangeToken extends Token {
18526
18705
  }
18527
18706
  }
18528
18707
 
18708
+ /**
18709
+ * LiquidTagToken is different from TagToken by not having delimiters `{%` or `%}`
18710
+ */
18529
18711
  class LiquidTagToken extends DelimitedToken {
18530
18712
  constructor(input, begin, end, options, file) {
18531
- const value = input.slice(begin, end);
18532
- super(TokenKind.Tag, value, input, begin, end, false, false, file);
18533
- if (!/\S/.test(value)) {
18534
- // A line that contains only whitespace.
18535
- this.name = '';
18536
- this.args = '';
18537
- }
18538
- else {
18539
- const tokenizer = new Tokenizer(this.content, options.operators);
18540
- this.name = tokenizer.readTagName();
18541
- if (!this.name)
18542
- throw new TokenizationError(`illegal liquid tag syntax`, this);
18543
- tokenizer.skipBlank();
18544
- this.args = tokenizer.remaining();
18545
- }
18713
+ super(TokenKind.Tag, [begin, end], input, begin, end, false, false, file);
18714
+ this.tokenizer = new Tokenizer(input, options.operators, file, this.contentRange);
18715
+ this.name = this.tokenizer.readTagName();
18716
+ this.tokenizer.assert(this.name, 'illegal liquid tag syntax');
18717
+ this.tokenizer.skipBlank();
18718
+ }
18719
+ get args() {
18720
+ return this.tokenizer.input.slice(this.tokenizer.p, this.contentRange[1]);
18721
+ }
18722
+ }
18723
+
18724
+ /**
18725
+ * value expression with optional filters
18726
+ * e.g.
18727
+ * {% assign foo="bar" | append: "coo" %}
18728
+ */
18729
+ class FilteredValueToken extends Token {
18730
+ constructor(initial, filters, input, begin, end, file) {
18731
+ super(TokenKind.FilteredValue, input, begin, end, file);
18732
+ this.initial = initial;
18733
+ this.filters = filters;
18734
+ this.input = input;
18735
+ this.begin = begin;
18736
+ this.end = end;
18737
+ this.file = file;
18546
18738
  }
18547
18739
  }
18548
18740
 
18741
+ const polyfill = {
18742
+ now: () => Date.now()
18743
+ };
18744
+ function getPerformance() {
18745
+ return (typeof global === 'object' && global.performance) ||
18746
+ (typeof window === 'object' && window.performance) ||
18747
+ polyfill;
18748
+ }
18749
+
18549
18750
  class SimpleEmitter {
18550
18751
  constructor() {
18551
18752
  this.buffer = '';
@@ -18601,20 +18802,28 @@ class Render {
18601
18802
  if (!emitter) {
18602
18803
  emitter = ctx.opts.keepOutputType ? new KeepingTypeEmitter() : new SimpleEmitter();
18603
18804
  }
18805
+ const errors = [];
18604
18806
  for (const tpl of templates) {
18807
+ ctx.renderLimit.check(getPerformance().now());
18605
18808
  try {
18606
18809
  // if tpl.render supports emitter, it'll return empty `html`
18607
18810
  const html = yield tpl.render(ctx, emitter);
18608
18811
  // if not, it'll return an `html`, write to the emitter for it
18609
18812
  html && emitter.write(html);
18610
- if (emitter['break'] || emitter['continue'])
18813
+ if (ctx.breakCalled || ctx.continueCalled)
18611
18814
  break;
18612
18815
  }
18613
18816
  catch (e) {
18614
- const err = RenderError.is(e) ? e : new RenderError(e, tpl);
18615
- throw err;
18817
+ const err = LiquidError.is(e) ? e : new RenderError(e, tpl);
18818
+ if (ctx.opts.catchAllErrors)
18819
+ errors.push(err);
18820
+ else
18821
+ throw err;
18616
18822
  }
18617
18823
  }
18824
+ if (errors.length) {
18825
+ throw new LiquidErrors(errors);
18826
+ }
18618
18827
  return emitter.buffer;
18619
18828
  }
18620
18829
  }
@@ -18640,25 +18849,24 @@ class Expression {
18640
18849
  operands.push(result);
18641
18850
  }
18642
18851
  else {
18643
- operands.push(yield evalToken(token, ctx, lenient && this.postfix.length === 1));
18852
+ operands.push(yield evalToken(token, ctx, lenient));
18644
18853
  }
18645
18854
  }
18646
18855
  return operands[0];
18647
18856
  }
18857
+ valid() {
18858
+ return !!this.postfix.length;
18859
+ }
18648
18860
  }
18649
18861
  function* evalToken(token, ctx, lenient = false) {
18862
+ if (!token)
18863
+ return;
18864
+ if ('content' in token)
18865
+ return token.content;
18650
18866
  if (isPropertyAccessToken(token))
18651
18867
  return yield evalPropertyAccessToken(token, ctx, lenient);
18652
18868
  if (isRangeToken(token))
18653
18869
  return yield evalRangeToken(token, ctx);
18654
- if (isLiteralToken(token))
18655
- return evalLiteralToken(token);
18656
- if (isNumberToken(token))
18657
- return evalNumberToken(token);
18658
- if (isWordToken(token))
18659
- return token.getText();
18660
- if (isQuotedToken(token))
18661
- return evalQuotedToken(token);
18662
18870
  }
18663
18871
  function* evalPropertyAccessToken(token, ctx, lenient) {
18664
18872
  const props = [];
@@ -18666,7 +18874,13 @@ function* evalPropertyAccessToken(token, ctx, lenient) {
18666
18874
  props.push((yield evalToken(prop, ctx, false)));
18667
18875
  }
18668
18876
  try {
18669
- return yield ctx._get([token.propertyName, ...props]);
18877
+ if (token.variable) {
18878
+ const variable = yield evalToken(token.variable, ctx, lenient);
18879
+ return yield ctx._getFromScope(variable, props);
18880
+ }
18881
+ else {
18882
+ return yield ctx._get(props);
18883
+ }
18670
18884
  }
18671
18885
  catch (e) {
18672
18886
  if (lenient && e.name === 'InternalUndefinedVariableError')
@@ -18674,19 +18888,13 @@ function* evalPropertyAccessToken(token, ctx, lenient) {
18674
18888
  throw (new UndefinedVariableError(e, token));
18675
18889
  }
18676
18890
  }
18677
- function evalNumberToken(token) {
18678
- const str = token.whole.content + '.' + (token.decimal ? token.decimal.content : '');
18679
- return Number(str);
18680
- }
18681
18891
  function evalQuotedToken(token) {
18682
- return parseStringLiteral(token.getText());
18683
- }
18684
- function evalLiteralToken(token) {
18685
- return literalValues[token.literal];
18892
+ return token.content;
18686
18893
  }
18687
18894
  function* evalRangeToken(token, ctx) {
18688
18895
  const low = yield evalToken(token.lhs, ctx);
18689
18896
  const high = yield evalToken(token.rhs, ctx);
18897
+ ctx.memoryLimit.use(high - low + 1);
18690
18898
  return range(+low, +high + 1);
18691
18899
  }
18692
18900
  function* toPostfix(tokens) {
@@ -18710,6 +18918,7 @@ function isTruthy(val, ctx) {
18710
18918
  return !isFalsy(val, ctx);
18711
18919
  }
18712
18920
  function isFalsy(val, ctx) {
18921
+ val = toValue(val);
18713
18922
  if (ctx.opts.jsTruthy) {
18714
18923
  return !val;
18715
18924
  }
@@ -18719,8 +18928,8 @@ function isFalsy(val, ctx) {
18719
18928
  }
18720
18929
 
18721
18930
  const defaultOperators = {
18722
- '==': equal,
18723
- '!=': (l, r) => !equal(l, r),
18931
+ '==': equals,
18932
+ '!=': (l, r) => !equals(l, r),
18724
18933
  '>': (l, r) => {
18725
18934
  if (isComparable(l))
18726
18935
  return l.gt(r);
@@ -18751,14 +18960,17 @@ const defaultOperators = {
18751
18960
  },
18752
18961
  'contains': (l, r) => {
18753
18962
  l = toValue(l);
18754
- r = toValue(r);
18755
- return l && isFunction(l.indexOf) ? l.indexOf(r) > -1 : false;
18963
+ if (isArray(l))
18964
+ return l.some((i) => equals(i, r));
18965
+ if (isFunction(l === null || l === void 0 ? void 0 : l.indexOf))
18966
+ return l.indexOf(toValue(r)) > -1;
18967
+ return false;
18756
18968
  },
18757
18969
  'not': (v, ctx) => isFalsy(toValue(v), ctx),
18758
18970
  'and': (l, r, ctx) => isTruthy(toValue(l), ctx) && isTruthy(toValue(r), ctx),
18759
18971
  'or': (l, r, ctx) => isTruthy(toValue(l), ctx) || isTruthy(toValue(r), ctx)
18760
18972
  };
18761
- function equal(lhs, rhs) {
18973
+ function equals(lhs, rhs) {
18762
18974
  if (isComparable(lhs))
18763
18975
  return lhs.equals(rhs);
18764
18976
  if (isComparable(rhs))
@@ -18766,14 +18978,17 @@ function equal(lhs, rhs) {
18766
18978
  lhs = toValue(lhs);
18767
18979
  rhs = toValue(rhs);
18768
18980
  if (isArray(lhs)) {
18769
- return isArray(rhs) && arrayEqual(lhs, rhs);
18981
+ return isArray(rhs) && arrayEquals(lhs, rhs);
18770
18982
  }
18771
18983
  return lhs === rhs;
18772
18984
  }
18773
- function arrayEqual(lhs, rhs) {
18985
+ function arrayEquals(lhs, rhs) {
18774
18986
  if (lhs.length !== rhs.length)
18775
18987
  return false;
18776
- return !lhs.some((value, i) => !equal(value, rhs[i]));
18988
+ return !lhs.some((value, i) => !equals(value, rhs[i]));
18989
+ }
18990
+ function arrayIncludes(arr, item) {
18991
+ return arr.some(value => equals(value, item));
18777
18992
  }
18778
18993
 
18779
18994
  class Node {
@@ -18834,7 +19049,10 @@ class LRU {
18834
19049
  }
18835
19050
  }
18836
19051
 
18837
- const requireResolve = require.resolve;
19052
+ function requireResolve (file) {
19053
+ const require = module$1.createRequire(process.cwd() + '/');
19054
+ return require.resolve(file)
19055
+ }
18838
19056
 
18839
19057
  const statAsync = promisify(require$$6.stat);
18840
19058
  const readFileAsync = promisify(require$$6.readFile);
@@ -18897,7 +19115,7 @@ var fs = /*#__PURE__*/Object.freeze({
18897
19115
  sep: require$$1$1.sep
18898
19116
  });
18899
19117
 
18900
- function Default(value, defaultValue, ...args) {
19118
+ function defaultFilter(value, defaultValue, ...args) {
18901
19119
  value = toValue(value);
18902
19120
  if (isArray(value) || isString(value))
18903
19121
  return value.length ? value : defaultValue;
@@ -18908,9 +19126,34 @@ function Default(value, defaultValue, ...args) {
18908
19126
  function json(value, space = 0) {
18909
19127
  return JSON.stringify(value, null, space);
18910
19128
  }
19129
+ function inspect(value, space = 0) {
19130
+ const ancestors = [];
19131
+ return JSON.stringify(value, function (_key, value) {
19132
+ if (typeof value !== 'object' || value === null)
19133
+ return value;
19134
+ // `this` is the object that value is contained in, i.e., its direct parent.
19135
+ while (ancestors.length > 0 && ancestors[ancestors.length - 1] !== this)
19136
+ ancestors.pop();
19137
+ if (ancestors.includes(value))
19138
+ return '[Circular]';
19139
+ ancestors.push(value);
19140
+ return value;
19141
+ }, space);
19142
+ }
19143
+ function to_integer(value) {
19144
+ return Number(value);
19145
+ }
18911
19146
  const raw = {
18912
19147
  raw: true,
18913
19148
  handler: identify
19149
+ };
19150
+ var misc = {
19151
+ default: defaultFilter,
19152
+ raw,
19153
+ jsonify: json,
19154
+ to_integer,
19155
+ json,
19156
+ inspect
18914
19157
  };
18915
19158
 
18916
19159
  const escapeMap = {
@@ -18928,41 +19171,103 @@ const unescapeMap = {
18928
19171
  '&#39;': "'"
18929
19172
  };
18930
19173
  function escape(str) {
18931
- return stringify(str).replace(/&|<|>|"|'/g, m => escapeMap[m]);
19174
+ str = stringify(str);
19175
+ this.context.memoryLimit.use(str.length);
19176
+ return str.replace(/&|<|>|"|'/g, m => escapeMap[m]);
19177
+ }
19178
+ function xml_escape(str) {
19179
+ return escape.call(this, str);
18932
19180
  }
18933
19181
  function unescape$1(str) {
18934
- return stringify(str).replace(/&(amp|lt|gt|#34|#39);/g, m => unescapeMap[m]);
19182
+ str = stringify(str);
19183
+ this.context.memoryLimit.use(str.length);
19184
+ return str.replace(/&(amp|lt|gt|#34|#39);/g, m => unescapeMap[m]);
18935
19185
  }
18936
19186
  function escape_once(str) {
18937
- return escape(unescape$1(stringify(str)));
19187
+ return escape.call(this, unescape$1.call(this, str));
18938
19188
  }
18939
19189
  function newline_to_br(v) {
18940
- return stringify(v).replace(/\n/g, '<br />\n');
19190
+ const str = stringify(v);
19191
+ this.context.memoryLimit.use(str.length);
19192
+ return str.replace(/\r?\n/gm, '<br />\n');
18941
19193
  }
18942
19194
  function strip_html(v) {
18943
- return stringify(v).replace(/<script[\s\S]*?<\/script>|<style[\s\S]*?<\/style>|<.*?>|<!--[\s\S]*?-->/g, '');
19195
+ const str = stringify(v);
19196
+ this.context.memoryLimit.use(str.length);
19197
+ return str.replace(/<script[\s\S]*?<\/script>|<style[\s\S]*?<\/style>|<.*?>|<!--[\s\S]*?-->/g, '');
18944
19198
  }
18945
19199
 
18946
19200
  var htmlFilters = /*#__PURE__*/Object.freeze({
18947
19201
  __proto__: null,
18948
19202
  escape: escape,
19203
+ xml_escape: xml_escape,
18949
19204
  escape_once: escape_once,
18950
19205
  newline_to_br: newline_to_br,
18951
19206
  strip_html: strip_html
18952
19207
  });
18953
19208
 
19209
+ class MapFS {
19210
+ constructor(mapping) {
19211
+ this.mapping = mapping;
19212
+ this.sep = '/';
19213
+ }
19214
+ exists(filepath) {
19215
+ return __awaiter(this, void 0, void 0, function* () {
19216
+ return this.existsSync(filepath);
19217
+ });
19218
+ }
19219
+ existsSync(filepath) {
19220
+ return !isNil(this.mapping[filepath]);
19221
+ }
19222
+ readFile(filepath) {
19223
+ return __awaiter(this, void 0, void 0, function* () {
19224
+ return this.readFileSync(filepath);
19225
+ });
19226
+ }
19227
+ readFileSync(filepath) {
19228
+ const content = this.mapping[filepath];
19229
+ if (isNil(content))
19230
+ throw new Error(`ENOENT: ${filepath}`);
19231
+ return content;
19232
+ }
19233
+ dirname(filepath) {
19234
+ const segments = filepath.split(this.sep);
19235
+ segments.pop();
19236
+ return segments.join(this.sep);
19237
+ }
19238
+ resolve(dir, file, ext) {
19239
+ file += ext;
19240
+ if (dir === '.')
19241
+ return file;
19242
+ const segments = dir.split(/\/+/);
19243
+ for (const segment of file.split(this.sep)) {
19244
+ if (segment === '.' || segment === '')
19245
+ continue;
19246
+ else if (segment === '..') {
19247
+ if (segments.length > 1 || segments[0] !== '')
19248
+ segments.pop();
19249
+ }
19250
+ else
19251
+ segments.push(segment);
19252
+ }
19253
+ return segments.join(this.sep);
19254
+ }
19255
+ }
19256
+
18954
19257
  const defaultOptions = {
18955
19258
  root: ['.'],
18956
19259
  layouts: ['.'],
18957
19260
  partials: ['.'],
18958
19261
  relativeReference: true,
18959
19262
  jekyllInclude: false,
19263
+ keyValueSeparator: ':',
18960
19264
  cache: undefined,
18961
19265
  extname: '',
18962
19266
  fs: fs,
18963
19267
  dynamicPartials: true,
18964
19268
  jsTruthy: false,
18965
19269
  dateFormat: '%A, %B %-e, %Y at %-l:%M %P %z',
19270
+ locale: '',
18966
19271
  trimTagRight: false,
18967
19272
  trimTagLeft: false,
18968
19273
  trimOutputRight: false,
@@ -18979,9 +19284,13 @@ const defaultOptions = {
18979
19284
  lenientIf: false,
18980
19285
  globals: {},
18981
19286
  keepOutputType: false,
18982
- operators: defaultOperators
19287
+ operators: defaultOperators,
19288
+ memoryLimit: Infinity,
19289
+ parseLimit: Infinity,
19290
+ renderLimit: Infinity
18983
19291
  };
18984
19292
  function normalize(options) {
19293
+ var _a, _b;
18985
19294
  if (options.hasOwnProperty('root')) {
18986
19295
  if (!options.hasOwnProperty('partials'))
18987
19296
  options.partials = options.root;
@@ -19007,13 +19316,21 @@ function normalize(options) {
19007
19316
  options.partials = normalizeDirectoryList(options.partials);
19008
19317
  options.layouts = normalizeDirectoryList(options.layouts);
19009
19318
  options.outputEscape = options.outputEscape && getOutputEscapeFunction(options.outputEscape);
19319
+ if (!options.locale) {
19320
+ options.locale = (_b = (_a = getDateTimeFormat()) === null || _a === void 0 ? void 0 : _a().resolvedOptions().locale) !== null && _b !== void 0 ? _b : 'en-US';
19321
+ }
19322
+ if (options.templates) {
19323
+ options.fs = new MapFS(options.templates);
19324
+ options.relativeReference = true;
19325
+ options.root = options.partials = options.layouts = '.';
19326
+ }
19010
19327
  return options;
19011
19328
  }
19012
19329
  function getOutputEscapeFunction(nameOrFunction) {
19013
19330
  if (nameOrFunction === 'escape')
19014
19331
  return escape;
19015
19332
  if (nameOrFunction === 'json')
19016
- return json;
19333
+ return misc.json;
19017
19334
  assert(isFunction(nameOrFunction), '`outputEscape` need to be of type string or function');
19018
19335
  return nameOrFunction;
19019
19336
  }
@@ -19026,22 +19343,6 @@ function normalizeDirectoryList(value) {
19026
19343
  return list;
19027
19344
  }
19028
19345
 
19029
- function matchOperator(str, begin, trie, end = str.length) {
19030
- let node = trie;
19031
- let i = begin;
19032
- let info;
19033
- while (node[str[i]] && i < end) {
19034
- node = node[str[i++]];
19035
- if (node['end'])
19036
- info = node;
19037
- }
19038
- if (!info)
19039
- return -1;
19040
- if (info['needBoundary'] && (TYPES[str.charCodeAt(i)] & IDENTIFIER))
19041
- return -1;
19042
- return i;
19043
- }
19044
-
19045
19346
  function whiteSpaceCtrl(tokens, options) {
19046
19347
  let inRaw = false;
19047
19348
  for (let i = 0; i < tokens.length; i++) {
@@ -19080,13 +19381,14 @@ function trimRight(token, greedy) {
19080
19381
  }
19081
19382
 
19082
19383
  class Tokenizer {
19083
- constructor(input, operators = defaultOptions.operators, file) {
19384
+ constructor(input, operators = defaultOptions.operators, file, range) {
19084
19385
  this.input = input;
19085
19386
  this.file = file;
19086
- this.p = 0;
19087
19387
  this.rawBeginAt = -1;
19088
- this.N = input.length;
19388
+ this.p = range ? range[0] : 0;
19389
+ this.N = range ? range[1] : input.length;
19089
19390
  this.opTrie = createTrie(operators);
19391
+ this.literalTrie = createTrie(literalValues);
19090
19392
  }
19091
19393
  readExpression() {
19092
19394
  return new Expression(this.readExpressionTokens());
@@ -19108,11 +19410,33 @@ class Tokenizer {
19108
19410
  }
19109
19411
  readOperator() {
19110
19412
  this.skipBlank();
19111
- const end = matchOperator(this.input, this.p, this.opTrie);
19413
+ const end = this.matchTrie(this.opTrie);
19112
19414
  if (end === -1)
19113
19415
  return;
19114
19416
  return new OperatorToken(this.input, this.p, (this.p = end), this.file);
19115
19417
  }
19418
+ matchTrie(trie) {
19419
+ let node = trie;
19420
+ let i = this.p;
19421
+ let info;
19422
+ while (node[this.input[i]] && i < this.N) {
19423
+ node = node[this.input[i++]];
19424
+ if (node['end'])
19425
+ info = node;
19426
+ }
19427
+ if (!info)
19428
+ return -1;
19429
+ if (info['needBoundary'] && isWord(this.peek(i - this.p)))
19430
+ return -1;
19431
+ return i;
19432
+ }
19433
+ readFilteredValue() {
19434
+ const begin = this.p;
19435
+ const initial = this.readExpression();
19436
+ this.assert(initial.valid(), `invalid value expression: ${this.snapshot()}`);
19437
+ const filters = this.readFilters();
19438
+ return new FilteredValueToken(initial, filters, this.input, begin, this.p, this.file);
19439
+ }
19116
19440
  readFilters() {
19117
19441
  const filters = [];
19118
19442
  while (true) {
@@ -19126,12 +19450,12 @@ class Tokenizer {
19126
19450
  this.skipBlank();
19127
19451
  if (this.end())
19128
19452
  return null;
19129
- assert(this.peek() === '|', () => `unexpected token at ${this.snapshot()}`);
19130
- this.p++;
19131
- const begin = this.p;
19453
+ this.assert(this.read() === '|', `expected "|" before filter`);
19132
19454
  const name = this.readIdentifier();
19133
- if (!name.size())
19455
+ if (!name.size()) {
19456
+ this.assert(this.end(), `expected filter name`);
19134
19457
  return null;
19458
+ }
19135
19459
  const args = [];
19136
19460
  this.skipBlank();
19137
19461
  if (this.peek() === ':') {
@@ -19140,10 +19464,14 @@ class Tokenizer {
19140
19464
  const arg = this.readFilterArg();
19141
19465
  arg && args.push(arg);
19142
19466
  this.skipBlank();
19143
- assert(this.end() || this.peek() === ',' || this.peek() === '|', () => `unexpected character ${this.snapshot()}`);
19467
+ this.assert(this.end() || this.peek() === ',' || this.peek() === '|', () => `unexpected character ${this.snapshot()}`);
19144
19468
  } while (this.peek() === ',');
19145
19469
  }
19146
- return new FilterToken(name.getText(), args, this.input, begin, this.p, this.file);
19470
+ else if (this.peek() === '|' || this.end()) ;
19471
+ else {
19472
+ throw this.error('expected ":" after filter name');
19473
+ }
19474
+ return new FilterToken(name.getText(), args, this.input, name.begin, this.p, this.file);
19147
19475
  }
19148
19476
  readFilterArg() {
19149
19477
  const key = this.readValue();
@@ -19184,20 +19512,21 @@ class Tokenizer {
19184
19512
  }
19185
19513
  return new HTMLToken(this.input, begin, this.p, this.file);
19186
19514
  }
19187
- readTagToken(options = defaultOptions) {
19515
+ readTagToken(options) {
19188
19516
  const { file, input } = this;
19189
19517
  const begin = this.p;
19190
19518
  if (this.readToDelimiter(options.tagDelimiterRight) === -1) {
19191
- throw this.mkError(`tag ${this.snapshot(begin)} not closed`, begin);
19519
+ throw this.error(`tag ${this.snapshot(begin)} not closed`, begin);
19192
19520
  }
19193
19521
  const token = new TagToken(input, begin, this.p, options, file);
19194
19522
  if (token.name === 'raw')
19195
19523
  this.rawBeginAt = begin;
19196
19524
  return token;
19197
19525
  }
19198
- readToDelimiter(delimiter) {
19526
+ readToDelimiter(delimiter, respectQuoted = false) {
19527
+ this.skipBlank();
19199
19528
  while (this.p < this.N) {
19200
- if ((this.peekType() & QUOTE)) {
19529
+ if (respectQuoted && (this.peekType() & QUOTE)) {
19201
19530
  this.readQuoted();
19202
19531
  continue;
19203
19532
  }
@@ -19211,8 +19540,8 @@ class Tokenizer {
19211
19540
  const { file, input } = this;
19212
19541
  const { outputDelimiterRight } = options;
19213
19542
  const begin = this.p;
19214
- if (this.readToDelimiter(outputDelimiterRight) === -1) {
19215
- throw this.mkError(`output ${this.snapshot(begin)} not closed`, begin);
19543
+ if (this.readToDelimiter(outputDelimiterRight, true) === -1) {
19544
+ throw this.error(`output ${this.snapshot(begin)} not closed`, begin);
19216
19545
  }
19217
19546
  return new OutputToken(input, begin, this.p, options, file);
19218
19547
  }
@@ -19242,30 +19571,34 @@ class Tokenizer {
19242
19571
  this.p++;
19243
19572
  }
19244
19573
  }
19245
- throw this.mkError(`raw ${this.snapshot(this.rawBeginAt)} not closed`, begin);
19574
+ throw this.error(`raw ${this.snapshot(this.rawBeginAt)} not closed`, begin);
19246
19575
  }
19247
19576
  readLiquidTagTokens(options = defaultOptions) {
19248
19577
  const tokens = [];
19249
19578
  while (this.p < this.N) {
19250
19579
  const token = this.readLiquidTagToken(options);
19251
- if (token.name)
19252
- tokens.push(token);
19580
+ token && tokens.push(token);
19253
19581
  }
19254
19582
  return tokens;
19255
19583
  }
19256
19584
  readLiquidTagToken(options) {
19257
- const { file, input } = this;
19585
+ this.skipBlank();
19586
+ if (this.end())
19587
+ return;
19258
19588
  const begin = this.p;
19259
- let end = this.N;
19260
- if (this.readToDelimiter('\n') !== -1)
19261
- end = this.p;
19262
- return new LiquidTagToken(input, begin, end, options, file);
19589
+ this.readToDelimiter('\n');
19590
+ const end = this.p;
19591
+ return new LiquidTagToken(this.input, begin, end, options, this.file);
19263
19592
  }
19264
- mkError(msg, begin) {
19265
- return new TokenizationError(msg, new IdentifierToken(this.input, begin, this.N, this.file));
19593
+ error(msg, pos = this.p) {
19594
+ return new TokenizationError(msg, new IdentifierToken(this.input, pos, this.N, this.file));
19595
+ }
19596
+ assert(pred, msg, pos) {
19597
+ if (!pred)
19598
+ throw this.error(typeof msg === 'function' ? msg() : msg, pos);
19266
19599
  }
19267
19600
  snapshot(begin = this.p) {
19268
- return JSON.stringify(ellipsis(this.input.slice(begin), 16));
19601
+ return JSON.stringify(ellipsis(this.input.slice(begin, this.N), 32));
19269
19602
  }
19270
19603
  /**
19271
19604
  * @deprecated use #readIdentifier instead
@@ -19276,10 +19609,14 @@ class Tokenizer {
19276
19609
  readIdentifier() {
19277
19610
  this.skipBlank();
19278
19611
  const begin = this.p;
19279
- while (this.peekType() & IDENTIFIER)
19612
+ while (!this.end() && isWord(this.peek()))
19280
19613
  ++this.p;
19281
19614
  return new IdentifierToken(this.input, begin, this.p, this.file);
19282
19615
  }
19616
+ readNonEmptyIdentifier() {
19617
+ const id = this.readIdentifier();
19618
+ return id.size() ? id : undefined;
19619
+ }
19283
19620
  readTagName() {
19284
19621
  this.skipBlank();
19285
19622
  // Handle inline comment tags
@@ -19301,12 +19638,12 @@ class Tokenizer {
19301
19638
  if (this.peek() === ',')
19302
19639
  ++this.p;
19303
19640
  const begin = this.p;
19304
- const name = this.readIdentifier();
19305
- if (!name.size())
19641
+ const name = this.readNonEmptyIdentifier();
19642
+ if (!name)
19306
19643
  return;
19307
19644
  let value;
19308
19645
  this.skipBlank();
19309
- const sep = jekyllStyle ? '=' : ':';
19646
+ const sep = isString(jekyllStyle) ? jekyllStyle : (jekyllStyle ? '=' : ':');
19310
19647
  if (this.peek() === sep) {
19311
19648
  ++this.p;
19312
19649
  value = this.readValue();
@@ -19314,14 +19651,17 @@ class Tokenizer {
19314
19651
  return new HashToken(this.input, begin, this.p, name, value, this.file);
19315
19652
  }
19316
19653
  remaining() {
19317
- return this.input.slice(this.p);
19654
+ return this.input.slice(this.p, this.N);
19318
19655
  }
19319
- advance(i = 1) {
19320
- this.p += i;
19656
+ advance(step = 1) {
19657
+ this.p += step;
19321
19658
  }
19322
19659
  end() {
19323
19660
  return this.p >= this.N;
19324
19661
  }
19662
+ read() {
19663
+ return this.input[this.p++];
19664
+ }
19325
19665
  readTo(end) {
19326
19666
  while (this.p < this.N) {
19327
19667
  ++this.p;
@@ -19331,50 +19671,86 @@ class Tokenizer {
19331
19671
  return -1;
19332
19672
  }
19333
19673
  readValue() {
19334
- const value = this.readQuoted() || this.readRange();
19335
- if (value)
19336
- return value;
19337
- if (this.peek() === '[') {
19338
- this.p++;
19339
- const prop = this.readQuoted();
19340
- if (!prop)
19341
- return;
19342
- if (this.peek() !== ']')
19343
- return;
19344
- this.p++;
19345
- return new PropertyAccessToken(prop, [], this.p);
19346
- }
19347
- const variable = this.readIdentifier();
19348
- if (!variable.size())
19349
- return;
19350
- let isNumber = variable.isNumber(true);
19674
+ this.skipBlank();
19675
+ const begin = this.p;
19676
+ const variable = this.readLiteral() || this.readQuoted() || this.readRange() || this.readNumber();
19677
+ const props = this.readProperties(!variable);
19678
+ if (!props.length)
19679
+ return variable;
19680
+ return new PropertyAccessToken(variable, props, this.input, begin, this.p);
19681
+ }
19682
+ readScopeValue() {
19683
+ this.skipBlank();
19684
+ const begin = this.p;
19685
+ const props = this.readProperties();
19686
+ if (!props.length)
19687
+ return undefined;
19688
+ return new PropertyAccessToken(undefined, props, this.input, begin, this.p);
19689
+ }
19690
+ readProperties(isBegin = true) {
19351
19691
  const props = [];
19352
19692
  while (true) {
19353
19693
  if (this.peek() === '[') {
19354
- isNumber = false;
19355
19694
  this.p++;
19356
19695
  const prop = this.readValue() || new IdentifierToken(this.input, this.p, this.p, this.file);
19357
- this.readTo(']');
19696
+ this.assert(this.readTo(']') !== -1, '[ not closed');
19358
19697
  props.push(prop);
19698
+ continue;
19699
+ }
19700
+ if (isBegin && !props.length) {
19701
+ const prop = this.readNonEmptyIdentifier();
19702
+ if (prop) {
19703
+ props.push(prop);
19704
+ continue;
19705
+ }
19359
19706
  }
19360
- else if (this.peek() === '.' && this.peek(1) !== '.') { // skip range syntax
19707
+ if (this.peek() === '.' && this.peek(1) !== '.') { // skip range syntax
19361
19708
  this.p++;
19362
- const prop = this.readIdentifier();
19363
- if (!prop.size())
19709
+ const prop = this.readNonEmptyIdentifier();
19710
+ if (!prop)
19364
19711
  break;
19365
- if (!prop.isNumber())
19366
- isNumber = false;
19367
19712
  props.push(prop);
19713
+ continue;
19714
+ }
19715
+ break;
19716
+ }
19717
+ return props;
19718
+ }
19719
+ readNumber() {
19720
+ this.skipBlank();
19721
+ let decimalFound = false;
19722
+ let digitFound = false;
19723
+ let n = 0;
19724
+ if (this.peekType() & SIGN)
19725
+ n++;
19726
+ while (this.p + n <= this.N) {
19727
+ if (this.peekType(n) & NUMBER) {
19728
+ digitFound = true;
19729
+ n++;
19730
+ }
19731
+ else if (this.peek(n) === '.' && this.peek(n + 1) !== '.') {
19732
+ if (decimalFound || !digitFound)
19733
+ return;
19734
+ decimalFound = true;
19735
+ n++;
19368
19736
  }
19369
19737
  else
19370
19738
  break;
19371
19739
  }
19372
- if (!props.length && literalValues.hasOwnProperty(variable.content)) {
19373
- return new LiteralToken(this.input, variable.begin, variable.end, this.file);
19740
+ if (digitFound && !isWord(this.peek(n))) {
19741
+ const num = new NumberToken(this.input, this.p, this.p + n, this.file);
19742
+ this.advance(n);
19743
+ return num;
19374
19744
  }
19375
- if (isNumber)
19376
- return new NumberToken(variable, props[0]);
19377
- return new PropertyAccessToken(variable, props, this.p);
19745
+ }
19746
+ readLiteral() {
19747
+ this.skipBlank();
19748
+ const end = this.matchTrie(this.literalTrie);
19749
+ if (end === -1)
19750
+ return;
19751
+ const literal = new LiteralToken(this.input, this.p, end, this.file);
19752
+ this.p = end;
19753
+ return literal;
19378
19754
  }
19379
19755
  readRange() {
19380
19756
  this.skipBlank();
@@ -19390,7 +19766,7 @@ class Tokenizer {
19390
19766
  }
19391
19767
  readValueOrThrow() {
19392
19768
  const value = this.readValue();
19393
- assert(value, () => `unexpected token ${this.snapshot()}, value expected`);
19769
+ this.assert(value, () => `unexpected token ${this.snapshot()}, value expected`);
19394
19770
  return value;
19395
19771
  }
19396
19772
  readQuoted() {
@@ -19437,10 +19813,10 @@ class Tokenizer {
19437
19813
  return true;
19438
19814
  }
19439
19815
  peekType(n = 0) {
19440
- return TYPES[this.input.charCodeAt(this.p + n)];
19816
+ return this.p + n >= this.N ? 0 : TYPES[this.input.charCodeAt(this.p + n)];
19441
19817
  }
19442
19818
  peek(n = 0) {
19443
- return this.input[this.p + n];
19819
+ return this.p + n >= this.N ? '' : this.input[this.p + n];
19444
19820
  }
19445
19821
  skipBlank() {
19446
19822
  while (this.peekType() & BLANK)
@@ -19496,6 +19872,7 @@ class Tag extends TemplateImpl {
19496
19872
  super(token);
19497
19873
  this.name = token.name;
19498
19874
  this.liquid = liquid;
19875
+ this.tokenizer = token.tokenizer;
19499
19876
  }
19500
19877
  }
19501
19878
 
@@ -19508,9 +19885,9 @@ class Tag extends TemplateImpl {
19508
19885
  * hash['reversed'] === undefined
19509
19886
  */
19510
19887
  class Hash {
19511
- constructor(markup, jekyllStyle) {
19888
+ constructor(input, jekyllStyle) {
19512
19889
  this.hash = {};
19513
- const tokenizer = new Tokenizer(markup, {});
19890
+ const tokenizer = input instanceof Tokenizer ? input : new Tokenizer(input, {});
19514
19891
  for (const hash of tokenizer.readHashes(jekyllStyle)) {
19515
19892
  this.hash[hash.name.content] = hash.value;
19516
19893
  }
@@ -19533,7 +19910,7 @@ function createTagClass(options) {
19533
19910
  }
19534
19911
  }
19535
19912
  *render(ctx, emitter) {
19536
- const hash = (yield new Hash(this.token.args).render(ctx));
19913
+ const hash = (yield new Hash(this.token.args, ctx.opts.keyValueSeparator).render(ctx));
19537
19914
  return yield options.render.call(this, ctx, emitter, hash);
19538
19915
  }
19539
19916
  };
@@ -19544,13 +19921,14 @@ function isKeyValuePair(arr) {
19544
19921
  }
19545
19922
 
19546
19923
  class Filter {
19547
- constructor(name, options, args, liquid) {
19548
- this.name = name;
19924
+ constructor(token, options, liquid) {
19925
+ this.token = token;
19926
+ this.name = token.name;
19549
19927
  this.handler = isFunction(options)
19550
19928
  ? options
19551
19929
  : (isFunction(options === null || options === void 0 ? void 0 : options.handler) ? options.handler : identify);
19552
19930
  this.raw = !isFunction(options) && !!(options === null || options === void 0 ? void 0 : options.raw);
19553
- this.args = args;
19931
+ this.args = token.args;
19554
19932
  this.liquid = liquid;
19555
19933
  }
19556
19934
  *render(value, context) {
@@ -19561,7 +19939,7 @@ class Filter {
19561
19939
  else
19562
19940
  argv.push(yield evalToken(arg, context));
19563
19941
  }
19564
- return this.handler.apply({ context, liquid: this.liquid }, [value, ...argv]);
19942
+ return yield this.handler.apply({ context, token: this.token, liquid: this.liquid }, [value, ...argv]);
19565
19943
  }
19566
19944
  }
19567
19945
 
@@ -19569,11 +19947,13 @@ class Value {
19569
19947
  /**
19570
19948
  * @param str the value to be valuated, eg.: "foobar" | truncate: 3
19571
19949
  */
19572
- constructor(str, liquid) {
19950
+ constructor(input, liquid) {
19573
19951
  this.filters = [];
19574
- const tokenizer = new Tokenizer(str, liquid.options.operators);
19575
- this.initial = tokenizer.readExpression();
19576
- this.filters = tokenizer.readFilters().map(({ name, args }) => new Filter(name, this.getFilter(liquid, name), args, liquid));
19952
+ const token = typeof input === 'string'
19953
+ ? new Tokenizer(input, liquid.options.operators).readFilteredValue()
19954
+ : input;
19955
+ this.initial = token.initial;
19956
+ this.filters = token.filters.map(token => new Filter(token, this.getFilter(liquid, token.name), liquid));
19577
19957
  }
19578
19958
  *value(ctx, lenient) {
19579
19959
  lenient = lenient || (ctx.opts.lenientIf && this.filters.length > 0 && this.filters[0].name === 'default');
@@ -19594,17 +19974,22 @@ class Output extends TemplateImpl {
19594
19974
  constructor(token, liquid) {
19595
19975
  var _a;
19596
19976
  super(token);
19597
- this.value = new Value(token.content, liquid);
19977
+ const tokenizer = new Tokenizer(token.input, liquid.options.operators, token.file, token.contentRange);
19978
+ this.value = new Value(tokenizer.readFilteredValue(), liquid);
19598
19979
  const filters = this.value.filters;
19599
19980
  const outputEscape = liquid.options.outputEscape;
19600
19981
  if (!((_a = filters[filters.length - 1]) === null || _a === void 0 ? void 0 : _a.raw) && outputEscape) {
19601
- filters.push(new Filter(toString.call(outputEscape), outputEscape, [], liquid));
19982
+ const token = new FilterToken(toString.call(outputEscape), [], '', 0, 0);
19983
+ filters.push(new Filter(token, outputEscape, liquid));
19602
19984
  }
19603
19985
  }
19604
19986
  *render(ctx, emitter) {
19605
19987
  const val = yield this.value.value(ctx, false);
19606
19988
  emitter.write(val);
19607
19989
  }
19990
+ *arguments() {
19991
+ yield this.value;
19992
+ }
19608
19993
  }
19609
19994
 
19610
19995
  class HTML extends TemplateImpl {
@@ -19617,6 +20002,336 @@ class HTML extends TemplateImpl {
19617
20002
  }
19618
20003
  }
19619
20004
 
20005
+ /**
20006
+ * A variable's segments and location, which can be coerced to a string.
20007
+ */
20008
+ class Variable {
20009
+ constructor(segments, location) {
20010
+ this.segments = segments;
20011
+ this.location = location;
20012
+ }
20013
+ toString() {
20014
+ return segmentsString(this.segments, true);
20015
+ }
20016
+ /** Return this variable's segments as an array, possibly with nested arrays for nested paths. */
20017
+ toArray() {
20018
+ function* _visit(...segments) {
20019
+ for (const segment of segments) {
20020
+ if (segment instanceof Variable) {
20021
+ yield Array.from(_visit(...segment.segments));
20022
+ }
20023
+ else {
20024
+ yield segment;
20025
+ }
20026
+ }
20027
+ }
20028
+ return Array.from(_visit(...this.segments));
20029
+ }
20030
+ }
20031
+ /**
20032
+ * Group variables by the string representation of their root segment.
20033
+ */
20034
+ class VariableMap {
20035
+ constructor() {
20036
+ this.map = new Map();
20037
+ }
20038
+ get(key) {
20039
+ const k = segmentsString([key.segments[0]]);
20040
+ if (!this.map.has(k)) {
20041
+ this.map.set(k, []);
20042
+ }
20043
+ return this.map.get(k);
20044
+ }
20045
+ has(key) {
20046
+ return this.map.has(segmentsString([key.segments[0]]));
20047
+ }
20048
+ push(variable) {
20049
+ this.get(variable).push(variable);
20050
+ }
20051
+ asObject() {
20052
+ return Object.fromEntries(this.map);
20053
+ }
20054
+ }
20055
+ const defaultStaticAnalysisOptions = {
20056
+ partials: true
20057
+ };
20058
+ function* _analyze(templates, partials, sync) {
20059
+ const variables = new VariableMap();
20060
+ const globals = new VariableMap();
20061
+ const locals = new VariableMap();
20062
+ const rootScope = new DummyScope(new Set());
20063
+ // Names of partial templates that we've already analyzed.
20064
+ const seen = new Set();
20065
+ function updateVariables(variable, scope) {
20066
+ variables.push(variable);
20067
+ const aliased = scope.alias(variable);
20068
+ if (aliased !== undefined) {
20069
+ const root = aliased.segments[0];
20070
+ // TODO: What if a a template renders a rendered template? Do we need scope.parent?
20071
+ if (isString(root) && !rootScope.has(root)) {
20072
+ globals.push(aliased);
20073
+ }
20074
+ }
20075
+ else {
20076
+ const root = variable.segments[0];
20077
+ if (isString(root) && !scope.has(root)) {
20078
+ globals.push(variable);
20079
+ }
20080
+ }
20081
+ // Recurse for nested Variables
20082
+ for (const segment of variable.segments) {
20083
+ if (segment instanceof Variable) {
20084
+ updateVariables(segment, scope);
20085
+ }
20086
+ }
20087
+ }
20088
+ function* visit(template, scope) {
20089
+ if (template.arguments) {
20090
+ for (const arg of template.arguments()) {
20091
+ for (const variable of extractVariables(arg)) {
20092
+ updateVariables(variable, scope);
20093
+ }
20094
+ }
20095
+ }
20096
+ if (template.localScope) {
20097
+ for (const ident of template.localScope()) {
20098
+ scope.add(ident.content);
20099
+ scope.deleteAlias(ident.content);
20100
+ const [row, col] = ident.getPosition();
20101
+ locals.push(new Variable([ident.content], { row, col, file: ident.file }));
20102
+ }
20103
+ }
20104
+ if (template.children) {
20105
+ if (template.partialScope) {
20106
+ const partial = template.partialScope();
20107
+ if (partial === undefined) {
20108
+ // Layouts, for example, can have children that are not partials.
20109
+ for (const child of (yield template.children(partials, sync))) {
20110
+ yield visit(child, scope);
20111
+ }
20112
+ return;
20113
+ }
20114
+ if (seen.has(partial.name))
20115
+ return;
20116
+ const partialScopeNames = new Set();
20117
+ const partialScope = partial.isolated
20118
+ ? new DummyScope(partialScopeNames)
20119
+ : scope.push(partialScopeNames);
20120
+ for (const name of partial.scope) {
20121
+ if (isString(name)) {
20122
+ partialScopeNames.add(name);
20123
+ }
20124
+ else {
20125
+ const [alias, argument] = name;
20126
+ partialScopeNames.add(alias);
20127
+ const variables = Array.from(extractVariables(argument));
20128
+ if (variables.length) {
20129
+ partialScope.setAlias(alias, variables[0].segments);
20130
+ }
20131
+ }
20132
+ }
20133
+ for (const child of (yield template.children(partials, sync))) {
20134
+ yield visit(child, partialScope);
20135
+ seen.add(partial.name);
20136
+ }
20137
+ partialScope.pop();
20138
+ }
20139
+ else {
20140
+ if (template.blockScope) {
20141
+ scope.push(new Set(template.blockScope()));
20142
+ }
20143
+ for (const child of (yield template.children(partials, sync))) {
20144
+ yield visit(child, scope);
20145
+ }
20146
+ if (template.blockScope) {
20147
+ scope.pop();
20148
+ }
20149
+ }
20150
+ }
20151
+ }
20152
+ for (const template of templates) {
20153
+ yield visit(template, rootScope);
20154
+ }
20155
+ return {
20156
+ variables: variables.asObject(),
20157
+ globals: globals.asObject(),
20158
+ locals: locals.asObject()
20159
+ };
20160
+ }
20161
+ /**
20162
+ * Statically analyze a template and report variable usage.
20163
+ */
20164
+ function analyze(template, options = {}) {
20165
+ const opts = Object.assign(Object.assign({}, defaultStaticAnalysisOptions), options);
20166
+ return toPromise(_analyze(template, opts.partials, false));
20167
+ }
20168
+ /**
20169
+ * Statically analyze a template and report variable usage.
20170
+ */
20171
+ function analyzeSync(template, options = {}) {
20172
+ const opts = Object.assign(Object.assign({}, defaultStaticAnalysisOptions), options);
20173
+ return toValueSync(_analyze(template, opts.partials, true));
20174
+ }
20175
+ /**
20176
+ * A stack to manage scopes while traversing templates during static analysis.
20177
+ */
20178
+ class DummyScope {
20179
+ constructor(globals) {
20180
+ this.stack = [{ names: globals, aliases: new Map() }];
20181
+ }
20182
+ /** Return true if `name` is in scope. */
20183
+ has(name) {
20184
+ for (const scope of this.stack) {
20185
+ if (scope.names.has(name)) {
20186
+ return true;
20187
+ }
20188
+ }
20189
+ return false;
20190
+ }
20191
+ push(scope) {
20192
+ this.stack.push({ names: scope, aliases: new Map() });
20193
+ return this;
20194
+ }
20195
+ pop() {
20196
+ var _a;
20197
+ return (_a = this.stack.pop()) === null || _a === void 0 ? void 0 : _a.names;
20198
+ }
20199
+ // Add a name to the template scope.
20200
+ add(name) {
20201
+ this.stack[0].names.add(name);
20202
+ }
20203
+ /** Return the variable that `variable` aliases, or `variable` if it doesn't alias anything. */
20204
+ alias(variable) {
20205
+ const root = variable.segments[0];
20206
+ if (!isString(root))
20207
+ return undefined;
20208
+ const alias = this.getAlias(root);
20209
+ if (alias === undefined)
20210
+ return undefined;
20211
+ return new Variable([...alias, ...variable.segments.slice(1)], variable.location);
20212
+ }
20213
+ // TODO: `from` could be a path with multiple segments, like `include.x`.
20214
+ setAlias(from, to) {
20215
+ this.stack[this.stack.length - 1].aliases.set(from, to);
20216
+ }
20217
+ deleteAlias(name) {
20218
+ this.stack[this.stack.length - 1].aliases.delete(name);
20219
+ }
20220
+ getAlias(name) {
20221
+ for (const scope of this.stack) {
20222
+ if (scope.aliases.has(name)) {
20223
+ return scope.aliases.get(name);
20224
+ }
20225
+ // If a scope has defined `name`, then it masks aliases in parent scopes.
20226
+ if (scope.names.has(name)) {
20227
+ return undefined;
20228
+ }
20229
+ }
20230
+ return undefined;
20231
+ }
20232
+ }
20233
+ function* extractVariables(value) {
20234
+ if (isValueToken(value)) {
20235
+ yield* extractValueTokenVariables(value);
20236
+ }
20237
+ else if (value instanceof Value) {
20238
+ yield* extractFilteredValueVariables(value);
20239
+ }
20240
+ }
20241
+ function* extractFilteredValueVariables(value) {
20242
+ for (const token of value.initial.postfix) {
20243
+ if (isValueToken(token)) {
20244
+ yield* extractValueTokenVariables(token);
20245
+ }
20246
+ }
20247
+ for (const filter of value.filters) {
20248
+ for (const arg of filter.args) {
20249
+ if (isKeyValuePair(arg) && arg[1]) {
20250
+ yield* extractValueTokenVariables(arg[1]);
20251
+ }
20252
+ else if (isValueToken(arg)) {
20253
+ yield* extractValueTokenVariables(arg);
20254
+ }
20255
+ }
20256
+ }
20257
+ }
20258
+ function* extractValueTokenVariables(token) {
20259
+ if (isRangeToken(token)) {
20260
+ yield* extractValueTokenVariables(token.lhs);
20261
+ yield* extractValueTokenVariables(token.rhs);
20262
+ }
20263
+ else if (isPropertyAccessToken(token)) {
20264
+ yield extractPropertyAccessVariable(token);
20265
+ }
20266
+ }
20267
+ function extractPropertyAccessVariable(token) {
20268
+ const segments = [];
20269
+ // token is not guaranteed to have `file` set. We'll try to get it from a prop if not.
20270
+ let file = token.file;
20271
+ // Here we're flattening the first segment of a path if it is a nested path.
20272
+ const root = token.props[0];
20273
+ file = file || root.file;
20274
+ if (isQuotedToken(root) || isNumberToken(root) || isWordToken(root)) {
20275
+ segments.push(root.content);
20276
+ }
20277
+ else if (isPropertyAccessToken(root)) {
20278
+ // Flatten paths that start with a nested path.
20279
+ segments.push(...extractPropertyAccessVariable(root).segments);
20280
+ }
20281
+ for (const prop of token.props.slice(1)) {
20282
+ file = file || prop.file;
20283
+ if (isQuotedToken(prop) || isNumberToken(prop) || isWordToken(prop)) {
20284
+ segments.push(prop.content);
20285
+ }
20286
+ else if (isPropertyAccessToken(prop)) {
20287
+ segments.push(extractPropertyAccessVariable(prop));
20288
+ }
20289
+ }
20290
+ const [row, col] = token.getPosition();
20291
+ return new Variable(segments, {
20292
+ row,
20293
+ col,
20294
+ file
20295
+ });
20296
+ }
20297
+ // This is used to detect segments that can be represented with dot notation
20298
+ // when creating a string representation of VariableSegments.
20299
+ const RE_PROPERTY = /^[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*$/;
20300
+ /**
20301
+ * Return a string representation of segments using dot notation where possible.
20302
+ * @param segments - The property names and array indices that make up a path to a variable.
20303
+ * @param bracketedRoot - If false (the default), don't surround the root segment with square brackets.
20304
+ */
20305
+ function segmentsString(segments, bracketedRoot = false) {
20306
+ const buf = [];
20307
+ const root = segments[0];
20308
+ if (isString(root)) {
20309
+ if (!bracketedRoot || root.match(RE_PROPERTY)) {
20310
+ buf.push(`${root}`);
20311
+ }
20312
+ else {
20313
+ buf.push(`['${root}']`);
20314
+ }
20315
+ }
20316
+ for (const segment of segments.slice(1)) {
20317
+ if (segment instanceof Variable) {
20318
+ buf.push(`[${segmentsString(segment.segments)}]`);
20319
+ }
20320
+ else if (isString(segment)) {
20321
+ if (segment.match(RE_PROPERTY)) {
20322
+ buf.push(`.${segment}`);
20323
+ }
20324
+ else {
20325
+ buf.push(`['${segment}']`);
20326
+ }
20327
+ }
20328
+ else {
20329
+ buf.push(`[${segment}]`);
20330
+ }
20331
+ }
20332
+ return buf.join('');
20333
+ }
20334
+
19620
20335
  var LookupType;
19621
20336
  (function (LookupType) {
19622
20337
  LookupType["Partials"] = "partials";
@@ -19633,7 +20348,7 @@ class Loader {
19633
20348
  this.shouldLoadRelative = (referencedFile) => rRelativePath.test(referencedFile);
19634
20349
  }
19635
20350
  else {
19636
- this.shouldLoadRelative = (referencedFile) => false;
20351
+ this.shouldLoadRelative = (_referencedFile) => false;
19637
20352
  }
19638
20353
  this.contains = this.options.fs.contains || (() => true);
19639
20354
  }
@@ -19690,8 +20405,11 @@ class Parser {
19690
20405
  this.fs = this.liquid.options.fs;
19691
20406
  this.parseFile = this.cache ? this._parseFileCached : this._parseFile;
19692
20407
  this.loader = new Loader(this.liquid.options);
20408
+ this.parseLimit = new Limiter('parse length', liquid.options.parseLimit);
19693
20409
  }
19694
20410
  parse(html, filepath) {
20411
+ html = String(html);
20412
+ this.parseLimit.use(html.length);
19695
20413
  const tokenizer = new Tokenizer(html, this.liquid.options.operators, filepath);
19696
20414
  const tokens = tokenizer.readTopLevelTokens(this.liquid.options);
19697
20415
  return this.parseTokens(tokens);
@@ -19699,9 +20417,20 @@ class Parser {
19699
20417
  parseTokens(tokens) {
19700
20418
  let token;
19701
20419
  const templates = [];
20420
+ const errors = [];
19702
20421
  while ((token = tokens.shift())) {
19703
- templates.push(this.parseToken(token, tokens));
20422
+ try {
20423
+ templates.push(this.parseToken(token, tokens));
20424
+ }
20425
+ catch (err) {
20426
+ if (this.liquid.options.catchAllErrors)
20427
+ errors.push(err);
20428
+ else
20429
+ throw err;
20430
+ }
19704
20431
  }
20432
+ if (errors.length)
20433
+ throw new LiquidErrors(errors);
19705
20434
  return templates;
19706
20435
  }
19707
20436
  parseToken(token, remainTokens) {
@@ -19709,7 +20438,7 @@ class Parser {
19709
20438
  if (isTagToken(token)) {
19710
20439
  const TagClass = this.liquid.tags[token.name];
19711
20440
  assert(TagClass, `tag "${token.name}" not found`);
19712
- return new TagClass(token, remainTokens, this.liquid);
20441
+ return new TagClass(token, remainTokens, this.liquid, this);
19713
20442
  }
19714
20443
  if (isOutputToken(token)) {
19715
20444
  return new Output(token, this.liquid);
@@ -19717,6 +20446,8 @@ class Parser {
19717
20446
  return new HTML(token);
19718
20447
  }
19719
20448
  catch (e) {
20449
+ if (LiquidError.is(e))
20450
+ throw e;
19720
20451
  throw new ParseError(e, token);
19721
20452
  }
19722
20453
  }
@@ -19745,63 +20476,10 @@ class Parser {
19745
20476
  }
19746
20477
  *_parseFile(file, sync, type = LookupType.Root, currentFile) {
19747
20478
  const filepath = yield this.loader.lookup(file, type, sync, currentFile);
19748
- return this.liquid.parse(sync ? this.fs.readFileSync(filepath) : yield this.fs.readFile(filepath), filepath);
20479
+ return this.parse(sync ? this.fs.readFileSync(filepath) : yield this.fs.readFile(filepath), filepath);
19749
20480
  }
19750
20481
  }
19751
20482
 
19752
- const rHex = /[\da-fA-F]/;
19753
- const rOct = /[0-7]/;
19754
- const escapeChar = {
19755
- b: '\b',
19756
- f: '\f',
19757
- n: '\n',
19758
- r: '\r',
19759
- t: '\t',
19760
- v: '\x0B'
19761
- };
19762
- function hexVal(c) {
19763
- const code = c.charCodeAt(0);
19764
- if (code >= 97)
19765
- return code - 87;
19766
- if (code >= 65)
19767
- return code - 55;
19768
- return code - 48;
19769
- }
19770
- function parseStringLiteral(str) {
19771
- let ret = '';
19772
- for (let i = 1; i < str.length - 1; i++) {
19773
- if (str[i] !== '\\') {
19774
- ret += str[i];
19775
- continue;
19776
- }
19777
- if (escapeChar[str[i + 1]] !== undefined) {
19778
- ret += escapeChar[str[++i]];
19779
- }
19780
- else if (str[i + 1] === 'u') {
19781
- let val = 0;
19782
- let j = i + 2;
19783
- while (j <= i + 5 && rHex.test(str[j])) {
19784
- val = val * 16 + hexVal(str[j++]);
19785
- }
19786
- i = j - 1;
19787
- ret += String.fromCharCode(val);
19788
- }
19789
- else if (!rOct.test(str[i + 1])) {
19790
- ret += str[++i];
19791
- }
19792
- else {
19793
- let j = i + 1;
19794
- let val = 0;
19795
- while (j <= i + 3 && rOct.test(str[j])) {
19796
- val = val * 8 + hexVal(str[j++]);
19797
- }
19798
- i = j - 1;
19799
- ret += String.fromCharCode(val);
19800
- }
19801
- }
19802
- return ret;
19803
- }
19804
-
19805
20483
  var TokenKind;
19806
20484
  (function (TokenKind) {
19807
20485
  TokenKind[TokenKind["Number"] = 1] = "Number";
@@ -19816,6 +20494,7 @@ var TokenKind;
19816
20494
  TokenKind[TokenKind["Range"] = 512] = "Range";
19817
20495
  TokenKind[TokenKind["Quoted"] = 1024] = "Quoted";
19818
20496
  TokenKind[TokenKind["Operator"] = 2048] = "Operator";
20497
+ TokenKind[TokenKind["FilteredValue"] = 4096] = "FilteredValue";
19819
20498
  TokenKind[TokenKind["Delimited"] = 12] = "Delimited";
19820
20499
  })(TokenKind || (TokenKind = {}));
19821
20500
 
@@ -19837,9 +20516,6 @@ function isTagToken(val) {
19837
20516
  function isQuotedToken(val) {
19838
20517
  return getKind(val) === TokenKind.Quoted;
19839
20518
  }
19840
- function isLiteralToken(val) {
19841
- return getKind(val) === TokenKind.Literal;
19842
- }
19843
20519
  function isNumberToken(val) {
19844
20520
  return getKind(val) === TokenKind.Number;
19845
20521
  }
@@ -19852,25 +20528,33 @@ function isWordToken(val) {
19852
20528
  function isRangeToken(val) {
19853
20529
  return getKind(val) === TokenKind.Range;
19854
20530
  }
20531
+ function isValueToken(val) {
20532
+ // valueTokenBitMask = TokenKind.Number | TokenKind.Literal | TokenKind.Quoted | TokenKind.PropertyAccess | TokenKind.Range
20533
+ return (getKind(val) & 1667) > 0;
20534
+ }
19855
20535
  function getKind(val) {
19856
20536
  return val ? val.kind : -1;
19857
20537
  }
19858
20538
 
19859
20539
  class Context {
19860
- constructor(env = {}, opts = defaultOptions, renderOptions = {}) {
19861
- var _a, _b, _c;
20540
+ constructor(env = {}, opts = defaultOptions, renderOptions = {}, { memoryLimit, renderLimit } = {}) {
20541
+ var _a, _b, _c, _d, _e;
19862
20542
  /**
19863
20543
  * insert a Context-level empty scope,
19864
20544
  * for tags like `{% capture %}` `{% assign %}` to operate
19865
20545
  */
19866
20546
  this.scopes = [{}];
19867
20547
  this.registers = {};
20548
+ this.breakCalled = false;
20549
+ this.continueCalled = false;
19868
20550
  this.sync = !!renderOptions.sync;
19869
20551
  this.opts = opts;
19870
20552
  this.globals = (_a = renderOptions.globals) !== null && _a !== void 0 ? _a : opts.globals;
19871
- this.environments = env;
20553
+ this.environments = isObject(env) ? env : Object(env);
19872
20554
  this.strictVariables = (_b = renderOptions.strictVariables) !== null && _b !== void 0 ? _b : this.opts.strictVariables;
19873
20555
  this.ownPropertyOnly = (_c = renderOptions.ownPropertyOnly) !== null && _c !== void 0 ? _c : opts.ownPropertyOnly;
20556
+ this.memoryLimit = memoryLimit !== null && memoryLimit !== void 0 ? memoryLimit : new Limiter('memory alloc', (_d = renderOptions.memoryLimit) !== null && _d !== void 0 ? _d : opts.memoryLimit);
20557
+ this.renderLimit = renderLimit !== null && renderLimit !== void 0 ? renderLimit : new Limiter('template render', getPerformance().now() + ((_e = renderOptions.renderLimit) !== null && _e !== void 0 ? _e : opts.renderLimit));
19874
20558
  }
19875
20559
  getRegister(key) {
19876
20560
  return (this.registers[key] = this.registers[key] || {});
@@ -19898,7 +20582,7 @@ class Context {
19898
20582
  return toValueSync(this._get(paths));
19899
20583
  }
19900
20584
  *_get(paths) {
19901
- const scope = this.findScope(paths[0]);
20585
+ const scope = this.findScope(paths[0]); // first prop should always be a string
19902
20586
  return yield this._getFromScope(scope, paths);
19903
20587
  }
19904
20588
  /**
@@ -19907,12 +20591,12 @@ class Context {
19907
20591
  getFromScope(scope, paths) {
19908
20592
  return toValueSync(this._getFromScope(scope, paths));
19909
20593
  }
19910
- *_getFromScope(scope, paths) {
20594
+ *_getFromScope(scope, paths, strictVariables = this.strictVariables) {
19911
20595
  if (isString(paths))
19912
20596
  paths = paths.split('.');
19913
20597
  for (let i = 0; i < paths.length; i++) {
19914
20598
  scope = yield readProperty(scope, paths[i], this.ownPropertyOnly);
19915
- if (isNil(scope) && this.strictVariables) {
20599
+ if (strictVariables && isUndefined(scope)) {
19916
20600
  throw new InternalUndefinedVariableError(paths.slice(0, i + 1).join('.'));
19917
20601
  }
19918
20602
  }
@@ -19927,6 +20611,16 @@ class Context {
19927
20611
  bottom() {
19928
20612
  return this.scopes[0];
19929
20613
  }
20614
+ spawn(scope = {}) {
20615
+ return new Context(scope, this.opts, {
20616
+ sync: this.sync,
20617
+ globals: this.globals,
20618
+ strictVariables: this.strictVariables
20619
+ }, {
20620
+ renderLimit: this.renderLimit,
20621
+ memoryLimit: this.memoryLimit
20622
+ });
20623
+ }
19930
20624
  findScope(key) {
19931
20625
  for (let i = this.scopes.length - 1; i >= 0; i--) {
19932
20626
  const candidate = this.scopes[i];
@@ -19940,6 +20634,7 @@ class Context {
19940
20634
  }
19941
20635
  function readProperty(obj, key, ownPropertyOnly) {
19942
20636
  obj = toLiquid(obj);
20637
+ key = toValue(key);
19943
20638
  if (isNil(obj))
19944
20639
  return obj;
19945
20640
  if (isArray(obj) && key < 0)
@@ -19958,7 +20653,7 @@ function readProperty(obj, key, ownPropertyOnly) {
19958
20653
  return value;
19959
20654
  }
19960
20655
  function readJSProperty(obj, key, ownPropertyOnly) {
19961
- if (ownPropertyOnly && !Object.hasOwnProperty.call(obj, key) && !(obj instanceof Drop))
20656
+ if (ownPropertyOnly && !hasOwnProperty.call(obj, key) && !(obj instanceof Drop))
19962
20657
  return undefined;
19963
20658
  return obj[key];
19964
20659
  }
@@ -19973,7 +20668,7 @@ function readLast(obj) {
19973
20668
  return obj['last'];
19974
20669
  }
19975
20670
  function readSize(obj) {
19976
- if (obj.hasOwnProperty('size') || obj['size'] !== undefined)
20671
+ if (hasOwnProperty.call(obj, 'size') || obj['size'] !== undefined)
19977
20672
  return obj['size'];
19978
20673
  if (isArray(obj) || isString(obj))
19979
20674
  return obj.length;
@@ -20025,25 +20720,83 @@ var mathFilters = /*#__PURE__*/Object.freeze({
20025
20720
  plus: plus
20026
20721
  });
20027
20722
 
20028
- const url_decode = (x) => stringify(x).split('+').map(decodeURIComponent).join(' ');
20029
- const url_encode = (x) => stringify(x).split(' ').map(encodeURIComponent).join('+');
20723
+ const url_decode = (x) => decodeURIComponent(stringify(x)).replace(/\+/g, ' ');
20724
+ const url_encode = (x) => encodeURIComponent(stringify(x)).replace(/%20/g, '+');
20725
+ const cgi_escape = (x) => encodeURIComponent(stringify(x))
20726
+ .replace(/%20/g, '+')
20727
+ .replace(/[!'()*]/g, c => '%' + c.charCodeAt(0).toString(16).toUpperCase());
20728
+ const uri_escape = (x) => encodeURI(stringify(x))
20729
+ .replace(/%5B/g, '[')
20730
+ .replace(/%5D/g, ']');
20731
+ const rSlugifyDefault = /[^\p{M}\p{L}\p{Nd}]+/ug;
20732
+ const rSlugifyReplacers = {
20733
+ 'raw': /\s+/g,
20734
+ 'default': rSlugifyDefault,
20735
+ 'pretty': /[^\p{M}\p{L}\p{Nd}._~!$&'()+,;=@]+/ug,
20736
+ 'ascii': /[^A-Za-z0-9]+/g,
20737
+ 'latin': rSlugifyDefault,
20738
+ 'none': null
20739
+ };
20740
+ function slugify(str, mode = 'default', cased = false) {
20741
+ str = stringify(str);
20742
+ const replacer = rSlugifyReplacers[mode];
20743
+ if (replacer) {
20744
+ if (mode === 'latin')
20745
+ str = removeAccents(str);
20746
+ str = str.replace(replacer, '-').replace(/^-|-$/g, '');
20747
+ }
20748
+ return cased ? str : str.toLowerCase();
20749
+ }
20750
+ function removeAccents(str) {
20751
+ return str.replace(/[àáâãäå]/g, 'a')
20752
+ .replace(/[æ]/g, 'ae')
20753
+ .replace(/[ç]/g, 'c')
20754
+ .replace(/[èéêë]/g, 'e')
20755
+ .replace(/[ìíîï]/g, 'i')
20756
+ .replace(/[ð]/g, 'd')
20757
+ .replace(/[ñ]/g, 'n')
20758
+ .replace(/[òóôõöø]/g, 'o')
20759
+ .replace(/[ùúûü]/g, 'u')
20760
+ .replace(/[ýÿ]/g, 'y')
20761
+ .replace(/[ß]/g, 'ss')
20762
+ .replace(/[œ]/g, 'oe')
20763
+ .replace(/[þ]/g, 'th')
20764
+ .replace(/[ẞ]/g, 'SS')
20765
+ .replace(/[Œ]/g, 'OE')
20766
+ .replace(/[Þ]/g, 'TH');
20767
+ }
20030
20768
 
20031
20769
  var urlFilters = /*#__PURE__*/Object.freeze({
20032
20770
  __proto__: null,
20033
20771
  url_decode: url_decode,
20034
- url_encode: url_encode
20772
+ url_encode: url_encode,
20773
+ cgi_escape: cgi_escape,
20774
+ uri_escape: uri_escape,
20775
+ slugify: slugify
20035
20776
  });
20036
20777
 
20037
- const join = argumentsToValue((v, arg) => toArray(v).join(arg === undefined ? ' ' : arg));
20038
- const last$1 = argumentsToValue((v) => isArray(v) ? last(v) : '');
20039
- const first = argumentsToValue((v) => isArray(v) ? v[0] : '');
20040
- const reverse = argumentsToValue((v) => [...toArray(v)].reverse());
20778
+ const join = argumentsToValue(function (v, arg) {
20779
+ const array = toArray(v);
20780
+ const sep = isNil(arg) ? ' ' : stringify(arg);
20781
+ const complexity = array.length * (1 + sep.length);
20782
+ this.context.memoryLimit.use(complexity);
20783
+ return array.join(sep);
20784
+ });
20785
+ const last$1 = argumentsToValue((v) => isArrayLike(v) ? last(v) : '');
20786
+ const first = argumentsToValue((v) => isArrayLike(v) ? v[0] : '');
20787
+ const reverse = argumentsToValue(function (v) {
20788
+ const array = toArray(v);
20789
+ this.context.memoryLimit.use(array.length);
20790
+ return [...array].reverse();
20791
+ });
20041
20792
  function* sort(arr, property) {
20042
20793
  const values = [];
20043
- for (const item of toArray(toValue(arr))) {
20794
+ const array = toArray(arr);
20795
+ this.context.memoryLimit.use(array.length);
20796
+ for (const item of array) {
20044
20797
  values.push([
20045
20798
  item,
20046
- property ? yield this.context._getFromScope(item, stringify(property).split('.')) : item
20799
+ property ? yield this.context._getFromScope(item, stringify(property).split('.'), false) : item
20047
20800
  ]);
20048
20801
  }
20049
20802
  return values.sort((lhs, rhs) => {
@@ -20053,29 +20806,65 @@ function* sort(arr, property) {
20053
20806
  }).map(tuple => tuple[0]);
20054
20807
  }
20055
20808
  function sort_natural(input, property) {
20056
- input = toValue(input);
20057
20809
  const propertyString = stringify(property);
20058
20810
  const compare = property === undefined
20059
20811
  ? caseInsensitiveCompare
20060
20812
  : (lhs, rhs) => caseInsensitiveCompare(lhs[propertyString], rhs[propertyString]);
20061
- return [...toArray(input)].sort(compare);
20813
+ const array = toArray(input);
20814
+ this.context.memoryLimit.use(array.length);
20815
+ return [...array].sort(compare);
20062
20816
  }
20063
20817
  const size = (v) => (v && v.length) || 0;
20064
20818
  function* map(arr, property) {
20065
20819
  const results = [];
20066
- for (const item of toArray(toValue(arr))) {
20067
- results.push(yield this.context._getFromScope(item, stringify(property).split('.')));
20820
+ const array = toArray(arr);
20821
+ this.context.memoryLimit.use(array.length);
20822
+ for (const item of array) {
20823
+ results.push(yield this.context._getFromScope(item, stringify(property), false));
20068
20824
  }
20069
20825
  return results;
20070
20826
  }
20827
+ function* sum(arr, property) {
20828
+ let sum = 0;
20829
+ const array = toArray(arr);
20830
+ for (const item of array) {
20831
+ const data = Number(property ? yield this.context._getFromScope(item, stringify(property), false) : item);
20832
+ sum += Number.isNaN(data) ? 0 : data;
20833
+ }
20834
+ return sum;
20835
+ }
20071
20836
  function compact(arr) {
20072
- arr = toValue(arr);
20073
- return toArray(arr).filter(x => !isNil(toValue(x)));
20837
+ const array = toArray(arr);
20838
+ this.context.memoryLimit.use(array.length);
20839
+ return array.filter(x => !isNil(toValue(x)));
20074
20840
  }
20075
20841
  function concat(v, arg = []) {
20076
- v = toValue(v);
20077
- arg = toArray(arg).map(v => toValue(v));
20078
- return toArray(v).concat(arg);
20842
+ const lhs = toArray(v);
20843
+ const rhs = toArray(arg);
20844
+ this.context.memoryLimit.use(lhs.length + rhs.length);
20845
+ return lhs.concat(rhs);
20846
+ }
20847
+ function push(v, arg) {
20848
+ return concat.call(this, v, [arg]);
20849
+ }
20850
+ function unshift(v, arg) {
20851
+ const array = toArray(v);
20852
+ this.context.memoryLimit.use(array.length);
20853
+ const clone = [...array];
20854
+ clone.unshift(arg);
20855
+ return clone;
20856
+ }
20857
+ function pop(v) {
20858
+ const clone = [...toArray(v)];
20859
+ clone.pop();
20860
+ return clone;
20861
+ }
20862
+ function shift(v) {
20863
+ const array = toArray(v);
20864
+ this.context.memoryLimit.use(array.length);
20865
+ const clone = [...array];
20866
+ clone.shift();
20867
+ return clone;
20079
20868
  }
20080
20869
  function slice(v, begin, length = 1) {
20081
20870
  v = toValue(v);
@@ -20084,31 +20873,98 @@ function slice(v, begin, length = 1) {
20084
20873
  if (!isArray(v))
20085
20874
  v = stringify(v);
20086
20875
  begin = begin < 0 ? v.length + begin : begin;
20876
+ this.context.memoryLimit.use(length);
20087
20877
  return v.slice(begin, begin + length);
20088
20878
  }
20089
20879
  function* where(arr, property, expected) {
20090
20880
  const values = [];
20091
- arr = toArray(toValue(arr));
20881
+ arr = toArray(arr);
20882
+ this.context.memoryLimit.use(arr.length);
20883
+ const token = new Tokenizer(stringify(property)).readScopeValue();
20092
20884
  for (const item of arr) {
20093
- values.push(yield this.context._getFromScope(item, stringify(property).split('.')));
20885
+ values.push(yield evalToken(token, this.context.spawn(item)));
20094
20886
  }
20887
+ const matcher = this.context.opts.jekyllWhere
20888
+ ? (v) => EmptyDrop.is(expected) ? equals(v, expected) : (isArray(v) ? arrayIncludes(v, expected) : equals(v, expected))
20889
+ : (v) => equals(v, expected);
20095
20890
  return arr.filter((_, i) => {
20096
20891
  if (expected === undefined)
20097
20892
  return isTruthy(values[i], this.context);
20098
- if (isComparable(expected))
20099
- return expected.equals(values[i]);
20100
- return values[i] === expected;
20893
+ return matcher(values[i]);
20101
20894
  });
20102
20895
  }
20896
+ function* where_exp(arr, itemName, exp) {
20897
+ const filtered = [];
20898
+ const keyTemplate = new Value(stringify(exp), this.liquid);
20899
+ const array = toArray(arr);
20900
+ this.context.memoryLimit.use(array.length);
20901
+ for (const item of array) {
20902
+ const value = yield keyTemplate.value(this.context.spawn({ [itemName]: item }));
20903
+ if (value)
20904
+ filtered.push(item);
20905
+ }
20906
+ return filtered;
20907
+ }
20908
+ function* group_by(arr, property) {
20909
+ const map = new Map();
20910
+ arr = toEnumerable(arr);
20911
+ const token = new Tokenizer(stringify(property)).readScopeValue();
20912
+ this.context.memoryLimit.use(arr.length);
20913
+ for (const item of arr) {
20914
+ const key = yield evalToken(token, this.context.spawn(item));
20915
+ if (!map.has(key))
20916
+ map.set(key, []);
20917
+ map.get(key).push(item);
20918
+ }
20919
+ return [...map.entries()].map(([name, items]) => ({ name, items }));
20920
+ }
20921
+ function* group_by_exp(arr, itemName, exp) {
20922
+ const map = new Map();
20923
+ const keyTemplate = new Value(stringify(exp), this.liquid);
20924
+ arr = toEnumerable(arr);
20925
+ this.context.memoryLimit.use(arr.length);
20926
+ for (const item of arr) {
20927
+ const key = yield keyTemplate.value(this.context.spawn({ [itemName]: item }));
20928
+ if (!map.has(key))
20929
+ map.set(key, []);
20930
+ map.get(key).push(item);
20931
+ }
20932
+ return [...map.entries()].map(([name, items]) => ({ name, items }));
20933
+ }
20934
+ function* find(arr, property, expected) {
20935
+ const token = new Tokenizer(stringify(property)).readScopeValue();
20936
+ const array = toArray(arr);
20937
+ for (const item of array) {
20938
+ const value = yield evalToken(token, this.context.spawn(item));
20939
+ if (equals(value, expected))
20940
+ return item;
20941
+ }
20942
+ }
20943
+ function* find_exp(arr, itemName, exp) {
20944
+ const predicate = new Value(stringify(exp), this.liquid);
20945
+ const array = toArray(arr);
20946
+ for (const item of array) {
20947
+ const value = yield predicate.value(this.context.spawn({ [itemName]: item }));
20948
+ if (value)
20949
+ return item;
20950
+ }
20951
+ }
20103
20952
  function uniq(arr) {
20104
- arr = toValue(arr);
20105
- const u = {};
20106
- return (arr || []).filter(val => {
20107
- if (hasOwnProperty.call(u, String(val)))
20108
- return false;
20109
- u[String(val)] = true;
20110
- return true;
20111
- });
20953
+ arr = toArray(arr);
20954
+ this.context.memoryLimit.use(arr.length);
20955
+ return [...new Set(arr)];
20956
+ }
20957
+ function sample(v, count = 1) {
20958
+ v = toValue(v);
20959
+ if (isNil(v))
20960
+ return [];
20961
+ if (!isArray(v))
20962
+ v = stringify(v);
20963
+ this.context.memoryLimit.use(count);
20964
+ const shuffled = [...v].sort(() => Math.random() - 0.5);
20965
+ if (count === 1)
20966
+ return shuffled[0];
20967
+ return shuffled.slice(0, count);
20112
20968
  }
20113
20969
 
20114
20970
  var arrayFilters = /*#__PURE__*/Object.freeze({
@@ -20121,69 +20977,94 @@ var arrayFilters = /*#__PURE__*/Object.freeze({
20121
20977
  sort_natural: sort_natural,
20122
20978
  size: size,
20123
20979
  map: map,
20980
+ sum: sum,
20124
20981
  compact: compact,
20125
20982
  concat: concat,
20983
+ push: push,
20984
+ unshift: unshift,
20985
+ pop: pop,
20986
+ shift: shift,
20126
20987
  slice: slice,
20127
20988
  where: where,
20128
- uniq: uniq
20989
+ where_exp: where_exp,
20990
+ group_by: group_by,
20991
+ group_by_exp: group_by_exp,
20992
+ find: find,
20993
+ find_exp: find_exp,
20994
+ uniq: uniq,
20995
+ sample: sample
20129
20996
  });
20130
20997
 
20131
20998
  function date(v, format, timezoneOffset) {
20132
- const opts = this.context.opts;
20999
+ var _a, _b, _c;
21000
+ const size = ((_a = v === null || v === void 0 ? void 0 : v.length) !== null && _a !== void 0 ? _a : 0) + ((_b = format === null || format === void 0 ? void 0 : format.length) !== null && _b !== void 0 ? _b : 0) + ((_c = timezoneOffset === null || timezoneOffset === void 0 ? void 0 : timezoneOffset.length) !== null && _c !== void 0 ? _c : 0);
21001
+ this.context.memoryLimit.use(size);
21002
+ const date = parseDate(v, this.context.opts, timezoneOffset);
21003
+ if (!date)
21004
+ return v;
21005
+ format = toValue(format);
21006
+ format = isNil(format) ? this.context.opts.dateFormat : stringify(format);
21007
+ return strftime(date, format);
21008
+ }
21009
+ function date_to_xmlschema(v) {
21010
+ return date.call(this, v, '%Y-%m-%dT%H:%M:%S%:z');
21011
+ }
21012
+ function date_to_rfc822(v) {
21013
+ return date.call(this, v, '%a, %d %b %Y %H:%M:%S %z');
21014
+ }
21015
+ function date_to_string(v, type, style) {
21016
+ return stringify_date.call(this, v, '%b', type, style);
21017
+ }
21018
+ function date_to_long_string(v, type, style) {
21019
+ return stringify_date.call(this, v, '%B', type, style);
21020
+ }
21021
+ function stringify_date(v, month_type, type, style) {
21022
+ const date = parseDate(v, this.context.opts);
21023
+ if (!date)
21024
+ return v;
21025
+ if (type === 'ordinal') {
21026
+ const d = date.getDate();
21027
+ return style === 'US'
21028
+ ? strftime(date, `${month_type} ${d}%q, %Y`)
21029
+ : strftime(date, `${d}%q ${month_type} %Y`);
21030
+ }
21031
+ return strftime(date, `%d ${month_type} %Y`);
21032
+ }
21033
+ function parseDate(v, opts, timezoneOffset) {
20133
21034
  let date;
21035
+ const defaultTimezoneOffset = timezoneOffset !== null && timezoneOffset !== void 0 ? timezoneOffset : opts.timezoneOffset;
21036
+ const locale = opts.locale;
20134
21037
  v = toValue(v);
20135
- format = toValue(format);
20136
- if (isNil(format))
20137
- format = opts.dateFormat;
20138
- else
20139
- format = stringify(format);
20140
21038
  if (v === 'now' || v === 'today') {
20141
- date = new Date();
21039
+ date = new LiquidDate(Date.now(), locale, defaultTimezoneOffset);
20142
21040
  }
20143
21041
  else if (isNumber(v)) {
20144
- date = new Date(v * 1000);
21042
+ date = new LiquidDate(v * 1000, locale, defaultTimezoneOffset);
20145
21043
  }
20146
21044
  else if (isString(v)) {
20147
21045
  if (/^\d+$/.test(v)) {
20148
- date = new Date(+v * 1000);
21046
+ date = new LiquidDate(+v * 1000, locale, defaultTimezoneOffset);
20149
21047
  }
20150
- else if (opts.preserveTimezones) {
20151
- date = TimezoneDate.createDateFixedToTimezone(v);
21048
+ else if (opts.preserveTimezones && timezoneOffset === undefined) {
21049
+ date = LiquidDate.createDateFixedToTimezone(v, locale);
20152
21050
  }
20153
21051
  else {
20154
- date = new Date(v);
21052
+ date = new LiquidDate(v, locale, defaultTimezoneOffset);
20155
21053
  }
20156
21054
  }
20157
21055
  else {
20158
- date = v;
20159
- }
20160
- if (!isValidDate(date))
20161
- return v;
20162
- if (timezoneOffset !== undefined) {
20163
- date = new TimezoneDate(date, parseTimezoneOffset(date, timezoneOffset));
21056
+ date = new LiquidDate(v, locale, defaultTimezoneOffset);
20164
21057
  }
20165
- else if (!(date instanceof TimezoneDate) && opts.timezoneOffset !== undefined) {
20166
- date = new TimezoneDate(date, parseTimezoneOffset(date, opts.timezoneOffset));
20167
- }
20168
- return strftime(date, format);
20169
- }
20170
- function isValidDate(date) {
20171
- return (date instanceof Date || date instanceof TimezoneDate) && !isNaN(date.getTime());
20172
- }
20173
- /**
20174
- * need pass in a `date` because offset is dependent on whether DST is active
20175
- */
20176
- function parseTimezoneOffset(date, timeZone) {
20177
- if (isNumber(timeZone))
20178
- return timeZone;
20179
- const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
20180
- const tzDate = new Date(date.toLocaleString('en-US', { timeZone }));
20181
- return (utcDate.getTime() - tzDate.getTime()) / 6e4;
21058
+ return date.valid() ? date : undefined;
20182
21059
  }
20183
21060
 
20184
21061
  var dateFilters = /*#__PURE__*/Object.freeze({
20185
21062
  __proto__: null,
20186
- date: date
21063
+ date: date,
21064
+ date_to_xmlschema: date_to_xmlschema,
21065
+ date_to_rfc822: date_to_rfc822,
21066
+ date_to_string: date_to_string,
21067
+ date_to_long_string: date_to_long_string
20187
21068
  });
20188
21069
 
20189
21070
  /**
@@ -20191,50 +21072,74 @@ var dateFilters = /*#__PURE__*/Object.freeze({
20191
21072
  *
20192
21073
  * * prefer stringify() to String() since `undefined`, `null` should eval ''
20193
21074
  */
21075
+ const rCJKWord = /[\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF\u3040-\u309F\u30A0-\u30FF\uAC00-\uD7AF]/gu;
21076
+ // Word boundary followed by word characters (for detecting words)
21077
+ const rNonCJKWord = /[^\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF\u3040-\u309F\u30A0-\u30FF\uAC00-\uD7AF\s]+/gu;
20194
21078
  function append(v, arg) {
20195
21079
  assert(arguments.length === 2, 'append expect 2 arguments');
20196
- return stringify(v) + stringify(arg);
21080
+ const lhs = stringify(v);
21081
+ const rhs = stringify(arg);
21082
+ this.context.memoryLimit.use(lhs.length + rhs.length);
21083
+ return lhs + rhs;
20197
21084
  }
20198
21085
  function prepend(v, arg) {
20199
21086
  assert(arguments.length === 2, 'prepend expect 2 arguments');
20200
- return stringify(arg) + stringify(v);
21087
+ const lhs = stringify(v);
21088
+ const rhs = stringify(arg);
21089
+ this.context.memoryLimit.use(lhs.length + rhs.length);
21090
+ return rhs + lhs;
20201
21091
  }
20202
21092
  function lstrip(v, chars) {
21093
+ const str = stringify(v);
21094
+ this.context.memoryLimit.use(str.length);
20203
21095
  if (chars) {
20204
21096
  chars = escapeRegExp(stringify(chars));
20205
- return stringify(v).replace(new RegExp(`^[${chars}]+`, 'g'), '');
21097
+ return str.replace(new RegExp(`^[${chars}]+`, 'g'), '');
20206
21098
  }
20207
- return stringify(v).replace(/^\s+/, '');
21099
+ return str.replace(/^\s+/, '');
20208
21100
  }
20209
21101
  function downcase(v) {
20210
- return stringify(v).toLowerCase();
21102
+ const str = stringify(v);
21103
+ this.context.memoryLimit.use(str.length);
21104
+ return str.toLowerCase();
20211
21105
  }
20212
- function upcase(str) {
21106
+ function upcase(v) {
21107
+ const str = stringify(v);
21108
+ this.context.memoryLimit.use(str.length);
20213
21109
  return stringify(str).toUpperCase();
20214
21110
  }
20215
21111
  function remove(v, arg) {
20216
- return stringify(v).split(String(arg)).join('');
21112
+ const str = stringify(v);
21113
+ this.context.memoryLimit.use(str.length);
21114
+ return str.split(stringify(arg)).join('');
20217
21115
  }
20218
21116
  function remove_first(v, l) {
20219
- return stringify(v).replace(String(l), '');
21117
+ const str = stringify(v);
21118
+ this.context.memoryLimit.use(str.length);
21119
+ return str.replace(stringify(l), '');
20220
21120
  }
20221
21121
  function remove_last(v, l) {
20222
21122
  const str = stringify(v);
20223
- const pattern = String(l);
21123
+ this.context.memoryLimit.use(str.length);
21124
+ const pattern = stringify(l);
20224
21125
  const index = str.lastIndexOf(pattern);
20225
21126
  if (index === -1)
20226
21127
  return str;
20227
- return str.substring(0, index) + str.substring(index + pattern.length + 1);
21128
+ return str.substring(0, index) + str.substring(index + pattern.length);
20228
21129
  }
20229
21130
  function rstrip(str, chars) {
21131
+ str = stringify(str);
21132
+ this.context.memoryLimit.use(str.length);
20230
21133
  if (chars) {
20231
21134
  chars = escapeRegExp(stringify(chars));
20232
- return stringify(str).replace(new RegExp(`[${chars}]+$`, 'g'), '');
21135
+ return str.replace(new RegExp(`[${chars}]+$`, 'g'), '');
20233
21136
  }
20234
- return stringify(str).replace(/\s+$/, '');
21137
+ return str.replace(/\s+$/, '');
20235
21138
  }
20236
21139
  function split(v, arg) {
20237
- const arr = stringify(v).split(String(arg));
21140
+ const str = stringify(v);
21141
+ this.context.memoryLimit.use(str.length);
21142
+ const arr = str.split(stringify(arg));
20238
21143
  // align to ruby split, which is the behavior of shopify/liquid
20239
21144
  // see: https://ruby-doc.org/core-2.4.0/String.html#method-i-split
20240
21145
  while (arr.length && arr[arr.length - 1] === '')
@@ -20242,50 +21147,101 @@ function split(v, arg) {
20242
21147
  return arr;
20243
21148
  }
20244
21149
  function strip(v, chars) {
21150
+ const str = stringify(v);
21151
+ this.context.memoryLimit.use(str.length);
20245
21152
  if (chars) {
20246
21153
  chars = escapeRegExp(stringify(chars));
20247
- return stringify(v)
21154
+ return str
20248
21155
  .replace(new RegExp(`^[${chars}]+`, 'g'), '')
20249
21156
  .replace(new RegExp(`[${chars}]+$`, 'g'), '');
20250
21157
  }
20251
- return stringify(v).trim();
21158
+ return str.trim();
20252
21159
  }
20253
21160
  function strip_newlines(v) {
20254
- return stringify(v).replace(/\n/g, '');
21161
+ const str = stringify(v);
21162
+ this.context.memoryLimit.use(str.length);
21163
+ return str.replace(/\r?\n/gm, '');
20255
21164
  }
20256
21165
  function capitalize(str) {
20257
21166
  str = stringify(str);
21167
+ this.context.memoryLimit.use(str.length);
20258
21168
  return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
20259
21169
  }
20260
21170
  function replace(v, pattern, replacement) {
20261
- return stringify(v).split(String(pattern)).join(replacement);
21171
+ const str = stringify(v);
21172
+ this.context.memoryLimit.use(str.length);
21173
+ return str.split(stringify(pattern)).join(replacement);
20262
21174
  }
20263
21175
  function replace_first(v, arg1, arg2) {
20264
- return stringify(v).replace(String(arg1), arg2);
21176
+ const str = stringify(v);
21177
+ this.context.memoryLimit.use(str.length);
21178
+ return str.replace(stringify(arg1), arg2);
20265
21179
  }
20266
21180
  function replace_last(v, arg1, arg2) {
20267
21181
  const str = stringify(v);
20268
- const pattern = String(arg1);
21182
+ this.context.memoryLimit.use(str.length);
21183
+ const pattern = stringify(arg1);
20269
21184
  const index = str.lastIndexOf(pattern);
20270
21185
  if (index === -1)
20271
21186
  return str;
20272
- const replacement = String(arg2);
21187
+ const replacement = stringify(arg2);
20273
21188
  return str.substring(0, index) + replacement + str.substring(index + pattern.length);
20274
21189
  }
20275
21190
  function truncate(v, l = 50, o = '...') {
20276
- v = stringify(v);
20277
- if (v.length <= l)
21191
+ const str = stringify(v);
21192
+ this.context.memoryLimit.use(str.length);
21193
+ if (str.length <= l)
20278
21194
  return v;
20279
- return v.substring(0, l - o.length) + o;
21195
+ return str.substring(0, l - o.length) + o;
20280
21196
  }
20281
21197
  function truncatewords(v, words = 15, o = '...') {
20282
- const arr = stringify(v).split(/\s+/);
21198
+ const str = stringify(v);
21199
+ this.context.memoryLimit.use(str.length);
21200
+ const arr = str.split(/\s+/);
20283
21201
  if (words <= 0)
20284
21202
  words = 1;
20285
21203
  let ret = arr.slice(0, words).join(' ');
20286
21204
  if (arr.length >= words)
20287
21205
  ret += o;
20288
21206
  return ret;
21207
+ }
21208
+ function normalize_whitespace(v) {
21209
+ const str = stringify(v);
21210
+ this.context.memoryLimit.use(str.length);
21211
+ return str.replace(/\s+/g, ' ');
21212
+ }
21213
+ function number_of_words(input, mode) {
21214
+ const str = stringify(input);
21215
+ this.context.memoryLimit.use(str.length);
21216
+ input = str.trim();
21217
+ if (!input)
21218
+ return 0;
21219
+ switch (mode) {
21220
+ case 'cjk':
21221
+ // Count CJK characters and words
21222
+ return (input.match(rCJKWord) || []).length + (input.match(rNonCJKWord) || []).length;
21223
+ case 'auto':
21224
+ // Count CJK characters, if none, count words
21225
+ return rCJKWord.test(input)
21226
+ ? input.match(rCJKWord).length + (input.match(rNonCJKWord) || []).length
21227
+ : input.split(/\s+/).length;
21228
+ default:
21229
+ // Count words only
21230
+ return input.split(/\s+/).length;
21231
+ }
21232
+ }
21233
+ function array_to_sentence_string(array, connector = 'and') {
21234
+ this.context.memoryLimit.use(array.length);
21235
+ switch (array.length) {
21236
+ case 0:
21237
+ return '';
21238
+ case 1:
21239
+ return array[0];
21240
+ case 2:
21241
+ return `${array[0]} ${connector} ${array[1]}`;
21242
+ default:
21243
+ return `${array.slice(0, -1).join(', ')}, ${connector} ${array[array.length - 1]}`;
21244
+ }
20289
21245
  }
20290
21246
 
20291
21247
  var stringFilters = /*#__PURE__*/Object.freeze({
@@ -20307,52 +21263,58 @@ var stringFilters = /*#__PURE__*/Object.freeze({
20307
21263
  replace_first: replace_first,
20308
21264
  replace_last: replace_last,
20309
21265
  truncate: truncate,
20310
- truncatewords: truncatewords
21266
+ truncatewords: truncatewords,
21267
+ normalize_whitespace: normalize_whitespace,
21268
+ number_of_words: number_of_words,
21269
+ array_to_sentence_string: array_to_sentence_string
20311
21270
  });
20312
21271
 
20313
- const filters = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, htmlFilters), mathFilters), urlFilters), arrayFilters), dateFilters), stringFilters), { json,
20314
- raw, default: Default });
21272
+ const filters = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, htmlFilters), mathFilters), urlFilters), arrayFilters), dateFilters), stringFilters), misc);
20315
21273
 
20316
21274
  class AssignTag extends Tag {
20317
21275
  constructor(token, remainTokens, liquid) {
20318
21276
  super(token, remainTokens, liquid);
20319
- const tokenizer = new Tokenizer(token.args, liquid.options.operators);
20320
- this.key = tokenizer.readIdentifier().content;
20321
- tokenizer.skipBlank();
20322
- assert(tokenizer.peek() === '=', () => `illegal token ${token.getText()}`);
20323
- tokenizer.advance();
20324
- this.value = new Value(tokenizer.remaining(), this.liquid);
21277
+ this.identifier = this.tokenizer.readIdentifier();
21278
+ this.key = this.identifier.content;
21279
+ this.tokenizer.assert(this.key, 'expected variable name');
21280
+ this.tokenizer.skipBlank();
21281
+ this.tokenizer.assert(this.tokenizer.peek() === '=', 'expected "="');
21282
+ this.tokenizer.advance();
21283
+ this.value = new Value(this.tokenizer.readFilteredValue(), this.liquid);
20325
21284
  }
20326
21285
  *render(ctx) {
20327
21286
  ctx.bottom()[this.key] = yield this.value.value(ctx, this.liquid.options.lenientIf);
20328
21287
  }
21288
+ *arguments() {
21289
+ yield this.value;
21290
+ }
21291
+ *localScope() {
21292
+ yield this.identifier;
21293
+ }
20329
21294
  }
20330
21295
 
20331
21296
  const MODIFIERS = ['offset', 'limit', 'reversed'];
20332
21297
  class ForTag extends Tag {
20333
- constructor(token, remainTokens, liquid) {
21298
+ constructor(token, remainTokens, liquid, parser) {
20334
21299
  super(token, remainTokens, liquid);
20335
- const tokenizer = new Tokenizer(token.args, this.liquid.options.operators);
20336
- const variable = tokenizer.readIdentifier();
20337
- const inStr = tokenizer.readIdentifier();
20338
- const collection = tokenizer.readValue();
21300
+ const variable = this.tokenizer.readIdentifier();
21301
+ const inStr = this.tokenizer.readIdentifier();
21302
+ const collection = this.tokenizer.readValue();
20339
21303
  if (!variable.size() || inStr.content !== 'in' || !collection) {
20340
21304
  throw new Error(`illegal tag: ${token.getText()}`);
20341
21305
  }
20342
21306
  this.variable = variable.content;
20343
21307
  this.collection = collection;
20344
- this.hash = new Hash(tokenizer.remaining());
21308
+ this.hash = new Hash(this.tokenizer, liquid.options.keyValueSeparator);
20345
21309
  this.templates = [];
20346
21310
  this.elseTemplates = [];
20347
21311
  let p;
20348
- const stream = this.liquid.parser.parseStream(remainTokens)
21312
+ const stream = parser.parseStream(remainTokens)
20349
21313
  .on('start', () => (p = this.templates))
20350
- .on('tag:else', () => (p = this.elseTemplates))
20351
- .on('tag:endfor', () => stream.stop())
21314
+ .on('tag:else', tag => { assertEmpty(tag.args); p = this.elseTemplates; })
21315
+ .on('tag:endfor', tag => { assertEmpty(tag.args); stream.stop(); })
20352
21316
  .on('template', (tpl) => p.push(tpl))
20353
- .on('end', () => {
20354
- throw new Error(`tag ${token.getText()} not closed`);
20355
- });
21317
+ .on('end', () => { throw new Error(`tag ${token.getText()} not closed`); });
20356
21318
  stream.start();
20357
21319
  }
20358
21320
  *render(ctx, emitter) {
@@ -20381,16 +21343,33 @@ class ForTag extends Tag {
20381
21343
  ctx.push(scope);
20382
21344
  for (const item of collection) {
20383
21345
  scope[this.variable] = item;
21346
+ ctx.continueCalled = ctx.breakCalled = false;
20384
21347
  yield r.renderTemplates(this.templates, ctx, emitter);
20385
- if (emitter['break']) {
20386
- emitter['break'] = false;
21348
+ if (ctx.breakCalled)
20387
21349
  break;
20388
- }
20389
- emitter['continue'] = false;
20390
21350
  scope.forloop.next();
20391
21351
  }
21352
+ ctx.continueCalled = ctx.breakCalled = false;
20392
21353
  ctx.pop();
20393
21354
  }
21355
+ *children() {
21356
+ const templates = this.templates.slice();
21357
+ if (this.elseTemplates) {
21358
+ templates.push(...this.elseTemplates);
21359
+ }
21360
+ return templates;
21361
+ }
21362
+ *arguments() {
21363
+ yield this.collection;
21364
+ for (const v of Object.values(this.hash.hash)) {
21365
+ if (isValueToken(v)) {
21366
+ yield v;
21367
+ }
21368
+ }
21369
+ }
21370
+ blockScope() {
21371
+ return [this.variable, 'forloop'];
21372
+ }
20394
21373
  }
20395
21374
  function reversed(arr) {
20396
21375
  return [...arr].reverse();
@@ -20403,60 +21382,82 @@ function limit(arr, count) {
20403
21382
  }
20404
21383
 
20405
21384
  class CaptureTag extends Tag {
20406
- constructor(tagToken, remainTokens, liquid) {
21385
+ constructor(tagToken, remainTokens, liquid, parser) {
20407
21386
  super(tagToken, remainTokens, liquid);
20408
21387
  this.templates = [];
20409
- const tokenizer = new Tokenizer(tagToken.args, this.liquid.options.operators);
20410
- this.variable = readVariableName(tokenizer);
20411
- assert(this.variable, () => `${tagToken.args} not valid identifier`);
21388
+ this.identifier = this.readVariable();
21389
+ this.variable = this.identifier.content;
20412
21390
  while (remainTokens.length) {
20413
21391
  const token = remainTokens.shift();
20414
21392
  if (isTagToken(token) && token.name === 'endcapture')
20415
21393
  return;
20416
- this.templates.push(liquid.parser.parseToken(token, remainTokens));
21394
+ this.templates.push(parser.parseToken(token, remainTokens));
20417
21395
  }
20418
21396
  throw new Error(`tag ${tagToken.getText()} not closed`);
20419
21397
  }
21398
+ readVariable() {
21399
+ let ident = this.tokenizer.readIdentifier();
21400
+ if (ident.content)
21401
+ return ident;
21402
+ ident = this.tokenizer.readQuoted();
21403
+ if (ident)
21404
+ return ident;
21405
+ throw this.tokenizer.error('invalid capture name');
21406
+ }
20420
21407
  *render(ctx) {
20421
21408
  const r = this.liquid.renderer;
20422
21409
  const html = yield r.renderTemplates(this.templates, ctx);
20423
21410
  ctx.bottom()[this.variable] = html;
20424
21411
  }
20425
- }
20426
- function readVariableName(tokenizer) {
20427
- const word = tokenizer.readIdentifier().content;
20428
- if (word)
20429
- return word;
20430
- const quoted = tokenizer.readQuoted();
20431
- if (quoted)
20432
- return evalQuotedToken(quoted);
21412
+ *children() {
21413
+ return this.templates;
21414
+ }
21415
+ *localScope() {
21416
+ yield this.identifier;
21417
+ }
20433
21418
  }
20434
21419
 
20435
21420
  class CaseTag extends Tag {
20436
- constructor(tagToken, remainTokens, liquid) {
21421
+ constructor(tagToken, remainTokens, liquid, parser) {
20437
21422
  super(tagToken, remainTokens, liquid);
20438
21423
  this.branches = [];
20439
21424
  this.elseTemplates = [];
20440
- this.value = new Value(tagToken.args, this.liquid);
21425
+ this.value = new Value(this.tokenizer.readFilteredValue(), this.liquid);
20441
21426
  this.elseTemplates = [];
20442
21427
  let p = [];
20443
- const stream = this.liquid.parser.parseStream(remainTokens)
21428
+ let elseCount = 0;
21429
+ const stream = parser.parseStream(remainTokens)
20444
21430
  .on('tag:when', (token) => {
21431
+ if (elseCount > 0) {
21432
+ return;
21433
+ }
20445
21434
  p = [];
20446
- const tokenizer = new Tokenizer(token.args, this.liquid.options.operators);
20447
21435
  const values = [];
20448
- while (!tokenizer.end()) {
20449
- values.push(tokenizer.readValueOrThrow());
20450
- tokenizer.readTo(',');
21436
+ while (!token.tokenizer.end()) {
21437
+ values.push(token.tokenizer.readValueOrThrow());
21438
+ token.tokenizer.skipBlank();
21439
+ if (token.tokenizer.peek() === ',') {
21440
+ token.tokenizer.readTo(',');
21441
+ }
21442
+ else {
21443
+ token.tokenizer.readTo('or');
21444
+ }
20451
21445
  }
20452
21446
  this.branches.push({
20453
21447
  values,
20454
21448
  templates: p
20455
21449
  });
20456
21450
  })
20457
- .on('tag:else', () => (p = this.elseTemplates))
21451
+ .on('tag:else', () => {
21452
+ elseCount++;
21453
+ p = this.elseTemplates;
21454
+ })
20458
21455
  .on('tag:endcase', () => stream.stop())
20459
- .on('template', (tpl) => p.push(tpl))
21456
+ .on('template', (tpl) => {
21457
+ if (p !== this.elseTemplates || elseCount === 1) {
21458
+ p.push(tpl);
21459
+ }
21460
+ })
20460
21461
  .on('end', () => {
20461
21462
  throw new Error(`tag ${tagToken.getText()} not closed`);
20462
21463
  });
@@ -20469,7 +21470,7 @@ class CaseTag extends Tag {
20469
21470
  for (const branch of this.branches) {
20470
21471
  for (const valueToken of branch.values) {
20471
21472
  const value = yield evalToken(valueToken, ctx, ctx.opts.lenientIf);
20472
- if (target === value) {
21473
+ if (equals(target, value)) {
20473
21474
  yield r.renderTemplates(branch.templates, ctx, emitter);
20474
21475
  branchHit = true;
20475
21476
  break;
@@ -20480,6 +21481,17 @@ class CaseTag extends Tag {
20480
21481
  yield r.renderTemplates(this.elseTemplates, ctx, emitter);
20481
21482
  }
20482
21483
  }
21484
+ *arguments() {
21485
+ yield this.value;
21486
+ yield* this.branches.flatMap(b => b.values);
21487
+ }
21488
+ *children() {
21489
+ const templates = this.branches.flatMap(b => b.templates);
21490
+ if (this.elseTemplates) {
21491
+ templates.push(...this.elseTemplates);
21492
+ }
21493
+ return templates;
21494
+ }
20483
21495
  }
20484
21496
 
20485
21497
  class CommentTag extends Tag {
@@ -20496,11 +21508,10 @@ class CommentTag extends Tag {
20496
21508
  }
20497
21509
 
20498
21510
  class RenderTag extends Tag {
20499
- constructor(token, remainTokens, liquid) {
21511
+ constructor(token, remainTokens, liquid, parser) {
20500
21512
  super(token, remainTokens, liquid);
20501
- const args = token.args;
20502
- const tokenizer = new Tokenizer(args, this.liquid.options.operators);
20503
- this.file = parseFilePath(tokenizer, this.liquid);
21513
+ const tokenizer = this.tokenizer;
21514
+ this.file = parseFilePath(tokenizer, this.liquid, parser);
20504
21515
  this.currentFile = token.file;
20505
21516
  while (!tokenizer.end()) {
20506
21517
  tokenizer.skipBlank();
@@ -20534,13 +21545,13 @@ class RenderTag extends Tag {
20534
21545
  tokenizer.p = begin;
20535
21546
  break;
20536
21547
  }
20537
- this.hash = new Hash(tokenizer.remaining());
21548
+ this.hash = new Hash(tokenizer, liquid.options.keyValueSeparator);
20538
21549
  }
20539
21550
  *render(ctx, emitter) {
20540
21551
  const { liquid, hash } = this;
20541
21552
  const filepath = (yield renderFilePath(this['file'], ctx, liquid));
20542
- assert(filepath, () => `illegal filename "${filepath}"`);
20543
- const childCtx = new Context({}, ctx.opts, { sync: ctx.sync, globals: ctx.globals, strictVariables: ctx.strictVariables });
21553
+ assert(filepath, () => `illegal file path "${filepath}"`);
21554
+ const childCtx = ctx.spawn();
20544
21555
  const scope = childCtx.bottom();
20545
21556
  __assign(scope, yield hash.render(ctx));
20546
21557
  if (this['with']) {
@@ -20563,6 +21574,55 @@ class RenderTag extends Tag {
20563
21574
  yield liquid.renderer.renderTemplates(templates, childCtx, emitter);
20564
21575
  }
20565
21576
  }
21577
+ *children(partials, sync) {
21578
+ if (partials && isString(this['file'])) {
21579
+ return (yield this.liquid._parsePartialFile(this['file'], sync, this['currentFile']));
21580
+ }
21581
+ return [];
21582
+ }
21583
+ partialScope() {
21584
+ if (isString(this['file'])) {
21585
+ const names = Object.keys(this.hash.hash);
21586
+ if (this['with']) {
21587
+ const { value, alias } = this['with'];
21588
+ if (isString(alias)) {
21589
+ names.push([alias, value]);
21590
+ }
21591
+ else if (isString(this.file)) {
21592
+ names.push([this.file, value]);
21593
+ }
21594
+ }
21595
+ if (this['for']) {
21596
+ const { value, alias } = this['for'];
21597
+ if (isString(alias)) {
21598
+ names.push([alias, value]);
21599
+ }
21600
+ else if (isString(this.file)) {
21601
+ names.push([this.file, value]);
21602
+ }
21603
+ }
21604
+ return { name: this['file'], isolated: true, scope: names };
21605
+ }
21606
+ }
21607
+ *arguments() {
21608
+ for (const v of Object.values(this.hash.hash)) {
21609
+ if (isValueToken(v)) {
21610
+ yield v;
21611
+ }
21612
+ }
21613
+ if (this['with']) {
21614
+ const { value } = this['with'];
21615
+ if (isValueToken(value)) {
21616
+ yield value;
21617
+ }
21618
+ }
21619
+ if (this['for']) {
21620
+ const { value } = this['for'];
21621
+ if (isValueToken(value)) {
21622
+ yield value;
21623
+ }
21624
+ }
21625
+ }
20566
21626
  }
20567
21627
  /**
20568
21628
  * @return null for "none",
@@ -20570,22 +21630,21 @@ class RenderTag extends Tag {
20570
21630
  * @return Token for expression (not quoted)
20571
21631
  * @throws TypeError if cannot read next token
20572
21632
  */
20573
- function parseFilePath(tokenizer, liquid) {
21633
+ function parseFilePath(tokenizer, liquid, parser) {
20574
21634
  if (liquid.options.dynamicPartials) {
20575
21635
  const file = tokenizer.readValue();
20576
- if (file === undefined)
20577
- throw new TypeError(`illegal argument "${tokenizer.input}"`);
21636
+ tokenizer.assert(file, 'illegal file path');
20578
21637
  if (file.getText() === 'none')
20579
21638
  return;
20580
21639
  if (isQuotedToken(file)) {
20581
21640
  // for filenames like "files/{{file}}", eval as liquid template
20582
- const templates = liquid.parse(evalQuotedToken(file));
21641
+ const templates = parser.parse(evalQuotedToken(file));
20583
21642
  return optimize(templates);
20584
21643
  }
20585
21644
  return file;
20586
21645
  }
20587
21646
  const tokens = [...tokenizer.readFileNameTemplate(liquid.options)];
20588
- const templates = optimize(liquid.parser.parseTokens(tokens));
21647
+ const templates = optimize(parser.parseTokens(tokens));
20589
21648
  return templates === 'none' ? undefined : templates;
20590
21649
  }
20591
21650
  function optimize(templates) {
@@ -20603,11 +21662,10 @@ function* renderFilePath(file, ctx, liquid) {
20603
21662
  }
20604
21663
 
20605
21664
  class IncludeTag extends Tag {
20606
- constructor(token, remainTokens, liquid) {
21665
+ constructor(token, remainTokens, liquid, parser) {
20607
21666
  super(token, remainTokens, liquid);
20608
- const args = token.args;
20609
- const tokenizer = new Tokenizer(args, this.liquid.options.operators);
20610
- this['file'] = parseFilePath(tokenizer, this.liquid);
21667
+ const { tokenizer } = token;
21668
+ this['file'] = parseFilePath(tokenizer, this.liquid, parser);
20611
21669
  this['currentFile'] = token.file;
20612
21670
  const begin = tokenizer.p;
20613
21671
  const withStr = tokenizer.readIdentifier();
@@ -20621,13 +21679,13 @@ class IncludeTag extends Tag {
20621
21679
  }
20622
21680
  else
20623
21681
  tokenizer.p = begin;
20624
- this.hash = new Hash(tokenizer.remaining(), this.liquid.options.jekyllInclude);
21682
+ this.hash = new Hash(tokenizer, liquid.options.jekyllInclude || liquid.options.keyValueSeparator);
20625
21683
  }
20626
21684
  *render(ctx, emitter) {
20627
21685
  const { liquid, hash, withVar } = this;
20628
21686
  const { renderer } = liquid;
20629
21687
  const filepath = (yield renderFilePath(this['file'], ctx, liquid));
20630
- assert(filepath, () => `illegal filename "${filepath}"`);
21688
+ assert(filepath, () => `illegal file path "${filepath}"`);
20631
21689
  const saved = ctx.saveRegister('blocks', 'blockMode');
20632
21690
  ctx.setRegister('blocks', {});
20633
21691
  ctx.setRegister('blockMode', BlockMode.OUTPUT);
@@ -20640,13 +21698,43 @@ class IncludeTag extends Tag {
20640
21698
  ctx.pop();
20641
21699
  ctx.restoreRegister(saved);
20642
21700
  }
21701
+ *children(partials, sync) {
21702
+ if (partials && isString(this['file'])) {
21703
+ return (yield this.liquid._parsePartialFile(this['file'], sync, this['currentFile']));
21704
+ }
21705
+ return [];
21706
+ }
21707
+ partialScope() {
21708
+ if (isString(this['file'])) {
21709
+ let names;
21710
+ if (this.liquid.options.jekyllInclude) {
21711
+ names = ['include'];
21712
+ }
21713
+ else {
21714
+ names = Object.keys(this.hash.hash);
21715
+ if (this.withVar) {
21716
+ names.push([this['file'], this.withVar]);
21717
+ }
21718
+ }
21719
+ return { name: this['file'], isolated: false, scope: names };
21720
+ }
21721
+ }
21722
+ *arguments() {
21723
+ yield* Object.values(this.hash.hash).filter(isValueToken);
21724
+ if (isValueToken(this['file'])) {
21725
+ yield this['file'];
21726
+ }
21727
+ if (isValueToken(this.withVar)) {
21728
+ yield this.withVar;
21729
+ }
21730
+ }
20643
21731
  }
20644
21732
 
20645
21733
  class DecrementTag extends Tag {
20646
21734
  constructor(token, remainTokens, liquid) {
20647
21735
  super(token, remainTokens, liquid);
20648
- const tokenizer = new Tokenizer(token.args, this.liquid.options.operators);
20649
- this.variable = tokenizer.readIdentifier().content;
21736
+ this.identifier = this.tokenizer.readIdentifier();
21737
+ this.variable = this.identifier.content;
20650
21738
  }
20651
21739
  render(context, emitter) {
20652
21740
  const scope = context.environments;
@@ -20655,30 +21743,32 @@ class DecrementTag extends Tag {
20655
21743
  }
20656
21744
  emitter.write(stringify(--scope[this.variable]));
20657
21745
  }
21746
+ *localScope() {
21747
+ yield this.identifier;
21748
+ }
20658
21749
  }
20659
21750
 
20660
21751
  class CycleTag extends Tag {
20661
- constructor(tagToken, remainTokens, liquid) {
20662
- super(tagToken, remainTokens, liquid);
21752
+ constructor(token, remainTokens, liquid) {
21753
+ super(token, remainTokens, liquid);
20663
21754
  this.candidates = [];
20664
- const tokenizer = new Tokenizer(tagToken.args, this.liquid.options.operators);
20665
- const group = tokenizer.readValue();
20666
- tokenizer.skipBlank();
21755
+ const group = this.tokenizer.readValue();
21756
+ this.tokenizer.skipBlank();
20667
21757
  if (group) {
20668
- if (tokenizer.peek() === ':') {
21758
+ if (this.tokenizer.peek() === ':') {
20669
21759
  this.group = group;
20670
- tokenizer.advance();
21760
+ this.tokenizer.advance();
20671
21761
  }
20672
21762
  else
20673
21763
  this.candidates.push(group);
20674
21764
  }
20675
- while (!tokenizer.end()) {
20676
- const value = tokenizer.readValue();
21765
+ while (!this.tokenizer.end()) {
21766
+ const value = this.tokenizer.readValue();
20677
21767
  if (value)
20678
21768
  this.candidates.push(value);
20679
- tokenizer.readTo(',');
21769
+ this.tokenizer.readTo(',');
20680
21770
  }
20681
- assert(this.candidates.length, () => `empty candidates: ${tagToken.getText()}`);
21771
+ this.tokenizer.assert(this.candidates.length, () => `empty candidates: "${token.getText()}"`);
20682
21772
  }
20683
21773
  *render(ctx, emitter) {
20684
21774
  const group = (yield evalToken(this.group, ctx));
@@ -20693,25 +21783,37 @@ class CycleTag extends Tag {
20693
21783
  groups[fingerprint] = idx;
20694
21784
  return yield evalToken(candidate, ctx);
20695
21785
  }
21786
+ *arguments() {
21787
+ yield* this.candidates;
21788
+ if (this.group) {
21789
+ yield this.group;
21790
+ }
21791
+ }
20696
21792
  }
20697
21793
 
20698
21794
  class IfTag extends Tag {
20699
- constructor(tagToken, remainTokens, liquid) {
21795
+ constructor(tagToken, remainTokens, liquid, parser) {
20700
21796
  super(tagToken, remainTokens, liquid);
20701
21797
  this.branches = [];
20702
- this.elseTemplates = [];
20703
- let p;
20704
- liquid.parser.parseStream(remainTokens)
21798
+ let p = [];
21799
+ parser.parseStream(remainTokens)
20705
21800
  .on('start', () => this.branches.push({
20706
- value: new Value(tagToken.args, this.liquid),
20707
- templates: (p = [])
20708
- }))
20709
- .on('tag:elsif', (token) => this.branches.push({
20710
- value: new Value(token.args, this.liquid),
21801
+ value: new Value(tagToken.tokenizer.readFilteredValue(), this.liquid),
20711
21802
  templates: (p = [])
20712
21803
  }))
20713
- .on('tag:else', () => (p = this.elseTemplates))
20714
- .on('tag:endif', function () { this.stop(); })
21804
+ .on('tag:elsif', (token) => {
21805
+ assert(!this.elseTemplates, 'unexpected elsif after else');
21806
+ this.branches.push({
21807
+ value: new Value(token.tokenizer.readFilteredValue(), this.liquid),
21808
+ templates: (p = [])
21809
+ });
21810
+ })
21811
+ .on('tag:else', tag => {
21812
+ assertEmpty(tag.args);
21813
+ assert(!this.elseTemplates, 'duplicated else');
21814
+ p = this.elseTemplates = [];
21815
+ })
21816
+ .on('tag:endif', function (tag) { assertEmpty(tag.args); this.stop(); })
20715
21817
  .on('template', (tpl) => p.push(tpl))
20716
21818
  .on('end', () => { throw new Error(`tag ${tagToken.getText()} not closed`); })
20717
21819
  .start();
@@ -20725,15 +21827,25 @@ class IfTag extends Tag {
20725
21827
  return;
20726
21828
  }
20727
21829
  }
20728
- yield r.renderTemplates(this.elseTemplates, ctx, emitter);
21830
+ yield r.renderTemplates(this.elseTemplates || [], ctx, emitter);
21831
+ }
21832
+ *children() {
21833
+ const templates = this.branches.flatMap(b => b.templates);
21834
+ if (this.elseTemplates) {
21835
+ templates.push(...this.elseTemplates);
21836
+ }
21837
+ return templates;
21838
+ }
21839
+ arguments() {
21840
+ return this.branches.map(b => b.value);
20729
21841
  }
20730
21842
  }
20731
21843
 
20732
21844
  class IncrementTag extends Tag {
20733
21845
  constructor(token, remainTokens, liquid) {
20734
21846
  super(token, remainTokens, liquid);
20735
- const tokenizer = new Tokenizer(token.args, this.liquid.options.operators);
20736
- this.variable = tokenizer.readIdentifier().content;
21847
+ this.identifier = this.tokenizer.readIdentifier();
21848
+ this.variable = this.identifier.content;
20737
21849
  }
20738
21850
  render(context, emitter) {
20739
21851
  const scope = context.environments;
@@ -20744,16 +21856,18 @@ class IncrementTag extends Tag {
20744
21856
  scope[this.variable]++;
20745
21857
  emitter.write(stringify(val));
20746
21858
  }
21859
+ *localScope() {
21860
+ yield this.identifier;
21861
+ }
20747
21862
  }
20748
21863
 
20749
21864
  class LayoutTag extends Tag {
20750
- constructor(token, remainTokens, liquid) {
21865
+ constructor(token, remainTokens, liquid, parser) {
20751
21866
  super(token, remainTokens, liquid);
20752
- const tokenizer = new Tokenizer(token.args, this.liquid.options.operators);
20753
- this.file = parseFilePath(tokenizer, this.liquid);
21867
+ this.file = parseFilePath(this.tokenizer, this.liquid, parser);
20754
21868
  this['currentFile'] = token.file;
20755
- this.args = new Hash(tokenizer.remaining());
20756
- this.templates = this.liquid.parser.parseTokens(remainTokens);
21869
+ this.args = new Hash(this.tokenizer, liquid.options.keyValueSeparator);
21870
+ this.templates = parser.parseTokens(remainTokens);
20757
21871
  }
20758
21872
  *render(ctx, emitter) {
20759
21873
  const { liquid, args, file } = this;
@@ -20764,7 +21878,7 @@ class LayoutTag extends Tag {
20764
21878
  return;
20765
21879
  }
20766
21880
  const filepath = (yield renderFilePath(this.file, ctx, liquid));
20767
- assert(filepath, () => `illegal filename "${filepath}"`);
21881
+ assert(filepath, () => `illegal file path "${filepath}"`);
20768
21882
  const templates = (yield liquid._parseLayoutFile(filepath, ctx.sync, this['currentFile']));
20769
21883
  // render remaining contents and store rendered results
20770
21884
  ctx.setRegister('blockMode', BlockMode.STORE);
@@ -20779,10 +21893,32 @@ class LayoutTag extends Tag {
20779
21893
  yield renderer.renderTemplates(templates, ctx, emitter);
20780
21894
  ctx.pop();
20781
21895
  }
21896
+ *children(partials) {
21897
+ const templates = this.templates.slice();
21898
+ if (partials && isString(this.file)) {
21899
+ templates.push(...(yield this.liquid._parsePartialFile(this.file, true, this['currentFile'])));
21900
+ }
21901
+ return templates;
21902
+ }
21903
+ *arguments() {
21904
+ for (const v of Object.values(this.args.hash)) {
21905
+ if (isValueToken(v)) {
21906
+ yield v;
21907
+ }
21908
+ }
21909
+ if (isValueToken(this.file)) {
21910
+ yield this.file;
21911
+ }
21912
+ }
21913
+ partialScope() {
21914
+ if (isString(this.file)) {
21915
+ return { name: this.file, isolated: false, scope: Object.keys(this.args.hash) };
21916
+ }
21917
+ }
20782
21918
  }
20783
21919
 
20784
21920
  class BlockTag extends Tag {
20785
- constructor(token, remainTokens, liquid) {
21921
+ constructor(token, remainTokens, liquid, parser) {
20786
21922
  super(token, remainTokens, liquid);
20787
21923
  this.templates = [];
20788
21924
  const match = /\w+/.exec(token.args);
@@ -20791,7 +21927,7 @@ class BlockTag extends Tag {
20791
21927
  const token = remainTokens.shift();
20792
21928
  if (isTagToken(token) && token.name === 'endblock')
20793
21929
  return;
20794
- const template = liquid.parser.parseToken(token, remainTokens);
21930
+ const template = parser.parseToken(token, remainTokens);
20795
21931
  this.templates.push(template);
20796
21932
  }
20797
21933
  throw new Error(`tag ${token.getText()} not closed`);
@@ -20818,6 +21954,12 @@ class BlockTag extends Tag {
20818
21954
  ? (superBlock, emitter) => renderChild(new BlockDrop(() => renderCurrent(superBlock, emitter)), emitter)
20819
21955
  : renderCurrent;
20820
21956
  }
21957
+ *children() {
21958
+ return this.templates;
21959
+ }
21960
+ blockScope() {
21961
+ return ['block'];
21962
+ }
20821
21963
  }
20822
21964
 
20823
21965
  class RawTag extends Tag {
@@ -20861,22 +22003,21 @@ class TablerowloopDrop extends ForloopDrop {
20861
22003
  }
20862
22004
 
20863
22005
  class TablerowTag extends Tag {
20864
- constructor(tagToken, remainTokens, liquid) {
22006
+ constructor(tagToken, remainTokens, liquid, parser) {
20865
22007
  super(tagToken, remainTokens, liquid);
20866
- const tokenizer = new Tokenizer(tagToken.args, this.liquid.options.operators);
20867
- const variable = tokenizer.readIdentifier();
20868
- tokenizer.skipBlank();
20869
- const predicate = tokenizer.readIdentifier();
20870
- const collectionToken = tokenizer.readValue();
22008
+ const variable = this.tokenizer.readIdentifier();
22009
+ this.tokenizer.skipBlank();
22010
+ const predicate = this.tokenizer.readIdentifier();
22011
+ const collectionToken = this.tokenizer.readValue();
20871
22012
  if (predicate.content !== 'in' || !collectionToken) {
20872
22013
  throw new Error(`illegal tag: ${tagToken.getText()}`);
20873
22014
  }
20874
22015
  this.variable = variable.content;
20875
22016
  this.collection = collectionToken;
20876
- this.args = new Hash(tokenizer.remaining());
22017
+ this.args = new Hash(this.tokenizer, liquid.options.keyValueSeparator);
20877
22018
  this.templates = [];
20878
22019
  let p;
20879
- const stream = this.liquid.parser.parseStream(remainTokens)
22020
+ const stream = parser.parseStream(remainTokens)
20880
22021
  .on('start', () => (p = this.templates))
20881
22022
  .on('tag:endtablerow', () => stream.stop())
20882
22023
  .on('template', (tpl) => p.push(tpl))
@@ -20911,28 +22052,56 @@ class TablerowTag extends Tag {
20911
22052
  emitter.write('</tr>');
20912
22053
  ctx.pop();
20913
22054
  }
22055
+ *children() {
22056
+ return this.templates;
22057
+ }
22058
+ *arguments() {
22059
+ yield this.collection;
22060
+ for (const v of Object.values(this.args.hash)) {
22061
+ if (isValueToken(v)) {
22062
+ yield v;
22063
+ }
22064
+ }
22065
+ }
22066
+ blockScope() {
22067
+ return [this.variable, 'tablerowloop'];
22068
+ }
20914
22069
  }
20915
22070
 
20916
22071
  class UnlessTag extends Tag {
20917
- constructor(tagToken, remainTokens, liquid) {
22072
+ constructor(tagToken, remainTokens, liquid, parser) {
20918
22073
  super(tagToken, remainTokens, liquid);
20919
22074
  this.branches = [];
20920
22075
  this.elseTemplates = [];
20921
- let p;
20922
- this.liquid.parser.parseStream(remainTokens)
22076
+ let p = [];
22077
+ let elseCount = 0;
22078
+ parser.parseStream(remainTokens)
20923
22079
  .on('start', () => this.branches.push({
20924
- value: new Value(tagToken.args, this.liquid),
22080
+ value: new Value(tagToken.tokenizer.readFilteredValue(), this.liquid),
20925
22081
  test: isFalsy,
20926
22082
  templates: (p = [])
20927
22083
  }))
20928
- .on('tag:elsif', (token) => this.branches.push({
20929
- value: new Value(token.args, this.liquid),
20930
- test: isTruthy,
20931
- templates: (p = [])
20932
- }))
20933
- .on('tag:else', () => (p = this.elseTemplates))
22084
+ .on('tag:elsif', (token) => {
22085
+ if (elseCount > 0) {
22086
+ p = [];
22087
+ return;
22088
+ }
22089
+ this.branches.push({
22090
+ value: new Value(token.tokenizer.readFilteredValue(), this.liquid),
22091
+ test: isTruthy,
22092
+ templates: (p = [])
22093
+ });
22094
+ })
22095
+ .on('tag:else', () => {
22096
+ elseCount++;
22097
+ p = this.elseTemplates;
22098
+ })
20934
22099
  .on('tag:endunless', function () { this.stop(); })
20935
- .on('template', (tpl) => p.push(tpl))
22100
+ .on('template', (tpl) => {
22101
+ if (p !== this.elseTemplates || elseCount === 1) {
22102
+ p.push(tpl);
22103
+ }
22104
+ })
20936
22105
  .on('end', () => { throw new Error(`tag ${tagToken.getText()} not closed`); })
20937
22106
  .start();
20938
22107
  }
@@ -20947,41 +22116,63 @@ class UnlessTag extends Tag {
20947
22116
  }
20948
22117
  yield r.renderTemplates(this.elseTemplates, ctx, emitter);
20949
22118
  }
22119
+ *children() {
22120
+ const children = this.branches.flatMap(b => b.templates);
22121
+ if (this.elseTemplates) {
22122
+ children.push(...this.elseTemplates);
22123
+ }
22124
+ return children;
22125
+ }
22126
+ arguments() {
22127
+ return this.branches.map(b => b.value);
22128
+ }
20950
22129
  }
20951
22130
 
20952
22131
  class BreakTag extends Tag {
20953
- render(ctx, emitter) {
20954
- emitter['break'] = true;
22132
+ render(ctx, _emitter) {
22133
+ ctx.breakCalled = true;
20955
22134
  }
20956
22135
  }
20957
22136
 
20958
22137
  class ContinueTag extends Tag {
20959
- render(ctx, emitter) {
20960
- emitter['continue'] = true;
22138
+ render(ctx, _emitter) {
22139
+ ctx.continueCalled = true;
20961
22140
  }
20962
22141
  }
20963
22142
 
20964
22143
  class EchoTag extends Tag {
20965
22144
  constructor(token, remainTokens, liquid) {
20966
22145
  super(token, remainTokens, liquid);
20967
- this.value = new Value(token.args, this.liquid);
22146
+ this.tokenizer.skipBlank();
22147
+ if (!this.tokenizer.end()) {
22148
+ this.value = new Value(this.tokenizer.readFilteredValue(), this.liquid);
22149
+ }
20968
22150
  }
20969
22151
  *render(ctx, emitter) {
22152
+ if (!this.value)
22153
+ return;
20970
22154
  const val = yield this.value.value(ctx, false);
20971
22155
  emitter.write(val);
20972
22156
  }
22157
+ *arguments() {
22158
+ if (this.value) {
22159
+ yield this.value;
22160
+ }
22161
+ }
20973
22162
  }
20974
22163
 
20975
22164
  class LiquidTag extends Tag {
20976
- constructor(token, remainTokens, liquid) {
22165
+ constructor(token, remainTokens, liquid, parser) {
20977
22166
  super(token, remainTokens, liquid);
20978
- const tokenizer = new Tokenizer(token.args, this.liquid.options.operators);
20979
- const tokens = tokenizer.readLiquidTagTokens(this.liquid.options);
20980
- this.templates = this.liquid.parser.parseTokens(tokens);
22167
+ const tokens = this.tokenizer.readLiquidTagTokens(this.liquid.options);
22168
+ this.templates = parser.parseTokens(tokens);
20981
22169
  }
20982
22170
  *render(ctx, emitter) {
20983
22171
  yield this.liquid.renderer.renderTemplates(this.templates, ctx, emitter);
20984
22172
  }
22173
+ *children() {
22174
+ return this.templates;
22175
+ }
20985
22176
  }
20986
22177
 
20987
22178
  class InlineCommentTag extends Tag {
@@ -21024,12 +22215,14 @@ class Liquid {
21024
22215
  this.filters = {};
21025
22216
  this.tags = {};
21026
22217
  this.options = normalize(opts);
22218
+ // eslint-disable-next-line deprecation/deprecation
21027
22219
  this.parser = new Parser(this);
21028
22220
  forOwn(tags, (conf, name) => this.registerTag(name, conf));
21029
22221
  forOwn(filters, (handler, name) => this.registerFilter(name, handler));
21030
22222
  }
21031
22223
  parse(html, filepath) {
21032
- return this.parser.parse(html, filepath);
22224
+ const parser = new Parser(this);
22225
+ return parser.parse(html, filepath);
21033
22226
  }
21034
22227
  _render(tpl, scope, renderOptions) {
21035
22228
  const ctx = scope instanceof Context ? scope : new Context(scope, this.options, renderOptions);
@@ -21060,21 +22253,21 @@ class Liquid {
21060
22253
  return toValueSync(this._parseAndRender(html, scope, Object.assign(Object.assign({}, renderOptions), { sync: true })));
21061
22254
  }
21062
22255
  _parsePartialFile(file, sync, currentFile) {
21063
- return this.parser.parseFile(file, sync, LookupType.Partials, currentFile);
22256
+ return new Parser(this).parseFile(file, sync, LookupType.Partials, currentFile);
21064
22257
  }
21065
22258
  _parseLayoutFile(file, sync, currentFile) {
21066
- return this.parser.parseFile(file, sync, LookupType.Layouts, currentFile);
22259
+ return new Parser(this).parseFile(file, sync, LookupType.Layouts, currentFile);
21067
22260
  }
21068
22261
  _parseFile(file, sync, lookupType, currentFile) {
21069
- return this.parser.parseFile(file, sync, lookupType, currentFile);
22262
+ return new Parser(this).parseFile(file, sync, lookupType, currentFile);
21070
22263
  }
21071
22264
  parseFile(file, lookupType) {
21072
22265
  return __awaiter(this, void 0, void 0, function* () {
21073
- return toPromise(this.parser.parseFile(file, false, lookupType));
22266
+ return toPromise(new Parser(this).parseFile(file, false, lookupType));
21074
22267
  });
21075
22268
  }
21076
22269
  parseFileSync(file, lookupType) {
21077
- return toValueSync(this.parser.parseFile(file, true, lookupType));
22270
+ return toValueSync(new Parser(this).parseFile(file, true, lookupType));
21078
22271
  }
21079
22272
  *_renderFile(file, ctx, renderFileOptions) {
21080
22273
  const templates = (yield this._parseFile(file, renderFileOptions.sync, renderFileOptions.lookupType));
@@ -21130,6 +22323,94 @@ class Liquid {
21130
22323
  self.renderFile(filePath, ctx).then(html => callback(null, html), callback);
21131
22324
  };
21132
22325
  }
22326
+ analyze(template, options = {}) {
22327
+ return __awaiter(this, void 0, void 0, function* () {
22328
+ return analyze(template, options);
22329
+ });
22330
+ }
22331
+ analyzeSync(template, options = {}) {
22332
+ return analyzeSync(template, options);
22333
+ }
22334
+ parseAndAnalyze(html, filename, options = {}) {
22335
+ return __awaiter(this, void 0, void 0, function* () {
22336
+ return analyze(this.parse(html, filename), options);
22337
+ });
22338
+ }
22339
+ parseAndAnalyzeSync(html, filename, options = {}) {
22340
+ return analyzeSync(this.parse(html, filename), options);
22341
+ }
22342
+ /** Return an array of all variables without their properties. */
22343
+ variables(template, options = {}) {
22344
+ return __awaiter(this, void 0, void 0, function* () {
22345
+ const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
22346
+ return Object.keys(analysis.variables);
22347
+ });
22348
+ }
22349
+ /** Return an array of all variables without their properties. */
22350
+ variablesSync(template, options = {}) {
22351
+ const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
22352
+ return Object.keys(analysis.variables);
22353
+ }
22354
+ /** Return an array of all variables including their properties/paths. */
22355
+ fullVariables(template, options = {}) {
22356
+ return __awaiter(this, void 0, void 0, function* () {
22357
+ const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
22358
+ return Array.from(new Set(Object.values(analysis.variables).flatMap((a) => a.map((v) => String(v)))));
22359
+ });
22360
+ }
22361
+ /** Return an array of all variables including their properties/paths. */
22362
+ fullVariablesSync(template, options = {}) {
22363
+ const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
22364
+ return Array.from(new Set(Object.values(analysis.variables).flatMap((a) => a.map((v) => String(v)))));
22365
+ }
22366
+ /** Return an array of all variables, each as an array of properties/segments. */
22367
+ variableSegments(template, options = {}) {
22368
+ return __awaiter(this, void 0, void 0, function* () {
22369
+ const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
22370
+ return Array.from(strictUniq(Object.values(analysis.variables).flatMap((a) => a.map((v) => v.toArray()))));
22371
+ });
22372
+ }
22373
+ /** Return an array of all variables, each as an array of properties/segments. */
22374
+ variableSegmentsSync(template, options = {}) {
22375
+ const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
22376
+ return Array.from(strictUniq(Object.values(analysis.variables).flatMap((a) => a.map((v) => v.toArray()))));
22377
+ }
22378
+ /** Return an array of all expected context variables without their properties. */
22379
+ globalVariables(template, options = {}) {
22380
+ return __awaiter(this, void 0, void 0, function* () {
22381
+ const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
22382
+ return Object.keys(analysis.globals);
22383
+ });
22384
+ }
22385
+ /** Return an array of all expected context variables without their properties. */
22386
+ globalVariablesSync(template, options = {}) {
22387
+ const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
22388
+ return Object.keys(analysis.globals);
22389
+ }
22390
+ /** Return an array of all expected context variables including their properties/paths. */
22391
+ globalFullVariables(template, options = {}) {
22392
+ return __awaiter(this, void 0, void 0, function* () {
22393
+ const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
22394
+ return Array.from(new Set(Object.values(analysis.globals).flatMap((a) => a.map((v) => String(v)))));
22395
+ });
22396
+ }
22397
+ /** Return an array of all expected context variables including their properties/paths. */
22398
+ globalFullVariablesSync(template, options = {}) {
22399
+ const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
22400
+ return Array.from(new Set(Object.values(analysis.globals).flatMap((a) => a.map((v) => String(v)))));
22401
+ }
22402
+ /** Return an array of all expected context variables, each as an array of properties/segments. */
22403
+ globalVariableSegments(template, options = {}) {
22404
+ return __awaiter(this, void 0, void 0, function* () {
22405
+ const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
22406
+ return Array.from(strictUniq(Object.values(analysis.globals).flatMap((a) => a.map((v) => v.toArray()))));
22407
+ });
22408
+ }
22409
+ /** Return an array of all expected context variables, each as an array of properties/segments. */
22410
+ globalVariableSegmentsSync(template, options = {}) {
22411
+ const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
22412
+ return Array.from(strictUniq(Object.values(analysis.globals).flatMap((a) => a.map((v) => v.toArray()))));
22413
+ }
21133
22414
  }
21134
22415
 
21135
22416
  const engine = new Liquid();
@@ -21183,6 +22464,14 @@ var e4 = defineHook(({}, { services, getSchema, logger, env }) => {
21183
22464
  await addField("format", "string");
21184
22465
  if (fields.orientation == null)
21185
22466
  await addField("orientation", "string");
22467
+ if (fields.input_type == null)
22468
+ await addField("input_type", "string");
22469
+ if (fields.input_flow == null)
22470
+ await addField("input_flow", "string");
22471
+ if (fields.input_flow_body == null)
22472
+ await addField("input_flow_body", "text");
22473
+ if (fields.input_fixed == null)
22474
+ await addField("input_fixed", "text");
21186
22475
  if (((_a = schema.collections.directus_settings) == null ? void 0 : _a.fields.TTA_KEY) == null) {
21187
22476
  await addField(
21188
22477
  "TTA_KEY",
@@ -21297,8 +22586,9 @@ var e4 = defineHook(({}, { services, getSchema, logger, env }) => {
21297
22586
  });
21298
22587
  } catch (error) {
21299
22588
  logger.warn(
21300
- "[TTA] Error while creating '" + name + "' in the collection " + collection
22589
+ "[TTA] Error while creating '" + name + "' in the collection " + collection + ": "
21301
22590
  );
22591
+ console.error(error);
21302
22592
  }
21303
22593
  }
21304
22594
  async function handleRapidAPIError(error) {