fix: preserve trailingSlash config when error boundary is above page node#15358
fix: preserve trailingSlash config when error boundary is above page node#15358danielalanbates wants to merge 4 commits intosveltejs:mainfrom
Conversation
…node (sveltejs#13516) When an error occurs and the branch is sliced to the nearest error boundary, the trailingSlash config from deeper nodes (e.g. the page) was lost because those nodes were removed from the branch. This caused the URL to be rewritten using the default 'never' setting, stripping trailing slashes even when trailingSlash='always' was configured on the page. The fix extracts the trailingSlash value from the full branch before slicing and passes it to get_navigation_result_from_branch as a fallback, so the config is preserved even when the branch is truncated for error handling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: b30c5bd 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 |
|
It looks like you need to run |
| // preserve trailingSlash config from the full branch so that | ||
| // slicing the branch for error handling doesn't lose it (#13516) | ||
| let trailing_slash; | ||
| for (const node of branch) { | ||
| if (node?.slash !== undefined) trailing_slash = node.slash; | ||
| } |
There was a problem hiding this comment.
It's probably better to create a util for this so we don't repeat it so many times. It's currently repeated 3 times in the module.
Maybe something like:
/**
* @param {(import('./types.js').BranchNode | undefined)[]} branch
* @param {URL} url
* @returns {import('types').TrailingSlash}
*/
function get_trailing_slash(branch, url) {
// if `paths.base === '/a/b/c`, then the root route is always `/a/b/c/`, regardless of
// the `trailingSlash` route option, so that relative paths to JS and CSS work
if (base && (url.pathname === base || url.pathname === base + '/')) {
return 'always';
}
return branch.reduce((value, node) => {
return node?.slash ?? value;
}, /** @type {import('types').TrailingSlash} */ ('never'));
}There was a problem hiding this comment.
This is almost the right solution but we need one more change. The trailing slash info for the node isn't returned when the load function fails. Ideally, we'd still keep track of it even if the load function fails. We need to make changes so that we get
kit/packages/kit/src/runtime/client/client.js
Line 890 in fca32df
| return get_navigation_result_from_branch({ | ||
| url, | ||
| params, | ||
| branch: [root_layout, root_error], |
There was a problem hiding this comment.
We probably want to compute the trailing slash option here too so that it respects the root layout's trailing slash option
Summary
Fixes #13516
When an error occurs during client-side rendering and the branch is sliced to the nearest error boundary, the
trailingSlashconfig from deeper nodes (e.g. the page's+page.js) is lost because those nodes are removed from the branch. This causesget_navigation_result_from_branchto fall back to the default'never'setting, stripping the trailing slash from the URL even whentrailingSlash = 'always'is configured on the page.Changes
packages/kit/src/runtime/client/client.js: Added atrailing_slashparameter toget_navigation_result_from_branchthat serves as a fallback when the (sliced) branch contains no nodes with aslashproperty. At both call sites where the branch is sliced for error handling, thetrailingSlashvalue is extracted from the full branch before slicing and passed through.Test: Added a test case that navigates to a page with
trailingSlash = 'always'that throws an error, verifying the URL retains the trailing slash when the error boundary (defined at a higher level) catches the error.How it works
The root cause is that
branch.slice(0, error_load.idx).concat(error_load.node)removes the page node that contained thetrailingSlashconfig. The fix preserves this config by:slashbefore slicingtrailing_slashtoget_navigation_result_from_branchslash(instead of'never'), which can still be overridden by anyslashproperty found in the sliced branchThis PR was created with the assistance of Claude Opus 4.6 by Anthropic. Happy to make any adjustments!