From 596a5bf6c96f5d512970173968ecd7be9a7c15fb Mon Sep 17 00:00:00 2001 From: svemagie <869694+svemagie@users.noreply.github.com> Date: Thu, 19 Mar 2026 22:05:02 +0100 Subject: [PATCH] docs: address spec review feedback on build performance design --- .../specs/2026-03-19-eleventy-build-performance-design.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/superpowers/specs/2026-03-19-eleventy-build-performance-design.md b/docs/superpowers/specs/2026-03-19-eleventy-build-performance-design.md index 7e89b89..11ca26d 100644 --- a/docs/superpowers/specs/2026-03-19-eleventy-build-performance-design.md +++ b/docs/superpowers/specs/2026-03-19-eleventy-build-performance-design.md @@ -39,6 +39,8 @@ DEBUG=Eleventy:Benchmark* npm run build 2>&1 | grep -E "Benchmark|og|unfurl" This captures both Eleventy's internal timings (collections, transforms, filters, rendering) and the two before-hook timings in a single build run. +**Note on OG subprocess output:** The OG hook uses `execFileSync` with `stdio: "inherit"`, which means the subprocess writes directly to the parent's stdout/stderr — its output bypasses any pipe or grep on the parent process. The `console.time("[og] image generation")` and `console.timeEnd(...)` calls on the parent process measure the full wall time of the entire subprocess-spawn loop and will appear in filtered output. Individual per-batch log lines from the subprocess itself (`[og] Generating...`) will appear unfiltered inline. + --- ### 2. `hash` filter memoization @@ -47,7 +49,7 @@ This captures both Eleventy's internal timings (collections, transforms, filters **Problem:** `readFileSync()` + MD5 on every call. With 500+ pages each calling `| hash` for 2–3 asset paths, this is ~1000+ redundant disk reads per build. -**Fix:** Wrap with a `Map` cache, cleared on `eleventy.before`: +**Fix:** Wrap with a `Map` cache, cleared on `eleventy.before`. Declare `const _hashCache = new Map()` inside the config export function body, immediately before the `addFilter("hash", ...)` call — matching the placement of `_dateDisplayCache` at line 550 (not at module scope): ```js const _hashCache = new Map(); @@ -123,6 +125,7 @@ eleventyConfig.on("eleventy.before", async () => { - Manifest stored in `.cache/` — same gitignore and passthrough-copy rules as existing unfurl cache. - `writeFileSync` is used (not async) to guarantee the manifest is written even if the process exits unexpectedly mid-batch. On failure, next build falls back to full prefetch. - The content walk still runs on every build (cheap: local FS reads of small frontmatter). Only the prefetch calls are skipped. +- Writing `[...urls]` (not `[...seen, ...newUrls]`) is intentional: it prunes URLs for soft-deleted posts on the next prefetch-triggering build, preventing unbounded manifest growth. --- @@ -141,5 +144,5 @@ No new files. No new dependencies. 1. `console.time` output visible in production build logs. 2. `DEBUG=Eleventy:Benchmark*` output available for baseline comparison. 3. On a build with no new posts: `[unfurl] No new URLs — skipping prefetch` logged. -4. Hash filter: same cache-busting behaviour as before (values change when files change between builds). +4. Hash filter: modify a referenced asset (e.g. `css/style.css`) between two builds and confirm the `?v=` query string changes in rendered HTML. No log output expected — verify via view-source or build diff. 5. No change to rendered output.