@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,11 +1,12 @@
|
|
|
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
|
import { PassThrough } from 'stream';
|
|
7
7
|
import { sep, extname, resolve as resolve$1, dirname as dirname$1 } from 'path';
|
|
8
8
|
import { statSync, readFileSync as readFileSync$1, stat, readFile as readFile$1 } from 'fs';
|
|
9
|
+
import { createRequire } from 'module';
|
|
9
10
|
|
|
10
11
|
class Token {
|
|
11
12
|
constructor(kind, input, begin, end, file) {
|
|
@@ -102,6 +103,10 @@ function toArray(val) {
|
|
|
102
103
|
function toValue(value) {
|
|
103
104
|
return (value instanceof Drop && isFunction(value.valueOf)) ? value.valueOf() : value;
|
|
104
105
|
}
|
|
106
|
+
function toNumber(value) {
|
|
107
|
+
value = Number(value);
|
|
108
|
+
return isNaN(value) ? 0 : value;
|
|
109
|
+
}
|
|
105
110
|
function isNumber(value) {
|
|
106
111
|
return typeof value === 'number';
|
|
107
112
|
}
|
|
@@ -120,6 +125,9 @@ function isArray(value) {
|
|
|
120
125
|
// be compatible with IE 8
|
|
121
126
|
return toString$1.call(value) === '[object Array]';
|
|
122
127
|
}
|
|
128
|
+
function isArrayLike(value) {
|
|
129
|
+
return value && isNumber(value.length);
|
|
130
|
+
}
|
|
123
131
|
function isIterable(value) {
|
|
124
132
|
return isObject(value) && Symbol.iterator in value;
|
|
125
133
|
}
|
|
@@ -201,14 +209,25 @@ function caseInsensitiveCompare(a, b) {
|
|
|
201
209
|
return 0;
|
|
202
210
|
}
|
|
203
211
|
function argumentsToValue(fn) {
|
|
204
|
-
return (...args)
|
|
212
|
+
return function (...args) { return fn.call(this, ...args.map(toValue)); };
|
|
205
213
|
}
|
|
206
214
|
function escapeRegExp(text) {
|
|
207
215
|
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
|
216
|
+
}
|
|
217
|
+
/** Return an array containing unique elements from _array_. Works with nested arrays and objects. */
|
|
218
|
+
function* strictUniq(array) {
|
|
219
|
+
const seen = new Set();
|
|
220
|
+
for (const element of array) {
|
|
221
|
+
const key = JSON.stringify(element);
|
|
222
|
+
if (!seen.has(key)) {
|
|
223
|
+
seen.add(key);
|
|
224
|
+
yield element;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
208
227
|
}
|
|
209
228
|
|
|
210
229
|
/**
|
|
211
|
-
* targeting ES5, extends Error won't create a proper prototype chain, need a trait to
|
|
230
|
+
* targeting ES5, extends Error won't create a proper prototype chain, need a trait to keep track of classes
|
|
212
231
|
*/
|
|
213
232
|
const TRAIT = '__liquidClass__';
|
|
214
233
|
class LiquidError extends Error {
|
|
@@ -262,6 +281,19 @@ class RenderError extends LiquidError {
|
|
|
262
281
|
return obj.name === 'RenderError';
|
|
263
282
|
}
|
|
264
283
|
}
|
|
284
|
+
class LiquidErrors extends LiquidError {
|
|
285
|
+
constructor(errors) {
|
|
286
|
+
super(errors[0], errors[0].token);
|
|
287
|
+
this.errors = errors;
|
|
288
|
+
this.name = 'LiquidErrors';
|
|
289
|
+
const s = errors.length > 1 ? 's' : '';
|
|
290
|
+
this.message = `${errors.length} error${s} found`;
|
|
291
|
+
super.update();
|
|
292
|
+
}
|
|
293
|
+
static is(obj) {
|
|
294
|
+
return obj.name === 'LiquidErrors';
|
|
295
|
+
}
|
|
296
|
+
}
|
|
265
297
|
class UndefinedVariableError extends LiquidError {
|
|
266
298
|
constructor(err, token) {
|
|
267
299
|
super(err, token);
|
|
@@ -340,6 +372,9 @@ function assert(predicate, message) {
|
|
|
340
372
|
: (message || `expect ${predicate} to be true`);
|
|
341
373
|
throw new AssertionError(msg);
|
|
342
374
|
}
|
|
375
|
+
}
|
|
376
|
+
function assertEmpty(predicate, message = `unexpected ${JSON.stringify(predicate)}`) {
|
|
377
|
+
assert(!predicate, message);
|
|
343
378
|
}
|
|
344
379
|
|
|
345
380
|
class NullDrop extends Drop {
|
|
@@ -389,6 +424,9 @@ class EmptyDrop extends Drop {
|
|
|
389
424
|
valueOf() {
|
|
390
425
|
return '';
|
|
391
426
|
}
|
|
427
|
+
static is(value) {
|
|
428
|
+
return value instanceof EmptyDrop;
|
|
429
|
+
}
|
|
392
430
|
}
|
|
393
431
|
|
|
394
432
|
class BlankDrop extends EmptyDrop {
|
|
@@ -401,6 +439,9 @@ class BlankDrop extends EmptyDrop {
|
|
|
401
439
|
return /^\s*$/.test(value);
|
|
402
440
|
return super.equals(value);
|
|
403
441
|
}
|
|
442
|
+
static is(value) {
|
|
443
|
+
return value instanceof BlankDrop;
|
|
444
|
+
}
|
|
404
445
|
}
|
|
405
446
|
|
|
406
447
|
class ForloopDrop extends Drop {
|
|
@@ -453,7 +494,12 @@ class BlockDrop extends Drop {
|
|
|
453
494
|
}
|
|
454
495
|
|
|
455
496
|
function isComparable(arg) {
|
|
456
|
-
return arg &&
|
|
497
|
+
return (arg &&
|
|
498
|
+
isFunction(arg.equals) &&
|
|
499
|
+
isFunction(arg.gt) &&
|
|
500
|
+
isFunction(arg.geq) &&
|
|
501
|
+
isFunction(arg.lt) &&
|
|
502
|
+
isFunction(arg.leq));
|
|
457
503
|
}
|
|
458
504
|
|
|
459
505
|
const nil = new NullDrop();
|
|
@@ -573,18 +619,6 @@ function toValueSync(val) {
|
|
|
573
619
|
}
|
|
574
620
|
|
|
575
621
|
const rFormat = /%([-_0^#:]+)?(\d+)?([EO])?(.)/;
|
|
576
|
-
const monthNames = [
|
|
577
|
-
'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
|
|
578
|
-
'September', 'October', 'November', 'December'
|
|
579
|
-
];
|
|
580
|
-
const dayNames = [
|
|
581
|
-
'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'
|
|
582
|
-
];
|
|
583
|
-
const monthNamesShort = monthNames.map(abbr);
|
|
584
|
-
const dayNamesShort = dayNames.map(abbr);
|
|
585
|
-
function abbr(str) {
|
|
586
|
-
return str.slice(0, 3);
|
|
587
|
-
}
|
|
588
622
|
// prototype extensions
|
|
589
623
|
function daysInMonth(d) {
|
|
590
624
|
const feb = isLeapYear(d) ? 29 : 28;
|
|
@@ -639,19 +673,7 @@ const padWidths = {
|
|
|
639
673
|
U: 2,
|
|
640
674
|
W: 2
|
|
641
675
|
};
|
|
642
|
-
|
|
643
|
-
const padChars = {
|
|
644
|
-
a: ' ',
|
|
645
|
-
A: ' ',
|
|
646
|
-
b: ' ',
|
|
647
|
-
B: ' ',
|
|
648
|
-
c: ' ',
|
|
649
|
-
e: ' ',
|
|
650
|
-
k: ' ',
|
|
651
|
-
l: ' ',
|
|
652
|
-
p: ' ',
|
|
653
|
-
P: ' '
|
|
654
|
-
};
|
|
676
|
+
const padSpaceChars = new Set('aAbBceklpP');
|
|
655
677
|
function getTimezoneOffset(d, opts) {
|
|
656
678
|
const nOffset = Math.abs(d.getTimezoneOffset());
|
|
657
679
|
const h = Math.floor(nOffset / 60);
|
|
@@ -662,10 +684,10 @@ function getTimezoneOffset(d, opts) {
|
|
|
662
684
|
padStart(m, 2, '0');
|
|
663
685
|
}
|
|
664
686
|
const formatCodes = {
|
|
665
|
-
a: (d) =>
|
|
666
|
-
A: (d) =>
|
|
667
|
-
b: (d) =>
|
|
668
|
-
B: (d) =>
|
|
687
|
+
a: (d) => d.getShortWeekdayName(),
|
|
688
|
+
A: (d) => d.getLongWeekdayName(),
|
|
689
|
+
b: (d) => d.getShortMonthName(),
|
|
690
|
+
B: (d) => d.getLongMonthName(),
|
|
669
691
|
c: (d) => d.toLocaleString(),
|
|
670
692
|
C: (d) => century(d),
|
|
671
693
|
d: (d) => d.getDate(),
|
|
@@ -697,12 +719,7 @@ const formatCodes = {
|
|
|
697
719
|
y: (d) => d.getFullYear().toString().slice(2, 4),
|
|
698
720
|
Y: (d) => d.getFullYear(),
|
|
699
721
|
z: getTimezoneOffset,
|
|
700
|
-
Z: (d, opts) =>
|
|
701
|
-
if (d.getTimezoneName) {
|
|
702
|
-
return d.getTimezoneName() || getTimezoneOffset(d, opts);
|
|
703
|
-
}
|
|
704
|
-
return (typeof Intl !== 'undefined' ? Intl.DateTimeFormat().resolvedOptions().timeZone : '');
|
|
705
|
-
},
|
|
722
|
+
Z: (d, opts) => d.getTimeZoneName() || getTimezoneOffset(d, opts),
|
|
706
723
|
't': () => '\t',
|
|
707
724
|
'n': () => '\n',
|
|
708
725
|
'%': () => '%'
|
|
@@ -728,7 +745,7 @@ function format(d, match) {
|
|
|
728
745
|
for (const flag of flagStr)
|
|
729
746
|
flags[flag] = true;
|
|
730
747
|
let ret = String(convert(d, { flags, width, modifier }));
|
|
731
|
-
let padChar =
|
|
748
|
+
let padChar = padSpaceChars.has(conversion) ? ' ' : '0';
|
|
732
749
|
let padWidth = width || padWidths[conversion] || 0;
|
|
733
750
|
if (flags['^'])
|
|
734
751
|
ret = ret.toUpperCase();
|
|
@@ -743,9 +760,26 @@ function format(d, match) {
|
|
|
743
760
|
return padStart(ret, padWidth, padChar);
|
|
744
761
|
}
|
|
745
762
|
|
|
763
|
+
function getDateTimeFormat() {
|
|
764
|
+
return (typeof Intl !== 'undefined' ? Intl.DateTimeFormat : undefined);
|
|
765
|
+
}
|
|
766
|
+
|
|
746
767
|
// one minute in milliseconds
|
|
747
768
|
const OneMinute = 60000;
|
|
748
|
-
|
|
769
|
+
/**
|
|
770
|
+
* Need support both ISO8601 and RFC2822 as in major browsers & NodeJS
|
|
771
|
+
* RFC2822: https://datatracker.ietf.org/doc/html/rfc2822#section-3.3
|
|
772
|
+
*/
|
|
773
|
+
const TIMEZONE_PATTERN = /([zZ]|([+-])(\d{2}):?(\d{2}))$/;
|
|
774
|
+
const monthNames = [
|
|
775
|
+
'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
|
|
776
|
+
'September', 'October', 'November', 'December'
|
|
777
|
+
];
|
|
778
|
+
const monthNamesShort = monthNames.map(name => name.slice(0, 3));
|
|
779
|
+
const dayNames = [
|
|
780
|
+
'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'
|
|
781
|
+
];
|
|
782
|
+
const dayNamesShort = dayNames.map(name => name.slice(0, 3));
|
|
749
783
|
/**
|
|
750
784
|
* A date implementation with timezone info, just like Ruby date
|
|
751
785
|
*
|
|
@@ -753,12 +787,16 @@ const ISO8601_TIMEZONE_PATTERN = /([zZ]|([+-])(\d{2}):(\d{2}))$/;
|
|
|
753
787
|
* - create a Date offset by it's timezone difference, avoiding overriding a bunch of methods
|
|
754
788
|
* - rewrite getTimezoneOffset() to trick strftime
|
|
755
789
|
*/
|
|
756
|
-
class
|
|
757
|
-
constructor(init, timezone) {
|
|
758
|
-
this.
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
this.
|
|
790
|
+
class LiquidDate {
|
|
791
|
+
constructor(init, locale, timezone) {
|
|
792
|
+
this.locale = locale;
|
|
793
|
+
this.DateTimeFormat = getDateTimeFormat();
|
|
794
|
+
this.date = new Date(init);
|
|
795
|
+
this.timezoneFixed = timezone !== undefined;
|
|
796
|
+
if (timezone === undefined) {
|
|
797
|
+
timezone = this.date.getTimezoneOffset();
|
|
798
|
+
}
|
|
799
|
+
this.timezoneOffset = isString(timezone) ? LiquidDate.getTimezoneOffset(timezone, this.date) : timezone;
|
|
762
800
|
this.timezoneName = isString(timezone) ? timezone : '';
|
|
763
801
|
const diff = (this.date.getTimezoneOffset() - this.timezoneOffset) * OneMinute;
|
|
764
802
|
const time = this.date.getTime() + diff;
|
|
@@ -806,8 +844,34 @@ class TimezoneDate {
|
|
|
806
844
|
getTimezoneOffset() {
|
|
807
845
|
return this.timezoneOffset;
|
|
808
846
|
}
|
|
809
|
-
|
|
810
|
-
|
|
847
|
+
getTimeZoneName() {
|
|
848
|
+
if (this.timezoneFixed)
|
|
849
|
+
return this.timezoneName;
|
|
850
|
+
if (!this.DateTimeFormat)
|
|
851
|
+
return;
|
|
852
|
+
return this.DateTimeFormat().resolvedOptions().timeZone;
|
|
853
|
+
}
|
|
854
|
+
getLongMonthName() {
|
|
855
|
+
var _a;
|
|
856
|
+
return (_a = this.format({ month: 'long' })) !== null && _a !== void 0 ? _a : monthNames[this.getMonth()];
|
|
857
|
+
}
|
|
858
|
+
getShortMonthName() {
|
|
859
|
+
var _a;
|
|
860
|
+
return (_a = this.format({ month: 'short' })) !== null && _a !== void 0 ? _a : monthNamesShort[this.getMonth()];
|
|
861
|
+
}
|
|
862
|
+
getLongWeekdayName() {
|
|
863
|
+
var _a;
|
|
864
|
+
return (_a = this.format({ weekday: 'long' })) !== null && _a !== void 0 ? _a : dayNames[this.displayDate.getDay()];
|
|
865
|
+
}
|
|
866
|
+
getShortWeekdayName() {
|
|
867
|
+
var _a;
|
|
868
|
+
return (_a = this.format({ weekday: 'short' })) !== null && _a !== void 0 ? _a : dayNamesShort[this.displayDate.getDay()];
|
|
869
|
+
}
|
|
870
|
+
valid() {
|
|
871
|
+
return !isNaN(this.getTime());
|
|
872
|
+
}
|
|
873
|
+
format(options) {
|
|
874
|
+
return this.DateTimeFormat && this.DateTimeFormat(this.locale, options).format(this.displayDate);
|
|
811
875
|
}
|
|
812
876
|
/**
|
|
813
877
|
* Create a Date object fixed to it's declared Timezone. Both
|
|
@@ -822,21 +886,21 @@ class TimezoneDate {
|
|
|
822
886
|
* we create a different Date to trick strftime, it's both simpler and more performant.
|
|
823
887
|
* Given that a template is expected to be parsed fewer times than rendered.
|
|
824
888
|
*/
|
|
825
|
-
static createDateFixedToTimezone(dateString) {
|
|
826
|
-
const m = dateString.match(
|
|
889
|
+
static createDateFixedToTimezone(dateString, locale) {
|
|
890
|
+
const m = dateString.match(TIMEZONE_PATTERN);
|
|
827
891
|
// representing a UTC timestamp
|
|
828
892
|
if (m && m[1] === 'Z') {
|
|
829
|
-
return new
|
|
893
|
+
return new LiquidDate(+new Date(dateString), locale, 0);
|
|
830
894
|
}
|
|
831
895
|
// has a timezone specified
|
|
832
896
|
if (m && m[2] && m[3] && m[4]) {
|
|
833
897
|
const [, , sign, hours, minutes] = m;
|
|
834
898
|
const offset = (sign === '+' ? -1 : 1) * (parseInt(hours, 10) * 60 + parseInt(minutes, 10));
|
|
835
|
-
return new
|
|
899
|
+
return new LiquidDate(+new Date(dateString), locale, offset);
|
|
836
900
|
}
|
|
837
|
-
return new
|
|
901
|
+
return new LiquidDate(dateString, locale);
|
|
838
902
|
}
|
|
839
|
-
static getTimezoneOffset(timezoneName, date
|
|
903
|
+
static getTimezoneOffset(timezoneName, date) {
|
|
840
904
|
const localDateString = date.toLocaleString('en-US', { timeZone: timezoneName });
|
|
841
905
|
const utcDateString = date.toLocaleString('en-US', { timeZone: 'UTC' });
|
|
842
906
|
const localDate = new Date(localDateString);
|
|
@@ -845,6 +909,23 @@ class TimezoneDate {
|
|
|
845
909
|
}
|
|
846
910
|
}
|
|
847
911
|
|
|
912
|
+
class Limiter {
|
|
913
|
+
constructor(resource, limit) {
|
|
914
|
+
this.base = 0;
|
|
915
|
+
this.message = `${resource} limit exceeded`;
|
|
916
|
+
this.limit = limit;
|
|
917
|
+
}
|
|
918
|
+
use(count) {
|
|
919
|
+
count = toNumber(count);
|
|
920
|
+
assert(this.base + count <= this.limit, this.message);
|
|
921
|
+
this.base += count;
|
|
922
|
+
}
|
|
923
|
+
check(count) {
|
|
924
|
+
count = toNumber(count);
|
|
925
|
+
assert(count <= this.limit, this.message);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
848
929
|
class DelimitedToken extends Token {
|
|
849
930
|
constructor(kind, [contentBegin, contentEnd], input, begin, end, trimLeft, trimRight, file) {
|
|
850
931
|
super(kind, input, begin, end, file);
|
|
@@ -925,16 +1006,6 @@ class IdentifierToken extends Token {
|
|
|
925
1006
|
this.file = file;
|
|
926
1007
|
this.content = this.getText();
|
|
927
1008
|
}
|
|
928
|
-
isNumber(allowSign = false) {
|
|
929
|
-
const begin = allowSign && TYPES[this.input.charCodeAt(this.begin)] & SIGN
|
|
930
|
-
? this.begin + 1
|
|
931
|
-
: this.begin;
|
|
932
|
-
for (let i = begin; i < this.end; i++) {
|
|
933
|
-
if (!(TYPES[this.input.charCodeAt(i)] & NUMBER))
|
|
934
|
-
return false;
|
|
935
|
-
}
|
|
936
|
-
return true;
|
|
937
|
-
}
|
|
938
1009
|
}
|
|
939
1010
|
|
|
940
1011
|
class LiteralToken extends Token {
|
|
@@ -1102,7 +1173,9 @@ class LiquidTagToken extends DelimitedToken {
|
|
|
1102
1173
|
this.name = this.tokenizer.readTagName();
|
|
1103
1174
|
this.tokenizer.assert(this.name, 'illegal liquid tag syntax');
|
|
1104
1175
|
this.tokenizer.skipBlank();
|
|
1105
|
-
|
|
1176
|
+
}
|
|
1177
|
+
get args() {
|
|
1178
|
+
return this.tokenizer.input.slice(this.tokenizer.p, this.contentRange[1]);
|
|
1106
1179
|
}
|
|
1107
1180
|
}
|
|
1108
1181
|
|
|
@@ -1123,6 +1196,15 @@ class FilteredValueToken extends Token {
|
|
|
1123
1196
|
}
|
|
1124
1197
|
}
|
|
1125
1198
|
|
|
1199
|
+
const polyfill = {
|
|
1200
|
+
now: () => Date.now()
|
|
1201
|
+
};
|
|
1202
|
+
function getPerformance() {
|
|
1203
|
+
return (typeof global === 'object' && global.performance) ||
|
|
1204
|
+
(typeof window === 'object' && window.performance) ||
|
|
1205
|
+
polyfill;
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1126
1208
|
class SimpleEmitter {
|
|
1127
1209
|
constructor() {
|
|
1128
1210
|
this.buffer = '';
|
|
@@ -1178,20 +1260,28 @@ class Render {
|
|
|
1178
1260
|
if (!emitter) {
|
|
1179
1261
|
emitter = ctx.opts.keepOutputType ? new KeepingTypeEmitter() : new SimpleEmitter();
|
|
1180
1262
|
}
|
|
1263
|
+
const errors = [];
|
|
1181
1264
|
for (const tpl of templates) {
|
|
1265
|
+
ctx.renderLimit.check(getPerformance().now());
|
|
1182
1266
|
try {
|
|
1183
1267
|
// if tpl.render supports emitter, it'll return empty `html`
|
|
1184
1268
|
const html = yield tpl.render(ctx, emitter);
|
|
1185
1269
|
// if not, it'll return an `html`, write to the emitter for it
|
|
1186
1270
|
html && emitter.write(html);
|
|
1187
|
-
if (
|
|
1271
|
+
if (ctx.breakCalled || ctx.continueCalled)
|
|
1188
1272
|
break;
|
|
1189
1273
|
}
|
|
1190
1274
|
catch (e) {
|
|
1191
|
-
const err =
|
|
1192
|
-
|
|
1275
|
+
const err = LiquidError.is(e) ? e : new RenderError(e, tpl);
|
|
1276
|
+
if (ctx.opts.catchAllErrors)
|
|
1277
|
+
errors.push(err);
|
|
1278
|
+
else
|
|
1279
|
+
throw err;
|
|
1193
1280
|
}
|
|
1194
1281
|
}
|
|
1282
|
+
if (errors.length) {
|
|
1283
|
+
throw new LiquidErrors(errors);
|
|
1284
|
+
}
|
|
1195
1285
|
return emitter.buffer;
|
|
1196
1286
|
}
|
|
1197
1287
|
}
|
|
@@ -1262,6 +1352,7 @@ function evalQuotedToken(token) {
|
|
|
1262
1352
|
function* evalRangeToken(token, ctx) {
|
|
1263
1353
|
const low = yield evalToken(token.lhs, ctx);
|
|
1264
1354
|
const high = yield evalToken(token.rhs, ctx);
|
|
1355
|
+
ctx.memoryLimit.use(high - low + 1);
|
|
1265
1356
|
return range(+low, +high + 1);
|
|
1266
1357
|
}
|
|
1267
1358
|
function* toPostfix(tokens) {
|
|
@@ -1285,6 +1376,7 @@ function isTruthy(val, ctx) {
|
|
|
1285
1376
|
return !isFalsy(val, ctx);
|
|
1286
1377
|
}
|
|
1287
1378
|
function isFalsy(val, ctx) {
|
|
1379
|
+
val = toValue(val);
|
|
1288
1380
|
if (ctx.opts.jsTruthy) {
|
|
1289
1381
|
return !val;
|
|
1290
1382
|
}
|
|
@@ -1352,6 +1444,9 @@ function arrayEquals(lhs, rhs) {
|
|
|
1352
1444
|
if (lhs.length !== rhs.length)
|
|
1353
1445
|
return false;
|
|
1354
1446
|
return !lhs.some((value, i) => !equals(value, rhs[i]));
|
|
1447
|
+
}
|
|
1448
|
+
function arrayIncludes(arr, item) {
|
|
1449
|
+
return arr.some(value => equals(value, item));
|
|
1355
1450
|
}
|
|
1356
1451
|
|
|
1357
1452
|
class Node {
|
|
@@ -1412,7 +1507,10 @@ class LRU {
|
|
|
1412
1507
|
}
|
|
1413
1508
|
}
|
|
1414
1509
|
|
|
1415
|
-
|
|
1510
|
+
function requireResolve (file) {
|
|
1511
|
+
const require = createRequire(process.cwd() + '/');
|
|
1512
|
+
return require.resolve(file)
|
|
1513
|
+
}
|
|
1416
1514
|
|
|
1417
1515
|
const statAsync = promisify(stat);
|
|
1418
1516
|
const readFileAsync = promisify(readFile$1);
|
|
@@ -1531,22 +1629,30 @@ const unescapeMap = {
|
|
|
1531
1629
|
''': "'"
|
|
1532
1630
|
};
|
|
1533
1631
|
function escape(str) {
|
|
1534
|
-
|
|
1632
|
+
str = stringify(str);
|
|
1633
|
+
this.context.memoryLimit.use(str.length);
|
|
1634
|
+
return str.replace(/&|<|>|"|'/g, m => escapeMap[m]);
|
|
1535
1635
|
}
|
|
1536
1636
|
function xml_escape(str) {
|
|
1537
|
-
return escape(str);
|
|
1637
|
+
return escape.call(this, str);
|
|
1538
1638
|
}
|
|
1539
1639
|
function unescape(str) {
|
|
1540
|
-
|
|
1640
|
+
str = stringify(str);
|
|
1641
|
+
this.context.memoryLimit.use(str.length);
|
|
1642
|
+
return str.replace(/&(amp|lt|gt|#34|#39);/g, m => unescapeMap[m]);
|
|
1541
1643
|
}
|
|
1542
1644
|
function escape_once(str) {
|
|
1543
|
-
return escape(unescape(
|
|
1645
|
+
return escape.call(this, unescape.call(this, str));
|
|
1544
1646
|
}
|
|
1545
1647
|
function newline_to_br(v) {
|
|
1546
|
-
|
|
1648
|
+
const str = stringify(v);
|
|
1649
|
+
this.context.memoryLimit.use(str.length);
|
|
1650
|
+
return str.replace(/\r?\n/gm, '<br />\n');
|
|
1547
1651
|
}
|
|
1548
1652
|
function strip_html(v) {
|
|
1549
|
-
|
|
1653
|
+
const str = stringify(v);
|
|
1654
|
+
this.context.memoryLimit.use(str.length);
|
|
1655
|
+
return str.replace(/<script[\s\S]*?<\/script>|<style[\s\S]*?<\/style>|<.*?>|<!--[\s\S]*?-->/g, '');
|
|
1550
1656
|
}
|
|
1551
1657
|
|
|
1552
1658
|
var htmlFilters = /*#__PURE__*/Object.freeze({
|
|
@@ -1558,18 +1664,68 @@ var htmlFilters = /*#__PURE__*/Object.freeze({
|
|
|
1558
1664
|
strip_html: strip_html
|
|
1559
1665
|
});
|
|
1560
1666
|
|
|
1667
|
+
class MapFS {
|
|
1668
|
+
constructor(mapping) {
|
|
1669
|
+
this.mapping = mapping;
|
|
1670
|
+
this.sep = '/';
|
|
1671
|
+
}
|
|
1672
|
+
exists(filepath) {
|
|
1673
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1674
|
+
return this.existsSync(filepath);
|
|
1675
|
+
});
|
|
1676
|
+
}
|
|
1677
|
+
existsSync(filepath) {
|
|
1678
|
+
return !isNil(this.mapping[filepath]);
|
|
1679
|
+
}
|
|
1680
|
+
readFile(filepath) {
|
|
1681
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1682
|
+
return this.readFileSync(filepath);
|
|
1683
|
+
});
|
|
1684
|
+
}
|
|
1685
|
+
readFileSync(filepath) {
|
|
1686
|
+
const content = this.mapping[filepath];
|
|
1687
|
+
if (isNil(content))
|
|
1688
|
+
throw new Error(`ENOENT: ${filepath}`);
|
|
1689
|
+
return content;
|
|
1690
|
+
}
|
|
1691
|
+
dirname(filepath) {
|
|
1692
|
+
const segments = filepath.split(this.sep);
|
|
1693
|
+
segments.pop();
|
|
1694
|
+
return segments.join(this.sep);
|
|
1695
|
+
}
|
|
1696
|
+
resolve(dir, file, ext) {
|
|
1697
|
+
file += ext;
|
|
1698
|
+
if (dir === '.')
|
|
1699
|
+
return file;
|
|
1700
|
+
const segments = dir.split(/\/+/);
|
|
1701
|
+
for (const segment of file.split(this.sep)) {
|
|
1702
|
+
if (segment === '.' || segment === '')
|
|
1703
|
+
continue;
|
|
1704
|
+
else if (segment === '..') {
|
|
1705
|
+
if (segments.length > 1 || segments[0] !== '')
|
|
1706
|
+
segments.pop();
|
|
1707
|
+
}
|
|
1708
|
+
else
|
|
1709
|
+
segments.push(segment);
|
|
1710
|
+
}
|
|
1711
|
+
return segments.join(this.sep);
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1561
1715
|
const defaultOptions = {
|
|
1562
1716
|
root: ['.'],
|
|
1563
1717
|
layouts: ['.'],
|
|
1564
1718
|
partials: ['.'],
|
|
1565
1719
|
relativeReference: true,
|
|
1566
1720
|
jekyllInclude: false,
|
|
1721
|
+
keyValueSeparator: ':',
|
|
1567
1722
|
cache: undefined,
|
|
1568
1723
|
extname: '',
|
|
1569
1724
|
fs: fs,
|
|
1570
1725
|
dynamicPartials: true,
|
|
1571
1726
|
jsTruthy: false,
|
|
1572
1727
|
dateFormat: '%A, %B %-e, %Y at %-l:%M %P %z',
|
|
1728
|
+
locale: '',
|
|
1573
1729
|
trimTagRight: false,
|
|
1574
1730
|
trimTagLeft: false,
|
|
1575
1731
|
trimOutputRight: false,
|
|
@@ -1586,9 +1742,13 @@ const defaultOptions = {
|
|
|
1586
1742
|
lenientIf: false,
|
|
1587
1743
|
globals: {},
|
|
1588
1744
|
keepOutputType: false,
|
|
1589
|
-
operators: defaultOperators
|
|
1745
|
+
operators: defaultOperators,
|
|
1746
|
+
memoryLimit: Infinity,
|
|
1747
|
+
parseLimit: Infinity,
|
|
1748
|
+
renderLimit: Infinity
|
|
1590
1749
|
};
|
|
1591
1750
|
function normalize(options) {
|
|
1751
|
+
var _a, _b;
|
|
1592
1752
|
if (options.hasOwnProperty('root')) {
|
|
1593
1753
|
if (!options.hasOwnProperty('partials'))
|
|
1594
1754
|
options.partials = options.root;
|
|
@@ -1614,6 +1774,14 @@ function normalize(options) {
|
|
|
1614
1774
|
options.partials = normalizeDirectoryList(options.partials);
|
|
1615
1775
|
options.layouts = normalizeDirectoryList(options.layouts);
|
|
1616
1776
|
options.outputEscape = options.outputEscape && getOutputEscapeFunction(options.outputEscape);
|
|
1777
|
+
if (!options.locale) {
|
|
1778
|
+
options.locale = (_b = (_a = getDateTimeFormat()) === null || _a === void 0 ? void 0 : _a().resolvedOptions().locale) !== null && _b !== void 0 ? _b : 'en-US';
|
|
1779
|
+
}
|
|
1780
|
+
if (options.templates) {
|
|
1781
|
+
options.fs = new MapFS(options.templates);
|
|
1782
|
+
options.relativeReference = true;
|
|
1783
|
+
options.root = options.partials = options.layouts = '.';
|
|
1784
|
+
}
|
|
1617
1785
|
return options;
|
|
1618
1786
|
}
|
|
1619
1787
|
function getOutputEscapeFunction(nameOrFunction) {
|
|
@@ -1740,9 +1908,7 @@ class Tokenizer {
|
|
|
1740
1908
|
this.skipBlank();
|
|
1741
1909
|
if (this.end())
|
|
1742
1910
|
return null;
|
|
1743
|
-
this.assert(this.
|
|
1744
|
-
this.p++;
|
|
1745
|
-
const begin = this.p;
|
|
1911
|
+
this.assert(this.read() === '|', `expected "|" before filter`);
|
|
1746
1912
|
const name = this.readIdentifier();
|
|
1747
1913
|
if (!name.size()) {
|
|
1748
1914
|
this.assert(this.end(), `expected filter name`);
|
|
@@ -1763,7 +1929,7 @@ class Tokenizer {
|
|
|
1763
1929
|
else {
|
|
1764
1930
|
throw this.error('expected ":" after filter name');
|
|
1765
1931
|
}
|
|
1766
|
-
return new FilterToken(name.getText(), args, this.input, begin, this.p, this.file);
|
|
1932
|
+
return new FilterToken(name.getText(), args, this.input, name.begin, this.p, this.file);
|
|
1767
1933
|
}
|
|
1768
1934
|
readFilterArg() {
|
|
1769
1935
|
const key = this.readValue();
|
|
@@ -1804,7 +1970,7 @@ class Tokenizer {
|
|
|
1804
1970
|
}
|
|
1805
1971
|
return new HTMLToken(this.input, begin, this.p, this.file);
|
|
1806
1972
|
}
|
|
1807
|
-
readTagToken(options
|
|
1973
|
+
readTagToken(options) {
|
|
1808
1974
|
const { file, input } = this;
|
|
1809
1975
|
const begin = this.p;
|
|
1810
1976
|
if (this.readToDelimiter(options.tagDelimiterRight) === -1) {
|
|
@@ -1935,7 +2101,7 @@ class Tokenizer {
|
|
|
1935
2101
|
return;
|
|
1936
2102
|
let value;
|
|
1937
2103
|
this.skipBlank();
|
|
1938
|
-
const sep = jekyllStyle ? '=' : ':';
|
|
2104
|
+
const sep = isString(jekyllStyle) ? jekyllStyle : (jekyllStyle ? '=' : ':');
|
|
1939
2105
|
if (this.peek() === sep) {
|
|
1940
2106
|
++this.p;
|
|
1941
2107
|
value = this.readValue();
|
|
@@ -1951,6 +2117,9 @@ class Tokenizer {
|
|
|
1951
2117
|
end() {
|
|
1952
2118
|
return this.p >= this.N;
|
|
1953
2119
|
}
|
|
2120
|
+
read() {
|
|
2121
|
+
return this.input[this.p++];
|
|
2122
|
+
}
|
|
1954
2123
|
readTo(end) {
|
|
1955
2124
|
while (this.p < this.N) {
|
|
1956
2125
|
++this.p;
|
|
@@ -2174,9 +2343,9 @@ class Tag extends TemplateImpl {
|
|
|
2174
2343
|
* hash['reversed'] === undefined
|
|
2175
2344
|
*/
|
|
2176
2345
|
class Hash {
|
|
2177
|
-
constructor(
|
|
2346
|
+
constructor(input, jekyllStyle) {
|
|
2178
2347
|
this.hash = {};
|
|
2179
|
-
const tokenizer = new Tokenizer(
|
|
2348
|
+
const tokenizer = input instanceof Tokenizer ? input : new Tokenizer(input, {});
|
|
2180
2349
|
for (const hash of tokenizer.readHashes(jekyllStyle)) {
|
|
2181
2350
|
this.hash[hash.name.content] = hash.value;
|
|
2182
2351
|
}
|
|
@@ -2199,7 +2368,7 @@ function createTagClass(options) {
|
|
|
2199
2368
|
}
|
|
2200
2369
|
}
|
|
2201
2370
|
*render(ctx, emitter) {
|
|
2202
|
-
const hash = (yield new Hash(this.token.args).render(ctx));
|
|
2371
|
+
const hash = (yield new Hash(this.token.args, ctx.opts.keyValueSeparator).render(ctx));
|
|
2203
2372
|
return yield options.render.call(this, ctx, emitter, hash);
|
|
2204
2373
|
}
|
|
2205
2374
|
};
|
|
@@ -2210,13 +2379,14 @@ function isKeyValuePair(arr) {
|
|
|
2210
2379
|
}
|
|
2211
2380
|
|
|
2212
2381
|
class Filter {
|
|
2213
|
-
constructor(
|
|
2214
|
-
this.
|
|
2382
|
+
constructor(token, options, liquid) {
|
|
2383
|
+
this.token = token;
|
|
2384
|
+
this.name = token.name;
|
|
2215
2385
|
this.handler = isFunction(options)
|
|
2216
2386
|
? options
|
|
2217
2387
|
: (isFunction(options === null || options === void 0 ? void 0 : options.handler) ? options.handler : identify);
|
|
2218
2388
|
this.raw = !isFunction(options) && !!(options === null || options === void 0 ? void 0 : options.raw);
|
|
2219
|
-
this.args = args;
|
|
2389
|
+
this.args = token.args;
|
|
2220
2390
|
this.liquid = liquid;
|
|
2221
2391
|
}
|
|
2222
2392
|
*render(value, context) {
|
|
@@ -2227,7 +2397,7 @@ class Filter {
|
|
|
2227
2397
|
else
|
|
2228
2398
|
argv.push(yield evalToken(arg, context));
|
|
2229
2399
|
}
|
|
2230
|
-
return yield this.handler.apply({ context, liquid: this.liquid }, [value, ...argv]);
|
|
2400
|
+
return yield this.handler.apply({ context, token: this.token, liquid: this.liquid }, [value, ...argv]);
|
|
2231
2401
|
}
|
|
2232
2402
|
}
|
|
2233
2403
|
|
|
@@ -2241,7 +2411,7 @@ class Value {
|
|
|
2241
2411
|
? new Tokenizer(input, liquid.options.operators).readFilteredValue()
|
|
2242
2412
|
: input;
|
|
2243
2413
|
this.initial = token.initial;
|
|
2244
|
-
this.filters = token.filters.map(
|
|
2414
|
+
this.filters = token.filters.map(token => new Filter(token, this.getFilter(liquid, token.name), liquid));
|
|
2245
2415
|
}
|
|
2246
2416
|
*value(ctx, lenient) {
|
|
2247
2417
|
lenient = lenient || (ctx.opts.lenientIf && this.filters.length > 0 && this.filters[0].name === 'default');
|
|
@@ -2267,13 +2437,17 @@ class Output extends TemplateImpl {
|
|
|
2267
2437
|
const filters = this.value.filters;
|
|
2268
2438
|
const outputEscape = liquid.options.outputEscape;
|
|
2269
2439
|
if (!((_a = filters[filters.length - 1]) === null || _a === void 0 ? void 0 : _a.raw) && outputEscape) {
|
|
2270
|
-
|
|
2440
|
+
const token = new FilterToken(toString.call(outputEscape), [], '', 0, 0);
|
|
2441
|
+
filters.push(new Filter(token, outputEscape, liquid));
|
|
2271
2442
|
}
|
|
2272
2443
|
}
|
|
2273
2444
|
*render(ctx, emitter) {
|
|
2274
2445
|
const val = yield this.value.value(ctx, false);
|
|
2275
2446
|
emitter.write(val);
|
|
2276
2447
|
}
|
|
2448
|
+
*arguments() {
|
|
2449
|
+
yield this.value;
|
|
2450
|
+
}
|
|
2277
2451
|
}
|
|
2278
2452
|
|
|
2279
2453
|
class HTML extends TemplateImpl {
|
|
@@ -2286,6 +2460,336 @@ class HTML extends TemplateImpl {
|
|
|
2286
2460
|
}
|
|
2287
2461
|
}
|
|
2288
2462
|
|
|
2463
|
+
/**
|
|
2464
|
+
* A variable's segments and location, which can be coerced to a string.
|
|
2465
|
+
*/
|
|
2466
|
+
class Variable {
|
|
2467
|
+
constructor(segments, location) {
|
|
2468
|
+
this.segments = segments;
|
|
2469
|
+
this.location = location;
|
|
2470
|
+
}
|
|
2471
|
+
toString() {
|
|
2472
|
+
return segmentsString(this.segments, true);
|
|
2473
|
+
}
|
|
2474
|
+
/** Return this variable's segments as an array, possibly with nested arrays for nested paths. */
|
|
2475
|
+
toArray() {
|
|
2476
|
+
function* _visit(...segments) {
|
|
2477
|
+
for (const segment of segments) {
|
|
2478
|
+
if (segment instanceof Variable) {
|
|
2479
|
+
yield Array.from(_visit(...segment.segments));
|
|
2480
|
+
}
|
|
2481
|
+
else {
|
|
2482
|
+
yield segment;
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
return Array.from(_visit(...this.segments));
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2489
|
+
/**
|
|
2490
|
+
* Group variables by the string representation of their root segment.
|
|
2491
|
+
*/
|
|
2492
|
+
class VariableMap {
|
|
2493
|
+
constructor() {
|
|
2494
|
+
this.map = new Map();
|
|
2495
|
+
}
|
|
2496
|
+
get(key) {
|
|
2497
|
+
const k = segmentsString([key.segments[0]]);
|
|
2498
|
+
if (!this.map.has(k)) {
|
|
2499
|
+
this.map.set(k, []);
|
|
2500
|
+
}
|
|
2501
|
+
return this.map.get(k);
|
|
2502
|
+
}
|
|
2503
|
+
has(key) {
|
|
2504
|
+
return this.map.has(segmentsString([key.segments[0]]));
|
|
2505
|
+
}
|
|
2506
|
+
push(variable) {
|
|
2507
|
+
this.get(variable).push(variable);
|
|
2508
|
+
}
|
|
2509
|
+
asObject() {
|
|
2510
|
+
return Object.fromEntries(this.map);
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
const defaultStaticAnalysisOptions = {
|
|
2514
|
+
partials: true
|
|
2515
|
+
};
|
|
2516
|
+
function* _analyze(templates, partials, sync) {
|
|
2517
|
+
const variables = new VariableMap();
|
|
2518
|
+
const globals = new VariableMap();
|
|
2519
|
+
const locals = new VariableMap();
|
|
2520
|
+
const rootScope = new DummyScope(new Set());
|
|
2521
|
+
// Names of partial templates that we've already analyzed.
|
|
2522
|
+
const seen = new Set();
|
|
2523
|
+
function updateVariables(variable, scope) {
|
|
2524
|
+
variables.push(variable);
|
|
2525
|
+
const aliased = scope.alias(variable);
|
|
2526
|
+
if (aliased !== undefined) {
|
|
2527
|
+
const root = aliased.segments[0];
|
|
2528
|
+
// TODO: What if a a template renders a rendered template? Do we need scope.parent?
|
|
2529
|
+
if (isString(root) && !rootScope.has(root)) {
|
|
2530
|
+
globals.push(aliased);
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2533
|
+
else {
|
|
2534
|
+
const root = variable.segments[0];
|
|
2535
|
+
if (isString(root) && !scope.has(root)) {
|
|
2536
|
+
globals.push(variable);
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
// Recurse for nested Variables
|
|
2540
|
+
for (const segment of variable.segments) {
|
|
2541
|
+
if (segment instanceof Variable) {
|
|
2542
|
+
updateVariables(segment, scope);
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
function* visit(template, scope) {
|
|
2547
|
+
if (template.arguments) {
|
|
2548
|
+
for (const arg of template.arguments()) {
|
|
2549
|
+
for (const variable of extractVariables(arg)) {
|
|
2550
|
+
updateVariables(variable, scope);
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
if (template.localScope) {
|
|
2555
|
+
for (const ident of template.localScope()) {
|
|
2556
|
+
scope.add(ident.content);
|
|
2557
|
+
scope.deleteAlias(ident.content);
|
|
2558
|
+
const [row, col] = ident.getPosition();
|
|
2559
|
+
locals.push(new Variable([ident.content], { row, col, file: ident.file }));
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
if (template.children) {
|
|
2563
|
+
if (template.partialScope) {
|
|
2564
|
+
const partial = template.partialScope();
|
|
2565
|
+
if (partial === undefined) {
|
|
2566
|
+
// Layouts, for example, can have children that are not partials.
|
|
2567
|
+
for (const child of (yield template.children(partials, sync))) {
|
|
2568
|
+
yield visit(child, scope);
|
|
2569
|
+
}
|
|
2570
|
+
return;
|
|
2571
|
+
}
|
|
2572
|
+
if (seen.has(partial.name))
|
|
2573
|
+
return;
|
|
2574
|
+
const partialScopeNames = new Set();
|
|
2575
|
+
const partialScope = partial.isolated
|
|
2576
|
+
? new DummyScope(partialScopeNames)
|
|
2577
|
+
: scope.push(partialScopeNames);
|
|
2578
|
+
for (const name of partial.scope) {
|
|
2579
|
+
if (isString(name)) {
|
|
2580
|
+
partialScopeNames.add(name);
|
|
2581
|
+
}
|
|
2582
|
+
else {
|
|
2583
|
+
const [alias, argument] = name;
|
|
2584
|
+
partialScopeNames.add(alias);
|
|
2585
|
+
const variables = Array.from(extractVariables(argument));
|
|
2586
|
+
if (variables.length) {
|
|
2587
|
+
partialScope.setAlias(alias, variables[0].segments);
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
for (const child of (yield template.children(partials, sync))) {
|
|
2592
|
+
yield visit(child, partialScope);
|
|
2593
|
+
seen.add(partial.name);
|
|
2594
|
+
}
|
|
2595
|
+
partialScope.pop();
|
|
2596
|
+
}
|
|
2597
|
+
else {
|
|
2598
|
+
if (template.blockScope) {
|
|
2599
|
+
scope.push(new Set(template.blockScope()));
|
|
2600
|
+
}
|
|
2601
|
+
for (const child of (yield template.children(partials, sync))) {
|
|
2602
|
+
yield visit(child, scope);
|
|
2603
|
+
}
|
|
2604
|
+
if (template.blockScope) {
|
|
2605
|
+
scope.pop();
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
2610
|
+
for (const template of templates) {
|
|
2611
|
+
yield visit(template, rootScope);
|
|
2612
|
+
}
|
|
2613
|
+
return {
|
|
2614
|
+
variables: variables.asObject(),
|
|
2615
|
+
globals: globals.asObject(),
|
|
2616
|
+
locals: locals.asObject()
|
|
2617
|
+
};
|
|
2618
|
+
}
|
|
2619
|
+
/**
|
|
2620
|
+
* Statically analyze a template and report variable usage.
|
|
2621
|
+
*/
|
|
2622
|
+
function analyze(template, options = {}) {
|
|
2623
|
+
const opts = Object.assign(Object.assign({}, defaultStaticAnalysisOptions), options);
|
|
2624
|
+
return toPromise(_analyze(template, opts.partials, false));
|
|
2625
|
+
}
|
|
2626
|
+
/**
|
|
2627
|
+
* Statically analyze a template and report variable usage.
|
|
2628
|
+
*/
|
|
2629
|
+
function analyzeSync(template, options = {}) {
|
|
2630
|
+
const opts = Object.assign(Object.assign({}, defaultStaticAnalysisOptions), options);
|
|
2631
|
+
return toValueSync(_analyze(template, opts.partials, true));
|
|
2632
|
+
}
|
|
2633
|
+
/**
|
|
2634
|
+
* A stack to manage scopes while traversing templates during static analysis.
|
|
2635
|
+
*/
|
|
2636
|
+
class DummyScope {
|
|
2637
|
+
constructor(globals) {
|
|
2638
|
+
this.stack = [{ names: globals, aliases: new Map() }];
|
|
2639
|
+
}
|
|
2640
|
+
/** Return true if `name` is in scope. */
|
|
2641
|
+
has(name) {
|
|
2642
|
+
for (const scope of this.stack) {
|
|
2643
|
+
if (scope.names.has(name)) {
|
|
2644
|
+
return true;
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
return false;
|
|
2648
|
+
}
|
|
2649
|
+
push(scope) {
|
|
2650
|
+
this.stack.push({ names: scope, aliases: new Map() });
|
|
2651
|
+
return this;
|
|
2652
|
+
}
|
|
2653
|
+
pop() {
|
|
2654
|
+
var _a;
|
|
2655
|
+
return (_a = this.stack.pop()) === null || _a === void 0 ? void 0 : _a.names;
|
|
2656
|
+
}
|
|
2657
|
+
// Add a name to the template scope.
|
|
2658
|
+
add(name) {
|
|
2659
|
+
this.stack[0].names.add(name);
|
|
2660
|
+
}
|
|
2661
|
+
/** Return the variable that `variable` aliases, or `variable` if it doesn't alias anything. */
|
|
2662
|
+
alias(variable) {
|
|
2663
|
+
const root = variable.segments[0];
|
|
2664
|
+
if (!isString(root))
|
|
2665
|
+
return undefined;
|
|
2666
|
+
const alias = this.getAlias(root);
|
|
2667
|
+
if (alias === undefined)
|
|
2668
|
+
return undefined;
|
|
2669
|
+
return new Variable([...alias, ...variable.segments.slice(1)], variable.location);
|
|
2670
|
+
}
|
|
2671
|
+
// TODO: `from` could be a path with multiple segments, like `include.x`.
|
|
2672
|
+
setAlias(from, to) {
|
|
2673
|
+
this.stack[this.stack.length - 1].aliases.set(from, to);
|
|
2674
|
+
}
|
|
2675
|
+
deleteAlias(name) {
|
|
2676
|
+
this.stack[this.stack.length - 1].aliases.delete(name);
|
|
2677
|
+
}
|
|
2678
|
+
getAlias(name) {
|
|
2679
|
+
for (const scope of this.stack) {
|
|
2680
|
+
if (scope.aliases.has(name)) {
|
|
2681
|
+
return scope.aliases.get(name);
|
|
2682
|
+
}
|
|
2683
|
+
// If a scope has defined `name`, then it masks aliases in parent scopes.
|
|
2684
|
+
if (scope.names.has(name)) {
|
|
2685
|
+
return undefined;
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
return undefined;
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
function* extractVariables(value) {
|
|
2692
|
+
if (isValueToken(value)) {
|
|
2693
|
+
yield* extractValueTokenVariables(value);
|
|
2694
|
+
}
|
|
2695
|
+
else if (value instanceof Value) {
|
|
2696
|
+
yield* extractFilteredValueVariables(value);
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
function* extractFilteredValueVariables(value) {
|
|
2700
|
+
for (const token of value.initial.postfix) {
|
|
2701
|
+
if (isValueToken(token)) {
|
|
2702
|
+
yield* extractValueTokenVariables(token);
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
for (const filter of value.filters) {
|
|
2706
|
+
for (const arg of filter.args) {
|
|
2707
|
+
if (isKeyValuePair(arg) && arg[1]) {
|
|
2708
|
+
yield* extractValueTokenVariables(arg[1]);
|
|
2709
|
+
}
|
|
2710
|
+
else if (isValueToken(arg)) {
|
|
2711
|
+
yield* extractValueTokenVariables(arg);
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
function* extractValueTokenVariables(token) {
|
|
2717
|
+
if (isRangeToken(token)) {
|
|
2718
|
+
yield* extractValueTokenVariables(token.lhs);
|
|
2719
|
+
yield* extractValueTokenVariables(token.rhs);
|
|
2720
|
+
}
|
|
2721
|
+
else if (isPropertyAccessToken(token)) {
|
|
2722
|
+
yield extractPropertyAccessVariable(token);
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
function extractPropertyAccessVariable(token) {
|
|
2726
|
+
const segments = [];
|
|
2727
|
+
// token is not guaranteed to have `file` set. We'll try to get it from a prop if not.
|
|
2728
|
+
let file = token.file;
|
|
2729
|
+
// Here we're flattening the first segment of a path if it is a nested path.
|
|
2730
|
+
const root = token.props[0];
|
|
2731
|
+
file = file || root.file;
|
|
2732
|
+
if (isQuotedToken(root) || isNumberToken(root) || isWordToken(root)) {
|
|
2733
|
+
segments.push(root.content);
|
|
2734
|
+
}
|
|
2735
|
+
else if (isPropertyAccessToken(root)) {
|
|
2736
|
+
// Flatten paths that start with a nested path.
|
|
2737
|
+
segments.push(...extractPropertyAccessVariable(root).segments);
|
|
2738
|
+
}
|
|
2739
|
+
for (const prop of token.props.slice(1)) {
|
|
2740
|
+
file = file || prop.file;
|
|
2741
|
+
if (isQuotedToken(prop) || isNumberToken(prop) || isWordToken(prop)) {
|
|
2742
|
+
segments.push(prop.content);
|
|
2743
|
+
}
|
|
2744
|
+
else if (isPropertyAccessToken(prop)) {
|
|
2745
|
+
segments.push(extractPropertyAccessVariable(prop));
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
const [row, col] = token.getPosition();
|
|
2749
|
+
return new Variable(segments, {
|
|
2750
|
+
row,
|
|
2751
|
+
col,
|
|
2752
|
+
file
|
|
2753
|
+
});
|
|
2754
|
+
}
|
|
2755
|
+
// This is used to detect segments that can be represented with dot notation
|
|
2756
|
+
// when creating a string representation of VariableSegments.
|
|
2757
|
+
const RE_PROPERTY = /^[\u0080-\uFFFFa-zA-Z_][\u0080-\uFFFFa-zA-Z0-9_-]*$/;
|
|
2758
|
+
/**
|
|
2759
|
+
* Return a string representation of segments using dot notation where possible.
|
|
2760
|
+
* @param segments - The property names and array indices that make up a path to a variable.
|
|
2761
|
+
* @param bracketedRoot - If false (the default), don't surround the root segment with square brackets.
|
|
2762
|
+
*/
|
|
2763
|
+
function segmentsString(segments, bracketedRoot = false) {
|
|
2764
|
+
const buf = [];
|
|
2765
|
+
const root = segments[0];
|
|
2766
|
+
if (isString(root)) {
|
|
2767
|
+
if (!bracketedRoot || root.match(RE_PROPERTY)) {
|
|
2768
|
+
buf.push(`${root}`);
|
|
2769
|
+
}
|
|
2770
|
+
else {
|
|
2771
|
+
buf.push(`['${root}']`);
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
for (const segment of segments.slice(1)) {
|
|
2775
|
+
if (segment instanceof Variable) {
|
|
2776
|
+
buf.push(`[${segmentsString(segment.segments)}]`);
|
|
2777
|
+
}
|
|
2778
|
+
else if (isString(segment)) {
|
|
2779
|
+
if (segment.match(RE_PROPERTY)) {
|
|
2780
|
+
buf.push(`.${segment}`);
|
|
2781
|
+
}
|
|
2782
|
+
else {
|
|
2783
|
+
buf.push(`['${segment}']`);
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
else {
|
|
2787
|
+
buf.push(`[${segment}]`);
|
|
2788
|
+
}
|
|
2789
|
+
}
|
|
2790
|
+
return buf.join('');
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2289
2793
|
var LookupType;
|
|
2290
2794
|
(function (LookupType) {
|
|
2291
2795
|
LookupType["Partials"] = "partials";
|
|
@@ -2302,7 +2806,7 @@ class Loader {
|
|
|
2302
2806
|
this.shouldLoadRelative = (referencedFile) => rRelativePath.test(referencedFile);
|
|
2303
2807
|
}
|
|
2304
2808
|
else {
|
|
2305
|
-
this.shouldLoadRelative = (
|
|
2809
|
+
this.shouldLoadRelative = (_referencedFile) => false;
|
|
2306
2810
|
}
|
|
2307
2811
|
this.contains = this.options.fs.contains || (() => true);
|
|
2308
2812
|
}
|
|
@@ -2359,8 +2863,11 @@ class Parser {
|
|
|
2359
2863
|
this.fs = this.liquid.options.fs;
|
|
2360
2864
|
this.parseFile = this.cache ? this._parseFileCached : this._parseFile;
|
|
2361
2865
|
this.loader = new Loader(this.liquid.options);
|
|
2866
|
+
this.parseLimit = new Limiter('parse length', liquid.options.parseLimit);
|
|
2362
2867
|
}
|
|
2363
2868
|
parse(html, filepath) {
|
|
2869
|
+
html = String(html);
|
|
2870
|
+
this.parseLimit.use(html.length);
|
|
2364
2871
|
const tokenizer = new Tokenizer(html, this.liquid.options.operators, filepath);
|
|
2365
2872
|
const tokens = tokenizer.readTopLevelTokens(this.liquid.options);
|
|
2366
2873
|
return this.parseTokens(tokens);
|
|
@@ -2368,9 +2875,20 @@ class Parser {
|
|
|
2368
2875
|
parseTokens(tokens) {
|
|
2369
2876
|
let token;
|
|
2370
2877
|
const templates = [];
|
|
2878
|
+
const errors = [];
|
|
2371
2879
|
while ((token = tokens.shift())) {
|
|
2372
|
-
|
|
2880
|
+
try {
|
|
2881
|
+
templates.push(this.parseToken(token, tokens));
|
|
2882
|
+
}
|
|
2883
|
+
catch (err) {
|
|
2884
|
+
if (this.liquid.options.catchAllErrors)
|
|
2885
|
+
errors.push(err);
|
|
2886
|
+
else
|
|
2887
|
+
throw err;
|
|
2888
|
+
}
|
|
2373
2889
|
}
|
|
2890
|
+
if (errors.length)
|
|
2891
|
+
throw new LiquidErrors(errors);
|
|
2374
2892
|
return templates;
|
|
2375
2893
|
}
|
|
2376
2894
|
parseToken(token, remainTokens) {
|
|
@@ -2378,7 +2896,7 @@ class Parser {
|
|
|
2378
2896
|
if (isTagToken(token)) {
|
|
2379
2897
|
const TagClass = this.liquid.tags[token.name];
|
|
2380
2898
|
assert(TagClass, `tag "${token.name}" not found`);
|
|
2381
|
-
return new TagClass(token, remainTokens, this.liquid);
|
|
2899
|
+
return new TagClass(token, remainTokens, this.liquid, this);
|
|
2382
2900
|
}
|
|
2383
2901
|
if (isOutputToken(token)) {
|
|
2384
2902
|
return new Output(token, this.liquid);
|
|
@@ -2416,7 +2934,7 @@ class Parser {
|
|
|
2416
2934
|
}
|
|
2417
2935
|
*_parseFile(file, sync, type = LookupType.Root, currentFile) {
|
|
2418
2936
|
const filepath = yield this.loader.lookup(file, type, sync, currentFile);
|
|
2419
|
-
return this.
|
|
2937
|
+
return this.parse(sync ? this.fs.readFileSync(filepath) : yield this.fs.readFile(filepath), filepath);
|
|
2420
2938
|
}
|
|
2421
2939
|
}
|
|
2422
2940
|
|
|
@@ -2471,6 +2989,10 @@ function isWordToken(val) {
|
|
|
2471
2989
|
function isRangeToken(val) {
|
|
2472
2990
|
return getKind(val) === TokenKind.Range;
|
|
2473
2991
|
}
|
|
2992
|
+
function isValueToken(val) {
|
|
2993
|
+
// valueTokenBitMask = TokenKind.Number | TokenKind.Literal | TokenKind.Quoted | TokenKind.PropertyAccess | TokenKind.Range
|
|
2994
|
+
return (getKind(val) & 1667) > 0;
|
|
2995
|
+
}
|
|
2474
2996
|
function getKind(val) {
|
|
2475
2997
|
return val ? val.kind : -1;
|
|
2476
2998
|
}
|
|
@@ -2487,24 +3009,29 @@ var typeGuards = /*#__PURE__*/Object.freeze({
|
|
|
2487
3009
|
isNumberToken: isNumberToken,
|
|
2488
3010
|
isPropertyAccessToken: isPropertyAccessToken,
|
|
2489
3011
|
isWordToken: isWordToken,
|
|
2490
|
-
isRangeToken: isRangeToken
|
|
3012
|
+
isRangeToken: isRangeToken,
|
|
3013
|
+
isValueToken: isValueToken
|
|
2491
3014
|
});
|
|
2492
3015
|
|
|
2493
3016
|
class Context {
|
|
2494
|
-
constructor(env = {}, opts = defaultOptions, renderOptions = {}) {
|
|
2495
|
-
var _a, _b, _c;
|
|
3017
|
+
constructor(env = {}, opts = defaultOptions, renderOptions = {}, { memoryLimit, renderLimit } = {}) {
|
|
3018
|
+
var _a, _b, _c, _d, _e;
|
|
2496
3019
|
/**
|
|
2497
3020
|
* insert a Context-level empty scope,
|
|
2498
3021
|
* for tags like `{% capture %}` `{% assign %}` to operate
|
|
2499
3022
|
*/
|
|
2500
3023
|
this.scopes = [{}];
|
|
2501
3024
|
this.registers = {};
|
|
3025
|
+
this.breakCalled = false;
|
|
3026
|
+
this.continueCalled = false;
|
|
2502
3027
|
this.sync = !!renderOptions.sync;
|
|
2503
3028
|
this.opts = opts;
|
|
2504
3029
|
this.globals = (_a = renderOptions.globals) !== null && _a !== void 0 ? _a : opts.globals;
|
|
2505
3030
|
this.environments = isObject(env) ? env : Object(env);
|
|
2506
3031
|
this.strictVariables = (_b = renderOptions.strictVariables) !== null && _b !== void 0 ? _b : this.opts.strictVariables;
|
|
2507
3032
|
this.ownPropertyOnly = (_c = renderOptions.ownPropertyOnly) !== null && _c !== void 0 ? _c : opts.ownPropertyOnly;
|
|
3033
|
+
this.memoryLimit = memoryLimit !== null && memoryLimit !== void 0 ? memoryLimit : new Limiter('memory alloc', (_d = renderOptions.memoryLimit) !== null && _d !== void 0 ? _d : opts.memoryLimit);
|
|
3034
|
+
this.renderLimit = renderLimit !== null && renderLimit !== void 0 ? renderLimit : new Limiter('template render', getPerformance().now() + ((_e = renderOptions.renderLimit) !== null && _e !== void 0 ? _e : opts.renderLimit));
|
|
2508
3035
|
}
|
|
2509
3036
|
getRegister(key) {
|
|
2510
3037
|
return (this.registers[key] = this.registers[key] || {});
|
|
@@ -2532,7 +3059,7 @@ class Context {
|
|
|
2532
3059
|
return toValueSync(this._get(paths));
|
|
2533
3060
|
}
|
|
2534
3061
|
*_get(paths) {
|
|
2535
|
-
const scope = this.findScope(paths[0]);
|
|
3062
|
+
const scope = this.findScope(paths[0]); // first prop should always be a string
|
|
2536
3063
|
return yield this._getFromScope(scope, paths);
|
|
2537
3064
|
}
|
|
2538
3065
|
/**
|
|
@@ -2561,6 +3088,16 @@ class Context {
|
|
|
2561
3088
|
bottom() {
|
|
2562
3089
|
return this.scopes[0];
|
|
2563
3090
|
}
|
|
3091
|
+
spawn(scope = {}) {
|
|
3092
|
+
return new Context(scope, this.opts, {
|
|
3093
|
+
sync: this.sync,
|
|
3094
|
+
globals: this.globals,
|
|
3095
|
+
strictVariables: this.strictVariables
|
|
3096
|
+
}, {
|
|
3097
|
+
renderLimit: this.renderLimit,
|
|
3098
|
+
memoryLimit: this.memoryLimit
|
|
3099
|
+
});
|
|
3100
|
+
}
|
|
2564
3101
|
findScope(key) {
|
|
2565
3102
|
for (let i = this.scopes.length - 1; i >= 0; i--) {
|
|
2566
3103
|
const candidate = this.scopes[i];
|
|
@@ -2574,6 +3111,7 @@ class Context {
|
|
|
2574
3111
|
}
|
|
2575
3112
|
function readProperty(obj, key, ownPropertyOnly) {
|
|
2576
3113
|
obj = toLiquid(obj);
|
|
3114
|
+
key = toValue(key);
|
|
2577
3115
|
if (isNil(obj))
|
|
2578
3116
|
return obj;
|
|
2579
3117
|
if (isArray(obj) && key < 0)
|
|
@@ -2592,7 +3130,7 @@ function readProperty(obj, key, ownPropertyOnly) {
|
|
|
2592
3130
|
return value;
|
|
2593
3131
|
}
|
|
2594
3132
|
function readJSProperty(obj, key, ownPropertyOnly) {
|
|
2595
|
-
if (ownPropertyOnly && !
|
|
3133
|
+
if (ownPropertyOnly && !hasOwnProperty.call(obj, key) && !(obj instanceof Drop))
|
|
2596
3134
|
return undefined;
|
|
2597
3135
|
return obj[key];
|
|
2598
3136
|
}
|
|
@@ -2607,7 +3145,7 @@ function readLast(obj) {
|
|
|
2607
3145
|
return obj['last'];
|
|
2608
3146
|
}
|
|
2609
3147
|
function readSize(obj) {
|
|
2610
|
-
if (
|
|
3148
|
+
if (hasOwnProperty.call(obj, 'size') || obj['size'] !== undefined)
|
|
2611
3149
|
return obj['size'];
|
|
2612
3150
|
if (isArray(obj) || isString(obj))
|
|
2613
3151
|
return obj.length;
|
|
@@ -2714,13 +3252,25 @@ var urlFilters = /*#__PURE__*/Object.freeze({
|
|
|
2714
3252
|
slugify: slugify
|
|
2715
3253
|
});
|
|
2716
3254
|
|
|
2717
|
-
const join = argumentsToValue((v, arg)
|
|
2718
|
-
const
|
|
2719
|
-
const
|
|
2720
|
-
const
|
|
3255
|
+
const join = argumentsToValue(function (v, arg) {
|
|
3256
|
+
const array = toArray(v);
|
|
3257
|
+
const sep = isNil(arg) ? ' ' : stringify(arg);
|
|
3258
|
+
const complexity = array.length * (1 + sep.length);
|
|
3259
|
+
this.context.memoryLimit.use(complexity);
|
|
3260
|
+
return array.join(sep);
|
|
3261
|
+
});
|
|
3262
|
+
const last$1 = argumentsToValue((v) => isArrayLike(v) ? last(v) : '');
|
|
3263
|
+
const first = argumentsToValue((v) => isArrayLike(v) ? v[0] : '');
|
|
3264
|
+
const reverse = argumentsToValue(function (v) {
|
|
3265
|
+
const array = toArray(v);
|
|
3266
|
+
this.context.memoryLimit.use(array.length);
|
|
3267
|
+
return [...array].reverse();
|
|
3268
|
+
});
|
|
2721
3269
|
function* sort(arr, property) {
|
|
2722
3270
|
const values = [];
|
|
2723
|
-
|
|
3271
|
+
const array = toArray(arr);
|
|
3272
|
+
this.context.memoryLimit.use(array.length);
|
|
3273
|
+
for (const item of array) {
|
|
2724
3274
|
values.push([
|
|
2725
3275
|
item,
|
|
2726
3276
|
property ? yield this.context._getFromScope(item, stringify(property).split('.'), false) : item
|
|
@@ -2737,35 +3287,47 @@ function sort_natural(input, property) {
|
|
|
2737
3287
|
const compare = property === undefined
|
|
2738
3288
|
? caseInsensitiveCompare
|
|
2739
3289
|
: (lhs, rhs) => caseInsensitiveCompare(lhs[propertyString], rhs[propertyString]);
|
|
2740
|
-
|
|
3290
|
+
const array = toArray(input);
|
|
3291
|
+
this.context.memoryLimit.use(array.length);
|
|
3292
|
+
return [...array].sort(compare);
|
|
2741
3293
|
}
|
|
2742
3294
|
const size = (v) => (v && v.length) || 0;
|
|
2743
3295
|
function* map(arr, property) {
|
|
2744
3296
|
const results = [];
|
|
2745
|
-
|
|
3297
|
+
const array = toArray(arr);
|
|
3298
|
+
this.context.memoryLimit.use(array.length);
|
|
3299
|
+
for (const item of array) {
|
|
2746
3300
|
results.push(yield this.context._getFromScope(item, stringify(property), false));
|
|
2747
3301
|
}
|
|
2748
3302
|
return results;
|
|
2749
3303
|
}
|
|
2750
3304
|
function* sum(arr, property) {
|
|
2751
3305
|
let sum = 0;
|
|
2752
|
-
|
|
3306
|
+
const array = toArray(arr);
|
|
3307
|
+
for (const item of array) {
|
|
2753
3308
|
const data = Number(property ? yield this.context._getFromScope(item, stringify(property), false) : item);
|
|
2754
3309
|
sum += Number.isNaN(data) ? 0 : data;
|
|
2755
3310
|
}
|
|
2756
3311
|
return sum;
|
|
2757
3312
|
}
|
|
2758
3313
|
function compact(arr) {
|
|
2759
|
-
|
|
3314
|
+
const array = toArray(arr);
|
|
3315
|
+
this.context.memoryLimit.use(array.length);
|
|
3316
|
+
return array.filter(x => !isNil(toValue(x)));
|
|
2760
3317
|
}
|
|
2761
3318
|
function concat(v, arg = []) {
|
|
2762
|
-
|
|
3319
|
+
const lhs = toArray(v);
|
|
3320
|
+
const rhs = toArray(arg);
|
|
3321
|
+
this.context.memoryLimit.use(lhs.length + rhs.length);
|
|
3322
|
+
return lhs.concat(rhs);
|
|
2763
3323
|
}
|
|
2764
3324
|
function push(v, arg) {
|
|
2765
|
-
return concat(v, [arg]);
|
|
3325
|
+
return concat.call(this, v, [arg]);
|
|
2766
3326
|
}
|
|
2767
3327
|
function unshift(v, arg) {
|
|
2768
|
-
const
|
|
3328
|
+
const array = toArray(v);
|
|
3329
|
+
this.context.memoryLimit.use(array.length);
|
|
3330
|
+
const clone = [...array];
|
|
2769
3331
|
clone.unshift(arg);
|
|
2770
3332
|
return clone;
|
|
2771
3333
|
}
|
|
@@ -2775,7 +3337,9 @@ function pop(v) {
|
|
|
2775
3337
|
return clone;
|
|
2776
3338
|
}
|
|
2777
3339
|
function shift(v) {
|
|
2778
|
-
const
|
|
3340
|
+
const array = toArray(v);
|
|
3341
|
+
this.context.memoryLimit.use(array.length);
|
|
3342
|
+
const clone = [...array];
|
|
2779
3343
|
clone.shift();
|
|
2780
3344
|
return clone;
|
|
2781
3345
|
}
|
|
@@ -2786,26 +3350,33 @@ function slice(v, begin, length = 1) {
|
|
|
2786
3350
|
if (!isArray(v))
|
|
2787
3351
|
v = stringify(v);
|
|
2788
3352
|
begin = begin < 0 ? v.length + begin : begin;
|
|
3353
|
+
this.context.memoryLimit.use(length);
|
|
2789
3354
|
return v.slice(begin, begin + length);
|
|
2790
3355
|
}
|
|
2791
3356
|
function* where(arr, property, expected) {
|
|
2792
3357
|
const values = [];
|
|
2793
3358
|
arr = toArray(arr);
|
|
3359
|
+
this.context.memoryLimit.use(arr.length);
|
|
2794
3360
|
const token = new Tokenizer(stringify(property)).readScopeValue();
|
|
2795
3361
|
for (const item of arr) {
|
|
2796
|
-
values.push(yield evalToken(token,
|
|
3362
|
+
values.push(yield evalToken(token, this.context.spawn(item)));
|
|
2797
3363
|
}
|
|
3364
|
+
const matcher = this.context.opts.jekyllWhere
|
|
3365
|
+
? (v) => EmptyDrop.is(expected) ? equals(v, expected) : (isArray(v) ? arrayIncludes(v, expected) : equals(v, expected))
|
|
3366
|
+
: (v) => equals(v, expected);
|
|
2798
3367
|
return arr.filter((_, i) => {
|
|
2799
3368
|
if (expected === undefined)
|
|
2800
3369
|
return isTruthy(values[i], this.context);
|
|
2801
|
-
return
|
|
3370
|
+
return matcher(values[i]);
|
|
2802
3371
|
});
|
|
2803
3372
|
}
|
|
2804
3373
|
function* where_exp(arr, itemName, exp) {
|
|
2805
3374
|
const filtered = [];
|
|
2806
3375
|
const keyTemplate = new Value(stringify(exp), this.liquid);
|
|
2807
|
-
|
|
2808
|
-
|
|
3376
|
+
const array = toArray(arr);
|
|
3377
|
+
this.context.memoryLimit.use(array.length);
|
|
3378
|
+
for (const item of array) {
|
|
3379
|
+
const value = yield keyTemplate.value(this.context.spawn({ [itemName]: item }));
|
|
2809
3380
|
if (value)
|
|
2810
3381
|
filtered.push(item);
|
|
2811
3382
|
}
|
|
@@ -2813,10 +3384,11 @@ function* where_exp(arr, itemName, exp) {
|
|
|
2813
3384
|
}
|
|
2814
3385
|
function* group_by(arr, property) {
|
|
2815
3386
|
const map = new Map();
|
|
2816
|
-
arr =
|
|
3387
|
+
arr = toEnumerable(arr);
|
|
2817
3388
|
const token = new Tokenizer(stringify(property)).readScopeValue();
|
|
3389
|
+
this.context.memoryLimit.use(arr.length);
|
|
2818
3390
|
for (const item of arr) {
|
|
2819
|
-
const key = yield evalToken(token,
|
|
3391
|
+
const key = yield evalToken(token, this.context.spawn(item));
|
|
2820
3392
|
if (!map.has(key))
|
|
2821
3393
|
map.set(key, []);
|
|
2822
3394
|
map.get(key).push(item);
|
|
@@ -2826,8 +3398,10 @@ function* group_by(arr, property) {
|
|
|
2826
3398
|
function* group_by_exp(arr, itemName, exp) {
|
|
2827
3399
|
const map = new Map();
|
|
2828
3400
|
const keyTemplate = new Value(stringify(exp), this.liquid);
|
|
2829
|
-
|
|
2830
|
-
|
|
3401
|
+
arr = toEnumerable(arr);
|
|
3402
|
+
this.context.memoryLimit.use(arr.length);
|
|
3403
|
+
for (const item of arr) {
|
|
3404
|
+
const key = yield keyTemplate.value(this.context.spawn({ [itemName]: item }));
|
|
2831
3405
|
if (!map.has(key))
|
|
2832
3406
|
map.set(key, []);
|
|
2833
3407
|
map.get(key).push(item);
|
|
@@ -2836,31 +3410,26 @@ function* group_by_exp(arr, itemName, exp) {
|
|
|
2836
3410
|
}
|
|
2837
3411
|
function* find(arr, property, expected) {
|
|
2838
3412
|
const token = new Tokenizer(stringify(property)).readScopeValue();
|
|
2839
|
-
|
|
2840
|
-
|
|
3413
|
+
const array = toArray(arr);
|
|
3414
|
+
for (const item of array) {
|
|
3415
|
+
const value = yield evalToken(token, this.context.spawn(item));
|
|
2841
3416
|
if (equals(value, expected))
|
|
2842
3417
|
return item;
|
|
2843
3418
|
}
|
|
2844
|
-
return null;
|
|
2845
3419
|
}
|
|
2846
3420
|
function* find_exp(arr, itemName, exp) {
|
|
2847
3421
|
const predicate = new Value(stringify(exp), this.liquid);
|
|
2848
|
-
|
|
2849
|
-
|
|
3422
|
+
const array = toArray(arr);
|
|
3423
|
+
for (const item of array) {
|
|
3424
|
+
const value = yield predicate.value(this.context.spawn({ [itemName]: item }));
|
|
2850
3425
|
if (value)
|
|
2851
3426
|
return item;
|
|
2852
3427
|
}
|
|
2853
|
-
return null;
|
|
2854
3428
|
}
|
|
2855
3429
|
function uniq(arr) {
|
|
2856
|
-
arr =
|
|
2857
|
-
|
|
2858
|
-
return (arr
|
|
2859
|
-
if (hasOwnProperty.call(u, String(val)))
|
|
2860
|
-
return false;
|
|
2861
|
-
u[String(val)] = true;
|
|
2862
|
-
return true;
|
|
2863
|
-
});
|
|
3430
|
+
arr = toArray(arr);
|
|
3431
|
+
this.context.memoryLimit.use(arr.length);
|
|
3432
|
+
return [...new Set(arr)];
|
|
2864
3433
|
}
|
|
2865
3434
|
function sample(v, count = 1) {
|
|
2866
3435
|
v = toValue(v);
|
|
@@ -2868,6 +3437,7 @@ function sample(v, count = 1) {
|
|
|
2868
3437
|
return [];
|
|
2869
3438
|
if (!isArray(v))
|
|
2870
3439
|
v = stringify(v);
|
|
3440
|
+
this.context.memoryLimit.use(count);
|
|
2871
3441
|
const shuffled = [...v].sort(() => Math.random() - 0.5);
|
|
2872
3442
|
if (count === 1)
|
|
2873
3443
|
return shuffled[0];
|
|
@@ -2903,6 +3473,9 @@ var arrayFilters = /*#__PURE__*/Object.freeze({
|
|
|
2903
3473
|
});
|
|
2904
3474
|
|
|
2905
3475
|
function date(v, format, timezoneOffset) {
|
|
3476
|
+
var _a, _b, _c;
|
|
3477
|
+
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);
|
|
3478
|
+
this.context.memoryLimit.use(size);
|
|
2906
3479
|
const date = parseDate(v, this.context.opts, timezoneOffset);
|
|
2907
3480
|
if (!date)
|
|
2908
3481
|
return v;
|
|
@@ -2936,39 +3509,30 @@ function stringify_date(v, month_type, type, style) {
|
|
|
2936
3509
|
}
|
|
2937
3510
|
function parseDate(v, opts, timezoneOffset) {
|
|
2938
3511
|
let date;
|
|
3512
|
+
const defaultTimezoneOffset = timezoneOffset !== null && timezoneOffset !== void 0 ? timezoneOffset : opts.timezoneOffset;
|
|
3513
|
+
const locale = opts.locale;
|
|
2939
3514
|
v = toValue(v);
|
|
2940
3515
|
if (v === 'now' || v === 'today') {
|
|
2941
|
-
date = new Date();
|
|
3516
|
+
date = new LiquidDate(Date.now(), locale, defaultTimezoneOffset);
|
|
2942
3517
|
}
|
|
2943
3518
|
else if (isNumber(v)) {
|
|
2944
|
-
date = new
|
|
3519
|
+
date = new LiquidDate(v * 1000, locale, defaultTimezoneOffset);
|
|
2945
3520
|
}
|
|
2946
3521
|
else if (isString(v)) {
|
|
2947
3522
|
if (/^\d+$/.test(v)) {
|
|
2948
|
-
date = new
|
|
3523
|
+
date = new LiquidDate(+v * 1000, locale, defaultTimezoneOffset);
|
|
2949
3524
|
}
|
|
2950
|
-
else if (opts.preserveTimezones) {
|
|
2951
|
-
date =
|
|
3525
|
+
else if (opts.preserveTimezones && timezoneOffset === undefined) {
|
|
3526
|
+
date = LiquidDate.createDateFixedToTimezone(v, locale);
|
|
2952
3527
|
}
|
|
2953
3528
|
else {
|
|
2954
|
-
date = new
|
|
3529
|
+
date = new LiquidDate(v, locale, defaultTimezoneOffset);
|
|
2955
3530
|
}
|
|
2956
3531
|
}
|
|
2957
3532
|
else {
|
|
2958
|
-
date = v;
|
|
2959
|
-
}
|
|
2960
|
-
if (!isValidDate(date))
|
|
2961
|
-
return;
|
|
2962
|
-
if (timezoneOffset !== undefined) {
|
|
2963
|
-
date = new TimezoneDate(date, timezoneOffset);
|
|
2964
|
-
}
|
|
2965
|
-
else if (!(date instanceof TimezoneDate) && opts.timezoneOffset !== undefined) {
|
|
2966
|
-
date = new TimezoneDate(date, opts.timezoneOffset);
|
|
3533
|
+
date = new LiquidDate(v, locale, defaultTimezoneOffset);
|
|
2967
3534
|
}
|
|
2968
|
-
return date;
|
|
2969
|
-
}
|
|
2970
|
-
function isValidDate(date) {
|
|
2971
|
-
return (date instanceof Date || date instanceof TimezoneDate) && !isNaN(date.getTime());
|
|
3535
|
+
return date.valid() ? date : undefined;
|
|
2972
3536
|
}
|
|
2973
3537
|
|
|
2974
3538
|
var dateFilters = /*#__PURE__*/Object.freeze({
|
|
@@ -2990,33 +3554,50 @@ const rCJKWord = /[\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF\u3040-\u309F\u30A0-\u
|
|
|
2990
3554
|
const rNonCJKWord = /[^\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF\u3040-\u309F\u30A0-\u30FF\uAC00-\uD7AF\s]+/gu;
|
|
2991
3555
|
function append(v, arg) {
|
|
2992
3556
|
assert(arguments.length === 2, 'append expect 2 arguments');
|
|
2993
|
-
|
|
3557
|
+
const lhs = stringify(v);
|
|
3558
|
+
const rhs = stringify(arg);
|
|
3559
|
+
this.context.memoryLimit.use(lhs.length + rhs.length);
|
|
3560
|
+
return lhs + rhs;
|
|
2994
3561
|
}
|
|
2995
3562
|
function prepend(v, arg) {
|
|
2996
3563
|
assert(arguments.length === 2, 'prepend expect 2 arguments');
|
|
2997
|
-
|
|
3564
|
+
const lhs = stringify(v);
|
|
3565
|
+
const rhs = stringify(arg);
|
|
3566
|
+
this.context.memoryLimit.use(lhs.length + rhs.length);
|
|
3567
|
+
return rhs + lhs;
|
|
2998
3568
|
}
|
|
2999
3569
|
function lstrip(v, chars) {
|
|
3570
|
+
const str = stringify(v);
|
|
3571
|
+
this.context.memoryLimit.use(str.length);
|
|
3000
3572
|
if (chars) {
|
|
3001
3573
|
chars = escapeRegExp(stringify(chars));
|
|
3002
|
-
return
|
|
3574
|
+
return str.replace(new RegExp(`^[${chars}]+`, 'g'), '');
|
|
3003
3575
|
}
|
|
3004
|
-
return
|
|
3576
|
+
return str.replace(/^\s+/, '');
|
|
3005
3577
|
}
|
|
3006
3578
|
function downcase(v) {
|
|
3007
|
-
|
|
3579
|
+
const str = stringify(v);
|
|
3580
|
+
this.context.memoryLimit.use(str.length);
|
|
3581
|
+
return str.toLowerCase();
|
|
3008
3582
|
}
|
|
3009
|
-
function upcase(
|
|
3583
|
+
function upcase(v) {
|
|
3584
|
+
const str = stringify(v);
|
|
3585
|
+
this.context.memoryLimit.use(str.length);
|
|
3010
3586
|
return stringify(str).toUpperCase();
|
|
3011
3587
|
}
|
|
3012
3588
|
function remove(v, arg) {
|
|
3013
|
-
|
|
3589
|
+
const str = stringify(v);
|
|
3590
|
+
this.context.memoryLimit.use(str.length);
|
|
3591
|
+
return str.split(stringify(arg)).join('');
|
|
3014
3592
|
}
|
|
3015
3593
|
function remove_first(v, l) {
|
|
3016
|
-
|
|
3594
|
+
const str = stringify(v);
|
|
3595
|
+
this.context.memoryLimit.use(str.length);
|
|
3596
|
+
return str.replace(stringify(l), '');
|
|
3017
3597
|
}
|
|
3018
3598
|
function remove_last(v, l) {
|
|
3019
3599
|
const str = stringify(v);
|
|
3600
|
+
this.context.memoryLimit.use(str.length);
|
|
3020
3601
|
const pattern = stringify(l);
|
|
3021
3602
|
const index = str.lastIndexOf(pattern);
|
|
3022
3603
|
if (index === -1)
|
|
@@ -3024,14 +3605,18 @@ function remove_last(v, l) {
|
|
|
3024
3605
|
return str.substring(0, index) + str.substring(index + pattern.length);
|
|
3025
3606
|
}
|
|
3026
3607
|
function rstrip(str, chars) {
|
|
3608
|
+
str = stringify(str);
|
|
3609
|
+
this.context.memoryLimit.use(str.length);
|
|
3027
3610
|
if (chars) {
|
|
3028
3611
|
chars = escapeRegExp(stringify(chars));
|
|
3029
|
-
return
|
|
3612
|
+
return str.replace(new RegExp(`[${chars}]+$`, 'g'), '');
|
|
3030
3613
|
}
|
|
3031
|
-
return
|
|
3614
|
+
return str.replace(/\s+$/, '');
|
|
3032
3615
|
}
|
|
3033
3616
|
function split(v, arg) {
|
|
3034
|
-
const
|
|
3617
|
+
const str = stringify(v);
|
|
3618
|
+
this.context.memoryLimit.use(str.length);
|
|
3619
|
+
const arr = str.split(stringify(arg));
|
|
3035
3620
|
// align to ruby split, which is the behavior of shopify/liquid
|
|
3036
3621
|
// see: https://ruby-doc.org/core-2.4.0/String.html#method-i-split
|
|
3037
3622
|
while (arr.length && arr[arr.length - 1] === '')
|
|
@@ -3039,29 +3624,39 @@ function split(v, arg) {
|
|
|
3039
3624
|
return arr;
|
|
3040
3625
|
}
|
|
3041
3626
|
function strip(v, chars) {
|
|
3627
|
+
const str = stringify(v);
|
|
3628
|
+
this.context.memoryLimit.use(str.length);
|
|
3042
3629
|
if (chars) {
|
|
3043
3630
|
chars = escapeRegExp(stringify(chars));
|
|
3044
|
-
return
|
|
3631
|
+
return str
|
|
3045
3632
|
.replace(new RegExp(`^[${chars}]+`, 'g'), '')
|
|
3046
3633
|
.replace(new RegExp(`[${chars}]+$`, 'g'), '');
|
|
3047
3634
|
}
|
|
3048
|
-
return
|
|
3635
|
+
return str.trim();
|
|
3049
3636
|
}
|
|
3050
3637
|
function strip_newlines(v) {
|
|
3051
|
-
|
|
3638
|
+
const str = stringify(v);
|
|
3639
|
+
this.context.memoryLimit.use(str.length);
|
|
3640
|
+
return str.replace(/\r?\n/gm, '');
|
|
3052
3641
|
}
|
|
3053
3642
|
function capitalize(str) {
|
|
3054
3643
|
str = stringify(str);
|
|
3644
|
+
this.context.memoryLimit.use(str.length);
|
|
3055
3645
|
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
3056
3646
|
}
|
|
3057
3647
|
function replace(v, pattern, replacement) {
|
|
3058
|
-
|
|
3648
|
+
const str = stringify(v);
|
|
3649
|
+
this.context.memoryLimit.use(str.length);
|
|
3650
|
+
return str.split(stringify(pattern)).join(replacement);
|
|
3059
3651
|
}
|
|
3060
3652
|
function replace_first(v, arg1, arg2) {
|
|
3061
|
-
|
|
3653
|
+
const str = stringify(v);
|
|
3654
|
+
this.context.memoryLimit.use(str.length);
|
|
3655
|
+
return str.replace(stringify(arg1), arg2);
|
|
3062
3656
|
}
|
|
3063
3657
|
function replace_last(v, arg1, arg2) {
|
|
3064
3658
|
const str = stringify(v);
|
|
3659
|
+
this.context.memoryLimit.use(str.length);
|
|
3065
3660
|
const pattern = stringify(arg1);
|
|
3066
3661
|
const index = str.lastIndexOf(pattern);
|
|
3067
3662
|
if (index === -1)
|
|
@@ -3070,13 +3665,16 @@ function replace_last(v, arg1, arg2) {
|
|
|
3070
3665
|
return str.substring(0, index) + replacement + str.substring(index + pattern.length);
|
|
3071
3666
|
}
|
|
3072
3667
|
function truncate(v, l = 50, o = '...') {
|
|
3073
|
-
|
|
3074
|
-
|
|
3668
|
+
const str = stringify(v);
|
|
3669
|
+
this.context.memoryLimit.use(str.length);
|
|
3670
|
+
if (str.length <= l)
|
|
3075
3671
|
return v;
|
|
3076
|
-
return
|
|
3672
|
+
return str.substring(0, l - o.length) + o;
|
|
3077
3673
|
}
|
|
3078
3674
|
function truncatewords(v, words = 15, o = '...') {
|
|
3079
|
-
const
|
|
3675
|
+
const str = stringify(v);
|
|
3676
|
+
this.context.memoryLimit.use(str.length);
|
|
3677
|
+
const arr = str.split(/\s+/);
|
|
3080
3678
|
if (words <= 0)
|
|
3081
3679
|
words = 1;
|
|
3082
3680
|
let ret = arr.slice(0, words).join(' ');
|
|
@@ -3085,11 +3683,14 @@ function truncatewords(v, words = 15, o = '...') {
|
|
|
3085
3683
|
return ret;
|
|
3086
3684
|
}
|
|
3087
3685
|
function normalize_whitespace(v) {
|
|
3088
|
-
|
|
3089
|
-
|
|
3686
|
+
const str = stringify(v);
|
|
3687
|
+
this.context.memoryLimit.use(str.length);
|
|
3688
|
+
return str.replace(/\s+/g, ' ');
|
|
3090
3689
|
}
|
|
3091
3690
|
function number_of_words(input, mode) {
|
|
3092
|
-
|
|
3691
|
+
const str = stringify(input);
|
|
3692
|
+
this.context.memoryLimit.use(str.length);
|
|
3693
|
+
input = str.trim();
|
|
3093
3694
|
if (!input)
|
|
3094
3695
|
return 0;
|
|
3095
3696
|
switch (mode) {
|
|
@@ -3107,6 +3708,7 @@ function number_of_words(input, mode) {
|
|
|
3107
3708
|
}
|
|
3108
3709
|
}
|
|
3109
3710
|
function array_to_sentence_string(array, connector = 'and') {
|
|
3711
|
+
this.context.memoryLimit.use(array.length);
|
|
3110
3712
|
switch (array.length) {
|
|
3111
3713
|
case 0:
|
|
3112
3714
|
return '';
|
|
@@ -3149,7 +3751,8 @@ const filters = Object.assign(Object.assign(Object.assign(Object.assign(Object.a
|
|
|
3149
3751
|
class AssignTag extends Tag {
|
|
3150
3752
|
constructor(token, remainTokens, liquid) {
|
|
3151
3753
|
super(token, remainTokens, liquid);
|
|
3152
|
-
this.
|
|
3754
|
+
this.identifier = this.tokenizer.readIdentifier();
|
|
3755
|
+
this.key = this.identifier.content;
|
|
3153
3756
|
this.tokenizer.assert(this.key, 'expected variable name');
|
|
3154
3757
|
this.tokenizer.skipBlank();
|
|
3155
3758
|
this.tokenizer.assert(this.tokenizer.peek() === '=', 'expected "="');
|
|
@@ -3159,11 +3762,17 @@ class AssignTag extends Tag {
|
|
|
3159
3762
|
*render(ctx) {
|
|
3160
3763
|
ctx.bottom()[this.key] = yield this.value.value(ctx, this.liquid.options.lenientIf);
|
|
3161
3764
|
}
|
|
3765
|
+
*arguments() {
|
|
3766
|
+
yield this.value;
|
|
3767
|
+
}
|
|
3768
|
+
*localScope() {
|
|
3769
|
+
yield this.identifier;
|
|
3770
|
+
}
|
|
3162
3771
|
}
|
|
3163
3772
|
|
|
3164
3773
|
const MODIFIERS = ['offset', 'limit', 'reversed'];
|
|
3165
3774
|
class ForTag extends Tag {
|
|
3166
|
-
constructor(token, remainTokens, liquid) {
|
|
3775
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3167
3776
|
super(token, remainTokens, liquid);
|
|
3168
3777
|
const variable = this.tokenizer.readIdentifier();
|
|
3169
3778
|
const inStr = this.tokenizer.readIdentifier();
|
|
@@ -3173,18 +3782,16 @@ class ForTag extends Tag {
|
|
|
3173
3782
|
}
|
|
3174
3783
|
this.variable = variable.content;
|
|
3175
3784
|
this.collection = collection;
|
|
3176
|
-
this.hash = new Hash(this.tokenizer.
|
|
3785
|
+
this.hash = new Hash(this.tokenizer, liquid.options.keyValueSeparator);
|
|
3177
3786
|
this.templates = [];
|
|
3178
3787
|
this.elseTemplates = [];
|
|
3179
3788
|
let p;
|
|
3180
|
-
const stream =
|
|
3789
|
+
const stream = parser.parseStream(remainTokens)
|
|
3181
3790
|
.on('start', () => (p = this.templates))
|
|
3182
|
-
.on('tag:else',
|
|
3183
|
-
.on('tag:endfor', ()
|
|
3791
|
+
.on('tag:else', tag => { assertEmpty(tag.args); p = this.elseTemplates; })
|
|
3792
|
+
.on('tag:endfor', tag => { assertEmpty(tag.args); stream.stop(); })
|
|
3184
3793
|
.on('template', (tpl) => p.push(tpl))
|
|
3185
|
-
.on('end', () => {
|
|
3186
|
-
throw new Error(`tag ${token.getText()} not closed`);
|
|
3187
|
-
});
|
|
3794
|
+
.on('end', () => { throw new Error(`tag ${token.getText()} not closed`); });
|
|
3188
3795
|
stream.start();
|
|
3189
3796
|
}
|
|
3190
3797
|
*render(ctx, emitter) {
|
|
@@ -3213,16 +3820,33 @@ class ForTag extends Tag {
|
|
|
3213
3820
|
ctx.push(scope);
|
|
3214
3821
|
for (const item of collection) {
|
|
3215
3822
|
scope[this.variable] = item;
|
|
3823
|
+
ctx.continueCalled = ctx.breakCalled = false;
|
|
3216
3824
|
yield r.renderTemplates(this.templates, ctx, emitter);
|
|
3217
|
-
if (
|
|
3218
|
-
emitter['break'] = false;
|
|
3825
|
+
if (ctx.breakCalled)
|
|
3219
3826
|
break;
|
|
3220
|
-
}
|
|
3221
|
-
emitter['continue'] = false;
|
|
3222
3827
|
scope.forloop.next();
|
|
3223
3828
|
}
|
|
3829
|
+
ctx.continueCalled = ctx.breakCalled = false;
|
|
3224
3830
|
ctx.pop();
|
|
3225
3831
|
}
|
|
3832
|
+
*children() {
|
|
3833
|
+
const templates = this.templates.slice();
|
|
3834
|
+
if (this.elseTemplates) {
|
|
3835
|
+
templates.push(...this.elseTemplates);
|
|
3836
|
+
}
|
|
3837
|
+
return templates;
|
|
3838
|
+
}
|
|
3839
|
+
*arguments() {
|
|
3840
|
+
yield this.collection;
|
|
3841
|
+
for (const v of Object.values(this.hash.hash)) {
|
|
3842
|
+
if (isValueToken(v)) {
|
|
3843
|
+
yield v;
|
|
3844
|
+
}
|
|
3845
|
+
}
|
|
3846
|
+
}
|
|
3847
|
+
blockScope() {
|
|
3848
|
+
return [this.variable, 'forloop'];
|
|
3849
|
+
}
|
|
3226
3850
|
}
|
|
3227
3851
|
function reversed(arr) {
|
|
3228
3852
|
return [...arr].reverse();
|
|
@@ -3235,36 +3859,43 @@ function limit(arr, count) {
|
|
|
3235
3859
|
}
|
|
3236
3860
|
|
|
3237
3861
|
class CaptureTag extends Tag {
|
|
3238
|
-
constructor(tagToken, remainTokens, liquid) {
|
|
3862
|
+
constructor(tagToken, remainTokens, liquid, parser) {
|
|
3239
3863
|
super(tagToken, remainTokens, liquid);
|
|
3240
3864
|
this.templates = [];
|
|
3241
|
-
this.
|
|
3865
|
+
this.identifier = this.readVariable();
|
|
3866
|
+
this.variable = this.identifier.content;
|
|
3242
3867
|
while (remainTokens.length) {
|
|
3243
3868
|
const token = remainTokens.shift();
|
|
3244
3869
|
if (isTagToken(token) && token.name === 'endcapture')
|
|
3245
3870
|
return;
|
|
3246
|
-
this.templates.push(
|
|
3871
|
+
this.templates.push(parser.parseToken(token, remainTokens));
|
|
3247
3872
|
}
|
|
3248
3873
|
throw new Error(`tag ${tagToken.getText()} not closed`);
|
|
3249
3874
|
}
|
|
3875
|
+
readVariable() {
|
|
3876
|
+
let ident = this.tokenizer.readIdentifier();
|
|
3877
|
+
if (ident.content)
|
|
3878
|
+
return ident;
|
|
3879
|
+
ident = this.tokenizer.readQuoted();
|
|
3880
|
+
if (ident)
|
|
3881
|
+
return ident;
|
|
3882
|
+
throw this.tokenizer.error('invalid capture name');
|
|
3883
|
+
}
|
|
3250
3884
|
*render(ctx) {
|
|
3251
3885
|
const r = this.liquid.renderer;
|
|
3252
3886
|
const html = yield r.renderTemplates(this.templates, ctx);
|
|
3253
3887
|
ctx.bottom()[this.variable] = html;
|
|
3254
3888
|
}
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
if (quoted)
|
|
3261
|
-
return evalQuotedToken(quoted);
|
|
3262
|
-
throw this.tokenizer.error('invalid capture name');
|
|
3889
|
+
*children() {
|
|
3890
|
+
return this.templates;
|
|
3891
|
+
}
|
|
3892
|
+
*localScope() {
|
|
3893
|
+
yield this.identifier;
|
|
3263
3894
|
}
|
|
3264
3895
|
}
|
|
3265
3896
|
|
|
3266
3897
|
class CaseTag extends Tag {
|
|
3267
|
-
constructor(tagToken, remainTokens, liquid) {
|
|
3898
|
+
constructor(tagToken, remainTokens, liquid, parser) {
|
|
3268
3899
|
super(tagToken, remainTokens, liquid);
|
|
3269
3900
|
this.branches = [];
|
|
3270
3901
|
this.elseTemplates = [];
|
|
@@ -3272,7 +3903,7 @@ class CaseTag extends Tag {
|
|
|
3272
3903
|
this.elseTemplates = [];
|
|
3273
3904
|
let p = [];
|
|
3274
3905
|
let elseCount = 0;
|
|
3275
|
-
const stream =
|
|
3906
|
+
const stream = parser.parseStream(remainTokens)
|
|
3276
3907
|
.on('tag:when', (token) => {
|
|
3277
3908
|
if (elseCount > 0) {
|
|
3278
3909
|
return;
|
|
@@ -3327,6 +3958,17 @@ class CaseTag extends Tag {
|
|
|
3327
3958
|
yield r.renderTemplates(this.elseTemplates, ctx, emitter);
|
|
3328
3959
|
}
|
|
3329
3960
|
}
|
|
3961
|
+
*arguments() {
|
|
3962
|
+
yield this.value;
|
|
3963
|
+
yield* this.branches.flatMap(b => b.values);
|
|
3964
|
+
}
|
|
3965
|
+
*children() {
|
|
3966
|
+
const templates = this.branches.flatMap(b => b.templates);
|
|
3967
|
+
if (this.elseTemplates) {
|
|
3968
|
+
templates.push(...this.elseTemplates);
|
|
3969
|
+
}
|
|
3970
|
+
return templates;
|
|
3971
|
+
}
|
|
3330
3972
|
}
|
|
3331
3973
|
|
|
3332
3974
|
class CommentTag extends Tag {
|
|
@@ -3343,10 +3985,10 @@ class CommentTag extends Tag {
|
|
|
3343
3985
|
}
|
|
3344
3986
|
|
|
3345
3987
|
class RenderTag extends Tag {
|
|
3346
|
-
constructor(token, remainTokens, liquid) {
|
|
3988
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3347
3989
|
super(token, remainTokens, liquid);
|
|
3348
3990
|
const tokenizer = this.tokenizer;
|
|
3349
|
-
this.file = parseFilePath(tokenizer, this.liquid);
|
|
3991
|
+
this.file = parseFilePath(tokenizer, this.liquid, parser);
|
|
3350
3992
|
this.currentFile = token.file;
|
|
3351
3993
|
while (!tokenizer.end()) {
|
|
3352
3994
|
tokenizer.skipBlank();
|
|
@@ -3380,13 +4022,13 @@ class RenderTag extends Tag {
|
|
|
3380
4022
|
tokenizer.p = begin;
|
|
3381
4023
|
break;
|
|
3382
4024
|
}
|
|
3383
|
-
this.hash = new Hash(tokenizer.
|
|
4025
|
+
this.hash = new Hash(tokenizer, liquid.options.keyValueSeparator);
|
|
3384
4026
|
}
|
|
3385
4027
|
*render(ctx, emitter) {
|
|
3386
4028
|
const { liquid, hash } = this;
|
|
3387
4029
|
const filepath = (yield renderFilePath(this['file'], ctx, liquid));
|
|
3388
4030
|
assert(filepath, () => `illegal file path "${filepath}"`);
|
|
3389
|
-
const childCtx =
|
|
4031
|
+
const childCtx = ctx.spawn();
|
|
3390
4032
|
const scope = childCtx.bottom();
|
|
3391
4033
|
__assign(scope, yield hash.render(ctx));
|
|
3392
4034
|
if (this['with']) {
|
|
@@ -3409,6 +4051,55 @@ class RenderTag extends Tag {
|
|
|
3409
4051
|
yield liquid.renderer.renderTemplates(templates, childCtx, emitter);
|
|
3410
4052
|
}
|
|
3411
4053
|
}
|
|
4054
|
+
*children(partials, sync) {
|
|
4055
|
+
if (partials && isString(this['file'])) {
|
|
4056
|
+
return (yield this.liquid._parsePartialFile(this['file'], sync, this['currentFile']));
|
|
4057
|
+
}
|
|
4058
|
+
return [];
|
|
4059
|
+
}
|
|
4060
|
+
partialScope() {
|
|
4061
|
+
if (isString(this['file'])) {
|
|
4062
|
+
const names = Object.keys(this.hash.hash);
|
|
4063
|
+
if (this['with']) {
|
|
4064
|
+
const { value, alias } = this['with'];
|
|
4065
|
+
if (isString(alias)) {
|
|
4066
|
+
names.push([alias, value]);
|
|
4067
|
+
}
|
|
4068
|
+
else if (isString(this.file)) {
|
|
4069
|
+
names.push([this.file, value]);
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4072
|
+
if (this['for']) {
|
|
4073
|
+
const { value, alias } = this['for'];
|
|
4074
|
+
if (isString(alias)) {
|
|
4075
|
+
names.push([alias, value]);
|
|
4076
|
+
}
|
|
4077
|
+
else if (isString(this.file)) {
|
|
4078
|
+
names.push([this.file, value]);
|
|
4079
|
+
}
|
|
4080
|
+
}
|
|
4081
|
+
return { name: this['file'], isolated: true, scope: names };
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4084
|
+
*arguments() {
|
|
4085
|
+
for (const v of Object.values(this.hash.hash)) {
|
|
4086
|
+
if (isValueToken(v)) {
|
|
4087
|
+
yield v;
|
|
4088
|
+
}
|
|
4089
|
+
}
|
|
4090
|
+
if (this['with']) {
|
|
4091
|
+
const { value } = this['with'];
|
|
4092
|
+
if (isValueToken(value)) {
|
|
4093
|
+
yield value;
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
if (this['for']) {
|
|
4097
|
+
const { value } = this['for'];
|
|
4098
|
+
if (isValueToken(value)) {
|
|
4099
|
+
yield value;
|
|
4100
|
+
}
|
|
4101
|
+
}
|
|
4102
|
+
}
|
|
3412
4103
|
}
|
|
3413
4104
|
/**
|
|
3414
4105
|
* @return null for "none",
|
|
@@ -3416,7 +4107,7 @@ class RenderTag extends Tag {
|
|
|
3416
4107
|
* @return Token for expression (not quoted)
|
|
3417
4108
|
* @throws TypeError if cannot read next token
|
|
3418
4109
|
*/
|
|
3419
|
-
function parseFilePath(tokenizer, liquid) {
|
|
4110
|
+
function parseFilePath(tokenizer, liquid, parser) {
|
|
3420
4111
|
if (liquid.options.dynamicPartials) {
|
|
3421
4112
|
const file = tokenizer.readValue();
|
|
3422
4113
|
tokenizer.assert(file, 'illegal file path');
|
|
@@ -3424,13 +4115,13 @@ function parseFilePath(tokenizer, liquid) {
|
|
|
3424
4115
|
return;
|
|
3425
4116
|
if (isQuotedToken(file)) {
|
|
3426
4117
|
// for filenames like "files/{{file}}", eval as liquid template
|
|
3427
|
-
const templates =
|
|
4118
|
+
const templates = parser.parse(evalQuotedToken(file));
|
|
3428
4119
|
return optimize(templates);
|
|
3429
4120
|
}
|
|
3430
4121
|
return file;
|
|
3431
4122
|
}
|
|
3432
4123
|
const tokens = [...tokenizer.readFileNameTemplate(liquid.options)];
|
|
3433
|
-
const templates = optimize(
|
|
4124
|
+
const templates = optimize(parser.parseTokens(tokens));
|
|
3434
4125
|
return templates === 'none' ? undefined : templates;
|
|
3435
4126
|
}
|
|
3436
4127
|
function optimize(templates) {
|
|
@@ -3448,10 +4139,10 @@ function* renderFilePath(file, ctx, liquid) {
|
|
|
3448
4139
|
}
|
|
3449
4140
|
|
|
3450
4141
|
class IncludeTag extends Tag {
|
|
3451
|
-
constructor(token, remainTokens, liquid) {
|
|
4142
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3452
4143
|
super(token, remainTokens, liquid);
|
|
3453
4144
|
const { tokenizer } = token;
|
|
3454
|
-
this['file'] = parseFilePath(tokenizer, this.liquid);
|
|
4145
|
+
this['file'] = parseFilePath(tokenizer, this.liquid, parser);
|
|
3455
4146
|
this['currentFile'] = token.file;
|
|
3456
4147
|
const begin = tokenizer.p;
|
|
3457
4148
|
const withStr = tokenizer.readIdentifier();
|
|
@@ -3465,7 +4156,7 @@ class IncludeTag extends Tag {
|
|
|
3465
4156
|
}
|
|
3466
4157
|
else
|
|
3467
4158
|
tokenizer.p = begin;
|
|
3468
|
-
this.hash = new Hash(tokenizer
|
|
4159
|
+
this.hash = new Hash(tokenizer, liquid.options.jekyllInclude || liquid.options.keyValueSeparator);
|
|
3469
4160
|
}
|
|
3470
4161
|
*render(ctx, emitter) {
|
|
3471
4162
|
const { liquid, hash, withVar } = this;
|
|
@@ -3484,12 +4175,43 @@ class IncludeTag extends Tag {
|
|
|
3484
4175
|
ctx.pop();
|
|
3485
4176
|
ctx.restoreRegister(saved);
|
|
3486
4177
|
}
|
|
4178
|
+
*children(partials, sync) {
|
|
4179
|
+
if (partials && isString(this['file'])) {
|
|
4180
|
+
return (yield this.liquid._parsePartialFile(this['file'], sync, this['currentFile']));
|
|
4181
|
+
}
|
|
4182
|
+
return [];
|
|
4183
|
+
}
|
|
4184
|
+
partialScope() {
|
|
4185
|
+
if (isString(this['file'])) {
|
|
4186
|
+
let names;
|
|
4187
|
+
if (this.liquid.options.jekyllInclude) {
|
|
4188
|
+
names = ['include'];
|
|
4189
|
+
}
|
|
4190
|
+
else {
|
|
4191
|
+
names = Object.keys(this.hash.hash);
|
|
4192
|
+
if (this.withVar) {
|
|
4193
|
+
names.push([this['file'], this.withVar]);
|
|
4194
|
+
}
|
|
4195
|
+
}
|
|
4196
|
+
return { name: this['file'], isolated: false, scope: names };
|
|
4197
|
+
}
|
|
4198
|
+
}
|
|
4199
|
+
*arguments() {
|
|
4200
|
+
yield* Object.values(this.hash.hash).filter(isValueToken);
|
|
4201
|
+
if (isValueToken(this['file'])) {
|
|
4202
|
+
yield this['file'];
|
|
4203
|
+
}
|
|
4204
|
+
if (isValueToken(this.withVar)) {
|
|
4205
|
+
yield this.withVar;
|
|
4206
|
+
}
|
|
4207
|
+
}
|
|
3487
4208
|
}
|
|
3488
4209
|
|
|
3489
4210
|
class DecrementTag extends Tag {
|
|
3490
4211
|
constructor(token, remainTokens, liquid) {
|
|
3491
4212
|
super(token, remainTokens, liquid);
|
|
3492
|
-
this.
|
|
4213
|
+
this.identifier = this.tokenizer.readIdentifier();
|
|
4214
|
+
this.variable = this.identifier.content;
|
|
3493
4215
|
}
|
|
3494
4216
|
render(context, emitter) {
|
|
3495
4217
|
const scope = context.environments;
|
|
@@ -3498,6 +4220,9 @@ class DecrementTag extends Tag {
|
|
|
3498
4220
|
}
|
|
3499
4221
|
emitter.write(stringify(--scope[this.variable]));
|
|
3500
4222
|
}
|
|
4223
|
+
*localScope() {
|
|
4224
|
+
yield this.identifier;
|
|
4225
|
+
}
|
|
3501
4226
|
}
|
|
3502
4227
|
|
|
3503
4228
|
class CycleTag extends Tag {
|
|
@@ -3535,40 +4260,38 @@ class CycleTag extends Tag {
|
|
|
3535
4260
|
groups[fingerprint] = idx;
|
|
3536
4261
|
return yield evalToken(candidate, ctx);
|
|
3537
4262
|
}
|
|
4263
|
+
*arguments() {
|
|
4264
|
+
yield* this.candidates;
|
|
4265
|
+
if (this.group) {
|
|
4266
|
+
yield this.group;
|
|
4267
|
+
}
|
|
4268
|
+
}
|
|
3538
4269
|
}
|
|
3539
4270
|
|
|
3540
4271
|
class IfTag extends Tag {
|
|
3541
|
-
constructor(tagToken, remainTokens, liquid) {
|
|
4272
|
+
constructor(tagToken, remainTokens, liquid, parser) {
|
|
3542
4273
|
super(tagToken, remainTokens, liquid);
|
|
3543
4274
|
this.branches = [];
|
|
3544
|
-
this.elseTemplates = [];
|
|
3545
4275
|
let p = [];
|
|
3546
|
-
|
|
3547
|
-
liquid.parser.parseStream(remainTokens)
|
|
4276
|
+
parser.parseStream(remainTokens)
|
|
3548
4277
|
.on('start', () => this.branches.push({
|
|
3549
|
-
value: new Value(tagToken.
|
|
4278
|
+
value: new Value(tagToken.tokenizer.readFilteredValue(), this.liquid),
|
|
3550
4279
|
templates: (p = [])
|
|
3551
4280
|
}))
|
|
3552
4281
|
.on('tag:elsif', (token) => {
|
|
3553
|
-
|
|
3554
|
-
p = [];
|
|
3555
|
-
return;
|
|
3556
|
-
}
|
|
4282
|
+
assert(!this.elseTemplates, 'unexpected elsif after else');
|
|
3557
4283
|
this.branches.push({
|
|
3558
|
-
value: new Value(token.
|
|
4284
|
+
value: new Value(token.tokenizer.readFilteredValue(), this.liquid),
|
|
3559
4285
|
templates: (p = [])
|
|
3560
4286
|
});
|
|
3561
4287
|
})
|
|
3562
|
-
.on('tag:else',
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
.on('tag:endif', function () { this.stop(); })
|
|
3567
|
-
.on('template', (tpl) => {
|
|
3568
|
-
if (p !== this.elseTemplates || elseCount === 1) {
|
|
3569
|
-
p.push(tpl);
|
|
3570
|
-
}
|
|
4288
|
+
.on('tag:else', tag => {
|
|
4289
|
+
assertEmpty(tag.args);
|
|
4290
|
+
assert(!this.elseTemplates, 'duplicated else');
|
|
4291
|
+
p = this.elseTemplates = [];
|
|
3571
4292
|
})
|
|
4293
|
+
.on('tag:endif', function (tag) { assertEmpty(tag.args); this.stop(); })
|
|
4294
|
+
.on('template', (tpl) => p.push(tpl))
|
|
3572
4295
|
.on('end', () => { throw new Error(`tag ${tagToken.getText()} not closed`); })
|
|
3573
4296
|
.start();
|
|
3574
4297
|
}
|
|
@@ -3581,14 +4304,25 @@ class IfTag extends Tag {
|
|
|
3581
4304
|
return;
|
|
3582
4305
|
}
|
|
3583
4306
|
}
|
|
3584
|
-
yield r.renderTemplates(this.elseTemplates, ctx, emitter);
|
|
4307
|
+
yield r.renderTemplates(this.elseTemplates || [], ctx, emitter);
|
|
4308
|
+
}
|
|
4309
|
+
*children() {
|
|
4310
|
+
const templates = this.branches.flatMap(b => b.templates);
|
|
4311
|
+
if (this.elseTemplates) {
|
|
4312
|
+
templates.push(...this.elseTemplates);
|
|
4313
|
+
}
|
|
4314
|
+
return templates;
|
|
4315
|
+
}
|
|
4316
|
+
arguments() {
|
|
4317
|
+
return this.branches.map(b => b.value);
|
|
3585
4318
|
}
|
|
3586
4319
|
}
|
|
3587
4320
|
|
|
3588
4321
|
class IncrementTag extends Tag {
|
|
3589
4322
|
constructor(token, remainTokens, liquid) {
|
|
3590
4323
|
super(token, remainTokens, liquid);
|
|
3591
|
-
this.
|
|
4324
|
+
this.identifier = this.tokenizer.readIdentifier();
|
|
4325
|
+
this.variable = this.identifier.content;
|
|
3592
4326
|
}
|
|
3593
4327
|
render(context, emitter) {
|
|
3594
4328
|
const scope = context.environments;
|
|
@@ -3599,15 +4333,18 @@ class IncrementTag extends Tag {
|
|
|
3599
4333
|
scope[this.variable]++;
|
|
3600
4334
|
emitter.write(stringify(val));
|
|
3601
4335
|
}
|
|
4336
|
+
*localScope() {
|
|
4337
|
+
yield this.identifier;
|
|
4338
|
+
}
|
|
3602
4339
|
}
|
|
3603
4340
|
|
|
3604
4341
|
class LayoutTag extends Tag {
|
|
3605
|
-
constructor(token, remainTokens, liquid) {
|
|
4342
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3606
4343
|
super(token, remainTokens, liquid);
|
|
3607
|
-
this.file = parseFilePath(this.tokenizer, this.liquid);
|
|
4344
|
+
this.file = parseFilePath(this.tokenizer, this.liquid, parser);
|
|
3608
4345
|
this['currentFile'] = token.file;
|
|
3609
|
-
this.args = new Hash(this.tokenizer.
|
|
3610
|
-
this.templates =
|
|
4346
|
+
this.args = new Hash(this.tokenizer, liquid.options.keyValueSeparator);
|
|
4347
|
+
this.templates = parser.parseTokens(remainTokens);
|
|
3611
4348
|
}
|
|
3612
4349
|
*render(ctx, emitter) {
|
|
3613
4350
|
const { liquid, args, file } = this;
|
|
@@ -3633,10 +4370,32 @@ class LayoutTag extends Tag {
|
|
|
3633
4370
|
yield renderer.renderTemplates(templates, ctx, emitter);
|
|
3634
4371
|
ctx.pop();
|
|
3635
4372
|
}
|
|
4373
|
+
*children(partials) {
|
|
4374
|
+
const templates = this.templates.slice();
|
|
4375
|
+
if (partials && isString(this.file)) {
|
|
4376
|
+
templates.push(...(yield this.liquid._parsePartialFile(this.file, true, this['currentFile'])));
|
|
4377
|
+
}
|
|
4378
|
+
return templates;
|
|
4379
|
+
}
|
|
4380
|
+
*arguments() {
|
|
4381
|
+
for (const v of Object.values(this.args.hash)) {
|
|
4382
|
+
if (isValueToken(v)) {
|
|
4383
|
+
yield v;
|
|
4384
|
+
}
|
|
4385
|
+
}
|
|
4386
|
+
if (isValueToken(this.file)) {
|
|
4387
|
+
yield this.file;
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
partialScope() {
|
|
4391
|
+
if (isString(this.file)) {
|
|
4392
|
+
return { name: this.file, isolated: false, scope: Object.keys(this.args.hash) };
|
|
4393
|
+
}
|
|
4394
|
+
}
|
|
3636
4395
|
}
|
|
3637
4396
|
|
|
3638
4397
|
class BlockTag extends Tag {
|
|
3639
|
-
constructor(token, remainTokens, liquid) {
|
|
4398
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3640
4399
|
super(token, remainTokens, liquid);
|
|
3641
4400
|
this.templates = [];
|
|
3642
4401
|
const match = /\w+/.exec(token.args);
|
|
@@ -3645,7 +4404,7 @@ class BlockTag extends Tag {
|
|
|
3645
4404
|
const token = remainTokens.shift();
|
|
3646
4405
|
if (isTagToken(token) && token.name === 'endblock')
|
|
3647
4406
|
return;
|
|
3648
|
-
const template =
|
|
4407
|
+
const template = parser.parseToken(token, remainTokens);
|
|
3649
4408
|
this.templates.push(template);
|
|
3650
4409
|
}
|
|
3651
4410
|
throw new Error(`tag ${token.getText()} not closed`);
|
|
@@ -3672,6 +4431,12 @@ class BlockTag extends Tag {
|
|
|
3672
4431
|
? (superBlock, emitter) => renderChild(new BlockDrop(() => renderCurrent(superBlock, emitter)), emitter)
|
|
3673
4432
|
: renderCurrent;
|
|
3674
4433
|
}
|
|
4434
|
+
*children() {
|
|
4435
|
+
return this.templates;
|
|
4436
|
+
}
|
|
4437
|
+
blockScope() {
|
|
4438
|
+
return ['block'];
|
|
4439
|
+
}
|
|
3675
4440
|
}
|
|
3676
4441
|
|
|
3677
4442
|
class RawTag extends Tag {
|
|
@@ -3715,7 +4480,7 @@ class TablerowloopDrop extends ForloopDrop {
|
|
|
3715
4480
|
}
|
|
3716
4481
|
|
|
3717
4482
|
class TablerowTag extends Tag {
|
|
3718
|
-
constructor(tagToken, remainTokens, liquid) {
|
|
4483
|
+
constructor(tagToken, remainTokens, liquid, parser) {
|
|
3719
4484
|
super(tagToken, remainTokens, liquid);
|
|
3720
4485
|
const variable = this.tokenizer.readIdentifier();
|
|
3721
4486
|
this.tokenizer.skipBlank();
|
|
@@ -3726,10 +4491,10 @@ class TablerowTag extends Tag {
|
|
|
3726
4491
|
}
|
|
3727
4492
|
this.variable = variable.content;
|
|
3728
4493
|
this.collection = collectionToken;
|
|
3729
|
-
this.args = new Hash(this.tokenizer.
|
|
4494
|
+
this.args = new Hash(this.tokenizer, liquid.options.keyValueSeparator);
|
|
3730
4495
|
this.templates = [];
|
|
3731
4496
|
let p;
|
|
3732
|
-
const stream =
|
|
4497
|
+
const stream = parser.parseStream(remainTokens)
|
|
3733
4498
|
.on('start', () => (p = this.templates))
|
|
3734
4499
|
.on('tag:endtablerow', () => stream.stop())
|
|
3735
4500
|
.on('template', (tpl) => p.push(tpl))
|
|
@@ -3764,18 +4529,32 @@ class TablerowTag extends Tag {
|
|
|
3764
4529
|
emitter.write('</tr>');
|
|
3765
4530
|
ctx.pop();
|
|
3766
4531
|
}
|
|
4532
|
+
*children() {
|
|
4533
|
+
return this.templates;
|
|
4534
|
+
}
|
|
4535
|
+
*arguments() {
|
|
4536
|
+
yield this.collection;
|
|
4537
|
+
for (const v of Object.values(this.args.hash)) {
|
|
4538
|
+
if (isValueToken(v)) {
|
|
4539
|
+
yield v;
|
|
4540
|
+
}
|
|
4541
|
+
}
|
|
4542
|
+
}
|
|
4543
|
+
blockScope() {
|
|
4544
|
+
return [this.variable, 'tablerowloop'];
|
|
4545
|
+
}
|
|
3767
4546
|
}
|
|
3768
4547
|
|
|
3769
4548
|
class UnlessTag extends Tag {
|
|
3770
|
-
constructor(tagToken, remainTokens, liquid) {
|
|
4549
|
+
constructor(tagToken, remainTokens, liquid, parser) {
|
|
3771
4550
|
super(tagToken, remainTokens, liquid);
|
|
3772
4551
|
this.branches = [];
|
|
3773
4552
|
this.elseTemplates = [];
|
|
3774
4553
|
let p = [];
|
|
3775
4554
|
let elseCount = 0;
|
|
3776
|
-
|
|
4555
|
+
parser.parseStream(remainTokens)
|
|
3777
4556
|
.on('start', () => this.branches.push({
|
|
3778
|
-
value: new Value(tagToken.
|
|
4557
|
+
value: new Value(tagToken.tokenizer.readFilteredValue(), this.liquid),
|
|
3779
4558
|
test: isFalsy,
|
|
3780
4559
|
templates: (p = [])
|
|
3781
4560
|
}))
|
|
@@ -3785,7 +4564,7 @@ class UnlessTag extends Tag {
|
|
|
3785
4564
|
return;
|
|
3786
4565
|
}
|
|
3787
4566
|
this.branches.push({
|
|
3788
|
-
value: new Value(token.
|
|
4567
|
+
value: new Value(token.tokenizer.readFilteredValue(), this.liquid),
|
|
3789
4568
|
test: isTruthy,
|
|
3790
4569
|
templates: (p = [])
|
|
3791
4570
|
});
|
|
@@ -3814,17 +4593,27 @@ class UnlessTag extends Tag {
|
|
|
3814
4593
|
}
|
|
3815
4594
|
yield r.renderTemplates(this.elseTemplates, ctx, emitter);
|
|
3816
4595
|
}
|
|
4596
|
+
*children() {
|
|
4597
|
+
const children = this.branches.flatMap(b => b.templates);
|
|
4598
|
+
if (this.elseTemplates) {
|
|
4599
|
+
children.push(...this.elseTemplates);
|
|
4600
|
+
}
|
|
4601
|
+
return children;
|
|
4602
|
+
}
|
|
4603
|
+
arguments() {
|
|
4604
|
+
return this.branches.map(b => b.value);
|
|
4605
|
+
}
|
|
3817
4606
|
}
|
|
3818
4607
|
|
|
3819
4608
|
class BreakTag extends Tag {
|
|
3820
|
-
render(ctx,
|
|
3821
|
-
|
|
4609
|
+
render(ctx, _emitter) {
|
|
4610
|
+
ctx.breakCalled = true;
|
|
3822
4611
|
}
|
|
3823
4612
|
}
|
|
3824
4613
|
|
|
3825
4614
|
class ContinueTag extends Tag {
|
|
3826
|
-
render(ctx,
|
|
3827
|
-
|
|
4615
|
+
render(ctx, _emitter) {
|
|
4616
|
+
ctx.continueCalled = true;
|
|
3828
4617
|
}
|
|
3829
4618
|
}
|
|
3830
4619
|
|
|
@@ -3842,17 +4631,25 @@ class EchoTag extends Tag {
|
|
|
3842
4631
|
const val = yield this.value.value(ctx, false);
|
|
3843
4632
|
emitter.write(val);
|
|
3844
4633
|
}
|
|
4634
|
+
*arguments() {
|
|
4635
|
+
if (this.value) {
|
|
4636
|
+
yield this.value;
|
|
4637
|
+
}
|
|
4638
|
+
}
|
|
3845
4639
|
}
|
|
3846
4640
|
|
|
3847
4641
|
class LiquidTag extends Tag {
|
|
3848
|
-
constructor(token, remainTokens, liquid) {
|
|
4642
|
+
constructor(token, remainTokens, liquid, parser) {
|
|
3849
4643
|
super(token, remainTokens, liquid);
|
|
3850
4644
|
const tokens = this.tokenizer.readLiquidTagTokens(this.liquid.options);
|
|
3851
|
-
this.templates =
|
|
4645
|
+
this.templates = parser.parseTokens(tokens);
|
|
3852
4646
|
}
|
|
3853
4647
|
*render(ctx, emitter) {
|
|
3854
4648
|
yield this.liquid.renderer.renderTemplates(this.templates, ctx, emitter);
|
|
3855
4649
|
}
|
|
4650
|
+
*children() {
|
|
4651
|
+
return this.templates;
|
|
4652
|
+
}
|
|
3856
4653
|
}
|
|
3857
4654
|
|
|
3858
4655
|
class InlineCommentTag extends Tag {
|
|
@@ -3895,12 +4692,14 @@ class Liquid {
|
|
|
3895
4692
|
this.filters = {};
|
|
3896
4693
|
this.tags = {};
|
|
3897
4694
|
this.options = normalize(opts);
|
|
4695
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
3898
4696
|
this.parser = new Parser(this);
|
|
3899
4697
|
forOwn(tags, (conf, name) => this.registerTag(name, conf));
|
|
3900
4698
|
forOwn(filters, (handler, name) => this.registerFilter(name, handler));
|
|
3901
4699
|
}
|
|
3902
4700
|
parse(html, filepath) {
|
|
3903
|
-
|
|
4701
|
+
const parser = new Parser(this);
|
|
4702
|
+
return parser.parse(html, filepath);
|
|
3904
4703
|
}
|
|
3905
4704
|
_render(tpl, scope, renderOptions) {
|
|
3906
4705
|
const ctx = scope instanceof Context ? scope : new Context(scope, this.options, renderOptions);
|
|
@@ -3931,21 +4730,21 @@ class Liquid {
|
|
|
3931
4730
|
return toValueSync(this._parseAndRender(html, scope, Object.assign(Object.assign({}, renderOptions), { sync: true })));
|
|
3932
4731
|
}
|
|
3933
4732
|
_parsePartialFile(file, sync, currentFile) {
|
|
3934
|
-
return this.
|
|
4733
|
+
return new Parser(this).parseFile(file, sync, LookupType.Partials, currentFile);
|
|
3935
4734
|
}
|
|
3936
4735
|
_parseLayoutFile(file, sync, currentFile) {
|
|
3937
|
-
return this.
|
|
4736
|
+
return new Parser(this).parseFile(file, sync, LookupType.Layouts, currentFile);
|
|
3938
4737
|
}
|
|
3939
4738
|
_parseFile(file, sync, lookupType, currentFile) {
|
|
3940
|
-
return this.
|
|
4739
|
+
return new Parser(this).parseFile(file, sync, lookupType, currentFile);
|
|
3941
4740
|
}
|
|
3942
4741
|
parseFile(file, lookupType) {
|
|
3943
4742
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3944
|
-
return toPromise(this.
|
|
4743
|
+
return toPromise(new Parser(this).parseFile(file, false, lookupType));
|
|
3945
4744
|
});
|
|
3946
4745
|
}
|
|
3947
4746
|
parseFileSync(file, lookupType) {
|
|
3948
|
-
return toValueSync(this.
|
|
4747
|
+
return toValueSync(new Parser(this).parseFile(file, true, lookupType));
|
|
3949
4748
|
}
|
|
3950
4749
|
*_renderFile(file, ctx, renderFileOptions) {
|
|
3951
4750
|
const templates = (yield this._parseFile(file, renderFileOptions.sync, renderFileOptions.lookupType));
|
|
@@ -4001,9 +4800,97 @@ class Liquid {
|
|
|
4001
4800
|
self.renderFile(filePath, ctx).then(html => callback(null, html), callback);
|
|
4002
4801
|
};
|
|
4003
4802
|
}
|
|
4803
|
+
analyze(template, options = {}) {
|
|
4804
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4805
|
+
return analyze(template, options);
|
|
4806
|
+
});
|
|
4807
|
+
}
|
|
4808
|
+
analyzeSync(template, options = {}) {
|
|
4809
|
+
return analyzeSync(template, options);
|
|
4810
|
+
}
|
|
4811
|
+
parseAndAnalyze(html, filename, options = {}) {
|
|
4812
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4813
|
+
return analyze(this.parse(html, filename), options);
|
|
4814
|
+
});
|
|
4815
|
+
}
|
|
4816
|
+
parseAndAnalyzeSync(html, filename, options = {}) {
|
|
4817
|
+
return analyzeSync(this.parse(html, filename), options);
|
|
4818
|
+
}
|
|
4819
|
+
/** Return an array of all variables without their properties. */
|
|
4820
|
+
variables(template, options = {}) {
|
|
4821
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4822
|
+
const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
|
|
4823
|
+
return Object.keys(analysis.variables);
|
|
4824
|
+
});
|
|
4825
|
+
}
|
|
4826
|
+
/** Return an array of all variables without their properties. */
|
|
4827
|
+
variablesSync(template, options = {}) {
|
|
4828
|
+
const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
|
|
4829
|
+
return Object.keys(analysis.variables);
|
|
4830
|
+
}
|
|
4831
|
+
/** Return an array of all variables including their properties/paths. */
|
|
4832
|
+
fullVariables(template, options = {}) {
|
|
4833
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4834
|
+
const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
|
|
4835
|
+
return Array.from(new Set(Object.values(analysis.variables).flatMap((a) => a.map((v) => String(v)))));
|
|
4836
|
+
});
|
|
4837
|
+
}
|
|
4838
|
+
/** Return an array of all variables including their properties/paths. */
|
|
4839
|
+
fullVariablesSync(template, options = {}) {
|
|
4840
|
+
const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
|
|
4841
|
+
return Array.from(new Set(Object.values(analysis.variables).flatMap((a) => a.map((v) => String(v)))));
|
|
4842
|
+
}
|
|
4843
|
+
/** Return an array of all variables, each as an array of properties/segments. */
|
|
4844
|
+
variableSegments(template, options = {}) {
|
|
4845
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4846
|
+
const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
|
|
4847
|
+
return Array.from(strictUniq(Object.values(analysis.variables).flatMap((a) => a.map((v) => v.toArray()))));
|
|
4848
|
+
});
|
|
4849
|
+
}
|
|
4850
|
+
/** Return an array of all variables, each as an array of properties/segments. */
|
|
4851
|
+
variableSegmentsSync(template, options = {}) {
|
|
4852
|
+
const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
|
|
4853
|
+
return Array.from(strictUniq(Object.values(analysis.variables).flatMap((a) => a.map((v) => v.toArray()))));
|
|
4854
|
+
}
|
|
4855
|
+
/** Return an array of all expected context variables without their properties. */
|
|
4856
|
+
globalVariables(template, options = {}) {
|
|
4857
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4858
|
+
const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
|
|
4859
|
+
return Object.keys(analysis.globals);
|
|
4860
|
+
});
|
|
4861
|
+
}
|
|
4862
|
+
/** Return an array of all expected context variables without their properties. */
|
|
4863
|
+
globalVariablesSync(template, options = {}) {
|
|
4864
|
+
const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
|
|
4865
|
+
return Object.keys(analysis.globals);
|
|
4866
|
+
}
|
|
4867
|
+
/** Return an array of all expected context variables including their properties/paths. */
|
|
4868
|
+
globalFullVariables(template, options = {}) {
|
|
4869
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4870
|
+
const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
|
|
4871
|
+
return Array.from(new Set(Object.values(analysis.globals).flatMap((a) => a.map((v) => String(v)))));
|
|
4872
|
+
});
|
|
4873
|
+
}
|
|
4874
|
+
/** Return an array of all expected context variables including their properties/paths. */
|
|
4875
|
+
globalFullVariablesSync(template, options = {}) {
|
|
4876
|
+
const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
|
|
4877
|
+
return Array.from(new Set(Object.values(analysis.globals).flatMap((a) => a.map((v) => String(v)))));
|
|
4878
|
+
}
|
|
4879
|
+
/** Return an array of all expected context variables, each as an array of properties/segments. */
|
|
4880
|
+
globalVariableSegments(template, options = {}) {
|
|
4881
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4882
|
+
const analysis = yield analyze(isString(template) ? this.parse(template) : template, options);
|
|
4883
|
+
return Array.from(strictUniq(Object.values(analysis.globals).flatMap((a) => a.map((v) => v.toArray()))));
|
|
4884
|
+
});
|
|
4885
|
+
}
|
|
4886
|
+
/** Return an array of all expected context variables, each as an array of properties/segments. */
|
|
4887
|
+
globalVariableSegmentsSync(template, options = {}) {
|
|
4888
|
+
const analysis = analyzeSync(isString(template) ? this.parse(template) : template, options);
|
|
4889
|
+
return Array.from(strictUniq(Object.values(analysis.globals).flatMap((a) => a.map((v) => v.toArray()))));
|
|
4890
|
+
}
|
|
4004
4891
|
}
|
|
4005
4892
|
|
|
4006
4893
|
/* istanbul ignore file */
|
|
4007
|
-
const version = '
|
|
4894
|
+
const version = '[VI]{version}[/VI]';
|
|
4008
4895
|
|
|
4009
|
-
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,
|
|
4896
|
+
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 };
|