fix: keep dot runs out of chunk and asset file names - #393
Open
everton-dgn wants to merge 6 commits into
Open
everton-dgn wants to merge 6 commits into
everton-dgn wants to merge 6 commits into
Conversation
A catch-all route module such as `[...404].tsx` built to `_...404_-<hash>.js`, with a `_..-<hash>.css` asset named after it. Hosts and middleware that reject any URL containing `..` refused both files, so the lazy route failed to hydrate. Client builds now wrap `output.sanitizeFileName` in an `outputOptions` hook: the user's sanitizer (or the bundler default) runs first, then every run of dots collapses to one. `sanitizeFileName: false` is left alone, and server output is unchanged. The start-ssr example gains a lazy `[...rest]` route and a file-names mode covering the default build, a user sanitizer and the opt-out. Fixes solidjs#391
The collapse only ran for client builds, but the server bundle writes the URLs of the assets it imports, computed with its own sanitizer. An asset named with a dot run (`logo..png`) then built to `logo-<hash>.png` on the client while the server-rendered markup pointed at `logo.-<hash>.png`, a file nobody wrote, and hydration keeps the server's attribute. The wrapper now applies to every build environment, so both sides name assets the same way. Server chunk names collapse too. The catch-all route in start-ssr renders `mark..svg`, and the file-names mode checks that its server-rendered `src` names a file under dist/client that server.js serves, and that no path under dist/server contains `..`.
The main plugin is pre-enforced, so its `outputOptions` hook ran before every normal plugin's. A later plugin that set `sanitizeFileName` from its own hook replaced the wrapper and brought `..` back. The hook is now post-order: it runs after every pre and normal `outputOptions` hook and wraps whatever they set. A post hook further down the plugin array can still override it. The file-names mode now uses a user sanitizer that turns the brackets into dots, so `[...rest]` only loses its `..` when the collapse runs after the user function; the old `~` sanitizer passed in either order. A new SANITIZE_FILE_NAME=plugin variant sets the same function from a later plugin's `outputOptions` hook.
Rolldown refuses a `[name]` substitution that starts with `..`, since it reads as a relative path. When the dots a user sanitizer produces are not collapsed (no wrapper on that build, or the collapse running before the user function), the custom and plugin builds fail outright, and the exception ended the file-names mode before the remaining variants ran. Each variant now records the build error as its failure and the mode moves on.
With preserveModules the name carries the module's directories, including `../` segments, and collapsing those makes the bundler reject the name.
Also moves the file-names mode to port 3185 and says in the changeset that only the last segment of a name is collapsed.
🦋 Changeset detectedLatest commit: 13a51f7 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #391
Problem
A catch-all route module like
[...404].tsxbuilds to_...404_-<hash>.js: the bundler's defaultsanitizeFileNameswaps the brackets and keeps the dots. Vite names the route's CSS after the chunk, so it comes out as_..-<hash>.css. Any server, CDN or middleware that refuses URLs containing..refuses both files, and the lazy route fails to hydrate.Fix
The main plugin wraps
output.sanitizeFileNamein a post-orderoutputOptionshook: the configured sanitizer runs first (or the default rule, when none is set), then runs of dots in the last segment of the name collapse to one.[...404]now builds to_.404_-<hash>.js.sanitizeFileNamerather thanchunkFileNames, because it also covers assets such as the CSS file above.order: 'post', so a sanitizer set by another plugin'soutputOptionshook gets wrapped instead of replacing the wrapper.src/hrefattributes at files the client never wrote (an importedmark..svgbecamemark-<hash>.svgon the client while the SSR markup saidmark.-<hash>.svg).preserveModulesthe name carries the module's directories,../included, and changing those makes Rolldown reject the name.mark..svgintomarksvg-<hash>, with no extension.sanitizeFileName: falseis left alone, as the way to ask for raw names.Verification
examples/start-ssrgets a lazy catch-all route,src/routes/[...rest].tsx, with its own CSS and an image namedmark..svg, and afile-namesmode intest/run.mjs(node test/run.mjs file-names). It builds the example with the default sanitizer, with a user function from the config, with the same function from another plugin'soutputOptionshook, and withsanitizeFileName: false, then serves the build through the example'sserver.js, which already skips static files for URLs containing... It also builds a small library withpreserveModulesfrom a directory namedtest-dot..lib.With the plugin built from
nextthe mode fails 11 of 16: the chunk and CSS keep their dots,server.jsanswers them with the SSR page, and Rolldown rejects the two builds with a user function. With the fix it passes 16/16. Reverting each piece fails it too: collapsing on the client only, collapsing before the user's function, droppingorder: 'post', or collapsing the whole path.The
file-namesmode isn't part of the PR workflow, so I ran the fullpnpm testof the examples the hook affects:examples/start-ssr:run.mjs661/661,http-bridge10/10,components-warning11/11,webworker-warning12/12,dedupe8/8examples/start-client: 65/65examples/ssr: 12/12,boundary8/8examples/css-matrix: 87/87,bridge19/19examples/start-env: 47/47pnpm exec tsc --noEmit -p .is clean.