@samuelbines/nunjucks 0.0.3
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/LICENSE +26 -0
- package/README.md +55 -0
- package/dist/scripts/smoke.d.ts +1 -0
- package/dist/scripts/smoke.js +95 -0
- package/dist/src/compiler.d.ts +12 -0
- package/dist/src/compiler.js +1050 -0
- package/dist/src/environment.d.ts +103 -0
- package/dist/src/environment.js +621 -0
- package/dist/src/express-app.d.ts +2 -0
- package/dist/src/express-app.js +33 -0
- package/dist/src/filters.d.ts +44 -0
- package/dist/src/filters.js +424 -0
- package/dist/src/globals.d.ts +14 -0
- package/dist/src/globals.js +342 -0
- package/dist/src/index.d.ts +28 -0
- package/dist/src/index.js +116 -0
- package/dist/src/interpreter.d.ts +16 -0
- package/dist/src/interpreter.js +489 -0
- package/dist/src/lexer.d.ts +72 -0
- package/dist/src/lexer.js +480 -0
- package/dist/src/lib.d.ts +74 -0
- package/dist/src/lib.js +237 -0
- package/dist/src/loader.d.ts +80 -0
- package/dist/src/loader.js +175 -0
- package/dist/src/nodes.d.ts +362 -0
- package/dist/src/nodes.js +894 -0
- package/dist/src/parser.d.ts +66 -0
- package/dist/src/parser.js +1068 -0
- package/dist/src/precompile.d.ts +15 -0
- package/dist/src/precompile.js +108 -0
- package/dist/src/runtime.d.ts +33 -0
- package/dist/src/runtime.js +314 -0
- package/dist/src/transformer.d.ts +3 -0
- package/dist/src/transformer.js +161 -0
- package/dist/src/types.d.ts +27 -0
- package/dist/src/types.js +2 -0
- package/dist/tests/compiler.test.d.ts +1 -0
- package/dist/tests/compiler.test.js +201 -0
- package/dist/tests/enviornment.test.d.ts +1 -0
- package/dist/tests/enviornment.test.js +279 -0
- package/dist/tests/express.test.d.ts +1 -0
- package/dist/tests/express.test.js +86 -0
- package/dist/tests/filters.test.d.ts +13 -0
- package/dist/tests/filters.test.js +286 -0
- package/dist/tests/globals.test.d.ts +1 -0
- package/dist/tests/globals.test.js +579 -0
- package/dist/tests/interpreter.test.d.ts +1 -0
- package/dist/tests/interpreter.test.js +208 -0
- package/dist/tests/lexer.test.d.ts +1 -0
- package/dist/tests/lexer.test.js +249 -0
- package/dist/tests/lib.test.d.ts +1 -0
- package/dist/tests/lib.test.js +236 -0
- package/dist/tests/loader.test.d.ts +1 -0
- package/dist/tests/loader.test.js +301 -0
- package/dist/tests/nodes.test.d.ts +1 -0
- package/dist/tests/nodes.test.js +137 -0
- package/dist/tests/parser.test.d.ts +1 -0
- package/dist/tests/parser.test.js +294 -0
- package/dist/tests/precompile.test.d.ts +1 -0
- package/dist/tests/precompile.test.js +224 -0
- package/dist/tests/runtime.test.d.ts +1 -0
- package/dist/tests/runtime.test.js +237 -0
- package/dist/tests/transformer.test.d.ts +1 -0
- package/dist/tests/transformer.test.js +125 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.lex = exports.Tokenizer = exports.TOKEN_REGEX = exports.TOKEN_SPECIAL = exports.TOKEN_SYMBOL = exports.TOKEN_NONE = exports.TOKEN_BOOLEAN = exports.TOKEN_FLOAT = exports.TOKEN_INT = exports.TOKEN_PIPE = exports.TOKEN_TILDE = exports.TOKEN_COLON = exports.TOKEN_COMMA = exports.TOKEN_OPERATOR = exports.TOKEN_RIGHT_CURLY = exports.TOKEN_LEFT_CURLY = exports.TOKEN_RIGHT_BRACKET = exports.TOKEN_LEFT_BRACKET = exports.TOKEN_RIGHT_PAREN = exports.TOKEN_LEFT_PAREN = exports.TOKEN_COMMENT = exports.TOKEN_VARIABLE_END = exports.TOKEN_VARIABLE_START = exports.TOKEN_BLOCK_END = exports.TOKEN_BLOCK_START = exports.TOKEN_DATA = exports.TOKEN_WHITESPACE = exports.TOKEN_STRING = exports.COMMENT_END = exports.COMMENT_START = exports.VARIABLE_END = exports.VARIABLE_START = exports.BLOCK_END = exports.BLOCK_START = exports.intChars = exports.delimChars = exports.whitespaceChars = void 0;
|
|
4
|
+
exports.whitespaceChars = ' \n\t\r\u00A0';
|
|
5
|
+
exports.delimChars = '()[]{}%*-+~/#,:|.<>=!';
|
|
6
|
+
exports.intChars = '0123456789';
|
|
7
|
+
exports.BLOCK_START = '{%';
|
|
8
|
+
exports.BLOCK_END = '%}';
|
|
9
|
+
exports.VARIABLE_START = '{{';
|
|
10
|
+
exports.VARIABLE_END = '}}';
|
|
11
|
+
exports.COMMENT_START = '{#';
|
|
12
|
+
exports.COMMENT_END = '#}';
|
|
13
|
+
exports.TOKEN_STRING = 'string';
|
|
14
|
+
exports.TOKEN_WHITESPACE = 'whitespace';
|
|
15
|
+
exports.TOKEN_DATA = 'data';
|
|
16
|
+
exports.TOKEN_BLOCK_START = 'block-start';
|
|
17
|
+
exports.TOKEN_BLOCK_END = 'block-end';
|
|
18
|
+
exports.TOKEN_VARIABLE_START = 'variable-start';
|
|
19
|
+
exports.TOKEN_VARIABLE_END = 'variable-end';
|
|
20
|
+
exports.TOKEN_COMMENT = 'comment';
|
|
21
|
+
exports.TOKEN_LEFT_PAREN = 'left-paren';
|
|
22
|
+
exports.TOKEN_RIGHT_PAREN = 'right-paren';
|
|
23
|
+
exports.TOKEN_LEFT_BRACKET = 'left-bracket';
|
|
24
|
+
exports.TOKEN_RIGHT_BRACKET = 'right-bracket';
|
|
25
|
+
exports.TOKEN_LEFT_CURLY = 'left-curly';
|
|
26
|
+
exports.TOKEN_RIGHT_CURLY = 'right-curly';
|
|
27
|
+
exports.TOKEN_OPERATOR = 'operator';
|
|
28
|
+
exports.TOKEN_COMMA = 'comma';
|
|
29
|
+
exports.TOKEN_COLON = 'colon';
|
|
30
|
+
exports.TOKEN_TILDE = 'tilde';
|
|
31
|
+
exports.TOKEN_PIPE = 'pipe';
|
|
32
|
+
exports.TOKEN_INT = 'int';
|
|
33
|
+
exports.TOKEN_FLOAT = 'float';
|
|
34
|
+
exports.TOKEN_BOOLEAN = 'boolean';
|
|
35
|
+
exports.TOKEN_NONE = 'none';
|
|
36
|
+
exports.TOKEN_SYMBOL = 'symbol';
|
|
37
|
+
exports.TOKEN_SPECIAL = 'special';
|
|
38
|
+
exports.TOKEN_REGEX = 'regex';
|
|
39
|
+
const token = (type, value, lineno, colno) => ({
|
|
40
|
+
type: type,
|
|
41
|
+
value: value,
|
|
42
|
+
lineno: lineno,
|
|
43
|
+
colno: colno,
|
|
44
|
+
});
|
|
45
|
+
class Tokenizer {
|
|
46
|
+
str = '';
|
|
47
|
+
index = 0;
|
|
48
|
+
len = 0;
|
|
49
|
+
lineno = 0;
|
|
50
|
+
colno = 0;
|
|
51
|
+
inCode = false;
|
|
52
|
+
trimBlocks = false;
|
|
53
|
+
lstripBlocks = false;
|
|
54
|
+
tags;
|
|
55
|
+
src;
|
|
56
|
+
constructor(str, opts) {
|
|
57
|
+
this.str = str;
|
|
58
|
+
this.len = str?.length;
|
|
59
|
+
opts = opts || {};
|
|
60
|
+
let tags = opts.tags || {};
|
|
61
|
+
this.tags = {
|
|
62
|
+
BLOCK_START: tags.blockStart || exports.BLOCK_START,
|
|
63
|
+
BLOCK_END: tags.blockEnd || exports.BLOCK_END,
|
|
64
|
+
VARIABLE_START: tags.variableStart || exports.VARIABLE_START,
|
|
65
|
+
VARIABLE_END: tags.variableEnd || exports.VARIABLE_END,
|
|
66
|
+
COMMENT_START: tags.commentStart || exports.COMMENT_START,
|
|
67
|
+
COMMENT_END: tags.commentEnd || exports.COMMENT_END,
|
|
68
|
+
};
|
|
69
|
+
this.trimBlocks = !!opts.trimBlocks;
|
|
70
|
+
this.lstripBlocks = !!opts.lstripBlocks;
|
|
71
|
+
}
|
|
72
|
+
nextToken() {
|
|
73
|
+
let lineno = this?.lineno;
|
|
74
|
+
let colno = this?.colno;
|
|
75
|
+
let tok;
|
|
76
|
+
if (this.inCode) {
|
|
77
|
+
// Otherwise, if we are in a block parse it as code
|
|
78
|
+
let cur = this.current();
|
|
79
|
+
if (this.isFinished()) {
|
|
80
|
+
// We have nothing else to parse
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
else if (cur === '"' || cur === "'") {
|
|
84
|
+
// We've hit a string
|
|
85
|
+
return token(exports.TOKEN_STRING, this._parseString(cur), lineno, colno);
|
|
86
|
+
}
|
|
87
|
+
else if ((tok = this._extract(exports.whitespaceChars))) {
|
|
88
|
+
// We hit some whitespace
|
|
89
|
+
return token(exports.TOKEN_WHITESPACE, tok, lineno, colno);
|
|
90
|
+
}
|
|
91
|
+
else if ((tok = this._extractString(this.tags.BLOCK_END)) ||
|
|
92
|
+
(tok = this._extractString('-' + this.tags.BLOCK_END))) {
|
|
93
|
+
// Special check for the block end tag
|
|
94
|
+
//
|
|
95
|
+
// It is a requirement that start and end tags are composed of
|
|
96
|
+
// delimiter characters (%{}[] etc), and our code always
|
|
97
|
+
// breaks on delimiters so we can assume the token parsing
|
|
98
|
+
// doesn't consume these elsewhere
|
|
99
|
+
this.inCode = false;
|
|
100
|
+
if (this.trimBlocks) {
|
|
101
|
+
cur = this.current();
|
|
102
|
+
if (cur === '\n') {
|
|
103
|
+
// Skip newline
|
|
104
|
+
this.forward();
|
|
105
|
+
}
|
|
106
|
+
else if (cur === '\r') {
|
|
107
|
+
// Skip CRLF newline
|
|
108
|
+
this.forward();
|
|
109
|
+
cur = this.current();
|
|
110
|
+
if (cur === '\n') {
|
|
111
|
+
this.forward();
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
// Was not a CRLF, so go back
|
|
115
|
+
this.back();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return token(exports.TOKEN_BLOCK_END, tok, lineno, colno);
|
|
120
|
+
}
|
|
121
|
+
else if ((tok = this._extractString(this.tags.VARIABLE_END)) ||
|
|
122
|
+
(tok = this._extractString('-' + this.tags.VARIABLE_END))) {
|
|
123
|
+
// Special check for variable end tag (see above)
|
|
124
|
+
this.inCode = false;
|
|
125
|
+
return token(exports.TOKEN_VARIABLE_END, tok, lineno, colno);
|
|
126
|
+
}
|
|
127
|
+
else if (cur === 'r' && this.str.charAt(this.index + 1) === '/') {
|
|
128
|
+
// Skip past 'r/'.
|
|
129
|
+
this.forwardN(2);
|
|
130
|
+
// Extract until the end of the regex -- / ends it, \/ does not.
|
|
131
|
+
let regexBody = '';
|
|
132
|
+
while (!this.isFinished()) {
|
|
133
|
+
if (this.current() === '/' && this.previous() !== '\\') {
|
|
134
|
+
this.forward();
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
regexBody += this.current();
|
|
139
|
+
this.forward();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Check for flags.
|
|
143
|
+
// The possible flags are according to https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/RegExp)
|
|
144
|
+
let POSSIBLE_FLAGS = ['g', 'i', 'm', 'y'];
|
|
145
|
+
let regexFlags = '';
|
|
146
|
+
while (!this.isFinished()) {
|
|
147
|
+
let isCurrentAFlag = POSSIBLE_FLAGS.indexOf(this.current()) !== -1;
|
|
148
|
+
if (isCurrentAFlag) {
|
|
149
|
+
regexFlags += this.current();
|
|
150
|
+
this.forward();
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return token(exports.TOKEN_REGEX, {
|
|
157
|
+
body: regexBody,
|
|
158
|
+
flags: regexFlags,
|
|
159
|
+
}, lineno, colno);
|
|
160
|
+
}
|
|
161
|
+
else if (exports.delimChars.indexOf(cur) !== -1) {
|
|
162
|
+
// We've hit a delimiter (a special char like a bracket)
|
|
163
|
+
this.forward();
|
|
164
|
+
let complexOps = ['==', '===', '!=', '!==', '<=', '>=', '//', '**'];
|
|
165
|
+
let curComplex = cur + this.current();
|
|
166
|
+
let type;
|
|
167
|
+
if (complexOps.indexOf(curComplex) !== -1) {
|
|
168
|
+
this.forward();
|
|
169
|
+
cur = curComplex;
|
|
170
|
+
// See if this is a strict equality/inequality comparator
|
|
171
|
+
if (complexOps.indexOf(curComplex + this.current()) !== -1) {
|
|
172
|
+
cur = curComplex + this.current();
|
|
173
|
+
this.forward();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
switch (cur) {
|
|
177
|
+
case '(':
|
|
178
|
+
type = exports.TOKEN_LEFT_PAREN;
|
|
179
|
+
break;
|
|
180
|
+
case ')':
|
|
181
|
+
type = exports.TOKEN_RIGHT_PAREN;
|
|
182
|
+
break;
|
|
183
|
+
case '[':
|
|
184
|
+
type = exports.TOKEN_LEFT_BRACKET;
|
|
185
|
+
break;
|
|
186
|
+
case ']':
|
|
187
|
+
type = exports.TOKEN_RIGHT_BRACKET;
|
|
188
|
+
break;
|
|
189
|
+
case '{':
|
|
190
|
+
type = exports.TOKEN_LEFT_CURLY;
|
|
191
|
+
break;
|
|
192
|
+
case '}':
|
|
193
|
+
type = exports.TOKEN_RIGHT_CURLY;
|
|
194
|
+
break;
|
|
195
|
+
case ',':
|
|
196
|
+
type = exports.TOKEN_COMMA;
|
|
197
|
+
break;
|
|
198
|
+
case ':':
|
|
199
|
+
type = exports.TOKEN_COLON;
|
|
200
|
+
break;
|
|
201
|
+
case '~':
|
|
202
|
+
type = exports.TOKEN_TILDE;
|
|
203
|
+
break;
|
|
204
|
+
case '|':
|
|
205
|
+
type = exports.TOKEN_PIPE;
|
|
206
|
+
break;
|
|
207
|
+
default:
|
|
208
|
+
type = exports.TOKEN_OPERATOR;
|
|
209
|
+
}
|
|
210
|
+
return token(type, cur, lineno, colno);
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
// We are not at whitespace or a delimiter, so extract the
|
|
214
|
+
// text and parse it
|
|
215
|
+
tok = this._extractUntil(exports.whitespaceChars + exports.delimChars);
|
|
216
|
+
if (tok && tok.match(/^[-+]?[0-9]+$/)) {
|
|
217
|
+
if (this.current() === '.') {
|
|
218
|
+
this.forward();
|
|
219
|
+
let dec = this._extract(exports.intChars);
|
|
220
|
+
return token(exports.TOKEN_FLOAT, tok + '.' + dec, lineno, colno);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
return token(exports.TOKEN_INT, tok, lineno, colno);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
else if (tok && tok.match(/^(true|false)$/)) {
|
|
227
|
+
return token(exports.TOKEN_BOOLEAN, tok, lineno, colno);
|
|
228
|
+
}
|
|
229
|
+
else if (tok === 'none') {
|
|
230
|
+
return token(exports.TOKEN_NONE, tok, lineno, colno);
|
|
231
|
+
/*
|
|
232
|
+
* Added to make the test `null is null` evaluate truthily.
|
|
233
|
+
* Otherwise, Nunjucks will look up null in the context and
|
|
234
|
+
* return `undefined`, which is not what we want. This *may* have
|
|
235
|
+
* consequences is someone is using null in their templates as a
|
|
236
|
+
* variable.
|
|
237
|
+
*/
|
|
238
|
+
}
|
|
239
|
+
else if (tok === 'null') {
|
|
240
|
+
return token(exports.TOKEN_NONE, tok, lineno, colno);
|
|
241
|
+
}
|
|
242
|
+
else if (tok) {
|
|
243
|
+
return token(exports.TOKEN_SYMBOL, tok, lineno, colno);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
throw new Error('Unexpected value while parsing: ' + tok);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
// Parse out the template text, breaking on tag
|
|
252
|
+
// delimiters because we need to look for block/variable start
|
|
253
|
+
// tags (don't use the full delimChars for optimization)
|
|
254
|
+
let beginChars = this.tags.BLOCK_START.charAt(0) +
|
|
255
|
+
this.tags.VARIABLE_START.charAt(0) +
|
|
256
|
+
this.tags.COMMENT_START.charAt(0) +
|
|
257
|
+
this.tags.COMMENT_END.charAt(0);
|
|
258
|
+
if (this.isFinished()) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
else if ((tok = this._extractString(this.tags.BLOCK_START + '-')) ||
|
|
262
|
+
(tok = this._extractString(this.tags.BLOCK_START))) {
|
|
263
|
+
this.inCode = true;
|
|
264
|
+
return token(exports.TOKEN_BLOCK_START, tok, lineno, colno);
|
|
265
|
+
}
|
|
266
|
+
else if ((tok = this._extractString(this.tags.VARIABLE_START + '-')) ||
|
|
267
|
+
(tok = this._extractString(this.tags.VARIABLE_START))) {
|
|
268
|
+
this.inCode = true;
|
|
269
|
+
return token(exports.TOKEN_VARIABLE_START, tok, lineno, colno);
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
tok = '';
|
|
273
|
+
let data;
|
|
274
|
+
let inComment = false;
|
|
275
|
+
if (this._matches(this.tags.COMMENT_START)) {
|
|
276
|
+
inComment = true;
|
|
277
|
+
tok = this._extractString(this.tags.COMMENT_START);
|
|
278
|
+
}
|
|
279
|
+
// Continually consume text, breaking on the tag delimiter
|
|
280
|
+
// characters and checking to see if it's a start tag.
|
|
281
|
+
//
|
|
282
|
+
// We could hit the end of the template in the middle of
|
|
283
|
+
// our looping, so check for the null return value from
|
|
284
|
+
// _extractUntil
|
|
285
|
+
while ((data = this._extractUntil(beginChars)) !== null) {
|
|
286
|
+
tok += data;
|
|
287
|
+
if ((this._matches(this.tags.BLOCK_START) ||
|
|
288
|
+
this._matches(this.tags.VARIABLE_START) ||
|
|
289
|
+
this._matches(this.tags.COMMENT_START)) &&
|
|
290
|
+
!inComment) {
|
|
291
|
+
if (this.lstripBlocks &&
|
|
292
|
+
this._matches(this.tags.BLOCK_START) &&
|
|
293
|
+
this?.colno > 0 &&
|
|
294
|
+
tok &&
|
|
295
|
+
this?.colno <= tok?.length) {
|
|
296
|
+
if (!tok)
|
|
297
|
+
return;
|
|
298
|
+
let lastLine = tok.slice(-this?.colno);
|
|
299
|
+
if (/^\s+$/.test(lastLine)) {
|
|
300
|
+
// Remove block leading whitespace from beginning of the string
|
|
301
|
+
tok = tok.slice(0, -this?.colno);
|
|
302
|
+
if (!tok?.length) {
|
|
303
|
+
// All data removed, collapse to avoid unnecessary nodes
|
|
304
|
+
// by returning next token (block start)
|
|
305
|
+
return this.nextToken();
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// If it is a start tag, stop looping
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
else if (this._matches(this.tags.COMMENT_END)) {
|
|
313
|
+
if (!inComment) {
|
|
314
|
+
throw new Error('unexpected end of comment');
|
|
315
|
+
}
|
|
316
|
+
if (tok)
|
|
317
|
+
tok += this._extractString(this.tags.COMMENT_END);
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
// It does not match any tag, so add the character and
|
|
322
|
+
// carry on
|
|
323
|
+
tok += this.current();
|
|
324
|
+
this.forward();
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (data === null && inComment) {
|
|
328
|
+
throw new Error('expected end of comment, got end of file');
|
|
329
|
+
}
|
|
330
|
+
return token(inComment ? exports.TOKEN_COMMENT : exports.TOKEN_DATA, tok, lineno, colno);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
_parseString(delimiter) {
|
|
335
|
+
this.forward();
|
|
336
|
+
let str = '';
|
|
337
|
+
while (!this.isFinished() && this.current() !== delimiter) {
|
|
338
|
+
let cur = this.current();
|
|
339
|
+
if (cur === '\\') {
|
|
340
|
+
this.forward();
|
|
341
|
+
switch (this.current()) {
|
|
342
|
+
case 'n':
|
|
343
|
+
str += '\n';
|
|
344
|
+
break;
|
|
345
|
+
case 't':
|
|
346
|
+
str += '\t';
|
|
347
|
+
break;
|
|
348
|
+
case 'r':
|
|
349
|
+
str += '\r';
|
|
350
|
+
break;
|
|
351
|
+
default:
|
|
352
|
+
str += this.current();
|
|
353
|
+
}
|
|
354
|
+
this.forward();
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
str += cur;
|
|
358
|
+
this.forward();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
this.forward();
|
|
362
|
+
return str;
|
|
363
|
+
}
|
|
364
|
+
_matches(str) {
|
|
365
|
+
if (this.index + str?.length > this.len)
|
|
366
|
+
return null;
|
|
367
|
+
return this.str.slice(this.index, this.index + str?.length) === str;
|
|
368
|
+
}
|
|
369
|
+
_extractString(str) {
|
|
370
|
+
if (this._matches(str)) {
|
|
371
|
+
this.forwardN(str?.length);
|
|
372
|
+
return str;
|
|
373
|
+
}
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
_extractUntil(charString) {
|
|
377
|
+
// Extract all non-matching chars, with the default matching set
|
|
378
|
+
// to everything
|
|
379
|
+
return this._extractMatching(true, charString || '');
|
|
380
|
+
}
|
|
381
|
+
_extract(charString) {
|
|
382
|
+
// Extract all matching chars (no default, so charString must be
|
|
383
|
+
// explicit)
|
|
384
|
+
return this._extractMatching(false, charString);
|
|
385
|
+
}
|
|
386
|
+
_extractMatching(breakOnMatch, charString) {
|
|
387
|
+
// Pull out characters until a breaking char is hit.
|
|
388
|
+
// If breakOnMatch is false, a non-matching char stops it.
|
|
389
|
+
// If breakOnMatch is true, a matching char stops it.
|
|
390
|
+
if (this.isFinished()) {
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
let first = charString.indexOf(this.current());
|
|
394
|
+
// Only proceed if the first character doesn't meet our condition
|
|
395
|
+
if ((breakOnMatch && first === -1) || (!breakOnMatch && first !== -1)) {
|
|
396
|
+
let t = this.current();
|
|
397
|
+
this.forward();
|
|
398
|
+
// And pull out all the chars one at a time until we hit a
|
|
399
|
+
// breaking char
|
|
400
|
+
let idx = charString.indexOf(this.current());
|
|
401
|
+
while (((breakOnMatch && idx === -1) || (!breakOnMatch && idx !== -1)) &&
|
|
402
|
+
!this.isFinished()) {
|
|
403
|
+
t += this.current();
|
|
404
|
+
this.forward();
|
|
405
|
+
idx = charString.indexOf(this.current());
|
|
406
|
+
}
|
|
407
|
+
return t;
|
|
408
|
+
}
|
|
409
|
+
return '';
|
|
410
|
+
}
|
|
411
|
+
_extractRegex(regex) {
|
|
412
|
+
let matches = this.currentStr().match(regex);
|
|
413
|
+
if (!matches) {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
// Move forward whatever was matched
|
|
417
|
+
this.forwardN(matches[0]?.length);
|
|
418
|
+
return matches;
|
|
419
|
+
}
|
|
420
|
+
isFinished() {
|
|
421
|
+
return this.index >= this.len;
|
|
422
|
+
}
|
|
423
|
+
forwardN(n) {
|
|
424
|
+
for (let i = 0; i < n; i++) {
|
|
425
|
+
this.forward();
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
forward() {
|
|
429
|
+
this.index++;
|
|
430
|
+
if (this.previous() === '\n') {
|
|
431
|
+
this.lineno++;
|
|
432
|
+
this.colno = 0;
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
this.colno++;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
backN(n) {
|
|
439
|
+
for (let i = 0; i < n; i++) {
|
|
440
|
+
this.back();
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
back() {
|
|
444
|
+
this.index--;
|
|
445
|
+
if (this.current() === '\n') {
|
|
446
|
+
this.lineno--;
|
|
447
|
+
let idx = this.src.lastIndexOf('\n', this.index - 1);
|
|
448
|
+
if (idx === -1) {
|
|
449
|
+
this.colno = this.index;
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
this.colno = this.index - idx;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
this.colno--;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
current() {
|
|
460
|
+
if (!this.isFinished()) {
|
|
461
|
+
return this.str.charAt(this.index);
|
|
462
|
+
}
|
|
463
|
+
return '';
|
|
464
|
+
}
|
|
465
|
+
// currentStr returns what's left of the unparsed string
|
|
466
|
+
currentStr() {
|
|
467
|
+
if (!this.isFinished()) {
|
|
468
|
+
return this.str.substr(this.index);
|
|
469
|
+
}
|
|
470
|
+
return '';
|
|
471
|
+
}
|
|
472
|
+
previous() {
|
|
473
|
+
return this.str.charAt(this.index - 1);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
exports.Tokenizer = Tokenizer;
|
|
477
|
+
const lex = (str, opts) => {
|
|
478
|
+
return new Tokenizer(str, opts);
|
|
479
|
+
};
|
|
480
|
+
exports.lex = lex;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Callback } from './types';
|
|
2
|
+
export declare const escapeMap: {
|
|
3
|
+
'&': string;
|
|
4
|
+
'"': string;
|
|
5
|
+
"'": string;
|
|
6
|
+
'<': string;
|
|
7
|
+
'>': string;
|
|
8
|
+
'\\': string;
|
|
9
|
+
};
|
|
10
|
+
export type EscapeEntity = (typeof escapeMap)[EscapeChar];
|
|
11
|
+
declare const typeOfItems: {
|
|
12
|
+
undefined: string;
|
|
13
|
+
object: string;
|
|
14
|
+
boolean: string;
|
|
15
|
+
number: string;
|
|
16
|
+
bigint: string;
|
|
17
|
+
string: string;
|
|
18
|
+
symbol: string;
|
|
19
|
+
function: string;
|
|
20
|
+
};
|
|
21
|
+
export interface ILib {
|
|
22
|
+
ILib: ILib;
|
|
23
|
+
EscapeChar: EscapeChar;
|
|
24
|
+
EscapeEntity: EscapeEntity;
|
|
25
|
+
TypeOfChar: TypeOfChar;
|
|
26
|
+
TemplateErr: TemplateErr;
|
|
27
|
+
escape: (val: EscapeChar) => string;
|
|
28
|
+
dump: (obj: Record<any, any>, spaces?: string | number) => string;
|
|
29
|
+
isFunction: (obj: unknown) => boolean;
|
|
30
|
+
isString: (obj: unknown) => boolean;
|
|
31
|
+
isObject: (obj: unknown) => boolean;
|
|
32
|
+
}
|
|
33
|
+
export declare const escape: (val: string) => string;
|
|
34
|
+
export type EscapeChar = keyof typeof escapeMap;
|
|
35
|
+
export type TypeOfChar = keyof typeof typeOfItems;
|
|
36
|
+
export declare const dump: (obj: Record<any, any>, spaces?: string | number) => string;
|
|
37
|
+
export declare const hasOwnProp: (obj: Record<string | number, any>, key: string | number) => boolean;
|
|
38
|
+
export declare function _prettifyError(path: string, withInternals: boolean, err: TemplateErr): TemplateErr;
|
|
39
|
+
export type TemplateErr = Error & {
|
|
40
|
+
name: string;
|
|
41
|
+
lineno: number;
|
|
42
|
+
colno: number;
|
|
43
|
+
firstUpdate: boolean;
|
|
44
|
+
cause?: Error;
|
|
45
|
+
update: (path?: string) => TemplateErr;
|
|
46
|
+
};
|
|
47
|
+
export declare function TemplateError(message: string | Error, lineno?: number, colno?: number): TemplateErr;
|
|
48
|
+
export declare const isFunction: (obj: unknown) => obj is Function;
|
|
49
|
+
export declare const isString: (obj: unknown) => obj is string;
|
|
50
|
+
export declare const isObject: (obj: unknown) => obj is object;
|
|
51
|
+
export declare const _prepareAttributeParts: (attr: string | number) => string[] | number[];
|
|
52
|
+
export declare function getAttrGetter(attribute: string): (obj: Object) => any;
|
|
53
|
+
export declare function groupBy(obj: Record<string | number, any>, val: Function | string, throwOnUndefined: boolean): Record<string, any>;
|
|
54
|
+
export declare function toArray(obj: any): any;
|
|
55
|
+
export declare function without<T>(array?: T[], ...contains: T[]): T[];
|
|
56
|
+
export declare const repeat: (char_: string, n: number) => string;
|
|
57
|
+
export declare function each(obj: any, func: Function, context: any): void;
|
|
58
|
+
export declare function asyncIter(arr: any, iter: Function, cb: Function): void;
|
|
59
|
+
export declare function asyncFor(obj: Record<string, any>, iter: Function, cb: Callback): void;
|
|
60
|
+
type Log = (...msg: any[]) => void;
|
|
61
|
+
export declare const p: {
|
|
62
|
+
log: Log;
|
|
63
|
+
llog: Log;
|
|
64
|
+
warn: Log;
|
|
65
|
+
lwarn: Log;
|
|
66
|
+
debug: Log;
|
|
67
|
+
ldebug: Log;
|
|
68
|
+
err: Log;
|
|
69
|
+
lerr: Log;
|
|
70
|
+
error: Log;
|
|
71
|
+
lerror: Log;
|
|
72
|
+
exit: Log;
|
|
73
|
+
};
|
|
74
|
+
export {};
|