fix: build sitemap.xml statically#18802
Conversation
The sitemap route transitively calls getAppsData/getDeveloperToolsData, both wrapped with a finite revalidate, which opts the route into ISR. Netlify then re-renders it in the serverless function, where public/content is excluded from the bundle -- so getPostSlugs returns [] and the sitemap drops every markdown page and truncates nondeterministically. Add a no-revalidate getStaticDeveloperToolsData (mirroring the existing getStaticAppsData), point the sitemap's data reads at the static getters, and set force-static on app/sitemap.ts so it is generated at build time where public/content is present. Also guard getVideoSlugs against ENOENT to match getPostSlugs. Part 1 of #18014. The sitemap is now complete but still a single ~51MB file; per-locale sharding to get under Google's 50MB per-file limit follows in a stacked PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
✅ Deploy Preview for ethereumorg ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
🔎 First-pass review — ✅ Looks mergeableSurgical build fix: swaps the sitemap's transitive data reads to the AnalysisLane: code (build/data-caching infra) — Checked:
Non-blocking: As the description states, this only makes the sitemap complete — it's still a single ~51 MB file over Google's 50 MB limit until the Checks: Lint/type-check ✅, Unit ✅, Chromatic gate ✅; Visual regression + Netlify deploy in progress. The real verification surface (lambda empty-slug path) is the deploy preview, as noted.
|

Summary
Part 1 of #18014. Makes
/sitemap.xmlbuild statically so it stops rendering in the Netlify serverless function, wherepublic/content/is excluded from the bundle.Root cause
app/sitemap.ts->getAllPagesWithTranslations()->getDynamicIntlPagePaths()callsgetAppsData()andgetDeveloperToolsData(). Both arecreateCachedGetterwrappers with a finiterevalidate(CACHE_REVALIDATE_DAY), which opts the route into ISR. Netlify then re-renders the sitemap in the serverless function, wherepublic/content/is stripped from the bundle viaoutputFileTracingExcludes(next.config.js).getPostSlugs("/")hitsENOENT, returns[], and the sitemap silently drops every markdown-driven page and truncates nondeterministically.This is the same failure class already documented in
docs/solutions/integration-issues/netlify-isr-404-async-server-components.md; the repo already shipsgetStaticAppsData(revalidate: false) as the sanctioned escape hatch for exactly this.Changes
getStaticDeveloperToolsData(revalidate: false) mirroring the existinggetStaticAppsData.getStaticAppsData/getStaticDeveloperToolsData), removing the ISR trigger.export const dynamic = "force-static"onapp/sitemap.ts, deliberately withoutrevalidate(ISR would re-enter the broken lambda path; freshness rides the deploy cadence).getVideoSlugs()againstENOENTto matchgetPostSlugs, silencing the companion Grafana error.Scope / what this does NOT fix
The sitemap is now complete but is still a single ~51 MB file (17.5k URLs x ~26 hreflang alternates), which is over Google's 50 MB per-file limit and rejected on size. Per-locale sharding via
generateSitemaps()lands in a stacked follow-up PR and is required before the file is accepted. A CI regression guard (parse each shard, assert closing</urlset>, URL-count floor, sentinel late-alphabet URL) follows that.Verification
pnpm type-checkandpnpm lintpass.public/content/is always present in dev), so the deploy preview is the real verification surface.-- Opus 4.8