binja 0.5.1 → 0.5.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/README.md +127 -95
- package/dist/cli.js +179 -65
- package/dist/errors/index.d.ts +65 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/filters/index.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +375 -120
- package/dist/lexer/index.d.ts.map +1 -1
- package/dist/parser/index.d.ts +2 -1
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/runtime/index.d.ts +3 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -153,6 +153,159 @@ function tokenizeNative(source) {
|
|
|
153
153
|
return tokens;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
// src/errors/index.ts
|
|
157
|
+
var colors = {
|
|
158
|
+
red: "\x1B[31m",
|
|
159
|
+
yellow: "\x1B[33m",
|
|
160
|
+
cyan: "\x1B[36m",
|
|
161
|
+
gray: "\x1B[90m",
|
|
162
|
+
bold: "\x1B[1m",
|
|
163
|
+
dim: "\x1B[2m",
|
|
164
|
+
reset: "\x1B[0m"
|
|
165
|
+
};
|
|
166
|
+
var useColors = process.stdout?.isTTY !== false;
|
|
167
|
+
function c(color, text) {
|
|
168
|
+
return useColors ? `${colors[color]}${text}${colors.reset}` : text;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
class TemplateError extends Error {
|
|
172
|
+
line;
|
|
173
|
+
column;
|
|
174
|
+
source;
|
|
175
|
+
templateName;
|
|
176
|
+
suggestion;
|
|
177
|
+
availableOptions;
|
|
178
|
+
constructor(message, options) {
|
|
179
|
+
const formatted = formatError("TemplateError", message, options);
|
|
180
|
+
super(formatted);
|
|
181
|
+
this.name = "TemplateError";
|
|
182
|
+
this.line = options.line;
|
|
183
|
+
this.column = options.column;
|
|
184
|
+
this.source = options.source;
|
|
185
|
+
this.templateName = options.templateName;
|
|
186
|
+
this.suggestion = options.suggestion;
|
|
187
|
+
this.availableOptions = options.availableOptions;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
class TemplateSyntaxError extends Error {
|
|
192
|
+
line;
|
|
193
|
+
column;
|
|
194
|
+
source;
|
|
195
|
+
templateName;
|
|
196
|
+
suggestion;
|
|
197
|
+
constructor(message, options) {
|
|
198
|
+
const formatted = formatError("TemplateSyntaxError", message, options);
|
|
199
|
+
super(formatted);
|
|
200
|
+
this.name = "TemplateSyntaxError";
|
|
201
|
+
this.line = options.line;
|
|
202
|
+
this.column = options.column;
|
|
203
|
+
this.source = options.source;
|
|
204
|
+
this.templateName = options.templateName;
|
|
205
|
+
this.suggestion = options.suggestion;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
class TemplateRuntimeError extends Error {
|
|
210
|
+
line;
|
|
211
|
+
column;
|
|
212
|
+
source;
|
|
213
|
+
templateName;
|
|
214
|
+
suggestion;
|
|
215
|
+
availableOptions;
|
|
216
|
+
constructor(message, options) {
|
|
217
|
+
const formatted = formatError("TemplateRuntimeError", message, options);
|
|
218
|
+
super(formatted);
|
|
219
|
+
this.name = "TemplateRuntimeError";
|
|
220
|
+
this.line = options.line;
|
|
221
|
+
this.column = options.column;
|
|
222
|
+
this.source = options.source;
|
|
223
|
+
this.templateName = options.templateName;
|
|
224
|
+
this.suggestion = options.suggestion;
|
|
225
|
+
this.availableOptions = options.availableOptions;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function formatError(type, message, options) {
|
|
229
|
+
const parts = [];
|
|
230
|
+
const location = options.templateName ? `${options.templateName}:${options.line}:${options.column}` : `line ${options.line}, column ${options.column}`;
|
|
231
|
+
parts.push(`${c("red", c("bold", type))}: ${message} at ${c("cyan", location)}`);
|
|
232
|
+
if (options.source) {
|
|
233
|
+
parts.push("");
|
|
234
|
+
parts.push(generateSnippet(options.source, options.line, options.column));
|
|
235
|
+
}
|
|
236
|
+
if (options.suggestion) {
|
|
237
|
+
parts.push("");
|
|
238
|
+
parts.push(`${c("yellow", "Did you mean")}: ${c("cyan", options.suggestion)}?`);
|
|
239
|
+
}
|
|
240
|
+
if (options.availableOptions && options.availableOptions.length > 0) {
|
|
241
|
+
parts.push("");
|
|
242
|
+
const truncated = options.availableOptions.slice(0, 8);
|
|
243
|
+
const more = options.availableOptions.length > 8 ? ` ${c("gray", `... and ${options.availableOptions.length - 8} more`)}` : "";
|
|
244
|
+
parts.push(`${c("gray", "Available")}: ${truncated.join(", ")}${more}`);
|
|
245
|
+
}
|
|
246
|
+
return parts.join(`
|
|
247
|
+
`);
|
|
248
|
+
}
|
|
249
|
+
function generateSnippet(source, errorLine, errorColumn) {
|
|
250
|
+
const lines = source.split(`
|
|
251
|
+
`);
|
|
252
|
+
const parts = [];
|
|
253
|
+
const startLine = Math.max(1, errorLine - 2);
|
|
254
|
+
const endLine = Math.min(lines.length, errorLine + 1);
|
|
255
|
+
const gutterWidth = String(endLine).length;
|
|
256
|
+
for (let i = startLine;i <= endLine; i++) {
|
|
257
|
+
const lineContent = lines[i - 1] || "";
|
|
258
|
+
const lineNum = String(i).padStart(gutterWidth, " ");
|
|
259
|
+
const isErrorLine = i === errorLine;
|
|
260
|
+
if (isErrorLine) {
|
|
261
|
+
parts.push(`${c("red", " \u2192")} ${c("gray", lineNum)} ${c("dim", "\u2502")} ${lineContent}`);
|
|
262
|
+
const caretPadding = " ".repeat(gutterWidth + 4 + Math.max(0, errorColumn - 1));
|
|
263
|
+
const caret = c("red", "^");
|
|
264
|
+
parts.push(`${caretPadding}${caret}`);
|
|
265
|
+
} else {
|
|
266
|
+
parts.push(` ${c("gray", lineNum)} ${c("dim", "\u2502")} ${c("gray", lineContent)}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return parts.join(`
|
|
270
|
+
`);
|
|
271
|
+
}
|
|
272
|
+
function findSimilar(input, candidates, maxDistance = 3) {
|
|
273
|
+
let bestMatch = null;
|
|
274
|
+
let bestDistance = maxDistance + 1;
|
|
275
|
+
const inputLower = input.toLowerCase();
|
|
276
|
+
for (const candidate of candidates) {
|
|
277
|
+
const distance = levenshteinDistance(inputLower, candidate.toLowerCase());
|
|
278
|
+
if (distance < bestDistance) {
|
|
279
|
+
bestDistance = distance;
|
|
280
|
+
bestMatch = candidate;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return bestDistance <= maxDistance ? bestMatch : null;
|
|
284
|
+
}
|
|
285
|
+
function levenshteinDistance(a, b) {
|
|
286
|
+
if (a.length === 0)
|
|
287
|
+
return b.length;
|
|
288
|
+
if (b.length === 0)
|
|
289
|
+
return a.length;
|
|
290
|
+
const matrix = [];
|
|
291
|
+
for (let i = 0;i <= b.length; i++) {
|
|
292
|
+
matrix[i] = [i];
|
|
293
|
+
}
|
|
294
|
+
for (let j = 0;j <= a.length; j++) {
|
|
295
|
+
matrix[0][j] = j;
|
|
296
|
+
}
|
|
297
|
+
for (let i = 1;i <= b.length; i++) {
|
|
298
|
+
for (let j = 1;j <= a.length; j++) {
|
|
299
|
+
if (b[i - 1] === a[j - 1]) {
|
|
300
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
301
|
+
} else {
|
|
302
|
+
matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j] + 1);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return matrix[b.length][a.length];
|
|
307
|
+
}
|
|
308
|
+
|
|
156
309
|
// src/lexer/index.ts
|
|
157
310
|
class Lexer {
|
|
158
311
|
state;
|
|
@@ -240,7 +393,11 @@ class Lexer {
|
|
|
240
393
|
if (this.peek() === "-")
|
|
241
394
|
this.advance();
|
|
242
395
|
if (!this.match(this.blockEnd)) {
|
|
243
|
-
throw new
|
|
396
|
+
throw new TemplateSyntaxError(`Expected '${this.blockEnd}' after '${tagName}'`, {
|
|
397
|
+
line: this.state.line,
|
|
398
|
+
column: this.state.column,
|
|
399
|
+
source: this.state.source
|
|
400
|
+
});
|
|
244
401
|
}
|
|
245
402
|
const endTag = `end${tagName}`;
|
|
246
403
|
const contentStart = this.state.pos;
|
|
@@ -270,7 +427,11 @@ class Lexer {
|
|
|
270
427
|
if (this.peek() === "-")
|
|
271
428
|
this.advance();
|
|
272
429
|
if (!this.match(this.blockEnd)) {
|
|
273
|
-
throw new
|
|
430
|
+
throw new TemplateSyntaxError(`Expected '${this.blockEnd}' after '${endTag}'`, {
|
|
431
|
+
line: this.state.line,
|
|
432
|
+
column: this.state.column,
|
|
433
|
+
source: this.state.source
|
|
434
|
+
});
|
|
274
435
|
}
|
|
275
436
|
return;
|
|
276
437
|
}
|
|
@@ -285,7 +446,12 @@ class Lexer {
|
|
|
285
446
|
}
|
|
286
447
|
this.advance();
|
|
287
448
|
}
|
|
288
|
-
throw new
|
|
449
|
+
throw new TemplateSyntaxError(`Unclosed '${tagName}' block`, {
|
|
450
|
+
line: startLine,
|
|
451
|
+
column: startColumn,
|
|
452
|
+
source: this.state.source,
|
|
453
|
+
suggestion: `Add {% end${tagName} %} to close the block`
|
|
454
|
+
});
|
|
289
455
|
}
|
|
290
456
|
scanText() {
|
|
291
457
|
const start = this.state.pos;
|
|
@@ -325,22 +491,27 @@ class Lexer {
|
|
|
325
491
|
}
|
|
326
492
|
this.scanExpressionToken();
|
|
327
493
|
}
|
|
328
|
-
throw new
|
|
494
|
+
throw new TemplateSyntaxError(`Unclosed template tag`, {
|
|
495
|
+
line: this.state.line,
|
|
496
|
+
column: this.state.column,
|
|
497
|
+
source: this.state.source,
|
|
498
|
+
suggestion: `Add closing delimiter '${endDelimiter}'`
|
|
499
|
+
});
|
|
329
500
|
}
|
|
330
501
|
scanExpressionToken() {
|
|
331
502
|
this.skipWhitespace();
|
|
332
503
|
if (this.isAtEnd())
|
|
333
504
|
return;
|
|
334
|
-
const
|
|
335
|
-
if (
|
|
336
|
-
this.scanString(
|
|
505
|
+
const c2 = this.peek();
|
|
506
|
+
if (c2 === '"' || c2 === "'") {
|
|
507
|
+
this.scanString(c2);
|
|
337
508
|
return;
|
|
338
509
|
}
|
|
339
|
-
if (this.isDigit(
|
|
510
|
+
if (this.isDigit(c2)) {
|
|
340
511
|
this.scanNumber();
|
|
341
512
|
return;
|
|
342
513
|
}
|
|
343
|
-
if (this.isAlpha(
|
|
514
|
+
if (this.isAlpha(c2) || c2 === "_") {
|
|
344
515
|
this.scanIdentifier();
|
|
345
516
|
return;
|
|
346
517
|
}
|
|
@@ -361,7 +532,12 @@ class Lexer {
|
|
|
361
532
|
this.advance();
|
|
362
533
|
}
|
|
363
534
|
if (this.isAtEnd()) {
|
|
364
|
-
throw new
|
|
535
|
+
throw new TemplateSyntaxError(`Unterminated string literal`, {
|
|
536
|
+
line: this.state.line,
|
|
537
|
+
column: this.state.column,
|
|
538
|
+
source: this.state.source,
|
|
539
|
+
suggestion: `Add closing quote '${quote}'`
|
|
540
|
+
});
|
|
365
541
|
}
|
|
366
542
|
const value = this.state.source.slice(start, this.state.pos);
|
|
367
543
|
this.advance();
|
|
@@ -391,55 +567,55 @@ class Lexer {
|
|
|
391
567
|
this.addToken(type, value);
|
|
392
568
|
}
|
|
393
569
|
scanOperator() {
|
|
394
|
-
const
|
|
395
|
-
switch (
|
|
570
|
+
const c2 = this.advance();
|
|
571
|
+
switch (c2) {
|
|
396
572
|
case ".":
|
|
397
|
-
this.addToken("DOT" /* DOT */,
|
|
573
|
+
this.addToken("DOT" /* DOT */, c2);
|
|
398
574
|
break;
|
|
399
575
|
case ",":
|
|
400
|
-
this.addToken("COMMA" /* COMMA */,
|
|
576
|
+
this.addToken("COMMA" /* COMMA */, c2);
|
|
401
577
|
break;
|
|
402
578
|
case ":":
|
|
403
|
-
this.addToken("COLON" /* COLON */,
|
|
579
|
+
this.addToken("COLON" /* COLON */, c2);
|
|
404
580
|
break;
|
|
405
581
|
case "|":
|
|
406
|
-
this.addToken("PIPE" /* PIPE */,
|
|
582
|
+
this.addToken("PIPE" /* PIPE */, c2);
|
|
407
583
|
break;
|
|
408
584
|
case "(":
|
|
409
|
-
this.addToken("LPAREN" /* LPAREN */,
|
|
585
|
+
this.addToken("LPAREN" /* LPAREN */, c2);
|
|
410
586
|
break;
|
|
411
587
|
case ")":
|
|
412
|
-
this.addToken("RPAREN" /* RPAREN */,
|
|
588
|
+
this.addToken("RPAREN" /* RPAREN */, c2);
|
|
413
589
|
break;
|
|
414
590
|
case "[":
|
|
415
|
-
this.addToken("LBRACKET" /* LBRACKET */,
|
|
591
|
+
this.addToken("LBRACKET" /* LBRACKET */, c2);
|
|
416
592
|
break;
|
|
417
593
|
case "]":
|
|
418
|
-
this.addToken("RBRACKET" /* RBRACKET */,
|
|
594
|
+
this.addToken("RBRACKET" /* RBRACKET */, c2);
|
|
419
595
|
break;
|
|
420
596
|
case "{":
|
|
421
|
-
this.addToken("LBRACE" /* LBRACE */,
|
|
597
|
+
this.addToken("LBRACE" /* LBRACE */, c2);
|
|
422
598
|
break;
|
|
423
599
|
case "}":
|
|
424
|
-
this.addToken("RBRACE" /* RBRACE */,
|
|
600
|
+
this.addToken("RBRACE" /* RBRACE */, c2);
|
|
425
601
|
break;
|
|
426
602
|
case "+":
|
|
427
|
-
this.addToken("ADD" /* ADD */,
|
|
603
|
+
this.addToken("ADD" /* ADD */, c2);
|
|
428
604
|
break;
|
|
429
605
|
case "-":
|
|
430
|
-
this.addToken("SUB" /* SUB */,
|
|
606
|
+
this.addToken("SUB" /* SUB */, c2);
|
|
431
607
|
break;
|
|
432
608
|
case "*":
|
|
433
|
-
this.addToken("MUL" /* MUL */,
|
|
609
|
+
this.addToken("MUL" /* MUL */, c2);
|
|
434
610
|
break;
|
|
435
611
|
case "/":
|
|
436
|
-
this.addToken("DIV" /* DIV */,
|
|
612
|
+
this.addToken("DIV" /* DIV */, c2);
|
|
437
613
|
break;
|
|
438
614
|
case "%":
|
|
439
|
-
this.addToken("MOD" /* MOD */,
|
|
615
|
+
this.addToken("MOD" /* MOD */, c2);
|
|
440
616
|
break;
|
|
441
617
|
case "~":
|
|
442
|
-
this.addToken("TILDE" /* TILDE */,
|
|
618
|
+
this.addToken("TILDE" /* TILDE */, c2);
|
|
443
619
|
break;
|
|
444
620
|
case "=":
|
|
445
621
|
if (this.match("=")) {
|
|
@@ -452,7 +628,12 @@ class Lexer {
|
|
|
452
628
|
if (this.match("=")) {
|
|
453
629
|
this.addToken("NE" /* NE */, "!=");
|
|
454
630
|
} else {
|
|
455
|
-
throw new
|
|
631
|
+
throw new TemplateSyntaxError(`Unexpected character '!'`, {
|
|
632
|
+
line: this.state.line,
|
|
633
|
+
column: this.state.column - 1,
|
|
634
|
+
source: this.state.source,
|
|
635
|
+
suggestion: `Use '!=' for not-equal comparison or 'not' for negation`
|
|
636
|
+
});
|
|
456
637
|
}
|
|
457
638
|
break;
|
|
458
639
|
case "<":
|
|
@@ -470,8 +651,12 @@ class Lexer {
|
|
|
470
651
|
}
|
|
471
652
|
break;
|
|
472
653
|
default:
|
|
473
|
-
if (!this.isWhitespace(
|
|
474
|
-
throw new
|
|
654
|
+
if (!this.isWhitespace(c2)) {
|
|
655
|
+
throw new TemplateSyntaxError(`Unexpected character '${c2}'`, {
|
|
656
|
+
line: this.state.line,
|
|
657
|
+
column: this.state.column - 1,
|
|
658
|
+
source: this.state.source
|
|
659
|
+
});
|
|
475
660
|
}
|
|
476
661
|
}
|
|
477
662
|
}
|
|
@@ -502,10 +687,10 @@ class Lexer {
|
|
|
502
687
|
return this.state.source[this.state.pos + 1];
|
|
503
688
|
}
|
|
504
689
|
advance() {
|
|
505
|
-
const
|
|
690
|
+
const c2 = this.state.source[this.state.pos];
|
|
506
691
|
this.state.pos++;
|
|
507
692
|
this.state.column++;
|
|
508
|
-
return
|
|
693
|
+
return c2;
|
|
509
694
|
}
|
|
510
695
|
match(expected, offset = 0) {
|
|
511
696
|
const source = this.state.source;
|
|
@@ -545,20 +730,20 @@ class Lexer {
|
|
|
545
730
|
this.advance();
|
|
546
731
|
}
|
|
547
732
|
}
|
|
548
|
-
isWhitespace(
|
|
549
|
-
return
|
|
550
|
-
` ||
|
|
733
|
+
isWhitespace(c2) {
|
|
734
|
+
return c2 === " " || c2 === "\t" || c2 === `
|
|
735
|
+
` || c2 === "\r";
|
|
551
736
|
}
|
|
552
|
-
isDigit(
|
|
553
|
-
const code =
|
|
737
|
+
isDigit(c2) {
|
|
738
|
+
const code = c2.charCodeAt(0);
|
|
554
739
|
return code >= 48 && code <= 57;
|
|
555
740
|
}
|
|
556
|
-
isAlpha(
|
|
557
|
-
const code =
|
|
741
|
+
isAlpha(c2) {
|
|
742
|
+
const code = c2.charCodeAt(0);
|
|
558
743
|
return code >= 97 && code <= 122 || code >= 65 && code <= 90;
|
|
559
744
|
}
|
|
560
|
-
isAlphaNumeric(
|
|
561
|
-
const code =
|
|
745
|
+
isAlphaNumeric(c2) {
|
|
746
|
+
const code = c2.charCodeAt(0);
|
|
562
747
|
return code >= 48 && code <= 57 || code >= 97 && code <= 122 || code >= 65 && code <= 90;
|
|
563
748
|
}
|
|
564
749
|
addToken(type, value) {
|
|
@@ -575,8 +760,10 @@ class Lexer {
|
|
|
575
760
|
class Parser {
|
|
576
761
|
tokens;
|
|
577
762
|
current = 0;
|
|
578
|
-
|
|
763
|
+
source;
|
|
764
|
+
constructor(tokens, source) {
|
|
579
765
|
this.tokens = tokens;
|
|
766
|
+
this.source = source;
|
|
580
767
|
}
|
|
581
768
|
parse() {
|
|
582
769
|
const body = [];
|
|
@@ -1668,7 +1855,11 @@ class Parser {
|
|
|
1668
1855
|
}
|
|
1669
1856
|
error(message) {
|
|
1670
1857
|
const token = this.peek();
|
|
1671
|
-
return new
|
|
1858
|
+
return new TemplateSyntaxError(message, {
|
|
1859
|
+
line: token.line,
|
|
1860
|
+
column: token.column,
|
|
1861
|
+
source: this.source
|
|
1862
|
+
});
|
|
1672
1863
|
}
|
|
1673
1864
|
}
|
|
1674
1865
|
|
|
@@ -1830,7 +2021,7 @@ var capfirst = (value) => {
|
|
|
1830
2021
|
const str = String(value);
|
|
1831
2022
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
1832
2023
|
};
|
|
1833
|
-
var title = (value) => String(value).replace(TITLE_REGEX, (
|
|
2024
|
+
var title = (value) => String(value).replace(TITLE_REGEX, (c2) => c2.toUpperCase());
|
|
1834
2025
|
var trim = (value) => String(value).trim();
|
|
1835
2026
|
var striptags = (value) => String(value).replace(STRIPTAGS_REGEX, "");
|
|
1836
2027
|
var escape = (value) => {
|
|
@@ -1983,9 +2174,18 @@ var sort = (value, reverse2 = false) => {
|
|
|
1983
2174
|
return reverse2 ? sorted.reverse() : sorted;
|
|
1984
2175
|
};
|
|
1985
2176
|
var unique = (value) => {
|
|
1986
|
-
if (Array.isArray(value))
|
|
1987
|
-
return
|
|
1988
|
-
|
|
2177
|
+
if (!Array.isArray(value))
|
|
2178
|
+
return value;
|
|
2179
|
+
const seen = new Set;
|
|
2180
|
+
const result = [];
|
|
2181
|
+
for (let i = 0;i < value.length; i++) {
|
|
2182
|
+
const v = value[i];
|
|
2183
|
+
if (!seen.has(v)) {
|
|
2184
|
+
seen.add(v);
|
|
2185
|
+
result.push(v);
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
return result;
|
|
1989
2189
|
};
|
|
1990
2190
|
var make_list = (value) => {
|
|
1991
2191
|
if (Array.isArray(value))
|
|
@@ -2358,7 +2558,12 @@ var max = (value, attribute, defaultValue) => {
|
|
|
2358
2558
|
}
|
|
2359
2559
|
return maxItem;
|
|
2360
2560
|
}
|
|
2361
|
-
|
|
2561
|
+
let maxVal = value[0];
|
|
2562
|
+
for (let i = 1;i < value.length; i++) {
|
|
2563
|
+
if (value[i] > maxVal)
|
|
2564
|
+
maxVal = value[i];
|
|
2565
|
+
}
|
|
2566
|
+
return maxVal;
|
|
2362
2567
|
};
|
|
2363
2568
|
var min = (value, attribute, defaultValue) => {
|
|
2364
2569
|
if (!Array.isArray(value) || value.length === 0)
|
|
@@ -2372,7 +2577,12 @@ var min = (value, attribute, defaultValue) => {
|
|
|
2372
2577
|
}
|
|
2373
2578
|
return minItem;
|
|
2374
2579
|
}
|
|
2375
|
-
|
|
2580
|
+
let minVal = value[0];
|
|
2581
|
+
for (let i = 1;i < value.length; i++) {
|
|
2582
|
+
if (value[i] < minVal)
|
|
2583
|
+
minVal = value[i];
|
|
2584
|
+
}
|
|
2585
|
+
return minVal;
|
|
2376
2586
|
};
|
|
2377
2587
|
var sum = (value, attribute, start = 0) => {
|
|
2378
2588
|
if (!Array.isArray(value))
|
|
@@ -2662,7 +2872,7 @@ var xmlattr = (value, autospace = true) => {
|
|
|
2662
2872
|
if (val === true) {
|
|
2663
2873
|
attrs.push(key);
|
|
2664
2874
|
} else if (val !== false && val != null) {
|
|
2665
|
-
const escaped = String(val)
|
|
2875
|
+
const escaped = Bun.escapeHTML(String(val));
|
|
2666
2876
|
attrs.push(`${key}="${escaped}"`);
|
|
2667
2877
|
}
|
|
2668
2878
|
}
|
|
@@ -2924,6 +3134,7 @@ class Runtime {
|
|
|
2924
3134
|
tests;
|
|
2925
3135
|
blocks = new Map;
|
|
2926
3136
|
parentTemplate = null;
|
|
3137
|
+
source;
|
|
2927
3138
|
constructor(options = {}) {
|
|
2928
3139
|
this.options = {
|
|
2929
3140
|
autoescape: options.autoescape ?? true,
|
|
@@ -3242,9 +3453,12 @@ class Runtime {
|
|
|
3242
3453
|
}
|
|
3243
3454
|
cycleState = new Map;
|
|
3244
3455
|
renderCycleSync(node, ctx) {
|
|
3245
|
-
const key = node.
|
|
3456
|
+
const key = `cycle_${node.line}_${node.column}`;
|
|
3246
3457
|
const currentIndex = this.cycleState.get(key) ?? 0;
|
|
3247
|
-
const values =
|
|
3458
|
+
const values = [];
|
|
3459
|
+
for (let i = 0;i < node.values.length; i++) {
|
|
3460
|
+
values.push(this.eval(node.values[i], ctx));
|
|
3461
|
+
}
|
|
3248
3462
|
const value = values[currentIndex % values.length];
|
|
3249
3463
|
this.cycleState.set(key, currentIndex + 1);
|
|
3250
3464
|
if (node.asVar) {
|
|
@@ -3276,12 +3490,16 @@ class Runtime {
|
|
|
3276
3490
|
const key = `ifchanged_${node.line}_${node.column}`;
|
|
3277
3491
|
let currentValue;
|
|
3278
3492
|
if (node.values.length > 0) {
|
|
3279
|
-
|
|
3493
|
+
const values = [];
|
|
3494
|
+
for (let i = 0;i < node.values.length; i++) {
|
|
3495
|
+
values.push(this.eval(node.values[i], ctx));
|
|
3496
|
+
}
|
|
3497
|
+
currentValue = values;
|
|
3280
3498
|
} else {
|
|
3281
3499
|
currentValue = this.renderNodesSync(node.body, ctx);
|
|
3282
3500
|
}
|
|
3283
3501
|
const lastValue = this.ifchangedState.get(key);
|
|
3284
|
-
const changed =
|
|
3502
|
+
const changed = !this.deepEqual(currentValue, lastValue);
|
|
3285
3503
|
this.ifchangedState.set(key, currentValue);
|
|
3286
3504
|
if (changed) {
|
|
3287
3505
|
if (node.values.length > 0) {
|
|
@@ -3653,8 +3871,17 @@ class Runtime {
|
|
|
3653
3871
|
}
|
|
3654
3872
|
const kwargs = this.evalObjectSync(node.kwargs, ctx);
|
|
3655
3873
|
const filter = this.filters[node.filter];
|
|
3656
|
-
if (!filter)
|
|
3657
|
-
|
|
3874
|
+
if (!filter) {
|
|
3875
|
+
const available = Object.keys(this.filters);
|
|
3876
|
+
const suggestion = findSimilar(node.filter, available);
|
|
3877
|
+
throw new TemplateRuntimeError(`Unknown filter '${node.filter}'`, {
|
|
3878
|
+
line: node.line,
|
|
3879
|
+
column: node.column,
|
|
3880
|
+
source: this.source,
|
|
3881
|
+
suggestion: suggestion || undefined,
|
|
3882
|
+
availableOptions: available.slice(0, 15)
|
|
3883
|
+
});
|
|
3884
|
+
}
|
|
3658
3885
|
return filter(value, ...args, ...Object.values(kwargs));
|
|
3659
3886
|
}
|
|
3660
3887
|
evalBinaryOp(node, ctx) {
|
|
@@ -4028,6 +4255,28 @@ class Runtime {
|
|
|
4028
4255
|
}
|
|
4029
4256
|
return true;
|
|
4030
4257
|
}
|
|
4258
|
+
deepEqual(a, b) {
|
|
4259
|
+
if (a === b)
|
|
4260
|
+
return true;
|
|
4261
|
+
if (a == null || b == null)
|
|
4262
|
+
return a === b;
|
|
4263
|
+
const typeA = typeof a;
|
|
4264
|
+
const typeB = typeof b;
|
|
4265
|
+
if (typeA !== typeB)
|
|
4266
|
+
return false;
|
|
4267
|
+
if (typeA !== "object")
|
|
4268
|
+
return false;
|
|
4269
|
+
if (Array.isArray(a)) {
|
|
4270
|
+
if (!Array.isArray(b) || a.length !== b.length)
|
|
4271
|
+
return false;
|
|
4272
|
+
for (let i = 0;i < a.length; i++) {
|
|
4273
|
+
if (!this.deepEqual(a[i], b[i]))
|
|
4274
|
+
return false;
|
|
4275
|
+
}
|
|
4276
|
+
return true;
|
|
4277
|
+
}
|
|
4278
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
4279
|
+
}
|
|
4031
4280
|
isIn(needle, haystack) {
|
|
4032
4281
|
if (Array.isArray(haystack))
|
|
4033
4282
|
return haystack.includes(needle);
|
|
@@ -4069,6 +4318,9 @@ class Runtime {
|
|
|
4069
4318
|
addGlobal(name, value) {
|
|
4070
4319
|
this.options.globals[name] = value;
|
|
4071
4320
|
}
|
|
4321
|
+
setSource(source) {
|
|
4322
|
+
this.source = source;
|
|
4323
|
+
}
|
|
4072
4324
|
}
|
|
4073
4325
|
|
|
4074
4326
|
// src/compiler/index.ts
|
|
@@ -5005,13 +5257,13 @@ var DEFAULT_OPTIONS = {
|
|
|
5005
5257
|
function generateDebugPanel(data, options = {}) {
|
|
5006
5258
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
5007
5259
|
const id = `binja-debug-${Date.now()}`;
|
|
5008
|
-
const
|
|
5260
|
+
const colors2 = opts.dark ? darkTheme : lightTheme;
|
|
5009
5261
|
return `
|
|
5010
5262
|
<!-- Binja Debug Panel -->
|
|
5011
5263
|
<div id="${id}" class="binja-debugger" data-theme="${opts.dark ? "dark" : "light"}">
|
|
5012
|
-
<style>${generateStyles(id,
|
|
5013
|
-
${generateToggle(id, data,
|
|
5014
|
-
${generatePanel(id, data,
|
|
5264
|
+
<style>${generateStyles(id, colors2, opts)}</style>
|
|
5265
|
+
${generateToggle(id, data, colors2)}
|
|
5266
|
+
${generatePanel(id, data, colors2, opts)}
|
|
5015
5267
|
<script>${generateScript(id)}</script>
|
|
5016
5268
|
</div>
|
|
5017
5269
|
<!-- /Binja Debug Panel -->
|
|
@@ -5049,84 +5301,84 @@ var lightTheme = {
|
|
|
5049
5301
|
error: "#dc2626",
|
|
5050
5302
|
info: "#0891b2"
|
|
5051
5303
|
};
|
|
5052
|
-
function generateStyles(id,
|
|
5304
|
+
function generateStyles(id, c2, opts) {
|
|
5053
5305
|
const pos = getPosition(opts.position);
|
|
5054
5306
|
return `
|
|
5055
5307
|
#${id} { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif; font-size: 13px; line-height: 1.5; position: fixed; ${pos} z-index: 2147483647; }
|
|
5056
5308
|
#${id} * { box-sizing: border-box; margin: 0; padding: 0; }
|
|
5057
|
-
#${id} .dbg-toggle { display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px; background: ${
|
|
5058
|
-
#${id} .dbg-toggle:hover { border-color: ${
|
|
5309
|
+
#${id} .dbg-toggle { display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px; background: ${c2.bg}; border: 1px solid ${c2.border}; border-radius: 8px; color: ${c2.text}; cursor: pointer; font-size: 12px; font-weight: 500; box-shadow: 0 4px 12px rgba(0,0,0,0.15); transition: all 0.2s ease; }
|
|
5310
|
+
#${id} .dbg-toggle:hover { border-color: ${c2.accent}; box-shadow: 0 4px 16px rgba(0,0,0,0.2); }
|
|
5059
5311
|
#${id} .dbg-toggle svg { width: 16px; height: 16px; }
|
|
5060
|
-
#${id} .dbg-toggle .dbg-time { font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; font-size: 11px; padding: 2px 8px; background: ${
|
|
5061
|
-
#${id} .dbg-panel { display: none; width: ${opts.width}px; max-height: 85vh; background: ${
|
|
5312
|
+
#${id} .dbg-toggle .dbg-time { font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; font-size: 11px; padding: 2px 8px; background: ${c2.bgTertiary}; border-radius: 4px; color: ${c2.success}; }
|
|
5313
|
+
#${id} .dbg-panel { display: none; width: ${opts.width}px; max-height: 85vh; background: ${c2.bg}; border: 1px solid ${c2.border}; border-radius: 10px; box-shadow: 0 8px 32px rgba(0,0,0,0.24); overflow: hidden; margin-top: 8px; }
|
|
5062
5314
|
#${id} .dbg-panel.open { display: block; }
|
|
5063
|
-
#${id} .dbg-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; background: ${
|
|
5064
|
-
#${id} .dbg-logo { display: flex; align-items: center; gap: 10px; font-weight: 600; color: ${
|
|
5065
|
-
#${id} .dbg-logo svg { width: 20px; height: 20px; color: ${
|
|
5315
|
+
#${id} .dbg-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; background: ${c2.bgSecondary}; border-bottom: 1px solid ${c2.border}; }
|
|
5316
|
+
#${id} .dbg-logo { display: flex; align-items: center; gap: 10px; font-weight: 600; color: ${c2.text}; }
|
|
5317
|
+
#${id} .dbg-logo svg { width: 20px; height: 20px; color: ${c2.accent}; }
|
|
5066
5318
|
#${id} .dbg-meta { display: flex; align-items: center; gap: 12px; }
|
|
5067
5319
|
#${id} .dbg-badge { font-family: 'SF Mono', Monaco, monospace; font-size: 11px; padding: 3px 10px; border-radius: 4px; font-weight: 500; }
|
|
5068
|
-
#${id} .dbg-badge.time { background: rgba(34,197,94,0.1); color: ${
|
|
5069
|
-
#${id} .dbg-badge.mode { background: rgba(59,130,246,0.1); color: ${
|
|
5070
|
-
#${id} .dbg-close { background: none; border: none; color: ${
|
|
5071
|
-
#${id} .dbg-close:hover { background: ${
|
|
5320
|
+
#${id} .dbg-badge.time { background: rgba(34,197,94,0.1); color: ${c2.success}; }
|
|
5321
|
+
#${id} .dbg-badge.mode { background: rgba(59,130,246,0.1); color: ${c2.accent}; }
|
|
5322
|
+
#${id} .dbg-close { background: none; border: none; color: ${c2.textMuted}; cursor: pointer; padding: 4px; border-radius: 4px; display: flex; }
|
|
5323
|
+
#${id} .dbg-close:hover { background: ${c2.bgTertiary}; color: ${c2.text}; }
|
|
5072
5324
|
#${id} .dbg-close svg { width: 18px; height: 18px; }
|
|
5073
5325
|
#${id} .dbg-body { max-height: calc(85vh - 52px); overflow-y: auto; }
|
|
5074
|
-
#${id} .dbg-section { border-bottom: 1px solid ${
|
|
5326
|
+
#${id} .dbg-section { border-bottom: 1px solid ${c2.border}; }
|
|
5075
5327
|
#${id} .dbg-section:last-child { border-bottom: none; }
|
|
5076
5328
|
#${id} .dbg-section-header { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; cursor: pointer; user-select: none; transition: background 0.15s; }
|
|
5077
|
-
#${id} .dbg-section-header:hover { background: ${
|
|
5078
|
-
#${id} .dbg-section-title { display: flex; align-items: center; gap: 8px; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; color: ${
|
|
5329
|
+
#${id} .dbg-section-header:hover { background: ${c2.bgSecondary}; }
|
|
5330
|
+
#${id} .dbg-section-title { display: flex; align-items: center; gap: 8px; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; color: ${c2.textSecondary}; }
|
|
5079
5331
|
#${id} .dbg-section-title svg { width: 14px; height: 14px; opacity: 0.7; }
|
|
5080
|
-
#${id} .dbg-section-meta { font-size: 11px; color: ${
|
|
5081
|
-
#${id} .dbg-section-content { display: none; padding: 12px 16px; background: ${
|
|
5332
|
+
#${id} .dbg-section-meta { font-size: 11px; color: ${c2.textMuted}; font-family: 'SF Mono', Monaco, monospace; }
|
|
5333
|
+
#${id} .dbg-section-content { display: none; padding: 12px 16px; background: ${c2.bgSecondary}; }
|
|
5082
5334
|
#${id} .dbg-section.open .dbg-section-content { display: block; }
|
|
5083
|
-
#${id} .dbg-section .dbg-chevron { transition: transform 0.2s; color: ${
|
|
5335
|
+
#${id} .dbg-section .dbg-chevron { transition: transform 0.2s; color: ${c2.textMuted}; }
|
|
5084
5336
|
#${id} .dbg-section.open .dbg-chevron { transform: rotate(90deg); }
|
|
5085
|
-
#${id} .dbg-row { display: flex; justify-content: space-between; align-items: center; padding: 6px 0; border-bottom: 1px solid ${
|
|
5337
|
+
#${id} .dbg-row { display: flex; justify-content: space-between; align-items: center; padding: 6px 0; border-bottom: 1px solid ${c2.border}; }
|
|
5086
5338
|
#${id} .dbg-row:last-child { border-bottom: none; }
|
|
5087
|
-
#${id} .dbg-label { color: ${
|
|
5088
|
-
#${id} .dbg-value { color: ${
|
|
5089
|
-
#${id} .dbg-bar { height: 3px; background: ${
|
|
5339
|
+
#${id} .dbg-label { color: ${c2.textSecondary}; font-size: 12px; }
|
|
5340
|
+
#${id} .dbg-value { color: ${c2.text}; font-family: 'SF Mono', Monaco, monospace; font-size: 12px; text-align: right; max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
5341
|
+
#${id} .dbg-bar { height: 3px; background: ${c2.bgTertiary}; border-radius: 2px; margin-top: 4px; overflow: hidden; }
|
|
5090
5342
|
#${id} .dbg-bar-fill { height: 100%; border-radius: 2px; transition: width 0.3s ease; }
|
|
5091
|
-
#${id} .dbg-bar-fill.lexer { background: ${
|
|
5092
|
-
#${id} .dbg-bar-fill.parser { background: ${
|
|
5093
|
-
#${id} .dbg-bar-fill.render { background: ${
|
|
5343
|
+
#${id} .dbg-bar-fill.lexer { background: ${c2.info}; }
|
|
5344
|
+
#${id} .dbg-bar-fill.parser { background: ${c2.warning}; }
|
|
5345
|
+
#${id} .dbg-bar-fill.render { background: ${c2.success}; }
|
|
5094
5346
|
#${id} .dbg-templates { display: flex; flex-direction: column; gap: 6px; }
|
|
5095
|
-
#${id} .dbg-template { display: flex; align-items: center; gap: 8px; padding: 8px 10px; background: ${
|
|
5096
|
-
#${id} .dbg-template-icon { width: 16px; height: 16px; color: ${
|
|
5097
|
-
#${id} .dbg-template-name { color: ${
|
|
5347
|
+
#${id} .dbg-template { display: flex; align-items: center; gap: 8px; padding: 8px 10px; background: ${c2.bg}; border-radius: 6px; font-size: 12px; }
|
|
5348
|
+
#${id} .dbg-template-icon { width: 16px; height: 16px; color: ${c2.textMuted}; flex-shrink: 0; }
|
|
5349
|
+
#${id} .dbg-template-name { color: ${c2.text}; font-family: 'SF Mono', Monaco, monospace; }
|
|
5098
5350
|
#${id} .dbg-template-tag { font-size: 10px; padding: 2px 6px; border-radius: 3px; font-weight: 500; text-transform: uppercase; }
|
|
5099
|
-
#${id} .dbg-template-tag.root { background: rgba(59,130,246,0.15); color: ${
|
|
5351
|
+
#${id} .dbg-template-tag.root { background: rgba(59,130,246,0.15); color: ${c2.accent}; }
|
|
5100
5352
|
#${id} .dbg-template-tag.extends { background: rgba(168,85,247,0.15); color: #a855f7; }
|
|
5101
|
-
#${id} .dbg-template-tag.include { background: rgba(34,197,94,0.15); color: ${
|
|
5353
|
+
#${id} .dbg-template-tag.include { background: rgba(34,197,94,0.15); color: ${c2.success}; }
|
|
5102
5354
|
#${id} .dbg-ctx-grid { display: flex; flex-direction: column; gap: 4px; }
|
|
5103
|
-
#${id} .dbg-ctx-item { background: ${
|
|
5355
|
+
#${id} .dbg-ctx-item { background: ${c2.bg}; border-radius: 6px; overflow: hidden; }
|
|
5104
5356
|
#${id} .dbg-ctx-row { display: flex; align-items: center; justify-content: space-between; padding: 8px 10px; cursor: default; }
|
|
5105
5357
|
#${id} .dbg-ctx-row.expandable { cursor: pointer; }
|
|
5106
|
-
#${id} .dbg-ctx-row.expandable:hover { background: ${
|
|
5358
|
+
#${id} .dbg-ctx-row.expandable:hover { background: ${c2.bgTertiary}; }
|
|
5107
5359
|
#${id} .dbg-ctx-key { display: flex; align-items: center; gap: 6px; }
|
|
5108
|
-
#${id} .dbg-ctx-arrow { width: 12px; height: 12px; color: ${
|
|
5360
|
+
#${id} .dbg-ctx-arrow { width: 12px; height: 12px; color: ${c2.textMuted}; transition: transform 0.15s; flex-shrink: 0; }
|
|
5109
5361
|
#${id} .dbg-ctx-item.open > .dbg-ctx-row .dbg-ctx-arrow { transform: rotate(90deg); }
|
|
5110
|
-
#${id} .dbg-ctx-name { color: ${
|
|
5111
|
-
#${id} .dbg-ctx-type { font-size: 10px; color: ${
|
|
5112
|
-
#${id} .dbg-ctx-preview { color: ${
|
|
5113
|
-
#${id} .dbg-ctx-children { display: none; padding-left: 16px; border-left: 1px solid ${
|
|
5362
|
+
#${id} .dbg-ctx-name { color: ${c2.text}; font-family: 'SF Mono', Monaco, monospace; font-size: 12px; }
|
|
5363
|
+
#${id} .dbg-ctx-type { font-size: 10px; color: ${c2.accent}; background: rgba(59,130,246,0.1); padding: 1px 5px; border-radius: 3px; }
|
|
5364
|
+
#${id} .dbg-ctx-preview { color: ${c2.textSecondary}; font-family: 'SF Mono', Monaco, monospace; font-size: 11px; max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
5365
|
+
#${id} .dbg-ctx-children { display: none; padding-left: 16px; border-left: 1px solid ${c2.border}; margin-left: 10px; }
|
|
5114
5366
|
#${id} .dbg-ctx-item.open > .dbg-ctx-children { display: block; }
|
|
5115
5367
|
#${id} .dbg-ctx-children .dbg-ctx-item { background: transparent; }
|
|
5116
5368
|
#${id} .dbg-ctx-children .dbg-ctx-row { padding: 4px 8px; }
|
|
5117
5369
|
#${id} .dbg-filters { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
5118
|
-
#${id} .dbg-filter { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; background: ${
|
|
5119
|
-
#${id} .dbg-filter-count { font-size: 10px; color: ${
|
|
5370
|
+
#${id} .dbg-filter { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; background: ${c2.bg}; border-radius: 5px; font-size: 12px; font-family: 'SF Mono', Monaco, monospace; color: ${c2.text}; }
|
|
5371
|
+
#${id} .dbg-filter-count { font-size: 10px; color: ${c2.accent}; font-weight: 600; }
|
|
5120
5372
|
#${id} .dbg-cache { display: flex; gap: 16px; }
|
|
5121
|
-
#${id} .dbg-cache-stat { flex: 1; padding: 12px; background: ${
|
|
5373
|
+
#${id} .dbg-cache-stat { flex: 1; padding: 12px; background: ${c2.bg}; border-radius: 6px; text-align: center; }
|
|
5122
5374
|
#${id} .dbg-cache-num { font-size: 24px; font-weight: 600; font-family: 'SF Mono', Monaco, monospace; }
|
|
5123
|
-
#${id} .dbg-cache-num.hit { color: ${
|
|
5124
|
-
#${id} .dbg-cache-num.miss { color: ${
|
|
5125
|
-
#${id} .dbg-cache-label { font-size: 11px; color: ${
|
|
5375
|
+
#${id} .dbg-cache-num.hit { color: ${c2.success}; }
|
|
5376
|
+
#${id} .dbg-cache-num.miss { color: ${c2.error}; }
|
|
5377
|
+
#${id} .dbg-cache-label { font-size: 11px; color: ${c2.textMuted}; margin-top: 4px; }
|
|
5126
5378
|
#${id} .dbg-warnings { display: flex; flex-direction: column; gap: 6px; }
|
|
5127
|
-
#${id} .dbg-warning { display: flex; align-items: flex-start; gap: 8px; padding: 10px 12px; background: rgba(234,179,8,0.1); border-radius: 6px; border-left: 3px solid ${
|
|
5128
|
-
#${id} .dbg-warning-icon { color: ${
|
|
5129
|
-
#${id} .dbg-warning-text { color: ${
|
|
5379
|
+
#${id} .dbg-warning { display: flex; align-items: flex-start; gap: 8px; padding: 10px 12px; background: rgba(234,179,8,0.1); border-radius: 6px; border-left: 3px solid ${c2.warning}; }
|
|
5380
|
+
#${id} .dbg-warning-icon { color: ${c2.warning}; flex-shrink: 0; margin-top: 1px; }
|
|
5381
|
+
#${id} .dbg-warning-text { color: ${c2.text}; font-size: 12px; }
|
|
5130
5382
|
`;
|
|
5131
5383
|
}
|
|
5132
5384
|
function getPosition(pos) {
|
|
@@ -5153,7 +5405,7 @@ var icons = {
|
|
|
5153
5405
|
warning: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>`,
|
|
5154
5406
|
file: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V9z"/></svg>`
|
|
5155
5407
|
};
|
|
5156
|
-
function generateToggle(id, data,
|
|
5408
|
+
function generateToggle(id, data, c2) {
|
|
5157
5409
|
const time2 = (data.totalTime || 0).toFixed(1);
|
|
5158
5410
|
return `
|
|
5159
5411
|
<button class="dbg-toggle" onclick="document.querySelector('#${id} .dbg-panel').classList.add('open');this.style.display='none'">
|
|
@@ -5162,7 +5414,7 @@ function generateToggle(id, data, c) {
|
|
|
5162
5414
|
<span class="dbg-time">${time2}ms</span>
|
|
5163
5415
|
</button>`;
|
|
5164
5416
|
}
|
|
5165
|
-
function generatePanel(id, data,
|
|
5417
|
+
function generatePanel(id, data, c2, opts) {
|
|
5166
5418
|
const time2 = (data.totalTime || 0).toFixed(2);
|
|
5167
5419
|
const mode = data.mode === "aot" ? "AOT" : "Runtime";
|
|
5168
5420
|
return `
|
|
@@ -5436,12 +5688,12 @@ function createDebugRenderer(env, options = {}) {
|
|
|
5436
5688
|
function debugMiddleware(env, options = {}) {
|
|
5437
5689
|
return {
|
|
5438
5690
|
hono() {
|
|
5439
|
-
return async (
|
|
5691
|
+
return async (c2, next) => {
|
|
5440
5692
|
await next();
|
|
5441
|
-
const contentType =
|
|
5693
|
+
const contentType = c2.res.headers.get("content-type") || "";
|
|
5442
5694
|
if (!contentType.includes("text/html"))
|
|
5443
5695
|
return;
|
|
5444
|
-
const body = await
|
|
5696
|
+
const body = await c2.res.text();
|
|
5445
5697
|
const collector = startDebugCollection();
|
|
5446
5698
|
collector.captureContext({});
|
|
5447
5699
|
collector.setMode("runtime");
|
|
@@ -5449,9 +5701,9 @@ function debugMiddleware(env, options = {}) {
|
|
|
5449
5701
|
const data = endDebugCollection();
|
|
5450
5702
|
const panel = generateDebugPanel(data, options.panel);
|
|
5451
5703
|
const newBody = body.includes("</body>") ? body.replace("</body>", `${panel}</body>`) : body + panel;
|
|
5452
|
-
|
|
5453
|
-
status:
|
|
5454
|
-
headers:
|
|
5704
|
+
c2.res = new Response(newBody, {
|
|
5705
|
+
status: c2.res.status,
|
|
5706
|
+
headers: c2.res.headers
|
|
5455
5707
|
});
|
|
5456
5708
|
};
|
|
5457
5709
|
},
|
|
@@ -5572,7 +5824,7 @@ class Environment {
|
|
|
5572
5824
|
compile(source) {
|
|
5573
5825
|
const lexer = new Lexer(source);
|
|
5574
5826
|
const tokens = lexer.tokenize();
|
|
5575
|
-
const parser = new Parser(tokens);
|
|
5827
|
+
const parser = new Parser(tokens, source);
|
|
5576
5828
|
return parser.parse();
|
|
5577
5829
|
}
|
|
5578
5830
|
async loadTemplate(templateName) {
|
|
@@ -5693,7 +5945,7 @@ function Template(source, options = {}) {
|
|
|
5693
5945
|
function compile(source, options = {}) {
|
|
5694
5946
|
const lexer = new Lexer(source);
|
|
5695
5947
|
const tokens = lexer.tokenize();
|
|
5696
|
-
const parser = new Parser(tokens);
|
|
5948
|
+
const parser = new Parser(tokens, source);
|
|
5697
5949
|
const ast = parser.parse();
|
|
5698
5950
|
return compileToFunction(ast, options);
|
|
5699
5951
|
}
|
|
@@ -5716,7 +5968,7 @@ async function compileWithInheritance(templateName, options) {
|
|
|
5716
5968
|
parse(source2) {
|
|
5717
5969
|
const lexer = new Lexer(source2);
|
|
5718
5970
|
const tokens = lexer.tokenize();
|
|
5719
|
-
const parser = new Parser(tokens);
|
|
5971
|
+
const parser = new Parser(tokens, source2);
|
|
5720
5972
|
return parser.parse();
|
|
5721
5973
|
}
|
|
5722
5974
|
};
|
|
@@ -5748,7 +6000,7 @@ async function compileWithInheritanceToCode(templateName, options) {
|
|
|
5748
6000
|
parse(source2) {
|
|
5749
6001
|
const lexer = new Lexer(source2);
|
|
5750
6002
|
const tokens = lexer.tokenize();
|
|
5751
|
-
const parser = new Parser(tokens);
|
|
6003
|
+
const parser = new Parser(tokens, source2);
|
|
5752
6004
|
return parser.parse();
|
|
5753
6005
|
}
|
|
5754
6006
|
};
|
|
@@ -5764,7 +6016,7 @@ async function compileWithInheritanceToCode(templateName, options) {
|
|
|
5764
6016
|
function compileToCode(source, options = {}) {
|
|
5765
6017
|
const lexer = new Lexer(source);
|
|
5766
6018
|
const tokens = lexer.tokenize();
|
|
5767
|
-
const parser = new Parser(tokens);
|
|
6019
|
+
const parser = new Parser(tokens, source);
|
|
5768
6020
|
const ast = parser.parse();
|
|
5769
6021
|
return compileToString(ast, options);
|
|
5770
6022
|
}
|
|
@@ -5784,6 +6036,9 @@ export {
|
|
|
5784
6036
|
builtinTests,
|
|
5785
6037
|
builtinFilters,
|
|
5786
6038
|
TokenType,
|
|
6039
|
+
TemplateSyntaxError,
|
|
6040
|
+
TemplateRuntimeError,
|
|
6041
|
+
TemplateError,
|
|
5787
6042
|
Template,
|
|
5788
6043
|
Runtime,
|
|
5789
6044
|
Parser,
|