- 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.
- Add scoped logger (pugz/runtime) for better log filtering
- Add warnings when mixin not found
- Add warnings for mixin attribute failures
- Add warnings for mixin directory/file lookup failures
- Add warnings for mixin parse/tokenize failures
- Use 'action first, then reason' pattern in all log messages
The loadMixinFromDir function used openDirAbsolute which requires an
absolute path, but ViewEngine passes a relative path for mixins_dir.
This caused a panic when a mixin call couldn't find the mixin in the
current template.
Fix: Check if mixins_dir is absolute and use the appropriate method:
- std.fs.openDirAbsolute() for absolute paths
- std.fs.cwd().openDir() for relative paths