Brutalist Blog Generator

Because, often, less is more


Progress: file watching and local server

2025-04-26

We’ve got progress! The first thing I wanted to tackle after making bbg public was to add file watching and local server functionality. This allowed me to see changes in real-time and test my code locally without having to constantly run bbg build.

File watching

First I tried to see if there was something simple which would work out of the box, but I’m either bad at searching or didn’t quite find what I was looking for. I did find FileMonitor but for some reason it would enter some infinite loop when I ran it. I was probably Using It Wrong (TM). Since one of the goals of this project was to learn Swift, I thought I’d implement it myself (with some AI assistance).

The plan is:

We are going to use DispatchSource which works on both macOS and Linux, though this is not obvious from the documentation, which I found confusing.

On macOS we can use makeFileSystemObjectSource directly to monitor the directory and each file, but on Linux we’ll need to use the Glibc or Musl libraries to use the inotify API and then use the makeReadSource and manually read the inotify event.

In the end it turned out ok and thus now you can do bbg build --watch which will build the site and rebuild it when any changes occur.

Local server

The next obvious step is to have a local server which will serve the site. This will allow us to see the site in a browser quickly while writing a post, for example.

The candidates for such a task were Vapor and Hummingbird. I had already looked at the former a bit, since it has a templating library I was interested in and ended up not using, but it felt very heavy for the little task I needed, so I went with Hummingbird. It also has a great name!

Hummingbird was a breeze to integrate, this is all it took to serve the static files off the build directory:

let router = Router()
router.add(middleware: FileMiddleware(builder.config.outputDir, searchForIndexHtml: true))

let app = Application(
    router: router,
    configuration: .init(address: .hostname(hostname, port: port))
)

try await app.runService()

You can see how the web server and file watching interact in the ServeCommand.swift file. With that we now have bbg serve which does exactly what you expect! 🎉

↩ Back