mirror of
https://github.com/jetzig-framework/jetzig.git
synced 2025-05-14 05:56:07 +00:00
Zmpl v2
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).
This commit is contained in:
parent
8096962cf6
commit
16204a4832
59
build.zig
59
build.zig
@ -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",
|
||||
@ -28,6 +37,24 @@ 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",
|
||||
.{
|
||||
@ -35,10 +62,16 @@ pub fn build(b: *std.Build) !void {
|
||||
.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");
|
||||
@ -46,19 +79,6 @@ pub fn build(b: *std.Build) !void {
|
||||
|
||||
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"));
|
||||
@ -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");
|
||||
@ -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);
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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>
|
||||
|
@ -1,3 +1,4 @@
|
||||
const std = @import("std");
|
||||
const jetzig = @import("jetzig");
|
||||
|
||||
/// `src/app/views/root.zig` represents the root URL `/`
|
||||
@ -18,7 +19,7 @@ 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
|
||||
@ -26,9 +27,7 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
|
||||
// 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.
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -9,7 +9,7 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>{zmpl.content}</main>
|
||||
<main>{{zmpl.content}}</main>
|
||||
<script src="/prism.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
7
demo/src/app/views/nested/route/markdown.md
Normal file
7
demo/src/app/views/nested/route/markdown.md
Normal file
@ -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/).
|
@ -1,2 +1,2 @@
|
||||
<div>"{.quote}"</div>
|
||||
<div><b>--{.author}</b></div>
|
||||
<div>"{{.quote}}"</div>
|
||||
<div><b>--{{.author}}</b></div>
|
||||
|
@ -1 +1 @@
|
||||
<div>{.param}</div>
|
||||
<div>{{.param}}</div>
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
1
demo/zmpl_options.zig
Normal file
1
demo/zmpl_options.zig
Normal file
@ -0,0 +1 @@
|
||||
hello
|
@ -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, .{
|
||||
@ -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),
|
||||
});
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
@ -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
|
||||
@ -290,7 +290,7 @@ 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 {
|
||||
@ -298,10 +298,10 @@ fn renderTemplateWithLayout(
|
||||
|
||||
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});
|
||||
|
Loading…
x
Reference in New Issue
Block a user