Skip to content

Commit

Permalink
Zmpl v2
Browse files Browse the repository at this point in the history
Update to Zmpl v2, update demo app to be compatible with v2 syntax. Add
deprecation warning for v1 (v1 is default for now - will force v2 soon).
  • Loading branch information
bobf committed Apr 7, 2024
1 parent 8096962 commit b3dc38c
Show file tree
Hide file tree
Showing 17 changed files with 87 additions and 50 deletions.
59 changes: 40 additions & 19 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,20 @@ pub const StaticRequest = @import("src/jetzig.zig").StaticRequest;
pub const http = @import("src/jetzig/http.zig");
pub const data = @import("src/jetzig/data.zig");
pub const views = @import("src/jetzig/views.zig");
const zmpl_build = @import("zmpl");

pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const template_path = b.option([]const u8, "zmpl_templates_path", "Path to templates") orelse "src/app/views/";
const template_path_option = b.option([]const u8, "zmpl_templates_path", "Path to templates") orelse
"src/app/views/";
const template_path: []const u8 = if (std.fs.path.isAbsolute(template_path_option))
try b.allocator.dupe(u8, template_path_option)
else
std.fs.cwd().realpathAlloc(b.allocator, template_path_option) catch |err| switch (err) {
error.FileNotFound => "",
else => return err,
};

