mirror of
https://github.com/jetzig-framework/jetzig.git
synced 2025-05-14 22:16:08 +00:00
Update Zmpl, adds Markdown mode formatters
Also adds support for `.md.zmpl` templates - root node is Markdown.
This commit is contained in:
parent
3c47435fd6
commit
9d12b5c717
49
build.zig
49
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;
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -16,3 +16,9 @@
|
||||
<div>Take a look at the <span class="font-mono">/demo/src/app/</span> directory to see how this application works.</div>
|
||||
<div>Visit <a class="font-bold text-[#39b54a]" href="https://jetzig.dev/">jetzig.dev</a> to get started.</div>
|
||||
</div>
|
||||
|
||||
@markdown {
|
||||
# Hello
|
||||
|
||||
[foobar](https://www.google.com)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub const jetzig = @import("jetzig");
|
||||
const jetzig = @import("jetzig");
|
||||
const zmd = @import("zmd");
|
||||
|
||||
pub const routes = @import("routes");
|
||||
|
||||
@ -54,14 +55,14 @@ pub const jetzig_options = struct {
|
||||
// pub const force_development_email_delivery = false;
|
||||
|
||||
// Set custom fragments for rendering markdown templates. Any values will fall back to
|
||||
// defaults provided by Zmd (https://github.com/bobf/zmd/blob/main/src/zmd/html.zig).
|
||||
// defaults provided by Zmd (https://github.com/jetzig-framework/zmd/blob/main/src/zmd/html.zig).
|
||||
pub const markdown_fragments = struct {
|
||||
pub const root = .{
|
||||
"<div class='p-5'>",
|
||||
"</div>",
|
||||
};
|
||||
pub const h1 = .{
|
||||
"<h1 class='text-2xl mb-3 font-bold'>",
|
||||
"<h1 class='text-2xl mb-3 text-green font-bold'>",
|
||||
"</h1>",
|
||||
};
|
||||
pub const h2 = .{
|
||||
@ -91,13 +92,13 @@ pub const jetzig_options = struct {
|
||||
"</ul>",
|
||||
};
|
||||
|
||||
pub fn block(allocator: std.mem.Allocator, node: jetzig.zmd.Node) ![]const u8 {
|
||||
pub fn block(allocator: std.mem.Allocator, node: zmd.Node) ![]const u8 {
|
||||
return try std.fmt.allocPrint(allocator,
|
||||
\\<pre class="w-1/2 font-mono mt-4 ms-3 bg-gray-900 p-2 text-white"><code class="language-{?s}">{s}</code></pre>
|
||||
, .{ 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,
|
||||
\\<a class="underline decoration-sky-500" href="{0s}" title={1s}>{1s}</a>
|
||||
, .{ node.href.?, node.title.? });
|
||||
|
@ -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, "");
|
||||
|
@ -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,28 +154,23 @@ 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, .{});
|
||||
}
|
||||
}
|
||||
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, .{});
|
||||
};
|
||||
if (request.status_code != .not_found) {
|
||||
return request.setResponse(rendered, .{});
|
||||
}
|
||||
}
|
||||
|
||||
if (try self.renderMarkdown(request, route)) |rendered| {
|
||||
return request.setResponse(rendered, .{});
|
||||
} else {
|
||||
return request.setResponse(try renderNotFound(request), .{});
|
||||
}
|
||||
} else {
|
||||
if (try self.renderMarkdown(request)) |rendered| {
|
||||
return request.setResponse(rendered, .{});
|
||||
} else {
|
||||
return request.setResponse(try renderNotFound(request), .{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn renderJSON(
|
||||
@ -199,12 +195,8 @@ fn renderJSON(
|
||||
}
|
||||
}
|
||||
|
||||
fn renderMarkdown(
|
||||
self: *Server,
|
||||
request: *jetzig.http.Request,
|
||||
maybe_route: ?*jetzig.views.Route,
|
||||
) !?RenderedView {
|
||||
const route = maybe_route 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| {
|
||||
@ -215,41 +207,6 @@ fn renderMarkdown(
|
||||
} 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
|
||||
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);
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user