- Lexer now emits start_pug_interpolation/end_pug_interpolation tokens
- Sub-lexer parses content inside #[...] as full Pug syntax
- Supports tags with attributes, classes, IDs, and buffered code
- Added tag_interp_test.zig with 8 test cases
- Memory management: sub-lexer buffers tracked and freed properly
Examples now working:
p Dear #[strong= "asdasd"] -> <p>Dear <strong>asdasd</strong></p>
p This is #[em emphasized] text -> <p>This is <em>emphasized</em> text</p>
p Click #[a(href='/') here] -> <p>Click <a href="/">here</a></p>
Mixin definitions from included files were being rendered as content.
Now generateNode explicitly skips mixin definitions (node.call=false)
while still processing expanded mixin calls.
Bump version to 0.3.12
- Flush static buffer at end of each conditional branch (if/else/else-if)
to ensure content is rendered inside the correct blocks
- Add helpers_path parameter to zig_codegen.generate() for correct
relative imports in nested directories (e.g., '../helpers.zig')
- Fix build.zig to use correct CLI path (src/tpl_compiler/main.zig)
- Export zig_codegen from root.zig for CLI module usage
Bump version to 0.3.11
- mixin.zig: expandNode and expandNodeWithArgs now recurse into
node.consequent and node.alternate for Conditional nodes
- view_engine.zig: process includes and collect mixins from child
template before extracting blocks in processExtends
This fixes mixin calls inside if/else blocks not being rendered
in compiled templates.
- Renamed std.ArrayListUnmanaged to std.ArrayList across all source files
- Updated CLAUDE.md with Zig version standards rule
- Removed debug print from mixin test
- No API changes (allocator still passed to methods)
- Move isHtmlEntity to runtime.zig (was duplicated in codegen.zig and template.zig)
- Move appendTextEscaped to runtime.zig (was in template.zig)
- Add isXhtmlDoctype helper to runtime.zig for doctype detection
- Update template.zig to use codegen.void_elements instead of local isSelfClosing
- Update codegen.zig and zig_codegen.zig to use shared functions
- Update CLAUDE.md with shared utilities documentation
This establishes runtime.zig as the single source of truth for shared
utilities across all three rendering modes (codegen, template, zig_codegen).
- parser.zig: collect and merge all class values (shorthand and parenthesized) into single attribute
- Filter out empty, null, undefined class values during parsing
- Reverted redundant merging logic from codegen.zig, template.zig, zig_codegen.zig
- Added documentation about shared AST consumers relationship
- zig_codegen.zig: collect and merge class values in both static and dynamic attribute paths
- Completes the fix for multiple classes (.a.b -> class="a b") across all rendering modes
- codegen.zig: collect class values and output as single merged attribute
- template.zig: respect quoted flag to prevent data lookup for static class values
- Added tests for multiple class merging scenarios
- Rename parseWithIncludes to parseTemplate for clarity
- Add resolveRelativePath to handle ../path and ./path relative to current file
- Paths without ./ or ../ prefix are relative to views_dir root (Pug convention)
- Fix duplicate include processing when extends loads parent with includes
- Add tests for relative path resolution
- All paths still validated against views_dir root (security unchanged)
- Add TypeHint node type in parser for //- @TypeOf(field): type syntax
- Support scalar types (f32, i32, bool, etc.) and array/struct types
- Use helpers.appendValue() for non-string typed fields
- Filter out loop variable references from Data struct fields
- Preserve @TypeOf comments during comment stripping
Example usage:
//- @TypeOf(subtotal): f32
span $#{subtotal}
//- @TypeOf(items): []{name: []const u8, price: f32}
each item in items
h3 #{item.name}
- Add two benchmark modes: cached AST (render only) and no cache (parse+render)
- Shows parse overhead is 69.2% of total time (331ms out of 478ms)
- Fix use-after-free in ViewEngine.processIncludes by transferring child ownership
- Fix memory leaks by using ArenaAllocator pattern in test_includes
- Update test expectations to match actual template content (mixins, not partials)
- All tests pass
Benchmark results (2000 iterations):
- Cached (render only): 147.3ms
- No cache (parse+render): 478.3ms
- Parse overhead: 331.0ms (3.2x slower without caching)
- Wrap lexer -> parser -> codegen pipeline in ArenaAllocator
- All temporary allocations freed in one shot after HTML generation
- Applied to pug.compile() and template.renderWithData()
- Reduces allocator overhead and improves cache locality
- 22% faster than Pug.js (149.3ms vs 182.9ms on benchmark)
- All tests pass
- Add std.log.scoped(.pugz) to template.zig and view_engine.zig
- Log detailed error info (code, line, column, message) when parsing fails
- Log template path context in ViewEngine on parse errors
- Remove debug print from lexer, use proper scoped logging instead
- Move benchmarks, docs, examples, playground, tests out of src/ to project root
- Update build.zig and documentation paths accordingly
- Bump version to 0.3.1
- Add includes/some_partial.pug partial in demo views
- Add pages/include-demo.pug page using include directive
- Add /include-demo route in demo server
- Add link to include demo in about page
- Fix test_includes.zig path in build.zig
- Add test_views for include testing
- ViewEngine now supports extends and named blocks
- Each route gets exclusive cached AST (no shared parent layouts)
- Fix iteration over struct arrays in each loops
- Add demo app with full e-commerce layout using extends
- Serve static files from public folder
- Bump version to 0.3.0
- Added comprehensive module-level documentation explaining architecture
- Added doc comments to all public and key internal functions
- Improved inline comments focusing on 'why' not 'what'
- Updated CLAUDE.md with code comments rule
- Bump version to 0.2.0
- Create typed attributes struct for each mixin call with optional fields (class, id, style)
- Use unique variable names (mixin_attrs_N) to avoid shadowing in nested mixin calls
- Track current attributes variable for buildAccessor to resolve attributes.class correctly
- Only suppress unused variable warning when attributes aren't actually accessed
- HTML-escape double quotes as " in backtick template literals for valid attribute values
- Merge shorthand classes (.alert) with class attribute values instead of emitting duplicates
- Handle string concatenation expressions in class attributes (e.g., class="btn btn-" + type)
When a mixin calls another mixin passing a variable with the same name
as the parameter (e.g., +alert(message) where alert has param message),
skip generating redundant const declaration since the variable is already
in scope.
Also adds missing alert.pug mixin for demo project.