@skeptools/skep-organization-project 0.0.18 → 0.0.19
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 +22 -2139
- package/API.md +533 -418
- package/lib/skep-organization-project.js +36 -5
- package/node_modules/liquidjs/README.md +110 -67
- package/node_modules/liquidjs/dist/build/base64-impl-browser.d.ts +2 -0
- package/node_modules/liquidjs/dist/build/base64-impl-browser.spec.d.ts +1 -0
- package/node_modules/liquidjs/dist/build/crypto-impl-browser.d.ts +2 -0
- package/node_modules/liquidjs/dist/build/crypto-impl-browser.spec.d.ts +1 -0
- package/node_modules/liquidjs/dist/context/context.d.ts +10 -4
- package/node_modules/liquidjs/dist/context/scope.d.ts +2 -2
- package/node_modules/liquidjs/dist/drop/blank-drop.d.ts +1 -1
- package/node_modules/liquidjs/dist/drop/block-drop.d.ts +3 -2
- package/node_modules/liquidjs/dist/drop/comparable.d.ts +11 -5
- package/node_modules/liquidjs/dist/drop/drop.d.ts +3 -1
- package/node_modules/liquidjs/dist/drop/empty-drop.d.ts +1 -1
- package/node_modules/liquidjs/dist/drop/null-drop.d.ts +1 -1
- package/node_modules/liquidjs/dist/filters/array.d.ts +9 -3
- package/node_modules/liquidjs/dist/filters/base64-impl.d.ts +2 -0
- package/node_modules/liquidjs/dist/filters/base64.d.ts +9 -0
- package/node_modules/liquidjs/dist/filters/crypto-impl.d.ts +2 -0
- package/node_modules/liquidjs/dist/filters/crypto.d.ts +8 -0
- package/node_modules/liquidjs/dist/filters/math.d.ts +1 -1
- package/node_modules/liquidjs/dist/filters/misc.d.ts +2 -2
- package/node_modules/liquidjs/dist/filters/string.d.ts +2 -1
- package/node_modules/liquidjs/dist/fs/fs-impl.d.ts +2 -1
- package/node_modules/liquidjs/dist/fs/fs.d.ts +4 -2
- package/node_modules/liquidjs/dist/fs/loader.d.ts +2 -1
- package/node_modules/liquidjs/dist/index.d.ts +8 -3
- package/node_modules/liquidjs/dist/liquid-options.d.ts +8 -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.mjs +836 -406
- package/node_modules/liquidjs/dist/liquid.browser.umd.js +1683 -788
- package/node_modules/liquidjs/dist/liquid.browser.umd.js.map +1 -1
- package/node_modules/liquidjs/dist/liquid.d.ts +1 -0
- package/node_modules/liquidjs/dist/liquid.node.js +848 -426
- package/node_modules/liquidjs/dist/liquid.node.mjs +849 -422
- package/node_modules/liquidjs/dist/parser/parser.d.ts +1 -0
- package/node_modules/liquidjs/dist/parser/token-kind.d.ts +1 -0
- package/node_modules/liquidjs/dist/parser/tokenizer.d.ts +4 -3
- package/node_modules/liquidjs/dist/render/expression.d.ts +11 -1
- package/node_modules/liquidjs/dist/render/operator.d.ts +11 -4
- package/node_modules/liquidjs/dist/tags/case.d.ts +2 -2
- package/node_modules/liquidjs/dist/tags/for.d.ts +2 -2
- package/node_modules/liquidjs/dist/tags/include.d.ts +2 -0
- package/node_modules/liquidjs/dist/tags/layout.d.ts +1 -0
- package/node_modules/liquidjs/dist/tags/render.d.ts +2 -0
- package/node_modules/liquidjs/dist/tags/tablerow.d.ts +2 -2
- package/node_modules/liquidjs/dist/tokens/literal-token.d.ts +2 -2
- package/node_modules/liquidjs/dist/tokens/operator-token.d.ts +3 -2
- package/node_modules/liquidjs/dist/tokens/property-access-token.d.ts +3 -2
- package/node_modules/liquidjs/dist/tokens/tag-token.d.ts +1 -1
- package/node_modules/liquidjs/dist/tokens/value-token.d.ts +2 -1
- package/node_modules/liquidjs/dist/util/async.d.ts +2 -0
- package/node_modules/liquidjs/dist/util/liquid-date.d.ts +7 -0
- package/node_modules/liquidjs/dist/util/operator-trie.d.ts +4 -8
- package/node_modules/liquidjs/dist/util/strftime.d.ts +2 -1
- package/node_modules/liquidjs/dist/util/type-guards.d.ts +2 -1
- package/node_modules/liquidjs/dist/util/underscore.d.ts +5 -1
- package/node_modules/liquidjs/package.json +20 -14
- package/package.json +47 -41
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* liquidjs@10.
|
|
3
|
-
* (c) 2016-
|
|
2
|
+
* liquidjs@10.30.1, https://github.com/harttle/liquidjs
|
|
3
|
+
* (c) 2016-2026 harttle
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
6
6
|
class Token {
|
|
@@ -32,7 +32,7 @@ class Token {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
class Drop {
|
|
35
|
-
liquidMethodMissing(key) {
|
|
35
|
+
liquidMethodMissing(key, context) {
|
|
36
36
|
return undefined;
|
|
37
37
|
}
|
|
38
38
|
}
|
|
@@ -53,9 +53,6 @@ function isPromise(val) {
|
|
|
53
53
|
function isIterator(val) {
|
|
54
54
|
return val && isFunction(val.next) && isFunction(val.throw) && isFunction(val.return);
|
|
55
55
|
}
|
|
56
|
-
function escapeRegex(str) {
|
|
57
|
-
return str.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
58
|
-
}
|
|
59
56
|
function stringify(value) {
|
|
60
57
|
value = toValue(value);
|
|
61
58
|
if (isString(value))
|
|
@@ -66,6 +63,25 @@ function stringify(value) {
|
|
|
66
63
|
return value.map(x => stringify(x)).join('');
|
|
67
64
|
return String(value);
|
|
68
65
|
}
|
|
66
|
+
function advanceCodePoints(str, from, count) {
|
|
67
|
+
let index = from;
|
|
68
|
+
for (let i = 0; i < count && index < str.length; i++)
|
|
69
|
+
index += str.codePointAt(index) > 0xFFFF ? 2 : 1;
|
|
70
|
+
return index;
|
|
71
|
+
}
|
|
72
|
+
function codePointLength(str) {
|
|
73
|
+
let length = 0;
|
|
74
|
+
for (let index = 0; index < str.length; index = advanceCodePoints(str, index, 1))
|
|
75
|
+
length++;
|
|
76
|
+
return length;
|
|
77
|
+
}
|
|
78
|
+
function readArrayElement(arr, index, ownPropertyOnly) {
|
|
79
|
+
if (index < 0)
|
|
80
|
+
index = arr.length + index;
|
|
81
|
+
if (ownPropertyOnly && !hasOwnProperty.call(arr, index))
|
|
82
|
+
return undefined;
|
|
83
|
+
return arr[index];
|
|
84
|
+
}
|
|
69
85
|
function toEnumerable(val) {
|
|
70
86
|
val = toValue(val);
|
|
71
87
|
if (isArray(val))
|
|
@@ -90,8 +106,7 @@ function toValue(value) {
|
|
|
90
106
|
return (value instanceof Drop && isFunction(value.valueOf)) ? value.valueOf() : value;
|
|
91
107
|
}
|
|
92
108
|
function toNumber(value) {
|
|
93
|
-
|
|
94
|
-
return isNaN(value) ? 0 : value;
|
|
109
|
+
return +toValue(value) || 0;
|
|
95
110
|
}
|
|
96
111
|
function isNumber(value) {
|
|
97
112
|
return typeof value === 'number';
|
|
@@ -163,10 +178,10 @@ function padEnd(str, length, ch = ' ') {
|
|
|
163
178
|
}
|
|
164
179
|
function pad(str, length, ch, add) {
|
|
165
180
|
str = String(str);
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
return str;
|
|
181
|
+
const n = length - str.length;
|
|
182
|
+
if (n <= 0)
|
|
183
|
+
return str;
|
|
184
|
+
return add(str, ch.repeat(n));
|
|
170
185
|
}
|
|
171
186
|
function identify(val) {
|
|
172
187
|
return val;
|
|
@@ -178,13 +193,26 @@ function changeCase(str) {
|
|
|
178
193
|
function ellipsis(str, N) {
|
|
179
194
|
return str.length > N ? str.slice(0, N - 3) + '...' : str;
|
|
180
195
|
}
|
|
196
|
+
function orderedCompare(a, b) {
|
|
197
|
+
if (isNil(a) && isNil(b))
|
|
198
|
+
return 0;
|
|
199
|
+
if (isNil(a))
|
|
200
|
+
return 1;
|
|
201
|
+
if (isNil(b))
|
|
202
|
+
return -1;
|
|
203
|
+
if (a < b)
|
|
204
|
+
return -1;
|
|
205
|
+
if (a > b)
|
|
206
|
+
return 1;
|
|
207
|
+
return 0;
|
|
208
|
+
}
|
|
181
209
|
// compare string in case-insensitive way, undefined values to the tail
|
|
182
210
|
function caseInsensitiveCompare(a, b) {
|
|
183
|
-
if (a
|
|
211
|
+
if (isNil(a) && isNil(b))
|
|
184
212
|
return 0;
|
|
185
|
-
if (a
|
|
213
|
+
if (isNil(a))
|
|
186
214
|
return 1;
|
|
187
|
-
if (b
|
|
215
|
+
if (isNil(b))
|
|
188
216
|
return -1;
|
|
189
217
|
a = toLowerCase.call(a);
|
|
190
218
|
b = toLowerCase.call(b);
|
|
@@ -197,8 +225,8 @@ function caseInsensitiveCompare(a, b) {
|
|
|
197
225
|
function argumentsToValue(fn) {
|
|
198
226
|
return function (...args) { return fn.call(this, ...args.map(toValue)); };
|
|
199
227
|
}
|
|
200
|
-
function
|
|
201
|
-
return
|
|
228
|
+
function argumentsToNumber(fn) {
|
|
229
|
+
return function (...args) { return fn.call(this, ...args.map(toNumber)); };
|
|
202
230
|
}
|
|
203
231
|
/** Return an array containing unique elements from _array_. Works with nested arrays and objects. */
|
|
204
232
|
function* strictUniq(array) {
|
|
@@ -364,8 +392,8 @@ function assertEmpty(predicate, message = `unexpected ${JSON.stringify(predicate
|
|
|
364
392
|
}
|
|
365
393
|
|
|
366
394
|
class NullDrop extends Drop {
|
|
367
|
-
equals(value) {
|
|
368
|
-
return isNil(toValue(value));
|
|
395
|
+
*equals(value) {
|
|
396
|
+
return isNil(yield toValue(value));
|
|
369
397
|
}
|
|
370
398
|
gt() {
|
|
371
399
|
return false;
|
|
@@ -385,10 +413,10 @@ class NullDrop extends Drop {
|
|
|
385
413
|
}
|
|
386
414
|
|
|
387
415
|
class EmptyDrop extends Drop {
|
|
388
|
-
equals(value) {
|
|
416
|
+
*equals(value) {
|
|
389
417
|
if (value instanceof EmptyDrop)
|
|
390
418
|
return false;
|
|
391
|
-
value = toValue(value);
|
|
419
|
+
value = yield toValue(value);
|
|
392
420
|
if (isString(value) || isArray(value))
|
|
393
421
|
return value.length === 0;
|
|
394
422
|
if (isObject(value))
|
|
@@ -416,14 +444,14 @@ class EmptyDrop extends Drop {
|
|
|
416
444
|
}
|
|
417
445
|
|
|
418
446
|
class BlankDrop extends EmptyDrop {
|
|
419
|
-
equals(value) {
|
|
447
|
+
*equals(value) {
|
|
420
448
|
if (value === false)
|
|
421
449
|
return true;
|
|
422
|
-
if (isNil(toValue(value)))
|
|
450
|
+
if (isNil(yield toValue(value)))
|
|
423
451
|
return true;
|
|
424
452
|
if (isString(value))
|
|
425
453
|
return /^\s*$/.test(value);
|
|
426
|
-
return super.equals(value);
|
|
454
|
+
return yield super.equals(value);
|
|
427
455
|
}
|
|
428
456
|
static is(value) {
|
|
429
457
|
return value instanceof BlankDrop;
|
|
@@ -463,6 +491,42 @@ class ForloopDrop extends Drop {
|
|
|
463
491
|
}
|
|
464
492
|
}
|
|
465
493
|
|
|
494
|
+
class SimpleEmitter {
|
|
495
|
+
constructor() {
|
|
496
|
+
this.buffer = '';
|
|
497
|
+
}
|
|
498
|
+
write(html) {
|
|
499
|
+
this.buffer += stringify(html);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
class StreamedEmitter {
|
|
504
|
+
constructor() {
|
|
505
|
+
this.buffer = '';
|
|
506
|
+
this.stream = null;
|
|
507
|
+
throw new Error('streaming not supported in browser');
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
class KeepingTypeEmitter {
|
|
512
|
+
constructor() {
|
|
513
|
+
this.buffer = '';
|
|
514
|
+
}
|
|
515
|
+
write(html) {
|
|
516
|
+
html = toValue(html);
|
|
517
|
+
// This will only preserve the type if the value is isolated.
|
|
518
|
+
// I.E:
|
|
519
|
+
// {{ my-port }} -> 42
|
|
520
|
+
// {{ my-host }}:{{ my-port }} -> 'host:42'
|
|
521
|
+
if (typeof html !== 'string' && this.buffer === '') {
|
|
522
|
+
this.buffer = html;
|
|
523
|
+
}
|
|
524
|
+
else {
|
|
525
|
+
this.buffer = stringify(this.buffer) + stringify(html);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
466
530
|
class BlockDrop extends Drop {
|
|
467
531
|
constructor(
|
|
468
532
|
// the block render from layout template
|
|
@@ -474,8 +538,10 @@ class BlockDrop extends Drop {
|
|
|
474
538
|
* Provide parent access in child block by
|
|
475
539
|
* {{ block.super }}
|
|
476
540
|
*/
|
|
477
|
-
super() {
|
|
478
|
-
|
|
541
|
+
*super() {
|
|
542
|
+
const emitter = new SimpleEmitter();
|
|
543
|
+
yield this.superBlockRender(emitter);
|
|
544
|
+
return emitter.buffer;
|
|
479
545
|
}
|
|
480
546
|
}
|
|
481
547
|
|
|
@@ -498,7 +564,16 @@ const literalValues = {
|
|
|
498
564
|
'blank': new BlankDrop()
|
|
499
565
|
};
|
|
500
566
|
|
|
567
|
+
// Tries are built once per input object and reused: the Tokenizer rebuilds them
|
|
568
|
+
// on every instantiation, but `input` (operators/literalValues) is a stable
|
|
569
|
+
// reference. WeakMap-keying by `input` lets short-lived operator objects (and
|
|
570
|
+
// their tries) be garbage collected. The returned trie is treated as read-only
|
|
571
|
+
// by callers (matchTrie only reads it); do not mutate it.
|
|
572
|
+
const trieCache = new WeakMap();
|
|
501
573
|
function createTrie(input) {
|
|
574
|
+
const cached = trieCache.get(input);
|
|
575
|
+
if (cached)
|
|
576
|
+
return cached;
|
|
502
577
|
const trie = {};
|
|
503
578
|
for (const [name, data] of Object.entries(input)) {
|
|
504
579
|
let node = trie;
|
|
@@ -513,6 +588,7 @@ function createTrie(input) {
|
|
|
513
588
|
node.data = data;
|
|
514
589
|
node.end = true;
|
|
515
590
|
}
|
|
591
|
+
trieCache.set(input, trie);
|
|
516
592
|
return trie;
|
|
517
593
|
}
|
|
518
594
|
|
|
@@ -552,6 +628,12 @@ function __awaiter(thisArg, _arguments, P, generator) {
|
|
|
552
628
|
});
|
|
553
629
|
}
|
|
554
630
|
|
|
631
|
+
function toLiquidAsync(asyncFn, syncFn) {
|
|
632
|
+
const syncImpl = syncFn || asyncFn;
|
|
633
|
+
return (sync, ...args) => {
|
|
634
|
+
return sync ? syncImpl(...args) : asyncFn(...args);
|
|
635
|
+
};
|
|
636
|
+
}
|
|
555
637
|
// convert an async iterator to a Promise
|
|
556
638
|
function toPromise(val) {
|
|
557
639
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -562,7 +644,7 @@ function toPromise(val) {
|
|
|
562
644
|
let next = 'next';
|
|
563
645
|
do {
|
|
564
646
|
const state = val[next](value);
|
|
565
|
-
done = state.done;
|
|
647
|
+
done = !!state.done;
|
|
566
648
|
value = state.value;
|
|
567
649
|
next = 'next';
|
|
568
650
|
try {
|
|
@@ -588,7 +670,7 @@ function toValueSync(val) {
|
|
|
588
670
|
let next = 'next';
|
|
589
671
|
do {
|
|
590
672
|
const state = val[next](value);
|
|
591
|
-
done = state.done;
|
|
673
|
+
done = !!state.done;
|
|
592
674
|
value = state.value;
|
|
593
675
|
next = 'next';
|
|
594
676
|
if (isIterator(value)) {
|
|
@@ -618,12 +700,9 @@ function getDayOfYear(d) {
|
|
|
618
700
|
return num + d.getDate();
|
|
619
701
|
}
|
|
620
702
|
function getWeekOfYear(d, startDay) {
|
|
621
|
-
//
|
|
622
|
-
const
|
|
623
|
-
|
|
624
|
-
const jan1 = new Date(d.getFullYear(), 0, 1);
|
|
625
|
-
const then = (7 - jan1.getDay() + startDay);
|
|
626
|
-
return String(Math.floor((now - then) / 7) + 1);
|
|
703
|
+
// Days before the first startDay of the year belong to week 0
|
|
704
|
+
const daysSinceStartDay = (d.getDay() - startDay + 7) % 7;
|
|
705
|
+
return String(Math.floor((getDayOfYear(d) - 1 - daysSinceStartDay + 7) / 7));
|
|
627
706
|
}
|
|
628
707
|
function isLeapYear(d) {
|
|
629
708
|
const year = d.getFullYear();
|
|
@@ -641,10 +720,11 @@ function ordinal(d) {
|
|
|
641
720
|
}
|
|
642
721
|
}
|
|
643
722
|
function century(d) {
|
|
644
|
-
return
|
|
723
|
+
return Math.floor(d.getFullYear() / 100);
|
|
645
724
|
}
|
|
646
725
|
// default to 0
|
|
647
726
|
const padWidths = {
|
|
727
|
+
C: 2,
|
|
648
728
|
d: 2,
|
|
649
729
|
e: 2,
|
|
650
730
|
H: 2,
|
|
@@ -657,7 +737,9 @@ const padWidths = {
|
|
|
657
737
|
M: 2,
|
|
658
738
|
S: 2,
|
|
659
739
|
U: 2,
|
|
660
|
-
W: 2
|
|
740
|
+
W: 2,
|
|
741
|
+
y: 2,
|
|
742
|
+
Y: 4
|
|
661
743
|
};
|
|
662
744
|
const padSpaceChars = new Set('aAbBceklpP');
|
|
663
745
|
function getTimezoneOffset(d, opts) {
|
|
@@ -687,14 +769,16 @@ const formatCodes = {
|
|
|
687
769
|
m: (d) => d.getMonth() + 1,
|
|
688
770
|
M: (d) => d.getMinutes(),
|
|
689
771
|
N: (d, opts) => {
|
|
772
|
+
var _a;
|
|
690
773
|
const width = Number(opts.width) || 9;
|
|
691
|
-
const str = String(d.getMilliseconds()).slice(0, width);
|
|
774
|
+
const str = padStart(String(d.getMilliseconds()), 3, '0').slice(0, width);
|
|
775
|
+
(_a = opts.memoryLimit) === null || _a === void 0 ? void 0 : _a.use(width - str.length);
|
|
692
776
|
return padEnd(str, width, '0');
|
|
693
777
|
},
|
|
694
778
|
p: (d) => (d.getHours() < 12 ? 'AM' : 'PM'),
|
|
695
779
|
P: (d) => (d.getHours() < 12 ? 'am' : 'pm'),
|
|
696
780
|
q: (d) => ordinal(d),
|
|
697
|
-
s: (d) => Math.
|
|
781
|
+
s: (d) => Math.floor(d.dateValue() / 1000),
|
|
698
782
|
S: (d) => d.getSeconds(),
|
|
699
783
|
u: (d) => d.getDay() || 7,
|
|
700
784
|
U: (d) => getWeekOfYear(d, 0),
|
|
@@ -702,7 +786,7 @@ const formatCodes = {
|
|
|
702
786
|
W: (d) => getWeekOfYear(d, 1),
|
|
703
787
|
x: (d) => d.toLocaleDateString(),
|
|
704
788
|
X: (d) => d.toLocaleTimeString(),
|
|
705
|
-
y: (d) => d.getFullYear()
|
|
789
|
+
y: (d) => (d.getFullYear() % 100 + 100) % 100,
|
|
706
790
|
Y: (d) => d.getFullYear(),
|
|
707
791
|
z: getTimezoneOffset,
|
|
708
792
|
Z: (d, opts) => d.getTimeZoneName() || getTimezoneOffset(d, opts),
|
|
@@ -711,18 +795,18 @@ const formatCodes = {
|
|
|
711
795
|
'%': () => '%'
|
|
712
796
|
};
|
|
713
797
|
formatCodes.h = formatCodes.b;
|
|
714
|
-
function strftime(d, formatStr) {
|
|
798
|
+
function strftime(d, formatStr, memoryLimit) {
|
|
715
799
|
let output = '';
|
|
716
800
|
let remaining = formatStr;
|
|
717
801
|
let match;
|
|
718
802
|
while ((match = rFormat.exec(remaining))) {
|
|
719
803
|
output += remaining.slice(0, match.index);
|
|
720
804
|
remaining = remaining.slice(match.index + match[0].length);
|
|
721
|
-
output += format(d, match);
|
|
805
|
+
output += format(d, match, memoryLimit);
|
|
722
806
|
}
|
|
723
807
|
return output + remaining;
|
|
724
808
|
}
|
|
725
|
-
function format(d, match) {
|
|
809
|
+
function format(d, match, memoryLimit) {
|
|
726
810
|
const [input, flagStr = '', width, modifier, conversion] = match;
|
|
727
811
|
const convert = formatCodes[conversion];
|
|
728
812
|
if (!convert)
|
|
@@ -730,9 +814,9 @@ function format(d, match) {
|
|
|
730
814
|
const flags = {};
|
|
731
815
|
for (const flag of flagStr)
|
|
732
816
|
flags[flag] = true;
|
|
733
|
-
let ret = String(convert(d, { flags, width, modifier }));
|
|
817
|
+
let ret = String(convert(d, { flags, width, modifier, memoryLimit }));
|
|
734
818
|
let padChar = padSpaceChars.has(conversion) ? ' ' : '0';
|
|
735
|
-
let padWidth = width || padWidths[conversion] || 0;
|
|
819
|
+
let padWidth = Number(width) || padWidths[conversion] || 0;
|
|
736
820
|
if (flags['^'])
|
|
737
821
|
ret = ret.toUpperCase();
|
|
738
822
|
else if (flags['#'])
|
|
@@ -743,6 +827,9 @@ function format(d, match) {
|
|
|
743
827
|
padChar = '0';
|
|
744
828
|
if (flags['-'])
|
|
745
829
|
padWidth = 0;
|
|
830
|
+
memoryLimit === null || memoryLimit === void 0 ? void 0 : memoryLimit.use(Number(padWidth) - ret.length);
|
|
831
|
+
if (padChar === '0' && ret[0] === '-')
|
|
832
|
+
return '-' + padStart(ret.slice(1), padWidth - 1, padChar);
|
|
746
833
|
return padStart(ret, padWidth, padChar);
|
|
747
834
|
}
|
|
748
835
|
|
|
@@ -791,6 +878,15 @@ class LiquidDate {
|
|
|
791
878
|
getTime() {
|
|
792
879
|
return this.displayDate.getTime();
|
|
793
880
|
}
|
|
881
|
+
/**
|
|
882
|
+
* The underlying UTC timestamp in milliseconds, unaffected by the display
|
|
883
|
+
* timezone. Use this (not `getTime()`) for timezone-invariant values like
|
|
884
|
+
* `%s`: `getTime()` reads `displayDate`, which is deliberately shifted by
|
|
885
|
+
* the display timezone offset so wall-clock getters can delegate to Date.
|
|
886
|
+
*/
|
|
887
|
+
dateValue() {
|
|
888
|
+
return this.date.getTime();
|
|
889
|
+
}
|
|
794
890
|
getMilliseconds() {
|
|
795
891
|
return this.displayDate.getMilliseconds();
|
|
796
892
|
}
|
|
@@ -902,13 +998,15 @@ class Limiter {
|
|
|
902
998
|
this.limit = limit;
|
|
903
999
|
}
|
|
904
1000
|
use(count) {
|
|
905
|
-
count
|
|
906
|
-
|
|
907
|
-
|
|
1001
|
+
if (+count > 0) {
|
|
1002
|
+
assert(this.base + +count <= this.limit, this.message);
|
|
1003
|
+
this.base += +count;
|
|
1004
|
+
}
|
|
908
1005
|
}
|
|
909
1006
|
check(count) {
|
|
910
|
-
count
|
|
911
|
-
|
|
1007
|
+
if (+count > 0) {
|
|
1008
|
+
assert(+count <= this.limit, this.message);
|
|
1009
|
+
}
|
|
912
1010
|
}
|
|
913
1011
|
}
|
|
914
1012
|
|
|
@@ -939,13 +1037,11 @@ class TagToken extends DelimitedToken {
|
|
|
939
1037
|
const { trimTagLeft, trimTagRight, tagDelimiterLeft, tagDelimiterRight } = options;
|
|
940
1038
|
const [valueBegin, valueEnd] = [begin + tagDelimiterLeft.length, end - tagDelimiterRight.length];
|
|
941
1039
|
super(TokenKind.Tag, [valueBegin, valueEnd], input, begin, end, trimTagLeft, trimTagRight, file);
|
|
942
|
-
this.tokenizer = new Tokenizer(input, options.operators, file, this.contentRange);
|
|
1040
|
+
this.tokenizer = new Tokenizer(input, options.operators, file, this.contentRange, options.groupedExpressions);
|
|
943
1041
|
this.name = this.tokenizer.readTagName();
|
|
944
1042
|
this.tokenizer.assert(this.name, `illegal tag syntax, tag name expected`);
|
|
945
1043
|
this.tokenizer.skipBlank();
|
|
946
|
-
|
|
947
|
-
get args() {
|
|
948
|
-
return this.tokenizer.input.slice(this.tokenizer.p, this.contentRange[1]);
|
|
1044
|
+
this.args = this.tokenizer.input.slice(this.tokenizer.p, this.contentRange[1]);
|
|
949
1045
|
}
|
|
950
1046
|
}
|
|
951
1047
|
|
|
@@ -1040,8 +1136,7 @@ class OperatorToken extends Token {
|
|
|
1040
1136
|
this.operator = this.getText();
|
|
1041
1137
|
}
|
|
1042
1138
|
getPrecedence() {
|
|
1043
|
-
|
|
1044
|
-
return key in operatorPrecedences ? operatorPrecedences[key] : 1;
|
|
1139
|
+
return this.operator in operatorPrecedences ? operatorPrecedences[this.operator] : 1;
|
|
1045
1140
|
}
|
|
1046
1141
|
}
|
|
1047
1142
|
|
|
@@ -1155,7 +1250,7 @@ class RangeToken extends Token {
|
|
|
1155
1250
|
class LiquidTagToken extends DelimitedToken {
|
|
1156
1251
|
constructor(input, begin, end, options, file) {
|
|
1157
1252
|
super(TokenKind.Tag, [begin, end], input, begin, end, false, false, file);
|
|
1158
|
-
this.tokenizer = new Tokenizer(input, options.operators, file, this.contentRange);
|
|
1253
|
+
this.tokenizer = new Tokenizer(input, options.operators, file, this.contentRange, options.groupedExpressions);
|
|
1159
1254
|
this.name = this.tokenizer.readTagName();
|
|
1160
1255
|
this.tokenizer.assert(this.name, 'illegal liquid tag syntax');
|
|
1161
1256
|
this.tokenizer.skipBlank();
|
|
@@ -1191,42 +1286,6 @@ function getPerformance() {
|
|
|
1191
1286
|
polyfill;
|
|
1192
1287
|
}
|
|
1193
1288
|
|
|
1194
|
-
class SimpleEmitter {
|
|
1195
|
-
constructor() {
|
|
1196
|
-
this.buffer = '';
|
|
1197
|
-
}
|
|
1198
|
-
write(html) {
|
|
1199
|
-
this.buffer += stringify(html);
|
|
1200
|
-
}
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
class StreamedEmitter {
|
|
1204
|
-
constructor() {
|
|
1205
|
-
this.buffer = '';
|
|
1206
|
-
this.stream = null;
|
|
1207
|
-
throw new Error('streaming not supported in browser');
|
|
1208
|
-
}
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
class KeepingTypeEmitter {
|
|
1212
|
-
constructor() {
|
|
1213
|
-
this.buffer = '';
|
|
1214
|
-
}
|
|
1215
|
-
write(html) {
|
|
1216
|
-
html = toValue(html);
|
|
1217
|
-
// This will only preserve the type if the value is isolated.
|
|
1218
|
-
// I.E:
|
|
1219
|
-
// {{ my-port }} -> 42
|
|
1220
|
-
// {{ my-host }}:{{ my-port }} -> 'host:42'
|
|
1221
|
-
if (typeof html !== 'string' && this.buffer === '') {
|
|
1222
|
-
this.buffer = html;
|
|
1223
|
-
}
|
|
1224
|
-
else {
|
|
1225
|
-
this.buffer = stringify(this.buffer) + stringify(html);
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
1289
|
class Render {
|
|
1231
1290
|
renderTemplatesToNodeStream(templates, ctx) {
|
|
1232
1291
|
const emitter = new StreamedEmitter();
|
|
@@ -1238,6 +1297,7 @@ class Render {
|
|
|
1238
1297
|
if (!emitter) {
|
|
1239
1298
|
emitter = ctx.opts.keepOutputType ? new KeepingTypeEmitter() : new SimpleEmitter();
|
|
1240
1299
|
}
|
|
1300
|
+
ctx.renderLimit.check(getPerformance().now());
|
|
1241
1301
|
const errors = [];
|
|
1242
1302
|
for (const tpl of templates) {
|
|
1243
1303
|
ctx.renderLimit.check(getPerformance().now());
|
|
@@ -1264,6 +1324,33 @@ class Render {
|
|
|
1264
1324
|
}
|
|
1265
1325
|
}
|
|
1266
1326
|
|
|
1327
|
+
function isKeyValuePair(arr) {
|
|
1328
|
+
return isArray(arr);
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
class Filter {
|
|
1332
|
+
constructor(token, options, liquid) {
|
|
1333
|
+
this.token = token;
|
|
1334
|
+
this.name = token.name;
|
|
1335
|
+
this.handler = isFunction(options)
|
|
1336
|
+
? options
|
|
1337
|
+
: (isFunction(options === null || options === void 0 ? void 0 : options.handler) ? options.handler : identify);
|
|
1338
|
+
this.raw = !isFunction(options) && !!(options === null || options === void 0 ? void 0 : options.raw);
|
|
1339
|
+
this.args = token.args;
|
|
1340
|
+
this.liquid = liquid;
|
|
1341
|
+
}
|
|
1342
|
+
*render(value, context) {
|
|
1343
|
+
const argv = [];
|
|
1344
|
+
for (const arg of this.args) {
|
|
1345
|
+
if (isKeyValuePair(arg))
|
|
1346
|
+
argv.push([arg[0], yield evalToken(arg[1], context)]);
|
|
1347
|
+
else
|
|
1348
|
+
argv.push(yield evalToken(arg, context));
|
|
1349
|
+
}
|
|
1350
|
+
return yield this.handler.apply({ context, token: this.token, liquid: this.liquid }, [value, ...argv]);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1267
1354
|
class Expression {
|
|
1268
1355
|
constructor(tokens) {
|
|
1269
1356
|
this.postfix = [...toPostfix(tokens)];
|
|
@@ -1294,6 +1381,11 @@ class Expression {
|
|
|
1294
1381
|
return !!this.postfix.length;
|
|
1295
1382
|
}
|
|
1296
1383
|
}
|
|
1384
|
+
/**
|
|
1385
|
+
* Evaluate `token`, preserving Drops.
|
|
1386
|
+
* Operators use this so `Comparable` drops (`empty`, `nil`, `blank`) and custom drops
|
|
1387
|
+
* are compared as drops rather than as their `valueOf()` result.
|
|
1388
|
+
*/
|
|
1297
1389
|
function* evalToken(token, ctx, lenient = false) {
|
|
1298
1390
|
if (!token)
|
|
1299
1391
|
return;
|
|
@@ -1303,11 +1395,32 @@ function* evalToken(token, ctx, lenient = false) {
|
|
|
1303
1395
|
return yield evalPropertyAccessToken(token, ctx, lenient);
|
|
1304
1396
|
if (isRangeToken(token))
|
|
1305
1397
|
return yield evalRangeToken(token, ctx);
|
|
1398
|
+
if (isFilteredValueToken(token))
|
|
1399
|
+
return yield evalFilteredValueToken(token, ctx, lenient);
|
|
1400
|
+
}
|
|
1401
|
+
function* evalFilteredValueToken(token, ctx, lenient) {
|
|
1402
|
+
assert(ctx.liquid, 'FilteredValueToken evaluation requires liquid instance in context');
|
|
1403
|
+
lenient = lenient || (ctx.opts.lenientIf && token.filters.length > 0 && token.filters[0].name === 'default');
|
|
1404
|
+
let val = yield token.initial.evaluate(ctx, lenient);
|
|
1405
|
+
for (const filterToken of token.filters) {
|
|
1406
|
+
const filterImpl = ctx.liquid.filters[filterToken.name];
|
|
1407
|
+
assert(filterImpl || !ctx.liquid.options.strictFilters, () => `undefined filter: ${filterToken.name}`);
|
|
1408
|
+
const filter = new Filter(filterToken, filterImpl, ctx.liquid);
|
|
1409
|
+
val = yield filter.render(val, ctx);
|
|
1410
|
+
}
|
|
1411
|
+
return val;
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Evaluate `token` to the value filters and tags consume.
|
|
1415
|
+
* Awaits a promise returned by `Drop.valueOf()`.
|
|
1416
|
+
*/
|
|
1417
|
+
function* evalTokenValue(token, ctx, lenient = false) {
|
|
1418
|
+
return yield toValue(yield evalToken(token, ctx, lenient));
|
|
1306
1419
|
}
|
|
1307
1420
|
function* evalPropertyAccessToken(token, ctx, lenient) {
|
|
1308
1421
|
const props = [];
|
|
1309
1422
|
for (const prop of token.props) {
|
|
1310
|
-
props.push((yield
|
|
1423
|
+
props.push((yield evalTokenValue(prop, ctx, false)));
|
|
1311
1424
|
}
|
|
1312
1425
|
try {
|
|
1313
1426
|
if (token.variable) {
|
|
@@ -1328,8 +1441,8 @@ function evalQuotedToken(token) {
|
|
|
1328
1441
|
return token.content;
|
|
1329
1442
|
}
|
|
1330
1443
|
function* evalRangeToken(token, ctx) {
|
|
1331
|
-
const low = yield
|
|
1332
|
-
const high = yield
|
|
1444
|
+
const low = (yield evalTokenValue(token.lhs, ctx));
|
|
1445
|
+
const high = (yield evalTokenValue(token.rhs, ctx));
|
|
1333
1446
|
ctx.memoryLimit.use(high - low + 1);
|
|
1334
1447
|
return range(+low, +high + 1);
|
|
1335
1448
|
}
|
|
@@ -1364,67 +1477,75 @@ function isFalsy(val, ctx) {
|
|
|
1364
1477
|
}
|
|
1365
1478
|
|
|
1366
1479
|
const defaultOperators = {
|
|
1367
|
-
'==': equals,
|
|
1368
|
-
'!=': (l, r)
|
|
1369
|
-
'>': (l, r)
|
|
1480
|
+
'==': function* (l, r) { return yield equals(l, r); },
|
|
1481
|
+
'!=': function* (l, r) { return !(yield equals(l, r)); },
|
|
1482
|
+
'>': function* (l, r) {
|
|
1370
1483
|
if (isComparable(l))
|
|
1371
|
-
return l.gt(r);
|
|
1484
|
+
return yield l.gt(r);
|
|
1372
1485
|
if (isComparable(r))
|
|
1373
|
-
return r.lt(l);
|
|
1374
|
-
return toValue(l) > toValue(r);
|
|
1486
|
+
return yield r.lt(l);
|
|
1487
|
+
return (yield toValue(l)) > (yield toValue(r));
|
|
1375
1488
|
},
|
|
1376
|
-
'<': (l, r)
|
|
1489
|
+
'<': function* (l, r) {
|
|
1377
1490
|
if (isComparable(l))
|
|
1378
|
-
return l.lt(r);
|
|
1491
|
+
return yield l.lt(r);
|
|
1379
1492
|
if (isComparable(r))
|
|
1380
|
-
return r.gt(l);
|
|
1381
|
-
return toValue(l) < toValue(r);
|
|
1493
|
+
return yield r.gt(l);
|
|
1494
|
+
return (yield toValue(l)) < (yield toValue(r));
|
|
1382
1495
|
},
|
|
1383
|
-
'>=': (l, r)
|
|
1496
|
+
'>=': function* (l, r) {
|
|
1384
1497
|
if (isComparable(l))
|
|
1385
|
-
return l.geq(r);
|
|
1498
|
+
return yield l.geq(r);
|
|
1386
1499
|
if (isComparable(r))
|
|
1387
|
-
return r.leq(l);
|
|
1388
|
-
return toValue(l) >= toValue(r);
|
|
1500
|
+
return yield r.leq(l);
|
|
1501
|
+
return (yield toValue(l)) >= (yield toValue(r));
|
|
1389
1502
|
},
|
|
1390
|
-
'<=': (l, r)
|
|
1503
|
+
'<=': function* (l, r) {
|
|
1391
1504
|
if (isComparable(l))
|
|
1392
|
-
return l.leq(r);
|
|
1505
|
+
return yield l.leq(r);
|
|
1393
1506
|
if (isComparable(r))
|
|
1394
|
-
return r.geq(l);
|
|
1395
|
-
return toValue(l) <= toValue(r);
|
|
1507
|
+
return yield r.geq(l);
|
|
1508
|
+
return (yield toValue(l)) <= (yield toValue(r));
|
|
1396
1509
|
},
|
|
1397
|
-
'contains': (l, r)
|
|
1398
|
-
l = toValue(l);
|
|
1510
|
+
'contains': function* (l, r) {
|
|
1511
|
+
l = yield toValue(l);
|
|
1399
1512
|
if (isArray(l))
|
|
1400
|
-
return
|
|
1513
|
+
return yield arrayIncludes(l, r);
|
|
1401
1514
|
if (isFunction(l === null || l === void 0 ? void 0 : l.indexOf))
|
|
1402
|
-
return l.indexOf(toValue(r)) > -1;
|
|
1515
|
+
return l.indexOf(yield toValue(r)) > -1;
|
|
1403
1516
|
return false;
|
|
1404
1517
|
},
|
|
1405
|
-
'not': (v, ctx)
|
|
1406
|
-
'and': (l, r, ctx)
|
|
1407
|
-
'or': (l, r, ctx)
|
|
1518
|
+
'not': function* (v, ctx) { return isFalsy(yield toValue(v), ctx); },
|
|
1519
|
+
'and': function* (l, r, ctx) { return isTruthy(yield toValue(l), ctx) && isTruthy(yield toValue(r), ctx); },
|
|
1520
|
+
'or': function* (l, r, ctx) { return isTruthy(yield toValue(l), ctx) || isTruthy(yield toValue(r), ctx); }
|
|
1408
1521
|
};
|
|
1409
|
-
function equals(lhs, rhs) {
|
|
1522
|
+
function* equals(lhs, rhs) {
|
|
1410
1523
|
if (isComparable(lhs))
|
|
1411
|
-
return lhs.equals(rhs);
|
|
1524
|
+
return yield lhs.equals(rhs);
|
|
1412
1525
|
if (isComparable(rhs))
|
|
1413
|
-
return rhs.equals(lhs);
|
|
1414
|
-
lhs = toValue(lhs);
|
|
1415
|
-
rhs = toValue(rhs);
|
|
1526
|
+
return yield rhs.equals(lhs);
|
|
1527
|
+
lhs = yield toValue(lhs);
|
|
1528
|
+
rhs = yield toValue(rhs);
|
|
1416
1529
|
if (isArray(lhs)) {
|
|
1417
|
-
return isArray(rhs) && arrayEquals(lhs, rhs);
|
|
1530
|
+
return isArray(rhs) && (yield arrayEquals(lhs, rhs));
|
|
1418
1531
|
}
|
|
1419
1532
|
return lhs === rhs;
|
|
1420
1533
|
}
|
|
1421
|
-
function arrayEquals(lhs, rhs) {
|
|
1534
|
+
function* arrayEquals(lhs, rhs) {
|
|
1422
1535
|
if (lhs.length !== rhs.length)
|
|
1423
1536
|
return false;
|
|
1424
|
-
|
|
1537
|
+
for (let i = 0; i < lhs.length; i++) {
|
|
1538
|
+
if (!(yield equals(lhs[i], rhs[i])))
|
|
1539
|
+
return false;
|
|
1540
|
+
}
|
|
1541
|
+
return true;
|
|
1425
1542
|
}
|
|
1426
|
-
function arrayIncludes(arr, item) {
|
|
1427
|
-
|
|
1543
|
+
function* arrayIncludes(arr, item) {
|
|
1544
|
+
for (const value of arr) {
|
|
1545
|
+
if (yield equals(value, item))
|
|
1546
|
+
return true;
|
|
1547
|
+
}
|
|
1548
|
+
return false;
|
|
1428
1549
|
}
|
|
1429
1550
|
|
|
1430
1551
|
class Node {
|
|
@@ -1560,6 +1681,20 @@ var fs = /*#__PURE__*/Object.freeze({
|
|
|
1560
1681
|
sep: sep
|
|
1561
1682
|
});
|
|
1562
1683
|
|
|
1684
|
+
function chargeJsonReplacerValue(memoryLimit, val) {
|
|
1685
|
+
if (typeof val === 'string') {
|
|
1686
|
+
memoryLimit.use(val.length);
|
|
1687
|
+
}
|
|
1688
|
+
else if (val === null || typeof val === 'number' || typeof val === 'boolean') {
|
|
1689
|
+
memoryLimit.use(JSON.stringify(val).length);
|
|
1690
|
+
}
|
|
1691
|
+
else if (Array.isArray(val)) {
|
|
1692
|
+
memoryLimit.use(val.length + 1);
|
|
1693
|
+
}
|
|
1694
|
+
else if (typeof val === 'object') {
|
|
1695
|
+
memoryLimit.use(2);
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1563
1698
|
function defaultFilter(value, defaultValue, ...args) {
|
|
1564
1699
|
value = toValue(value);
|
|
1565
1700
|
if (isArray(value) || isString(value))
|
|
@@ -1569,19 +1704,29 @@ function defaultFilter(value, defaultValue, ...args) {
|
|
|
1569
1704
|
return isFalsy(value, this.context) ? defaultValue : value;
|
|
1570
1705
|
}
|
|
1571
1706
|
function json(value, space = 0) {
|
|
1572
|
-
|
|
1707
|
+
const memoryLimit = this.context.memoryLimit;
|
|
1708
|
+
return JSON.stringify(value, (_key, val) => {
|
|
1709
|
+
chargeJsonReplacerValue(memoryLimit, val);
|
|
1710
|
+
return val;
|
|
1711
|
+
}, space);
|
|
1573
1712
|
}
|
|
1574
1713
|
function inspect(value, space = 0) {
|
|
1714
|
+
const memoryLimit = this.context.memoryLimit;
|
|
1575
1715
|
const ancestors = [];
|
|
1576
1716
|
return JSON.stringify(value, function (_key, value) {
|
|
1577
|
-
if (typeof value !== 'object' || value === null)
|
|
1717
|
+
if (typeof value !== 'object' || value === null) {
|
|
1718
|
+
chargeJsonReplacerValue(memoryLimit, value);
|
|
1578
1719
|
return value;
|
|
1720
|
+
}
|
|
1579
1721
|
// `this` is the object that value is contained in, i.e., its direct parent.
|
|
1580
1722
|
while (ancestors.length > 0 && ancestors[ancestors.length - 1] !== this)
|
|
1581
1723
|
ancestors.pop();
|
|
1582
|
-
if (ancestors.includes(value))
|
|
1724
|
+
if (ancestors.includes(value)) {
|
|
1725
|
+
memoryLimit.use('[Circular]'.length);
|
|
1583
1726
|
return '[Circular]';
|
|
1727
|
+
}
|
|
1584
1728
|
ancestors.push(value);
|
|
1729
|
+
chargeJsonReplacerValue(memoryLimit, value);
|
|
1585
1730
|
return value;
|
|
1586
1731
|
}, space);
|
|
1587
1732
|
}
|
|
@@ -1636,10 +1781,33 @@ function newline_to_br(v) {
|
|
|
1636
1781
|
this.context.memoryLimit.use(str.length);
|
|
1637
1782
|
return str.replace(/\r?\n/gm, '<br />\n');
|
|
1638
1783
|
}
|
|
1784
|
+
// Raw-text blocks (HTML5) plus '<...>' as the catch-all kind; a regex
|
|
1785
|
+
// equivalent is O(n^2) in V8 on unclosed openers.
|
|
1639
1786
|
function strip_html(v) {
|
|
1640
1787
|
const str = stringify(v);
|
|
1641
1788
|
this.context.memoryLimit.use(str.length);
|
|
1642
|
-
|
|
1789
|
+
const blocks = new Map([['<script', '</script>'], ['<style', '</style>'], ['<!--', '-->'], ['<', '>']]);
|
|
1790
|
+
let out = '';
|
|
1791
|
+
let i = 0;
|
|
1792
|
+
while (i < str.length) {
|
|
1793
|
+
const lt = str.indexOf('<', i);
|
|
1794
|
+
if (lt < 0)
|
|
1795
|
+
return out + str.slice(i);
|
|
1796
|
+
out += str.slice(i, lt);
|
|
1797
|
+
for (const [opener, closer] of blocks) {
|
|
1798
|
+
if (!str.startsWith(opener, lt))
|
|
1799
|
+
continue;
|
|
1800
|
+
const e = str.indexOf(closer, lt + opener.length);
|
|
1801
|
+
if (e >= 0) {
|
|
1802
|
+
i = e + closer.length;
|
|
1803
|
+
break;
|
|
1804
|
+
}
|
|
1805
|
+
blocks.delete(opener);
|
|
1806
|
+
}
|
|
1807
|
+
if (i <= lt)
|
|
1808
|
+
return out + str.slice(lt);
|
|
1809
|
+
}
|
|
1810
|
+
return out;
|
|
1643
1811
|
}
|
|
1644
1812
|
|
|
1645
1813
|
var htmlFilters = /*#__PURE__*/Object.freeze({
|
|
@@ -1730,6 +1898,7 @@ const defaultOptions = {
|
|
|
1730
1898
|
globals: {},
|
|
1731
1899
|
keepOutputType: false,
|
|
1732
1900
|
operators: defaultOperators,
|
|
1901
|
+
groupedExpressions: false,
|
|
1733
1902
|
memoryLimit: Infinity,
|
|
1734
1903
|
parseLimit: Infinity,
|
|
1735
1904
|
renderLimit: Infinity
|
|
@@ -1826,7 +1995,7 @@ function trimRight(token, greedy) {
|
|
|
1826
1995
|
}
|
|
1827
1996
|
|
|
1828
1997
|
class Tokenizer {
|
|
1829
|
-
constructor(input, operators = defaultOptions.operators, file, range) {
|
|
1998
|
+
constructor(input, operators = defaultOptions.operators, file, range, groupedExpressions = false) {
|
|
1830
1999
|
this.input = input;
|
|
1831
2000
|
this.file = file;
|
|
1832
2001
|
this.rawBeginAt = -1;
|
|
@@ -1834,6 +2003,7 @@ class Tokenizer {
|
|
|
1834
2003
|
this.N = range ? range[1] : input.length;
|
|
1835
2004
|
this.opTrie = createTrie(operators);
|
|
1836
2005
|
this.literalTrie = createTrie(literalValues);
|
|
2006
|
+
this.groupedExpressions = groupedExpressions;
|
|
1837
2007
|
}
|
|
1838
2008
|
readExpression() {
|
|
1839
2009
|
return new Expression(this.readExpressionTokens());
|
|
@@ -1895,6 +2065,8 @@ class Tokenizer {
|
|
|
1895
2065
|
this.skipBlank();
|
|
1896
2066
|
if (this.end())
|
|
1897
2067
|
return null;
|
|
2068
|
+
if (this.peek() === ')' || this.peek() === ',')
|
|
2069
|
+
return null;
|
|
1898
2070
|
this.assert(this.read() === '|', `expected "|" before filter`);
|
|
1899
2071
|
const name = this.readIdentifier();
|
|
1900
2072
|
if (!name.size()) {
|
|
@@ -1909,10 +2081,10 @@ class Tokenizer {
|
|
|
1909
2081
|
const arg = this.readFilterArg();
|
|
1910
2082
|
arg && args.push(arg);
|
|
1911
2083
|
this.skipBlank();
|
|
1912
|
-
this.assert(this.end() || this.peek() === ',' || this.peek() === '|', () => `unexpected character ${this.snapshot()}`);
|
|
2084
|
+
this.assert(this.end() || this.peek() === ',' || this.peek() === '|' || this.peek() === ')', () => `unexpected character ${this.snapshot()}`);
|
|
1913
2085
|
} while (this.peek() === ',');
|
|
1914
2086
|
}
|
|
1915
|
-
else if (this.peek() === '|' || this.end()) ;
|
|
2087
|
+
else if (this.peek() === '|' || this.peek() === ')' || this.peek() === ',' || this.end()) ;
|
|
1916
2088
|
else {
|
|
1917
2089
|
throw this.error('expected ":" after filter name');
|
|
1918
2090
|
}
|
|
@@ -2118,7 +2290,10 @@ class Tokenizer {
|
|
|
2118
2290
|
readValue() {
|
|
2119
2291
|
this.skipBlank();
|
|
2120
2292
|
const begin = this.p;
|
|
2121
|
-
|
|
2293
|
+
let variable = this.readLiteral() || this.readQuoted() || this.readNumber();
|
|
2294
|
+
if (!variable && this.peek() === '(') {
|
|
2295
|
+
variable = this.readGroupOrRange();
|
|
2296
|
+
}
|
|
2122
2297
|
const props = this.readProperties(!variable);
|
|
2123
2298
|
if (!props.length)
|
|
2124
2299
|
return variable;
|
|
@@ -2197,17 +2372,30 @@ class Tokenizer {
|
|
|
2197
2372
|
this.p = end;
|
|
2198
2373
|
return literal;
|
|
2199
2374
|
}
|
|
2200
|
-
|
|
2375
|
+
readGroupOrRange() {
|
|
2201
2376
|
this.skipBlank();
|
|
2202
2377
|
const begin = this.p;
|
|
2203
2378
|
if (this.peek() !== '(')
|
|
2204
2379
|
return;
|
|
2205
2380
|
++this.p;
|
|
2206
2381
|
const lhs = this.readValueOrThrow();
|
|
2207
|
-
this.
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2382
|
+
this.skipBlank();
|
|
2383
|
+
if (this.peek() === '.' && this.peek(1) === '.') {
|
|
2384
|
+
this.p += 2;
|
|
2385
|
+
const rhs = this.readValueOrThrow();
|
|
2386
|
+
this.skipBlank();
|
|
2387
|
+
this.assert(this.read() === ')', 'invalid range syntax');
|
|
2388
|
+
return new RangeToken(this.input, begin, this.p, lhs, rhs, this.file);
|
|
2389
|
+
}
|
|
2390
|
+
if (this.groupedExpressions) {
|
|
2391
|
+
const initial = new Expression([lhs, ...this.readExpressionTokens()]);
|
|
2392
|
+
this.assert(initial.valid(), () => `invalid value expression: ${this.snapshot()}`);
|
|
2393
|
+
const filters = this.readFilters();
|
|
2394
|
+
this.skipBlank();
|
|
2395
|
+
this.assert(this.read() === ')', 'unbalanced parentheses');
|
|
2396
|
+
return new FilteredValueToken(initial, filters, this.input, begin, this.p, this.file);
|
|
2397
|
+
}
|
|
2398
|
+
throw this.error('invalid range syntax');
|
|
2211
2399
|
}
|
|
2212
2400
|
readValueOrThrow() {
|
|
2213
2401
|
const value = this.readValue();
|
|
@@ -2234,7 +2422,7 @@ class Tokenizer {
|
|
|
2234
2422
|
}
|
|
2235
2423
|
*readFileNameTemplate(options) {
|
|
2236
2424
|
const { outputDelimiterLeft } = options;
|
|
2237
|
-
const htmlStopStrings = [',', ' ', outputDelimiterLeft];
|
|
2425
|
+
const htmlStopStrings = [',', ' ', '\r', '\n', '\t', outputDelimiterLeft];
|
|
2238
2426
|
const htmlStopStringSet = new Set(htmlStopStrings);
|
|
2239
2427
|
// break on ',' and ' ', outputDelimiterLeft only stops HTML token
|
|
2240
2428
|
while (this.p < this.N && !htmlStopStringSet.has(this.peek())) {
|
|
@@ -2361,33 +2549,6 @@ function createTagClass(options) {
|
|
|
2361
2549
|
};
|
|
2362
2550
|
}
|
|
2363
2551
|
|
|
2364
|
-
function isKeyValuePair(arr) {
|
|
2365
|
-
return isArray(arr);
|
|
2366
|
-
}
|
|
2367
|
-
|
|
2368
|
-
class Filter {
|
|
2369
|
-
constructor(token, options, liquid) {
|
|
2370
|
-
this.token = token;
|
|
2371
|
-
this.name = token.name;
|
|
2372
|
-
this.handler = isFunction(options)
|
|
2373
|
-
? options
|
|
2374
|
-
: (isFunction(options === null || options === void 0 ? void 0 : options.handler) ? options.handler : identify);
|
|
2375
|
-
this.raw = !isFunction(options) && !!(options === null || options === void 0 ? void 0 : options.raw);
|
|
2376
|
-
this.args = token.args;
|
|
2377
|
-
this.liquid = liquid;
|
|
2378
|
-
}
|
|
2379
|
-
*render(value, context) {
|
|
2380
|
-
const argv = [];
|
|
2381
|
-
for (const arg of this.args) {
|
|
2382
|
-
if (isKeyValuePair(arg))
|
|
2383
|
-
argv.push([arg[0], yield evalToken(arg[1], context)]);
|
|
2384
|
-
else
|
|
2385
|
-
argv.push(yield evalToken(arg, context));
|
|
2386
|
-
}
|
|
2387
|
-
return yield this.handler.apply({ context, token: this.token, liquid: this.liquid }, [value, ...argv]);
|
|
2388
|
-
}
|
|
2389
|
-
}
|
|
2390
|
-
|
|
2391
2552
|
class Value {
|
|
2392
2553
|
/**
|
|
2393
2554
|
* @param str the value to be valuated, eg.: "foobar" | truncate: 3
|
|
@@ -2395,7 +2556,7 @@ class Value {
|
|
|
2395
2556
|
constructor(input, liquid) {
|
|
2396
2557
|
this.filters = [];
|
|
2397
2558
|
const token = typeof input === 'string'
|
|
2398
|
-
? new Tokenizer(input, liquid.options.operators).readFilteredValue()
|
|
2559
|
+
? new Tokenizer(input, liquid.options.operators, undefined, undefined, liquid.options.groupedExpressions).readFilteredValue()
|
|
2399
2560
|
: input;
|
|
2400
2561
|
this.initial = token.initial;
|
|
2401
2562
|
this.filters = token.filters.map(token => new Filter(token, this.getFilter(liquid, token.name), liquid));
|
|
@@ -2419,7 +2580,7 @@ class Output extends TemplateImpl {
|
|
|
2419
2580
|
constructor(token, liquid) {
|
|
2420
2581
|
var _a;
|
|
2421
2582
|
super(token);
|
|
2422
|
-
const tokenizer = new Tokenizer(token.input, liquid.options.operators, token.file, token.contentRange);
|
|
2583
|
+
const tokenizer = new Tokenizer(token.input, liquid.options.operators, token.file, token.contentRange, liquid.options.groupedExpressions);
|
|
2423
2584
|
this.value = new Value(tokenizer.readFilteredValue(), liquid);
|
|
2424
2585
|
const filters = this.value.filters;
|
|
2425
2586
|
const outputEscape = liquid.options.outputEscape;
|
|
@@ -2429,7 +2590,7 @@ class Output extends TemplateImpl {
|
|
|
2429
2590
|
}
|
|
2430
2591
|
}
|
|
2431
2592
|
*render(ctx, emitter) {
|
|
2432
|
-
const val = yield this.value.value(ctx, false);
|
|
2593
|
+
const val = yield toValue(yield this.value.value(ctx, false));
|
|
2433
2594
|
emitter.write(val);
|
|
2434
2595
|
}
|
|
2435
2596
|
*arguments() {
|
|
@@ -2705,10 +2866,31 @@ function* extractValueTokenVariables(token) {
|
|
|
2705
2866
|
yield* extractValueTokenVariables(token.lhs);
|
|
2706
2867
|
yield* extractValueTokenVariables(token.rhs);
|
|
2707
2868
|
}
|
|
2869
|
+
else if (isFilteredValueToken(token)) {
|
|
2870
|
+
yield* extractGroupedExpressionTokenVariables(token);
|
|
2871
|
+
}
|
|
2708
2872
|
else if (isPropertyAccessToken(token)) {
|
|
2709
2873
|
yield extractPropertyAccessVariable(token);
|
|
2710
2874
|
}
|
|
2711
2875
|
}
|
|
2876
|
+
function* extractGroupedExpressionTokenVariables(token) {
|
|
2877
|
+
if (!isFilteredValueToken(token))
|
|
2878
|
+
return;
|
|
2879
|
+
for (const t of token.initial.postfix) {
|
|
2880
|
+
if (isValueToken(t))
|
|
2881
|
+
yield* extractValueTokenVariables(t);
|
|
2882
|
+
}
|
|
2883
|
+
for (const filter of token.filters) {
|
|
2884
|
+
for (const arg of filter.args) {
|
|
2885
|
+
if (isKeyValuePair(arg) && arg[1]) {
|
|
2886
|
+
yield* extractValueTokenVariables(arg[1]);
|
|
2887
|
+
}
|
|
2888
|
+
else if (isValueToken(arg)) {
|
|
2889
|
+
yield* extractValueTokenVariables(arg);
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2712
2894
|
function extractPropertyAccessVariable(token) {
|
|
2713
2895
|
const segments = [];
|
|
2714
2896
|
// token is not guaranteed to have `file` set. We'll try to get it from a prop if not.
|
|
@@ -2785,44 +2967,47 @@ var LookupType;
|
|
|
2785
2967
|
})(LookupType || (LookupType = {}));
|
|
2786
2968
|
class Loader {
|
|
2787
2969
|
constructor(options) {
|
|
2970
|
+
var _a, _b, _c, _d;
|
|
2788
2971
|
this.options = options;
|
|
2789
2972
|
if (options.relativeReference) {
|
|
2790
2973
|
const sep = options.fs.sep;
|
|
2791
2974
|
assert(sep, '`fs.sep` is required for relative reference');
|
|
2792
|
-
const
|
|
2793
|
-
this.shouldLoadRelative = (referencedFile) =>
|
|
2975
|
+
const prefixes = ['.' + sep, '..' + sep, './', '../'];
|
|
2976
|
+
this.shouldLoadRelative = (referencedFile) => prefixes.some(prefix => referencedFile.startsWith(prefix));
|
|
2794
2977
|
}
|
|
2795
2978
|
else {
|
|
2796
2979
|
this.shouldLoadRelative = (_referencedFile) => false;
|
|
2797
2980
|
}
|
|
2798
|
-
|
|
2981
|
+
const fs = options.fs;
|
|
2982
|
+
this.contains = toLiquidAsync(((_a = fs.contains) === null || _a === void 0 ? void 0 : _a.bind(fs)) || (() => __awaiter(this, void 0, void 0, function* () { return true; })), ((_b = fs.containsSync) === null || _b === void 0 ? void 0 : _b.bind(fs)) || (() => true));
|
|
2983
|
+
this.exists = toLiquidAsync(((_c = fs.exists) === null || _c === void 0 ? void 0 : _c.bind(fs)) || (() => __awaiter(this, void 0, void 0, function* () { return false; })), (_d = fs.existsSync) === null || _d === void 0 ? void 0 : _d.bind(fs));
|
|
2799
2984
|
}
|
|
2800
2985
|
*lookup(file, type, sync, currentFile) {
|
|
2801
|
-
const { fs } = this.options;
|
|
2802
2986
|
const dirs = this.options[type];
|
|
2803
|
-
for (const filepath of this.candidates(file, dirs, currentFile
|
|
2804
|
-
|
|
2987
|
+
for (const filepath of this.candidates(file, dirs, currentFile)) {
|
|
2988
|
+
let allowed = false;
|
|
2989
|
+
for (const dir of dirs) {
|
|
2990
|
+
if (yield this.contains(!!sync, dir, filepath)) {
|
|
2991
|
+
allowed = true;
|
|
2992
|
+
break;
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
if (!allowed)
|
|
2996
|
+
continue;
|
|
2997
|
+
if (yield this.exists(!!sync, filepath))
|
|
2805
2998
|
return filepath;
|
|
2806
2999
|
}
|
|
2807
3000
|
throw this.lookupError(file, dirs);
|
|
2808
3001
|
}
|
|
2809
|
-
*candidates(file, dirs, currentFile
|
|
3002
|
+
*candidates(file, dirs, currentFile) {
|
|
2810
3003
|
const { fs, extname } = this.options;
|
|
2811
3004
|
if (this.shouldLoadRelative(file) && currentFile) {
|
|
2812
3005
|
const referenced = fs.resolve(this.dirname(currentFile), file, extname);
|
|
2813
|
-
|
|
2814
|
-
if (!enforceRoot || this.contains(dir, referenced)) {
|
|
2815
|
-
// the relatively referenced file is within one of root dirs
|
|
2816
|
-
yield referenced;
|
|
2817
|
-
break;
|
|
2818
|
-
}
|
|
2819
|
-
}
|
|
3006
|
+
yield referenced;
|
|
2820
3007
|
}
|
|
2821
3008
|
for (const dir of dirs) {
|
|
2822
3009
|
const referenced = fs.resolve(dir, file, extname);
|
|
2823
|
-
|
|
2824
|
-
yield referenced;
|
|
2825
|
-
}
|
|
3010
|
+
yield referenced;
|
|
2826
3011
|
}
|
|
2827
3012
|
if (fs.fallback !== undefined) {
|
|
2828
3013
|
const filepath = fs.fallback(file);
|
|
@@ -2845,17 +3030,19 @@ class Loader {
|
|
|
2845
3030
|
|
|
2846
3031
|
class Parser {
|
|
2847
3032
|
constructor(liquid) {
|
|
3033
|
+
var _a, _b;
|
|
2848
3034
|
this.liquid = liquid;
|
|
2849
3035
|
this.cache = this.liquid.options.cache;
|
|
2850
3036
|
this.fs = this.liquid.options.fs;
|
|
2851
3037
|
this.parseFile = this.cache ? this._parseFileCached : this._parseFile;
|
|
2852
3038
|
this.loader = new Loader(this.liquid.options);
|
|
2853
3039
|
this.parseLimit = new Limiter('parse length', liquid.options.parseLimit);
|
|
3040
|
+
this.readFile = toLiquidAsync(((_a = this.fs.readFile) === null || _a === void 0 ? void 0 : _a.bind(this.fs)) || (() => __awaiter(this, void 0, void 0, function* () { throw new Error('readFile not implemented'); })), (_b = this.fs.readFileSync) === null || _b === void 0 ? void 0 : _b.bind(this.fs));
|
|
2854
3041
|
}
|
|
2855
3042
|
parse(html, filepath) {
|
|
2856
3043
|
html = String(html);
|
|
2857
3044
|
this.parseLimit.use(html.length);
|
|
2858
|
-
const tokenizer = new Tokenizer(html, this.liquid.options.operators, filepath);
|
|
3045
|
+
const tokenizer = new Tokenizer(html, this.liquid.options.operators, filepath, undefined, this.liquid.options.groupedExpressions);
|
|
2859
3046
|
const tokens = tokenizer.readTopLevelTokens(this.liquid.options);
|
|
2860
3047
|
return this.parseTokens(tokens);
|
|
2861
3048
|
}
|
|
@@ -2921,7 +3108,7 @@ class Parser {
|
|
|
2921
3108
|
}
|
|
2922
3109
|
*_parseFile(file, sync, type = LookupType.Root, currentFile) {
|
|
2923
3110
|
const filepath = yield this.loader.lookup(file, type, sync, currentFile);
|
|
2924
|
-
return this.parse(
|
|
3111
|
+
return this.parse(yield this.readFile(!!sync, filepath), filepath);
|
|
2925
3112
|
}
|
|
2926
3113
|
}
|
|
2927
3114
|
|
|
@@ -2940,6 +3127,7 @@ var TokenKind;
|
|
|
2940
3127
|
TokenKind[TokenKind["Quoted"] = 1024] = "Quoted";
|
|
2941
3128
|
TokenKind[TokenKind["Operator"] = 2048] = "Operator";
|
|
2942
3129
|
TokenKind[TokenKind["FilteredValue"] = 4096] = "FilteredValue";
|
|
3130
|
+
TokenKind[TokenKind["GroupedExpression"] = 8192] = "GroupedExpression";
|
|
2943
3131
|
TokenKind[TokenKind["Delimited"] = 12] = "Delimited";
|
|
2944
3132
|
})(TokenKind || (TokenKind = {}));
|
|
2945
3133
|
|
|
@@ -2976,9 +3164,12 @@ function isWordToken(val) {
|
|
|
2976
3164
|
function isRangeToken(val) {
|
|
2977
3165
|
return getKind(val) === TokenKind.Range;
|
|
2978
3166
|
}
|
|
3167
|
+
function isFilteredValueToken(val) {
|
|
3168
|
+
return getKind(val) === TokenKind.FilteredValue;
|
|
3169
|
+
}
|
|
2979
3170
|
function isValueToken(val) {
|
|
2980
|
-
// valueTokenBitMask = TokenKind.Number | TokenKind.Literal | TokenKind.Quoted | TokenKind.PropertyAccess | TokenKind.Range
|
|
2981
|
-
return (getKind(val) &
|
|
3171
|
+
// valueTokenBitMask = TokenKind.Number | TokenKind.Literal | TokenKind.Quoted | TokenKind.PropertyAccess | TokenKind.Range | TokenKind.FilteredValue
|
|
3172
|
+
return (getKind(val) & 5763) > 0;
|
|
2982
3173
|
}
|
|
2983
3174
|
function getKind(val) {
|
|
2984
3175
|
return val ? val.kind : -1;
|
|
@@ -2997,17 +3188,25 @@ var typeGuards = /*#__PURE__*/Object.freeze({
|
|
|
2997
3188
|
isPropertyAccessToken: isPropertyAccessToken,
|
|
2998
3189
|
isWordToken: isWordToken,
|
|
2999
3190
|
isRangeToken: isRangeToken,
|
|
3191
|
+
isFilteredValueToken: isFilteredValueToken,
|
|
3000
3192
|
isValueToken: isValueToken
|
|
3001
3193
|
});
|
|
3002
3194
|
|
|
3195
|
+
function createScope(from) {
|
|
3196
|
+
const scope = Object.create(null);
|
|
3197
|
+
if (from)
|
|
3198
|
+
Object.assign(scope, from);
|
|
3199
|
+
return scope;
|
|
3200
|
+
}
|
|
3201
|
+
|
|
3003
3202
|
class Context {
|
|
3004
|
-
constructor(env = {}, opts = defaultOptions, renderOptions = {}, { memoryLimit, renderLimit } = {}) {
|
|
3203
|
+
constructor(env = {}, opts = defaultOptions, renderOptions = {}, { memoryLimit, renderLimit, liquid } = {}) {
|
|
3005
3204
|
var _a, _b, _c, _d, _e;
|
|
3006
3205
|
/**
|
|
3007
3206
|
* insert a Context-level empty scope,
|
|
3008
3207
|
* for tags like `{% capture %}` `{% assign %}` to operate
|
|
3009
3208
|
*/
|
|
3010
|
-
this.scopes = [
|
|
3209
|
+
this.scopes = [createScope()];
|
|
3011
3210
|
this.registers = {};
|
|
3012
3211
|
this.breakCalled = false;
|
|
3013
3212
|
this.continueCalled = false;
|
|
@@ -3019,9 +3218,10 @@ class Context {
|
|
|
3019
3218
|
this.ownPropertyOnly = (_c = renderOptions.ownPropertyOnly) !== null && _c !== void 0 ? _c : opts.ownPropertyOnly;
|
|
3020
3219
|
this.memoryLimit = memoryLimit !== null && memoryLimit !== void 0 ? memoryLimit : new Limiter('memory alloc', (_d = renderOptions.memoryLimit) !== null && _d !== void 0 ? _d : opts.memoryLimit);
|
|
3021
3220
|
this.renderLimit = renderLimit !== null && renderLimit !== void 0 ? renderLimit : new Limiter('template render', getPerformance().now() + ((_e = renderOptions.renderLimit) !== null && _e !== void 0 ? _e : opts.renderLimit));
|
|
3221
|
+
this.liquid = liquid;
|
|
3022
3222
|
}
|
|
3023
|
-
getRegister(key) {
|
|
3024
|
-
return (this.registers[key] = this.registers[key] ||
|
|
3223
|
+
getRegister(key, defaultValue = undefined) {
|
|
3224
|
+
return (this.registers[key] = this.registers[key] || defaultValue);
|
|
3025
3225
|
}
|
|
3026
3226
|
setRegister(key, value) {
|
|
3027
3227
|
return (this.registers[key] = value);
|
|
@@ -3059,7 +3259,7 @@ class Context {
|
|
|
3059
3259
|
if (isString(paths))
|
|
3060
3260
|
paths = paths.split('.');
|
|
3061
3261
|
for (let i = 0; i < paths.length; i++) {
|
|
3062
|
-
scope = yield readProperty(scope, paths[i]
|
|
3262
|
+
scope = yield this.readProperty(scope, paths[i]);
|
|
3063
3263
|
if (strictVariables && isUndefined(scope)) {
|
|
3064
3264
|
throw new InternalUndefinedVariableError(paths.slice(0, i + 1).join('.'));
|
|
3065
3265
|
}
|
|
@@ -3079,10 +3279,12 @@ class Context {
|
|
|
3079
3279
|
return new Context(scope, this.opts, {
|
|
3080
3280
|
sync: this.sync,
|
|
3081
3281
|
globals: this.globals,
|
|
3082
|
-
strictVariables: this.strictVariables
|
|
3282
|
+
strictVariables: this.strictVariables,
|
|
3283
|
+
ownPropertyOnly: this.ownPropertyOnly
|
|
3083
3284
|
}, {
|
|
3084
3285
|
renderLimit: this.renderLimit,
|
|
3085
|
-
memoryLimit: this.memoryLimit
|
|
3286
|
+
memoryLimit: this.memoryLimit,
|
|
3287
|
+
liquid: this.liquid
|
|
3086
3288
|
});
|
|
3087
3289
|
}
|
|
3088
3290
|
findScope(key) {
|
|
@@ -3095,41 +3297,41 @@ class Context {
|
|
|
3095
3297
|
return this.environments;
|
|
3096
3298
|
return this.globals;
|
|
3097
3299
|
}
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
return
|
|
3117
|
-
|
|
3300
|
+
readProperty(obj, key) {
|
|
3301
|
+
obj = toLiquid(obj);
|
|
3302
|
+
key = toValue(key);
|
|
3303
|
+
if (isNil(obj))
|
|
3304
|
+
return obj;
|
|
3305
|
+
if (isArray(obj) && isNumber(key))
|
|
3306
|
+
return readArrayElement(obj, key, this.ownPropertyOnly);
|
|
3307
|
+
const value = readJSProperty(obj, key, this.ownPropertyOnly);
|
|
3308
|
+
if (value === undefined && obj instanceof Drop)
|
|
3309
|
+
return obj.liquidMethodMissing(key, this);
|
|
3310
|
+
if (isFunction(value))
|
|
3311
|
+
return value.call(obj);
|
|
3312
|
+
if (key === 'size')
|
|
3313
|
+
return readSize(obj);
|
|
3314
|
+
else if (key === 'first')
|
|
3315
|
+
return readFirst(obj, this.ownPropertyOnly);
|
|
3316
|
+
else if (key === 'last')
|
|
3317
|
+
return readLast(obj, this.ownPropertyOnly);
|
|
3318
|
+
return value;
|
|
3319
|
+
}
|
|
3118
3320
|
}
|
|
3119
3321
|
function readJSProperty(obj, key, ownPropertyOnly) {
|
|
3120
3322
|
if (ownPropertyOnly && !hasOwnProperty.call(obj, key) && !(obj instanceof Drop))
|
|
3121
3323
|
return undefined;
|
|
3122
3324
|
return obj[key];
|
|
3123
3325
|
}
|
|
3124
|
-
function readFirst(obj) {
|
|
3326
|
+
function readFirst(obj, ownPropertyOnly) {
|
|
3125
3327
|
if (isArray(obj))
|
|
3126
|
-
return obj
|
|
3127
|
-
return obj
|
|
3328
|
+
return readArrayElement(obj, 0, ownPropertyOnly);
|
|
3329
|
+
return readJSProperty(obj, 'first', ownPropertyOnly);
|
|
3128
3330
|
}
|
|
3129
|
-
function readLast(obj) {
|
|
3331
|
+
function readLast(obj, ownPropertyOnly) {
|
|
3130
3332
|
if (isArray(obj))
|
|
3131
|
-
return obj
|
|
3132
|
-
return obj
|
|
3333
|
+
return readArrayElement(obj, -1, ownPropertyOnly);
|
|
3334
|
+
return readJSProperty(obj, 'last', ownPropertyOnly);
|
|
3133
3335
|
}
|
|
3134
3336
|
function readSize(obj) {
|
|
3135
3337
|
if (hasOwnProperty.call(obj, 'size') || obj['size'] !== undefined)
|
|
@@ -3148,25 +3350,28 @@ var BlockMode;
|
|
|
3148
3350
|
BlockMode[BlockMode["STORE"] = 1] = "STORE";
|
|
3149
3351
|
})(BlockMode || (BlockMode = {}));
|
|
3150
3352
|
|
|
3151
|
-
const abs =
|
|
3152
|
-
const at_least =
|
|
3153
|
-
const at_most =
|
|
3154
|
-
const ceil =
|
|
3155
|
-
const divided_by =
|
|
3156
|
-
const floor =
|
|
3157
|
-
const minus =
|
|
3158
|
-
const
|
|
3159
|
-
const
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
return Math.round(v * amp) / amp;
|
|
3353
|
+
const abs = argumentsToNumber(Math.abs);
|
|
3354
|
+
const at_least = argumentsToNumber(Math.max);
|
|
3355
|
+
const at_most = argumentsToNumber(Math.min);
|
|
3356
|
+
const ceil = argumentsToNumber(Math.ceil);
|
|
3357
|
+
const divided_by = argumentsToNumber((dividend, divisor, integerArithmetic = false) => integerArithmetic ? Math.floor(dividend / divisor) : dividend / divisor);
|
|
3358
|
+
const floor = argumentsToNumber(Math.floor);
|
|
3359
|
+
const minus = argumentsToNumber((v, arg) => v - arg);
|
|
3360
|
+
const plus = argumentsToNumber((lhs, rhs) => lhs + rhs);
|
|
3361
|
+
const modulo = argumentsToNumber((v, arg) => ((v % arg) + arg) % arg);
|
|
3362
|
+
const times = argumentsToNumber((v, arg) => v * arg);
|
|
3363
|
+
function shiftDecimal(n, exponent) {
|
|
3364
|
+
const [mantissa, power = '0'] = String(n).split('e');
|
|
3365
|
+
return Number(`${mantissa}e${Number(power) + exponent}`);
|
|
3165
3366
|
}
|
|
3166
|
-
function
|
|
3167
|
-
v =
|
|
3168
|
-
arg =
|
|
3169
|
-
|
|
3367
|
+
function round(v, arg = 0) {
|
|
3368
|
+
v = toNumber(v);
|
|
3369
|
+
arg = toNumber(arg);
|
|
3370
|
+
const scaled = shiftDecimal(Math.abs(v), arg);
|
|
3371
|
+
if (!(scaled <= Number.MAX_SAFE_INTEGER))
|
|
3372
|
+
return v;
|
|
3373
|
+
const rounded = shiftDecimal(Math.round(scaled), -arg);
|
|
3374
|
+
return v < 0 ? -rounded : rounded;
|
|
3170
3375
|
}
|
|
3171
3376
|
|
|
3172
3377
|
var mathFilters = /*#__PURE__*/Object.freeze({
|
|
@@ -3178,13 +3383,13 @@ var mathFilters = /*#__PURE__*/Object.freeze({
|
|
|
3178
3383
|
divided_by: divided_by,
|
|
3179
3384
|
floor: floor,
|
|
3180
3385
|
minus: minus,
|
|
3386
|
+
plus: plus,
|
|
3181
3387
|
modulo: modulo,
|
|
3182
3388
|
times: times,
|
|
3183
|
-
round: round
|
|
3184
|
-
plus: plus
|
|
3389
|
+
round: round
|
|
3185
3390
|
});
|
|
3186
3391
|
|
|
3187
|
-
const url_decode = (x) => decodeURIComponent(stringify(x)
|
|
3392
|
+
const url_decode = (x) => decodeURIComponent(stringify(x).replace(/\+/g, ' '));
|
|
3188
3393
|
const url_encode = (x) => encodeURIComponent(stringify(x)).replace(/%20/g, '+');
|
|
3189
3394
|
const cgi_escape = (x) => encodeURIComponent(stringify(x))
|
|
3190
3395
|
.replace(/%20/g, '+')
|
|
@@ -3242,18 +3447,24 @@ var urlFilters = /*#__PURE__*/Object.freeze({
|
|
|
3242
3447
|
const join = argumentsToValue(function (v, arg) {
|
|
3243
3448
|
const array = toArray(v);
|
|
3244
3449
|
const sep = isNil(arg) ? ' ' : stringify(arg);
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3450
|
+
let outputSize = sep.length * Math.max(array.length - 1, 0);
|
|
3451
|
+
for (let i = 0; i < array.length; i++)
|
|
3452
|
+
outputSize += String(array[i]).length;
|
|
3453
|
+
this.context.memoryLimit.use(outputSize);
|
|
3454
|
+
return Array.prototype.join.call(array, sep);
|
|
3455
|
+
});
|
|
3456
|
+
const last$1 = argumentsToValue(function (v) {
|
|
3457
|
+
return isArrayLike(v) ? readArrayElement(v, -1, this.context.ownPropertyOnly) : '';
|
|
3458
|
+
});
|
|
3459
|
+
const first = argumentsToValue(function (v) {
|
|
3460
|
+
return isArrayLike(v) ? readArrayElement(v, 0, this.context.ownPropertyOnly) : '';
|
|
3248
3461
|
});
|
|
3249
|
-
const last$1 = argumentsToValue((v) => isArrayLike(v) ? last(v) : '');
|
|
3250
|
-
const first = argumentsToValue((v) => isArrayLike(v) ? v[0] : '');
|
|
3251
3462
|
const reverse = argumentsToValue(function (v) {
|
|
3252
3463
|
const array = toArray(v);
|
|
3253
3464
|
this.context.memoryLimit.use(array.length);
|
|
3254
3465
|
return [...array].reverse();
|
|
3255
3466
|
});
|
|
3256
|
-
function*
|
|
3467
|
+
function* sortBy(arr, property, comparator) {
|
|
3257
3468
|
const values = [];
|
|
3258
3469
|
const array = toArray(arr);
|
|
3259
3470
|
this.context.memoryLimit.use(array.length);
|
|
@@ -3263,20 +3474,13 @@ function* sort(arr, property) {
|
|
|
3263
3474
|
property ? yield this.context._getFromScope(item, stringify(property).split('.'), false) : item
|
|
3264
3475
|
]);
|
|
3265
3476
|
}
|
|
3266
|
-
return values.sort((lhs, rhs) =>
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
const propertyString = stringify(property);
|
|
3274
|
-
const compare = property === undefined
|
|
3275
|
-
? caseInsensitiveCompare
|
|
3276
|
-
: (lhs, rhs) => caseInsensitiveCompare(lhs[propertyString], rhs[propertyString]);
|
|
3277
|
-
const array = toArray(input);
|
|
3278
|
-
this.context.memoryLimit.use(array.length);
|
|
3279
|
-
return [...array].sort(compare);
|
|
3477
|
+
return values.sort((lhs, rhs) => comparator(lhs[1], rhs[1])).map(tuple => tuple[0]);
|
|
3478
|
+
}
|
|
3479
|
+
function* sort(arr, property) {
|
|
3480
|
+
return yield* sortBy.call(this, arr, property, orderedCompare);
|
|
3481
|
+
}
|
|
3482
|
+
function* sort_natural(arr, property) {
|
|
3483
|
+
return yield* sortBy.call(this, arr, property, caseInsensitiveCompare);
|
|
3280
3484
|
}
|
|
3281
3485
|
const size = (v) => (v && v.length) || 0;
|
|
3282
3486
|
function* map(arr, property) {
|
|
@@ -3300,13 +3504,13 @@ function* sum(arr, property) {
|
|
|
3300
3504
|
function compact(arr) {
|
|
3301
3505
|
const array = toArray(arr);
|
|
3302
3506
|
this.context.memoryLimit.use(array.length);
|
|
3303
|
-
return
|
|
3507
|
+
return Array.prototype.filter.call(array, x => !isNil(toValue(x)));
|
|
3304
3508
|
}
|
|
3305
3509
|
function concat(v, arg = []) {
|
|
3306
3510
|
const lhs = toArray(v);
|
|
3307
3511
|
const rhs = toArray(arg);
|
|
3308
3512
|
this.context.memoryLimit.use(lhs.length + rhs.length);
|
|
3309
|
-
return
|
|
3513
|
+
return Array.prototype.concat.call(lhs, rhs);
|
|
3310
3514
|
}
|
|
3311
3515
|
function push(v, arg) {
|
|
3312
3516
|
return concat.call(this, v, [arg]);
|
|
@@ -3319,7 +3523,9 @@ function unshift(v, arg) {
|
|
|
3319
3523
|
return clone;
|
|
3320
3524
|
}
|
|
3321
3525
|
function pop(v) {
|
|
3322
|
-
const
|
|
3526
|
+
const array = toArray(v);
|
|
3527
|
+
this.context.memoryLimit.use(array.length);
|
|
3528
|
+
const clone = [...array];
|
|
3323
3529
|
clone.pop();
|
|
3324
3530
|
return clone;
|
|
3325
3531
|
}
|
|
@@ -3336,39 +3542,65 @@ function slice(v, begin, length = 1) {
|
|
|
3336
3542
|
return [];
|
|
3337
3543
|
if (!isArray(v))
|
|
3338
3544
|
v = stringify(v);
|
|
3339
|
-
begin = begin < 0 ? v.length + begin : begin;
|
|
3545
|
+
begin = begin < 0 ? (isArray(v) ? v.length : codePointLength(v)) + begin : begin;
|
|
3546
|
+
if (begin < 0 || length < 0)
|
|
3547
|
+
return isArray(v) ? [] : '';
|
|
3340
3548
|
this.context.memoryLimit.use(length);
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3549
|
+
if (isArray(v))
|
|
3550
|
+
return Array.prototype.slice.call(v, begin, begin + length);
|
|
3551
|
+
const start = advanceCodePoints(v, 0, begin);
|
|
3552
|
+
return v.slice(start, advanceCodePoints(v, start, length));
|
|
3553
|
+
}
|
|
3554
|
+
function* matches(value, expected) {
|
|
3555
|
+
if (this.context.opts.jekyllWhere) {
|
|
3556
|
+
if (EmptyDrop.is(expected))
|
|
3557
|
+
return yield equals(value, expected);
|
|
3558
|
+
if (isArray(value))
|
|
3559
|
+
return yield arrayIncludes(value, expected);
|
|
3560
|
+
return yield equals(value, expected);
|
|
3561
|
+
}
|
|
3562
|
+
if (expected === undefined)
|
|
3563
|
+
return isTruthy(value, this.context);
|
|
3564
|
+
return yield equals(value, expected);
|
|
3565
|
+
}
|
|
3566
|
+
function* filter(include, arr, property, expected) {
|
|
3345
3567
|
arr = toArray(arr);
|
|
3346
3568
|
this.context.memoryLimit.use(arr.length);
|
|
3347
3569
|
const token = new Tokenizer(stringify(property)).readScopeValue();
|
|
3570
|
+
const result = [];
|
|
3348
3571
|
for (const item of arr) {
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
return arr.filter((_, i) => {
|
|
3355
|
-
if (expected === undefined)
|
|
3356
|
-
return isTruthy(values[i], this.context);
|
|
3357
|
-
return matcher(values[i]);
|
|
3358
|
-
});
|
|
3572
|
+
const value = yield evalToken(token, this.context.spawn(item));
|
|
3573
|
+
if ((yield matches.call(this, value, expected)) === include)
|
|
3574
|
+
result.push(item);
|
|
3575
|
+
}
|
|
3576
|
+
return result;
|
|
3359
3577
|
}
|
|
3360
|
-
function*
|
|
3578
|
+
function* filter_exp(include, arr, itemName, exp) {
|
|
3361
3579
|
const filtered = [];
|
|
3362
3580
|
const keyTemplate = new Value(stringify(exp), this.liquid);
|
|
3363
3581
|
const array = toArray(arr);
|
|
3364
3582
|
this.context.memoryLimit.use(array.length);
|
|
3365
3583
|
for (const item of array) {
|
|
3366
|
-
|
|
3367
|
-
|
|
3584
|
+
this.context.push({ [itemName]: item });
|
|
3585
|
+
const value = yield keyTemplate.value(this.context);
|
|
3586
|
+
this.context.pop();
|
|
3587
|
+
if (value === include)
|
|
3368
3588
|
filtered.push(item);
|
|
3369
3589
|
}
|
|
3370
3590
|
return filtered;
|
|
3371
3591
|
}
|
|
3592
|
+
function* where(arr, property, expected) {
|
|
3593
|
+
return yield* filter.call(this, true, arr, property, expected);
|
|
3594
|
+
}
|
|
3595
|
+
function* reject(arr, property, expected) {
|
|
3596
|
+
return yield* filter.call(this, false, arr, property, expected);
|
|
3597
|
+
}
|
|
3598
|
+
function* where_exp(arr, itemName, exp) {
|
|
3599
|
+
return yield* filter_exp.call(this, true, arr, itemName, exp);
|
|
3600
|
+
}
|
|
3601
|
+
function* reject_exp(arr, itemName, exp) {
|
|
3602
|
+
return yield* filter_exp.call(this, false, arr, itemName, exp);
|
|
3603
|
+
}
|
|
3372
3604
|
function* group_by(arr, property) {
|
|
3373
3605
|
const map = new Map();
|
|
3374
3606
|
arr = toEnumerable(arr);
|
|
@@ -3388,31 +3620,59 @@ function* group_by_exp(arr, itemName, exp) {
|
|
|
3388
3620
|
arr = toEnumerable(arr);
|
|
3389
3621
|
this.context.memoryLimit.use(arr.length);
|
|
3390
3622
|
for (const item of arr) {
|
|
3391
|
-
|
|
3623
|
+
this.context.push({ [itemName]: item });
|
|
3624
|
+
const key = yield keyTemplate.value(this.context);
|
|
3625
|
+
this.context.pop();
|
|
3392
3626
|
if (!map.has(key))
|
|
3393
3627
|
map.set(key, []);
|
|
3394
3628
|
map.get(key).push(item);
|
|
3395
3629
|
}
|
|
3396
3630
|
return [...map.entries()].map(([name, items]) => ({ name, items }));
|
|
3397
3631
|
}
|
|
3398
|
-
function*
|
|
3632
|
+
function* search(arr, property, expected) {
|
|
3399
3633
|
const token = new Tokenizer(stringify(property)).readScopeValue();
|
|
3400
3634
|
const array = toArray(arr);
|
|
3401
|
-
for (
|
|
3402
|
-
const value = yield evalToken(token, this.context.spawn(
|
|
3403
|
-
if (
|
|
3404
|
-
return
|
|
3635
|
+
for (let index = 0; index < array.length; index++) {
|
|
3636
|
+
const value = yield evalToken(token, this.context.spawn(array[index]));
|
|
3637
|
+
if (yield matches.call(this, value, expected))
|
|
3638
|
+
return [index, array[index]];
|
|
3405
3639
|
}
|
|
3406
3640
|
}
|
|
3407
|
-
function*
|
|
3641
|
+
function* search_exp(arr, itemName, exp) {
|
|
3408
3642
|
const predicate = new Value(stringify(exp), this.liquid);
|
|
3409
3643
|
const array = toArray(arr);
|
|
3410
|
-
for (
|
|
3411
|
-
|
|
3644
|
+
for (let index = 0; index < array.length; index++) {
|
|
3645
|
+
this.context.push({ [itemName]: array[index] });
|
|
3646
|
+
const value = yield predicate.value(this.context);
|
|
3647
|
+
this.context.pop();
|
|
3412
3648
|
if (value)
|
|
3413
|
-
return
|
|
3649
|
+
return [index, array[index]];
|
|
3414
3650
|
}
|
|
3415
3651
|
}
|
|
3652
|
+
function* has(arr, property, expected) {
|
|
3653
|
+
const result = yield* search.call(this, arr, property, expected);
|
|
3654
|
+
return !!result;
|
|
3655
|
+
}
|
|
3656
|
+
function* has_exp(arr, itemName, exp) {
|
|
3657
|
+
const result = yield* search_exp.call(this, arr, itemName, exp);
|
|
3658
|
+
return !!result;
|
|
3659
|
+
}
|
|
3660
|
+
function* find_index(arr, property, expected) {
|
|
3661
|
+
const result = yield* search.call(this, arr, property, expected);
|
|
3662
|
+
return result ? result[0] : undefined;
|
|
3663
|
+
}
|
|
3664
|
+
function* find_index_exp(arr, itemName, exp) {
|
|
3665
|
+
const result = yield* search_exp.call(this, arr, itemName, exp);
|
|
3666
|
+
return result ? result[0] : undefined;
|
|
3667
|
+
}
|
|
3668
|
+
function* find(arr, property, expected) {
|
|
3669
|
+
const result = yield* search.call(this, arr, property, expected);
|
|
3670
|
+
return result ? result[1] : undefined;
|
|
3671
|
+
}
|
|
3672
|
+
function* find_exp(arr, itemName, exp) {
|
|
3673
|
+
const result = yield* search_exp.call(this, arr, itemName, exp);
|
|
3674
|
+
return result ? result[1] : undefined;
|
|
3675
|
+
}
|
|
3416
3676
|
function uniq(arr) {
|
|
3417
3677
|
arr = toArray(arr);
|
|
3418
3678
|
this.context.memoryLimit.use(arr.length);
|
|
@@ -3424,7 +3684,7 @@ function sample(v, count = 1) {
|
|
|
3424
3684
|
return [];
|
|
3425
3685
|
if (!isArray(v))
|
|
3426
3686
|
v = stringify(v);
|
|
3427
|
-
this.context.memoryLimit.use(
|
|
3687
|
+
this.context.memoryLimit.use(v.length);
|
|
3428
3688
|
const shuffled = [...v].sort(() => Math.random() - 0.5);
|
|
3429
3689
|
if (count === 1)
|
|
3430
3690
|
return shuffled[0];
|
|
@@ -3450,9 +3710,15 @@ var arrayFilters = /*#__PURE__*/Object.freeze({
|
|
|
3450
3710
|
shift: shift,
|
|
3451
3711
|
slice: slice,
|
|
3452
3712
|
where: where,
|
|
3713
|
+
reject: reject,
|
|
3453
3714
|
where_exp: where_exp,
|
|
3715
|
+
reject_exp: reject_exp,
|
|
3454
3716
|
group_by: group_by,
|
|
3455
3717
|
group_by_exp: group_by_exp,
|
|
3718
|
+
has: has,
|
|
3719
|
+
has_exp: has_exp,
|
|
3720
|
+
find_index: find_index,
|
|
3721
|
+
find_index_exp: find_index_exp,
|
|
3456
3722
|
find: find,
|
|
3457
3723
|
find_exp: find_exp,
|
|
3458
3724
|
uniq: uniq,
|
|
@@ -3460,21 +3726,25 @@ var arrayFilters = /*#__PURE__*/Object.freeze({
|
|
|
3460
3726
|
});
|
|
3461
3727
|
|
|
3462
3728
|
function date(v, format, timezoneOffset) {
|
|
3463
|
-
var _a, _b
|
|
3464
|
-
const size = ((_a = v === null || v === void 0 ? void 0 : v.length) !== null && _a !== void 0 ? _a : 0) + ((_b =
|
|
3729
|
+
var _a, _b;
|
|
3730
|
+
const size = ((_a = v === null || v === void 0 ? void 0 : v.length) !== null && _a !== void 0 ? _a : 0) + ((_b = timezoneOffset === null || timezoneOffset === void 0 ? void 0 : timezoneOffset.length) !== null && _b !== void 0 ? _b : 0);
|
|
3465
3731
|
this.context.memoryLimit.use(size);
|
|
3466
3732
|
const date = parseDate(v, this.context.opts, timezoneOffset);
|
|
3467
3733
|
if (!date)
|
|
3468
3734
|
return v;
|
|
3469
3735
|
format = toValue(format);
|
|
3470
3736
|
format = isNil(format) ? this.context.opts.dateFormat : stringify(format);
|
|
3471
|
-
|
|
3737
|
+
this.context.memoryLimit.use(format.length);
|
|
3738
|
+
return strftime(date, format, this.context.memoryLimit);
|
|
3472
3739
|
}
|
|
3473
3740
|
function date_to_xmlschema(v) {
|
|
3474
3741
|
return date.call(this, v, '%Y-%m-%dT%H:%M:%S%:z');
|
|
3475
3742
|
}
|
|
3476
3743
|
function date_to_rfc822(v) {
|
|
3477
|
-
|
|
3744
|
+
const date = parseDate(v, this.context.opts, undefined, 'en-US');
|
|
3745
|
+
if (!date)
|
|
3746
|
+
return v;
|
|
3747
|
+
return strftime(date, '%a, %d %b %Y %H:%M:%S %z', this.context.memoryLimit);
|
|
3478
3748
|
}
|
|
3479
3749
|
function date_to_string(v, type, style) {
|
|
3480
3750
|
return stringify_date.call(this, v, '%b', type, style);
|
|
@@ -3486,20 +3756,23 @@ function stringify_date(v, month_type, type, style) {
|
|
|
3486
3756
|
const date = parseDate(v, this.context.opts);
|
|
3487
3757
|
if (!date)
|
|
3488
3758
|
return v;
|
|
3759
|
+
const ml = this.context.memoryLimit;
|
|
3489
3760
|
if (type === 'ordinal') {
|
|
3490
3761
|
const d = date.getDate();
|
|
3491
3762
|
return style === 'US'
|
|
3492
|
-
? strftime(date, `${month_type} ${d}%q, %Y
|
|
3493
|
-
: strftime(date, `${d}%q ${month_type} %Y
|
|
3763
|
+
? strftime(date, `${month_type} ${d}%q, %Y`, ml)
|
|
3764
|
+
: strftime(date, `${d}%q ${month_type} %Y`, ml);
|
|
3494
3765
|
}
|
|
3495
|
-
return strftime(date, `%d ${month_type} %Y
|
|
3766
|
+
return strftime(date, `%d ${month_type} %Y`, ml);
|
|
3496
3767
|
}
|
|
3497
|
-
function parseDate(v, opts, timezoneOffset) {
|
|
3768
|
+
function parseDate(v, opts, timezoneOffset, locale = opts.locale) {
|
|
3498
3769
|
let date;
|
|
3499
3770
|
const defaultTimezoneOffset = timezoneOffset !== null && timezoneOffset !== void 0 ? timezoneOffset : opts.timezoneOffset;
|
|
3500
|
-
const locale = opts.locale;
|
|
3501
3771
|
v = toValue(v);
|
|
3502
|
-
if (v
|
|
3772
|
+
if (isNil(v)) {
|
|
3773
|
+
return undefined;
|
|
3774
|
+
}
|
|
3775
|
+
else if (v === 'now' || v === 'today') {
|
|
3503
3776
|
date = new LiquidDate(Date.now(), locale, defaultTimezoneOffset);
|
|
3504
3777
|
}
|
|
3505
3778
|
else if (isNumber(v)) {
|
|
@@ -3553,14 +3826,34 @@ function prepend(v, arg) {
|
|
|
3553
3826
|
this.context.memoryLimit.use(lhs.length + rhs.length);
|
|
3554
3827
|
return rhs + lhs;
|
|
3555
3828
|
}
|
|
3829
|
+
function getTrimStart(str, chars) {
|
|
3830
|
+
let start = 0;
|
|
3831
|
+
for (const char of str) {
|
|
3832
|
+
if (!chars.has(char))
|
|
3833
|
+
break;
|
|
3834
|
+
start += char.length;
|
|
3835
|
+
}
|
|
3836
|
+
return start;
|
|
3837
|
+
}
|
|
3838
|
+
function getTrimEnd(str, chars, start = 0) {
|
|
3839
|
+
let end = str.length;
|
|
3840
|
+
while (end > start) {
|
|
3841
|
+
const size = end - start >= 2 && str.codePointAt(end - 2) > 0xFFFF ? 2 : 1;
|
|
3842
|
+
if (!chars.has(str.slice(end - size, end)))
|
|
3843
|
+
break;
|
|
3844
|
+
end -= size;
|
|
3845
|
+
}
|
|
3846
|
+
return end;
|
|
3847
|
+
}
|
|
3556
3848
|
function lstrip(v, chars) {
|
|
3557
3849
|
const str = stringify(v);
|
|
3558
3850
|
this.context.memoryLimit.use(str.length);
|
|
3559
3851
|
if (chars) {
|
|
3560
|
-
chars =
|
|
3561
|
-
|
|
3852
|
+
chars = stringify(chars);
|
|
3853
|
+
this.context.memoryLimit.use(chars.length);
|
|
3854
|
+
return str.slice(getTrimStart(str, new Set(chars)));
|
|
3562
3855
|
}
|
|
3563
|
-
return str.
|
|
3856
|
+
return str.trimStart();
|
|
3564
3857
|
}
|
|
3565
3858
|
function downcase(v) {
|
|
3566
3859
|
const str = stringify(v);
|
|
@@ -3574,18 +3867,20 @@ function upcase(v) {
|
|
|
3574
3867
|
}
|
|
3575
3868
|
function remove(v, arg) {
|
|
3576
3869
|
const str = stringify(v);
|
|
3577
|
-
|
|
3578
|
-
|
|
3870
|
+
arg = stringify(arg);
|
|
3871
|
+
this.context.memoryLimit.use(str.length + arg.length);
|
|
3872
|
+
return str.split(arg).join('');
|
|
3579
3873
|
}
|
|
3580
3874
|
function remove_first(v, l) {
|
|
3581
3875
|
const str = stringify(v);
|
|
3582
|
-
|
|
3583
|
-
|
|
3876
|
+
l = stringify(l);
|
|
3877
|
+
this.context.memoryLimit.use(str.length + l.length);
|
|
3878
|
+
return str.replace(l, '');
|
|
3584
3879
|
}
|
|
3585
3880
|
function remove_last(v, l) {
|
|
3586
3881
|
const str = stringify(v);
|
|
3587
|
-
this.context.memoryLimit.use(str.length);
|
|
3588
3882
|
const pattern = stringify(l);
|
|
3883
|
+
this.context.memoryLimit.use(str.length + pattern.length);
|
|
3589
3884
|
const index = str.lastIndexOf(pattern);
|
|
3590
3885
|
if (index === -1)
|
|
3591
3886
|
return str;
|
|
@@ -3595,10 +3890,11 @@ function rstrip(str, chars) {
|
|
|
3595
3890
|
str = stringify(str);
|
|
3596
3891
|
this.context.memoryLimit.use(str.length);
|
|
3597
3892
|
if (chars) {
|
|
3598
|
-
chars =
|
|
3599
|
-
|
|
3893
|
+
chars = stringify(chars);
|
|
3894
|
+
this.context.memoryLimit.use(chars.length);
|
|
3895
|
+
return str.slice(0, getTrimEnd(str, new Set(chars)));
|
|
3600
3896
|
}
|
|
3601
|
-
return str.
|
|
3897
|
+
return str.trimEnd();
|
|
3602
3898
|
}
|
|
3603
3899
|
function split(v, arg) {
|
|
3604
3900
|
const str = stringify(v);
|
|
@@ -3614,10 +3910,10 @@ function strip(v, chars) {
|
|
|
3614
3910
|
const str = stringify(v);
|
|
3615
3911
|
this.context.memoryLimit.use(str.length);
|
|
3616
3912
|
if (chars) {
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3913
|
+
const set = new Set(stringify(chars));
|
|
3914
|
+
this.context.memoryLimit.use(set.size);
|
|
3915
|
+
const start = getTrimStart(str, set);
|
|
3916
|
+
return str.slice(start, getTrimEnd(str, set, start));
|
|
3621
3917
|
}
|
|
3622
3918
|
return str.trim();
|
|
3623
3919
|
}
|
|
@@ -3626,6 +3922,11 @@ function strip_newlines(v) {
|
|
|
3626
3922
|
this.context.memoryLimit.use(str.length);
|
|
3627
3923
|
return str.replace(/\r?\n/gm, '');
|
|
3628
3924
|
}
|
|
3925
|
+
function squish(v) {
|
|
3926
|
+
const str = stringify(v);
|
|
3927
|
+
this.context.memoryLimit.use(str.length);
|
|
3928
|
+
return str.replace(/\s+/g, ' ').trim();
|
|
3929
|
+
}
|
|
3629
3930
|
function capitalize(str) {
|
|
3630
3931
|
str = stringify(str);
|
|
3631
3932
|
this.context.memoryLimit.use(str.length);
|
|
@@ -3633,35 +3934,43 @@ function capitalize(str) {
|
|
|
3633
3934
|
}
|
|
3634
3935
|
function replace(v, pattern, replacement) {
|
|
3635
3936
|
const str = stringify(v);
|
|
3636
|
-
|
|
3637
|
-
|
|
3937
|
+
pattern = stringify(pattern);
|
|
3938
|
+
replacement = stringify(replacement);
|
|
3939
|
+
const parts = str.split(pattern);
|
|
3940
|
+
const outputSize = str.length + (parts.length - 1) * (replacement.length - pattern.length);
|
|
3941
|
+
this.context.memoryLimit.use(outputSize);
|
|
3942
|
+
return parts.join(replacement);
|
|
3638
3943
|
}
|
|
3639
3944
|
function replace_first(v, arg1, arg2) {
|
|
3640
3945
|
const str = stringify(v);
|
|
3641
|
-
|
|
3642
|
-
|
|
3946
|
+
arg1 = stringify(arg1);
|
|
3947
|
+
arg2 = stringify(arg2);
|
|
3948
|
+
this.context.memoryLimit.use(str.length + arg1.length + arg2.length);
|
|
3949
|
+
return str.replace(arg1, () => arg2);
|
|
3643
3950
|
}
|
|
3644
3951
|
function replace_last(v, arg1, arg2) {
|
|
3645
3952
|
const str = stringify(v);
|
|
3646
|
-
this.context.memoryLimit.use(str.length);
|
|
3647
3953
|
const pattern = stringify(arg1);
|
|
3954
|
+
const replacement = stringify(arg2);
|
|
3955
|
+
this.context.memoryLimit.use(str.length + pattern.length + replacement.length);
|
|
3648
3956
|
const index = str.lastIndexOf(pattern);
|
|
3649
3957
|
if (index === -1)
|
|
3650
3958
|
return str;
|
|
3651
|
-
const replacement = stringify(arg2);
|
|
3652
3959
|
return str.substring(0, index) + replacement + str.substring(index + pattern.length);
|
|
3653
3960
|
}
|
|
3654
3961
|
function truncate(v, l = 50, o = '...') {
|
|
3655
3962
|
const str = stringify(v);
|
|
3656
|
-
|
|
3657
|
-
|
|
3963
|
+
o = stringify(o);
|
|
3964
|
+
this.context.memoryLimit.use(str.length + o.length);
|
|
3965
|
+
if (str.length <= l || advanceCodePoints(str, 0, l) >= str.length)
|
|
3658
3966
|
return v;
|
|
3659
|
-
return str.
|
|
3967
|
+
return str.slice(0, advanceCodePoints(str, 0, Math.max(l - codePointLength(o), 0))) + o;
|
|
3660
3968
|
}
|
|
3661
3969
|
function truncatewords(v, words = 15, o = '...') {
|
|
3662
3970
|
const str = stringify(v);
|
|
3663
|
-
|
|
3664
|
-
|
|
3971
|
+
o = stringify(o);
|
|
3972
|
+
this.context.memoryLimit.use(str.length + o.length);
|
|
3973
|
+
const arr = str.trimStart().split(/\s+/);
|
|
3665
3974
|
if (words <= 0)
|
|
3666
3975
|
words = 1;
|
|
3667
3976
|
let ret = arr.slice(0, words).join(' ');
|
|
@@ -3694,8 +4003,13 @@ function number_of_words(input, mode) {
|
|
|
3694
4003
|
return input.split(/\s+/).length;
|
|
3695
4004
|
}
|
|
3696
4005
|
}
|
|
3697
|
-
function array_to_sentence_string(
|
|
3698
|
-
|
|
4006
|
+
function array_to_sentence_string(input, connector = 'and') {
|
|
4007
|
+
const array = toArray(input);
|
|
4008
|
+
connector = stringify(connector);
|
|
4009
|
+
let outputSize = connector.length + array.length * 2;
|
|
4010
|
+
for (let i = 0; i < array.length; i++)
|
|
4011
|
+
outputSize += stringify(array[i]).length;
|
|
4012
|
+
this.context.memoryLimit.use(outputSize);
|
|
3699
4013
|
switch (array.length) {
|
|
3700
4014
|
case 0:
|
|
3701
4015
|
return '';
|
|
@@ -3722,6 +4036,7 @@ var stringFilters = /*#__PURE__*/Object.freeze({
|
|
|
3722
4036
|
split: split,
|
|
3723
4037
|
strip: strip,
|
|
3724
4038
|
strip_newlines: strip_newlines,
|
|
4039
|
+
squish: squish,
|
|
3725
4040
|
capitalize: capitalize,
|
|
3726
4041
|
replace: replace,
|
|
3727
4042
|
replace_first: replace_first,
|
|
@@ -3733,7 +4048,99 @@ var stringFilters = /*#__PURE__*/Object.freeze({
|
|
|
3733
4048
|
array_to_sentence_string: array_to_sentence_string
|
|
3734
4049
|
});
|
|
3735
4050
|
|
|
3736
|
-
|
|
4051
|
+
function base64Encode(str) {
|
|
4052
|
+
const bytes = new TextEncoder().encode(str);
|
|
4053
|
+
let binary = '';
|
|
4054
|
+
for (let i = 0; i < bytes.length; i += 0x8000) {
|
|
4055
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
|
|
4056
|
+
}
|
|
4057
|
+
return btoa(binary);
|
|
4058
|
+
}
|
|
4059
|
+
function base64Decode(str) {
|
|
4060
|
+
return new TextDecoder().decode(Uint8Array.from(atob(str), c => c.charCodeAt(0)));
|
|
4061
|
+
}
|
|
4062
|
+
|
|
4063
|
+
/**
|
|
4064
|
+
* Base64 related filters
|
|
4065
|
+
*
|
|
4066
|
+
* Implements base64_encode and base64_decode filters for Shopify compatibility
|
|
4067
|
+
*/
|
|
4068
|
+
function base64_encode(value) {
|
|
4069
|
+
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
|
|
4070
|
+
this.context.memoryLimit.use(value.byteLength);
|
|
4071
|
+
return value.toString('base64');
|
|
4072
|
+
}
|
|
4073
|
+
const str = stringify(value);
|
|
4074
|
+
this.context.memoryLimit.use(str.length);
|
|
4075
|
+
return base64Encode(str);
|
|
4076
|
+
}
|
|
4077
|
+
function base64_decode(value) {
|
|
4078
|
+
const str = stringify(value);
|
|
4079
|
+
this.context.memoryLimit.use(str.length);
|
|
4080
|
+
assert(isBase64(str), 'invalid base64 provided to base64_decode');
|
|
4081
|
+
return base64Decode(str);
|
|
4082
|
+
}
|
|
4083
|
+
function isBase64(str) {
|
|
4084
|
+
str = str.replace(/[\t\n\f\r ]/g, '');
|
|
4085
|
+
if (str.length % 4 === 0)
|
|
4086
|
+
str = str.replace(/==?$/, '');
|
|
4087
|
+
return str.length % 4 !== 1 && /^[A-Za-z0-9+/]*$/.test(str);
|
|
4088
|
+
}
|
|
4089
|
+
|
|
4090
|
+
var base64Filters = /*#__PURE__*/Object.freeze({
|
|
4091
|
+
__proto__: null,
|
|
4092
|
+
base64_encode: base64_encode,
|
|
4093
|
+
base64_decode: base64_decode
|
|
4094
|
+
});
|
|
4095
|
+
|
|
4096
|
+
function bufferToHex(buffer) {
|
|
4097
|
+
const bytes = new Uint8Array(buffer);
|
|
4098
|
+
let hex = '';
|
|
4099
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
4100
|
+
hex += bytes[i].toString(16).padStart(2, '0');
|
|
4101
|
+
}
|
|
4102
|
+
return hex;
|
|
4103
|
+
}
|
|
4104
|
+
function sha256(str) {
|
|
4105
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4106
|
+
const data = new TextEncoder().encode(str);
|
|
4107
|
+
const digest = yield crypto.subtle.digest('SHA-256', data);
|
|
4108
|
+
return bufferToHex(digest);
|
|
4109
|
+
});
|
|
4110
|
+
}
|
|
4111
|
+
function hmacSha256(str, key) {
|
|
4112
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4113
|
+
const encoder = new TextEncoder();
|
|
4114
|
+
const cryptoKey = yield crypto.subtle.importKey('raw', encoder.encode(key), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
4115
|
+
const signature = yield crypto.subtle.sign('HMAC', cryptoKey, encoder.encode(str));
|
|
4116
|
+
return bufferToHex(signature);
|
|
4117
|
+
});
|
|
4118
|
+
}
|
|
4119
|
+
|
|
4120
|
+
/**
|
|
4121
|
+
* Crypto related filters
|
|
4122
|
+
*
|
|
4123
|
+
* Implements sha256 and hmac_sha256 filters for Shopify compatibility
|
|
4124
|
+
*/
|
|
4125
|
+
function sha256$1(value) {
|
|
4126
|
+
const str = stringify(value);
|
|
4127
|
+
this.context.memoryLimit.use(str.length);
|
|
4128
|
+
return sha256(str);
|
|
4129
|
+
}
|
|
4130
|
+
function hmac_sha256(value, key) {
|
|
4131
|
+
const str = stringify(value);
|
|
4132
|
+
const keyStr = stringify(key);
|
|
4133
|
+
this.context.memoryLimit.use(str.length + keyStr.length);
|
|
4134
|
+
return hmacSha256(str, keyStr);
|
|
4135
|
+
}
|
|
4136
|
+
|
|
4137
|
+
var cryptoFilters = /*#__PURE__*/Object.freeze({
|
|
4138
|
+
__proto__: null,
|
|
4139
|
+
sha256: sha256$1,
|
|
4140
|
+
hmac_sha256: hmac_sha256
|
|
4141
|
+
});
|
|
4142
|
+
|
|
4143
|
+
const filters = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, htmlFilters), mathFilters), urlFilters), arrayFilters), dateFilters), stringFilters), base64Filters), cryptoFilters), misc);
|
|
3737
4144
|
|
|
3738
4145
|
class AssignTag extends Tag {
|
|
3739
4146
|
constructor(token, remainTokens, liquid) {
|
|
@@ -3789,8 +4196,8 @@ class ForTag extends Tag {
|
|
|
3789
4196
|
return;
|
|
3790
4197
|
}
|
|
3791
4198
|
const continueKey = 'continue-' + this.variable + '-' + this.collection.getText();
|
|
3792
|
-
ctx.push({ continue: ctx.getRegister(continueKey) });
|
|
3793
|
-
const hash = yield this.hash.render(ctx);
|
|
4199
|
+
ctx.push(createScope({ continue: ctx.getRegister(continueKey, {}) }));
|
|
4200
|
+
const hash = (yield this.hash.render(ctx));
|
|
3794
4201
|
ctx.pop();
|
|
3795
4202
|
const modifiers = this.liquid.options.orderedFilterParameters
|
|
3796
4203
|
? Object.keys(hash).filter(x => MODIFIERS.includes(x))
|
|
@@ -3803,7 +4210,7 @@ class ForTag extends Tag {
|
|
|
3803
4210
|
return reversed(collection);
|
|
3804
4211
|
}, collection);
|
|
3805
4212
|
ctx.setRegister(continueKey, (hash['offset'] || 0) + collection.length);
|
|
3806
|
-
const scope = { forloop: new ForloopDrop(collection.length, this.collection.getText(), this.variable) };
|
|
4213
|
+
const scope = createScope({ forloop: new ForloopDrop(collection.length, this.collection.getText(), this.variable) });
|
|
3807
4214
|
ctx.push(scope);
|
|
3808
4215
|
for (const item of collection) {
|
|
3809
4216
|
scope[this.variable] = item;
|
|
@@ -3929,12 +4336,12 @@ class CaseTag extends Tag {
|
|
|
3929
4336
|
}
|
|
3930
4337
|
*render(ctx, emitter) {
|
|
3931
4338
|
const r = this.liquid.renderer;
|
|
3932
|
-
const target = toValue(yield this.value.value(ctx, ctx.opts.lenientIf));
|
|
4339
|
+
const target = yield toValue(yield this.value.value(ctx, ctx.opts.lenientIf));
|
|
3933
4340
|
let branchHit = false;
|
|
3934
4341
|
for (const branch of this.branches) {
|
|
3935
4342
|
for (const valueToken of branch.values) {
|
|
3936
4343
|
const value = yield evalToken(valueToken, ctx, ctx.opts.lenientIf);
|
|
3937
|
-
if (equals(target, value)) {
|
|
4344
|
+
if (yield equals(target, value)) {
|
|
3938
4345
|
yield r.renderTemplates(branch.templates, ctx, emitter);
|
|
3939
4346
|
branchHit = true;
|
|
3940
4347
|
break;
|
|
@@ -3995,7 +4402,11 @@ class RenderTag extends Tag {
|
|
|
3995
4402
|
alias = tokenizer.readIdentifier();
|
|
3996
4403
|
else
|
|
3997
4404
|
tokenizer.p = beforeAs;
|
|
3998
|
-
|
|
4405
|
+
const binding = { value, alias: alias && alias.content };
|
|
4406
|
+
if (keyword.content === 'with')
|
|
4407
|
+
this.with = binding;
|
|
4408
|
+
else
|
|
4409
|
+
this.forBinding = binding;
|
|
3999
4410
|
tokenizer.skipBlank();
|
|
4000
4411
|
if (tokenizer.peek() === ',')
|
|
4001
4412
|
tokenizer.advance();
|
|
@@ -4013,42 +4424,42 @@ class RenderTag extends Tag {
|
|
|
4013
4424
|
}
|
|
4014
4425
|
*render(ctx, emitter) {
|
|
4015
4426
|
const { liquid, hash } = this;
|
|
4016
|
-
const filepath = (yield renderFilePath(this
|
|
4427
|
+
const filepath = (yield renderFilePath(this.file, ctx, liquid));
|
|
4017
4428
|
assert(filepath, () => `illegal file path "${filepath}"`);
|
|
4018
4429
|
const childCtx = ctx.spawn();
|
|
4019
4430
|
const scope = childCtx.bottom();
|
|
4020
4431
|
__assign(scope, yield hash.render(ctx));
|
|
4021
|
-
if (this
|
|
4022
|
-
const { value, alias } = this
|
|
4432
|
+
if (this.with) {
|
|
4433
|
+
const { value, alias } = this.with;
|
|
4023
4434
|
scope[alias || filepath] = yield evalToken(value, ctx);
|
|
4024
4435
|
}
|
|
4025
|
-
if (this
|
|
4026
|
-
const { value, alias } = this
|
|
4436
|
+
if (this.forBinding) {
|
|
4437
|
+
const { value, alias } = this.forBinding;
|
|
4027
4438
|
const collection = toEnumerable(yield evalToken(value, ctx));
|
|
4028
4439
|
scope['forloop'] = new ForloopDrop(collection.length, value.getText(), alias);
|
|
4029
4440
|
for (const item of collection) {
|
|
4030
4441
|
scope[alias] = item;
|
|
4031
|
-
const templates = (yield liquid._parsePartialFile(filepath, childCtx.sync, this
|
|
4442
|
+
const templates = (yield liquid._parsePartialFile(filepath, childCtx.sync, this.currentFile));
|
|
4032
4443
|
yield liquid.renderer.renderTemplates(templates, childCtx, emitter);
|
|
4033
4444
|
scope['forloop'].next();
|
|
4034
4445
|
}
|
|
4035
4446
|
}
|
|
4036
4447
|
else {
|
|
4037
|
-
const templates = (yield liquid._parsePartialFile(filepath, childCtx.sync, this
|
|
4448
|
+
const templates = (yield liquid._parsePartialFile(filepath, childCtx.sync, this.currentFile));
|
|
4038
4449
|
yield liquid.renderer.renderTemplates(templates, childCtx, emitter);
|
|
4039
4450
|
}
|
|
4040
4451
|
}
|
|
4041
4452
|
*children(partials, sync) {
|
|
4042
|
-
if (partials && isString(this
|
|
4043
|
-
return (yield this.liquid._parsePartialFile(this
|
|
4453
|
+
if (partials && isString(this.file)) {
|
|
4454
|
+
return (yield this.liquid._parsePartialFile(this.file, sync, this.currentFile));
|
|
4044
4455
|
}
|
|
4045
4456
|
return [];
|
|
4046
4457
|
}
|
|
4047
4458
|
partialScope() {
|
|
4048
|
-
if (isString(this
|
|
4459
|
+
if (isString(this.file)) {
|
|
4049
4460
|
const names = Object.keys(this.hash.hash);
|
|
4050
|
-
if (this
|
|
4051
|
-
const { value, alias } = this
|
|
4461
|
+
if (this.with) {
|
|
4462
|
+
const { value, alias } = this.with;
|
|
4052
4463
|
if (isString(alias)) {
|
|
4053
4464
|
names.push([alias, value]);
|
|
4054
4465
|
}
|
|
@@ -4056,8 +4467,8 @@ class RenderTag extends Tag {
|
|
|
4056
4467
|
names.push([this.file, value]);
|
|
4057
4468
|
}
|
|
4058
4469
|
}
|
|
4059
|
-
if (this
|
|
4060
|
-
const { value, alias } = this
|
|
4470
|
+
if (this.forBinding) {
|
|
4471
|
+
const { value, alias } = this.forBinding;
|
|
4061
4472
|
if (isString(alias)) {
|
|
4062
4473
|
names.push([alias, value]);
|
|
4063
4474
|
}
|
|
@@ -4065,7 +4476,7 @@ class RenderTag extends Tag {
|
|
|
4065
4476
|
names.push([this.file, value]);
|
|
4066
4477
|
}
|
|
4067
4478
|
}
|
|
4068
|
-
return { name: this
|
|
4479
|
+
return { name: this.file, isolated: true, scope: names };
|
|
4069
4480
|
}
|
|
4070
4481
|
}
|
|
4071
4482
|
*arguments() {
|
|
@@ -4074,14 +4485,14 @@ class RenderTag extends Tag {
|
|
|
4074
4485
|
yield v;
|
|
4075
4486
|
}
|
|
4076
4487
|
}
|
|
4077
|
-
if (this
|
|
4078
|
-
const { value } = this
|
|
4488
|
+
if (this.with) {
|
|
4489
|
+
const { value } = this.with;
|
|
4079
4490
|
if (isValueToken(value)) {
|
|
4080
4491
|
yield value;
|
|
4081
4492
|
}
|
|
4082
4493
|
}
|
|
4083
|
-
if (this
|
|
4084
|
-
const { value } = this
|
|
4494
|
+
if (this.forBinding) {
|
|
4495
|
+
const { value } = this.forBinding;
|
|
4085
4496
|
if (isValueToken(value)) {
|
|
4086
4497
|
yield value;
|
|
4087
4498
|
}
|
|
@@ -4129,8 +4540,8 @@ class IncludeTag extends Tag {
|
|
|
4129
4540
|
constructor(token, remainTokens, liquid, parser) {
|
|
4130
4541
|
super(token, remainTokens, liquid);
|
|
4131
4542
|
const { tokenizer } = token;
|
|
4132
|
-
this
|
|
4133
|
-
this
|
|
4543
|
+
this.file = parseFilePath(tokenizer, this.liquid, parser);
|
|
4544
|
+
this.currentFile = token.file;
|
|
4134
4545
|
const begin = tokenizer.p;
|
|
4135
4546
|
const withStr = tokenizer.readIdentifier();
|
|
4136
4547
|
if (withStr.content === 'with') {
|
|
@@ -4148,28 +4559,28 @@ class IncludeTag extends Tag {
|
|
|
4148
4559
|
*render(ctx, emitter) {
|
|
4149
4560
|
const { liquid, hash, withVar } = this;
|
|
4150
4561
|
const { renderer } = liquid;
|
|
4151
|
-
const filepath = (yield renderFilePath(this
|
|
4562
|
+
const filepath = (yield renderFilePath(this.file, ctx, liquid));
|
|
4152
4563
|
assert(filepath, () => `illegal file path "${filepath}"`);
|
|
4153
4564
|
const saved = ctx.saveRegister('blocks', 'blockMode');
|
|
4154
4565
|
ctx.setRegister('blocks', {});
|
|
4155
4566
|
ctx.setRegister('blockMode', BlockMode.OUTPUT);
|
|
4156
|
-
const scope = (yield hash.render(ctx));
|
|
4567
|
+
const scope = createScope((yield hash.render(ctx)));
|
|
4157
4568
|
if (withVar)
|
|
4158
4569
|
scope[filepath] = yield evalToken(withVar, ctx);
|
|
4159
|
-
const templates = (yield liquid._parsePartialFile(filepath, ctx.sync, this
|
|
4160
|
-
ctx.push(ctx.opts.jekyllInclude ? { include: scope } : scope);
|
|
4570
|
+
const templates = (yield liquid._parsePartialFile(filepath, ctx.sync, this.currentFile));
|
|
4571
|
+
ctx.push(ctx.opts.jekyllInclude ? createScope({ include: scope }) : scope);
|
|
4161
4572
|
yield renderer.renderTemplates(templates, ctx, emitter);
|
|
4162
4573
|
ctx.pop();
|
|
4163
4574
|
ctx.restoreRegister(saved);
|
|
4164
4575
|
}
|
|
4165
4576
|
*children(partials, sync) {
|
|
4166
|
-
if (partials && isString(this
|
|
4167
|
-
return (yield this.liquid._parsePartialFile(this
|
|
4577
|
+
if (partials && isString(this.file)) {
|
|
4578
|
+
return (yield this.liquid._parsePartialFile(this.file, sync, this.currentFile));
|
|
4168
4579
|
}
|
|
4169
4580
|
return [];
|
|
4170
4581
|
}
|
|
4171
4582
|
partialScope() {
|
|
4172
|
-
if (isString(this
|
|
4583
|
+
if (isString(this.file)) {
|
|
4173
4584
|
let names;
|
|
4174
4585
|
if (this.liquid.options.jekyllInclude) {
|
|
4175
4586
|
names = ['include'];
|
|
@@ -4177,16 +4588,16 @@ class IncludeTag extends Tag {
|
|
|
4177
4588
|
else {
|
|
4178
4589
|
names = Object.keys(this.hash.hash);
|
|
4179
4590
|
if (this.withVar) {
|
|
4180
|
-
names.push([this
|
|
4591
|
+
names.push([this.file, this.withVar]);
|
|
4181
4592
|
}
|
|
4182
4593
|
}
|
|
4183
|
-
return { name: this
|
|
4594
|
+
return { name: this.file, isolated: false, scope: names };
|
|
4184
4595
|
}
|
|
4185
4596
|
}
|
|
4186
4597
|
*arguments() {
|
|
4187
4598
|
yield* Object.values(this.hash.hash).filter(isValueToken);
|
|
4188
|
-
if (isValueToken(this
|
|
4189
|
-
yield this
|
|
4599
|
+
if (isValueToken(this.file)) {
|
|
4600
|
+
yield this.file;
|
|
4190
4601
|
}
|
|
4191
4602
|
if (isValueToken(this.withVar)) {
|
|
4192
4603
|
yield this.withVar;
|
|
@@ -4216,6 +4627,7 @@ class CycleTag extends Tag {
|
|
|
4216
4627
|
constructor(token, remainTokens, liquid) {
|
|
4217
4628
|
super(token, remainTokens, liquid);
|
|
4218
4629
|
this.candidates = [];
|
|
4630
|
+
const begin = this.tokenizer.p;
|
|
4219
4631
|
const group = this.tokenizer.readValue();
|
|
4220
4632
|
this.tokenizer.skipBlank();
|
|
4221
4633
|
if (group) {
|
|
@@ -4224,28 +4636,33 @@ class CycleTag extends Tag {
|
|
|
4224
4636
|
this.tokenizer.advance();
|
|
4225
4637
|
}
|
|
4226
4638
|
else
|
|
4227
|
-
this.
|
|
4639
|
+
this.tokenizer.p = begin;
|
|
4228
4640
|
}
|
|
4229
4641
|
while (!this.tokenizer.end()) {
|
|
4230
|
-
const value = this.tokenizer.
|
|
4231
|
-
|
|
4232
|
-
this.candidates.push(value);
|
|
4642
|
+
const value = this.tokenizer.readFilteredValue();
|
|
4643
|
+
this.candidates.push(value);
|
|
4233
4644
|
this.tokenizer.readTo(',');
|
|
4234
4645
|
}
|
|
4235
4646
|
this.tokenizer.assert(this.candidates.length, () => `empty candidates: "${token.getText()}"`);
|
|
4236
4647
|
}
|
|
4237
4648
|
*render(ctx, emitter) {
|
|
4649
|
+
var _a;
|
|
4238
4650
|
const group = (yield evalToken(this.group, ctx));
|
|
4239
|
-
const fingerprint = `cycle:${group}:` + this.candidates.join(',');
|
|
4240
|
-
const groups = ctx.getRegister('cycle');
|
|
4651
|
+
const fingerprint = `cycle:${group}:` + this.candidates.map(c => c.getText()).join(',');
|
|
4652
|
+
const groups = ctx.getRegister('cycle', {});
|
|
4241
4653
|
let idx = groups[fingerprint];
|
|
4242
4654
|
if (idx === undefined) {
|
|
4243
4655
|
idx = groups[fingerprint] = 0;
|
|
4244
4656
|
}
|
|
4245
|
-
const candidate = this.candidates[idx];
|
|
4657
|
+
const candidate = new Value(this.candidates[idx], this.liquid);
|
|
4246
4658
|
idx = (idx + 1) % this.candidates.length;
|
|
4247
4659
|
groups[fingerprint] = idx;
|
|
4248
|
-
|
|
4660
|
+
let val = yield toValue(yield candidate.value(ctx, false));
|
|
4661
|
+
const outputEscape = this.liquid.options.outputEscape;
|
|
4662
|
+
if (outputEscape && !((_a = candidate.filters[candidate.filters.length - 1]) === null || _a === void 0 ? void 0 : _a.raw)) {
|
|
4663
|
+
val = yield outputEscape.call({ context: ctx, token: this.token, liquid: this.liquid }, val);
|
|
4664
|
+
}
|
|
4665
|
+
return val;
|
|
4249
4666
|
}
|
|
4250
4667
|
*arguments() {
|
|
4251
4668
|
yield* this.candidates;
|
|
@@ -4285,7 +4702,7 @@ class IfTag extends Tag {
|
|
|
4285
4702
|
*render(ctx, emitter) {
|
|
4286
4703
|
const r = this.liquid.renderer;
|
|
4287
4704
|
for (const { value, templates } of this.branches) {
|
|
4288
|
-
const v = yield value.value(ctx, ctx.opts.lenientIf);
|
|
4705
|
+
const v = yield toValue(yield value.value(ctx, ctx.opts.lenientIf));
|
|
4289
4706
|
if (isTruthy(v, ctx)) {
|
|
4290
4707
|
yield r.renderTemplates(templates, ctx, emitter);
|
|
4291
4708
|
return;
|
|
@@ -4329,7 +4746,7 @@ class LayoutTag extends Tag {
|
|
|
4329
4746
|
constructor(token, remainTokens, liquid, parser) {
|
|
4330
4747
|
super(token, remainTokens, liquid);
|
|
4331
4748
|
this.file = parseFilePath(this.tokenizer, this.liquid, parser);
|
|
4332
|
-
this
|
|
4749
|
+
this.currentFile = token.file;
|
|
4333
4750
|
this.args = new Hash(this.tokenizer, liquid.options.keyValueSeparator);
|
|
4334
4751
|
this.templates = parser.parseTokens(remainTokens);
|
|
4335
4752
|
}
|
|
@@ -4343,24 +4760,24 @@ class LayoutTag extends Tag {
|
|
|
4343
4760
|
}
|
|
4344
4761
|
const filepath = (yield renderFilePath(this.file, ctx, liquid));
|
|
4345
4762
|
assert(filepath, () => `illegal file path "${filepath}"`);
|
|
4346
|
-
const templates = (yield liquid._parseLayoutFile(filepath, ctx.sync, this
|
|
4763
|
+
const templates = (yield liquid._parseLayoutFile(filepath, ctx.sync, this.currentFile));
|
|
4347
4764
|
// render remaining contents and store rendered results
|
|
4348
4765
|
ctx.setRegister('blockMode', BlockMode.STORE);
|
|
4349
4766
|
const html = yield renderer.renderTemplates(this.templates, ctx);
|
|
4350
|
-
const blocks = ctx.getRegister('blocks');
|
|
4767
|
+
const blocks = ctx.getRegister('blocks', {});
|
|
4351
4768
|
// set whole content to anonymous block if anonymous doesn't specified
|
|
4352
4769
|
if (blocks[''] === undefined)
|
|
4353
4770
|
blocks[''] = (parent, emitter) => emitter.write(html);
|
|
4354
4771
|
ctx.setRegister('blockMode', BlockMode.OUTPUT);
|
|
4355
4772
|
// render the layout file use stored blocks
|
|
4356
|
-
ctx.push((yield args.render(ctx)));
|
|
4773
|
+
ctx.push(createScope((yield args.render(ctx))));
|
|
4357
4774
|
yield renderer.renderTemplates(templates, ctx, emitter);
|
|
4358
4775
|
ctx.pop();
|
|
4359
4776
|
}
|
|
4360
4777
|
*children(partials) {
|
|
4361
4778
|
const templates = this.templates.slice();
|
|
4362
4779
|
if (partials && isString(this.file)) {
|
|
4363
|
-
templates.push(...(yield this.liquid._parsePartialFile(this.file, true, this
|
|
4780
|
+
templates.push(...(yield this.liquid._parsePartialFile(this.file, true, this.currentFile)));
|
|
4364
4781
|
}
|
|
4365
4782
|
return templates;
|
|
4366
4783
|
}
|
|
@@ -4399,23 +4816,28 @@ class BlockTag extends Tag {
|
|
|
4399
4816
|
*render(ctx, emitter) {
|
|
4400
4817
|
const blockRender = this.getBlockRender(ctx);
|
|
4401
4818
|
if (ctx.getRegister('blockMode') === BlockMode.STORE) {
|
|
4402
|
-
ctx.getRegister('blocks')[this.block] = blockRender;
|
|
4819
|
+
ctx.getRegister('blocks', {})[this.block] = blockRender;
|
|
4403
4820
|
}
|
|
4404
4821
|
else {
|
|
4405
4822
|
yield blockRender(new BlockDrop(), emitter);
|
|
4406
4823
|
}
|
|
4407
4824
|
}
|
|
4408
4825
|
getBlockRender(ctx) {
|
|
4826
|
+
const self = this;
|
|
4409
4827
|
const { liquid, templates } = this;
|
|
4410
|
-
const renderChild = ctx.getRegister('blocks')[this.block];
|
|
4828
|
+
const renderChild = ctx.getRegister('blocks', {})[this.block];
|
|
4411
4829
|
const renderCurrent = function* (superBlock, emitter) {
|
|
4412
|
-
|
|
4413
|
-
|
|
4830
|
+
const stack = ctx.getRegister('blockStack', []);
|
|
4831
|
+
if (stack.includes(self))
|
|
4832
|
+
throw new Error('block tag cannot be nested');
|
|
4833
|
+
stack.push(self);
|
|
4834
|
+
ctx.push(createScope({ block: superBlock }));
|
|
4414
4835
|
yield liquid.renderer.renderTemplates(templates, ctx, emitter);
|
|
4415
4836
|
ctx.pop();
|
|
4837
|
+
stack.pop();
|
|
4416
4838
|
};
|
|
4417
4839
|
return renderChild
|
|
4418
|
-
? (superBlock, emitter) => renderChild(new BlockDrop(() => renderCurrent(superBlock, emitter)), emitter)
|
|
4840
|
+
? (superBlock, emitter) => renderChild(new BlockDrop((emitter) => renderCurrent(superBlock, emitter)), emitter)
|
|
4419
4841
|
: renderCurrent;
|
|
4420
4842
|
}
|
|
4421
4843
|
*children() {
|
|
@@ -4499,7 +4921,7 @@ class TablerowTag extends Tag {
|
|
|
4499
4921
|
const cols = args.cols || collection.length;
|
|
4500
4922
|
const r = this.liquid.renderer;
|
|
4501
4923
|
const tablerowloop = new TablerowloopDrop(collection.length, cols, this.collection.getText(), this.variable);
|
|
4502
|
-
const scope = { tablerowloop };
|
|
4924
|
+
const scope = createScope({ tablerowloop });
|
|
4503
4925
|
ctx.push(scope);
|
|
4504
4926
|
for (let idx = 0; idx < collection.length; idx++, tablerowloop.next()) {
|
|
4505
4927
|
scope[this.variable] = collection[idx];
|
|
@@ -4572,7 +4994,7 @@ class UnlessTag extends Tag {
|
|
|
4572
4994
|
*render(ctx, emitter) {
|
|
4573
4995
|
const r = this.liquid.renderer;
|
|
4574
4996
|
for (const { value, test, templates } of this.branches) {
|
|
4575
|
-
const v = yield value.value(ctx, ctx.opts.lenientIf);
|
|
4997
|
+
const v = yield toValue(yield value.value(ctx, ctx.opts.lenientIf));
|
|
4576
4998
|
if (test(v, ctx)) {
|
|
4577
4999
|
yield r.renderTemplates(templates, ctx, emitter);
|
|
4578
5000
|
return;
|
|
@@ -4613,9 +5035,14 @@ class EchoTag extends Tag {
|
|
|
4613
5035
|
}
|
|
4614
5036
|
}
|
|
4615
5037
|
*render(ctx, emitter) {
|
|
5038
|
+
var _a;
|
|
4616
5039
|
if (!this.value)
|
|
4617
5040
|
return;
|
|
4618
|
-
|
|
5041
|
+
let val = yield toValue(yield this.value.value(ctx, false));
|
|
5042
|
+
const outputEscape = this.liquid.options.outputEscape;
|
|
5043
|
+
if (outputEscape && !((_a = this.value.filters[this.value.filters.length - 1]) === null || _a === void 0 ? void 0 : _a.raw)) {
|
|
5044
|
+
val = yield outputEscape.call({ context: ctx, token: this.token, liquid: this.liquid }, val);
|
|
5045
|
+
}
|
|
4619
5046
|
emitter.write(val);
|
|
4620
5047
|
}
|
|
4621
5048
|
*arguments() {
|
|
@@ -4676,8 +5103,8 @@ const tags = {
|
|
|
4676
5103
|
class Liquid {
|
|
4677
5104
|
constructor(opts = {}) {
|
|
4678
5105
|
this.renderer = new Render();
|
|
4679
|
-
this.filters =
|
|
4680
|
-
this.tags =
|
|
5106
|
+
this.filters = Object.create(null);
|
|
5107
|
+
this.tags = Object.create(null);
|
|
4681
5108
|
this.options = normalize(opts);
|
|
4682
5109
|
// eslint-disable-next-line deprecation/deprecation
|
|
4683
5110
|
this.parser = new Parser(this);
|
|
@@ -4689,7 +5116,7 @@ class Liquid {
|
|
|
4689
5116
|
return parser.parse(html, filepath);
|
|
4690
5117
|
}
|
|
4691
5118
|
_render(tpl, scope, renderOptions) {
|
|
4692
|
-
const ctx = scope instanceof Context ? scope : new Context(scope, this.options, renderOptions);
|
|
5119
|
+
const ctx = scope instanceof Context ? scope : new Context(scope, this.options, renderOptions, { liquid: this });
|
|
4693
5120
|
return this.renderer.renderTemplates(tpl, ctx);
|
|
4694
5121
|
}
|
|
4695
5122
|
render(tpl, scope, renderOptions) {
|
|
@@ -4701,7 +5128,7 @@ class Liquid {
|
|
|
4701
5128
|
return toValueSync(this._render(tpl, scope, Object.assign(Object.assign({}, renderOptions), { sync: true })));
|
|
4702
5129
|
}
|
|
4703
5130
|
renderToNodeStream(tpl, scope, renderOptions = {}) {
|
|
4704
|
-
const ctx = new Context(scope, this.options, renderOptions);
|
|
5131
|
+
const ctx = new Context(scope, this.options, renderOptions, { liquid: this });
|
|
4705
5132
|
return this.renderer.renderTemplatesToNodeStream(tpl, ctx);
|
|
4706
5133
|
}
|
|
4707
5134
|
_parseAndRender(html, scope, renderOptions) {
|
|
@@ -4753,7 +5180,7 @@ class Liquid {
|
|
|
4753
5180
|
}
|
|
4754
5181
|
_evalValue(str, scope) {
|
|
4755
5182
|
const value = new Value(str, this);
|
|
4756
|
-
const ctx = scope instanceof Context ? scope : new Context(scope, this.options);
|
|
5183
|
+
const ctx = scope instanceof Context ? scope : new Context(scope, this.options, {}, { liquid: this });
|
|
4757
5184
|
return value.value(ctx);
|
|
4758
5185
|
}
|
|
4759
5186
|
evalValue(str, scope) {
|
|
@@ -4767,6 +5194,9 @@ class Liquid {
|
|
|
4767
5194
|
registerFilter(name, filter) {
|
|
4768
5195
|
this.filters[name] = filter;
|
|
4769
5196
|
}
|
|
5197
|
+
unregisterFilter(name) {
|
|
5198
|
+
delete this.filters[name];
|
|
5199
|
+
}
|
|
4770
5200
|
registerTag(name, tag) {
|
|
4771
5201
|
this.tags[name] = isFunction(tag) ? tag : createTagClass(tag);
|
|
4772
5202
|
}
|
|
@@ -4880,4 +5310,4 @@ class Liquid {
|
|
|
4880
5310
|
/* istanbul ignore file */
|
|
4881
5311
|
const version = '[VI]{version}[/VI]';
|
|
4882
5312
|
|
|
4883
|
-
export { AssertionError, AssignTag, BlockTag, BreakTag, CaptureTag, CaseTag, CommentTag, Context, ContinueTag, CycleTag, DecrementTag, Drop, EchoTag, Expression, Filter, ForTag, Hash, IfTag, IncludeTag, IncrementTag, InlineCommentTag, LayoutTag, Liquid, LiquidError, LiquidTag, Output, ParseError, ParseStream, Parser, RawTag, RenderError, RenderTag, TablerowTag, Tag, TagToken, Token, TokenKind, TokenizationError, Tokenizer, typeGuards as TypeGuards, UndefinedVariableError, UnlessTag, Value, Variable, analyze, analyzeSync, assert, createTrie, defaultOperators, defaultOptions, evalQuotedToken, evalToken, filters, isFalsy, isTruthy, tags, toPromise, toValue, toValueSync, version };
|
|
5313
|
+
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, LookupType, 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, evalTokenValue, filters, isFalsy, isTruthy, tags, toPromise, toValue, toValueSync, version };
|