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;