AOT Compilation
AOT (Ahead-of-Time) compilation converts templates into optimized JavaScript functions at build/startup time, providing 160x faster rendering than runtime mode.
Basic Usage
import { compile } from 'binja'
// Compile once at startupconst renderUser = compile('<h1>{{ name|upper }}</h1>')
// Use many times (sync, extremely fast!)const html = renderUser({ name: 'john' })// Output: <h1>JOHN</h1>Performance Comparison
| Mode | Speed | Use Case |
|---|---|---|
Runtime (render()) | 371K ops/s | Development |
AOT (compile()) | 14.3M ops/s | Production |
39x faster than runtime mode, 160x faster than Nunjucks.
Production Pattern
import { compile } from 'binja'
// Pre-compile all templates at server startupconst templates = { home: compile(await Bun.file('./views/home.html').text()), user: compile(await Bun.file('./views/user.html').text()), product: compile(await Bun.file('./views/product.html').text()),}
// Rendering is now synchronous and extremely fastapp.get('/', () => templates.home({ title: 'Welcome' }))app.get('/user/:id', ({ params }) => templates.user({ id: params.id }))app.get('/product/:id', ({ params }) => templates.product({ id: params.id }))Supported Features
AOT compilation supports:
- Variables and expressions
- Filters (all 84 built-in filters)
- Conditionals (
{% if %},{% elif %},{% else %}) - Loops (
{% for %},{% empty %}) - Set/With statements
- Comments
- Raw/Verbatim blocks
Not Supported in AOT
The following features require runtime mode:
{% extends %}- UsecompileWithInheritance()or Environment{% include %}- UsecompileWithInheritance()or Environment- Dynamic template loading
For Template Inheritance
Use Environment with caching for templates that use inheritance:
import { Environment } from 'binja'
const env = new Environment({ templates: './views', cache: true, // Enable caching})
// Pre-warm cache at startupawait env.loadTemplate('base.html')await env.loadTemplate('home.html')
// Rendering uses cached ASTconst html = await env.render('home.html', { title: 'Welcome' })Generate JavaScript Code
For build tools, generate standalone JavaScript code:
import { compileToCode } from 'binja'
const code = compileToCode('<h1>{{ title }}</h1>', { functionName: 'renderHeader'})
// Save to fileawait Bun.write('./compiled/header.js', code)Generated code:
export function renderHeader(ctx) { let __out = ''; __out += '<h1>'; __out += escape(ctx.title); __out += '</h1>'; return __out;}CLI Compilation
Compile templates from command line:
# Compile all templates to JavaScriptbinja compile ./templates -o ./dist
# Watch mode for developmentbinja watch ./templates -o ./distOptions
const template = compile(source, { // Disable autoescaping (not recommended) autoescape: false,
// Custom filters filters: { currency: (v) => `$${v.toFixed(2)}` },})When to Use AOT
Use AOT when:
- Templates are static (don’t use
{% extends %}or{% include %}) - Maximum performance is critical
- Templates can be compiled at build/startup time
Use Runtime when:
- Templates use inheritance (
{% extends %}) - Templates use includes (
{% include %}) - Templates are loaded dynamically
- During development
Hybrid Approach
Combine both for optimal performance:
import { compile, Environment } from 'binja'
// AOT for static templatesconst staticTemplates = { header: compile(await Bun.file('./partials/header.html').text()), footer: compile(await Bun.file('./partials/footer.html').text()),}
// Environment for templates with inheritanceconst env = new Environment({ templates: './views', cache: true,})
// Use bothapp.get('/', async (c) => { const header = staticTemplates.header({ nav: [...] }) const content = await env.render('pages/home.html', { ... }) const footer = staticTemplates.footer({ year: 2024 }) return c.html(header + content + footer)})