binja 0.9.0 → 0.9.2
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 +231 -18
- package/dist/adapters/elysia.js +12 -10
- package/dist/adapters/hono.js +13 -11
- package/dist/ai/index.d.ts +24 -0
- package/dist/ai/index.js +115 -0
- package/dist/ai/lint.d.ts +26 -0
- package/dist/ai/prompt.d.ts +10 -0
- package/dist/ai/providers/anthropic.d.ts +6 -0
- package/dist/ai/providers/groq.d.ts +6 -0
- package/dist/ai/providers/index.d.ts +22 -0
- package/dist/ai/providers/ollama.d.ts +6 -0
- package/dist/ai/providers/openai.d.ts +6 -0
- package/dist/ai/types.d.ts +42 -0
- package/dist/cli.js +185 -30
- package/dist/index.js +4 -2
- package/dist/lexer/index.d.ts +10 -0
- package/package.json +22 -2
package/README.md
CHANGED
|
@@ -5,18 +5,25 @@
|
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
+
<a href="https://egeominotti.github.io/binja/"><strong>📚 Documentation</strong></a> •
|
|
8
9
|
<a href="#installation">Installation</a> •
|
|
9
10
|
<a href="#quick-start">Quick Start</a> •
|
|
10
|
-
<a href="#
|
|
11
|
-
<a href="#
|
|
12
|
-
<a href="#filters">Filters</a>
|
|
11
|
+
<a href="#framework-adapters">Hono/Elysia</a> •
|
|
12
|
+
<a href="#multi-engine-support">Multi-Engine</a> •
|
|
13
|
+
<a href="#filters-84-built-in">Filters</a>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<a href="https://www.npmjs.com/package/binja"><img src="https://img.shields.io/npm/v/binja?label=npm&color=10b981" alt="npm version"></a>
|
|
18
|
+
<a href="https://www.npmjs.com/package/binja"><img src="https://img.shields.io/npm/dm/binja?color=10b981" alt="npm downloads"></a>
|
|
19
|
+
<a href="https://github.com/egeominotti/binja/actions"><img src="https://github.com/egeominotti/binja/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
20
|
+
<a href="https://github.com/egeominotti/binja/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-BSD--3--Clause-blue.svg" alt="BSD-3-Clause License"></a>
|
|
13
21
|
</p>
|
|
14
22
|
|
|
15
23
|
<p align="center">
|
|
16
24
|
<img src="https://img.shields.io/badge/bun-%23000000.svg?style=for-the-badge&logo=bun&logoColor=white" alt="Bun" />
|
|
17
25
|
<img src="https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript" />
|
|
18
26
|
<img src="https://img.shields.io/badge/Django-092E20?style=for-the-badge&logo=django&logoColor=white" alt="Django Compatible" />
|
|
19
|
-
<img src="https://img.shields.io/badge/license-BSD--3--Clause-blue.svg?style=for-the-badge" alt="BSD-3-Clause License" />
|
|
20
27
|
</p>
|
|
21
28
|
|
|
22
29
|
---
|
|
@@ -27,7 +34,8 @@
|
|
|
27
34
|
|---------|-----------|------------------|
|
|
28
35
|
| **Runtime Performance** | ✅ 2-4x faster | ❌ |
|
|
29
36
|
| **AOT Compilation** | ✅ 160x faster | ❌ |
|
|
30
|
-
| **Multi-Engine** | ✅ Jinja2, Handlebars, Liquid | ❌ |
|
|
37
|
+
| **Multi-Engine** | ✅ Jinja2, Handlebars, Liquid, Twig | ❌ |
|
|
38
|
+
| **Framework Adapters** | ✅ Hono, Elysia | ❌ |
|
|
31
39
|
| Django DTL Compatible | ✅ 100% | ❌ Partial |
|
|
32
40
|
| Jinja2 Compatible | ✅ Full | ⚠️ Limited |
|
|
33
41
|
| Template Inheritance | ✅ | ⚠️ |
|
|
@@ -430,6 +438,7 @@ Binja supports multiple template engines through a unified API. All engines pars
|
|
|
430
438
|
| **Jinja2/DTL** | `{{ var }}` `{% if %}` | Default, Python/Django compatibility |
|
|
431
439
|
| **Handlebars** | `{{var}}` `{{#if}}` | JavaScript ecosystem, Ember.js |
|
|
432
440
|
| **Liquid** | `{{ var }}` `{% if %}` | Shopify, Jekyll, static sites |
|
|
441
|
+
| **Twig** | `{{ var }}` `{% if %}` | PHP/Symfony, Drupal, Craft CMS |
|
|
433
442
|
|
|
434
443
|
### Usage
|
|
435
444
|
|
|
@@ -437,6 +446,7 @@ Binja supports multiple template engines through a unified API. All engines pars
|
|
|
437
446
|
// Direct engine imports
|
|
438
447
|
import * as handlebars from 'binja/engines/handlebars'
|
|
439
448
|
import * as liquid from 'binja/engines/liquid'
|
|
449
|
+
import * as twig from 'binja/engines/twig'
|
|
440
450
|
|
|
441
451
|
// Handlebars
|
|
442
452
|
await handlebars.render('Hello {{name}}!', { name: 'World' })
|
|
@@ -447,6 +457,11 @@ await handlebars.render('{{{html}}}', { html: '<b>unescaped</b>' })
|
|
|
447
457
|
await liquid.render('Hello {{ name }}!', { name: 'World' })
|
|
448
458
|
await liquid.render('{% for item in items %}{{ item }}{% endfor %}', { items: ['a', 'b'] })
|
|
449
459
|
await liquid.render('{% assign x = "value" %}{{ x }}', {})
|
|
460
|
+
|
|
461
|
+
// Twig (Symfony)
|
|
462
|
+
await twig.render('Hello {{ name }}!', { name: 'World' })
|
|
463
|
+
await twig.render('{% for item in items %}{{ item }}{% endfor %}', { items: ['a', 'b'] })
|
|
464
|
+
await twig.render('{{ name|upper }}', { name: 'world' })
|
|
450
465
|
```
|
|
451
466
|
|
|
452
467
|
### MultiEngine API
|
|
@@ -459,26 +474,117 @@ const engine = new MultiEngine()
|
|
|
459
474
|
// Render with any engine
|
|
460
475
|
await engine.render('Hello {{name}}!', { name: 'World' }, 'handlebars')
|
|
461
476
|
await engine.render('Hello {{ name }}!', { name: 'World' }, 'liquid')
|
|
477
|
+
await engine.render('Hello {{ name }}!', { name: 'World' }, 'twig')
|
|
462
478
|
await engine.render('Hello {{ name }}!', { name: 'World' }, 'jinja2')
|
|
463
479
|
|
|
464
480
|
// Auto-detect from file extension
|
|
465
481
|
import { detectEngine } from 'binja/engines'
|
|
466
|
-
const eng = detectEngine('template.hbs')
|
|
467
|
-
const eng2 = detectEngine('page.liquid')
|
|
482
|
+
const eng = detectEngine('template.hbs') // Returns Handlebars engine
|
|
483
|
+
const eng2 = detectEngine('page.liquid') // Returns Liquid engine
|
|
484
|
+
const eng3 = detectEngine('page.twig') // Returns Twig engine
|
|
468
485
|
```
|
|
469
486
|
|
|
470
487
|
### Engine Feature Matrix
|
|
471
488
|
|
|
472
|
-
| Feature | Jinja2 | Handlebars | Liquid |
|
|
473
|
-
|
|
474
|
-
| Variables | `{{ x }}` | `{{x}}` | `{{ x }}` |
|
|
475
|
-
| Conditionals | `{% if %}` | `{{#if}}` | `{% if %}` |
|
|
476
|
-
| Loops | `{% for %}` | `{{#each}}` | `{% for %}` |
|
|
477
|
-
| Filters | `{{ x\|filter }}` | `{{ x }}` | `{{ x \| filter }}` |
|
|
478
|
-
| Raw output | `{% raw %}` | - | `{% raw %}` |
|
|
479
|
-
| Comments | `{# #}` | `{{! }}` | `{% comment %}` |
|
|
480
|
-
| Assignment | `{% set %}` | - | `{% assign %}` |
|
|
481
|
-
| Unescaped | `{{ x\|safe }}` | `{{{x}}}` | - |
|
|
489
|
+
| Feature | Jinja2 | Handlebars | Liquid | Twig |
|
|
490
|
+
|---------|--------|------------|--------|------|
|
|
491
|
+
| Variables | `{{ x }}` | `{{x}}` | `{{ x }}` | `{{ x }}` |
|
|
492
|
+
| Conditionals | `{% if %}` | `{{#if}}` | `{% if %}` | `{% if %}` |
|
|
493
|
+
| Loops | `{% for %}` | `{{#each}}` | `{% for %}` | `{% for %}` |
|
|
494
|
+
| Filters | `{{ x\|filter }}` | `{{ x }}` | `{{ x \| filter }}` | `{{ x\|filter }}` |
|
|
495
|
+
| Raw output | `{% raw %}` | - | `{% raw %}` | `{% raw %}` |
|
|
496
|
+
| Comments | `{# #}` | `{{! }}` | `{% comment %}` | `{# #}` |
|
|
497
|
+
| Assignment | `{% set %}` | - | `{% assign %}` | `{% set %}` |
|
|
498
|
+
| Unescaped | `{{ x\|safe }}` | `{{{x}}}` | - | `{{ x\|raw }}` |
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
|
|
502
|
+
## Framework Adapters
|
|
503
|
+
|
|
504
|
+
Binja provides first-class integration with Bun's most popular web frameworks.
|
|
505
|
+
|
|
506
|
+
### Hono
|
|
507
|
+
|
|
508
|
+
```typescript
|
|
509
|
+
import { Hono } from 'hono'
|
|
510
|
+
import { binja } from 'binja/hono'
|
|
511
|
+
|
|
512
|
+
const app = new Hono()
|
|
513
|
+
|
|
514
|
+
// Add binja middleware
|
|
515
|
+
app.use(binja({
|
|
516
|
+
root: './views', // Template directory
|
|
517
|
+
extension: '.html', // Default extension
|
|
518
|
+
engine: 'jinja2', // jinja2 | handlebars | liquid | twig
|
|
519
|
+
cache: true, // Cache compiled templates
|
|
520
|
+
globals: { siteName: 'My App' }, // Global context
|
|
521
|
+
layout: 'layouts/base', // Optional layout template
|
|
522
|
+
}))
|
|
523
|
+
|
|
524
|
+
// Render templates with c.render()
|
|
525
|
+
app.get('/', (c) => c.render('index', { title: 'Home' }))
|
|
526
|
+
app.get('/users/:id', async (c) => {
|
|
527
|
+
const user = await getUser(c.req.param('id'))
|
|
528
|
+
return c.render('users/profile', { user })
|
|
529
|
+
})
|
|
530
|
+
|
|
531
|
+
export default app
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
### Elysia
|
|
535
|
+
|
|
536
|
+
```typescript
|
|
537
|
+
import { Elysia } from 'elysia'
|
|
538
|
+
import { binja } from 'binja/elysia'
|
|
539
|
+
|
|
540
|
+
const app = new Elysia()
|
|
541
|
+
// Add binja plugin
|
|
542
|
+
.use(binja({
|
|
543
|
+
root: './views',
|
|
544
|
+
extension: '.html',
|
|
545
|
+
engine: 'jinja2',
|
|
546
|
+
cache: true,
|
|
547
|
+
globals: { siteName: 'My App' },
|
|
548
|
+
layout: 'layouts/base',
|
|
549
|
+
}))
|
|
550
|
+
// Render templates with render()
|
|
551
|
+
.get('/', ({ render }) => render('index', { title: 'Home' }))
|
|
552
|
+
.get('/users/:id', async ({ render, params }) => {
|
|
553
|
+
const user = await getUser(params.id)
|
|
554
|
+
return render('users/profile', { user })
|
|
555
|
+
})
|
|
556
|
+
.listen(3000)
|
|
557
|
+
|
|
558
|
+
console.log('Server running at http://localhost:3000')
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### Adapter Options
|
|
562
|
+
|
|
563
|
+
| Option | Type | Default | Description |
|
|
564
|
+
|--------|------|---------|-------------|
|
|
565
|
+
| `root` | `string` | `./views` | Template directory |
|
|
566
|
+
| `extension` | `string` | `.html` | Default file extension |
|
|
567
|
+
| `engine` | `string` | `jinja2` | Template engine (`jinja2`, `handlebars`, `liquid`, `twig`) |
|
|
568
|
+
| `cache` | `boolean` | `true` (prod) | Cache compiled templates |
|
|
569
|
+
| `debug` | `boolean` | `false` | Show error details |
|
|
570
|
+
| `globals` | `object` | `{}` | Global context variables |
|
|
571
|
+
| `layout` | `string` | - | Layout template path |
|
|
572
|
+
| `contentVar` | `string` | `content` | Content variable name in layout |
|
|
573
|
+
|
|
574
|
+
### Cache Management
|
|
575
|
+
|
|
576
|
+
```typescript
|
|
577
|
+
import { clearCache, getCacheStats } from 'binja/hono'
|
|
578
|
+
// or
|
|
579
|
+
import { clearCache, getCacheStats } from 'binja/elysia'
|
|
580
|
+
|
|
581
|
+
// Clear all cached templates
|
|
582
|
+
clearCache()
|
|
583
|
+
|
|
584
|
+
// Get cache statistics
|
|
585
|
+
const stats = getCacheStats()
|
|
586
|
+
console.log(stats) // { size: 10, keys: ['jinja2:./views/index.html', ...] }
|
|
587
|
+
```
|
|
482
588
|
|
|
483
589
|
---
|
|
484
590
|
|
|
@@ -609,7 +715,7 @@ debugOptions: {
|
|
|
609
715
|
|
|
610
716
|
## CLI Tool
|
|
611
717
|
|
|
612
|
-
Binja includes a CLI for template pre-compilation:
|
|
718
|
+
Binja includes a CLI for template pre-compilation and linting:
|
|
613
719
|
|
|
614
720
|
```bash
|
|
615
721
|
# Compile all templates to JavaScript
|
|
@@ -620,6 +726,15 @@ binja check ./templates
|
|
|
620
726
|
|
|
621
727
|
# Watch mode for development
|
|
622
728
|
binja watch ./templates -o ./dist
|
|
729
|
+
|
|
730
|
+
# Lint templates (syntax check)
|
|
731
|
+
binja lint ./templates
|
|
732
|
+
|
|
733
|
+
# Lint with AI analysis (requires API key)
|
|
734
|
+
binja lint ./templates --ai
|
|
735
|
+
|
|
736
|
+
# Lint with specific AI provider
|
|
737
|
+
binja lint ./templates --ai=ollama
|
|
623
738
|
```
|
|
624
739
|
|
|
625
740
|
### Pre-compiled Templates
|
|
@@ -633,6 +748,104 @@ const html = render({ title: 'Home', items: [...] })
|
|
|
633
748
|
|
|
634
749
|
---
|
|
635
750
|
|
|
751
|
+
## AI-Powered Linting (Optional)
|
|
752
|
+
|
|
753
|
+
Binja includes an optional AI-powered linting module that detects security issues, performance problems, accessibility concerns, and best practice violations.
|
|
754
|
+
|
|
755
|
+
### Installation
|
|
756
|
+
|
|
757
|
+
The AI module is opt-in. Install the SDK for your preferred provider:
|
|
758
|
+
|
|
759
|
+
```bash
|
|
760
|
+
# For Claude (Anthropic)
|
|
761
|
+
bun add @anthropic-ai/sdk
|
|
762
|
+
|
|
763
|
+
# For OpenAI
|
|
764
|
+
bun add openai
|
|
765
|
+
|
|
766
|
+
# For Ollama (local) - no package needed
|
|
767
|
+
# For Groq - no package needed
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### Configuration
|
|
771
|
+
|
|
772
|
+
Set the API key for your provider:
|
|
773
|
+
|
|
774
|
+
```bash
|
|
775
|
+
# Anthropic
|
|
776
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
777
|
+
|
|
778
|
+
# OpenAI
|
|
779
|
+
export OPENAI_API_KEY=sk-...
|
|
780
|
+
|
|
781
|
+
# Groq (free tier available)
|
|
782
|
+
export GROQ_API_KEY=gsk_...
|
|
783
|
+
|
|
784
|
+
# Ollama - no key needed, just run: ollama serve
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
### Usage
|
|
788
|
+
|
|
789
|
+
#### CLI
|
|
790
|
+
|
|
791
|
+
```bash
|
|
792
|
+
# Lint with AI (auto-detect provider)
|
|
793
|
+
binja lint ./templates --ai
|
|
794
|
+
|
|
795
|
+
# Use specific provider
|
|
796
|
+
binja lint ./templates --ai=anthropic
|
|
797
|
+
binja lint ./templates --ai=openai
|
|
798
|
+
binja lint ./templates --ai=ollama
|
|
799
|
+
binja lint ./templates --ai=groq
|
|
800
|
+
|
|
801
|
+
# JSON output for CI/CD
|
|
802
|
+
binja lint ./templates --ai --format=json
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
#### Programmatic
|
|
806
|
+
|
|
807
|
+
```typescript
|
|
808
|
+
import { lint } from 'binja/ai'
|
|
809
|
+
|
|
810
|
+
// Auto-detect provider from environment
|
|
811
|
+
const result = await lint(template)
|
|
812
|
+
|
|
813
|
+
// Specify provider and API key directly
|
|
814
|
+
const result = await lint(template, {
|
|
815
|
+
provider: 'anthropic',
|
|
816
|
+
apiKey: 'sk-ant-...',
|
|
817
|
+
model: 'claude-sonnet-4-20250514'
|
|
818
|
+
})
|
|
819
|
+
|
|
820
|
+
// Check results
|
|
821
|
+
console.log(result.errors) // Syntax errors
|
|
822
|
+
console.log(result.warnings) // Security, performance issues
|
|
823
|
+
console.log(result.suggestions) // Best practice recommendations
|
|
824
|
+
console.log(result.provider) // Which AI was used
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
### What It Detects
|
|
828
|
+
|
|
829
|
+
| Category | Examples |
|
|
830
|
+
|----------|----------|
|
|
831
|
+
| **Security** | XSS vulnerabilities, `\|safe` on user input, sensitive data exposure |
|
|
832
|
+
| **Performance** | Heavy filters in loops, repeated calculations |
|
|
833
|
+
| **Accessibility** | Missing alt text, forms without labels |
|
|
834
|
+
| **Best Practices** | `{% for %}` without `{% empty %}`, deep nesting |
|
|
835
|
+
|
|
836
|
+
### Provider Comparison
|
|
837
|
+
|
|
838
|
+
| Provider | API Key | Speed | Cost |
|
|
839
|
+
|----------|---------|-------|------|
|
|
840
|
+
| **Anthropic** | `ANTHROPIC_API_KEY` | Fast | Paid |
|
|
841
|
+
| **OpenAI** | `OPENAI_API_KEY` | Fast | Paid |
|
|
842
|
+
| **Groq** | `GROQ_API_KEY` | Very Fast | Free tier |
|
|
843
|
+
| **Ollama** | None (local) | Varies | Free |
|
|
844
|
+
|
|
845
|
+
Auto-detect priority: Anthropic → OpenAI → Groq → Ollama
|
|
846
|
+
|
|
847
|
+
---
|
|
848
|
+
|
|
636
849
|
## Raw/Verbatim Tag
|
|
637
850
|
|
|
638
851
|
Output template syntax without processing:
|