binja 0.5.2 → 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/dist/cli.js +179 -65
- package/dist/errors/index.d.ts +65 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +317 -110
- 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 +2 -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) => {
|
|
@@ -2943,6 +3134,7 @@ class Runtime {
|
|
|
2943
3134
|
tests;
|
|
2944
3135
|
blocks = new Map;
|
|
2945
3136
|
parentTemplate = null;
|
|
3137
|
+
source;
|
|
2946
3138
|
constructor(options = {}) {
|
|
2947
3139
|
this.options = {
|
|
2948
3140
|
autoescape: options.autoescape ?? true,
|
|
@@ -3679,8 +3871,17 @@ class Runtime {
|
|
|
3679
3871
|
}
|
|
3680
3872
|
const kwargs = this.evalObjectSync(node.kwargs, ctx);
|
|
3681
3873
|
const filter = this.filters[node.filter];
|
|
3682
|
-
if (!filter)
|
|
3683
|
-
|
|
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
|
+
}
|
|
3684
3885
|
return filter(value, ...args, ...Object.values(kwargs));
|
|
3685
3886
|
}
|
|
3686
3887
|
evalBinaryOp(node, ctx) {
|
|
@@ -4117,6 +4318,9 @@ class Runtime {
|
|
|
4117
4318
|
addGlobal(name, value) {
|
|
4118
4319
|
this.options.globals[name] = value;
|
|
4119
4320
|
}
|
|
4321
|
+
setSource(source) {
|
|
4322
|
+
this.source = source;
|
|
4323
|
+
}
|
|
4120
4324
|
}
|
|
4121
4325
|
|
|
4122
4326
|
// src/compiler/index.ts
|
|
@@ -5053,13 +5257,13 @@ var DEFAULT_OPTIONS = {
|
|
|
5053
5257
|
function generateDebugPanel(data, options = {}) {
|
|
5054
5258
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
5055
5259
|
const id = `binja-debug-${Date.now()}`;
|
|
5056
|
-
const
|
|
5260
|
+
const colors2 = opts.dark ? darkTheme : lightTheme;
|
|
5057
5261
|
return `
|
|
5058
5262
|
<!-- Binja Debug Panel -->
|
|
5059
5263
|
<div id="${id}" class="binja-debugger" data-theme="${opts.dark ? "dark" : "light"}">
|
|
5060
|
-
<style>${generateStyles(id,
|
|
5061
|
-
${generateToggle(id, data,
|
|
5062
|
-
${generatePanel(id, data,
|
|
5264
|
+
<style>${generateStyles(id, colors2, opts)}</style>
|
|
5265
|
+
${generateToggle(id, data, colors2)}
|
|
5266
|
+
${generatePanel(id, data, colors2, opts)}
|
|
5063
5267
|
<script>${generateScript(id)}</script>
|
|
5064
5268
|
</div>
|
|
5065
5269
|
<!-- /Binja Debug Panel -->
|
|
@@ -5097,84 +5301,84 @@ var lightTheme = {
|
|
|
5097
5301
|
error: "#dc2626",
|
|
5098
5302
|
info: "#0891b2"
|
|
5099
5303
|
};
|
|
5100
|
-
function generateStyles(id,
|
|
5304
|
+
function generateStyles(id, c2, opts) {
|
|
5101
5305
|
const pos = getPosition(opts.position);
|
|
5102
5306
|
return `
|
|
5103
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; }
|
|
5104
5308
|
#${id} * { box-sizing: border-box; margin: 0; padding: 0; }
|
|
5105
|
-
#${id} .dbg-toggle { display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px; background: ${
|
|
5106
|
-
#${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); }
|
|
5107
5311
|
#${id} .dbg-toggle svg { width: 16px; height: 16px; }
|
|
5108
|
-
#${id} .dbg-toggle .dbg-time { font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace; font-size: 11px; padding: 2px 8px; background: ${
|
|
5109
|
-
#${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; }
|
|
5110
5314
|
#${id} .dbg-panel.open { display: block; }
|
|
5111
|
-
#${id} .dbg-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; background: ${
|
|
5112
|
-
#${id} .dbg-logo { display: flex; align-items: center; gap: 10px; font-weight: 600; color: ${
|
|
5113
|
-
#${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}; }
|
|
5114
5318
|
#${id} .dbg-meta { display: flex; align-items: center; gap: 12px; }
|
|
5115
5319
|
#${id} .dbg-badge { font-family: 'SF Mono', Monaco, monospace; font-size: 11px; padding: 3px 10px; border-radius: 4px; font-weight: 500; }
|
|
5116
|
-
#${id} .dbg-badge.time { background: rgba(34,197,94,0.1); color: ${
|
|
5117
|
-
#${id} .dbg-badge.mode { background: rgba(59,130,246,0.1); color: ${
|
|
5118
|
-
#${id} .dbg-close { background: none; border: none; color: ${
|
|
5119
|
-
#${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}; }
|
|
5120
5324
|
#${id} .dbg-close svg { width: 18px; height: 18px; }
|
|
5121
5325
|
#${id} .dbg-body { max-height: calc(85vh - 52px); overflow-y: auto; }
|
|
5122
|
-
#${id} .dbg-section { border-bottom: 1px solid ${
|
|
5326
|
+
#${id} .dbg-section { border-bottom: 1px solid ${c2.border}; }
|
|
5123
5327
|
#${id} .dbg-section:last-child { border-bottom: none; }
|
|
5124
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; }
|
|
5125
|
-
#${id} .dbg-section-header:hover { background: ${
|
|
5126
|
-
#${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}; }
|
|
5127
5331
|
#${id} .dbg-section-title svg { width: 14px; height: 14px; opacity: 0.7; }
|
|
5128
|
-
#${id} .dbg-section-meta { font-size: 11px; color: ${
|
|
5129
|
-
#${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}; }
|
|
5130
5334
|
#${id} .dbg-section.open .dbg-section-content { display: block; }
|
|
5131
|
-
#${id} .dbg-section .dbg-chevron { transition: transform 0.2s; color: ${
|
|
5335
|
+
#${id} .dbg-section .dbg-chevron { transition: transform 0.2s; color: ${c2.textMuted}; }
|
|
5132
5336
|
#${id} .dbg-section.open .dbg-chevron { transform: rotate(90deg); }
|
|
5133
|
-
#${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}; }
|
|
5134
5338
|
#${id} .dbg-row:last-child { border-bottom: none; }
|
|
5135
|
-
#${id} .dbg-label { color: ${
|
|
5136
|
-
#${id} .dbg-value { color: ${
|
|
5137
|
-
#${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; }
|
|
5138
5342
|
#${id} .dbg-bar-fill { height: 100%; border-radius: 2px; transition: width 0.3s ease; }
|
|
5139
|
-
#${id} .dbg-bar-fill.lexer { background: ${
|
|
5140
|
-
#${id} .dbg-bar-fill.parser { background: ${
|
|
5141
|
-
#${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}; }
|
|
5142
5346
|
#${id} .dbg-templates { display: flex; flex-direction: column; gap: 6px; }
|
|
5143
|
-
#${id} .dbg-template { display: flex; align-items: center; gap: 8px; padding: 8px 10px; background: ${
|
|
5144
|
-
#${id} .dbg-template-icon { width: 16px; height: 16px; color: ${
|
|
5145
|
-
#${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; }
|
|
5146
5350
|
#${id} .dbg-template-tag { font-size: 10px; padding: 2px 6px; border-radius: 3px; font-weight: 500; text-transform: uppercase; }
|
|
5147
|
-
#${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}; }
|
|
5148
5352
|
#${id} .dbg-template-tag.extends { background: rgba(168,85,247,0.15); color: #a855f7; }
|
|
5149
|
-
#${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}; }
|
|
5150
5354
|
#${id} .dbg-ctx-grid { display: flex; flex-direction: column; gap: 4px; }
|
|
5151
|
-
#${id} .dbg-ctx-item { background: ${
|
|
5355
|
+
#${id} .dbg-ctx-item { background: ${c2.bg}; border-radius: 6px; overflow: hidden; }
|
|
5152
5356
|
#${id} .dbg-ctx-row { display: flex; align-items: center; justify-content: space-between; padding: 8px 10px; cursor: default; }
|
|
5153
5357
|
#${id} .dbg-ctx-row.expandable { cursor: pointer; }
|
|
5154
|
-
#${id} .dbg-ctx-row.expandable:hover { background: ${
|
|
5358
|
+
#${id} .dbg-ctx-row.expandable:hover { background: ${c2.bgTertiary}; }
|
|
5155
5359
|
#${id} .dbg-ctx-key { display: flex; align-items: center; gap: 6px; }
|
|
5156
|
-
#${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; }
|
|
5157
5361
|
#${id} .dbg-ctx-item.open > .dbg-ctx-row .dbg-ctx-arrow { transform: rotate(90deg); }
|
|
5158
|
-
#${id} .dbg-ctx-name { color: ${
|
|
5159
|
-
#${id} .dbg-ctx-type { font-size: 10px; color: ${
|
|
5160
|
-
#${id} .dbg-ctx-preview { color: ${
|
|
5161
|
-
#${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; }
|
|
5162
5366
|
#${id} .dbg-ctx-item.open > .dbg-ctx-children { display: block; }
|
|
5163
5367
|
#${id} .dbg-ctx-children .dbg-ctx-item { background: transparent; }
|
|
5164
5368
|
#${id} .dbg-ctx-children .dbg-ctx-row { padding: 4px 8px; }
|
|
5165
5369
|
#${id} .dbg-filters { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
5166
|
-
#${id} .dbg-filter { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; background: ${
|
|
5167
|
-
#${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; }
|
|
5168
5372
|
#${id} .dbg-cache { display: flex; gap: 16px; }
|
|
5169
|
-
#${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; }
|
|
5170
5374
|
#${id} .dbg-cache-num { font-size: 24px; font-weight: 600; font-family: 'SF Mono', Monaco, monospace; }
|
|
5171
|
-
#${id} .dbg-cache-num.hit { color: ${
|
|
5172
|
-
#${id} .dbg-cache-num.miss { color: ${
|
|
5173
|
-
#${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; }
|
|
5174
5378
|
#${id} .dbg-warnings { display: flex; flex-direction: column; gap: 6px; }
|
|
5175
|
-
#${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 ${
|
|
5176
|
-
#${id} .dbg-warning-icon { color: ${
|
|
5177
|
-
#${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; }
|
|
5178
5382
|
`;
|
|
5179
5383
|
}
|
|
5180
5384
|
function getPosition(pos) {
|
|
@@ -5201,7 +5405,7 @@ var icons = {
|
|
|
5201
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>`,
|
|
5202
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>`
|
|
5203
5407
|
};
|
|
5204
|
-
function generateToggle(id, data,
|
|
5408
|
+
function generateToggle(id, data, c2) {
|
|
5205
5409
|
const time2 = (data.totalTime || 0).toFixed(1);
|
|
5206
5410
|
return `
|
|
5207
5411
|
<button class="dbg-toggle" onclick="document.querySelector('#${id} .dbg-panel').classList.add('open');this.style.display='none'">
|
|
@@ -5210,7 +5414,7 @@ function generateToggle(id, data, c) {
|
|
|
5210
5414
|
<span class="dbg-time">${time2}ms</span>
|
|
5211
5415
|
</button>`;
|
|
5212
5416
|
}
|
|
5213
|
-
function generatePanel(id, data,
|
|
5417
|
+
function generatePanel(id, data, c2, opts) {
|
|
5214
5418
|
const time2 = (data.totalTime || 0).toFixed(2);
|
|
5215
5419
|
const mode = data.mode === "aot" ? "AOT" : "Runtime";
|
|
5216
5420
|
return `
|
|
@@ -5484,12 +5688,12 @@ function createDebugRenderer(env, options = {}) {
|
|
|
5484
5688
|
function debugMiddleware(env, options = {}) {
|
|
5485
5689
|
return {
|
|
5486
5690
|
hono() {
|
|
5487
|
-
return async (
|
|
5691
|
+
return async (c2, next) => {
|
|
5488
5692
|
await next();
|
|
5489
|
-
const contentType =
|
|
5693
|
+
const contentType = c2.res.headers.get("content-type") || "";
|
|
5490
5694
|
if (!contentType.includes("text/html"))
|
|
5491
5695
|
return;
|
|
5492
|
-
const body = await
|
|
5696
|
+
const body = await c2.res.text();
|
|
5493
5697
|
const collector = startDebugCollection();
|
|
5494
5698
|
collector.captureContext({});
|
|
5495
5699
|
collector.setMode("runtime");
|
|
@@ -5497,9 +5701,9 @@ function debugMiddleware(env, options = {}) {
|
|
|
5497
5701
|
const data = endDebugCollection();
|
|
5498
5702
|
const panel = generateDebugPanel(data, options.panel);
|
|
5499
5703
|
const newBody = body.includes("</body>") ? body.replace("</body>", `${panel}</body>`) : body + panel;
|
|
5500
|
-
|
|
5501
|
-
status:
|
|
5502
|
-
headers:
|
|
5704
|
+
c2.res = new Response(newBody, {
|
|
5705
|
+
status: c2.res.status,
|
|
5706
|
+
headers: c2.res.headers
|
|
5503
5707
|
});
|
|
5504
5708
|
};
|
|
5505
5709
|
},
|
|
@@ -5620,7 +5824,7 @@ class Environment {
|
|
|
5620
5824
|
compile(source) {
|
|
5621
5825
|
const lexer = new Lexer(source);
|
|
5622
5826
|
const tokens = lexer.tokenize();
|
|
5623
|
-
const parser = new Parser(tokens);
|
|
5827
|
+
const parser = new Parser(tokens, source);
|
|
5624
5828
|
return parser.parse();
|
|
5625
5829
|
}
|
|
5626
5830
|
async loadTemplate(templateName) {
|
|
@@ -5741,7 +5945,7 @@ function Template(source, options = {}) {
|
|
|
5741
5945
|
function compile(source, options = {}) {
|
|
5742
5946
|
const lexer = new Lexer(source);
|
|
5743
5947
|
const tokens = lexer.tokenize();
|
|
5744
|
-
const parser = new Parser(tokens);
|
|
5948
|
+
const parser = new Parser(tokens, source);
|
|
5745
5949
|
const ast = parser.parse();
|
|
5746
5950
|
return compileToFunction(ast, options);
|
|
5747
5951
|
}
|
|
@@ -5764,7 +5968,7 @@ async function compileWithInheritance(templateName, options) {
|
|
|
5764
5968
|
parse(source2) {
|
|
5765
5969
|
const lexer = new Lexer(source2);
|
|
5766
5970
|
const tokens = lexer.tokenize();
|
|
5767
|
-
const parser = new Parser(tokens);
|
|
5971
|
+
const parser = new Parser(tokens, source2);
|
|
5768
5972
|
return parser.parse();
|
|
5769
5973
|
}
|
|
5770
5974
|
};
|
|
@@ -5796,7 +6000,7 @@ async function compileWithInheritanceToCode(templateName, options) {
|
|
|
5796
6000
|
parse(source2) {
|
|
5797
6001
|
const lexer = new Lexer(source2);
|
|
5798
6002
|
const tokens = lexer.tokenize();
|
|
5799
|
-
const parser = new Parser(tokens);
|
|
6003
|
+
const parser = new Parser(tokens, source2);
|
|
5800
6004
|
return parser.parse();
|
|
5801
6005
|
}
|
|
5802
6006
|
};
|
|
@@ -5812,7 +6016,7 @@ async function compileWithInheritanceToCode(templateName, options) {
|
|
|
5812
6016
|
function compileToCode(source, options = {}) {
|
|
5813
6017
|
const lexer = new Lexer(source);
|
|
5814
6018
|
const tokens = lexer.tokenize();
|
|
5815
|
-
const parser = new Parser(tokens);
|
|
6019
|
+
const parser = new Parser(tokens, source);
|
|
5816
6020
|
const ast = parser.parse();
|
|
5817
6021
|
return compileToString(ast, options);
|
|
5818
6022
|
}
|
|
@@ -5832,6 +6036,9 @@ export {
|
|
|
5832
6036
|
builtinTests,
|
|
5833
6037
|
builtinFilters,
|
|
5834
6038
|
TokenType,
|
|
6039
|
+
TemplateSyntaxError,
|
|
6040
|
+
TemplateRuntimeError,
|
|
6041
|
+
TemplateError,
|
|
5835
6042
|
Template,
|
|
5836
6043
|
Runtime,
|
|
5837
6044
|
Parser,
|