diff --git a/package.json b/package.json index fbdc00ca..eca6ee40 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "homepage", - "version": "1.86.1", + "version": "1.86.2", "private": true, "type": "module", "scripts": { diff --git a/src/hooks.server.ts b/src/hooks.server.ts index ea3b05c3..27867d82 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -1,6 +1,7 @@ import type { Handle, HandleServerError, ServerInit } from "@sveltejs/kit" import { redirect } from "@sveltejs/kit" import { sequence } from "@sveltejs/kit/hooks" +import { building } from "$app/environment" import * as auth from "./auth" import { initializeScheduler } from "./lib/server/scheduler" import { dbConnect } from "./utils/db" @@ -122,6 +123,15 @@ async function timing({ event, resolve }: Parameters[0]) { } export const init: ServerInit = async () => { + // SvelteKit runs prerendering/analysis inside a worker_threads worker (see + // @sveltejs/kit utils/fork.js) whose JS heap is capped well below the main + // thread's. `init` fires there too, so warming the romcal cache during a + // build exhausts that worker's heap → ERR_WORKER_OUT_OF_MEMORY and a failed + // build. None of it is needed at build time: no prerendered route touches the + // DB, and connecting to Mongo / starting the payment scheduler from a build + // is undesirable regardless. Skip startup work while building. + if (building) return; + console.log('🚀 Server starting - initializing database connection...'); try { await dbConnect(); diff --git a/src/routes/hikes/[slug]/+page.ts b/src/routes/hikes/[slug]/+page.ts index 3653fc2b..9d0ef368 100644 --- a/src/routes/hikes/[slug]/+page.ts +++ b/src/routes/hikes/[slug]/+page.ts @@ -4,7 +4,11 @@ import type { PageLoad } from './$types'; // Not prerendered: the page needs the live session so private images can be // gated behind login. Performance hit is small — the page is mostly hashed -// static assets (track JSON, image variants). +// static assets (track JSON, image variants). Without this flag the prerender +// crawler still followed the overview's hike links and rendered every detail +// page at build time, which exhausted the prerender worker's heap +// (ERR_WORKER_OUT_OF_MEMORY); `false` keeps the crawler from prerendering them. +export const prerender = false; // Glob the .svx modules so Vite can pre-bundle them and we can resolve // the matching one synchronously at load time. diff --git a/svelte.config.js b/svelte.config.js index 3a895ad3..4cd10587 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -21,6 +21,25 @@ const config = { adapter: adapter({ precompress: true // Enable brotli and gzip compression }), + prerender: { + // The only intentionally-static pages are /hikes (prerender=true) and + // the /errors/[status] set (via that route's EntryGenerator). With the + // crawler on, it follows the global nav out of /hikes and tries to + // prerender the whole dynamic, DB-/ML-backed app — which runs the + // forked prerender worker out of heap (ERR_WORKER_OUT_OF_MEMORY) and + // fails the build. Disable crawling: the prerendered set is then driven + // entirely by `prerender = true` + EntryGenerator. + crawl: false, + handleHttpError: ({ path, message }) => { + // Defensive: hike image binaries live in `hikes-assets/`, outside + // `/static` (nginx serves them in prod, a Vite middleware in dev — + // see vite.config.ts), so the crawler can't fetch them. Harmless + // while crawl is off, but keeps a 404 from failing the build if + // crawling is ever re-enabled. + if (/^\/hikes\/[^/]+\/images\//.test(path)) return; + throw new Error(message); + } + }, alias: { $models: 'src/models', $utils: 'src/utils',