@skeptools/skep-organization-project 0.0.15 → 0.0.17
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 +138 -3
- package/API.md +137 -44
- package/lib/skep-organization-project.js +1 -1
- package/node_modules/liquidjs/README.md +29 -17
- package/node_modules/liquidjs/dist/context/context.d.ts +13 -4
- package/node_modules/liquidjs/dist/drop/blank-drop.d.ts +1 -0
- package/node_modules/liquidjs/dist/drop/drop.d.ts +1 -1
- package/node_modules/liquidjs/dist/drop/empty-drop.d.ts +1 -0
- 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 +3 -3
- package/node_modules/liquidjs/dist/liquid-options.d.ts +30 -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} +1147 -264
- package/node_modules/liquidjs/dist/liquid.browser.umd.js +2072 -478
- package/node_modules/liquidjs/dist/liquid.browser.umd.js.map +1 -1
- package/node_modules/liquidjs/dist/liquid.d.ts +32 -1
- package/node_modules/liquidjs/dist/{liquid.node.cjs.js → liquid.node.js} +1123 -264
- package/node_modules/liquidjs/dist/{liquid.node.esm.js → liquid.node.mjs} +1152 -265
- 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/render/expression.d.ts +1 -1
- package/node_modules/liquidjs/dist/render/operator.d.ts +1 -0
- package/node_modules/liquidjs/dist/tags/assign.d.ts +5 -0
- package/node_modules/liquidjs/dist/tags/block.d.ts +4 -1
- package/node_modules/liquidjs/dist/tags/break.d.ts +1 -1
- package/node_modules/liquidjs/dist/tags/capture.d.ts +7 -2
- package/node_modules/liquidjs/dist/tags/case.d.ts +5 -1
- package/node_modules/liquidjs/dist/tags/continue.d.ts +1 -1
- package/node_modules/liquidjs/dist/tags/cycle.d.ts +2 -0
- package/node_modules/liquidjs/dist/tags/decrement.d.ts +3 -0
- package/node_modules/liquidjs/dist/tags/echo.d.ts +2 -0
- package/node_modules/liquidjs/dist/tags/for.d.ts +6 -1
- package/node_modules/liquidjs/dist/tags/if.d.ts +6 -2
- package/node_modules/liquidjs/dist/tags/include.d.ts +7 -2
- package/node_modules/liquidjs/dist/tags/increment.d.ts +3 -0
- package/node_modules/liquidjs/dist/tags/layout.d.ts +6 -1
- package/node_modules/liquidjs/dist/tags/liquid.d.ts +3 -1
- package/node_modules/liquidjs/dist/tags/render.d.ts +7 -2
- package/node_modules/liquidjs/dist/tags/tablerow.d.ts +6 -1
- package/node_modules/liquidjs/dist/tags/unless.d.ts +5 -1
- package/node_modules/liquidjs/dist/template/analysis.d.ts +85 -0
- package/node_modules/liquidjs/dist/template/analysis.spec.d.ts +1 -0
- 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 +2 -1
- package/node_modules/liquidjs/dist/template/index.d.ts +1 -0
- package/node_modules/liquidjs/dist/template/output.d.ts +2 -1
- package/node_modules/liquidjs/dist/template/tag.d.ts +2 -2
- package/node_modules/liquidjs/dist/template/template.d.ts +29 -0
- package/node_modules/liquidjs/dist/tokens/identifier-token.d.ts +0 -1
- package/node_modules/liquidjs/dist/tokens/liquid-tag-token.d.ts +1 -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.d.ts +2 -1
- package/node_modules/liquidjs/dist/util/type-guards.spec.d.ts +1 -0
- package/node_modules/liquidjs/dist/util/underscore.d.ts +5 -1
- package/node_modules/liquidjs/package.json +11 -7
- package/package.json +17 -13
- 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,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* liquidjs@10.
|
|
3
|
-
* (c) 2016-
|
|
2
|
+
* liquidjs@10.20.1, https://github.com/harttle/liquidjs
|
|
3
|
+
* (c) 2016-2025 harttle
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
class Token {
|
|
@@ -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
|
}
|
|
@@ -107,6 +111,9 @@ function isArray(value) {
|
|
|
107
111
|
// be compatible with IE 8
|
|
108
112
|
return toString$1.call(value) === '[object Array]';
|
|
109
113
|
}
|
|
114
|
+
function isArrayLike(value) {
|
|
115
|
+
return value && isNumber(value.length);
|
|
116
|
+
}
|
|
110
117
|
function isIterable(value) {
|
|
111
118
|
return isObject(value) && Symbol.iterator in value;
|
|
112
119
|
}
|
|
@@ -188,14 +195,25 @@ function caseInsensitiveCompare(a, b) {
|
|
|
188
195
|
return 0;
|
|
189
196
|
}
|
|
190
197
|
function argumentsToValue(fn) {
|
|
191
|
-
return (...args)
|
|
198
|
+
return function (...args) { return fn.call(this, ...args.map(toValue)); };
|
|
192
199
|
}
|
|
193
200
|
function escapeRegExp(text) {
|
|
194
201
|
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
|
202
|
+
}
|
|
203
|
+
/** Return an array containing unique elements from _array_. Works with nested arrays and objects. */
|
|
204
|
+
function* strictUniq(array) {
|
|
205
|
+
const seen = new Set();
|
|
206
|
+
for (const element of array) {
|
|
207
|
+
const key = JSON.stringify(element);
|
|
208
|
+
if (!seen.has(key)) {
|
|
209
|
+
seen.add(key);
|
|
210
|
+
yield element;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
195
213
|
}
|
|
196
214
|
|
|
197
215
|
/**
|
|
198
|
-
* targeting ES5, extends Error won't create a proper prototype chain, need a trait to
|
|
216
|
+
* targeting ES5, extends Error won't create a proper prototype chain, need a trait to keep track of classes
|
|
199
217
|
*/
|
|
200
218
|
const TRAIT = '__liquidClass__';
|
|
201
219
|
class LiquidError extends Error {
|
|
@@ -249,6 +267,19 @@ class RenderError extends LiquidError {
|
|
|
249
267
|
return obj.name === 'RenderError';
|
|
250
268
|
}
|
|
251
269
|
}
|
|
270
|
+
class LiquidErrors extends LiquidError {
|
|
271
|
+
constructor(errors) {
|
|
272
|
+
super(errors[0], errors[0].token);
|
|
273
|
+
this.errors = errors;
|
|
274
|
+
this.name = 'LiquidErrors';
|
|
275
|
+
const s = errors.length > 1 ? 's' : '';
|
|
276
|
+
this.message = `${errors.length} error${s} found`;
|
|
277
|
+
super.update();
|
|
278
|
+
}
|
|
279
|
+
static is(obj) {
|
|
280
|
+
return obj.name === 'LiquidErrors';
|
|
281
|
+
}
|
|
282
|
+
}
|
|
252
283
|
class UndefinedVariableError extends LiquidError {
|
|
253
284
|
constructor(err, token) {
|
|
254
285
|
super(err, token);
|
|
@@ -327,6 +358,9 @@ function assert(predicate, message) {
|
|
|
327
358
|
: (message || `expect ${predicate} to be true`);
|
|
328
359
|
throw new AssertionError(msg);
|
|
329
360
|
}
|
|
361
|
+
}
|
|
362
|
+
function assertEmpty(predicate, message = `unexpected ${JSON.stringify(predicate)}`) {
|
|
363
|
+
assert(!predicate, message);
|
|
330
364
|
}
|
|
331
365
|
|
|
332
366
|
class NullDrop extends Drop {
|
|
@@ -376,6 +410,9 @@ class EmptyDrop extends Drop {
|
|
|
376
410
|
valueOf() {
|
|
377
411
|
return '';
|
|
378
412
|
}
|
|
413
|
+
static is(value) {
|
|
414
|
+
return value instanceof EmptyDrop;
|
|
415
|
+
}
|
|
379
416
|
}
|
|
380
417
|
|
|
381
418
|
class BlankDrop extends EmptyDrop {
|
|
@@ -388,6 +425,9 @@ class BlankDrop extends EmptyDrop {
|
|
|
388
425
|
return /^\s*$/.test(value);
|
|
389
426
|
return super.equals(value);
|
|
390
427
|
}
|
|
428
|
+
static is(value) {
|
|
429
|
+
return value instanceof BlankDrop;
|
|
430
|
+
}
|
|
391
431
|
}
|
|
392
432
|
|
|
393
433
|
class ForloopDrop extends Drop {
|
|
@@ -440,7 +480,12 @@ class BlockDrop extends Drop {
|
|
|
440
480
|
}
|
|
441
481
|
|
|
442
482
|
function isComparable(arg) {
|
|
443
|
-
return arg &&
|
|
483
|
+
return (arg &&
|
|
484
|
+
isFunction(arg.equals) &&
|
|
485
|
+
isFunction(arg.gt) &&
|
|
486
|
+
isFunction(arg.geq) &&
|
|
487
|
+
isFunction(arg.lt) &&
|
|
488
|
+
isFunction(arg.leq));
|
|
444
489
|
}
|
|
445
490
|
|
|
446
491
|
const nil = new NullDrop();
|
|
@@ -560,18 +605,6 @@ function toValueSync(val) {
|
|
|
560
605
|
}
|
|
561
606
|
|
|
562
607
|
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
608
|
// prototype extensions
|
|
576
609
|
function daysInMonth(d) {
|
|
577
610
|
const feb = isLeapYear(d) ? 29 : 28;
|
|
@@ -626,19 +659,7 @@ const padWidths = {
|
|
|
626
659
|
U: 2,
|
|
627
660
|
W: 2
|
|
628
661
|
};
|
|
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
|
-
};
|
|
662
|
+
const padSpaceChars = new Set('aAbBceklpP');
|
|
642
663
|
function getTimezoneOffset(d, opts) {
|
|
643
664
|
const nOffset = Math.abs(d.getTimezoneOffset());
|
|
644
665
|
const h = Math.floor(nOffset / 60);
|
|
@@ -649,10 +670,10 @@ function getTimezoneOffset(d, opts) {
|
|
|
649
670
|
padStart(m, 2, '0');
|
|
650
671
|
}
|
|
651
672
|
const formatCodes = {
|
|
652
|
-
a: (d) =>
|
|
653
|
-
A: (d) =>
|
|
654
|
-
b: (d) =>
|
|
655
|
-
B: (d) =>
|
|
673
|
+
a: (d) => d.getShortWeekdayName(),
|
|
674
|
+
A: (d) => d.getLongWeekdayName(),
|
|
675
|
+
b: (d) => d.getShortMonthName(),
|
|
676
|
+
B: (d) => d.getLongMonthName(),
|
|
656
677
|
c: (d) => d.toLocaleString(),
|
|
657
678
|
C: (d) => century(d),
|
|
658
679
|
d: (d) => d.getDate(),
|
|
@@ -684,12 +705,7 @@ const formatCodes = {
|
|
|
684
705
|
y: (d) => d.getFullYear().toString().slice(2, 4),
|
|
685
706
|
Y: (d) => d.getFullYear(),
|
|
686
707
|
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
|
-
},
|
|
708
|
+
Z: (d, opts) => d.getTimeZoneName() || getTimezoneOffset(d, opts),
|
|
693
709
|
't': () => '\t',
|
|
694
710
|
'n': () => '\n',
|
|
695
711
|
'%': () => '%'
|
|
@@ -715,7 +731,7 @@ function format(d, match) {
|
|
|
715
731
|
for (const flag of flagStr)
|
|
716
732
|
flags[flag] = true;
|
|
717
733
|
let ret = String(convert(d, { flags, width, modifier }));
|
|
718
|
-
let padChar =
|
|
734
|
+
let padChar = padSpaceChars.has(conversion) ? ' ' : '0';
|
|
719
735
|
let padWidth = width || padWidths[conversion] || 0;
|
|
720
736
|
if (flags['^'])
|
|
721
737
|
ret = ret.toUpperCase();
|
|
@@ -730,9 +746,26 @@ function format(d, match) {
|
|
|
730
746
|
return padStart(ret, padWidth, padChar);
|
|
731
747
|
}
|
|
732
748
|
|
|
749
|
+
function getDateTimeFormat() {
|
|
750
|
+
return (typeof Intl !== 'undefined' ? Intl.DateTimeFormat : undefined);
|
|
751
|
+
}
|
|
752
|
+
|
|
733
753
|
// one minute in milliseconds
|
|
734
754
|
const OneMinute = 60000;
|
|
735
|
-
|
|
755
|
+
/**
|
|
756
|
+
* Need support both ISO8601 and RFC2822 as in major browsers & NodeJS
|
|
757
|
+
* RFC2822: https://datatracker.ietf.org/doc/html/rfc2822#section-3.3
|
|
758
|
+
*/
|
|
759
|
+
const TIMEZONE_PATTERN = /([zZ]|([+-])(\d{2}):?(\d{2}))$/;
|
|
760
|
+
const monthNames = [
|
|
761
|
+
'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
|
|
762
|
+
'September', 'October', 'November', 'December'
|
|
763
|
+
];
|
|
764
|
+
const monthNamesShort = monthNames.map(name => name.slice(0, 3));
|
|
765
|
+
const dayNames = [
|
|
766
|
+
'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'
|
|
767
|
+
];
|
|
768
|
+
const dayNamesShort = dayNames.map(name => name.slice(0, 3));
|
|
736
769
|
/**
|
|
737
770
|
* A date implementation with timezone info, just like Ruby date
|
|
738
771
|
*
|
|
@@ -740,12 +773,16 @@ const ISO8601_TIMEZONE_PATTERN = /([zZ]|([+-])(\d{2}):(\d{2}))$/;
|
|
|
740
773
|
* - create a Date offset by it's timezone difference, avoiding overriding a bunch of methods
|
|
741
774
|
* - rewrite getTimezoneOffset() to trick strftime
|
|
742
775
|
*/
|
|
743
|
-
class
|
|
744
|
-
constructor(init, timezone) {
|
|
745
|
-
this.
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
this.
|
|
776
|
+
class LiquidDate {
|
|
777
|
+
constructor(init, locale, timezone) {
|
|
778
|
+
this.locale = locale;
|
|
779
|
+
this.DateTimeFormat = getDateTimeFormat();
|
|
780
|
+
this.date = new Date(init);
|
|
781
|
+
this.timezoneFixed = timezone !== undefined;
|
|
782
|
+
if (timezone === undefined) {
|
|
783
|
+
timezone = this.date.getTimezoneOffset();
|
|
784
|
+
}
|
|
785
|
+
this.timezoneOffset = isString(timezone) ? LiquidDate.getTimezoneOffset(timezone, this.date) : timezone;
|
|
749
786
|
this.timezoneName = isString(timezone) ? timezone : '';
|
|
750
787
|
const diff = (this.date.getTimezoneOffset() - this.timezoneOffset) * OneMinute;
|
|
751
788
|
const time = this.date.getTime() + diff;
|
|
@@ -793,8 +830,34 @@ class TimezoneDate {
|
|
|
793
830
|
getTimezoneOffset() {
|
|
794
831
|
return this.timezoneOffset;
|
|
795
832
|
}
|
|
796
|
-
|
|
797
|
-
|
|
833
|
+
getTimeZoneName() {
|
|
834
|
+
if (this.timezoneFixed)
|
|
835
|
+
return this.timezoneName;
|
|
836
|
+
if (!this.DateTimeFormat)
|
|
837
|
+
return;
|
|
838
|
+
return this.DateTimeFormat().resolvedOptions().timeZone;
|
|
839
|
+
}
|
|
840
|
+
getLongMonthName() {
|
|
841
|
+
var _a;
|
|
842
|
+
return (_a = this.format({ month: 'long' })) !== null && _a !== void 0 ? _a : monthNames[this.getMonth()];
|
|
843
|
+
}
|
|
844
|
+
getShortMonthName() {
|
|
845
|
+
var _a;
|
|
846
|
+
return (_a = this.format({ month: 'short' })) !== null && _a !== void 0 ? _a : monthNamesShort[this.getMonth()];
|
|
847
|
+
}
|
|
848
|
+
getLongWeekdayName() {
|
|
849
|
+
var _a;
|
|
850
|
+
return (_a = this.format({ weekday: 'long' })) !== null && _a !== void 0 ? _a : dayNames[this.displayDate.getDay()];
|
|
851
|
+
}
|
|
852
|
+
getShortWeekdayName() {
|
|
853
|
+
var _a;
|
|
854
|
+
return (_a = this.format({ weekday: 'short' })) !== null && _a !== void 0 ? _a : dayNamesShort[this.displayDate.getDay()];
|
|
855
|
+
}
|
|
856
|
+
valid() {
|
|
857
|
+
return !isNaN(this.getTime());
|
|
858
|
+
}
|
|
859
|
+
format(options) {
|
|
860
|
+
return this.DateTimeFormat && this.DateTimeFormat(this.locale, options).format(this.displayDate);
|
|
798
861
|
}
|
|
799
862
|
/**
|
|
800
863
|
* Create a Date object fixed to it's declared Timezone. Both
|
|
@@ -809,21 +872,21 @@ class TimezoneDate {
|
|
|
809
872
|
* we create a different Date to trick strftime, it's both simpler and more performant.
|
|
810
873
|
* Given that a template is expected to be parsed fewer times than rendered.
|
|
811
874
|
*/
|
|
812
|
-
static createDateFixedToTimezone(dateString) {
|
|
813
|
-
const m = dateString.match(
|
|
875
|
+
static createDateFixedToTimezone(dateString, locale) {
|
|
876
|
+
const m = dateString.match(TIMEZONE_PATTERN);
|
|
814
877
|
// representing a UTC timestamp
|
|
815
878
|
if (m && m[1] === 'Z') {
|
|
816
|
-
return new
|
|
879
|
+
return new LiquidDate(+new Date(dateString), locale, 0);
|
|
817
880
|
}
|
|
818
881
|
// has a timezone specified
|
|
819
882
|
if (m && m[2] && m[3] && m[4]) {
|
|
820
883
|
const [, , sign, hours, minutes] = m;
|
|
821
884
|
const offset = (sign === '+' ? -1 : 1) * (parseInt(hours, 10) * 60 + parseInt(minutes, 10));
|
|
822
|
-
return new
|
|
885
|
+
return new LiquidDate(+new Date(dateString), locale, offset);
|
|
823
886
|
}
|
|
824
|
-
return new
|
|
887
|
+
return new LiquidDate(dateString, locale);
|
|
825
888
|
}
|
|
826
|
-
static getTimezoneOffset(timezoneName, date
|
|
889
|
+
static getTimezoneOffset(timezoneName, date) {
|
|
827
890
|
const localDateString = date.toLocaleString('en-US', { timeZone: timezoneName });
|
|
828
891
|
const utcDateString = date.toLocaleString('en-US', { timeZone: 'UTC' });
|
|
829
892
|
const localDate = new Date(localDateString);
|
|
@@ -832,6 +895,23 @@ class TimezoneDate {
|
|
|
832
895
|
}
|
|
833
896
|
}
|
|
834
897
|
|
|
898
|
+
class Limiter {
|
|
899
|
+
constructor(resource, limit) {
|
|
900
|
+
this.base = 0;
|
|
901
|
+
this.message = `${resource} limit exceeded`;
|
|
902
|
+
this.limit = limit;
|
|
903
|
+
}
|
|
904
|
+
use(count) {
|
|
905
|
+
count = toNumber(count);
|
|
906
|
+
assert(this.base + count <= this.limit, this.message);
|
|
907
|
+
this.base += count;
|
|
908
|
+
}
|
|
909
|
+
check(count) {
|
|
910
|
+
count = toNumber(count);
|
|
911
|
+
assert(count <= this.limit, this.message);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
|
|
835
915
|
class DelimitedToken extends Token {
|
|
836
916
|
constructor(kind, [contentBegin, contentEnd], input, begin, end, trimLeft, trimRight, file) {
|
|
837
917
|
super(kind, input, begin, end, file);
|
|
@@ -912,16 +992,6 @@ class IdentifierToken extends Token {
|
|
|
912
992
|
this.file = file;
|
|
913
993
|
this.content = this.getText();
|
|
914
994
|
}
|
|
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
995
|
}
|
|
926
996
|
|
|
927
997
|
class LiteralToken extends Token {
|
|
@@ -1089,7 +1159,9 @@ class LiquidTagToken extends DelimitedToken {
|
|
|
1089
1159
|
this.name = this.tokenizer.readTagName();
|
|
1090
1160
|
this.tokenizer.assert(this.name, 'illegal liquid tag syntax');
|
|
1091
1161
|
this.tokenizer.skipBlank();
|
|
1092
|
-
|
|
1162
|
+
}
|
|
1163
|
+
get args() {
|
|
1164
|
+
return this.tokenizer.input.slice(this.tokenizer.p, this.contentRange[1]);
|
|
1093
1165
|
}
|
|
1094
1166
|
}
|
|
1095
1167
|
|
|
@@ -1110,6 +1182,15 @@ class FilteredValueToken extends Token {
|
|
|
1110
1182
|
}
|
|
1111
1183
|
}
|
|
1112
1184
|
|
|
1185
|
+
const polyfill = {
|
|
1186
|
+
now: () => Date.now()
|
|
1187
|
+
};
|
|
1188
|
+
function getPerformance() {
|
|
1189
|
+
return (typeof global === 'object' && global.performance) ||
|
|
1190
|
+
(typeof window === 'object' && window.performance) ||
|
|
1191
|
+
polyfill;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1113
1194
|
class SimpleEmitter {
|
|
1114
1195
|
constructor() {
|
|
1115
1196
|
this.buffer = '';
|
|
@@ -1157,20 +1238,28 @@ class Render {
|
|
|
1157
1238
|
if (!emitter) {
|
|
1158
1239
|
emitter = ctx.opts.keepOutputType ? new KeepingTypeEmitter() : new SimpleEmitter();
|
|
1159
1240
|
}
|
|
1241
|
+
const errors = [];
|
|
1160
1242
|
for (const tpl of templates) {
|
|
1243
|
+
ctx.renderLimit.check(getPerformance().now());
|
|
1161
1244
|
try {
|
|
1162
1245
|
// if tpl.render supports emitter, it'll return empty `html`
|
|
1163
1246
|
const html = yield tpl.render(ctx, emitter);
|
|
1164
1247
|
// if not, it'll return an `html`, write to the emitter for it
|
|
1165
1248
|
html && emitter.write(html);
|
|
1166
|
-
if (
|
|
1249
|
+
if (ctx.breakCalled || ctx.continueCalled)
|
|
1167
1250
|
break;
|
|
1168
1251
|
}
|
|
1169
1252
|
catch (e) {
|
|
1170
|
-
const err =
|
|
1171
|
-
|
|
1253
|
+
const err = LiquidError.is(e) ? e : new RenderError(e, tpl);
|
|
1254
|
+
if (ctx.opts.catchAllErrors)
|
|
1255
|
+
errors.push(err);
|
|
1256
|
+
else
|
|
1257
|
+
throw err;
|
|
1172
1258
|
}
|
|
1173
1259
|
}
|
|
1260
|
+
if (errors.length) {
|
|
1261
|
+
throw new LiquidErrors(errors);
|
|
1262
|
+
}
|
|
1174
1263
|
return emitter.buffer;
|
|
1175
1264
|
}
|
|
1176
1265
|
}
|
|
@@ -1241,6 +1330,7 @@ function evalQuotedToken(token) {
|
|
|
1241
1330
|
function* evalRangeToken(token, ctx) {
|
|
1242
1331
|
const low = yield evalToken(token.lhs, ctx);
|
|
1243
1332
|
const high = yield evalToken(token.rhs, ctx);
|
|
1333
|
+
ctx.memoryLimit.use(high - low + 1);
|
|
1244
1334
|
return range(+low, +high + 1);
|
|
1245
1335
|
}
|
|
1246
1336
|
function* toPostfix(tokens) {
|
|
@@ -1264,6 +1354,7 @@ function isTruthy(val, ctx) {
|
|
|
1264
1354
|
return !isFalsy(val, ctx);
|
|
1265
1355
|
}
|
|
1266
1356
|
function isFalsy(val, ctx) {
|
|
1357
|
+
val = toValue(val);
|
|
1267
1358
|
if (ctx.opts.jsTruthy) {
|
|
1268
1359
|
return !val;
|
|
1269
1360
|
}
|
|
@@ -1331,6 +1422,9 @@ function arrayEquals(lhs, rhs) {
|
|
|
1331
1422
|
if (lhs.length !== rhs.length)
|
|
1332
1423
|
return false;
|
|
1333
1424
|
return !lhs.some((value, i) => !equals(value, rhs[i]));
|
|
1425
|
+
}
|
|
1426
|
+
function arrayIncludes(arr, item) {
|
|
1427
|
+
return arr.some(value => equals(value, item));
|
|
1334
1428
|
}
|
|
1335
1429
|
|
|
1336
1430
|
class Node {
|
|
@@ -1522,22 +1616,30 @@ const unescapeMap = {
|
|
|
1522
1616
|
''': "'"
|
|
1523
1617
|
};
|
|
1524
1618
|
function escape(str) {
|
|
1525
|
-
|
|
1619
|
+
str = stringify(str);
|
|
1620
|
+
this.context.memoryLimit.use(str.length);
|
|
1621
|
+
return str.replace(/&|<|>|"|'/g, m => escapeMap[m]);
|
|
1526
1622
|
}
|
|
1527
1623
|
function xml_escape(str) {
|
|
1528
|
-
return escape(str);
|
|
1624
|
+
return escape.call(this, str);
|
|
1529
1625
|
}
|
|
1530
1626
|
function unescape(str) {
|
|
1531
|
-
|
|
1627
|
+
str = stringify(str);
|
|
1628
|
+
this.context.memoryLimit.use(str.length);
|
|
1629
|
+
return str.replace(/&(amp|lt|gt|#34|#39);/g, m => unescapeMap[m]);
|
|
1532
1630
|
}
|
|
1533
1631
|
function escape_once(str) {
|
|
1534
|
-
return escape(unescape(
|
|
1632
|
+
return escape.call(this, unescape.call(this, str));
|
|
1535
1633
|
}
|
|
1536
1634
|
function newline_to_br(v) {
|
|
1537
|
-
|
|
1635
|
+
const str = stringify(v);
|
|
1636
|
+
this.context.memoryLimit.use(str.length);
|
|
1637
|
+
return str.replace(/\r?\n/gm, '<br />\n');
|
|
1538
1638
|
}
|
|
1539
1639
|
function strip_html(v) {
|
|
1540
|
-
|
|
1640
|
+
const str = stringify(v);
|
|
1641
|
+
this.context.memoryLimit.use(str.length);
|
|
1642
|
+
return str.replace(/<script[\s\S]*?<\/script>|<style[\s\S]*?<\/style>|<.*?>|<!--[\s\S]*?-->/g, '');
|
|
1541
1643
|
}
|
|
1542
1644
|
|
|
1543
1645
|
var htmlFilters = /*#__PURE__*/Object.freeze({
|
|
@@ -1549,18 +1651,68 @@ var htmlFilters = /*#__PURE__*/Object.freeze({
|
|
|
1549
1651
|
strip_html: strip_html
|
|
1550
1652
|
});
|
|
1551
1653
|
|
|
1654
|
+
class MapFS {
|
|
1655
|
+
constructor(mapping) {
|
|
1656
|
+
this.mapping = mapping;
|
|
1657
|
+
this.sep = '/';
|
|
1658
|
+
}
|
|
1659
|
+
exists(filepath) {
|
|
1660
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1661
|
+
return this.existsSync(filepath);
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1664
|
+
existsSync(filepath) {
|
|
1665
|
+
return !isNil(this.mapping[filepath]);
|
|
1666
|
+
}
|
|
1667
|
+
readFile(filepath) {
|
|
1668
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1669
|
+
return this.readFileSync(filepath);
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
readFileSync(filepath) {
|
|
1673
|
+
const content = this.mapping[filepath];
|
|
1674
|
+
if (isNil(content))
|
|
1675
|
+
throw new Error(`ENOENT: ${filepath}`);
|
|
1676
|
+
return content;
|
|
1677
|
+
}
|
|
1678
|
+
dirname(filepath) {
|
|
1679
|
+
const segments = filepath.split(this.sep);
|
|
1680
|
+
segments.pop();
|
|
1681
|
+
return segments.join(this.sep);
|
|
1682
|
+
}
|
|
1683
|
+
resolve(dir, file, ext) {
|
|
1684
|
+
file += ext;
|
|
1685
|
+
if (dir === '.')
|
|
1686
|
+
return file;
|
|
1687
|
+
const segments = dir.split(/\/+/);
|
|
1688
|
+
for (const segment of file.split(this.sep)) {
|
|
1689
|
+
if (segment === '.' || segment === '')
|
|
1690
|
+
continue;
|
|
1691
|
+
else if (segment === '..') {
|
|
1692
|
+
if (segments.length > 1 || segments[0] !== '')
|
|
1693
|
+
segments.pop();
|
|
1694
|
+
}
|
|
1695
|
+
else
|
|
1696
|
+
segments.push(segment);
|
|
1697
|
+
}
|
|
1698
|
+
return segments.join(this.sep);
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1552
1702
|
const defaultOptions = {
|
|
1553
1703
|
root: ['.'],
|
|
1554
1704
|
layouts: ['.'],
|
|
1555
1705
|
partials: ['.'],
|
|
1556
1706
|
relativeReference: true,
|
|
1557
1707
|
jekyllInclude: false,
|
|
1708
|
+
keyValueSeparator: ':',
|
|
1558
1709
|
cache: undefined,
|
|
1559
1710
|
extname: '',
|
|
1560
1711
|
fs: fs,
|
|
1561
1712
|
dynamicPartials: true,
|
|
1562
1713
|
jsTruthy: false,
|
|
1563
1714
|
dateFormat: '%A, %B %-e, %Y at %-l:%M %P %z',
|
|
1715
|
+
locale: '',
|
|
1564
1716
|
trimTagRight: false,
|
|
1565
1717
|
trimTagLeft: false,
|
|
1566
1718
|
trimOutputRight: false,
|
|
@@ -1577,9 +1729,13 @@ const defaultOptions = {
|
|
|
1577
1729
|
lenientIf: false,
|
|
1578
1730
|
globals: {},
|
|
1579
1731
|
keepOutputType: false,
|
|
1580
|
-
operators: defaultOperators
|
|
1732
|
+
operators: defaultOperators,
|
|
1733
|
+
memoryLimit: Infinity,
|
|
1734
|
+
parseLimit: Infinity,
|
|
1735
|
+
renderLimit: Infinity
|
|
1581
1736
|
};
|
|
1582
1737
|
function normalize(options) {
|
|
1738
|
+
var _a, _b;
|
|
1583
1739
|
if (options.hasOwnProperty('root')) {
|
|
1584
1740
|
if (!options.hasOwnProperty('partials'))
|
|
1585
1741
|
options.partials = options.root;
|
|
@@ -1605,6 +1761,14 @@ function normalize(options) {
|
|
|
1605
1761
|
options.partials = normalizeDirectoryList(options.partials);
|
|
1606
1762
|
options.layouts = normalizeDirectoryList(options.layouts);
|
|
1607
1763
|
options.outputEscape = options.outputEscape && getOutputEscapeFunction(options.outputEscape);
|
|
1764
|
+
if (!options.locale) {
|
|
1765
|
+
options.locale = (_b = (_a = getDateTimeFormat()) === null || _a === void 0 ? void 0 : _a().resolvedOptions().locale) !== null && _b !== void 0 ? _b : 'en-US';
|
|
1766
|
+
}
|
|
1767
|
+
if (options.templates) {
|
|
1768
|
+
options.fs = new MapFS(options.templates);
|
|
1769
|
+
options.relativeReference = true;
|
|
1770
|
+
options.root = options.partials = options.layouts = '.';
|
|
1771
|
+
}
|
|
1608
1772
|
return options;
|
|
1609
1773
|
}
|
|
1610
1774
|
function getOutputEscapeFunction(nameOrFunction) {
|
|
@@ -1731,9 +1895,7 @@ class Tokenizer {
|
|
|
1731
1895
|
this.skipBlank();
|
|
1732
1896
|
if (this.end())
|
|
1733
1897
|
return null;
|
|
1734
|
-
this.assert(this.
|
|
1735
|
-
this.p++;
|
|
1736
|
-
const begin = this.p;
|
|
1898
|
+
this.assert(this.read() === '|', `expected "|" before filter`);
|
|
1737
1899
|
const name = this.readIdentifier();
|
|
1738
1900
|
if (!name.size()) {
|
|
1739
1901
|
this.assert(this.end(), `expected filter name`);
|
|
@@ -1754,7 +1916,7 @@ class Tokenizer {
|
|
|
1754
1916
|
else {
|
|
1755
1917
|
throw this.error('expected ":" after filter name');
|
|
1756
1918
|
}
|
|
1757
|
-
return new FilterToken(name.getText(), args, this.input, begin, this.p, this.file);
|
|
1919
|
+
return new FilterToken(name.getText(), args, this.input, name.begin, this.p, this.file);
|
|
1758
1920
|
}
|
|
1759
1921
|
readFilterArg() {
|
|
1760
1922
|
const key = this.readValue();
|
|
@@ -1795,7 +1957,7 @@ class Tokenizer {
|
|
|
1795
1957
|
}
|
|
1796
1958
|
return new HTMLToken(this.input, begin, this.p, this.file);
|
|
1797
1959
|
}
|
|
1798
|
-
readTagToken(options
|
|
1960
|
+
readTagToken(options) {
|
|
1799
1961
|
const { file, input } = this;
|
|
1800
1962
|
const begin = this.p;
|
|
1801
1963
|
if (this.readToDelimiter(options.tagDelimiterRight) === -1) {
|
|
@@ -1926,7 +2088,7 @@ class Tokenizer {
|
|
|
1926
2088
|
return;
|
|
1927
2089
|
let value;
|
|
1928
2090
|
this.skipBlank();
|
|
1929
|
-
const sep = jekyllStyle ? '=' : ':';
|
|
2091
|
+
const sep = isString(jekyllStyle) ? jekyllStyle : (jekyllStyle ? '=' : ':');
|
|
1930
2092
|
if (this.peek() === sep) {
|
|
1931
2093
|
++this.p;
|
|
1932
2094
|
value = this.readValue();
|
|
@@ -1942,6 +2104,9 @@ class Tokenizer {
|
|
|
1942
2104
|
end() {
|
|
1943
2105
|
return this.p >= this.N;
|
|
1944
2106
|
}
|
|
2107
|
+
read() {
|
|
2108
|
+
return this.input[this.p++];
|
|
2109
|
+
}
|
|
1945
2110
|
readTo(end) {
|
|
1946
2111
|
while (this.p < this.N) {
|
|
1947
2112
|
++this.p;
|
|
@@ -2165,9 +2330,9 @@ class Tag extends TemplateImpl {
|
|
|
2165
2330
|
* hash['reversed'] === undefined
|
|
2166
2331
|
*/
|
|
2167
2332
|
class Hash {
|
|
2168
|
-
constructor(
|
|
2333
|
+
constructor(input, jekyllStyle) {
|
|
2169
2334
|
this.hash = {};
|
|
2170
|
-
const tokenizer = new Tokenizer(
|
|
2335
|
+
const tokenizer = input instanceof Tokenizer ? input : new Tokenizer(input, {});
|
|
2171
2336
|
for (const hash of tokenizer.readHashes(jekyllStyle)) {
|
|
2172
2337
|
this.hash[hash.name.content] = hash.value;
|
|
2173
2338
|
}
|
|
@@ -2190,7 +2355,7 @@ function createTagClass(options) {
|
|
|
2190
2355
|
}
|
|
2191
2356
|
}
|
|
2192
2357
|
*render(ctx, emitter) {
|
|
2193
|
-
const hash = (yield new Hash(this.token.args).render(ctx));
|
|
2358
|
+
const hash = (yield new Hash(this.token.args, ctx.opts.keyValueSeparator).render(ctx));
|
|
2194
2359
|
return yield options.render.call(this, ctx, emitter, hash);
|
|
2195
2360
|
}
|
|
2196
2361
|
};
|
|
@@ -2201,13 +2366,14 @@ function isKeyValuePair(arr) {
|
|
|
2201
2366
|
}
|
|
2202
2367
|
|
|
2203
2368
|
class Filter {
|
|
2204
|
-
constructor(
|
|
2205
|
-
this.
|
|
2369
|
+
constructor(token, options, liquid) {
|
|
2370
|
+
this.token = token;
|
|
2371
|
+
this.name = token.name;
|
|
2206
2372
|
this.handler = isFunction(options)
|
|
2207
2373
|
? options
|
|
2208
2374
|
: (isFunction(options === null || options === void 0 ? void 0 : options.handler) ? options.handler : identify);
|
|
2209
2375
|
this.raw = !isFunction(options) && !!(options === null || options === void 0 ? void 0 : options.raw);
|
|
2210
|
-
this.args = args;
|
|
2376
|
+
this.args = token.args;
|
|
2211
2377
|
this.liquid = liquid;
|
|
2212
2378
|
}
|
|
2213
2379
|
*render(value, context) {
|
|
@@ -2218,7 +2384,7 @@ class Filter {
|
|
|
2218
2384
|
else
|
|
2219
2385
|
argv.push(yield evalToken(arg, context));
|
|
2220
2386
|
}
|
|
2221
|
-
return yield this.handler.apply({ context, liquid: this.liquid }, [value, ...argv]);
|
|
2387
|
+
return yield this.handler.apply({ context, token: this.token, liquid: this.liquid }, [value, ...argv]);
|
|
2222
2388
|
}
|
|
2223
2389
|
}
|
|
2224
2390
|
|
|
@@ -2232,7 +2398,7 @@ class Value {
|
|
|
2232
2398
|
? new Tokenizer(input, liquid.options.operators).readFilteredValue()
|
|
2233
2399
|
: input;
|
|
2234
2400
|
this.initial = token.initial;
|
|
2235
|
-
this.filters = token.filters.map(
|
|
2401
|
+
this.filters = token.filters.map(token => new Filter(token, this.getFilter(liquid, token.name), liquid));
|
|
2236
2402
|
}
|
|
2237
2403
|
*value(ctx, lenient) {
|
|
2238
2404
|
lenient = lenient || (ctx.opts.lenientIf && this.filters.length > 0 && this.filters[0].name === 'default');
|
|
@@ -2258,13 +2424,17 @@ class Output extends TemplateImpl {
|
|
|
2258
2424
|
const filters = this.value.filters;
|
|
2259
2425
|
const outputEscape = liquid.options.outputEscape;
|
|
2260
2426
|
if (!((_a = filters[filters.length - 1]) === null || _a === void 0 ? void 0 : _a.raw) && outputEscape) {
|
|
2261
|
-
|
|
2427
|
+
const token = new FilterToken(toString.call(outputEscape), [], '', 0, 0);
|
|
2428
|
+
filters.push(new Filter(token, outputEscape, liquid));
|
|
2262
2429
|
}
|
|
2263
2430
|
}
|
|
2264
2431
|
*render(ctx, emitter) {
|
|
2265
2432
|
const val = yield this.value.value(ctx, false);
|
|
2266
2433
|
emitter.write(val);
|
|
2267
2434
|
}
|
|
2435
|
+
*arguments() {
|
|
2436
|
+
yield this.value;
|
|
2437
|
+
}
|
|
2268
2438
|
}
|
|
2269
2439
|
|
|
2270
2440
|
class HTML extends TemplateImpl {
|
|
@@ -2277,6 +2447,336 @@ class HTML extends TemplateImpl {
|
|
|
2277
2447
|
}
|
|
2278
2448
|
}
|
|
2279
2449
|
|
|
2450
|
+
/**
|
|
2451
|
+
* A variable's segments and location, which can be coerced to a string.
|
|
2452
|
+
*/
|
|
2453
|
+
class Variable {
|
|
2454
|
+
constructor(segments, location) {
|
|
2455
|
+
this.segments = segments;
|
|
2456
|
+
this.location = location;
|
|
2457
|
+
}
|
|
2458
|
+
toString() {
|
|
2459
|
+
return segmentsString(this.segments, true);
|
|
2460
|
+
}
|
|
2461
|
+
/** Return this variable's segments as an array, possibly with nested arrays for nested paths. */
|
|
2462
|
+
toArray() {
|
|
2463
|
+
function* _visit(...segments) {
|
|
2464
|
+
for (const segment of segments) {
|
|
2465
|
+
if (segment instanceof Variable) {
|
|
2466
|
+
yield Array.from(_visit(...segment.segments));
|
|
2467
|
+
}
|
|
2468
|
+
else {
|
|
2469
|
+
yield segment;
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
return Array.from(_visit(...this.segments));
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
/**
|
|
2477
|
+
* Group variables by the string representation of their root segment.
|
|
2478
|
+
*/
|
|
2479
|
+
class VariableMap {
|
|
2480
|
+
constructor() {
|
|
2481
|
+
this.map = new Map();
|
|
2482
|
+
}
|
|
2483
|
+
get(key) {
|
|
2484
|
+
const k = segmentsString([key.segments[0]]);
|
|
2485
|
+
if (!this.map.has(k)) {
|
|
2486
|
+
this.map.set(k, []);
|
|
2487
|
+
}
|
|
2488
|
+
return this.map.get(k);
|
|
2489
|
+
}
|
|
2490
|
+
has(key) {
|
|
2491
|
+
return this.map.has(segmentsString([key.segments[0]]));
|
|
2492
|
+
}
|
|
2493
|
+
push(variable) {
|
|
2494
|
+
this.get(variable).push(variable);
|
|
2495
|
+
}
|
|
2496
|
+
asObject() {
|
|
2497
|
+
return Object.fromEntries(this.map);
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
const defaultStaticAnalysisOptions = {
|
|
2501
|
+
partials: true
|
|
2502
|
+
};
|
|
2503
|
+
function* _analyze(templates, partials, sync) {
|
|
2504
|
+
const variables = new VariableMap();
|
|
2505
|
+
const globals = new VariableMap();
|
|
2506
|
+
const locals = new VariableMap();
|
|
2507
|
+
const rootScope = new DummyScope(new Set());
|
|
2508
|
+
// Names of partial templates that we've already analyzed.
|
|
2509
|
+
const seen = new Set();
|
|
2510
|
+
function updateVariables(variable, scope) {
|
|
2511
|
+
variables.push(variable);
|
|
2512
|
+
const aliased = scope.alias(variable);
|
|
2513
|
+
if (aliased !== undefined) {
|
|
2514
|
+
const root = aliased.segments[0];
|
|
2515
|
+
// TODO: What if a a template renders a rendered template? Do we need scope.parent?
|
|
2516
|
+
if (isString(root) && !rootScope.has(root)) {
|
|
2517
|
+
globals.push(aliased);
|
|
2518
|
+
}
|
|
2519
|
+
}
|
|
2520
|
+
else {
|
|
2521
|
+
const root = variable.segments[0];
|
|
2522
|
+
if (isString(root) && !scope.has(root)) {
|
|
2523
|
+
globals.push(variable);
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
// Recurse for nested Variables
|
|
2527
|
+
for (const segment of variable.segments) {
|
|
2528
|
+
if (segment instanceof Variable) {
|
|
2529
|
+
updateVariables(segment, scope);
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
function* visit(template, scope) {
|
|
2534
|
+
if (template.arguments) {
|
|
2535
|
+
for (const arg of template.arguments()) {
|
|
2536
|
+
for (const variable of extractVariables(arg)) {
|
|
2537
|
+
updateVariables(variable, scope);
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
if (template.localScope) {
|
|
2542
|
+
for (const ident of template.localScope()) {
|
|
2543
|
+
scope.add(ident.content);
|
|
2544
|
+
scope.deleteAlias(ident.content);
|
|
2545
|
+
const [row, col] = ident.getPosition();
|
|
2546
|
+
locals.push(new Variable([ident.content], { row, col, file: ident.file }));
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
if (template.children) {
|
|
2550
|
+
if (template.partialScope) {
|
|
2551
|
+
const partial = template.partialScope();
|
|
2552
|
+
if (partial === undefined) {
|
|
2553
|
+
// Layouts, for example, can have children that are not partials.
|
|
2554
|
+
for (const child of (yield template.children(partials, sync))) {
|
|
2555
|
+
yield visit(child, scope);
|
|
2556
|
+
}
|
|
2557
|
+
return;
|
|
2558
|
+
}
|
|
2559
|
+
if (seen.has(partial.name))
|
|
2560
|
+
return;
|
|
2561
|
+
const partialScopeNames = new Set();
|
|
2562
|
+
const partialScope = partial.isolated
|
|
2563
|
+
? new DummyScope(partialScopeNames)
|
|
2564
|
+
: scope.push(partialScopeNames);
|
|
2565
|
+
for (const name of partial.scope) {
|
|
2566
|
+
if (isString(name)) {
|
|
2567
|
+
partialScopeNames.add(name);
|
|
2568
|
+
}
|
|
2569
|
+
else {
|
|
2570
|
+
const [alias, argument] = name;
|
|
2571
|
+
partialScopeNames.add(alias);
|
|
2572
|
+
const variables = Array.from(extractVariables(argument));
|
|
2573
|
+
if (variables.length) {
|
|
2574
|
+
partialScope.setAlias(alias, variables[0].segments);
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
for (const child of (yield template.children(partials, sync))) {
|
|
2579
|
+
yield visit(child, partialScope);
|
|
2580
|
+
seen.add(partial.name);
|
|
2581
|
+
}
|
|
2582
|
+
partialScope.pop();
|
|
2583
|
+
}
|
|
2584
|
+
else {
|
|
2585
|
+
if (template.blockScope) {
|
|
2586
|
+
scope.push(new Set(template.blockScope()));
|
|
2587
|
+
}
|
|
2588
|
+
for (const child of (yield template.children(partials, sync))) {
|
|
2589
|
+
yield visit(child, scope);
|
|
2590
|
+
}
|
|
2591
|
+
if (template.blockScope) {
|
|
2592
|
+
scope.pop();
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
for (const template of templates) {
|
|
2598
|
+
yield visit(template, rootScope);
|
|
2599
|
+
}
|
|
2600
|
+
return {
|
|
2601
|
+
variables: variables.asObject(),
|
|
2602
|
+
globals: globals.asObject(),
|
|
2603
|
+
locals: locals.asObject()
|
|
2604
|
+
};
|
|
2605
|
+
}
|
|
2606
|
+
/**
|
|
2607
|
+
* Statically analyze a template and report variable usage.
|
|
2608
|
+
*/
|
|
2609
|
+
function analyze(template, options = {}) {
|
|
2610
|
+
const opts = Object.assign(Object.assign({}, defaultStaticAnalysisOptions), options);
|
|
2611
|
+
return toPromise(_analyze(template, opts.partials, false));
|
|
2612
|
+
}
|
|
2613
|
+
/**
|
|
2614
|
+
* Statically analyze a template and report variable usage.
|
|
2615
|
+
*/
|
|
2616
|
+
function analyzeSync(template, options = {}) {
|
|
2617
|
+
const opts = Object.assign(Object.assign({}, defaultStaticAnalysisOptions), options);
|
|
2618
|
+
return toValueSync(_analyze(template, opts.partials, true));
|
|
2619
|
+
}
|
|
2620
|
+
/**
|
|
2621
|
+
* A stack to manage scopes while traversing templates during static analysis.
|
|
2622
|
+
*/
|
|
2623
|
+
class DummyScope {
|
|
2624
|
+
constructor(globals) {
|
|
2625
|
+
this.stack = [{ names: globals, aliases: new Map() }];
|
|
2626
|
+
}
|
|
2627
|
+
/** Return true if `name` is in scope. */
|
|
2628
|
+
has(name) {
|
|
2629
|
+
for (const scope of this.stack) {
|
|
2630
|
+
if (scope.names.has(name)) {
|
|
2631
|
+
return true;
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
return false;
|
|
2635
|
+
}
|
|
2636
|
+
push(scope) {
|
|
2637
|
+
this.stack.push({ names: scope, aliases: new Map() });
|
|
2638
|
+
return this;
|
|
2639
|
+
}
|
|
2640
|
+
pop() {
|
|
2641
|
+
var _a;
|
|
2642
|
+
return (_a = this.stack.pop()) === null || _a === void 0 ? void 0 : _a.names;
|
|
2643
|
+
}
|
|
2644
|
+
// Add a name to the template scope.
|
|
2645
|
+
add(name) {
|
|
2646
|
+
this.stack[0].names.add(name);
|
|
2647
|
+
}
|
|
2648
|
+
/** Return the variable that `variable` aliases, or `variable` if it doesn't alias anything. */
|
|
2649
|
+
alias(variable) {
|
|
2650
|
+
const root = variable.segments[0];
|
|
2651
|
+
if (!isString(root))
|
|
2652
|
+
return undefined;
|
|
2653
|
+
const alias = this.getAlias(root);
|
|
2654
|
+
if (alias === undefined)
|
|
2655
|
+
return undefined;
|
|
2656
|
+
return new Variable([...alias, ...variable.segments.slice(1)], variable.location);
|
|
2657
|
+
}
|
|
2658
|
+
// TODO: `from` could be a path with multiple segments, like `include.x`.
|
|
2659
|
+
setAlias(from, to) {
|
|
2660
|
+
this.stack[this.stack.length - 1].aliases.set(from, to);
|
|
2661
|
+
}
|
|
2662
|
+
deleteAlias(name) {
|
|
2663
|
+
this.stack[this.stack.length - 1].aliases.delete(name);
|
|
2664
|
+
}
|
|
2665
|
+
getAlias(name) {
|
|
2666
|
+
for (const scope of this.stack) {
|
|
2667
|
+
if (scope.aliases.has(name)) {
|
|
2668
|
+
return scope.aliases.get(name);
|
|
2669
|
+
}
|
|
2670
|
+
// If a scope has defined `name`, then it masks aliases in parent scopes.
|
|
2671
|
+
if (scope.names.has(name)) {
|
|
2672
|
+
return undefined;
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
return undefined;
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
function* extractVariables(value) {
|
|
2679
|
+
if (isValueToken(value)) {
|
|
2680
|
+
yield* extractValueTokenVariables(value);
|
|
2681
|
+
}
|
|
2682
|
+
else if (value instanceof Value) {
|
|
2683
|
+
yield* extractFilteredValueVariables(value);
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
function* extractFilteredValueVariables(value) {
|
|
2687
|
+
for (const token of value.initial.postfix) {
|
|
2688
|
+
if (isValueToken(token)) {
|
|
2689
|
+
yield* extractValueTokenVariables(token);
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
for (const filter of value.filters) {
|
|
2693
|
+
for (const arg of filter.args) {
|
|
2694
|
+
if (isKeyValuePair(arg) && arg[1]) {
|
|
2695
|
+
yield* extractValueTokenVariables(arg[1]);
|
|
2696
|
+
}
|
|
2697
|
+
else if (isValueToken(arg)) {
|
|
2698
|
+
yield* extractValueTokenVariables(arg);
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2703
|
+
function* extractValueTokenVariables(token) {
|
|
2704
|
+
if (isRangeToken(token)) {
|
|
2705
|
+
yield* extractValueTokenVariables(token.lhs);
|
|
2706
|
+
yield* extractValueTokenVariables(token.rhs);
|
|
2707
|
+
}
|
|
2708
|
+
else if (isPropertyAccessToken(token)) {
|
|
2709
|
+
yield extractPropertyAccessVariable(token);
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
function extractPropertyAccessVariable(token) {
|
|
2713
|
+
const segments = [];
|
|
2714
|
+
// token is not guaranteed to have `file` set. We'll try to get it from a prop if not.
|
|
2715
|
+
let file = token.file;
|
|
2716
|
+
// Here we're flattening the first segment of a path if it is a nested path.
|
|
2717
|
+
const root = token.props[0];
|
|
2718
|
+
file = file || root.file;
|
|
2719
|
+
if (isQuotedToken(root) || isNumberToken(root) || isWordToken(root)) {
|
|
2720
|
+
segments.push(root.content);
|
|
2721
|
+
}
|
|
2722
|
+
else if (isPropertyAccessToken(root)) {
|
|
2723
|
+
// Flatten paths that start with a nested path.
|
|
2724
|
+
segments.push(...extractPropertyAccessVariable(root).segments);
|
|
2725
|
+
}
|
|
2726
|
+
for (const prop of token.props.slice(1)) {
|
|
2727
|
+
file = file || prop.file;
|
|
2728
|
+
if (isQuotedToken(prop) || isNumberToken(prop) || isWordToken(prop)) {
|
|
2729
|
+
segments.push(prop.content);
|
|
2730
|
+
}
|
|
2731
|
+
else if (isPropertyAccessToken(prop)) {
|
|
2732
|
+
segments.push(extractPropertyAccessVariable(prop));
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
const [row, col] = token.getPosition();
|
|
2736
|
+
return new Variable(segments, {
|
|
2737
|
+
row,
|
|
2738
|
+
col,
|
|
2739
|
+
file
|
|
2740
|
+
});
|
|
2741
|
+
}
|
|
2742
|
+
// This is used to detect segments that can be represented with dot notation
|
|
2743
|
+
// when creating a string representation of VariableSegments.
|
|
2744
|
+
const RE_PROPERTY = /^[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*$/;
|
|
2745
|
+
/**
|
|
2746
|
+
* Return a string representation of segments using dot notation where possible.
|
|
2747
|
+
* @param segments - The property names and array indices that make up a path to a variable.
|
|
2748
|
+
* @param bracketedRoot - If false (the default), don't surround the root segment with square brackets.
|
|
2749
|
+
*/
|
|
2750
|
+
function segmentsString(segments, bracketedRoot = false) {
|
|
2751
|
+
const buf = [];
|
|
2752
|
+
const root = segments[0];
|
|
2753
|
+
if (isString(root)) {
|
|
2754
|
+
if (!bracketedRoot || root.match(RE_PROPERTY)) {
|
|
2755
|
+
buf.push(`${root}`);
|
|
2756
|
+
}
|
|
2757
|
+
else {
|
|
2758
|
+
buf.push(`['${root}']`);
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
for (const segment of segments.slice(1)) {
|
|
2762
|
+
if (segment instanceof Variable) {
|
|
2763
|
+
buf.push(`[${segmentsString(segment.segments)}]`);
|
|
2764
|
+
}
|
|
2765
|
+
else if (isString(segment)) {
|
|
2766
|
+
if (segment.match(RE_PROPERTY)) {
|
|
2767
|
+
buf.push(`.${segment}`);
|
|
2768
|
+
}
|
|
2769
|
+
else {
|
|
2770
|
+
buf.push(`['${segment}']`);
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
else {
|
|
2774
|
+
buf.push(`[${segment}]`);
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
return buf.join('');
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2280
2780
|
var LookupType;
|
|
2281
2781
|
(function (LookupType) {
|
|
2282
2782
|
LookupType["Partials"] = "partials";
|
|
@@ -2293,7 +2793,7 @@ class Loader {
|
|
|
2293
2793
|
this.shouldLoadRelative = (referencedFile) => rRelativePath.test(referencedFile);
|
|
2294
2794
|
}
|
|
2295
2795
|
else {
|
|
2296
|
-
this.shouldLoadRelative = (
|
|
2796
|
+
this.shouldLoadRelative = (_referencedFile) => false;
|
|
2297
2797
|
}
|
|
2298
2798
|
this.contains = this.options.fs.contains || (() => true);
|
|
2299
2799
|
}
|
|
@@ -2350,8 +2850,11 @@ class Parser {
|
|
|
2350
2850
|
this.fs = this.liquid.options.fs;
|
|
2351
2851
|
this.parseFile = this.cache ? this._parseFileCached : this._parseFile;
|
|
2352
2852
|
this.loader = new Loader(this.liquid.options);
|
|
2853
|
+
this.parseLimit = new Limiter('parse length', liquid.options.parseLimit);
|
|
2353
2854
|
}
|
|
2354
2855
|
parse(html, filepath) {
|
|
2856
|
+
html = String(html);
|
|
2857
|
+
this.parseLimit.use(html.length);
|
|
2355
2858
|
const tokenizer = new Tokenizer(html, this.liquid.options.operators, filepath);
|
|
2356
2859
|
const tokens = tokenizer.readTopLevelTokens(this.liquid.options);
|
|
2357
2860
|
return this.parseTokens(tokens);
|
|
@@ -2359,9 +2862,20 @@ class Parser {
|
|
|
2359
2862
|
parseTokens(tokens) {
|
|
2360
2863
|
let token;
|
|
2361
2864
|
const templates = [];
|
|
2865
|
+
const errors = [];
|
|
2362
2866
|
while ((token = tokens.shift())) {
|
|
2363
|
-
|
|
2867
|
+
try {
|
|
2868
|
+
templates.push(this.parseToken(token, tokens));
|
|
2869
|
+
}
|
|
2870
|
+
catch (err) {
|
|
2871
|
+
if (this.liquid.options.catchAllErrors)
|
|
2872
|
+
errors.push(err);
|
|
2873
|
+
else
|
|
2874
|
+
throw err;
|
|
2875
|
+
}
|
|
2364
2876
|
}
|
|
2877
|
+
if (errors.length)
|
|
2878
|
+
throw new LiquidErrors(errors);
|
|
2365
2879
|
return templates;
|
|
2366
2880
|
}
|
|
2367
2881
|
parseToken(token, remainTokens) {
|
|
@@ -2369,7 +2883,7 @@ class Parser {
|
|
|
2369
2883
|
if (isTagToken(token)) {
|
|
2370
2884
|
const TagClass = this.liquid.tags[token.name];
|
|
2371
2885
|
assert(TagClass, `tag "${token.name}" not found`);
|
|
2372
|
-
return new TagClass(token, remainTokens, this.liquid);
|
|
2886
|
+
return new TagClass(token, remainTokens, this.liquid, this);
|
|
2373
2887
|
}
|
|
2374
2888
|
if (isOutputToken(token)) {
|
|
2375
2889
|
return new Output(token, this.liquid);
|
|
@@ -2407,7 +2921,7 @@ class Parser {
|
|
|
2407
2921
|
}
|
|
2408
2922
|
*_parseFile(file, sync, type = LookupType.Root, currentFile) {
|
|
2409
2923
|
const filepath = yield this.loader.lookup(file, type, sync, currentFile);
|
|
2410
|
-
return this.
|
|
2924
|
+
return this.parse(sync ? this.fs.readFileSync(filepath) : yield this.fs.readFile(filepath), filepath);
|
|
2411
2925
|
}
|
|
2412
2926
|
}
|
|
2413
2927
|
|
|
@@ -2462,6 +2976,10 @@ function isWordToken(val) {
|
|
|
2462
2976
|
function isRangeToken(val) {
|
|
2463
2977
|
return getKind(val) === TokenKind.Range;
|
|
2464
2978
|
}
|
|
2979
|
+
function isValueToken(val) {
|
|
2980
|
+
// valueTokenBitMask = TokenKind.Number | TokenKind.Literal | TokenKind.Quoted | TokenKind.PropertyAccess | TokenKind.Range
|
|
2981
|
+
return (getKind(val) & 1667) > 0;
|
|
2982
|
+
}
|
|
2465
2983
|
function getKind(val) {
|
|
2466
2984
|
return val ? val.kind : -1;
|
|
2467
2985
|
}
|
|
@@ -2478,24 +2996,29 @@ var typeGuards = /*#__PURE__*/Object.freeze({
|
|
|
2478
2996
|
isNumberToken: isNumberToken,
|
|
2479
2997
|
isPropertyAccessToken: isPropertyAccessToken,
|
|
2480
2998
|
isWordToken: isWordToken,
|
|
2481
|
-
isRangeToken: isRangeToken
|
|
2999
|
+
isRangeToken: isRangeToken,
|
|
3000
|
+
isValueToken: isValueToken
|
|
2482
3001
|
});
|
|
2483
3002
|
|
|
2484
3003
|
class Context {
|
|
2485
|
-
constructor(env = {}, opts = defaultOptions, renderOptions = {}) {
|
|
2486
|
-
var _a, _b, _c;
|
|
3004
|
+
constructor(env = {}, opts = defaultOptions, renderOptions = {}, { memoryLimit, renderLimit } = {}) {
|
|
3005
|
+
var _a, _b, _c, _d, _e;
|
|
2487
3006
|
/**
|
|
2488
3007
|
* insert a Context-level empty scope,
|
|
2489
3008
|
* for tags like `{% capture %}` `{% assign %}` to operate
|
|
2490
3009
|
*/
|
|
2491
3010
|
this.scopes = [{}];
|
|
2492
3011
|
this.registers = {};
|
|
3012
|
+
this.breakCalled = false;
|
|
3013
|
+
this.continueCalled = false;
|
|
2493
3014
|
this.sync = !!renderOptions.sync;
|
|
2494
3015
|
this.opts = opts;
|
|
2495
3016
|
this.globals = (_a = renderOptions.globals) !== null && _a !== void 0 ? _a : opts.globals;
|
|
2496
3017
|
this.environments = isObject(env) ? env : Object(env);
|
|
2497
3018
|
this.strictVariables = (_b = renderOptions.strictVariables) !== null && _b !== void 0 ? _b : this.opts.strictVariables;
|
|
2498
3019
|
this.ownPropertyOnly = (_c = renderOptions.ownPropertyOnly) !== null && _c !== void 0 ? _c : opts.ownPropertyOnly;
|
|
3020
|
+
this.memoryLimit = memoryLimit !== null && memoryLimit !== void 0 ? memoryLimit : new Limiter('memory alloc', (_d = renderOptions.memoryLimit) !== null && _d !== void 0 ? _d : opts.memoryLimit);
|
|
3021
|
+
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
3022
|
}
|
|
2500
3023
|
getRegister(key) {
|
|
2501
3024
|
return (this.registers[key] = this.registers[key] || {});
|
|
@@ -2523,7 +3046,7 @@ class Context {
|
|
|
2523
3046
|
return toValueSync(this._get(paths));
|
|
2524
3047
|
}
|
|
2525
3048
|
*_get(paths) {
|
|
2526
|
-
const scope = this.findScope(paths[0]);
|
|
3049
|
+
const scope = this.findScope(paths[0]); // first prop should always be a string
|
|
2527
3050
|
return yield this._getFromScope(scope, paths);
|
|
2528
3051
|
}
|
|
2529
3052
|
/**
|
|
@@ -2552,6 +3075,16 @@ class Context {
|
|
|
2552
3075
|
bottom() {
|
|
2553
3076
|
return this.scopes[0];
|
|
2554
3077
|
}
|
|
3078
|
+
spawn(scope = {}) {
|
|
3079
|
+
return new Context(scope, this.opts, {
|
|
3080
|
+
sync: this.sync,
|
|
3081
|
+
globals: this.globals,
|
|
3082
|
+
strictVariables: this.strictVariables
|
|
3083
|
+
}, {
|
|
3084
|
+
renderLimit: this.renderLimit,
|
|
3085
|
+
memoryLimit: this.memoryLimit
|
|
3086
|
+
});
|
|
3087
|
+
}
|
|
2555
3088
|
findScope(key) {
|
|
2556
3089
|
for (let i = this.scopes.length - 1; i >= 0; i--) {
|
|
2557
3090
|
const candidate = this.scopes[i];
|
|
@@ -2565,6 +3098,7 @@ class Context {
|
|
|
2565
3098
|
}
|
|
2566
3099
|
function readProperty(obj, key, ownPropertyOnly) {
|
|
2567
3100
|
obj = toLiquid(obj);
|
|
3101
|
+
key = toValue(key);
|
|
2568
3102
|
if (isNil(obj))
|
|
2569
3103
|
return obj;
|
|
2570
3104
|
if (isArray(obj) && key < 0)
|
|
@@ -2583,7 +3117,7 @@ function readProperty(obj, key, ownPropertyOnly) {
|
|
|
2583
3117
|
return value;
|
|
2584
3118
|
}
|
|
2585
3119
|
function readJSProperty(obj, key, ownPropertyOnly) {
|
|
2586
|
-
if (ownPropertyOnly && !
|
|
3120
|
+
if (ownPropertyOnly && !hasOwnProperty.call(obj, key) && !(obj instanceof Drop))
|
|
2587
3121
|
return undefined;
|
|
2588
3122
|
return obj[key];
|
|
2589
3123
|
}
|
|
@@ -2598,7 +3132,7 @@ function readLast(obj) {
|
|
|
2598
3132
|
return obj['last'];
|
|
2599
3133
|
}
|
|
2600
3134
|
function readSize(obj) {
|
|
2601
|
-
if (
|
|
3135
|
+
if (hasOwnProperty.call(obj, 'size') || obj['size'] !== undefined)
|
|
2602
3136
|
return obj['size'];
|
|
2603
3137
|
if (isArray(obj) || isString(obj))
|
|
2604
3138
|
return obj.length;
|
|
@@ -2705,13 +3239,25 @@ var urlFilters = /*#__PURE__*/Object.freeze({
|
|
|
2705
3239
|
slugify: slugify
|
|
2706
3240
|
});
|
|
2707
3241
|
|
|
2708
|
-
const join = argumentsToValue((v, arg)
|
|
2709
|
-
const
|
|
2710
|
-
const
|
|
2711
|
-
const
|
|
3242
|
+
const join = argumentsToValue(function (v, arg) {
|
|
3243
|
+
const array = toArray(v);
|
|
3244
|
+
const sep = isNil(arg) ? ' ' : stringify(arg);
|
|
3245
|
+
const complexity = array.length * (1 + sep.length);
|
|
3246
|
+
this.context.memoryLimit.use(complexity);
|
|
3247
|
+
return array.join(sep);
|
|
3248
|
+
});
|
|
3249
|
+
const last$1 = argumentsToValue((v) => isArrayLike(v) ? last(v) : '');
|
|
3250
|
+
const first = argumentsToValue((v) => isArrayLike(v) ? v[0] : '');
|
|
3251
|
+
const reverse = argumentsToValue(function (v) {
|
|
3252
|
+
const array = toArray(v);
|
|
3253
|
+
this.context.memoryLimit.use(array.length);
|
|
3254
|
+
return [...array].reverse();
|
|
3255
|
+
});
|
|
2712
3256
|
function* sort(arr, property) {
|
|
2713
3257
|
const values = [];
|
|
2714
|
-
|
|
3258
|
+
const array = toArray(arr);
|
|
3259
|
+
this.context.memoryLimit.use(array.length);
|
|
3260
|
+
for (const item of array) {
|
|
2715
3261
|
values.push([
|
|
2716
3262
|
item,
|
|
2717
3263
|
property ? yield this.context._getFromScope(item, stringify(property).split('.'), false) : item
|
|
@@ -2728,35 +3274,47 @@ function sort_natural(input, property) {
|
|
|
2728
3274
|
const compare = property === undefined
|
|
2729
3275
|
? caseInsensitiveCompare
|
|
2730
3276
|
: (lhs, rhs) => caseInsensitiveCompare(lhs[propertyString], rhs[propertyString]);
|
|
2731
|
-
|
|
3277
|
+
const array = toArray(input);
|
|
3278
|
+
this.context.memoryLimit.use(array.length);
|
|
3279
|
+
return [...array].sort(compare);
|
|
2732
3280
|
}
|
|
2733
3281
|
const size = (v) => (v && v.length) || 0;
|
|
2734
3282
|
function* map(arr, property) {
|
|
2735
3283
|
const results = [];
|
|
2736
|
-
|
|
3284
|
+
const array = toArray(arr);
|
|
3285
|
+
this.context.memoryLimit.use(array.length);
|
|
3286
|
+
for (const item of array) {
|
|
2737
3287
|
results.push(yield this.context._getFromScope(item, stringify(property), false));
|
|
2738
3288
|
}
|
|
2739
3289
|
return results;
|
|
2740
3290
|
}
|
|
2741
3291
|
function* sum(arr, property) {
|
|
2742
3292
|
let sum = 0;
|
|
2743
|
-
|
|
3293
|
+
const array = toArray(arr);
|
|
3294
|
+
for (const item of array) {
|
|
2744
3295
|
const data = Number(property ? yield this.context._getFromScope(item, stringify(property), false) : item);
|
|
2745
3296
|
sum += Number.isNaN(data) ? 0 : data;
|
|
2746
3297
|
}
|
|
2747
3298
|
return sum;
|
|
2748
3299
|
}
|
|
2749
3300
|
function compact(arr) {
|
|
2750
|
-
|
|
3301
|
+
const array = toArray(arr);
|
|
3302
|
+
this.context.memoryLimit.use(array.length);
|
|
3303
|
+
return array.filter(x => !isNil(toValue(x)));
|
|
2751
3304
|
}
|
|
2752
3305
|
function concat(v, arg = []) {
|
|
2753
|
-
|
|
3306
|
+
const lhs = toArray(v);
|
|
3307
|
+
const rhs = toArray(arg);
|
|
3308
|
+
this.context.memoryLimit.use(lhs.length + rhs.length);
|
|
3309
|
+
return lhs.concat(rhs);
|
|
2754
3310
|
}
|
|
2755
3311
|
function push(v, arg) {
|
|
2756
|
-
return concat(v, [arg]);
|
|
3312
|
+
return concat.call(this, v, [arg]);
|
|
2757
3313
|
}
|
|
2758
3314
|
function unshift(v, arg) {
|
|
2759
|
-
const
|
|
3315
|
+
const array = toArray(v);
|
|
3316
|
+
this.context.memoryLimit.use(array.length);
|
|
3317
|
+
const clone = [...array];
|
|
2760
3318
|
clone.unshift(arg);
|
|
2761
3319
|
return clone;
|
|
2762
3320
|
}
|
|
@@ -2766,7 +3324,9 @@ function pop(v) {
|
|
|
2766
3324
|
return clone;
|
|
2767
3325
|
}
|
|
2768
3326
|
function shift(v) {
|
|
2769
|
-
const
|
|
3327
|
+
const array = toArray(v);
|
|
3328
|
+
this.context.memoryLimit.use(array.length);
|
|
3329
|
+
const clone = [...array];
|
|
2770
3330
|
clone.shift();
|
|
2771
3331
|
return clone;
|
|
2772
3332
|
}
|
|
@@ -2777,26 +3337,33 @@ function slice(v, begin, length = 1) {
|
|
|
2777
3337
|
if (!isArray(v))
|
|
2778
3338
|
v = stringify(v);
|
|
2779
3339
|
begin = begin < 0 ? v.length + begin : begin;
|
|
3340
|
+
this.context.memoryLimit.use(length);
|
|
2780
3341
|
return v.slice(begin, begin + length);
|
|
2781
3342
|
}
|
|
2782
3343
|
function* where(arr, property, expected) {
|
|
2783
3344
|
const values = [];
|
|
2784
3345
|
arr = toArray(arr);
|
|
3346
|
+
this.context.memoryLimit.use(arr.length);
|
|
2785
3347
|
const token = new Tokenizer(stringify(property)).readScopeValue();
|
|
2786
3348
|
for (const item of arr) {
|
|
2787
|
-
values.push(yield evalToken(token,
|
|
3349
|
+
values.push(yield evalToken(token, this.context.spawn(item)));
|
|
2788
3350
|
}
|
|
3351
|
+
const matcher = this.context.opts.jekyllWhere
|
|
3352
|
+
? (v) => EmptyDrop.is(expected) ? equals(v, expected) : (isArray(v) ? arrayIncludes(v, expected) : equals(v, expected))
|
|
3353
|
+
: (v) => equals(v, expected);
|
|
2789
3354
|
return arr.filter((_, i) => {
|
|
2790
3355
|
if (expected === undefined)
|
|
2791
3356
|
return isTruthy(values[i], this.context);
|
|
2792
|
-
return
|
|
3357
|
+
return matcher(values[i]);
|
|
2793
3358
|
});
|
|
2794
3359
|
}
|
|
2795
3360
|
function* where_exp(arr, itemName, exp) {
|
|
2796
3361
|
const filtered = [];
|
|
2797
3362
|
const keyTemplate = new Value(stringify(exp), this.liquid);
|
|
2798
|
-
|
|
2799
|
-
|
|
3363
|
+
const array = toArray(arr);
|
|
3364
|
+
this.context.memoryLimit.use(array.length);
|
|
3365
|
+
for (const item of array) {
|
|
3366
|
+
const value = yield keyTemplate.value(this.context.spawn({ [itemName]: item }));
|
|
2800
3367
|
if (value)
|
|
2801
3368
|
filtered.push(item);
|
|
2802
3369
|
}
|
|
@@ -2804,10 +3371,11 @@ function* where_exp(arr, itemName, exp) {
|
|
|
2804
3371
|
}
|
|
2805
3372
|
function* group_by(arr, property) {
|
|
2806
3373
|
const map = new Map();
|
|
2807
|
-
arr =
|
|
3374
|
+
arr = toEnumerable(arr);
|
|
2808
3375
|
const token = new Tokenizer(stringify(property)).readScopeValue();
|
|
3376
|
+
this.context.memoryLimit.use(arr.length);
|
|
2809
3377
|
for (const item of arr) {
|
|
2810
|
-
const key = yield evalToken(token,
|
|
3378
|
+
const key = yield evalToken(token, this.context.spawn(item));
|
|
2811
3379
|
if (!map.has(key))
|
|
2812
3380
|
map.set(key, []);
|
|
2813
3381
|
map.get(key).push(item);
|
|
@@ -2817,8 +3385,10 @@ function* group_by(arr, property) {
|
|
|
2817
3385
|
function* group_by_exp(arr, itemName, exp) {
|
|
2818
3386
|
const map = new Map();
|
|
2819
3387
|
const keyTemplate = new Value(stringify(exp), this.liquid);
|
|
2820
|
-
|
|
2821
|
-
|
|
3388
|
+
arr = toEnumerable(arr);
|
|
3389
|
+
this.context.memoryLimit.use(arr.length);
|
|
3390
|
+
for (const item of arr) {
|
|
3391
|
+
const key = yield keyTemplate.value(this.context.spawn({ [itemName]: item }));
|
|
2822
3392
|
if (!map.has(key))
|
|
2823
3393
|
map.set(key, []);
|
|
2824
3394
|
map.get(key).push(item);
|
|
@@ -2827,31 +3397,26 @@ function* group_by_exp(arr, itemName, exp) {
|
|
|
2827
3397
|
}
|
|
2828
3398
|
function* find(arr, property, expected) {
|
|
2829
3399
|
const token = new Tokenizer(stringify(property)).readScopeValue();
|
|
2830
|
-
|
|
2831
|
-
|
|
3400
|
+
const array = toArray(arr);
|
|
3401
|
+
for (const item of array) {
|
|
3402
|
+
const value = yield evalToken(token, this.context.spawn(item));
|
|
2832
3403
|
if (equals(value, expected))
|
|
2833
3404
|
return item;
|
|
2834
3405
|
}
|
|
2835
|
-
return null;
|
|
2836
3406
|
}
|
|
2837
3407
|
function* find_exp(arr, itemName, exp) {
|
|
2838
3408
|
const predicate = new Value(stringify(exp), this.liquid);
|
|
2839
|
-
|
|
2840
|
-
|
|
3409
|
+
const array = toArray(arr);
|
|
3410
|
+
for (const item of array) {
|
|
3411
|
+
const value = yield predicate.value(this.context.spawn({ [itemName]: item }));
|
|
2841
3412
|
if (value)
|
|
2842
3413
|
return item;
|
|
2843
3414
|
}
|
|
2844
|
-
return null;
|
|
2845
3415
|
}
|
|
2846
3416
|
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
|
-
});
|
|
3417
|
+
arr = toArray(arr);
|
|
3418
|
+
this.context.memoryLimit.use(arr.length);
|
|
3419
|
+
return [...new Set(arr)];
|
|
2855
3420
|
}
|
|
2856
3421
|
function sample(v, count = 1) {
|
|
2857
3422
|
v = toValue(v);
|
|
@@ -2859,6 +3424,7 @@ function sample(v, count = 1) {
|
|
|
2859
3424
|
return [];
|
|
2860
3425
|
if (!isArray(v))
|
|
2861
3426
|
v = stringify(v);
|
|
3427
|
+
this.context.memoryLimit.use(count);
|
|
2862
3428
|
const shuffled = [...v].sort(() => Math.random() - 0.5);
|
|
2863
3429
|
if (count === 1)
|
|
2864
3430
|
return shuffled[0];
|
|
@@ -2894,6 +3460,9 @@ var arrayFilters = /*#__PURE__*/Object.freeze({
|
|
|
2894
3460
|
});
|
|
2895
3461
|
|
|
2896
3462
|
function date(v, format, timezoneOffset) {
|
|
3463
|
+
var _a, _b, _c;
|
|
3464
|
+
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);
|
|
3465
|
+
this.context.memoryLimit.use(size);
|
|
2897
3466
|
const date = parseDate(v, this.context.opts, timezoneOffset);
|
|
2898
3467
|
if (!date)
|
|
2899
3468
|
return v;
|
|
@@ -2927,39 +3496,30 @@ function stringify_date(v, month_type, type, style) {
|
|
|
2927
3496
|
}
|
|
2928
3497
|
function parseDate(v, opts, timezoneOffset) {
|
|
2929
3498
|
let date;
|
|
3499
|
+
const defaultTimezoneOffset = timezoneOffset !== null && timezoneOffset !== void 0 ? timezoneOffset : opts.timezoneOffset;
|
|
3500
|
+
const locale = opts.locale;
|
|
2930
3501
|
v = toValue(v);
|
|
2931
3502
|
if (v === 'now' || v === 'today') {
|
|
2932
|
-
date = new Date();
|
|
3503
|
+
date = new LiquidDate(Date.now(), locale, defaultTimezoneOffset);
|
|
2933
3504
|
}
|
|
2934
3505
|
else if (isNumber(v)) {
|
|
2935
|
-
date = new
|
|
3506
|
+
date = new LiquidDate(v * 1000, locale, defaultTimezoneOffset);
|
|
2936
3507
|
}
|
|
2937
3508
|
else if (isString(v)) {
|
|
2938
3509
|
if (/^\d+$/.test(v)) {
|
|
2939
|
-
date = new
|
|
3510
|
+
date = new LiquidDate(+v * 1000, locale, defaultTimezoneOffset);
|
|
2940
3511
|
}
|
|
2941
|
-
else if (opts.preserveTimezones) {
|
|
2942
|
-
date =
|
|
3512
|
+
else if (opts.preserveTimezones && timezoneOffset === undefined) {
|
|
3513
|
+
date = LiquidDate.createDateFixedToTimezone(v, locale);
|
|
2943
3514
|
}
|
|
2944
3515
|
else {
|
|
2945
|
-
date = new
|
|
3516
|
+
date = new LiquidDate(v, locale, defaultTimezoneOffset);
|
|
2946
3517
|
}
|
|
2947
3518
|
}
|
|
2948
3519
|
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);
|
|
3520
|
+
date = new LiquidDate(v, locale, defaultTimezoneOffset);
|
|
2958
3521
|
}
|
|
2959
|
-
return date;
|
|
2960
|
-
}
|
|
2961
|
-
function isValidDate(date) {
|
|
2962
|
-
return (date instanceof Date || date instanceof TimezoneDate) && !isNaN(date.getTime());
|
|
3522
|
+
return date.valid() ? date : undefined;
|
|
2963
3523
|
}
|
|
2964
3524
|
|
|
2965
3525
|
var dateFilters = /*#__PURE__*/Object.freeze({
|
|
@@ -2981,33 +3541,50 @@ const rCJKWord = /[\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF\u3040-\u309F\u30A0-\u
|
|
|
2981
3541
|
const rNonCJKWord = /[^\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF\u3040-\u309F\u30A0-\u30FF\uAC00-\uD7AF\s]+/gu;
|
|
2982
3542
|
function append(v, arg) {
|
|
2983
3543
|
assert(arguments.length === 2, 'append expect 2 arguments');
|
|
2984
|
-
|
|
3544
|
+
const lhs = stringify(v);
|
|
3545
|
+
const rhs = stringify(arg);
|
|
3546
|
+
this.context.memoryLimit.use(lhs.length + rhs.length);
|
|
3547
|
+
return lhs + rhs;
|
|
2985
3548
|
}
|
|
2986
3549
|
function prepend(v, arg) {
|
|
2987
3550
|
assert(arguments.length === 2, 'prepend expect 2 arguments');
|
|
2988
|
-
|
|
3551
|
+
const lhs = stringify(v);
|
|
3552
|
+
const rhs = stringify(arg);
|
|
3553
|
+
this.context.memoryLimit.use(lhs.length + rhs.length);
|
|
3554
|
+
return rhs + lhs;
|
|
2989
3555
|
}
|
|
2990
3556
|
function lstrip(v, chars) {
|
|
3557
|
+
const str = stringify(v);
|
|
3558
|
+
this.context.memoryLimit.use(str.length);
|
|
2991
3559
|
if (chars) {
|
|
2992
3560
|
chars = escapeRegExp(stringify(chars));
|
|
2993
|
-
return
|
|
3561
|
+
return str.replace(new RegExp(`^[${chars}]+`, 'g'), '');
|
|
2994
3562
|
}
|
|
2995
|
-
return
|
|
3563
|
+
return str.replace(/^\s+/, '');
|
|
2996
3564
|
}
|
|
2997
3565
|
function downcase(v) {
|
|
2998
|
-
|
|
3566
|
+
const str = stringify(v);
|
|
3567
|
+
this.context.memoryLimit.use(str.length);
|
|
3568
|
+
return str.toLowerCase();
|
|
2999
3569
|
}
|
|
3000
|
-
function upcase(
|
|
3570
|
+
function upcase(v) {
|
|
3571
|
+
const str = stringify(v);
|
|
3572
|
+
this.context.memoryLimit.use(str.length);
|
|
3001
3573
|
return stringify(str).toUpperCase();
|
|
3002
3574
|
}
|
|
3003
3575
|
function remove(v, arg) {
|
|
3004
|
-
|
|
3576
|
+
const str = stringify(v);
|
|
3577
|
+
this.context.memoryLimit.use(str.length);
|
|
3578
|
+
return str.split(stringify(arg)).join('');
|
|
3005
3579
|
}
|
|
3006
3580
|
function remove_first(v, l) {
|
|
3007
|
-
|
|
3581
|
+
const str = stringify(v);
|
|
3582
|
+
this.context.memoryLimit.use(str.length);
|
|
3583
|
+
return str.replace(stringify(l), '');
|
|
3008
3584
|
}
|
|
3009
3585
|
function remove_last(v, l) {
|
|
3010
3586
|
const str = stringify(v);
|
|
3587
|
+
this.context.memoryLimit.use(str.length);
|
|
3011
3588
|
const pattern = stringify(l);
|
|
3012
3589
|
const index = str.lastIndexOf(pattern);
|
|
3013
3590
|
if (index === -1)
|
|
@@ -3015,14 +3592,18 @@ function remove_last(v, l) {
|
|
|
3015
3592
|
return str.substring(0, index) + str.substring(index + pattern.length);
|
|
3016
3593
|
}
|
|
3017
3594
|
function rstrip(str, chars) {
|
|
3595
|
+
str = stringify(str);
|
|
3596
|
+
this.context.memoryLimit.use(str.length);
|
|
3018
3597
|
if (chars) {
|
|
3019
3598
|
chars = escapeRegExp(stringify(chars));
|
|
3020
|
-
return
|
|
3599
|
+
return str.replace(new RegExp(`[${chars}]+$`, 'g'), '');
|
|
3021
3600
|
}
|
|
3022
|
-
return
|
|
3601
|
+
return str.replace(/\s+$/, '');
|
|
3023
3602
|
}
|
|
3024
3603
|
function split(v, arg) {
|
|
3025
|
-
const
|
|
3604
|
+
const str = stringify(v);
|
|
3605
|
+
this.context.memoryLimit.use(str.length);
|
|
3606
|
+
const arr = str.split(stringify(arg));
|
|
3026
3607
|
// align to ruby split, which is the behavior of shopify/liquid
|
|
3027
3608
|
// see: https://ruby-doc.org/core-2.4.0/String.html#method-i-split
|
|
3028
3609
|
while (arr.length && arr[arr.length - 1] === '')
|
|
@@ -3030,29 +3611,39 @@ function split(v, arg) {
|
|
|
3030
3611
|
return arr;
|
|
3031
3612
|
}
|
|
3032
3613
|
function strip(v, chars) {
|
|
3614
|
+
const str = stringify(v);
|
|
3615
|
+
this.context.memoryLimit.use(str.length);
|
|
3033
3616
|
if (chars) {
|
|
3034
3617
|
chars = escapeRegExp(stringify(chars));
|
|
3035
|
-
return
|
|
3618
|
+
return str
|
|
3036
3619
|
.replace(new RegExp(`^[${chars}]+`, 'g'), '')
|
|
3037
3620
|
.replace(new RegExp(`[${chars}]+$`, 'g'), '');
|
|
3038
3621
|
}
|
|
3039
|
-
return
|
|
3622
|
+
return str.trim();
|
|
3040
3623
|
}
|
|
3041
3624
|
function strip_newlines(v) {
|
|
3042
|
-
|
|
3625
|
+
const str = stringify(v);
|
|
3626
|
+
this.context.memoryLimit.use(str.length);
|
|
3627
|
+
return str.replace(/\r?\n/gm, '');
|
|
3043
3628
|
}
|
|
3044
3629
|
function capitalize(str) {
|
|
3045
3630
|
str = stringify(str);
|
|
3631
|
+
this.context.memoryLimit.use(str.length);
|
|
3046
3632
|
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
3047
3633
|
}
|
|
3048
3634
|
function replace(v, pattern, replacement) {
|
|
3049
|
-
|
|
3635
|
+
const str = stringify(v);
|
|
3636
|
+
this.context.memoryLimit.use(str.length);
|
|
3637
|
+
return str.split(stringify(pattern)).join(replacement);
|
|
3050
3638
|
}
|
|
3051
3639
|
function replace_first(v, arg1, arg2) {
|
|
3052
|
-
|
|
3640
|
+
const str = stringify(v);
|
|
3641
|
+
this.context.memoryLimit.use(str.length);
|
|
3642
|
+
return str.replace(stringify(arg1), arg2);
|
|
3053
3643
|
}
|
|
3054
3644
|
function replace_last(v, arg1, arg2) {
|
|
3055
3645
|
const str = stringify(v);
|
|
3646
|
+
this.context.memoryLimit.use(str.length);
|
|
3056
3647
|
const pattern = stringify(arg1);
|
|
3057
3648
|
const index = str.lastIndexOf(pattern);
|
|
3058
3649
|
if (index === -1)
|
|
@@ -3061,13 +3652,16 @@ function replace_last(v, arg1, arg2) {
|
|
|
3061
3652
|
return str.substring(0, index) + replacement + str.substring(index + pattern.length);
|
|
3062
3653
|
}
|
|
3063
3654
|
function truncate(v, l = 50, o = '...') {
|
|
3064
|
-
|
|
3065
|
-
|
|
3655
|
+
const str = stringify(v);
|
|
3656
|
+
this.context.memoryLimit.use(str.length);
|
|
3657
|
+
if (str.length <= l)
|
|
3066
3658
|
return v;
|
|
3067
|
-
return
|
|
3659
|
+
return str.substring(0, l - o.length) + o;
|
|
3068
3660
|
}
|
|
3069
3661
|
function truncatewords(v, words = 15, o = '...') {
|
|
3070
|
-
const
|
|
3662
|
+
const str = stringify(v);
|
|
3663
|
+
this.context.memoryLimit.use(str.length);
|
|
3664
|
+
const arr = str.split(/\s+/);
|
|
3071
3665
|
if (words <= 0)
|
|
3072
3666
|
words = 1;
|
|
3073
3667
|
let ret = arr.slice(0, words).join(' ');
|
|
@@ -3076,11 +3670,14 @@ function truncatewords(v, words = 15, o = '...') {
|
|
|
3076
3670
|
return ret;
|
|
3077
3671
|
}
|
|
3078
3672
|
function normalize_whitespace(v) {
|
|
3079
|
-
|
|
3080
|
-
|
|
3673
|
+
const str = stringify(v);
|
|
3674
|
+
this.context.memoryLimit.use(str.length);
|
|
3675
|
+
return str.replace(/\s+/g, ' ');
|
|
3081
3676
|
}
|
|
3082
3677
|
function number_of_words(input, mode) {
|
|
3083
|
-
|
|
3678
|
+
const str = stringify(input);
|
|
3679
|
+
this.context.memoryLimit.use(str.length);
|
|
3680
|
+
input = str.trim();
|
|
3084
3681
|
if (!input)
|
|
3085
3682
|
return 0;
|
|
3086
3683
|
switch (mode) {
|
|
@@ -3098,6 +3695,7 @@ function number_of_words(input, mode) {
|
|
|
3098
3695
|
}
|
|
3099
3696
|
}
|
|
3100
3697
|
function array_to_sentence_string(array, connector = 'and') {
|
|
3698
|
+
this.context.memoryLimit.use(array.length);
|
|
3101
3699
|
switch (array.length) {
|
|
3102
3700
|
case 0:
|
|
3103
3701
|
return '';
|
|
@@ -3140,7 +3738,8 @@ const filters = Object.assign(Object.assign(Object.assign(Object.assign(Object.a
|
|
|
3140
3738
|
class AssignTag extends Tag {
|
|
3141
3739
|
constructor(token, remainTokens, liquid) {
|
|
3142
3740
|
super(token, remainTokens, liquid);
|
|
3143
|
-
this.
|
|
3741
|
+
this.identifier = this.tokenizer.readIdentifier();
|
|
3742
|
+
this.key = this.identifier.content;
|
|
3144
3743
|
this.tokenizer.assert(this.key, 'expected variable name');
|
|
3145
3744
|
this.tokenizer.skipBlank();
|
|
3146
3745
|
this.tokenizer.assert(this.tokenizer.peek() === '=', 'expected "="');
|
|
@@ -3150,11 +3749,17 @@ class AssignTag extends Tag {
|
|
|
3150
3749
|
*render(ctx) {
|
|
3151
3750
|
ctx.bottom()[this.key] = yield this.value.value(ctx, this.liquid.options.lenientIf);
|
|
3152
3751
|
}
|
|
3752
|
+
*arguments() {
|
|
3753
|
+
yield this.value;
|
|
3754
|
+
}
|
|
3755
|
+
*localScope() {
|
|
3756
|
+
yield this.identifier;
|
|
3757
|
+
}
|
|
3153
3758
|
}
|
|
3154
3759
|
|
|
3155
3760
|
const MODIFIERS = ['offset', 'limit', 'reversed'];
|
|
3156
3761
|
class ForTag extends Tag {
|
|
3157
|
-
constructor(token, remainTokens, liquid) {
|
|
3762
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3158
3763
|
super(token, remainTokens, liquid);
|
|
3159
3764
|
const variable = this.tokenizer.readIdentifier();
|
|
3160
3765
|
const inStr = this.tokenizer.readIdentifier();
|
|
@@ -3164,18 +3769,16 @@ class ForTag extends Tag {
|
|
|
3164
3769
|
}
|
|
3165
3770
|
this.variable = variable.content;
|
|
3166
3771
|
this.collection = collection;
|
|
3167
|
-
this.hash = new Hash(this.tokenizer.
|
|
3772
|
+
this.hash = new Hash(this.tokenizer, liquid.options.keyValueSeparator);
|
|
3168
3773
|
this.templates = [];
|
|
3169
3774
|
this.elseTemplates = [];
|
|
3170
3775
|
let p;
|
|
3171
|
-
const stream =
|
|
3776
|
+
const stream = parser.parseStream(remainTokens)
|
|
3172
3777
|
.on('start', () => (p = this.templates))
|
|
3173
|
-
.on('tag:else',
|
|
3174
|
-
.on('tag:endfor', ()
|
|
3778
|
+
.on('tag:else', tag => { assertEmpty(tag.args); p = this.elseTemplates; })
|
|
3779
|
+
.on('tag:endfor', tag => { assertEmpty(tag.args); stream.stop(); })
|
|
3175
3780
|
.on('template', (tpl) => p.push(tpl))
|
|
3176
|
-
.on('end', () => {
|
|
3177
|
-
throw new Error(`tag ${token.getText()} not closed`);
|
|
3178
|
-
});
|
|
3781
|
+
.on('end', () => { throw new Error(`tag ${token.getText()} not closed`); });
|
|
3179
3782
|
stream.start();
|
|
3180
3783
|
}
|
|
3181
3784
|
*render(ctx, emitter) {
|
|
@@ -3204,16 +3807,33 @@ class ForTag extends Tag {
|
|
|
3204
3807
|
ctx.push(scope);
|
|
3205
3808
|
for (const item of collection) {
|
|
3206
3809
|
scope[this.variable] = item;
|
|
3810
|
+
ctx.continueCalled = ctx.breakCalled = false;
|
|
3207
3811
|
yield r.renderTemplates(this.templates, ctx, emitter);
|
|
3208
|
-
if (
|
|
3209
|
-
emitter['break'] = false;
|
|
3812
|
+
if (ctx.breakCalled)
|
|
3210
3813
|
break;
|
|
3211
|
-
}
|
|
3212
|
-
emitter['continue'] = false;
|
|
3213
3814
|
scope.forloop.next();
|
|
3214
3815
|
}
|
|
3816
|
+
ctx.continueCalled = ctx.breakCalled = false;
|
|
3215
3817
|
ctx.pop();
|
|
3216
3818
|
}
|
|
3819
|
+
*children() {
|
|
3820
|
+
const templates = this.templates.slice();
|
|
3821
|
+
if (this.elseTemplates) {
|
|
3822
|
+
templates.push(...this.elseTemplates);
|
|
3823
|
+
}
|
|
3824
|
+
return templates;
|
|
3825
|
+
}
|
|
3826
|
+
*arguments() {
|
|
3827
|
+
yield this.collection;
|
|
3828
|
+
for (const v of Object.values(this.hash.hash)) {
|
|
3829
|
+
if (isValueToken(v)) {
|
|
3830
|
+
yield v;
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
3834
|
+
blockScope() {
|
|
3835
|
+
return [this.variable, 'forloop'];
|
|
3836
|
+
}
|
|
3217
3837
|
}
|
|
3218
3838
|
function reversed(arr) {
|
|
3219
3839
|
return [...arr].reverse();
|
|
@@ -3226,36 +3846,43 @@ function limit(arr, count) {
|
|
|
3226
3846
|
}
|
|
3227
3847
|
|
|
3228
3848
|
class CaptureTag extends Tag {
|
|
3229
|
-
constructor(tagToken, remainTokens, liquid) {
|
|
3849
|
+
constructor(tagToken, remainTokens, liquid, parser) {
|
|
3230
3850
|
super(tagToken, remainTokens, liquid);
|
|
3231
3851
|
this.templates = [];
|
|
3232
|
-
this.
|
|
3852
|
+
this.identifier = this.readVariable();
|
|
3853
|
+
this.variable = this.identifier.content;
|
|
3233
3854
|
while (remainTokens.length) {
|
|
3234
3855
|
const token = remainTokens.shift();
|
|
3235
3856
|
if (isTagToken(token) && token.name === 'endcapture')
|
|
3236
3857
|
return;
|
|
3237
|
-
this.templates.push(
|
|
3858
|
+
this.templates.push(parser.parseToken(token, remainTokens));
|
|
3238
3859
|
}
|
|
3239
3860
|
throw new Error(`tag ${tagToken.getText()} not closed`);
|
|
3240
3861
|
}
|
|
3862
|
+
readVariable() {
|
|
3863
|
+
let ident = this.tokenizer.readIdentifier();
|
|
3864
|
+
if (ident.content)
|
|
3865
|
+
return ident;
|
|
3866
|
+
ident = this.tokenizer.readQuoted();
|
|
3867
|
+
if (ident)
|
|
3868
|
+
return ident;
|
|
3869
|
+
throw this.tokenizer.error('invalid capture name');
|
|
3870
|
+
}
|
|
3241
3871
|
*render(ctx) {
|
|
3242
3872
|
const r = this.liquid.renderer;
|
|
3243
3873
|
const html = yield r.renderTemplates(this.templates, ctx);
|
|
3244
3874
|
ctx.bottom()[this.variable] = html;
|
|
3245
3875
|
}
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
if (quoted)
|
|
3252
|
-
return evalQuotedToken(quoted);
|
|
3253
|
-
throw this.tokenizer.error('invalid capture name');
|
|
3876
|
+
*children() {
|
|
3877
|
+
return this.templates;
|
|
3878
|
+
}
|
|
3879
|
+
*localScope() {
|
|
3880
|
+
yield this.identifier;
|
|
3254
3881
|
}
|
|
3255
3882
|
}
|
|
3256
3883
|
|
|
3257
3884
|
class CaseTag extends Tag {
|
|
3258
|
-
constructor(tagToken, remainTokens, liquid) {
|
|
3885
|
+
constructor(tagToken, remainTokens, liquid, parser) {
|
|
3259
3886
|
super(tagToken, remainTokens, liquid);
|
|
3260
3887
|
this.branches = [];
|
|
3261
3888
|
this.elseTemplates = [];
|
|
@@ -3263,7 +3890,7 @@ class CaseTag extends Tag {
|
|
|
3263
3890
|
this.elseTemplates = [];
|
|
3264
3891
|
let p = [];
|
|
3265
3892
|
let elseCount = 0;
|
|
3266
|
-
const stream =
|
|
3893
|
+
const stream = parser.parseStream(remainTokens)
|
|
3267
3894
|
.on('tag:when', (token) => {
|
|
3268
3895
|
if (elseCount > 0) {
|
|
3269
3896
|
return;
|
|
@@ -3318,6 +3945,17 @@ class CaseTag extends Tag {
|
|
|
3318
3945
|
yield r.renderTemplates(this.elseTemplates, ctx, emitter);
|
|
3319
3946
|
}
|
|
3320
3947
|
}
|
|
3948
|
+
*arguments() {
|
|
3949
|
+
yield this.value;
|
|
3950
|
+
yield* this.branches.flatMap(b => b.values);
|
|
3951
|
+
}
|
|
3952
|
+
*children() {
|
|
3953
|
+
const templates = this.branches.flatMap(b => b.templates);
|
|
3954
|
+
if (this.elseTemplates) {
|
|
3955
|
+
templates.push(...this.elseTemplates);
|
|
3956
|
+
}
|
|
3957
|
+
return templates;
|
|
3958
|
+
}
|
|
3321
3959
|
}
|
|
3322
3960
|
|
|
3323
3961
|
class CommentTag extends Tag {
|
|
@@ -3334,10 +3972,10 @@ class CommentTag extends Tag {
|
|
|
3334
3972
|
}
|
|
3335
3973
|
|
|
3336
3974
|
class RenderTag extends Tag {
|
|
3337
|
-
constructor(token, remainTokens, liquid) {
|
|
3975
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3338
3976
|
super(token, remainTokens, liquid);
|
|
3339
3977
|
const tokenizer = this.tokenizer;
|
|
3340
|
-
this.file = parseFilePath(tokenizer, this.liquid);
|
|
3978
|
+
this.file = parseFilePath(tokenizer, this.liquid, parser);
|
|
3341
3979
|
this.currentFile = token.file;
|
|
3342
3980
|
while (!tokenizer.end()) {
|
|
3343
3981
|
tokenizer.skipBlank();
|
|
@@ -3371,13 +4009,13 @@ class RenderTag extends Tag {
|
|
|
3371
4009
|
tokenizer.p = begin;
|
|
3372
4010
|
break;
|
|
3373
4011
|
}
|
|
3374
|
-
this.hash = new Hash(tokenizer.
|
|
4012
|
+
this.hash = new Hash(tokenizer, liquid.options.keyValueSeparator);
|
|
3375
4013
|
}
|
|
3376
4014
|
*render(ctx, emitter) {
|
|
3377
4015
|
const { liquid, hash } = this;
|
|
3378
4016
|
const filepath = (yield renderFilePath(this['file'], ctx, liquid));
|
|
3379
4017
|
assert(filepath, () => `illegal file path "${filepath}"`);
|
|
3380
|
-
const childCtx =
|
|
4018
|
+
const childCtx = ctx.spawn();
|
|
3381
4019
|
const scope = childCtx.bottom();
|
|
3382
4020
|
__assign(scope, yield hash.render(ctx));
|
|
3383
4021
|
if (this['with']) {
|
|
@@ -3400,6 +4038,55 @@ class RenderTag extends Tag {
|
|
|
3400
4038
|
yield liquid.renderer.renderTemplates(templates, childCtx, emitter);
|
|
3401
4039
|
}
|
|
3402
4040
|
}
|
|
4041
|
+
*children(partials, sync) {
|
|
4042
|
+
if (partials && isString(this['file'])) {
|
|
4043
|
+
return (yield this.liquid._parsePartialFile(this['file'], sync, this['currentFile']));
|
|
4044
|
+
}
|
|
4045
|
+
return [];
|
|
4046
|
+
}
|
|
4047
|
+
partialScope() {
|
|
4048
|
+
if (isString(this['file'])) {
|
|
4049
|
+
const names = Object.keys(this.hash.hash);
|
|
4050
|
+
if (this['with']) {
|
|
4051
|
+
const { value, alias } = this['with'];
|
|
4052
|
+
if (isString(alias)) {
|
|
4053
|
+
names.push([alias, value]);
|
|
4054
|
+
}
|
|
4055
|
+
else if (isString(this.file)) {
|
|
4056
|
+
names.push([this.file, value]);
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4059
|
+
if (this['for']) {
|
|
4060
|
+
const { value, alias } = this['for'];
|
|
4061
|
+
if (isString(alias)) {
|
|
4062
|
+
names.push([alias, value]);
|
|
4063
|
+
}
|
|
4064
|
+
else if (isString(this.file)) {
|
|
4065
|
+
names.push([this.file, value]);
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
return { name: this['file'], isolated: true, scope: names };
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
*arguments() {
|
|
4072
|
+
for (const v of Object.values(this.hash.hash)) {
|
|
4073
|
+
if (isValueToken(v)) {
|
|
4074
|
+
yield v;
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
if (this['with']) {
|
|
4078
|
+
const { value } = this['with'];
|
|
4079
|
+
if (isValueToken(value)) {
|
|
4080
|
+
yield value;
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
4083
|
+
if (this['for']) {
|
|
4084
|
+
const { value } = this['for'];
|
|
4085
|
+
if (isValueToken(value)) {
|
|
4086
|
+
yield value;
|
|
4087
|
+
}
|
|
4088
|
+
}
|
|
4089
|
+
}
|
|
3403
4090
|
}
|
|
3404
4091
|
/**
|
|
3405
4092
|
* @return null for "none",
|
|
@@ -3407,7 +4094,7 @@ class RenderTag extends Tag {
|
|
|
3407
4094
|
* @return Token for expression (not quoted)
|
|
3408
4095
|
* @throws TypeError if cannot read next token
|
|
3409
4096
|
*/
|
|
3410
|
-
function parseFilePath(tokenizer, liquid) {
|
|
4097
|
+
function parseFilePath(tokenizer, liquid, parser) {
|
|
3411
4098
|
if (liquid.options.dynamicPartials) {
|
|
3412
4099
|
const file = tokenizer.readValue();
|
|
3413
4100
|
tokenizer.assert(file, 'illegal file path');
|
|
@@ -3415,13 +4102,13 @@ function parseFilePath(tokenizer, liquid) {
|
|
|
3415
4102
|
return;
|
|
3416
4103
|
if (isQuotedToken(file)) {
|
|
3417
4104
|
// for filenames like "files/{{file}}", eval as liquid template
|
|
3418
|
-
const templates =
|
|
4105
|
+
const templates = parser.parse(evalQuotedToken(file));
|
|
3419
4106
|
return optimize(templates);
|
|
3420
4107
|
}
|
|
3421
4108
|
return file;
|
|
3422
4109
|
}
|
|
3423
4110
|
const tokens = [...tokenizer.readFileNameTemplate(liquid.options)];
|
|
3424
|
-
const templates = optimize(
|
|
4111
|
+
const templates = optimize(parser.parseTokens(tokens));
|
|
3425
4112
|
return templates === 'none' ? undefined : templates;
|
|
3426
4113
|
}
|
|
3427
4114
|
function optimize(templates) {
|
|
@@ -3439,10 +4126,10 @@ function* renderFilePath(file, ctx, liquid) {
|
|
|
3439
4126
|
}
|
|
3440
4127
|
|
|
3441
4128
|
class IncludeTag extends Tag {
|
|
3442
|
-
constructor(token, remainTokens, liquid) {
|
|
4129
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3443
4130
|
super(token, remainTokens, liquid);
|
|
3444
4131
|
const { tokenizer } = token;
|
|
3445
|
-
this['file'] = parseFilePath(tokenizer, this.liquid);
|
|
4132
|
+
this['file'] = parseFilePath(tokenizer, this.liquid, parser);
|
|
3446
4133
|
this['currentFile'] = token.file;
|
|
3447
4134
|
const begin = tokenizer.p;
|
|
3448
4135
|
const withStr = tokenizer.readIdentifier();
|
|
@@ -3456,7 +4143,7 @@ class IncludeTag extends Tag {
|
|
|
3456
4143
|
}
|
|
3457
4144
|
else
|
|
3458
4145
|
tokenizer.p = begin;
|
|
3459
|
-
this.hash = new Hash(tokenizer
|
|
4146
|
+
this.hash = new Hash(tokenizer, liquid.options.jekyllInclude || liquid.options.keyValueSeparator);
|
|
3460
4147
|
}
|
|
3461
4148
|
*render(ctx, emitter) {
|
|
3462
4149
|
const { liquid, hash, withVar } = this;
|
|
@@ -3475,12 +4162,43 @@ class IncludeTag extends Tag {
|
|
|
3475
4162
|
ctx.pop();
|
|
3476
4163
|
ctx.restoreRegister(saved);
|
|
3477
4164
|
}
|
|
4165
|
+
*children(partials, sync) {
|
|
4166
|
+
if (partials && isString(this['file'])) {
|
|
4167
|
+
return (yield this.liquid._parsePartialFile(this['file'], sync, this['currentFile']));
|
|
4168
|
+
}
|
|
4169
|
+
return [];
|
|
4170
|
+
}
|
|
4171
|
+
partialScope() {
|
|
4172
|
+
if (isString(this['file'])) {
|
|
4173
|
+
let names;
|
|
4174
|
+
if (this.liquid.options.jekyllInclude) {
|
|
4175
|
+
names = ['include'];
|
|
4176
|
+
}
|
|
4177
|
+
else {
|
|
4178
|
+
names = Object.keys(this.hash.hash);
|
|
4179
|
+
if (this.withVar) {
|
|
4180
|
+
names.push([this['file'], this.withVar]);
|
|
4181
|
+
}
|
|
4182
|
+
}
|
|
4183
|
+
return { name: this['file'], isolated: false, scope: names };
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
*arguments() {
|
|
4187
|
+
yield* Object.values(this.hash.hash).filter(isValueToken);
|
|
4188
|
+
if (isValueToken(this['file'])) {
|
|
4189
|
+
yield this['file'];
|
|
4190
|
+
}
|
|
4191
|
+
if (isValueToken(this.withVar)) {
|
|
4192
|
+
yield this.withVar;
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
3478
4195
|
}
|
|
3479
4196
|
|
|
3480
4197
|
class DecrementTag extends Tag {
|
|
3481
4198
|
constructor(token, remainTokens, liquid) {
|
|
3482
4199
|
super(token, remainTokens, liquid);
|
|
3483
|
-
this.
|
|
4200
|
+
this.identifier = this.tokenizer.readIdentifier();
|
|
4201
|
+
this.variable = this.identifier.content;
|
|
3484
4202
|
}
|
|
3485
4203
|
render(context, emitter) {
|
|
3486
4204
|
const scope = context.environments;
|
|
@@ -3489,6 +4207,9 @@ class DecrementTag extends Tag {
|
|
|
3489
4207
|
}
|
|
3490
4208
|
emitter.write(stringify(--scope[this.variable]));
|
|
3491
4209
|
}
|
|
4210
|
+
*localScope() {
|
|
4211
|
+
yield this.identifier;
|
|
4212
|
+
}
|
|
3492
4213
|
}
|
|
3493
4214
|
|
|
3494
4215
|
class CycleTag extends Tag {
|
|
@@ -3526,40 +4247,38 @@ class CycleTag extends Tag {
|
|
|
3526
4247
|
groups[fingerprint] = idx;
|
|
3527
4248
|
return yield evalToken(candidate, ctx);
|
|
3528
4249
|
}
|
|
4250
|
+
*arguments() {
|
|
4251
|
+
yield* this.candidates;
|
|
4252
|
+
if (this.group) {
|
|
4253
|
+
yield this.group;
|
|
4254
|
+
}
|
|
4255
|
+
}
|
|
3529
4256
|
}
|
|
3530
4257
|
|
|
3531
4258
|
class IfTag extends Tag {
|
|
3532
|
-
constructor(tagToken, remainTokens, liquid) {
|
|
4259
|
+
constructor(tagToken, remainTokens, liquid, parser) {
|
|
3533
4260
|
super(tagToken, remainTokens, liquid);
|
|
3534
4261
|
this.branches = [];
|
|
3535
|
-
this.elseTemplates = [];
|
|
3536
4262
|
let p = [];
|
|
3537
|
-
|
|
3538
|
-
liquid.parser.parseStream(remainTokens)
|
|
4263
|
+
parser.parseStream(remainTokens)
|
|
3539
4264
|
.on('start', () => this.branches.push({
|
|
3540
|
-
value: new Value(tagToken.
|
|
4265
|
+
value: new Value(tagToken.tokenizer.readFilteredValue(), this.liquid),
|
|
3541
4266
|
templates: (p = [])
|
|
3542
4267
|
}))
|
|
3543
4268
|
.on('tag:elsif', (token) => {
|
|
3544
|
-
|
|
3545
|
-
p = [];
|
|
3546
|
-
return;
|
|
3547
|
-
}
|
|
4269
|
+
assert(!this.elseTemplates, 'unexpected elsif after else');
|
|
3548
4270
|
this.branches.push({
|
|
3549
|
-
value: new Value(token.
|
|
4271
|
+
value: new Value(token.tokenizer.readFilteredValue(), this.liquid),
|
|
3550
4272
|
templates: (p = [])
|
|
3551
4273
|
});
|
|
3552
4274
|
})
|
|
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
|
-
}
|
|
4275
|
+
.on('tag:else', tag => {
|
|
4276
|
+
assertEmpty(tag.args);
|
|
4277
|
+
assert(!this.elseTemplates, 'duplicated else');
|
|
4278
|
+
p = this.elseTemplates = [];
|
|
3562
4279
|
})
|
|
4280
|
+
.on('tag:endif', function (tag) { assertEmpty(tag.args); this.stop(); })
|
|
4281
|
+
.on('template', (tpl) => p.push(tpl))
|
|
3563
4282
|
.on('end', () => { throw new Error(`tag ${tagToken.getText()} not closed`); })
|
|
3564
4283
|
.start();
|
|
3565
4284
|
}
|
|
@@ -3572,14 +4291,25 @@ class IfTag extends Tag {
|
|
|
3572
4291
|
return;
|
|
3573
4292
|
}
|
|
3574
4293
|
}
|
|
3575
|
-
yield r.renderTemplates(this.elseTemplates, ctx, emitter);
|
|
4294
|
+
yield r.renderTemplates(this.elseTemplates || [], ctx, emitter);
|
|
4295
|
+
}
|
|
4296
|
+
*children() {
|
|
4297
|
+
const templates = this.branches.flatMap(b => b.templates);
|
|
4298
|
+
if (this.elseTemplates) {
|
|
4299
|
+
templates.push(...this.elseTemplates);
|
|
4300
|
+
}
|
|
4301
|
+
return templates;
|
|
4302
|
+
}
|
|
4303
|
+
arguments() {
|
|
4304
|
+
return this.branches.map(b => b.value);
|
|
3576
4305
|
}
|
|
3577
4306
|
}
|
|
3578
4307
|
|
|
3579
4308
|
class IncrementTag extends Tag {
|
|
3580
4309
|
constructor(token, remainTokens, liquid) {
|
|
3581
4310
|
super(token, remainTokens, liquid);
|
|
3582
|
-
this.
|
|
4311
|
+
this.identifier = this.tokenizer.readIdentifier();
|
|
4312
|
+
this.variable = this.identifier.content;
|
|
3583
4313
|
}
|
|
3584
4314
|
render(context, emitter) {
|
|
3585
4315
|
const scope = context.environments;
|
|
@@ -3590,15 +4320,18 @@ class IncrementTag extends Tag {
|
|
|
3590
4320
|
scope[this.variable]++;
|
|
3591
4321
|
emitter.write(stringify(val));
|
|
3592
4322
|
}
|
|
4323
|
+
*localScope() {
|
|
4324
|
+
yield this.identifier;
|
|
4325
|
+
}
|
|
3593
4326
|
}
|
|
3594
4327
|
|
|
3595
4328
|
class LayoutTag extends Tag {
|
|
3596
|
-
constructor(token, remainTokens, liquid) {
|
|
4329
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3597
4330
|
super(token, remainTokens, liquid);
|
|
3598
|
-
this.file = parseFilePath(this.tokenizer, this.liquid);
|
|
4331
|
+
this.file = parseFilePath(this.tokenizer, this.liquid, parser);
|
|
3599
4332
|
this['currentFile'] = token.file;
|
|
3600
|
-
this.args = new Hash(this.tokenizer.
|
|
3601
|
-
this.templates =
|
|
4333
|
+
this.args = new Hash(this.tokenizer, liquid.options.keyValueSeparator);
|
|
4334
|
+
this.templates = parser.parseTokens(remainTokens);
|
|
3602
4335
|
}
|
|
3603
4336
|
*render(ctx, emitter) {
|
|
3604
4337
|
const { liquid, args, file } = this;
|
|
@@ -3624,10 +4357,32 @@ class LayoutTag extends Tag {
|
|
|
3624
4357
|
yield renderer.renderTemplates(templates, ctx, emitter);
|
|
3625
4358
|
ctx.pop();
|
|
3626
4359
|
}
|
|
4360
|
+
*children(partials) {
|
|
4361
|
+
const templates = this.templates.slice();
|
|
4362
|
+
if (partials && isString(this.file)) {
|
|
4363
|
+
templates.push(...(yield this.liquid._parsePartialFile(this.file, true, this['currentFile'])));
|
|
4364
|
+
}
|
|
4365
|
+
return templates;
|
|
4366
|
+
}
|
|
4367
|
+
*arguments() {
|
|
4368
|
+
for (const v of Object.values(this.args.hash)) {
|
|
4369
|
+
if (isValueToken(v)) {
|
|
4370
|
+
yield v;
|
|
4371
|
+
}
|
|
4372
|
+
}
|
|
4373
|
+
if (isValueToken(this.file)) {
|
|
4374
|
+
yield this.file;
|
|
4375
|
+
}
|
|
4376
|
+
}
|
|
4377
|
+
partialScope() {
|
|
4378
|
+
if (isString(this.file)) {
|
|
4379
|
+
return { name: this.file, isolated: false, scope: Object.keys(this.args.hash) };
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
3627
4382
|
}
|
|
3628
4383
|
|
|
3629
4384
|
class BlockTag extends Tag {
|
|
3630
|
-
constructor(token, remainTokens, liquid) {
|
|
4385
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3631
4386
|
super(token, remainTokens, liquid);
|
|
3632
4387
|
this.templates = [];
|
|
3633
4388
|
const match = /\w+/.exec(token.args);
|
|
@@ -3636,7 +4391,7 @@ class BlockTag extends Tag {
|
|
|
3636
4391
|
const token = remainTokens.shift();
|
|
3637
4392
|
if (isTagToken(token) && token.name === 'endblock')
|
|
3638
4393
|
return;
|
|
3639
|
-
const template =
|
|
4394
|
+
const template = parser.parseToken(token, remainTokens);
|
|
3640
4395
|
this.templates.push(template);
|
|
3641
4396
|
}
|
|
3642
4397
|
throw new Error(`tag ${token.getText()} not closed`);
|
|
@@ -3663,6 +4418,12 @@ class BlockTag extends Tag {
|
|
|
3663
4418
|
? (superBlock, emitter) => renderChild(new BlockDrop(() => renderCurrent(superBlock, emitter)), emitter)
|
|
3664
4419
|
: renderCurrent;
|
|
3665
4420
|
}
|
|
4421
|
+
*children() {
|
|
4422
|
+
return this.templates;
|
|
4423
|
+
}
|
|
4424
|
+
blockScope() {
|
|
4425
|
+
return ['block'];
|
|
4426
|
+
}
|
|
3666
4427
|
}
|
|
3667
4428
|
|
|
3668
4429
|
class RawTag extends Tag {
|
|
@@ -3706,7 +4467,7 @@ class TablerowloopDrop extends ForloopDrop {
|
|
|
3706
4467
|
}
|
|
3707
4468
|
|
|
3708
4469
|
class TablerowTag extends Tag {
|
|
3709
|
-
constructor(tagToken, remainTokens, liquid) {
|
|
4470
|
+
constructor(tagToken, remainTokens, liquid, parser) {
|
|
3710
4471
|
super(tagToken, remainTokens, liquid);
|
|
3711
4472
|
const variable = this.tokenizer.readIdentifier();
|
|
3712
4473
|
this.tokenizer.skipBlank();
|
|
@@ -3717,10 +4478,10 @@ class TablerowTag extends Tag {
|
|
|
3717
4478
|
}
|
|
3718
4479
|
this.variable = variable.content;
|
|
3719
4480
|
this.collection = collectionToken;
|
|
3720
|
-
this.args = new Hash(this.tokenizer.
|
|
4481
|
+
this.args = new Hash(this.tokenizer, liquid.options.keyValueSeparator);
|
|
3721
4482
|
this.templates = [];
|
|
3722
4483
|
let p;
|
|
3723
|
-
const stream =
|
|
4484
|
+
const stream = parser.parseStream(remainTokens)
|
|
3724
4485
|
.on('start', () => (p = this.templates))
|
|
3725
4486
|
.on('tag:endtablerow', () => stream.stop())
|
|
3726
4487
|
.on('template', (tpl) => p.push(tpl))
|
|
@@ -3755,18 +4516,32 @@ class TablerowTag extends Tag {
|
|
|
3755
4516
|
emitter.write('</tr>');
|
|
3756
4517
|
ctx.pop();
|
|
3757
4518
|
}
|
|
4519
|
+
*children() {
|
|
4520
|
+
return this.templates;
|
|
4521
|
+
}
|
|
4522
|
+
*arguments() {
|
|
4523
|
+
yield this.collection;
|
|
4524
|
+
for (const v of Object.values(this.args.hash)) {
|
|
4525
|
+
if (isValueToken(v)) {
|
|
4526
|
+
yield v;
|
|
4527
|
+
}
|
|
4528
|
+
}
|
|
4529
|
+
}
|
|
4530
|
+
blockScope() {
|
|
4531
|
+
return [this.variable, 'tablerowloop'];
|
|
4532
|
+
}
|
|
3758
4533
|
}
|
|
3759
4534
|
|
|
3760
4535
|
class UnlessTag extends Tag {
|
|
3761
|
-
constructor(tagToken, remainTokens, liquid) {
|
|
4536
|
+
constructor(tagToken, remainTokens, liquid, parser) {
|
|
3762
4537
|
super(tagToken, remainTokens, liquid);
|
|
3763
4538
|
this.branches = [];
|
|
3764
4539
|
this.elseTemplates = [];
|
|
3765
4540
|
let p = [];
|
|
3766
4541
|
let elseCount = 0;
|
|
3767
|
-
|
|
4542
|
+
parser.parseStream(remainTokens)
|
|
3768
4543
|
.on('start', () => this.branches.push({
|
|
3769
|
-
value: new Value(tagToken.
|
|
4544
|
+
value: new Value(tagToken.tokenizer.readFilteredValue(), this.liquid),
|
|
3770
4545
|
test: isFalsy,
|
|
3771
4546
|
templates: (p = [])
|
|
3772
4547
|
}))
|
|
@@ -3776,7 +4551,7 @@ class UnlessTag extends Tag {
|
|
|
3776
4551
|
return;
|
|
3777
4552
|
}
|
|
3778
4553
|
this.branches.push({
|
|
3779
|
-
value: new Value(token.
|
|
4554
|
+
value: new Value(token.tokenizer.readFilteredValue(), this.liquid),
|
|
3780
4555
|
test: isTruthy,
|
|
3781
4556
|
templates: (p = [])
|
|
3782
4557
|
});
|
|
@@ -3805,17 +4580,27 @@ class UnlessTag extends Tag {
|
|
|
3805
4580
|
}
|
|
3806
4581
|
yield r.renderTemplates(this.elseTemplates, ctx, emitter);
|
|
3807
4582
|
}
|
|
4583
|
+
*children() {
|
|
4584
|
+
const children = this.branches.flatMap(b => b.templates);
|
|
4585
|
+
if (this.elseTemplates) {
|
|
4586
|
+
children.push(...this.elseTemplates);
|
|
4587
|
+
}
|
|
4588
|
+
return children;
|
|
4589
|
+
}
|
|
4590
|
+
arguments() {
|
|
4591
|
+
return this.branches.map(b => b.value);
|
|
4592
|
+
}
|
|
3808
4593
|
}
|
|
3809
4594
|
|
|
3810
4595
|
class BreakTag extends Tag {
|
|
3811
|
-
render(ctx,
|
|
3812
|
-
|
|
4596
|
+
render(ctx, _emitter) {
|
|
4597
|
+
ctx.breakCalled = true;
|
|
3813
4598
|
}
|
|
3814
4599
|
}
|
|
3815
4600
|
|
|
3816
4601
|
class ContinueTag extends Tag {
|
|
3817
|
-
render(ctx,
|
|
3818
|
-
|
|
4602
|
+
render(ctx, _emitter) {
|
|
4603
|
+
ctx.continueCalled = true;
|
|
3819
4604
|
}
|
|
3820
4605
|
}
|
|
3821
4606
|
|
|
@@ -3833,17 +4618,25 @@ class EchoTag extends Tag {
|
|
|
3833
4618
|
const val = yield this.value.value(ctx, false);
|
|
3834
4619
|
emitter.write(val);
|
|
3835
4620
|
}
|
|
4621
|
+
*arguments() {
|
|
4622
|
+
if (this.value) {
|
|
4623
|
+
yield this.value;
|
|
4624
|
+
}
|
|
4625
|
+
}
|
|
3836
4626
|
}
|
|
3837
4627
|
|
|
3838
4628
|
class LiquidTag extends Tag {
|
|
3839
|
-
constructor(token, remainTokens, liquid) {
|
|
4629
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3840
4630
|
super(token, remainTokens, liquid);
|
|
3841
4631
|
const tokens = this.tokenizer.readLiquidTagTokens(this.liquid.options);
|
|
3842
|
-
this.templates =
|
|
4632
|
+
this.templates = parser.parseTokens(tokens);
|
|
3843
4633
|
}
|
|
3844
4634
|
*render(ctx, emitter) {
|
|
3845
4635
|
yield this.liquid.renderer.renderTemplates(this.templates, ctx, emitter);
|
|
3846
4636
|
}
|
|
4637
|
+
*children() {
|
|
4638
|
+
return this.templates;
|
|
4639
|
+
}
|
|
3847
4640
|
}
|
|
3848
4641
|
|
|
3849
4642
|
class InlineCommentTag extends Tag {
|
|
@@ -3886,12 +4679,14 @@ class Liquid {
|
|
|
3886
4679
|
this.filters = {};
|
|
3887
4680
|
this.tags = {};
|
|
3888
4681
|
this.options = normalize(opts);
|
|
4682
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
3889
4683
|
this.parser = new Parser(this);
|
|
3890
4684
|
forOwn(tags, (conf, name) => this.registerTag(name, conf));
|
|
3891
4685
|
forOwn(filters, (handler, name) => this.registerFilter(name, handler));
|
|
3892
4686
|
}
|
|
3893
4687
|
parse(html, filepath) {
|
|
3894
|
-
|
|
4688
|
+
const parser = new Parser(this);
|
|
4689
|
+
return parser.parse(html, filepath);
|
|
3895
4690
|
}
|
|
3896
4691
|
_render(tpl, scope, renderOptions) {
|
|
3897
4692
|
const ctx = scope instanceof Context ? scope : new Context(scope, this.options, renderOptions);
|
|
@@ -3922,21 +4717,21 @@ class Liquid {
|
|
|
3922
4717
|
return toValueSync(this._parseAndRender(html, scope, Object.assign(Object.assign({}, renderOptions), { sync: true })));
|
|
3923
4718
|
}
|
|
3924
4719
|
_parsePartialFile(file, sync, currentFile) {
|
|
3925
|
-
return this.
|
|
4720
|
+
return new Parser(this).parseFile(file, sync, LookupType.Partials, currentFile);
|
|
3926
4721
|
}
|
|
3927
4722
|
_parseLayoutFile(file, sync, currentFile) {
|
|
3928
|
-
return this.
|
|
4723
|
+
return new Parser(this).parseFile(file, sync, LookupType.Layouts, currentFile);
|
|
3929
4724
|
}
|
|
3930
4725
|
_parseFile(file, sync, lookupType, currentFile) {
|
|
3931
|
-
return this.
|
|
4726
|
+
return new Parser(this).parseFile(file, sync, lookupType, currentFile);
|
|
3932
4727
|
}
|
|
3933
4728
|
parseFile(file, lookupType) {
|
|
3934
4729
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3935
|
-
return toPromise(this.
|
|
4730
|
+
return toPromise(new Parser(this).parseFile(file, false, lookupType));
|
|
3936
4731
|
});
|
|
3937
4732
|
}
|
|
3938
4733
|
parseFileSync(file, lookupType) {
|
|
3939
|
-
return toValueSync(this.
|
|
4734
|
+
return toValueSync(new Parser(this).parseFile(file, true, lookupType));
|
|
3940
4735
|
}
|
|
3941
4736
|
*_renderFile(file, ctx, renderFileOptions) {
|
|
3942
4737
|
const templates = (yield this._parseFile(file, renderFileOptions.sync, renderFileOptions.lookupType));
|
|
@@ -3992,9 +4787,97 @@ class Liquid {
|
|
|
3992
4787
|
self.renderFile(filePath, ctx).then(html => callback(null, html), callback);
|
|
3993
4788
|
};
|
|
3994
4789
|
}
|
|
4790
|
+
analyze(template, options = {}) {
|
|
4791
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4792
|
+
return analyze(template, options);
|
|
4793
|
+
});
|
|
4794
|
+
}
|
|
4795
|
+
analyzeSync(template, options = {}) {
|
|
4796
|
+
return analyzeSync(template, options);
|
|
4797
|
+
}
|
|
4798
|
+
parseAndAnalyze(html, filename, options = {}) {
|
|
4799
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4800
|
+
return analyze(this.parse(html, filename), options);
|
|
4801
|
+
});
|
|
4802
|
+
}
|
|
4803
|
+
parseAndAnalyzeSync(html, filename, options = {}) {
|
|
4804
|
+
return analyzeSync(this.parse(html, filename), options);
|
|
4805
|
+
}
|
|
4806
|
+
/** Return an array of all variables without their properties. */
|
|
4807
|
+
variables(template, options = {}) {
|
|
4808
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4809
|
+
const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
|
|
4810
|
+
return Object.keys(analysis.variables);
|
|
4811
|
+
});
|
|
4812
|
+
}
|
|
4813
|
+
/** Return an array of all variables without their properties. */
|
|
4814
|
+
variablesSync(template, options = {}) {
|
|
4815
|
+
const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
|
|
4816
|
+
return Object.keys(analysis.variables);
|
|
4817
|
+
}
|
|
4818
|
+
/** Return an array of all variables including their properties/paths. */
|
|
4819
|
+
fullVariables(template, options = {}) {
|
|
4820
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4821
|
+
const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
|
|
4822
|
+
return Array.from(new Set(Object.values(analysis.variables).flatMap((a) => a.map((v) => String(v)))));
|
|
4823
|
+
});
|
|
4824
|
+
}
|
|
4825
|
+
/** Return an array of all variables including their properties/paths. */
|
|
4826
|
+
fullVariablesSync(template, options = {}) {
|
|
4827
|
+
const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
|
|
4828
|
+
return Array.from(new Set(Object.values(analysis.variables).flatMap((a) => a.map((v) => String(v)))));
|
|
4829
|
+
}
|
|
4830
|
+
/** Return an array of all variables, each as an array of properties/segments. */
|
|
4831
|
+
variableSegments(template, options = {}) {
|
|
4832
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4833
|
+
const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
|
|
4834
|
+
return Array.from(strictUniq(Object.values(analysis.variables).flatMap((a) => a.map((v) => v.toArray()))));
|
|
4835
|
+
});
|
|
4836
|
+
}
|
|
4837
|
+
/** Return an array of all variables, each as an array of properties/segments. */
|
|
4838
|
+
variableSegmentsSync(template, options = {}) {
|
|
4839
|
+
const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
|
|
4840
|
+
return Array.from(strictUniq(Object.values(analysis.variables).flatMap((a) => a.map((v) => v.toArray()))));
|
|
4841
|
+
}
|
|
4842
|
+
/** Return an array of all expected context variables without their properties. */
|
|
4843
|
+
globalVariables(template, options = {}) {
|
|
4844
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4845
|
+
const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
|
|
4846
|
+
return Object.keys(analysis.globals);
|
|
4847
|
+
});
|
|
4848
|
+
}
|
|
4849
|
+
/** Return an array of all expected context variables without their properties. */
|
|
4850
|
+
globalVariablesSync(template, options = {}) {
|
|
4851
|
+
const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
|
|
4852
|
+
return Object.keys(analysis.globals);
|
|
4853
|
+
}
|
|
4854
|
+
/** Return an array of all expected context variables including their properties/paths. */
|
|
4855
|
+
globalFullVariables(template, options = {}) {
|
|
4856
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4857
|
+
const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
|
|
4858
|
+
return Array.from(new Set(Object.values(analysis.globals).flatMap((a) => a.map((v) => String(v)))));
|
|
4859
|
+
});
|
|
4860
|
+
}
|
|
4861
|
+
/** Return an array of all expected context variables including their properties/paths. */
|
|
4862
|
+
globalFullVariablesSync(template, options = {}) {
|
|
4863
|
+
const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
|
|
4864
|
+
return Array.from(new Set(Object.values(analysis.globals).flatMap((a) => a.map((v) => String(v)))));
|
|
4865
|
+
}
|
|
4866
|
+
/** Return an array of all expected context variables, each as an array of properties/segments. */
|
|
4867
|
+
globalVariableSegments(template, options = {}) {
|
|
4868
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4869
|
+
const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
|
|
4870
|
+
return Array.from(strictUniq(Object.values(analysis.globals).flatMap((a) => a.map((v) => v.toArray()))));
|
|
4871
|
+
});
|
|
4872
|
+
}
|
|
4873
|
+
/** Return an array of all expected context variables, each as an array of properties/segments. */
|
|
4874
|
+
globalVariableSegmentsSync(template, options = {}) {
|
|
4875
|
+
const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
|
|
4876
|
+
return Array.from(strictUniq(Object.values(analysis.globals).flatMap((a) => a.map((v) => v.toArray()))));
|
|
4877
|
+
}
|
|
3995
4878
|
}
|
|
3996
4879
|
|
|
3997
4880
|
/* istanbul ignore file */
|
|
3998
|
-
const version = '
|
|
4881
|
+
const version = '[VI]{version}[/VI]';
|
|
3999
4882
|
|
|
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,
|
|
4883
|
+
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, Parser, RawTag, RenderError, RenderTag, TablerowTag, Tag, TagToken, Token, TokenKind, TokenizationError, Tokenizer, typeGuards as TypeGuards, UndefinedVariableError, UnlessTag, Value, Variable, analyze, analyzeSync, assert, createTrie, defaultOperators, defaultOptions, evalQuotedToken, evalToken, filters, isFalsy, isTruthy, tags, toPromise, toValue, toValueSync, version };
|