Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/wicked-brooms-like.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@sveltejs/kit': minor
---

Adds optional arguments for URL params `hash` and `searchParams` to `resolve()` from `$app/paths`.
Appends these to the returned URL after resolving the path.
77 changes: 72 additions & 5 deletions packages/kit/src/runtime/app/paths/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ export function asset(file) {
}

/**
* Resolve a pathname by prefixing it with the base path, if any, or resolve a route ID by populating dynamic segments with parameters.
* Resolve a pathname by prefixing it with the base path, if any,
* or resolve a route ID by populating dynamic segments with route parameters.
* Optionally accepts URL parameters and appends these to the resolved route.
*
* During server rendering, the base path is relative and depends on the page currently being rendered.
*
Expand All @@ -37,10 +39,26 @@ export function asset(file) {
* // using a pathname
* const resolved = resolve(`/blog/hello-world`);
*
* // using a route ID plus parameters
* // using a route ID plus route parameters
* const resolved = resolve('/blog/[slug]', {
* slug: 'hello-world'
* });
*
* // using a route ID plus URL parameters as Record
* const resolved = resolve('/blog/search',
* { hash: 'results', searchParams: { author: 'John Doe', year: '2025' } }
* });
*
* // using a route ID plus URL parameters as URLSearchParams
* const resolved = resolve('/blog/search',
* { hash: 'results', searchParams: new URLSearchParams({ author: 'John Doe', year: '2025' }) }
* });
*
* // using a route ID plus route parameters and URL parameters
* const resolved = resolve('/blog/[slug]',
* { slug: 'hello-world' },
* { hash: 'introduction' }
* });
* ```
* @since 2.26
*
Expand All @@ -49,9 +67,58 @@ export function asset(file) {
* @returns {ResolvedPathname}
*/
export function resolve(...args) {
// args[0] is always the route ID or pathname
const routeID = /** @type {string} */ (args[0]);

/** @type {Record<string, string> | undefined} */
let routeParams;

/** @type {{ searchParams?: Record<string, string> | URLSearchParams; hash?: string } | undefined} */
let urlParams;

// Determine if args[1] is route params or URL params
if (args.length === 2) {
const searchParamsOrURLParams = args[1];
// If args[1] is actually undefined, we don't need to do anything
if (searchParamsOrURLParams) {
if ('searchParams' in searchParamsOrURLParams || 'hash' in searchParamsOrURLParams) {
// It's URL params
urlParams = searchParamsOrURLParams;
} else {
// Otherwise, it's route params
routeParams = /** @type {Record<string, string>} */ (searchParamsOrURLParams);
}
}
} else if (args.length === 3) {
// args[1] is route params, args[2] is URL params
routeParams = args[1];
urlParams = args[2];
}

// The type error is correct here, and if someone doesn't pass params when they should there's a runtime error,
// but we don't want to adjust the internal resolve_route function to accept `undefined`, hence the type cast.
return base + resolve_route(args[0], /** @type {Record<string, string>} */ (args[1]));
}
// but we don't want to adjust the internal resolve_route function to accept undefined, hence the type cast.
let resolvedPath =
base + resolve_route(routeID, /** @type {Record<string, string>} */ (routeParams));

// Append searchParams and hash if provided. These do not affect route resolving.
if (urlParams?.searchParams) {
const { searchParams } = urlParams;

if (searchParams instanceof URLSearchParams) {
resolvedPath += `?${searchParams.toString()}`;
} else {
const query = new URLSearchParams();
for (const [key, value] of Object.entries(searchParams)) {
query.append(key, value);
}
resolvedPath += `?${query.toString()}`;
}
}

if (urlParams?.hash) {
resolvedPath += `#${urlParams.hash}`;
}

return resolvedPath;
}
export { base, assets, resolve as resolveRoute };
8 changes: 4 additions & 4 deletions packages/kit/src/runtime/app/paths/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Pathname, RouteId, RouteParams } from '$app/types';
import { Pathname, ResolveURLParams, RouteId, RouteParams } from '$app/types';

