feat: allow resolve() to accept pathnames with query strings and hash fragments#15458
Conversation
… fragments
- Add `ResolvablePath` type: `Pathname | ${Pathname}?${string} | ${Pathname}#${string}`
- Update `resolve()` and `resolveRoute()` signatures to accept `ResolvablePath`
- No runtime changes needed — `resolve_route()` already passes through `?` and `#`
- Enables `goto(resolve('/products?page=2'))` to satisfy both type checker and `svelte/no-navigation-without-resolve` lint rule
Closes sveltejs#14750
🦋 Changeset detectedLatest commit: 977191d 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 |
- Update resolve() in types/index.d.ts (was still using Pathname) - Update JSDoc template in client.js
teemingc
left a comment
There was a problem hiding this comment.
Thank you for submitting this. I've added a few comments to help align things with our current conventions. Other than that, it looks good to me.
elliott-with-the-longest-name-on-github
left a comment
There was a problem hiding this comment.
I love this, but it's missing one crucial bit. Because this only applies to pathnames, it means you can't do this:
resolve('/[slug]?foo=bar', { slug: 'banana' });...because route parameter notation like this falls under the RouteId type. I think I'd prefer if the ?{string} and #{string} variants applied to the RouteId version as well, though... that might be challenging. want to give it a crack?
Would that also be something like: export type RouteIdWithSearchOrHash = RouteId |
`${RouteId}?${string}` |
`${RouteId}#${string}` |
|
Yeah I think so, but I'm not sure how it would affect our ability to extract the types of the route parameters for the second argument... |
- Rename ResolvablePath to PathnameWithSearchOrHash
- Remove redundant ${Pathname}?${string}#${string} variant
- Add RouteIdWithSearchOrHash type so resolve('/[slug]?foo=bar', { slug: 'banana' }) works
- Update ResolveArgs to extract params from RouteId with search/hash suffix
- Shorten changeset text
The contributed solution really is quite brilliant 😄 https://github.com/sveltejs/kit/pull/15458/changes#diff-ca363a5801bb1240cce13fcdf36af610cfa8d5aae2007e77244a0d91f9ce5b4dR8-R12 I've tested it along with params that have matchers and it works well! |
… fragments (sveltejs#15458) ## Summary Adds a `ResolvablePath` type that extends `Pathname` with optional `?${string}` and `#${string}` suffixes, allowing `resolve()` to accept pathnames with query strings and hash fragments. - No runtime changes — `resolve_route()` already passes through `?` and `#` correctly - `Pathname` type is unchanged (still used for `page.url.pathname`) - Existing `resolve()` behavior with route IDs and params is unaffected ## Motivation Currently there is no way to use `goto()` with query strings or hash fragments without either disabling the lint rule or bypassing `resolve()`: 1. `resolve('/products?page=2')` fails type-checking (`Pathname` doesn't include `?`) 2. `goto(resolve('/products') + '?page=2')` fails the `svelte/no-navigation-without-resolve` lint rule (argument isn't a `resolve()` call) Per @teemingc's suggestion in sveltejs#14750: > "It should be possible to add a union type with a template string such as `/my-route#${string}` and `/my-route?${string}`. This would continue to keep the function type-safe." This is a minimal type-only fix that doesn't conflict with sveltejs#14756 (which adds richer `{ hash, searchParams }` options). ## What this enables ```js // All of these now type-check AND satisfy the lint rule: goto(resolve('/products?page=2')); goto(resolve(`/search?${params}`)); goto(resolve('/docs/intro#getting-started')); ``` ## Test plan - [x] `pnpm check` passes - [x] `pnpm lint` passes - [x] Existing tests pass (no runtime changes, only type widening) Closes sveltejs#14750 --------- Co-authored-by: Tee Ming <chewteeming01@gmail.com>
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @sveltejs/kit@2.54.0 ### Minor Changes - feat: allow error boundaries to catch errors on the server ([#15308](#15308)) ### Patch Changes - chore: upgrade `devalue` ([#15535](#15535)) - fix: don't wait for remote functions that are not awaited in the template ([#15280](#15280)) - feat: allow `resolve()` to accept pathnames with a search string and/or hash ([#15458](#15458)) - chore: remove deprecation warnings for `config.kit.files.*` options when validating the Svelte config file ([#15482](#15482)) - fix: handles form target attribute in remote form redirects ([#15457](#15457)) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary
Adds a
ResolvablePathtype that extendsPathnamewith optional?${string}and#${string}suffixes, allowingresolve()to accept pathnames with query strings and hash fragments.resolve_route()already passes through?and#correctlyPathnametype is unchanged (still used forpage.url.pathname)resolve()behavior with route IDs and params is unaffectedMotivation
Currently there is no way to use
goto()with query strings or hash fragments without either disabling the lint rule or bypassingresolve():resolve('/products?page=2')fails type-checking (Pathnamedoesn't include?)goto(resolve('/products') + '?page=2')fails thesvelte/no-navigation-without-resolvelint rule (argument isn't aresolve()call)Per @teemingc's suggestion in #14750:
This is a minimal type-only fix that doesn't conflict with #14756 (which adds richer
{ hash, searchParams }options).What this enables
Test plan
pnpm checkpassespnpm lintpassesCloses #14750