From c0d6fb162b77f13d7d14ae2cde6c509526587d24 Mon Sep 17 00:00:00 2001 From: Bob Farrell Date: Sat, 1 Mar 2025 12:28:08 +0000 Subject: [PATCH 1/6] Latest Zig build.zig.zon format compatibility --- demo/build.zig.zon | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/demo/build.zig.zon b/demo/build.zig.zon index 321158c..a6e4283 100644 --- a/demo/build.zig.zon +++ b/demo/build.zig.zon @@ -1,7 +1,8 @@ .{ - .name = "jetzig-demo", + .name = .jetzig_demo, .version = "0.0.0", .minimum_zig_version = "0.12.0", + .fingerprint = 0x3877c19710a92a5c, .dependencies = .{ .jetzig = .{ .path = "../", From 099a2a5349b00cabf5fb2bdf88c4392decb9301b Mon Sep 17 00:00:00 2001 From: Bob Farrell Date: Wed, 25 Dec 2024 13:27:35 +0000 Subject: [PATCH 2/6] Use Zig compiler Default to not using LLVM for application compilation. This gives more than 2x performance improvement for compilation stage. --- build.zig | 26 ++++++++--- build.zig.zon | 43 ++++++++---------- cli/build.zig.zon | 3 +- cli/commands/bundle.zig | 4 +- cli/commands/init.zig | 4 ++ cli/commands/server.zig | 6 +-- cli/util.zig | 63 +++++++++++++++++--------- demo/.gitignore | 1 + demo/src/app/views/channels/index.zmpl | 3 ++ demo/src/app/views/root.zig | 1 + demo/src/app/views/root/index.zmpl | 2 +- src/jetzig/App.zig | 15 +++--- src/jetzig/http/Server.zig | 31 ++++++++----- src/jetzig/http/middleware.zig | 8 ++++ 14 files changed, 130 insertions(+), 80 deletions(-) create mode 100644 demo/src/app/views/channels/index.zmpl diff --git a/build.zig b/build.zig index 1cb3e55..09e1433 100644 --- a/build.zig +++ b/build.zig @@ -36,6 +36,7 @@ pub fn build(b: *std.Build) !void { .{ .target = target, .optimize = optimize, + .use_llvm = b.option(bool, "use_llvm", "Use LLVM") orelse false, .zmpl_templates_paths = templates_paths, .zmpl_auto_build = false, .zmpl_markdown_fragments = try generateMarkdownFragments(b), @@ -46,6 +47,11 @@ pub fn build(b: *std.Build) !void { }, ); + const zmpl_steps = zmpl_dep.builder.top_level_steps; + const zmpl_compile_step = zmpl_steps.get("compile").?; + const compile_step = b.step("compile", "Compile Zmpl templates"); + compile_step.dependOn(&zmpl_compile_step.step); + const zmpl_module = zmpl_dep.module("zmpl"); const jetkv_dep = b.dependency("jetkv", .{ .target = target, .optimize = optimize }); @@ -58,18 +64,15 @@ pub fn build(b: *std.Build) !void { const jetcommon_dep = b.dependency("jetcommon", .{ .target = target, .optimize = optimize }); const zmd_dep = b.dependency("zmd", .{ .target = target, .optimize = optimize }); const httpz_dep = b.dependency("httpz", .{ .target = target, .optimize = optimize }); - const pg_dep = b.dependency("pg", .{ .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"); b.modules.put("zmd", zmd_dep.module("zmd")) catch @panic("Out of memory"); - b.modules.put("pg", pg_dep.module("pg")) catch @panic("Out of memory"); b.modules.put("jetquery", jetquery_dep.module("jetquery")) catch @panic("Out of memory"); b.modules.put("jetcommon", jetcommon_dep.module("jetcommon")) catch @panic("Out of memory"); b.modules.put("jetquery_migrate", jetquery_dep.module("jetquery_migrate")) catch @panic("Out of memory"); - jetquery_dep.module("jetquery").addImport("pg", pg_dep.module("pg")); const smtp_client_dep = b.dependency("smtp_client", .{ .target = target, @@ -132,6 +135,8 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn const target = exe.root_module.resolved_target orelse @panic("Unable to detect compile target."); const optimize = exe.root_module.optimize orelse .Debug; + exe.use_llvm = exe.use_llvm orelse (optimize != .Debug); + if (optimize != .Debug) exe.linkLibC(); const environment = b.option( @@ -172,7 +177,6 @@ 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"); - const pg_module = jetzig_dep.module("pg"); const jetquery_module = jetzig_dep.module("jetquery"); const jetcommon_module = jetzig_dep.module("jetcommon"); const jetquery_migrate_module = jetzig_dep.module("jetquery_migrate"); @@ -187,7 +191,6 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn exe.root_module.addImport("jetzig", jetzig_module); exe.root_module.addImport("zmpl", zmpl_module); exe.root_module.addImport("zmd", zmd_module); - exe.root_module.addImport("pg", pg_module); if (b.option(bool, "jetzig_runner", "Used internally by `jetzig server` command.")) |jetzig_runner| { if (jetzig_runner) { @@ -197,7 +200,10 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn } } - const root_path = b.build_root.path orelse try std.fs.cwd().realpathAlloc(b.allocator, "."); + const root_path = if (b.build_root.path) |build_root_path| + try std.fs.path.join(b.allocator, &.{ build_root_path, "." }) + else + try std.fs.cwd().realpathAlloc(b.allocator, "."); const templates_path: []const u8 = try std.fs.path.join( b.allocator, &[_][]const u8{ root_path, "src", "app" }, @@ -220,6 +226,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn .root_source_file = jetzig_dep.path("src/routes_file.zig"), .target = target, .optimize = optimize, + .use_llvm = exe.use_llvm, }); exe_routes_file.root_module.addImport("jetzig", jetzig_module); @@ -228,6 +235,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn exe_routes_file.root_module.addImport("zmpl", zmpl_module); const run_routes_file_cmd = b.addRunArtifact(exe_routes_file); + run_routes_file_cmd.has_side_effects = true; const routes_file_path = run_routes_file_cmd.addOutputFileArg("routes.zig"); run_routes_file_cmd.addArgs(&.{ root_path, @@ -247,6 +255,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn .root_source_file = jetzig_dep.path("src/compile_static_routes.zig"), .target = target, .optimize = optimize, + .use_llvm = exe.use_llvm, }); const main_module = b.createModule(.{ .root_source_file = b.path("src/main.zig") }); @@ -355,6 +364,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn .root_source_file = jetzig_dep.path("src/commands/routes.zig"), .target = target, .optimize = optimize, + .use_llvm = exe.use_llvm, }); const auth_user_create_step = b.step("jetzig:auth:user:create", "List all routes in your app"); @@ -363,6 +373,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn .root_source_file = jetzig_dep.path("src/commands/auth.zig"), .target = target, .optimize = optimize, + .use_llvm = exe.use_llvm, }); exe_auth.root_module.addImport("jetquery", jetquery_module); exe_auth.root_module.addImport("jetzig", jetzig_module); @@ -384,6 +395,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn .root_source_file = jetzig_dep.path("src/commands/database.zig"), .target = target, .optimize = optimize, + .use_llvm = exe.use_llvm, }); exe_database.root_module.addImport("jetquery", jetquery_module); exe_database.root_module.addImport("jetzig", jetzig_module); @@ -395,7 +407,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn registerDatabaseSteps(b, exe_database); const option_db_exe = b.option(bool, "database_exe", "option to install 'database' executable default is false") orelse false; - if(option_db_exe) b.installArtifact(exe_database); + if (option_db_exe) b.installArtifact(exe_database); exe_routes.root_module.addImport("jetzig", jetzig_module); exe_routes.root_module.addImport("routes", routes_module); diff --git a/build.zig.zon b/build.zig.zon index 8f2467a..cf6fc5c 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,42 +1,39 @@ .{ - .name = "jetzig", + .name = .jetzig, .version = "0.0.0", + .fingerprint = 0x93ad8bfa2d209022, .dependencies = .{ - .zmd = .{ - .url = "https://github.com/jetzig-framework/zmd/archive/d6c8aa9a9cde99674ccb096d8f94ed09cba8dab.tar.gz", - .hash = "1220d0e8734628fd910a73146e804d10a3269e3e7d065de6bb0e3e88d5ba234eb163", + .jetquery = .{ + .url = "https://github.com/jetzig-framework/jetquery/archive/795136c43f6d5bd216c136748bafd22892277725.tar.gz", + .hash = "jetquery-0.0.0-TNf3zqZFBgDS2CaIG4DvLwaiGeAxxbfAi-DnEnSKx-mf", }, .jetkv = .{ .url = "https://github.com/jetzig-framework/jetkv/archive/9d754e552e7569239a900ed9e0f313a0554ed2d3.tar.gz", .hash = "122013f8596bc615990fd7771c833cab4d2959ecac8d05c4f6c973aa46624e43afea", }, - .args = .{ - .url = "https://github.com/ikskuh/zig-args/archive/968258dc1b1230493d8f1677097c832a3d7e0bd8.tar.gz", - .hash = "1220bdedf1a993d852d8aebcd63922a8fb163fac37b9c6ff72d187b2847a4a3a4248", - }, .jetcommon = .{ - .url = "https://github.com/jetzig-framework/jetcommon/archive/5be57d534b3d469f5570cd4b373b8d61032b1b8b.tar.gz", - .hash = "122079c6ceb28fa93163c2f95e2f175bb8f93f3075fa34af63045671ab7dd824e756", + .url = "https://github.com/jetzig-framework/jetcommon/archive/fb4edc13759d87bfcd9b1f5fcefdf93f8c9c62dd.tar.gz", + .hash = "jetcommon-0.1.0-jPY_DS1HAAAP8xp5HSWB_ZY7m9JEYUmm8adQFrse0lwB", }, - .pg = .{ - .url = "https://github.com/karlseguin/pg.zig/archive/0110cfdf387403a5a326115b5184861c4604d711.tar.gz", - .hash = "12205019ce2bc2e08c76352ea37a14600d412e5e0ecdd7ddd27b4e83a62f37d8ba94", + .zmpl = .{ + .url = "https://github.com/jetzig-framework/zmpl/archive/d34fd5aa69cc5f68dc73b1ef844f7cbdebdfc93e.tar.gz", + .hash = "zmpl-0.0.1-SYFGBhRpAwAIJWGukw-MWyAbXq8WCDudBjAAerZ8jZ_u", + }, + .zmd = .{ + .url = "https://github.com/jetzig-framework/zmd/archive/d6c8aa9a9cde99674ccb096d8f94ed09cba8dab.tar.gz", + .hash = "1220d0e8734628fd910a73146e804d10a3269e3e7d065de6bb0e3e88d5ba234eb163", }, .httpz = .{ - .url = "https://github.com/karlseguin/http.zig/archive/f16b296a2772be97e48a57259c004aa6584a02c6.tar.gz", - .hash = "1220e524a72c18aa2585f326902066fda544c5df0cf6618eea885b04041204dc5d7f", + .url = "https://github.com/karlseguin/http.zig/archive/eced2d8c37f921a2dfc8ab639964cdc9b505a888.tar.gz", + .hash = "httpz-0.0.0-AAAAAL6qBgAeyws7FZLTv3e_Sbsjg4PKfB1Fg6AOHctf", }, .smtp_client = .{ .url = "https://github.com/karlseguin/smtp_client.zig/archive/5163c66cc42cdd93176a6b1cad45f3db3a291a6a.tar.gz", - .hash = "1220a7807b5161550cb0cba772689c4872bfeee8305a26c3cd0e12a8ccde1d546910", + .hash = "smtp_client-0.0.1-AAAAAIJkAQCngHtRYVUMsMuncmicSHK_7ugwWibDzQ4S", }, - .jetquery = .{ - .url = "https://github.com/jetzig-framework/jetquery/archive/55ebed84ad80d3d6c6e026c06e37b7de22168e7b.tar.gz", - .hash = "1220715b9064087cdc114e1a6a087e56d242cea56bc5759b740f4c2d5c1765822add", - }, - .zmpl = .{ - .url = "https://github.com/jetzig-framework/zmpl/archive/04d9fa7c3f6369790aac1fc64625e91222072ebc.tar.gz", - .hash = "122078b3cd8ac2be7ba7b122312de7abee6de6be905287e2b3d80b09e5481a39e70f", + .args = .{ + .url = "https://github.com/ikskuh/zig-args/archive/968258dc1b1230493d8f1677097c832a3d7e0bd8.tar.gz", + .hash = "1220bdedf1a993d852d8aebcd63922a8fb163fac37b9c6ff72d187b2847a4a3a4248", }, }, diff --git a/cli/build.zig.zon b/cli/build.zig.zon index e8fbdc8..67a3c63 100644 --- a/cli/build.zig.zon +++ b/cli/build.zig.zon @@ -1,5 +1,6 @@ .{ - .name = "jetzig-cli", + .name = .jetzig_cli, + .fingerprint = 0x73894a3e0616c96a, .version = "0.0.0", .minimum_zig_version = "0.12.0", diff --git a/cli/commands/bundle.zig b/cli/commands/bundle.zig index cf4f0af..e689e56 100644 --- a/cli/commands/bundle.zig +++ b/cli/commands/bundle.zig @@ -149,7 +149,7 @@ pub fn run( const tmpdir_real_path = try tmpdir.realpathAlloc(allocator, "."); defer allocator.free(tmpdir_real_path); - try util.runCommandInDir(allocator, tar_argv.items, .{ .path = tmpdir_real_path }); + try util.runCommandInDir(allocator, tar_argv.items, .{ .path = tmpdir_real_path }, .{}); switch (builtin.os.tag) { .windows => {}, @@ -215,7 +215,7 @@ fn zig_build_install(allocator: std.mem.Allocator, path: []const u8, options: Op defer project_dir.close(); project_dir.makePath(".bundle") catch {}; - try util.runCommandInDir(allocator, install_argv.items, .{ .path = path }); + try util.runCommandInDir(allocator, install_argv.items, .{ .path = path }, .{}); const install_bin_path = try std.fs.path.join(allocator, &[_][]const u8{ ".bundle", "bin" }); defer allocator.free(install_bin_path); diff --git a/cli/commands/init.zig b/cli/commands/init.zig index 9556661..3b960ec 100644 --- a/cli/commands/init.zig +++ b/cli/commands/init.zig @@ -202,6 +202,7 @@ pub fn run( github_url, }, .{ .dir = install_dir }, + .{}, ); // TODO: Use arg or interactive prompt to do Git setup in net project, default to no. @@ -333,6 +334,7 @@ fn gitSetup(allocator: std.mem.Allocator, install_dir: *std.fs.Dir) !void { ".", }, .{ .path = install_dir }, + .{}, ); try util.runCommandInDir( @@ -343,6 +345,7 @@ fn gitSetup(allocator: std.mem.Allocator, install_dir: *std.fs.Dir) !void { ".", }, .{ .path = install_dir }, + .{}, ); try util.runCommandInDir( @@ -354,5 +357,6 @@ fn gitSetup(allocator: std.mem.Allocator, install_dir: *std.fs.Dir) !void { "Initialize Jetzig project", }, .{ .path = install_dir }, + .{}, ); } diff --git a/cli/commands/server.zig b/cli/commands/server.zig index e51c29f..36cd777 100644 --- a/cli/commands/server.zig +++ b/cli/commands/server.zig @@ -78,11 +78,7 @@ pub fn run( }); while (true) { - util.runCommandInDir( - allocator, - argv.items, - .{ .path = realpath }, - ) catch { + util.runCommandInDir(allocator, argv.items, .{ .path = realpath }, .{}) catch { std.debug.print("Build failed, waiting for file change...\n", .{}); try awaitFileChange(allocator, cwd, &mtime); std.debug.print("Changes detected, restarting server...\n", .{}); diff --git a/cli/util.zig b/cli/util.zig index 0f2b925..f058881 100644 --- a/cli/util.zig +++ b/cli/util.zig @@ -153,7 +153,7 @@ pub fn runCommandStreaming(allocator: std.mem.Allocator, install_path: []const u pub fn runCommand(allocator: std.mem.Allocator, argv: []const []const u8) !void { var dir = try detectJetzigProjectDir(); defer dir.close(); - try runCommandInDir(allocator, argv, .{ .dir = dir }); + try runCommandInDir(allocator, argv, .{ .dir = dir }, .{}); } const Dir = union(enum) { @@ -161,34 +161,53 @@ const Dir = union(enum) { dir: std.fs.Dir, }; +pub const RunOptions = struct { + output: enum { stream, capture } = .capture, + wait: bool = true, +}; + /// Runs a command as a child process in the given directory and verifies successful exit code. -pub fn runCommandInDir(allocator: std.mem.Allocator, argv: []const []const u8, dir: Dir) !void { +pub fn runCommandInDir(allocator: std.mem.Allocator, argv: []const []const u8, dir: Dir, options: RunOptions) !void { const cwd_path = switch (dir) { .path => |capture| capture, .dir => |capture| try capture.realpathAlloc(allocator, "."), }; defer if (dir == .dir) allocator.free(cwd_path); - const result = std.process.Child.run(.{ - .allocator = allocator, - .argv = argv, - .cwd = cwd_path, - }) catch |err| { - switch (err) { - error.FileNotFound => { - printFailure(); - const cmd_str = try std.mem.join(allocator, " ", argv); - defer allocator.free(cmd_str); - std.debug.print( - \\Error: Could not execute command - executable '{s}' not found - \\Command: {s} - \\Working directory: {s} - \\ - , .{ argv[0], cmd_str, cwd_path }); - return error.JetzigCommandError; - }, - else => return err, - } + const output_behaviour: std.process.Child.StdIo = switch (options.output) { + .stream => .Inherit, + .capture => .Pipe, + }; + + var child = std.process.Child.init(argv, allocator); + child.stdin_behavior = .Ignore; + child.stdout_behavior = output_behaviour; + child.stderr_behavior = output_behaviour; + if (options.output == .stream) { + child.stdout = std.io.getStdOut(); + child.stderr = std.io.getStdErr(); + } + child.cwd = cwd_path; + + var stdout = try std.ArrayListUnmanaged(u8).initCapacity(allocator, 0); + var stderr = try std.ArrayListUnmanaged(u8).initCapacity(allocator, 0); + errdefer { + stdout.deinit(allocator); + stderr.deinit(allocator); + } + + try child.spawn(); + switch (options.output) { + .capture => try child.collectOutput(allocator, &stdout, &stderr, 50 * 1024), + .stream => {}, + } + + if (!options.wait) return; + + const result = std.process.Child.RunResult{ + .term = try child.wait(), + .stdout = try stdout.toOwnedSlice(allocator), + .stderr = try stderr.toOwnedSlice(allocator), }; defer allocator.free(result.stdout); defer allocator.free(result.stderr); diff --git a/demo/.gitignore b/demo/.gitignore index 1ddcd8a..0d1d8f7 100644 --- a/demo/.gitignore +++ b/demo/.gitignore @@ -4,3 +4,4 @@ static/ src/app/views/**/.*.zig .DS_Store log/ +src/routes.zig diff --git a/demo/src/app/views/channels/index.zmpl b/demo/src/app/views/channels/index.zmpl new file mode 100644 index 0000000..76457d0 --- /dev/null +++ b/demo/src/app/views/channels/index.zmpl @@ -0,0 +1,3 @@ +
+ Content goes here +
diff --git a/demo/src/app/views/root.zig b/demo/src/app/views/root.zig index 488b29b..68e5ce5 100644 --- a/demo/src/app/views/root.zig +++ b/demo/src/app/views/root.zig @@ -12,6 +12,7 @@ pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { try root.put("imported_number", importedFunction(100, 200, 300)); try request.response.headers.append("x-example-header", "example header value"); + try request.server.logger.INFO("data", .{}); return request.render(.ok); } diff --git a/demo/src/app/views/root/index.zmpl b/demo/src/app/views/root/index.zmpl index 1fb1e42..f720ea6 100644 --- a/demo/src/app/views/root/index.zmpl +++ b/demo/src/app/views/root/index.zmpl @@ -4,7 +4,7 @@
- @partial root/quotes(message: .message) + @partial root/quotes(message: $.message)
diff --git a/src/jetzig/App.zig b/src/jetzig/App.zig index 3ee7cea..268aafb 100644 --- a/src/jetzig/App.zig +++ b/src/jetzig/App.zig @@ -24,8 +24,7 @@ const AppOptions = struct { }; /// Starts an application. `routes` should be `@import("routes").routes`, a generated file -/// automatically created at build time. `templates` should be -/// `@import("src/app/views/zmpl.manifest.zig").templates`, created by Zmpl at compile time. +/// automatically created at build time. pub fn start(self: *const App, routes_module: type, options: AppOptions) !void { defer self.env.deinit(); @@ -35,7 +34,7 @@ pub fn start(self: *const App, routes_module: type, options: AppOptions) !void { defer mime_map.deinit(); try mime_map.build(); - const routes = try createRoutes(self.allocator, &routes_module.routes); + const routes = try createRoutes(self.allocator, if (@hasDecl(routes_module, "routes")) &routes_module.routes else &.{}); defer { for (routes) |var_route| { var_route.deinitParams(); @@ -87,8 +86,8 @@ pub fn start(self: *const App, routes_module: type, options: AppOptions) !void { self.env, routes, self.custom_routes.items, - &routes_module.jobs, - &routes_module.mailers, + if (@hasDecl(routes_module, "jobs")) &routes_module.jobs else &.{}, + if (@hasDecl(routes_module, "jobs")) &routes_module.mailers else &.{}, &mime_map, &store, &job_queue, @@ -107,8 +106,8 @@ pub fn start(self: *const App, routes_module: type, options: AppOptions) !void { .vars = self.env.vars, .environment = self.env.environment, .routes = routes, - .jobs = &routes_module.jobs, - .mailers = &routes_module.mailers, + .jobs = if (@hasDecl(routes_module, "jobs")) &routes_module.jobs else &.{}, + .mailers = if (@hasDecl(routes_module, "jobs")) &routes_module.mailers else &.{}, .store = &store, .cache = &cache, .repo = &repo, @@ -132,7 +131,7 @@ pub fn start(self: *const App, routes_module: type, options: AppOptions) !void { return; }, else => { - try server.logger.ERROR("Encountered error: {}\nExiting.\n", .{err}); + try server.logger.ERROR("Encountered error at server launch: {}\nExiting.\n", .{err}); std.process.exit(1); }, } diff --git a/src/jetzig/http/Server.zig b/src/jetzig/http/Server.zig index 4ba090c..9fd9ce1 100644 --- a/src/jetzig/http/Server.zig +++ b/src/jetzig/http/Server.zig @@ -110,6 +110,8 @@ pub fn listen(self: *Server) !void { self.initialized = true; + try jetzig.http.middleware.afterLaunch(self); + return try httpz_server.listen(); } @@ -151,7 +153,23 @@ pub fn processNextRequest( try request.process(); var middleware_data = try jetzig.http.middleware.afterRequest(&request); + if (try maybeMiddlewareRender(&request, &response)) { + try self.logger.logRequest(&request); + return; + } + try self.renderResponse(&request); + try request.response.headers.append("Content-Type", response.content_type); + + try jetzig.http.middleware.beforeResponse(&middleware_data, &request); + try request.respond(); + try jetzig.http.middleware.afterResponse(&middleware_data, &request); + jetzig.http.middleware.deinit(&middleware_data, &request); + + try self.logger.logRequest(&request); +} + +fn maybeMiddlewareRender(request: *jetzig.http.Request, response: *const jetzig.http.Response) !bool { if (request.middleware_rendered) |_| { // Request processing ends when a middleware renders or redirects. if (request.redirect_state) |state| { @@ -162,17 +180,8 @@ pub fn processNextRequest( } try request.response.headers.append("Content-Type", response.content_type); try request.respond(); - } else { - try self.renderResponse(&request); - try request.response.headers.append("Content-Type", response.content_type); - - try jetzig.http.middleware.beforeResponse(&middleware_data, &request); - try request.respond(); - try jetzig.http.middleware.afterResponse(&middleware_data, &request); - jetzig.http.middleware.deinit(&middleware_data, &request); - } - - try self.logger.logRequest(&request); + return true; + } else return false; } fn renderResponse(self: *Server, request: *jetzig.http.Request) !void { diff --git a/src/jetzig/http/middleware.zig b/src/jetzig/http/middleware.zig index e049a17..e0a4a15 100644 --- a/src/jetzig/http/middleware.zig +++ b/src/jetzig/http/middleware.zig @@ -48,6 +48,14 @@ pub fn Type(comptime name: MiddlewareEnum()) type { } } +pub fn afterLaunch(server: *jetzig.http.Server) !void { + inline for (middlewares) |middleware| { + if (comptime @hasDecl(middleware, "afterLaunch")) { + try middleware.afterLaunch(server); + } + } +} + pub fn afterRequest(request: *jetzig.http.Request) !MiddlewareData { var middleware_data = MiddlewareData.init(0) catch unreachable; From 06674db51fed921159fc2f3e3af65707a6c0ed12 Mon Sep 17 00:00:00 2001 From: Bob Farrell Date: Sun, 2 Mar 2025 13:12:29 +0000 Subject: [PATCH 3/6] Use fork of zig-args (temporary) Revert when [this PR](https://github.com/ikskuh/zig-args/pull/67) is merged. --- build.zig.zon | 4 ++-- cli/build.zig.zon | 4 ++-- demo/src/app/views/root.zig | 5 ++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index cf6fc5c..072f37e 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -32,8 +32,8 @@ .hash = "smtp_client-0.0.1-AAAAAIJkAQCngHtRYVUMsMuncmicSHK_7ugwWibDzQ4S", }, .args = .{ - .url = "https://github.com/ikskuh/zig-args/archive/968258dc1b1230493d8f1677097c832a3d7e0bd8.tar.gz", - .hash = "1220bdedf1a993d852d8aebcd63922a8fb163fac37b9c6ff72d187b2847a4a3a4248", + .url = "https://github.com/bobf/zig-args/archive/88cbade9a517a4014824f8f53f3c48c8a0b2ffe1.tar.gz", + .hash = "zig_args-0.0.0-jqtN6P_NAAC97fGpk9hS2K681jkiqPsWP6w3ucb_ctGH", }, }, diff --git a/cli/build.zig.zon b/cli/build.zig.zon index 67a3c63..c812754 100644 --- a/cli/build.zig.zon +++ b/cli/build.zig.zon @@ -10,8 +10,8 @@ .hash = "1220bdedf1a993d852d8aebcd63922a8fb163fac37b9c6ff72d187b2847a4a3a4248", }, .jetquery = .{ - .url = "https://github.com/jetzig-framework/jetquery/archive/55ebed84ad80d3d6c6e026c06e37b7de22168e7b.tar.gz", - .hash = "1220715b9064087cdc114e1a6a087e56d242cea56bc5759b740f4c2d5c1765822add", + .url = "https://github.com/jetzig-framework/jetquery/archive/795136c43f6d5bd216c136748bafd22892277725.tar.gz", + .hash = "jetquery-0.0.0-TNf3zqZFBgDS2CaIG4DvLwaiGeAxxbfAi-DnEnSKx-mf", }, }, .paths = .{ diff --git a/demo/src/app/views/root.zig b/demo/src/app/views/root.zig index 68e5ce5..23386dc 100644 --- a/demo/src/app/views/root.zig +++ b/demo/src/app/views/root.zig @@ -5,14 +5,13 @@ const importedFunction = @import("../lib/example.zig").exampleFunction; pub const layout = "application"; -pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View { - var root = try data.object(); +pub fn index(request: *jetzig.Request) !jetzig.View { + var root = try request.data(.object); try root.put("message", "Welcome to Jetzig!"); try root.put("custom_number", customFunction(100, 200, 300)); try root.put("imported_number", importedFunction(100, 200, 300)); try request.response.headers.append("x-example-header", "example header value"); - try request.server.logger.INFO("data", .{}); return request.render(.ok); } From 9c4b6198cd8d156d5029010dd8187db76a7e0942 Mon Sep 17 00:00:00 2001 From: Bob Farrell Date: Sun, 2 Mar 2025 13:49:54 +0000 Subject: [PATCH 4/6] Use llvm on MacOS Experimental feature currently only tested with Linux, builds failing for Mac. --- build.zig | 7 +++++-- cli/build.zig.zon | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/build.zig b/build.zig index 09e1433..9e18463 100644 --- a/build.zig +++ b/build.zig @@ -5,6 +5,9 @@ pub const GenerateMimeTypes = @import("src/GenerateMimeTypes.zig"); const zmpl_build = @import("zmpl"); const Environment = enum { development, testing, production }; +const builtin = @import("builtin"); + +const use_llvm_default = builtin.os.tag == .macos; pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); @@ -36,7 +39,7 @@ pub fn build(b: *std.Build) !void { .{ .target = target, .optimize = optimize, - .use_llvm = b.option(bool, "use_llvm", "Use LLVM") orelse false, + .use_llvm = b.option(bool, "use_llvm", "Use LLVM") orelse use_llvm_default, .zmpl_templates_paths = templates_paths, .zmpl_auto_build = false, .zmpl_markdown_fragments = try generateMarkdownFragments(b), @@ -135,7 +138,7 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn const target = exe.root_module.resolved_target orelse @panic("Unable to detect compile target."); const optimize = exe.root_module.optimize orelse .Debug; - exe.use_llvm = exe.use_llvm orelse (optimize != .Debug); + exe.use_llvm = exe.use_llvm orelse use_llvm_default; if (optimize != .Debug) exe.linkLibC(); diff --git a/cli/build.zig.zon b/cli/build.zig.zon index c812754..75d1776 100644 --- a/cli/build.zig.zon +++ b/cli/build.zig.zon @@ -6,8 +6,8 @@ .dependencies = .{ .args = .{ - .url = "https://github.com/ikskuh/zig-args/archive/968258dc1b1230493d8f1677097c832a3d7e0bd8.tar.gz", - .hash = "1220bdedf1a993d852d8aebcd63922a8fb163fac37b9c6ff72d187b2847a4a3a4248", + .url = "https://github.com/bobf/zig-args/archive/88cbade9a517a4014824f8f53f3c48c8a0b2ffe1.tar.gz", + .hash = "zig_args-0.0.0-jqtN6P_NAAC97fGpk9hS2K681jkiqPsWP6w3ucb_ctGH", }, .jetquery = .{ .url = "https://github.com/jetzig-framework/jetquery/archive/795136c43f6d5bd216c136748bafd22892277725.tar.gz", From 34186982202bad777471e7c7ab996f0bf9a9ea47 Mon Sep 17 00:00:00 2001 From: Bob Farrell Date: Sun, 2 Mar 2025 14:05:01 +0000 Subject: [PATCH 5/6] Use LLVM everywhere except Linux Zig Compiler seems not ready for Windows yet (builds failing), restricting to Linux for now, we can add other OSes as the compiler matures. --- build.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.zig b/build.zig index 9e18463..f54d3fe 100644 --- a/build.zig +++ b/build.zig @@ -7,7 +7,7 @@ const zmpl_build = @import("zmpl"); const Environment = enum { development, testing, production }; const builtin = @import("builtin"); -const use_llvm_default = builtin.os.tag == .macos; +const use_llvm_default = builtin.os.tag != .linux; pub fn build(b: *std.Build) !void { const target = b.standardTargetOptions(.{}); @@ -238,7 +238,6 @@ pub fn jetzigInit(b: *std.Build, exe: *std.Build.Step.Compile, options: JetzigIn exe_routes_file.root_module.addImport("zmpl", zmpl_module); const run_routes_file_cmd = b.addRunArtifact(exe_routes_file); - run_routes_file_cmd.has_side_effects = true; const routes_file_path = run_routes_file_cmd.addOutputFileArg("routes.zig"); run_routes_file_cmd.addArgs(&.{ root_path, From 7eea1e3f0a7e2149e816843c365224b1f38c6324 Mon Sep 17 00:00:00 2001 From: Bob Farrell Date: Sun, 2 Mar 2025 14:14:42 +0000 Subject: [PATCH 6/6] Minor CLI improvements Show some progress "..." when running external commands to give the user a bit of extra feedback while commands run. --- build.zig.zon | 4 ++-- cli/commands/bundle.zig | 2 +- cli/commands/init.zig | 2 +- cli/util.zig | 53 +++++++++++++++++++++++++++++++++++++---- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 072f37e..2fb196d 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -16,8 +16,8 @@ .hash = "jetcommon-0.1.0-jPY_DS1HAAAP8xp5HSWB_ZY7m9JEYUmm8adQFrse0lwB", }, .zmpl = .{ - .url = "https://github.com/jetzig-framework/zmpl/archive/d34fd5aa69cc5f68dc73b1ef844f7cbdebdfc93e.tar.gz", - .hash = "zmpl-0.0.1-SYFGBhRpAwAIJWGukw-MWyAbXq8WCDudBjAAerZ8jZ_u", + .url = "https://github.com/jetzig-framework/zmpl/archive/3ec11289fdee2e0c70975cb5dd85d3041d723912.tar.gz", + .hash = "zmpl-0.0.1-SYFGBuZoAwAMuvHNkO_1BbutpWhg7CdSgYd8t4OaaZeR", }, .zmd = .{ .url = "https://github.com/jetzig-framework/zmd/archive/d6c8aa9a9cde99674ccb096d8f94ed09cba8dab.tar.gz", diff --git a/cli/commands/bundle.zig b/cli/commands/bundle.zig index e689e56..86d44cf 100644 --- a/cli/commands/bundle.zig +++ b/cli/commands/bundle.zig @@ -155,7 +155,7 @@ pub fn run( .windows => {}, else => std.debug.print("Bundle `bundle.tar.gz` generated successfully.", .{}), } - util.printSuccess(); + util.printSuccess(null); } fn locateMarkdownFiles(allocator: std.mem.Allocator, dir: std.fs.Dir, views_path: []const u8, paths: *std.ArrayList([]const u8)) !void { diff --git a/cli/commands/init.zig b/cli/commands/init.zig index 3b960ec..42ae81e 100644 --- a/cli/commands/init.zig +++ b/cli/commands/init.zig @@ -261,7 +261,7 @@ fn copySourceFile( util.printFailure(); return err; }; - util.printSuccess(); + util.printSuccess(null); } // Read a file from Jetzig source code. diff --git a/cli/util.zig b/cli/util.zig index f058881..1cba0f1 100644 --- a/cli/util.zig +++ b/cli/util.zig @@ -23,8 +23,8 @@ const icons = .{ }; /// Print a success confirmation. -pub fn printSuccess() void { - std.debug.print(" " ++ icons.check ++ "\n", .{}); +pub fn printSuccess(message: ?[]const u8) void { + std.debug.print(" " ++ icons.check ++ " {s}\n", .{message orelse ""}); } /// Print a failure confirmation. @@ -198,7 +198,7 @@ pub fn runCommandInDir(allocator: std.mem.Allocator, argv: []const []const u8, d try child.spawn(); switch (options.output) { - .capture => try child.collectOutput(allocator, &stdout, &stderr, 50 * 1024), + .capture => try collectOutput(child, allocator, &stdout, &stderr), .stream => {}, } @@ -222,10 +222,55 @@ pub fn runCommandInDir(allocator: std.mem.Allocator, argv: []const []const u8, d } return error.JetzigCommandError; } else { - printSuccess(); + printSuccess(try std.mem.join(allocator, " ", argv)); } } +fn collectOutput( + child: std.process.Child, + allocator: std.mem.Allocator, + stdout: *std.ArrayListUnmanaged(u8), + stderr: *std.ArrayListUnmanaged(u8), +) !void { + const max_output_bytes = 50 * 1024; + std.debug.assert(child.stdout_behavior == .Pipe); + std.debug.assert(child.stderr_behavior == .Pipe); + var poller = std.io.poll(allocator, enum { stdout, stderr }, .{ + .stdout = child.stdout.?, + .stderr = child.stderr.?, + }); + defer poller.deinit(); + + std.debug.print("(working) .", .{}); + + while (try poller.poll()) { + if (poller.fifo(.stdout).count > max_output_bytes) + return error.StdoutStreamTooLong; + if (poller.fifo(.stderr).count > max_output_bytes) + return error.StderrStreamTooLong; + std.debug.print(".", .{}); + } + + std.debug.print(" (done)\n", .{}); + + try writeFifoDataToArrayList(allocator, stdout, poller.fifo(.stdout)); + try writeFifoDataToArrayList(allocator, stderr, poller.fifo(.stderr)); +} + +// Borrowed from `std.process.Child.writeFifoDataToArrayList` - non-public but needed in our +// modified `collectOutput` +fn writeFifoDataToArrayList(allocator: std.mem.Allocator, list: *std.ArrayListUnmanaged(u8), fifo: *std.io.PollFifo) !void { + if (fifo.head != 0) fifo.realign(); + if (list.capacity == 0) { + list.* = .{ + .items = fifo.buf[0..fifo.count], + .capacity = fifo.buf.len, + }; + fifo.* = std.io.PollFifo.init(fifo.allocator); + } else { + try list.appendSlice(allocator, fifo.buf[0..fifo.count]); + } +} /// Generate a full GitHub URL for passing to `zig fetch`. pub fn githubUrl(allocator: std.mem.Allocator) ![]const u8 { var client = std.http.Client{ .allocator = allocator };