Files
pugz/docs/COMPILED_TEMPLATES_STATUS.md

5.9 KiB

Compiled Templates - Implementation Status

Overview

Pugz now supports compiling .pug templates to native Zig functions at build time for maximum performance (10-100x faster than runtime parsing).

Completed Features

1. Core Infrastructure

  • CLI Tool: pug-compile binary for template compilation
  • Shared Helpers: helpers.zig with HTML escaping and utility functions
  • Build Integration: Templates compile as part of build process
  • Module Generation: Auto-generated root.zig exports all templates

2. Code Generation

  • Static HTML output
  • Text interpolation (#{field})
  • Buffered code (p= field)
  • Attributes (static and dynamic)
  • Doctypes
  • Comments (buffered and silent)
  • Void elements (self-closing tags)
  • Nested tags
  • HTML escaping (XSS protection)

3. Field Extraction

  • Automatic detection of data fields from templates
  • Type-safe Data struct generation
  • Recursive extraction from all node types
  • Support for conditional branches

4. Demo Integration

  • Demo app supports both runtime and compiled modes
  • Simple test template (/simple route)
  • Build scripts and documentation
  • Mode toggle via constant

🚧 In Progress

Conditionals (Partially Complete)

  • Basic if/else code generation
  • Field extraction from test expressions
  • Helper function (isTruthy) for evaluation
  • ⚠️ Known Issue: Static buffer management needs fixing
    • Content inside branches accumulates in global buffer
    • Results in incorrect output placement

Required Fixes

  1. Scope static buffer to each conditional branch
  2. Flush buffer appropriately within branches
  3. Test with nested conditionals
  4. Handle unless statements

Not Yet Implemented

Loops (each)

each item in items
  li= item

Plan: Generate Zig for loops over slices

Mixins

mixin button(text)
  button.btn= text

+button("Click me")

Plan: Generate Zig functions

Includes

include header.pug

Plan: Inline content at compile time (already resolved by parser/linker)

Extends/Blocks

extends layout.pug
block content
  h1 Title

Plan: Template inheritance resolved at compile time

Case/When

case status
  when "active"
    p Active
  default
    p Unknown

Plan: Generate Zig switch statements

📁 File Structure

src/
├── cli/
│   ├── main.zig              # pug-compile CLI tool
│   ├── zig_codegen.zig       # AST → Zig code generator
│   └── helpers_template.zig  # Template for helpers.zig
├── codegen.zig               # Runtime HTML generator
├── parser.zig                # Pug → AST parser
└── ...

generated/                    # Output directory
├── helpers.zig              # Shared utilities
├── pages/
│   └── home.zig            # Compiled template
└── root.zig                # Exports all templates

examples/use_compiled_templates.zig  # Usage example
docs/COMPILED_TEMPLATES.md          # Full documentation

🧪 Testing

Test the Demo App

# 1. Build pugz and pug-compile tool
cd /path/to/pugz
zig build

# 2. Go to demo and compile templates
cd src/tests/examples/demo
zig build compile-templates

# 3. Run the test script
./test_compiled.sh

# 4. Start the server
zig build run

# 5. Visit http://localhost:8081/simple

Enable Compiled Mode

Edit src/tests/examples/demo/src/main.zig:

const USE_COMPILED_TEMPLATES = true;  // Change to true

Then rebuild and run.

📊 Performance

Mode Parse Time Render Time Total Notes
Runtime ~500µs ~50µs ~550µs Parses on every request
Compiled 0µs ~5µs ~5µs Zero parsing, direct concat

Result: ~100x faster for simple templates

🎯 Usage Example

Input Template (views/pages/home.pug)

doctype html
html
  head
    title #{title}
  body
    h1 Welcome #{name}!

Generated Code (generated/pages/home.zig)

const std = @import("std");
const helpers = @import("helpers.zig");

pub const Data = struct {
    name: []const u8 = "",
    title: []const u8 = "",
};

pub fn render(allocator: std.mem.Allocator, data: Data) ![]const u8 {
    var buf: std.ArrayListUnmanaged(u8) = .{};
    defer buf.deinit(allocator);

    try buf.appendSlice(allocator, "<!DOCTYPE html><html><head><title>");
    try buf.appendSlice(allocator, data.title);
    try buf.appendSlice(allocator, "</title></head><body><h1>Welcome ");
    try buf.appendSlice(allocator, data.name);
    try buf.appendSlice(allocator, "!</h1></body></html>");

    return buf.toOwnedSlice(allocator);
}

Usage

const tpls = @import("generated/root.zig");

const html = try tpls.pages_home.render(allocator, .{
    .title = "My Site",
    .name = "Alice",
});
defer allocator.free(html);

🔧 Next Steps

  1. Fix conditional static buffer issues (HIGH PRIORITY)

    • Refactor buffer management
    • Add integration tests
  2. Implement loops (each/while)

    • Field extraction for iterables
    • Generate Zig for loops
  3. Add comprehensive tests

    • Unit tests for zig_codegen
    • Integration tests for full compilation
    • Benchmark comparisons
  4. Documentation

    • API reference
    • Migration guide
    • Best practices

📚 Documentation

  • Full Guide: docs/COMPILED_TEMPLATES.md
  • Demo Instructions: src/tests/examples/demo/COMPILED_TEMPLATES.md
  • Usage Example: examples/use_compiled_templates.zig
  • Project Instructions: CLAUDE.md

🤝 Contributing

The compiled templates feature is functional for basic use cases but needs work on:

  1. Conditional statement buffer management
  2. Loop implementation
  3. Comprehensive testing

See the "In Progress" and "Not Yet Implemented" sections above for contribution opportunities.