AI Linting
binja includes an optional AI-powered linting module that analyzes templates for security vulnerabilities, performance issues, accessibility problems, and best practice violations.
Installation
Install the SDK for your preferred AI provider:
# For Claude (Anthropic)bun add @anthropic-ai/sdk
# For OpenAIbun add openai
# For Ollama (local) or Groq - no package neededConfiguration
Set the API key for your provider:
# Anthropicexport ANTHROPIC_API_KEY=sk-ant-...
# OpenAIexport OPENAI_API_KEY=sk-...
# Groq (free tier available)export GROQ_API_KEY=gsk_...
# Ollama - no key needed, just run: ollama serveCLI Usage
# Syntax check onlybinja lint ./templates
# With AI analysis (auto-detect provider)binja lint ./templates --ai
# With specific providerbinja lint ./templates --ai=anthropicbinja lint ./templates --ai=openaibinja lint ./templates --ai=ollamabinja lint ./templates --ai=groq
# JSON output for CI/CDbinja lint ./templates --ai --format=jsonProgrammatic Usage
import { lint } from 'binja/ai'
// Auto-detect provider from environmentconst result = await lint(templateSource)
// Specify provider and optionsconst result = await lint(templateSource, { provider: 'anthropic', apiKey: 'sk-ant-...', model: 'claude-sonnet-4-20250514'})
// Check resultsconsole.log(result.errors) // Syntax errorsconsole.log(result.warnings) // Security, performance issuesconsole.log(result.suggestions) // Best practice recommendationsconsole.log(result.provider) // Which AI was usedWhat It Detects
Security
| Issue | Example |
|---|---|
| XSS vulnerability | {{ user_input|safe }} on untrusted data |
| Sensitive data exposure | {{ user.password }} in template |
| Unsafe URL handling | href="{{ url }}" without encoding |
Performance
| Issue | Example |
|---|---|
| Heavy filters in loops | {% for x in items %}{{ x|sort }}{% endfor %} |
| Repeated calculations | Same filter applied multiple times |
| Large data rendering | Rendering huge lists without pagination |
Accessibility
| Issue | Example |
|---|---|
| Missing alt text | <img src="{{ url }}"> |
| Forms without labels | <input> without associated <label> |
| Missing ARIA attributes | Interactive elements without roles |
Best Practices
| Issue | Example |
|---|---|
| Missing empty block | {% for %} without {% empty %} |
| Deep nesting | More than 3-4 levels of nesting |
| Unused variables | Variables in context but never used |
| Complex conditionals | Long chains of {% elif %} |
Provider Comparison
| Provider | API Key | Speed | Cost | Best For |
|---|---|---|---|---|
| Anthropic | ANTHROPIC_API_KEY | Fast | Paid | Best quality |
| OpenAI | OPENAI_API_KEY | Fast | Paid | Good quality |
| Groq | GROQ_API_KEY | Very Fast | Free tier | Quick checks |
| Ollama | None (local) | Varies | Free | Privacy, offline |
Auto-detect priority: Anthropic → OpenAI → Groq → Ollama
Response Format
interface LintResult { valid: boolean errors: Issue[] warnings: Issue[] suggestions: Issue[] provider: string}
interface Issue { line: number column?: number type: 'security' | 'performance' | 'accessibility' | 'best-practice' severity: 'error' | 'warning' | 'info' message: string fix?: string}CI/CD Integration
GitHub Actions
name: Template Lint
on: [push, pull_request]
jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: oven-sh/setup-bun@v1
- name: Install dependencies run: bun install
- name: Lint templates env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} run: bunx binja lint ./templates --ai --format json > lint-results.json
- name: Check for errors run: | if jq -e '.errors | length > 0' lint-results.json; then echo "Template errors found!" exit 1 fiLocal Development with Ollama
For free, private, offline linting:
# Install Ollamacurl -fsSL https://ollama.com/install.sh | sh
# Start Ollamaollama serve
# Pull a modelollama pull llama2
# Use with binjabinja lint ./templates --ai=ollamaCustomization
const result = await lint(template, { provider: 'anthropic', // Focus on specific categories checks: ['security', 'accessibility'], // Ignore specific rules ignore: ['missing-empty-block'], // Custom severity thresholds minSeverity: 'warning',})