+
d
e
f
diff --git a/documentation/docs/60-appendix/10-migrating.md b/documentation/docs/60-appendix/10-migrating.md
index 4827b530cea2..1c46a7f7c694 100644
--- a/documentation/docs/60-appendix/10-migrating.md
+++ b/documentation/docs/60-appendix/10-migrating.md
@@ -85,7 +85,7 @@ Your custom error page component should be renamed from `_error.svelte` to `+err
#### Imports
-The `goto`, `prefetch` and `prefetchRoutes` imports from `@sapper/app` should be replaced with identical imports from [`$app/navigation`](/docs/modules#$app-navigation).
+The `goto`, `prefetch` and `prefetchRoutes` imports from `@sapper/app` should be replaced with `goto`, `preloadData` and `preloadCode` imports respectively from [`$app/navigation`](/docs/modules#$app-navigation).
The `stores` import from `@sapper/app` should be replaced — see the [Stores](/docs/migrating#pages-and-layouts-stores) section below.
@@ -133,7 +133,7 @@ This caused problems and is no longer the case in SvelteKit. Instead, relative U
#### <a> attributes
-- `sapper:prefetch` is now `data-sveltekit-prefetch`
+- `sapper:prefetch` is now `data-sveltekit-preload-data`
- `sapper:noscroll` is now `data-sveltekit-noscroll`
### Endpoints
diff --git a/packages/create-svelte/templates/default/src/app.html b/packages/create-svelte/templates/default/src/app.html
index c361a2248b68..effe0d0d266c 100644
--- a/packages/create-svelte/templates/default/src/app.html
+++ b/packages/create-svelte/templates/default/src/app.html
@@ -6,7 +6,7 @@
%sveltekit.head%
-
+
%sveltekit.body%
diff --git a/packages/create-svelte/templates/skeleton/src/app.html b/packages/create-svelte/templates/skeleton/src/app.html
index 30c14fdbf9cf..effe0d0d266c 100644
--- a/packages/create-svelte/templates/skeleton/src/app.html
+++ b/packages/create-svelte/templates/skeleton/src/app.html
@@ -6,7 +6,7 @@
%sveltekit.head%
-
+
%sveltekit.body%
diff --git a/packages/kit/src/runtime/app/navigation.js b/packages/kit/src/runtime/app/navigation.js
index 974e83a13e90..80f8df1c8908 100644
--- a/packages/kit/src/runtime/app/navigation.js
+++ b/packages/kit/src/runtime/app/navigation.js
@@ -17,7 +17,7 @@ export const disableScrollHandling = ssr
export const goto = ssr ? guard('goto') : client.goto;
export const invalidate = ssr ? guard('invalidate') : client.invalidate;
export const invalidateAll = ssr ? guard('invalidateAll') : client.invalidateAll;
-export const prefetch = ssr ? guard('prefetch') : client.prefetch;
-export const prefetchRoutes = ssr ? guard('prefetchRoutes') : client.prefetch_routes;
+export const preloadData = ssr ? guard('preloadData') : client.preload_data;
+export const preloadCode = ssr ? guard('preloadCode') : client.preload_code;
export const beforeNavigate = ssr ? () => {} : client.before_navigate;
export const afterNavigate = ssr ? () => {} : client.after_navigate;
diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js
index 23c0b918007e..6659e43b052d 100644
--- a/packages/kit/src/runtime/client/client.js
+++ b/packages/kit/src/runtime/client/client.js
@@ -6,7 +6,7 @@ import {
normalize_path,
add_data_suffix
} from '../../utils/url.js';
-import { find_anchor, get_base_uri, scroll_state } from './utils.js';
+import { find_anchor, get_base_uri, is_external_url, scroll_state } from './utils.js';
import {
lock_fetch,
unlock_fetch,
@@ -22,9 +22,7 @@ import { HttpError, Redirect } from '../control.js';
import { stores } from './singletons.js';
import { unwrap_promises } from '../../utils/promises.js';
import * as devalue from 'devalue';
-
-const SCROLL_KEY = 'sveltekit:scroll';
-const INDEX_KEY = 'sveltekit:index';
+import { INDEX_KEY, PRELOAD_PRIORITIES, SCROLL_KEY } from './constants.js';
const routes = parse(nodes, server_loads, dictionary, matchers);
@@ -66,7 +64,9 @@ function check_for_removed_attributes() {
if (!warned_about_attributes[attr]) {
warned_about_attributes[attr] = true;
console.error(
- `The sveltekit:${attr} attribute has been replaced with data-sveltekit-${attr}`
+ `The sveltekit:${attr} attribute has been replaced with data-sveltekit-${
+ attr === 'prefetch' ? 'preload-data' : attr
+ }`
);
}
}
@@ -159,8 +159,8 @@ export function create_client({ target, base }) {
const url = new URL(location.href);
const intent = get_navigation_intent(url, true);
- // Clear prefetch, it might be affected by the invalidation.
- // Also solves an edge case where a prefetch is triggered, the navigation for it
+ // Clear preload, it might be affected by the invalidation.
+ // Also solves an edge case where a preload is triggered, the navigation for it
// was then triggered and is still running while the invalidation kicks in,
// at which point the invalidation should take over and "win".
load_cache = null;
@@ -210,11 +210,11 @@ export function create_client({ target, base }) {
}
/** @param {URL} url */
- async function prefetch(url) {
+ async function preload_data(url) {
const intent = get_navigation_intent(url, false);
if (!intent) {
- throw new Error(`Attempted to prefetch a URL that does not belong to this app: ${url}`);
+ throw new Error(`Attempted to preload a URL that does not belong to this app: ${url}`);
}
load_cache = {
@@ -231,6 +231,17 @@ export function create_client({ target, base }) {
return load_cache.promise;
}
+ /** @param {...string} pathnames */
+ async function preload_code(...pathnames) {
+ const matching = routes.filter((route) => pathnames.some((pathname) => route.exec(pathname)));
+
+ const promises = matching.map((r) => {
+ return Promise.all([...r.layouts, r.leaf].map((load) => load?.[1]()));
+ });
+
+ await Promise.all(promises);
+ }
+
/**
* Returns `true` if update completes, `false` if it is aborted
* @param {import('./types').NavigationIntent | undefined} intent
@@ -306,7 +317,7 @@ export function create_client({ target, base }) {
history[details.replaceState ? 'replaceState' : 'pushState'](details.state, '', url);
}
- // reset prefetch synchronously after the history state has been set to avoid race conditions
+ // reset preload synchronously after the history state has been set to avoid race conditions
load_cache = null;
if (started) {
@@ -1004,7 +1015,7 @@ export function create_client({ target, base }) {
* @param {boolean} invalidating
*/
function get_navigation_intent(url, invalidating) {
- if (is_external_url(url)) return;
+ if (is_external_url(url, base)) return;
const path = decode_pathname(url.pathname.slice(base.length) || '/');
@@ -1020,11 +1031,6 @@ export function create_client({ target, base }) {
}
}
- /** @param {URL} url */
- function is_external_url(url) {
- return url.origin !== location.origin || !url.pathname.startsWith(base);
- }
-
/**
* @param {{
* url: URL;
@@ -1177,6 +1183,77 @@ export function create_client({ target, base }) {
});
}
+ function setup_preload() {
+ /** @type {NodeJS.Timeout} */
+ let mousemove_timeout;
+
+ target.addEventListener('mousemove', (event) => {
+ const target = /** @type {Element} */ (event.target);
+
+ clearTimeout(mousemove_timeout);
+ mousemove_timeout = setTimeout(() => {
+ preload(target, 2);
+ }, 20);
+ });
+
+ /** @param {Event} event */
+ function tap(event) {
+ preload(/** @type {Element} */ (event.composedPath()[0]), 1);
+ }
+
+ target.addEventListener('mousedown', tap);
+ target.addEventListener('touchstart', tap, { passive: true });
+
+ const observer = new IntersectionObserver(
+ (entries) => {
+ for (const entry of entries) {
+ if (entry.isIntersecting) {
+ preload_code(new URL(/** @type {HTMLAnchorElement} */ (entry.target).href).pathname);
+ observer.unobserve(entry.target);
+ }
+ }
+ },
+ { threshold: 0 }
+ );
+
+ /**
+ * @param {Element} element
+ * @param {number} priority
+ */
+ function preload(element, priority) {
+ const { url, options, external } = find_anchor(element, base);
+
+ if (!external) {
+ if (priority <= options.preload_data) {
+ preload_data(/** @type {URL} */ (url));
+ } else if (priority <= options.preload_code) {
+ preload_code(/** @type {URL} */ (url).pathname);
+ }
+ }
+ }
+
+ function after_navigate() {
+ observer.disconnect();
+
+ for (const a of target.querySelectorAll('a')) {
+ const { url, external, options } = find_anchor(a, base);
+
+ if (external) continue;
+
+ if (options.preload_code === PRELOAD_PRIORITIES.viewport) {
+ observer.observe(a);
+ }
+
+ if (options.preload_code === PRELOAD_PRIORITIES.eager) {
+ preload_code(/** @type {URL} */ (url).pathname);
+ }
+ }
+ }
+
+ callbacks.after_navigate.push(after_navigate);
+ after_navigate();
+ }
+
return {
after_navigate: (fn) => {
onMount(() => {
@@ -1250,23 +1327,12 @@ export function create_client({ target, base }) {
return invalidate();
},
- prefetch: async (href) => {
+ preload_data: async (href) => {
const url = new URL(href, get_base_uri(document));
- await prefetch(url);
+ await preload_data(url);
},
- // TODO rethink this API
- prefetch_routes: async (pathnames) => {
- const matching = pathnames
- ? routes.filter((route) => pathnames.some((pathname) => route.exec(pathname)))
- : routes;
-
- const promises = matching.map((r) => {
- return Promise.all([...r.layouts, r.leaf].map((load) => load?.[1]()));
- });
-
- await Promise.all(promises);
- },
+ preload_code,
apply_action: async (result) => {
if (result.type === 'error') {
@@ -1365,33 +1431,10 @@ export function create_client({ target, base }) {
}
});
- /** @param {Event} event */
- const trigger_prefetch = (event) => {
- const { url, options, has } = find_anchor(event);
- if (url && options.prefetch && !is_external_url(url)) {
- if (options.reload || has.rel_external || has.target || has.download) return;
- prefetch(url);
- }
- };
-
- /** @type {NodeJS.Timeout} */
- let mousemove_timeout;
-
- /** @param {MouseEvent|TouchEvent} event */
- const handle_mousemove = (event) => {
- clearTimeout(mousemove_timeout);
- mousemove_timeout = setTimeout(() => {
- // event.composedPath(), which is used in find_anchor, will be empty if the event is read in a timeout
- // add a layer of indirection to address that
- event.target?.dispatchEvent(
- new CustomEvent('sveltekit:trigger_prefetch', { bubbles: true })
- );
- }, 20);
- };
-
- target.addEventListener('touchstart', trigger_prefetch, { passive: true });
- target.addEventListener('mousemove', handle_mousemove);
- target.addEventListener('sveltekit:trigger_prefetch', trigger_prefetch);
+ // @ts-expect-error this isn't supported everywhere yet
+ if (!navigator.connection?.saveData) {
+ setup_preload();
+ }
/** @param {MouseEvent} event */
target.addEventListener('click', (event) => {
@@ -1401,7 +1444,10 @@ export function create_client({ target, base }) {
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
if (event.defaultPrevented) return;
- const { a, url, options, has } = find_anchor(event);
+ const { a, url, options, has } = find_anchor(
+ /** @type {Element} */ (event.composedPath()[0]),
+ base
+ );
if (!a || !url) return;
const is_svg_a_element = a instanceof SVGAElement;
@@ -1436,8 +1482,8 @@ export function create_client({ target, base }) {
// Check if new url only differs by hash and use the browser default behavior in that case
// This will ensure the `hashchange` event is fired
// Removing the hash does a full page navigation in the browser, so make sure a hash is present
- const [base, hash] = url.href.split('#');
- if (hash !== undefined && base === location.href.split('#')[0]) {
+ const [nonhash, hash] = url.href.split('#');
+ if (hash !== undefined && nonhash === location.href.split('#')[0]) {
// set this flag to distinguish between navigations triggered by
// clicking a hash link and those triggered by popstate
// TODO why not update history here directly?
diff --git a/packages/kit/src/runtime/client/constants.js b/packages/kit/src/runtime/client/constants.js
new file mode 100644
index 000000000000..b7a8d9d61700
--- /dev/null
+++ b/packages/kit/src/runtime/client/constants.js
@@ -0,0 +1,10 @@
+export const SCROLL_KEY = 'sveltekit:scroll';
+export const INDEX_KEY = 'sveltekit:index';
+
+export const PRELOAD_PRIORITIES = /** @type {const} */ ({
+ tap: 1,
+ hover: 2,
+ viewport: 3,
+ eager: 4,
+ off: -1
+});
diff --git a/packages/kit/src/runtime/client/types.d.ts b/packages/kit/src/runtime/client/types.d.ts
index d6e39578f548..3622abcf4544 100644
--- a/packages/kit/src/runtime/client/types.d.ts
+++ b/packages/kit/src/runtime/client/types.d.ts
@@ -5,8 +5,8 @@ import {
goto,
invalidate,
invalidateAll,
- prefetch,
- prefetchRoutes
+ preloadCode,
+ preloadData
} from '$app/navigation';
import { CSRPageNode, CSRPageNodeLoader, CSRRoute, TrailingSlash, Uses } from 'types';
@@ -18,8 +18,8 @@ export interface Client {
goto: typeof goto;
invalidate: typeof invalidate;
invalidateAll: typeof invalidateAll;
- prefetch: typeof prefetch;
- prefetch_routes: typeof prefetchRoutes;
+ preload_code: typeof preloadCode;
+ preload_data: typeof preloadData;
apply_action: typeof applyAction;
// private API
diff --git a/packages/kit/src/runtime/client/utils.js b/packages/kit/src/runtime/client/utils.js
index 7c0706210846..c8f7ca529138 100644
--- a/packages/kit/src/runtime/client/utils.js
+++ b/packages/kit/src/runtime/client/utils.js
@@ -1,6 +1,7 @@
import { writable } from 'svelte/store';
import { assets } from '../paths.js';
import { version } from '../env.js';
+import { PRELOAD_PRIORITIES } from './constants.js';
/* global __SVELTEKIT_APP_VERSION_FILE__, __SVELTEKIT_APP_VERSION_POLL_INTERVAL__ */
@@ -23,72 +24,137 @@ export function scroll_state() {
};
}
-/** @param {Event} event */
-export function find_anchor(event) {
+const warned = new WeakSet();
+
+/** @typedef {keyof typeof valid_link_options} LinkOptionName */
+
+const valid_link_options = /** @type {const} */ ({
+ 'preload-code': ['', 'off', 'tap', 'hover', 'viewport', 'eager'],
+ 'preload-data': ['', 'off', 'tap', 'hover'],
+ noscroll: ['', 'off'],
+ reload: ['', 'off']
+});
+
+/**
+ * @template {LinkOptionName} T
+ * @param {Element} element
+ * @param {T} name
+ */
+function link_option(element, name) {
+ const value = /** @type {typeof valid_link_options[T][number] | null} */ (
+ element.getAttribute(`data-sveltekit-${name}`)
+ );
+
+ if (__SVELTEKIT_DEV__) validate_link_option(element, name, value);
+
+ return value;
+}
+
+/**
+ * @template {LinkOptionName} T
+ * @template {typeof valid_link_options[T][number] | null} U
+ * @param {Element} element
+ * @param {T} name
+ * @param {U} value
+ */
+function validate_link_option(element, name, value) {
+ if (value === null) return;
+
+ // @ts-expect-error - includes is dumb
+ if (!warned.has(element) && !valid_link_options[name].includes(value)) {
+ console.error(
+ `Unexpected value for ${name} — should be one of ${valid_link_options[name]
+ .map((option) => JSON.stringify(option))
+ .join(', ')}`,
+ element
+ );
+
+ warned.add(element);
+ }
+}
+
+const levels = {
+ ...PRELOAD_PRIORITIES,
+ '': PRELOAD_PRIORITIES.hover
+};
+
+/**
+ * @param {Element} element
+ * @param {string} base
+ */
+export function find_anchor(element, base) {
/** @type {HTMLAnchorElement | SVGAElement | undefined} */
let a;
- /** @type {boolean | null} */
+ /** @type {typeof valid_link_options['noscroll'][number] | null} */
let noscroll = null;
- /** @type {boolean | null} */
- let prefetch = null;
+ /** @type {typeof valid_link_options['preload-code'][number] | null} */
+ let preload_code = null;
- /** @type {boolean | null} */
- let reload = null;
+ /** @type {typeof valid_link_options['preload-data'][number] | null} */
+ let preload_data = null;
- for (const element of event.composedPath()) {
- if (!(element instanceof Element)) continue;
+ /** @type {typeof valid_link_options['reload'][number] | null} */
+ let reload = null;
+ while (element !== document.documentElement) {
if (!a && element.nodeName.toUpperCase() === 'A') {
// SVG
elements have a lowercase name
a = /** @type {HTMLAnchorElement | SVGAElement} */ (element);
}
- if (noscroll === null) noscroll = get_link_option(element, 'data-sveltekit-noscroll');
- if (prefetch === null) prefetch = get_link_option(element, 'data-sveltekit-prefetch');
- if (reload === null) reload = get_link_option(element, 'data-sveltekit-reload');
- }
+ if (a) {
+ if (preload_code === null) preload_code = link_option(element, 'preload-code');
+ if (preload_data === null) preload_data = link_option(element, 'preload-data');
+ if (noscroll === null) noscroll = link_option(element, 'noscroll');
+ if (reload === null) reload = link_option(element, 'reload');
+ }
- const url = a && new URL(a instanceof SVGAElement ? a.href.baseVal : a.href, document.baseURI);
+ // @ts-expect-error handle shadow roots
+ element = element.assignedSlot ?? element.parentNode;
- return {
- a,
- url,
- options: {
- noscroll,
- prefetch,
- reload
- },
- has: a
- ? {
- rel_external: (a.getAttribute('rel') || '').split(/\s+/).includes('external'),
- download: a.hasAttribute('download'),
- target: !!(a instanceof SVGAElement ? a.target.baseVal : a.target)
- }
- : {}
- };
-}
+ // @ts-expect-error handle shadow roots
+ if (element.nodeType === 11) element = element.host;
+ }
-const warned = new WeakSet();
+ /** @type {URL | undefined} */
+ let url;
-/**
- * @param {Element} element
- * @param {string} attribute
- */
-function get_link_option(element, attribute) {
- const value = element.getAttribute(attribute);
- if (value === null) return value;
+ try {
+ url = a && new URL(a instanceof SVGAElement ? a.href.baseVal : a.href, document.baseURI);
+ } catch {}
- if (value === '') return true;
- if (value === 'off') return false;
+ const options = {
+ preload_code: levels[preload_code ?? 'off'],
+ preload_data: levels[preload_data ?? 'off'],
+ noscroll: noscroll === 'off' ? false : noscroll === '' ? true : null,
+ reload: reload === 'off' ? false : reload === '' ? true : null
+ };
- if (__SVELTEKIT_DEV__ && !warned.has(element)) {
- console.error(`Unexpected value for ${attribute} — should be "" or "off"`, element);
- warned.add(element);
- }
+ const has = a
+ ? {
+ rel_external: (a.getAttribute('rel') || '').split(/\s+/).includes('external'),
+ download: a.hasAttribute('download'),
+ target: !!(a instanceof SVGAElement ? a.target.baseVal : a.target)
+ }
+ : {};
+
+ const external =
+ !url ||
+ is_external_url(url, base) ||
+ options.reload ||
+ has.rel_external ||
+ has.target ||
+ has.download;
- return false;
+ return {
+ a,
+ url,
+ options,
+ external,
+ has
+ };
}
/** @param {any} value */
@@ -165,3 +231,11 @@ export function create_updated_store() {
check
};
}
+
+/**
+ * @param {URL} url
+ * @param {string} base
+ */
+export function is_external_url(url, base) {
+ return url.origin !== location.origin || !url.pathname.startsWith(base);
+}
diff --git a/packages/kit/test/ambient.d.ts b/packages/kit/test/ambient.d.ts
index 44e3d0c8c8a5..998e04686eaa 100644
--- a/packages/kit/test/ambient.d.ts
+++ b/packages/kit/test/ambient.d.ts
@@ -13,10 +13,10 @@ declare global {
) => Promise;
const invalidate: (url: string) => Promise;
- const prefetch: (url: string) => Promise;
+ const preloadData: (url: string) => Promise;
const beforeNavigate: (fn: (url: URL) => void | boolean) => void;
const afterNavigate: (fn: () => void) => void;
- const prefetchRoutes: (urls?: string[]) => Promise;
+ const preloadCode: (...urls: string[]) => Promise;
}
export {};
diff --git a/packages/kit/test/apps/basics/src/routes/data-sveltekit/prefetch/+page.svelte b/packages/kit/test/apps/basics/src/routes/data-sveltekit/prefetch/+page.svelte
deleted file mode 100644
index 04ebcd22ce76..000000000000
--- a/packages/kit/test/apps/basics/src/routes/data-sveltekit/prefetch/+page.svelte
+++ /dev/null
@@ -1,6 +0,0 @@
-one
-
-
diff --git a/packages/kit/test/apps/basics/src/routes/data-sveltekit/preload-data/+page.svelte b/packages/kit/test/apps/basics/src/routes/data-sveltekit/preload-data/+page.svelte
new file mode 100644
index 000000000000..e8d2726b3964
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/data-sveltekit/preload-data/+page.svelte
@@ -0,0 +1,8 @@
+one
+
+
diff --git a/packages/kit/test/apps/basics/src/routes/data-sveltekit/prefetch/target/+page.svelte b/packages/kit/test/apps/basics/src/routes/data-sveltekit/preload-data/target/+page.svelte
similarity index 100%
rename from packages/kit/test/apps/basics/src/routes/data-sveltekit/prefetch/target/+page.svelte
rename to packages/kit/test/apps/basics/src/routes/data-sveltekit/preload-data/target/+page.svelte
diff --git a/packages/kit/test/apps/basics/src/routes/routing/prefetching/hash-route/+page.js b/packages/kit/test/apps/basics/src/routes/routing/preloading/hash-route/+page.js
similarity index 100%
rename from packages/kit/test/apps/basics/src/routes/routing/prefetching/hash-route/+page.js
rename to packages/kit/test/apps/basics/src/routes/routing/preloading/hash-route/+page.js
diff --git a/packages/kit/test/apps/basics/src/routes/routing/prefetching/hash-route/+page.svelte b/packages/kit/test/apps/basics/src/routes/routing/preloading/hash-route/+page.svelte
similarity index 100%
rename from packages/kit/test/apps/basics/src/routes/routing/prefetching/hash-route/+page.svelte
rename to packages/kit/test/apps/basics/src/routes/routing/preloading/hash-route/+page.svelte
diff --git a/packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetch-error/+page.js b/packages/kit/test/apps/basics/src/routes/routing/preloading/preload-error/+page.js
similarity index 100%
rename from packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetch-error/+page.js
rename to packages/kit/test/apps/basics/src/routes/routing/preloading/preload-error/+page.js
diff --git a/packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetch-error/+page.svelte b/packages/kit/test/apps/basics/src/routes/routing/preloading/preload-error/+page.svelte
similarity index 100%
rename from packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetch-error/+page.svelte
rename to packages/kit/test/apps/basics/src/routes/routing/preloading/preload-error/+page.svelte
diff --git a/packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetched.json/+server.js b/packages/kit/test/apps/basics/src/routes/routing/preloading/preloaded.json/+server.js
similarity index 69%
rename from packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetched.json/+server.js
rename to packages/kit/test/apps/basics/src/routes/routing/preloading/preloaded.json/+server.js
index 04004e8da206..0c42a739e0aa 100644
--- a/packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetched.json/+server.js
+++ b/packages/kit/test/apps/basics/src/routes/routing/preloading/preloaded.json/+server.js
@@ -1,5 +1,5 @@
import { json } from '@sveltejs/kit';
export function GET() {
- return json('prefetched');
+ return json('preloaded');
}
diff --git a/packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetched/+page.js b/packages/kit/test/apps/basics/src/routes/routing/preloading/preloaded/+page.js
similarity index 53%
rename from packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetched/+page.js
rename to packages/kit/test/apps/basics/src/routes/routing/preloading/preloaded/+page.js
index 2c658c282bea..5d7154c4dc7e 100644
--- a/packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetched/+page.js
+++ b/packages/kit/test/apps/basics/src/routes/routing/preloading/preloaded/+page.js
@@ -1,5 +1,5 @@
/** @type {import('@sveltejs/kit').Load} */
export async function load({ fetch }) {
- const message = await fetch('/routing/prefetching/prefetched.json').then((r) => r.json());
+ const message = await fetch('/routing/preloading/preloaded.json').then((r) => r.json());
return { message };
}
diff --git a/packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetched/+page.svelte b/packages/kit/test/apps/basics/src/routes/routing/preloading/preloaded/+page.svelte
similarity index 100%
rename from packages/kit/test/apps/basics/src/routes/routing/prefetching/prefetched/+page.svelte
rename to packages/kit/test/apps/basics/src/routes/routing/preloading/preloaded/+page.svelte
diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js
index 8d3c917f7d1a..25967356336d 100644
--- a/packages/kit/test/apps/basics/test/client.test.js
+++ b/packages/kit/test/apps/basics/test/client.test.js
@@ -668,8 +668,8 @@ test.describe('Prefetching', () => {
// also wait for network processing to complete, see
// https://playwright.dev/docs/network#network-events
await Promise.all([
- page.waitForResponse(`${baseURL}/routing/prefetching/prefetched.json`),
- app.prefetch('/routing/prefetching/prefetched')
+ page.waitForResponse(`${baseURL}/routing/preloading/preloaded.json`),
+ app.preloadData('/routing/preloading/preloaded')
]);
// svelte request made is environment dependent
@@ -681,53 +681,53 @@ test.describe('Prefetching', () => {
expect(requests.filter((req) => req.endsWith('.js')).length).toBeGreaterThan(0);
}
- expect(requests.includes(`${baseURL}/routing/prefetching/prefetched.json`)).toBe(true);
+ expect(requests.includes(`${baseURL}/routing/preloading/preloaded.json`)).toBe(true);
requests = [];
- await app.goto('/routing/prefetching/prefetched');
+ await app.goto('/routing/preloading/preloaded');
expect(requests).toEqual([]);
try {
- await app.prefetch('https://example.com');
+ await app.preloadData('https://example.com');
throw new Error('Error was not thrown');
} catch (/** @type {any} */ e) {
- expect(e.message).toMatch('Attempted to prefetch a URL that does not belong to this app');
+ expect(e.message).toMatch('Attempted to preload a URL that does not belong to this app');
}
});
- test('chooses correct route when hash route is prefetched but regular route is clicked', async ({
+ test('chooses correct route when hash route is preloaded but regular route is clicked', async ({
app,
page
}) => {
await page.goto('/routing/a');
- await app.prefetch('/routing/prefetching/hash-route#please-dont-show-me');
- await app.goto('/routing/prefetching/hash-route');
+ await app.preloadData('/routing/preloading/hash-route#please-dont-show-me');
+ await app.goto('/routing/preloading/hash-route');
await expect(page.locator('h1')).not.toHaveText('Oopsie');
});
test('does not rerun load on calls to duplicate preload hash route', async ({ app, page }) => {
await page.goto('/routing/a');
- await app.prefetch('/routing/prefetching/hash-route#please-dont-show-me');
- await app.prefetch('/routing/prefetching/hash-route#please-dont-show-me');
- await app.goto('/routing/prefetching/hash-route#please-dont-show-me');
+ await app.preloadData('/routing/preloading/hash-route#please-dont-show-me');
+ await app.preloadData('/routing/preloading/hash-route#please-dont-show-me');
+ await app.goto('/routing/preloading/hash-route#please-dont-show-me');
await expect(page.locator('p')).toHaveText('Loaded 1 times.');
});
test('does not rerun load on calls to different preload hash route', async ({ app, page }) => {
await page.goto('/routing/a');
- await app.prefetch('/routing/prefetching/hash-route#please-dont-show-me');
- await app.prefetch('/routing/prefetching/hash-route#please-dont-show-me-jr');
- await app.goto('/routing/prefetching/hash-route#please-dont-show-me');
+ await app.preloadData('/routing/preloading/hash-route#please-dont-show-me');
+ await app.preloadData('/routing/preloading/hash-route#please-dont-show-me-jr');
+ await app.goto('/routing/preloading/hash-route#please-dont-show-me');
await expect(page.locator('p')).toHaveText('Loaded 1 times.');
});
- test('does rerun load when prefetch errored', async ({ app, page }) => {
+ test('does rerun load when preload errored', async ({ app, page }) => {
await page.goto('/routing/a');
- await app.prefetch('/routing/prefetching/prefetch-error');
- await app.goto('/routing/prefetching/prefetch-error');
+ await app.preloadData('/routing/preloading/preload-error');
+ await app.goto('/routing/preloading/preload-error');
await expect(page.locator('p')).toHaveText('hello');
});
});
@@ -736,7 +736,7 @@ test.describe('Routing', () => {
test('navigates to a new page without reloading', async ({ app, page, clicknav }) => {
await page.goto('/routing');
- await app.prefetch('/routing/a').catch((e) => {
+ await app.preloadData('/routing/a').catch((e) => {
// from error handler tests; ignore
if (!e.message.includes('Crashing now')) throw e;
});
@@ -820,7 +820,7 @@ test.describe('Shadow DOM', () => {
test('client router captures anchors in shadow dom', async ({ app, page, clicknav }) => {
await page.goto('/routing/shadow-dom');
- await app.prefetch('/routing/a').catch((e) => {
+ await app.preloadData('/routing/a').catch((e) => {
// from error handler tests; ignore
if (!e.message.includes('Crashing now')) throw e;
});
@@ -1061,38 +1061,38 @@ test.describe.serial('Invalidation', () => {
});
test.describe('data-sveltekit attributes', () => {
- test('data-sveltekit-prefetch', async ({ baseURL, page }) => {
+ test('data-sveltekit-preload-data', async ({ baseURL, page }) => {
/** @type {string[]} */
const requests = [];
page.on('request', (r) => requests.push(r.url()));
const module = process.env.DEV
- ? `${baseURL}/src/routes/data-sveltekit/prefetch/target/+page.svelte`
- : `${baseURL}/_app/immutable/components/pages/data-sveltekit/prefetch/target/_page`;
+ ? `${baseURL}/src/routes/data-sveltekit/preload-data/target/+page.svelte`
+ : `${baseURL}/_app/immutable/components/pages/data-sveltekit/preload-data/target/_page`;
- await page.goto('/data-sveltekit/prefetch');
+ await page.goto('/data-sveltekit/preload-data');
await page.locator('#one').dispatchEvent('mousemove');
await Promise.all([
- page.waitForTimeout(100), // wait for prefetching to start
- page.waitForLoadState('networkidle') // wait for prefetching to finish
+ page.waitForTimeout(100), // wait for preloading to start
+ page.waitForLoadState('networkidle') // wait for preloading to finish
]);
expect(requests.find((r) => r.startsWith(module))).toBeDefined();
requests.length = 0;
- await page.goto('/data-sveltekit/prefetch');
+ await page.goto('/data-sveltekit/preload-data');
await page.locator('#two').dispatchEvent('mousemove');
await Promise.all([
- page.waitForTimeout(100), // wait for prefetching to start
- page.waitForLoadState('networkidle') // wait for prefetching to finish
+ page.waitForTimeout(100), // wait for preloading to start
+ page.waitForLoadState('networkidle') // wait for preloading to finish
]);
expect(requests.find((r) => r.startsWith(module))).toBeDefined();
requests.length = 0;
- await page.goto('/data-sveltekit/prefetch');
+ await page.goto('/data-sveltekit/preload-data');
await page.locator('#three').dispatchEvent('mousemove');
await Promise.all([
- page.waitForTimeout(100), // wait for prefetching to start
- page.waitForLoadState('networkidle') // wait for prefetching to finish
+ page.waitForTimeout(100), // wait for preloading to start
+ page.waitForLoadState('networkidle') // wait for preloading to finish
]);
expect(requests.find((r) => r.startsWith(module))).toBeUndefined();
});
diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js
index 633ea3b7a880..82ef086752cf 100644
--- a/packages/kit/test/apps/basics/test/test.js
+++ b/packages/kit/test/apps/basics/test/test.js
@@ -1164,7 +1164,7 @@ test.describe('$app/stores', () => {
expect(await page.textContent('#nav-status')).toBe('not currently navigating');
if (javaScriptEnabled) {
- await app.prefetchRoutes(['/store/navigating/b']);
+ await app.preloadCode('/store/navigating/b');
const res = await Promise.all([
page.click('a[href="/store/navigating/b"]'),
diff --git a/packages/kit/test/apps/options/source/pages/custom-extensions/+layout.svelte b/packages/kit/test/apps/options/source/pages/custom-extensions/+layout.svelte
index aa61d9f00f23..839e4e610c88 100644
--- a/packages/kit/test/apps/options/source/pages/custom-extensions/+layout.svelte
+++ b/packages/kit/test/apps/options/source/pages/custom-extensions/+layout.svelte
@@ -1,8 +1,8 @@
diff --git a/packages/kit/test/apps/options/source/pages/prefetching/+page.svelte b/packages/kit/test/apps/options/source/pages/prefetching/+page.svelte
deleted file mode 100644
index 4d55ffcdf64e..000000000000
--- a/packages/kit/test/apps/options/source/pages/prefetching/+page.svelte
+++ /dev/null
@@ -1 +0,0 @@
-click me
diff --git a/packages/kit/test/apps/options/source/pages/preloading/+page.svelte b/packages/kit/test/apps/options/source/pages/preloading/+page.svelte
new file mode 100644
index 000000000000..10d9cdce50d8
--- /dev/null
+++ b/packages/kit/test/apps/options/source/pages/preloading/+page.svelte
@@ -0,0 +1 @@
+click me
diff --git a/packages/kit/test/apps/options/source/pages/prefetching/prefetched/+page.server.js b/packages/kit/test/apps/options/source/pages/preloading/preloaded/+page.server.js
similarity index 63%
rename from packages/kit/test/apps/options/source/pages/prefetching/prefetched/+page.server.js
rename to packages/kit/test/apps/options/source/pages/preloading/preloaded/+page.server.js
index 085b3690a2c2..eab0a6b365ab 100644
--- a/packages/kit/test/apps/options/source/pages/prefetching/prefetched/+page.server.js
+++ b/packages/kit/test/apps/options/source/pages/preloading/preloaded/+page.server.js
@@ -1,5 +1,5 @@
export function load() {
return {
- message: 'prefetched'
+ message: 'preloaded'
};
}
diff --git a/packages/kit/test/apps/options/source/pages/prefetching/prefetched/+page.svelte b/packages/kit/test/apps/options/source/pages/preloading/preloaded/+page.svelte
similarity index 100%
rename from packages/kit/test/apps/options/source/pages/prefetching/prefetched/+page.svelte
rename to packages/kit/test/apps/options/source/pages/preloading/preloaded/+page.svelte
diff --git a/packages/kit/test/apps/options/test/test.js b/packages/kit/test/apps/options/test/test.js
index 149111ca34c8..a60fdcb5520d 100644
--- a/packages/kit/test/apps/options/test/test.js
+++ b/packages/kit/test/apps/options/test/test.js
@@ -184,10 +184,10 @@ test.describe('trailingSlash', () => {
});
});
- test('accounts for trailingSlash when prefetching', async ({ app, page, javaScriptEnabled }) => {
+ test('accounts for trailingSlash when preloading', async ({ app, page, javaScriptEnabled }) => {
if (!javaScriptEnabled) return;
- await page.goto('/path-base/prefetching');
+ await page.goto('/path-base/preloading');
/** @type {string[]} */
let requests = [];
@@ -195,7 +195,7 @@ test.describe('trailingSlash', () => {
// also wait for network processing to complete, see
// https://playwright.dev/docs/network#network-events
- await app.prefetch('/path-base/prefetching/prefetched');
+ await app.preloadData('/path-base/preloading/preloaded');
// svelte request made is environment dependent
if (process.env.DEV) {
@@ -204,10 +204,10 @@ test.describe('trailingSlash', () => {
expect(requests.filter((req) => req.endsWith('.js')).length).toBeGreaterThan(0);
}
- expect(requests.includes(`/path-base/prefetching/prefetched/__data.json`)).toBe(true);
+ expect(requests.includes(`/path-base/preloading/preloaded/__data.json`)).toBe(true);
requests = [];
- await app.goto('/path-base/prefetching/prefetched');
+ await app.goto('/path-base/preloading/preloaded');
expect(requests).toEqual([]);
});
});
diff --git a/packages/kit/test/setup.js b/packages/kit/test/setup.js
index 6bc6ca76fa8b..60931035115d 100644
--- a/packages/kit/test/setup.js
+++ b/packages/kit/test/setup.js
@@ -1,8 +1,8 @@
import {
goto,
invalidate,
- prefetch,
- prefetchRoutes,
+ preloadCode,
+ preloadData,
beforeNavigate,
afterNavigate
} from '$app/navigation';
@@ -14,8 +14,8 @@ export function setup() {
Object.assign(window, {
goto,
invalidate,
- prefetch,
- prefetchRoutes,
+ preloadCode,
+ preloadData,
beforeNavigate,
afterNavigate
});
diff --git a/packages/kit/test/utils.d.ts b/packages/kit/test/utils.d.ts
index 31d83058f9e4..2fda168d741c 100644
--- a/packages/kit/test/utils.d.ts
+++ b/packages/kit/test/utils.d.ts
@@ -17,8 +17,8 @@ export const test: TestType<
invalidate(url: string): Promise;
beforeNavigate(url: URL): void | boolean;
afterNavigate(url: URL): void;
- prefetch(url: string): Promise;
- prefetchRoutes(urls: string[]): Promise;
+ preloadCode(...urls: string[]): Promise;
+ preloadData(url: string): Promise;
};
clicknav(selector: string, options?: { timeout?: number }): Promise;
in_view(selector: string): Promise;
diff --git a/packages/kit/test/utils.js b/packages/kit/test/utils.js
index 4bfc9ebdc94e..a4591d546bae 100644
--- a/packages/kit/test/utils.js
+++ b/packages/kit/test/utils.js
@@ -37,16 +37,16 @@ export const test = base.extend({
afterNavigate: () => page.evaluate(() => afterNavigate(() => {})),
/**
- * @param {string} url
+ * @param {string[]} urls
* @returns {Promise}
*/
- prefetch: (url) => page.evaluate((/** @type {string} */ url) => prefetch(url), url),
+ preloadCode: (...urls) => page.evaluate((urls) => preloadCode(...urls), urls),
/**
- * @param {string[]} [urls]
+ * @param {string} url
* @returns {Promise}
*/
- prefetchRoutes: (urls) => page.evaluate((urls) => prefetchRoutes(urls), urls)
+ preloadData: (url) => page.evaluate((/** @type {string} */ url) => preloadData(url), url)
});
},
diff --git a/packages/kit/types/ambient.d.ts b/packages/kit/types/ambient.d.ts
index 40c17382bb68..999adb62b2ab 100644
--- a/packages/kit/types/ambient.d.ts
+++ b/packages/kit/types/ambient.d.ts
@@ -186,8 +186,8 @@ declare module '$app/forms' {
* goto,
* invalidate,
* invalidateAll,
- * prefetch,
- * prefetchRoutes
+ * preloadCode,
+ * preloadData
* } from '$app/navigation';
* ```
*/
@@ -251,28 +251,27 @@ declare module '$app/navigation' {
*/
export function invalidateAll(): Promise;
/**
- * Programmatically prefetches the given page, which means
+ * Programmatically preloads the given page, which means
* 1. ensuring that the code for the page is loaded, and
* 2. 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 `data-sveltekit-prefetch`.
+ * This is the same behaviour that SvelteKit triggers when the user taps or mouses over an `` element with `data-sveltekit-preload-data`.
* 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.
+ * Returns a Promise that resolves when the preload is complete.
*
- * @param href Page to prefetch
+ * @param href Page to preload
*/
- export function prefetch(href: string): Promise;
+ export function preloadData(href: string): Promise;
/**
- * Programmatically prefetches the code for routes that haven't yet been fetched.
+ * Programmatically imports 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`).
+ * 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 load for individual pages.
- * Returns a Promise that resolves when the routes have been prefetched.
+ * Unlike `preloadData`, this won't call `load` functions.
+ * Returns a Promise that resolves when the modules have been imported.
*/
- export function prefetchRoutes(routes?: string[]): Promise;
+ export function preloadCode(...urls: string[]): Promise;
/**
* A navigation interceptor that triggers before we navigate to a new URL, whether by clicking a link, calling `goto(...)`, or using the browser back/forward controls.
diff --git a/sites/kit.svelte.dev/src/lib/docs/Contents.svelte b/sites/kit.svelte.dev/src/lib/docs/Contents.svelte
index 3a4e398fc00c..28f5ab516bcb 100644
--- a/sites/kit.svelte.dev/src/lib/docs/Contents.svelte
+++ b/sites/kit.svelte.dev/src/lib/docs/Contents.svelte
@@ -17,7 +17,7 @@
{#each section.pages as { title, path }}
dispatch('select', { href: result.href })}
data-has-node={result.node ? true : undefined}
diff --git a/sites/kit.svelte.dev/src/routes/+page.svelte b/sites/kit.svelte.dev/src/routes/+page.svelte
index bcd2a02bd792..77409b3682a6 100644
--- a/sites/kit.svelte.dev/src/routes/+page.svelte
+++ b/sites/kit.svelte.dev/src/routes/+page.svelte
@@ -41,7 +41,7 @@
of an SPA
- read the docs
+ read the docs