@skeptools/skep-organization-project 0.0.15 → 0.0.16

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 (57) hide show
  1. package/.jsii +113 -3
  2. package/API.md +74 -43
  3. package/lib/skep-organization-project.js +1 -1
  4. package/node_modules/liquidjs/README.md +17 -10
  5. package/node_modules/liquidjs/dist/context/context.d.ts +7 -1
  6. package/node_modules/liquidjs/dist/drop/drop.d.ts +1 -1
  7. package/node_modules/liquidjs/dist/filters/array.d.ts +14 -14
  8. package/node_modules/liquidjs/dist/filters/html.d.ts +6 -5
  9. package/node_modules/liquidjs/dist/filters/math.d.ts +9 -9
  10. package/node_modules/liquidjs/dist/filters/string.d.ts +22 -21
  11. package/node_modules/liquidjs/dist/fs/map-fs.d.ts +13 -0
  12. package/node_modules/liquidjs/dist/fs/node-require.d.ts +1 -2
  13. package/node_modules/liquidjs/dist/index.d.ts +1 -1
  14. package/node_modules/liquidjs/dist/liquid-options.d.ts +28 -2
  15. package/node_modules/liquidjs/dist/liquid.browser.min.js +1 -1
  16. package/node_modules/liquidjs/dist/liquid.browser.min.js.map +1 -1
  17. package/node_modules/liquidjs/dist/{liquid.browser.esm.js → liquid.browser.mjs} +443 -227
  18. package/node_modules/liquidjs/dist/liquid.browser.umd.js +604 -368
  19. package/node_modules/liquidjs/dist/liquid.browser.umd.js.map +1 -1
  20. package/node_modules/liquidjs/dist/liquid.d.ts +3 -0
  21. package/node_modules/liquidjs/dist/{liquid.node.cjs.js → liquid.node.js} +432 -227
  22. package/node_modules/liquidjs/dist/{liquid.node.esm.js → liquid.node.mjs} +448 -228
  23. package/node_modules/liquidjs/dist/parser/parser.d.ts +1 -0
  24. package/node_modules/liquidjs/dist/parser/parser.spec.d.ts +1 -0
  25. package/node_modules/liquidjs/dist/parser/tokenizer.d.ts +4 -3
  26. package/node_modules/liquidjs/dist/tags/block.d.ts +2 -1
  27. package/node_modules/liquidjs/dist/tags/capture.d.ts +2 -1
  28. package/node_modules/liquidjs/dist/tags/case.d.ts +2 -1
  29. package/node_modules/liquidjs/dist/tags/for.d.ts +2 -1
  30. package/node_modules/liquidjs/dist/tags/if.d.ts +3 -2
  31. package/node_modules/liquidjs/dist/tags/include.d.ts +2 -1
  32. package/node_modules/liquidjs/dist/tags/layout.d.ts +2 -1
  33. package/node_modules/liquidjs/dist/tags/liquid.d.ts +2 -1
  34. package/node_modules/liquidjs/dist/tags/render.d.ts +3 -2
  35. package/node_modules/liquidjs/dist/tags/tablerow.d.ts +2 -1
  36. package/node_modules/liquidjs/dist/tags/unless.d.ts +2 -1
  37. package/node_modules/liquidjs/dist/template/filter-impl-options.d.ts +2 -0
  38. package/node_modules/liquidjs/dist/template/filter.d.ts +3 -1
  39. package/node_modules/liquidjs/dist/template/hash.d.ts +1 -1
  40. package/node_modules/liquidjs/dist/template/tag.d.ts +2 -2
  41. package/node_modules/liquidjs/dist/tokens/identifier-token.d.ts +0 -1
  42. package/node_modules/liquidjs/dist/util/assert.d.ts +1 -0
  43. package/node_modules/liquidjs/dist/util/error.d.ts +7 -2
  44. package/node_modules/liquidjs/dist/util/error.spec.d.ts +1 -0
  45. package/node_modules/liquidjs/dist/util/index.d.ts +2 -1
  46. package/node_modules/liquidjs/dist/util/intl.d.ts +6 -0
  47. package/node_modules/liquidjs/dist/util/limiter.d.ts +8 -0
  48. package/node_modules/liquidjs/dist/util/liquid-date.d.ts +39 -7
  49. package/node_modules/liquidjs/dist/util/liquid-date.spec.d.ts +1 -0
  50. package/node_modules/liquidjs/dist/util/performance.d.ts +5 -0
  51. package/node_modules/liquidjs/dist/util/performance.spec.d.ts +1 -0
  52. package/node_modules/liquidjs/dist/util/type-guards.spec.d.ts +1 -0
  53. package/node_modules/liquidjs/dist/util/underscore.d.ts +2 -1
  54. package/node_modules/liquidjs/package.json +10 -7
  55. package/package.json +16 -12
  56. package/node_modules/liquidjs/dist/util/timezone-date.d.ts +0 -44
  57. /package/node_modules/liquidjs/dist/{util/timezone-date.spec.d.ts → fs/map-fs.spec.d.ts} +0 -0
@@ -1,5 +1,5 @@
1
1
  /*
2
- * liquidjs@10.13.0, https://github.com/harttle/liquidjs
2
+ * liquidjs@10.18.0, https://github.com/harttle/liquidjs
3
3
  * (c) 2016-2024 harttle
4
4
  * Released under the MIT License.
5
5
  */
