download patch
commit c344489275666cf4e6c06c0f7f8e7705b563dd68
Author: tri <tri@thac.loan>
Date: Sat Oct 11 22:52:23 2025 +0700
bail early if metadata not found; add readme
diff --git a/Makefile b/Makefile
index a359cf5..87612c5 100644
--- a/Makefile
+++ b/Makefile
+build:
+ zig build -Doptimize=ReleaseSafe
+
+watch:
+ zig build run --watch -- sample
+
test:
zig build test --watch
diff --git a/build.zig b/build.zig
index 6d3b5df..c909164 100644
--- a/build.zig
+++ b/build.zig
pub fn build(b: *std.Build) void {
const lua_dep = b.dependency("zlua", .{
.target = target,
.optimize = optimize,
- .shared = true,
});
exe.root_module.addImport("zlua", lua_dep.module("zlua"));
diff --git a/readme.md b/readme.md
index 6972efc..ffa6e7e 100644
--- a/readme.md
+++ b/readme.md
-WIP: static blog generator
+loa (Vietnamese, noun): megaphone
+
+## What
+
+Loa is a dead simple static site generator that I use for my blog at <https://loa.thac.loan>.
+
+````sh
+mkdir mysite
+cd mysite
+
+# Write site metadata
+cat > _loa_meta.json << EOF
+{
+ "site_name": "Loa Sample Site",
+ "tagline": "Who samples the samplers?"
+}
+EOF
+
+# Create a post.
+# Loa assumes frontmatter followed by djot markup: https://djot.net/
+mkdir first
+cat > first/index.dj << 'EOF'
+```
+title: First
+published_at: 2025-10-09T21:13:03+07:00
+```
+
+## This is my very dope blog post
+
+A quick brown fox jumps over a lazy dog.
+EOF
+````
diff --git a/sample/meta.json b/sample/_loa_meta.json
similarity index 100%
rename from sample/meta.json
rename to sample/_loa_meta.json
diff --git a/sample/second/meta.json b/sample/second/meta.json
deleted file mode 100644
index 9261e08..0000000
--- a/sample/second/meta.json
+++ /dev/null
-{
- "title": "Secondly",
- "published_at": "2025-10-09T23:51:45+07:00"
-}
diff --git a/src/Meta.zig b/src/Meta.zig
index 1bead20..d90b7fe 100644
--- a/src/Meta.zig
+++ b/src/Meta.zig
const fs = std.fs;
const json = std.json;
const mem = std.mem;
+pub const file_path = "_loa_meta.json";
+
const Meta = @This();
site_name: []const u8,
tagline: []const u8,
pub fn load(arena: mem.Allocator, dir: fs.Dir) !Meta {
- const meta_text = try dir.readFileAlloc(arena, "meta.json", 1024 * 16);
+ const meta_text = try dir.readFileAlloc(arena, file_path, 1024 * 16);
return try json.parseFromSliceLeaky(Meta, arena, meta_text, .{
.allocate = .alloc_if_needed,
diff --git a/src/main.zig b/src/main.zig
index 9c6fdd3..1aa8461 100644
--- a/src/main.zig
+++ b/src/main.zig
pub fn main() !u8 {
},
}
- try entrypoint(target_dir);
-
- return 0;
+ return try entrypoint(target_dir);
}
-pub fn entrypoint(target_dir: [*:0]const u8) !void {
+pub fn entrypoint(target_dir: [*:0]const u8) !u8 {
var gpa_state = std.heap.DebugAllocator(.{}){};
const gpa = gpa_state.allocator();
defer _ = gpa_state.deinit();
+ var global_arena_state = std.heap.ArenaAllocator.init(gpa);
+ defer global_arena_state.deinit();
+ const global_arena = global_arena_state.allocator();
+
var dir = try fs.cwd().openDirZ(target_dir, .{ .iterate = true });
defer dir.close();
+ const meta = Meta.load(global_arena, dir) catch |err| {
+ switch (err) {
+ error.FileNotFound => {
+ println(
+ "Error: {s} not found. Please create it first.",
+ .{Meta.file_path},
+ );
+ return 1;
+ },
+ else => return err,
+ }
+ };
+
// Write static file(s)
{
var static_dir = try dir.makeOpenPath("_loa", .{});
pub fn entrypoint(target_dir: [*:0]const u8) !void {
{
defer _ = post_arena_state.reset(.retain_capacity);
- const meta = try Meta.load(post_arena, dir);
-
var home_file = try dir.createFile("index.html", .{});
defer home_file.close();
pub fn entrypoint(target_dir: [*:0]const u8) !void {
try templates.base_end(writer);
try writer.flush();
}
+ return 0;
}
test {