export type ResolveArgs<T extends RouteId | Pathname> = T extends RouteId
? RouteParams<T> extends Record<string, never>
? [route: T]
: [route: T, params: RouteParams<T>]
: [route: T];
? [route: T, options?: ResolveURLParams]
: [route: T, params: RouteParams<T>, options?: ResolveURLParams]
: [route: T, options?: ResolveURLParams];
9 changes: 9 additions & 0 deletions packages/kit/src/types/ambient.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,15 @@ declare module '$app/types' {
? ReturnType<AppTypes['RouteParams']>[T]
: Record<string, never>;

/**
* URL Parameters that can be optionally passed to the `resolve` function.
* Used to specify a hash and/or search parameters to be included in the resolved URL.
*/
export type ResolveURLParams = {
hash?: string;
searchParams?: Record<string, string> | URLSearchParams;
};

/**
* A utility for getting the parameters associated with a given layout, which is similar to `RouteParams` but also includes optional parameters for any child route.
*/
Expand Down
39 changes: 33 additions & 6 deletions packages/kit/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3043,7 +3043,7 @@ declare module '$app/navigation' {
}

declare module '$app/paths' {
import type { RouteId, Pathname, ResolvedPathname, RouteParams, Asset } from '$app/types';
import type { RouteId, Pathname, ResolvedPathname, ResolveURLParams, RouteParams, Asset } from '$app/types';
/**
* A string that matches [`config.kit.paths.base`](https://svelte.dev/docs/kit/configuration#paths).
*
Expand All @@ -3070,9 +3070,9 @@ declare module '$app/paths' {
): ResolvedPathname;
type ResolveArgs<T extends RouteId | Pathname> = T extends RouteId
? RouteParams<T> extends Record<string, never>
? [route: T]
: [route: T, params: RouteParams<T>]
: [route: T];
? [route: T, options?: ResolveURLParams]
: [route: T, params: RouteParams<T>, options?: ResolveURLParams]
: [route: T, options?: ResolveURLParams];
/**
* Resolve the URL of an asset in your `static` directory, by prefixing it with [`config.kit.paths.assets`](https://svelte.dev/docs/kit/configuration#paths) if configured, or otherwise by prefixing it with the base path.
*
Expand All @@ -3091,7 +3091,9 @@ declare module '$app/paths' {
* */
export function asset(file: Asset): string;
/**
* Resolve a pathname by prefixing it with the base path, if any, or resolve a route ID by populating dynamic segments with parameters.
* Resolve a pathname by prefixing it with the base path, if any,
* or resolve a route ID by populating dynamic segments with route parameters.
* Optionally accepts URL parameters and appends these to the resolved route.
*
* During server rendering, the base path is relative and depends on the page currently being rendered.
*
Expand All @@ -3102,10 +3104,26 @@ declare module '$app/paths' {
* // using a pathname
* const resolved = resolve(`/blog/hello-world`);
*
* // using a route ID plus parameters
* // using a route ID plus route parameters
* const resolved = resolve('/blog/[slug]', {
* slug: 'hello-world'
* });
*
* // using a route ID plus URL parameters as Record
* const resolved = resolve('/blog/search',
* { hash: 'results', searchParams: { author: 'John Doe', year: '2025' } }
* });
*
* // using a route ID plus URL parameters as URLSearchParams
* const resolved = resolve('/blog/search',
* { hash: 'results', searchParams: new URLSearchParams({ author: 'John Doe', year: '2025' }) }
* });
*
* // using a route ID plus route parameters and URL parameters
* const resolved = resolve('/blog/[slug]',
* { slug: 'hello-world' },
* { hash: 'introduction' }
* });
* ```
* @since 2.26
*
Expand Down Expand Up @@ -3481,6 +3499,15 @@ declare module '$app/types' {
? ReturnType<AppTypes['RouteParams']>[T]
: Record<string, never>;

/**
* URL Parameters that can be optionally passed to the `resolve` function.
* Used to specify a hash and/or search parameters to be included in the resolved URL.
*/
export type ResolveURLParams = {
hash?: string;
searchParams?: Record<string, string> | URLSearchParams;
};

/**
* A utility for getting the parameters associated with a given layout, which is similar to `RouteParams` but also includes optional parameters for any child route.
*/
Expand Down
Loading