binja 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +246 -104
- package/dist/cli.d.ts +16 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +2316 -0
- package/dist/compiler/flattener.d.ts +36 -0
- package/dist/compiler/flattener.d.ts.map +1 -0
- package/dist/compiler/index.d.ts +32 -0
- package/dist/compiler/index.d.ts.map +1 -0
- package/dist/debug/collector.d.ts +73 -0
- package/dist/debug/collector.d.ts.map +1 -0
- package/dist/debug/index.d.ts +54 -0
- package/dist/debug/index.d.ts.map +1 -0
- package/dist/debug/panel.d.ts +16 -0
- package/dist/debug/panel.d.ts.map +1 -0
- package/dist/filters/index.d.ts +20 -0
- package/dist/filters/index.d.ts.map +1 -1
- package/dist/index.d.ts +78 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2438 -328
- package/dist/lexer/index.d.ts +2 -0
- package/dist/lexer/index.d.ts.map +1 -1
- package/dist/runtime/context.d.ts +4 -0
- package/dist/runtime/context.d.ts.map +1 -1
- package/dist/runtime/index.d.ts +34 -22
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/tests/index.d.ts.map +1 -1
- package/package.json +12 -2
package/README.md
CHANGED
|
@@ -23,12 +23,15 @@
|
|
|
23
23
|
|
|
24
24
|
## Why binja?
|
|
25
25
|
|
|
26
|
-
| Feature |
|
|
26
|
+
| Feature | Binja | Other JS engines |
|
|
27
27
|
|---------|-----------|------------------|
|
|
28
|
+
| **AOT Compilation** | ✅ 160x faster | ❌ |
|
|
28
29
|
| Django DTL Compatible | ✅ 100% | ❌ Partial |
|
|
29
30
|
| Jinja2 Compatible | ✅ Full | ⚠️ Limited |
|
|
30
31
|
| Template Inheritance | ✅ | ⚠️ |
|
|
31
32
|
| 50+ Built-in Filters | ✅ | ❌ |
|
|
33
|
+
| Debug Panel | ✅ | ❌ |
|
|
34
|
+
| CLI Tool | ✅ | ⚠️ |
|
|
32
35
|
| Autoescape by Default | ✅ | ❌ |
|
|
33
36
|
| TypeScript | ✅ Native | ⚠️ |
|
|
34
37
|
| Bun Optimized | ✅ | ❌ |
|
|
@@ -37,107 +40,32 @@
|
|
|
37
40
|
|
|
38
41
|
## Benchmarks
|
|
39
42
|
|
|
40
|
-
Tested on
|
|
43
|
+
Tested on Mac Studio M1 Max, Bun 1.3.5, 10,000 iterations.
|
|
41
44
|
|
|
42
|
-
###
|
|
43
|
-
```
|
|
44
|
-
{{ name }} - {{ title|upper }}
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
| Engine | Ops/sec | Relative |
|
|
48
|
-
|--------|---------|----------|
|
|
49
|
-
| **binja** | **142,857** | **1.0x** |
|
|
50
|
-
| Nunjucks | 45,662 | 3.1x slower |
|
|
51
|
-
| EJS | 38,461 | 3.7x slower |
|
|
52
|
-
| Handlebars | 52,631 | 2.7x slower |
|
|
53
|
-
|
|
54
|
-
### Complex Template (loops, conditions, filters)
|
|
55
|
-
```django
|
|
56
|
-
{% for item in items %}
|
|
57
|
-
{% if item.active %}
|
|
58
|
-
{{ item.name|title }} - ${{ item.price|floatformat:2 }}
|
|
59
|
-
{% endif %}
|
|
60
|
-
{% endfor %}
|
|
61
|
-
```
|
|
45
|
+
### Two Rendering Modes
|
|
62
46
|
|
|
63
|
-
|
|
|
64
|
-
|
|
65
|
-
| **
|
|
66
|
-
|
|
|
67
|
-
| EJS | 12,500 | 2.3x slower |
|
|
68
|
-
| Handlebars | 15,384 | 1.9x slower |
|
|
47
|
+
| Mode | Function | Best For | vs Nunjucks |
|
|
48
|
+
|------|----------|----------|-------------|
|
|
49
|
+
| **Runtime** | `render()` | Development | **3.7x faster** |
|
|
50
|
+
| **AOT** | `compile()` | Production | **160x faster** |
|
|
69
51
|
|
|
70
|
-
###
|
|
71
|
-
```django
|
|
72
|
-
{% extends "base.html" %}
|
|
73
|
-
{% block content %}...{% endblock %}
|
|
74
|
-
```
|
|
52
|
+
### Performance Comparison
|
|
75
53
|
|
|
76
|
-
|
|
|
77
|
-
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
|
|
|
81
|
-
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
| Engine | Heap (MB) | RSS (MB) |
|
|
86
|
-
|--------|-----------|----------|
|
|
87
|
-
| **binja** | **12.4** | **45.2** |
|
|
88
|
-
| Nunjucks | 28.6 | 89.4 |
|
|
89
|
-
| EJS | 18.2 | 62.1 |
|
|
54
|
+
| Benchmark | Nunjucks | binja Runtime | binja AOT |
|
|
55
|
+
|-----------|----------|---------------|-----------|
|
|
56
|
+
| Simple Template | 95K ops/s | 290K ops/s | **14.3M ops/s** |
|
|
57
|
+
| Complex Template | 28K ops/s | 103K ops/s | **1.07M ops/s** |
|
|
58
|
+
| Nested Loops | 27K ops/s | 130K ops/s | **1.75M ops/s** |
|
|
59
|
+
| HTML Escaping | 65K ops/s | 241K ops/s | **2.23M ops/s** |
|
|
60
|
+
| Conditionals | 27K ops/s | 126K ops/s | **22.8M ops/s** |
|
|
61
|
+
| Large Dataset (100 items) | 21K ops/s | 36K ops/s | **202K ops/s** |
|
|
90
62
|
|
|
91
63
|
### Run Benchmarks
|
|
92
64
|
|
|
93
65
|
```bash
|
|
94
|
-
bun run benchmark
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
<details>
|
|
98
|
-
<summary>📊 Full Benchmark Code</summary>
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
import { Environment } from 'binja'
|
|
102
|
-
|
|
103
|
-
const env = new Environment()
|
|
104
|
-
const iterations = 1000
|
|
105
|
-
|
|
106
|
-
// Simple benchmark
|
|
107
|
-
const simpleTemplate = '{{ name }} - {{ title|upper }}'
|
|
108
|
-
const simpleContext = { name: 'John', title: 'hello world' }
|
|
109
|
-
|
|
110
|
-
console.time('Simple Template')
|
|
111
|
-
for (let i = 0; i < iterations; i++) {
|
|
112
|
-
await env.renderString(simpleTemplate, simpleContext)
|
|
113
|
-
}
|
|
114
|
-
console.timeEnd('Simple Template')
|
|
115
|
-
|
|
116
|
-
// Complex benchmark
|
|
117
|
-
const complexTemplate = `
|
|
118
|
-
{% for item in items %}
|
|
119
|
-
{% if item.active %}
|
|
120
|
-
{{ item.name|title }} - ${{ item.price|floatformat:2 }}
|
|
121
|
-
{% endif %}
|
|
122
|
-
{% endfor %}
|
|
123
|
-
`
|
|
124
|
-
const complexContext = {
|
|
125
|
-
items: Array.from({ length: 50 }, (_, i) => ({
|
|
126
|
-
name: `product ${i}`,
|
|
127
|
-
price: Math.random() * 100,
|
|
128
|
-
active: Math.random() > 0.3
|
|
129
|
-
}))
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
console.time('Complex Template')
|
|
133
|
-
for (let i = 0; i < iterations; i++) {
|
|
134
|
-
await env.renderString(complexTemplate, complexContext)
|
|
135
|
-
}
|
|
136
|
-
console.timeEnd('Complex Template')
|
|
66
|
+
bun run full-benchmark.ts
|
|
137
67
|
```
|
|
138
68
|
|
|
139
|
-
</details>
|
|
140
|
-
|
|
141
69
|
---
|
|
142
70
|
|
|
143
71
|
## Installation
|
|
@@ -181,6 +109,37 @@ const html = await env.render('pages/home.html', {
|
|
|
181
109
|
})
|
|
182
110
|
```
|
|
183
111
|
|
|
112
|
+
### AOT Compilation (Maximum Performance)
|
|
113
|
+
|
|
114
|
+
For production, use `compile()` for **160x faster** rendering:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
import { compile } from 'binja'
|
|
118
|
+
|
|
119
|
+
// Compile once at startup
|
|
120
|
+
const renderUser = compile('<h1>{{ name|upper }}</h1>')
|
|
121
|
+
|
|
122
|
+
// Use many times (sync, extremely fast!)
|
|
123
|
+
const html = renderUser({ name: 'john' })
|
|
124
|
+
// Output: <h1>JOHN</h1>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Production example:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import { compile } from 'binja'
|
|
131
|
+
|
|
132
|
+
// Pre-compile all templates at server startup
|
|
133
|
+
const templates = {
|
|
134
|
+
home: compile(await Bun.file('./views/home.html').text()),
|
|
135
|
+
user: compile(await Bun.file('./views/user.html').text()),
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Rendering is now synchronous and extremely fast
|
|
139
|
+
app.get('/', () => templates.home({ title: 'Welcome' }))
|
|
140
|
+
app.get('/user/:id', ({ params }) => templates.user({ id: params.id }))
|
|
141
|
+
```
|
|
142
|
+
|
|
184
143
|
---
|
|
185
144
|
|
|
186
145
|
## Features
|
|
@@ -351,6 +310,43 @@ const html = await env.render('pages/home.html', {
|
|
|
351
310
|
|
|
352
311
|
---
|
|
353
312
|
|
|
313
|
+
## Tests (is operator)
|
|
314
|
+
|
|
315
|
+
Tests check values using the `is` operator (Jinja2 syntax):
|
|
316
|
+
|
|
317
|
+
```django
|
|
318
|
+
{% if value is defined %}...{% endif %}
|
|
319
|
+
{% if num is even %}...{% endif %}
|
|
320
|
+
{% if num is divisibleby(3) %}...{% endif %}
|
|
321
|
+
{% if items is empty %}...{% endif %}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Built-in Tests
|
|
325
|
+
|
|
326
|
+
| Test | Description |
|
|
327
|
+
|------|-------------|
|
|
328
|
+
| `divisibleby(n)` | Divisible by n |
|
|
329
|
+
| `even` / `odd` | Even/odd integer |
|
|
330
|
+
| `number` / `integer` / `float` | Type checks |
|
|
331
|
+
| `defined` / `undefined` | Variable exists |
|
|
332
|
+
| `none` | Is null |
|
|
333
|
+
| `empty` | Empty array/string/object |
|
|
334
|
+
| `truthy` / `falsy` | Truthiness checks |
|
|
335
|
+
| `string` / `mapping` / `iterable` | Type checks |
|
|
336
|
+
| `gt(n)` / `lt(n)` / `ge(n)` / `le(n)` | Comparisons |
|
|
337
|
+
| `eq(v)` / `ne(v)` / `sameas(v)` | Equality |
|
|
338
|
+
| `upper` / `lower` | String case checks |
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
import { builtinTests } from 'binja'
|
|
342
|
+
|
|
343
|
+
// All 30+ built-in tests
|
|
344
|
+
console.log(Object.keys(builtinTests))
|
|
345
|
+
// ['divisibleby', 'even', 'odd', 'number', 'integer', ...]
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
354
350
|
## Django Compatibility
|
|
355
351
|
|
|
356
352
|
binja is designed to be a drop-in replacement for Django templates:
|
|
@@ -408,6 +404,90 @@ const env = new Environment({
|
|
|
408
404
|
|
|
409
405
|
---
|
|
410
406
|
|
|
407
|
+
## Debug Panel
|
|
408
|
+
|
|
409
|
+
Binja includes a professional debug panel for development, similar to Django Debug Toolbar:
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
const env = new Environment({
|
|
413
|
+
templates: './templates',
|
|
414
|
+
debug: true, // Enable debug panel
|
|
415
|
+
debugOptions: {
|
|
416
|
+
dark: true,
|
|
417
|
+
position: 'bottom-right',
|
|
418
|
+
},
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
// Debug panel is automatically injected into HTML responses
|
|
422
|
+
const html = await env.render('page.html', context)
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Features
|
|
426
|
+
|
|
427
|
+
- **Performance Metrics** - Lexer, Parser, Render timing with visual bars
|
|
428
|
+
- **Template Chain** - See extends/include hierarchy
|
|
429
|
+
- **Context Inspector** - Expandable tree view of all context variables
|
|
430
|
+
- **Filter Usage** - Which filters were used and how many times
|
|
431
|
+
- **Cache Stats** - Hit/miss rates
|
|
432
|
+
- **Warnings** - Optimization suggestions
|
|
433
|
+
|
|
434
|
+
### Options
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
debugOptions: {
|
|
438
|
+
dark: true, // Dark/light theme
|
|
439
|
+
collapsed: true, // Start collapsed
|
|
440
|
+
position: 'bottom-right', // Panel position
|
|
441
|
+
width: 420, // Panel width
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
---
|
|
446
|
+
|
|
447
|
+
## CLI Tool
|
|
448
|
+
|
|
449
|
+
Binja includes a CLI for template pre-compilation:
|
|
450
|
+
|
|
451
|
+
```bash
|
|
452
|
+
# Compile all templates to JavaScript
|
|
453
|
+
binja compile ./templates -o ./dist
|
|
454
|
+
|
|
455
|
+
# Check templates for errors
|
|
456
|
+
binja check ./templates
|
|
457
|
+
|
|
458
|
+
# Watch mode for development
|
|
459
|
+
binja watch ./templates -o ./dist
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Pre-compiled Templates
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
// Generated: dist/home.js
|
|
466
|
+
import { render } from './dist/home.js'
|
|
467
|
+
|
|
468
|
+
const html = render({ title: 'Home', items: [...] })
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
## Raw/Verbatim Tag
|
|
474
|
+
|
|
475
|
+
Output template syntax without processing:
|
|
476
|
+
|
|
477
|
+
```django
|
|
478
|
+
{% raw %}
|
|
479
|
+
{{ this will not be processed }}
|
|
480
|
+
{% neither will this %}
|
|
481
|
+
{% endraw %}
|
|
482
|
+
|
|
483
|
+
{# Or Django-style #}
|
|
484
|
+
{% verbatim %}
|
|
485
|
+
{{ raw output }}
|
|
486
|
+
{% endverbatim %}
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
411
491
|
## Custom Filters
|
|
412
492
|
|
|
413
493
|
```typescript
|
|
@@ -459,31 +539,84 @@ await render('{{ script }}', {
|
|
|
459
539
|
|
|
460
540
|
## Performance Tips
|
|
461
541
|
|
|
462
|
-
1. **
|
|
463
|
-
2. **
|
|
464
|
-
3. **
|
|
542
|
+
1. **Use AOT in Production** - `compile()` is 160x faster than Nunjucks
|
|
543
|
+
2. **Pre-compile at Startup** - Compile templates once, use many times
|
|
544
|
+
3. **Reuse Environment** - For templates with `{% extends %}`, create once
|
|
545
|
+
4. **Enable caching** - Templates are cached automatically
|
|
465
546
|
|
|
466
547
|
```typescript
|
|
467
|
-
|
|
468
|
-
const env = new Environment({ templates: './templates' })
|
|
548
|
+
import { compile } from 'binja'
|
|
469
549
|
|
|
470
|
-
//
|
|
471
|
-
|
|
472
|
-
|
|
550
|
+
// Best: AOT compilation for static templates
|
|
551
|
+
const templates = {
|
|
552
|
+
home: compile(await Bun.file('./views/home.html').text()),
|
|
553
|
+
user: compile(await Bun.file('./views/user.html').text()),
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// Sync rendering, extremely fast
|
|
557
|
+
app.get('/', () => templates.home({ title: 'Home' }))
|
|
558
|
+
app.get('/user/:id', () => templates.user({ id: params.id }))
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
For templates with inheritance (`{% extends %}`):
|
|
562
|
+
|
|
563
|
+
```typescript
|
|
564
|
+
import { Environment } from 'binja'
|
|
565
|
+
|
|
566
|
+
// Environment with cache for inherited templates
|
|
567
|
+
const env = new Environment({ templates: './views', cache: true })
|
|
568
|
+
|
|
569
|
+
// Pre-warm cache at startup
|
|
570
|
+
await env.loadTemplate('base.html')
|
|
571
|
+
await env.loadTemplate('home.html')
|
|
473
572
|
```
|
|
474
573
|
|
|
475
574
|
---
|
|
476
575
|
|
|
477
576
|
## API Reference
|
|
478
577
|
|
|
479
|
-
### `render(template, context)`
|
|
578
|
+
### `render(template, context)` - Runtime Mode
|
|
480
579
|
|
|
481
|
-
Render a template string with context.
|
|
580
|
+
Render a template string with context (async, easy development).
|
|
482
581
|
|
|
483
582
|
```typescript
|
|
583
|
+
import { render } from 'binja'
|
|
584
|
+
|
|
484
585
|
const html = await render('Hello {{ name }}', { name: 'World' })
|
|
485
586
|
```
|
|
486
587
|
|
|
588
|
+
### `compile(template, options?)` - AOT Mode
|
|
589
|
+
|
|
590
|
+
Compile a template to an optimized function (sync, **160x faster**).
|
|
591
|
+
|
|
592
|
+
```typescript
|
|
593
|
+
import { compile } from 'binja'
|
|
594
|
+
|
|
595
|
+
// Compile once
|
|
596
|
+
const renderGreeting = compile('<h1>{{ name|upper }}</h1>')
|
|
597
|
+
|
|
598
|
+
// Use many times (sync!)
|
|
599
|
+
const html = renderGreeting({ name: 'world' }) // <h1>WORLD</h1>
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
**Supported:** Variables, filters, conditions, loops, set/with, comments.
|
|
603
|
+
**Not supported:** `{% extends %}`, `{% include %}` (use Environment for these).
|
|
604
|
+
|
|
605
|
+
### `compileToCode(template, options?)`
|
|
606
|
+
|
|
607
|
+
Generate JavaScript code string for build tools.
|
|
608
|
+
|
|
609
|
+
```typescript
|
|
610
|
+
import { compileToCode } from 'binja'
|
|
611
|
+
|
|
612
|
+
const code = compileToCode('<h1>{{ title }}</h1>', {
|
|
613
|
+
functionName: 'renderHeader'
|
|
614
|
+
})
|
|
615
|
+
|
|
616
|
+
// Save to file for bundling
|
|
617
|
+
await Bun.write('./compiled/header.js', code)
|
|
618
|
+
```
|
|
619
|
+
|
|
487
620
|
### `Environment`
|
|
488
621
|
|
|
489
622
|
Create a configured template environment.
|
|
@@ -496,6 +629,7 @@ env.render(name, context) // Render template file
|
|
|
496
629
|
env.renderString(str, context) // Render template string
|
|
497
630
|
env.addFilter(name, fn) // Add custom filter
|
|
498
631
|
env.addGlobal(name, value) // Add global variable
|
|
632
|
+
env.loadTemplate(name) // Pre-load template (for cache warming)
|
|
499
633
|
```
|
|
500
634
|
|
|
501
635
|
---
|
|
@@ -508,8 +642,11 @@ env.addGlobal(name, value) // Add global variable
|
|
|
508
642
|
import { Elysia } from 'elysia'
|
|
509
643
|
import { Environment } from 'binja'
|
|
510
644
|
|
|
645
|
+
// Development with debug panel
|
|
511
646
|
const templates = new Environment({
|
|
512
647
|
templates: './views',
|
|
648
|
+
debug: Bun.env.NODE_ENV !== 'production',
|
|
649
|
+
debugOptions: { dark: true },
|
|
513
650
|
globals: {
|
|
514
651
|
site_name: 'My App',
|
|
515
652
|
current_year: new Date().getFullYear()
|
|
@@ -621,7 +758,13 @@ import { Hono } from 'hono'
|
|
|
621
758
|
import { Environment } from 'binja'
|
|
622
759
|
|
|
623
760
|
const app = new Hono()
|
|
624
|
-
|
|
761
|
+
|
|
762
|
+
// Development with debug panel
|
|
763
|
+
const templates = new Environment({
|
|
764
|
+
templates: './views',
|
|
765
|
+
debug: process.env.NODE_ENV !== 'production',
|
|
766
|
+
debugOptions: { dark: true, position: 'bottom-right' }
|
|
767
|
+
})
|
|
625
768
|
|
|
626
769
|
app.get('/', async (c) => {
|
|
627
770
|
const html = await templates.render('index.html', {
|
|
@@ -725,4 +868,3 @@ See [LICENSE](./LICENSE) for details.
|
|
|
725
868
|
<p align="center">
|
|
726
869
|
Made with ❤️ for the Bun ecosystem
|
|
727
870
|
</p>
|
|
728
|
-
# binja
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* binja CLI - Template pre-compilation tool
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* binja compile <templates-dir> -o <output-dir>
|
|
7
|
+
* binja compile page.html -o dist/
|
|
8
|
+
* binja watch <templates-dir> -o <output-dir>
|
|
9
|
+
*
|
|
10
|
+
* Examples:
|
|
11
|
+
* binja compile ./templates -o ./dist/templates
|
|
12
|
+
* binja compile ./templates -o ./dist --minify
|
|
13
|
+
* binja compile ./views/home.html -o ./compiled --name renderHome
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;GAYG"}
|