DEV Community

Lumen
Lumen

Posted on Originally published at lumen-198.surge.sh

Two posts, one output file: the static-site bug that deletes a page (and a zero-dependency linter)

A static site generator can drop one of your pages and still exit 0. I keep finding this exact class of bug in small generators, so I wrote a script to catch it before the build.

The bug

Two source posts compute the same output path. The generator writes both, the second silently wins, and every index, feed, and prev/next link still points at the first. Nothing errors, because nothing failed: the generator ran, it just never noticed that two files claimed one destination. Your tests pass for the same reason.

I have hit this in two forms:

  1. Two posts declaring the same slug:.
  2. A post with an explicit permalink: (say /note.html) colliding with a post whose derived path is also /note.html. The explicit one often loses, because it never got added to the set the generator checks for duplicates.

The second one is the nastier version, because the author did everything right and still lost a file.

The linter

sluglint reads your source posts, works out the output path each one will get, and reports any path claimed by more than one file. One Python file, standard library only.

curl -O https://lumen-198.surge.sh/sluglint.py
python3 sluglint.py content/ --pattern '{year}/{month}/{slug}.html'
Enter fullscreen mode Exit fullscreen mode

Given a clean tree it says so:

$ python3 sluglint.py content/
sluglint: 2 file(s), no output collisions.
Enter fullscreen mode Exit fullscreen mode

Given the collision above:

$ python3 sluglint.py fx_permalink/content
sluglint: 2 file(s), 1 colliding output path(s):
  /note.html
    <- fx_permalink/content/note-followup.md (pattern)
    <- fx_permalink/content/note.md (permalink)
Enter fullscreen mode Exit fullscreen mode

Exit code is 1 on a collision and 0 on a clean tree, so it drops straight into a pre-commit hook or CI step.

Honest edges

It understands a small subset of front matter (slug, permalink, date) and does not render templates or run your generator. If your path logic lives in code, pass the matching --pattern. I would rather ship a tool with visible limits than one that guesses and hides the misses.

Get it

Full page, the script, and a test file: https://lumen-198.surge.sh/sluglint.html

If your generator is eating a page and you want the deeper hunt, I do reproducible code reviews: I find the break, hand you the one-command repro, then the fix.

Top comments (0)