diff --git a/build.zig b/build.zig index bee5b61..18f3429 100644 --- a/build.zig +++ b/build.zig @@ -45,6 +45,7 @@ pub fn build(b: *std.Build) !void { .optimize = optimize, .zmpl_templates_paths = templates_paths, .zmpl_auto_build = false, + .zmpl_markdown_fragments = try generateMarkdownFragments(b), .zmpl_constants = try zmpl_build.addTemplateConstants(b, struct { jetzig_view: []const u8, jetzig_action: []const u8, @@ -55,13 +56,13 @@ pub fn build(b: *std.Build) !void { const zmpl_module = zmpl_dep.module("zmpl"); const jetkv_dep = b.dependency("jetkv", .{ .target = target, .optimize = optimize }); + const zmd_dep = b.dependency("zmd", .{ .target = target, .optimize = optimize }); // 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 }); + b.modules.put("zmd", zmd_dep.module("zmd")) catch @panic("Out of memory"); const smtp_client_dep = b.dependency("smtp_client", .{ .target = target, @@ -117,9 +118,11 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn ); const jetzig_module = jetzig_dep.module("jetzig"); const zmpl_module = jetzig_dep.module("zmpl"); + const zmd_module = jetzig_dep.module("zmd"); exe.root_module.addImport("jetzig", jetzig_module); exe.root_module.addImport("zmpl", zmpl_module); + exe.root_module.addImport("zmd", zmd_module); if (b.option(bool, "jetzig_runner", "Used internally by `jetzig server` command.")) |jetzig_runner| { if (jetzig_runner) { @@ -204,3 +207,45 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn run_static_routes_cmd.expectExitCode(0); exe.step.dependOn(&run_static_routes_cmd.step); } + +fn generateMarkdownFragments(b: *std.Build) ![]const u8 { + const file = std.fs.cwd().openFile(b.pathJoin(&.{ "src", "main.zig" }), .{}) catch |err| { + switch (err) { + error.FileNotFound => return "", + else => return err, + } + }; + const stat = try file.stat(); + const source = try file.readToEndAllocOptions(b.allocator, stat.size, null, @alignOf(u8), 0); + if (try getMarkdownFragmentsSource(b.allocator, source)) |markdown_fragments_source| { + return try std.fmt.allocPrint(b.allocator, + \\const std = @import("std"); + \\const zmd = @import("zmd"); + \\ + \\{s}; + \\ + , .{markdown_fragments_source}); + } else { + return ""; + } +} + +fn getMarkdownFragmentsSource(allocator: std.mem.Allocator, source: [:0]const u8) !?[]const u8 { + var ast = try std.zig.Ast.parse(allocator, source, .zig); + defer ast.deinit(allocator); + + for (ast.nodes.items(.tag), 0..) |tag, index| { + switch (tag) { + .simple_var_decl => { + const decl = ast.simpleVarDecl(@intCast(index)); + const identifier = ast.tokenSlice(decl.ast.mut_token + 1); + if (std.mem.eql(u8, identifier, "markdown_fragments")) { + return ast.getNodeSource(@intCast(index)); + } + }, + else => continue, + } + } + + return null; +} diff --git a/build.zig.zon b/build.zig.zon index 4bcec26..ebf6b63 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -3,12 +3,12 @@ .version = "0.0.0", .dependencies = .{ .zmd = .{ - .url = "https://github.com/jetzig-framework/zmd/archive/901e4ce55cdbfd82c42cfd4feb3a1682dab4b418.tar.gz", - .hash = "12207d49df326e0c180a90fa65d9993898e0a0ffd8e79616b4b81f81769261858856", + .url = "https://github.com/jetzig-framework/zmd/archive/45a9ff0e3a55b4758163abad5a601bd3ef8127a8.tar.gz", + .hash = "122067a3107499e4b561a883a9f1e9f39662c4b87249a2e4e630279bd91ab7840230", }, .zmpl = .{ - .url = "https://github.com/jetzig-framework/zmpl/archive/4511ae706e8679385d38cc1366497082f8f53afb.tar.gz", - .hash = "1220d493e6fdfaccbafff41df2b7b407728ed11619bebb198c90dae9420f03a6d29d", + .url = "https://github.com/jetzig-framework/zmpl/archive/9712b85e61f33879f388d5e1829856e326c53822.tar.gz", + .hash = "1220b185cf8316ae5ad6dd7d45bea278575391986b7d8233a9d9b2c342e339d65ac0", }, .args = .{ .url = "https://github.com/MasterQ32/zig-args/archive/01d72b9a0128c474aeeb9019edd48605fa6d95f7.tar.gz", diff --git a/demo/src/app/views/markdown/index.md b/demo/src/app/views/markdown/index.md.zmpl similarity index 83% rename from demo/src/app/views/markdown/index.md rename to demo/src/app/views/markdown/index.md.zmpl index 1ab7e18..5efae1d 100644 --- a/demo/src/app/views/markdown/index.md +++ b/demo/src/app/views/markdown/index.md.zmpl @@ -2,11 +2,11 @@  -_Markdown_ is rendered by _[zmd](https://github.com/jetzig-framework/zmd)_. +_Markdown_ is rendered by [zmd](https://github.com/jetzig-framework/zmd). You can use a `StaticRequest` in your view if you prefer to render your _Markdown_ at build time, or use `Request` in development to render at run time without a server restart. -Simply create a `.md` file instead of a `.zmpl` file, e.g. `src/app/views/iguanas/index.md` and _Markdown_ will be rendered. +Simply create a `.md.zmpl` file instead of a `.zmpl` file, e.g. `src/app/views/iguanas/index.md.zmpl` and _Markdown_ will be rendered. You can still use _Zmpl_ references, modes, and partials. In fact, a `.md.zmpl` template is simply a regular _Zmpl_ template with the root mode set to `markdown`. ## An _ordered_ list diff --git a/demo/src/app/views/root/index.zmpl b/demo/src/app/views/root/index.zmpl index 1fb1e42..f5fc30b 100644 --- a/demo/src/app/views/root/index.zmpl +++ b/demo/src/app/views/root/index.zmpl @@ -16,3 +16,9 @@
{s}
, .{ node.meta, node.content });
}
- pub fn link(allocator: std.mem.Allocator, node: jetzig.zmd.Node) ![]const u8 {
+ pub fn link(allocator: std.mem.Allocator, node: zmd.Node) ![]const u8 {
return try std.fmt.allocPrint(allocator,
\\{1s}
, .{ node.href.?, node.title.? });
diff --git a/src/compile_static_routes.zig b/src/compile_static_routes.zig
index 99e7c86..70197c0 100644
--- a/src/compile_static_routes.zig
+++ b/src/compile_static_routes.zig
@@ -143,7 +143,7 @@ fn renderZmplTemplate(
defer allocator.free(prefixed_name);
if (zmpl.findPrefixed("views", prefixed_name)) |layout| {
- return try template.renderWithLayout(layout, view.data);
+ return try template.renderWithOptions(view.data, .{ .layout = layout });
} else {
std.debug.print("Unknown layout: {s}\n", .{layout_name});
return try allocator.dupe(u8, "");
diff --git a/src/jetzig/http/Server.zig b/src/jetzig/http/Server.zig
index c0c979d..1c98d19 100644
--- a/src/jetzig/http/Server.zig
+++ b/src/jetzig/http/Server.zig
@@ -2,6 +2,7 @@ const std = @import("std");
const jetzig = @import("../../jetzig.zig");
const zmpl = @import("zmpl");
+const zmd = @import("zmd");
pub const ServerOptions = struct {
logger: jetzig.loggers.Logger,
@@ -153,27 +154,22 @@ fn renderHTML(
route: ?*jetzig.views.Route,
) !void {
if (route) |matched_route| {
- const template = zmpl.findPrefixed("views", matched_route.template);
- if (template == null) {
- request.response_data.noop(bool, false); // FIXME: Weird Zig bug ? Any call here fixes it.
- if (try self.renderMarkdown(request, route)) |rendered_markdown| {
- return request.setResponse(rendered_markdown, .{});
- }
- }
- const rendered = self.renderView(matched_route, request, template) catch |err| {
- if (isUnhandledError(err)) return err;
- const rendered_error = try self.renderInternalServerError(request, err);
- return request.setResponse(rendered_error, .{});
- };
- if (request.status_code != .not_found) {
+ if (zmpl.findPrefixed("views", matched_route.template)) |template| {
+ const rendered = self.renderView(matched_route, request, template) catch |err| {
+ if (isUnhandledError(err)) return err;
+ const rendered_error = try self.renderInternalServerError(request, err);
+ return request.setResponse(rendered_error, .{});
+ };
return request.setResponse(rendered, .{});
+ } else {
+ return request.setResponse(try renderNotFound(request), .{});
}
- }
-
- if (try self.renderMarkdown(request, route)) |rendered| {
- return request.setResponse(rendered, .{});
} else {
- return request.setResponse(try renderNotFound(request), .{});
+ if (try self.renderMarkdown(request)) |rendered| {
+ return request.setResponse(rendered, .{});
+ } else {
+ return request.setResponse(try renderNotFound(request), .{});
+ }
}
}
@@ -199,57 +195,18 @@ fn renderJSON(
}
}
-fn renderMarkdown(
- self: *Server,
- request: *jetzig.http.Request,
- maybe_route: ?*jetzig.views.Route,
-) !?RenderedView {
- const route = maybe_route orelse {
- // No route recognized, but we can still render a static markdown file if it matches the URI:
- if (request.method != .GET) return null;
- if (try jetzig.markdown.render(request.allocator, request.path.base_path, null)) |content| {
- return .{
- .view = jetzig.views.View{ .data = request.response_data, .status_code = .ok },
- .content = content,
- };
- } else {
- return null;
- }
- };
-
- const path = try std.mem.join(
- request.allocator,
- "/",
- &[_][]const u8{ route.uri_path, @tagName(route.action) },
- );
- const markdown_content = try jetzig.markdown.render(request.allocator, path, null) orelse
+fn renderMarkdown(self: *Server, request: *jetzig.http.Request) !?RenderedView {
+ _ = self;
+ // No route recognized, but we can still render a static markdown file if it matches the URI:
+ if (request.method != .GET) return null;
+ if (try jetzig.markdown.render(request.allocator, request.path.base_path, null)) |content| {
+ return .{
+ .view = jetzig.views.View{ .data = request.response_data, .status_code = .ok },
+ .content = content,
+ };
+ } else {
return null;
-
- var rendered = self.renderView(route, request, null) catch |err| {
- if (isUnhandledError(err)) return err;
- return try self.renderInternalServerError(request, err);
- };
-
- try addTemplateConstants(rendered.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 },
- );
- defer self.allocator.free(prefixed_name);
-
- if (zmpl.findPrefixed("views", prefixed_name)) |layout| {
- rendered.view.data.content = .{ .data = markdown_content };
- rendered.content = try layout.render(rendered.view.data);
- } else {
- try self.logger.WARN("Unknown layout: {s}", .{layout_name});
- rendered.content = markdown_content;
- }
}
- return rendered;
}
pub const RenderedView = struct { view: jetzig.views.View, content: []const u8 };
@@ -260,7 +217,7 @@ fn renderView(
request: *jetzig.http.Request,
template: ?zmpl.Template,
) !RenderedView {
- // View functions return a `View` to help encourage users to return from a view function with
+ // View functions return a `View` to encourage users to return from a view function with
// `return request.render(.ok)`, but the actual rendered view is stored in
// `request.rendered_view`.
_ = route.render(route.*, request) catch |err| {
@@ -307,11 +264,15 @@ 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.findPrefixed("views", prefixed_name)) |layout| {
- return try template.renderWithLayout(layout, view.data);
+ return try template.renderWithOptions(view.data, .{ .layout = layout });
} else {
try self.logger.WARN("Unknown layout: {s}", .{layout_name});
return try template.render(view.data);
diff --git a/src/jetzig/markdown.zig b/src/jetzig/markdown.zig
index 1e92410..d02ac86 100644
--- a/src/jetzig/markdown.zig
+++ b/src/jetzig/markdown.zig
@@ -1,8 +1,9 @@
const std = @import("std");
+const Zmd = @import("zmd").Zmd;
+
const jetzig = @import("../jetzig.zig");
-const Zmd = @import("zmd").Zmd;
pub fn render(
allocator: std.mem.Allocator,
path: []const u8,