diff --git a/build.zig.zon b/build.zig.zon index af505d1..7b23897 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = .pugz, - .version = "0.3.4", + .version = "0.3.5", .fingerprint = 0x822db0790e17621d, // Changing this has security and trust implications. .minimum_zig_version = "0.15.2", .dependencies = .{}, diff --git a/src/tpl_compiler/zig_codegen.zig b/src/tpl_compiler/zig_codegen.zig index d2ab183..67c6f68 100644 --- a/src/tpl_compiler/zig_codegen.zig +++ b/src/tpl_compiler/zig_codegen.zig @@ -224,28 +224,69 @@ pub const Codegen = struct { if (!has_dynamic_attrs) { // All static attributes - include in buffer + // Collect class values to merge into single attribute + var class_values = std.ArrayListUnmanaged([]const u8){}; + defer class_values.deinit(self.allocator); + for (tag.attrs.items) |attr| { - try self.addStatic(" "); - try self.addStatic(attr.name); - if (attr.val) |val| { - try self.addStatic("=\""); - try self.addStatic(val); - try self.addStatic("\""); + if (std.mem.eql(u8, attr.name, "class")) { + // Collect class values for merging + if (attr.val) |val| { + if (val.len > 0) { + try class_values.append(self.allocator, val); + } + } } else { - // Boolean attribute - if (!self.terse) { + try self.addStatic(" "); + try self.addStatic(attr.name); + if (attr.val) |val| { try self.addStatic("=\""); - try self.addStatic(attr.name); + try self.addStatic(val); try self.addStatic("\""); + } else { + // Boolean attribute + if (!self.terse) { + try self.addStatic("=\""); + try self.addStatic(attr.name); + try self.addStatic("\""); + } } } } + + // Output merged class attribute + if (class_values.items.len > 0) { + try self.addStatic(" class=\""); + for (class_values.items, 0..) |class_val, i| { + if (i > 0) { + try self.addStatic(" "); + } + try self.addStatic(class_val); + } + try self.addStatic("\""); + } + try self.addStatic(">"); } else { // Flush static content before dynamic attributes (this closes any open string) try self.flushStaticBuffer(); + // Collect static class values for merging + var static_class_values = std.ArrayListUnmanaged([]const u8){}; + defer static_class_values.deinit(self.allocator); + + // First pass: output non-class attributes for (tag.attrs.items) |attr| { + // Skip class attributes - handle them separately + if (std.mem.eql(u8, attr.name, "class")) { + if (attr.val) |val| { + if (val.len > 0) { + try static_class_values.append(self.allocator, val); + } + } + continue; + } + if (attr.val) |val| { // Quoted values are always static, unquoted can be field references if (!attr.quoted and self.isDataFieldReference(val)) { @@ -303,6 +344,19 @@ pub const Codegen = struct { } } + // Output merged class attribute + if (static_class_values.items.len > 0) { + try self.writeIndent(); + try self.write("try buf.appendSlice(allocator, \" class=\\\""); + for (static_class_values.items, 0..) |class_val, i| { + if (i > 0) { + try self.write(" "); + } + try self.writeEscaped(class_val); + } + try self.writeLine("\\\"\");"); + } + try self.writeIndent(); try self.writeLine("try buf.appendSlice(allocator, \">\");"); }