Skip to content

Commit

Permalink
Merge pull request #55 from jetzig-framework/zmpl-v2
Browse files Browse the repository at this point in the history
Zmpl v2
  • Loading branch information
bobf authored Apr 7, 2024
2 parents 8096962 + 16204a4 commit b592923
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>
7 changes: 3 additions & 4 deletions demo/src/app/views/init.zig
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const std = @import("std");
const jetzig = @import("jetzig");

/// `src/app/views/root.zig` represents the root URL `/`
Expand All @@ -18,17 +19,15 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
var root = try data.object();

// Add a string to the root object.
try root.put("message", data.string("Welcome to Jetzig!"));
try root.put("welcome_message", data.string("Welcome to Jetzig!"));

// Request params have the same type as a `data.object()` so they can be inserted them
// directly into the response data. Fetch `http://localhost:8080/?message=hello` to set the
// 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
4 changes: 2 additions & 2 deletions demo/src/app/views/init/_content.zmpl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Renders the `message` response data value.
<h3 class="message text-[#39b54a]">{.message}</h3>
@args message: *ZmplValue
<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 `welcome_message` field from template data. -->
<div>
@partial init/content(message: .welcome_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 b592923

Please sign in to comment.