@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
|
*/
|
|
@@ -89,6 +89,10 @@ function toArray(val) {
|
|
|
89
89
|
function toValue(value) {
|
|
90
90
|
return (value instanceof Drop && isFunction(value.valueOf)) ? value.valueOf() : value;
|
|
91
91
|
}
|
|
92
|
+
function toNumber(value) {
|
|
93
|
+
value = Number(value);
|
|
94
|
+
return isNaN(value) ? 0 : value;
|
|
95
|
+
}
|
|
92
96
|
function isNumber(value) {
|
|
93
97
|
return typeof value === 'number';
|
|
94
98
|
}
|
|
@@ -188,14 +192,14 @@ function caseInsensitiveCompare(a, b) {
|
|
|
188
192
|
return 0;
|
|
189
193
|
}
|
|
190
194
|
function argumentsToValue(fn) {
|
|
191
|
-
return (...args)
|
|
195
|
+
return function (...args) { return fn.call(this, ...args.map(toValue)); };
|
|
192
196
|
}
|
|
193
197
|
function escapeRegExp(text) {
|
|
194
198
|
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
|
195
199
|
}
|
|
196
200
|
|
|
197
201
|
/**
|
|
198
|
-
* targeting ES5, extends Error won't create a proper prototype chain, need a trait to
|
|
202
|
+
* targeting ES5, extends Error won't create a proper prototype chain, need a trait to keep track of classes
|
|
199
203
|
*/
|
|
200
204
|
const TRAIT = '__liquidClass__';
|
|
201
205
|
class LiquidError extends Error {
|
|
@@ -249,6 +253,19 @@ class RenderError extends LiquidError {
|
|
|
249
253
|
return obj.name === 'RenderError';
|
|
250
254
|
}
|
|
251
255
|
}
|
|
256
|
+
class LiquidErrors extends LiquidError {
|
|
257
|
+
constructor(errors) {
|
|
258
|
+
super(errors[0], errors[0].token);
|
|
259
|
+
this.errors = errors;
|
|
260
|
+
this.name = 'LiquidErrors';
|
|
261
|
+
const s = errors.length > 1 ? 's' : '';
|
|
262
|
+
this.message = `${errors.length} error${s} found`;
|
|
263
|
+
super.update();
|
|
264
|
+
}
|
|
265
|
+
static is(obj) {
|
|
266
|
+
return obj.name === 'LiquidErrors';
|
|
267
|
+
}
|
|
268
|
+
}
|
|
252
269
|
class UndefinedVariableError extends LiquidError {
|
|
253
270
|
constructor(err, token) {
|
|
254
271
|
super(err, token);
|
|
@@ -327,6 +344,9 @@ function assert(predicate, message) {
|
|
|
327
344
|
: (message || `expect ${predicate} to be true`);
|
|
328
345
|
throw new AssertionError(msg);
|
|
329
346
|
}
|
|
347
|
+
}
|
|
348
|
+
function assertEmpty(predicate, message = `unexpected ${JSON.stringify(predicate)}`) {
|
|
349
|
+
assert(!predicate, message);
|
|
330
350
|
}
|
|
331
351
|
|
|
332
352
|
class NullDrop extends Drop {
|
|
@@ -440,7 +460,12 @@ class BlockDrop extends Drop {
|
|
|
440
460
|
}
|
|
441
461
|
|
|
442
462
|
function isComparable(arg) {
|
|
443
|
-
return arg &&
|
|
463
|
+
return (arg &&
|
|
464
|
+
isFunction(arg.equals) &&
|
|
465
|
+
isFunction(arg.gt) &&
|
|
466
|
+
isFunction(arg.geq) &&
|
|
467
|
+
isFunction(arg.lt) &&
|
|
468
|
+
isFunction(arg.leq));
|
|
444
469
|
}
|
|
445
470
|
|
|
446
471
|
const nil = new NullDrop();
|
|
@@ -560,18 +585,6 @@ function toValueSync(val) {
|
|
|
560
585
|
}
|
|
561
586
|
|
|
562
587
|
const rFormat = /%([-_0^#:]+)?(\d+)?([EO])?(.)/;
|
|
563
|
-
const monthNames = [
|
|
564
|
-
'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
|
|
565
|
-
'September', 'October', 'November', 'December'
|
|
566
|
-
];
|
|
567
|
-
const dayNames = [
|
|
568
|
-
'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'
|
|
569
|
-
];
|
|
570
|
-
const monthNamesShort = monthNames.map(abbr);
|
|
571
|
-
const dayNamesShort = dayNames.map(abbr);
|
|
572
|
-
function abbr(str) {
|
|
573
|
-
return str.slice(0, 3);
|
|
574
|
-
}
|
|
575
588
|
// prototype extensions
|
|
576
589
|
function daysInMonth(d) {
|
|
577
590
|
const feb = isLeapYear(d) ? 29 : 28;
|
|
@@ -626,19 +639,7 @@ const padWidths = {
|
|
|
626
639
|
U: 2,
|
|
627
640
|
W: 2
|
|
628
641
|
};
|
|
629
|
-
|
|
630
|
-
const padChars = {
|
|
631
|
-
a: ' ',
|
|
632
|
-
A: ' ',
|
|
633
|
-
b: ' ',
|
|
634
|
-
B: ' ',
|
|
635
|
-
c: ' ',
|
|
636
|
-
e: ' ',
|
|
637
|
-
k: ' ',
|
|
638
|
-
l: ' ',
|
|
639
|
-
p: ' ',
|
|
640
|
-
P: ' '
|
|
641
|
-
};
|
|
642
|
+
const padSpaceChars = new Set('aAbBceklpP');
|
|
642
643
|
function getTimezoneOffset(d, opts) {
|
|
643
644
|
const nOffset = Math.abs(d.getTimezoneOffset());
|
|
644
645
|
const h = Math.floor(nOffset / 60);
|
|
@@ -649,10 +650,10 @@ function getTimezoneOffset(d, opts) {
|
|
|
649
650
|
padStart(m, 2, '0');
|
|
650
651
|
}
|
|
651
652
|
const formatCodes = {
|
|
652
|
-
a: (d) =>
|
|
653
|
-
A: (d) =>
|
|
654
|
-
b: (d) =>
|
|
655
|
-
B: (d) =>
|
|
653
|
+
a: (d) => d.getShortWeekdayName(),
|
|
654
|
+
A: (d) => d.getLongWeekdayName(),
|
|
655
|
+
b: (d) => d.getShortMonthName(),
|
|
656
|
+
B: (d) => d.getLongMonthName(),
|
|
656
657
|
c: (d) => d.toLocaleString(),
|
|
657
658
|
C: (d) => century(d),
|
|
658
659
|
d: (d) => d.getDate(),
|
|
@@ -684,12 +685,7 @@ const formatCodes = {
|
|
|
684
685
|
y: (d) => d.getFullYear().toString().slice(2, 4),
|
|
685
686
|
Y: (d) => d.getFullYear(),
|
|
686
687
|
z: getTimezoneOffset,
|
|
687
|
-
Z: (d, opts) =>
|
|
688
|
-
if (d.getTimezoneName) {
|
|
689
|
-
return d.getTimezoneName() || getTimezoneOffset(d, opts);
|
|
690
|
-
}
|
|
691
|
-
return (typeof Intl !== 'undefined' ? Intl.DateTimeFormat().resolvedOptions().timeZone : '');
|
|
692
|
-
},
|
|
688
|
+
Z: (d, opts) => d.getTimeZoneName() || getTimezoneOffset(d, opts),
|
|
693
689
|
't': () => '\t',
|
|
694
690
|
'n': () => '\n',
|
|
695
691
|
'%': () => '%'
|
|
@@ -715,7 +711,7 @@ function format(d, match) {
|
|
|
715
711
|
for (const flag of flagStr)
|
|
716
712
|
flags[flag] = true;
|
|
717
713
|
let ret = String(convert(d, { flags, width, modifier }));
|
|
718
|
-
let padChar =
|
|
714
|
+
let padChar = padSpaceChars.has(conversion) ? ' ' : '0';
|
|
719
715
|
let padWidth = width || padWidths[conversion] || 0;
|
|
720
716
|
if (flags['^'])
|
|
721
717
|
ret = ret.toUpperCase();
|
|
@@ -730,9 +726,22 @@ function format(d, match) {
|
|
|
730
726
|
return padStart(ret, padWidth, padChar);
|
|
731
727
|
}
|
|
732
728
|
|
|
729
|
+
function getDateTimeFormat() {
|
|
730
|
+
return (typeof Intl !== 'undefined' ? Intl.DateTimeFormat : undefined);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
733
|
// one minute in milliseconds
|
|
734
734
|
const OneMinute = 60000;
|
|
735
735
|
const ISO8601_TIMEZONE_PATTERN = /([zZ]|([+-])(\d{2}):(\d{2}))$/;
|
|
736
|
+
const monthNames = [
|
|
737
|
+
'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
|
|
738
|
+
'September', 'October', 'November', 'December'
|
|
739
|
+
];
|
|
740
|
+
const monthNamesShort = monthNames.map(name => name.slice(0, 3));
|
|
741
|
+
const dayNames = [
|
|
742
|
+
'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'
|
|
743
|
+
];
|
|
744
|
+
const dayNamesShort = dayNames.map(name => name.slice(0, 3));
|
|
736
745
|
/**
|
|
737
746
|
* A date implementation with timezone info, just like Ruby date
|
|
738
747
|
*
|
|
@@ -740,12 +749,16 @@ const ISO8601_TIMEZONE_PATTERN = /([zZ]|([+-])(\d{2}):(\d{2}))$/;
|
|
|
740
749
|
* - create a Date offset by it's timezone difference, avoiding overriding a bunch of methods
|
|
741
750
|
* - rewrite getTimezoneOffset() to trick strftime
|
|
742
751
|
*/
|
|
743
|
-
class
|
|
744
|
-
constructor(init, timezone) {
|
|
745
|
-
this.
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
this.
|
|
752
|
+
class LiquidDate {
|
|
753
|
+
constructor(init, locale, timezone) {
|
|
754
|
+
this.locale = locale;
|
|
755
|
+
this.DateTimeFormat = getDateTimeFormat();
|
|
756
|
+
this.date = new Date(init);
|
|
757
|
+
this.timezoneFixed = timezone !== undefined;
|
|
758
|
+
if (timezone === undefined) {
|
|
759
|
+
timezone = this.date.getTimezoneOffset();
|
|
760
|
+
}
|
|
761
|
+
this.timezoneOffset = isString(timezone) ? LiquidDate.getTimezoneOffset(timezone, this.date) : timezone;
|
|
749
762
|
this.timezoneName = isString(timezone) ? timezone : '';
|
|
750
763
|
const diff = (this.date.getTimezoneOffset() - this.timezoneOffset) * OneMinute;
|
|
751
764
|
const time = this.date.getTime() + diff;
|
|
@@ -793,8 +806,34 @@ class TimezoneDate {
|
|
|
793
806
|
getTimezoneOffset() {
|
|
794
807
|
return this.timezoneOffset;
|
|
795
808
|
}
|
|
796
|
-
|
|
797
|
-
|
|
809
|
+
getTimeZoneName() {
|
|
810
|
+
if (this.timezoneFixed)
|
|
811
|
+
return this.timezoneName;
|
|
812
|
+
if (!this.DateTimeFormat)
|
|
813
|
+
return;
|
|
814
|
+
return this.DateTimeFormat().resolvedOptions().timeZone;
|
|
815
|
+
}
|
|
816
|
+
getLongMonthName() {
|
|
817
|
+
var _a;
|
|
818
|
+
return (_a = this.format({ month: 'long' })) !== null && _a !== void 0 ? _a : monthNames[this.getMonth()];
|
|
819
|
+
}
|
|
820
|
+
getShortMonthName() {
|
|
821
|
+
var _a;
|
|
822
|
+
return (_a = this.format({ month: 'short' })) !== null && _a !== void 0 ? _a : monthNamesShort[this.getMonth()];
|
|
823
|
+
}
|
|
824
|
+
getLongWeekdayName() {
|
|
825
|
+
var _a;
|
|
826
|
+
return (_a = this.format({ weekday: 'long' })) !== null && _a !== void 0 ? _a : dayNames[this.displayDate.getDay()];
|
|
827
|
+
}
|
|
828
|
+
getShortWeekdayName() {
|
|
829
|
+
var _a;
|
|
830
|
+
return (_a = this.format({ weekday: 'short' })) !== null && _a !== void 0 ? _a : dayNamesShort[this.displayDate.getDay()];
|
|
831
|
+
}
|
|
832
|
+
valid() {
|
|
833
|
+
return !isNaN(this.getTime());
|
|
834
|
+
}
|
|
835
|
+
format(options) {
|
|
836
|
+
return this.DateTimeFormat && this.DateTimeFormat(this.locale, options).format(this.displayDate);
|
|
798
837
|
}
|
|
799
838
|
/**
|
|
800
839
|
* Create a Date object fixed to it's declared Timezone. Both
|
|
@@ -809,21 +848,21 @@ class TimezoneDate {
|
|
|
809
848
|
* we create a different Date to trick strftime, it's both simpler and more performant.
|
|
810
849
|
* Given that a template is expected to be parsed fewer times than rendered.
|
|
811
850
|
*/
|
|
812
|
-
static createDateFixedToTimezone(dateString) {
|
|
851
|
+
static createDateFixedToTimezone(dateString, locale) {
|
|
813
852
|
const m = dateString.match(ISO8601_TIMEZONE_PATTERN);
|
|
814
853
|
// representing a UTC timestamp
|
|
815
854
|
if (m && m[1] === 'Z') {
|
|
816
|
-
return new
|
|
855
|
+
return new LiquidDate(+new Date(dateString), locale, 0);
|
|
817
856
|
}
|
|
818
857
|
// has a timezone specified
|
|
819
858
|
if (m && m[2] && m[3] && m[4]) {
|
|
820
859
|
const [, , sign, hours, minutes] = m;
|
|
821
860
|
const offset = (sign === '+' ? -1 : 1) * (parseInt(hours, 10) * 60 + parseInt(minutes, 10));
|
|
822
|
-
return new
|
|
861
|
+
return new LiquidDate(+new Date(dateString), locale, offset);
|
|
823
862
|
}
|
|
824
|
-
return new
|
|
863
|
+
return new LiquidDate(dateString, locale);
|
|
825
864
|
}
|
|
826
|
-
static getTimezoneOffset(timezoneName, date
|
|
865
|
+
static getTimezoneOffset(timezoneName, date) {
|
|
827
866
|
const localDateString = date.toLocaleString('en-US', { timeZone: timezoneName });
|
|
828
867
|
const utcDateString = date.toLocaleString('en-US', { timeZone: 'UTC' });
|
|
829
868
|
const localDate = new Date(localDateString);
|
|
@@ -832,6 +871,23 @@ class TimezoneDate {
|
|
|
832
871
|
}
|
|
833
872
|
}
|
|
834
873
|
|
|
874
|
+
class Limiter {
|
|
875
|
+
constructor(resource, limit) {
|
|
876
|
+
this.base = 0;
|
|
877
|
+
this.message = `${resource} limit exceeded`;
|
|
878
|
+
this.limit = limit;
|
|
879
|
+
}
|
|
880
|
+
use(count) {
|
|
881
|
+
count = toNumber(count);
|
|
882
|
+
assert(this.base + count <= this.limit, this.message);
|
|
883
|
+
this.base += count;
|
|
884
|
+
}
|
|
885
|
+
check(count) {
|
|
886
|
+
count = toNumber(count);
|
|
887
|
+
assert(count <= this.limit, this.message);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
|
|
835
891
|
class DelimitedToken extends Token {
|
|
836
892
|
constructor(kind, [contentBegin, contentEnd], input, begin, end, trimLeft, trimRight, file) {
|
|
837
893
|
super(kind, input, begin, end, file);
|
|
@@ -912,16 +968,6 @@ class IdentifierToken extends Token {
|
|
|
912
968
|
this.file = file;
|
|
913
969
|
this.content = this.getText();
|
|
914
970
|
}
|
|
915
|
-
isNumber(allowSign = false) {
|
|
916
|
-
const begin = allowSign && TYPES[this.input.charCodeAt(this.begin)] & SIGN
|
|
917
|
-
? this.begin + 1
|
|
918
|
-
: this.begin;
|
|
919
|
-
for (let i = begin; i < this.end; i++) {
|
|
920
|
-
if (!(TYPES[this.input.charCodeAt(i)] & NUMBER))
|
|
921
|
-
return false;
|
|
922
|
-
}
|
|
923
|
-
return true;
|
|
924
|
-
}
|
|
925
971
|
}
|
|
926
972
|
|
|
927
973
|
class LiteralToken extends Token {
|
|
@@ -1110,6 +1156,15 @@ class FilteredValueToken extends Token {
|
|
|
1110
1156
|
}
|
|
1111
1157
|
}
|
|
1112
1158
|
|
|
1159
|
+
const polyfill = {
|
|
1160
|
+
now: () => Date.now()
|
|
1161
|
+
};
|
|
1162
|
+
function getPerformance() {
|
|
1163
|
+
return (typeof global === 'object' && global.performance) ||
|
|
1164
|
+
(typeof window === 'object' && window.performance) ||
|
|
1165
|
+
polyfill;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1113
1168
|
class SimpleEmitter {
|
|
1114
1169
|
constructor() {
|
|
1115
1170
|
this.buffer = '';
|
|
@@ -1157,7 +1212,9 @@ class Render {
|
|
|
1157
1212
|
if (!emitter) {
|
|
1158
1213
|
emitter = ctx.opts.keepOutputType ? new KeepingTypeEmitter() : new SimpleEmitter();
|
|
1159
1214
|
}
|
|
1215
|
+
const errors = [];
|
|
1160
1216
|
for (const tpl of templates) {
|
|
1217
|
+
ctx.renderLimit.check(getPerformance().now());
|
|
1161
1218
|
try {
|
|
1162
1219
|
// if tpl.render supports emitter, it'll return empty `html`
|
|
1163
1220
|
const html = yield tpl.render(ctx, emitter);
|
|
@@ -1167,10 +1224,16 @@ class Render {
|
|
|
1167
1224
|
break;
|
|
1168
1225
|
}
|
|
1169
1226
|
catch (e) {
|
|
1170
|
-
const err =
|
|
1171
|
-
|
|
1227
|
+
const err = LiquidError.is(e) ? e : new RenderError(e, tpl);
|
|
1228
|
+
if (ctx.opts.catchAllErrors)
|
|
1229
|
+
errors.push(err);
|
|
1230
|
+
else
|
|
1231
|
+
throw err;
|
|
1172
1232
|
}
|
|
1173
1233
|
}
|
|
1234
|
+
if (errors.length) {
|
|
1235
|
+
throw new LiquidErrors(errors);
|
|
1236
|
+
}
|
|
1174
1237
|
return emitter.buffer;
|
|
1175
1238
|
}
|
|
1176
1239
|
}
|
|
@@ -1264,6 +1327,7 @@ function isTruthy(val, ctx) {
|
|
|
1264
1327
|
return !isFalsy(val, ctx);
|
|
1265
1328
|
}
|
|
1266
1329
|
function isFalsy(val, ctx) {
|
|
1330
|
+
val = toValue(val);
|
|
1267
1331
|
if (ctx.opts.jsTruthy) {
|
|
1268
1332
|
return !val;
|
|
1269
1333
|
}
|
|
@@ -1522,22 +1586,30 @@ const unescapeMap = {
|
|
|
1522
1586
|
''': "'"
|
|
1523
1587
|
};
|
|
1524
1588
|
function escape(str) {
|
|
1525
|
-
|
|
1589
|
+
str = stringify(str);
|
|
1590
|
+
this.context.memoryLimit.use(str.length);
|
|
1591
|
+
return str.replace(/&|<|>|"|'/g, m => escapeMap[m]);
|
|
1526
1592
|
}
|
|
1527
1593
|
function xml_escape(str) {
|
|
1528
|
-
return escape(str);
|
|
1594
|
+
return escape.call(this, str);
|
|
1529
1595
|
}
|
|
1530
1596
|
function unescape(str) {
|
|
1531
|
-
|
|
1597
|
+
str = stringify(str);
|
|
1598
|
+
this.context.memoryLimit.use(str.length);
|
|
1599
|
+
return str.replace(/&(amp|lt|gt|#34|#39);/g, m => unescapeMap[m]);
|
|
1532
1600
|
}
|
|
1533
1601
|
function escape_once(str) {
|
|
1534
|
-
return escape(unescape(
|
|
1602
|
+
return escape.call(this, unescape.call(this, str));
|
|
1535
1603
|
}
|
|
1536
1604
|
function newline_to_br(v) {
|
|
1537
|
-
|
|
1605
|
+
const str = stringify(v);
|
|
1606
|
+
this.context.memoryLimit.use(str.length);
|
|
1607
|
+
return str.replace(/\r?\n/gm, '<br />\n');
|
|
1538
1608
|
}
|
|
1539
1609
|
function strip_html(v) {
|
|
1540
|
-
|
|
1610
|
+
const str = stringify(v);
|
|
1611
|
+
this.context.memoryLimit.use(str.length);
|
|
1612
|
+
return str.replace(/<script[\s\S]*?<\/script>|<style[\s\S]*?<\/style>|<.*?>|<!--[\s\S]*?-->/g, '');
|
|
1541
1613
|
}
|
|
1542
1614
|
|
|
1543
1615
|
var htmlFilters = /*#__PURE__*/Object.freeze({
|
|
@@ -1549,18 +1621,68 @@ var htmlFilters = /*#__PURE__*/Object.freeze({
|
|
|
1549
1621
|
strip_html: strip_html
|
|
1550
1622
|
});
|
|
1551
1623
|
|
|
1624
|
+
class MapFS {
|
|
1625
|
+
constructor(mapping) {
|
|
1626
|
+
this.mapping = mapping;
|
|
1627
|
+
this.sep = '/';
|
|
1628
|
+
}
|
|
1629
|
+
exists(filepath) {
|
|
1630
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1631
|
+
return this.existsSync(filepath);
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1634
|
+
existsSync(filepath) {
|
|
1635
|
+
return !isNil(this.mapping[filepath]);
|
|
1636
|
+
}
|
|
1637
|
+
readFile(filepath) {
|
|
1638
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1639
|
+
return this.readFileSync(filepath);
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
readFileSync(filepath) {
|
|
1643
|
+
const content = this.mapping[filepath];
|
|
1644
|
+
if (isNil(content))
|
|
1645
|
+
throw new Error(`ENOENT: ${filepath}`);
|
|
1646
|
+
return content;
|
|
1647
|
+
}
|
|
1648
|
+
dirname(filepath) {
|
|
1649
|
+
const segments = filepath.split(this.sep);
|
|
1650
|
+
segments.pop();
|
|
1651
|
+
return segments.join(this.sep);
|
|
1652
|
+
}
|
|
1653
|
+
resolve(dir, file, ext) {
|
|
1654
|
+
file += ext;
|
|
1655
|
+
if (dir === '.')
|
|
1656
|
+
return file;
|
|
1657
|
+
const segments = dir.split(/\/+/);
|
|
1658
|
+
for (const segment of file.split(this.sep)) {
|
|
1659
|
+
if (segment === '.' || segment === '')
|
|
1660
|
+
continue;
|
|
1661
|
+
else if (segment === '..') {
|
|
1662
|
+
if (segments.length > 1 || segments[0] !== '')
|
|
1663
|
+
segments.pop();
|
|
1664
|
+
}
|
|
1665
|
+
else
|
|
1666
|
+
segments.push(segment);
|
|
1667
|
+
}
|
|
1668
|
+
return segments.join(this.sep);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1552
1672
|
const defaultOptions = {
|
|
1553
1673
|
root: ['.'],
|
|
1554
1674
|
layouts: ['.'],
|
|
1555
1675
|
partials: ['.'],
|
|
1556
1676
|
relativeReference: true,
|
|
1557
1677
|
jekyllInclude: false,
|
|
1678
|
+
keyValueSeparator: ':',
|
|
1558
1679
|
cache: undefined,
|
|
1559
1680
|
extname: '',
|
|
1560
1681
|
fs: fs,
|
|
1561
1682
|
dynamicPartials: true,
|
|
1562
1683
|
jsTruthy: false,
|
|
1563
1684
|
dateFormat: '%A, %B %-e, %Y at %-l:%M %P %z',
|
|
1685
|
+
locale: '',
|
|
1564
1686
|
trimTagRight: false,
|
|
1565
1687
|
trimTagLeft: false,
|
|
1566
1688
|
trimOutputRight: false,
|
|
@@ -1577,9 +1699,13 @@ const defaultOptions = {
|
|
|
1577
1699
|
lenientIf: false,
|
|
1578
1700
|
globals: {},
|
|
1579
1701
|
keepOutputType: false,
|
|
1580
|
-
operators: defaultOperators
|
|
1702
|
+
operators: defaultOperators,
|
|
1703
|
+
memoryLimit: Infinity,
|
|
1704
|
+
parseLimit: Infinity,
|
|
1705
|
+
renderLimit: Infinity
|
|
1581
1706
|
};
|
|
1582
1707
|
function normalize(options) {
|
|
1708
|
+
var _a, _b;
|
|
1583
1709
|
if (options.hasOwnProperty('root')) {
|
|
1584
1710
|
if (!options.hasOwnProperty('partials'))
|
|
1585
1711
|
options.partials = options.root;
|
|
@@ -1605,6 +1731,14 @@ function normalize(options) {
|
|
|
1605
1731
|
options.partials = normalizeDirectoryList(options.partials);
|
|
1606
1732
|
options.layouts = normalizeDirectoryList(options.layouts);
|
|
1607
1733
|
options.outputEscape = options.outputEscape && getOutputEscapeFunction(options.outputEscape);
|
|
1734
|
+
if (!options.locale) {
|
|
1735
|
+
options.locale = (_b = (_a = getDateTimeFormat()) === null || _a === void 0 ? void 0 : _a().resolvedOptions().locale) !== null && _b !== void 0 ? _b : 'en-US';
|
|
1736
|
+
}
|
|
1737
|
+
if (options.templates) {
|
|
1738
|
+
options.fs = new MapFS(options.templates);
|
|
1739
|
+
options.relativeReference = true;
|
|
1740
|
+
options.root = options.partials = options.layouts = '.';
|
|
1741
|
+
}
|
|
1608
1742
|
return options;
|
|
1609
1743
|
}
|
|
1610
1744
|
function getOutputEscapeFunction(nameOrFunction) {
|
|
@@ -1731,9 +1865,7 @@ class Tokenizer {
|
|
|
1731
1865
|
this.skipBlank();
|
|
1732
1866
|
if (this.end())
|
|
1733
1867
|
return null;
|
|
1734
|
-
this.assert(this.
|
|
1735
|
-
this.p++;
|
|
1736
|
-
const begin = this.p;
|
|
1868
|
+
this.assert(this.read() === '|', `expected "|" before filter`);
|
|
1737
1869
|
const name = this.readIdentifier();
|
|
1738
1870
|
if (!name.size()) {
|
|
1739
1871
|
this.assert(this.end(), `expected filter name`);
|
|
@@ -1754,7 +1886,7 @@ class Tokenizer {
|
|
|
1754
1886
|
else {
|
|
1755
1887
|
throw this.error('expected ":" after filter name');
|
|
1756
1888
|
}
|
|
1757
|
-
return new FilterToken(name.getText(), args, this.input, begin, this.p, this.file);
|
|
1889
|
+
return new FilterToken(name.getText(), args, this.input, name.begin, this.p, this.file);
|
|
1758
1890
|
}
|
|
1759
1891
|
readFilterArg() {
|
|
1760
1892
|
const key = this.readValue();
|
|
@@ -1795,7 +1927,7 @@ class Tokenizer {
|
|
|
1795
1927
|
}
|
|
1796
1928
|
return new HTMLToken(this.input, begin, this.p, this.file);
|
|
1797
1929
|
}
|
|
1798
|
-
readTagToken(options
|
|
1930
|
+
readTagToken(options) {
|
|
1799
1931
|
const { file, input } = this;
|
|
1800
1932
|
const begin = this.p;
|
|
1801
1933
|
if (this.readToDelimiter(options.tagDelimiterRight) === -1) {
|
|
@@ -1926,7 +2058,7 @@ class Tokenizer {
|
|
|
1926
2058
|
return;
|
|
1927
2059
|
let value;
|
|
1928
2060
|
this.skipBlank();
|
|
1929
|
-
const sep = jekyllStyle ? '=' : ':';
|
|
2061
|
+
const sep = isString(jekyllStyle) ? jekyllStyle : (jekyllStyle ? '=' : ':');
|
|
1930
2062
|
if (this.peek() === sep) {
|
|
1931
2063
|
++this.p;
|
|
1932
2064
|
value = this.readValue();
|
|
@@ -1942,6 +2074,9 @@ class Tokenizer {
|
|
|
1942
2074
|
end() {
|
|
1943
2075
|
return this.p >= this.N;
|
|
1944
2076
|
}
|
|
2077
|
+
read() {
|
|
2078
|
+
return this.input[this.p++];
|
|
2079
|
+
}
|
|
1945
2080
|
readTo(end) {
|
|
1946
2081
|
while (this.p < this.N) {
|
|
1947
2082
|
++this.p;
|
|
@@ -2190,7 +2325,7 @@ function createTagClass(options) {
|
|
|
2190
2325
|
}
|
|
2191
2326
|
}
|
|
2192
2327
|
*render(ctx, emitter) {
|
|
2193
|
-
const hash = (yield new Hash(this.token.args).render(ctx));
|
|
2328
|
+
const hash = (yield new Hash(this.token.args, ctx.opts.keyValueSeparator).render(ctx));
|
|
2194
2329
|
return yield options.render.call(this, ctx, emitter, hash);
|
|
2195
2330
|
}
|
|
2196
2331
|
};
|
|
@@ -2201,13 +2336,14 @@ function isKeyValuePair(arr) {
|
|
|
2201
2336
|
}
|
|
2202
2337
|
|
|
2203
2338
|
class Filter {
|
|
2204
|
-
constructor(
|
|
2205
|
-
this.
|
|
2339
|
+
constructor(token, options, liquid) {
|
|
2340
|
+
this.token = token;
|
|
2341
|
+
this.name = token.name;
|
|
2206
2342
|
this.handler = isFunction(options)
|
|
2207
2343
|
? options
|
|
2208
2344
|
: (isFunction(options === null || options === void 0 ? void 0 : options.handler) ? options.handler : identify);
|
|
2209
2345
|
this.raw = !isFunction(options) && !!(options === null || options === void 0 ? void 0 : options.raw);
|
|
2210
|
-
this.args = args;
|
|
2346
|
+
this.args = token.args;
|
|
2211
2347
|
this.liquid = liquid;
|
|
2212
2348
|
}
|
|
2213
2349
|
*render(value, context) {
|
|
@@ -2218,7 +2354,7 @@ class Filter {
|
|
|
2218
2354
|
else
|
|
2219
2355
|
argv.push(yield evalToken(arg, context));
|
|
2220
2356
|
}
|
|
2221
|
-
return yield this.handler.apply({ context, liquid: this.liquid }, [value, ...argv]);
|
|
2357
|
+
return yield this.handler.apply({ context, token: this.token, liquid: this.liquid }, [value, ...argv]);
|
|
2222
2358
|
}
|
|
2223
2359
|
}
|
|
2224
2360
|
|
|
@@ -2232,7 +2368,7 @@ class Value {
|
|
|
2232
2368
|
? new Tokenizer(input, liquid.options.operators).readFilteredValue()
|
|
2233
2369
|
: input;
|
|
2234
2370
|
this.initial = token.initial;
|
|
2235
|
-
this.filters = token.filters.map(
|
|
2371
|
+
this.filters = token.filters.map(token => new Filter(token, this.getFilter(liquid, token.name), liquid));
|
|
2236
2372
|
}
|
|
2237
2373
|
*value(ctx, lenient) {
|
|
2238
2374
|
lenient = lenient || (ctx.opts.lenientIf && this.filters.length > 0 && this.filters[0].name === 'default');
|
|
@@ -2258,7 +2394,8 @@ class Output extends TemplateImpl {
|
|
|
2258
2394
|
const filters = this.value.filters;
|
|
2259
2395
|
const outputEscape = liquid.options.outputEscape;
|
|
2260
2396
|
if (!((_a = filters[filters.length - 1]) === null || _a === void 0 ? void 0 : _a.raw) && outputEscape) {
|
|
2261
|
-
|
|
2397
|
+
const token = new FilterToken(toString.call(outputEscape), [], '', 0, 0);
|
|
2398
|
+
filters.push(new Filter(token, outputEscape, liquid));
|
|
2262
2399
|
}
|
|
2263
2400
|
}
|
|
2264
2401
|
*render(ctx, emitter) {
|
|
@@ -2293,7 +2430,7 @@ class Loader {
|
|
|
2293
2430
|
this.shouldLoadRelative = (referencedFile) => rRelativePath.test(referencedFile);
|
|
2294
2431
|
}
|
|
2295
2432
|
else {
|
|
2296
|
-
this.shouldLoadRelative = (
|
|
2433
|
+
this.shouldLoadRelative = (_referencedFile) => false;
|
|
2297
2434
|
}
|
|
2298
2435
|
this.contains = this.options.fs.contains || (() => true);
|
|
2299
2436
|
}
|
|
@@ -2350,8 +2487,11 @@ class Parser {
|
|
|
2350
2487
|
this.fs = this.liquid.options.fs;
|
|
2351
2488
|
this.parseFile = this.cache ? this._parseFileCached : this._parseFile;
|
|
2352
2489
|
this.loader = new Loader(this.liquid.options);
|
|
2490
|
+
this.parseLimit = new Limiter('parse length', liquid.options.parseLimit);
|
|
2353
2491
|
}
|
|
2354
2492
|
parse(html, filepath) {
|
|
2493
|
+
html = String(html);
|
|
2494
|
+
this.parseLimit.use(html.length);
|
|
2355
2495
|
const tokenizer = new Tokenizer(html, this.liquid.options.operators, filepath);
|
|
2356
2496
|
const tokens = tokenizer.readTopLevelTokens(this.liquid.options);
|
|
2357
2497
|
return this.parseTokens(tokens);
|
|
@@ -2359,9 +2499,20 @@ class Parser {
|
|
|
2359
2499
|
parseTokens(tokens) {
|
|
2360
2500
|
let token;
|
|
2361
2501
|
const templates = [];
|
|
2502
|
+
const errors = [];
|
|
2362
2503
|
while ((token = tokens.shift())) {
|
|
2363
|
-
|
|
2504
|
+
try {
|
|
2505
|
+
templates.push(this.parseToken(token, tokens));
|
|
2506
|
+
}
|
|
2507
|
+
catch (err) {
|
|
2508
|
+
if (this.liquid.options.catchAllErrors)
|
|
2509
|
+
errors.push(err);
|
|
2510
|
+
else
|
|
2511
|
+
throw err;
|
|
2512
|
+
}
|
|
2364
2513
|
}
|
|
2514
|
+
if (errors.length)
|
|
2515
|
+
throw new LiquidErrors(errors);
|
|
2365
2516
|
return templates;
|
|
2366
2517
|
}
|
|
2367
2518
|
parseToken(token, remainTokens) {
|
|
@@ -2369,7 +2520,7 @@ class Parser {
|
|
|
2369
2520
|
if (isTagToken(token)) {
|
|
2370
2521
|
const TagClass = this.liquid.tags[token.name];
|
|
2371
2522
|
assert(TagClass, `tag "${token.name}" not found`);
|
|
2372
|
-
return new TagClass(token, remainTokens, this.liquid);
|
|
2523
|
+
return new TagClass(token, remainTokens, this.liquid, this);
|
|
2373
2524
|
}
|
|
2374
2525
|
if (isOutputToken(token)) {
|
|
2375
2526
|
return new Output(token, this.liquid);
|
|
@@ -2407,7 +2558,7 @@ class Parser {
|
|
|
2407
2558
|
}
|
|
2408
2559
|
*_parseFile(file, sync, type = LookupType.Root, currentFile) {
|
|
2409
2560
|
const filepath = yield this.loader.lookup(file, type, sync, currentFile);
|
|
2410
|
-
return this.
|
|
2561
|
+
return this.parse(sync ? this.fs.readFileSync(filepath) : yield this.fs.readFile(filepath), filepath);
|
|
2411
2562
|
}
|
|
2412
2563
|
}
|
|
2413
2564
|
|
|
@@ -2482,8 +2633,8 @@ var typeGuards = /*#__PURE__*/Object.freeze({
|
|
|
2482
2633
|
});
|
|
2483
2634
|
|
|
2484
2635
|
class Context {
|
|
2485
|
-
constructor(env = {}, opts = defaultOptions, renderOptions = {}) {
|
|
2486
|
-
var _a, _b, _c;
|
|
2636
|
+
constructor(env = {}, opts = defaultOptions, renderOptions = {}, { memoryLimit, renderLimit } = {}) {
|
|
2637
|
+
var _a, _b, _c, _d, _e;
|
|
2487
2638
|
/**
|
|
2488
2639
|
* insert a Context-level empty scope,
|
|
2489
2640
|
* for tags like `{% capture %}` `{% assign %}` to operate
|
|
@@ -2496,6 +2647,8 @@ class Context {
|
|
|
2496
2647
|
this.environments = isObject(env) ? env : Object(env);
|
|
2497
2648
|
this.strictVariables = (_b = renderOptions.strictVariables) !== null && _b !== void 0 ? _b : this.opts.strictVariables;
|
|
2498
2649
|
this.ownPropertyOnly = (_c = renderOptions.ownPropertyOnly) !== null && _c !== void 0 ? _c : opts.ownPropertyOnly;
|
|
2650
|
+
this.memoryLimit = memoryLimit !== null && memoryLimit !== void 0 ? memoryLimit : new Limiter('memory alloc', (_d = renderOptions.memoryLimit) !== null && _d !== void 0 ? _d : opts.memoryLimit);
|
|
2651
|
+
this.renderLimit = renderLimit !== null && renderLimit !== void 0 ? renderLimit : new Limiter('template render', getPerformance().now() + ((_e = renderOptions.renderLimit) !== null && _e !== void 0 ? _e : opts.renderLimit));
|
|
2499
2652
|
}
|
|
2500
2653
|
getRegister(key) {
|
|
2501
2654
|
return (this.registers[key] = this.registers[key] || {});
|
|
@@ -2552,6 +2705,16 @@ class Context {
|
|
|
2552
2705
|
bottom() {
|
|
2553
2706
|
return this.scopes[0];
|
|
2554
2707
|
}
|
|
2708
|
+
spawn(scope = {}) {
|
|
2709
|
+
return new Context(scope, this.opts, {
|
|
2710
|
+
sync: this.sync,
|
|
2711
|
+
globals: this.globals,
|
|
2712
|
+
strictVariables: this.strictVariables
|
|
2713
|
+
}, {
|
|
2714
|
+
renderLimit: this.renderLimit,
|
|
2715
|
+
memoryLimit: this.memoryLimit
|
|
2716
|
+
});
|
|
2717
|
+
}
|
|
2555
2718
|
findScope(key) {
|
|
2556
2719
|
for (let i = this.scopes.length - 1; i >= 0; i--) {
|
|
2557
2720
|
const candidate = this.scopes[i];
|
|
@@ -2583,7 +2746,7 @@ function readProperty(obj, key, ownPropertyOnly) {
|
|
|
2583
2746
|
return value;
|
|
2584
2747
|
}
|
|
2585
2748
|
function readJSProperty(obj, key, ownPropertyOnly) {
|
|
2586
|
-
if (ownPropertyOnly && !
|
|
2749
|
+
if (ownPropertyOnly && !hasOwnProperty.call(obj, key) && !(obj instanceof Drop))
|
|
2587
2750
|
return undefined;
|
|
2588
2751
|
return obj[key];
|
|
2589
2752
|
}
|
|
@@ -2598,7 +2761,7 @@ function readLast(obj) {
|
|
|
2598
2761
|
return obj['last'];
|
|
2599
2762
|
}
|
|
2600
2763
|
function readSize(obj) {
|
|
2601
|
-
if (
|
|
2764
|
+
if (hasOwnProperty.call(obj, 'size') || obj['size'] !== undefined)
|
|
2602
2765
|
return obj['size'];
|
|
2603
2766
|
if (isArray(obj) || isString(obj))
|
|
2604
2767
|
return obj.length;
|
|
@@ -2705,13 +2868,25 @@ var urlFilters = /*#__PURE__*/Object.freeze({
|
|
|
2705
2868
|
slugify: slugify
|
|
2706
2869
|
});
|
|
2707
2870
|
|
|
2708
|
-
const join = argumentsToValue((v, arg)
|
|
2871
|
+
const join = argumentsToValue(function (v, arg) {
|
|
2872
|
+
const array = toArray(v);
|
|
2873
|
+
const sep = isNil(arg) ? ' ' : stringify(arg);
|
|
2874
|
+
const complexity = array.length * (1 + sep.length);
|
|
2875
|
+
this.context.memoryLimit.use(complexity);
|
|
2876
|
+
return array.join(sep);
|
|
2877
|
+
});
|
|
2709
2878
|
const last$1 = argumentsToValue((v) => isArray(v) ? last(v) : '');
|
|
2710
2879
|
const first = argumentsToValue((v) => isArray(v) ? v[0] : '');
|
|
2711
|
-
const reverse = argumentsToValue((v)
|
|
2880
|
+
const reverse = argumentsToValue(function (v) {
|
|
2881
|
+
const array = toArray(v);
|
|
2882
|
+
this.context.memoryLimit.use(array.length);
|
|
2883
|
+
return [...array].reverse();
|
|
2884
|
+
});
|
|
2712
2885
|
function* sort(arr, property) {
|
|
2713
2886
|
const values = [];
|
|
2714
|
-
|
|
2887
|
+
const array = toArray(arr);
|
|
2888
|
+
this.context.memoryLimit.use(array.length);
|
|
2889
|
+
for (const item of array) {
|
|
2715
2890
|
values.push([
|
|
2716
2891
|
item,
|
|
2717
2892
|
property ? yield this.context._getFromScope(item, stringify(property).split('.'), false) : item
|
|
@@ -2728,35 +2903,47 @@ function sort_natural(input, property) {
|
|
|
2728
2903
|
const compare = property === undefined
|
|
2729
2904
|
? caseInsensitiveCompare
|
|
2730
2905
|
: (lhs, rhs) => caseInsensitiveCompare(lhs[propertyString], rhs[propertyString]);
|
|
2731
|
-
|
|
2906
|
+
const array = toArray(input);
|
|
2907
|
+
this.context.memoryLimit.use(array.length);
|
|
2908
|
+
return [...array].sort(compare);
|
|
2732
2909
|
}
|
|
2733
2910
|
const size = (v) => (v && v.length) || 0;
|
|
2734
2911
|
function* map(arr, property) {
|
|
2735
2912
|
const results = [];
|
|
2736
|
-
|
|
2913
|
+
const array = toArray(arr);
|
|
2914
|
+
this.context.memoryLimit.use(array.length);
|
|
2915
|
+
for (const item of array) {
|
|
2737
2916
|
results.push(yield this.context._getFromScope(item, stringify(property), false));
|
|
2738
2917
|
}
|
|
2739
2918
|
return results;
|
|
2740
2919
|
}
|
|
2741
2920
|
function* sum(arr, property) {
|
|
2742
2921
|
let sum = 0;
|
|
2743
|
-
|
|
2922
|
+
const array = toArray(arr);
|
|
2923
|
+
for (const item of array) {
|
|
2744
2924
|
const data = Number(property ? yield this.context._getFromScope(item, stringify(property), false) : item);
|
|
2745
2925
|
sum += Number.isNaN(data) ? 0 : data;
|
|
2746
2926
|
}
|
|
2747
2927
|
return sum;
|
|
2748
2928
|
}
|
|
2749
2929
|
function compact(arr) {
|
|
2750
|
-
|
|
2930
|
+
const array = toArray(arr);
|
|
2931
|
+
this.context.memoryLimit.use(array.length);
|
|
2932
|
+
return array.filter(x => !isNil(toValue(x)));
|
|
2751
2933
|
}
|
|
2752
2934
|
function concat(v, arg = []) {
|
|
2753
|
-
|
|
2935
|
+
const lhs = toArray(v);
|
|
2936
|
+
const rhs = toArray(arg);
|
|
2937
|
+
this.context.memoryLimit.use(lhs.length + rhs.length);
|
|
2938
|
+
return lhs.concat(rhs);
|
|
2754
2939
|
}
|
|
2755
2940
|
function push(v, arg) {
|
|
2756
|
-
return concat(v, [arg]);
|
|
2941
|
+
return concat.call(this, v, [arg]);
|
|
2757
2942
|
}
|
|
2758
2943
|
function unshift(v, arg) {
|
|
2759
|
-
const
|
|
2944
|
+
const array = toArray(v);
|
|
2945
|
+
this.context.memoryLimit.use(array.length);
|
|
2946
|
+
const clone = [...array];
|
|
2760
2947
|
clone.unshift(arg);
|
|
2761
2948
|
return clone;
|
|
2762
2949
|
}
|
|
@@ -2766,7 +2953,9 @@ function pop(v) {
|
|
|
2766
2953
|
return clone;
|
|
2767
2954
|
}
|
|
2768
2955
|
function shift(v) {
|
|
2769
|
-
const
|
|
2956
|
+
const array = toArray(v);
|
|
2957
|
+
this.context.memoryLimit.use(array.length);
|
|
2958
|
+
const clone = [...array];
|
|
2770
2959
|
clone.shift();
|
|
2771
2960
|
return clone;
|
|
2772
2961
|
}
|
|
@@ -2777,14 +2966,16 @@ function slice(v, begin, length = 1) {
|
|
|
2777
2966
|
if (!isArray(v))
|
|
2778
2967
|
v = stringify(v);
|
|
2779
2968
|
begin = begin < 0 ? v.length + begin : begin;
|
|
2969
|
+
this.context.memoryLimit.use(length);
|
|
2780
2970
|
return v.slice(begin, begin + length);
|
|
2781
2971
|
}
|
|
2782
2972
|
function* where(arr, property, expected) {
|
|
2783
2973
|
const values = [];
|
|
2784
2974
|
arr = toArray(arr);
|
|
2975
|
+
this.context.memoryLimit.use(arr.length);
|
|
2785
2976
|
const token = new Tokenizer(stringify(property)).readScopeValue();
|
|
2786
2977
|
for (const item of arr) {
|
|
2787
|
-
values.push(yield evalToken(token,
|
|
2978
|
+
values.push(yield evalToken(token, this.context.spawn(item)));
|
|
2788
2979
|
}
|
|
2789
2980
|
return arr.filter((_, i) => {
|
|
2790
2981
|
if (expected === undefined)
|
|
@@ -2795,8 +2986,10 @@ function* where(arr, property, expected) {
|
|
|
2795
2986
|
function* where_exp(arr, itemName, exp) {
|
|
2796
2987
|
const filtered = [];
|
|
2797
2988
|
const keyTemplate = new Value(stringify(exp), this.liquid);
|
|
2798
|
-
|
|
2799
|
-
|
|
2989
|
+
const array = toArray(arr);
|
|
2990
|
+
this.context.memoryLimit.use(array.length);
|
|
2991
|
+
for (const item of array) {
|
|
2992
|
+
const value = yield keyTemplate.value(this.context.spawn({ [itemName]: item }));
|
|
2800
2993
|
if (value)
|
|
2801
2994
|
filtered.push(item);
|
|
2802
2995
|
}
|
|
@@ -2806,8 +2999,9 @@ function* group_by(arr, property) {
|
|
|
2806
2999
|
const map = new Map();
|
|
2807
3000
|
arr = toArray(arr);
|
|
2808
3001
|
const token = new Tokenizer(stringify(property)).readScopeValue();
|
|
3002
|
+
this.context.memoryLimit.use(arr.length);
|
|
2809
3003
|
for (const item of arr) {
|
|
2810
|
-
const key = yield evalToken(token,
|
|
3004
|
+
const key = yield evalToken(token, this.context.spawn(item));
|
|
2811
3005
|
if (!map.has(key))
|
|
2812
3006
|
map.set(key, []);
|
|
2813
3007
|
map.get(key).push(item);
|
|
@@ -2817,8 +3011,10 @@ function* group_by(arr, property) {
|
|
|
2817
3011
|
function* group_by_exp(arr, itemName, exp) {
|
|
2818
3012
|
const map = new Map();
|
|
2819
3013
|
const keyTemplate = new Value(stringify(exp), this.liquid);
|
|
2820
|
-
|
|
2821
|
-
|
|
3014
|
+
arr = toArray(arr);
|
|
3015
|
+
this.context.memoryLimit.use(arr.length);
|
|
3016
|
+
for (const item of arr) {
|
|
3017
|
+
const key = yield keyTemplate.value(this.context.spawn({ [itemName]: item }));
|
|
2822
3018
|
if (!map.has(key))
|
|
2823
3019
|
map.set(key, []);
|
|
2824
3020
|
map.get(key).push(item);
|
|
@@ -2827,31 +3023,26 @@ function* group_by_exp(arr, itemName, exp) {
|
|
|
2827
3023
|
}
|
|
2828
3024
|
function* find(arr, property, expected) {
|
|
2829
3025
|
const token = new Tokenizer(stringify(property)).readScopeValue();
|
|
2830
|
-
|
|
2831
|
-
|
|
3026
|
+
const array = toArray(arr);
|
|
3027
|
+
for (const item of array) {
|
|
3028
|
+
const value = yield evalToken(token, this.context.spawn(item));
|
|
2832
3029
|
if (equals(value, expected))
|
|
2833
3030
|
return item;
|
|
2834
3031
|
}
|
|
2835
|
-
return null;
|
|
2836
3032
|
}
|
|
2837
3033
|
function* find_exp(arr, itemName, exp) {
|
|
2838
3034
|
const predicate = new Value(stringify(exp), this.liquid);
|
|
2839
|
-
|
|
2840
|
-
|
|
3035
|
+
const array = toArray(arr);
|
|
3036
|
+
for (const item of array) {
|
|
3037
|
+
const value = yield predicate.value(this.context.spawn({ [itemName]: item }));
|
|
2841
3038
|
if (value)
|
|
2842
3039
|
return item;
|
|
2843
3040
|
}
|
|
2844
|
-
return null;
|
|
2845
3041
|
}
|
|
2846
3042
|
function uniq(arr) {
|
|
2847
|
-
arr =
|
|
2848
|
-
|
|
2849
|
-
return (arr
|
|
2850
|
-
if (hasOwnProperty.call(u, String(val)))
|
|
2851
|
-
return false;
|
|
2852
|
-
u[String(val)] = true;
|
|
2853
|
-
return true;
|
|
2854
|
-
});
|
|
3043
|
+
arr = toArray(arr);
|
|
3044
|
+
this.context.memoryLimit.use(arr.length);
|
|
3045
|
+
return [...new Set(arr)];
|
|
2855
3046
|
}
|
|
2856
3047
|
function sample(v, count = 1) {
|
|
2857
3048
|
v = toValue(v);
|
|
@@ -2859,6 +3050,7 @@ function sample(v, count = 1) {
|
|
|
2859
3050
|
return [];
|
|
2860
3051
|
if (!isArray(v))
|
|
2861
3052
|
v = stringify(v);
|
|
3053
|
+
this.context.memoryLimit.use(count);
|
|
2862
3054
|
const shuffled = [...v].sort(() => Math.random() - 0.5);
|
|
2863
3055
|
if (count === 1)
|
|
2864
3056
|
return shuffled[0];
|
|
@@ -2894,6 +3086,9 @@ var arrayFilters = /*#__PURE__*/Object.freeze({
|
|
|
2894
3086
|
});
|
|
2895
3087
|
|
|
2896
3088
|
function date(v, format, timezoneOffset) {
|
|
3089
|
+
var _a, _b, _c;
|
|
3090
|
+
const size = ((_a = v === null || v === void 0 ? void 0 : v.length) !== null && _a !== void 0 ? _a : 0) + ((_b = format === null || format === void 0 ? void 0 : format.length) !== null && _b !== void 0 ? _b : 0) + ((_c = timezoneOffset === null || timezoneOffset === void 0 ? void 0 : timezoneOffset.length) !== null && _c !== void 0 ? _c : 0);
|
|
3091
|
+
this.context.memoryLimit.use(size);
|
|
2897
3092
|
const date = parseDate(v, this.context.opts, timezoneOffset);
|
|
2898
3093
|
if (!date)
|
|
2899
3094
|
return v;
|
|
@@ -2927,39 +3122,30 @@ function stringify_date(v, month_type, type, style) {
|
|
|
2927
3122
|
}
|
|
2928
3123
|
function parseDate(v, opts, timezoneOffset) {
|
|
2929
3124
|
let date;
|
|
3125
|
+
const defaultTimezoneOffset = timezoneOffset !== null && timezoneOffset !== void 0 ? timezoneOffset : opts.timezoneOffset;
|
|
3126
|
+
const locale = opts.locale;
|
|
2930
3127
|
v = toValue(v);
|
|
2931
3128
|
if (v === 'now' || v === 'today') {
|
|
2932
|
-
date = new Date();
|
|
3129
|
+
date = new LiquidDate(Date.now(), locale, defaultTimezoneOffset);
|
|
2933
3130
|
}
|
|
2934
3131
|
else if (isNumber(v)) {
|
|
2935
|
-
date = new
|
|
3132
|
+
date = new LiquidDate(v * 1000, locale, defaultTimezoneOffset);
|
|
2936
3133
|
}
|
|
2937
3134
|
else if (isString(v)) {
|
|
2938
3135
|
if (/^\d+$/.test(v)) {
|
|
2939
|
-
date = new
|
|
3136
|
+
date = new LiquidDate(+v * 1000, locale, defaultTimezoneOffset);
|
|
2940
3137
|
}
|
|
2941
|
-
else if (opts.preserveTimezones) {
|
|
2942
|
-
date =
|
|
3138
|
+
else if (opts.preserveTimezones && timezoneOffset === undefined) {
|
|
3139
|
+
date = LiquidDate.createDateFixedToTimezone(v, locale);
|
|
2943
3140
|
}
|
|
2944
3141
|
else {
|
|
2945
|
-
date = new
|
|
3142
|
+
date = new LiquidDate(v, locale, defaultTimezoneOffset);
|
|
2946
3143
|
}
|
|
2947
3144
|
}
|
|
2948
3145
|
else {
|
|
2949
|
-
date = v;
|
|
2950
|
-
}
|
|
2951
|
-
if (!isValidDate(date))
|
|
2952
|
-
return;
|
|
2953
|
-
if (timezoneOffset !== undefined) {
|
|
2954
|
-
date = new TimezoneDate(date, timezoneOffset);
|
|
2955
|
-
}
|
|
2956
|
-
else if (!(date instanceof TimezoneDate) && opts.timezoneOffset !== undefined) {
|
|
2957
|
-
date = new TimezoneDate(date, opts.timezoneOffset);
|
|
3146
|
+
date = new LiquidDate(v, locale, defaultTimezoneOffset);
|
|
2958
3147
|
}
|
|
2959
|
-
return date;
|
|
2960
|
-
}
|
|
2961
|
-
function isValidDate(date) {
|
|
2962
|
-
return (date instanceof Date || date instanceof TimezoneDate) && !isNaN(date.getTime());
|
|
3148
|
+
return date.valid() ? date : undefined;
|
|
2963
3149
|
}
|
|
2964
3150
|
|
|
2965
3151
|
var dateFilters = /*#__PURE__*/Object.freeze({
|
|
@@ -2981,33 +3167,50 @@ const rCJKWord = /[\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF\u3040-\u309F\u30A0-\u
|
|
|
2981
3167
|
const rNonCJKWord = /[^\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF\u3040-\u309F\u30A0-\u30FF\uAC00-\uD7AF\s]+/gu;
|
|
2982
3168
|
function append(v, arg) {
|
|
2983
3169
|
assert(arguments.length === 2, 'append expect 2 arguments');
|
|
2984
|
-
|
|
3170
|
+
const lhs = stringify(v);
|
|
3171
|
+
const rhs = stringify(arg);
|
|
3172
|
+
this.context.memoryLimit.use(lhs.length + rhs.length);
|
|
3173
|
+
return lhs + rhs;
|
|
2985
3174
|
}
|
|
2986
3175
|
function prepend(v, arg) {
|
|
2987
3176
|
assert(arguments.length === 2, 'prepend expect 2 arguments');
|
|
2988
|
-
|
|
3177
|
+
const lhs = stringify(v);
|
|
3178
|
+
const rhs = stringify(arg);
|
|
3179
|
+
this.context.memoryLimit.use(lhs.length + rhs.length);
|
|
3180
|
+
return rhs + lhs;
|
|
2989
3181
|
}
|
|
2990
3182
|
function lstrip(v, chars) {
|
|
3183
|
+
const str = stringify(v);
|
|
3184
|
+
this.context.memoryLimit.use(str.length);
|
|
2991
3185
|
if (chars) {
|
|
2992
3186
|
chars = escapeRegExp(stringify(chars));
|
|
2993
|
-
return
|
|
3187
|
+
return str.replace(new RegExp(`^[${chars}]+`, 'g'), '');
|
|
2994
3188
|
}
|
|
2995
|
-
return
|
|
3189
|
+
return str.replace(/^\s+/, '');
|
|
2996
3190
|
}
|
|
2997
3191
|
function downcase(v) {
|
|
2998
|
-
|
|
3192
|
+
const str = stringify(v);
|
|
3193
|
+
this.context.memoryLimit.use(str.length);
|
|
3194
|
+
return str.toLowerCase();
|
|
2999
3195
|
}
|
|
3000
|
-
function upcase(
|
|
3196
|
+
function upcase(v) {
|
|
3197
|
+
const str = stringify(v);
|
|
3198
|
+
this.context.memoryLimit.use(str.length);
|
|
3001
3199
|
return stringify(str).toUpperCase();
|
|
3002
3200
|
}
|
|
3003
3201
|
function remove(v, arg) {
|
|
3004
|
-
|
|
3202
|
+
const str = stringify(v);
|
|
3203
|
+
this.context.memoryLimit.use(str.length);
|
|
3204
|
+
return str.split(stringify(arg)).join('');
|
|
3005
3205
|
}
|
|
3006
3206
|
function remove_first(v, l) {
|
|
3007
|
-
|
|
3207
|
+
const str = stringify(v);
|
|
3208
|
+
this.context.memoryLimit.use(str.length);
|
|
3209
|
+
return str.replace(stringify(l), '');
|
|
3008
3210
|
}
|
|
3009
3211
|
function remove_last(v, l) {
|
|
3010
3212
|
const str = stringify(v);
|
|
3213
|
+
this.context.memoryLimit.use(str.length);
|
|
3011
3214
|
const pattern = stringify(l);
|
|
3012
3215
|
const index = str.lastIndexOf(pattern);
|
|
3013
3216
|
if (index === -1)
|
|
@@ -3015,14 +3218,18 @@ function remove_last(v, l) {
|
|
|
3015
3218
|
return str.substring(0, index) + str.substring(index + pattern.length);
|
|
3016
3219
|
}
|
|
3017
3220
|
function rstrip(str, chars) {
|
|
3221
|
+
str = stringify(str);
|
|
3222
|
+
this.context.memoryLimit.use(str.length);
|
|
3018
3223
|
if (chars) {
|
|
3019
3224
|
chars = escapeRegExp(stringify(chars));
|
|
3020
|
-
return
|
|
3225
|
+
return str.replace(new RegExp(`[${chars}]+$`, 'g'), '');
|
|
3021
3226
|
}
|
|
3022
|
-
return
|
|
3227
|
+
return str.replace(/\s+$/, '');
|
|
3023
3228
|
}
|
|
3024
3229
|
function split(v, arg) {
|
|
3025
|
-
const
|
|
3230
|
+
const str = stringify(v);
|
|
3231
|
+
this.context.memoryLimit.use(str.length);
|
|
3232
|
+
const arr = str.split(stringify(arg));
|
|
3026
3233
|
// align to ruby split, which is the behavior of shopify/liquid
|
|
3027
3234
|
// see: https://ruby-doc.org/core-2.4.0/String.html#method-i-split
|
|
3028
3235
|
while (arr.length && arr[arr.length - 1] === '')
|
|
@@ -3030,29 +3237,39 @@ function split(v, arg) {
|
|
|
3030
3237
|
return arr;
|
|
3031
3238
|
}
|
|
3032
3239
|
function strip(v, chars) {
|
|
3240
|
+
const str = stringify(v);
|
|
3241
|
+
this.context.memoryLimit.use(str.length);
|
|
3033
3242
|
if (chars) {
|
|
3034
3243
|
chars = escapeRegExp(stringify(chars));
|
|
3035
|
-
return
|
|
3244
|
+
return str
|
|
3036
3245
|
.replace(new RegExp(`^[${chars}]+`, 'g'), '')
|
|
3037
3246
|
.replace(new RegExp(`[${chars}]+$`, 'g'), '');
|
|
3038
3247
|
}
|
|
3039
|
-
return
|
|
3248
|
+
return str.trim();
|
|
3040
3249
|
}
|
|
3041
3250
|
function strip_newlines(v) {
|
|
3042
|
-
|
|
3251
|
+
const str = stringify(v);
|
|
3252
|
+
this.context.memoryLimit.use(str.length);
|
|
3253
|
+
return str.replace(/\r?\n/gm, '');
|
|
3043
3254
|
}
|
|
3044
3255
|
function capitalize(str) {
|
|
3045
3256
|
str = stringify(str);
|
|
3257
|
+
this.context.memoryLimit.use(str.length);
|
|
3046
3258
|
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
3047
3259
|
}
|
|
3048
3260
|
function replace(v, pattern, replacement) {
|
|
3049
|
-
|
|
3261
|
+
const str = stringify(v);
|
|
3262
|
+
this.context.memoryLimit.use(str.length);
|
|
3263
|
+
return str.split(stringify(pattern)).join(replacement);
|
|
3050
3264
|
}
|
|
3051
3265
|
function replace_first(v, arg1, arg2) {
|
|
3052
|
-
|
|
3266
|
+
const str = stringify(v);
|
|
3267
|
+
this.context.memoryLimit.use(str.length);
|
|
3268
|
+
return str.replace(stringify(arg1), arg2);
|
|
3053
3269
|
}
|
|
3054
3270
|
function replace_last(v, arg1, arg2) {
|
|
3055
3271
|
const str = stringify(v);
|
|
3272
|
+
this.context.memoryLimit.use(str.length);
|
|
3056
3273
|
const pattern = stringify(arg1);
|
|
3057
3274
|
const index = str.lastIndexOf(pattern);
|
|
3058
3275
|
if (index === -1)
|
|
@@ -3061,13 +3278,16 @@ function replace_last(v, arg1, arg2) {
|
|
|
3061
3278
|
return str.substring(0, index) + replacement + str.substring(index + pattern.length);
|
|
3062
3279
|
}
|
|
3063
3280
|
function truncate(v, l = 50, o = '...') {
|
|
3064
|
-
|
|
3065
|
-
|
|
3281
|
+
const str = stringify(v);
|
|
3282
|
+
this.context.memoryLimit.use(str.length);
|
|
3283
|
+
if (str.length <= l)
|
|
3066
3284
|
return v;
|
|
3067
|
-
return
|
|
3285
|
+
return str.substring(0, l - o.length) + o;
|
|
3068
3286
|
}
|
|
3069
3287
|
function truncatewords(v, words = 15, o = '...') {
|
|
3070
|
-
const
|
|
3288
|
+
const str = stringify(v);
|
|
3289
|
+
this.context.memoryLimit.use(str.length);
|
|
3290
|
+
const arr = str.split(/\s+/);
|
|
3071
3291
|
if (words <= 0)
|
|
3072
3292
|
words = 1;
|
|
3073
3293
|
let ret = arr.slice(0, words).join(' ');
|
|
@@ -3076,11 +3296,14 @@ function truncatewords(v, words = 15, o = '...') {
|
|
|
3076
3296
|
return ret;
|
|
3077
3297
|
}
|
|
3078
3298
|
function normalize_whitespace(v) {
|
|
3079
|
-
|
|
3080
|
-
|
|
3299
|
+
const str = stringify(v);
|
|
3300
|
+
this.context.memoryLimit.use(str.length);
|
|
3301
|
+
return str.replace(/\s+/g, ' ');
|
|
3081
3302
|
}
|
|
3082
3303
|
function number_of_words(input, mode) {
|
|
3083
|
-
|
|
3304
|
+
const str = stringify(input);
|
|
3305
|
+
this.context.memoryLimit.use(str.length);
|
|
3306
|
+
input = str.trim();
|
|
3084
3307
|
if (!input)
|
|
3085
3308
|
return 0;
|
|
3086
3309
|
switch (mode) {
|
|
@@ -3098,6 +3321,7 @@ function number_of_words(input, mode) {
|
|
|
3098
3321
|
}
|
|
3099
3322
|
}
|
|
3100
3323
|
function array_to_sentence_string(array, connector = 'and') {
|
|
3324
|
+
this.context.memoryLimit.use(array.length);
|
|
3101
3325
|
switch (array.length) {
|
|
3102
3326
|
case 0:
|
|
3103
3327
|
return '';
|
|
@@ -3154,7 +3378,7 @@ class AssignTag extends Tag {
|
|
|
3154
3378
|
|
|
3155
3379
|
const MODIFIERS = ['offset', 'limit', 'reversed'];
|
|
3156
3380
|
class ForTag extends Tag {
|
|
3157
|
-
constructor(token, remainTokens, liquid) {
|
|
3381
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3158
3382
|
super(token, remainTokens, liquid);
|
|
3159
3383
|
const variable = this.tokenizer.readIdentifier();
|
|
3160
3384
|
const inStr = this.tokenizer.readIdentifier();
|
|
@@ -3164,18 +3388,16 @@ class ForTag extends Tag {
|
|
|
3164
3388
|
}
|
|
3165
3389
|
this.variable = variable.content;
|
|
3166
3390
|
this.collection = collection;
|
|
3167
|
-
this.hash = new Hash(this.tokenizer.remaining());
|
|
3391
|
+
this.hash = new Hash(this.tokenizer.remaining(), liquid.options.keyValueSeparator);
|
|
3168
3392
|
this.templates = [];
|
|
3169
3393
|
this.elseTemplates = [];
|
|
3170
3394
|
let p;
|
|
3171
|
-
const stream =
|
|
3395
|
+
const stream = parser.parseStream(remainTokens)
|
|
3172
3396
|
.on('start', () => (p = this.templates))
|
|
3173
|
-
.on('tag:else',
|
|
3174
|
-
.on('tag:endfor', ()
|
|
3397
|
+
.on('tag:else', tag => { assertEmpty(tag.args); p = this.elseTemplates; })
|
|
3398
|
+
.on('tag:endfor', tag => { assertEmpty(tag.args); stream.stop(); })
|
|
3175
3399
|
.on('template', (tpl) => p.push(tpl))
|
|
3176
|
-
.on('end', () => {
|
|
3177
|
-
throw new Error(`tag ${token.getText()} not closed`);
|
|
3178
|
-
});
|
|
3400
|
+
.on('end', () => { throw new Error(`tag ${token.getText()} not closed`); });
|
|
3179
3401
|
stream.start();
|
|
3180
3402
|
}
|
|
3181
3403
|
*render(ctx, emitter) {
|
|
@@ -3226,7 +3448,7 @@ function limit(arr, count) {
|
|
|
3226
3448
|
}
|
|
3227
3449
|
|
|
3228
3450
|
class CaptureTag extends Tag {
|
|
3229
|
-
constructor(tagToken, remainTokens, liquid) {
|
|
3451
|
+
constructor(tagToken, remainTokens, liquid, parser) {
|
|
3230
3452
|
super(tagToken, remainTokens, liquid);
|
|
3231
3453
|
this.templates = [];
|
|
3232
3454
|
this.variable = this.readVariableName();
|
|
@@ -3234,7 +3456,7 @@ class CaptureTag extends Tag {
|
|
|
3234
3456
|
const token = remainTokens.shift();
|
|
3235
3457
|
if (isTagToken(token) && token.name === 'endcapture')
|
|
3236
3458
|
return;
|
|
3237
|
-
this.templates.push(
|
|
3459
|
+
this.templates.push(parser.parseToken(token, remainTokens));
|
|
3238
3460
|
}
|
|
3239
3461
|
throw new Error(`tag ${tagToken.getText()} not closed`);
|
|
3240
3462
|
}
|
|
@@ -3255,7 +3477,7 @@ class CaptureTag extends Tag {
|
|
|
3255
3477
|
}
|
|
3256
3478
|
|
|
3257
3479
|
class CaseTag extends Tag {
|
|
3258
|
-
constructor(tagToken, remainTokens, liquid) {
|
|
3480
|
+
constructor(tagToken, remainTokens, liquid, parser) {
|
|
3259
3481
|
super(tagToken, remainTokens, liquid);
|
|
3260
3482
|
this.branches = [];
|
|
3261
3483
|
this.elseTemplates = [];
|
|
@@ -3263,7 +3485,7 @@ class CaseTag extends Tag {
|
|
|
3263
3485
|
this.elseTemplates = [];
|
|
3264
3486
|
let p = [];
|
|
3265
3487
|
let elseCount = 0;
|
|
3266
|
-
const stream =
|
|
3488
|
+
const stream = parser.parseStream(remainTokens)
|
|
3267
3489
|
.on('tag:when', (token) => {
|
|
3268
3490
|
if (elseCount > 0) {
|
|
3269
3491
|
return;
|
|
@@ -3334,10 +3556,10 @@ class CommentTag extends Tag {
|
|
|
3334
3556
|
}
|
|
3335
3557
|
|
|
3336
3558
|
class RenderTag extends Tag {
|
|
3337
|
-
constructor(token, remainTokens, liquid) {
|
|
3559
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3338
3560
|
super(token, remainTokens, liquid);
|
|
3339
3561
|
const tokenizer = this.tokenizer;
|
|
3340
|
-
this.file = parseFilePath(tokenizer, this.liquid);
|
|
3562
|
+
this.file = parseFilePath(tokenizer, this.liquid, parser);
|
|
3341
3563
|
this.currentFile = token.file;
|
|
3342
3564
|
while (!tokenizer.end()) {
|
|
3343
3565
|
tokenizer.skipBlank();
|
|
@@ -3371,13 +3593,13 @@ class RenderTag extends Tag {
|
|
|
3371
3593
|
tokenizer.p = begin;
|
|
3372
3594
|
break;
|
|
3373
3595
|
}
|
|
3374
|
-
this.hash = new Hash(tokenizer.remaining());
|
|
3596
|
+
this.hash = new Hash(tokenizer.remaining(), liquid.options.keyValueSeparator);
|
|
3375
3597
|
}
|
|
3376
3598
|
*render(ctx, emitter) {
|
|
3377
3599
|
const { liquid, hash } = this;
|
|
3378
3600
|
const filepath = (yield renderFilePath(this['file'], ctx, liquid));
|
|
3379
3601
|
assert(filepath, () => `illegal file path "${filepath}"`);
|
|
3380
|
-
const childCtx =
|
|
3602
|
+
const childCtx = ctx.spawn();
|
|
3381
3603
|
const scope = childCtx.bottom();
|
|
3382
3604
|
__assign(scope, yield hash.render(ctx));
|
|
3383
3605
|
if (this['with']) {
|
|
@@ -3407,7 +3629,7 @@ class RenderTag extends Tag {
|
|
|
3407
3629
|
* @return Token for expression (not quoted)
|
|
3408
3630
|
* @throws TypeError if cannot read next token
|
|
3409
3631
|
*/
|
|
3410
|
-
function parseFilePath(tokenizer, liquid) {
|
|
3632
|
+
function parseFilePath(tokenizer, liquid, parser) {
|
|
3411
3633
|
if (liquid.options.dynamicPartials) {
|
|
3412
3634
|
const file = tokenizer.readValue();
|
|
3413
3635
|
tokenizer.assert(file, 'illegal file path');
|
|
@@ -3415,13 +3637,13 @@ function parseFilePath(tokenizer, liquid) {
|
|
|
3415
3637
|
return;
|
|
3416
3638
|
if (isQuotedToken(file)) {
|
|
3417
3639
|
// for filenames like "files/{{file}}", eval as liquid template
|
|
3418
|
-
const templates =
|
|
3640
|
+
const templates = parser.parse(evalQuotedToken(file));
|
|
3419
3641
|
return optimize(templates);
|
|
3420
3642
|
}
|
|
3421
3643
|
return file;
|
|
3422
3644
|
}
|
|
3423
3645
|
const tokens = [...tokenizer.readFileNameTemplate(liquid.options)];
|
|
3424
|
-
const templates = optimize(
|
|
3646
|
+
const templates = optimize(parser.parseTokens(tokens));
|
|
3425
3647
|
return templates === 'none' ? undefined : templates;
|
|
3426
3648
|
}
|
|
3427
3649
|
function optimize(templates) {
|
|
@@ -3439,10 +3661,10 @@ function* renderFilePath(file, ctx, liquid) {
|
|
|
3439
3661
|
}
|
|
3440
3662
|
|
|
3441
3663
|
class IncludeTag extends Tag {
|
|
3442
|
-
constructor(token, remainTokens, liquid) {
|
|
3664
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3443
3665
|
super(token, remainTokens, liquid);
|
|
3444
3666
|
const { tokenizer } = token;
|
|
3445
|
-
this['file'] = parseFilePath(tokenizer, this.liquid);
|
|
3667
|
+
this['file'] = parseFilePath(tokenizer, this.liquid, parser);
|
|
3446
3668
|
this['currentFile'] = token.file;
|
|
3447
3669
|
const begin = tokenizer.p;
|
|
3448
3670
|
const withStr = tokenizer.readIdentifier();
|
|
@@ -3456,7 +3678,7 @@ class IncludeTag extends Tag {
|
|
|
3456
3678
|
}
|
|
3457
3679
|
else
|
|
3458
3680
|
tokenizer.p = begin;
|
|
3459
|
-
this.hash = new Hash(tokenizer.remaining(),
|
|
3681
|
+
this.hash = new Hash(tokenizer.remaining(), liquid.options.jekyllInclude || liquid.options.keyValueSeparator);
|
|
3460
3682
|
}
|
|
3461
3683
|
*render(ctx, emitter) {
|
|
3462
3684
|
const { liquid, hash, withVar } = this;
|
|
@@ -3529,37 +3751,29 @@ class CycleTag extends Tag {
|
|
|
3529
3751
|
}
|
|
3530
3752
|
|
|
3531
3753
|
class IfTag extends Tag {
|
|
3532
|
-
constructor(tagToken, remainTokens, liquid) {
|
|
3754
|
+
constructor(tagToken, remainTokens, liquid, parser) {
|
|
3533
3755
|
super(tagToken, remainTokens, liquid);
|
|
3534
3756
|
this.branches = [];
|
|
3535
|
-
this.elseTemplates = [];
|
|
3536
3757
|
let p = [];
|
|
3537
|
-
|
|
3538
|
-
liquid.parser.parseStream(remainTokens)
|
|
3758
|
+
parser.parseStream(remainTokens)
|
|
3539
3759
|
.on('start', () => this.branches.push({
|
|
3540
3760
|
value: new Value(tagToken.args, this.liquid),
|
|
3541
3761
|
templates: (p = [])
|
|
3542
3762
|
}))
|
|
3543
3763
|
.on('tag:elsif', (token) => {
|
|
3544
|
-
|
|
3545
|
-
p = [];
|
|
3546
|
-
return;
|
|
3547
|
-
}
|
|
3764
|
+
assert(!this.elseTemplates, 'unexpected elsif after else');
|
|
3548
3765
|
this.branches.push({
|
|
3549
3766
|
value: new Value(token.args, this.liquid),
|
|
3550
3767
|
templates: (p = [])
|
|
3551
3768
|
});
|
|
3552
3769
|
})
|
|
3553
|
-
.on('tag:else',
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
.on('tag:endif', function () { this.stop(); })
|
|
3558
|
-
.on('template', (tpl) => {
|
|
3559
|
-
if (p !== this.elseTemplates || elseCount === 1) {
|
|
3560
|
-
p.push(tpl);
|
|
3561
|
-
}
|
|
3770
|
+
.on('tag:else', tag => {
|
|
3771
|
+
assertEmpty(tag.args);
|
|
3772
|
+
assert(!this.elseTemplates, 'duplicated else');
|
|
3773
|
+
p = this.elseTemplates = [];
|
|
3562
3774
|
})
|
|
3775
|
+
.on('tag:endif', function (tag) { assertEmpty(tag.args); this.stop(); })
|
|
3776
|
+
.on('template', (tpl) => p.push(tpl))
|
|
3563
3777
|
.on('end', () => { throw new Error(`tag ${tagToken.getText()} not closed`); })
|
|
3564
3778
|
.start();
|
|
3565
3779
|
}
|
|
@@ -3572,7 +3786,7 @@ class IfTag extends Tag {
|
|
|
3572
3786
|
return;
|
|
3573
3787
|
}
|
|
3574
3788
|
}
|
|
3575
|
-
yield r.renderTemplates(this.elseTemplates, ctx, emitter);
|
|
3789
|
+
yield r.renderTemplates(this.elseTemplates || [], ctx, emitter);
|
|
3576
3790
|
}
|
|
3577
3791
|
}
|
|
3578
3792
|
|
|
@@ -3593,12 +3807,12 @@ class IncrementTag extends Tag {
|
|
|
3593
3807
|
}
|
|
3594
3808
|
|
|
3595
3809
|
class LayoutTag extends Tag {
|
|
3596
|
-
constructor(token, remainTokens, liquid) {
|
|
3810
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3597
3811
|
super(token, remainTokens, liquid);
|
|
3598
|
-
this.file = parseFilePath(this.tokenizer, this.liquid);
|
|
3812
|
+
this.file = parseFilePath(this.tokenizer, this.liquid, parser);
|
|
3599
3813
|
this['currentFile'] = token.file;
|
|
3600
|
-
this.args = new Hash(this.tokenizer.remaining());
|
|
3601
|
-
this.templates =
|
|
3814
|
+
this.args = new Hash(this.tokenizer.remaining(), liquid.options.keyValueSeparator);
|
|
3815
|
+
this.templates = parser.parseTokens(remainTokens);
|
|
3602
3816
|
}
|
|
3603
3817
|
*render(ctx, emitter) {
|
|
3604
3818
|
const { liquid, args, file } = this;
|
|
@@ -3627,7 +3841,7 @@ class LayoutTag extends Tag {
|
|
|
3627
3841
|
}
|
|
3628
3842
|
|
|
3629
3843
|
class BlockTag extends Tag {
|
|
3630
|
-
constructor(token, remainTokens, liquid) {
|
|
3844
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3631
3845
|
super(token, remainTokens, liquid);
|
|
3632
3846
|
this.templates = [];
|
|
3633
3847
|
const match = /\w+/.exec(token.args);
|
|
@@ -3636,7 +3850,7 @@ class BlockTag extends Tag {
|
|
|
3636
3850
|
const token = remainTokens.shift();
|
|
3637
3851
|
if (isTagToken(token) && token.name === 'endblock')
|
|
3638
3852
|
return;
|
|
3639
|
-
const template =
|
|
3853
|
+
const template = parser.parseToken(token, remainTokens);
|
|
3640
3854
|
this.templates.push(template);
|
|
3641
3855
|
}
|
|
3642
3856
|
throw new Error(`tag ${token.getText()} not closed`);
|
|
@@ -3706,7 +3920,7 @@ class TablerowloopDrop extends ForloopDrop {
|
|
|
3706
3920
|
}
|
|
3707
3921
|
|
|
3708
3922
|
class TablerowTag extends Tag {
|
|
3709
|
-
constructor(tagToken, remainTokens, liquid) {
|
|
3923
|
+
constructor(tagToken, remainTokens, liquid, parser) {
|
|
3710
3924
|
super(tagToken, remainTokens, liquid);
|
|
3711
3925
|
const variable = this.tokenizer.readIdentifier();
|
|
3712
3926
|
this.tokenizer.skipBlank();
|
|
@@ -3717,10 +3931,10 @@ class TablerowTag extends Tag {
|
|
|
3717
3931
|
}
|
|
3718
3932
|
this.variable = variable.content;
|
|
3719
3933
|
this.collection = collectionToken;
|
|
3720
|
-
this.args = new Hash(this.tokenizer.remaining());
|
|
3934
|
+
this.args = new Hash(this.tokenizer.remaining(), liquid.options.keyValueSeparator);
|
|
3721
3935
|
this.templates = [];
|
|
3722
3936
|
let p;
|
|
3723
|
-
const stream =
|
|
3937
|
+
const stream = parser.parseStream(remainTokens)
|
|
3724
3938
|
.on('start', () => (p = this.templates))
|
|
3725
3939
|
.on('tag:endtablerow', () => stream.stop())
|
|
3726
3940
|
.on('template', (tpl) => p.push(tpl))
|
|
@@ -3758,13 +3972,13 @@ class TablerowTag extends Tag {
|
|
|
3758
3972
|
}
|
|
3759
3973
|
|
|
3760
3974
|
class UnlessTag extends Tag {
|
|
3761
|
-
constructor(tagToken, remainTokens, liquid) {
|
|
3975
|
+
constructor(tagToken, remainTokens, liquid, parser) {
|
|
3762
3976
|
super(tagToken, remainTokens, liquid);
|
|
3763
3977
|
this.branches = [];
|
|
3764
3978
|
this.elseTemplates = [];
|
|
3765
3979
|
let p = [];
|
|
3766
3980
|
let elseCount = 0;
|
|
3767
|
-
|
|
3981
|
+
parser.parseStream(remainTokens)
|
|
3768
3982
|
.on('start', () => this.branches.push({
|
|
3769
3983
|
value: new Value(tagToken.args, this.liquid),
|
|
3770
3984
|
test: isFalsy,
|
|
@@ -3836,10 +4050,10 @@ class EchoTag extends Tag {
|
|
|
3836
4050
|
}
|
|
3837
4051
|
|
|
3838
4052
|
class LiquidTag extends Tag {
|
|
3839
|
-
constructor(token, remainTokens, liquid) {
|
|
4053
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3840
4054
|
super(token, remainTokens, liquid);
|
|
3841
4055
|
const tokens = this.tokenizer.readLiquidTagTokens(this.liquid.options);
|
|
3842
|
-
this.templates =
|
|
4056
|
+
this.templates = parser.parseTokens(tokens);
|
|
3843
4057
|
}
|
|
3844
4058
|
*render(ctx, emitter) {
|
|
3845
4059
|
yield this.liquid.renderer.renderTemplates(this.templates, ctx, emitter);
|
|
@@ -3886,12 +4100,14 @@ class Liquid {
|
|
|
3886
4100
|
this.filters = {};
|
|
3887
4101
|
this.tags = {};
|
|
3888
4102
|
this.options = normalize(opts);
|
|
4103
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
3889
4104
|
this.parser = new Parser(this);
|
|
3890
4105
|
forOwn(tags, (conf, name) => this.registerTag(name, conf));
|
|
3891
4106
|
forOwn(filters, (handler, name) => this.registerFilter(name, handler));
|
|
3892
4107
|
}
|
|
3893
4108
|
parse(html, filepath) {
|
|
3894
|
-
|
|
4109
|
+
const parser = new Parser(this);
|
|
4110
|
+
return parser.parse(html, filepath);
|
|
3895
4111
|
}
|
|
3896
4112
|
_render(tpl, scope, renderOptions) {
|
|
3897
4113
|
const ctx = scope instanceof Context ? scope : new Context(scope, this.options, renderOptions);
|
|
@@ -3922,21 +4138,21 @@ class Liquid {
|
|
|
3922
4138
|
return toValueSync(this._parseAndRender(html, scope, Object.assign(Object.assign({}, renderOptions), { sync: true })));
|
|
3923
4139
|
}
|
|
3924
4140
|
_parsePartialFile(file, sync, currentFile) {
|
|
3925
|
-
return this.
|
|
4141
|
+
return new Parser(this).parseFile(file, sync, LookupType.Partials, currentFile);
|
|
3926
4142
|
}
|
|
3927
4143
|
_parseLayoutFile(file, sync, currentFile) {
|
|
3928
|
-
return this.
|
|
4144
|
+
return new Parser(this).parseFile(file, sync, LookupType.Layouts, currentFile);
|
|
3929
4145
|
}
|
|
3930
4146
|
_parseFile(file, sync, lookupType, currentFile) {
|
|
3931
|
-
return this.
|
|
4147
|
+
return new Parser(this).parseFile(file, sync, lookupType, currentFile);
|
|
3932
4148
|
}
|
|
3933
4149
|
parseFile(file, lookupType) {
|
|
3934
4150
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3935
|
-
return toPromise(this.
|
|
4151
|
+
return toPromise(new Parser(this).parseFile(file, false, lookupType));
|
|
3936
4152
|
});
|
|
3937
4153
|
}
|
|
3938
4154
|
parseFileSync(file, lookupType) {
|
|
3939
|
-
return toValueSync(this.
|
|
4155
|
+
return toValueSync(new Parser(this).parseFile(file, true, lookupType));
|
|
3940
4156
|
}
|
|
3941
4157
|
*_renderFile(file, ctx, renderFileOptions) {
|
|
3942
4158
|
const templates = (yield this._parseFile(file, renderFileOptions.sync, renderFileOptions.lookupType));
|
|
@@ -3995,6 +4211,6 @@ class Liquid {
|
|
|
3995
4211
|
}
|
|
3996
4212
|
|
|
3997
4213
|
/* istanbul ignore file */
|
|
3998
|
-
const version = '
|
|
4214
|
+
const version = '[VI]{version}[/VI]';
|
|
3999
4215
|
|
|
4000
|
-
export { AssertionError, AssignTag, BlockTag, BreakTag, CaptureTag, CaseTag, CommentTag, Context, ContinueTag, CycleTag, DecrementTag, Drop, EchoTag, Expression, Filter, ForTag, Hash, IfTag, IncludeTag, IncrementTag, InlineCommentTag, LayoutTag, Liquid, LiquidError, LiquidTag, Output, ParseError, ParseStream, RawTag, RenderError, RenderTag, TablerowTag, Tag, TagToken,
|
|
4216
|
+
export { AssertionError, AssignTag, BlockTag, BreakTag, CaptureTag, CaseTag, CommentTag, Context, ContinueTag, CycleTag, DecrementTag, Drop, EchoTag, Expression, Filter, ForTag, Hash, IfTag, IncludeTag, IncrementTag, InlineCommentTag, LayoutTag, Liquid, LiquidError, LiquidTag, Output, ParseError, ParseStream, RawTag, RenderError, RenderTag, TablerowTag, Tag, TagToken, Token, TokenKind, TokenizationError, Tokenizer, typeGuards as TypeGuards, UndefinedVariableError, UnlessTag, Value, assert, createTrie, defaultOperators, defaultOptions, evalQuotedToken, evalToken, filters, isFalsy, isTruthy, tags, toPromise, toValue, toValueSync, version };
|