Lossless Changelog
product

The build that only worked on my machine

Vercel moved to Node 24 and the deploy broke — not on Node, but on three fonts this repo never declared and was borrowing from its parent.

Michael StatonMusic Editoraugmented with Claude Code on Claude Opus 5 (1M context)

The build that only worked on my machine

Why Care?

Vercel moved its default runtime to Node 24, so we went to bump ours. The deploy was already failing — and not because of Node:

[vite]: Rolldown failed to resolve import "@fontsource-variable/bodoni-moda"
        from "/vercel/path0/src/layouts/BaseLayout.astro"

That import has been in BaseLayout.astro since the typography landed. It has never been in package.json. It built fine locally every single time, because Node resolution walks up the directory tree and found the package in astro-knots/node_modules — the parent pseudomonorepo's install, one level above this repo.

Vercel clones the site repo alone. There is no parent. There never was.

This is the failure mode the whole astro-knots independence rule exists to prevent: a site must build from its own directory, with nothing above it. The rule was written down. Nothing was checking it.

What's New?

  • Node 24. engines moves to >=24.0.0 and a .nvmrc pins 24, verified against a real local Node 24.19.0 rather than declared and hoped for.

  • Three fonts and a syntax highlighter, finally declared. @fontsource-variable/bodoni-moda, figtree, jetbrains-mono at 5.3.0, and shiki at 4.4.3.

  • Astro 7.2.0 → 7.2.4.

  • An audit that would have caught this, run over every bare import in src/.

How it Works

Phantom dependencies, and why local builds lie

A phantom dependency is a package your code imports and your manifest never mentions. It works right up until something changes the resolution context — a different install layout, a hoisting flag, or in this case a machine that doesn't have the parent directory at all.

shamefully-hoist=true in this repo's .npmrc makes it likelier, but it wasn't the cause. The fonts weren't in this repo's node_modules or its lockfile:

bodoni-moda      MISSING
figtree          MISSING
jetbrains-mono   MISSING
lockfile         NOT IN LOCKFILE — resolving by accident

They were at astro-knots/node_modules/@fontsource-variable/, and every local build had been quietly reaching a level up to get them.

shiki was the same story with a different sponsor — src/lib/highlighter.ts imports it directly, and it survived only because Astro happens to bundle it as a transitive dependency. Astro dropping or relocating it would have broken the highlighter with no change on our side.

The check that should have existed

Rather than fix three imports and move on, we now walk every .astro, .ts and .mjs file under src/, collect every bare specifier, and diff against the manifest:

UNDECLARED: mdast, shiki, Michael P. Staton

Three results, three verdicts. shiki is real — declared. mdast is a type-only import satisfied by @types/mdast, which is declared, and erased before it reaches the bundler. Michael P. Staton is an author line in a changelog entry that the regex mistook for an import, which is a decent reminder that a scanner is not a parser.

Under the Hood

TypeScript 7 is blocked, and we reverted it

TypeScript 7.0.2 installs and the site builds, but astro check dies:

The TypeScript module loaded (found 7.0.2) does not expose the programmatic API that astro check relies on. TypeScript's native compiler (7.0 and later) does not ship this API yet.

The native Go compiler doesn't implement the API @astrojs/language-server depends on. Astro tracks it in [roadmap discussion #1321][1].

We reverted to 6.0.3. Shipping TypeScript 7 would have meant a repo with no working type checker in exchange for nothing a reader of this site could see — a bad trade. It goes back on the list when Astro ships support.

Node 24 was the easy part

Zero code changes. Astro 7.2.4, Vite 8, and Rolldown all run clean on 24.19.0 — 483 pages, astro check at 0 errors. The runtime bump that prompted all of this turned out to be the only part of it that just worked.

Files Touched

  • package.json — engines to >=24.0.0; four new dependencies; astro 7.2.4

  • .nvmrc — new, pins 24 so Vercel doesn't decide for us again

  • pnpm-lock.yaml — the fonts and shiki now actually recorded

What's Next

  • The import audit is a script that ran once in a shell. It belongs in the repo as pnpm check:deps, and in CI, or the next phantom lands the same way.

  • Every other astro-knots site has the same .npmrc and the same parent directory. This repo is unlikely to be the only borrower.

  • TypeScript 7 when astro check supports it.

References

Sources

  1. [1]
    Astro roadmap — TypeScript 7 supportgithub.comPublished: 2026-08-19