diff --git a/documentation/docs/98-reference/20-$app-types.md b/documentation/docs/98-reference/20-$app-types.md
new file mode 100644
index 000000000000..d9147c61f7c4
--- /dev/null
+++ b/documentation/docs/98-reference/20-$app-types.md
@@ -0,0 +1,91 @@
+---
+title: $app/types
+---
+
+This module contains generated types for the routes in your app.
+
+
+ Available since 2.26
+
+
+```js
+// @noErrors
+import type { RouteId, RouteParams, LayoutParams } from '$app/types';
+```
+
+## Asset
+
+A union of all the filenames of assets contained in your `static` directory.
+
+
+
+```dts
+type Asset = '/favicon.png' | '/robots.txt';
+```
+
+
+
+## RouteId
+
+A union of all the route IDs in your app. Used for `page.route.id` and `event.route.id`.
+
+
+
+```dts
+type RouteId = '/' | '/my-route' | '/my-other-route/[param]';
+```
+
+
+
+## Pathname
+
+A union of all valid pathnames in your app.
+
+
+
+```dts
+type Pathname = '/' | '/my-route' | `/my-other-route/${string}` & {};
+```
+
+
+
+## ResolvedPathname
+
+`Pathname`, but possibly prefixed with a [base path](https://svelte.dev/docs/kit/configuration#paths). Used for `page.url.pathname`.
+
+
+
+```dts
+type Pathname = `${'' | `/${string}`}/` | `${'' | `/${string}`}/my-route` | `${'' | `/${string}`}/my-other-route/${string}` | {};
+```
+
+
+
+## RouteParams
+
+A utility for getting the parameters associated with a given route.
+
+```ts
+// @errors: 2552
+type BlogParams = RouteParams<'/blog/[slug]'>; // { slug: string }
+```
+
+
+
+```dts
+type RouteParams = { /* generated */ } | Record;
+```
+
+
+
+## LayoutParams
+
+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.
+
+
+
+```dts
+type RouteParams = { /* generated */ } | Record;
+```
+
+
diff --git a/packages/adapter-auto/tsconfig.json b/packages/adapter-auto/tsconfig.json
index c1f396b255ce..99b99d00b826 100644
--- a/packages/adapter-auto/tsconfig.json
+++ b/packages/adapter-auto/tsconfig.json
@@ -7,7 +7,8 @@
"target": "es2022",
"module": "node16",
"moduleResolution": "node16",
- "baseUrl": "."
+ "baseUrl": ".",
+ "skipLibCheck": true
},
"include": ["**/*.js"]
}
diff --git a/packages/adapter-node/tsconfig.json b/packages/adapter-node/tsconfig.json
index 895d76f908cf..e550f4e7748e 100644
--- a/packages/adapter-node/tsconfig.json
+++ b/packages/adapter-node/tsconfig.json
@@ -11,7 +11,8 @@
"baseUrl": ".",
"paths": {
"@sveltejs/kit": ["../kit/types/index"]
- }
+ },
+ "skipLibCheck": true
},
"include": ["index.js", "src/**/*.js", "tests/**/*.js", "internal.d.ts", "utils.js"],
"exclude": ["tests/smoke.spec_disabled.js"]
diff --git a/packages/adapter-static/tsconfig.json b/packages/adapter-static/tsconfig.json
index dafbce422a39..9360c09e0730 100644
--- a/packages/adapter-static/tsconfig.json
+++ b/packages/adapter-static/tsconfig.json
@@ -11,7 +11,8 @@
"baseUrl": ".",
"paths": {
"@sveltejs/kit": ["../kit/types/index"]
- }
+ },
+ "skipLibCheck": true
},
"include": ["index.js", "test/utils.js"]
}
diff --git a/packages/adapter-vercel/tsconfig.json b/packages/adapter-vercel/tsconfig.json
index 703ccfe50659..718d677bc526 100644
--- a/packages/adapter-vercel/tsconfig.json
+++ b/packages/adapter-vercel/tsconfig.json
@@ -12,7 +12,8 @@
"baseUrl": ".",
"paths": {
"@sveltejs/kit": ["../kit/types/index"]
- }
+ },
+ "skipLibCheck": true
},
"include": ["*.js", "files/**/*.js", "internal.d.ts", "test/**/*.js"]
}
diff --git a/packages/kit/CHANGELOG.md b/packages/kit/CHANGELOG.md
index 4d22340c2563..bf033e8a8ddc 100644
--- a/packages/kit/CHANGELOG.md
+++ b/packages/kit/CHANGELOG.md
@@ -1,5 +1,17 @@
# @sveltejs/kit
+## 2.26.0
+### Minor Changes
+
+
+- feat: better type-safety for `page.route.id`, `page.params`, page.url.pathname` and various other places ([#13864](https://github.com/sveltejs/kit/pull/13864))
+
+
+- feat: `resolve(...)` and `asset(...)` helpers for resolving paths ([#13864](https://github.com/sveltejs/kit/pull/13864))
+
+
+- feat: Add `$app/types` module with `Asset`, `RouteId`, `Pathname`, `ResolvedPathname` `RouteParams` and `LayoutParams` ([#13864](https://github.com/sveltejs/kit/pull/13864))
+
## 2.25.2
### Patch Changes
diff --git a/packages/kit/package.json b/packages/kit/package.json
index 2b47e0f9e278..302dd6276a65 100644
--- a/packages/kit/package.json
+++ b/packages/kit/package.json
@@ -1,6 +1,6 @@
{
"name": "@sveltejs/kit",
- "version": "2.25.2",
+ "version": "2.26.0",
"description": "SvelteKit is the fastest way to build Svelte apps",
"keywords": [
"framework",
diff --git a/packages/kit/scripts/generate-dts.js b/packages/kit/scripts/generate-dts.js
index e8579ec59054..1af5db6ae96b 100644
--- a/packages/kit/scripts/generate-dts.js
+++ b/packages/kit/scripts/generate-dts.js
@@ -1,5 +1,5 @@
import { createBundle } from 'dts-buddy';
-import { readFileSync } from 'node:fs';
+import { readFileSync, writeFileSync } from 'node:fs';
await createBundle({
output: 'types/index.d.ts',
@@ -28,3 +28,9 @@ if (types.includes('__sveltekit/')) {
types
);
}
+
+// this is hacky as all hell but it gets the tests passing. might be a bug in dts-buddy?
+// prettier-ignore
+writeFileSync('./types/index.d.ts', types.replace("declare module '$app/server' {", `declare module '$app/server' {
+ // @ts-ignore
+ import { LayoutParams as AppLayoutParams, RouteId as AppRouteId } from '$app/types'`));
diff --git a/packages/kit/src/core/sync/write_tsconfig.js b/packages/kit/src/core/sync/write_tsconfig.js
index e9df340902e1..6bca8214bdb1 100644
--- a/packages/kit/src/core/sync/write_tsconfig.js
+++ b/packages/kit/src/core/sync/write_tsconfig.js
@@ -98,7 +98,10 @@ export function get_tsconfig(kit) {
const config = {
compilerOptions: {
// generated options
- paths: get_tsconfig_paths(kit),
+ paths: {
+ ...get_tsconfig_paths(kit),
+ '$app/types': ['./types/index.d.ts']
+ },
rootDirs: [config_relative('.'), './types'],
// essential options
diff --git a/packages/kit/src/core/sync/write_tsconfig.spec.js b/packages/kit/src/core/sync/write_tsconfig.spec.js
index 65a6d09f1499..fbd780d74bdd 100644
--- a/packages/kit/src/core/sync/write_tsconfig.spec.js
+++ b/packages/kit/src/core/sync/write_tsconfig.spec.js
@@ -20,6 +20,7 @@ test('Creates tsconfig path aliases from kit.alias', () => {
// $lib isn't part of the outcome because there's a "path exists"
// check in the implementation
expect(compilerOptions.paths).toEqual({
+ '$app/types': ['./types/index.d.ts'],
simpleKey: ['../simple/value'],
'simpleKey/*': ['../simple/value/*'],
key: ['../value'],
diff --git a/packages/kit/src/core/sync/write_types/index.js b/packages/kit/src/core/sync/write_types/index.js
index 62dc72f60f3e..5039a5caec19 100644
--- a/packages/kit/src/core/sync/write_types/index.js
+++ b/packages/kit/src/core/sync/write_types/index.js
@@ -5,6 +5,15 @@ import MagicString from 'magic-string';
import { posixify, rimraf, walk } from '../../../utils/filesystem.js';
import { compact } from '../../../utils/array.js';
import { ts } from '../ts.js';
+import { s } from '../../../utils/misc.js';
+
+const remove_relative_parent_traversals = (/** @type {string} */ path) =>
+ path.replace(/\.\.\//g, '');
+const replace_optional_params = (/** @type {string} */ id) =>
+ id.replace(/\/\[\[[^\]]+\]\]/g, '${string}');
+const replace_required_params = (/** @type {string} */ id) =>
+ id.replace(/\/\[[^\]]+\]/g, '/${string}');
+const is_whitespace = (/** @type {string} */ char) => /\s/.test(char);
/**
* @typedef {{
@@ -35,7 +44,9 @@ export function write_all_types(config, manifest_data) {
const types_dir = `${config.kit.outDir}/types`;
// empty out files that no longer need to exist
- const routes_dir = posixify(path.relative('.', config.kit.files.routes)).replace(/\.\.\//g, '');
+ const routes_dir = remove_relative_parent_traversals(
+ posixify(path.relative('.', config.kit.files.routes))
+ );
const expected_directories = new Set(
manifest_data.routes.map((route) => path.join(routes_dir, route.id))
);
@@ -49,6 +60,65 @@ export function write_all_types(config, manifest_data) {
}
}
+ /** @type {string[]} */
+ const pathnames = [];
+
+ /** @type {string[]} */
+ const dynamic_routes = [];
+
+ /** @type {string[]} */
+ const layouts = [];
+
+ for (const route of manifest_data.routes) {
+ if (route.params.length > 0) {
+ const params = route.params.map((p) => `${p.name}${p.optional ? '?:' : ':'} string`);
+ const route_type = `${s(route.id)}: { ${params.join('; ')} }`;
+
+ dynamic_routes.push(route_type);
+
+ pathnames.push(`\`${replace_required_params(replace_optional_params(route.id))}\` & {}`);
+ } else {
+ pathnames.push(s(route.id));
+ }
+
+ /** @type {Map} */
+ const child_params = new Map(route.params.map((p) => [p.name, p.optional]));
+
+ for (const child of manifest_data.routes.filter((r) => r.id.startsWith(route.id))) {
+ for (const p of child.params) {
+ if (!child_params.has(p.name)) {
+ child_params.set(p.name, true); // always optional
+ }
+ }
+ }
+
+ const layout_params = Array.from(child_params)
+ .map(([name, optional]) => `${name}${optional ? '?:' : ':'} string`)
+ .join('; ');
+
+ const layout_type = `${s(route.id)}: ${layout_params.length > 0 ? `{ ${layout_params} }` : 'undefined'}`;
+ layouts.push(layout_type);
+ }
+
+ try {
+ fs.mkdirSync(types_dir, { recursive: true });
+ } catch {}
+
+ fs.writeFileSync(
+ `${types_dir}/index.d.ts`,
+ [
+ `type DynamicRoutes = {\n\t${dynamic_routes.join(';\n\t')}\n};`,
+ `type Layouts = {\n\t${layouts.join(';\n\t')}\n};`,
+ // we enumerate these rather than doing `keyof Routes` so that the list is visible on hover
+ `export type RouteId = ${manifest_data.routes.map((r) => s(r.id)).join(' | ')};`,
+ 'export type RouteParams = T extends keyof DynamicRoutes ? DynamicRoutes[T] : Record;',
+ 'export type LayoutParams = Layouts[T] | Record;',
+ `export type Pathname = ${pathnames.join(' | ')};`,
+ 'export type ResolvedPathname = `${"" | `/${string}`}${Pathname}`;',
+ `export type Asset = ${manifest_data.assets.map((asset) => s('/' + asset.file)).join(' | ') || 'never'};`
+ ].join('\n\n')
+ );
+
// Read/write meta data on each invocation, not once per node process,
// it could be invoked by another process in the meantime.
const meta_data_file = `${types_dir}/route_meta_data.json`;
@@ -174,7 +244,9 @@ function create_routes_map(manifest_data) {
* @param {Set} [to_delete]
*/
function update_types(config, routes, route, to_delete = new Set()) {
- const routes_dir = posixify(path.relative('.', config.kit.files.routes)).replace(/\.\.\//g, '');
+ const routes_dir = remove_relative_parent_traversals(
+ posixify(path.relative('.', config.kit.files.routes))
+ );
const outdir = path.join(config.kit.outDir, 'types', routes_dir, route.id);
// now generate new types
@@ -733,7 +805,7 @@ export function tweak_types(content, is_server) {
if (declaration.type) {
let a = declaration.type.pos;
const b = declaration.type.end;
- while (/\s/.test(content[a])) a += 1;
+ while (is_whitespace(content[a])) a += 1;
const type = content.slice(a, b);
code.remove(declaration.name.end, declaration.type.end);
@@ -805,7 +877,7 @@ export function tweak_types(content, is_server) {
if (declaration.type) {
let a = declaration.type.pos;
const b = declaration.type.end;
- while (/\s/.test(content[a])) a += 1;
+ while (is_whitespace(content[a])) a += 1;
const type = content.slice(a, b);
code.remove(declaration.name.end, declaration.type.end);
diff --git a/packages/kit/src/core/sync/write_types/index.spec.js b/packages/kit/src/core/sync/write_types/index.spec.js
index 210922514089..82af21f9d3f4 100644
--- a/packages/kit/src/core/sync/write_types/index.spec.js
+++ b/packages/kit/src/core/sync/write_types/index.spec.js
@@ -1,4 +1,5 @@
import { execSync } from 'node:child_process';
+import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { assert, expect, test } from 'vitest';
@@ -33,15 +34,14 @@ test('Creates correct $types', { timeout: 6000 }, () => {
// To save us from creating a real SvelteKit project for each of the tests,
// we first run the type generation directly for each test case, and then
// call `tsc` to check that the generated types are valid.
- run_test('actions');
- run_test('simple-page-shared-only');
- run_test('simple-page-server-only');
- run_test('simple-page-server-and-shared');
- run_test('layout');
- run_test('layout-advanced');
- run_test('slugs');
- run_test('slugs-layout-not-all-pages-have-load');
- run_test('param-type-inference');
+ const directories = fs
+ .readdirSync(cwd)
+ .filter((dir) => fs.statSync(`${cwd}/${dir}`).isDirectory());
+
+ for (const dir of directories) {
+ run_test(dir);
+ }
+
try {
execSync('pnpm testtypes', { cwd });
} catch (e) {
diff --git a/packages/kit/src/core/sync/write_types/test/app-types/+page.ts b/packages/kit/src/core/sync/write_types/test/app-types/+page.ts
new file mode 100644
index 000000000000..6da67ea0e75a
--- /dev/null
+++ b/packages/kit/src/core/sync/write_types/test/app-types/+page.ts
@@ -0,0 +1,30 @@
+import type { RouteId, RouteParams, Pathname } from './.svelte-kit/types/index.d.ts';
+
+declare let id: RouteId;
+
+// okay
+id = '/';
+id = '/foo/[bar]/[baz]';
+
+// @ts-expect-error
+id = '/nope';
+
+// read `id` otherwise it is treated as unused
+id;
+
+declare let params: RouteParams<'/foo/[bar]/[baz]'>;
+
+// @ts-expect-error
+params.foo; // not okay
+params.bar; // okay
+params.baz; // okay
+
+declare let pathname: Pathname;
+
+// @ts-expect-error
+pathname = '/nope';
+pathname = '/foo';
+pathname = '/foo/1/2';
+
+// read `pathname` otherwise it is treated as unused
+pathname;
diff --git a/packages/kit/src/core/sync/write_types/test/app-types/foo/[bar]/[baz]/+page.ts b/packages/kit/src/core/sync/write_types/test/app-types/foo/[bar]/[baz]/+page.ts
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/packages/kit/src/core/sync/write_types/test/tsconfig.json b/packages/kit/src/core/sync/write_types/test/tsconfig.json
index 39ea59584873..9c43f3c10e41 100644
--- a/packages/kit/src/core/sync/write_types/test/tsconfig.json
+++ b/packages/kit/src/core/sync/write_types/test/tsconfig.json
@@ -14,6 +14,6 @@
"types": ["../../../../types/internal"]
}
},
- "include": ["./**/*.js"],
+ "include": ["./**/*.js", "./**/*.ts"],
"exclude": ["./**/.svelte-kit/**"]
}
diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts
index afc5f1d6d450..9e94e7d46e57 100644
--- a/packages/kit/src/exports/public.d.ts
+++ b/packages/kit/src/exports/public.d.ts
@@ -17,7 +17,13 @@ import {
RouteSegment
} from '../types/private.js';
import { BuildData, SSRNodeLoader, SSRRoute, ValidatedConfig } from 'types';
-import type { SvelteConfig } from '@sveltejs/vite-plugin-svelte';
+import type { SvelteConfig, PluginOptions } from '@sveltejs/vite-plugin-svelte';
+import {
+ RouteId as AppRouteId,
+ LayoutParams as AppLayoutParams,
+ ResolvedPathname
+ // @ts-ignore
+} from '$app/types';
export { PrerenderOption } from '../types/private.js';
@@ -850,11 +856,11 @@ export interface Transporter<
* rather than using `Load` directly.
*/
export type Load<
- Params extends Partial> = Partial>,
+ Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>,
InputData extends Record | null = Record | null,
ParentData extends Record = Record,
OutputData extends Record | void = Record | void,
- RouteId extends string | null = string | null
+ RouteId extends AppRouteId | null = AppRouteId | null
> = (event: LoadEvent) => MaybePromise;
/**
@@ -862,10 +868,10 @@ export type Load<
* rather than using `LoadEvent` directly.
*/
export interface LoadEvent<
- Params extends Partial> = Partial>,
+ Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>,
Data extends Record | null = Record | null,
ParentData extends Record = Record,
- RouteId extends string | null = string | null
+ RouteId extends AppRouteId | null = AppRouteId | null
> extends NavigationEvent {
/**
* `fetch` is equivalent to the [native `fetch` web API](https://developer.mozilla.org/en-US/docs/Web/API/fetch), with a few additional features:
@@ -970,8 +976,8 @@ export interface LoadEvent<
}
export interface NavigationEvent<
- Params extends Partial> = Partial>,
- RouteId extends string | null = string | null
+ Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>,
+ RouteId extends AppRouteId | null = AppRouteId | null
> {
/**
* The parameters of the current page - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object
@@ -1110,13 +1116,13 @@ export interface AfterNavigate extends Omit {
* The shape of the [`page`](https://svelte.dev/docs/kit/$app-state#page) reactive object and the [`$page`](https://svelte.dev/docs/kit/$app-stores) store.
*/
export interface Page<
- Params extends Record = Record,
- RouteId extends string | null = string | null
+ Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>,
+ RouteId extends AppRouteId | null = AppRouteId | null
> {
/**
* The URL of the current page.
*/
- url: URL;
+ url: URL & { pathname: ResolvedPathname };
/**
* The parameters of the current page - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object.
*/
@@ -1158,8 +1164,8 @@ export interface Page<
export type ParamMatcher = (param: string) => boolean;
export interface RequestEvent<
- Params extends Partial> = Partial>,
- RouteId extends string | null = string | null
+ Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>,
+ RouteId extends AppRouteId | null = AppRouteId | null
> {
/**
* Get or set cookies related to the current request
@@ -1250,8 +1256,8 @@ export interface RequestEvent<
* It receives `Params` as the first generic argument, which you can skip by using [generated types](https://svelte.dev/docs/kit/types#Generated-types) instead.
*/
export type RequestHandler<
- Params extends Partial> = Partial>,
- RouteId extends string | null = string | null
+ Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>,
+ RouteId extends AppRouteId | null = AppRouteId | null
> = (event: RequestEvent) => MaybePromise;
export interface ResolveOptions {
@@ -1329,16 +1335,16 @@ export interface SSRManifest {
* rather than using `ServerLoad` directly.
*/
export type ServerLoad<
- Params extends Partial> = Partial>,
+ Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>,
ParentData extends Record = Record,
OutputData extends Record | void = Record | void,
- RouteId extends string | null = string | null
+ RouteId extends AppRouteId | null = AppRouteId | null
> = (event: ServerLoadEvent) => MaybePromise;
export interface ServerLoadEvent<
- Params extends Partial> = Partial>,
+ Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>,
ParentData extends Record = Record,
- RouteId extends string | null = string | null
+ RouteId extends AppRouteId | null = AppRouteId | null
> extends RequestEvent {
/**
* `await parent()` returns data from parent `+layout.server.js` `load` functions.
@@ -1405,9 +1411,9 @@ export interface ServerLoadEvent<
* See [form actions](https://svelte.dev/docs/kit/form-actions) for more information.
*/
export type Action<
- Params extends Partial> = Partial>,
+ Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>,
OutputData extends Record | void = Record | void,
- RouteId extends string | null = string | null
+ RouteId extends AppRouteId | null = AppRouteId | null
> = (event: RequestEvent) => MaybePromise;
/**
@@ -1415,9 +1421,9 @@ export type Action<
* See [form actions](https://svelte.dev/docs/kit/form-actions) for more information.
*/
export type Actions<
- Params extends Partial> = Partial>,
+ Params extends AppLayoutParams<'/'> = AppLayoutParams<'/'>,
OutputData extends Record | void = Record | void,
- RouteId extends string | null = string | null
+ RouteId extends AppRouteId | null = AppRouteId | null
> = Record>;
/**
diff --git a/packages/kit/src/runtime/app/paths/index.js b/packages/kit/src/runtime/app/paths/index.js
index 5a150fbfaa0f..d9f29bbdc395 100644
--- a/packages/kit/src/runtime/app/paths/index.js
+++ b/packages/kit/src/runtime/app/paths/index.js
@@ -1,8 +1,14 @@
-export { base, assets } from '__sveltekit/paths';
-import { base } from '__sveltekit/paths';
+import { base, assets } from '__sveltekit/paths';
import { resolve_route } from '../../../utils/routing.js';
-/** @type {import('./types.d.ts').resolveRoute} */
-export function resolveRoute(id, params) {
+/** @type {import('./types.d.ts').asset} */
+export function asset(file) {
+ return (assets || base) + file;
+}
+
+/** @type {import('./types.d.ts').resolve} */
+export function resolve(id, params) {
return base + resolve_route(id, params);
}
+
+export { base, assets, resolve as resolveRoute };
diff --git a/packages/kit/src/runtime/app/paths/types.d.ts b/packages/kit/src/runtime/app/paths/types.d.ts
index d17c45dd4efd..6f8a4a481fe0 100644
--- a/packages/kit/src/runtime/app/paths/types.d.ts
+++ b/packages/kit/src/runtime/app/paths/types.d.ts
@@ -1,7 +1,12 @@
+// @ts-ignore
+import { Asset, RouteId, RouteParams, Pathname, ResolvedPathname } from '$app/types';
+
/**
* A string that matches [`config.kit.paths.base`](https://svelte.dev/docs/kit/configuration#paths).
*
* Example usage: `Link`
+ *
+ * @deprecated Use [`resolve(...)`](https://svelte.dev/docs/kit/$app-paths#resolve) instead
*/
export let base: '' | `/${string}`;
@@ -9,22 +14,58 @@ export let base: '' | `/${string}`;
* An absolute path that matches [`config.kit.paths.assets`](https://svelte.dev/docs/kit/configuration#paths).
*
* > [!NOTE] If a value for `config.kit.paths.assets` is specified, it will be replaced with `'/_svelte_kit_assets'` during `vite dev` or `vite preview`, since the assets don't yet live at their eventual URL.
+ *
+ * @deprecated Use [`asset(...)`](https://svelte.dev/docs/kit/$app-paths#asset) instead
*/
export let assets: '' | `https://${string}` | `http://${string}` | '/_svelte_kit_assets';
+type ResolveArgs = T extends RouteId
+ ? RouteParams extends Record
+ ? [route: T]
+ : [route: T, params: RouteParams]
+ : [route: T];
+
/**
- * Populate a route ID with params to resolve a pathname.
+ * Resolve a pathname by prefixing it with the base path, if any, or resolve a route ID by populating dynamic segments with parameters.
+ *
+ * During server rendering, the base path is relative and depends on the page currently being rendered.
+ *
* @example
* ```js
- * import { resolveRoute } from '$app/paths';
- *
- * resolveRoute(
- * `/blog/[slug]/[...somethingElse]`,
- * {
- * slug: 'hello-world',
- * somethingElse: 'something/else'
- * }
- * ); // `/blog/hello-world/something/else`
+ * import { resolve } from '$app/paths';
+ *
+ * // using a pathname
+ * const resolved = resolve(`/blog/hello-world`);
+ *
+ * // using a route ID plus parameters
+ * const resolved = resolve('/blog/[slug]', {
+ * slug: 'hello-world'
+ * });
* ```
+ * @since 2.26
+ */
+export function resolve(...args: ResolveArgs): ResolvedPathname;
+
+/**
+ * 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.
+ *
+ * During server rendering, the base path is relative and depends on the page currently being rendered.
+ *
+ * @example
+ * ```svelte
+ *
+ *
+ *
+ * ```
+ * @since 2.26
+ */
+export function asset(file: Asset): string;
+
+/**
+ * @deprecated Use [`resolve(...)`](https://svelte.dev/docs/kit/$app-paths#resolve) instead
*/
-export function resolveRoute(id: string, params: Record): string;
+export function resolveRoute(
+ ...args: ResolveArgs
+): ResolvedPathname;
diff --git a/packages/kit/src/version.js b/packages/kit/src/version.js
index 44c199d49062..522e8f11d70d 100644
--- a/packages/kit/src/version.js
+++ b/packages/kit/src/version.js
@@ -1,4 +1,4 @@
// generated during release, do not modify
/** @type {string} */
-export const VERSION = '2.25.2';
+export const VERSION = '2.26.0';
diff --git a/packages/kit/test/apps/basics/src/routes/+layout.svelte b/packages/kit/test/apps/basics/src/routes/+layout.svelte
index 6272671c81ae..969229a5f678 100644
--- a/packages/kit/test/apps/basics/src/routes/+layout.svelte
+++ b/packages/kit/test/apps/basics/src/routes/+layout.svelte
@@ -1,4 +1,4 @@
-
+ *
+ *
* ```
+ * @since 2.26
+ */
+ export function asset(file: Asset): string;
+
+ /**
+ * @deprecated Use [`resolve(...)`](https://svelte.dev/docs/kit/$app-paths#resolve) instead
*/
- export function resolveRoute(id: string, params: Record): string;
+ export function resolveRoute(
+ ...args: ResolveArgs
+ ): ResolvedPathname;
export {};
}
declare module '$app/server' {
+ // @ts-ignore
+ import { LayoutParams as AppLayoutParams, RouteId as AppRouteId } from '$app/types'
import type { RequestEvent } from '@sveltejs/kit';
/**
* Read the contents of an imported asset from the filesystem
@@ -2414,7 +2456,7 @@ declare module '$app/server' {
* In environments without [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage), this must be called synchronously (i.e. not after an `await`).
* @since 2.20.0
*/
- export function getRequestEvent(): RequestEvent>, string | null>;
+ export function getRequestEvent(): RequestEvent, any>;
export {};
}