feat: add template inheritance (extends/block) support

- 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
This commit is contained in:
2026-01-25 15:23:57 +05:30
parent 776f8a68f5
commit 1b2da224be
52 changed files with 2962 additions and 728 deletions

View File

@@ -52,7 +52,7 @@ test "Link with class and href (space separated)" {
try expectOutput(
"a(class='button' href='//google.com') Google",
.{},
"<a class=\"button\" href=\"//google.com\">Google</a>",
"<a href=\"//google.com\" class=\"button\">Google</a>",
);
}
@@ -60,7 +60,7 @@ test "Link with class and href (comma separated)" {
try expectOutput(
"a(class='button', href='//google.com') Google",
.{},
"<a class=\"button\" href=\"//google.com\">Google</a>",
"<a href=\"//google.com\" class=\"button\">Google</a>",
);
}

View File

@@ -0,0 +1,24 @@
const std = @import("std");
const pugz = @import("pugz");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var engine = pugz.ViewEngine.init(allocator, .{
.views_dir = "test_views",
}) catch |err| {
std.debug.print("Init Error: {}\n", .{err});
return err;
};
defer engine.deinit();
const html = engine.render(allocator, "home", .{}) catch |err| {
std.debug.print("Error: {}\n", .{err});
return err;
};
defer allocator.free(html);
std.debug.print("=== Rendered HTML ===\n{s}\n=== End ===\n", .{html});
}

View File

@@ -0,0 +1,8 @@
include mixins/_buttons.pug
include mixins/_cards.pug
doctype html
html
body
+primary-button("Click me")
+card("Title", "content here")

View File

@@ -0,0 +1,2 @@
mixin btn(text)
button.btn= text

View File

@@ -0,0 +1,4 @@
mixin card(title, content)
.card
.card-header= title
.card-body= content