fix: prerendered root +server.js endpoints use dynamic file extensions, warn user if not html - #14135
fix: prerendered root +server.js endpoints use dynamic file extensions, warn user if not html#14135aewing wants to merge 10 commits into
Conversation
Fixes sveltejs#11087 When a root +server.js endpoint is prerendered with non-HTML content, it was incorrectly saved as 'index.html' due to a fallback in the output_filename function. This change: - Only defaults to 'index.html' for HTML content at the root path - Uses content-type headers to determine appropriate file extensions for non-HTML content (e.g., 'index.json' for JSON, 'index.txt' for text) - Adds a warning when prerendering root +server.js endpoints since most static hosts expect 'index.html' at the root - Includes tests to verify the fix works correctly
🦋 Changeset detectedLatest commit: 9671daa The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
- Add test for root +server.js endpoint returning JSON (saved as index.json) - Add test for root +server.js endpoint returning text/plain (saved as index.txt) - Remove incorrect nested endpoint tests that didn't actually test the root case - Tests verify the fix correctly generates index.json and index.txt instead of index.html
|
@eltigerchino thoughts on this approach? |
| // Common mime type to extension mappings | ||
| /** @type {Record<string, string>} */ | ||
| const mime_to_ext = { | ||
| 'application/json': '.json', | ||
| 'text/plain': '.txt', | ||
| 'text/css': '.css', | ||
| 'text/javascript': '.js', | ||
| 'application/javascript': '.js', | ||
| 'application/xml': '.xml', | ||
| 'text/xml': '.xml', | ||
| 'application/pdf': '.pdf', | ||
| 'image/svg+xml': '.svg' | ||
| }; | ||
|
|
||
| return mime_to_ext[mime] || ''; |
There was a problem hiding this comment.
We can probably use the mrmime package that's already installed to do this. So something like:
import { mimes } from 'mrmime';
// TODO: `mimes` is a map of file extensions to mime types but we want it the other way aroundThere was a problem hiding this comment.
I actually looked into this, but from what I recall mrmime doesn't export mime -> ext it's ext -> mime, so then we have to dedupe in some cases (jpeg, jpg) -- I felt like this was a safer API because we could easily reason about our commitments and support this way -- but happy to reverse-map the mrmime package and go with first/last match if that's what we'd prefer.
There was a problem hiding this comment.
jpeg/jpg is a weird example but you get my meaning
|
|
||
| // Special handling for root path | ||
| if (!file) { | ||
| if (is_html) { |
There was a problem hiding this comment.
Looking at how is_html is evaluated, I'm wondering why a redirect response automatically considers the file as a html file. We might need to rethink that one.
| if (content_type) { | ||
| const ext = get_extension_from_content_type(content_type); | ||
| return ext ? `index${ext}` : 'index'; | ||
| } |
There was a problem hiding this comment.
I wonder if we need this logic for non-root paths as well. We're already appending .html to paths missing it if the content-type header is html.
There was a problem hiding this comment.
We probably only need one test app for this and it could be used for edge cases like this one where there is no root page. (So we might rename this to prerendering/no-root-page , etc.)
improved language per @eltigerchino Co-authored-by: Tee Ming <chewteeming01@gmail.com>
…ypes - Apply content-type based extensions to non-root paths (e.g., /json -> json/index.json) - Handle redirects from paths with extensions better (e.g., /data.json redirect -> data.json/index.html) - Use mrmime for mime type to extension mapping with simple heuristic for common types - Consolidate root-server-text and root-server-endpoint tests into no-root-page test
|
This... doesn't seem like a good idea to me at all. The point of a root route is that you can access it by visiting Combine that with all the extra complexity this PR entails, and the fact that we have to add a whole new test app (which will slow down every CI run) it's a hard 👎 from me. I would accept a PR that threw an error if a prerenderable root |
Yea, this one felt a bit off to me. |
I'm fine with that. It doesn't seem like this was a problem anyone ran into since the issue had very few responses. I also can't remember where that original discussion was that made me file the issue in the first place. |
Summary
Fixes #11087
When a root
+server.jsendpoint is prerendered with non-HTML content, it was incorrectly saved asindex.htmldue to a fallback in theoutput_filenamefunction.The Problem
The issue occurs when:
+server.js(without a+page.svelte)export const prerender = truePreviously, the code would always default to
index.htmlfor the root path, regardless of content type.The Solution
This PR:
index.htmlfor HTML content at the root pathapplication/json→index.jsontext/plain→index.txt+server.jsendpoints, since most static hosts expectindex.htmlat the rootindex(no extension) for unrecognized content typesTests
Added tests to verify:
.htmlextension.htmlextensionPlease don't delete this checklist! Before submitting the PR, please make sure you do the following:
Tests
pnpm testand lint the project withpnpm lintandpnpm checkChangesets
pnpm changesetand following the prompts. Changesets that add features should beminorand those that fix bugs should bepatch. Please prefix changeset messages withfeat:,fix:, orchore:.Edits