diff --git a/.changeset/fix-prerender-malformed-urls.md b/.changeset/fix-prerender-malformed-urls.md new file mode 100644 index 000000000000..1b7b399d62fc --- /dev/null +++ b/.changeset/fix-prerender-malformed-urls.md @@ -0,0 +1,5 @@ +--- +"@sveltejs/kit": patch +--- + +fix: prevent prerender memory issues with malformed URLs in meta tags \ No newline at end of file diff --git a/packages/kit/src/core/postbuild/prerender.js b/packages/kit/src/core/postbuild/prerender.js index bd815b37ba36..439bf1754bba 100644 --- a/packages/kit/src/core/postbuild/prerender.js +++ b/packages/kit/src/core/postbuild/prerender.js @@ -221,6 +221,26 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { const file = decoded.slice(config.paths.base.length + 1); if (files.has(file)) return; + // Check for URLs that contain the prerender origin hostname in the path + // This can happen when meta tags reference URLs like "{page.url.host}/file.jpg" + // and can cause infinite loops during prerendering + if (config.prerender.origin !== 'http://sveltekit-prerender') { + // Extract hostname from prerender origin + const prerenderHost = new URL(config.prerender.origin).hostname; + // Check if the path starts with the prerender hostname (avoiding circular references) + if (decoded.startsWith(`/${prerenderHost}/`)) { + // This looks like a circular reference - handle as 404 + handle_http_error({ status: 404, path: decoded, referrer, referenceType: 'linked' }); + return; + } + } else { + // For the default sveltekit-prerender origin, check for the specific pattern + if (decoded.startsWith('/sveltekit-prerender/')) { + handle_http_error({ status: 404, path: decoded, referrer, referenceType: 'linked' }); + return; + } + } + return q.add(() => visit(decoded, encoded || encodeURI(decoded), referrer, generated_from_id)); } @@ -258,7 +278,13 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { } // stuff in `static` - return readFileSync(join(config.files.assets, file)); + const static_path = join(config.files.assets, file); + // Check if the static file exists before trying to read it + // This prevents potential issues with malformed paths or non-existent files + if (!existsSync(static_path)) { + throw new Error(`Static file not found: ${file}`); + } + return readFileSync(static_path); }, emulator }); diff --git a/packages/kit/test/prerendering/basics/src/routes/circular-ref/+page.svelte b/packages/kit/test/prerendering/basics/src/routes/circular-ref/+page.svelte new file mode 100644 index 000000000000..9fd6dd27bd34 --- /dev/null +++ b/packages/kit/test/prerendering/basics/src/routes/circular-ref/+page.svelte @@ -0,0 +1,18 @@ + + + + + + + + + +

Circular Reference Test

+

This page tests that circular references in meta tags don't cause infinite loops

diff --git a/packages/kit/test/prerendering/basics/test/tests.spec.js b/packages/kit/test/prerendering/basics/test/tests.spec.js index e3972a1d369d..e12701b78b0f 100644 --- a/packages/kit/test/prerendering/basics/test/tests.spec.js +++ b/packages/kit/test/prerendering/basics/test/tests.spec.js @@ -286,3 +286,11 @@ test('identifies missing ids', () => { const missing_ids = JSON.parse(`[${missing_ids_content.slice(0, -1)}]`); expect(missing_ids).toEqual(['missing-id']); }); + +test('handles circular references in meta tags without infinite loops', () => { + // This test verifies that the circular-ref route was prerendered successfully + // without causing memory issues or infinite loops + const content = read('circular-ref.html'); + expect(content).toMatch('

Circular Reference Test

'); + expect(content).toMatch('This page tests that circular references in meta tags'); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f440d5b37f90..f4b1ccf46d7f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -915,6 +915,27 @@ importers: specifier: 'catalog:' version: 6.3.6(@types/node@18.19.119)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) + packages/kit/test/build-errors/apps/prerender-meta-static-reference: + devDependencies: + '@sveltejs/kit': + specifier: workspace:^ + version: link:../../../.. + '@sveltejs/vite-plugin-svelte': + specifier: 'catalog:' + version: 6.0.0-next.3(svelte@5.39.3)(vite@6.3.6(@types/node@18.19.119)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)) + svelte: + specifier: 'catalog:' + version: 5.39.3 + svelte-check: + specifier: 'catalog:' + version: 4.1.1(picomatch@4.0.3)(svelte@5.39.3)(typescript@5.8.3) + typescript: + specifier: ^5.5.4 + version: 5.8.3 + vite: + specifier: 'catalog:' + version: 6.3.6(@types/node@18.19.119)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) + packages/kit/test/build-errors/apps/prerender-remote-function-error: devDependencies: '@sveltejs/adapter-auto':