@skeptools/skep-organization-project 0.0.0
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/.gitattributes +23 -0
- package/.jsii +164 -0
- package/.projenrc.ts +29 -0
- package/API.md +3610 -0
- package/LICENSE +202 -0
- package/README.md +8 -0
- package/files/generated/.seed +1 -0
- package/files/scaffolding/.seed +1 -0
- package/files/scaffolding/README.md.liquid +43 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +14 -0
- package/lib/skep-organization-project.d.ts +7 -0
- package/lib/skep-organization-project.js +24 -0
- package/node_modules/liquidjs/CHANGELOG.md +1015 -0
- package/node_modules/liquidjs/LICENSE +21 -0
- package/node_modules/liquidjs/README.md +163 -0
- package/node_modules/liquidjs/bin/liquid.js +24 -0
- package/node_modules/liquidjs/dist/builtin/filters/array.d.ts +15 -0
- package/node_modules/liquidjs/dist/builtin/filters/date.d.ts +2 -0
- package/node_modules/liquidjs/dist/builtin/filters/html.d.ts +4 -0
- package/node_modules/liquidjs/dist/builtin/filters/index.d.ts +7 -0
- package/node_modules/liquidjs/dist/builtin/filters/math.d.ts +11 -0
- package/node_modules/liquidjs/dist/builtin/filters/misc.d.ts +5 -0
- package/node_modules/liquidjs/dist/builtin/filters/string.d.ts +16 -0
- package/node_modules/liquidjs/dist/builtin/filters/url.d.ts +2 -0
- package/node_modules/liquidjs/dist/builtin/tags/assign.d.ts +3 -0
- package/node_modules/liquidjs/dist/builtin/tags/block.d.ts +8 -0
- package/node_modules/liquidjs/dist/builtin/tags/break.d.ts +5 -0
- package/node_modules/liquidjs/dist/builtin/tags/capture.d.ts +3 -0
- package/node_modules/liquidjs/dist/builtin/tags/case.d.ts +3 -0
- package/node_modules/liquidjs/dist/builtin/tags/comment.d.ts +3 -0
- package/node_modules/liquidjs/dist/builtin/tags/continue.d.ts +5 -0
- package/node_modules/liquidjs/dist/builtin/tags/cycle.d.ts +3 -0
- package/node_modules/liquidjs/dist/builtin/tags/decrement.d.ts +3 -0
- package/node_modules/liquidjs/dist/builtin/tags/echo.d.ts +3 -0
- package/node_modules/liquidjs/dist/builtin/tags/for.d.ts +3 -0
- package/node_modules/liquidjs/dist/builtin/tags/if.d.ts +3 -0
- package/node_modules/liquidjs/dist/builtin/tags/include.d.ts +3 -0
- package/node_modules/liquidjs/dist/builtin/tags/increment.d.ts +3 -0
- package/node_modules/liquidjs/dist/builtin/tags/index.d.ts +5 -0
- package/node_modules/liquidjs/dist/builtin/tags/inline-comment.d.ts +3 -0
- package/node_modules/liquidjs/dist/builtin/tags/layout.d.ts +3 -0
- package/node_modules/liquidjs/dist/builtin/tags/liquid.d.ts +3 -0
- package/node_modules/liquidjs/dist/builtin/tags/raw.d.ts +3 -0
- package/node_modules/liquidjs/dist/builtin/tags/render.d.ts +13 -0
- package/node_modules/liquidjs/dist/builtin/tags/tablerow.d.ts +3 -0
- package/node_modules/liquidjs/dist/builtin/tags/unless.d.ts +3 -0
- package/node_modules/liquidjs/dist/cache/cache.d.ts +7 -0
- package/node_modules/liquidjs/dist/cache/lru.d.ts +14 -0
- package/node_modules/liquidjs/dist/context/block-mode.d.ts +5 -0
- package/node_modules/liquidjs/dist/context/context.d.ts +53 -0
- package/node_modules/liquidjs/dist/context/scope.d.ts +6 -0
- package/node_modules/liquidjs/dist/drop/blank-drop.d.ts +4 -0
- package/node_modules/liquidjs/dist/drop/block-drop.d.ts +10 -0
- package/node_modules/liquidjs/dist/drop/comparable.d.ts +8 -0
- package/node_modules/liquidjs/dist/drop/drop.d.ts +3 -0
- package/node_modules/liquidjs/dist/drop/empty-drop.d.ts +10 -0
- package/node_modules/liquidjs/dist/drop/forloop-drop.d.ts +15 -0
- package/node_modules/liquidjs/dist/drop/null-drop.d.ts +10 -0
- package/node_modules/liquidjs/dist/drop/tablerowloop-drop.d.ts +10 -0
- package/node_modules/liquidjs/dist/emitters/emitter.d.ts +11 -0
- package/node_modules/liquidjs/dist/emitters/keeping-type-emitter.d.ts +5 -0
- package/node_modules/liquidjs/dist/emitters/simple-emitter.d.ts +5 -0
- package/node_modules/liquidjs/dist/emitters/streamed-emitter-browser.d.ts +10 -0
- package/node_modules/liquidjs/dist/emitters/streamed-emitter.d.ts +9 -0
- package/node_modules/liquidjs/dist/fs/browser.d.ts +7 -0
- package/node_modules/liquidjs/dist/fs/fs.d.ts +20 -0
- package/node_modules/liquidjs/dist/fs/loader.d.ts +24 -0
- package/node_modules/liquidjs/dist/fs/node-require.d.ts +2 -0
- package/node_modules/liquidjs/dist/fs/node.d.ts +9 -0
- package/node_modules/liquidjs/dist/liquid-options.d.ts +128 -0
- package/node_modules/liquidjs/dist/liquid.browser.esm.js +3523 -0
- package/node_modules/liquidjs/dist/liquid.browser.min.js +2 -0
- package/node_modules/liquidjs/dist/liquid.browser.min.js.map +1 -0
- package/node_modules/liquidjs/dist/liquid.browser.umd.js +4811 -0
- package/node_modules/liquidjs/dist/liquid.browser.umd.js.map +1 -0
- package/node_modules/liquidjs/dist/liquid.d.ts +43 -0
- package/node_modules/liquidjs/dist/liquid.node.cjs.js +4820 -0
- package/node_modules/liquidjs/dist/liquid.node.esm.js +3540 -0
- package/node_modules/liquidjs/dist/parser/filter-arg.d.ts +5 -0
- package/node_modules/liquidjs/dist/parser/match-operator.d.ts +2 -0
- package/node_modules/liquidjs/dist/parser/parse-stream.d.ts +16 -0
- package/node_modules/liquidjs/dist/parser/parse-string-literal.d.ts +1 -0
- package/node_modules/liquidjs/dist/parser/parser.d.ts +22 -0
- package/node_modules/liquidjs/dist/parser/token-kind.d.ts +15 -0
- package/node_modules/liquidjs/dist/parser/tokenizer.d.ts +66 -0
- package/node_modules/liquidjs/dist/parser/whitespace-ctrl.d.ts +3 -0
- package/node_modules/liquidjs/dist/render/boolean.d.ts +3 -0
- package/node_modules/liquidjs/dist/render/expression.d.ts +14 -0
- package/node_modules/liquidjs/dist/render/operator.d.ts +5 -0
- package/node_modules/liquidjs/dist/render/render.d.ts +8 -0
- package/node_modules/liquidjs/dist/template/filter/filter-impl-options.d.ts +4 -0
- package/node_modules/liquidjs/dist/template/filter/filter-impl.d.ts +6 -0
- package/node_modules/liquidjs/dist/template/filter/filter-map.d.ts +13 -0
- package/node_modules/liquidjs/dist/template/filter/filter.d.ts +12 -0
- package/node_modules/liquidjs/dist/template/html.d.ts +10 -0
- package/node_modules/liquidjs/dist/template/output.d.ts +11 -0
- package/node_modules/liquidjs/dist/template/tag/hash.d.ts +17 -0
- package/node_modules/liquidjs/dist/template/tag/tag-impl-options.d.ts +10 -0
- package/node_modules/liquidjs/dist/template/tag/tag-impl.d.ts +6 -0
- package/node_modules/liquidjs/dist/template/tag/tag-map.d.ts +6 -0
- package/node_modules/liquidjs/dist/template/tag/tag.d.ts +10 -0
- package/node_modules/liquidjs/dist/template/template-impl.d.ts +4 -0
- package/node_modules/liquidjs/dist/template/template.d.ts +7 -0
- package/node_modules/liquidjs/dist/template/value.d.ts +13 -0
- package/node_modules/liquidjs/dist/tokens/delimited-token.d.ts +8 -0
- package/node_modules/liquidjs/dist/tokens/filter-token.d.ts +7 -0
- package/node_modules/liquidjs/dist/tokens/hash-token.d.ts +12 -0
- package/node_modules/liquidjs/dist/tokens/html-token.d.ts +11 -0
- package/node_modules/liquidjs/dist/tokens/identifier-token.d.ts +10 -0
- package/node_modules/liquidjs/dist/tokens/liquid-tag-token.d.ts +7 -0
- package/node_modules/liquidjs/dist/tokens/literal-token.d.ts +9 -0
- package/node_modules/liquidjs/dist/tokens/number-token.d.ts +7 -0
- package/node_modules/liquidjs/dist/tokens/operator-token.d.ts +21 -0
- package/node_modules/liquidjs/dist/tokens/output-token.d.ts +5 -0
- package/node_modules/liquidjs/dist/tokens/property-access-token.d.ts +9 -0
- package/node_modules/liquidjs/dist/tokens/quoted-token.d.ts +8 -0
- package/node_modules/liquidjs/dist/tokens/range-token.d.ts +11 -0
- package/node_modules/liquidjs/dist/tokens/tag-token.d.ts +7 -0
- package/node_modules/liquidjs/dist/tokens/token.d.ts +12 -0
- package/node_modules/liquidjs/dist/tokens/toplevel-token.d.ts +4 -0
- package/node_modules/liquidjs/dist/tokens/value-token.d.ts +5 -0
- package/node_modules/liquidjs/dist/types.d.ts +30 -0
- package/node_modules/liquidjs/dist/util/assert.d.ts +1 -0
- package/node_modules/liquidjs/dist/util/async.d.ts +3 -0
- package/node_modules/liquidjs/dist/util/character.d.ts +8 -0
- package/node_modules/liquidjs/dist/util/collection.d.ts +2 -0
- package/node_modules/liquidjs/dist/util/error.d.ts +29 -0
- package/node_modules/liquidjs/dist/util/liquid-date.d.ts +19 -0
- package/node_modules/liquidjs/dist/util/literal.d.ts +11 -0
- package/node_modules/liquidjs/dist/util/operator-trie.d.ts +5 -0
- package/node_modules/liquidjs/dist/util/strftime.d.ts +2 -0
- package/node_modules/liquidjs/dist/util/timezone-date.d.ts +39 -0
- package/node_modules/liquidjs/dist/util/type-guards.d.ts +22 -0
- package/node_modules/liquidjs/dist/util/underscore.d.ts +37 -0
- package/node_modules/liquidjs/package.json +165 -0
- package/package.json +133 -0
|
@@ -0,0 +1,3540 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* liquidjs@9.43.0, https://github.com/harttle/liquidjs
|
|
3
|
+
* (c) 2016-2022 harttle
|
|
4
|
+
* Released under the MIT License.
|
|
5
|
+
*/
|
|
6
|
+
import { extname, resolve as resolve$1, dirname as dirname$1, sep } from 'path';
|
|
7
|
+
import { statSync, readFileSync as readFileSync$1, readFile as readFile$1, stat } from 'fs';
|
|
8
|
+
import { createRequire } from 'module';
|
|
9
|
+
import { PassThrough } from 'stream';
|
|
10
|
+
|
|
11
|
+
/*! *****************************************************************************
|
|
12
|
+
Copyright (c) Microsoft Corporation.
|
|
13
|
+
|
|
14
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
15
|
+
purpose with or without fee is hereby granted.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
18
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
19
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
20
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
21
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
22
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
23
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
24
|
+
***************************************************************************** */
|
|
25
|
+
|
|
26
|
+
var __assign = function() {
|
|
27
|
+
__assign = Object.assign || function __assign(t) {
|
|
28
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
29
|
+
s = arguments[i];
|
|
30
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
|
31
|
+
}
|
|
32
|
+
return t;
|
|
33
|
+
};
|
|
34
|
+
return __assign.apply(this, arguments);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
38
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
39
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
40
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
41
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
42
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
43
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
class Drop {
|
|
48
|
+
liquidMethodMissing(key) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const toString$1 = Object.prototype.toString;
|
|
54
|
+
const toLowerCase = String.prototype.toLowerCase;
|
|
55
|
+
const hasOwnProperty = Object.hasOwnProperty;
|
|
56
|
+
function isString(value) {
|
|
57
|
+
return typeof value === 'string';
|
|
58
|
+
}
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
60
|
+
function isFunction(value) {
|
|
61
|
+
return typeof value === 'function';
|
|
62
|
+
}
|
|
63
|
+
function isPromise(val) {
|
|
64
|
+
return val && isFunction(val.then);
|
|
65
|
+
}
|
|
66
|
+
function isIterator(val) {
|
|
67
|
+
return val && isFunction(val.next) && isFunction(val.throw) && isFunction(val.return);
|
|
68
|
+
}
|
|
69
|
+
function escapeRegex(str) {
|
|
70
|
+
return str.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
71
|
+
}
|
|
72
|
+
function promisify(fn) {
|
|
73
|
+
return function (...args) {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
fn(...args, (err, result) => {
|
|
76
|
+
err ? reject(err) : resolve(result);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function stringify(value) {
|
|
82
|
+
value = toValue(value);
|
|
83
|
+
if (isString(value))
|
|
84
|
+
return value;
|
|
85
|
+
if (isNil(value))
|
|
86
|
+
return '';
|
|
87
|
+
if (isArray(value))
|
|
88
|
+
return value.map(x => stringify(x)).join('');
|
|
89
|
+
return String(value);
|
|
90
|
+
}
|
|
91
|
+
function toValue(value) {
|
|
92
|
+
return (value instanceof Drop && isFunction(value.valueOf)) ? value.valueOf() : value;
|
|
93
|
+
}
|
|
94
|
+
function isNumber(value) {
|
|
95
|
+
return typeof value === 'number';
|
|
96
|
+
}
|
|
97
|
+
function toLiquid(value) {
|
|
98
|
+
if (value && isFunction(value.toLiquid))
|
|
99
|
+
return toLiquid(value.toLiquid());
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
function isNil(value) {
|
|
103
|
+
return value == null;
|
|
104
|
+
}
|
|
105
|
+
function isArray(value) {
|
|
106
|
+
// be compatible with IE 8
|
|
107
|
+
return toString$1.call(value) === '[object Array]';
|
|
108
|
+
}
|
|
109
|
+
function isIterable(value) {
|
|
110
|
+
return isObject(value) && Symbol.iterator in value;
|
|
111
|
+
}
|
|
112
|
+
/*
|
|
113
|
+
* Iterates over own enumerable string keyed properties of an object and invokes iteratee for each property.
|
|
114
|
+
* The iteratee is invoked with three arguments: (value, key, object).
|
|
115
|
+
* Iteratee functions may exit iteration early by explicitly returning false.
|
|
116
|
+
* @param {Object} object The object to iterate over.
|
|
117
|
+
* @param {Function} iteratee The function invoked per iteration.
|
|
118
|
+
* @return {Object} Returns object.
|
|
119
|
+
*/
|
|
120
|
+
function forOwn(obj, iteratee) {
|
|
121
|
+
obj = obj || {};
|
|
122
|
+
for (const k in obj) {
|
|
123
|
+
if (hasOwnProperty.call(obj, k)) {
|
|
124
|
+
if (iteratee(obj[k], k, obj) === false)
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return obj;
|
|
129
|
+
}
|
|
130
|
+
function last$1(arr) {
|
|
131
|
+
return arr[arr.length - 1];
|
|
132
|
+
}
|
|
133
|
+
/*
|
|
134
|
+
* Checks if value is the language type of Object.
|
|
135
|
+
* (e.g. arrays, functions, objects, regexes, new Number(0), and new String(''))
|
|
136
|
+
* @param {any} value The value to check.
|
|
137
|
+
* @return {Boolean} Returns true if value is an object, else false.
|
|
138
|
+
*/
|
|
139
|
+
function isObject(value) {
|
|
140
|
+
const type = typeof value;
|
|
141
|
+
return value !== null && (type === 'object' || type === 'function');
|
|
142
|
+
}
|
|
143
|
+
function range(start, stop, step = 1) {
|
|
144
|
+
const arr = [];
|
|
145
|
+
for (let i = start; i < stop; i += step) {
|
|
146
|
+
arr.push(i);
|
|
147
|
+
}
|
|
148
|
+
return arr;
|
|
149
|
+
}
|
|
150
|
+
function padStart(str, length, ch = ' ') {
|
|
151
|
+
return pad(str, length, ch, (str, ch) => ch + str);
|
|
152
|
+
}
|
|
153
|
+
function padEnd(str, length, ch = ' ') {
|
|
154
|
+
return pad(str, length, ch, (str, ch) => str + ch);
|
|
155
|
+
}
|
|
156
|
+
function pad(str, length, ch, add) {
|
|
157
|
+
str = String(str);
|
|
158
|
+
let n = length - str.length;
|
|
159
|
+
while (n-- > 0)
|
|
160
|
+
str = add(str, ch);
|
|
161
|
+
return str;
|
|
162
|
+
}
|
|
163
|
+
function identify(val) {
|
|
164
|
+
return val;
|
|
165
|
+
}
|
|
166
|
+
function snakeCase(str) {
|
|
167
|
+
return str.replace(/(\w?)([A-Z])/g, (_, a, b) => (a ? a + '_' : '') + b.toLowerCase());
|
|
168
|
+
}
|
|
169
|
+
function changeCase(str) {
|
|
170
|
+
const hasLowerCase = [...str].some(ch => ch >= 'a' && ch <= 'z');
|
|
171
|
+
return hasLowerCase ? str.toUpperCase() : str.toLowerCase();
|
|
172
|
+
}
|
|
173
|
+
function ellipsis(str, N) {
|
|
174
|
+
return str.length > N ? str.slice(0, N - 3) + '...' : str;
|
|
175
|
+
}
|
|
176
|
+
// compare string in case-insensitive way, undefined values to the tail
|
|
177
|
+
function caseInsensitiveCompare(a, b) {
|
|
178
|
+
if (a == null && b == null)
|
|
179
|
+
return 0;
|
|
180
|
+
if (a == null)
|
|
181
|
+
return 1;
|
|
182
|
+
if (b == null)
|
|
183
|
+
return -1;
|
|
184
|
+
a = toLowerCase.call(a);
|
|
185
|
+
b = toLowerCase.call(b);
|
|
186
|
+
if (a < b)
|
|
187
|
+
return -1;
|
|
188
|
+
if (a > b)
|
|
189
|
+
return 1;
|
|
190
|
+
return 0;
|
|
191
|
+
}
|
|
192
|
+
function argumentsToValue(fn) {
|
|
193
|
+
return (...args) => fn(...args.map(toValue));
|
|
194
|
+
}
|
|
195
|
+
function escapeRegExp(text) {
|
|
196
|
+
return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
class Node {
|
|
200
|
+
constructor(key, value, next, prev) {
|
|
201
|
+
this.key = key;
|
|
202
|
+
this.value = value;
|
|
203
|
+
this.next = next;
|
|
204
|
+
this.prev = prev;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
class LRU {
|
|
208
|
+
constructor(limit, size = 0) {
|
|
209
|
+
this.limit = limit;
|
|
210
|
+
this.size = size;
|
|
211
|
+
this.cache = {};
|
|
212
|
+
this.head = new Node('HEAD', null, null, null);
|
|
213
|
+
this.tail = new Node('TAIL', null, null, null);
|
|
214
|
+
this.head.next = this.tail;
|
|
215
|
+
this.tail.prev = this.head;
|
|
216
|
+
}
|
|
217
|
+
write(key, value) {
|
|
218
|
+
if (this.cache[key]) {
|
|
219
|
+
this.cache[key].value = value;
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
const node = new Node(key, value, this.head.next, this.head);
|
|
223
|
+
this.head.next.prev = node;
|
|
224
|
+
this.head.next = node;
|
|
225
|
+
this.cache[key] = node;
|
|
226
|
+
this.size++;
|
|
227
|
+
this.ensureLimit();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
read(key) {
|
|
231
|
+
if (!this.cache[key])
|
|
232
|
+
return;
|
|
233
|
+
const { value } = this.cache[key];
|
|
234
|
+
this.remove(key);
|
|
235
|
+
this.write(key, value);
|
|
236
|
+
return value;
|
|
237
|
+
}
|
|
238
|
+
remove(key) {
|
|
239
|
+
const node = this.cache[key];
|
|
240
|
+
node.prev.next = node.next;
|
|
241
|
+
node.next.prev = node.prev;
|
|
242
|
+
delete this.cache[key];
|
|
243
|
+
this.size--;
|
|
244
|
+
}
|
|
245
|
+
clear() {
|
|
246
|
+
this.head.next = this.tail;
|
|
247
|
+
this.tail.prev = this.head;
|
|
248
|
+
this.size = 0;
|
|
249
|
+
this.cache = {};
|
|
250
|
+
}
|
|
251
|
+
ensureLimit() {
|
|
252
|
+
if (this.size > this.limit)
|
|
253
|
+
this.remove(this.tail.prev.key);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function requireResolve (file) {
|
|
258
|
+
/**
|
|
259
|
+
* createRequire() can throw,
|
|
260
|
+
* when import.meta.url not begin with "file://".
|
|
261
|
+
*/
|
|
262
|
+
const require = createRequire(import.meta.url);
|
|
263
|
+
return require.resolve(file)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const statAsync = promisify(stat);
|
|
267
|
+
const readFileAsync = promisify(readFile$1);
|
|
268
|
+
function exists(filepath) {
|
|
269
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
270
|
+
try {
|
|
271
|
+
yield statAsync(filepath);
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
function readFile(filepath) {
|
|
280
|
+
return readFileAsync(filepath, 'utf8');
|
|
281
|
+
}
|
|
282
|
+
function existsSync(filepath) {
|
|
283
|
+
try {
|
|
284
|
+
statSync(filepath);
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
catch (err) {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function readFileSync(filepath) {
|
|
292
|
+
return readFileSync$1(filepath, 'utf8');
|
|
293
|
+
}
|
|
294
|
+
function resolve(root, file, ext) {
|
|
295
|
+
if (!extname(file))
|
|
296
|
+
file += ext;
|
|
297
|
+
return resolve$1(root, file);
|
|
298
|
+
}
|
|
299
|
+
function fallback(file) {
|
|
300
|
+
try {
|
|
301
|
+
return requireResolve(file);
|
|
302
|
+
}
|
|
303
|
+
catch (e) { }
|
|
304
|
+
}
|
|
305
|
+
function dirname(filepath) {
|
|
306
|
+
return dirname$1(filepath);
|
|
307
|
+
}
|
|
308
|
+
function contains(root, file) {
|
|
309
|
+
root = resolve$1(root);
|
|
310
|
+
root = root.endsWith(sep) ? root : root + sep;
|
|
311
|
+
return file.startsWith(root);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
var fs = /*#__PURE__*/Object.freeze({
|
|
315
|
+
__proto__: null,
|
|
316
|
+
exists: exists,
|
|
317
|
+
readFile: readFile,
|
|
318
|
+
existsSync: existsSync,
|
|
319
|
+
readFileSync: readFileSync,
|
|
320
|
+
resolve: resolve,
|
|
321
|
+
fallback: fallback,
|
|
322
|
+
dirname: dirname,
|
|
323
|
+
contains: contains,
|
|
324
|
+
sep: sep
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
function isComparable(arg) {
|
|
328
|
+
return arg && isFunction(arg.equals);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function isTruthy(val, ctx) {
|
|
332
|
+
return !isFalsy(val, ctx);
|
|
333
|
+
}
|
|
334
|
+
function isFalsy(val, ctx) {
|
|
335
|
+
if (ctx.opts.jsTruthy) {
|
|
336
|
+
return !val;
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
return val === false || undefined === val || val === null;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const defaultOperators = {
|
|
344
|
+
'==': (l, r) => {
|
|
345
|
+
if (isComparable(l))
|
|
346
|
+
return l.equals(r);
|
|
347
|
+
if (isComparable(r))
|
|
348
|
+
return r.equals(l);
|
|
349
|
+
return toValue(l) === toValue(r);
|
|
350
|
+
},
|
|
351
|
+
'!=': (l, r) => {
|
|
352
|
+
if (isComparable(l))
|
|
353
|
+
return !l.equals(r);
|
|
354
|
+
if (isComparable(r))
|
|
355
|
+
return !r.equals(l);
|
|
356
|
+
return toValue(l) !== toValue(r);
|
|
357
|
+
},
|
|
358
|
+
'>': (l, r) => {
|
|
359
|
+
if (isComparable(l))
|
|
360
|
+
return l.gt(r);
|
|
361
|
+
if (isComparable(r))
|
|
362
|
+
return r.lt(l);
|
|
363
|
+
return toValue(l) > toValue(r);
|
|
364
|
+
},
|
|
365
|
+
'<': (l, r) => {
|
|
366
|
+
if (isComparable(l))
|
|
367
|
+
return l.lt(r);
|
|
368
|
+
if (isComparable(r))
|
|
369
|
+
return r.gt(l);
|
|
370
|
+
return toValue(l) < toValue(r);
|
|
371
|
+
},
|
|
372
|
+
'>=': (l, r) => {
|
|
373
|
+
if (isComparable(l))
|
|
374
|
+
return l.geq(r);
|
|
375
|
+
if (isComparable(r))
|
|
376
|
+
return r.leq(l);
|
|
377
|
+
return toValue(l) >= toValue(r);
|
|
378
|
+
},
|
|
379
|
+
'<=': (l, r) => {
|
|
380
|
+
if (isComparable(l))
|
|
381
|
+
return l.leq(r);
|
|
382
|
+
if (isComparable(r))
|
|
383
|
+
return r.geq(l);
|
|
384
|
+
return toValue(l) <= toValue(r);
|
|
385
|
+
},
|
|
386
|
+
'contains': (l, r) => {
|
|
387
|
+
l = toValue(l);
|
|
388
|
+
r = toValue(r);
|
|
389
|
+
return l && isFunction(l.indexOf) ? l.indexOf(r) > -1 : false;
|
|
390
|
+
},
|
|
391
|
+
'and': (l, r, ctx) => isTruthy(toValue(l), ctx) && isTruthy(toValue(r), ctx),
|
|
392
|
+
'or': (l, r, ctx) => isTruthy(toValue(l), ctx) || isTruthy(toValue(r), ctx)
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
// **DO NOT CHANGE THIS FILE**
|
|
396
|
+
//
|
|
397
|
+
// This file is generated by bin/character-gen.js
|
|
398
|
+
// bitmask character types to boost performance
|
|
399
|
+
const TYPES = [0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 4, 4, 4, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 2, 8, 0, 0, 0, 0, 8, 0, 0, 0, 64, 0, 65, 0, 0, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 0, 0, 2, 2, 2, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0];
|
|
400
|
+
const IDENTIFIER = 1;
|
|
401
|
+
const BLANK = 4;
|
|
402
|
+
const QUOTE = 8;
|
|
403
|
+
const INLINE_BLANK = 16;
|
|
404
|
+
const NUMBER = 32;
|
|
405
|
+
const SIGN = 64;
|
|
406
|
+
TYPES[160] = TYPES[5760] = TYPES[6158] = TYPES[8192] = TYPES[8193] = TYPES[8194] = TYPES[8195] = TYPES[8196] = TYPES[8197] = TYPES[8198] = TYPES[8199] = TYPES[8200] = TYPES[8201] = TYPES[8202] = TYPES[8232] = TYPES[8233] = TYPES[8239] = TYPES[8287] = TYPES[12288] = BLANK;
|
|
407
|
+
|
|
408
|
+
function createTrie(operators) {
|
|
409
|
+
const trie = {};
|
|
410
|
+
for (const [name, handler] of Object.entries(operators)) {
|
|
411
|
+
let node = trie;
|
|
412
|
+
for (let i = 0; i < name.length; i++) {
|
|
413
|
+
const c = name[i];
|
|
414
|
+
node[c] = node[c] || {};
|
|
415
|
+
if (i === name.length - 1 && (TYPES[name.charCodeAt(i)] & IDENTIFIER)) {
|
|
416
|
+
node[c].needBoundary = true;
|
|
417
|
+
}
|
|
418
|
+
node = node[c];
|
|
419
|
+
}
|
|
420
|
+
node.handler = handler;
|
|
421
|
+
node.end = true;
|
|
422
|
+
}
|
|
423
|
+
return trie;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const escapeMap = {
|
|
427
|
+
'&': '&',
|
|
428
|
+
'<': '<',
|
|
429
|
+
'>': '>',
|
|
430
|
+
'"': '"',
|
|
431
|
+
"'": '''
|
|
432
|
+
};
|
|
433
|
+
const unescapeMap = {
|
|
434
|
+
'&': '&',
|
|
435
|
+
'<': '<',
|
|
436
|
+
'>': '>',
|
|
437
|
+
'"': '"',
|
|
438
|
+
''': "'"
|
|
439
|
+
};
|
|
440
|
+
function escape(str) {
|
|
441
|
+
return stringify(str).replace(/&|<|>|"|'/g, m => escapeMap[m]);
|
|
442
|
+
}
|
|
443
|
+
function unescape(str) {
|
|
444
|
+
return stringify(str).replace(/&(amp|lt|gt|#34|#39);/g, m => unescapeMap[m]);
|
|
445
|
+
}
|
|
446
|
+
function escapeOnce(str) {
|
|
447
|
+
return escape(unescape(stringify(str)));
|
|
448
|
+
}
|
|
449
|
+
function newlineToBr(v) {
|
|
450
|
+
return stringify(v).replace(/\n/g, '<br />\n');
|
|
451
|
+
}
|
|
452
|
+
function stripHtml(v) {
|
|
453
|
+
return stringify(v).replace(/<script.*?<\/script>|<!--.*?-->|<style.*?<\/style>|<.*?>/g, '');
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const abs = argumentsToValue(Math.abs);
|
|
457
|
+
const atLeast = argumentsToValue(Math.max);
|
|
458
|
+
const atMost = argumentsToValue(Math.min);
|
|
459
|
+
const ceil = argumentsToValue(Math.ceil);
|
|
460
|
+
const dividedBy = argumentsToValue((dividend, divisor, integerArithmetic = false) => integerArithmetic ? Math.floor(dividend / divisor) : dividend / divisor);
|
|
461
|
+
const floor = argumentsToValue(Math.floor);
|
|
462
|
+
const minus = argumentsToValue((v, arg) => v - arg);
|
|
463
|
+
const modulo = argumentsToValue((v, arg) => v % arg);
|
|
464
|
+
const times = argumentsToValue((v, arg) => v * arg);
|
|
465
|
+
function round(v, arg = 0) {
|
|
466
|
+
v = toValue(v);
|
|
467
|
+
arg = toValue(arg);
|
|
468
|
+
const amp = Math.pow(10, arg);
|
|
469
|
+
return Math.round(v * amp) / amp;
|
|
470
|
+
}
|
|
471
|
+
function plus(v, arg) {
|
|
472
|
+
v = toValue(v);
|
|
473
|
+
arg = toValue(arg);
|
|
474
|
+
return Number(v) + Number(arg);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const urlDecode = (x) => stringify(x).split('+').map(decodeURIComponent).join(' ');
|
|
478
|
+
const urlEncode = (x) => stringify(x).split(' ').map(encodeURIComponent).join('+');
|
|
479
|
+
|
|
480
|
+
function toEnumerable(val) {
|
|
481
|
+
val = toValue(val);
|
|
482
|
+
if (isArray(val))
|
|
483
|
+
return val;
|
|
484
|
+
if (isString(val) && val.length > 0)
|
|
485
|
+
return [val];
|
|
486
|
+
if (isIterable(val))
|
|
487
|
+
return Array.from(val);
|
|
488
|
+
if (isObject(val))
|
|
489
|
+
return Object.keys(val).map((key) => [key, val[key]]);
|
|
490
|
+
return [];
|
|
491
|
+
}
|
|
492
|
+
function toArray(val) {
|
|
493
|
+
if (isNil(val))
|
|
494
|
+
return [];
|
|
495
|
+
if (isArray(val))
|
|
496
|
+
return val;
|
|
497
|
+
return [val];
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const join = argumentsToValue((v, arg) => toArray(v).join(arg === undefined ? ' ' : arg));
|
|
501
|
+
const last = argumentsToValue((v) => isArray(v) ? last$1(v) : '');
|
|
502
|
+
const first = argumentsToValue((v) => isArray(v) ? v[0] : '');
|
|
503
|
+
const reverse = argumentsToValue((v) => [...toArray(v)].reverse());
|
|
504
|
+
function* sort(arr, property) {
|
|
505
|
+
const values = [];
|
|
506
|
+
for (const item of toArray(toValue(arr))) {
|
|
507
|
+
values.push([
|
|
508
|
+
item,
|
|
509
|
+
property ? yield this.context._getFromScope(item, stringify(property).split('.')) : item
|
|
510
|
+
]);
|
|
511
|
+
}
|
|
512
|
+
return values.sort((lhs, rhs) => {
|
|
513
|
+
const lvalue = lhs[1];
|
|
514
|
+
const rvalue = rhs[1];
|
|
515
|
+
return lvalue < rvalue ? -1 : (lvalue > rvalue ? 1 : 0);
|
|
516
|
+
}).map(tuple => tuple[0]);
|
|
517
|
+
}
|
|
518
|
+
function sortNatural(input, property) {
|
|
519
|
+
input = toValue(input);
|
|
520
|
+
const propertyString = stringify(property);
|
|
521
|
+
const compare = property === undefined
|
|
522
|
+
? caseInsensitiveCompare
|
|
523
|
+
: (lhs, rhs) => caseInsensitiveCompare(lhs[propertyString], rhs[propertyString]);
|
|
524
|
+
return [...toArray(input)].sort(compare);
|
|
525
|
+
}
|
|
526
|
+
const size = (v) => (v && v.length) || 0;
|
|
527
|
+
function* map(arr, property) {
|
|
528
|
+
const results = [];
|
|
529
|
+
for (const item of toArray(toValue(arr))) {
|
|
530
|
+
results.push(yield this.context._getFromScope(item, stringify(property).split('.')));
|
|
531
|
+
}
|
|
532
|
+
return results;
|
|
533
|
+
}
|
|
534
|
+
function compact(arr) {
|
|
535
|
+
arr = toValue(arr);
|
|
536
|
+
return toArray(arr).filter(x => !isNil(toValue(x)));
|
|
537
|
+
}
|
|
538
|
+
function concat(v, arg = []) {
|
|
539
|
+
v = toValue(v);
|
|
540
|
+
arg = toArray(arg).map(v => toValue(v));
|
|
541
|
+
return toArray(v).concat(arg);
|
|
542
|
+
}
|
|
543
|
+
function slice(v, begin, length = 1) {
|
|
544
|
+
v = toValue(v);
|
|
545
|
+
if (isNil(v))
|
|
546
|
+
return [];
|
|
547
|
+
if (!isArray(v))
|
|
548
|
+
v = stringify(v);
|
|
549
|
+
begin = begin < 0 ? v.length + begin : begin;
|
|
550
|
+
return v.slice(begin, begin + length);
|
|
551
|
+
}
|
|
552
|
+
function* where(arr, property, expected) {
|
|
553
|
+
const values = [];
|
|
554
|
+
arr = toArray(toValue(arr));
|
|
555
|
+
for (const item of arr) {
|
|
556
|
+
values.push(yield this.context._getFromScope(item, stringify(property).split('.')));
|
|
557
|
+
}
|
|
558
|
+
return arr.filter((_, i) => {
|
|
559
|
+
if (expected === undefined)
|
|
560
|
+
return isTruthy(values[i], this.context);
|
|
561
|
+
if (isComparable(expected))
|
|
562
|
+
return expected.equals(values[i]);
|
|
563
|
+
return values[i] === expected;
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
function uniq(arr) {
|
|
567
|
+
arr = toValue(arr);
|
|
568
|
+
const u = {};
|
|
569
|
+
return (arr || []).filter(val => {
|
|
570
|
+
if (hasOwnProperty.call(u, String(val)))
|
|
571
|
+
return false;
|
|
572
|
+
u[String(val)] = true;
|
|
573
|
+
return true;
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const rFormat = /%([-_0^#:]+)?(\d+)?([EO])?(.)/;
|
|
578
|
+
const monthNames = [
|
|
579
|
+
'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
|
|
580
|
+
'September', 'October', 'November', 'December'
|
|
581
|
+
];
|
|
582
|
+
const dayNames = [
|
|
583
|
+
'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'
|
|
584
|
+
];
|
|
585
|
+
const monthNamesShort = monthNames.map(abbr);
|
|
586
|
+
const dayNamesShort = dayNames.map(abbr);
|
|
587
|
+
const suffixes = {
|
|
588
|
+
1: 'st',
|
|
589
|
+
2: 'nd',
|
|
590
|
+
3: 'rd',
|
|
591
|
+
'default': 'th'
|
|
592
|
+
};
|
|
593
|
+
function abbr(str) {
|
|
594
|
+
return str.slice(0, 3);
|
|
595
|
+
}
|
|
596
|
+
// prototype extensions
|
|
597
|
+
function daysInMonth(d) {
|
|
598
|
+
const feb = isLeapYear(d) ? 29 : 28;
|
|
599
|
+
return [31, feb, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
600
|
+
}
|
|
601
|
+
function getDayOfYear(d) {
|
|
602
|
+
let num = 0;
|
|
603
|
+
for (let i = 0; i < d.getMonth(); ++i) {
|
|
604
|
+
num += daysInMonth(d)[i];
|
|
605
|
+
}
|
|
606
|
+
return num + d.getDate();
|
|
607
|
+
}
|
|
608
|
+
function getWeekOfYear(d, startDay) {
|
|
609
|
+
// Skip to startDay of this week
|
|
610
|
+
const now = getDayOfYear(d) + (startDay - d.getDay());
|
|
611
|
+
// Find the first startDay of the year
|
|
612
|
+
const jan1 = new Date(d.getFullYear(), 0, 1);
|
|
613
|
+
const then = (7 - jan1.getDay() + startDay);
|
|
614
|
+
return String(Math.floor((now - then) / 7) + 1);
|
|
615
|
+
}
|
|
616
|
+
function isLeapYear(d) {
|
|
617
|
+
const year = d.getFullYear();
|
|
618
|
+
return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year)));
|
|
619
|
+
}
|
|
620
|
+
function getSuffix(d) {
|
|
621
|
+
const str = d.getDate().toString();
|
|
622
|
+
const index = parseInt(str.slice(-1));
|
|
623
|
+
return suffixes[index] || suffixes['default'];
|
|
624
|
+
}
|
|
625
|
+
function century(d) {
|
|
626
|
+
return parseInt(d.getFullYear().toString().substring(0, 2), 10);
|
|
627
|
+
}
|
|
628
|
+
// default to 0
|
|
629
|
+
const padWidths = {
|
|
630
|
+
d: 2,
|
|
631
|
+
e: 2,
|
|
632
|
+
H: 2,
|
|
633
|
+
I: 2,
|
|
634
|
+
j: 3,
|
|
635
|
+
k: 2,
|
|
636
|
+
l: 2,
|
|
637
|
+
L: 3,
|
|
638
|
+
m: 2,
|
|
639
|
+
M: 2,
|
|
640
|
+
S: 2,
|
|
641
|
+
U: 2,
|
|
642
|
+
W: 2
|
|
643
|
+
};
|
|
644
|
+
// default to '0'
|
|
645
|
+
const padChars = {
|
|
646
|
+
a: ' ',
|
|
647
|
+
A: ' ',
|
|
648
|
+
b: ' ',
|
|
649
|
+
B: ' ',
|
|
650
|
+
c: ' ',
|
|
651
|
+
e: ' ',
|
|
652
|
+
k: ' ',
|
|
653
|
+
l: ' ',
|
|
654
|
+
p: ' ',
|
|
655
|
+
P: ' '
|
|
656
|
+
};
|
|
657
|
+
const formatCodes = {
|
|
658
|
+
a: (d) => dayNamesShort[d.getDay()],
|
|
659
|
+
A: (d) => dayNames[d.getDay()],
|
|
660
|
+
b: (d) => monthNamesShort[d.getMonth()],
|
|
661
|
+
B: (d) => monthNames[d.getMonth()],
|
|
662
|
+
c: (d) => d.toLocaleString(),
|
|
663
|
+
C: (d) => century(d),
|
|
664
|
+
d: (d) => d.getDate(),
|
|
665
|
+
e: (d) => d.getDate(),
|
|
666
|
+
H: (d) => d.getHours(),
|
|
667
|
+
I: (d) => String(d.getHours() % 12 || 12),
|
|
668
|
+
j: (d) => getDayOfYear(d),
|
|
669
|
+
k: (d) => d.getHours(),
|
|
670
|
+
l: (d) => String(d.getHours() % 12 || 12),
|
|
671
|
+
L: (d) => d.getMilliseconds(),
|
|
672
|
+
m: (d) => d.getMonth() + 1,
|
|
673
|
+
M: (d) => d.getMinutes(),
|
|
674
|
+
N: (d, opts) => {
|
|
675
|
+
const width = Number(opts.width) || 9;
|
|
676
|
+
const str = String(d.getMilliseconds()).slice(0, width);
|
|
677
|
+
return padEnd(str, width, '0');
|
|
678
|
+
},
|
|
679
|
+
p: (d) => (d.getHours() < 12 ? 'AM' : 'PM'),
|
|
680
|
+
P: (d) => (d.getHours() < 12 ? 'am' : 'pm'),
|
|
681
|
+
q: (d) => getSuffix(d),
|
|
682
|
+
s: (d) => Math.round(d.getTime() / 1000),
|
|
683
|
+
S: (d) => d.getSeconds(),
|
|
684
|
+
u: (d) => d.getDay() || 7,
|
|
685
|
+
U: (d) => getWeekOfYear(d, 0),
|
|
686
|
+
w: (d) => d.getDay(),
|
|
687
|
+
W: (d) => getWeekOfYear(d, 1),
|
|
688
|
+
x: (d) => d.toLocaleDateString(),
|
|
689
|
+
X: (d) => d.toLocaleTimeString(),
|
|
690
|
+
y: (d) => d.getFullYear().toString().slice(2, 4),
|
|
691
|
+
Y: (d) => d.getFullYear(),
|
|
692
|
+
z: (d, opts) => {
|
|
693
|
+
const nOffset = Math.abs(d.getTimezoneOffset());
|
|
694
|
+
const h = Math.floor(nOffset / 60);
|
|
695
|
+
const m = nOffset % 60;
|
|
696
|
+
return (d.getTimezoneOffset() > 0 ? '-' : '+') +
|
|
697
|
+
padStart(h, 2, '0') +
|
|
698
|
+
(opts.flags[':'] ? ':' : '') +
|
|
699
|
+
padStart(m, 2, '0');
|
|
700
|
+
},
|
|
701
|
+
't': () => '\t',
|
|
702
|
+
'n': () => '\n',
|
|
703
|
+
'%': () => '%'
|
|
704
|
+
};
|
|
705
|
+
formatCodes.h = formatCodes.b;
|
|
706
|
+
function strftime (d, formatStr) {
|
|
707
|
+
let output = '';
|
|
708
|
+
let remaining = formatStr;
|
|
709
|
+
let match;
|
|
710
|
+
while ((match = rFormat.exec(remaining))) {
|
|
711
|
+
output += remaining.slice(0, match.index);
|
|
712
|
+
remaining = remaining.slice(match.index + match[0].length);
|
|
713
|
+
output += format(d, match);
|
|
714
|
+
}
|
|
715
|
+
return output + remaining;
|
|
716
|
+
}
|
|
717
|
+
function format(d, match) {
|
|
718
|
+
const [input, flagStr = '', width, modifier, conversion] = match;
|
|
719
|
+
const convert = formatCodes[conversion];
|
|
720
|
+
if (!convert)
|
|
721
|
+
return input;
|
|
722
|
+
const flags = {};
|
|
723
|
+
for (const flag of flagStr)
|
|
724
|
+
flags[flag] = true;
|
|
725
|
+
let ret = String(convert(d, { flags, width, modifier }));
|
|
726
|
+
let padChar = padChars[conversion] || '0';
|
|
727
|
+
let padWidth = width || padWidths[conversion] || 0;
|
|
728
|
+
if (flags['^'])
|
|
729
|
+
ret = ret.toUpperCase();
|
|
730
|
+
else if (flags['#'])
|
|
731
|
+
ret = changeCase(ret);
|
|
732
|
+
if (flags['_'])
|
|
733
|
+
padChar = ' ';
|
|
734
|
+
else if (flags['0'])
|
|
735
|
+
padChar = '0';
|
|
736
|
+
if (flags['-'])
|
|
737
|
+
padWidth = 0;
|
|
738
|
+
return padStart(ret, padWidth, padChar);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// one minute in milliseconds
|
|
742
|
+
const OneMinute = 60000;
|
|
743
|
+
const hostTimezoneOffset = new Date().getTimezoneOffset();
|
|
744
|
+
const ISO8601_TIMEZONE_PATTERN = /([zZ]|([+-])(\d{2}):(\d{2}))$/;
|
|
745
|
+
/**
|
|
746
|
+
* A date implementation with timezone info, just like Ruby date
|
|
747
|
+
*
|
|
748
|
+
* Implementation:
|
|
749
|
+
* - create a Date offset by it's timezone difference, avoiding overriding a bunch of methods
|
|
750
|
+
* - rewrite getTimezoneOffset() to trick strftime
|
|
751
|
+
*/
|
|
752
|
+
class TimezoneDate {
|
|
753
|
+
constructor(init, timezoneOffset) {
|
|
754
|
+
if (init instanceof TimezoneDate) {
|
|
755
|
+
this.date = init.date;
|
|
756
|
+
timezoneOffset = init.timezoneOffset;
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
const diff = (hostTimezoneOffset - timezoneOffset) * OneMinute;
|
|
760
|
+
const time = new Date(init).getTime() + diff;
|
|
761
|
+
this.date = new Date(time);
|
|
762
|
+
}
|
|
763
|
+
this.timezoneOffset = timezoneOffset;
|
|
764
|
+
}
|
|
765
|
+
getTime() {
|
|
766
|
+
return this.date.getTime();
|
|
767
|
+
}
|
|
768
|
+
getMilliseconds() {
|
|
769
|
+
return this.date.getMilliseconds();
|
|
770
|
+
}
|
|
771
|
+
getSeconds() {
|
|
772
|
+
return this.date.getSeconds();
|
|
773
|
+
}
|
|
774
|
+
getMinutes() {
|
|
775
|
+
return this.date.getMinutes();
|
|
776
|
+
}
|
|
777
|
+
getHours() {
|
|
778
|
+
return this.date.getHours();
|
|
779
|
+
}
|
|
780
|
+
getDay() {
|
|
781
|
+
return this.date.getDay();
|
|
782
|
+
}
|
|
783
|
+
getDate() {
|
|
784
|
+
return this.date.getDate();
|
|
785
|
+
}
|
|
786
|
+
getMonth() {
|
|
787
|
+
return this.date.getMonth();
|
|
788
|
+
}
|
|
789
|
+
getFullYear() {
|
|
790
|
+
return this.date.getFullYear();
|
|
791
|
+
}
|
|
792
|
+
toLocaleTimeString(locale) {
|
|
793
|
+
return this.date.toLocaleTimeString(locale);
|
|
794
|
+
}
|
|
795
|
+
toLocaleDateString(locale) {
|
|
796
|
+
return this.date.toLocaleDateString(locale);
|
|
797
|
+
}
|
|
798
|
+
getTimezoneOffset() {
|
|
799
|
+
return this.timezoneOffset;
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Create a Date object fixed to it's declared Timezone. Both
|
|
803
|
+
* - 2021-08-06T02:29:00.000Z and
|
|
804
|
+
* - 2021-08-06T02:29:00.000+08:00
|
|
805
|
+
* will always be displayed as
|
|
806
|
+
* - 2021-08-06 02:29:00
|
|
807
|
+
* regardless timezoneOffset in JavaScript realm
|
|
808
|
+
*
|
|
809
|
+
* The implementation hack:
|
|
810
|
+
* Instead of calling `.getMonth()`/`.getUTCMonth()` respect to `preserveTimezones`,
|
|
811
|
+
* we create a different Date to trick strftime, it's both simpler and more performant.
|
|
812
|
+
* Given that a template is expected to be parsed fewer times than rendered.
|
|
813
|
+
*/
|
|
814
|
+
static createDateFixedToTimezone(dateString) {
|
|
815
|
+
const m = dateString.match(ISO8601_TIMEZONE_PATTERN);
|
|
816
|
+
// representing a UTC timestamp
|
|
817
|
+
if (m && m[1] === 'Z') {
|
|
818
|
+
return new TimezoneDate(+new Date(dateString), 0);
|
|
819
|
+
}
|
|
820
|
+
// has a timezone specified
|
|
821
|
+
if (m && m[2] && m[3] && m[4]) {
|
|
822
|
+
const [, , sign, hours, minutes] = m;
|
|
823
|
+
const delta = (sign === '+' ? -1 : 1) * (parseInt(hours, 10) * 60 + parseInt(minutes, 10));
|
|
824
|
+
return new TimezoneDate(+new Date(dateString), delta);
|
|
825
|
+
}
|
|
826
|
+
return new Date(dateString);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function date(v, format, timeZoneOffset) {
|
|
831
|
+
const opts = this.context.opts;
|
|
832
|
+
let date;
|
|
833
|
+
v = toValue(v);
|
|
834
|
+
format = stringify(format);
|
|
835
|
+
if (v === 'now' || v === 'today') {
|
|
836
|
+
date = new Date();
|
|
837
|
+
}
|
|
838
|
+
else if (isNumber(v)) {
|
|
839
|
+
date = new Date(v * 1000);
|
|
840
|
+
}
|
|
841
|
+
else if (isString(v)) {
|
|
842
|
+
if (/^\d+$/.test(v)) {
|
|
843
|
+
date = new Date(+v * 1000);
|
|
844
|
+
}
|
|
845
|
+
else if (opts.preserveTimezones) {
|
|
846
|
+
date = TimezoneDate.createDateFixedToTimezone(v);
|
|
847
|
+
}
|
|
848
|
+
else {
|
|
849
|
+
date = new Date(v);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
date = v;
|
|
854
|
+
}
|
|
855
|
+
if (!isValidDate(date))
|
|
856
|
+
return v;
|
|
857
|
+
if (timeZoneOffset !== undefined) {
|
|
858
|
+
date = new TimezoneDate(date, timeZoneOffset);
|
|
859
|
+
}
|
|
860
|
+
else if (opts.timezoneOffset !== undefined) {
|
|
861
|
+
date = new TimezoneDate(date, opts.timezoneOffset);
|
|
862
|
+
}
|
|
863
|
+
return strftime(date, format);
|
|
864
|
+
}
|
|
865
|
+
function isValidDate(date) {
|
|
866
|
+
return (date instanceof Date || date instanceof TimezoneDate) && !isNaN(date.getTime());
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
function Default(value, defaultValue, ...args) {
|
|
870
|
+
value = toValue(value);
|
|
871
|
+
if (isArray(value) || isString(value))
|
|
872
|
+
return value.length ? value : defaultValue;
|
|
873
|
+
if (value === false && (new Map(args)).get('allow_false'))
|
|
874
|
+
return false;
|
|
875
|
+
return isFalsy(value, this.context) ? defaultValue : value;
|
|
876
|
+
}
|
|
877
|
+
function json(value) {
|
|
878
|
+
return JSON.stringify(value);
|
|
879
|
+
}
|
|
880
|
+
const raw$1 = identify;
|
|
881
|
+
|
|
882
|
+
class LiquidError extends Error {
|
|
883
|
+
constructor(err, token) {
|
|
884
|
+
super(err.message);
|
|
885
|
+
this.originalError = err;
|
|
886
|
+
this.token = token;
|
|
887
|
+
this.context = '';
|
|
888
|
+
}
|
|
889
|
+
update() {
|
|
890
|
+
const err = this.originalError;
|
|
891
|
+
this.context = mkContext(this.token);
|
|
892
|
+
this.message = mkMessage(err.message, this.token);
|
|
893
|
+
this.stack = this.message + '\n' + this.context +
|
|
894
|
+
'\n' + this.stack + '\nFrom ' + err.stack;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
class TokenizationError extends LiquidError {
|
|
898
|
+
constructor(message, token) {
|
|
899
|
+
super(new Error(message), token);
|
|
900
|
+
this.name = 'TokenizationError';
|
|
901
|
+
super.update();
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
class ParseError extends LiquidError {
|
|
905
|
+
constructor(err, token) {
|
|
906
|
+
super(err, token);
|
|
907
|
+
this.name = 'ParseError';
|
|
908
|
+
this.message = err.message;
|
|
909
|
+
super.update();
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
class RenderError extends LiquidError {
|
|
913
|
+
constructor(err, tpl) {
|
|
914
|
+
super(err, tpl.token);
|
|
915
|
+
this.name = 'RenderError';
|
|
916
|
+
this.message = err.message;
|
|
917
|
+
super.update();
|
|
918
|
+
}
|
|
919
|
+
static is(obj) {
|
|
920
|
+
return obj.name === 'RenderError';
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
class UndefinedVariableError extends LiquidError {
|
|
924
|
+
constructor(err, token) {
|
|
925
|
+
super(err, token);
|
|
926
|
+
this.name = 'UndefinedVariableError';
|
|
927
|
+
this.message = err.message;
|
|
928
|
+
super.update();
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
// only used internally; raised where we don't have token information,
|
|
932
|
+
// so it can't be an UndefinedVariableError.
|
|
933
|
+
class InternalUndefinedVariableError extends Error {
|
|
934
|
+
constructor(variableName) {
|
|
935
|
+
super(`undefined variable: ${variableName}`);
|
|
936
|
+
this.name = 'InternalUndefinedVariableError';
|
|
937
|
+
this.variableName = variableName;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
class AssertionError extends Error {
|
|
941
|
+
constructor(message) {
|
|
942
|
+
super(message);
|
|
943
|
+
this.name = 'AssertionError';
|
|
944
|
+
this.message = message + '';
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
function mkContext(token) {
|
|
948
|
+
const [line] = token.getPosition();
|
|
949
|
+
const lines = token.input.split('\n');
|
|
950
|
+
const begin = Math.max(line - 2, 1);
|
|
951
|
+
const end = Math.min(line + 3, lines.length);
|
|
952
|
+
const context = range(begin, end + 1)
|
|
953
|
+
.map(lineNumber => {
|
|
954
|
+
const indicator = (lineNumber === line) ? '>> ' : ' ';
|
|
955
|
+
const num = padStart(String(lineNumber), String(end).length);
|
|
956
|
+
const text = lines[lineNumber - 1];
|
|
957
|
+
return `${indicator}${num}| ${text}`;
|
|
958
|
+
})
|
|
959
|
+
.join('\n');
|
|
960
|
+
return context;
|
|
961
|
+
}
|
|
962
|
+
function mkMessage(msg, token) {
|
|
963
|
+
if (token.file)
|
|
964
|
+
msg += `, file:${token.file}`;
|
|
965
|
+
const [line, col] = token.getPosition();
|
|
966
|
+
msg += `, line:${line}, col:${col}`;
|
|
967
|
+
return msg;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
function assert(predicate, message) {
|
|
971
|
+
if (!predicate) {
|
|
972
|
+
const msg = typeof message === 'function'
|
|
973
|
+
? message()
|
|
974
|
+
: (message || `expect ${predicate} to be true`);
|
|
975
|
+
throw new AssertionError(msg);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* String related filters
|
|
981
|
+
*
|
|
982
|
+
* * prefer stringify() to String() since `undefined`, `null` should eval ''
|
|
983
|
+
*/
|
|
984
|
+
function append(v, arg) {
|
|
985
|
+
assert(arguments.length === 2, 'append expect 2 arguments');
|
|
986
|
+
return stringify(v) + stringify(arg);
|
|
987
|
+
}
|
|
988
|
+
function prepend(v, arg) {
|
|
989
|
+
assert(arguments.length === 2, 'prepend expect 2 arguments');
|
|
990
|
+
return stringify(arg) + stringify(v);
|
|
991
|
+
}
|
|
992
|
+
function lstrip(v, chars) {
|
|
993
|
+
if (chars) {
|
|
994
|
+
chars = escapeRegExp(stringify(chars));
|
|
995
|
+
return stringify(v).replace(new RegExp(`^[${chars}]+`, 'g'), '');
|
|
996
|
+
}
|
|
997
|
+
return stringify(v).replace(/^\s+/, '');
|
|
998
|
+
}
|
|
999
|
+
function downcase(v) {
|
|
1000
|
+
return stringify(v).toLowerCase();
|
|
1001
|
+
}
|
|
1002
|
+
function upcase(str) {
|
|
1003
|
+
return stringify(str).toUpperCase();
|
|
1004
|
+
}
|
|
1005
|
+
function remove(v, arg) {
|
|
1006
|
+
return stringify(v).split(String(arg)).join('');
|
|
1007
|
+
}
|
|
1008
|
+
function removeFirst(v, l) {
|
|
1009
|
+
return stringify(v).replace(String(l), '');
|
|
1010
|
+
}
|
|
1011
|
+
function rstrip(str, chars) {
|
|
1012
|
+
if (chars) {
|
|
1013
|
+
chars = escapeRegExp(stringify(chars));
|
|
1014
|
+
return stringify(str).replace(new RegExp(`[${chars}]+$`, 'g'), '');
|
|
1015
|
+
}
|
|
1016
|
+
return stringify(str).replace(/\s+$/, '');
|
|
1017
|
+
}
|
|
1018
|
+
function split(v, arg) {
|
|
1019
|
+
const arr = stringify(v).split(String(arg));
|
|
1020
|
+
// align to ruby split, which is the behavior of shopify/liquid
|
|
1021
|
+
// see: https://ruby-doc.org/core-2.4.0/String.html#method-i-split
|
|
1022
|
+
while (arr.length && arr[arr.length - 1] === '')
|
|
1023
|
+
arr.pop();
|
|
1024
|
+
return arr;
|
|
1025
|
+
}
|
|
1026
|
+
function strip(v, chars) {
|
|
1027
|
+
if (chars) {
|
|
1028
|
+
chars = escapeRegExp(stringify(chars));
|
|
1029
|
+
return stringify(v)
|
|
1030
|
+
.replace(new RegExp(`^[${chars}]+`, 'g'), '')
|
|
1031
|
+
.replace(new RegExp(`[${chars}]+$`, 'g'), '');
|
|
1032
|
+
}
|
|
1033
|
+
return stringify(v).trim();
|
|
1034
|
+
}
|
|
1035
|
+
function stripNewlines(v) {
|
|
1036
|
+
return stringify(v).replace(/\n/g, '');
|
|
1037
|
+
}
|
|
1038
|
+
function capitalize(str) {
|
|
1039
|
+
str = stringify(str);
|
|
1040
|
+
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
1041
|
+
}
|
|
1042
|
+
function replace(v, pattern, replacement) {
|
|
1043
|
+
return stringify(v).split(String(pattern)).join(replacement);
|
|
1044
|
+
}
|
|
1045
|
+
function replaceFirst(v, arg1, arg2) {
|
|
1046
|
+
return stringify(v).replace(String(arg1), arg2);
|
|
1047
|
+
}
|
|
1048
|
+
function truncate(v, l = 50, o = '...') {
|
|
1049
|
+
v = stringify(v);
|
|
1050
|
+
if (v.length <= l)
|
|
1051
|
+
return v;
|
|
1052
|
+
return v.substring(0, l - o.length) + o;
|
|
1053
|
+
}
|
|
1054
|
+
function truncatewords(v, words = 15, o = '...') {
|
|
1055
|
+
const arr = stringify(v).split(/\s+/);
|
|
1056
|
+
if (words <= 0)
|
|
1057
|
+
words = 1;
|
|
1058
|
+
let ret = arr.slice(0, words).join(' ');
|
|
1059
|
+
if (arr.length >= words)
|
|
1060
|
+
ret += o;
|
|
1061
|
+
return ret;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
var builtinFilters = /*#__PURE__*/Object.freeze({
|
|
1065
|
+
__proto__: null,
|
|
1066
|
+
escape: escape,
|
|
1067
|
+
escapeOnce: escapeOnce,
|
|
1068
|
+
newlineToBr: newlineToBr,
|
|
1069
|
+
stripHtml: stripHtml,
|
|
1070
|
+
abs: abs,
|
|
1071
|
+
atLeast: atLeast,
|
|
1072
|
+
atMost: atMost,
|
|
1073
|
+
ceil: ceil,
|
|
1074
|
+
dividedBy: dividedBy,
|
|
1075
|
+
floor: floor,
|
|
1076
|
+
minus: minus,
|
|
1077
|
+
modulo: modulo,
|
|
1078
|
+
times: times,
|
|
1079
|
+
round: round,
|
|
1080
|
+
plus: plus,
|
|
1081
|
+
urlDecode: urlDecode,
|
|
1082
|
+
urlEncode: urlEncode,
|
|
1083
|
+
join: join,
|
|
1084
|
+
last: last,
|
|
1085
|
+
first: first,
|
|
1086
|
+
reverse: reverse,
|
|
1087
|
+
sort: sort,
|
|
1088
|
+
sortNatural: sortNatural,
|
|
1089
|
+
size: size,
|
|
1090
|
+
map: map,
|
|
1091
|
+
compact: compact,
|
|
1092
|
+
concat: concat,
|
|
1093
|
+
slice: slice,
|
|
1094
|
+
where: where,
|
|
1095
|
+
uniq: uniq,
|
|
1096
|
+
date: date,
|
|
1097
|
+
Default: Default,
|
|
1098
|
+
json: json,
|
|
1099
|
+
raw: raw$1,
|
|
1100
|
+
append: append,
|
|
1101
|
+
prepend: prepend,
|
|
1102
|
+
lstrip: lstrip,
|
|
1103
|
+
downcase: downcase,
|
|
1104
|
+
upcase: upcase,
|
|
1105
|
+
remove: remove,
|
|
1106
|
+
removeFirst: removeFirst,
|
|
1107
|
+
rstrip: rstrip,
|
|
1108
|
+
split: split,
|
|
1109
|
+
strip: strip,
|
|
1110
|
+
stripNewlines: stripNewlines,
|
|
1111
|
+
capitalize: capitalize,
|
|
1112
|
+
replace: replace,
|
|
1113
|
+
replaceFirst: replaceFirst,
|
|
1114
|
+
truncate: truncate,
|
|
1115
|
+
truncatewords: truncatewords
|
|
1116
|
+
});
|
|
1117
|
+
|
|
1118
|
+
var TokenKind;
|
|
1119
|
+
(function (TokenKind) {
|
|
1120
|
+
TokenKind[TokenKind["Number"] = 1] = "Number";
|
|
1121
|
+
TokenKind[TokenKind["Literal"] = 2] = "Literal";
|
|
1122
|
+
TokenKind[TokenKind["Tag"] = 4] = "Tag";
|
|
1123
|
+
TokenKind[TokenKind["Output"] = 8] = "Output";
|
|
1124
|
+
TokenKind[TokenKind["HTML"] = 16] = "HTML";
|
|
1125
|
+
TokenKind[TokenKind["Filter"] = 32] = "Filter";
|
|
1126
|
+
TokenKind[TokenKind["Hash"] = 64] = "Hash";
|
|
1127
|
+
TokenKind[TokenKind["PropertyAccess"] = 128] = "PropertyAccess";
|
|
1128
|
+
TokenKind[TokenKind["Word"] = 256] = "Word";
|
|
1129
|
+
TokenKind[TokenKind["Range"] = 512] = "Range";
|
|
1130
|
+
TokenKind[TokenKind["Quoted"] = 1024] = "Quoted";
|
|
1131
|
+
TokenKind[TokenKind["Operator"] = 2048] = "Operator";
|
|
1132
|
+
TokenKind[TokenKind["Delimited"] = 12] = "Delimited";
|
|
1133
|
+
})(TokenKind || (TokenKind = {}));
|
|
1134
|
+
|
|
1135
|
+
function isDelimitedToken(val) {
|
|
1136
|
+
return !!(getKind(val) & TokenKind.Delimited);
|
|
1137
|
+
}
|
|
1138
|
+
function isOperatorToken(val) {
|
|
1139
|
+
return getKind(val) === TokenKind.Operator;
|
|
1140
|
+
}
|
|
1141
|
+
function isHTMLToken(val) {
|
|
1142
|
+
return getKind(val) === TokenKind.HTML;
|
|
1143
|
+
}
|
|
1144
|
+
function isOutputToken(val) {
|
|
1145
|
+
return getKind(val) === TokenKind.Output;
|
|
1146
|
+
}
|
|
1147
|
+
function isTagToken(val) {
|
|
1148
|
+
return getKind(val) === TokenKind.Tag;
|
|
1149
|
+
}
|
|
1150
|
+
function isQuotedToken(val) {
|
|
1151
|
+
return getKind(val) === TokenKind.Quoted;
|
|
1152
|
+
}
|
|
1153
|
+
function isLiteralToken(val) {
|
|
1154
|
+
return getKind(val) === TokenKind.Literal;
|
|
1155
|
+
}
|
|
1156
|
+
function isNumberToken(val) {
|
|
1157
|
+
return getKind(val) === TokenKind.Number;
|
|
1158
|
+
}
|
|
1159
|
+
function isPropertyAccessToken(val) {
|
|
1160
|
+
return getKind(val) === TokenKind.PropertyAccess;
|
|
1161
|
+
}
|
|
1162
|
+
function isWordToken(val) {
|
|
1163
|
+
return getKind(val) === TokenKind.Word;
|
|
1164
|
+
}
|
|
1165
|
+
function isRangeToken(val) {
|
|
1166
|
+
return getKind(val) === TokenKind.Range;
|
|
1167
|
+
}
|
|
1168
|
+
function getKind(val) {
|
|
1169
|
+
return val ? val.kind : -1;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
var typeGuards = /*#__PURE__*/Object.freeze({
|
|
1173
|
+
__proto__: null,
|
|
1174
|
+
isDelimitedToken: isDelimitedToken,
|
|
1175
|
+
isOperatorToken: isOperatorToken,
|
|
1176
|
+
isHTMLToken: isHTMLToken,
|
|
1177
|
+
isOutputToken: isOutputToken,
|
|
1178
|
+
isTagToken: isTagToken,
|
|
1179
|
+
isQuotedToken: isQuotedToken,
|
|
1180
|
+
isLiteralToken: isLiteralToken,
|
|
1181
|
+
isNumberToken: isNumberToken,
|
|
1182
|
+
isPropertyAccessToken: isPropertyAccessToken,
|
|
1183
|
+
isWordToken: isWordToken,
|
|
1184
|
+
isRangeToken: isRangeToken
|
|
1185
|
+
});
|
|
1186
|
+
|
|
1187
|
+
class NullDrop extends Drop {
|
|
1188
|
+
equals(value) {
|
|
1189
|
+
return isNil(toValue(value));
|
|
1190
|
+
}
|
|
1191
|
+
gt() {
|
|
1192
|
+
return false;
|
|
1193
|
+
}
|
|
1194
|
+
geq() {
|
|
1195
|
+
return false;
|
|
1196
|
+
}
|
|
1197
|
+
lt() {
|
|
1198
|
+
return false;
|
|
1199
|
+
}
|
|
1200
|
+
leq() {
|
|
1201
|
+
return false;
|
|
1202
|
+
}
|
|
1203
|
+
valueOf() {
|
|
1204
|
+
return null;
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
class EmptyDrop extends Drop {
|
|
1209
|
+
equals(value) {
|
|
1210
|
+
if (value instanceof EmptyDrop)
|
|
1211
|
+
return false;
|
|
1212
|
+
value = toValue(value);
|
|
1213
|
+
if (isString(value) || isArray(value))
|
|
1214
|
+
return value.length === 0;
|
|
1215
|
+
if (isObject(value))
|
|
1216
|
+
return Object.keys(value).length === 0;
|
|
1217
|
+
return false;
|
|
1218
|
+
}
|
|
1219
|
+
gt() {
|
|
1220
|
+
return false;
|
|
1221
|
+
}
|
|
1222
|
+
geq() {
|
|
1223
|
+
return false;
|
|
1224
|
+
}
|
|
1225
|
+
lt() {
|
|
1226
|
+
return false;
|
|
1227
|
+
}
|
|
1228
|
+
leq() {
|
|
1229
|
+
return false;
|
|
1230
|
+
}
|
|
1231
|
+
valueOf() {
|
|
1232
|
+
return '';
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
class BlankDrop extends EmptyDrop {
|
|
1237
|
+
equals(value) {
|
|
1238
|
+
if (value === false)
|
|
1239
|
+
return true;
|
|
1240
|
+
if (isNil(toValue(value)))
|
|
1241
|
+
return true;
|
|
1242
|
+
if (isString(value))
|
|
1243
|
+
return /^\s*$/.test(value);
|
|
1244
|
+
return super.equals(value);
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
const nil = new NullDrop();
|
|
1249
|
+
const literalValues = {
|
|
1250
|
+
'true': true,
|
|
1251
|
+
'false': false,
|
|
1252
|
+
'nil': nil,
|
|
1253
|
+
'null': nil,
|
|
1254
|
+
'empty': new EmptyDrop(),
|
|
1255
|
+
'blank': new BlankDrop()
|
|
1256
|
+
};
|
|
1257
|
+
|
|
1258
|
+
const rHex = /[\da-fA-F]/;
|
|
1259
|
+
const rOct = /[0-7]/;
|
|
1260
|
+
const escapeChar = {
|
|
1261
|
+
b: '\b',
|
|
1262
|
+
f: '\f',
|
|
1263
|
+
n: '\n',
|
|
1264
|
+
r: '\r',
|
|
1265
|
+
t: '\t',
|
|
1266
|
+
v: '\x0B'
|
|
1267
|
+
};
|
|
1268
|
+
function hexVal(c) {
|
|
1269
|
+
const code = c.charCodeAt(0);
|
|
1270
|
+
if (code >= 97)
|
|
1271
|
+
return code - 87;
|
|
1272
|
+
if (code >= 65)
|
|
1273
|
+
return code - 55;
|
|
1274
|
+
return code - 48;
|
|
1275
|
+
}
|
|
1276
|
+
function parseStringLiteral(str) {
|
|
1277
|
+
let ret = '';
|
|
1278
|
+
for (let i = 1; i < str.length - 1; i++) {
|
|
1279
|
+
if (str[i] !== '\\') {
|
|
1280
|
+
ret += str[i];
|
|
1281
|
+
continue;
|
|
1282
|
+
}
|
|
1283
|
+
if (escapeChar[str[i + 1]] !== undefined) {
|
|
1284
|
+
ret += escapeChar[str[++i]];
|
|
1285
|
+
}
|
|
1286
|
+
else if (str[i + 1] === 'u') {
|
|
1287
|
+
let val = 0;
|
|
1288
|
+
let j = i + 2;
|
|
1289
|
+
while (j <= i + 5 && rHex.test(str[j])) {
|
|
1290
|
+
val = val * 16 + hexVal(str[j++]);
|
|
1291
|
+
}
|
|
1292
|
+
i = j - 1;
|
|
1293
|
+
ret += String.fromCharCode(val);
|
|
1294
|
+
}
|
|
1295
|
+
else if (!rOct.test(str[i + 1])) {
|
|
1296
|
+
ret += str[++i];
|
|
1297
|
+
}
|
|
1298
|
+
else {
|
|
1299
|
+
let j = i + 1;
|
|
1300
|
+
let val = 0;
|
|
1301
|
+
while (j <= i + 3 && rOct.test(str[j])) {
|
|
1302
|
+
val = val * 8 + hexVal(str[j++]);
|
|
1303
|
+
}
|
|
1304
|
+
i = j - 1;
|
|
1305
|
+
ret += String.fromCharCode(val);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
return ret;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// convert an async iterator to a Promise
|
|
1312
|
+
function toPromise(val) {
|
|
1313
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1314
|
+
if (!isIterator(val))
|
|
1315
|
+
return val;
|
|
1316
|
+
let value;
|
|
1317
|
+
let done = false;
|
|
1318
|
+
let next = 'next';
|
|
1319
|
+
do {
|
|
1320
|
+
const state = val[next](value);
|
|
1321
|
+
done = state.done;
|
|
1322
|
+
value = state.value;
|
|
1323
|
+
next = 'next';
|
|
1324
|
+
try {
|
|
1325
|
+
if (isIterator(value))
|
|
1326
|
+
value = toPromise(value);
|
|
1327
|
+
if (isPromise(value))
|
|
1328
|
+
value = yield value;
|
|
1329
|
+
}
|
|
1330
|
+
catch (err) {
|
|
1331
|
+
next = 'throw';
|
|
1332
|
+
value = err;
|
|
1333
|
+
}
|
|
1334
|
+
} while (!done);
|
|
1335
|
+
return value;
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
// convert an async iterator to a value in a synchronous manner
|
|
1339
|
+
function toValueSync(val) {
|
|
1340
|
+
if (!isIterator(val))
|
|
1341
|
+
return val;
|
|
1342
|
+
let value;
|
|
1343
|
+
let done = false;
|
|
1344
|
+
let next = 'next';
|
|
1345
|
+
do {
|
|
1346
|
+
const state = val[next](value);
|
|
1347
|
+
done = state.done;
|
|
1348
|
+
value = state.value;
|
|
1349
|
+
next = 'next';
|
|
1350
|
+
if (isIterator(value)) {
|
|
1351
|
+
try {
|
|
1352
|
+
value = toValueSync(value);
|
|
1353
|
+
}
|
|
1354
|
+
catch (err) {
|
|
1355
|
+
next = 'throw';
|
|
1356
|
+
value = err;
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
} while (!done);
|
|
1360
|
+
return value;
|
|
1361
|
+
}
|
|
1362
|
+
const toThenable = toPromise;
|
|
1363
|
+
|
|
1364
|
+
class Expression {
|
|
1365
|
+
constructor(tokens) {
|
|
1366
|
+
this.postfix = [...toPostfix(tokens)];
|
|
1367
|
+
}
|
|
1368
|
+
*evaluate(ctx, lenient) {
|
|
1369
|
+
assert(ctx, 'unable to evaluate: context not defined');
|
|
1370
|
+
const operands = [];
|
|
1371
|
+
for (const token of this.postfix) {
|
|
1372
|
+
if (isOperatorToken(token)) {
|
|
1373
|
+
const r = operands.pop();
|
|
1374
|
+
const l = operands.pop();
|
|
1375
|
+
const result = yield evalOperatorToken(ctx.opts.operators, token, l, r, ctx);
|
|
1376
|
+
operands.push(result);
|
|
1377
|
+
}
|
|
1378
|
+
else {
|
|
1379
|
+
operands.push(yield _evalToken(token, ctx, lenient && this.postfix.length === 1));
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
return operands[0];
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* @deprecated use `_evalToken` instead
|
|
1387
|
+
*/
|
|
1388
|
+
function* evalToken(token, ctx, lenient = false) {
|
|
1389
|
+
return toValueSync(_evalToken(token, ctx, lenient));
|
|
1390
|
+
}
|
|
1391
|
+
function* _evalToken(token, ctx, lenient = false) {
|
|
1392
|
+
if (isPropertyAccessToken(token))
|
|
1393
|
+
return yield evalPropertyAccessToken(token, ctx, lenient);
|
|
1394
|
+
if (isRangeToken(token))
|
|
1395
|
+
return yield evalRangeToken(token, ctx);
|
|
1396
|
+
if (isLiteralToken(token))
|
|
1397
|
+
return evalLiteralToken(token);
|
|
1398
|
+
if (isNumberToken(token))
|
|
1399
|
+
return evalNumberToken(token);
|
|
1400
|
+
if (isWordToken(token))
|
|
1401
|
+
return token.getText();
|
|
1402
|
+
if (isQuotedToken(token))
|
|
1403
|
+
return evalQuotedToken(token);
|
|
1404
|
+
}
|
|
1405
|
+
function* evalPropertyAccessToken(token, ctx, lenient) {
|
|
1406
|
+
const props = [];
|
|
1407
|
+
for (const prop of token.props) {
|
|
1408
|
+
props.push((yield _evalToken(prop, ctx, false)));
|
|
1409
|
+
}
|
|
1410
|
+
try {
|
|
1411
|
+
return yield ctx._get([token.propertyName, ...props]);
|
|
1412
|
+
}
|
|
1413
|
+
catch (e) {
|
|
1414
|
+
if (lenient && e.name === 'InternalUndefinedVariableError')
|
|
1415
|
+
return null;
|
|
1416
|
+
throw (new UndefinedVariableError(e, token));
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
function evalNumberToken(token) {
|
|
1420
|
+
const str = token.whole.content + '.' + (token.decimal ? token.decimal.content : '');
|
|
1421
|
+
return Number(str);
|
|
1422
|
+
}
|
|
1423
|
+
function evalQuotedToken(token) {
|
|
1424
|
+
return parseStringLiteral(token.getText());
|
|
1425
|
+
}
|
|
1426
|
+
function evalOperatorToken(operators, token, lhs, rhs, ctx) {
|
|
1427
|
+
const impl = operators[token.operator];
|
|
1428
|
+
return impl(lhs, rhs, ctx);
|
|
1429
|
+
}
|
|
1430
|
+
function evalLiteralToken(token) {
|
|
1431
|
+
return literalValues[token.literal];
|
|
1432
|
+
}
|
|
1433
|
+
function* evalRangeToken(token, ctx) {
|
|
1434
|
+
const low = yield _evalToken(token.lhs, ctx);
|
|
1435
|
+
const high = yield _evalToken(token.rhs, ctx);
|
|
1436
|
+
return range(+low, +high + 1);
|
|
1437
|
+
}
|
|
1438
|
+
function* toPostfix(tokens) {
|
|
1439
|
+
const ops = [];
|
|
1440
|
+
for (const token of tokens) {
|
|
1441
|
+
if (isOperatorToken(token)) {
|
|
1442
|
+
while (ops.length && ops[ops.length - 1].getPrecedence() > token.getPrecedence()) {
|
|
1443
|
+
yield ops.pop();
|
|
1444
|
+
}
|
|
1445
|
+
ops.push(token);
|
|
1446
|
+
}
|
|
1447
|
+
else
|
|
1448
|
+
yield token;
|
|
1449
|
+
}
|
|
1450
|
+
while (ops.length) {
|
|
1451
|
+
yield ops.pop();
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
class Token {
|
|
1456
|
+
constructor(kind, input, begin, end, file) {
|
|
1457
|
+
this.kind = kind;
|
|
1458
|
+
this.input = input;
|
|
1459
|
+
this.begin = begin;
|
|
1460
|
+
this.end = end;
|
|
1461
|
+
this.file = file;
|
|
1462
|
+
}
|
|
1463
|
+
getText() {
|
|
1464
|
+
return this.input.slice(this.begin, this.end);
|
|
1465
|
+
}
|
|
1466
|
+
getPosition() {
|
|
1467
|
+
let [row, col] = [1, 1];
|
|
1468
|
+
for (let i = 0; i < this.begin; i++) {
|
|
1469
|
+
if (this.input[i] === '\n') {
|
|
1470
|
+
row++;
|
|
1471
|
+
col = 1;
|
|
1472
|
+
}
|
|
1473
|
+
else
|
|
1474
|
+
col++;
|
|
1475
|
+
}
|
|
1476
|
+
return [row, col];
|
|
1477
|
+
}
|
|
1478
|
+
size() {
|
|
1479
|
+
return this.end - this.begin;
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
class DelimitedToken extends Token {
|
|
1484
|
+
constructor(kind, content, input, begin, end, trimLeft, trimRight, file) {
|
|
1485
|
+
super(kind, input, begin, end, file);
|
|
1486
|
+
this.trimLeft = false;
|
|
1487
|
+
this.trimRight = false;
|
|
1488
|
+
this.content = this.getText();
|
|
1489
|
+
const tl = content[0] === '-';
|
|
1490
|
+
const tr = last$1(content) === '-';
|
|
1491
|
+
this.content = content
|
|
1492
|
+
.slice(tl ? 1 : 0, tr ? -1 : content.length)
|
|
1493
|
+
.trim();
|
|
1494
|
+
this.trimLeft = tl || trimLeft;
|
|
1495
|
+
this.trimRight = tr || trimRight;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
function whiteSpaceCtrl(tokens, options) {
|
|
1500
|
+
let inRaw = false;
|
|
1501
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
1502
|
+
const token = tokens[i];
|
|
1503
|
+
if (!isDelimitedToken(token))
|
|
1504
|
+
continue;
|
|
1505
|
+
if (!inRaw && token.trimLeft) {
|
|
1506
|
+
trimLeft(tokens[i - 1], options.greedy);
|
|
1507
|
+
}
|
|
1508
|
+
if (isTagToken(token)) {
|
|
1509
|
+
if (token.name === 'raw')
|
|
1510
|
+
inRaw = true;
|
|
1511
|
+
else if (token.name === 'endraw')
|
|
1512
|
+
inRaw = false;
|
|
1513
|
+
}
|
|
1514
|
+
if (!inRaw && token.trimRight) {
|
|
1515
|
+
trimRight(tokens[i + 1], options.greedy);
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
function trimLeft(token, greedy) {
|
|
1520
|
+
if (!token || !isHTMLToken(token))
|
|
1521
|
+
return;
|
|
1522
|
+
const mask = greedy ? BLANK : INLINE_BLANK;
|
|
1523
|
+
while (TYPES[token.input.charCodeAt(token.end - 1 - token.trimRight)] & mask)
|
|
1524
|
+
token.trimRight++;
|
|
1525
|
+
}
|
|
1526
|
+
function trimRight(token, greedy) {
|
|
1527
|
+
if (!token || !isHTMLToken(token))
|
|
1528
|
+
return;
|
|
1529
|
+
const mask = greedy ? BLANK : INLINE_BLANK;
|
|
1530
|
+
while (TYPES[token.input.charCodeAt(token.begin + token.trimLeft)] & mask)
|
|
1531
|
+
token.trimLeft++;
|
|
1532
|
+
if (token.input.charAt(token.begin + token.trimLeft) === '\n')
|
|
1533
|
+
token.trimLeft++;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
class NumberToken extends Token {
|
|
1537
|
+
constructor(whole, decimal) {
|
|
1538
|
+
super(TokenKind.Number, whole.input, whole.begin, decimal ? decimal.end : whole.end, whole.file);
|
|
1539
|
+
this.whole = whole;
|
|
1540
|
+
this.decimal = decimal;
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
class IdentifierToken extends Token {
|
|
1545
|
+
constructor(input, begin, end, file) {
|
|
1546
|
+
super(TokenKind.Word, input, begin, end, file);
|
|
1547
|
+
this.input = input;
|
|
1548
|
+
this.begin = begin;
|
|
1549
|
+
this.end = end;
|
|
1550
|
+
this.file = file;
|
|
1551
|
+
this.content = this.getText();
|
|
1552
|
+
}
|
|
1553
|
+
isNumber(allowSign = false) {
|
|
1554
|
+
const begin = allowSign && TYPES[this.input.charCodeAt(this.begin)] & SIGN
|
|
1555
|
+
? this.begin + 1
|
|
1556
|
+
: this.begin;
|
|
1557
|
+
for (let i = begin; i < this.end; i++) {
|
|
1558
|
+
if (!(TYPES[this.input.charCodeAt(i)] & NUMBER))
|
|
1559
|
+
return false;
|
|
1560
|
+
}
|
|
1561
|
+
return true;
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
class LiteralToken extends Token {
|
|
1566
|
+
constructor(input, begin, end, file) {
|
|
1567
|
+
super(TokenKind.Literal, input, begin, end, file);
|
|
1568
|
+
this.input = input;
|
|
1569
|
+
this.begin = begin;
|
|
1570
|
+
this.end = end;
|
|
1571
|
+
this.file = file;
|
|
1572
|
+
this.literal = this.getText();
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
const precedence = {
|
|
1577
|
+
'==': 1,
|
|
1578
|
+
'!=': 1,
|
|
1579
|
+
'>': 1,
|
|
1580
|
+
'<': 1,
|
|
1581
|
+
'>=': 1,
|
|
1582
|
+
'<=': 1,
|
|
1583
|
+
'contains': 1,
|
|
1584
|
+
'and': 0,
|
|
1585
|
+
'or': 0
|
|
1586
|
+
};
|
|
1587
|
+
class OperatorToken extends Token {
|
|
1588
|
+
constructor(input, begin, end, file) {
|
|
1589
|
+
super(TokenKind.Operator, input, begin, end, file);
|
|
1590
|
+
this.input = input;
|
|
1591
|
+
this.begin = begin;
|
|
1592
|
+
this.end = end;
|
|
1593
|
+
this.file = file;
|
|
1594
|
+
this.operator = this.getText();
|
|
1595
|
+
}
|
|
1596
|
+
getPrecedence() {
|
|
1597
|
+
const key = this.getText();
|
|
1598
|
+
return key in precedence ? precedence[key] : 1;
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
class PropertyAccessToken extends Token {
|
|
1603
|
+
constructor(variable, props, end) {
|
|
1604
|
+
super(TokenKind.PropertyAccess, variable.input, variable.begin, end, variable.file);
|
|
1605
|
+
this.variable = variable;
|
|
1606
|
+
this.props = props;
|
|
1607
|
+
this.propertyName = this.variable instanceof IdentifierToken
|
|
1608
|
+
? this.variable.getText()
|
|
1609
|
+
: parseStringLiteral(this.variable.getText());
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
class FilterToken extends Token {
|
|
1614
|
+
constructor(name, args, input, begin, end, file) {
|
|
1615
|
+
super(TokenKind.Filter, input, begin, end, file);
|
|
1616
|
+
this.name = name;
|
|
1617
|
+
this.args = args;
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
class HashToken extends Token {
|
|
1622
|
+
constructor(input, begin, end, name, value, file) {
|
|
1623
|
+
super(TokenKind.Hash, input, begin, end, file);
|
|
1624
|
+
this.input = input;
|
|
1625
|
+
this.begin = begin;
|
|
1626
|
+
this.end = end;
|
|
1627
|
+
this.name = name;
|
|
1628
|
+
this.value = value;
|
|
1629
|
+
this.file = file;
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
class QuotedToken extends Token {
|
|
1634
|
+
constructor(input, begin, end, file) {
|
|
1635
|
+
super(TokenKind.Quoted, input, begin, end, file);
|
|
1636
|
+
this.input = input;
|
|
1637
|
+
this.begin = begin;
|
|
1638
|
+
this.end = end;
|
|
1639
|
+
this.file = file;
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
class HTMLToken extends Token {
|
|
1644
|
+
constructor(input, begin, end, file) {
|
|
1645
|
+
super(TokenKind.HTML, input, begin, end, file);
|
|
1646
|
+
this.input = input;
|
|
1647
|
+
this.begin = begin;
|
|
1648
|
+
this.end = end;
|
|
1649
|
+
this.file = file;
|
|
1650
|
+
this.trimLeft = 0;
|
|
1651
|
+
this.trimRight = 0;
|
|
1652
|
+
}
|
|
1653
|
+
getContent() {
|
|
1654
|
+
return this.input.slice(this.begin + this.trimLeft, this.end - this.trimRight);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
class RangeToken extends Token {
|
|
1659
|
+
constructor(input, begin, end, lhs, rhs, file) {
|
|
1660
|
+
super(TokenKind.Range, input, begin, end, file);
|
|
1661
|
+
this.input = input;
|
|
1662
|
+
this.begin = begin;
|
|
1663
|
+
this.end = end;
|
|
1664
|
+
this.lhs = lhs;
|
|
1665
|
+
this.rhs = rhs;
|
|
1666
|
+
this.file = file;
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
class OutputToken extends DelimitedToken {
|
|
1671
|
+
constructor(input, begin, end, options, file) {
|
|
1672
|
+
const { trimOutputLeft, trimOutputRight, outputDelimiterLeft, outputDelimiterRight } = options;
|
|
1673
|
+
const value = input.slice(begin + outputDelimiterLeft.length, end - outputDelimiterRight.length);
|
|
1674
|
+
super(TokenKind.Output, value, input, begin, end, trimOutputLeft, trimOutputRight, file);
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
function matchOperator(str, begin, trie, end = str.length) {
|
|
1679
|
+
let node = trie;
|
|
1680
|
+
let i = begin;
|
|
1681
|
+
let info;
|
|
1682
|
+
while (node[str[i]] && i < end) {
|
|
1683
|
+
node = node[str[i++]];
|
|
1684
|
+
if (node['end'])
|
|
1685
|
+
info = node;
|
|
1686
|
+
}
|
|
1687
|
+
if (!info)
|
|
1688
|
+
return -1;
|
|
1689
|
+
if (info['needBoundary'] && (TYPES[str.charCodeAt(i)] & IDENTIFIER))
|
|
1690
|
+
return -1;
|
|
1691
|
+
return i;
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
class LiquidTagToken extends DelimitedToken {
|
|
1695
|
+
constructor(input, begin, end, options, file) {
|
|
1696
|
+
const value = input.slice(begin, end);
|
|
1697
|
+
super(TokenKind.Tag, value, input, begin, end, false, false, file);
|
|
1698
|
+
if (!/\S/.test(value)) {
|
|
1699
|
+
// A line that contains only whitespace.
|
|
1700
|
+
this.name = '';
|
|
1701
|
+
this.args = '';
|
|
1702
|
+
}
|
|
1703
|
+
else {
|
|
1704
|
+
const tokenizer = new Tokenizer(this.content, options.operatorsTrie);
|
|
1705
|
+
this.name = tokenizer.readTagName();
|
|
1706
|
+
if (!this.name)
|
|
1707
|
+
throw new TokenizationError(`illegal liquid tag syntax`, this);
|
|
1708
|
+
tokenizer.skipBlank();
|
|
1709
|
+
this.args = tokenizer.remaining();
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
class Tokenizer {
|
|
1715
|
+
constructor(input, trie = defaultOptions.operatorsTrie, file = '') {
|
|
1716
|
+
this.input = input;
|
|
1717
|
+
this.trie = trie;
|
|
1718
|
+
this.file = file;
|
|
1719
|
+
this.p = 0;
|
|
1720
|
+
this.rawBeginAt = -1;
|
|
1721
|
+
this.N = input.length;
|
|
1722
|
+
}
|
|
1723
|
+
readExpression() {
|
|
1724
|
+
return new Expression(this.readExpressionTokens());
|
|
1725
|
+
}
|
|
1726
|
+
*readExpressionTokens() {
|
|
1727
|
+
const operand = this.readValue();
|
|
1728
|
+
if (!operand)
|
|
1729
|
+
return;
|
|
1730
|
+
yield operand;
|
|
1731
|
+
while (this.p < this.N) {
|
|
1732
|
+
const operator = this.readOperator();
|
|
1733
|
+
if (!operator)
|
|
1734
|
+
return;
|
|
1735
|
+
const operand = this.readValue();
|
|
1736
|
+
if (!operand)
|
|
1737
|
+
return;
|
|
1738
|
+
yield operator;
|
|
1739
|
+
yield operand;
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
readOperator() {
|
|
1743
|
+
this.skipBlank();
|
|
1744
|
+
const end = matchOperator(this.input, this.p, this.trie);
|
|
1745
|
+
if (end === -1)
|
|
1746
|
+
return;
|
|
1747
|
+
return new OperatorToken(this.input, this.p, (this.p = end), this.file);
|
|
1748
|
+
}
|
|
1749
|
+
readFilters() {
|
|
1750
|
+
const filters = [];
|
|
1751
|
+
while (true) {
|
|
1752
|
+
const filter = this.readFilter();
|
|
1753
|
+
if (!filter)
|
|
1754
|
+
return filters;
|
|
1755
|
+
filters.push(filter);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
readFilter() {
|
|
1759
|
+
this.skipBlank();
|
|
1760
|
+
if (this.end())
|
|
1761
|
+
return null;
|
|
1762
|
+
assert(this.peek() === '|', () => `unexpected token at ${this.snapshot()}`);
|
|
1763
|
+
this.p++;
|
|
1764
|
+
const begin = this.p;
|
|
1765
|
+
const name = this.readIdentifier();
|
|
1766
|
+
if (!name.size())
|
|
1767
|
+
return null;
|
|
1768
|
+
const args = [];
|
|
1769
|
+
this.skipBlank();
|
|
1770
|
+
if (this.peek() === ':') {
|
|
1771
|
+
do {
|
|
1772
|
+
++this.p;
|
|
1773
|
+
const arg = this.readFilterArg();
|
|
1774
|
+
arg && args.push(arg);
|
|
1775
|
+
this.skipBlank();
|
|
1776
|
+
assert(this.end() || this.peek() === ',' || this.peek() === '|', () => `unexpected character ${this.snapshot()}`);
|
|
1777
|
+
} while (this.peek() === ',');
|
|
1778
|
+
}
|
|
1779
|
+
return new FilterToken(name.getText(), args, this.input, begin, this.p, this.file);
|
|
1780
|
+
}
|
|
1781
|
+
readFilterArg() {
|
|
1782
|
+
const key = this.readValue();
|
|
1783
|
+
if (!key)
|
|
1784
|
+
return;
|
|
1785
|
+
this.skipBlank();
|
|
1786
|
+
if (this.peek() !== ':')
|
|
1787
|
+
return key;
|
|
1788
|
+
++this.p;
|
|
1789
|
+
const value = this.readValue();
|
|
1790
|
+
return [key.getText(), value];
|
|
1791
|
+
}
|
|
1792
|
+
readTopLevelTokens(options = defaultOptions) {
|
|
1793
|
+
const tokens = [];
|
|
1794
|
+
while (this.p < this.N) {
|
|
1795
|
+
const token = this.readTopLevelToken(options);
|
|
1796
|
+
tokens.push(token);
|
|
1797
|
+
}
|
|
1798
|
+
whiteSpaceCtrl(tokens, options);
|
|
1799
|
+
return tokens;
|
|
1800
|
+
}
|
|
1801
|
+
readTopLevelToken(options) {
|
|
1802
|
+
const { tagDelimiterLeft, outputDelimiterLeft } = options;
|
|
1803
|
+
if (this.rawBeginAt > -1)
|
|
1804
|
+
return this.readEndrawOrRawContent(options);
|
|
1805
|
+
if (this.match(tagDelimiterLeft))
|
|
1806
|
+
return this.readTagToken(options);
|
|
1807
|
+
if (this.match(outputDelimiterLeft))
|
|
1808
|
+
return this.readOutputToken(options);
|
|
1809
|
+
return this.readHTMLToken([tagDelimiterLeft, outputDelimiterLeft]);
|
|
1810
|
+
}
|
|
1811
|
+
readHTMLToken(stopStrings) {
|
|
1812
|
+
const begin = this.p;
|
|
1813
|
+
while (this.p < this.N) {
|
|
1814
|
+
if (stopStrings.some(str => this.match(str)))
|
|
1815
|
+
break;
|
|
1816
|
+
++this.p;
|
|
1817
|
+
}
|
|
1818
|
+
return new HTMLToken(this.input, begin, this.p, this.file);
|
|
1819
|
+
}
|
|
1820
|
+
readTagToken(options = defaultOptions) {
|
|
1821
|
+
const { file, input } = this;
|
|
1822
|
+
const begin = this.p;
|
|
1823
|
+
if (this.readToDelimiter(options.tagDelimiterRight) === -1) {
|
|
1824
|
+
throw this.mkError(`tag ${this.snapshot(begin)} not closed`, begin);
|
|
1825
|
+
}
|
|
1826
|
+
const token = new TagToken(input, begin, this.p, options, file);
|
|
1827
|
+
if (token.name === 'raw')
|
|
1828
|
+
this.rawBeginAt = begin;
|
|
1829
|
+
return token;
|
|
1830
|
+
}
|
|
1831
|
+
readToDelimiter(delimiter) {
|
|
1832
|
+
while (this.p < this.N) {
|
|
1833
|
+
if ((this.peekType() & QUOTE)) {
|
|
1834
|
+
this.readQuoted();
|
|
1835
|
+
continue;
|
|
1836
|
+
}
|
|
1837
|
+
++this.p;
|
|
1838
|
+
if (this.rmatch(delimiter))
|
|
1839
|
+
return this.p;
|
|
1840
|
+
}
|
|
1841
|
+
return -1;
|
|
1842
|
+
}
|
|
1843
|
+
readOutputToken(options = defaultOptions) {
|
|
1844
|
+
const { file, input } = this;
|
|
1845
|
+
const { outputDelimiterRight } = options;
|
|
1846
|
+
const begin = this.p;
|
|
1847
|
+
if (this.readToDelimiter(outputDelimiterRight) === -1) {
|
|
1848
|
+
throw this.mkError(`output ${this.snapshot(begin)} not closed`, begin);
|
|
1849
|
+
}
|
|
1850
|
+
return new OutputToken(input, begin, this.p, options, file);
|
|
1851
|
+
}
|
|
1852
|
+
readEndrawOrRawContent(options) {
|
|
1853
|
+
const { tagDelimiterLeft, tagDelimiterRight } = options;
|
|
1854
|
+
const begin = this.p;
|
|
1855
|
+
let leftPos = this.readTo(tagDelimiterLeft) - tagDelimiterLeft.length;
|
|
1856
|
+
while (this.p < this.N) {
|
|
1857
|
+
if (this.readIdentifier().getText() !== 'endraw') {
|
|
1858
|
+
leftPos = this.readTo(tagDelimiterLeft) - tagDelimiterLeft.length;
|
|
1859
|
+
continue;
|
|
1860
|
+
}
|
|
1861
|
+
while (this.p <= this.N) {
|
|
1862
|
+
if (this.rmatch(tagDelimiterRight)) {
|
|
1863
|
+
const end = this.p;
|
|
1864
|
+
if (begin === leftPos) {
|
|
1865
|
+
this.rawBeginAt = -1;
|
|
1866
|
+
return new TagToken(this.input, begin, end, options, this.file);
|
|
1867
|
+
}
|
|
1868
|
+
else {
|
|
1869
|
+
this.p = leftPos;
|
|
1870
|
+
return new HTMLToken(this.input, begin, leftPos, this.file);
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
if (this.rmatch(tagDelimiterLeft))
|
|
1874
|
+
break;
|
|
1875
|
+
this.p++;
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
throw this.mkError(`raw ${this.snapshot(this.rawBeginAt)} not closed`, begin);
|
|
1879
|
+
}
|
|
1880
|
+
readLiquidTagTokens(options = defaultOptions) {
|
|
1881
|
+
const tokens = [];
|
|
1882
|
+
while (this.p < this.N) {
|
|
1883
|
+
const token = this.readLiquidTagToken(options);
|
|
1884
|
+
if (token.name)
|
|
1885
|
+
tokens.push(token);
|
|
1886
|
+
}
|
|
1887
|
+
return tokens;
|
|
1888
|
+
}
|
|
1889
|
+
readLiquidTagToken(options) {
|
|
1890
|
+
const { file, input } = this;
|
|
1891
|
+
const begin = this.p;
|
|
1892
|
+
let end = this.N;
|
|
1893
|
+
if (this.readToDelimiter('\n') !== -1)
|
|
1894
|
+
end = this.p;
|
|
1895
|
+
return new LiquidTagToken(input, begin, end, options, file);
|
|
1896
|
+
}
|
|
1897
|
+
mkError(msg, begin) {
|
|
1898
|
+
return new TokenizationError(msg, new IdentifierToken(this.input, begin, this.N, this.file));
|
|
1899
|
+
}
|
|
1900
|
+
snapshot(begin = this.p) {
|
|
1901
|
+
return JSON.stringify(ellipsis(this.input.slice(begin), 16));
|
|
1902
|
+
}
|
|
1903
|
+
/**
|
|
1904
|
+
* @deprecated
|
|
1905
|
+
*/
|
|
1906
|
+
readWord() {
|
|
1907
|
+
console.warn('Tokenizer#readWord() will be removed, use #readIdentifier instead');
|
|
1908
|
+
return this.readIdentifier();
|
|
1909
|
+
}
|
|
1910
|
+
readIdentifier() {
|
|
1911
|
+
this.skipBlank();
|
|
1912
|
+
const begin = this.p;
|
|
1913
|
+
while (this.peekType() & IDENTIFIER)
|
|
1914
|
+
++this.p;
|
|
1915
|
+
return new IdentifierToken(this.input, begin, this.p, this.file);
|
|
1916
|
+
}
|
|
1917
|
+
readTagName() {
|
|
1918
|
+
this.skipBlank();
|
|
1919
|
+
// Handle inline comment tags
|
|
1920
|
+
if (this.input[this.p] === '#')
|
|
1921
|
+
return this.input.slice(this.p, ++this.p);
|
|
1922
|
+
return this.readIdentifier().getText();
|
|
1923
|
+
}
|
|
1924
|
+
readHashes(jekyllStyle) {
|
|
1925
|
+
const hashes = [];
|
|
1926
|
+
while (true) {
|
|
1927
|
+
const hash = this.readHash(jekyllStyle);
|
|
1928
|
+
if (!hash)
|
|
1929
|
+
return hashes;
|
|
1930
|
+
hashes.push(hash);
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
readHash(jekyllStyle) {
|
|
1934
|
+
this.skipBlank();
|
|
1935
|
+
if (this.peek() === ',')
|
|
1936
|
+
++this.p;
|
|
1937
|
+
const begin = this.p;
|
|
1938
|
+
const name = this.readIdentifier();
|
|
1939
|
+
if (!name.size())
|
|
1940
|
+
return;
|
|
1941
|
+
let value;
|
|
1942
|
+
this.skipBlank();
|
|
1943
|
+
const sep = jekyllStyle ? '=' : ':';
|
|
1944
|
+
if (this.peek() === sep) {
|
|
1945
|
+
++this.p;
|
|
1946
|
+
value = this.readValue();
|
|
1947
|
+
}
|
|
1948
|
+
return new HashToken(this.input, begin, this.p, name, value, this.file);
|
|
1949
|
+
}
|
|
1950
|
+
remaining() {
|
|
1951
|
+
return this.input.slice(this.p);
|
|
1952
|
+
}
|
|
1953
|
+
advance(i = 1) {
|
|
1954
|
+
this.p += i;
|
|
1955
|
+
}
|
|
1956
|
+
end() {
|
|
1957
|
+
return this.p >= this.N;
|
|
1958
|
+
}
|
|
1959
|
+
readTo(end) {
|
|
1960
|
+
while (this.p < this.N) {
|
|
1961
|
+
++this.p;
|
|
1962
|
+
if (this.rmatch(end))
|
|
1963
|
+
return this.p;
|
|
1964
|
+
}
|
|
1965
|
+
return -1;
|
|
1966
|
+
}
|
|
1967
|
+
readValue() {
|
|
1968
|
+
const value = this.readQuoted() || this.readRange();
|
|
1969
|
+
if (value)
|
|
1970
|
+
return value;
|
|
1971
|
+
if (this.peek() === '[') {
|
|
1972
|
+
this.p++;
|
|
1973
|
+
const prop = this.readQuoted();
|
|
1974
|
+
if (!prop)
|
|
1975
|
+
return;
|
|
1976
|
+
if (this.peek() !== ']')
|
|
1977
|
+
return;
|
|
1978
|
+
this.p++;
|
|
1979
|
+
return new PropertyAccessToken(prop, [], this.p);
|
|
1980
|
+
}
|
|
1981
|
+
const variable = this.readIdentifier();
|
|
1982
|
+
if (!variable.size())
|
|
1983
|
+
return;
|
|
1984
|
+
let isNumber = variable.isNumber(true);
|
|
1985
|
+
const props = [];
|
|
1986
|
+
while (true) {
|
|
1987
|
+
if (this.peek() === '[') {
|
|
1988
|
+
isNumber = false;
|
|
1989
|
+
this.p++;
|
|
1990
|
+
const prop = this.readValue() || new IdentifierToken(this.input, this.p, this.p, this.file);
|
|
1991
|
+
this.readTo(']');
|
|
1992
|
+
props.push(prop);
|
|
1993
|
+
}
|
|
1994
|
+
else if (this.peek() === '.' && this.peek(1) !== '.') { // skip range syntax
|
|
1995
|
+
this.p++;
|
|
1996
|
+
const prop = this.readIdentifier();
|
|
1997
|
+
if (!prop.size())
|
|
1998
|
+
break;
|
|
1999
|
+
if (!prop.isNumber())
|
|
2000
|
+
isNumber = false;
|
|
2001
|
+
props.push(prop);
|
|
2002
|
+
}
|
|
2003
|
+
else
|
|
2004
|
+
break;
|
|
2005
|
+
}
|
|
2006
|
+
if (!props.length && literalValues.hasOwnProperty(variable.content)) {
|
|
2007
|
+
return new LiteralToken(this.input, variable.begin, variable.end, this.file);
|
|
2008
|
+
}
|
|
2009
|
+
if (isNumber)
|
|
2010
|
+
return new NumberToken(variable, props[0]);
|
|
2011
|
+
return new PropertyAccessToken(variable, props, this.p);
|
|
2012
|
+
}
|
|
2013
|
+
readRange() {
|
|
2014
|
+
this.skipBlank();
|
|
2015
|
+
const begin = this.p;
|
|
2016
|
+
if (this.peek() !== '(')
|
|
2017
|
+
return;
|
|
2018
|
+
++this.p;
|
|
2019
|
+
const lhs = this.readValueOrThrow();
|
|
2020
|
+
this.p += 2;
|
|
2021
|
+
const rhs = this.readValueOrThrow();
|
|
2022
|
+
++this.p;
|
|
2023
|
+
return new RangeToken(this.input, begin, this.p, lhs, rhs, this.file);
|
|
2024
|
+
}
|
|
2025
|
+
readValueOrThrow() {
|
|
2026
|
+
const value = this.readValue();
|
|
2027
|
+
assert(value, () => `unexpected token ${this.snapshot()}, value expected`);
|
|
2028
|
+
return value;
|
|
2029
|
+
}
|
|
2030
|
+
readQuoted() {
|
|
2031
|
+
this.skipBlank();
|
|
2032
|
+
const begin = this.p;
|
|
2033
|
+
if (!(this.peekType() & QUOTE))
|
|
2034
|
+
return;
|
|
2035
|
+
++this.p;
|
|
2036
|
+
let escaped = false;
|
|
2037
|
+
while (this.p < this.N) {
|
|
2038
|
+
++this.p;
|
|
2039
|
+
if (this.input[this.p - 1] === this.input[begin] && !escaped)
|
|
2040
|
+
break;
|
|
2041
|
+
if (escaped)
|
|
2042
|
+
escaped = false;
|
|
2043
|
+
else if (this.input[this.p - 1] === '\\')
|
|
2044
|
+
escaped = true;
|
|
2045
|
+
}
|
|
2046
|
+
return new QuotedToken(this.input, begin, this.p, this.file);
|
|
2047
|
+
}
|
|
2048
|
+
*readFileNameTemplate(options) {
|
|
2049
|
+
const { outputDelimiterLeft } = options;
|
|
2050
|
+
const htmlStopStrings = [',', ' ', outputDelimiterLeft];
|
|
2051
|
+
const htmlStopStringSet = new Set(htmlStopStrings);
|
|
2052
|
+
// break on ',' and ' ', outputDelimiterLeft only stops HTML token
|
|
2053
|
+
while (this.p < this.N && !htmlStopStringSet.has(this.peek())) {
|
|
2054
|
+
yield this.match(outputDelimiterLeft)
|
|
2055
|
+
? this.readOutputToken(options)
|
|
2056
|
+
: this.readHTMLToken(htmlStopStrings);
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
match(word) {
|
|
2060
|
+
for (let i = 0; i < word.length; i++) {
|
|
2061
|
+
if (word[i] !== this.input[this.p + i])
|
|
2062
|
+
return false;
|
|
2063
|
+
}
|
|
2064
|
+
return true;
|
|
2065
|
+
}
|
|
2066
|
+
rmatch(pattern) {
|
|
2067
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
2068
|
+
if (pattern[pattern.length - 1 - i] !== this.input[this.p - 1 - i])
|
|
2069
|
+
return false;
|
|
2070
|
+
}
|
|
2071
|
+
return true;
|
|
2072
|
+
}
|
|
2073
|
+
peekType(n = 0) {
|
|
2074
|
+
return TYPES[this.input.charCodeAt(this.p + n)];
|
|
2075
|
+
}
|
|
2076
|
+
peek(n = 0) {
|
|
2077
|
+
return this.input[this.p + n];
|
|
2078
|
+
}
|
|
2079
|
+
skipBlank() {
|
|
2080
|
+
while (this.peekType() & BLANK)
|
|
2081
|
+
++this.p;
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
|
|
2085
|
+
class TagToken extends DelimitedToken {
|
|
2086
|
+
constructor(input, begin, end, options, file) {
|
|
2087
|
+
const { trimTagLeft, trimTagRight, tagDelimiterLeft, tagDelimiterRight } = options;
|
|
2088
|
+
const value = input.slice(begin + tagDelimiterLeft.length, end - tagDelimiterRight.length);
|
|
2089
|
+
super(TokenKind.Tag, value, input, begin, end, trimTagLeft, trimTagRight, file);
|
|
2090
|
+
const tokenizer = new Tokenizer(this.content, options.operatorsTrie);
|
|
2091
|
+
this.name = tokenizer.readTagName();
|
|
2092
|
+
if (!this.name)
|
|
2093
|
+
throw new TokenizationError(`illegal tag syntax`, this);
|
|
2094
|
+
tokenizer.skipBlank();
|
|
2095
|
+
this.args = tokenizer.remaining();
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
class ParseStream {
|
|
2100
|
+
constructor(tokens, parseToken) {
|
|
2101
|
+
this.handlers = {};
|
|
2102
|
+
this.stopRequested = false;
|
|
2103
|
+
this.tokens = tokens;
|
|
2104
|
+
this.parseToken = parseToken;
|
|
2105
|
+
}
|
|
2106
|
+
on(name, cb) {
|
|
2107
|
+
this.handlers[name] = cb;
|
|
2108
|
+
return this;
|
|
2109
|
+
}
|
|
2110
|
+
trigger(event, arg) {
|
|
2111
|
+
const h = this.handlers[event];
|
|
2112
|
+
return h ? (h.call(this, arg), true) : false;
|
|
2113
|
+
}
|
|
2114
|
+
start() {
|
|
2115
|
+
this.trigger('start');
|
|
2116
|
+
let token;
|
|
2117
|
+
while (!this.stopRequested && (token = this.tokens.shift())) {
|
|
2118
|
+
if (this.trigger('token', token))
|
|
2119
|
+
continue;
|
|
2120
|
+
if (isTagToken(token) && this.trigger(`tag:${token.name}`, token)) {
|
|
2121
|
+
continue;
|
|
2122
|
+
}
|
|
2123
|
+
const template = this.parseToken(token, this.tokens);
|
|
2124
|
+
this.trigger('template', template);
|
|
2125
|
+
}
|
|
2126
|
+
if (!this.stopRequested)
|
|
2127
|
+
this.trigger('end');
|
|
2128
|
+
return this;
|
|
2129
|
+
}
|
|
2130
|
+
stop() {
|
|
2131
|
+
this.stopRequested = true;
|
|
2132
|
+
return this;
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
/**
|
|
2137
|
+
* Key-Value Pairs Representing Tag Arguments
|
|
2138
|
+
* Example:
|
|
2139
|
+
* For the markup `, foo:'bar', coo:2 reversed %}`,
|
|
2140
|
+
* hash['foo'] === 'bar'
|
|
2141
|
+
* hash['coo'] === 2
|
|
2142
|
+
* hash['reversed'] === undefined
|
|
2143
|
+
*/
|
|
2144
|
+
class Hash {
|
|
2145
|
+
constructor(markup, jekyllStyle) {
|
|
2146
|
+
this.hash = {};
|
|
2147
|
+
const tokenizer = new Tokenizer(markup, {});
|
|
2148
|
+
for (const hash of tokenizer.readHashes(jekyllStyle)) {
|
|
2149
|
+
this.hash[hash.name.content] = hash.value;
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
*render(ctx) {
|
|
2153
|
+
const hash = {};
|
|
2154
|
+
for (const key of Object.keys(this.hash)) {
|
|
2155
|
+
hash[key] = this.hash[key] === undefined ? true : yield _evalToken(this.hash[key], ctx);
|
|
2156
|
+
}
|
|
2157
|
+
return hash;
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
function isKeyValuePair(arr) {
|
|
2162
|
+
return isArray(arr);
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
class Filter {
|
|
2166
|
+
constructor(name, impl, args, liquid) {
|
|
2167
|
+
this.name = name;
|
|
2168
|
+
this.impl = impl || identify;
|
|
2169
|
+
this.args = args;
|
|
2170
|
+
this.liquid = liquid;
|
|
2171
|
+
}
|
|
2172
|
+
*render(value, context) {
|
|
2173
|
+
const argv = [];
|
|
2174
|
+
for (const arg of this.args) {
|
|
2175
|
+
if (isKeyValuePair(arg))
|
|
2176
|
+
argv.push([arg[0], yield _evalToken(arg[1], context)]);
|
|
2177
|
+
else
|
|
2178
|
+
argv.push(yield _evalToken(arg, context));
|
|
2179
|
+
}
|
|
2180
|
+
return this.impl.apply({ context, liquid: this.liquid }, [value, ...argv]);
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
class Value {
|
|
2185
|
+
/**
|
|
2186
|
+
* @param str the value to be valuated, eg.: "foobar" | truncate: 3
|
|
2187
|
+
*/
|
|
2188
|
+
constructor(str, liquid) {
|
|
2189
|
+
this.filters = [];
|
|
2190
|
+
const tokenizer = new Tokenizer(str, liquid.options.operatorsTrie);
|
|
2191
|
+
this.initial = tokenizer.readExpression();
|
|
2192
|
+
this.filters = tokenizer.readFilters().map(({ name, args }) => new Filter(name, liquid.filters.get(name), args, liquid));
|
|
2193
|
+
}
|
|
2194
|
+
*value(ctx, lenient) {
|
|
2195
|
+
lenient = lenient || (ctx.opts.lenientIf && this.filters.length > 0 && this.filters[0].name === 'default');
|
|
2196
|
+
let val = yield this.initial.evaluate(ctx, lenient);
|
|
2197
|
+
for (const filter of this.filters) {
|
|
2198
|
+
val = yield filter.render(val, ctx);
|
|
2199
|
+
}
|
|
2200
|
+
return val;
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
|
|
2204
|
+
var assign = {
|
|
2205
|
+
parse: function (token) {
|
|
2206
|
+
const tokenizer = new Tokenizer(token.args, this.liquid.options.operatorsTrie);
|
|
2207
|
+
this.key = tokenizer.readIdentifier().content;
|
|
2208
|
+
tokenizer.skipBlank();
|
|
2209
|
+
assert(tokenizer.peek() === '=', () => `illegal token ${token.getText()}`);
|
|
2210
|
+
tokenizer.advance();
|
|
2211
|
+
this.value = new Value(tokenizer.remaining(), this.liquid);
|
|
2212
|
+
},
|
|
2213
|
+
render: function* (ctx) {
|
|
2214
|
+
ctx.bottom()[this.key] = yield this.value.value(ctx, this.liquid.options.lenientIf);
|
|
2215
|
+
}
|
|
2216
|
+
};
|
|
2217
|
+
|
|
2218
|
+
class ForloopDrop extends Drop {
|
|
2219
|
+
constructor(length, collection, variable) {
|
|
2220
|
+
super();
|
|
2221
|
+
this.i = 0;
|
|
2222
|
+
this.length = length;
|
|
2223
|
+
this.name = `${variable}-${collection}`;
|
|
2224
|
+
}
|
|
2225
|
+
next() {
|
|
2226
|
+
this.i++;
|
|
2227
|
+
}
|
|
2228
|
+
index0() {
|
|
2229
|
+
return this.i;
|
|
2230
|
+
}
|
|
2231
|
+
index() {
|
|
2232
|
+
return this.i + 1;
|
|
2233
|
+
}
|
|
2234
|
+
first() {
|
|
2235
|
+
return this.i === 0;
|
|
2236
|
+
}
|
|
2237
|
+
last() {
|
|
2238
|
+
return this.i === this.length - 1;
|
|
2239
|
+
}
|
|
2240
|
+
rindex() {
|
|
2241
|
+
return this.length - this.i;
|
|
2242
|
+
}
|
|
2243
|
+
rindex0() {
|
|
2244
|
+
return this.length - this.i - 1;
|
|
2245
|
+
}
|
|
2246
|
+
valueOf() {
|
|
2247
|
+
return JSON.stringify(this);
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
|
|
2251
|
+
const MODIFIERS = ['offset', 'limit', 'reversed'];
|
|
2252
|
+
var For = {
|
|
2253
|
+
type: 'block',
|
|
2254
|
+
parse: function (token, remainTokens) {
|
|
2255
|
+
const tokenizer = new Tokenizer(token.args, this.liquid.options.operatorsTrie);
|
|
2256
|
+
const variable = tokenizer.readIdentifier();
|
|
2257
|
+
const inStr = tokenizer.readIdentifier();
|
|
2258
|
+
const collection = tokenizer.readValue();
|
|
2259
|
+
assert(variable.size() && inStr.content === 'in' && collection, () => `illegal tag: ${token.getText()}`);
|
|
2260
|
+
this.variable = variable.content;
|
|
2261
|
+
this.collection = collection;
|
|
2262
|
+
this.hash = new Hash(tokenizer.remaining());
|
|
2263
|
+
this.templates = [];
|
|
2264
|
+
this.elseTemplates = [];
|
|
2265
|
+
let p;
|
|
2266
|
+
const stream = this.liquid.parser.parseStream(remainTokens)
|
|
2267
|
+
.on('start', () => (p = this.templates))
|
|
2268
|
+
.on('tag:else', () => (p = this.elseTemplates))
|
|
2269
|
+
.on('tag:endfor', () => stream.stop())
|
|
2270
|
+
.on('template', (tpl) => p.push(tpl))
|
|
2271
|
+
.on('end', () => {
|
|
2272
|
+
throw new Error(`tag ${token.getText()} not closed`);
|
|
2273
|
+
});
|
|
2274
|
+
stream.start();
|
|
2275
|
+
},
|
|
2276
|
+
render: function* (ctx, emitter) {
|
|
2277
|
+
const r = this.liquid.renderer;
|
|
2278
|
+
let collection = toEnumerable(yield _evalToken(this.collection, ctx));
|
|
2279
|
+
if (!collection.length) {
|
|
2280
|
+
yield r.renderTemplates(this.elseTemplates, ctx, emitter);
|
|
2281
|
+
return;
|
|
2282
|
+
}
|
|
2283
|
+
const continueKey = 'continue-' + this.variable + '-' + this.collection.getText();
|
|
2284
|
+
ctx.push({ continue: ctx.getRegister(continueKey) });
|
|
2285
|
+
const hash = yield this.hash.render(ctx);
|
|
2286
|
+
ctx.pop();
|
|
2287
|
+
const modifiers = this.liquid.options.orderedFilterParameters
|
|
2288
|
+
? Object.keys(hash).filter(x => MODIFIERS.includes(x))
|
|
2289
|
+
: MODIFIERS.filter(x => hash[x] !== undefined);
|
|
2290
|
+
collection = modifiers.reduce((collection, modifier) => {
|
|
2291
|
+
if (modifier === 'offset')
|
|
2292
|
+
return offset(collection, hash['offset']);
|
|
2293
|
+
if (modifier === 'limit')
|
|
2294
|
+
return limit(collection, hash['limit']);
|
|
2295
|
+
return reversed(collection);
|
|
2296
|
+
}, collection);
|
|
2297
|
+
ctx.setRegister(continueKey, (hash['offset'] || 0) + collection.length);
|
|
2298
|
+
const scope = { forloop: new ForloopDrop(collection.length, this.collection.getText(), this.variable) };
|
|
2299
|
+
ctx.push(scope);
|
|
2300
|
+
for (const item of collection) {
|
|
2301
|
+
scope[this.variable] = item;
|
|
2302
|
+
yield r.renderTemplates(this.templates, ctx, emitter);
|
|
2303
|
+
if (emitter['break']) {
|
|
2304
|
+
emitter['break'] = false;
|
|
2305
|
+
break;
|
|
2306
|
+
}
|
|
2307
|
+
emitter['continue'] = false;
|
|
2308
|
+
scope.forloop.next();
|
|
2309
|
+
}
|
|
2310
|
+
ctx.pop();
|
|
2311
|
+
}
|
|
2312
|
+
};
|
|
2313
|
+
function reversed(arr) {
|
|
2314
|
+
return [...arr].reverse();
|
|
2315
|
+
}
|
|
2316
|
+
function offset(arr, count) {
|
|
2317
|
+
return arr.slice(count);
|
|
2318
|
+
}
|
|
2319
|
+
function limit(arr, count) {
|
|
2320
|
+
return arr.slice(0, count);
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
var capture = {
|
|
2324
|
+
parse: function (tagToken, remainTokens) {
|
|
2325
|
+
const tokenizer = new Tokenizer(tagToken.args, this.liquid.options.operatorsTrie);
|
|
2326
|
+
this.variable = readVariableName(tokenizer);
|
|
2327
|
+
assert(this.variable, () => `${tagToken.args} not valid identifier`);
|
|
2328
|
+
this.templates = [];
|
|
2329
|
+
const stream = this.liquid.parser.parseStream(remainTokens);
|
|
2330
|
+
stream.on('tag:endcapture', () => stream.stop())
|
|
2331
|
+
.on('template', (tpl) => this.templates.push(tpl))
|
|
2332
|
+
.on('end', () => {
|
|
2333
|
+
throw new Error(`tag ${tagToken.getText()} not closed`);
|
|
2334
|
+
});
|
|
2335
|
+
stream.start();
|
|
2336
|
+
},
|
|
2337
|
+
render: function* (ctx) {
|
|
2338
|
+
const r = this.liquid.renderer;
|
|
2339
|
+
const html = yield r.renderTemplates(this.templates, ctx);
|
|
2340
|
+
ctx.bottom()[this.variable] = html;
|
|
2341
|
+
}
|
|
2342
|
+
};
|
|
2343
|
+
function readVariableName(tokenizer) {
|
|
2344
|
+
const word = tokenizer.readIdentifier().content;
|
|
2345
|
+
if (word)
|
|
2346
|
+
return word;
|
|
2347
|
+
const quoted = tokenizer.readQuoted();
|
|
2348
|
+
if (quoted)
|
|
2349
|
+
return evalQuotedToken(quoted);
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
var Case = {
|
|
2353
|
+
parse: function (tagToken, remainTokens) {
|
|
2354
|
+
this.cond = new Value(tagToken.args, this.liquid);
|
|
2355
|
+
this.cases = [];
|
|
2356
|
+
this.elseTemplates = [];
|
|
2357
|
+
let p = [];
|
|
2358
|
+
const stream = this.liquid.parser.parseStream(remainTokens)
|
|
2359
|
+
.on('tag:when', (token) => {
|
|
2360
|
+
p = [];
|
|
2361
|
+
const tokenizer = new Tokenizer(token.args, this.liquid.options.operatorsTrie);
|
|
2362
|
+
while (!tokenizer.end()) {
|
|
2363
|
+
const value = tokenizer.readValue();
|
|
2364
|
+
this.cases.push({
|
|
2365
|
+
val: value,
|
|
2366
|
+
templates: p
|
|
2367
|
+
});
|
|
2368
|
+
tokenizer.readTo(',');
|
|
2369
|
+
}
|
|
2370
|
+
})
|
|
2371
|
+
.on('tag:else', () => (p = this.elseTemplates))
|
|
2372
|
+
.on('tag:endcase', () => stream.stop())
|
|
2373
|
+
.on('template', (tpl) => p.push(tpl))
|
|
2374
|
+
.on('end', () => {
|
|
2375
|
+
throw new Error(`tag ${tagToken.getText()} not closed`);
|
|
2376
|
+
});
|
|
2377
|
+
stream.start();
|
|
2378
|
+
},
|
|
2379
|
+
render: function* (ctx, emitter) {
|
|
2380
|
+
const r = this.liquid.renderer;
|
|
2381
|
+
const cond = toValue(yield this.cond.value(ctx, ctx.opts.lenientIf));
|
|
2382
|
+
for (const branch of this.cases) {
|
|
2383
|
+
const val = yield _evalToken(branch.val, ctx, ctx.opts.lenientIf);
|
|
2384
|
+
if (val === cond) {
|
|
2385
|
+
yield r.renderTemplates(branch.templates, ctx, emitter);
|
|
2386
|
+
return;
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
yield r.renderTemplates(this.elseTemplates, ctx, emitter);
|
|
2390
|
+
}
|
|
2391
|
+
};
|
|
2392
|
+
|
|
2393
|
+
var comment = {
|
|
2394
|
+
parse: function (tagToken, remainTokens) {
|
|
2395
|
+
const stream = this.liquid.parser.parseStream(remainTokens);
|
|
2396
|
+
stream
|
|
2397
|
+
.on('token', (token) => {
|
|
2398
|
+
if (token.name === 'endcomment')
|
|
2399
|
+
stream.stop();
|
|
2400
|
+
})
|
|
2401
|
+
.on('end', () => {
|
|
2402
|
+
throw new Error(`tag ${tagToken.getText()} not closed`);
|
|
2403
|
+
});
|
|
2404
|
+
stream.start();
|
|
2405
|
+
}
|
|
2406
|
+
};
|
|
2407
|
+
|
|
2408
|
+
var BlockMode;
|
|
2409
|
+
(function (BlockMode) {
|
|
2410
|
+
/* store rendered html into blocks */
|
|
2411
|
+
BlockMode[BlockMode["OUTPUT"] = 0] = "OUTPUT";
|
|
2412
|
+
/* output rendered html directly */
|
|
2413
|
+
BlockMode[BlockMode["STORE"] = 1] = "STORE";
|
|
2414
|
+
})(BlockMode || (BlockMode = {}));
|
|
2415
|
+
var BlockMode$1 = BlockMode;
|
|
2416
|
+
|
|
2417
|
+
var render = {
|
|
2418
|
+
parseFilePath,
|
|
2419
|
+
renderFilePath,
|
|
2420
|
+
parse: function (token) {
|
|
2421
|
+
const args = token.args;
|
|
2422
|
+
const tokenizer = new Tokenizer(args, this.liquid.options.operatorsTrie);
|
|
2423
|
+
this['file'] = this.parseFilePath(tokenizer, this.liquid);
|
|
2424
|
+
this['currentFile'] = token.file;
|
|
2425
|
+
while (!tokenizer.end()) {
|
|
2426
|
+
tokenizer.skipBlank();
|
|
2427
|
+
const begin = tokenizer.p;
|
|
2428
|
+
const keyword = tokenizer.readIdentifier();
|
|
2429
|
+
if (keyword.content === 'with' || keyword.content === 'for') {
|
|
2430
|
+
tokenizer.skipBlank();
|
|
2431
|
+
// can be normal key/value pair, like "with: true"
|
|
2432
|
+
if (tokenizer.peek() !== ':') {
|
|
2433
|
+
const value = tokenizer.readValue();
|
|
2434
|
+
// can be normal key, like "with,"
|
|
2435
|
+
if (value) {
|
|
2436
|
+
const beforeAs = tokenizer.p;
|
|
2437
|
+
const asStr = tokenizer.readIdentifier();
|
|
2438
|
+
let alias;
|
|
2439
|
+
if (asStr.content === 'as')
|
|
2440
|
+
alias = tokenizer.readIdentifier();
|
|
2441
|
+
else
|
|
2442
|
+
tokenizer.p = beforeAs;
|
|
2443
|
+
this[keyword.content] = { value, alias: alias && alias.content };
|
|
2444
|
+
tokenizer.skipBlank();
|
|
2445
|
+
if (tokenizer.peek() === ',')
|
|
2446
|
+
tokenizer.advance();
|
|
2447
|
+
// matched!
|
|
2448
|
+
continue;
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
/**
|
|
2453
|
+
* restore cursor if with/for not matched
|
|
2454
|
+
*/
|
|
2455
|
+
tokenizer.p = begin;
|
|
2456
|
+
break;
|
|
2457
|
+
}
|
|
2458
|
+
this.hash = new Hash(tokenizer.remaining());
|
|
2459
|
+
},
|
|
2460
|
+
render: function* (ctx, emitter) {
|
|
2461
|
+
const { liquid, hash } = this;
|
|
2462
|
+
const filepath = yield this.renderFilePath(this['file'], ctx, liquid);
|
|
2463
|
+
assert(filepath, () => `illegal filename "${filepath}"`);
|
|
2464
|
+
const childCtx = new Context({}, ctx.opts, { sync: ctx.sync, globals: ctx.globals, strictVariables: ctx.strictVariables });
|
|
2465
|
+
const scope = childCtx.bottom();
|
|
2466
|
+
__assign(scope, yield hash.render(ctx));
|
|
2467
|
+
if (this['with']) {
|
|
2468
|
+
const { value, alias } = this['with'];
|
|
2469
|
+
scope[alias || filepath] = yield _evalToken(value, ctx);
|
|
2470
|
+
}
|
|
2471
|
+
if (this['for']) {
|
|
2472
|
+
const { value, alias } = this['for'];
|
|
2473
|
+
let collection = yield _evalToken(value, ctx);
|
|
2474
|
+
collection = toEnumerable(collection);
|
|
2475
|
+
scope['forloop'] = new ForloopDrop(collection.length, value.getText(), alias);
|
|
2476
|
+
for (const item of collection) {
|
|
2477
|
+
scope[alias] = item;
|
|
2478
|
+
const templates = yield liquid._parsePartialFile(filepath, childCtx.sync, this['currentFile']);
|
|
2479
|
+
yield liquid.renderer.renderTemplates(templates, childCtx, emitter);
|
|
2480
|
+
scope['forloop'].next();
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
else {
|
|
2484
|
+
const templates = yield liquid._parsePartialFile(filepath, childCtx.sync, this['currentFile']);
|
|
2485
|
+
yield liquid.renderer.renderTemplates(templates, childCtx, emitter);
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
};
|
|
2489
|
+
/**
|
|
2490
|
+
* @return null for "none",
|
|
2491
|
+
* @return Template[] for quoted with tags and/or filters
|
|
2492
|
+
* @return Token for expression (not quoted)
|
|
2493
|
+
* @throws TypeError if cannot read next token
|
|
2494
|
+
*/
|
|
2495
|
+
function parseFilePath(tokenizer, liquid) {
|
|
2496
|
+
if (liquid.options.dynamicPartials) {
|
|
2497
|
+
const file = tokenizer.readValue();
|
|
2498
|
+
if (file === undefined)
|
|
2499
|
+
throw new TypeError(`illegal argument "${tokenizer.input}"`);
|
|
2500
|
+
if (file.getText() === 'none')
|
|
2501
|
+
return null;
|
|
2502
|
+
if (isQuotedToken(file)) {
|
|
2503
|
+
// for filenames like "files/{{file}}", eval as liquid template
|
|
2504
|
+
const templates = liquid.parse(evalQuotedToken(file));
|
|
2505
|
+
return optimize(templates);
|
|
2506
|
+
}
|
|
2507
|
+
return file;
|
|
2508
|
+
}
|
|
2509
|
+
const tokens = [...tokenizer.readFileNameTemplate(liquid.options)];
|
|
2510
|
+
const templates = optimize(liquid.parser.parseTokens(tokens));
|
|
2511
|
+
return templates === 'none' ? null : templates;
|
|
2512
|
+
}
|
|
2513
|
+
function optimize(templates) {
|
|
2514
|
+
// for filenames like "files/file.liquid", extract the string directly
|
|
2515
|
+
if (templates.length === 1 && isHTMLToken(templates[0].token))
|
|
2516
|
+
return templates[0].token.getContent();
|
|
2517
|
+
return templates;
|
|
2518
|
+
}
|
|
2519
|
+
function* renderFilePath(file, ctx, liquid) {
|
|
2520
|
+
if (typeof file === 'string')
|
|
2521
|
+
return file;
|
|
2522
|
+
if (Array.isArray(file))
|
|
2523
|
+
return liquid.renderer.renderTemplates(file, ctx);
|
|
2524
|
+
return yield _evalToken(file, ctx);
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
var include = {
|
|
2528
|
+
parseFilePath,
|
|
2529
|
+
renderFilePath,
|
|
2530
|
+
parse: function (token) {
|
|
2531
|
+
const args = token.args;
|
|
2532
|
+
const tokenizer = new Tokenizer(args, this.liquid.options.operatorsTrie);
|
|
2533
|
+
this['file'] = this.parseFilePath(tokenizer, this.liquid);
|
|
2534
|
+
this['currentFile'] = token.file;
|
|
2535
|
+
const begin = tokenizer.p;
|
|
2536
|
+
const withStr = tokenizer.readIdentifier();
|
|
2537
|
+
if (withStr.content === 'with') {
|
|
2538
|
+
tokenizer.skipBlank();
|
|
2539
|
+
if (tokenizer.peek() !== ':') {
|
|
2540
|
+
this.withVar = tokenizer.readValue();
|
|
2541
|
+
}
|
|
2542
|
+
else
|
|
2543
|
+
tokenizer.p = begin;
|
|
2544
|
+
}
|
|
2545
|
+
else
|
|
2546
|
+
tokenizer.p = begin;
|
|
2547
|
+
this.hash = new Hash(tokenizer.remaining(), this.liquid.options.jekyllInclude);
|
|
2548
|
+
},
|
|
2549
|
+
render: function* (ctx, emitter) {
|
|
2550
|
+
const { liquid, hash, withVar } = this;
|
|
2551
|
+
const { renderer } = liquid;
|
|
2552
|
+
const filepath = yield this.renderFilePath(this['file'], ctx, liquid);
|
|
2553
|
+
assert(filepath, () => `illegal filename "${filepath}"`);
|
|
2554
|
+
const saved = ctx.saveRegister('blocks', 'blockMode');
|
|
2555
|
+
ctx.setRegister('blocks', {});
|
|
2556
|
+
ctx.setRegister('blockMode', BlockMode$1.OUTPUT);
|
|
2557
|
+
const scope = yield hash.render(ctx);
|
|
2558
|
+
if (withVar)
|
|
2559
|
+
scope[filepath] = yield _evalToken(withVar, ctx);
|
|
2560
|
+
const templates = yield liquid._parsePartialFile(filepath, ctx.sync, this['currentFile']);
|
|
2561
|
+
ctx.push(ctx.opts.jekyllInclude ? { include: scope } : scope);
|
|
2562
|
+
yield renderer.renderTemplates(templates, ctx, emitter);
|
|
2563
|
+
ctx.pop();
|
|
2564
|
+
ctx.restoreRegister(saved);
|
|
2565
|
+
}
|
|
2566
|
+
};
|
|
2567
|
+
|
|
2568
|
+
var decrement = {
|
|
2569
|
+
parse: function (token) {
|
|
2570
|
+
const tokenizer = new Tokenizer(token.args, this.liquid.options.operatorsTrie);
|
|
2571
|
+
this.variable = tokenizer.readIdentifier().content;
|
|
2572
|
+
},
|
|
2573
|
+
render: function (context, emitter) {
|
|
2574
|
+
const scope = context.environments;
|
|
2575
|
+
if (!isNumber(scope[this.variable])) {
|
|
2576
|
+
scope[this.variable] = 0;
|
|
2577
|
+
}
|
|
2578
|
+
emitter.write(stringify(--scope[this.variable]));
|
|
2579
|
+
}
|
|
2580
|
+
};
|
|
2581
|
+
|
|
2582
|
+
var cycle = {
|
|
2583
|
+
parse: function (tagToken) {
|
|
2584
|
+
const tokenizer = new Tokenizer(tagToken.args, this.liquid.options.operatorsTrie);
|
|
2585
|
+
const group = tokenizer.readValue();
|
|
2586
|
+
tokenizer.skipBlank();
|
|
2587
|
+
this.candidates = [];
|
|
2588
|
+
if (group) {
|
|
2589
|
+
if (tokenizer.peek() === ':') {
|
|
2590
|
+
this.group = group;
|
|
2591
|
+
tokenizer.advance();
|
|
2592
|
+
}
|
|
2593
|
+
else
|
|
2594
|
+
this.candidates.push(group);
|
|
2595
|
+
}
|
|
2596
|
+
while (!tokenizer.end()) {
|
|
2597
|
+
const value = tokenizer.readValue();
|
|
2598
|
+
if (value)
|
|
2599
|
+
this.candidates.push(value);
|
|
2600
|
+
tokenizer.readTo(',');
|
|
2601
|
+
}
|
|
2602
|
+
assert(this.candidates.length, () => `empty candidates: ${tagToken.getText()}`);
|
|
2603
|
+
},
|
|
2604
|
+
render: function* (ctx, emitter) {
|
|
2605
|
+
const group = yield _evalToken(this.group, ctx);
|
|
2606
|
+
const fingerprint = `cycle:${group}:` + this.candidates.join(',');
|
|
2607
|
+
const groups = ctx.getRegister('cycle');
|
|
2608
|
+
let idx = groups[fingerprint];
|
|
2609
|
+
if (idx === undefined) {
|
|
2610
|
+
idx = groups[fingerprint] = 0;
|
|
2611
|
+
}
|
|
2612
|
+
const candidate = this.candidates[idx];
|
|
2613
|
+
idx = (idx + 1) % this.candidates.length;
|
|
2614
|
+
groups[fingerprint] = idx;
|
|
2615
|
+
const html = yield _evalToken(candidate, ctx);
|
|
2616
|
+
emitter.write(html);
|
|
2617
|
+
}
|
|
2618
|
+
};
|
|
2619
|
+
|
|
2620
|
+
var If = {
|
|
2621
|
+
parse: function (tagToken, remainTokens) {
|
|
2622
|
+
this.branches = [];
|
|
2623
|
+
this.elseTemplates = [];
|
|
2624
|
+
let p;
|
|
2625
|
+
this.liquid.parser.parseStream(remainTokens)
|
|
2626
|
+
.on('start', () => this.branches.push({
|
|
2627
|
+
predicate: new Value(tagToken.args, this.liquid),
|
|
2628
|
+
templates: (p = [])
|
|
2629
|
+
}))
|
|
2630
|
+
.on('tag:elsif', (token) => this.branches.push({
|
|
2631
|
+
predicate: new Value(token.args, this.liquid),
|
|
2632
|
+
templates: (p = [])
|
|
2633
|
+
}))
|
|
2634
|
+
.on('tag:else', () => (p = this.elseTemplates))
|
|
2635
|
+
.on('tag:endif', function () { this.stop(); })
|
|
2636
|
+
.on('template', (tpl) => p.push(tpl))
|
|
2637
|
+
.on('end', () => { throw new Error(`tag ${tagToken.getText()} not closed`); })
|
|
2638
|
+
.start();
|
|
2639
|
+
},
|
|
2640
|
+
render: function* (ctx, emitter) {
|
|
2641
|
+
const r = this.liquid.renderer;
|
|
2642
|
+
for (const { predicate, templates } of this.branches) {
|
|
2643
|
+
const value = yield predicate.value(ctx, ctx.opts.lenientIf);
|
|
2644
|
+
if (isTruthy(value, ctx)) {
|
|
2645
|
+
yield r.renderTemplates(templates, ctx, emitter);
|
|
2646
|
+
return;
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
yield r.renderTemplates(this.elseTemplates, ctx, emitter);
|
|
2650
|
+
}
|
|
2651
|
+
};
|
|
2652
|
+
|
|
2653
|
+
var increment = {
|
|
2654
|
+
parse: function (token) {
|
|
2655
|
+
const tokenizer = new Tokenizer(token.args, this.liquid.options.operatorsTrie);
|
|
2656
|
+
this.variable = tokenizer.readIdentifier().content;
|
|
2657
|
+
},
|
|
2658
|
+
render: function (context, emitter) {
|
|
2659
|
+
const scope = context.environments;
|
|
2660
|
+
if (!isNumber(scope[this.variable])) {
|
|
2661
|
+
scope[this.variable] = 0;
|
|
2662
|
+
}
|
|
2663
|
+
const val = scope[this.variable];
|
|
2664
|
+
scope[this.variable]++;
|
|
2665
|
+
emitter.write(stringify(val));
|
|
2666
|
+
}
|
|
2667
|
+
};
|
|
2668
|
+
|
|
2669
|
+
var layout = {
|
|
2670
|
+
parseFilePath,
|
|
2671
|
+
renderFilePath,
|
|
2672
|
+
parse: function (token, remainTokens) {
|
|
2673
|
+
const tokenizer = new Tokenizer(token.args, this.liquid.options.operatorsTrie);
|
|
2674
|
+
this['file'] = this.parseFilePath(tokenizer, this.liquid);
|
|
2675
|
+
this['currentFile'] = token.file;
|
|
2676
|
+
this.hash = new Hash(tokenizer.remaining());
|
|
2677
|
+
this.tpls = this.liquid.parser.parseTokens(remainTokens);
|
|
2678
|
+
},
|
|
2679
|
+
render: function* (ctx, emitter) {
|
|
2680
|
+
const { liquid, hash, file } = this;
|
|
2681
|
+
const { renderer } = liquid;
|
|
2682
|
+
if (file === null) {
|
|
2683
|
+
ctx.setRegister('blockMode', BlockMode$1.OUTPUT);
|
|
2684
|
+
yield renderer.renderTemplates(this.tpls, ctx, emitter);
|
|
2685
|
+
return;
|
|
2686
|
+
}
|
|
2687
|
+
const filepath = yield this.renderFilePath(this['file'], ctx, liquid);
|
|
2688
|
+
assert(filepath, () => `illegal filename "${filepath}"`);
|
|
2689
|
+
const templates = yield liquid._parseLayoutFile(filepath, ctx.sync, this['currentFile']);
|
|
2690
|
+
// render remaining contents and store rendered results
|
|
2691
|
+
ctx.setRegister('blockMode', BlockMode$1.STORE);
|
|
2692
|
+
const html = yield renderer.renderTemplates(this.tpls, ctx);
|
|
2693
|
+
const blocks = ctx.getRegister('blocks');
|
|
2694
|
+
// set whole content to anonymous block if anonymous doesn't specified
|
|
2695
|
+
if (blocks[''] === undefined)
|
|
2696
|
+
blocks[''] = (parent, emitter) => emitter.write(html);
|
|
2697
|
+
ctx.setRegister('blockMode', BlockMode$1.OUTPUT);
|
|
2698
|
+
// render the layout file use stored blocks
|
|
2699
|
+
ctx.push(yield hash.render(ctx));
|
|
2700
|
+
yield renderer.renderTemplates(templates, ctx, emitter);
|
|
2701
|
+
ctx.pop();
|
|
2702
|
+
}
|
|
2703
|
+
};
|
|
2704
|
+
|
|
2705
|
+
class BlockDrop extends Drop {
|
|
2706
|
+
constructor(
|
|
2707
|
+
// the block render from layout template
|
|
2708
|
+
superBlockRender = () => '') {
|
|
2709
|
+
super();
|
|
2710
|
+
this.superBlockRender = superBlockRender;
|
|
2711
|
+
}
|
|
2712
|
+
/**
|
|
2713
|
+
* Provide parent access in child block by
|
|
2714
|
+
* {{ block.super }}
|
|
2715
|
+
*/
|
|
2716
|
+
super() {
|
|
2717
|
+
return this.superBlockRender();
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
|
|
2721
|
+
var block = {
|
|
2722
|
+
parse(token, remainTokens) {
|
|
2723
|
+
const match = /\w+/.exec(token.args);
|
|
2724
|
+
this.block = match ? match[0] : '';
|
|
2725
|
+
this.tpls = [];
|
|
2726
|
+
this.liquid.parser.parseStream(remainTokens)
|
|
2727
|
+
.on('tag:endblock', function () { this.stop(); })
|
|
2728
|
+
.on('template', (tpl) => this.tpls.push(tpl))
|
|
2729
|
+
.on('end', () => { throw new Error(`tag ${token.getText()} not closed`); })
|
|
2730
|
+
.start();
|
|
2731
|
+
},
|
|
2732
|
+
*render(ctx, emitter) {
|
|
2733
|
+
const blockRender = this.getBlockRender(ctx);
|
|
2734
|
+
if (ctx.getRegister('blockMode') === BlockMode$1.STORE) {
|
|
2735
|
+
ctx.getRegister('blocks')[this.block] = blockRender;
|
|
2736
|
+
}
|
|
2737
|
+
else {
|
|
2738
|
+
yield blockRender(new BlockDrop(), emitter);
|
|
2739
|
+
}
|
|
2740
|
+
},
|
|
2741
|
+
getBlockRender(ctx) {
|
|
2742
|
+
const { liquid, tpls } = this;
|
|
2743
|
+
const renderChild = ctx.getRegister('blocks')[this.block];
|
|
2744
|
+
const renderCurrent = function* (superBlock, emitter) {
|
|
2745
|
+
// add {{ block.super }} support when rendering
|
|
2746
|
+
ctx.push({ block: superBlock });
|
|
2747
|
+
yield liquid.renderer.renderTemplates(tpls, ctx, emitter);
|
|
2748
|
+
ctx.pop();
|
|
2749
|
+
};
|
|
2750
|
+
return renderChild
|
|
2751
|
+
? (superBlock, emitter) => renderChild(new BlockDrop(() => renderCurrent(superBlock, emitter)), emitter)
|
|
2752
|
+
: renderCurrent;
|
|
2753
|
+
}
|
|
2754
|
+
};
|
|
2755
|
+
|
|
2756
|
+
var raw = {
|
|
2757
|
+
parse: function (tagToken, remainTokens) {
|
|
2758
|
+
this.tokens = [];
|
|
2759
|
+
const stream = this.liquid.parser.parseStream(remainTokens);
|
|
2760
|
+
stream
|
|
2761
|
+
.on('token', (token) => {
|
|
2762
|
+
if (token.name === 'endraw')
|
|
2763
|
+
stream.stop();
|
|
2764
|
+
else
|
|
2765
|
+
this.tokens.push(token);
|
|
2766
|
+
})
|
|
2767
|
+
.on('end', () => {
|
|
2768
|
+
throw new Error(`tag ${tagToken.getText()} not closed`);
|
|
2769
|
+
});
|
|
2770
|
+
stream.start();
|
|
2771
|
+
},
|
|
2772
|
+
render: function () {
|
|
2773
|
+
return this.tokens.map((token) => token.getText()).join('');
|
|
2774
|
+
}
|
|
2775
|
+
};
|
|
2776
|
+
|
|
2777
|
+
class TablerowloopDrop extends ForloopDrop {
|
|
2778
|
+
constructor(length, cols, collection, variable) {
|
|
2779
|
+
super(length, collection, variable);
|
|
2780
|
+
this.length = length;
|
|
2781
|
+
this.cols = cols;
|
|
2782
|
+
}
|
|
2783
|
+
row() {
|
|
2784
|
+
return Math.floor(this.i / this.cols) + 1;
|
|
2785
|
+
}
|
|
2786
|
+
col0() {
|
|
2787
|
+
return (this.i % this.cols);
|
|
2788
|
+
}
|
|
2789
|
+
col() {
|
|
2790
|
+
return this.col0() + 1;
|
|
2791
|
+
}
|
|
2792
|
+
col_first() {
|
|
2793
|
+
return this.col0() === 0;
|
|
2794
|
+
}
|
|
2795
|
+
col_last() {
|
|
2796
|
+
return this.col() === this.cols;
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
|
|
2800
|
+
var tablerow = {
|
|
2801
|
+
parse: function (tagToken, remainTokens) {
|
|
2802
|
+
const tokenizer = new Tokenizer(tagToken.args, this.liquid.options.operatorsTrie);
|
|
2803
|
+
const variable = tokenizer.readIdentifier();
|
|
2804
|
+
tokenizer.skipBlank();
|
|
2805
|
+
const tmp = tokenizer.readIdentifier();
|
|
2806
|
+
assert(tmp && tmp.content === 'in', () => `illegal tag: ${tagToken.getText()}`);
|
|
2807
|
+
this.variable = variable.content;
|
|
2808
|
+
this.collection = tokenizer.readValue();
|
|
2809
|
+
this.hash = new Hash(tokenizer.remaining());
|
|
2810
|
+
this.templates = [];
|
|
2811
|
+
let p;
|
|
2812
|
+
const stream = this.liquid.parser.parseStream(remainTokens)
|
|
2813
|
+
.on('start', () => (p = this.templates))
|
|
2814
|
+
.on('tag:endtablerow', () => stream.stop())
|
|
2815
|
+
.on('template', (tpl) => p.push(tpl))
|
|
2816
|
+
.on('end', () => {
|
|
2817
|
+
throw new Error(`tag ${tagToken.getText()} not closed`);
|
|
2818
|
+
});
|
|
2819
|
+
stream.start();
|
|
2820
|
+
},
|
|
2821
|
+
render: function* (ctx, emitter) {
|
|
2822
|
+
let collection = toEnumerable(yield _evalToken(this.collection, ctx));
|
|
2823
|
+
const hash = yield this.hash.render(ctx);
|
|
2824
|
+
const offset = hash.offset || 0;
|
|
2825
|
+
const limit = (hash.limit === undefined) ? collection.length : hash.limit;
|
|
2826
|
+
collection = collection.slice(offset, offset + limit);
|
|
2827
|
+
const cols = hash.cols || collection.length;
|
|
2828
|
+
const r = this.liquid.renderer;
|
|
2829
|
+
const tablerowloop = new TablerowloopDrop(collection.length, cols, this.collection.getText(), this.variable);
|
|
2830
|
+
const scope = { tablerowloop };
|
|
2831
|
+
ctx.push(scope);
|
|
2832
|
+
for (let idx = 0; idx < collection.length; idx++, tablerowloop.next()) {
|
|
2833
|
+
scope[this.variable] = collection[idx];
|
|
2834
|
+
if (tablerowloop.col0() === 0) {
|
|
2835
|
+
if (tablerowloop.row() !== 1)
|
|
2836
|
+
emitter.write('</tr>');
|
|
2837
|
+
emitter.write(`<tr class="row${tablerowloop.row()}">`);
|
|
2838
|
+
}
|
|
2839
|
+
emitter.write(`<td class="col${tablerowloop.col()}">`);
|
|
2840
|
+
yield r.renderTemplates(this.templates, ctx, emitter);
|
|
2841
|
+
emitter.write('</td>');
|
|
2842
|
+
}
|
|
2843
|
+
if (collection.length)
|
|
2844
|
+
emitter.write('</tr>');
|
|
2845
|
+
ctx.pop();
|
|
2846
|
+
}
|
|
2847
|
+
};
|
|
2848
|
+
|
|
2849
|
+
var unless = {
|
|
2850
|
+
parse: function (tagToken, remainTokens) {
|
|
2851
|
+
this.branches = [];
|
|
2852
|
+
this.elseTemplates = [];
|
|
2853
|
+
let p;
|
|
2854
|
+
this.liquid.parser.parseStream(remainTokens)
|
|
2855
|
+
.on('start', () => this.branches.push({
|
|
2856
|
+
predicate: new Value(tagToken.args, this.liquid),
|
|
2857
|
+
test: isFalsy,
|
|
2858
|
+
templates: (p = [])
|
|
2859
|
+
}))
|
|
2860
|
+
.on('tag:elsif', (token) => this.branches.push({
|
|
2861
|
+
predicate: new Value(token.args, this.liquid),
|
|
2862
|
+
test: isTruthy,
|
|
2863
|
+
templates: (p = [])
|
|
2864
|
+
}))
|
|
2865
|
+
.on('tag:else', () => (p = this.elseTemplates))
|
|
2866
|
+
.on('tag:endunless', function () { this.stop(); })
|
|
2867
|
+
.on('template', (tpl) => p.push(tpl))
|
|
2868
|
+
.on('end', () => { throw new Error(`tag ${tagToken.getText()} not closed`); })
|
|
2869
|
+
.start();
|
|
2870
|
+
},
|
|
2871
|
+
render: function* (ctx, emitter) {
|
|
2872
|
+
const r = this.liquid.renderer;
|
|
2873
|
+
for (const { predicate, test, templates } of this.branches) {
|
|
2874
|
+
const value = yield predicate.value(ctx, ctx.opts.lenientIf);
|
|
2875
|
+
if (test(value, ctx)) {
|
|
2876
|
+
yield r.renderTemplates(templates, ctx, emitter);
|
|
2877
|
+
return;
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
yield r.renderTemplates(this.elseTemplates, ctx, emitter);
|
|
2881
|
+
}
|
|
2882
|
+
};
|
|
2883
|
+
|
|
2884
|
+
var Break = {
|
|
2885
|
+
render: function (ctx, emitter) {
|
|
2886
|
+
emitter['break'] = true;
|
|
2887
|
+
}
|
|
2888
|
+
};
|
|
2889
|
+
|
|
2890
|
+
var Continue = {
|
|
2891
|
+
render: function (ctx, emitter) {
|
|
2892
|
+
emitter['continue'] = true;
|
|
2893
|
+
}
|
|
2894
|
+
};
|
|
2895
|
+
|
|
2896
|
+
var echo = {
|
|
2897
|
+
parse: function (token) {
|
|
2898
|
+
this.value = new Value(token.args, this.liquid);
|
|
2899
|
+
},
|
|
2900
|
+
render: function* (ctx, emitter) {
|
|
2901
|
+
const val = yield this.value.value(ctx, false);
|
|
2902
|
+
emitter.write(val);
|
|
2903
|
+
}
|
|
2904
|
+
};
|
|
2905
|
+
|
|
2906
|
+
var liquid = {
|
|
2907
|
+
parse: function (token) {
|
|
2908
|
+
const tokenizer = new Tokenizer(token.args, this.liquid.options.operatorsTrie);
|
|
2909
|
+
const tokens = tokenizer.readLiquidTagTokens(this.liquid.options);
|
|
2910
|
+
this.tpls = this.liquid.parser.parseTokens(tokens);
|
|
2911
|
+
},
|
|
2912
|
+
render: function* (ctx, emitter) {
|
|
2913
|
+
yield this.liquid.renderer.renderTemplates(this.tpls, ctx, emitter);
|
|
2914
|
+
}
|
|
2915
|
+
};
|
|
2916
|
+
|
|
2917
|
+
var inlineComment = {
|
|
2918
|
+
parse: function (tagToken, remainTokens) {
|
|
2919
|
+
if (tagToken.args.search(/\n\s*[^#\s]/g) !== -1) {
|
|
2920
|
+
throw new Error('every line of an inline comment must start with a \'#\' character');
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2923
|
+
};
|
|
2924
|
+
|
|
2925
|
+
const tags = {
|
|
2926
|
+
assign, 'for': For, capture, 'case': Case, comment, include, render, decrement, increment, cycle, 'if': If, layout, block, raw, tablerow, unless, 'break': Break, 'continue': Continue, echo, liquid, '#': inlineComment
|
|
2927
|
+
};
|
|
2928
|
+
|
|
2929
|
+
var index = /*#__PURE__*/Object.freeze({
|
|
2930
|
+
__proto__: null,
|
|
2931
|
+
'default': tags
|
|
2932
|
+
});
|
|
2933
|
+
|
|
2934
|
+
const filters = new Map();
|
|
2935
|
+
forOwn(builtinFilters, (conf, name) => {
|
|
2936
|
+
filters.set(snakeCase(name), conf);
|
|
2937
|
+
});
|
|
2938
|
+
const defaultOptions = {
|
|
2939
|
+
root: ['.'],
|
|
2940
|
+
layouts: ['.'],
|
|
2941
|
+
partials: ['.'],
|
|
2942
|
+
relativeReference: true,
|
|
2943
|
+
jekyllInclude: false,
|
|
2944
|
+
cache: undefined,
|
|
2945
|
+
extname: '',
|
|
2946
|
+
fs: fs,
|
|
2947
|
+
dynamicPartials: true,
|
|
2948
|
+
jsTruthy: false,
|
|
2949
|
+
trimTagRight: false,
|
|
2950
|
+
trimTagLeft: false,
|
|
2951
|
+
trimOutputRight: false,
|
|
2952
|
+
trimOutputLeft: false,
|
|
2953
|
+
greedy: true,
|
|
2954
|
+
tagDelimiterLeft: '{%',
|
|
2955
|
+
tagDelimiterRight: '%}',
|
|
2956
|
+
outputDelimiterLeft: '{{',
|
|
2957
|
+
outputDelimiterRight: '}}',
|
|
2958
|
+
preserveTimezones: false,
|
|
2959
|
+
strictFilters: false,
|
|
2960
|
+
strictVariables: false,
|
|
2961
|
+
ownPropertyOnly: false,
|
|
2962
|
+
lenientIf: false,
|
|
2963
|
+
globals: {},
|
|
2964
|
+
keepOutputType: false,
|
|
2965
|
+
operators: defaultOperators,
|
|
2966
|
+
operatorsTrie: createTrie(defaultOperators)
|
|
2967
|
+
};
|
|
2968
|
+
function normalize(options) {
|
|
2969
|
+
if (options.hasOwnProperty('operators')) {
|
|
2970
|
+
options.operatorsTrie = createTrie(options.operators);
|
|
2971
|
+
}
|
|
2972
|
+
if (options.hasOwnProperty('root')) {
|
|
2973
|
+
if (!options.hasOwnProperty('partials'))
|
|
2974
|
+
options.partials = options.root;
|
|
2975
|
+
if (!options.hasOwnProperty('layouts'))
|
|
2976
|
+
options.layouts = options.root;
|
|
2977
|
+
}
|
|
2978
|
+
if (options.hasOwnProperty('cache')) {
|
|
2979
|
+
let cache;
|
|
2980
|
+
if (typeof options.cache === 'number')
|
|
2981
|
+
cache = options.cache > 0 ? new LRU(options.cache) : undefined;
|
|
2982
|
+
else if (typeof options.cache === 'object')
|
|
2983
|
+
cache = options.cache;
|
|
2984
|
+
else
|
|
2985
|
+
cache = options.cache ? new LRU(1024) : undefined;
|
|
2986
|
+
options.cache = cache;
|
|
2987
|
+
}
|
|
2988
|
+
options = Object.assign(Object.assign(Object.assign({}, defaultOptions), (options.jekyllInclude ? { dynamicPartials: false } : {})), options);
|
|
2989
|
+
if (!options.fs.dirname && options.relativeReference) {
|
|
2990
|
+
console.warn('[LiquidJS] `fs.dirname` is required for relativeReference, set relativeReference to `false` to suppress this warning, or provide implementation for `fs.dirname`');
|
|
2991
|
+
options.relativeReference = false;
|
|
2992
|
+
}
|
|
2993
|
+
options.root = normalizeDirectoryList(options.root);
|
|
2994
|
+
options.partials = normalizeDirectoryList(options.partials);
|
|
2995
|
+
options.layouts = normalizeDirectoryList(options.layouts);
|
|
2996
|
+
options.outputEscape = options.outputEscape && getOutputEscapeFunction(options.outputEscape);
|
|
2997
|
+
return options;
|
|
2998
|
+
}
|
|
2999
|
+
function getOutputEscapeFunction(nameOrFunction) {
|
|
3000
|
+
if (isString(nameOrFunction)) {
|
|
3001
|
+
const filterImpl = filters.get(nameOrFunction);
|
|
3002
|
+
assert(isFunction(filterImpl), `filter "${nameOrFunction}" not found`);
|
|
3003
|
+
return filterImpl;
|
|
3004
|
+
}
|
|
3005
|
+
else {
|
|
3006
|
+
assert(isFunction(nameOrFunction), '`outputEscape` need to be of type string or function');
|
|
3007
|
+
return nameOrFunction;
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
function normalizeDirectoryList(value) {
|
|
3011
|
+
let list = [];
|
|
3012
|
+
if (isArray(value))
|
|
3013
|
+
list = value;
|
|
3014
|
+
if (isString(value))
|
|
3015
|
+
list = [value];
|
|
3016
|
+
return list;
|
|
3017
|
+
}
|
|
3018
|
+
|
|
3019
|
+
class Context {
|
|
3020
|
+
constructor(env = {}, opts = defaultOptions, renderOptions = {}) {
|
|
3021
|
+
var _a, _b;
|
|
3022
|
+
/**
|
|
3023
|
+
* insert a Context-level empty scope,
|
|
3024
|
+
* for tags like `{% capture %}` `{% assign %}` to operate
|
|
3025
|
+
*/
|
|
3026
|
+
this.scopes = [{}];
|
|
3027
|
+
this.registers = {};
|
|
3028
|
+
this.sync = !!renderOptions.sync;
|
|
3029
|
+
this.opts = opts;
|
|
3030
|
+
this.globals = (_a = renderOptions.globals) !== null && _a !== void 0 ? _a : opts.globals;
|
|
3031
|
+
this.environments = env;
|
|
3032
|
+
this.strictVariables = (_b = renderOptions.strictVariables) !== null && _b !== void 0 ? _b : this.opts.strictVariables;
|
|
3033
|
+
}
|
|
3034
|
+
getRegister(key) {
|
|
3035
|
+
return (this.registers[key] = this.registers[key] || {});
|
|
3036
|
+
}
|
|
3037
|
+
setRegister(key, value) {
|
|
3038
|
+
return (this.registers[key] = value);
|
|
3039
|
+
}
|
|
3040
|
+
saveRegister(...keys) {
|
|
3041
|
+
return keys.map(key => [key, this.getRegister(key)]);
|
|
3042
|
+
}
|
|
3043
|
+
restoreRegister(keyValues) {
|
|
3044
|
+
return keyValues.forEach(([key, value]) => this.setRegister(key, value));
|
|
3045
|
+
}
|
|
3046
|
+
getAll() {
|
|
3047
|
+
return [this.globals, this.environments, ...this.scopes]
|
|
3048
|
+
.reduce((ctx, val) => __assign(ctx, val), {});
|
|
3049
|
+
}
|
|
3050
|
+
/**
|
|
3051
|
+
* @deprecated use `_get()` instead
|
|
3052
|
+
*/
|
|
3053
|
+
get(paths) {
|
|
3054
|
+
return toValueSync(this._get(paths));
|
|
3055
|
+
}
|
|
3056
|
+
*_get(paths) {
|
|
3057
|
+
const scope = this.findScope(paths[0]);
|
|
3058
|
+
return yield this._getFromScope(scope, paths);
|
|
3059
|
+
}
|
|
3060
|
+
/**
|
|
3061
|
+
* @deprecated use `_get()` instead
|
|
3062
|
+
*/
|
|
3063
|
+
getFromScope(scope, paths) {
|
|
3064
|
+
return toValueSync(this._getFromScope(scope, paths));
|
|
3065
|
+
}
|
|
3066
|
+
*_getFromScope(scope, paths) {
|
|
3067
|
+
if (isString(paths))
|
|
3068
|
+
paths = paths.split('.');
|
|
3069
|
+
for (let i = 0; i < paths.length; i++) {
|
|
3070
|
+
scope = yield readProperty(scope, paths[i], this.opts.ownPropertyOnly);
|
|
3071
|
+
if (isNil(scope) && this.strictVariables) {
|
|
3072
|
+
throw new InternalUndefinedVariableError(paths.slice(0, i + 1).join('.'));
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
return scope;
|
|
3076
|
+
}
|
|
3077
|
+
push(ctx) {
|
|
3078
|
+
return this.scopes.push(ctx);
|
|
3079
|
+
}
|
|
3080
|
+
pop() {
|
|
3081
|
+
return this.scopes.pop();
|
|
3082
|
+
}
|
|
3083
|
+
bottom() {
|
|
3084
|
+
return this.scopes[0];
|
|
3085
|
+
}
|
|
3086
|
+
findScope(key) {
|
|
3087
|
+
for (let i = this.scopes.length - 1; i >= 0; i--) {
|
|
3088
|
+
const candidate = this.scopes[i];
|
|
3089
|
+
if (key in candidate)
|
|
3090
|
+
return candidate;
|
|
3091
|
+
}
|
|
3092
|
+
if (key in this.environments)
|
|
3093
|
+
return this.environments;
|
|
3094
|
+
return this.globals;
|
|
3095
|
+
}
|
|
3096
|
+
}
|
|
3097
|
+
function readProperty(obj, key, ownPropertyOnly) {
|
|
3098
|
+
if (isNil(obj))
|
|
3099
|
+
return obj;
|
|
3100
|
+
obj = toLiquid(obj);
|
|
3101
|
+
if (isArray(obj) && key < 0)
|
|
3102
|
+
return obj[obj.length + +key];
|
|
3103
|
+
const jsProperty = readJSProperty(obj, key, ownPropertyOnly);
|
|
3104
|
+
if (jsProperty === undefined && obj instanceof Drop)
|
|
3105
|
+
return obj.liquidMethodMissing(key);
|
|
3106
|
+
if (isFunction(jsProperty))
|
|
3107
|
+
return jsProperty.call(obj);
|
|
3108
|
+
if (key === 'size')
|
|
3109
|
+
return readSize(obj);
|
|
3110
|
+
else if (key === 'first')
|
|
3111
|
+
return readFirst(obj);
|
|
3112
|
+
else if (key === 'last')
|
|
3113
|
+
return readLast(obj);
|
|
3114
|
+
return jsProperty;
|
|
3115
|
+
}
|
|
3116
|
+
function readJSProperty(obj, key, ownPropertyOnly) {
|
|
3117
|
+
if (ownPropertyOnly && !Object.hasOwnProperty.call(obj, key))
|
|
3118
|
+
return undefined;
|
|
3119
|
+
return obj[key];
|
|
3120
|
+
}
|
|
3121
|
+
function readFirst(obj) {
|
|
3122
|
+
if (isArray(obj))
|
|
3123
|
+
return obj[0];
|
|
3124
|
+
return obj['first'];
|
|
3125
|
+
}
|
|
3126
|
+
function readLast(obj) {
|
|
3127
|
+
if (isArray(obj))
|
|
3128
|
+
return obj[obj.length - 1];
|
|
3129
|
+
return obj['last'];
|
|
3130
|
+
}
|
|
3131
|
+
function readSize(obj) {
|
|
3132
|
+
if (obj.hasOwnProperty('size') || obj['size'] !== undefined)
|
|
3133
|
+
return obj['size'];
|
|
3134
|
+
if (isArray(obj) || isString(obj))
|
|
3135
|
+
return obj.length;
|
|
3136
|
+
if (typeof obj === 'object')
|
|
3137
|
+
return Object.keys(obj).length;
|
|
3138
|
+
}
|
|
3139
|
+
|
|
3140
|
+
var LookupType;
|
|
3141
|
+
(function (LookupType) {
|
|
3142
|
+
LookupType["Partials"] = "partials";
|
|
3143
|
+
LookupType["Layouts"] = "layouts";
|
|
3144
|
+
LookupType["Root"] = "root";
|
|
3145
|
+
})(LookupType || (LookupType = {}));
|
|
3146
|
+
class Loader {
|
|
3147
|
+
constructor(options) {
|
|
3148
|
+
this.options = options;
|
|
3149
|
+
if (options.relativeReference) {
|
|
3150
|
+
const sep = options.fs.sep;
|
|
3151
|
+
assert(sep, '`fs.sep` is required for relative reference');
|
|
3152
|
+
const rRelativePath = new RegExp(['.' + sep, '..' + sep, './', '../'].map(prefix => escapeRegex(prefix)).join('|'));
|
|
3153
|
+
this.shouldLoadRelative = (referencedFile) => rRelativePath.test(referencedFile);
|
|
3154
|
+
}
|
|
3155
|
+
else {
|
|
3156
|
+
this.shouldLoadRelative = (referencedFile) => false;
|
|
3157
|
+
}
|
|
3158
|
+
this.contains = this.options.fs.contains || (() => true);
|
|
3159
|
+
}
|
|
3160
|
+
*lookup(file, type, sync, currentFile) {
|
|
3161
|
+
const { fs } = this.options;
|
|
3162
|
+
const dirs = this.options[type];
|
|
3163
|
+
for (const filepath of this.candidates(file, dirs, currentFile, type !== LookupType.Root)) {
|
|
3164
|
+
if (sync ? fs.existsSync(filepath) : yield fs.exists(filepath))
|
|
3165
|
+
return filepath;
|
|
3166
|
+
}
|
|
3167
|
+
throw this.lookupError(file, dirs);
|
|
3168
|
+
}
|
|
3169
|
+
*candidates(file, dirs, currentFile, enforceRoot) {
|
|
3170
|
+
const { fs, extname } = this.options;
|
|
3171
|
+
if (this.shouldLoadRelative(file) && currentFile) {
|
|
3172
|
+
const referenced = fs.resolve(this.dirname(currentFile), file, extname);
|
|
3173
|
+
for (const dir of dirs) {
|
|
3174
|
+
if (!enforceRoot || this.contains(dir, referenced)) {
|
|
3175
|
+
// the relatively referenced file is within one of root dirs
|
|
3176
|
+
yield referenced;
|
|
3177
|
+
break;
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
for (const dir of dirs) {
|
|
3182
|
+
const referenced = fs.resolve(dir, file, extname);
|
|
3183
|
+
if (!enforceRoot || this.contains(dir, referenced)) {
|
|
3184
|
+
yield referenced;
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
if (fs.fallback !== undefined) {
|
|
3188
|
+
const filepath = fs.fallback(file);
|
|
3189
|
+
if (filepath !== undefined)
|
|
3190
|
+
yield filepath;
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
dirname(path) {
|
|
3194
|
+
const fs = this.options.fs;
|
|
3195
|
+
assert(fs.dirname, '`fs.dirname` is required for relative reference');
|
|
3196
|
+
return fs.dirname(path);
|
|
3197
|
+
}
|
|
3198
|
+
lookupError(file, roots) {
|
|
3199
|
+
const err = new Error('ENOENT');
|
|
3200
|
+
err.message = `ENOENT: Failed to lookup "${file}" in "${roots}"`;
|
|
3201
|
+
err.code = 'ENOENT';
|
|
3202
|
+
return err;
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
|
|
3206
|
+
class SimpleEmitter {
|
|
3207
|
+
constructor() {
|
|
3208
|
+
this.buffer = '';
|
|
3209
|
+
}
|
|
3210
|
+
write(html) {
|
|
3211
|
+
this.buffer += stringify(html);
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
|
|
3215
|
+
class StreamedEmitter {
|
|
3216
|
+
constructor() {
|
|
3217
|
+
this.buffer = '';
|
|
3218
|
+
this.stream = new PassThrough();
|
|
3219
|
+
}
|
|
3220
|
+
write(html) {
|
|
3221
|
+
this.stream.write(stringify(html));
|
|
3222
|
+
}
|
|
3223
|
+
error(err) {
|
|
3224
|
+
this.stream.emit('error', err);
|
|
3225
|
+
}
|
|
3226
|
+
end() {
|
|
3227
|
+
this.stream.end();
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
|
|
3231
|
+
class KeepingTypeEmitter {
|
|
3232
|
+
constructor() {
|
|
3233
|
+
this.buffer = '';
|
|
3234
|
+
}
|
|
3235
|
+
write(html) {
|
|
3236
|
+
html = toValue(html);
|
|
3237
|
+
// This will only preserve the type if the value is isolated.
|
|
3238
|
+
// I.E:
|
|
3239
|
+
// {{ my-port }} -> 42
|
|
3240
|
+
// {{ my-host }}:{{ my-port }} -> 'host:42'
|
|
3241
|
+
if (typeof html !== 'string' && this.buffer === '') {
|
|
3242
|
+
this.buffer = html;
|
|
3243
|
+
}
|
|
3244
|
+
else {
|
|
3245
|
+
this.buffer = stringify(this.buffer) + stringify(html);
|
|
3246
|
+
}
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
|
|
3250
|
+
class Render {
|
|
3251
|
+
renderTemplatesToNodeStream(templates, ctx) {
|
|
3252
|
+
const emitter = new StreamedEmitter();
|
|
3253
|
+
Promise.resolve().then(() => toPromise(this.renderTemplates(templates, ctx, emitter)))
|
|
3254
|
+
.then(() => emitter.end(), err => emitter.error(err));
|
|
3255
|
+
return emitter.stream;
|
|
3256
|
+
}
|
|
3257
|
+
*renderTemplates(templates, ctx, emitter) {
|
|
3258
|
+
if (!emitter) {
|
|
3259
|
+
emitter = ctx.opts.keepOutputType ? new KeepingTypeEmitter() : new SimpleEmitter();
|
|
3260
|
+
}
|
|
3261
|
+
for (const tpl of templates) {
|
|
3262
|
+
try {
|
|
3263
|
+
// if tpl.render supports emitter, it'll return empty `html`
|
|
3264
|
+
const html = yield tpl.render(ctx, emitter);
|
|
3265
|
+
// if not, it'll return an `html`, write to the emitter for it
|
|
3266
|
+
html && emitter.write(html);
|
|
3267
|
+
if (emitter['break'] || emitter['continue'])
|
|
3268
|
+
break;
|
|
3269
|
+
}
|
|
3270
|
+
catch (e) {
|
|
3271
|
+
const err = RenderError.is(e) ? e : new RenderError(e, tpl);
|
|
3272
|
+
throw err;
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3275
|
+
return emitter.buffer;
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
|
|
3279
|
+
class TemplateImpl {
|
|
3280
|
+
constructor(token) {
|
|
3281
|
+
this.token = token;
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3284
|
+
|
|
3285
|
+
class Tag extends TemplateImpl {
|
|
3286
|
+
constructor(token, tokens, liquid) {
|
|
3287
|
+
super(token);
|
|
3288
|
+
this.name = token.name;
|
|
3289
|
+
const impl = liquid.tags.get(token.name);
|
|
3290
|
+
this.impl = Object.create(impl);
|
|
3291
|
+
this.impl.liquid = liquid;
|
|
3292
|
+
if (this.impl.parse) {
|
|
3293
|
+
this.impl.parse(token, tokens);
|
|
3294
|
+
}
|
|
3295
|
+
}
|
|
3296
|
+
*render(ctx, emitter) {
|
|
3297
|
+
const hash = (yield new Hash(this.token.args).render(ctx));
|
|
3298
|
+
const impl = this.impl;
|
|
3299
|
+
if (isFunction(impl.render))
|
|
3300
|
+
return yield impl.render(ctx, emitter, hash);
|
|
3301
|
+
}
|
|
3302
|
+
}
|
|
3303
|
+
|
|
3304
|
+
class Output extends TemplateImpl {
|
|
3305
|
+
constructor(token, liquid) {
|
|
3306
|
+
super(token);
|
|
3307
|
+
this.value = new Value(token.content, liquid);
|
|
3308
|
+
const filters = this.value.filters;
|
|
3309
|
+
const outputEscape = liquid.options.outputEscape;
|
|
3310
|
+
if (filters.length && filters[filters.length - 1].name === 'raw') {
|
|
3311
|
+
filters.pop();
|
|
3312
|
+
}
|
|
3313
|
+
else if (outputEscape) {
|
|
3314
|
+
filters.push(new Filter(toString.call(outputEscape), outputEscape, [], liquid));
|
|
3315
|
+
}
|
|
3316
|
+
}
|
|
3317
|
+
*render(ctx, emitter) {
|
|
3318
|
+
const val = yield this.value.value(ctx, false);
|
|
3319
|
+
emitter.write(val);
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
class HTML extends TemplateImpl {
|
|
3324
|
+
constructor(token) {
|
|
3325
|
+
super(token);
|
|
3326
|
+
this.str = token.getContent();
|
|
3327
|
+
}
|
|
3328
|
+
*render(ctx, emitter) {
|
|
3329
|
+
emitter.write(this.str);
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
|
|
3333
|
+
class Parser {
|
|
3334
|
+
constructor(liquid) {
|
|
3335
|
+
this.liquid = liquid;
|
|
3336
|
+
this.cache = this.liquid.options.cache;
|
|
3337
|
+
this.fs = this.liquid.options.fs;
|
|
3338
|
+
this.parseFile = this.cache ? this._parseFileCached : this._parseFile;
|
|
3339
|
+
this.loader = new Loader(this.liquid.options);
|
|
3340
|
+
}
|
|
3341
|
+
parse(html, filepath) {
|
|
3342
|
+
const tokenizer = new Tokenizer(html, this.liquid.options.operatorsTrie, filepath);
|
|
3343
|
+
const tokens = tokenizer.readTopLevelTokens(this.liquid.options);
|
|
3344
|
+
return this.parseTokens(tokens);
|
|
3345
|
+
}
|
|
3346
|
+
parseTokens(tokens) {
|
|
3347
|
+
let token;
|
|
3348
|
+
const templates = [];
|
|
3349
|
+
while ((token = tokens.shift())) {
|
|
3350
|
+
templates.push(this.parseToken(token, tokens));
|
|
3351
|
+
}
|
|
3352
|
+
return templates;
|
|
3353
|
+
}
|
|
3354
|
+
parseToken(token, remainTokens) {
|
|
3355
|
+
try {
|
|
3356
|
+
if (isTagToken(token)) {
|
|
3357
|
+
return new Tag(token, remainTokens, this.liquid);
|
|
3358
|
+
}
|
|
3359
|
+
if (isOutputToken(token)) {
|
|
3360
|
+
return new Output(token, this.liquid);
|
|
3361
|
+
}
|
|
3362
|
+
return new HTML(token);
|
|
3363
|
+
}
|
|
3364
|
+
catch (e) {
|
|
3365
|
+
throw new ParseError(e, token);
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
parseStream(tokens) {
|
|
3369
|
+
return new ParseStream(tokens, (token, tokens) => this.parseToken(token, tokens));
|
|
3370
|
+
}
|
|
3371
|
+
*_parseFileCached(file, sync, type = LookupType.Root, currentFile) {
|
|
3372
|
+
const cache = this.cache;
|
|
3373
|
+
const key = this.loader.shouldLoadRelative(file) ? currentFile + ',' + file : type + ':' + file;
|
|
3374
|
+
const tpls = yield cache.read(key);
|
|
3375
|
+
if (tpls)
|
|
3376
|
+
return tpls;
|
|
3377
|
+
const task = this._parseFile(file, sync, type, currentFile);
|
|
3378
|
+
// sync mode: exec the task and cache the result
|
|
3379
|
+
// async mode: cache the task before exec
|
|
3380
|
+
const taskOrTpl = sync ? yield task : toPromise(task);
|
|
3381
|
+
cache.write(key, taskOrTpl);
|
|
3382
|
+
// note: concurrent tasks will be reused, cache for failed task is removed until its end
|
|
3383
|
+
try {
|
|
3384
|
+
return yield taskOrTpl;
|
|
3385
|
+
}
|
|
3386
|
+
catch (err) {
|
|
3387
|
+
cache.remove(key);
|
|
3388
|
+
throw err;
|
|
3389
|
+
}
|
|
3390
|
+
}
|
|
3391
|
+
*_parseFile(file, sync, type = LookupType.Root, currentFile) {
|
|
3392
|
+
const filepath = yield this.loader.lookup(file, type, sync, currentFile);
|
|
3393
|
+
return this.liquid.parse(sync ? this.fs.readFileSync(filepath) : yield this.fs.readFile(filepath), filepath);
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
|
|
3397
|
+
class TagMap {
|
|
3398
|
+
constructor() {
|
|
3399
|
+
this.impls = {};
|
|
3400
|
+
}
|
|
3401
|
+
get(name) {
|
|
3402
|
+
const impl = this.impls[name];
|
|
3403
|
+
assert(impl, () => `tag "${name}" not found`);
|
|
3404
|
+
return impl;
|
|
3405
|
+
}
|
|
3406
|
+
set(name, impl) {
|
|
3407
|
+
this.impls[name] = impl;
|
|
3408
|
+
}
|
|
3409
|
+
}
|
|
3410
|
+
|
|
3411
|
+
class FilterMap {
|
|
3412
|
+
constructor(strictFilters, liquid) {
|
|
3413
|
+
this.strictFilters = strictFilters;
|
|
3414
|
+
this.liquid = liquid;
|
|
3415
|
+
this.impls = {};
|
|
3416
|
+
}
|
|
3417
|
+
get(name) {
|
|
3418
|
+
const impl = this.impls[name];
|
|
3419
|
+
assert(impl || !this.strictFilters, () => `undefined filter: ${name}`);
|
|
3420
|
+
return impl;
|
|
3421
|
+
}
|
|
3422
|
+
set(name, impl) {
|
|
3423
|
+
this.impls[name] = impl;
|
|
3424
|
+
}
|
|
3425
|
+
create(name, args) {
|
|
3426
|
+
return new Filter(name, this.get(name), args, this.liquid);
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
|
|
3430
|
+
const version = '9.43.0';
|
|
3431
|
+
class Liquid {
|
|
3432
|
+
constructor(opts = {}) {
|
|
3433
|
+
this.options = normalize(opts);
|
|
3434
|
+
this.parser = new Parser(this);
|
|
3435
|
+
this.renderer = new Render();
|
|
3436
|
+
this.filters = new FilterMap(this.options.strictFilters, this);
|
|
3437
|
+
this.tags = new TagMap();
|
|
3438
|
+
forOwn(tags, (conf, name) => this.registerTag(snakeCase(name), conf));
|
|
3439
|
+
forOwn(builtinFilters, (handler, name) => this.registerFilter(snakeCase(name), handler));
|
|
3440
|
+
}
|
|
3441
|
+
parse(html, filepath) {
|
|
3442
|
+
return this.parser.parse(html, filepath);
|
|
3443
|
+
}
|
|
3444
|
+
_render(tpl, scope, renderOptions) {
|
|
3445
|
+
const ctx = new Context(scope, this.options, renderOptions);
|
|
3446
|
+
return this.renderer.renderTemplates(tpl, ctx);
|
|
3447
|
+
}
|
|
3448
|
+
render(tpl, scope, renderOptions) {
|
|
3449
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3450
|
+
return toPromise(this._render(tpl, scope, Object.assign(Object.assign({}, renderOptions), { sync: false })));
|
|
3451
|
+
});
|
|
3452
|
+
}
|
|
3453
|
+
renderSync(tpl, scope, renderOptions) {
|
|
3454
|
+
return toValueSync(this._render(tpl, scope, Object.assign(Object.assign({}, renderOptions), { sync: true })));
|
|
3455
|
+
}
|
|
3456
|
+
renderToNodeStream(tpl, scope, renderOptions = {}) {
|
|
3457
|
+
const ctx = new Context(scope, this.options, renderOptions);
|
|
3458
|
+
return this.renderer.renderTemplatesToNodeStream(tpl, ctx);
|
|
3459
|
+
}
|
|
3460
|
+
_parseAndRender(html, scope, renderOptions) {
|
|
3461
|
+
const tpl = this.parse(html);
|
|
3462
|
+
return this._render(tpl, scope, renderOptions);
|
|
3463
|
+
}
|
|
3464
|
+
parseAndRender(html, scope, renderOptions) {
|
|
3465
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3466
|
+
return toPromise(this._parseAndRender(html, scope, Object.assign(Object.assign({}, renderOptions), { sync: false })));
|
|
3467
|
+
});
|
|
3468
|
+
}
|
|
3469
|
+
parseAndRenderSync(html, scope, renderOptions) {
|
|
3470
|
+
return toValueSync(this._parseAndRender(html, scope, Object.assign(Object.assign({}, renderOptions), { sync: true })));
|
|
3471
|
+
}
|
|
3472
|
+
_parsePartialFile(file, sync, currentFile) {
|
|
3473
|
+
return this.parser.parseFile(file, sync, LookupType.Partials, currentFile);
|
|
3474
|
+
}
|
|
3475
|
+
_parseLayoutFile(file, sync, currentFile) {
|
|
3476
|
+
return this.parser.parseFile(file, sync, LookupType.Layouts, currentFile);
|
|
3477
|
+
}
|
|
3478
|
+
parseFile(file) {
|
|
3479
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3480
|
+
return toPromise(this.parser.parseFile(file, false));
|
|
3481
|
+
});
|
|
3482
|
+
}
|
|
3483
|
+
parseFileSync(file) {
|
|
3484
|
+
return toValueSync(this.parser.parseFile(file, true));
|
|
3485
|
+
}
|
|
3486
|
+
renderFile(file, ctx, renderOptions) {
|
|
3487
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3488
|
+
const templates = yield this.parseFile(file);
|
|
3489
|
+
return this.render(templates, ctx, renderOptions);
|
|
3490
|
+
});
|
|
3491
|
+
}
|
|
3492
|
+
renderFileSync(file, ctx, renderOptions) {
|
|
3493
|
+
const templates = this.parseFileSync(file);
|
|
3494
|
+
return this.renderSync(templates, ctx, renderOptions);
|
|
3495
|
+
}
|
|
3496
|
+
renderFileToNodeStream(file, scope, renderOptions) {
|
|
3497
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3498
|
+
const templates = yield this.parseFile(file);
|
|
3499
|
+
return this.renderToNodeStream(templates, scope, renderOptions);
|
|
3500
|
+
});
|
|
3501
|
+
}
|
|
3502
|
+
_evalValue(str, scopeOrContext) {
|
|
3503
|
+
const value = new Value(str, this);
|
|
3504
|
+
const ctx = scopeOrContext instanceof Context ? scopeOrContext : new Context(scopeOrContext, this.options);
|
|
3505
|
+
return value.value(ctx, false);
|
|
3506
|
+
}
|
|
3507
|
+
evalValue(str, scopeOrContext) {
|
|
3508
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3509
|
+
return toPromise(this._evalValue(str, scopeOrContext));
|
|
3510
|
+
});
|
|
3511
|
+
}
|
|
3512
|
+
evalValueSync(str, scopeOrContext) {
|
|
3513
|
+
return toValueSync(this._evalValue(str, scopeOrContext));
|
|
3514
|
+
}
|
|
3515
|
+
registerFilter(name, filter) {
|
|
3516
|
+
this.filters.set(name, filter);
|
|
3517
|
+
}
|
|
3518
|
+
registerTag(name, tag) {
|
|
3519
|
+
this.tags.set(name, tag);
|
|
3520
|
+
}
|
|
3521
|
+
plugin(plugin) {
|
|
3522
|
+
return plugin.call(this, Liquid);
|
|
3523
|
+
}
|
|
3524
|
+
express() {
|
|
3525
|
+
const self = this; // eslint-disable-line
|
|
3526
|
+
let firstCall = true;
|
|
3527
|
+
return function (filePath, ctx, callback) {
|
|
3528
|
+
if (firstCall) {
|
|
3529
|
+
firstCall = false;
|
|
3530
|
+
const dirs = normalizeDirectoryList(this.root);
|
|
3531
|
+
self.options.root.unshift(...dirs);
|
|
3532
|
+
self.options.layouts.unshift(...dirs);
|
|
3533
|
+
self.options.partials.unshift(...dirs);
|
|
3534
|
+
}
|
|
3535
|
+
self.renderFile(filePath, ctx).then(html => callback(null, html), callback);
|
|
3536
|
+
};
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
|
|
3540
|
+
export { AssertionError, Context, Drop, Expression, Hash, InternalUndefinedVariableError, Liquid, LiquidError, ParseError, ParseStream, RenderError, TagToken, TimezoneDate, Token, TokenKind, TokenizationError, Tokenizer, typeGuards as TypeGuards, UndefinedVariableError, Value, _evalToken, assert, createTrie, defaultOperators, defaultOptions, evalQuotedToken, evalToken, builtinFilters as filters, isFalsy, isTruthy, index as tags, toPromise, toThenable, toValue, toValueSync, version };
|