@sveltejs/kit 1.0.0-next.563 → 1.0.0-next.565
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/runtime/app/navigation.js +2 -2
- package/src/runtime/client/client.js +107 -61
- package/src/runtime/client/constants.js +10 -0
- package/src/runtime/client/fetcher.js +6 -1
- package/src/runtime/client/types.d.ts +4 -4
- package/src/runtime/client/utils.js +120 -46
- package/types/ambient.d.ts +14 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sveltejs/kit",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.565",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/sveltejs/kit",
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"set-cookie-parser": "^2.5.1",
|
|
22
22
|
"sirv": "^2.0.2",
|
|
23
23
|
"tiny-glob": "^0.2.9",
|
|
24
|
-
"undici": "5.
|
|
24
|
+
"undici": "5.13.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@playwright/test": "1.
|
|
27
|
+
"@playwright/test": "^1.28.1",
|
|
28
28
|
"@types/connect": "^3.4.35",
|
|
29
29
|
"@types/marked": "^4.0.7",
|
|
30
30
|
"@types/mime": "^3.0.1",
|
|
@@ -17,7 +17,7 @@ export const disableScrollHandling = ssr
|
|
|
17
17
|
export const goto = ssr ? guard('goto') : client.goto;
|
|
18
18
|
export const invalidate = ssr ? guard('invalidate') : client.invalidate;
|
|
19
19
|
export const invalidateAll = ssr ? guard('invalidateAll') : client.invalidateAll;
|
|
20
|
-
export const
|
|
21
|
-
export const
|
|
20
|
+
export const preloadData = ssr ? guard('preloadData') : client.preload_data;
|
|
21
|
+
export const preloadCode = ssr ? guard('preloadCode') : client.preload_code;
|
|
22
22
|
export const beforeNavigate = ssr ? () => {} : client.before_navigate;
|
|
23
23
|
export const afterNavigate = ssr ? () => {} : client.after_navigate;
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
normalize_path,
|
|
7
7
|
add_data_suffix
|
|
8
8
|
} from '../../utils/url.js';
|
|
9
|
-
import { find_anchor, get_base_uri, scroll_state } from './utils.js';
|
|
9
|
+
import { find_anchor, get_base_uri, is_external_url, scroll_state } from './utils.js';
|
|
10
10
|
import {
|
|
11
11
|
lock_fetch,
|
|
12
12
|
unlock_fetch,
|
|
@@ -22,9 +22,7 @@ import { HttpError, Redirect } from '../control.js';
|
|
|
22
22
|
import { stores } from './singletons.js';
|
|
23
23
|
import { unwrap_promises } from '../../utils/promises.js';
|
|
24
24
|
import * as devalue from 'devalue';
|
|
25
|
-
|
|
26
|
-
const SCROLL_KEY = 'sveltekit:scroll';
|
|
27
|
-
const INDEX_KEY = 'sveltekit:index';
|
|
25
|
+
import { INDEX_KEY, PRELOAD_PRIORITIES, SCROLL_KEY } from './constants.js';
|
|
28
26
|
|
|
29
27
|
const routes = parse(nodes, server_loads, dictionary, matchers);
|
|
30
28
|
|
|
@@ -66,7 +64,9 @@ function check_for_removed_attributes() {
|
|
|
66
64
|
if (!warned_about_attributes[attr]) {
|
|
67
65
|
warned_about_attributes[attr] = true;
|
|
68
66
|
console.error(
|
|
69
|
-
`The sveltekit:${attr} attribute has been replaced with data-sveltekit-${
|
|
67
|
+
`The sveltekit:${attr} attribute has been replaced with data-sveltekit-${
|
|
68
|
+
attr === 'prefetch' ? 'preload-data' : attr
|
|
69
|
+
}`
|
|
70
70
|
);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
@@ -159,8 +159,8 @@ export function create_client({ target, base }) {
|
|
|
159
159
|
|
|
160
160
|
const url = new URL(location.href);
|
|
161
161
|
const intent = get_navigation_intent(url, true);
|
|
162
|
-
// Clear
|
|
163
|
-
// Also solves an edge case where a
|
|
162
|
+
// Clear preload, it might be affected by the invalidation.
|
|
163
|
+
// Also solves an edge case where a preload is triggered, the navigation for it
|
|
164
164
|
// was then triggered and is still running while the invalidation kicks in,
|
|
165
165
|
// at which point the invalidation should take over and "win".
|
|
166
166
|
load_cache = null;
|
|
@@ -210,11 +210,11 @@ export function create_client({ target, base }) {
|
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
/** @param {URL} url */
|
|
213
|
-
async function
|
|
213
|
+
async function preload_data(url) {
|
|
214
214
|
const intent = get_navigation_intent(url, false);
|
|
215
215
|
|
|
216
216
|
if (!intent) {
|
|
217
|
-
throw new Error(`Attempted to
|
|
217
|
+
throw new Error(`Attempted to preload a URL that does not belong to this app: ${url}`);
|
|
218
218
|
}
|
|
219
219
|
|
|
220
220
|
load_cache = {
|
|
@@ -231,6 +231,17 @@ export function create_client({ target, base }) {
|
|
|
231
231
|
return load_cache.promise;
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
+
/** @param {...string} pathnames */
|
|
235
|
+
async function preload_code(...pathnames) {
|
|
236
|
+
const matching = routes.filter((route) => pathnames.some((pathname) => route.exec(pathname)));
|
|
237
|
+
|
|
238
|
+
const promises = matching.map((r) => {
|
|
239
|
+
return Promise.all([...r.layouts, r.leaf].map((load) => load?.[1]()));
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
await Promise.all(promises);
|
|
243
|
+
}
|
|
244
|
+
|
|
234
245
|
/**
|
|
235
246
|
* Returns `true` if update completes, `false` if it is aborted
|
|
236
247
|
* @param {import('./types').NavigationIntent | undefined} intent
|
|
@@ -306,7 +317,7 @@ export function create_client({ target, base }) {
|
|
|
306
317
|
history[details.replaceState ? 'replaceState' : 'pushState'](details.state, '', url);
|
|
307
318
|
}
|
|
308
319
|
|
|
309
|
-
// reset
|
|
320
|
+
// reset preload synchronously after the history state has been set to avoid race conditions
|
|
310
321
|
load_cache = null;
|
|
311
322
|
|
|
312
323
|
if (started) {
|
|
@@ -478,7 +489,7 @@ export function create_client({ target, base }) {
|
|
|
478
489
|
params,
|
|
479
490
|
route,
|
|
480
491
|
status,
|
|
481
|
-
url,
|
|
492
|
+
url: new URL(url),
|
|
482
493
|
form,
|
|
483
494
|
// The whole page store is updated, but this way the object reference stays the same
|
|
484
495
|
data: data_changed ? data : page.data
|
|
@@ -1004,7 +1015,7 @@ export function create_client({ target, base }) {
|
|
|
1004
1015
|
* @param {boolean} invalidating
|
|
1005
1016
|
*/
|
|
1006
1017
|
function get_navigation_intent(url, invalidating) {
|
|
1007
|
-
if (is_external_url(url)) return;
|
|
1018
|
+
if (is_external_url(url, base)) return;
|
|
1008
1019
|
|
|
1009
1020
|
const path = decode_pathname(url.pathname.slice(base.length) || '/');
|
|
1010
1021
|
|
|
@@ -1020,11 +1031,6 @@ export function create_client({ target, base }) {
|
|
|
1020
1031
|
}
|
|
1021
1032
|
}
|
|
1022
1033
|
|
|
1023
|
-
/** @param {URL} url */
|
|
1024
|
-
function is_external_url(url) {
|
|
1025
|
-
return url.origin !== location.origin || !url.pathname.startsWith(base);
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
1034
|
/**
|
|
1029
1035
|
* @param {{
|
|
1030
1036
|
* url: URL;
|
|
@@ -1177,6 +1183,77 @@ export function create_client({ target, base }) {
|
|
|
1177
1183
|
});
|
|
1178
1184
|
}
|
|
1179
1185
|
|
|
1186
|
+
function setup_preload() {
|
|
1187
|
+
/** @type {NodeJS.Timeout} */
|
|
1188
|
+
let mousemove_timeout;
|
|
1189
|
+
|
|
1190
|
+
target.addEventListener('mousemove', (event) => {
|
|
1191
|
+
const target = /** @type {Element} */ (event.target);
|
|
1192
|
+
|
|
1193
|
+
clearTimeout(mousemove_timeout);
|
|
1194
|
+
mousemove_timeout = setTimeout(() => {
|
|
1195
|
+
preload(target, 2);
|
|
1196
|
+
}, 20);
|
|
1197
|
+
});
|
|
1198
|
+
|
|
1199
|
+
/** @param {Event} event */
|
|
1200
|
+
function tap(event) {
|
|
1201
|
+
preload(/** @type {Element} */ (event.composedPath()[0]), 1);
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
target.addEventListener('mousedown', tap);
|
|
1205
|
+
target.addEventListener('touchstart', tap, { passive: true });
|
|
1206
|
+
|
|
1207
|
+
const observer = new IntersectionObserver(
|
|
1208
|
+
(entries) => {
|
|
1209
|
+
for (const entry of entries) {
|
|
1210
|
+
if (entry.isIntersecting) {
|
|
1211
|
+
preload_code(new URL(/** @type {HTMLAnchorElement} */ (entry.target).href).pathname);
|
|
1212
|
+
observer.unobserve(entry.target);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
},
|
|
1216
|
+
{ threshold: 0 }
|
|
1217
|
+
);
|
|
1218
|
+
|
|
1219
|
+
/**
|
|
1220
|
+
* @param {Element} element
|
|
1221
|
+
* @param {number} priority
|
|
1222
|
+
*/
|
|
1223
|
+
function preload(element, priority) {
|
|
1224
|
+
const { url, options, external } = find_anchor(element, base);
|
|
1225
|
+
|
|
1226
|
+
if (!external) {
|
|
1227
|
+
if (priority <= options.preload_data) {
|
|
1228
|
+
preload_data(/** @type {URL} */ (url));
|
|
1229
|
+
} else if (priority <= options.preload_code) {
|
|
1230
|
+
preload_code(/** @type {URL} */ (url).pathname);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
function after_navigate() {
|
|
1236
|
+
observer.disconnect();
|
|
1237
|
+
|
|
1238
|
+
for (const a of target.querySelectorAll('a')) {
|
|
1239
|
+
const { url, external, options } = find_anchor(a, base);
|
|
1240
|
+
|
|
1241
|
+
if (external) continue;
|
|
1242
|
+
|
|
1243
|
+
if (options.preload_code === PRELOAD_PRIORITIES.viewport) {
|
|
1244
|
+
observer.observe(a);
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
if (options.preload_code === PRELOAD_PRIORITIES.eager) {
|
|
1248
|
+
preload_code(/** @type {URL} */ (url).pathname);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
callbacks.after_navigate.push(after_navigate);
|
|
1254
|
+
after_navigate();
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1180
1257
|
return {
|
|
1181
1258
|
after_navigate: (fn) => {
|
|
1182
1259
|
onMount(() => {
|
|
@@ -1250,23 +1327,12 @@ export function create_client({ target, base }) {
|
|
|
1250
1327
|
return invalidate();
|
|
1251
1328
|
},
|
|
1252
1329
|
|
|
1253
|
-
|
|
1330
|
+
preload_data: async (href) => {
|
|
1254
1331
|
const url = new URL(href, get_base_uri(document));
|
|
1255
|
-
await
|
|
1332
|
+
await preload_data(url);
|
|
1256
1333
|
},
|
|
1257
1334
|
|
|
1258
|
-
|
|
1259
|
-
prefetch_routes: async (pathnames) => {
|
|
1260
|
-
const matching = pathnames
|
|
1261
|
-
? routes.filter((route) => pathnames.some((pathname) => route.exec(pathname)))
|
|
1262
|
-
: routes;
|
|
1263
|
-
|
|
1264
|
-
const promises = matching.map((r) => {
|
|
1265
|
-
return Promise.all([...r.layouts, r.leaf].map((load) => load?.[1]()));
|
|
1266
|
-
});
|
|
1267
|
-
|
|
1268
|
-
await Promise.all(promises);
|
|
1269
|
-
},
|
|
1335
|
+
preload_code,
|
|
1270
1336
|
|
|
1271
1337
|
apply_action: async (result) => {
|
|
1272
1338
|
if (result.type === 'error') {
|
|
@@ -1365,33 +1431,10 @@ export function create_client({ target, base }) {
|
|
|
1365
1431
|
}
|
|
1366
1432
|
});
|
|
1367
1433
|
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
if (options.reload || has.rel_external || has.target || has.download) return;
|
|
1373
|
-
prefetch(url);
|
|
1374
|
-
}
|
|
1375
|
-
};
|
|
1376
|
-
|
|
1377
|
-
/** @type {NodeJS.Timeout} */
|
|
1378
|
-
let mousemove_timeout;
|
|
1379
|
-
|
|
1380
|
-
/** @param {MouseEvent|TouchEvent} event */
|
|
1381
|
-
const handle_mousemove = (event) => {
|
|
1382
|
-
clearTimeout(mousemove_timeout);
|
|
1383
|
-
mousemove_timeout = setTimeout(() => {
|
|
1384
|
-
// event.composedPath(), which is used in find_anchor, will be empty if the event is read in a timeout
|
|
1385
|
-
// add a layer of indirection to address that
|
|
1386
|
-
event.target?.dispatchEvent(
|
|
1387
|
-
new CustomEvent('sveltekit:trigger_prefetch', { bubbles: true })
|
|
1388
|
-
);
|
|
1389
|
-
}, 20);
|
|
1390
|
-
};
|
|
1391
|
-
|
|
1392
|
-
target.addEventListener('touchstart', trigger_prefetch, { passive: true });
|
|
1393
|
-
target.addEventListener('mousemove', handle_mousemove);
|
|
1394
|
-
target.addEventListener('sveltekit:trigger_prefetch', trigger_prefetch);
|
|
1434
|
+
// @ts-expect-error this isn't supported everywhere yet
|
|
1435
|
+
if (!navigator.connection?.saveData) {
|
|
1436
|
+
setup_preload();
|
|
1437
|
+
}
|
|
1395
1438
|
|
|
1396
1439
|
/** @param {MouseEvent} event */
|
|
1397
1440
|
target.addEventListener('click', (event) => {
|
|
@@ -1401,7 +1444,10 @@ export function create_client({ target, base }) {
|
|
|
1401
1444
|
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
1402
1445
|
if (event.defaultPrevented) return;
|
|
1403
1446
|
|
|
1404
|
-
const { a, url, options, has } = find_anchor(
|
|
1447
|
+
const { a, url, options, has } = find_anchor(
|
|
1448
|
+
/** @type {Element} */ (event.composedPath()[0]),
|
|
1449
|
+
base
|
|
1450
|
+
);
|
|
1405
1451
|
if (!a || !url) return;
|
|
1406
1452
|
|
|
1407
1453
|
const is_svg_a_element = a instanceof SVGAElement;
|
|
@@ -1436,8 +1482,8 @@ export function create_client({ target, base }) {
|
|
|
1436
1482
|
// Check if new url only differs by hash and use the browser default behavior in that case
|
|
1437
1483
|
// This will ensure the `hashchange` event is fired
|
|
1438
1484
|
// Removing the hash does a full page navigation in the browser, so make sure a hash is present
|
|
1439
|
-
const [
|
|
1440
|
-
if (hash !== undefined &&
|
|
1485
|
+
const [nonhash, hash] = url.href.split('#');
|
|
1486
|
+
if (hash !== undefined && nonhash === location.href.split('#')[0]) {
|
|
1441
1487
|
// set this flag to distinguish between navigations triggered by
|
|
1442
1488
|
// clicking a hash link and those triggered by popstate
|
|
1443
1489
|
// TODO why not update history here directly?
|
|
@@ -26,7 +26,12 @@ if (import.meta.env.DEV) {
|
|
|
26
26
|
const url = input instanceof Request ? input.url : input.toString();
|
|
27
27
|
const stack = /** @type {string} */ (new Error().stack);
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
// check if fetch was called via load_node. the lock method only checks if it was called at the
|
|
30
|
+
// same time, but not necessarily if it was called from `load`
|
|
31
|
+
// we use just the filename as the method name sometimes does not appear on the CI
|
|
32
|
+
const heuristic = can_inspect_stack_trace
|
|
33
|
+
? stack.includes('src/runtime/client/client.js')
|
|
34
|
+
: loading;
|
|
30
35
|
if (heuristic) {
|
|
31
36
|
console.warn(
|
|
32
37
|
`Loading ${url} using \`window.fetch\`. For best results, use the \`fetch\` that is passed to your \`load\` function: https://kit.svelte.dev/docs/load#making-fetch-requests`
|
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
goto,
|
|
6
6
|
invalidate,
|
|
7
7
|
invalidateAll,
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
preloadCode,
|
|
9
|
+
preloadData
|
|
10
10
|
} from '$app/navigation';
|
|
11
11
|
import { CSRPageNode, CSRPageNodeLoader, CSRRoute, TrailingSlash, Uses } from 'types';
|
|
12
12
|
|
|
@@ -18,8 +18,8 @@ export interface Client {
|
|
|
18
18
|
goto: typeof goto;
|
|
19
19
|
invalidate: typeof invalidate;
|
|
20
20
|
invalidateAll: typeof invalidateAll;
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
preload_code: typeof preloadCode;
|
|
22
|
+
preload_data: typeof preloadData;
|
|
23
23
|
apply_action: typeof applyAction;
|
|
24
24
|
|
|
25
25
|
// private API
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { writable } from 'svelte/store';
|
|
2
2
|
import { assets } from '../paths.js';
|
|
3
3
|
import { version } from '../env.js';
|
|
4
|
+
import { PRELOAD_PRIORITIES } from './constants.js';
|
|
4
5
|
|
|
5
6
|
/* global __SVELTEKIT_APP_VERSION_FILE__, __SVELTEKIT_APP_VERSION_POLL_INTERVAL__ */
|
|
6
7
|
|
|
@@ -23,72 +24,137 @@ export function scroll_state() {
|
|
|
23
24
|
};
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
const warned = new WeakSet();
|
|
28
|
+
|
|
29
|
+
/** @typedef {keyof typeof valid_link_options} LinkOptionName */
|
|
30
|
+
|
|
31
|
+
const valid_link_options = /** @type {const} */ ({
|
|
32
|
+
'preload-code': ['', 'off', 'tap', 'hover', 'viewport', 'eager'],
|
|
33
|
+
'preload-data': ['', 'off', 'tap', 'hover'],
|
|
34
|
+
noscroll: ['', 'off'],
|
|
35
|
+
reload: ['', 'off']
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @template {LinkOptionName} T
|
|
40
|
+
* @param {Element} element
|
|
41
|
+
* @param {T} name
|
|
42
|
+
*/
|
|
43
|
+
function link_option(element, name) {
|
|
44
|
+
const value = /** @type {typeof valid_link_options[T][number] | null} */ (
|
|
45
|
+
element.getAttribute(`data-sveltekit-${name}`)
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
if (__SVELTEKIT_DEV__) validate_link_option(element, name, value);
|
|
49
|
+
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @template {LinkOptionName} T
|
|
55
|
+
* @template {typeof valid_link_options[T][number] | null} U
|
|
56
|
+
* @param {Element} element
|
|
57
|
+
* @param {T} name
|
|
58
|
+
* @param {U} value
|
|
59
|
+
*/
|
|
60
|
+
function validate_link_option(element, name, value) {
|
|
61
|
+
if (value === null) return;
|
|
62
|
+
|
|
63
|
+
// @ts-expect-error - includes is dumb
|
|
64
|
+
if (!warned.has(element) && !valid_link_options[name].includes(value)) {
|
|
65
|
+
console.error(
|
|
66
|
+
`Unexpected value for ${name} — should be one of ${valid_link_options[name]
|
|
67
|
+
.map((option) => JSON.stringify(option))
|
|
68
|
+
.join(', ')}`,
|
|
69
|
+
element
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
warned.add(element);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const levels = {
|
|
77
|
+
...PRELOAD_PRIORITIES,
|
|
78
|
+
'': PRELOAD_PRIORITIES.hover
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @param {Element} element
|
|
83
|
+
* @param {string} base
|
|
84
|
+
*/
|
|
85
|
+
export function find_anchor(element, base) {
|
|
28
86
|
/** @type {HTMLAnchorElement | SVGAElement | undefined} */
|
|
29
87
|
let a;
|
|
30
88
|
|
|
31
|
-
/** @type {
|
|
89
|
+
/** @type {typeof valid_link_options['noscroll'][number] | null} */
|
|
32
90
|
let noscroll = null;
|
|
33
91
|
|
|
34
|
-
/** @type {
|
|
35
|
-
let
|
|
92
|
+
/** @type {typeof valid_link_options['preload-code'][number] | null} */
|
|
93
|
+
let preload_code = null;
|
|
36
94
|
|
|
37
|
-
/** @type {
|
|
38
|
-
let
|
|
95
|
+
/** @type {typeof valid_link_options['preload-data'][number] | null} */
|
|
96
|
+
let preload_data = null;
|
|
39
97
|
|
|
40
|
-
|
|
41
|
-
|
|
98
|
+
/** @type {typeof valid_link_options['reload'][number] | null} */
|
|
99
|
+
let reload = null;
|
|
42
100
|
|
|
101
|
+
while (element !== document.documentElement) {
|
|
43
102
|
if (!a && element.nodeName.toUpperCase() === 'A') {
|
|
44
103
|
// SVG <a> elements have a lowercase name
|
|
45
104
|
a = /** @type {HTMLAnchorElement | SVGAElement} */ (element);
|
|
46
105
|
}
|
|
47
106
|
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
107
|
+
if (a) {
|
|
108
|
+
if (preload_code === null) preload_code = link_option(element, 'preload-code');
|
|
109
|
+
if (preload_data === null) preload_data = link_option(element, 'preload-data');
|
|
110
|
+
if (noscroll === null) noscroll = link_option(element, 'noscroll');
|
|
111
|
+
if (reload === null) reload = link_option(element, 'reload');
|
|
112
|
+
}
|
|
52
113
|
|
|
53
|
-
|
|
114
|
+
// @ts-expect-error handle shadow roots
|
|
115
|
+
element = element.assignedSlot ?? element.parentNode;
|
|
54
116
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
options: {
|
|
59
|
-
noscroll,
|
|
60
|
-
prefetch,
|
|
61
|
-
reload
|
|
62
|
-
},
|
|
63
|
-
has: a
|
|
64
|
-
? {
|
|
65
|
-
rel_external: (a.getAttribute('rel') || '').split(/\s+/).includes('external'),
|
|
66
|
-
download: a.hasAttribute('download'),
|
|
67
|
-
target: !!(a instanceof SVGAElement ? a.target.baseVal : a.target)
|
|
68
|
-
}
|
|
69
|
-
: {}
|
|
70
|
-
};
|
|
71
|
-
}
|
|
117
|
+
// @ts-expect-error handle shadow roots
|
|
118
|
+
if (element.nodeType === 11) element = element.host;
|
|
119
|
+
}
|
|
72
120
|
|
|
73
|
-
|
|
121
|
+
/** @type {URL | undefined} */
|
|
122
|
+
let url;
|
|
74
123
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
*/
|
|
79
|
-
function get_link_option(element, attribute) {
|
|
80
|
-
const value = element.getAttribute(attribute);
|
|
81
|
-
if (value === null) return value;
|
|
124
|
+
try {
|
|
125
|
+
url = a && new URL(a instanceof SVGAElement ? a.href.baseVal : a.href, document.baseURI);
|
|
126
|
+
} catch {}
|
|
82
127
|
|
|
83
|
-
|
|
84
|
-
|
|
128
|
+
const options = {
|
|
129
|
+
preload_code: levels[preload_code ?? 'off'],
|
|
130
|
+
preload_data: levels[preload_data ?? 'off'],
|
|
131
|
+
noscroll: noscroll === 'off' ? false : noscroll === '' ? true : null,
|
|
132
|
+
reload: reload === 'off' ? false : reload === '' ? true : null
|
|
133
|
+
};
|
|
85
134
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
135
|
+
const has = a
|
|
136
|
+
? {
|
|
137
|
+
rel_external: (a.getAttribute('rel') || '').split(/\s+/).includes('external'),
|
|
138
|
+
download: a.hasAttribute('download'),
|
|
139
|
+
target: !!(a instanceof SVGAElement ? a.target.baseVal : a.target)
|
|
140
|
+
}
|
|
141
|
+
: {};
|
|
142
|
+
|
|
143
|
+
const external =
|
|
144
|
+
!url ||
|
|
145
|
+
is_external_url(url, base) ||
|
|
146
|
+
options.reload ||
|
|
147
|
+
has.rel_external ||
|
|
148
|
+
has.target ||
|
|
149
|
+
has.download;
|
|
90
150
|
|
|
91
|
-
return
|
|
151
|
+
return {
|
|
152
|
+
a,
|
|
153
|
+
url,
|
|
154
|
+
options,
|
|
155
|
+
external,
|
|
156
|
+
has
|
|
157
|
+
};
|
|
92
158
|
}
|
|
93
159
|
|
|
94
160
|
/** @param {any} value */
|
|
@@ -165,3 +231,11 @@ export function create_updated_store() {
|
|
|
165
231
|
check
|
|
166
232
|
};
|
|
167
233
|
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* @param {URL} url
|
|
237
|
+
* @param {string} base
|
|
238
|
+
*/
|
|
239
|
+
export function is_external_url(url, base) {
|
|
240
|
+
return url.origin !== location.origin || !url.pathname.startsWith(base);
|
|
241
|
+
}
|
package/types/ambient.d.ts
CHANGED
|
@@ -186,8 +186,8 @@ declare module '$app/forms' {
|
|
|
186
186
|
* goto,
|
|
187
187
|
* invalidate,
|
|
188
188
|
* invalidateAll,
|
|
189
|
-
*
|
|
190
|
-
*
|
|
189
|
+
* preloadCode,
|
|
190
|
+
* preloadData
|
|
191
191
|
* } from '$app/navigation';
|
|
192
192
|
* ```
|
|
193
193
|
*/
|
|
@@ -251,28 +251,27 @@ declare module '$app/navigation' {
|
|
|
251
251
|
*/
|
|
252
252
|
export function invalidateAll(): Promise<void>;
|
|
253
253
|
/**
|
|
254
|
-
* Programmatically
|
|
254
|
+
* Programmatically preloads the given page, which means
|
|
255
255
|
* 1. ensuring that the code for the page is loaded, and
|
|
256
256
|
* 2. calling the page's load function with the appropriate options.
|
|
257
257
|
*
|
|
258
|
-
* This is the same behaviour that SvelteKit triggers when the user taps or mouses over an `<a>` element with `data-sveltekit-
|
|
258
|
+
* This is the same behaviour that SvelteKit triggers when the user taps or mouses over an `<a>` element with `data-sveltekit-preload-data`.
|
|
259
259
|
* If the next navigation is to `href`, the values returned from load will be used, making navigation instantaneous.
|
|
260
|
-
* Returns a Promise that resolves when the
|
|
260
|
+
* Returns a Promise that resolves when the preload is complete.
|
|
261
261
|
*
|
|
262
|
-
* @param href Page to
|
|
262
|
+
* @param href Page to preload
|
|
263
263
|
*/
|
|
264
|
-
export function
|
|
264
|
+
export function preloadData(href: string): Promise<void>;
|
|
265
265
|
/**
|
|
266
|
-
* Programmatically
|
|
266
|
+
* Programmatically imports the code for routes that haven't yet been fetched.
|
|
267
267
|
* Typically, you might call this to speed up subsequent navigation.
|
|
268
268
|
*
|
|
269
|
-
*
|
|
270
|
-
* such as `/about` (to match `src/routes/about.svelte`) or `/blog/*` (to match `src/routes/blog/[slug].svelte`).
|
|
269
|
+
* 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`).
|
|
271
270
|
*
|
|
272
|
-
* Unlike
|
|
273
|
-
* Returns a Promise that resolves when the
|
|
271
|
+
* Unlike `preloadData`, this won't call `load` functions.
|
|
272
|
+
* Returns a Promise that resolves when the modules have been imported.
|
|
274
273
|
*/
|
|
275
|
-
export function
|
|
274
|
+
export function preloadCode(...urls: string[]): Promise<void>;
|
|
276
275
|
|
|
277
276
|
/**
|
|
278
277
|
* 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.
|
|
@@ -363,7 +362,7 @@ declare module '$app/stores' {
|
|
|
363
362
|
declare module '$service-worker' {
|
|
364
363
|
/**
|
|
365
364
|
* An array of URL strings representing the files generated by Vite, suitable for caching with `cache.addAll(build)`.
|
|
366
|
-
* During development, this is
|
|
365
|
+
* During development, this is an empty array.
|
|
367
366
|
*/
|
|
368
367
|
export const build: string[];
|
|
369
368
|
/**
|
|
@@ -372,7 +371,7 @@ declare module '$service-worker' {
|
|
|
372
371
|
export const files: string[];
|
|
373
372
|
/**
|
|
374
373
|
* An array of pathnames corresponding to prerendered pages and endpoints.
|
|
375
|
-
* During development, this is
|
|
374
|
+
* During development, this is an empty array.
|
|
376
375
|
*/
|
|
377
376
|
export const prerendered: string[];
|
|
378
377
|
/**
|