const lib = b.addStaticLibrary(.{
.name = "jetzig",
Expand All @@ -28,37 +37,48 @@ pub fn build(b: *std.Build) !void {
jetzig_module.addImport("mime_types", mime_module);
lib.root_module.addImport("jetzig", jetzig_module);

const zmpl_version = b.option(
enum { v1, v2 },
"zmpl_version",
"Zmpl syntax version (default: v1)",
) orelse .v2;

if (zmpl_version == .v1) {
std.debug.print(
\\[WARN] Zmpl v1 is deprecated and will soon be removed.
\\ Update to v2 by modifying `jetzigInit` in your `build.zig`:
\\
\\ try jetzig.jetzigInit(b, exe, .{{ .zmpl_version = .v2 }});
\\
\\ See https://jetzig.dev/documentation.html for information on migrating to Zmpl v2.
\\
, .{});
}

const zmpl_dep = b.dependency(
"zmpl",
.{
.target = target,
.optimize = optimize,
.zmpl_templates_path = template_path,
.zmpl_auto_build = false,
.zmpl_version = zmpl_version,
.zmpl_constants = try zmpl_build.addTemplateConstants(b, struct {
jetzig_view: []const u8,
jetzig_action: []const u8,
}),
},
);

const zmpl_module = zmpl_dep.module("zmpl");

// This is the way to make it look nice in the zig build script
// If we would do it the other way around, we would have to do
// b.dependency("jetzig",.{}).builder.dependency("zmpl",.{}).module("zmpl");
b.modules.put("zmpl", zmpl_dep.module("zmpl")) catch @panic("Out of memory");

const zmd_dep = b.dependency("zmd", .{ .target = target, .optimize = optimize });

const ZmplBuild = @import("zmpl").ZmplBuild;
const ZmplTemplate = @import("zmpl").Template;
var zmpl_build = ZmplBuild.init(b, lib, template_path);
const TemplateConstants = struct {
jetzig_view: []const u8,
jetzig_action: []const u8,
};
const ZmplOptions = struct {
pub const template_constants = TemplateConstants;
};
const manifest_module = try zmpl_build.compile(ZmplTemplate, ZmplOptions);
zmpl_module.addImport("zmpl.manifest", manifest_module);

lib.root_module.addImport("zmpl", zmpl_module);
jetzig_module.addImport("zmpl", zmpl_module);
jetzig_module.addImport("args", zig_args_dep.module("args"));
Expand Down Expand Up @@ -86,17 +106,17 @@ pub fn build(b: *std.Build) !void {
test_step.dependOn(&run_main_tests.step);
}

/// Placeholder for potential options we may add in future without breaking
/// backward-compatibility.
pub const JetzigInitOptions = struct {};
/// Build-time options for Jetzig.
pub const JetzigInitOptions = struct {
zmpl_version: enum { v1, v2 } = .v1,
};

pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigInitOptions) !void {
_ = options;
const target = b.host;
const optimize = exe.root_module.optimize orelse .Debug;
const jetzig_dep = b.dependency(
"jetzig",
.{ .optimize = optimize, .target = b.host },
.{ .optimize = optimize, .target = target, .zmpl_version = options.zmpl_version },
);
const jetzig_module = jetzig_dep.module("jetzig");
const zmpl_module = jetzig_dep.module("zmpl");
Expand Down Expand Up @@ -159,5 +179,6 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn
exe_static_routes.root_module.addImport("jetzig_app", &exe.root_module);

const run_static_routes_cmd = b.addRunArtifact(exe_static_routes);
run_static_routes_cmd.expectExitCode(0);
exe.step.dependOn(&run_static_routes_cmd.step);
}
4 changes: 2 additions & 2 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
.hash = "1220bfc5c29bc930b5a524c210712ef65c6cde6770450899bef01164a3089e6707fa",
},
.zmpl = .{
.url = "https://github.com/jetzig-framework/zmpl/archive/ffdbd3767da28d5ab07c1a786ea778152d2f79f6.tar.gz",
.hash = "12202cf05fd4ba2482a9b4b89c632b435310a76ac501b7a3d87dfd41006748dd138d",
.url = "https://github.com/jetzig-framework/zmpl/archive/d907a96ffa28721477f5c8895732a4a9396276f5.tar.gz",
.hash = "1220e96969db44c451577e6a59a0d99af73b4370781c9361e3ea7e0d6f24c7f13abb",
},
.args = .{
.url = "https://github.com/MasterQ32/zig-args/archive/01d72b9a0128c474aeeb9019edd48605fa6d95f7.tar.gz",
Expand Down
2 changes: 1 addition & 1 deletion demo/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub fn build(b: *std.Build) !void {

// All dependencies **must** be added to imports above this line.

try jetzig.jetzigInit(b, exe, .{});
try jetzig.jetzigInit(b, exe, .{ .zmpl_version = .v2 });

b.installArtifact(exe);

Expand Down
7 changes: 4 additions & 3 deletions demo/src/app/views/iguanas/index.zmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<div>
var it = zmpl.value.?.array.iterator();
while (it.next()) |iguana| {
<div>{(iguana.string.value)}</div>
@zig {
for (zmpl.items(.array)) |iguana| {
<div>{{iguana}}</div>
}
}
</div>
5 changes: 1 addition & 4 deletions demo/src/app/views/init.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
// param. JSON data is also accepted when the `content-type: application/json` header is
// present.
const params = try request.params();

if (params.get("message")) |value| {
try root.put("message_param", value);
}
try root.put("message_param", params.get("message"));

// Set arbitrary response headers as required. `content-type` is automatically assigned for
// HTML, JSON responses.
Expand Down
6 changes: 4 additions & 2 deletions demo/src/app/views/init/_content.zmpl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Renders the `message` response data value.
<h3 class="message text-[#39b54a]">{.message}</h3>
@args message: *ZmplValue

<!-- Renders the `message` response data value. -->
<h3 class="message text-[#39b54a]">{{message}}</h3>

<div><img class="p-3 mx-auto" src="/jetzig.png" /></div>

Expand Down
12 changes: 7 additions & 5 deletions demo/src/app/views/init/index.zmpl
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@

<body>
<div class="text-center pt-10 m-auto">
// If present, renders the `message_param` response data value, add `?message=hello` to the
// URL to see the output:
<h2 class="param text-3xl text-[#f7931e]">{.message_param}</h2>
<!-- If present, renders the `message_param` response data value, add `?message=hello` to the
URL to see the output: -->
<h2 class="param text-3xl text-[#f7931e]">{{.message_param}}</h2>

// Renders `src/app/views/init/_content.zmpl` with the same template data available:
<div>{^init/content}</div>
<!-- Renders `src/app/views/init/_content.zmpl`, passing in the `message` field from template data. -->
<div>
@partial init/content(message: .message)
</div>
</div>
</body>
</html>
2 changes: 1 addition & 1 deletion demo/src/app/views/layouts/application.zmpl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</head>

<body>
<main>{zmpl.content}</main>
<main>{{zmpl.content}}</main>
<script src="/prism.js"></script>
</body>
</html>
7 changes: 7 additions & 0 deletions demo/src/app/views/nested/route/markdown.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Dynamic Markdown Routes

_Markdown_ can be stored in any path in `src/app/views/` and _Jetzig_ will automatically render it if it matches a URI.

This _Markdown_ page can be accessed at `/nested/route/markdown.html` and `/nested/route/markdown`.

This functionality is particularly useful if you want to load _Markdown_ content with [htmx](https://htmx.org/).
4 changes: 2 additions & 2 deletions demo/src/app/views/quotes/get.zmpl
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
<div>"{.quote}"</div>
<div><b>--{.author}</b></div>
<div>"{{.quote}}"</div>
<div><b>--{{.author}}</b></div>
2 changes: 1 addition & 1 deletion demo/src/app/views/quotes/post.zmpl
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<div>{.param}</div>
<div>{{.param}}</div>
3 changes: 2 additions & 1 deletion demo/src/app/views/root/_quotes.zmpl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@args message: *ZmplValue
<div>
<h1 class="text-3xl text-center p-3 pb-6 font-bold">{.message}</h1>
<h1 class="text-3xl text-center p-3 pb-6 font-bold">{{message}}</h1>
</div>

<button hx-get="/quotes/random" hx-trigger="click" hx-target="#quote" class="bg-[#39b54a] text-white font-bold py-2 px-4 rounded">Click Me</button>
Expand Down
7 changes: 5 additions & 2 deletions demo/src/app/views/root/index.zmpl
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
<div class="text-center pt-10 m-auto">

<div><img class="p-3 mx-auto" src="/jetzig.png" /></div>

// Renders `src/app/views/root/_quotes.zmpl`:
<div>{^root/quotes}</div>
<!-- Renders `src/app/views/root/_quotes.zmpl`: -->
<div>
@partial root/quotes(message: .message)
</div>

<div>
<a href="https://github.com/jetzig-framework/zmpl">
Expand Down
1 change: 1 addition & 0 deletions demo/zmpl_options.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hello
4 changes: 3 additions & 1 deletion src/GenerateRoutes.zig
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ fn writeRoute(self: *Self, writer: std.ArrayList(u8).Writer, route: Function) !v
std.mem.replaceScalar(u8, view_name, '\\', '/');
defer self.allocator.free(view_name);

const template = try std.mem.concat(self.allocator, u8, &[_][]const u8{ view_name, "/", route.name });

std.mem.replaceScalar(u8, module_path, '\\', '/');

const output = try std.fmt.allocPrint(self.allocator, output_template, .{
Expand All @@ -190,7 +192,7 @@ fn writeRoute(self: *Self, writer: std.ArrayList(u8).Writer, route: Function) !v
if (route.static) "static" else "dynamic",
if (route.static) "true" else "false",
uri_path,
full_name,
template,
module_path,
try std.mem.join(self.allocator, ", \n", route.params.items),
});
Expand Down
2 changes: 1 addition & 1 deletion src/jetzig/http/Query.zig
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ fn dataValue(self: Query, value: ?[]const u8) *jetzig.data.Data.Value {
if (value) |item_value| {
return self.data.string(item_value);
} else {
return self.data._null();
return jetzig.zmpl.Data._null(self.data.getAllocator());
}
}

Expand Down
10 changes: 5 additions & 5 deletions src/jetzig/http/Server.zig
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ fn renderMarkdown(
);
defer self.allocator.free(prefixed_name);

if (zmpl.manifest.find(prefixed_name)) |layout| {
if (zmpl.find(prefixed_name)) |layout| {
rendered.view.data.content = .{ .data = markdown_content };
rendered.content = try layout.render(rendered.view.data);
} else {
Expand All @@ -249,7 +249,7 @@ fn renderView(
self: *Self,
route: *jetzig.views.Route,
request: *jetzig.http.Request,
template: ?zmpl.manifest.Template,
template: ?zmpl.Template,
) !RenderedView {
// View functions return a `View` to help encourage users to return from a view function with
// `return request.render(.ok)`, but the actual rendered view is stored in
Expand Down Expand Up @@ -290,18 +290,18 @@ fn renderView(
fn renderTemplateWithLayout(
self: *Self,
request: *jetzig.http.Request,
template: zmpl.manifest.Template,
template: zmpl.Template,
view: jetzig.views.View,
route: *jetzig.views.Route,
) ![]const u8 {
try addTemplateConstants(view, route);

if (request.getLayout(route)) |layout_name| {
// TODO: Allow user to configure layouts directory other than src/app/views/layouts/
const prefixed_name = try std.mem.concat(self.allocator, u8, &[_][]const u8{ "layouts_", layout_name });
const prefixed_name = try std.mem.concat(self.allocator, u8, &[_][]const u8{ "layouts", "/", layout_name });
defer self.allocator.free(prefixed_name);

if (zmpl.manifest.find(prefixed_name)) |layout| {
if (zmpl.find(prefixed_name)) |layout| {
return try template.renderWithLayout(layout, view.data);
} else {
try self.logger.WARN("Unknown layout: {s}", .{layout_name});
Expand Down

0 comments on commit b3dc38c

Please sign in to comment.