@@ -106,6 +106,10 @@ function toArray(val) {
106
106
  function toValue(value) {
107
107
  return (value instanceof Drop && isFunction(value.valueOf)) ? value.valueOf() : value;
108
108
  }
109
+ function toNumber(value) {
110
+ value = Number(value);
111
+ return isNaN(value) ? 0 : value;
112
+ }
109
113
  function isNumber(value) {
110
114
  return typeof value === 'number';
111
115
  }
@@ -205,14 +209,14 @@ function caseInsensitiveCompare(a, b) {
205
209
  return 0;
206
210
  }
207
211
  function argumentsToValue(fn) {
208
- return (...args) => fn(...args.map(toValue));
212
+ return function (...args) { return fn.call(this, ...args.map(toValue)); };
209
213
  }
210
214
  function escapeRegExp(text) {
211
215
  return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
212
216
  }
213
217
 
214
218
  /**
215
- * targeting ES5, extends Error won't create a proper prototype chain, need a trait to kee track of classes
219
+ * targeting ES5, extends Error won't create a proper prototype chain, need a trait to keep track of classes
216
220
  */
217
221
  const TRAIT = '__liquidClass__';
218
222
  class LiquidError extends Error {
@@ -266,6 +270,19 @@ class RenderError extends LiquidError {
266
270
  return obj.name === 'RenderError';
267
271
  }
268
272
  }
273
+ class LiquidErrors extends LiquidError {
274
+ constructor(errors) {
275
+ super(errors[0], errors[0].token);
276
+ this.errors = errors;
277
+ this.name = 'LiquidErrors';
278
+ const s = errors.length > 1 ? 's' : '';
279
+ this.message = `${errors.length} error${s} found`;
280
+ super.update();
281
+ }
282
+ static is(obj) {
283
+ return obj.name === 'LiquidErrors';
284
+ }
285
+ }
269
286
  class UndefinedVariableError extends LiquidError {
270
287
  constructor(err, token) {
271
288
  super(err, token);
@@ -344,6 +361,9 @@ function assert(predicate, message) {
344
361
  : (message || `expect ${predicate} to be true`);
345
362
  throw new AssertionError(msg);
346
363
  }
364
+ }
365
+ function assertEmpty(predicate, message = `unexpected ${JSON.stringify(predicate)}`) {
366
+ assert(!predicate, message);
347
367
  }
348
368
 
349
369
  class NullDrop extends Drop {
@@ -457,7 +477,12 @@ class BlockDrop extends Drop {
457
477
  }
458
478
 
459
479
  function isComparable(arg) {
460
- return arg && isFunction(arg.equals);
480
+ return (arg &&
481
+ isFunction(arg.equals) &&
482
+ isFunction(arg.gt) &&
483
+ isFunction(arg.geq) &&
484
+ isFunction(arg.lt) &&
485
+ isFunction(arg.leq));
461
486
  }
462
487
 
463
488
  const nil = new NullDrop();
@@ -539,18 +564,6 @@ function toValueSync(val) {
539
564
  }
540
565
 
541
566
  const rFormat = /%([-_0^#:]+)?(\d+)?([EO])?(.)/;
542
- const monthNames = [
543
- 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
544
- 'September', 'October', 'November', 'December'
545
- ];
546
- const dayNames = [
547
- 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'
548
- ];
549
- const monthNamesShort = monthNames.map(abbr);
550
- const dayNamesShort = dayNames.map(abbr);
551
- function abbr(str) {
552
- return str.slice(0, 3);
553
- }
554
567
  // prototype extensions
555
568
  function daysInMonth(d) {
556
569
  const feb = isLeapYear(d) ? 29 : 28;
@@ -605,19 +618,7 @@ const padWidths = {
605
618
  U: 2,
606
619
  W: 2
607
620
  };
608
- // default to '0'
609
- const padChars = {
610
- a: ' ',
611
- A: ' ',
612
- b: ' ',
613
- B: ' ',
614
- c: ' ',
615
- e: ' ',
616
- k: ' ',
617
- l: ' ',
618
- p: ' ',
619
- P: ' '
620
- };
621
+ const padSpaceChars = new Set('aAbBceklpP');
621
622
  function getTimezoneOffset(d, opts) {
622
623
  const nOffset = Math.abs(d.getTimezoneOffset());
623
624
  const h = Math.floor(nOffset / 60);
@@ -628,10 +629,10 @@ function getTimezoneOffset(d, opts) {
628
629
  padStart(m, 2, '0');
629
630
  }
630
631
  const formatCodes = {
631
- a: (d) => dayNamesShort[d.getDay()],
632
- A: (d) => dayNames[d.getDay()],
633
- b: (d) => monthNamesShort[d.getMonth()],
634
- B: (d) => monthNames[d.getMonth()],
632
+ a: (d) => d.getShortWeekdayName(),
633
+ A: (d) => d.getLongWeekdayName(),
634
+ b: (d) => d.getShortMonthName(),
635
+ B: (d) => d.getLongMonthName(),
635
636
  c: (d) => d.toLocaleString(),
636
637
  C: (d) => century(d),
637
638
  d: (d) => d.getDate(),
@@ -663,12 +664,7 @@ const formatCodes = {
663
664
  y: (d) => d.getFullYear().toString().slice(2, 4),
664
665
  Y: (d) => d.getFullYear(),
665
666
  z: getTimezoneOffset,
666
- Z: (d, opts) => {
667
- if (d.getTimezoneName) {
668
- return d.getTimezoneName() || getTimezoneOffset(d, opts);
669
- }
670
- return (typeof Intl !== 'undefined' ? Intl.DateTimeFormat().resolvedOptions().timeZone : '');
671
- },
667
+ Z: (d, opts) => d.getTimeZoneName() || getTimezoneOffset(d, opts),
672
668
  't': () => '\t',
673
669
  'n': () => '\n',
674
670
  '%': () => '%'
@@ -694,7 +690,7 @@ function format(d, match) {
694
690
  for (const flag of flagStr)
695
691
  flags[flag] = true;
696
692
  let ret = String(convert(d, { flags, width, modifier }));
697
- let padChar = padChars[conversion] || '0';
693
+ let padChar = padSpaceChars.has(conversion) ? ' ' : '0';
698
694
  let padWidth = width || padWidths[conversion] || 0;
699
695
  if (flags['^'])
700
696
  ret = ret.toUpperCase();
@@ -709,9 +705,22 @@ function format(d, match) {
709
705
  return padStart(ret, padWidth, padChar);
710
706
  }
711
707
 
708
+ function getDateTimeFormat() {
709
+ return (typeof Intl !== 'undefined' ? Intl.DateTimeFormat : undefined);
710
+ }
711
+
712
712
  // one minute in milliseconds
713
713
  const OneMinute = 60000;
714
714
  const ISO8601_TIMEZONE_PATTERN = /([zZ]|([+-])(\d{2}):(\d{2}))$/;
715
+ const monthNames = [
716
+ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
717
+ 'September', 'October', 'November', 'December'
718
+ ];
719
+ const monthNamesShort = monthNames.map(name => name.slice(0, 3));
720
+ const dayNames = [
721
+ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'
722
+ ];
723
+ const dayNamesShort = dayNames.map(name => name.slice(0, 3));
715
724
  /**
716
725
  * A date implementation with timezone info, just like Ruby date
717
726
  *
@@ -719,12 +728,16 @@ const ISO8601_TIMEZONE_PATTERN = /([zZ]|([+-])(\d{2}):(\d{2}))$/;
719
728
  * - create a Date offset by it's timezone difference, avoiding overriding a bunch of methods
720
729
  * - rewrite getTimezoneOffset() to trick strftime
721
730
  */
722
- class TimezoneDate {
723
- constructor(init, timezone) {
724
- this.date = init instanceof TimezoneDate
725
- ? init.date
726
- : new Date(init);
727
- this.timezoneOffset = isString(timezone) ? TimezoneDate.getTimezoneOffset(timezone, this.date) : timezone;
731
+ class LiquidDate {
732
+ constructor(init, locale, timezone) {
733
+ this.locale = locale;
734
+ this.DateTimeFormat = getDateTimeFormat();
735
+ this.date = new Date(init);
736
+ this.timezoneFixed = timezone !== undefined;
737
+ if (timezone === undefined) {
738
+ timezone = this.date.getTimezoneOffset();
739
+ }
740
+ this.timezoneOffset = isString(timezone) ? LiquidDate.getTimezoneOffset(timezone, this.date) : timezone;
728
741
  this.timezoneName = isString(timezone) ? timezone : '';
729
742
  const diff = (this.date.getTimezoneOffset() - this.timezoneOffset) * OneMinute;
730
743
  const time = this.date.getTime() + diff;
@@ -772,8 +785,30 @@ class TimezoneDate {
772
785
  getTimezoneOffset() {
773
786
  return this.timezoneOffset;
774
787
  }
775
- getTimezoneName() {
776
- return this.timezoneName;
788
+ getTimeZoneName() {
789
+ if (this.timezoneFixed)
790
+ return this.timezoneName;
791
+ if (!this.DateTimeFormat)
792
+ return;
793
+ return this.DateTimeFormat().resolvedOptions().timeZone;
794
+ }
795
+ getLongMonthName() {
796
+ return this.format({ month: 'long' }) ?? monthNames[this.getMonth()];
797
+ }
798
+ getShortMonthName() {
799
+ return this.format({ month: 'short' }) ?? monthNamesShort[this.getMonth()];
800
+ }
801
+ getLongWeekdayName() {
802
+ return this.format({ weekday: 'long' }) ?? dayNames[this.displayDate.getDay()];
803
+ }
804
+ getShortWeekdayName() {
805
+ return this.format({ weekday: 'short' }) ?? dayNamesShort[this.displayDate.getDay()];
806
+ }
807
+ valid() {
808
+ return !isNaN(this.getTime());
809
+ }
810
+ format(options) {
811
+ return this.DateTimeFormat && this.DateTimeFormat(this.locale, options).format(this.displayDate);
777
812
  }
778
813
  /**
779
814
  * Create a Date object fixed to it's declared Timezone. Both
@@ -788,21 +823,21 @@ class TimezoneDate {
788
823
  * we create a different Date to trick strftime, it's both simpler and more performant.
789
824
  * Given that a template is expected to be parsed fewer times than rendered.
790
825
  */
791
- static createDateFixedToTimezone(dateString) {
826
+ static createDateFixedToTimezone(dateString, locale) {
792
827
  const m = dateString.match(ISO8601_TIMEZONE_PATTERN);
793
828
  // representing a UTC timestamp
794
829
  if (m && m[1] === 'Z') {
795
- return new TimezoneDate(+new Date(dateString), 0);
830
+ return new LiquidDate(+new Date(dateString), locale, 0);
796
831
  }
797
832
  // has a timezone specified
798
833
  if (m && m[2] && m[3] && m[4]) {
799
834
  const [, , sign, hours, minutes] = m;
800
835
  const offset = (sign === '+' ? -1 : 1) * (parseInt(hours, 10) * 60 + parseInt(minutes, 10));
801
- return new TimezoneDate(+new Date(dateString), offset);
836
+ return new LiquidDate(+new Date(dateString), locale, offset);
802
837
  }
803
- return new Date(dateString);
838
+ return new LiquidDate(dateString, locale);
804
839
  }
805
- static getTimezoneOffset(timezoneName, date = new Date()) {
840
+ static getTimezoneOffset(timezoneName, date) {
806
841
  const localDateString = date.toLocaleString('en-US', { timeZone: timezoneName });
807
842
  const utcDateString = date.toLocaleString('en-US', { timeZone: 'UTC' });
808
843
  const localDate = new Date(localDateString);
@@ -811,6 +846,23 @@ class TimezoneDate {
811
846
  }
812
847
  }
813
848
 
849
+ class Limiter {
850
+ constructor(resource, limit) {
851
+ this.base = 0;
852
+ this.message = `${resource} limit exceeded`;
853
+ this.limit = limit;
854
+ }
855
+ use(count) {
856
+ count = toNumber(count);
857
+ assert(this.base + count <= this.limit, this.message);
858
+ this.base += count;
859
+ }
860
+ check(count) {
861
+ count = toNumber(count);
862
+ assert(count <= this.limit, this.message);
863
+ }
864
+ }
865
+
814
866
  class DelimitedToken extends Token {
815
867
  constructor(kind, [contentBegin, contentEnd], input, begin, end, trimLeft, trimRight, file) {
816
868
  super(kind, input, begin, end, file);
@@ -891,16 +943,6 @@ class IdentifierToken extends Token {
891
943
  this.file = file;
892
944
  this.content = this.getText();
893
945
  }
894
- isNumber(allowSign = false) {
895
- const begin = allowSign && TYPES[this.input.charCodeAt(this.begin)] & SIGN
896
- ? this.begin + 1
897
- : this.begin;
898
- for (let i = begin; i < this.end; i++) {
899
- if (!(TYPES[this.input.charCodeAt(i)] & NUMBER))
900
- return false;
901
- }
902
- return true;
903
- }
904
946
  }
905
947
 
906
948
  class LiteralToken extends Token {
@@ -1089,6 +1131,15 @@ class FilteredValueToken extends Token {
1089
1131
  }
1090
1132
  }
1091
1133
 
1134
+ const polyfill = {
1135
+ now: () => Date.now()
1136
+ };
1137
+ function getPerformance() {
1138
+ return (typeof global === 'object' && global.performance) ||
1139
+ (typeof window === 'object' && window.performance) ||
1140
+ polyfill;
1141
+ }
1142
+
1092
1143
  class SimpleEmitter {
1093
1144
  constructor() {
1094
1145
  this.buffer = '';
@@ -1144,7 +1195,9 @@ class Render {
1144
1195
  if (!emitter) {
1145
1196
  emitter = ctx.opts.keepOutputType ? new KeepingTypeEmitter() : new SimpleEmitter();
1146
1197
  }
1198
+ const errors = [];
1147
1199
  for (const tpl of templates) {
1200
+ ctx.renderLimit.check(getPerformance().now());
1148
1201
  try {
1149
1202
  // if tpl.render supports emitter, it'll return empty `html`
1150
1203
  const html = yield tpl.render(ctx, emitter);
@@ -1154,10 +1207,16 @@ class Render {
1154
1207
  break;
1155
1208
  }
1156
1209
  catch (e) {
1157
- const err = RenderError.is(e) ? e : new RenderError(e, tpl);
1158
- throw err;
1210
+ const err = LiquidError.is(e) ? e : new RenderError(e, tpl);
1211
+ if (ctx.opts.catchAllErrors)
1212
+ errors.push(err);
1213
+ else
1214
+ throw err;
1159
1215
  }
1160
1216
  }
1217
+ if (errors.length) {
1218
+ throw new LiquidErrors(errors);
1219
+ }
1161
1220
  return emitter.buffer;
1162
1221
  }
1163
1222
  }
@@ -1251,6 +1310,7 @@ function isTruthy(val, ctx) {
1251
1310
  return !isFalsy(val, ctx);
1252
1311
  }
1253
1312
  function isFalsy(val, ctx) {
1313
+ val = toValue(val);
1254
1314
  if (ctx.opts.jsTruthy) {
1255
1315
  return !val;
1256
1316
  }
@@ -1378,7 +1438,7 @@ class LRU {
1378
1438
  }
1379
1439
  }
1380
1440
 
1381
- const requireResolve = require.resolve;
1441
+ const requireResolve = (partial) => require.resolve(partial, { paths: ['.'] });
1382
1442
 
1383
1443
  const statAsync = promisify(fs$1.stat);
1384
1444
  const readFileAsync = promisify(fs$1.readFile);
@@ -1495,22 +1555,30 @@ const unescapeMap = {
1495
1555
  '&#39;': "'"
1496
1556
  };
1497
1557
  function escape(str) {
1498
- return stringify(str).replace(/&|<|>|"|'/g, m => escapeMap[m]);
1558
+ str = stringify(str);
1559
+ this.context.memoryLimit.use(str.length);
1560
+ return str.replace(/&|<|>|"|'/g, m => escapeMap[m]);
1499
1561
  }
1500
1562
  function xml_escape(str) {
1501
- return escape(str);
1563
+ return escape.call(this, str);
1502
1564
  }
1503
1565
  function unescape(str) {
1504
- return stringify(str).replace(/&(amp|lt|gt|#34|#39);/g, m => unescapeMap[m]);
1566
+ str = stringify(str);
1567
+ this.context.memoryLimit.use(str.length);
1568
+ return str.replace(/&(amp|lt|gt|#34|#39);/g, m => unescapeMap[m]);
1505
1569
  }
1506
1570
  function escape_once(str) {
1507
- return escape(unescape(stringify(str)));
1571
+ return escape.call(this, unescape.call(this, str));
1508
1572
  }
1509
1573
  function newline_to_br(v) {
1510
- return stringify(v).replace(/\r?\n/gm, '<br />\n');
1574
+ const str = stringify(v);
1575
+ this.context.memoryLimit.use(str.length);
1576
+ return str.replace(/\r?\n/gm, '<br />\n');
1511
1577
  }
1512
1578
  function strip_html(v) {
1513
- return stringify(v).replace(/<script[\s\S]*?<\/script>|<style[\s\S]*?<\/style>|<.*?>|<!--[\s\S]*?-->/g, '');
1579
+ const str = stringify(v);
1580
+ this.context.memoryLimit.use(str.length);
1581
+ return str.replace(/<script[\s\S]*?<\/script>|<style[\s\S]*?<\/style>|<.*?>|<!--[\s\S]*?-->/g, '');
1514
1582
  }
1515
1583
 
1516
1584
  var htmlFilters = /*#__PURE__*/Object.freeze({
@@ -1522,18 +1590,64 @@ var htmlFilters = /*#__PURE__*/Object.freeze({
1522
1590
  strip_html: strip_html
1523
1591
  });
1524
1592
 
1593
+ class MapFS {
1594
+ constructor(mapping) {
1595
+ this.mapping = mapping;
1596
+ this.sep = '/';
1597
+ }
1598
+ async exists(filepath) {
1599
+ return this.existsSync(filepath);
1600
+ }
1601
+ existsSync(filepath) {
1602
+ return !isNil(this.mapping[filepath]);
1603
+ }
1604
+ async readFile(filepath) {
1605
+ return this.readFileSync(filepath);
1606
+ }
1607
+ readFileSync(filepath) {
1608
+ const content = this.mapping[filepath];
1609
+ if (isNil(content))
1610
+ throw new Error(`ENOENT: ${filepath}`);
1611
+ return content;
1612
+ }
1613
+ dirname(filepath) {
1614
+ const segments = filepath.split(this.sep);
1615
+ segments.pop();
1616
+ return segments.join(this.sep);
1617
+ }
1618
+ resolve(dir, file, ext) {
1619
+ file += ext;
1620
+ if (dir === '.')
1621
+ return file;
1622
+ const segments = dir.split(/\/+/);
1623
+ for (const segment of file.split(this.sep)) {
1624
+ if (segment === '.' || segment === '')
1625
+ continue;
1626
+ else if (segment === '..') {
1627
+ if (segments.length > 1 || segments[0] !== '')
1628
+ segments.pop();
1629
+ }
1630
+ else
1631
+ segments.push(segment);
1632
+ }
1633
+ return segments.join(this.sep);
1634
+ }
1635
+ }
1636
+
1525
1637
  const defaultOptions = {
1526
1638
  root: ['.'],
1527
1639
  layouts: ['.'],
1528
1640
  partials: ['.'],
1529
1641
  relativeReference: true,
1530
1642
  jekyllInclude: false,
1643
+ keyValueSeparator: ':',
1531
1644
  cache: undefined,
1532
1645
  extname: '',
1533
1646
  fs: fs,
1534
1647
  dynamicPartials: true,
1535
1648
  jsTruthy: false,
1536
1649
  dateFormat: '%A, %B %-e, %Y at %-l:%M %P %z',
1650
+ locale: '',
1537
1651
  trimTagRight: false,
1538
1652
  trimTagLeft: false,
1539
1653
  trimOutputRight: false,
@@ -1550,7 +1664,10 @@ const defaultOptions = {
1550
1664
  lenientIf: false,
1551
1665
  globals: {},
1552
1666
  keepOutputType: false,
1553
- operators: defaultOperators
1667
+ operators: defaultOperators,
1668
+ memoryLimit: Infinity,
1669
+ parseLimit: Infinity,
1670
+ renderLimit: Infinity
1554
1671
  };
1555
1672
  function normalize(options) {
1556
1673
  if (options.hasOwnProperty('root')) {
@@ -1578,6 +1695,14 @@ function normalize(options) {
1578
1695
  options.partials = normalizeDirectoryList(options.partials);
1579
1696
  options.layouts = normalizeDirectoryList(options.layouts);
1580
1697
  options.outputEscape = options.outputEscape && getOutputEscapeFunction(options.outputEscape);
1698
+ if (!options.locale) {
1699
+ options.locale = getDateTimeFormat()?.().resolvedOptions().locale ?? 'en-US';
1700
+ }
1701
+ if (options.templates) {
1702
+ options.fs = new MapFS(options.templates);
1703
+ options.relativeReference = true;
1704
+ options.root = options.partials = options.layouts = '.';
1705
+ }
1581
1706
  return options;
1582
1707
  }
1583
1708
  function getOutputEscapeFunction(nameOrFunction) {
@@ -1704,9 +1829,7 @@ class Tokenizer {
1704
1829
  this.skipBlank();
1705
1830
  if (this.end())
1706
1831
  return null;
1707
- this.assert(this.peek() === '|', `expected "|" before filter`);
1708
- this.p++;
1709
- const begin = this.p;
1832
+ this.assert(this.read() === '|', `expected "|" before filter`);
1710
1833
  const name = this.readIdentifier();
1711
1834
  if (!name.size()) {
1712
1835
  this.assert(this.end(), `expected filter name`);
@@ -1727,7 +1850,7 @@ class Tokenizer {
1727
1850
  else {
1728
1851
  throw this.error('expected ":" after filter name');
1729
1852
  }
1730
- return new FilterToken(name.getText(), args, this.input, begin, this.p, this.file);
1853
+ return new FilterToken(name.getText(), args, this.input, name.begin, this.p, this.file);
1731
1854
  }
1732
1855
  readFilterArg() {
1733
1856
  const key = this.readValue();
@@ -1768,7 +1891,7 @@ class Tokenizer {
1768
1891
  }
1769
1892
  return new HTMLToken(this.input, begin, this.p, this.file);
1770
1893
  }
1771
- readTagToken(options = defaultOptions) {
1894
+ readTagToken(options) {
1772
1895
  const { file, input } = this;
1773
1896
  const begin = this.p;
1774
1897
  if (this.readToDelimiter(options.tagDelimiterRight) === -1) {
@@ -1899,7 +2022,7 @@ class Tokenizer {
1899
2022
  return;
1900
2023
  let value;
1901
2024
  this.skipBlank();
1902
- const sep = jekyllStyle ? '=' : ':';
2025
+ const sep = isString(jekyllStyle) ? jekyllStyle : (jekyllStyle ? '=' : ':');
1903
2026
  if (this.peek() === sep) {
1904
2027
  ++this.p;
1905
2028
  value = this.readValue();
@@ -1915,6 +2038,9 @@ class Tokenizer {
1915
2038
  end() {
1916
2039
  return this.p >= this.N;
1917
2040
  }
2041
+ read() {
2042
+ return this.input[this.p++];
2043
+ }
1918
2044
  readTo(end) {
1919
2045
  while (this.p < this.N) {
1920
2046
  ++this.p;
@@ -2163,7 +2289,7 @@ function createTagClass(options) {
2163
2289
  }
2164
2290
  }
2165
2291
  *render(ctx, emitter) {
2166
- const hash = (yield new Hash(this.token.args).render(ctx));
2292
+ const hash = (yield new Hash(this.token.args, ctx.opts.keyValueSeparator).render(ctx));
2167
2293
  return yield options.render.call(this, ctx, emitter, hash);
2168
2294
  }
2169
2295
  };
@@ -2174,13 +2300,14 @@ function isKeyValuePair(arr) {
2174
2300
  }
2175
2301
 
2176
2302
  class Filter {
2177
- constructor(name, options, args, liquid) {
2178
- this.name = name;
2303
+ constructor(token, options, liquid) {
2304
+ this.token = token;
2305
+ this.name = token.name;
2179
2306
  this.handler = isFunction(options)
2180
2307
  ? options
2181
2308
  : (isFunction(options?.handler) ? options.handler : identify);
2182
2309
  this.raw = !isFunction(options) && !!options?.raw;
2183
- this.args = args;
2310
+ this.args = token.args;
2184
2311
  this.liquid = liquid;
2185
2312
  }
2186
2313
  *render(value, context) {
@@ -2191,7 +2318,7 @@ class Filter {
2191
2318
  else
2192
2319
  argv.push(yield evalToken(arg, context));
2193
2320
  }
2194
- return yield this.handler.apply({ context, liquid: this.liquid }, [value, ...argv]);
2321
+ return yield this.handler.apply({ context, token: this.token, liquid: this.liquid }, [value, ...argv]);
2195
2322
  }
2196
2323
  }
2197
2324
 
@@ -2205,7 +2332,7 @@ class Value {
2205
2332
  ? new Tokenizer(input, liquid.options.operators).readFilteredValue()
2206
2333
  : input;
2207
2334
  this.initial = token.initial;
2208
- this.filters = token.filters.map(({ name, args }) => new Filter(name, this.getFilter(liquid, name), args, liquid));
2335
+ this.filters = token.filters.map(token => new Filter(token, this.getFilter(liquid, token.name), liquid));
2209
2336
  }
2210
2337
  *value(ctx, lenient) {
2211
2338
  lenient = lenient || (ctx.opts.lenientIf && this.filters.length > 0 && this.filters[0].name === 'default');
@@ -2230,7 +2357,8 @@ class Output extends TemplateImpl {
2230
2357
  const filters = this.value.filters;
2231
2358
  const outputEscape = liquid.options.outputEscape;
2232
2359
  if (!filters[filters.length - 1]?.raw && outputEscape) {
2233
- filters.push(new Filter(toString.call(outputEscape), outputEscape, [], liquid));
2360
+ const token = new FilterToken(toString.call(outputEscape), [], '', 0, 0);
2361
+ filters.push(new Filter(token, outputEscape, liquid));
2234
2362
  }
2235
2363
  }
2236
2364
  *render(ctx, emitter) {
@@ -2265,7 +2393,7 @@ class Loader {
2265
2393
  this.shouldLoadRelative = (referencedFile) => rRelativePath.test(referencedFile);
2266
2394
  }
2267
2395
  else {
2268
- this.shouldLoadRelative = (referencedFile) => false;
2396
+ this.shouldLoadRelative = (_referencedFile) => false;
2269
2397
  }
2270
2398
  this.contains = this.options.fs.contains || (() => true);
2271
2399
  }
@@ -2322,8 +2450,11 @@ class Parser {
2322
2450
  this.fs = this.liquid.options.fs;
2323
2451
  this.parseFile = this.cache ? this._parseFileCached : this._parseFile;
2324
2452
  this.loader = new Loader(this.liquid.options);
2453
+ this.parseLimit = new Limiter('parse length', liquid.options.parseLimit);
2325
2454
  }
2326
2455
  parse(html, filepath) {
2456
+ html = String(html);
2457
+ this.parseLimit.use(html.length);
2327
2458
  const tokenizer = new Tokenizer(html, this.liquid.options.operators, filepath);
2328
2459
  const tokens = tokenizer.readTopLevelTokens(this.liquid.options);
2329
2460
  return this.parseTokens(tokens);
@@ -2331,9 +2462,20 @@ class Parser {
2331
2462
  parseTokens(tokens) {
2332
2463
  let token;
2333
2464
  const templates = [];
2465
+ const errors = [];
2334
2466
  while ((token = tokens.shift())) {
2335
- templates.push(this.parseToken(token, tokens));
2467
+ try {
2468
+ templates.push(this.parseToken(token, tokens));
2469
+ }
2470
+ catch (err) {
2471
+ if (this.liquid.options.catchAllErrors)
2472
+ errors.push(err);
2473
+ else
2474
+ throw err;
2475
+ }
2336
2476
  }
2477
+ if (errors.length)
2478
+ throw new LiquidErrors(errors);
2337
2479
  return templates;
2338
2480
  }
2339
2481
  parseToken(token, remainTokens) {
@@ -2341,7 +2483,7 @@ class Parser {
2341
2483
  if (isTagToken(token)) {
2342
2484
  const TagClass = this.liquid.tags[token.name];
2343
2485
  assert(TagClass, `tag "${token.name}" not found`);
2344
- return new TagClass(token, remainTokens, this.liquid);
2486
+ return new TagClass(token, remainTokens, this.liquid, this);
2345
2487
  }
2346
2488
  if (isOutputToken(token)) {
2347
2489
  return new Output(token, this.liquid);
@@ -2379,7 +2521,7 @@ class Parser {
2379
2521
  }
2380
2522
  *_parseFile(file, sync, type = LookupType.Root, currentFile) {
2381
2523
  const filepath = yield this.loader.lookup(file, type, sync, currentFile);
2382
- return this.liquid.parse(sync ? this.fs.readFileSync(filepath) : yield this.fs.readFile(filepath), filepath);
2524
+ return this.parse(sync ? this.fs.readFileSync(filepath) : yield this.fs.readFile(filepath), filepath);
2383
2525
  }
2384
2526
  }
2385
2527
 
@@ -2479,7 +2621,7 @@ var __assign = function() {
2479
2621
  };
2480
2622
 
2481
2623
  class Context {
2482
- constructor(env = {}, opts = defaultOptions, renderOptions = {}) {
2624
+ constructor(env = {}, opts = defaultOptions, renderOptions = {}, { memoryLimit, renderLimit } = {}) {
2483
2625
  /**
2484
2626
  * insert a Context-level empty scope,
2485
2627
  * for tags like `{% capture %}` `{% assign %}` to operate
@@ -2492,6 +2634,8 @@ class Context {
2492
2634
  this.environments = isObject(env) ? env : Object(env);
2493
2635
  this.strictVariables = renderOptions.strictVariables ?? this.opts.strictVariables;
2494
2636
  this.ownPropertyOnly = renderOptions.ownPropertyOnly ?? opts.ownPropertyOnly;
2637
+ this.memoryLimit = memoryLimit ?? new Limiter('memory alloc', renderOptions.memoryLimit ?? opts.memoryLimit);
2638
+ this.renderLimit = renderLimit ?? new Limiter('template render', getPerformance().now() + (renderOptions.renderLimit ?? opts.renderLimit));
2495
2639
  }
2496
2640
  getRegister(key) {
2497
2641
  return (this.registers[key] = this.registers[key] || {});
@@ -2548,6 +2692,16 @@ class Context {
2548
2692
  bottom() {
2549
2693
  return this.scopes[0];
2550
2694
  }
2695
+ spawn(scope = {}) {
2696
+ return new Context(scope, this.opts, {
2697
+ sync: this.sync,
2698
+ globals: this.globals,
2699
+ strictVariables: this.strictVariables
2700
+ }, {
2701
+ renderLimit: this.renderLimit,
2702
+ memoryLimit: this.memoryLimit
2703
+ });
2704
+ }
2551
2705
  findScope(key) {
2552
2706
  for (let i = this.scopes.length - 1; i >= 0; i--) {
2553
2707
  const candidate = this.scopes[i];
@@ -2579,7 +2733,7 @@ function readProperty(obj, key, ownPropertyOnly) {
2579
2733
  return value;
2580
2734
  }
2581
2735
  function readJSProperty(obj, key, ownPropertyOnly) {
2582
- if (ownPropertyOnly && !Object.hasOwnProperty.call(obj, key) && !(obj instanceof Drop))
2736
+ if (ownPropertyOnly && !hasOwnProperty.call(obj, key) && !(obj instanceof Drop))
2583
2737
  return undefined;
2584
2738
  return obj[key];
2585
2739
  }
@@ -2594,7 +2748,7 @@ function readLast(obj) {
2594
2748
  return obj['last'];
2595
2749
  }
2596
2750
  function readSize(obj) {
2597
- if (obj.hasOwnProperty('size') || obj['size'] !== undefined)
2751
+ if (hasOwnProperty.call(obj, 'size') || obj['size'] !== undefined)
2598
2752
  return obj['size'];
2599
2753
  if (isArray(obj) || isString(obj))
2600
2754
  return obj.length;
@@ -2701,13 +2855,25 @@ var urlFilters = /*#__PURE__*/Object.freeze({
2701
2855
  slugify: slugify
2702
2856
  });
2703
2857
 
2704
- const join = argumentsToValue((v, arg) => toArray(v).join(arg === undefined ? ' ' : arg));
2858
+ const join = argumentsToValue(function (v, arg) {
2859
+ const array = toArray(v);
2860
+ const sep = isNil(arg) ? ' ' : stringify(arg);
2861
+ const complexity = array.length * (1 + sep.length);
2862
+ this.context.memoryLimit.use(complexity);
2863
+ return array.join(sep);
2864
+ });
2705
2865
  const last$1 = argumentsToValue((v) => isArray(v) ? last(v) : '');
2706
2866
  const first = argumentsToValue((v) => isArray(v) ? v[0] : '');
2707
- const reverse = argumentsToValue((v) => [...toArray(v)].reverse());
2867
+ const reverse = argumentsToValue(function (v) {
2868
+ const array = toArray(v);
2869
+ this.context.memoryLimit.use(array.length);
2870
+ return [...array].reverse();
2871
+ });
2708
2872
  function* sort(arr, property) {
2709
2873
  const values = [];
2710
- for (const item of toArray(arr)) {
2874
+ const array = toArray(arr);
2875
+ this.context.memoryLimit.use(array.length);
2876
+ for (const item of array) {
2711
2877
  values.push([
2712
2878
  item,
2713
2879
  property ? yield this.context._getFromScope(item, stringify(property).split('.'), false) : item
@@ -2724,35 +2890,47 @@ function sort_natural(input, property) {
2724
2890
  const compare = property === undefined
2725
2891
  ? caseInsensitiveCompare
2726
2892
  : (lhs, rhs) => caseInsensitiveCompare(lhs[propertyString], rhs[propertyString]);
2727
- return [...toArray(input)].sort(compare);
2893
+ const array = toArray(input);
2894
+ this.context.memoryLimit.use(array.length);
2895
+ return [...array].sort(compare);
2728
2896
  }
2729
2897
  const size = (v) => (v && v.length) || 0;
2730
2898
  function* map(arr, property) {
2731
2899
  const results = [];
2732
- for (const item of toArray(arr)) {
2900
+ const array = toArray(arr);
2901
+ this.context.memoryLimit.use(array.length);
2902
+ for (const item of array) {
2733
2903
  results.push(yield this.context._getFromScope(item, stringify(property), false));
2734
2904
  }
2735
2905
  return results;
2736
2906
  }
2737
2907
  function* sum(arr, property) {
2738
2908
  let sum = 0;
2739
- for (const item of toArray(arr)) {
2909
+ const array = toArray(arr);
2910
+ for (const item of array) {
2740
2911
  const data = Number(property ? yield this.context._getFromScope(item, stringify(property), false) : item);
2741
2912
  sum += Number.isNaN(data) ? 0 : data;
2742
2913
  }
2743
2914
  return sum;
2744
2915
  }
2745
2916
  function compact(arr) {
2746
- return toArray(arr).filter(x => !isNil(toValue(x)));
2917
+ const array = toArray(arr);
2918
+ this.context.memoryLimit.use(array.length);
2919
+ return array.filter(x => !isNil(toValue(x)));
2747
2920
  }
2748
2921
  function concat(v, arg = []) {
2749
- return toArray(v).concat(toArray(arg));
2922
+ const lhs = toArray(v);
2923
+ const rhs = toArray(arg);
2924
+ this.context.memoryLimit.use(lhs.length + rhs.length);
2925
+ return lhs.concat(rhs);
2750
2926
  }
2751
2927
  function push(v, arg) {
2752
- return concat(v, [arg]);
2928
+ return concat.call(this, v, [arg]);
2753
2929
  }
2754
2930
  function unshift(v, arg) {
2755
- const clone = [...toArray(v)];
2931
+ const array = toArray(v);
2932
+ this.context.memoryLimit.use(array.length);
2933
+ const clone = [...array];
2756
2934
  clone.unshift(arg);
2757
2935
  return clone;
2758
2936
  }
@@ -2762,7 +2940,9 @@ function pop(v) {
2762
2940
  return clone;
2763
2941
  }
2764
2942
  function shift(v) {
2765
- const clone = [...toArray(v)];
2943
+ const array = toArray(v);
2944
+ this.context.memoryLimit.use(array.length);
2945
+ const clone = [...array];
2766
2946
  clone.shift();
2767
2947
  return clone;
2768
2948
  }
@@ -2773,14 +2953,16 @@ function slice(v, begin, length = 1) {
2773
2953
  if (!isArray(v))
2774
2954
  v = stringify(v);
2775
2955
  begin = begin < 0 ? v.length + begin : begin;
2956
+ this.context.memoryLimit.use(length);
2776
2957
  return v.slice(begin, begin + length);
2777
2958
  }
2778
2959
  function* where(arr, property, expected) {
2779
2960
  const values = [];
2780
2961
  arr = toArray(arr);
2962
+ this.context.memoryLimit.use(arr.length);
2781
2963
  const token = new Tokenizer(stringify(property)).readScopeValue();
2782
2964
  for (const item of arr) {
2783
- values.push(yield evalToken(token, new Context(item)));
2965
+ values.push(yield evalToken(token, this.context.spawn(item)));
2784
2966
  }
2785
2967
  return arr.filter((_, i) => {
2786
2968
  if (expected === undefined)
@@ -2791,8 +2973,10 @@ function* where(arr, property, expected) {
2791
2973
  function* where_exp(arr, itemName, exp) {
2792
2974
  const filtered = [];
2793
2975
  const keyTemplate = new Value(stringify(exp), this.liquid);
2794
- for (const item of toArray(arr)) {
2795
- const value = yield keyTemplate.value(new Context({ [itemName]: item }));
2976
+ const array = toArray(arr);
2977
+ this.context.memoryLimit.use(array.length);
2978
+ for (const item of array) {
2979
+ const value = yield keyTemplate.value(this.context.spawn({ [itemName]: item }));
2796
2980
  if (value)
2797
2981
  filtered.push(item);
2798
2982
  }
@@ -2802,8 +2986,9 @@ function* group_by(arr, property) {
2802
2986
  const map = new Map();
2803
2987
  arr = toArray(arr);
2804
2988
  const token = new Tokenizer(stringify(property)).readScopeValue();
2989
+ this.context.memoryLimit.use(arr.length);
2805
2990
  for (const item of arr) {
2806
- const key = yield evalToken(token, new Context(item));
2991
+ const key = yield evalToken(token, this.context.spawn(item));
2807
2992
  if (!map.has(key))
2808
2993
  map.set(key, []);
2809
2994
  map.get(key).push(item);
@@ -2813,8 +2998,10 @@ function* group_by(arr, property) {
2813
2998
  function* group_by_exp(arr, itemName, exp) {
2814
2999
  const map = new Map();
2815
3000
  const keyTemplate = new Value(stringify(exp), this.liquid);
2816
- for (const item of toArray(arr)) {
2817
- const key = yield keyTemplate.value(new Context({ [itemName]: item }));
3001
+ arr = toArray(arr);
3002
+ this.context.memoryLimit.use(arr.length);
3003
+ for (const item of arr) {
3004
+ const key = yield keyTemplate.value(this.context.spawn({ [itemName]: item }));
2818
3005
  if (!map.has(key))
2819
3006
  map.set(key, []);
2820
3007
  map.get(key).push(item);
@@ -2823,31 +3010,26 @@ function* group_by_exp(arr, itemName, exp) {
2823
3010
  }
2824
3011
  function* find(arr, property, expected) {
2825
3012
  const token = new Tokenizer(stringify(property)).readScopeValue();
2826
- for (const item of toArray(arr)) {
2827
- const value = yield evalToken(token, new Context(item));
3013
+ const array = toArray(arr);
3014
+ for (const item of array) {
3015
+ const value = yield evalToken(token, this.context.spawn(item));
2828
3016
  if (equals(value, expected))
2829
3017
  return item;
2830
3018
  }
2831
- return null;
2832
3019
  }
2833
3020
  function* find_exp(arr, itemName, exp) {
2834
3021
  const predicate = new Value(stringify(exp), this.liquid);
2835
- for (const item of toArray(arr)) {
2836
- const value = yield predicate.value(new Context({ [itemName]: item }));
3022
+ const array = toArray(arr);
3023
+ for (const item of array) {
3024
+ const value = yield predicate.value(this.context.spawn({ [itemName]: item }));
2837
3025
  if (value)
2838
3026
  return item;
2839
3027
  }
2840
- return null;
2841
3028
  }
2842
3029
  function uniq(arr) {
2843
- arr = toValue(arr);
2844
- const u = {};
2845
- return (arr || []).filter(val => {
2846
- if (hasOwnProperty.call(u, String(val)))
2847
- return false;
2848
- u[String(val)] = true;
2849
- return true;
2850
- });
3030
+ arr = toArray(arr);
3031
+ this.context.memoryLimit.use(arr.length);
3032
+ return [...new Set(arr)];
2851
3033
  }
2852
3034
  function sample(v, count = 1) {
2853
3035
  v = toValue(v);
@@ -2855,6 +3037,7 @@ function sample(v, count = 1) {
2855
3037
  return [];
2856
3038
  if (!isArray(v))
2857
3039
  v = stringify(v);
3040
+ this.context.memoryLimit.use(count);
2858
3041
  const shuffled = [...v].sort(() => Math.random() - 0.5);
2859
3042
  if (count === 1)
2860
3043
  return shuffled[0];
@@ -2890,6 +3073,8 @@ var arrayFilters = /*#__PURE__*/Object.freeze({
2890
3073
  });
2891
3074
 
2892
3075
  function date(v, format, timezoneOffset) {
3076
+ const size = (v?.length ?? 0) + (format?.length ?? 0) + (timezoneOffset?.length ?? 0);
3077
+ this.context.memoryLimit.use(size);
2893
3078
  const date = parseDate(v, this.context.opts, timezoneOffset);
2894
3079
  if (!date)
2895
3080
  return v;
@@ -2923,39 +3108,30 @@ function stringify_date(v, month_type, type, style) {
2923
3108
  }
2924
3109
  function parseDate(v, opts, timezoneOffset) {
2925
3110
  let date;
3111
+ const defaultTimezoneOffset = timezoneOffset ?? opts.timezoneOffset;
3112
+ const locale = opts.locale;
2926
3113
  v = toValue(v);
2927
3114
  if (v === 'now' || v === 'today') {
2928
- date = new Date();
3115
+ date = new LiquidDate(Date.now(), locale, defaultTimezoneOffset);
2929
3116
  }
2930
3117
  else if (isNumber(v)) {
2931
- date = new Date(v * 1000);
3118
+ date = new LiquidDate(v * 1000, locale, defaultTimezoneOffset);
2932
3119
  }
2933
3120
  else if (isString(v)) {
2934
3121
  if (/^\d+$/.test(v)) {
2935
- date = new Date(+v * 1000);
3122
+ date = new LiquidDate(+v * 1000, locale, defaultTimezoneOffset);
2936
3123
  }
2937
- else if (opts.preserveTimezones) {
2938
- date = TimezoneDate.createDateFixedToTimezone(v);
3124
+ else if (opts.preserveTimezones && timezoneOffset === undefined) {
3125
+ date = LiquidDate.createDateFixedToTimezone(v, locale);
2939
3126
  }
2940
3127
  else {
2941
- date = new Date(v);
3128
+ date = new LiquidDate(v, locale, defaultTimezoneOffset);
2942
3129
  }
2943
3130
  }
2944
3131
  else {
2945
- date = v;
3132
+ date = new LiquidDate(v, locale, defaultTimezoneOffset);
2946
3133
  }
2947
- if (!isValidDate(date))
2948
- return;
2949
- if (timezoneOffset !== undefined) {
2950
- date = new TimezoneDate(date, timezoneOffset);
2951
- }
2952
- else if (!(date instanceof TimezoneDate) && opts.timezoneOffset !== undefined) {
2953
- date = new TimezoneDate(date, opts.timezoneOffset);
2954
- }
2955
- return date;
2956
- }
2957
- function isValidDate(date) {
2958
- return (date instanceof Date || date instanceof TimezoneDate) && !isNaN(date.getTime());
3134
+ return date.valid() ? date : undefined;
2959
3135
  }
2960
3136
 
2961
3137
  var dateFilters = /*#__PURE__*/Object.freeze({
@@ -2977,33 +3153,50 @@ const rCJKWord = /[\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF\u3040-\u309F\u30A0-\u
2977
3153
  const rNonCJKWord = /[^\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF\u3040-\u309F\u30A0-\u30FF\uAC00-\uD7AF\s]+/gu;
2978
3154
  function append(v, arg) {
2979
3155
  assert(arguments.length === 2, 'append expect 2 arguments');
2980
- return stringify(v) + stringify(arg);
3156
+ const lhs = stringify(v);
3157
+ const rhs = stringify(arg);
3158
+ this.context.memoryLimit.use(lhs.length + rhs.length);
3159
+ return lhs + rhs;
2981
3160
  }
2982
3161
  function prepend(v, arg) {
2983
3162
  assert(arguments.length === 2, 'prepend expect 2 arguments');
2984
- return stringify(arg) + stringify(v);
3163
+ const lhs = stringify(v);
3164
+ const rhs = stringify(arg);
3165
+ this.context.memoryLimit.use(lhs.length + rhs.length);
3166
+ return rhs + lhs;
2985
3167
  }
2986
3168
  function lstrip(v, chars) {
3169
+ const str = stringify(v);
3170
+ this.context.memoryLimit.use(str.length);
2987
3171
  if (chars) {
2988
3172
  chars = escapeRegExp(stringify(chars));
2989
- return stringify(v).replace(new RegExp(`^[${chars}]+`, 'g'), '');
3173
+ return str.replace(new RegExp(`^[${chars}]+`, 'g'), '');
2990
3174
  }
2991
- return stringify(v).replace(/^\s+/, '');
3175
+ return str.replace(/^\s+/, '');
2992
3176
  }
2993
3177
  function downcase(v) {
2994
- return stringify(v).toLowerCase();
3178
+ const str = stringify(v);
3179
+ this.context.memoryLimit.use(str.length);
3180
+ return str.toLowerCase();
2995
3181
  }
2996
- function upcase(str) {
3182
+ function upcase(v) {
3183
+ const str = stringify(v);
3184
+ this.context.memoryLimit.use(str.length);
2997
3185
  return stringify(str).toUpperCase();
2998
3186
  }
2999
3187
  function remove(v, arg) {
3000
- return stringify(v).split(stringify(arg)).join('');
3188
+ const str = stringify(v);
3189
+ this.context.memoryLimit.use(str.length);
3190
+ return str.split(stringify(arg)).join('');
3001
3191
  }
3002
3192
  function remove_first(v, l) {
3003
- return stringify(v).replace(stringify(l), '');
3193
+ const str = stringify(v);
3194
+ this.context.memoryLimit.use(str.length);
3195
+ return str.replace(stringify(l), '');
3004
3196
  }
3005
3197
  function remove_last(v, l) {
3006
3198
  const str = stringify(v);
3199
+ this.context.memoryLimit.use(str.length);
3007
3200
  const pattern = stringify(l);
3008
3201
  const index = str.lastIndexOf(pattern);
3009
3202
  if (index === -1)
@@ -3011,14 +3204,18 @@ function remove_last(v, l) {
3011
3204
  return str.substring(0, index) + str.substring(index + pattern.length);
3012
3205
  }
3013
3206
  function rstrip(str, chars) {
3207
+ str = stringify(str);
3208
+ this.context.memoryLimit.use(str.length);
3014
3209
  if (chars) {
3015
3210
  chars = escapeRegExp(stringify(chars));
3016
- return stringify(str).replace(new RegExp(`[${chars}]+$`, 'g'), '');
3211
+ return str.replace(new RegExp(`[${chars}]+$`, 'g'), '');
3017
3212
  }
3018
- return stringify(str).replace(/\s+$/, '');
3213
+ return str.replace(/\s+$/, '');
3019
3214
  }
3020
3215
  function split(v, arg) {
3021
- const arr = stringify(v).split(stringify(arg));
3216
+ const str = stringify(v);
3217
+ this.context.memoryLimit.use(str.length);
3218
+ const arr = str.split(stringify(arg));
3022
3219
  // align to ruby split, which is the behavior of shopify/liquid
3023
3220
  // see: https://ruby-doc.org/core-2.4.0/String.html#method-i-split
3024
3221
  while (arr.length && arr[arr.length - 1] === '')
@@ -3026,29 +3223,39 @@ function split(v, arg) {
3026
3223
  return arr;
3027
3224
  }
3028
3225
  function strip(v, chars) {
3226
+ const str = stringify(v);
3227
+ this.context.memoryLimit.use(str.length);
3029
3228
  if (chars) {
3030
3229
  chars = escapeRegExp(stringify(chars));
3031
- return stringify(v)
3230
+ return str
3032
3231
  .replace(new RegExp(`^[${chars}]+`, 'g'), '')
3033
3232
  .replace(new RegExp(`[${chars}]+$`, 'g'), '');
3034
3233
  }
3035
- return stringify(v).trim();
3234
+ return str.trim();
3036
3235
  }
3037
3236
  function strip_newlines(v) {
3038
- return stringify(v).replace(/\r?\n/gm, '');
3237
+ const str = stringify(v);
3238
+ this.context.memoryLimit.use(str.length);
3239
+ return str.replace(/\r?\n/gm, '');
3039
3240
  }
3040
3241
  function capitalize(str) {
3041
3242
  str = stringify(str);
3243
+ this.context.memoryLimit.use(str.length);
3042
3244
  return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
3043
3245
  }
3044
3246
  function replace(v, pattern, replacement) {
3045
- return stringify(v).split(stringify(pattern)).join(replacement);
3247
+ const str = stringify(v);
3248
+ this.context.memoryLimit.use(str.length);
3249
+ return str.split(stringify(pattern)).join(replacement);
3046
3250
  }
3047
3251
  function replace_first(v, arg1, arg2) {
3048
- return stringify(v).replace(stringify(arg1), arg2);
3252
+ const str = stringify(v);
3253
+ this.context.memoryLimit.use(str.length);
3254
+ return str.replace(stringify(arg1), arg2);
3049
3255
  }
3050
3256
  function replace_last(v, arg1, arg2) {
3051
3257
  const str = stringify(v);
3258
+ this.context.memoryLimit.use(str.length);
3052
3259
  const pattern = stringify(arg1);
3053
3260
  const index = str.lastIndexOf(pattern);
3054
3261
  if (index === -1)
@@ -3057,13 +3264,16 @@ function replace_last(v, arg1, arg2) {
3057
3264
  return str.substring(0, index) + replacement + str.substring(index + pattern.length);
3058
3265
  }
3059
3266
  function truncate(v, l = 50, o = '...') {
3060
- v = stringify(v);
3061
- if (v.length <= l)
3267
+ const str = stringify(v);
3268
+ this.context.memoryLimit.use(str.length);
3269
+ if (str.length <= l)
3062
3270
  return v;
3063
- return v.substring(0, l - o.length) + o;
3271
+ return str.substring(0, l - o.length) + o;
3064
3272
  }
3065
3273
  function truncatewords(v, words = 15, o = '...') {
3066
- const arr = stringify(v).split(/\s+/);
3274
+ const str = stringify(v);
3275
+ this.context.memoryLimit.use(str.length);
3276
+ const arr = str.split(/\s+/);
3067
3277
  if (words <= 0)
3068
3278
  words = 1;
3069
3279
  let ret = arr.slice(0, words).join(' ');
@@ -3072,11 +3282,14 @@ function truncatewords(v, words = 15, o = '...') {
3072
3282
  return ret;
3073
3283
  }
3074
3284
  function normalize_whitespace(v) {
3075
- v = stringify(v);
3076
- return v.replace(/\s+/g, ' ');
3285
+ const str = stringify(v);
3286
+ this.context.memoryLimit.use(str.length);
3287
+ return str.replace(/\s+/g, ' ');
3077
3288
  }
3078
3289
  function number_of_words(input, mode) {
3079
- input = stringify(input).trim();
3290
+ const str = stringify(input);
3291
+ this.context.memoryLimit.use(str.length);
3292
+ input = str.trim();
3080
3293
  if (!input)
3081
3294
  return 0;
3082
3295
  switch (mode) {
@@ -3094,6 +3307,7 @@ function number_of_words(input, mode) {
3094
3307
  }
3095
3308
  }
3096
3309
  function array_to_sentence_string(array, connector = 'and') {
3310
+ this.context.memoryLimit.use(array.length);
3097
3311
  switch (array.length) {
3098
3312
  case 0:
3099
3313
  return '';
@@ -3158,7 +3372,7 @@ class AssignTag extends Tag {
3158
3372
 
3159
3373
  const MODIFIERS = ['offset', 'limit', 'reversed'];
3160
3374
  class ForTag extends Tag {
3161
- constructor(token, remainTokens, liquid) {
3375
+ constructor(token, remainTokens, liquid, parser) {
3162
3376
  super(token, remainTokens, liquid);
3163
3377
  const variable = this.tokenizer.readIdentifier();
3164
3378
  const inStr = this.tokenizer.readIdentifier();
@@ -3168,18 +3382,16 @@ class ForTag extends Tag {
3168
3382
  }
3169
3383
  this.variable = variable.content;
3170
3384
  this.collection = collection;
3171
- this.hash = new Hash(this.tokenizer.remaining());
3385
+ this.hash = new Hash(this.tokenizer.remaining(), liquid.options.keyValueSeparator);
3172
3386
  this.templates = [];
3173
3387
  this.elseTemplates = [];
3174
3388
  let p;
3175
- const stream = this.liquid.parser.parseStream(remainTokens)
3389
+ const stream = parser.parseStream(remainTokens)
3176
3390
  .on('start', () => (p = this.templates))
3177
- .on('tag:else', () => (p = this.elseTemplates))
3178
- .on('tag:endfor', () => stream.stop())
3391
+ .on('tag:else', tag => { assertEmpty(tag.args); p = this.elseTemplates; })
3392
+ .on('tag:endfor', tag => { assertEmpty(tag.args); stream.stop(); })
3179
3393
  .on('template', (tpl) => p.push(tpl))
3180
- .on('end', () => {
3181
- throw new Error(`tag ${token.getText()} not closed`);
3182
- });
3394
+ .on('end', () => { throw new Error(`tag ${token.getText()} not closed`); });
3183
3395
  stream.start();
3184
3396
  }
3185
3397
  *render(ctx, emitter) {
@@ -3230,7 +3442,7 @@ function limit(arr, count) {
3230
3442
  }
3231
3443
 
3232
3444
  class CaptureTag extends Tag {
3233
- constructor(tagToken, remainTokens, liquid) {
3445
+ constructor(tagToken, remainTokens, liquid, parser) {
3234
3446
  super(tagToken, remainTokens, liquid);
3235
3447
  this.templates = [];
3236
3448
  this.variable = this.readVariableName();
@@ -3238,7 +3450,7 @@ class CaptureTag extends Tag {
3238
3450
  const token = remainTokens.shift();
3239
3451
  if (isTagToken(token) && token.name === 'endcapture')
3240
3452
  return;
3241
- this.templates.push(liquid.parser.parseToken(token, remainTokens));
3453
+ this.templates.push(parser.parseToken(token, remainTokens));
3242
3454
  }
3243
3455
  throw new Error(`tag ${tagToken.getText()} not closed`);
3244
3456
  }
@@ -3259,7 +3471,7 @@ class CaptureTag extends Tag {
3259
3471
  }
3260
3472
 
3261
3473
  class CaseTag extends Tag {
3262
- constructor(tagToken, remainTokens, liquid) {
3474
+ constructor(tagToken, remainTokens, liquid, parser) {
3263
3475
  super(tagToken, remainTokens, liquid);
3264
3476
  this.branches = [];
3265
3477
  this.elseTemplates = [];
@@ -3267,7 +3479,7 @@ class CaseTag extends Tag {
3267
3479
  this.elseTemplates = [];
3268
3480
  let p = [];
3269
3481
  let elseCount = 0;
3270
- const stream = this.liquid.parser.parseStream(remainTokens)
3482
+ const stream = parser.parseStream(remainTokens)
3271
3483
  .on('tag:when', (token) => {
3272
3484
  if (elseCount > 0) {
3273
3485
  return;
@@ -3338,10 +3550,10 @@ class CommentTag extends Tag {
3338
3550
  }
3339
3551
 
3340
3552
  class RenderTag extends Tag {
3341
- constructor(token, remainTokens, liquid) {
3553
+ constructor(token, remainTokens, liquid, parser) {
3342
3554
  super(token, remainTokens, liquid);
3343
3555
  const tokenizer = this.tokenizer;
3344
- this.file = parseFilePath(tokenizer, this.liquid);
3556
+ this.file = parseFilePath(tokenizer, this.liquid, parser);
3345
3557
  this.currentFile = token.file;
3346
3558
  while (!tokenizer.end()) {
3347
3559
  tokenizer.skipBlank();
@@ -3375,13 +3587,13 @@ class RenderTag extends Tag {
3375
3587
  tokenizer.p = begin;
3376
3588
  break;
3377
3589
  }
3378
- this.hash = new Hash(tokenizer.remaining());
3590
+ this.hash = new Hash(tokenizer.remaining(), liquid.options.keyValueSeparator);
3379
3591
  }
3380
3592
  *render(ctx, emitter) {
3381
3593
  const { liquid, hash } = this;
3382
3594
  const filepath = (yield renderFilePath(this['file'], ctx, liquid));
3383
3595
  assert(filepath, () => `illegal file path "${filepath}"`);
3384
- const childCtx = new Context({}, ctx.opts, { sync: ctx.sync, globals: ctx.globals, strictVariables: ctx.strictVariables });
3596
+ const childCtx = ctx.spawn();
3385
3597
  const scope = childCtx.bottom();
3386
3598
  __assign(scope, yield hash.render(ctx));
3387
3599
  if (this['with']) {
@@ -3411,7 +3623,7 @@ class RenderTag extends Tag {
3411
3623
  * @return Token for expression (not quoted)
3412
3624
  * @throws TypeError if cannot read next token
3413
3625
  */
3414
- function parseFilePath(tokenizer, liquid) {
3626
+ function parseFilePath(tokenizer, liquid, parser) {
3415
3627
  if (liquid.options.dynamicPartials) {
3416
3628
  const file = tokenizer.readValue();
3417
3629
  tokenizer.assert(file, 'illegal file path');
@@ -3419,13 +3631,13 @@ function parseFilePath(tokenizer, liquid) {
3419
3631
  return;
3420
3632
  if (isQuotedToken(file)) {
3421
3633
  // for filenames like "files/{{file}}", eval as liquid template
3422
- const templates = liquid.parse(evalQuotedToken(file));
3634
+ const templates = parser.parse(evalQuotedToken(file));
3423
3635
  return optimize(templates);
3424
3636
  }
3425
3637
  return file;
3426
3638
  }
3427
3639
  const tokens = [...tokenizer.readFileNameTemplate(liquid.options)];
3428
- const templates = optimize(liquid.parser.parseTokens(tokens));
3640
+ const templates = optimize(parser.parseTokens(tokens));
3429
3641
  return templates === 'none' ? undefined : templates;
3430
3642
  }
3431
3643
  function optimize(templates) {
@@ -3443,10 +3655,10 @@ function* renderFilePath(file, ctx, liquid) {
3443
3655
  }
3444
3656
 
3445
3657
  class IncludeTag extends Tag {
3446
- constructor(token, remainTokens, liquid) {
3658
+ constructor(token, remainTokens, liquid, parser) {
3447
3659
  super(token, remainTokens, liquid);
3448
3660
  const { tokenizer } = token;
3449
- this['file'] = parseFilePath(tokenizer, this.liquid);
3661
+ this['file'] = parseFilePath(tokenizer, this.liquid, parser);
3450
3662
  this['currentFile'] = token.file;
3451
3663
  const begin = tokenizer.p;
3452
3664
  const withStr = tokenizer.readIdentifier();
@@ -3460,7 +3672,7 @@ class IncludeTag extends Tag {
3460
3672
  }
3461
3673
  else
3462
3674
  tokenizer.p = begin;
3463
- this.hash = new Hash(tokenizer.remaining(), this.liquid.options.jekyllInclude);
3675
+ this.hash = new Hash(tokenizer.remaining(), liquid.options.jekyllInclude || liquid.options.keyValueSeparator);
3464
3676
  }
3465
3677
  *render(ctx, emitter) {
3466
3678
  const { liquid, hash, withVar } = this;
@@ -3533,37 +3745,29 @@ class CycleTag extends Tag {
3533
3745
  }
3534
3746
 
3535
3747
  class IfTag extends Tag {
3536
- constructor(tagToken, remainTokens, liquid) {
3748
+ constructor(tagToken, remainTokens, liquid, parser) {
3537
3749
  super(tagToken, remainTokens, liquid);
3538
3750
  this.branches = [];
3539
- this.elseTemplates = [];
3540
3751
  let p = [];
3541
- let elseCount = 0;
3542
- liquid.parser.parseStream(remainTokens)
3752
+ parser.parseStream(remainTokens)
3543
3753
  .on('start', () => this.branches.push({
3544
3754
  value: new Value(tagToken.args, this.liquid),
3545
3755
  templates: (p = [])
3546
3756
  }))
3547
3757
  .on('tag:elsif', (token) => {
3548
- if (elseCount > 0) {
3549
- p = [];
3550
- return;
3551
- }
3758
+ assert(!this.elseTemplates, 'unexpected elsif after else');
3552
3759
  this.branches.push({
3553
3760
  value: new Value(token.args, this.liquid),
3554
3761
  templates: (p = [])
3555
3762
  });
3556
3763
  })
3557
- .on('tag:else', () => {
3558
- elseCount++;
3559
- p = this.elseTemplates;
3560
- })
3561
- .on('tag:endif', function () { this.stop(); })
3562
- .on('template', (tpl) => {
3563
- if (p !== this.elseTemplates || elseCount === 1) {
3564
- p.push(tpl);
3565
- }
3764
+ .on('tag:else', tag => {
3765
+ assertEmpty(tag.args);
3766
+ assert(!this.elseTemplates, 'duplicated else');
3767
+ p = this.elseTemplates = [];
3566
3768
  })
3769
+ .on('tag:endif', function (tag) { assertEmpty(tag.args); this.stop(); })
3770
+ .on('template', (tpl) => p.push(tpl))
3567
3771
  .on('end', () => { throw new Error(`tag ${tagToken.getText()} not closed`); })
3568
3772
  .start();
3569
3773
  }
@@ -3576,7 +3780,7 @@ class IfTag extends Tag {
3576
3780
  return;
3577
3781
  }
3578
3782
  }
3579
- yield r.renderTemplates(this.elseTemplates, ctx, emitter);
3783
+ yield r.renderTemplates(this.elseTemplates || [], ctx, emitter);
3580
3784
  }
3581
3785
  }
3582
3786
 
@@ -3597,12 +3801,12 @@ class IncrementTag extends Tag {
3597
3801
  }
3598
3802
 
3599
3803
  class LayoutTag extends Tag {
3600
- constructor(token, remainTokens, liquid) {
3804
+ constructor(token, remainTokens, liquid, parser) {
3601
3805
  super(token, remainTokens, liquid);
3602
- this.file = parseFilePath(this.tokenizer, this.liquid);
3806
+ this.file = parseFilePath(this.tokenizer, this.liquid, parser);
3603
3807
  this['currentFile'] = token.file;
3604
- this.args = new Hash(this.tokenizer.remaining());
3605
- this.templates = this.liquid.parser.parseTokens(remainTokens);
3808
+ this.args = new Hash(this.tokenizer.remaining(), liquid.options.keyValueSeparator);
3809
+ this.templates = parser.parseTokens(remainTokens);
3606
3810
  }
3607
3811
  *render(ctx, emitter) {
3608
3812
  const { liquid, args, file } = this;
@@ -3631,7 +3835,7 @@ class LayoutTag extends Tag {
3631
3835
  }
3632
3836
 
3633
3837
  class BlockTag extends Tag {
3634
- constructor(token, remainTokens, liquid) {
3838
+ constructor(token, remainTokens, liquid, parser) {
3635
3839
  super(token, remainTokens, liquid);
3636
3840
  this.templates = [];
3637
3841
  const match = /\w+/.exec(token.args);
@@ -3640,7 +3844,7 @@ class BlockTag extends Tag {
3640
3844
  const token = remainTokens.shift();
3641
3845
  if (isTagToken(token) && token.name === 'endblock')
3642
3846
  return;
3643
- const template = liquid.parser.parseToken(token, remainTokens);
3847
+ const template = parser.parseToken(token, remainTokens);
3644
3848
  this.templates.push(template);
3645
3849
  }
3646
3850
  throw new Error(`tag ${token.getText()} not closed`);
@@ -3710,7 +3914,7 @@ class TablerowloopDrop extends ForloopDrop {
3710
3914
  }
3711
3915
 
3712
3916
  class TablerowTag extends Tag {
3713
- constructor(tagToken, remainTokens, liquid) {
3917
+ constructor(tagToken, remainTokens, liquid, parser) {
3714
3918
  super(tagToken, remainTokens, liquid);
3715
3919
  const variable = this.tokenizer.readIdentifier();
3716
3920
  this.tokenizer.skipBlank();
@@ -3721,10 +3925,10 @@ class TablerowTag extends Tag {
3721
3925
  }
3722
3926
  this.variable = variable.content;
3723
3927
  this.collection = collectionToken;
3724
- this.args = new Hash(this.tokenizer.remaining());
3928
+ this.args = new Hash(this.tokenizer.remaining(), liquid.options.keyValueSeparator);
3725
3929
  this.templates = [];
3726
3930
  let p;
3727
- const stream = this.liquid.parser.parseStream(remainTokens)
3931
+ const stream = parser.parseStream(remainTokens)
3728
3932
  .on('start', () => (p = this.templates))
3729
3933
  .on('tag:endtablerow', () => stream.stop())
3730
3934
  .on('template', (tpl) => p.push(tpl))
@@ -3762,13 +3966,13 @@ class TablerowTag extends Tag {
3762
3966
  }
3763
3967
 
3764
3968
  class UnlessTag extends Tag {
3765
- constructor(tagToken, remainTokens, liquid) {
3969
+ constructor(tagToken, remainTokens, liquid, parser) {
3766
3970
  super(tagToken, remainTokens, liquid);
3767
3971
  this.branches = [];
3768
3972
  this.elseTemplates = [];
3769
3973
  let p = [];
3770
3974
  let elseCount = 0;
3771
- this.liquid.parser.parseStream(remainTokens)
3975
+ parser.parseStream(remainTokens)
3772
3976
  .on('start', () => this.branches.push({
3773
3977
  value: new Value(tagToken.args, this.liquid),
3774
3978
  test: isFalsy,
@@ -3840,10 +4044,10 @@ class EchoTag extends Tag {
3840
4044
  }
3841
4045
 
3842
4046
  class LiquidTag extends Tag {
3843
- constructor(token, remainTokens, liquid) {
4047
+ constructor(token, remainTokens, liquid, parser) {
3844
4048
  super(token, remainTokens, liquid);
3845
4049
  const tokens = this.tokenizer.readLiquidTagTokens(this.liquid.options);
3846
- this.templates = this.liquid.parser.parseTokens(tokens);
4050
+ this.templates = parser.parseTokens(tokens);
3847
4051
  }
3848
4052
  *render(ctx, emitter) {
3849
4053
  yield this.liquid.renderer.renderTemplates(this.templates, ctx, emitter);
@@ -3890,12 +4094,14 @@ class Liquid {
3890
4094
  this.filters = {};
3891
4095
  this.tags = {};
3892
4096
  this.options = normalize(opts);
4097
+ // eslint-disable-next-line deprecation/deprecation
3893
4098
  this.parser = new Parser(this);
3894
4099
  forOwn(tags, (conf, name) => this.registerTag(name, conf));
3895
4100
  forOwn(filters, (handler, name) => this.registerFilter(name, handler));
3896
4101
  }
3897
4102
  parse(html, filepath) {
3898
- return this.parser.parse(html, filepath);
4103
+ const parser = new Parser(this);
4104
+ return parser.parse(html, filepath);
3899
4105
  }
3900
4106
  _render(tpl, scope, renderOptions) {
3901
4107
  const ctx = scope instanceof Context ? scope : new Context(scope, this.options, renderOptions);
@@ -3922,19 +4128,19 @@ class Liquid {
3922
4128
  return toValueSync(this._parseAndRender(html, scope, { ...renderOptions, sync: true }));
3923
4129
  }
3924
4130
  _parsePartialFile(file, sync, currentFile) {
3925
- return this.parser.parseFile(file, sync, LookupType.Partials, currentFile);
4131
+ return new Parser(this).parseFile(file, sync, LookupType.Partials, currentFile);
3926
4132
  }
3927
4133
  _parseLayoutFile(file, sync, currentFile) {
3928
- return this.parser.parseFile(file, sync, LookupType.Layouts, currentFile);
4134
+ return new Parser(this).parseFile(file, sync, LookupType.Layouts, currentFile);
3929
4135
  }
3930
4136
  _parseFile(file, sync, lookupType, currentFile) {
3931
- return this.parser.parseFile(file, sync, lookupType, currentFile);
4137
+ return new Parser(this).parseFile(file, sync, lookupType, currentFile);
3932
4138
  }
3933
4139
  async parseFile(file, lookupType) {
3934
- return toPromise(this.parser.parseFile(file, false, lookupType));
4140
+ return toPromise(new Parser(this).parseFile(file, false, lookupType));
3935
4141
  }
3936
4142
  parseFileSync(file, lookupType) {
3937
- return toValueSync(this.parser.parseFile(file, true, lookupType));
4143
+ return toValueSync(new Parser(this).parseFile(file, true, lookupType));
3938
4144
  }
3939
4145
  *_renderFile(file, ctx, renderFileOptions) {
3940
4146
  const templates = (yield this._parseFile(file, renderFileOptions.sync, renderFileOptions.lookupType));
@@ -3987,7 +4193,7 @@ class Liquid {
3987
4193
  }
3988
4194
 
3989
4195
  /* istanbul ignore file */
3990
- const version = '10.13.0';
4196
+ const version = '10.18.0';
3991
4197
 
3992
4198
  exports.AssertionError = AssertionError;
3993
4199
  exports.AssignTag = AssignTag;
@@ -4023,7 +4229,6 @@ exports.RenderTag = RenderTag;
4023
4229
  exports.TablerowTag = TablerowTag;
4024
4230
  exports.Tag = Tag;
4025
4231
  exports.TagToken = TagToken;
4026
- exports.TimezoneDate = TimezoneDate;
4027
4232
  exports.Token = Token;
4028
4233
  exports.TokenizationError = TokenizationError;
4029
4234
  exports.Tokenizer = Tokenizer;