@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.
- package/.jsii +113 -3
- package/API.md +74 -43
- package/lib/skep-organization-project.js +1 -1
- package/node_modules/liquidjs/README.md +17 -10
- package/node_modules/liquidjs/dist/context/context.d.ts +7 -1
- package/node_modules/liquidjs/dist/drop/drop.d.ts +1 -1
- package/node_modules/liquidjs/dist/filters/array.d.ts +14 -14
- package/node_modules/liquidjs/dist/filters/html.d.ts +6 -5
- package/node_modules/liquidjs/dist/filters/math.d.ts +9 -9
- package/node_modules/liquidjs/dist/filters/string.d.ts +22 -21
- package/node_modules/liquidjs/dist/fs/map-fs.d.ts +13 -0
- package/node_modules/liquidjs/dist/fs/node-require.d.ts +1 -2
- package/node_modules/liquidjs/dist/index.d.ts +1 -1
- package/node_modules/liquidjs/dist/liquid-options.d.ts +28 -2
- package/node_modules/liquidjs/dist/liquid.browser.min.js +1 -1
- package/node_modules/liquidjs/dist/liquid.browser.min.js.map +1 -1
- package/node_modules/liquidjs/dist/{liquid.browser.esm.js → liquid.browser.mjs} +443 -227
- package/node_modules/liquidjs/dist/liquid.browser.umd.js +604 -368
- package/node_modules/liquidjs/dist/liquid.browser.umd.js.map +1 -1
- package/node_modules/liquidjs/dist/liquid.d.ts +3 -0
- package/node_modules/liquidjs/dist/{liquid.node.cjs.js → liquid.node.js} +432 -227
- package/node_modules/liquidjs/dist/{liquid.node.esm.js → liquid.node.mjs} +448 -228
- package/node_modules/liquidjs/dist/parser/parser.d.ts +1 -0
- package/node_modules/liquidjs/dist/parser/parser.spec.d.ts +1 -0
- package/node_modules/liquidjs/dist/parser/tokenizer.d.ts +4 -3
- package/node_modules/liquidjs/dist/tags/block.d.ts +2 -1
- package/node_modules/liquidjs/dist/tags/capture.d.ts +2 -1
- package/node_modules/liquidjs/dist/tags/case.d.ts +2 -1
- package/node_modules/liquidjs/dist/tags/for.d.ts +2 -1
- package/node_modules/liquidjs/dist/tags/if.d.ts +3 -2
- package/node_modules/liquidjs/dist/tags/include.d.ts +2 -1
- package/node_modules/liquidjs/dist/tags/layout.d.ts +2 -1
- package/node_modules/liquidjs/dist/tags/liquid.d.ts +2 -1
- package/node_modules/liquidjs/dist/tags/render.d.ts +3 -2
- package/node_modules/liquidjs/dist/tags/tablerow.d.ts +2 -1
- package/node_modules/liquidjs/dist/tags/unless.d.ts +2 -1
- package/node_modules/liquidjs/dist/template/filter-impl-options.d.ts +2 -0
- package/node_modules/liquidjs/dist/template/filter.d.ts +3 -1
- package/node_modules/liquidjs/dist/template/hash.d.ts +1 -1
- package/node_modules/liquidjs/dist/template/tag.d.ts +2 -2
- package/node_modules/liquidjs/dist/tokens/identifier-token.d.ts +0 -1
- package/node_modules/liquidjs/dist/util/assert.d.ts +1 -0
- package/node_modules/liquidjs/dist/util/error.d.ts +7 -2
- package/node_modules/liquidjs/dist/util/error.spec.d.ts +1 -0
- package/node_modules/liquidjs/dist/util/index.d.ts +2 -1
- package/node_modules/liquidjs/dist/util/intl.d.ts +6 -0
- package/node_modules/liquidjs/dist/util/limiter.d.ts +8 -0
- package/node_modules/liquidjs/dist/util/liquid-date.d.ts +39 -7
- package/node_modules/liquidjs/dist/util/liquid-date.spec.d.ts +1 -0
- package/node_modules/liquidjs/dist/util/performance.d.ts +5 -0
- package/node_modules/liquidjs/dist/util/performance.spec.d.ts +1 -0
- package/node_modules/liquidjs/dist/util/type-guards.spec.d.ts +1 -0
- package/node_modules/liquidjs/dist/util/underscore.d.ts +2 -1
- package/node_modules/liquidjs/package.json +10 -7
- package/package.json +16 -12
- package/node_modules/liquidjs/dist/util/timezone-date.d.ts +0 -44
- /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.
|
|
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)
|
|
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
|
|
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 &&
|
|
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
|
-
|
|
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) =>
|
|
632
|
-
A: (d) =>
|
|
633
|
-
b: (d) =>
|
|
634
|
-
B: (d) =>
|
|
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 =
|
|
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
|
|
723
|
-
constructor(init, timezone) {
|
|
724
|
-
this.
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
this.
|
|
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
|
-
|
|
776
|
-
|
|
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
|
|
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
|
|
836
|
+
return new LiquidDate(+new Date(dateString), locale, offset);
|
|
802
837
|
}
|
|
803
|
-
return new
|
|
838
|
+
return new LiquidDate(dateString, locale);
|
|
804
839
|
}
|
|
805
|
-
static getTimezoneOffset(timezoneName, 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 =
|
|
1158
|
-
|
|
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
|
''': "'"
|
|
1496
1556
|
};
|
|
1497
1557
|
function escape(str) {
|
|
1498
|
-
|
|
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
|
-
|
|
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(
|
|
1571
|
+
return escape.call(this, unescape.call(this, str));
|
|
1508
1572
|
}
|
|
1509
1573
|
function newline_to_br(v) {
|
|
1510
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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(
|
|
2178
|
-
this.
|
|
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(
|
|
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
|
-
|
|
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 = (
|
|
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
|
-
|
|
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.
|
|
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 && !
|
|
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 (
|
|
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)
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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,
|
|
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
|
-
|
|
2795
|
-
|
|
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,
|
|
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
|
-
|
|
2817
|
-
|
|
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
|
-
|
|
2827
|
-
|
|
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
|
-
|
|
2836
|
-
|
|
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 =
|
|
2844
|
-
|
|
2845
|
-
return (arr
|
|
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
|
|
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
|
|
3122
|
+
date = new LiquidDate(+v * 1000, locale, defaultTimezoneOffset);
|
|
2936
3123
|
}
|
|
2937
|
-
else if (opts.preserveTimezones) {
|
|
2938
|
-
date =
|
|
3124
|
+
else if (opts.preserveTimezones && timezoneOffset === undefined) {
|
|
3125
|
+
date = LiquidDate.createDateFixedToTimezone(v, locale);
|
|
2939
3126
|
}
|
|
2940
3127
|
else {
|
|
2941
|
-
date = new
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3173
|
+
return str.replace(new RegExp(`^[${chars}]+`, 'g'), '');
|
|
2990
3174
|
}
|
|
2991
|
-
return
|
|
3175
|
+
return str.replace(/^\s+/, '');
|
|
2992
3176
|
}
|
|
2993
3177
|
function downcase(v) {
|
|
2994
|
-
|
|
3178
|
+
const str = stringify(v);
|
|
3179
|
+
this.context.memoryLimit.use(str.length);
|
|
3180
|
+
return str.toLowerCase();
|
|
2995
3181
|
}
|
|
2996
|
-
function upcase(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3211
|
+
return str.replace(new RegExp(`[${chars}]+$`, 'g'), '');
|
|
3017
3212
|
}
|
|
3018
|
-
return
|
|
3213
|
+
return str.replace(/\s+$/, '');
|
|
3019
3214
|
}
|
|
3020
3215
|
function split(v, arg) {
|
|
3021
|
-
const
|
|
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
|
|
3230
|
+
return str
|
|
3032
3231
|
.replace(new RegExp(`^[${chars}]+`, 'g'), '')
|
|
3033
3232
|
.replace(new RegExp(`[${chars}]+$`, 'g'), '');
|
|
3034
3233
|
}
|
|
3035
|
-
return
|
|
3234
|
+
return str.trim();
|
|
3036
3235
|
}
|
|
3037
3236
|
function strip_newlines(v) {
|
|
3038
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3061
|
-
|
|
3267
|
+
const str = stringify(v);
|
|
3268
|
+
this.context.memoryLimit.use(str.length);
|
|
3269
|
+
if (str.length <= l)
|
|
3062
3270
|
return v;
|
|
3063
|
-
return
|
|
3271
|
+
return str.substring(0, l - o.length) + o;
|
|
3064
3272
|
}
|
|
3065
3273
|
function truncatewords(v, words = 15, o = '...') {
|
|
3066
|
-
const
|
|
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
|
-
|
|
3076
|
-
|
|
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
|
-
|
|
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 =
|
|
3389
|
+
const stream = parser.parseStream(remainTokens)
|
|
3176
3390
|
.on('start', () => (p = this.templates))
|
|
3177
|
-
.on('tag:else',
|
|
3178
|
-
.on('tag:endfor', ()
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(),
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3559
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
4131
|
+
return new Parser(this).parseFile(file, sync, LookupType.Partials, currentFile);
|
|
3926
4132
|
}
|
|
3927
4133
|
_parseLayoutFile(file, sync, currentFile) {
|
|
3928
|
-
return this.
|
|
4134
|
+
return new Parser(this).parseFile(file, sync, LookupType.Layouts, currentFile);
|
|
3929
4135
|
}
|
|
3930
4136
|
_parseFile(file, sync, lookupType, currentFile) {
|
|
3931
|
-
return this.
|
|
4137
|
+
return new Parser(this).parseFile(file, sync, lookupType, currentFile);
|
|
3932
4138
|
}
|
|
3933
4139
|
async parseFile(file, lookupType) {
|
|
3934
|
-
return toPromise(this.
|
|
4140
|
+
return toPromise(new Parser(this).parseFile(file, false, lookupType));
|
|
3935
4141
|
}
|
|
3936
4142
|
parseFileSync(file, lookupType) {
|
|
3937
|
-
return toValueSync(this.
|
|
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.
|
|
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;
|