@skeptools/skep-organization-project 0.0.17 → 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
|
'use strict';
|
|
@@ -10,6 +10,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
10
10
|
var stream = require('stream');
|
|
11
11
|
var path = require('path');
|
|
12
12
|
var fs$1 = require('fs');
|
|
13
|
+
var crypto = require('crypto');
|
|
13
14
|
|
|
14
15
|
class Token {
|
|
15
16
|
constructor(kind, input, begin, end, file) {
|
|
@@ -40,7 +41,7 @@ class Token {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
class Drop {
|
|
43
|
-
liquidMethodMissing(key) {
|
|
44
|
+
liquidMethodMissing(key, context) {
|
|
44
45
|
return undefined;
|
|
45
46
|
}
|
|
46
47
|
}
|
|
@@ -61,9 +62,6 @@ function isPromise(val) {
|
|
|
61
62
|
function isIterator(val) {
|
|
62
63
|
return val && isFunction(val.next) && isFunction(val.throw) && isFunction(val.return);
|
|
63
64
|
}
|
|
64
|
-
function escapeRegex(str) {
|
|
65
|
-
return str.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
66
|
-
}
|
|
67
65
|
function promisify(fn) {
|
|
68
66
|
return function (...args) {
|
|
69
67
|
return new Promise((resolve, reject) => {
|
|
@@ -83,6 +81,25 @@ function stringify(value) {
|
|
|
83
81
|
return value.map(x => stringify(x)).join('');
|
|
84
82
|
return String(value);
|
|
85
83
|
}
|
|
84
|
+
function advanceCodePoints(str, from, count) {
|
|
85
|
+
let index = from;
|
|
86
|
+
for (let i = 0; i < count && index < str.length; i++)
|
|
87
|
+
index += str.codePointAt(index) > 0xFFFF ? 2 : 1;
|
|
88
|
+
return index;
|
|
89
|
+
}
|
|
90
|
+
function codePointLength(str) {
|
|
91
|
+
let length = 0;
|
|
92
|
+
for (let index = 0; index < str.length; index = advanceCodePoints(str, index, 1))
|
|
93
|
+
length++;
|
|
94
|
+
return length;
|
|
95
|
+
}
|
|
96
|
+
function readArrayElement(arr, index, ownPropertyOnly) {
|
|
97
|
+
if (index < 0)
|
|
98
|
+
index = arr.length + index;
|
|
99
|
+
if (ownPropertyOnly && !hasOwnProperty.call(arr, index))
|
|
100
|
+
return undefined;
|
|
101
|
+
return arr[index];
|
|
102
|
+
}
|
|
86
103
|
function toEnumerable(val) {
|
|
87
104
|
val = toValue(val);
|
|
88
105
|
if (isArray(val))
|
|
@@ -107,8 +124,7 @@ function toValue(value) {
|
|
|
107
124
|
return (value instanceof Drop && isFunction(value.valueOf)) ? value.valueOf() : value;
|
|
108
125
|
}
|
|
109
126
|
function toNumber(value) {
|
|
110
|
-
|
|
111
|
-
return isNaN(value) ? 0 : value;
|
|
127
|
+
return +toValue(value) || 0;
|
|
112
128
|
}
|
|
113
129
|
function isNumber(value) {
|
|
114
130
|
return typeof value === 'number';
|
|
@@ -152,9 +168,6 @@ function forOwn(obj, iteratee) {
|
|
|
152
168
|
}
|
|
153
169
|
return obj;
|
|
154
170
|
}
|
|
155
|
-
function last(arr) {
|
|
156
|
-
return arr[arr.length - 1];
|
|
157
|
-
}
|
|
158
171
|
/*
|
|
159
172
|
* Checks if value is the language type of Object.
|
|
160
173
|
* (e.g. arrays, functions, objects, regexes, new Number(0), and new String(''))
|
|
@@ -180,10 +193,10 @@ function padEnd(str, length, ch = ' ') {
|
|
|
180
193
|
}
|
|
181
194
|
function pad(str, length, ch, add) {
|
|
182
195
|
str = String(str);
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
return str;
|
|
196
|
+
const n = length - str.length;
|
|
197
|
+
if (n <= 0)
|
|
198
|
+
return str;
|
|
199
|
+
return add(str, ch.repeat(n));
|
|
187
200
|
}
|
|
188
201
|
function identify(val) {
|
|
189
202
|
return val;
|
|
@@ -195,13 +208,26 @@ function changeCase(str) {
|
|
|
195
208
|
function ellipsis(str, N) {
|
|
196
209
|
return str.length > N ? str.slice(0, N - 3) + '...' : str;
|
|
197
210
|
}
|
|
211
|
+
function orderedCompare(a, b) {
|
|
212
|
+
if (isNil(a) && isNil(b))
|
|
213
|
+
return 0;
|
|
214
|
+
if (isNil(a))
|
|
215
|
+
return 1;
|
|
216
|
+
if (isNil(b))
|
|
217
|
+
return -1;
|
|
218
|
+
if (a < b)
|
|
219
|
+
return -1;
|
|
220
|
+
if (a > b)
|
|
221
|
+
return 1;
|
|
222
|
+
return 0;
|
|
223
|
+
}
|
|
198
224
|
// compare string in case-insensitive way, undefined values to the tail
|
|
199
225
|
function caseInsensitiveCompare(a, b) {
|
|
200
|
-
if (a
|
|
226
|
+
if (isNil(a) && isNil(b))
|
|
201
227
|
return 0;
|
|
202
|
-
if (a
|
|
228
|
+
if (isNil(a))
|
|
203
229
|
return 1;
|
|
204
|
-
if (b
|
|
230
|
+
if (isNil(b))
|
|
205
231
|
return -1;
|
|
206
232
|
a = toLowerCase.call(a);
|
|
207
233
|
b = toLowerCase.call(b);
|
|
@@ -214,8 +240,8 @@ function caseInsensitiveCompare(a, b) {
|
|
|
214
240
|
function argumentsToValue(fn) {
|
|
215
241
|
return function (...args) { return fn.call(this, ...args.map(toValue)); };
|
|
216
242
|
}
|
|
217
|
-
function
|
|
218
|
-
return
|
|
243
|
+
function argumentsToNumber(fn) {
|
|
244
|
+
return function (...args) { return fn.call(this, ...args.map(toNumber)); };
|
|
219
245
|
}
|
|
220
246
|
/** Return an array containing unique elements from _array_. Works with nested arrays and objects. */
|
|
221
247
|
function* strictUniq(array) {
|
|
@@ -381,8 +407,8 @@ function assertEmpty(predicate, message = `unexpected ${JSON.stringify(predicate
|
|
|
381
407
|
}
|
|
382
408
|
|
|
383
409
|
class NullDrop extends Drop {
|
|
384
|
-
equals(value) {
|
|
385
|
-
return isNil(toValue(value));
|
|
410
|
+
*equals(value) {
|
|
411
|
+
return isNil(yield toValue(value));
|
|
386
412
|
}
|
|
387
413
|
gt() {
|
|
388
414
|
return false;
|
|
@@ -402,10 +428,10 @@ class NullDrop extends Drop {
|
|
|
402
428
|
}
|
|
403
429
|
|
|
404
430
|
class EmptyDrop extends Drop {
|
|
405
|
-
equals(value) {
|
|
431
|
+
*equals(value) {
|
|
406
432
|
if (value instanceof EmptyDrop)
|
|
407
433
|
return false;
|
|
408
|
-
value = toValue(value);
|
|
434
|
+
value = yield toValue(value);
|
|
409
435
|
if (isString(value) || isArray(value))
|
|
410
436
|
return value.length === 0;
|
|
411
437
|
if (isObject(value))
|
|
@@ -433,14 +459,14 @@ class EmptyDrop extends Drop {
|
|
|
433
459
|
}
|
|
434
460
|
|
|
435
461
|
class BlankDrop extends EmptyDrop {
|
|
436
|
-
equals(value) {
|
|
462
|
+
*equals(value) {
|
|
437
463
|
if (value === false)
|
|
438
464
|
return true;
|
|
439
|
-
if (isNil(toValue(value)))
|
|
465
|
+
if (isNil(yield toValue(value)))
|
|
440
466
|
return true;
|
|
441
467
|
if (isString(value))
|
|
442
468
|
return /^\s*$/.test(value);
|
|
443
|
-
return super.equals(value);
|
|
469
|
+
return yield super.equals(value);
|
|
444
470
|
}
|
|
445
471
|
static is(value) {
|
|
446
472
|
return value instanceof BlankDrop;
|
|
@@ -480,6 +506,50 @@ class ForloopDrop extends Drop {
|
|
|
480
506
|
}
|
|
481
507
|
}
|
|
482
508
|
|
|
509
|
+
class SimpleEmitter {
|
|
510
|
+
constructor() {
|
|
511
|
+
this.buffer = '';
|
|
512
|
+
}
|
|
513
|
+
write(html) {
|
|
514
|
+
this.buffer += stringify(html);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
class StreamedEmitter {
|
|
519
|
+
constructor() {
|
|
520
|
+
this.buffer = '';
|
|
521
|
+
this.stream = new stream.PassThrough();
|
|
522
|
+
}
|
|
523
|
+
write(html) {
|
|
524
|
+
this.stream.write(stringify(html));
|
|
525
|
+
}
|
|
526
|
+
error(err) {
|
|
527
|
+
this.stream.emit('error', err);
|
|
528
|
+
}
|
|
529
|
+
end() {
|
|
530
|
+
this.stream.end();
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
class KeepingTypeEmitter {
|
|
535
|
+
constructor() {
|
|
536
|
+
this.buffer = '';
|
|
537
|
+
}
|
|
538
|
+
write(html) {
|
|
539
|
+
html = toValue(html);
|
|
540
|
+
// This will only preserve the type if the value is isolated.
|
|
541
|
+
// I.E:
|
|
542
|
+
// {{ my-port }} -> 42
|
|
543
|
+
// {{ my-host }}:{{ my-port }} -> 'host:42'
|
|
544
|
+
if (typeof html !== 'string' && this.buffer === '') {
|
|
545
|
+
this.buffer = html;
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
this.buffer = stringify(this.buffer) + stringify(html);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
483
553
|
class BlockDrop extends Drop {
|
|
484
554
|
constructor(
|
|
485
555
|
// the block render from layout template
|
|
@@ -491,8 +561,10 @@ class BlockDrop extends Drop {
|
|
|
491
561
|
* Provide parent access in child block by
|
|
492
562
|
* {{ block.super }}
|
|
493
563
|
*/
|
|
494
|
-
super() {
|
|
495
|
-
|
|
564
|
+
*super() {
|
|
565
|
+
const emitter = new SimpleEmitter();
|
|
566
|
+
yield this.superBlockRender(emitter);
|
|
567
|
+
return emitter.buffer;
|
|
496
568
|
}
|
|
497
569
|
}
|
|
498
570
|
|
|
@@ -515,7 +587,16 @@ const literalValues = {
|
|
|
515
587
|
'blank': new BlankDrop()
|
|
516
588
|
};
|
|
517
589
|
|
|
590
|
+
// Tries are built once per input object and reused: the Tokenizer rebuilds them
|
|
591
|
+
// on every instantiation, but `input` (operators/literalValues) is a stable
|
|
592
|
+
// reference. WeakMap-keying by `input` lets short-lived operator objects (and
|
|
593
|
+
// their tries) be garbage collected. The returned trie is treated as read-only
|
|
594
|
+
// by callers (matchTrie only reads it); do not mutate it.
|
|
595
|
+
const trieCache = new WeakMap();
|
|
518
596
|
function createTrie(input) {
|
|
597
|
+
const cached = trieCache.get(input);
|
|
598
|
+
if (cached)
|
|
599
|
+
return cached;
|
|
519
600
|
const trie = {};
|
|
520
601
|
for (const [name, data] of Object.entries(input)) {
|
|
521
602
|
let node = trie;
|
|
@@ -530,9 +611,16 @@ function createTrie(input) {
|
|
|
530
611
|
node.data = data;
|
|
531
612
|
node.end = true;
|
|
532
613
|
}
|
|
614
|
+
trieCache.set(input, trie);
|
|
533
615
|
return trie;
|
|
534
616
|
}
|
|
535
617
|
|
|
618
|
+
function toLiquidAsync(asyncFn, syncFn) {
|
|
619
|
+
const syncImpl = syncFn || asyncFn;
|
|
620
|
+
return (sync, ...args) => {
|
|
621
|
+
return sync ? syncImpl(...args) : asyncFn(...args);
|
|
622
|
+
};
|
|
623
|
+
}
|
|
536
624
|
// convert an async iterator to a Promise
|
|
537
625
|
async function toPromise(val) {
|
|
538
626
|
if (!isIterator(val))
|
|
@@ -542,7 +630,7 @@ async function toPromise(val) {
|
|
|
542
630
|
let next = 'next';
|
|
543
631
|
do {
|
|
544
632
|
const state = val[next](value);
|
|
545
|
-
done = state.done;
|
|
633
|
+
done = !!state.done;
|
|
546
634
|
value = state.value;
|
|
547
635
|
next = 'next';
|
|
548
636
|
try {
|
|
@@ -567,7 +655,7 @@ function toValueSync(val) {
|
|
|
567
655
|
let next = 'next';
|
|
568
656
|
do {
|
|
569
657
|
const state = val[next](value);
|
|
570
|
-
done = state.done;
|
|
658
|
+
done = !!state.done;
|
|
571
659
|
value = state.value;
|
|
572
660
|
next = 'next';
|
|
573
661
|
if (isIterator(value)) {
|
|
@@ -597,12 +685,9 @@ function getDayOfYear(d) {
|
|
|
597
685
|
return num + d.getDate();
|
|
598
686
|
}
|
|
599
687
|
function getWeekOfYear(d, startDay) {
|
|
600
|
-
//
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
const jan1 = new Date(d.getFullYear(), 0, 1);
|
|
604
|
-
const then = (7 - jan1.getDay() + startDay);
|
|
605
|
-
return String(Math.floor((now - then) / 7) + 1);
|
|
688
|
+
// Days before the first startDay of the year belong to week 0
|
|
689
|
+
const daysSinceStartDay = (d.getDay() - startDay + 7) % 7;
|
|
690
|
+
return String(Math.floor((getDayOfYear(d) - 1 - daysSinceStartDay + 7) / 7));
|
|
606
691
|
}
|
|
607
692
|
function isLeapYear(d) {
|
|
608
693
|
const year = d.getFullYear();
|
|
@@ -620,10 +705,11 @@ function ordinal(d) {
|
|
|
620
705
|
}
|
|
621
706
|
}
|
|
622
707
|
function century(d) {
|
|
623
|
-
return
|
|
708
|
+
return Math.floor(d.getFullYear() / 100);
|
|
624
709
|
}
|
|
625
710
|
// default to 0
|
|
626
711
|
const padWidths = {
|
|
712
|
+
C: 2,
|
|
627
713
|
d: 2,
|
|
628
714
|
e: 2,
|
|
629
715
|
H: 2,
|
|
@@ -636,7 +722,9 @@ const padWidths = {
|
|
|
636
722
|
M: 2,
|
|
637
723
|
S: 2,
|
|
638
724
|
U: 2,
|
|
639
|
-
W: 2
|
|
725
|
+
W: 2,
|
|
726
|
+
y: 2,
|
|
727
|
+
Y: 4
|
|
640
728
|
};
|
|
641
729
|
const padSpaceChars = new Set('aAbBceklpP');
|
|
642
730
|
function getTimezoneOffset(d, opts) {
|
|
@@ -667,13 +755,14 @@ const formatCodes = {
|
|
|
667
755
|
M: (d) => d.getMinutes(),
|
|
668
756
|
N: (d, opts) => {
|
|
669
757
|
const width = Number(opts.width) || 9;
|
|
670
|
-
const str = String(d.getMilliseconds()).slice(0, width);
|
|
758
|
+
const str = padStart(String(d.getMilliseconds()), 3, '0').slice(0, width);
|
|
759
|
+
opts.memoryLimit?.use(width - str.length);
|
|
671
760
|
return padEnd(str, width, '0');
|
|
672
761
|
},
|
|
673
762
|
p: (d) => (d.getHours() < 12 ? 'AM' : 'PM'),
|
|
674
763
|
P: (d) => (d.getHours() < 12 ? 'am' : 'pm'),
|
|
675
764
|
q: (d) => ordinal(d),
|
|
676
|
-
s: (d) => Math.
|
|
765
|
+
s: (d) => Math.floor(d.dateValue() / 1000),
|
|
677
766
|
S: (d) => d.getSeconds(),
|
|
678
767
|
u: (d) => d.getDay() || 7,
|
|
679
768
|
U: (d) => getWeekOfYear(d, 0),
|
|
@@ -681,7 +770,7 @@ const formatCodes = {
|
|
|
681
770
|
W: (d) => getWeekOfYear(d, 1),
|
|
682
771
|
x: (d) => d.toLocaleDateString(),
|
|
683
772
|
X: (d) => d.toLocaleTimeString(),
|
|
684
|
-
y: (d) => d.getFullYear()
|
|
773
|
+
y: (d) => (d.getFullYear() % 100 + 100) % 100,
|
|
685
774
|
Y: (d) => d.getFullYear(),
|
|
686
775
|
z: getTimezoneOffset,
|
|
687
776
|
Z: (d, opts) => d.getTimeZoneName() || getTimezoneOffset(d, opts),
|
|
@@ -690,18 +779,18 @@ const formatCodes = {
|
|
|
690
779
|
'%': () => '%'
|
|
691
780
|
};
|
|
692
781
|
formatCodes.h = formatCodes.b;
|
|
693
|
-
function strftime(d, formatStr) {
|
|
782
|
+
function strftime(d, formatStr, memoryLimit) {
|
|
694
783
|
let output = '';
|
|
695
784
|
let remaining = formatStr;
|
|
696
785
|
let match;
|
|
697
786
|
while ((match = rFormat.exec(remaining))) {
|
|
698
787
|
output += remaining.slice(0, match.index);
|
|
699
788
|
remaining = remaining.slice(match.index + match[0].length);
|
|
700
|
-
output += format(d, match);
|
|
789
|
+
output += format(d, match, memoryLimit);
|
|
701
790
|
}
|
|
702
791
|
return output + remaining;
|
|
703
792
|
}
|
|
704
|
-
function format(d, match) {
|
|
793
|
+
function format(d, match, memoryLimit) {
|
|
705
794
|
const [input, flagStr = '', width, modifier, conversion] = match;
|
|
706
795
|
const convert = formatCodes[conversion];
|
|
707
796
|
if (!convert)
|
|
@@ -709,9 +798,9 @@ function format(d, match) {
|
|
|
709
798
|
const flags = {};
|
|
710
799
|
for (const flag of flagStr)
|
|
711
800
|
flags[flag] = true;
|
|
712
|
-
let ret = String(convert(d, { flags, width, modifier }));
|
|
801
|
+
let ret = String(convert(d, { flags, width, modifier, memoryLimit }));
|
|
713
802
|
let padChar = padSpaceChars.has(conversion) ? ' ' : '0';
|
|
714
|
-
let padWidth = width || padWidths[conversion] || 0;
|
|
803
|
+
let padWidth = Number(width) || padWidths[conversion] || 0;
|
|
715
804
|
if (flags['^'])
|
|
716
805
|
ret = ret.toUpperCase();
|
|
717
806
|
else if (flags['#'])
|
|
@@ -722,6 +811,9 @@ function format(d, match) {
|
|
|
722
811
|
padChar = '0';
|
|
723
812
|
if (flags['-'])
|
|
724
813
|
padWidth = 0;
|
|
814
|
+
memoryLimit?.use(Number(padWidth) - ret.length);
|
|
815
|
+
if (padChar === '0' && ret[0] === '-')
|
|
816
|
+
return '-' + padStart(ret.slice(1), padWidth - 1, padChar);
|
|
725
817
|
return padStart(ret, padWidth, padChar);
|
|
726
818
|
}
|
|
727
819
|
|
|
@@ -770,6 +862,15 @@ class LiquidDate {
|
|
|
770
862
|
getTime() {
|
|
771
863
|
return this.displayDate.getTime();
|
|
772
864
|
}
|
|
865
|
+
/**
|
|
866
|
+
* The underlying UTC timestamp in milliseconds, unaffected by the display
|
|
867
|
+
* timezone. Use this (not `getTime()`) for timezone-invariant values like
|
|
868
|
+
* `%s`: `getTime()` reads `displayDate`, which is deliberately shifted by
|
|
869
|
+
* the display timezone offset so wall-clock getters can delegate to Date.
|
|
870
|
+
*/
|
|
871
|
+
dateValue() {
|
|
872
|
+
return this.date.getTime();
|
|
873
|
+
}
|
|
773
874
|
getMilliseconds() {
|
|
774
875
|
return this.displayDate.getMilliseconds();
|
|
775
876
|
}
|
|
@@ -877,13 +978,15 @@ class Limiter {
|
|
|
877
978
|
this.limit = limit;
|
|
878
979
|
}
|
|
879
980
|
use(count) {
|
|
880
|
-
count
|
|
881
|
-
|
|
882
|
-
|
|
981
|
+
if (+count > 0) {
|
|
982
|
+
assert(this.base + +count <= this.limit, this.message);
|
|
983
|
+
this.base += +count;
|
|
984
|
+
}
|
|
883
985
|
}
|
|
884
986
|
check(count) {
|
|
885
|
-
count
|
|
886
|
-
|
|
987
|
+
if (+count > 0) {
|
|
988
|
+
assert(+count <= this.limit, this.message);
|
|
989
|
+
}
|
|
887
990
|
}
|
|
888
991
|
}
|
|
889
992
|
|
|
@@ -914,13 +1017,11 @@ class TagToken extends DelimitedToken {
|
|
|
914
1017
|
const { trimTagLeft, trimTagRight, tagDelimiterLeft, tagDelimiterRight } = options;
|
|
915
1018
|
const [valueBegin, valueEnd] = [begin + tagDelimiterLeft.length, end - tagDelimiterRight.length];
|
|
916
1019
|
super(exports.TokenKind.Tag, [valueBegin, valueEnd], input, begin, end, trimTagLeft, trimTagRight, file);
|
|
917
|
-
this.tokenizer = new Tokenizer(input, options.operators, file, this.contentRange);
|
|
1020
|
+
this.tokenizer = new Tokenizer(input, options.operators, file, this.contentRange, options.groupedExpressions);
|
|
918
1021
|
this.name = this.tokenizer.readTagName();
|
|
919
1022
|
this.tokenizer.assert(this.name, `illegal tag syntax, tag name expected`);
|
|
920
1023
|
this.tokenizer.skipBlank();
|
|
921
|
-
|
|
922
|
-
get args() {
|
|
923
|
-
return this.tokenizer.input.slice(this.tokenizer.p, this.contentRange[1]);
|
|
1024
|
+
this.args = this.tokenizer.input.slice(this.tokenizer.p, this.contentRange[1]);
|
|
924
1025
|
}
|
|
925
1026
|
}
|
|
926
1027
|
|
|
@@ -1015,8 +1116,7 @@ class OperatorToken extends Token {
|
|
|
1015
1116
|
this.operator = this.getText();
|
|
1016
1117
|
}
|
|
1017
1118
|
getPrecedence() {
|
|
1018
|
-
|
|
1019
|
-
return key in operatorPrecedences ? operatorPrecedences[key] : 1;
|
|
1119
|
+
return this.operator in operatorPrecedences ? operatorPrecedences[this.operator] : 1;
|
|
1020
1120
|
}
|
|
1021
1121
|
}
|
|
1022
1122
|
|
|
@@ -1130,7 +1230,7 @@ class RangeToken extends Token {
|
|
|
1130
1230
|
class LiquidTagToken extends DelimitedToken {
|
|
1131
1231
|
constructor(input, begin, end, options, file) {
|
|
1132
1232
|
super(exports.TokenKind.Tag, [begin, end], input, begin, end, false, false, file);
|
|
1133
|
-
this.tokenizer = new Tokenizer(input, options.operators, file, this.contentRange);
|
|
1233
|
+
this.tokenizer = new Tokenizer(input, options.operators, file, this.contentRange, options.groupedExpressions);
|
|
1134
1234
|
this.name = this.tokenizer.readTagName();
|
|
1135
1235
|
this.tokenizer.assert(this.name, 'illegal liquid tag syntax');
|
|
1136
1236
|
this.tokenizer.skipBlank();
|
|
@@ -1166,50 +1266,6 @@ function getPerformance() {
|
|
|
1166
1266
|
polyfill;
|
|
1167
1267
|
}
|
|
1168
1268
|
|
|
1169
|
-
class SimpleEmitter {
|
|
1170
|
-
constructor() {
|
|
1171
|
-
this.buffer = '';
|
|
1172
|
-
}
|
|
1173
|
-
write(html) {
|
|
1174
|
-
this.buffer += stringify(html);
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
class StreamedEmitter {
|
|
1179
|
-
constructor() {
|
|
1180
|
-
this.buffer = '';
|
|
1181
|
-
this.stream = new stream.PassThrough();
|
|
1182
|
-
}
|
|
1183
|
-
write(html) {
|
|
1184
|
-
this.stream.write(stringify(html));
|
|
1185
|
-
}
|
|
1186
|
-
error(err) {
|
|
1187
|
-
this.stream.emit('error', err);
|
|
1188
|
-
}
|
|
1189
|
-
end() {
|
|
1190
|
-
this.stream.end();
|
|
1191
|
-
}
|
|
1192
|
-
}
|
|
1193
|
-
|
|
1194
|
-
class KeepingTypeEmitter {
|
|
1195
|
-
constructor() {
|
|
1196
|
-
this.buffer = '';
|
|
1197
|
-
}
|
|
1198
|
-
write(html) {
|
|
1199
|
-
html = toValue(html);
|
|
1200
|
-
// This will only preserve the type if the value is isolated.
|
|
1201
|
-
// I.E:
|
|
1202
|
-
// {{ my-port }} -> 42
|
|
1203
|
-
// {{ my-host }}:{{ my-port }} -> 'host:42'
|
|
1204
|
-
if (typeof html !== 'string' && this.buffer === '') {
|
|
1205
|
-
this.buffer = html;
|
|
1206
|
-
}
|
|
1207
|
-
else {
|
|
1208
|
-
this.buffer = stringify(this.buffer) + stringify(html);
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
1269
|
class Render {
|
|
1214
1270
|
renderTemplatesToNodeStream(templates, ctx) {
|
|
1215
1271
|
const emitter = new StreamedEmitter();
|
|
@@ -1221,6 +1277,7 @@ class Render {
|
|
|
1221
1277
|
if (!emitter) {
|
|
1222
1278
|
emitter = ctx.opts.keepOutputType ? new KeepingTypeEmitter() : new SimpleEmitter();
|
|
1223
1279
|
}
|
|
1280
|
+
ctx.renderLimit.check(getPerformance().now());
|
|
1224
1281
|
const errors = [];
|
|
1225
1282
|
for (const tpl of templates) {
|
|
1226
1283
|
ctx.renderLimit.check(getPerformance().now());
|
|
@@ -1247,6 +1304,33 @@ class Render {
|
|
|
1247
1304
|
}
|
|
1248
1305
|
}
|
|
1249
1306
|
|
|
1307
|
+
function isKeyValuePair(arr) {
|
|
1308
|
+
return isArray(arr);
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
class Filter {
|
|
1312
|
+
constructor(token, options, liquid) {
|
|
1313
|
+
this.token = token;
|
|
1314
|
+
this.name = token.name;
|
|
1315
|
+
this.handler = isFunction(options)
|
|
1316
|
+
? options
|
|
1317
|
+
: (isFunction(options?.handler) ? options.handler : identify);
|
|
1318
|
+
this.raw = !isFunction(options) && !!options?.raw;
|
|
1319
|
+
this.args = token.args;
|
|
1320
|
+
this.liquid = liquid;
|
|
1321
|
+
}
|
|
1322
|
+
*render(value, context) {
|
|
1323
|
+
const argv = [];
|
|
1324
|
+
for (const arg of this.args) {
|
|
1325
|
+
if (isKeyValuePair(arg))
|
|
1326
|
+
argv.push([arg[0], yield evalToken(arg[1], context)]);
|
|
1327
|
+
else
|
|
1328
|
+
argv.push(yield evalToken(arg, context));
|
|
1329
|
+
}
|
|
1330
|
+
return yield this.handler.apply({ context, token: this.token, liquid: this.liquid }, [value, ...argv]);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1250
1334
|
class Expression {
|
|
1251
1335
|
constructor(tokens) {
|
|
1252
1336
|
this.postfix = [...toPostfix(tokens)];
|
|
@@ -1277,6 +1361,11 @@ class Expression {
|
|
|
1277
1361
|
return !!this.postfix.length;
|
|
1278
1362
|
}
|
|
1279
1363
|
}
|
|
1364
|
+
/**
|
|
1365
|
+
* Evaluate `token`, preserving Drops.
|
|
1366
|
+
* Operators use this so `Comparable` drops (`empty`, `nil`, `blank`) and custom drops
|
|
1367
|
+
* are compared as drops rather than as their `valueOf()` result.
|
|
1368
|
+
*/
|
|
1280
1369
|
function* evalToken(token, ctx, lenient = false) {
|
|
1281
1370
|
if (!token)
|
|
1282
1371
|
return;
|
|
@@ -1286,11 +1375,32 @@ function* evalToken(token, ctx, lenient = false) {
|
|
|
1286
1375
|
return yield evalPropertyAccessToken(token, ctx, lenient);
|
|
1287
1376
|
if (isRangeToken(token))
|
|
1288
1377
|
return yield evalRangeToken(token, ctx);
|
|
1378
|
+
if (isFilteredValueToken(token))
|
|
1379
|
+
return yield evalFilteredValueToken(token, ctx, lenient);
|
|
1380
|
+
}
|
|
1381
|
+
function* evalFilteredValueToken(token, ctx, lenient) {
|
|
1382
|
+
assert(ctx.liquid, 'FilteredValueToken evaluation requires liquid instance in context');
|
|
1383
|
+
lenient = lenient || (ctx.opts.lenientIf && token.filters.length > 0 && token.filters[0].name === 'default');
|
|
1384
|
+
let val = yield token.initial.evaluate(ctx, lenient);
|
|
1385
|
+
for (const filterToken of token.filters) {
|
|
1386
|
+
const filterImpl = ctx.liquid.filters[filterToken.name];
|
|
1387
|
+
assert(filterImpl || !ctx.liquid.options.strictFilters, () => `undefined filter: ${filterToken.name}`);
|
|
1388
|
+
const filter = new Filter(filterToken, filterImpl, ctx.liquid);
|
|
1389
|
+
val = yield filter.render(val, ctx);
|
|
1390
|
+
}
|
|
1391
|
+
return val;
|
|
1392
|
+
}
|
|
1393
|
+
/**
|
|
1394
|
+
* Evaluate `token` to the value filters and tags consume.
|
|
1395
|
+
* Awaits a promise returned by `Drop.valueOf()`.
|
|
1396
|
+
*/
|
|
1397
|
+
function* evalTokenValue(token, ctx, lenient = false) {
|
|
1398
|
+
return yield toValue(yield evalToken(token, ctx, lenient));
|
|
1289
1399
|
}
|
|
1290
1400
|
function* evalPropertyAccessToken(token, ctx, lenient) {
|
|
1291
1401
|
const props = [];
|
|
1292
1402
|
for (const prop of token.props) {
|
|
1293
|
-
props.push((yield
|
|
1403
|
+
props.push((yield evalTokenValue(prop, ctx, false)));
|
|
1294
1404
|
}
|
|
1295
1405
|
try {
|
|
1296
1406
|
if (token.variable) {
|
|
@@ -1311,8 +1421,8 @@ function evalQuotedToken(token) {
|
|
|
1311
1421
|
return token.content;
|
|
1312
1422
|
}
|
|
1313
1423
|
function* evalRangeToken(token, ctx) {
|
|
1314
|
-
const low = yield
|
|
1315
|
-
const high = yield
|
|
1424
|
+
const low = (yield evalTokenValue(token.lhs, ctx));
|
|
1425
|
+
const high = (yield evalTokenValue(token.rhs, ctx));
|
|
1316
1426
|
ctx.memoryLimit.use(high - low + 1);
|
|
1317
1427
|
return range(+low, +high + 1);
|
|
1318
1428
|
}
|
|
@@ -1347,67 +1457,75 @@ function isFalsy(val, ctx) {
|
|
|
1347
1457
|
}
|
|
1348
1458
|
|
|
1349
1459
|
const defaultOperators = {
|
|
1350
|
-
'==': equals,
|
|
1351
|
-
'!=': (l, r)
|
|
1352
|
-
'>': (l, r)
|
|
1460
|
+
'==': function* (l, r) { return yield equals(l, r); },
|
|
1461
|
+
'!=': function* (l, r) { return !(yield equals(l, r)); },
|
|
1462
|
+
'>': function* (l, r) {
|
|
1353
1463
|
if (isComparable(l))
|
|
1354
|
-
return l.gt(r);
|
|
1464
|
+
return yield l.gt(r);
|
|
1355
1465
|
if (isComparable(r))
|
|
1356
|
-
return r.lt(l);
|
|
1357
|
-
return toValue(l) > toValue(r);
|
|
1466
|
+
return yield r.lt(l);
|
|
1467
|
+
return (yield toValue(l)) > (yield toValue(r));
|
|
1358
1468
|
},
|
|
1359
|
-
'<': (l, r)
|
|
1469
|
+
'<': function* (l, r) {
|
|
1360
1470
|
if (isComparable(l))
|
|
1361
|
-
return l.lt(r);
|
|
1471
|
+
return yield l.lt(r);
|
|
1362
1472
|
if (isComparable(r))
|
|
1363
|
-
return r.gt(l);
|
|
1364
|
-
return toValue(l) < toValue(r);
|
|
1473
|
+
return yield r.gt(l);
|
|
1474
|
+
return (yield toValue(l)) < (yield toValue(r));
|
|
1365
1475
|
},
|
|
1366
|
-
'>=': (l, r)
|
|
1476
|
+
'>=': function* (l, r) {
|
|
1367
1477
|
if (isComparable(l))
|
|
1368
|
-
return l.geq(r);
|
|
1478
|
+
return yield l.geq(r);
|
|
1369
1479
|
if (isComparable(r))
|
|
1370
|
-
return r.leq(l);
|
|
1371
|
-
return toValue(l) >= toValue(r);
|
|
1480
|
+
return yield r.leq(l);
|
|
1481
|
+
return (yield toValue(l)) >= (yield toValue(r));
|
|
1372
1482
|
},
|
|
1373
|
-
'<=': (l, r)
|
|
1483
|
+
'<=': function* (l, r) {
|
|
1374
1484
|
if (isComparable(l))
|
|
1375
|
-
return l.leq(r);
|
|
1485
|
+
return yield l.leq(r);
|
|
1376
1486
|
if (isComparable(r))
|
|
1377
|
-
return r.geq(l);
|
|
1378
|
-
return toValue(l) <= toValue(r);
|
|
1487
|
+
return yield r.geq(l);
|
|
1488
|
+
return (yield toValue(l)) <= (yield toValue(r));
|
|
1379
1489
|
},
|
|
1380
|
-
'contains': (l, r)
|
|
1381
|
-
l = toValue(l);
|
|
1490
|
+
'contains': function* (l, r) {
|
|
1491
|
+
l = yield toValue(l);
|
|
1382
1492
|
if (isArray(l))
|
|
1383
|
-
return
|
|
1493
|
+
return yield arrayIncludes(l, r);
|
|
1384
1494
|
if (isFunction(l?.indexOf))
|
|
1385
|
-
return l.indexOf(toValue(r)) > -1;
|
|
1495
|
+
return l.indexOf(yield toValue(r)) > -1;
|
|
1386
1496
|
return false;
|
|
1387
1497
|
},
|
|
1388
|
-
'not': (v, ctx)
|
|
1389
|
-
'and': (l, r, ctx)
|
|
1390
|
-
'or': (l, r, ctx)
|
|
1498
|
+
'not': function* (v, ctx) { return isFalsy(yield toValue(v), ctx); },
|
|
1499
|
+
'and': function* (l, r, ctx) { return isTruthy(yield toValue(l), ctx) && isTruthy(yield toValue(r), ctx); },
|
|
1500
|
+
'or': function* (l, r, ctx) { return isTruthy(yield toValue(l), ctx) || isTruthy(yield toValue(r), ctx); }
|
|
1391
1501
|
};
|
|
1392
|
-
function equals(lhs, rhs) {
|
|
1502
|
+
function* equals(lhs, rhs) {
|
|
1393
1503
|
if (isComparable(lhs))
|
|
1394
|
-
return lhs.equals(rhs);
|
|
1504
|
+
return yield lhs.equals(rhs);
|
|
1395
1505
|
if (isComparable(rhs))
|
|
1396
|
-
return rhs.equals(lhs);
|
|
1397
|
-
lhs = toValue(lhs);
|
|
1398
|
-
rhs = toValue(rhs);
|
|
1506
|
+
return yield rhs.equals(lhs);
|
|
1507
|
+
lhs = yield toValue(lhs);
|
|
1508
|
+
rhs = yield toValue(rhs);
|
|
1399
1509
|
if (isArray(lhs)) {
|
|
1400
|
-
return isArray(rhs) && arrayEquals(lhs, rhs);
|
|
1510
|
+
return isArray(rhs) && (yield arrayEquals(lhs, rhs));
|
|
1401
1511
|
}
|
|
1402
1512
|
return lhs === rhs;
|
|
1403
1513
|
}
|
|
1404
|
-
function arrayEquals(lhs, rhs) {
|
|
1514
|
+
function* arrayEquals(lhs, rhs) {
|
|
1405
1515
|
if (lhs.length !== rhs.length)
|
|
1406
1516
|
return false;
|
|
1407
|
-
|
|
1517
|
+
for (let i = 0; i < lhs.length; i++) {
|
|
1518
|
+
if (!(yield equals(lhs[i], rhs[i])))
|
|
1519
|
+
return false;
|
|
1520
|
+
}
|
|
1521
|
+
return true;
|
|
1408
1522
|
}
|
|
1409
|
-
function arrayIncludes(arr, item) {
|
|
1410
|
-
|
|
1523
|
+
function* arrayIncludes(arr, item) {
|
|
1524
|
+
for (const value of arr) {
|
|
1525
|
+
if (yield equals(value, item))
|
|
1526
|
+
return true;
|
|
1527
|
+
}
|
|
1528
|
+
return false;
|
|
1411
1529
|
}
|
|
1412
1530
|
|
|
1413
1531
|
class Node {
|
|
@@ -1510,10 +1628,28 @@ function fallback(file) {
|
|
|
1510
1628
|
function dirname(filepath) {
|
|
1511
1629
|
return path.dirname(filepath);
|
|
1512
1630
|
}
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1631
|
+
const realpathAsync = promisify(fs$1.realpath);
|
|
1632
|
+
async function contains(root, file) {
|
|
1633
|
+
try {
|
|
1634
|
+
const realRoot = await realpathAsync(root);
|
|
1635
|
+
const realFile = await realpathAsync(file);
|
|
1636
|
+
const prefix = realRoot.endsWith(path.sep) ? realRoot : realRoot + path.sep;
|
|
1637
|
+
return realFile.startsWith(prefix);
|
|
1638
|
+
}
|
|
1639
|
+
catch {
|
|
1640
|
+
return false;
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
function containsSync(root, file) {
|
|
1644
|
+
try {
|
|
1645
|
+
const realRoot = fs$1.realpathSync(root);
|
|
1646
|
+
const realFile = fs$1.realpathSync(file);
|
|
1647
|
+
const prefix = realRoot.endsWith(path.sep) ? realRoot : realRoot + path.sep;
|
|
1648
|
+
return realFile.startsWith(prefix);
|
|
1649
|
+
}
|
|
1650
|
+
catch {
|
|
1651
|
+
return false;
|
|
1652
|
+
}
|
|
1517
1653
|
}
|
|
1518
1654
|
|
|
1519
1655
|
var fs = /*#__PURE__*/Object.freeze({
|
|
@@ -1526,9 +1662,24 @@ var fs = /*#__PURE__*/Object.freeze({
|
|
|
1526
1662
|
fallback: fallback,
|
|
1527
1663
|
dirname: dirname,
|
|
1528
1664
|
contains: contains,
|
|
1665
|
+
containsSync: containsSync,
|
|
1529
1666
|
sep: path.sep
|
|
1530
1667
|
});
|
|
1531
1668
|
|
|
1669
|
+
function chargeJsonReplacerValue(memoryLimit, val) {
|
|
1670
|
+
if (typeof val === 'string') {
|
|
1671
|
+
memoryLimit.use(val.length);
|
|
1672
|
+
}
|
|
1673
|
+
else if (val === null || typeof val === 'number' || typeof val === 'boolean') {
|
|
1674
|
+
memoryLimit.use(JSON.stringify(val).length);
|
|
1675
|
+
}
|
|
1676
|
+
else if (Array.isArray(val)) {
|
|
1677
|
+
memoryLimit.use(val.length + 1);
|
|
1678
|
+
}
|
|
1679
|
+
else if (typeof val === 'object') {
|
|
1680
|
+
memoryLimit.use(2);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1532
1683
|
function defaultFilter(value, defaultValue, ...args) {
|
|
1533
1684
|
value = toValue(value);
|
|
1534
1685
|
if (isArray(value) || isString(value))
|
|
@@ -1538,19 +1689,29 @@ function defaultFilter(value, defaultValue, ...args) {
|
|
|
1538
1689
|
return isFalsy(value, this.context) ? defaultValue : value;
|
|
1539
1690
|
}
|
|
1540
1691
|
function json(value, space = 0) {
|
|
1541
|
-
|
|
1692
|
+
const memoryLimit = this.context.memoryLimit;
|
|
1693
|
+
return JSON.stringify(value, (_key, val) => {
|
|
1694
|
+
chargeJsonReplacerValue(memoryLimit, val);
|
|
1695
|
+
return val;
|
|
1696
|
+
}, space);
|
|
1542
1697
|
}
|
|
1543
1698
|
function inspect(value, space = 0) {
|
|
1699
|
+
const memoryLimit = this.context.memoryLimit;
|
|
1544
1700
|
const ancestors = [];
|
|
1545
1701
|
return JSON.stringify(value, function (_key, value) {
|
|
1546
|
-
if (typeof value !== 'object' || value === null)
|
|
1702
|
+
if (typeof value !== 'object' || value === null) {
|
|
1703
|
+
chargeJsonReplacerValue(memoryLimit, value);
|
|
1547
1704
|
return value;
|
|
1705
|
+
}
|
|
1548
1706
|
// `this` is the object that value is contained in, i.e., its direct parent.
|
|
1549
1707
|
while (ancestors.length > 0 && ancestors[ancestors.length - 1] !== this)
|
|
1550
1708
|
ancestors.pop();
|
|
1551
|
-
if (ancestors.includes(value))
|
|
1709
|
+
if (ancestors.includes(value)) {
|
|
1710
|
+
memoryLimit.use('[Circular]'.length);
|
|
1552
1711
|
return '[Circular]';
|
|
1712
|
+
}
|
|
1553
1713
|
ancestors.push(value);
|
|
1714
|
+
chargeJsonReplacerValue(memoryLimit, value);
|
|
1554
1715
|
return value;
|
|
1555
1716
|
}, space);
|
|
1556
1717
|
}
|
|
@@ -1605,10 +1766,33 @@ function newline_to_br(v) {
|
|
|
1605
1766
|
this.context.memoryLimit.use(str.length);
|
|
1606
1767
|
return str.replace(/\r?\n/gm, '<br />\n');
|
|
1607
1768
|
}
|
|
1769
|
+
// Raw-text blocks (HTML5) plus '<...>' as the catch-all kind; a regex
|
|
1770
|
+
// equivalent is O(n^2) in V8 on unclosed openers.
|
|
1608
1771
|
function strip_html(v) {
|
|
1609
1772
|
const str = stringify(v);
|
|
1610
1773
|
this.context.memoryLimit.use(str.length);
|
|
1611
|
-
|
|
1774
|
+
const blocks = new Map([['<script', '</script>'], ['<style', '</style>'], ['<!--', '-->'], ['<', '>']]);
|
|
1775
|
+
let out = '';
|
|
1776
|
+
let i = 0;
|
|
1777
|
+
while (i < str.length) {
|
|
1778
|
+
const lt = str.indexOf('<', i);
|
|
1779
|
+
if (lt < 0)
|
|
1780
|
+
return out + str.slice(i);
|
|
1781
|
+
out += str.slice(i, lt);
|
|
1782
|
+
for (const [opener, closer] of blocks) {
|
|
1783
|
+
if (!str.startsWith(opener, lt))
|
|
1784
|
+
continue;
|
|
1785
|
+
const e = str.indexOf(closer, lt + opener.length);
|
|
1786
|
+
if (e >= 0) {
|
|
1787
|
+
i = e + closer.length;
|
|
1788
|
+
break;
|
|
1789
|
+
}
|
|
1790
|
+
blocks.delete(opener);
|
|
1791
|
+
}
|
|
1792
|
+
if (i <= lt)
|
|
1793
|
+
return out + str.slice(lt);
|
|
1794
|
+
}
|
|
1795
|
+
return out;
|
|
1612
1796
|
}
|
|
1613
1797
|
|
|
1614
1798
|
var htmlFilters = /*#__PURE__*/Object.freeze({
|
|
@@ -1695,6 +1879,7 @@ const defaultOptions = {
|
|
|
1695
1879
|
globals: {},
|
|
1696
1880
|
keepOutputType: false,
|
|
1697
1881
|
operators: defaultOperators,
|
|
1882
|
+
groupedExpressions: false,
|
|
1698
1883
|
memoryLimit: Infinity,
|
|
1699
1884
|
parseLimit: Infinity,
|
|
1700
1885
|
renderLimit: Infinity
|
|
@@ -1790,7 +1975,7 @@ function trimRight(token, greedy) {
|
|
|
1790
1975
|
}
|
|
1791
1976
|
|
|
1792
1977
|
class Tokenizer {
|
|
1793
|
-
constructor(input, operators = defaultOptions.operators, file, range) {
|
|
1978
|
+
constructor(input, operators = defaultOptions.operators, file, range, groupedExpressions = false) {
|
|
1794
1979
|
this.input = input;
|
|
1795
1980
|
this.file = file;
|
|
1796
1981
|
this.rawBeginAt = -1;
|
|
@@ -1798,6 +1983,7 @@ class Tokenizer {
|
|
|
1798
1983
|
this.N = range ? range[1] : input.length;
|
|
1799
1984
|
this.opTrie = createTrie(operators);
|
|
1800
1985
|
this.literalTrie = createTrie(literalValues);
|
|
1986
|
+
this.groupedExpressions = groupedExpressions;
|
|
1801
1987
|
}
|
|
1802
1988
|
readExpression() {
|
|
1803
1989
|
return new Expression(this.readExpressionTokens());
|
|
@@ -1859,6 +2045,8 @@ class Tokenizer {
|
|
|
1859
2045
|
this.skipBlank();
|
|
1860
2046
|
if (this.end())
|
|
1861
2047
|
return null;
|
|
2048
|
+
if (this.peek() === ')' || this.peek() === ',')
|
|
2049
|
+
return null;
|
|
1862
2050
|
this.assert(this.read() === '|', `expected "|" before filter`);
|
|
1863
2051
|
const name = this.readIdentifier();
|
|
1864
2052
|
if (!name.size()) {
|
|
@@ -1873,10 +2061,10 @@ class Tokenizer {
|
|
|
1873
2061
|
const arg = this.readFilterArg();
|
|
1874
2062
|
arg && args.push(arg);
|
|
1875
2063
|
this.skipBlank();
|
|
1876
|
-
this.assert(this.end() || this.peek() === ',' || this.peek() === '|', () => `unexpected character ${this.snapshot()}`);
|
|
2064
|
+
this.assert(this.end() || this.peek() === ',' || this.peek() === '|' || this.peek() === ')', () => `unexpected character ${this.snapshot()}`);
|
|
1877
2065
|
} while (this.peek() === ',');
|
|
1878
2066
|
}
|
|
1879
|
-
else if (this.peek() === '|' || this.end()) ;
|
|
2067
|
+
else if (this.peek() === '|' || this.peek() === ')' || this.peek() === ',' || this.end()) ;
|
|
1880
2068
|
else {
|
|
1881
2069
|
throw this.error('expected ":" after filter name');
|
|
1882
2070
|
}
|
|
@@ -2082,7 +2270,10 @@ class Tokenizer {
|
|
|
2082
2270
|
readValue() {
|
|
2083
2271
|
this.skipBlank();
|
|
2084
2272
|
const begin = this.p;
|
|
2085
|
-
|
|
2273
|
+
let variable = this.readLiteral() || this.readQuoted() || this.readNumber();
|
|
2274
|
+
if (!variable && this.peek() === '(') {
|
|
2275
|
+
variable = this.readGroupOrRange();
|
|
2276
|
+
}
|
|
2086
2277
|
const props = this.readProperties(!variable);
|
|
2087
2278
|
if (!props.length)
|
|
2088
2279
|
return variable;
|
|
@@ -2161,17 +2352,30 @@ class Tokenizer {
|
|
|
2161
2352
|
this.p = end;
|
|
2162
2353
|
return literal;
|
|
2163
2354
|
}
|
|
2164
|
-
|
|
2355
|
+
readGroupOrRange() {
|
|
2165
2356
|
this.skipBlank();
|
|
2166
2357
|
const begin = this.p;
|
|
2167
2358
|
if (this.peek() !== '(')
|
|
2168
2359
|
return;
|
|
2169
2360
|
++this.p;
|
|
2170
2361
|
const lhs = this.readValueOrThrow();
|
|
2171
|
-
this.
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2362
|
+
this.skipBlank();
|
|
2363
|
+
if (this.peek() === '.' && this.peek(1) === '.') {
|
|
2364
|
+
this.p += 2;
|
|
2365
|
+
const rhs = this.readValueOrThrow();
|
|
2366
|
+
this.skipBlank();
|
|
2367
|
+
this.assert(this.read() === ')', 'invalid range syntax');
|
|
2368
|
+
return new RangeToken(this.input, begin, this.p, lhs, rhs, this.file);
|
|
2369
|
+
}
|
|
2370
|
+
if (this.groupedExpressions) {
|
|
2371
|
+
const initial = new Expression([lhs, ...this.readExpressionTokens()]);
|
|
2372
|
+
this.assert(initial.valid(), () => `invalid value expression: ${this.snapshot()}`);
|
|
2373
|
+
const filters = this.readFilters();
|
|
2374
|
+
this.skipBlank();
|
|
2375
|
+
this.assert(this.read() === ')', 'unbalanced parentheses');
|
|
2376
|
+
return new FilteredValueToken(initial, filters, this.input, begin, this.p, this.file);
|
|
2377
|
+
}
|
|
2378
|
+
throw this.error('invalid range syntax');
|
|
2175
2379
|
}
|
|
2176
2380
|
readValueOrThrow() {
|
|
2177
2381
|
const value = this.readValue();
|
|
@@ -2198,7 +2402,7 @@ class Tokenizer {
|
|
|
2198
2402
|
}
|
|
2199
2403
|
*readFileNameTemplate(options) {
|
|
2200
2404
|
const { outputDelimiterLeft } = options;
|
|
2201
|
-
const htmlStopStrings = [',', ' ', outputDelimiterLeft];
|
|
2405
|
+
const htmlStopStrings = [',', ' ', '\r', '\n', '\t', outputDelimiterLeft];
|
|
2202
2406
|
const htmlStopStringSet = new Set(htmlStopStrings);
|
|
2203
2407
|
// break on ',' and ' ', outputDelimiterLeft only stops HTML token
|
|
2204
2408
|
while (this.p < this.N && !htmlStopStringSet.has(this.peek())) {
|
|
@@ -2325,33 +2529,6 @@ function createTagClass(options) {
|
|
|
2325
2529
|
};
|
|
2326
2530
|
}
|
|
2327
2531
|
|
|
2328
|
-
function isKeyValuePair(arr) {
|
|
2329
|
-
return isArray(arr);
|
|
2330
|
-
}
|
|
2331
|
-
|
|
2332
|
-
class Filter {
|
|
2333
|
-
constructor(token, options, liquid) {
|
|
2334
|
-
this.token = token;
|
|
2335
|
-
this.name = token.name;
|
|
2336
|
-
this.handler = isFunction(options)
|
|
2337
|
-
? options
|
|
2338
|
-
: (isFunction(options?.handler) ? options.handler : identify);
|
|
2339
|
-
this.raw = !isFunction(options) && !!options?.raw;
|
|
2340
|
-
this.args = token.args;
|
|
2341
|
-
this.liquid = liquid;
|
|
2342
|
-
}
|
|
2343
|
-
*render(value, context) {
|
|
2344
|
-
const argv = [];
|
|
2345
|
-
for (const arg of this.args) {
|
|
2346
|
-
if (isKeyValuePair(arg))
|
|
2347
|
-
argv.push([arg[0], yield evalToken(arg[1], context)]);
|
|
2348
|
-
else
|
|
2349
|
-
argv.push(yield evalToken(arg, context));
|
|
2350
|
-
}
|
|
2351
|
-
return yield this.handler.apply({ context, token: this.token, liquid: this.liquid }, [value, ...argv]);
|
|
2352
|
-
}
|
|
2353
|
-
}
|
|
2354
|
-
|
|
2355
2532
|
class Value {
|
|
2356
2533
|
/**
|
|
2357
2534
|
* @param str the value to be valuated, eg.: "foobar" | truncate: 3
|
|
@@ -2359,7 +2536,7 @@ class Value {
|
|
|
2359
2536
|
constructor(input, liquid) {
|
|
2360
2537
|
this.filters = [];
|
|
2361
2538
|
const token = typeof input === 'string'
|
|
2362
|
-
? new Tokenizer(input, liquid.options.operators).readFilteredValue()
|
|
2539
|
+
? new Tokenizer(input, liquid.options.operators, undefined, undefined, liquid.options.groupedExpressions).readFilteredValue()
|
|
2363
2540
|
: input;
|
|
2364
2541
|
this.initial = token.initial;
|
|
2365
2542
|
this.filters = token.filters.map(token => new Filter(token, this.getFilter(liquid, token.name), liquid));
|
|
@@ -2382,7 +2559,7 @@ class Value {
|
|
|
2382
2559
|
class Output extends TemplateImpl {
|
|
2383
2560
|
constructor(token, liquid) {
|
|
2384
2561
|
super(token);
|
|
2385
|
-
const tokenizer = new Tokenizer(token.input, liquid.options.operators, token.file, token.contentRange);
|
|
2562
|
+
const tokenizer = new Tokenizer(token.input, liquid.options.operators, token.file, token.contentRange, liquid.options.groupedExpressions);
|
|
2386
2563
|
this.value = new Value(tokenizer.readFilteredValue(), liquid);
|
|
2387
2564
|
const filters = this.value.filters;
|
|
2388
2565
|
const outputEscape = liquid.options.outputEscape;
|
|
@@ -2392,7 +2569,7 @@ class Output extends TemplateImpl {
|
|
|
2392
2569
|
}
|
|
2393
2570
|
}
|
|
2394
2571
|
*render(ctx, emitter) {
|
|
2395
|
-
const val = yield this.value.value(ctx, false);
|
|
2572
|
+
const val = yield toValue(yield this.value.value(ctx, false));
|
|
2396
2573
|
emitter.write(val);
|
|
2397
2574
|
}
|
|
2398
2575
|
*arguments() {
|
|
@@ -2667,10 +2844,31 @@ function* extractValueTokenVariables(token) {
|
|
|
2667
2844
|
yield* extractValueTokenVariables(token.lhs);
|
|
2668
2845
|
yield* extractValueTokenVariables(token.rhs);
|
|
2669
2846
|
}
|
|
2847
|
+
else if (isFilteredValueToken(token)) {
|
|
2848
|
+
yield* extractGroupedExpressionTokenVariables(token);
|
|
2849
|
+
}
|
|
2670
2850
|
else if (isPropertyAccessToken(token)) {
|
|
2671
2851
|
yield extractPropertyAccessVariable(token);
|
|
2672
2852
|
}
|
|
2673
2853
|
}
|
|
2854
|
+
function* extractGroupedExpressionTokenVariables(token) {
|
|
2855
|
+
if (!isFilteredValueToken(token))
|
|
2856
|
+
return;
|
|
2857
|
+
for (const t of token.initial.postfix) {
|
|
2858
|
+
if (isValueToken(t))
|
|
2859
|
+
yield* extractValueTokenVariables(t);
|
|
2860
|
+
}
|
|
2861
|
+
for (const filter of token.filters) {
|
|
2862
|
+
for (const arg of filter.args) {
|
|
2863
|
+
if (isKeyValuePair(arg) && arg[1]) {
|
|
2864
|
+
yield* extractValueTokenVariables(arg[1]);
|
|
2865
|
+
}
|
|
2866
|
+
else if (isValueToken(arg)) {
|
|
2867
|
+
yield* extractValueTokenVariables(arg);
|
|
2868
|
+
}
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2674
2872
|
function extractPropertyAccessVariable(token) {
|
|
2675
2873
|
const segments = [];
|
|
2676
2874
|
// token is not guaranteed to have `file` set. We'll try to get it from a prop if not.
|
|
@@ -2739,52 +2937,53 @@ function segmentsString(segments, bracketedRoot = false) {
|
|
|
2739
2937
|
return buf.join('');
|
|
2740
2938
|
}
|
|
2741
2939
|
|
|
2742
|
-
var LookupType;
|
|
2743
2940
|
(function (LookupType) {
|
|
2744
2941
|
LookupType["Partials"] = "partials";
|
|
2745
2942
|
LookupType["Layouts"] = "layouts";
|
|
2746
2943
|
LookupType["Root"] = "root";
|
|
2747
|
-
})(LookupType || (LookupType = {}));
|
|
2944
|
+
})(exports.LookupType || (exports.LookupType = {}));
|
|
2748
2945
|
class Loader {
|
|
2749
2946
|
constructor(options) {
|
|
2750
2947
|
this.options = options;
|
|
2751
2948
|
if (options.relativeReference) {
|
|
2752
2949
|
const sep = options.fs.sep;
|
|
2753
2950
|
assert(sep, '`fs.sep` is required for relative reference');
|
|
2754
|
-
const
|
|
2755
|
-
this.shouldLoadRelative = (referencedFile) =>
|
|
2951
|
+
const prefixes = ['.' + sep, '..' + sep, './', '../'];
|
|
2952
|
+
this.shouldLoadRelative = (referencedFile) => prefixes.some(prefix => referencedFile.startsWith(prefix));
|
|
2756
2953
|
}
|
|
2757
2954
|
else {
|
|
2758
2955
|
this.shouldLoadRelative = (_referencedFile) => false;
|
|
2759
2956
|
}
|
|
2760
|
-
|
|
2957
|
+
const fs = options.fs;
|
|
2958
|
+
this.contains = toLiquidAsync(fs.contains?.bind(fs) || (async () => true), fs.containsSync?.bind(fs) || (() => true));
|
|
2959
|
+
this.exists = toLiquidAsync(fs.exists?.bind(fs) || (async () => false), fs.existsSync?.bind(fs));
|
|
2761
2960
|
}
|
|
2762
2961
|
*lookup(file, type, sync, currentFile) {
|
|
2763
|
-
const { fs } = this.options;
|
|
2764
2962
|
const dirs = this.options[type];
|
|
2765
|
-
for (const filepath of this.candidates(file, dirs, currentFile
|
|
2766
|
-
|
|
2963
|
+
for (const filepath of this.candidates(file, dirs, currentFile)) {
|
|
2964
|
+
let allowed = false;
|
|
2965
|
+
for (const dir of dirs) {
|
|
2966
|
+
if (yield this.contains(!!sync, dir, filepath)) {
|
|
2967
|
+
allowed = true;
|
|
2968
|
+
break;
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
if (!allowed)
|
|
2972
|
+
continue;
|
|
2973
|
+
if (yield this.exists(!!sync, filepath))
|
|
2767
2974
|
return filepath;
|
|
2768
2975
|
}
|
|
2769
2976
|
throw this.lookupError(file, dirs);
|
|
2770
2977
|
}
|
|
2771
|
-
*candidates(file, dirs, currentFile
|
|
2978
|
+
*candidates(file, dirs, currentFile) {
|
|
2772
2979
|
const { fs, extname } = this.options;
|
|
2773
2980
|
if (this.shouldLoadRelative(file) && currentFile) {
|
|
2774
2981
|
const referenced = fs.resolve(this.dirname(currentFile), file, extname);
|
|
2775
|
-
|
|
2776
|
-
if (!enforceRoot || this.contains(dir, referenced)) {
|
|
2777
|
-
// the relatively referenced file is within one of root dirs
|
|
2778
|
-
yield referenced;
|
|
2779
|
-
break;
|
|
2780
|
-
}
|
|
2781
|
-
}
|
|
2982
|
+
yield referenced;
|
|
2782
2983
|
}
|
|
2783
2984
|
for (const dir of dirs) {
|
|
2784
2985
|
const referenced = fs.resolve(dir, file, extname);
|
|
2785
|
-
|
|
2786
|
-
yield referenced;
|
|
2787
|
-
}
|
|
2986
|
+
yield referenced;
|
|
2788
2987
|
}
|
|
2789
2988
|
if (fs.fallback !== undefined) {
|
|
2790
2989
|
const filepath = fs.fallback(file);
|
|
@@ -2813,11 +3012,12 @@ class Parser {
|
|
|
2813
3012
|
this.parseFile = this.cache ? this._parseFileCached : this._parseFile;
|
|
2814
3013
|
this.loader = new Loader(this.liquid.options);
|
|
2815
3014
|
this.parseLimit = new Limiter('parse length', liquid.options.parseLimit);
|
|
3015
|
+
this.readFile = toLiquidAsync(this.fs.readFile?.bind(this.fs) || (async () => { throw new Error('readFile not implemented'); }), this.fs.readFileSync?.bind(this.fs));
|
|
2816
3016
|
}
|
|
2817
3017
|
parse(html, filepath) {
|
|
2818
3018
|
html = String(html);
|
|
2819
3019
|
this.parseLimit.use(html.length);
|
|
2820
|
-
const tokenizer = new Tokenizer(html, this.liquid.options.operators, filepath);
|
|
3020
|
+
const tokenizer = new Tokenizer(html, this.liquid.options.operators, filepath, undefined, this.liquid.options.groupedExpressions);
|
|
2821
3021
|
const tokens = tokenizer.readTopLevelTokens(this.liquid.options);
|
|
2822
3022
|
return this.parseTokens(tokens);
|
|
2823
3023
|
}
|
|
@@ -2861,7 +3061,7 @@ class Parser {
|
|
|
2861
3061
|
parseStream(tokens) {
|
|
2862
3062
|
return new ParseStream(tokens, (token, tokens) => this.parseToken(token, tokens));
|
|
2863
3063
|
}
|
|
2864
|
-
*_parseFileCached(file, sync, type = LookupType.Root, currentFile) {
|
|
3064
|
+
*_parseFileCached(file, sync, type = exports.LookupType.Root, currentFile) {
|
|
2865
3065
|
const cache = this.cache;
|
|
2866
3066
|
const key = this.loader.shouldLoadRelative(file) ? currentFile + ',' + file : type + ':' + file;
|
|
2867
3067
|
const tpls = yield cache.read(key);
|
|
@@ -2881,9 +3081,9 @@ class Parser {
|
|
|
2881
3081
|
throw err;
|
|
2882
3082
|
}
|
|
2883
3083
|
}
|
|
2884
|
-
*_parseFile(file, sync, type = LookupType.Root, currentFile) {
|
|
3084
|
+
*_parseFile(file, sync, type = exports.LookupType.Root, currentFile) {
|
|
2885
3085
|
const filepath = yield this.loader.lookup(file, type, sync, currentFile);
|
|
2886
|
-
return this.parse(
|
|
3086
|
+
return this.parse(yield this.readFile(!!sync, filepath), filepath);
|
|
2887
3087
|
}
|
|
2888
3088
|
}
|
|
2889
3089
|
|
|
@@ -2901,6 +3101,7 @@ class Parser {
|
|
|
2901
3101
|
TokenKind[TokenKind["Quoted"] = 1024] = "Quoted";
|
|
2902
3102
|
TokenKind[TokenKind["Operator"] = 2048] = "Operator";
|
|
2903
3103
|
TokenKind[TokenKind["FilteredValue"] = 4096] = "FilteredValue";
|
|
3104
|
+
TokenKind[TokenKind["GroupedExpression"] = 8192] = "GroupedExpression";
|
|
2904
3105
|
TokenKind[TokenKind["Delimited"] = 12] = "Delimited";
|
|
2905
3106
|
})(exports.TokenKind || (exports.TokenKind = {}));
|
|
2906
3107
|
|
|
@@ -2937,9 +3138,12 @@ function isWordToken(val) {
|
|
|
2937
3138
|
function isRangeToken(val) {
|
|
2938
3139
|
return getKind(val) === exports.TokenKind.Range;
|
|
2939
3140
|
}
|
|
3141
|
+
function isFilteredValueToken(val) {
|
|
3142
|
+
return getKind(val) === exports.TokenKind.FilteredValue;
|
|
3143
|
+
}
|
|
2940
3144
|
function isValueToken(val) {
|
|
2941
|
-
// valueTokenBitMask = TokenKind.Number | TokenKind.Literal | TokenKind.Quoted | TokenKind.PropertyAccess | TokenKind.Range
|
|
2942
|
-
return (getKind(val) &
|
|
3145
|
+
// valueTokenBitMask = TokenKind.Number | TokenKind.Literal | TokenKind.Quoted | TokenKind.PropertyAccess | TokenKind.Range | TokenKind.FilteredValue
|
|
3146
|
+
return (getKind(val) & 5763) > 0;
|
|
2943
3147
|
}
|
|
2944
3148
|
function getKind(val) {
|
|
2945
3149
|
return val ? val.kind : -1;
|
|
@@ -2958,6 +3162,7 @@ var typeGuards = /*#__PURE__*/Object.freeze({
|
|
|
2958
3162
|
isPropertyAccessToken: isPropertyAccessToken,
|
|
2959
3163
|
isWordToken: isWordToken,
|
|
2960
3164
|
isRangeToken: isRangeToken,
|
|
3165
|
+
isFilteredValueToken: isFilteredValueToken,
|
|
2961
3166
|
isValueToken: isValueToken
|
|
2962
3167
|
});
|
|
2963
3168
|
|
|
@@ -2987,13 +3192,20 @@ var __assign = function() {
|
|
|
2987
3192
|
return __assign.apply(this, arguments);
|
|
2988
3193
|
};
|
|
2989
3194
|
|
|
3195
|
+
function createScope(from) {
|
|
3196
|
+
const scope = Object.create(null);
|
|
3197
|
+
if (from)
|
|
3198
|
+
Object.assign(scope, from);
|
|
3199
|
+
return scope;
|
|
3200
|
+
}
|
|
3201
|
+
|
|
2990
3202
|
class Context {
|
|
2991
|
-
constructor(env = {}, opts = defaultOptions, renderOptions = {}, { memoryLimit, renderLimit } = {}) {
|
|
3203
|
+
constructor(env = {}, opts = defaultOptions, renderOptions = {}, { memoryLimit, renderLimit, liquid } = {}) {
|
|
2992
3204
|
/**
|
|
2993
3205
|
* insert a Context-level empty scope,
|
|
2994
3206
|
* for tags like `{% capture %}` `{% assign %}` to operate
|
|
2995
3207
|
*/
|
|
2996
|
-
this.scopes = [
|
|
3208
|
+
this.scopes = [createScope()];
|
|
2997
3209
|
this.registers = {};
|
|
2998
3210
|
this.breakCalled = false;
|
|
2999
3211
|
this.continueCalled = false;
|
|
@@ -3005,9 +3217,10 @@ class Context {
|
|
|
3005
3217
|
this.ownPropertyOnly = renderOptions.ownPropertyOnly ?? opts.ownPropertyOnly;
|
|
3006
3218
|
this.memoryLimit = memoryLimit ?? new Limiter('memory alloc', renderOptions.memoryLimit ?? opts.memoryLimit);
|
|
3007
3219
|
this.renderLimit = renderLimit ?? new Limiter('template render', getPerformance().now() + (renderOptions.renderLimit ?? opts.renderLimit));
|
|
3220
|
+
this.liquid = liquid;
|
|
3008
3221
|
}
|
|
3009
|
-
getRegister(key) {
|
|
3010
|
-
return (this.registers[key] = this.registers[key] ||
|
|
3222
|
+
getRegister(key, defaultValue = undefined) {
|
|
3223
|
+
return (this.registers[key] = this.registers[key] || defaultValue);
|
|
3011
3224
|
}
|
|
3012
3225
|
setRegister(key, value) {
|
|
3013
3226
|
return (this.registers[key] = value);
|
|
@@ -3045,7 +3258,7 @@ class Context {
|
|
|
3045
3258
|
if (isString(paths))
|
|
3046
3259
|
paths = paths.split('.');
|
|
3047
3260
|
for (let i = 0; i < paths.length; i++) {
|
|
3048
|
-
scope = yield readProperty(scope, paths[i]
|
|
3261
|
+
scope = yield this.readProperty(scope, paths[i]);
|
|
3049
3262
|
if (strictVariables && isUndefined(scope)) {
|
|
3050
3263
|
throw new InternalUndefinedVariableError(paths.slice(0, i + 1).join('.'));
|
|
3051
3264
|
}
|
|
@@ -3065,10 +3278,12 @@ class Context {
|
|
|
3065
3278
|
return new Context(scope, this.opts, {
|
|
3066
3279
|
sync: this.sync,
|
|
3067
3280
|
globals: this.globals,
|
|
3068
|
-
strictVariables: this.strictVariables
|
|
3281
|
+
strictVariables: this.strictVariables,
|
|
3282
|
+
ownPropertyOnly: this.ownPropertyOnly
|
|
3069
3283
|
}, {
|
|
3070
3284
|
renderLimit: this.renderLimit,
|
|
3071
|
-
memoryLimit: this.memoryLimit
|
|
3285
|
+
memoryLimit: this.memoryLimit,
|
|
3286
|
+
liquid: this.liquid
|
|
3072
3287
|
});
|
|
3073
3288
|
}
|
|
3074
3289
|
findScope(key) {
|
|
@@ -3081,41 +3296,41 @@ class Context {
|
|
|
3081
3296
|
return this.environments;
|
|
3082
3297
|
return this.globals;
|
|
3083
3298
|
}
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
return
|
|
3103
|
-
|
|
3299
|
+
readProperty(obj, key) {
|
|
3300
|
+
obj = toLiquid(obj);
|
|
3301
|
+
key = toValue(key);
|
|
3302
|
+
if (isNil(obj))
|
|
3303
|
+
return obj;
|
|
3304
|
+
if (isArray(obj) && isNumber(key))
|
|
3305
|
+
return readArrayElement(obj, key, this.ownPropertyOnly);
|
|
3306
|
+
const value = readJSProperty(obj, key, this.ownPropertyOnly);
|
|
3307
|
+
if (value === undefined && obj instanceof Drop)
|
|
3308
|
+
return obj.liquidMethodMissing(key, this);
|
|
3309
|
+
if (isFunction(value))
|
|
3310
|
+
return value.call(obj);
|
|
3311
|
+
if (key === 'size')
|
|
3312
|
+
return readSize(obj);
|
|
3313
|
+
else if (key === 'first')
|
|
3314
|
+
return readFirst(obj, this.ownPropertyOnly);
|
|
3315
|
+
else if (key === 'last')
|
|
3316
|
+
return readLast(obj, this.ownPropertyOnly);
|
|
3317
|
+
return value;
|
|
3318
|
+
}
|
|
3104
3319
|
}
|
|
3105
3320
|
function readJSProperty(obj, key, ownPropertyOnly) {
|
|
3106
3321
|
if (ownPropertyOnly && !hasOwnProperty.call(obj, key) && !(obj instanceof Drop))
|
|
3107
3322
|
return undefined;
|
|
3108
3323
|
return obj[key];
|
|
3109
3324
|
}
|
|
3110
|
-
function readFirst(obj) {
|
|
3325
|
+
function readFirst(obj, ownPropertyOnly) {
|
|
3111
3326
|
if (isArray(obj))
|
|
3112
|
-
return obj
|
|
3113
|
-
return obj
|
|
3327
|
+
return readArrayElement(obj, 0, ownPropertyOnly);
|
|
3328
|
+
return readJSProperty(obj, 'first', ownPropertyOnly);
|
|
3114
3329
|
}
|
|
3115
|
-
function readLast(obj) {
|
|
3330
|
+
function readLast(obj, ownPropertyOnly) {
|
|
3116
3331
|
if (isArray(obj))
|
|
3117
|
-
return obj
|
|
3118
|
-
return obj
|
|
3332
|
+
return readArrayElement(obj, -1, ownPropertyOnly);
|
|
3333
|
+
return readJSProperty(obj, 'last', ownPropertyOnly);
|
|
3119
3334
|
}
|
|
3120
3335
|
function readSize(obj) {
|
|
3121
3336
|
if (hasOwnProperty.call(obj, 'size') || obj['size'] !== undefined)
|
|
@@ -3134,25 +3349,28 @@ var BlockMode;
|
|
|
3134
3349
|
BlockMode[BlockMode["STORE"] = 1] = "STORE";
|
|
3135
3350
|
})(BlockMode || (BlockMode = {}));
|
|
3136
3351
|
|
|
3137
|
-
const abs =
|
|
3138
|
-
const at_least =
|
|
3139
|
-
const at_most =
|
|
3140
|
-
const ceil =
|
|
3141
|
-
const divided_by =
|
|
3142
|
-
const floor =
|
|
3143
|
-
const minus =
|
|
3144
|
-
const
|
|
3145
|
-
const
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
return Math.round(v * amp) / amp;
|
|
3352
|
+
const abs = argumentsToNumber(Math.abs);
|
|
3353
|
+
const at_least = argumentsToNumber(Math.max);
|
|
3354
|
+
const at_most = argumentsToNumber(Math.min);
|
|
3355
|
+
const ceil = argumentsToNumber(Math.ceil);
|
|
3356
|
+
const divided_by = argumentsToNumber((dividend, divisor, integerArithmetic = false) => integerArithmetic ? Math.floor(dividend / divisor) : dividend / divisor);
|
|
3357
|
+
const floor = argumentsToNumber(Math.floor);
|
|
3358
|
+
const minus = argumentsToNumber((v, arg) => v - arg);
|
|
3359
|
+
const plus = argumentsToNumber((lhs, rhs) => lhs + rhs);
|
|
3360
|
+
const modulo = argumentsToNumber((v, arg) => ((v % arg) + arg) % arg);
|
|
3361
|
+
const times = argumentsToNumber((v, arg) => v * arg);
|
|
3362
|
+
function shiftDecimal(n, exponent) {
|
|
3363
|
+
const [mantissa, power = '0'] = String(n).split('e');
|
|
3364
|
+
return Number(`${mantissa}e${Number(power) + exponent}`);
|
|
3151
3365
|
}
|
|
3152
|
-
function
|
|
3153
|
-
v =
|
|
3154
|
-
arg =
|
|
3155
|
-
|
|
3366
|
+
function round(v, arg = 0) {
|
|
3367
|
+
v = toNumber(v);
|
|
3368
|
+
arg = toNumber(arg);
|
|
3369
|
+
const scaled = shiftDecimal(Math.abs(v), arg);
|
|
3370
|
+
if (!(scaled <= Number.MAX_SAFE_INTEGER))
|
|
3371
|
+
return v;
|
|
3372
|
+
const rounded = shiftDecimal(Math.round(scaled), -arg);
|
|
3373
|
+
return v < 0 ? -rounded : rounded;
|
|
3156
3374
|
}
|
|
3157
3375
|
|
|
3158
3376
|
var mathFilters = /*#__PURE__*/Object.freeze({
|
|
@@ -3164,13 +3382,13 @@ var mathFilters = /*#__PURE__*/Object.freeze({
|
|
|
3164
3382
|
divided_by: divided_by,
|
|
3165
3383
|
floor: floor,
|
|
3166
3384
|
minus: minus,
|
|
3385
|
+
plus: plus,
|
|
3167
3386
|
modulo: modulo,
|
|
3168
3387
|
times: times,
|
|
3169
|
-
round: round
|
|
3170
|
-
plus: plus
|
|
3388
|
+
round: round
|
|
3171
3389
|
});
|
|
3172
3390
|
|
|
3173
|
-
const url_decode = (x) => decodeURIComponent(stringify(x)
|
|
3391
|
+
const url_decode = (x) => decodeURIComponent(stringify(x).replace(/\+/g, ' '));
|
|
3174
3392
|
const url_encode = (x) => encodeURIComponent(stringify(x)).replace(/%20/g, '+');
|
|
3175
3393
|
const cgi_escape = (x) => encodeURIComponent(stringify(x))
|
|
3176
3394
|
.replace(/%20/g, '+')
|
|
@@ -3228,18 +3446,24 @@ var urlFilters = /*#__PURE__*/Object.freeze({
|
|
|
3228
3446
|
const join = argumentsToValue(function (v, arg) {
|
|
3229
3447
|
const array = toArray(v);
|
|
3230
3448
|
const sep = isNil(arg) ? ' ' : stringify(arg);
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3449
|
+
let outputSize = sep.length * Math.max(array.length - 1, 0);
|
|
3450
|
+
for (let i = 0; i < array.length; i++)
|
|
3451
|
+
outputSize += String(array[i]).length;
|
|
3452
|
+
this.context.memoryLimit.use(outputSize);
|
|
3453
|
+
return Array.prototype.join.call(array, sep);
|
|
3454
|
+
});
|
|
3455
|
+
const last = argumentsToValue(function (v) {
|
|
3456
|
+
return isArrayLike(v) ? readArrayElement(v, -1, this.context.ownPropertyOnly) : '';
|
|
3457
|
+
});
|
|
3458
|
+
const first = argumentsToValue(function (v) {
|
|
3459
|
+
return isArrayLike(v) ? readArrayElement(v, 0, this.context.ownPropertyOnly) : '';
|
|
3234
3460
|
});
|
|
3235
|
-
const last$1 = argumentsToValue((v) => isArrayLike(v) ? last(v) : '');
|
|
3236
|
-
const first = argumentsToValue((v) => isArrayLike(v) ? v[0] : '');
|
|
3237
3461
|
const reverse = argumentsToValue(function (v) {
|
|
3238
3462
|
const array = toArray(v);
|
|
3239
3463
|
this.context.memoryLimit.use(array.length);
|
|
3240
3464
|
return [...array].reverse();
|
|
3241
3465
|
});
|
|
3242
|
-
function*
|
|
3466
|
+
function* sortBy(arr, property, comparator) {
|
|
3243
3467
|
const values = [];
|
|
3244
3468
|
const array = toArray(arr);
|
|
3245
3469
|
this.context.memoryLimit.use(array.length);
|
|
@@ -3249,20 +3473,13 @@ function* sort(arr, property) {
|
|
|
3249
3473
|
property ? yield this.context._getFromScope(item, stringify(property).split('.'), false) : item
|
|
3250
3474
|
]);
|
|
3251
3475
|
}
|
|
3252
|
-
return values.sort((lhs, rhs) =>
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
const propertyString = stringify(property);
|
|
3260
|
-
const compare = property === undefined
|
|
3261
|
-
? caseInsensitiveCompare
|
|
3262
|
-
: (lhs, rhs) => caseInsensitiveCompare(lhs[propertyString], rhs[propertyString]);
|
|
3263
|
-
const array = toArray(input);
|
|
3264
|
-
this.context.memoryLimit.use(array.length);
|
|
3265
|
-
return [...array].sort(compare);
|
|
3476
|
+
return values.sort((lhs, rhs) => comparator(lhs[1], rhs[1])).map(tuple => tuple[0]);
|
|
3477
|
+
}
|
|
3478
|
+
function* sort(arr, property) {
|
|
3479
|
+
return yield* sortBy.call(this, arr, property, orderedCompare);
|
|
3480
|
+
}
|
|
3481
|
+
function* sort_natural(arr, property) {
|
|
3482
|
+
return yield* sortBy.call(this, arr, property, caseInsensitiveCompare);
|
|
3266
3483
|
}
|
|
3267
3484
|
const size = (v) => (v && v.length) || 0;
|
|
3268
3485
|
function* map(arr, property) {
|
|
@@ -3286,13 +3503,13 @@ function* sum(arr, property) {
|
|
|
3286
3503
|
function compact(arr) {
|
|
3287
3504
|
const array = toArray(arr);
|
|
3288
3505
|
this.context.memoryLimit.use(array.length);
|
|
3289
|
-
return
|
|
3506
|
+
return Array.prototype.filter.call(array, x => !isNil(toValue(x)));
|
|
3290
3507
|
}
|
|
3291
3508
|
function concat(v, arg = []) {
|
|
3292
3509
|
const lhs = toArray(v);
|
|
3293
3510
|
const rhs = toArray(arg);
|
|
3294
3511
|
this.context.memoryLimit.use(lhs.length + rhs.length);
|
|
3295
|
-
return
|
|
3512
|
+
return Array.prototype.concat.call(lhs, rhs);
|
|
3296
3513
|
}
|
|
3297
3514
|
function push(v, arg) {
|
|
3298
3515
|
return concat.call(this, v, [arg]);
|
|
@@ -3305,7 +3522,9 @@ function unshift(v, arg) {
|
|
|
3305
3522
|
return clone;
|
|
3306
3523
|
}
|
|
3307
3524
|
function pop(v) {
|
|
3308
|
-
const
|
|
3525
|
+
const array = toArray(v);
|
|
3526
|
+
this.context.memoryLimit.use(array.length);
|
|
3527
|
+
const clone = [...array];
|
|
3309
3528
|
clone.pop();
|
|
3310
3529
|
return clone;
|
|
3311
3530
|
}
|
|
@@ -3322,39 +3541,65 @@ function slice(v, begin, length = 1) {
|
|
|
3322
3541
|
return [];
|
|
3323
3542
|
if (!isArray(v))
|
|
3324
3543
|
v = stringify(v);
|
|
3325
|
-
begin = begin < 0 ? v.length + begin : begin;
|
|
3544
|
+
begin = begin < 0 ? (isArray(v) ? v.length : codePointLength(v)) + begin : begin;
|
|
3545
|
+
if (begin < 0 || length < 0)
|
|
3546
|
+
return isArray(v) ? [] : '';
|
|
3326
3547
|
this.context.memoryLimit.use(length);
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3548
|
+
if (isArray(v))
|
|
3549
|
+
return Array.prototype.slice.call(v, begin, begin + length);
|
|
3550
|
+
const start = advanceCodePoints(v, 0, begin);
|
|
3551
|
+
return v.slice(start, advanceCodePoints(v, start, length));
|
|
3552
|
+
}
|
|
3553
|
+
function* matches(value, expected) {
|
|
3554
|
+
if (this.context.opts.jekyllWhere) {
|
|
3555
|
+
if (EmptyDrop.is(expected))
|
|
3556
|
+
return yield equals(value, expected);
|
|
3557
|
+
if (isArray(value))
|
|
3558
|
+
return yield arrayIncludes(value, expected);
|
|
3559
|
+
return yield equals(value, expected);
|
|
3560
|
+
}
|
|
3561
|
+
if (expected === undefined)
|
|
3562
|
+
return isTruthy(value, this.context);
|
|
3563
|
+
return yield equals(value, expected);
|
|
3564
|
+
}
|
|
3565
|
+
function* filter(include, arr, property, expected) {
|
|
3331
3566
|
arr = toArray(arr);
|
|
3332
3567
|
this.context.memoryLimit.use(arr.length);
|
|
3333
3568
|
const token = new Tokenizer(stringify(property)).readScopeValue();
|
|
3569
|
+
const result = [];
|
|
3334
3570
|
for (const item of arr) {
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
return arr.filter((_, i) => {
|
|
3341
|
-
if (expected === undefined)
|
|
3342
|
-
return isTruthy(values[i], this.context);
|
|
3343
|
-
return matcher(values[i]);
|
|
3344
|
-
});
|
|
3571
|
+
const value = yield evalToken(token, this.context.spawn(item));
|
|
3572
|
+
if ((yield matches.call(this, value, expected)) === include)
|
|
3573
|
+
result.push(item);
|
|
3574
|
+
}
|
|
3575
|
+
return result;
|
|
3345
3576
|
}
|
|
3346
|
-
function*
|
|
3577
|
+
function* filter_exp(include, arr, itemName, exp) {
|
|
3347
3578
|
const filtered = [];
|
|
3348
3579
|
const keyTemplate = new Value(stringify(exp), this.liquid);
|
|
3349
3580
|
const array = toArray(arr);
|
|
3350
3581
|
this.context.memoryLimit.use(array.length);
|
|
3351
3582
|
for (const item of array) {
|
|
3352
|
-
|
|
3353
|
-
|
|
3583
|
+
this.context.push({ [itemName]: item });
|
|
3584
|
+
const value = yield keyTemplate.value(this.context);
|
|
3585
|
+
this.context.pop();
|
|
3586
|
+
if (value === include)
|
|
3354
3587
|
filtered.push(item);
|
|
3355
3588
|
}
|
|
3356
3589
|
return filtered;
|
|
3357
3590
|
}
|
|
3591
|
+
function* where(arr, property, expected) {
|
|
3592
|
+
return yield* filter.call(this, true, arr, property, expected);
|
|
3593
|
+
}
|
|
3594
|
+
function* reject(arr, property, expected) {
|
|
3595
|
+
return yield* filter.call(this, false, arr, property, expected);
|
|
3596
|
+
}
|
|
3597
|
+
function* where_exp(arr, itemName, exp) {
|
|
3598
|
+
return yield* filter_exp.call(this, true, arr, itemName, exp);
|
|
3599
|
+
}
|
|
3600
|
+
function* reject_exp(arr, itemName, exp) {
|
|
3601
|
+
return yield* filter_exp.call(this, false, arr, itemName, exp);
|
|
3602
|
+
}
|
|
3358
3603
|
function* group_by(arr, property) {
|
|
3359
3604
|
const map = new Map();
|
|
3360
3605
|
arr = toEnumerable(arr);
|
|
@@ -3374,31 +3619,59 @@ function* group_by_exp(arr, itemName, exp) {
|
|
|
3374
3619
|
arr = toEnumerable(arr);
|
|
3375
3620
|
this.context.memoryLimit.use(arr.length);
|
|
3376
3621
|
for (const item of arr) {
|
|
3377
|
-
|
|
3622
|
+
this.context.push({ [itemName]: item });
|
|
3623
|
+
const key = yield keyTemplate.value(this.context);
|
|
3624
|
+
this.context.pop();
|
|
3378
3625
|
if (!map.has(key))
|
|
3379
3626
|
map.set(key, []);
|
|
3380
3627
|
map.get(key).push(item);
|
|
3381
3628
|
}
|
|
3382
3629
|
return [...map.entries()].map(([name, items]) => ({ name, items }));
|
|
3383
3630
|
}
|
|
3384
|
-
function*
|
|
3631
|
+
function* search(arr, property, expected) {
|
|
3385
3632
|
const token = new Tokenizer(stringify(property)).readScopeValue();
|
|
3386
3633
|
const array = toArray(arr);
|
|
3387
|
-
for (
|
|
3388
|
-
const value = yield evalToken(token, this.context.spawn(
|
|
3389
|
-
if (
|
|
3390
|
-
return
|
|
3634
|
+
for (let index = 0; index < array.length; index++) {
|
|
3635
|
+
const value = yield evalToken(token, this.context.spawn(array[index]));
|
|
3636
|
+
if (yield matches.call(this, value, expected))
|
|
3637
|
+
return [index, array[index]];
|
|
3391
3638
|
}
|
|
3392
3639
|
}
|
|
3393
|
-
function*
|
|
3640
|
+
function* search_exp(arr, itemName, exp) {
|
|
3394
3641
|
const predicate = new Value(stringify(exp), this.liquid);
|
|
3395
3642
|
const array = toArray(arr);
|
|
3396
|
-
for (
|
|
3397
|
-
|
|
3643
|
+
for (let index = 0; index < array.length; index++) {
|
|
3644
|
+
this.context.push({ [itemName]: array[index] });
|
|
3645
|
+
const value = yield predicate.value(this.context);
|
|
3646
|
+
this.context.pop();
|
|
3398
3647
|
if (value)
|
|
3399
|
-
return
|
|
3648
|
+
return [index, array[index]];
|
|
3400
3649
|
}
|
|
3401
3650
|
}
|
|
3651
|
+
function* has(arr, property, expected) {
|
|
3652
|
+
const result = yield* search.call(this, arr, property, expected);
|
|
3653
|
+
return !!result;
|
|
3654
|
+
}
|
|
3655
|
+
function* has_exp(arr, itemName, exp) {
|
|
3656
|
+
const result = yield* search_exp.call(this, arr, itemName, exp);
|
|
3657
|
+
return !!result;
|
|
3658
|
+
}
|
|
3659
|
+
function* find_index(arr, property, expected) {
|
|
3660
|
+
const result = yield* search.call(this, arr, property, expected);
|
|
3661
|
+
return result ? result[0] : undefined;
|
|
3662
|
+
}
|
|
3663
|
+
function* find_index_exp(arr, itemName, exp) {
|
|
3664
|
+
const result = yield* search_exp.call(this, arr, itemName, exp);
|
|
3665
|
+
return result ? result[0] : undefined;
|
|
3666
|
+
}
|
|
3667
|
+
function* find(arr, property, expected) {
|
|
3668
|
+
const result = yield* search.call(this, arr, property, expected);
|
|
3669
|
+
return result ? result[1] : undefined;
|
|
3670
|
+
}
|
|
3671
|
+
function* find_exp(arr, itemName, exp) {
|
|
3672
|
+
const result = yield* search_exp.call(this, arr, itemName, exp);
|
|
3673
|
+
return result ? result[1] : undefined;
|
|
3674
|
+
}
|
|
3402
3675
|
function uniq(arr) {
|
|
3403
3676
|
arr = toArray(arr);
|
|
3404
3677
|
this.context.memoryLimit.use(arr.length);
|
|
@@ -3410,7 +3683,7 @@ function sample(v, count = 1) {
|
|
|
3410
3683
|
return [];
|
|
3411
3684
|
if (!isArray(v))
|
|
3412
3685
|
v = stringify(v);
|
|
3413
|
-
this.context.memoryLimit.use(
|
|
3686
|
+
this.context.memoryLimit.use(v.length);
|
|
3414
3687
|
const shuffled = [...v].sort(() => Math.random() - 0.5);
|
|
3415
3688
|
if (count === 1)
|
|
3416
3689
|
return shuffled[0];
|
|
@@ -3420,7 +3693,7 @@ function sample(v, count = 1) {
|
|
|
3420
3693
|
var arrayFilters = /*#__PURE__*/Object.freeze({
|
|
3421
3694
|
__proto__: null,
|
|
3422
3695
|
join: join,
|
|
3423
|
-
last: last
|
|
3696
|
+
last: last,
|
|
3424
3697
|
first: first,
|
|
3425
3698
|
reverse: reverse,
|
|
3426
3699
|
sort: sort,
|
|
@@ -3436,9 +3709,15 @@ var arrayFilters = /*#__PURE__*/Object.freeze({
|
|
|
3436
3709
|
shift: shift,
|
|
3437
3710
|
slice: slice,
|
|
3438
3711
|
where: where,
|
|
3712
|
+
reject: reject,
|
|
3439
3713
|
where_exp: where_exp,
|
|
3714
|
+
reject_exp: reject_exp,
|
|
3440
3715
|
group_by: group_by,
|
|
3441
3716
|
group_by_exp: group_by_exp,
|
|
3717
|
+
has: has,
|
|
3718
|
+
has_exp: has_exp,
|
|
3719
|
+
find_index: find_index,
|
|
3720
|
+
find_index_exp: find_index_exp,
|
|
3442
3721
|
find: find,
|
|
3443
3722
|
find_exp: find_exp,
|
|
3444
3723
|
uniq: uniq,
|
|
@@ -3446,20 +3725,24 @@ var arrayFilters = /*#__PURE__*/Object.freeze({
|
|
|
3446
3725
|
});
|
|
3447
3726
|
|
|
3448
3727
|
function date(v, format, timezoneOffset) {
|
|
3449
|
-
const size = (v?.length ?? 0) + (
|
|
3728
|
+
const size = (v?.length ?? 0) + (timezoneOffset?.length ?? 0);
|
|
3450
3729
|
this.context.memoryLimit.use(size);
|
|
3451
3730
|
const date = parseDate(v, this.context.opts, timezoneOffset);
|
|
3452
3731
|
if (!date)
|
|
3453
3732
|
return v;
|
|
3454
3733
|
format = toValue(format);
|
|
3455
3734
|
format = isNil(format) ? this.context.opts.dateFormat : stringify(format);
|
|
3456
|
-
|
|
3735
|
+
this.context.memoryLimit.use(format.length);
|
|
3736
|
+
return strftime(date, format, this.context.memoryLimit);
|
|
3457
3737
|
}
|
|
3458
3738
|
function date_to_xmlschema(v) {
|
|
3459
3739
|
return date.call(this, v, '%Y-%m-%dT%H:%M:%S%:z');
|
|
3460
3740
|
}
|
|
3461
3741
|
function date_to_rfc822(v) {
|
|
3462
|
-
|
|
3742
|
+
const date = parseDate(v, this.context.opts, undefined, 'en-US');
|
|
3743
|
+
if (!date)
|
|
3744
|
+
return v;
|
|
3745
|
+
return strftime(date, '%a, %d %b %Y %H:%M:%S %z', this.context.memoryLimit);
|
|
3463
3746
|
}
|
|
3464
3747
|
function date_to_string(v, type, style) {
|
|
3465
3748
|
return stringify_date.call(this, v, '%b', type, style);
|
|
@@ -3471,20 +3754,23 @@ function stringify_date(v, month_type, type, style) {
|
|
|
3471
3754
|
const date = parseDate(v, this.context.opts);
|
|
3472
3755
|
if (!date)
|
|
3473
3756
|
return v;
|
|
3757
|
+
const ml = this.context.memoryLimit;
|
|
3474
3758
|
if (type === 'ordinal') {
|
|
3475
3759
|
const d = date.getDate();
|
|
3476
3760
|
return style === 'US'
|
|
3477
|
-
? strftime(date, `${month_type} ${d}%q, %Y
|
|
3478
|
-
: strftime(date, `${d}%q ${month_type} %Y
|
|
3761
|
+
? strftime(date, `${month_type} ${d}%q, %Y`, ml)
|
|
3762
|
+
: strftime(date, `${d}%q ${month_type} %Y`, ml);
|
|
3479
3763
|
}
|
|
3480
|
-
return strftime(date, `%d ${month_type} %Y
|
|
3764
|
+
return strftime(date, `%d ${month_type} %Y`, ml);
|
|
3481
3765
|
}
|
|
3482
|
-
function parseDate(v, opts, timezoneOffset) {
|
|
3766
|
+
function parseDate(v, opts, timezoneOffset, locale = opts.locale) {
|
|
3483
3767
|
let date;
|
|
3484
3768
|
const defaultTimezoneOffset = timezoneOffset ?? opts.timezoneOffset;
|
|
3485
|
-
const locale = opts.locale;
|
|
3486
3769
|
v = toValue(v);
|
|
3487
|
-
if (v
|
|
3770
|
+
if (isNil(v)) {
|
|
3771
|
+
return undefined;
|
|
3772
|
+
}
|
|
3773
|
+
else if (v === 'now' || v === 'today') {
|
|
3488
3774
|
date = new LiquidDate(Date.now(), locale, defaultTimezoneOffset);
|
|
3489
3775
|
}
|
|
3490
3776
|
else if (isNumber(v)) {
|
|
@@ -3538,14 +3824,34 @@ function prepend(v, arg) {
|
|
|
3538
3824
|
this.context.memoryLimit.use(lhs.length + rhs.length);
|
|
3539
3825
|
return rhs + lhs;
|
|
3540
3826
|
}
|
|
3827
|
+
function getTrimStart(str, chars) {
|
|
3828
|
+
let start = 0;
|
|
3829
|
+
for (const char of str) {
|
|
3830
|
+
if (!chars.has(char))
|
|
3831
|
+
break;
|
|
3832
|
+
start += char.length;
|
|
3833
|
+
}
|
|
3834
|
+
return start;
|
|
3835
|
+
}
|
|
3836
|
+
function getTrimEnd(str, chars, start = 0) {
|
|
3837
|
+
let end = str.length;
|
|
3838
|
+
while (end > start) {
|
|
3839
|
+
const size = end - start >= 2 && str.codePointAt(end - 2) > 0xFFFF ? 2 : 1;
|
|
3840
|
+
if (!chars.has(str.slice(end - size, end)))
|
|
3841
|
+
break;
|
|
3842
|
+
end -= size;
|
|
3843
|
+
}
|
|
3844
|
+
return end;
|
|
3845
|
+
}
|
|
3541
3846
|
function lstrip(v, chars) {
|
|
3542
3847
|
const str = stringify(v);
|
|
3543
3848
|
this.context.memoryLimit.use(str.length);
|
|
3544
3849
|
if (chars) {
|
|
3545
|
-
chars =
|
|
3546
|
-
|
|
3850
|
+
chars = stringify(chars);
|
|
3851
|
+
this.context.memoryLimit.use(chars.length);
|
|
3852
|
+
return str.slice(getTrimStart(str, new Set(chars)));
|
|
3547
3853
|
}
|
|
3548
|
-
return str.
|
|
3854
|
+
return str.trimStart();
|
|
3549
3855
|
}
|
|
3550
3856
|
function downcase(v) {
|
|
3551
3857
|
const str = stringify(v);
|
|
@@ -3559,18 +3865,20 @@ function upcase(v) {
|
|
|
3559
3865
|
}
|
|
3560
3866
|
function remove(v, arg) {
|
|
3561
3867
|
const str = stringify(v);
|
|
3562
|
-
|
|
3563
|
-
|
|
3868
|
+
arg = stringify(arg);
|
|
3869
|
+
this.context.memoryLimit.use(str.length + arg.length);
|
|
3870
|
+
return str.split(arg).join('');
|
|
3564
3871
|
}
|
|
3565
3872
|
function remove_first(v, l) {
|
|
3566
3873
|
const str = stringify(v);
|
|
3567
|
-
|
|
3568
|
-
|
|
3874
|
+
l = stringify(l);
|
|
3875
|
+
this.context.memoryLimit.use(str.length + l.length);
|
|
3876
|
+
return str.replace(l, '');
|
|
3569
3877
|
}
|
|
3570
3878
|
function remove_last(v, l) {
|
|
3571
3879
|
const str = stringify(v);
|
|
3572
|
-
this.context.memoryLimit.use(str.length);
|
|
3573
3880
|
const pattern = stringify(l);
|
|
3881
|
+
this.context.memoryLimit.use(str.length + pattern.length);
|
|
3574
3882
|
const index = str.lastIndexOf(pattern);
|
|
3575
3883
|
if (index === -1)
|
|
3576
3884
|
return str;
|
|
@@ -3580,10 +3888,11 @@ function rstrip(str, chars) {
|
|
|
3580
3888
|
str = stringify(str);
|
|
3581
3889
|
this.context.memoryLimit.use(str.length);
|
|
3582
3890
|
if (chars) {
|
|
3583
|
-
chars =
|
|
3584
|
-
|
|
3891
|
+
chars = stringify(chars);
|
|
3892
|
+
this.context.memoryLimit.use(chars.length);
|
|
3893
|
+
return str.slice(0, getTrimEnd(str, new Set(chars)));
|
|
3585
3894
|
}
|
|
3586
|
-
return str.
|
|
3895
|
+
return str.trimEnd();
|
|
3587
3896
|
}
|
|
3588
3897
|
function split(v, arg) {
|
|
3589
3898
|
const str = stringify(v);
|
|
@@ -3599,10 +3908,10 @@ function strip(v, chars) {
|
|
|
3599
3908
|
const str = stringify(v);
|
|
3600
3909
|
this.context.memoryLimit.use(str.length);
|
|
3601
3910
|
if (chars) {
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3911
|
+
const set = new Set(stringify(chars));
|
|
3912
|
+
this.context.memoryLimit.use(set.size);
|
|
3913
|
+
const start = getTrimStart(str, set);
|
|
3914
|
+
return str.slice(start, getTrimEnd(str, set, start));
|
|
3606
3915
|
}
|
|
3607
3916
|
return str.trim();
|
|
3608
3917
|
}
|
|
@@ -3611,6 +3920,11 @@ function strip_newlines(v) {
|
|
|
3611
3920
|
this.context.memoryLimit.use(str.length);
|
|
3612
3921
|
return str.replace(/\r?\n/gm, '');
|
|
3613
3922
|
}
|
|
3923
|
+
function squish(v) {
|
|
3924
|
+
const str = stringify(v);
|
|
3925
|
+
this.context.memoryLimit.use(str.length);
|
|
3926
|
+
return str.replace(/\s+/g, ' ').trim();
|
|
3927
|
+
}
|
|
3614
3928
|
function capitalize(str) {
|
|
3615
3929
|
str = stringify(str);
|
|
3616
3930
|
this.context.memoryLimit.use(str.length);
|
|
@@ -3618,35 +3932,43 @@ function capitalize(str) {
|
|
|
3618
3932
|
}
|
|
3619
3933
|
function replace(v, pattern, replacement) {
|
|
3620
3934
|
const str = stringify(v);
|
|
3621
|
-
|
|
3622
|
-
|
|
3935
|
+
pattern = stringify(pattern);
|
|
3936
|
+
replacement = stringify(replacement);
|
|
3937
|
+
const parts = str.split(pattern);
|
|
3938
|
+
const outputSize = str.length + (parts.length - 1) * (replacement.length - pattern.length);
|
|
3939
|
+
this.context.memoryLimit.use(outputSize);
|
|
3940
|
+
return parts.join(replacement);
|
|
3623
3941
|
}
|
|
3624
3942
|
function replace_first(v, arg1, arg2) {
|
|
3625
3943
|
const str = stringify(v);
|
|
3626
|
-
|
|
3627
|
-
|
|
3944
|
+
arg1 = stringify(arg1);
|
|
3945
|
+
arg2 = stringify(arg2);
|
|
3946
|
+
this.context.memoryLimit.use(str.length + arg1.length + arg2.length);
|
|
3947
|
+
return str.replace(arg1, () => arg2);
|
|
3628
3948
|
}
|
|
3629
3949
|
function replace_last(v, arg1, arg2) {
|
|
3630
3950
|
const str = stringify(v);
|
|
3631
|
-
this.context.memoryLimit.use(str.length);
|
|
3632
3951
|
const pattern = stringify(arg1);
|
|
3952
|
+
const replacement = stringify(arg2);
|
|
3953
|
+
this.context.memoryLimit.use(str.length + pattern.length + replacement.length);
|
|
3633
3954
|
const index = str.lastIndexOf(pattern);
|
|
3634
3955
|
if (index === -1)
|
|
3635
3956
|
return str;
|
|
3636
|
-
const replacement = stringify(arg2);
|
|
3637
3957
|
return str.substring(0, index) + replacement + str.substring(index + pattern.length);
|
|
3638
3958
|
}
|
|
3639
3959
|
function truncate(v, l = 50, o = '...') {
|
|
3640
3960
|
const str = stringify(v);
|
|
3641
|
-
|
|
3642
|
-
|
|
3961
|
+
o = stringify(o);
|
|
3962
|
+
this.context.memoryLimit.use(str.length + o.length);
|
|
3963
|
+
if (str.length <= l || advanceCodePoints(str, 0, l) >= str.length)
|
|
3643
3964
|
return v;
|
|
3644
|
-
return str.
|
|
3965
|
+
return str.slice(0, advanceCodePoints(str, 0, Math.max(l - codePointLength(o), 0))) + o;
|
|
3645
3966
|
}
|
|
3646
3967
|
function truncatewords(v, words = 15, o = '...') {
|
|
3647
3968
|
const str = stringify(v);
|
|
3648
|
-
|
|
3649
|
-
|
|
3969
|
+
o = stringify(o);
|
|
3970
|
+
this.context.memoryLimit.use(str.length + o.length);
|
|
3971
|
+
const arr = str.trimStart().split(/\s+/);
|
|
3650
3972
|
if (words <= 0)
|
|
3651
3973
|
words = 1;
|
|
3652
3974
|
let ret = arr.slice(0, words).join(' ');
|
|
@@ -3679,8 +4001,13 @@ function number_of_words(input, mode) {
|
|
|
3679
4001
|
return input.split(/\s+/).length;
|
|
3680
4002
|
}
|
|
3681
4003
|
}
|
|
3682
|
-
function array_to_sentence_string(
|
|
3683
|
-
|
|
4004
|
+
function array_to_sentence_string(input, connector = 'and') {
|
|
4005
|
+
const array = toArray(input);
|
|
4006
|
+
connector = stringify(connector);
|
|
4007
|
+
let outputSize = connector.length + array.length * 2;
|
|
4008
|
+
for (let i = 0; i < array.length; i++)
|
|
4009
|
+
outputSize += stringify(array[i]).length;
|
|
4010
|
+
this.context.memoryLimit.use(outputSize);
|
|
3684
4011
|
switch (array.length) {
|
|
3685
4012
|
case 0:
|
|
3686
4013
|
return '';
|
|
@@ -3707,6 +4034,7 @@ var stringFilters = /*#__PURE__*/Object.freeze({
|
|
|
3707
4034
|
split: split,
|
|
3708
4035
|
strip: strip,
|
|
3709
4036
|
strip_newlines: strip_newlines,
|
|
4037
|
+
squish: squish,
|
|
3710
4038
|
capitalize: capitalize,
|
|
3711
4039
|
replace: replace,
|
|
3712
4040
|
replace_first: replace_first,
|
|
@@ -3718,6 +4046,76 @@ var stringFilters = /*#__PURE__*/Object.freeze({
|
|
|
3718
4046
|
array_to_sentence_string: array_to_sentence_string
|
|
3719
4047
|
});
|
|
3720
4048
|
|
|
4049
|
+
function base64Encode(str) {
|
|
4050
|
+
return Buffer.from(str, 'utf8').toString('base64');
|
|
4051
|
+
}
|
|
4052
|
+
function base64Decode(str) {
|
|
4053
|
+
return Buffer.from(str, 'base64').toString('utf8');
|
|
4054
|
+
}
|
|
4055
|
+
|
|
4056
|
+
/**
|
|
4057
|
+
* Base64 related filters
|
|
4058
|
+
*
|
|
4059
|
+
* Implements base64_encode and base64_decode filters for Shopify compatibility
|
|
4060
|
+
*/
|
|
4061
|
+
function base64_encode(value) {
|
|
4062
|
+
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
|
|
4063
|
+
this.context.memoryLimit.use(value.byteLength);
|
|
4064
|
+
return value.toString('base64');
|
|
4065
|
+
}
|
|
4066
|
+
const str = stringify(value);
|
|
4067
|
+
this.context.memoryLimit.use(str.length);
|
|
4068
|
+
return base64Encode(str);
|
|
4069
|
+
}
|
|
4070
|
+
function base64_decode(value) {
|
|
4071
|
+
const str = stringify(value);
|
|
4072
|
+
this.context.memoryLimit.use(str.length);
|
|
4073
|
+
assert(isBase64(str), 'invalid base64 provided to base64_decode');
|
|
4074
|
+
return base64Decode(str);
|
|
4075
|
+
}
|
|
4076
|
+
function isBase64(str) {
|
|
4077
|
+
str = str.replace(/[\t\n\f\r ]/g, '');
|
|
4078
|
+
if (str.length % 4 === 0)
|
|
4079
|
+
str = str.replace(/==?$/, '');
|
|
4080
|
+
return str.length % 4 !== 1 && /^[A-Za-z0-9+/]*$/.test(str);
|
|
4081
|
+
}
|
|
4082
|
+
|
|
4083
|
+
var base64Filters = /*#__PURE__*/Object.freeze({
|
|
4084
|
+
__proto__: null,
|
|
4085
|
+
base64_encode: base64_encode,
|
|
4086
|
+
base64_decode: base64_decode
|
|
4087
|
+
});
|
|
4088
|
+
|
|
4089
|
+
function sha256(str) {
|
|
4090
|
+
return crypto.createHash('sha256').update(str, 'utf8').digest('hex');
|
|
4091
|
+
}
|
|
4092
|
+
function hmacSha256(str, key) {
|
|
4093
|
+
return crypto.createHmac('sha256', key).update(str, 'utf8').digest('hex');
|
|
4094
|
+
}
|
|
4095
|
+
|
|
4096
|
+
/**
|
|
4097
|
+
* Crypto related filters
|
|
4098
|
+
*
|
|
4099
|
+
* Implements sha256 and hmac_sha256 filters for Shopify compatibility
|
|
4100
|
+
*/
|
|
4101
|
+
function sha256$1(value) {
|
|
4102
|
+
const str = stringify(value);
|
|
4103
|
+
this.context.memoryLimit.use(str.length);
|
|
4104
|
+
return sha256(str);
|
|
4105
|
+
}
|
|
4106
|
+
function hmac_sha256(value, key) {
|
|
4107
|
+
const str = stringify(value);
|
|
4108
|
+
const keyStr = stringify(key);
|
|
4109
|
+
this.context.memoryLimit.use(str.length + keyStr.length);
|
|
4110
|
+
return hmacSha256(str, keyStr);
|
|
4111
|
+
}
|
|
4112
|
+
|
|
4113
|
+
var cryptoFilters = /*#__PURE__*/Object.freeze({
|
|
4114
|
+
__proto__: null,
|
|
4115
|
+
sha256: sha256$1,
|
|
4116
|
+
hmac_sha256: hmac_sha256
|
|
4117
|
+
});
|
|
4118
|
+
|
|
3721
4119
|
const filters = {
|
|
3722
4120
|
...htmlFilters,
|
|
3723
4121
|
...mathFilters,
|
|
@@ -3725,6 +4123,8 @@ const filters = {
|
|
|
3725
4123
|
...arrayFilters,
|
|
3726
4124
|
...dateFilters,
|
|
3727
4125
|
...stringFilters,
|
|
4126
|
+
...base64Filters,
|
|
4127
|
+
...cryptoFilters,
|
|
3728
4128
|
...misc
|
|
3729
4129
|
};
|
|
3730
4130
|
|
|
@@ -3782,8 +4182,8 @@ class ForTag extends Tag {
|
|
|
3782
4182
|
return;
|
|
3783
4183
|
}
|
|
3784
4184
|
const continueKey = 'continue-' + this.variable + '-' + this.collection.getText();
|
|
3785
|
-
ctx.push({ continue: ctx.getRegister(continueKey) });
|
|
3786
|
-
const hash = yield this.hash.render(ctx);
|
|
4185
|
+
ctx.push(createScope({ continue: ctx.getRegister(continueKey, {}) }));
|
|
4186
|
+
const hash = (yield this.hash.render(ctx));
|
|
3787
4187
|
ctx.pop();
|
|
3788
4188
|
const modifiers = this.liquid.options.orderedFilterParameters
|
|
3789
4189
|
? Object.keys(hash).filter(x => MODIFIERS.includes(x))
|
|
@@ -3796,7 +4196,7 @@ class ForTag extends Tag {
|
|
|
3796
4196
|
return reversed(collection);
|
|
3797
4197
|
}, collection);
|
|
3798
4198
|
ctx.setRegister(continueKey, (hash['offset'] || 0) + collection.length);
|
|
3799
|
-
const scope = { forloop: new ForloopDrop(collection.length, this.collection.getText(), this.variable) };
|
|
4199
|
+
const scope = createScope({ forloop: new ForloopDrop(collection.length, this.collection.getText(), this.variable) });
|
|
3800
4200
|
ctx.push(scope);
|
|
3801
4201
|
for (const item of collection) {
|
|
3802
4202
|
scope[this.variable] = item;
|
|
@@ -3922,12 +4322,12 @@ class CaseTag extends Tag {
|
|
|
3922
4322
|
}
|
|
3923
4323
|
*render(ctx, emitter) {
|
|
3924
4324
|
const r = this.liquid.renderer;
|
|
3925
|
-
const target = toValue(yield this.value.value(ctx, ctx.opts.lenientIf));
|
|
4325
|
+
const target = yield toValue(yield this.value.value(ctx, ctx.opts.lenientIf));
|
|
3926
4326
|
let branchHit = false;
|
|
3927
4327
|
for (const branch of this.branches) {
|
|
3928
4328
|
for (const valueToken of branch.values) {
|
|
3929
4329
|
const value = yield evalToken(valueToken, ctx, ctx.opts.lenientIf);
|
|
3930
|
-
if (equals(target, value)) {
|
|
4330
|
+
if (yield equals(target, value)) {
|
|
3931
4331
|
yield r.renderTemplates(branch.templates, ctx, emitter);
|
|
3932
4332
|
branchHit = true;
|
|
3933
4333
|
break;
|
|
@@ -3988,7 +4388,11 @@ class RenderTag extends Tag {
|
|
|
3988
4388
|
alias = tokenizer.readIdentifier();
|
|
3989
4389
|
else
|
|
3990
4390
|
tokenizer.p = beforeAs;
|
|
3991
|
-
|
|
4391
|
+
const binding = { value, alias: alias && alias.content };
|
|
4392
|
+
if (keyword.content === 'with')
|
|
4393
|
+
this.with = binding;
|
|
4394
|
+
else
|
|
4395
|
+
this.forBinding = binding;
|
|
3992
4396
|
tokenizer.skipBlank();
|
|
3993
4397
|
if (tokenizer.peek() === ',')
|
|
3994
4398
|
tokenizer.advance();
|
|
@@ -4006,42 +4410,42 @@ class RenderTag extends Tag {
|
|
|
4006
4410
|
}
|
|
4007
4411
|
*render(ctx, emitter) {
|
|
4008
4412
|
const { liquid, hash } = this;
|
|
4009
|
-
const filepath = (yield renderFilePath(this
|
|
4413
|
+
const filepath = (yield renderFilePath(this.file, ctx, liquid));
|
|
4010
4414
|
assert(filepath, () => `illegal file path "${filepath}"`);
|
|
4011
4415
|
const childCtx = ctx.spawn();
|
|
4012
4416
|
const scope = childCtx.bottom();
|
|
4013
4417
|
__assign(scope, yield hash.render(ctx));
|
|
4014
|
-
if (this
|
|
4015
|
-
const { value, alias } = this
|
|
4418
|
+
if (this.with) {
|
|
4419
|
+
const { value, alias } = this.with;
|
|
4016
4420
|
scope[alias || filepath] = yield evalToken(value, ctx);
|
|
4017
4421
|
}
|
|
4018
|
-
if (this
|
|
4019
|
-
const { value, alias } = this
|
|
4422
|
+
if (this.forBinding) {
|
|
4423
|
+
const { value, alias } = this.forBinding;
|
|
4020
4424
|
const collection = toEnumerable(yield evalToken(value, ctx));
|
|
4021
4425
|
scope['forloop'] = new ForloopDrop(collection.length, value.getText(), alias);
|
|
4022
4426
|
for (const item of collection) {
|
|
4023
4427
|
scope[alias] = item;
|
|
4024
|
-
const templates = (yield liquid._parsePartialFile(filepath, childCtx.sync, this
|
|
4428
|
+
const templates = (yield liquid._parsePartialFile(filepath, childCtx.sync, this.currentFile));
|
|
4025
4429
|
yield liquid.renderer.renderTemplates(templates, childCtx, emitter);
|
|
4026
4430
|
scope['forloop'].next();
|
|
4027
4431
|
}
|
|
4028
4432
|
}
|
|
4029
4433
|
else {
|
|
4030
|
-
const templates = (yield liquid._parsePartialFile(filepath, childCtx.sync, this
|
|
4434
|
+
const templates = (yield liquid._parsePartialFile(filepath, childCtx.sync, this.currentFile));
|
|
4031
4435
|
yield liquid.renderer.renderTemplates(templates, childCtx, emitter);
|
|
4032
4436
|
}
|
|
4033
4437
|
}
|
|
4034
4438
|
*children(partials, sync) {
|
|
4035
|
-
if (partials && isString(this
|
|
4036
|
-
return (yield this.liquid._parsePartialFile(this
|
|
4439
|
+
if (partials && isString(this.file)) {
|
|
4440
|
+
return (yield this.liquid._parsePartialFile(this.file, sync, this.currentFile));
|
|
4037
4441
|
}
|
|
4038
4442
|
return [];
|
|
4039
4443
|
}
|
|
4040
4444
|
partialScope() {
|
|
4041
|
-
if (isString(this
|
|
4445
|
+
if (isString(this.file)) {
|
|
4042
4446
|
const names = Object.keys(this.hash.hash);
|
|
4043
|
-
if (this
|
|
4044
|
-
const { value, alias } = this
|
|
4447
|
+
if (this.with) {
|
|
4448
|
+
const { value, alias } = this.with;
|
|
4045
4449
|
if (isString(alias)) {
|
|
4046
4450
|
names.push([alias, value]);
|
|
4047
4451
|
}
|
|
@@ -4049,8 +4453,8 @@ class RenderTag extends Tag {
|
|
|
4049
4453
|
names.push([this.file, value]);
|
|
4050
4454
|
}
|
|
4051
4455
|
}
|
|
4052
|
-
if (this
|
|
4053
|
-
const { value, alias } = this
|
|
4456
|
+
if (this.forBinding) {
|
|
4457
|
+
const { value, alias } = this.forBinding;
|
|
4054
4458
|
if (isString(alias)) {
|
|
4055
4459
|
names.push([alias, value]);
|
|
4056
4460
|
}
|
|
@@ -4058,7 +4462,7 @@ class RenderTag extends Tag {
|
|
|
4058
4462
|
names.push([this.file, value]);
|
|
4059
4463
|
}
|
|
4060
4464
|
}
|
|
4061
|
-
return { name: this
|
|
4465
|
+
return { name: this.file, isolated: true, scope: names };
|
|
4062
4466
|
}
|
|
4063
4467
|
}
|
|
4064
4468
|
*arguments() {
|
|
@@ -4067,14 +4471,14 @@ class RenderTag extends Tag {
|
|
|
4067
4471
|
yield v;
|
|
4068
4472
|
}
|
|
4069
4473
|
}
|
|
4070
|
-
if (this
|
|
4071
|
-
const { value } = this
|
|
4474
|
+
if (this.with) {
|
|
4475
|
+
const { value } = this.with;
|
|
4072
4476
|
if (isValueToken(value)) {
|
|
4073
4477
|
yield value;
|
|
4074
4478
|
}
|
|
4075
4479
|
}
|
|
4076
|
-
if (this
|
|
4077
|
-
const { value } = this
|
|
4480
|
+
if (this.forBinding) {
|
|
4481
|
+
const { value } = this.forBinding;
|
|
4078
4482
|
if (isValueToken(value)) {
|
|
4079
4483
|
yield value;
|
|
4080
4484
|
}
|
|
@@ -4122,8 +4526,8 @@ class IncludeTag extends Tag {
|
|
|
4122
4526
|
constructor(token, remainTokens, liquid, parser) {
|
|
4123
4527
|
super(token, remainTokens, liquid);
|
|
4124
4528
|
const { tokenizer } = token;
|
|
4125
|
-
this
|
|
4126
|
-
this
|
|
4529
|
+
this.file = parseFilePath(tokenizer, this.liquid, parser);
|
|
4530
|
+
this.currentFile = token.file;
|
|
4127
4531
|
const begin = tokenizer.p;
|
|
4128
4532
|
const withStr = tokenizer.readIdentifier();
|
|
4129
4533
|
if (withStr.content === 'with') {
|
|
@@ -4141,28 +4545,28 @@ class IncludeTag extends Tag {
|
|
|
4141
4545
|
*render(ctx, emitter) {
|
|
4142
4546
|
const { liquid, hash, withVar } = this;
|
|
4143
4547
|
const { renderer } = liquid;
|
|
4144
|
-
const filepath = (yield renderFilePath(this
|
|
4548
|
+
const filepath = (yield renderFilePath(this.file, ctx, liquid));
|
|
4145
4549
|
assert(filepath, () => `illegal file path "${filepath}"`);
|
|
4146
4550
|
const saved = ctx.saveRegister('blocks', 'blockMode');
|
|
4147
4551
|
ctx.setRegister('blocks', {});
|
|
4148
4552
|
ctx.setRegister('blockMode', BlockMode.OUTPUT);
|
|
4149
|
-
const scope = (yield hash.render(ctx));
|
|
4553
|
+
const scope = createScope((yield hash.render(ctx)));
|
|
4150
4554
|
if (withVar)
|
|
4151
4555
|
scope[filepath] = yield evalToken(withVar, ctx);
|
|
4152
|
-
const templates = (yield liquid._parsePartialFile(filepath, ctx.sync, this
|
|
4153
|
-
ctx.push(ctx.opts.jekyllInclude ? { include: scope } : scope);
|
|
4556
|
+
const templates = (yield liquid._parsePartialFile(filepath, ctx.sync, this.currentFile));
|
|
4557
|
+
ctx.push(ctx.opts.jekyllInclude ? createScope({ include: scope }) : scope);
|
|
4154
4558
|
yield renderer.renderTemplates(templates, ctx, emitter);
|
|
4155
4559
|
ctx.pop();
|
|
4156
4560
|
ctx.restoreRegister(saved);
|
|
4157
4561
|
}
|
|
4158
4562
|
*children(partials, sync) {
|
|
4159
|
-
if (partials && isString(this
|
|
4160
|
-
return (yield this.liquid._parsePartialFile(this
|
|
4563
|
+
if (partials && isString(this.file)) {
|
|
4564
|
+
return (yield this.liquid._parsePartialFile(this.file, sync, this.currentFile));
|
|
4161
4565
|
}
|
|
4162
4566
|
return [];
|
|
4163
4567
|
}
|
|
4164
4568
|
partialScope() {
|
|
4165
|
-
if (isString(this
|
|
4569
|
+
if (isString(this.file)) {
|
|
4166
4570
|
let names;
|
|
4167
4571
|
if (this.liquid.options.jekyllInclude) {
|
|
4168
4572
|
names = ['include'];
|
|
@@ -4170,16 +4574,16 @@ class IncludeTag extends Tag {
|
|
|
4170
4574
|
else {
|
|
4171
4575
|
names = Object.keys(this.hash.hash);
|
|
4172
4576
|
if (this.withVar) {
|
|
4173
|
-
names.push([this
|
|
4577
|
+
names.push([this.file, this.withVar]);
|
|
4174
4578
|
}
|
|
4175
4579
|
}
|
|
4176
|
-
return { name: this
|
|
4580
|
+
return { name: this.file, isolated: false, scope: names };
|
|
4177
4581
|
}
|
|
4178
4582
|
}
|
|
4179
4583
|
*arguments() {
|
|
4180
4584
|
yield* Object.values(this.hash.hash).filter(isValueToken);
|
|
4181
|
-
if (isValueToken(this
|
|
4182
|
-
yield this
|
|
4585
|
+
if (isValueToken(this.file)) {
|
|
4586
|
+
yield this.file;
|
|
4183
4587
|
}
|
|
4184
4588
|
if (isValueToken(this.withVar)) {
|
|
4185
4589
|
yield this.withVar;
|
|
@@ -4209,6 +4613,7 @@ class CycleTag extends Tag {
|
|
|
4209
4613
|
constructor(token, remainTokens, liquid) {
|
|
4210
4614
|
super(token, remainTokens, liquid);
|
|
4211
4615
|
this.candidates = [];
|
|
4616
|
+
const begin = this.tokenizer.p;
|
|
4212
4617
|
const group = this.tokenizer.readValue();
|
|
4213
4618
|
this.tokenizer.skipBlank();
|
|
4214
4619
|
if (group) {
|
|
@@ -4217,28 +4622,32 @@ class CycleTag extends Tag {
|
|
|
4217
4622
|
this.tokenizer.advance();
|
|
4218
4623
|
}
|
|
4219
4624
|
else
|
|
4220
|
-
this.
|
|
4625
|
+
this.tokenizer.p = begin;
|
|
4221
4626
|
}
|
|
4222
4627
|
while (!this.tokenizer.end()) {
|
|
4223
|
-
const value = this.tokenizer.
|
|
4224
|
-
|
|
4225
|
-
this.candidates.push(value);
|
|
4628
|
+
const value = this.tokenizer.readFilteredValue();
|
|
4629
|
+
this.candidates.push(value);
|
|
4226
4630
|
this.tokenizer.readTo(',');
|
|
4227
4631
|
}
|
|
4228
4632
|
this.tokenizer.assert(this.candidates.length, () => `empty candidates: "${token.getText()}"`);
|
|
4229
4633
|
}
|
|
4230
4634
|
*render(ctx, emitter) {
|
|
4231
4635
|
const group = (yield evalToken(this.group, ctx));
|
|
4232
|
-
const fingerprint = `cycle:${group}:` + this.candidates.join(',');
|
|
4233
|
-
const groups = ctx.getRegister('cycle');
|
|
4636
|
+
const fingerprint = `cycle:${group}:` + this.candidates.map(c => c.getText()).join(',');
|
|
4637
|
+
const groups = ctx.getRegister('cycle', {});
|
|
4234
4638
|
let idx = groups[fingerprint];
|
|
4235
4639
|
if (idx === undefined) {
|
|
4236
4640
|
idx = groups[fingerprint] = 0;
|
|
4237
4641
|
}
|
|
4238
|
-
const candidate = this.candidates[idx];
|
|
4642
|
+
const candidate = new Value(this.candidates[idx], this.liquid);
|
|
4239
4643
|
idx = (idx + 1) % this.candidates.length;
|
|
4240
4644
|
groups[fingerprint] = idx;
|
|
4241
|
-
|
|
4645
|
+
let val = yield toValue(yield candidate.value(ctx, false));
|
|
4646
|
+
const outputEscape = this.liquid.options.outputEscape;
|
|
4647
|
+
if (outputEscape && !candidate.filters[candidate.filters.length - 1]?.raw) {
|
|
4648
|
+
val = yield outputEscape.call({ context: ctx, token: this.token, liquid: this.liquid }, val);
|
|
4649
|
+
}
|
|
4650
|
+
return val;
|
|
4242
4651
|
}
|
|
4243
4652
|
*arguments() {
|
|
4244
4653
|
yield* this.candidates;
|
|
@@ -4278,7 +4687,7 @@ class IfTag extends Tag {
|
|
|
4278
4687
|
*render(ctx, emitter) {
|
|
4279
4688
|
const r = this.liquid.renderer;
|
|
4280
4689
|
for (const { value, templates } of this.branches) {
|
|
4281
|
-
const v = yield value.value(ctx, ctx.opts.lenientIf);
|
|
4690
|
+
const v = yield toValue(yield value.value(ctx, ctx.opts.lenientIf));
|
|
4282
4691
|
if (isTruthy(v, ctx)) {
|
|
4283
4692
|
yield r.renderTemplates(templates, ctx, emitter);
|
|
4284
4693
|
return;
|
|
@@ -4322,7 +4731,7 @@ class LayoutTag extends Tag {
|
|
|
4322
4731
|
constructor(token, remainTokens, liquid, parser) {
|
|
4323
4732
|
super(token, remainTokens, liquid);
|
|
4324
4733
|
this.file = parseFilePath(this.tokenizer, this.liquid, parser);
|
|
4325
|
-
this
|
|
4734
|
+
this.currentFile = token.file;
|
|
4326
4735
|
this.args = new Hash(this.tokenizer, liquid.options.keyValueSeparator);
|
|
4327
4736
|
this.templates = parser.parseTokens(remainTokens);
|
|
4328
4737
|
}
|
|
@@ -4336,24 +4745,24 @@ class LayoutTag extends Tag {
|
|
|
4336
4745
|
}
|
|
4337
4746
|
const filepath = (yield renderFilePath(this.file, ctx, liquid));
|
|
4338
4747
|
assert(filepath, () => `illegal file path "${filepath}"`);
|
|
4339
|
-
const templates = (yield liquid._parseLayoutFile(filepath, ctx.sync, this
|
|
4748
|
+
const templates = (yield liquid._parseLayoutFile(filepath, ctx.sync, this.currentFile));
|
|
4340
4749
|
// render remaining contents and store rendered results
|
|
4341
4750
|
ctx.setRegister('blockMode', BlockMode.STORE);
|
|
4342
4751
|
const html = yield renderer.renderTemplates(this.templates, ctx);
|
|
4343
|
-
const blocks = ctx.getRegister('blocks');
|
|
4752
|
+
const blocks = ctx.getRegister('blocks', {});
|
|
4344
4753
|
// set whole content to anonymous block if anonymous doesn't specified
|
|
4345
4754
|
if (blocks[''] === undefined)
|
|
4346
4755
|
blocks[''] = (parent, emitter) => emitter.write(html);
|
|
4347
4756
|
ctx.setRegister('blockMode', BlockMode.OUTPUT);
|
|
4348
4757
|
// render the layout file use stored blocks
|
|
4349
|
-
ctx.push((yield args.render(ctx)));
|
|
4758
|
+
ctx.push(createScope((yield args.render(ctx))));
|
|
4350
4759
|
yield renderer.renderTemplates(templates, ctx, emitter);
|
|
4351
4760
|
ctx.pop();
|
|
4352
4761
|
}
|
|
4353
4762
|
*children(partials) {
|
|
4354
4763
|
const templates = this.templates.slice();
|
|
4355
4764
|
if (partials && isString(this.file)) {
|
|
4356
|
-
templates.push(...(yield this.liquid._parsePartialFile(this.file, true, this
|
|
4765
|
+
templates.push(...(yield this.liquid._parsePartialFile(this.file, true, this.currentFile)));
|
|
4357
4766
|
}
|
|
4358
4767
|
return templates;
|
|
4359
4768
|
}
|
|
@@ -4392,23 +4801,28 @@ class BlockTag extends Tag {
|
|
|
4392
4801
|
*render(ctx, emitter) {
|
|
4393
4802
|
const blockRender = this.getBlockRender(ctx);
|
|
4394
4803
|
if (ctx.getRegister('blockMode') === BlockMode.STORE) {
|
|
4395
|
-
ctx.getRegister('blocks')[this.block] = blockRender;
|
|
4804
|
+
ctx.getRegister('blocks', {})[this.block] = blockRender;
|
|
4396
4805
|
}
|
|
4397
4806
|
else {
|
|
4398
4807
|
yield blockRender(new BlockDrop(), emitter);
|
|
4399
4808
|
}
|
|
4400
4809
|
}
|
|
4401
4810
|
getBlockRender(ctx) {
|
|
4811
|
+
const self = this;
|
|
4402
4812
|
const { liquid, templates } = this;
|
|
4403
|
-
const renderChild = ctx.getRegister('blocks')[this.block];
|
|
4813
|
+
const renderChild = ctx.getRegister('blocks', {})[this.block];
|
|
4404
4814
|
const renderCurrent = function* (superBlock, emitter) {
|
|
4405
|
-
|
|
4406
|
-
|
|
4815
|
+
const stack = ctx.getRegister('blockStack', []);
|
|
4816
|
+
if (stack.includes(self))
|
|
4817
|
+
throw new Error('block tag cannot be nested');
|
|
4818
|
+
stack.push(self);
|
|
4819
|
+
ctx.push(createScope({ block: superBlock }));
|
|
4407
4820
|
yield liquid.renderer.renderTemplates(templates, ctx, emitter);
|
|
4408
4821
|
ctx.pop();
|
|
4822
|
+
stack.pop();
|
|
4409
4823
|
};
|
|
4410
4824
|
return renderChild
|
|
4411
|
-
? (superBlock, emitter) => renderChild(new BlockDrop(() => renderCurrent(superBlock, emitter)), emitter)
|
|
4825
|
+
? (superBlock, emitter) => renderChild(new BlockDrop((emitter) => renderCurrent(superBlock, emitter)), emitter)
|
|
4412
4826
|
: renderCurrent;
|
|
4413
4827
|
}
|
|
4414
4828
|
*children() {
|
|
@@ -4492,7 +4906,7 @@ class TablerowTag extends Tag {
|
|
|
4492
4906
|
const cols = args.cols || collection.length;
|
|
4493
4907
|
const r = this.liquid.renderer;
|
|
4494
4908
|
const tablerowloop = new TablerowloopDrop(collection.length, cols, this.collection.getText(), this.variable);
|
|
4495
|
-
const scope = { tablerowloop };
|
|
4909
|
+
const scope = createScope({ tablerowloop });
|
|
4496
4910
|
ctx.push(scope);
|
|
4497
4911
|
for (let idx = 0; idx < collection.length; idx++, tablerowloop.next()) {
|
|
4498
4912
|
scope[this.variable] = collection[idx];
|
|
@@ -4565,7 +4979,7 @@ class UnlessTag extends Tag {
|
|
|
4565
4979
|
*render(ctx, emitter) {
|
|
4566
4980
|
const r = this.liquid.renderer;
|
|
4567
4981
|
for (const { value, test, templates } of this.branches) {
|
|
4568
|
-
const v = yield value.value(ctx, ctx.opts.lenientIf);
|
|
4982
|
+
const v = yield toValue(yield value.value(ctx, ctx.opts.lenientIf));
|
|
4569
4983
|
if (test(v, ctx)) {
|
|
4570
4984
|
yield r.renderTemplates(templates, ctx, emitter);
|
|
4571
4985
|
return;
|
|
@@ -4608,7 +5022,11 @@ class EchoTag extends Tag {
|
|
|
4608
5022
|
*render(ctx, emitter) {
|
|
4609
5023
|
if (!this.value)
|
|
4610
5024
|
return;
|
|
4611
|
-
|
|
5025
|
+
let val = yield toValue(yield this.value.value(ctx, false));
|
|
5026
|
+
const outputEscape = this.liquid.options.outputEscape;
|
|
5027
|
+
if (outputEscape && !this.value.filters[this.value.filters.length - 1]?.raw) {
|
|
5028
|
+
val = yield outputEscape.call({ context: ctx, token: this.token, liquid: this.liquid }, val);
|
|
5029
|
+
}
|
|
4612
5030
|
emitter.write(val);
|
|
4613
5031
|
}
|
|
4614
5032
|
*arguments() {
|
|
@@ -4669,8 +5087,8 @@ const tags = {
|
|
|
4669
5087
|
class Liquid {
|
|
4670
5088
|
constructor(opts = {}) {
|
|
4671
5089
|
this.renderer = new Render();
|
|
4672
|
-
this.filters =
|
|
4673
|
-
this.tags =
|
|
5090
|
+
this.filters = Object.create(null);
|
|
5091
|
+
this.tags = Object.create(null);
|
|
4674
5092
|
this.options = normalize(opts);
|
|
4675
5093
|
// eslint-disable-next-line deprecation/deprecation
|
|
4676
5094
|
this.parser = new Parser(this);
|
|
@@ -4682,7 +5100,7 @@ class Liquid {
|
|
|
4682
5100
|
return parser.parse(html, filepath);
|
|
4683
5101
|
}
|
|
4684
5102
|
_render(tpl, scope, renderOptions) {
|
|
4685
|
-
const ctx = scope instanceof Context ? scope : new Context(scope, this.options, renderOptions);
|
|
5103
|
+
const ctx = scope instanceof Context ? scope : new Context(scope, this.options, renderOptions, { liquid: this });
|
|
4686
5104
|
return this.renderer.renderTemplates(tpl, ctx);
|
|
4687
5105
|
}
|
|
4688
5106
|
async render(tpl, scope, renderOptions) {
|
|
@@ -4692,7 +5110,7 @@ class Liquid {
|
|
|
4692
5110
|
return toValueSync(this._render(tpl, scope, { ...renderOptions, sync: true }));
|
|
4693
5111
|
}
|
|
4694
5112
|
renderToNodeStream(tpl, scope, renderOptions = {}) {
|
|
4695
|
-
const ctx = new Context(scope, this.options, renderOptions);
|
|
5113
|
+
const ctx = new Context(scope, this.options, renderOptions, { liquid: this });
|
|
4696
5114
|
return this.renderer.renderTemplatesToNodeStream(tpl, ctx);
|
|
4697
5115
|
}
|
|
4698
5116
|
_parseAndRender(html, scope, renderOptions) {
|
|
@@ -4706,10 +5124,10 @@ class Liquid {
|
|
|
4706
5124
|
return toValueSync(this._parseAndRender(html, scope, { ...renderOptions, sync: true }));
|
|
4707
5125
|
}
|
|
4708
5126
|
_parsePartialFile(file, sync, currentFile) {
|
|
4709
|
-
return new Parser(this).parseFile(file, sync, LookupType.Partials, currentFile);
|
|
5127
|
+
return new Parser(this).parseFile(file, sync, exports.LookupType.Partials, currentFile);
|
|
4710
5128
|
}
|
|
4711
5129
|
_parseLayoutFile(file, sync, currentFile) {
|
|
4712
|
-
return new Parser(this).parseFile(file, sync, LookupType.Layouts, currentFile);
|
|
5130
|
+
return new Parser(this).parseFile(file, sync, exports.LookupType.Layouts, currentFile);
|
|
4713
5131
|
}
|
|
4714
5132
|
_parseFile(file, sync, lookupType, currentFile) {
|
|
4715
5133
|
return new Parser(this).parseFile(file, sync, lookupType, currentFile);
|
|
@@ -4736,7 +5154,7 @@ class Liquid {
|
|
|
4736
5154
|
}
|
|
4737
5155
|
_evalValue(str, scope) {
|
|
4738
5156
|
const value = new Value(str, this);
|
|
4739
|
-
const ctx = scope instanceof Context ? scope : new Context(scope, this.options);
|
|
5157
|
+
const ctx = scope instanceof Context ? scope : new Context(scope, this.options, {}, { liquid: this });
|
|
4740
5158
|
return value.value(ctx);
|
|
4741
5159
|
}
|
|
4742
5160
|
async evalValue(str, scope) {
|
|
@@ -4748,6 +5166,9 @@ class Liquid {
|
|
|
4748
5166
|
registerFilter(name, filter) {
|
|
4749
5167
|
this.filters[name] = filter;
|
|
4750
5168
|
}
|
|
5169
|
+
unregisterFilter(name) {
|
|
5170
|
+
delete this.filters[name];
|
|
5171
|
+
}
|
|
4751
5172
|
registerTag(name, tag) {
|
|
4752
5173
|
this.tags[name] = isFunction(tag) ? tag : createTagClass(tag);
|
|
4753
5174
|
}
|
|
@@ -4843,7 +5264,7 @@ class Liquid {
|
|
|
4843
5264
|
}
|
|
4844
5265
|
|
|
4845
5266
|
/* istanbul ignore file */
|
|
4846
|
-
const version = '10.
|
|
5267
|
+
const version = '10.30.1';
|
|
4847
5268
|
|
|
4848
5269
|
exports.AssertionError = AssertionError;
|
|
4849
5270
|
exports.AssignTag = AssignTag;
|
|
@@ -4896,6 +5317,7 @@ exports.defaultOperators = defaultOperators;
|
|
|
4896
5317
|
exports.defaultOptions = defaultOptions;
|
|
4897
5318
|
exports.evalQuotedToken = evalQuotedToken;
|
|
4898
5319
|
exports.evalToken = evalToken;
|
|
5320
|
+
exports.evalTokenValue = evalTokenValue;
|
|
4899
5321
|
exports.filters = filters;
|
|
4900
5322
|
exports.isFalsy = isFalsy;
|
|
4901
5323
|
exports.isTruthy = isTruthy;
|