diff --git a/.changeset/itchy-birds-admire.md b/.changeset/itchy-birds-admire.md new file mode 100644 index 000000000000..aa8ddfac3295 --- /dev/null +++ b/.changeset/itchy-birds-admire.md @@ -0,0 +1,6 @@ +--- +'create-svelte': patch +'@sveltejs/kit': patch +--- + +Add ambient type definitions for \$app imports diff --git a/packages/create-svelte/template-additions/tsconfig.json b/packages/create-svelte/template-additions/tsconfig.json index d6a24a9309a1..d826f6ef8b89 100644 --- a/packages/create-svelte/template-additions/tsconfig.json +++ b/packages/create-svelte/template-additions/tsconfig.json @@ -23,8 +23,6 @@ "allowJs": true, "checkJs": true, "paths": { - "$app/*": [".svelte/dev/runtime/app/*", ".svelte/build/runtime/app/*"], - "$service-worker": [".svelte/build/runtime/service-worker"], "$lib/*": ["src/lib/*"] } }, diff --git a/packages/kit/src/runtime/app/env.js b/packages/kit/src/runtime/app/env.js index 6e84c14b0af4..a5f1e045c6d4 100644 --- a/packages/kit/src/runtime/app/env.js +++ b/packages/kit/src/runtime/app/env.js @@ -1,4 +1,14 @@ +/** + * @type {import('$app/env').browser} + */ export const browser = !import.meta.env.SSR; +/** + * @type {import('$app/env').dev} + */ export const dev = !!import.meta.env.DEV; +/** + * @type {import('$app/env').amp} + */ export const amp = !!import.meta.env.VITE_SVELTEKIT_AMP; + export { prerendering } from '../env.js'; diff --git a/packages/kit/src/runtime/app/navigation.js b/packages/kit/src/runtime/app/navigation.js index c07a74217240..3a5330ea4d62 100644 --- a/packages/kit/src/runtime/app/navigation.js +++ b/packages/kit/src/runtime/app/navigation.js @@ -15,22 +15,22 @@ export const prefetch = import.meta.env.SSR ? guard('prefetch') : prefetch_; export const prefetchRoutes = import.meta.env.SSR ? guard('prefetchRoutes') : prefetchRoutes_; /** - * @param {string} href - * @param {{ - * noscroll?: boolean; - * replaceState?: boolean; - * }} [opts] + * @type {import('$app/navigation').goto} */ async function goto_(href, opts) { return router.goto(href, opts, []); } -/** @param {string} href */ +/** + * @type {import('$app/navigation').prefetch} + */ function prefetch_(href) { return router.prefetch(new URL(href, get_base_uri(document))); } -/** @param {string[]} [pathnames] */ +/** + * @type {import('$app/navigation').prefetchRoutes} + */ async function prefetchRoutes_(pathnames) { const matching = pathnames ? router.routes.filter((route) => pathnames.some((pathname) => route[0].test(pathname))) diff --git a/packages/kit/src/runtime/app/stores.js b/packages/kit/src/runtime/app/stores.js index 3af06b3de8e5..e33ebb3f4c91 100644 --- a/packages/kit/src/runtime/app/stores.js +++ b/packages/kit/src/runtime/app/stores.js @@ -13,37 +13,31 @@ export function stores() { return getStores(); } -/** @typedef {import('svelte/store').Readable<{ - * host: string; - * path: string; - * query: URLSearchParams; - * params: Record - * }>} PageStore */ - +/** + * @type {import('$app/stores').getStores} + */ export const getStores = () => { const stores = getContext('__svelte__'); return { - /** @type {PageStore} */ page: { subscribe: stores.page.subscribe }, - /** @type {import('svelte/store').Readable} */ navigating: { subscribe: stores.navigating.subscribe }, + // @ts-ignore - deprecated, not part of type definitions, but still callable get preloading() { console.error('stores.preloading is deprecated; use stores.navigating instead'); return { subscribe: stores.navigating.subscribe }; }, - /** @type {import('svelte/store').Writable} */ session: stores.session }; }; -/** @type {PageStore} */ +/** @type {typeof import('$app/stores').page} */ export const page = { /** @param {(value: any) => void} fn */ subscribe(fn) { @@ -52,7 +46,7 @@ export const page = { } }; -/** @type {import('svelte/store').Readable} */ +/** @type {typeof import('$app/stores').navigating} */ export const navigating = { /** @param {(value: any) => void} fn */ subscribe(fn) { @@ -70,7 +64,7 @@ const error = (verb) => { ); }; -/** @type {import('svelte/store').Writable} */ +/** @type {typeof import('$app/stores').session} */ export const session = { subscribe(fn) { const store = getStores().session; diff --git a/packages/kit/src/runtime/client/types.d.ts b/packages/kit/src/runtime/client/types.d.ts index 856084b4a933..e45ad7437751 100644 --- a/packages/kit/src/runtime/client/types.d.ts +++ b/packages/kit/src/runtime/client/types.d.ts @@ -1,4 +1,5 @@ -import { CSRComponent, CSRRoute, Page } from '../../../types.internal'; +import { Page } from '../../../types'; +import { CSRComponent, CSRRoute } from '../../../types.internal'; export type NavigationInfo = { id: string; diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index 49d97091a527..2fa5500ab38b 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -11,7 +11,7 @@ const s = JSON.stringify; * request: import('types').Request; * options: import('types.internal').SSRRenderOptions; * route: import('types.internal').SSRPage; - * page: import('types.internal').Page; + * page: import('types').Page; * node: import('types.internal').SSRNode; * $session: any; * context: Record; diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 7ef15d95c9ae..edd2d00a1c76 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -14,7 +14,7 @@ const s = JSON.stringify; * status: number; * error: Error, * branch: import('./types').Loaded[]; - * page: import('types.internal').Page + * page: import('types').Page * }} opts */ export async function render_response({ diff --git a/packages/kit/types.ambient.d.ts b/packages/kit/types.ambient.d.ts new file mode 100644 index 000000000000..f37e29981d45 --- /dev/null +++ b/packages/kit/types.ambient.d.ts @@ -0,0 +1,106 @@ +declare module '$app/env' { + /** + * Whether or not app is in AMP mode. + */ + export const amp: boolean; + /** + * Whether the app is running in the browser or on the server. + */ + export const browser: boolean; + /** + * `true` in development mode, `false` in production. + */ + export const dev: boolean; +} + +declare module '$app/navigation' { + /** + * Returns a Promise that resolves when SvelteKit navigates (or fails to navigate, in which case the promise rejects) to the specified href. + * + * @param href Where to navigate to + * @param opts Optional. If `replaceState` is `true`, a new history entry won't be created. If `noscroll` is `true`, the browser won't scroll to the top of the page after navigation. + */ + export function goto( + href: string, + opts?: { replaceState?: boolean; noscroll?: boolean } + ): Promise; + /** + * Programmatically prefetches the given page, which means a) ensuring that the code for the page is loaded, and b) calling the page's load function with the appropriate options. + * This is the same behaviour that SvelteKit triggers when the user taps or mouses over an `` element with `sveltekit:prefetch`. + * If the next navigation is to `href`, the values returned from load will be used, making navigation instantaneous. + * Returns a Promise that resolves when the prefetch is complete. + * + * @param href Page to prefetch + */ + export function prefetch(href: string): Promise; + /** + * Programmatically prefetches the code for routes that haven't yet been fetched. + * Typically, you might call this to speed up subsequent navigation. + * If no argument is given, all routes will be fetched, otherwise you can specify routes by any matching pathname such as `/about` (to match `src/routes/about.svelte`) + * or `/blog/*` (to match `src/routes/blog/[slug].svelte`). Unlike prefetch, this won't call preload for individual pages. + * Returns a Promise that resolves when the routes have been prefetched. + */ + export function prefetchRoutes(routes?: string[]): Promise; +} + +declare module '$app/paths' { + /** + * A root-relative (i.e. begins with a `/`) string that matches `config.kit.files.base` in your project configuration. + */ + export const base: string; + /** + * A root-relative or absolute path that matches `config.kit.files.assets` (after it has been resolved against base). + */ + export const assets: string; +} + +declare module '$app/stores' { + import { Readable, Writable } from 'svelte/store'; + import { Page } from '@sveltejs/kit'; + + /** + * A convenience function around `getContext` that returns `{ navigating, page, session }`. + * Most of the time, you won't need to use it. + */ + export function getStores(): { + navigating: Readable<{ from: string; to: string } | null>; + page: Readable; + session: Writable; + }; + /** + * A readable store whose value reflects the object passed to load functions. + */ + export const page: Readable; + /** + * A readable store. + * When navigating starts, its value is `{ from, to }`, where from and to both mirror the page store value. + * When navigating finishes, its value reverts to `null`. + */ + export const navigating: Readable<{ from: string; to: string } | null>; + /** + * A writable store whose initial value is whatever was returned from `getSession`. + * It can be written to, but this will not cause changes to persist on the server — this is something you must implement yourself. + */ + export const session: Writable; +} + +declare module '$service-worker' { + /** + * An array of URL strings representing the files generated by Vite, suitable for caching with `cache.addAll(build)`. + * This is only available to service workers. + */ + export const build: string[]; + /** + * An array of URL strings representing the files in your static directory, + * or whatever directory is specified by `config.kit.files.assets`. + * This is only available to service workers. + */ + export const assets: string[]; + /** + * The result of calling `Date.now()` at build time. + * It's useful for generating unique cache names inside your service worker, + * so that a later deployment of your app can invalidate old caches. + * This is only available to service workers. + */ + export const timestamp: number; +} diff --git a/packages/kit/types.d.ts b/packages/kit/types.d.ts index 5e34a5776ec4..2fa43cbbf724 100644 --- a/packages/kit/types.d.ts +++ b/packages/kit/types.d.ts @@ -1,3 +1,4 @@ +import './types.ambient'; import { Headers, LoadInput, LoadOutput, Logger } from './types.internal'; import { UserConfig as ViteConfig } from 'vite'; @@ -105,3 +106,10 @@ export type Handle = ( request: Request, render: (request: Request) => Response | Promise ) => Response | Promise; + +export type Page = { + host: string; + path: string; + params: Record; + query: URLSearchParams; +}; diff --git a/packages/kit/types.internal.d.ts b/packages/kit/types.internal.d.ts index cd3b292aa6b3..6a2b05b40f79 100644 --- a/packages/kit/types.internal.d.ts +++ b/packages/kit/types.internal.d.ts @@ -5,6 +5,7 @@ import { Handle, Incoming, Load, + Page, RequestHandler, Response } from './types'; @@ -85,13 +86,6 @@ export type App = { // but this can't happen until TypeScript 4.3 export type Headers = Record; -export type Page = { - host: string; - path: string; - params: Record; - query: URLSearchParams; -}; - export type LoadInput = { page: Page; fetch: (info: RequestInfo, init?: RequestInit) => Promise;