@sveltejs/kit 3.0.0-next.13 → 3.0.0-next.16
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 +7 -4
- package/src/cli.js +8 -5
- package/src/core/adapt/builder.js +20 -10
- package/src/core/config/index.js +4 -5
- package/src/core/env.js +3 -3
- package/src/core/generate_manifest/index.js +6 -0
- package/src/core/postbuild/entities.js +8 -2
- package/src/core/postbuild/fallback.js +2 -1
- package/src/core/postbuild/prerender.js +18 -13
- package/src/core/sync/create_manifest_data/conflict.js +1 -1
- package/src/core/sync/create_manifest_data/index.js +33 -2
- package/src/core/sync/sync.js +3 -2
- package/src/core/sync/utils.js +2 -2
- package/src/core/sync/write_app_types.js +72 -27
- package/src/core/sync/write_tsconfig/index.js +73 -54
- package/src/core/sync/write_tsconfig/utils.js +0 -61
- package/src/core/sync/write_tsconfig/validate.js +128 -0
- package/src/core/sync/write_types/index.js +7 -4
- package/src/exports/node/index.js +2 -7
- package/src/exports/public.d.ts +25 -18
- package/src/exports/vite/build/build_server.js +2 -3
- package/src/exports/vite/dev/index.js +44 -36
- package/src/exports/vite/index.js +91 -16
- package/src/exports/vite/utils.js +62 -15
- package/src/runner.js +4 -2
- package/src/runtime/app/forms.js +4 -7
- package/src/runtime/app/internal/transport.js +53 -0
- package/src/runtime/app/paths/client.js +2 -2
- package/src/runtime/app/paths/internal/client.js +2 -2
- package/src/runtime/app/server/remote/prerender.js +6 -10
- package/src/runtime/app/server/remote/query.js +3 -9
- package/src/runtime/app/server/remote/requested.js +2 -2
- package/src/runtime/app/server/remote/shared.js +1 -16
- package/src/runtime/client/client.js +177 -63
- package/src/runtime/client/fetcher.js +2 -13
- package/src/runtime/client/parse.js +1 -1
- package/src/runtime/client/remote-functions/command.svelte.js +1 -2
- package/src/runtime/client/remote-functions/form.svelte.js +3 -7
- package/src/runtime/client/remote-functions/prerender.svelte.js +2 -2
- package/src/runtime/client/remote-functions/query/index.js +1 -1
- package/src/runtime/client/remote-functions/query/proxy.js +2 -2
- package/src/runtime/client/remote-functions/query-batch.svelte.js +1 -1
- package/src/runtime/client/remote-functions/query-live/proxy.js +2 -12
- package/src/runtime/client/remote-functions/shared.svelte.js +1 -1
- package/src/runtime/client/utils.js +1 -0
- package/src/runtime/form-utils.js +4 -6
- package/src/runtime/pathname.js +44 -1
- package/src/runtime/server/data/index.js +5 -8
- package/src/runtime/server/dev.js +22 -0
- package/src/runtime/server/endpoint.js +3 -4
- package/src/runtime/server/fetch.js +25 -30
- package/src/runtime/server/index.js +64 -46
- package/src/runtime/server/internal.js +12 -5
- package/src/runtime/server/page/actions.js +18 -43
- package/src/runtime/server/page/crypto.js +2 -2
- package/src/runtime/server/page/csp.js +2 -1
- package/src/runtime/server/page/data_serializer.js +12 -13
- package/src/runtime/server/page/index.js +16 -32
- package/src/runtime/server/page/load_data.js +28 -44
- package/src/runtime/server/page/render.js +28 -12
- package/src/runtime/server/page/respond_with_error.js +8 -20
- package/src/runtime/server/page/serialize_data.js +2 -13
- package/src/runtime/server/page/server_routing.js +58 -9
- package/src/runtime/server/remote-functions.js +11 -15
- package/src/runtime/server/respond.js +44 -56
- package/src/runtime/server/state.js +60 -0
- package/src/runtime/server/utils.js +0 -20
- package/src/runtime/shared.js +20 -40
- package/src/runtime/utils.js +3 -0
- package/src/types/ambient-private.d.ts +1 -1
- package/src/types/ambient.d.ts +58 -7
- package/src/types/global-private.d.ts +1 -1
- package/src/types/internal.d.ts +47 -53
- package/src/utils/filesystem.js +1 -23
- package/src/utils/hash.js +21 -0
- package/src/utils/http.js +8 -7
- package/src/utils/import.js +2 -1
- package/src/utils/params.js +1 -1
- package/src/utils/regex.js +9 -0
- package/src/utils/routing.js +52 -27
- package/src/utils/url.js +1 -0
- package/src/version.js +1 -1
- package/types/index.d.ts +98 -28
- package/types/index.d.ts.map +1 -1
- package/src/exports/node/polyfills.js +0 -30
- package/src/runtime/server/app.js +0 -9
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** @import { Transport } from '@sveltejs/kit' */
|
|
2
|
+
import * as devalue from 'devalue';
|
|
3
|
+
import { DEV } from 'esm-env';
|
|
4
|
+
|
|
5
|
+
/** @type {(thing: any) => string} */
|
|
6
|
+
export let uneval = () => {
|
|
7
|
+
throw new Error(DEV ? 'called uneval before init_transport' : '');
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/** @type {(data: any) => string} */
|
|
11
|
+
export let stringify = () => {
|
|
12
|
+
throw new Error(DEV ? 'called stringify before init_transport' : '');
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/** @type {(data: string) => any} */
|
|
16
|
+
export let parse = () => {
|
|
17
|
+
throw new Error(DEV ? 'called parse before init_transport' : '');
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** @type {Record<string, (data: any) => any>} */
|
|
21
|
+
export let encoders = {};
|
|
22
|
+
|
|
23
|
+
/** @type {Record<string, (data: any) => any>} */
|
|
24
|
+
export let decoders = {};
|
|
25
|
+
|
|
26
|
+
export let has_custom_transporters = false;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
*
|
|
30
|
+
* @param {Transport} transport
|
|
31
|
+
*/
|
|
32
|
+
export function init_transport(transport) {
|
|
33
|
+
const transporters = Object.entries(transport);
|
|
34
|
+
|
|
35
|
+
has_custom_transporters = transporters.length > 0;
|
|
36
|
+
|
|
37
|
+
/** @param {unknown} thing */
|
|
38
|
+
const replacer = (thing) => {
|
|
39
|
+
for (const key of Object.keys(transport)) {
|
|
40
|
+
const encoded = transport[key].encode(thing);
|
|
41
|
+
if (encoded) {
|
|
42
|
+
return `app.decode('${key}', ${devalue.uneval(encoded, replacer)})`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
encoders = Object.fromEntries(transporters.map(([k, v]) => [k, v.encode]));
|
|
48
|
+
decoders = Object.fromEntries(transporters.map(([k, v]) => [k, v.decode]));
|
|
49
|
+
|
|
50
|
+
uneval = (data) => devalue.uneval(data, replacer);
|
|
51
|
+
stringify = (data) => devalue.stringify(data, encoders);
|
|
52
|
+
parse = (data) => devalue.parse(data, decoders);
|
|
53
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import { AssetPath, RouteId, RouteIdWithSearchOrHash,
|
|
1
|
+
/** @import { AssetPath, RouteId, RouteIdWithSearchOrHash, PathnameWithSearchOrHash, ResolvedPathname, RouteParams } from '$app/types' */
|
|
2
2
|
/** @import { ResolveArgs } from './types.js' */
|
|
3
3
|
import { base, assets, hash_routing, match_implementation } from './internal/client.js';
|
|
4
4
|
import { resolve_route } from '../../../utils/routing.js';
|
|
@@ -92,7 +92,7 @@ export function resolve(...args) {
|
|
|
92
92
|
* ```
|
|
93
93
|
* @since 2.52.0
|
|
94
94
|
*
|
|
95
|
-
* @param {
|
|
95
|
+
* @param {URL | string} url
|
|
96
96
|
* @returns {Promise<{ [K in RouteId]: { id: K; params: RouteParams<K>; } }[RouteId] | null>}
|
|
97
97
|
*/
|
|
98
98
|
export function match(url) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** @import { RouteId
|
|
1
|
+
/** @import { RouteId } from '$app/types' */
|
|
2
2
|
import { payload } from '../../../client/payload.js';
|
|
3
3
|
|
|
4
4
|
export const base = payload.base ?? __SVELTEKIT_PATHS_BASE__;
|
|
@@ -9,7 +9,7 @@ export const hash_routing = __SVELTEKIT_HASH_ROUTING__;
|
|
|
9
9
|
/**
|
|
10
10
|
* We make this configurable per-environment so that it's possible to import `$app/paths`
|
|
11
11
|
* into a service worker without importing the entire client
|
|
12
|
-
* @param {
|
|
12
|
+
* @param {URL | string} _url
|
|
13
13
|
* @returns {Promise<{ [K in RouteId]: { id: K; params: import('$app/types').RouteParams<K>; } }[RouteId] | null>}
|
|
14
14
|
*/
|
|
15
15
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
@@ -4,15 +4,11 @@
|
|
|
4
4
|
import { json } from '@sveltejs/kit';
|
|
5
5
|
import { HttpError } from '@sveltejs/kit/internal';
|
|
6
6
|
import { get_request_store } from '@sveltejs/kit/internal/server';
|
|
7
|
-
import {
|
|
7
|
+
import { stringify_remote_arg } from '../../../shared.js';
|
|
8
|
+
import { parse, stringify } from '#app/internal/transport';
|
|
8
9
|
import { noop } from '../../../../utils/functions.js';
|
|
9
10
|
import { app_dir, base } from '$app/paths/internal/server';
|
|
10
|
-
import {
|
|
11
|
-
create_validator,
|
|
12
|
-
get_response,
|
|
13
|
-
parse_remote_response,
|
|
14
|
-
run_remote_function
|
|
15
|
-
} from './shared.js';
|
|
11
|
+
import { create_validator, get_response, run_remote_function } from './shared.js';
|
|
16
12
|
|
|
17
13
|
/**
|
|
18
14
|
* Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a `fetch` call.
|
|
@@ -89,7 +85,7 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
|
|
|
89
85
|
/** @type {RemotePrerenderFunction<Input, Output> & { __: RemotePrerenderInternals }} */
|
|
90
86
|
const wrapper = (arg) => {
|
|
91
87
|
const { event, state } = get_request_store();
|
|
92
|
-
const payload = stringify_remote_arg(arg
|
|
88
|
+
const payload = stringify_remote_arg(arg);
|
|
93
89
|
|
|
94
90
|
// `get_response` (as opposed to bare `get_cache`) also registers the call in the
|
|
95
91
|
// implicit lookup, so that the result is inlined into the page payload (`data.p`)
|
|
@@ -121,7 +117,7 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
|
|
|
121
117
|
throw new HttpError(prerendered.error);
|
|
122
118
|
}
|
|
123
119
|
|
|
124
|
-
return
|
|
120
|
+
return parse(prerendered.data)._;
|
|
125
121
|
}
|
|
126
122
|
}
|
|
127
123
|
|
|
@@ -146,7 +142,7 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
|
|
|
146
142
|
const result = await promise;
|
|
147
143
|
|
|
148
144
|
if (state.prerendering) {
|
|
149
|
-
const body = { type: 'result', data: stringify({ _: result }
|
|
145
|
+
const body = { type: 'result', data: stringify({ _: result }) };
|
|
150
146
|
state.prerendering.dependencies.set(url, {
|
|
151
147
|
body: JSON.stringify(body),
|
|
152
148
|
response: json(body)
|
|
@@ -98,7 +98,7 @@ export function query(validate_or_fn, maybe_fn) {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
const { event, state } = get_request_store();
|
|
101
|
-
const payload = stringify_remote_arg(arg
|
|
101
|
+
const payload = stringify_remote_arg(arg);
|
|
102
102
|
|
|
103
103
|
return create_query_resource(__, payload, event, state, () =>
|
|
104
104
|
run_remote_function(
|
|
@@ -197,7 +197,7 @@ function live(validate_or_fn, maybe_fn) {
|
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
const { event, state } = get_request_store();
|
|
200
|
-
const payload = stringify_remote_arg(arg
|
|
200
|
+
const payload = stringify_remote_arg(arg);
|
|
201
201
|
|
|
202
202
|
return create_live_query_resource(__, payload, event, state, () =>
|
|
203
203
|
run(event, state, () => validate(arg))
|
|
@@ -376,7 +376,7 @@ function batch(validate_or_fn, maybe_fn) {
|
|
|
376
376
|
}
|
|
377
377
|
|
|
378
378
|
const { event, state } = get_request_store();
|
|
379
|
-
const payload = stringify_remote_arg(arg
|
|
379
|
+
const payload = stringify_remote_arg(arg);
|
|
380
380
|
|
|
381
381
|
return create_query_resource(__, payload, event, state, () =>
|
|
382
382
|
// Collect all the calls to the same query in the same macrotask,
|
|
@@ -575,12 +575,6 @@ function create_live_query_resource(__, payload, event, state, get_generator) {
|
|
|
575
575
|
|
|
576
576
|
return Promise.resolve();
|
|
577
577
|
},
|
|
578
|
-
/** @ts-expect-error This method no longer exists */
|
|
579
|
-
run() {
|
|
580
|
-
throw new Error(
|
|
581
|
-
'`.run()` has been removed from live queries. Use `for await (const value of liveQuery())` instead.'
|
|
582
|
-
);
|
|
583
|
-
},
|
|
584
578
|
/** @type {Promise<any>['then']} */
|
|
585
579
|
then(onfulfilled, onrejected) {
|
|
586
580
|
return get_promise().then(onfulfilled, onrejected);
|
|
@@ -163,7 +163,7 @@ export function requested(query, limit) {
|
|
|
163
163
|
*[Symbol.iterator]() {
|
|
164
164
|
for (const payload of selected) {
|
|
165
165
|
try {
|
|
166
|
-
const parsed = parse_remote_arg(payload
|
|
166
|
+
const parsed = parse_remote_arg(payload);
|
|
167
167
|
const validated = __.validate(parsed);
|
|
168
168
|
|
|
169
169
|
if (is_thenable(validated)) {
|
|
@@ -183,7 +183,7 @@ export function requested(query, limit) {
|
|
|
183
183
|
async *[Symbol.asyncIterator]() {
|
|
184
184
|
yield* race_all(selected, async (payload) => {
|
|
185
185
|
try {
|
|
186
|
-
const parsed = parse_remote_arg(payload
|
|
186
|
+
const parsed = parse_remote_arg(payload);
|
|
187
187
|
const validated = await __.validate(parsed);
|
|
188
188
|
return { arg: validated, query: __.bind(payload, validated) };
|
|
189
189
|
} catch (error) {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/** @import { RequestEvent } from '@sveltejs/kit' */
|
|
2
|
-
/** @import {
|
|
3
|
-
import { parse } from 'devalue';
|
|
2
|
+
/** @import { MaybePromise, RequestState, RemoteInternals, RequestStore, RemoteLiveQueryUserFunctionReturnType } from 'types' */
|
|
4
3
|
import { error } from '@sveltejs/kit';
|
|
5
4
|
import { with_request_store, get_request_store } from '@sveltejs/kit/internal/server';
|
|
6
5
|
|
|
@@ -80,20 +79,6 @@ export async function get_response(internals, payload, state, get_result) {
|
|
|
80
79
|
return (cache[payload] ??= get_result());
|
|
81
80
|
}
|
|
82
81
|
|
|
83
|
-
/**
|
|
84
|
-
* @param {any} data
|
|
85
|
-
* @param {ServerHooks['transport']} transport
|
|
86
|
-
*/
|
|
87
|
-
export function parse_remote_response(data, transport) {
|
|
88
|
-
/** @type {Record<string, any>} */
|
|
89
|
-
const revivers = {};
|
|
90
|
-
for (const key in transport) {
|
|
91
|
-
revivers[key] = transport[key].decode;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return parse(data, revivers);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
82
|
/**
|
|
98
83
|
* @param {RequestEvent} event
|
|
99
84
|
* @param {RequestState} state
|
|
@@ -9,7 +9,7 @@ import { settled, tick, fork, onMount, hydrate, mount } from 'svelte';
|
|
|
9
9
|
import { HttpError, Redirect, SvelteKitError } from '@sveltejs/kit/internal';
|
|
10
10
|
import { decode_pathname, strip_hash, make_trackable, normalize_path } from '../../utils/url.js';
|
|
11
11
|
import { dev_fetch, initial_fetch, lock_fetch, subsequent_fetch, unlock_fetch } from './fetcher.js';
|
|
12
|
-
import {
|
|
12
|
+
import { parse_routes, parse_server_route } from './parse.js';
|
|
13
13
|
import * as storage from './session-storage.js';
|
|
14
14
|
import {
|
|
15
15
|
find_anchor,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
scroll_state,
|
|
22
22
|
load_css
|
|
23
23
|
} from './utils.js';
|
|
24
|
-
import { base, set_match_implementation } from '$app/paths/internal/client';
|
|
24
|
+
import { base, app_dir, set_match_implementation } from '$app/paths/internal/client';
|
|
25
25
|
import * as devalue from 'devalue';
|
|
26
26
|
import {
|
|
27
27
|
HISTORY_INFO_KEY,
|
|
@@ -41,18 +41,23 @@ import {
|
|
|
41
41
|
import { get_message, get_status } from '../../utils/error.js';
|
|
42
42
|
import { page, navigating, updated, notify_version } from './state.svelte.js';
|
|
43
43
|
import { payload } from './payload.js';
|
|
44
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
add_data_suffix,
|
|
46
|
+
add_resolution_suffix,
|
|
47
|
+
route_id_resolution_pathname
|
|
48
|
+
} from '../pathname.js';
|
|
45
49
|
import { noop_span } from '../telemetry/noop.js';
|
|
46
50
|
import { read_ndjson } from './ndjson.js';
|
|
47
51
|
import Root from '../components/root.svelte';
|
|
48
52
|
import { Props, RenderNode } from '../props.svelte.js';
|
|
53
|
+
import { init_transport, parse, stringify } from '#app/internal/transport';
|
|
49
54
|
|
|
50
55
|
/**
|
|
51
56
|
* @typedef {{
|
|
52
57
|
* historyIndex: number;
|
|
53
58
|
* navigationIndex: number;
|
|
54
59
|
* pageUrl?: string;
|
|
55
|
-
* state:
|
|
60
|
+
* state: string;
|
|
56
61
|
* persistState: boolean;
|
|
57
62
|
* resetIndex: number;
|
|
58
63
|
* }} HistoryMetadata
|
|
@@ -294,6 +299,35 @@ function discard_load_cache() {
|
|
|
294
299
|
*/
|
|
295
300
|
const reroute_cache = new Map();
|
|
296
301
|
|
|
302
|
+
/**
|
|
303
|
+
* Sentinel for a route that exists but has no code to preload, i.e. a `+server.js` with no
|
|
304
|
+
* `+page`. Cached like a parsed route so that repeated `preloadCode(id)` calls for the same
|
|
305
|
+
* id don't re-request it.
|
|
306
|
+
*/
|
|
307
|
+
const ENDPOINT_ONLY = Symbol('endpoint only');
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Cache of route ID -> parsed route for server-side route resolution.
|
|
311
|
+
* Populated whenever a server route resolution occurs (`match`, link preloading,
|
|
312
|
+
* hydration), so that `preloadCode(id)` doesn't need an extra server round trip.
|
|
313
|
+
* Lives until full page reload.
|
|
314
|
+
* @type {Map<string, import('types').CSRRoute | typeof ENDPOINT_ONLY>}
|
|
315
|
+
*/
|
|
316
|
+
const route_id_cache = new Map();
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Parse a server-provided route and record it in `route_id_cache`, so that a subsequent
|
|
320
|
+
* `preloadCode(id)` for the same route doesn't need another server round trip. Always use
|
|
321
|
+
* this rather than calling `parse_server_route` directly.
|
|
322
|
+
* @param {import('types').CSRRouteServer} server_route
|
|
323
|
+
* @returns {import('types').CSRRoute}
|
|
324
|
+
*/
|
|
325
|
+
function parse_and_cache_server_route(server_route) {
|
|
326
|
+
const route = parse_server_route(server_route, app.nodes);
|
|
327
|
+
route_id_cache.set(route.id, route);
|
|
328
|
+
return route;
|
|
329
|
+
}
|
|
330
|
+
|
|
297
331
|
/**
|
|
298
332
|
* Note on before_navigate_callbacks, on_navigate_callbacks and after_navigate_callbacks:
|
|
299
333
|
* do not re-assign as some closures keep references to these Sets
|
|
@@ -454,9 +488,11 @@ async function _start(_app, _target, data) {
|
|
|
454
488
|
|
|
455
489
|
app = _app;
|
|
456
490
|
|
|
491
|
+
init_transport(app.hooks.transport ?? {});
|
|
492
|
+
|
|
457
493
|
await _app.hooks.init?.();
|
|
458
494
|
|
|
459
|
-
routes = __SVELTEKIT_CLIENT_ROUTING__ ?
|
|
495
|
+
routes = __SVELTEKIT_CLIENT_ROUTING__ ? parse_routes(_app) : [];
|
|
460
496
|
container = __SVELTEKIT_EMBEDDED__ ? _target : document.documentElement;
|
|
461
497
|
target = _target;
|
|
462
498
|
|
|
@@ -497,7 +533,7 @@ async function _start(_app, _target, data) {
|
|
|
497
533
|
[HISTORY_METADATA_KEY]: {
|
|
498
534
|
historyIndex: current_history_index,
|
|
499
535
|
navigationIndex: current_navigation_index,
|
|
500
|
-
state: {},
|
|
536
|
+
state: stringify({}),
|
|
501
537
|
persistState: false,
|
|
502
538
|
resetIndex: current_history_index
|
|
503
539
|
}
|
|
@@ -530,7 +566,7 @@ async function _start(_app, _target, data) {
|
|
|
530
566
|
type: 'enter',
|
|
531
567
|
url: resolve_url(app.hash ? decode_hash(new URL(location.href)) : location.href),
|
|
532
568
|
replace_state: true,
|
|
533
|
-
state: history_metadata?.persistState ? history_metadata.state : {},
|
|
569
|
+
state: history_metadata?.persistState ? parse(history_metadata.state) : {},
|
|
534
570
|
persist_state: history_metadata?.persistState ?? false
|
|
535
571
|
});
|
|
536
572
|
|
|
@@ -661,13 +697,13 @@ function persist_state() {
|
|
|
661
697
|
|
|
662
698
|
/**
|
|
663
699
|
* @param {string | URL} url
|
|
664
|
-
* @param {{ replace?: boolean; reset?: boolean; refreshAll?: boolean; invalidate?: Array<string | URL | ((url: URL) => boolean)>; state?: Record<string, any>; persistState?: boolean }} options
|
|
665
|
-
* @param {number} redirect_count
|
|
700
|
+
* @param {{ type?: import('@sveltejs/kit').NavigationType; replace?: boolean; reset?: boolean; refreshAll?: boolean; invalidate?: Array<string | URL | ((url: URL) => boolean)>; state?: Record<string, any>; persistState?: boolean; event?: Event }} [options]
|
|
701
|
+
* @param {number} [redirect_count]
|
|
666
702
|
* @param {{}} [nav_token]
|
|
667
703
|
* @param {NavigationIntent | undefined} [intent] navigation intent, when already known by the caller (avoids recomputing it)
|
|
668
704
|
* @returns {Promise<void>}
|
|
669
705
|
*/
|
|
670
|
-
export async function _goto(url, options, redirect_count, nav_token, intent) {
|
|
706
|
+
export async function _goto(url, options = {}, redirect_count = 0, nav_token = {}, intent) {
|
|
671
707
|
/** @type {Set<string>} */
|
|
672
708
|
let query_keys;
|
|
673
709
|
/** @type {Set<string>} */
|
|
@@ -680,12 +716,13 @@ export async function _goto(url, options, redirect_count, nav_token, intent) {
|
|
|
680
716
|
}
|
|
681
717
|
|
|
682
718
|
await navigate({
|
|
683
|
-
type: 'goto',
|
|
719
|
+
type: options.type ?? 'goto',
|
|
684
720
|
url: resolve_url(url),
|
|
685
721
|
reset: options.reset,
|
|
686
722
|
replace_state: options.replace,
|
|
687
723
|
state: options.state,
|
|
688
724
|
persist_state: options.persistState,
|
|
725
|
+
event: options.event,
|
|
689
726
|
redirect_count,
|
|
690
727
|
nav_token,
|
|
691
728
|
intent,
|
|
@@ -786,6 +823,53 @@ async function _preload_data(intent) {
|
|
|
786
823
|
return load_cache.promise;
|
|
787
824
|
}
|
|
788
825
|
|
|
826
|
+
/**
|
|
827
|
+
* Fetch and parse the route with the given ID from the server-side route resolution endpoint.
|
|
828
|
+
* Returns `ENDPOINT_ONLY` if the route exists but has no code to preload, or `undefined` if
|
|
829
|
+
* there is no such route. Only used when `router.resolution === 'server'`.
|
|
830
|
+
* @param {string} id
|
|
831
|
+
* @returns {Promise<import('types').CSRRoute | typeof ENDPOINT_ONLY | undefined>}
|
|
832
|
+
*/
|
|
833
|
+
async function load_route_by_id(id) {
|
|
834
|
+
/** @type {{ route?: import('types').CSRRouteServer, endpoint_only?: boolean }} */
|
|
835
|
+
let module;
|
|
836
|
+
|
|
837
|
+
try {
|
|
838
|
+
module = await import(
|
|
839
|
+
/* @vite-ignore */
|
|
840
|
+
base + route_id_resolution_pathname(app_dir, id)
|
|
841
|
+
);
|
|
842
|
+
} catch {
|
|
843
|
+
// if there's no module at that path the response is a 404 (or, on a static
|
|
844
|
+
// host, fallback HTML with the wrong MIME type) and the import rejects —
|
|
845
|
+
// treat it the same as an unknown route rather than surfacing a cryptic error
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
if (module.endpoint_only) {
|
|
850
|
+
// The route exists, it just has no code to preload.
|
|
851
|
+
route_id_cache.set(id, ENDPOINT_ONLY);
|
|
852
|
+
return ENDPOINT_ONLY;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
if (!module.route) return;
|
|
856
|
+
|
|
857
|
+
return parse_and_cache_server_route(module.route);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* Import the modules for a route's layout and leaf nodes, without running `load` functions.
|
|
862
|
+
* @param {import('types').CSRRoute} route
|
|
863
|
+
* @returns {Promise<void>}
|
|
864
|
+
*/
|
|
865
|
+
async function load_route_nodes(route) {
|
|
866
|
+
await Promise.all(
|
|
867
|
+
/** @type {[has_server_load: boolean, node_loader: import('types').CSRPageNodeLoader][]} */ (
|
|
868
|
+
[...route.layouts, route.leaf].filter(Boolean)
|
|
869
|
+
).map(([, node_loader]) => node_loader())
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
|
|
789
873
|
/**
|
|
790
874
|
* @param {URL} url
|
|
791
875
|
* @returns {Promise<void>}
|
|
@@ -794,11 +878,7 @@ async function _preload_code(url) {
|
|
|
794
878
|
const route = (await get_navigation_intent(url, false))?.route;
|
|
795
879
|
|
|
796
880
|
if (route) {
|
|
797
|
-
await
|
|
798
|
-
/** @type {[has_server_load: boolean, node_loader: import('types').CSRPageNodeLoader][]} */ (
|
|
799
|
-
[...route.layouts, route.leaf].filter(Boolean)
|
|
800
|
-
).map((load) => load[1]())
|
|
801
|
-
);
|
|
881
|
+
await load_route_nodes(route);
|
|
802
882
|
}
|
|
803
883
|
}
|
|
804
884
|
|
|
@@ -1607,7 +1687,7 @@ async function load_root_error_page({ error, url, route }) {
|
|
|
1607
1687
|
// client-side navigation if the root layout loader throws a redirect while
|
|
1608
1688
|
// rendering the default error page
|
|
1609
1689
|
if (error instanceof Redirect) {
|
|
1610
|
-
await _goto(new URL(error.location, location.href)
|
|
1690
|
+
await _goto(new URL(error.location, location.href));
|
|
1611
1691
|
return;
|
|
1612
1692
|
}
|
|
1613
1693
|
|
|
@@ -1729,7 +1809,7 @@ export async function get_navigation_intent(url, invalidating) {
|
|
|
1729
1809
|
return {
|
|
1730
1810
|
id: get_page_key(url),
|
|
1731
1811
|
invalidating,
|
|
1732
|
-
route:
|
|
1812
|
+
route: parse_and_cache_server_route(route),
|
|
1733
1813
|
params,
|
|
1734
1814
|
url
|
|
1735
1815
|
};
|
|
@@ -1977,10 +2057,17 @@ async function navigate({
|
|
|
1977
2057
|
url.pathname = navigation_result.props.page.url.pathname;
|
|
1978
2058
|
}
|
|
1979
2059
|
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
//
|
|
2060
|
+
if (popped) {
|
|
2061
|
+
state = popped.state;
|
|
2062
|
+
} else {
|
|
2063
|
+
// we immediately serialize-then-parse to ensure that the value is
|
|
2064
|
+
// serializable, and to prevent the developer from dangerously
|
|
2065
|
+
// relying on the identity of the serialized objects
|
|
2066
|
+
const serialized_state = stringify(state);
|
|
2067
|
+
state = parse(serialized_state);
|
|
2068
|
+
|
|
2069
|
+
// Store the serialized state so the browser history can preserve custom transport values.
|
|
2070
|
+
// This is a new navigation, rather than a popstate.
|
|
1984
2071
|
const change = replace_state ? 0 : 1;
|
|
1985
2072
|
if (type !== 'enter') {
|
|
1986
2073
|
if (reset) current_reset_index += 1;
|
|
@@ -1990,7 +2077,7 @@ async function navigate({
|
|
|
1990
2077
|
[HISTORY_METADATA_KEY]: /** @satisfies {HistoryMetadata} */ ({
|
|
1991
2078
|
historyIndex: (current_history_index += change),
|
|
1992
2079
|
navigationIndex: (current_navigation_index += change),
|
|
1993
|
-
state,
|
|
2080
|
+
state: serialized_state,
|
|
1994
2081
|
persistState: persist_state,
|
|
1995
2082
|
resetIndex: current_reset_index
|
|
1996
2083
|
})
|
|
@@ -2656,45 +2743,76 @@ export async function preloadData(href) {
|
|
|
2656
2743
|
* Programmatically imports the code for routes that haven't yet been fetched.
|
|
2657
2744
|
* Typically, you might call this to speed up subsequent navigation.
|
|
2658
2745
|
*
|
|
2659
|
-
*
|
|
2746
|
+
* Takes a route ID such as `/about` or `/blog/[slug]`. Unlike pathnames, route IDs
|
|
2747
|
+
* are never prefixed with the app's [base path](https://svelte.dev/docs/kit/configuration#paths).
|
|
2748
|
+
* If you have a pathname rather than a route ID, you can convert it with
|
|
2749
|
+
* [`match`](https://svelte.dev/docs/kit/$app-paths#match) from `$app/paths`:
|
|
2750
|
+
*
|
|
2751
|
+
* ```js
|
|
2752
|
+
* import { match } from '$app/paths';
|
|
2753
|
+
* import { preloadCode } from '$app/navigation';
|
|
2754
|
+
*
|
|
2755
|
+
* const matched = await match('/blog/hello-world');
|
|
2756
|
+
* if (matched) await preloadCode(matched.id);
|
|
2757
|
+
* ```
|
|
2660
2758
|
*
|
|
2661
2759
|
* Unlike `preloadData`, this won't call `load` functions.
|
|
2662
2760
|
* Returns a Promise that resolves when the modules have been imported.
|
|
2663
2761
|
*
|
|
2664
|
-
* @param {
|
|
2762
|
+
* @param {RouteId} id
|
|
2665
2763
|
* @returns {Promise<void>}
|
|
2666
2764
|
*/
|
|
2667
|
-
export async function preloadCode(
|
|
2765
|
+
export async function preloadCode(id) {
|
|
2668
2766
|
if (!BROWSER) {
|
|
2669
2767
|
throw new Error('Cannot call preloadCode(...) on the server');
|
|
2670
2768
|
}
|
|
2671
2769
|
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2770
|
+
if (DEV && id[0] !== '/') {
|
|
2771
|
+
throw new Error(
|
|
2772
|
+
`argument passed to preloadCode must be a route ID (i.e. "/blog/[slug]" rather than "blog/[slug]")`
|
|
2773
|
+
);
|
|
2774
|
+
}
|
|
2675
2775
|
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
'argument passed to preloadCode must be a pathname (i.e. "/about" rather than "http://example.com/about"'
|
|
2680
|
-
);
|
|
2681
|
-
}
|
|
2776
|
+
const route = __SVELTEKIT_CLIENT_ROUTING__
|
|
2777
|
+
? routes.find((r) => r.id === id)
|
|
2778
|
+
: (route_id_cache.get(id) ?? (await load_route_by_id(id)));
|
|
2682
2779
|
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2780
|
+
if (route === ENDPOINT_ONLY) {
|
|
2781
|
+
if (DEV) {
|
|
2782
|
+
console.warn(
|
|
2783
|
+
`'${id}' has no \`+page\`, so there is no code to preload. If you meant to warm up an ` +
|
|
2784
|
+
`endpoint, request it with \`fetch\` instead.`
|
|
2686
2785
|
);
|
|
2687
2786
|
}
|
|
2688
2787
|
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2788
|
+
return;
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
if (!route) {
|
|
2792
|
+
if (DEV) {
|
|
2793
|
+
// warn rather than throw, since under client routing an endpoint-only route id is
|
|
2794
|
+
// indistinguishable from a typo — the client manifest only contains routes with a `+page`
|
|
2795
|
+
let message = `'${id}' did not match any route`;
|
|
2796
|
+
|
|
2797
|
+
if (__SVELTEKIT_CLIENT_ROUTING__) {
|
|
2798
|
+
message += ` (note that routes without a \`+page\` have no code to preload)`;
|
|
2799
|
+
|
|
2800
|
+
// the most common migration mistake is passing a pathname, which used to work
|
|
2801
|
+
const candidates = [id];
|
|
2802
|
+
if (base && id.startsWith(base)) candidates.push(id.slice(base.length) || '/');
|
|
2803
|
+
|
|
2804
|
+
if (candidates.some((path) => routes.some((r) => r.exec(path)))) {
|
|
2805
|
+
message += `. It does match as a pathname — use \`match(...)\` from \`$app/paths\` to convert a pathname into a route ID`;
|
|
2806
|
+
}
|
|
2693
2807
|
}
|
|
2808
|
+
|
|
2809
|
+
console.warn(message);
|
|
2694
2810
|
}
|
|
2811
|
+
|
|
2812
|
+
return;
|
|
2695
2813
|
}
|
|
2696
2814
|
|
|
2697
|
-
|
|
2815
|
+
await load_route_nodes(route);
|
|
2698
2816
|
}
|
|
2699
2817
|
|
|
2700
2818
|
/**
|
|
@@ -2766,18 +2884,8 @@ export async function replaceState(url, state) {
|
|
|
2766
2884
|
async function update_state(intent, state, { replace, persist_state, reset }, caller) {
|
|
2767
2885
|
const url = intent.url;
|
|
2768
2886
|
|
|
2769
|
-
if (DEV) {
|
|
2770
|
-
|
|
2771
|
-
throw new Error(`Cannot call ${caller}(...) before router is initialized`);
|
|
2772
|
-
}
|
|
2773
|
-
|
|
2774
|
-
try {
|
|
2775
|
-
// use `devalue.stringify` as a convenient way to ensure we exclude values that can't be properly rehydrated, such as custom class instances
|
|
2776
|
-
devalue.stringify(state);
|
|
2777
|
-
} catch (error) {
|
|
2778
|
-
// @ts-expect-error
|
|
2779
|
-
throw new Error(`Could not serialize state${error.path}`, { cause: error });
|
|
2780
|
-
}
|
|
2887
|
+
if (DEV && !started) {
|
|
2888
|
+
throw new Error(`Cannot call ${caller}(...) before router is initialized`);
|
|
2781
2889
|
}
|
|
2782
2890
|
|
|
2783
2891
|
const nav =
|
|
@@ -2798,12 +2906,16 @@ async function update_state(intent, state, { replace, persist_state, reset }, ca
|
|
|
2798
2906
|
if (!replace) capture_scroll(current_history_index);
|
|
2799
2907
|
if (reset) current_reset_index += 1;
|
|
2800
2908
|
|
|
2909
|
+
// as above, serialize-then-parse to prevent bugs
|
|
2910
|
+
const serialized_state = stringify(state);
|
|
2911
|
+
state = parse(serialized_state);
|
|
2912
|
+
|
|
2801
2913
|
const entry = {
|
|
2802
2914
|
[HISTORY_METADATA_KEY]: /** @satisfies {HistoryMetadata} */ ({
|
|
2803
2915
|
historyIndex: (current_history_index += replace ? 0 : 1),
|
|
2804
2916
|
navigationIndex: current_navigation_index,
|
|
2805
2917
|
pageUrl: page.url.href,
|
|
2806
|
-
state,
|
|
2918
|
+
state: serialized_state,
|
|
2807
2919
|
persistState: persist_state,
|
|
2808
2920
|
resetIndex: current_reset_index
|
|
2809
2921
|
})
|
|
@@ -2892,7 +3004,7 @@ export async function applyAction(result) {
|
|
|
2892
3004
|
if (result.type === 'error') {
|
|
2893
3005
|
await set_nearest_error_page(result.error);
|
|
2894
3006
|
} else if (result.type === 'redirect') {
|
|
2895
|
-
await _goto(result.location, { refreshAll: true }
|
|
3007
|
+
await _goto(result.location, { refreshAll: true });
|
|
2896
3008
|
} else {
|
|
2897
3009
|
page.form = result.data;
|
|
2898
3010
|
page.status = result.status;
|
|
@@ -3108,11 +3220,13 @@ function _start_router() {
|
|
|
3108
3220
|
setTimeout(fulfil, 100); // fallback for edge case where rAF doesn't fire because e.g. tab was backgrounded
|
|
3109
3221
|
});
|
|
3110
3222
|
|
|
3111
|
-
|
|
3223
|
+
const changed = url.href !== location.href;
|
|
3224
|
+
|
|
3225
|
+
await _goto(url, {
|
|
3112
3226
|
type: 'link',
|
|
3113
|
-
url,
|
|
3114
3227
|
reset: options.reset,
|
|
3115
|
-
|
|
3228
|
+
replace: options.replace_state ?? !changed,
|
|
3229
|
+
refreshAll: !changed,
|
|
3116
3230
|
event
|
|
3117
3231
|
});
|
|
3118
3232
|
});
|
|
@@ -3181,7 +3295,7 @@ function _start_router() {
|
|
|
3181
3295
|
const reset_index = history_metadata.resetIndex;
|
|
3182
3296
|
const reset = reset_index !== (source_info?.resetIndex ?? current_reset_index);
|
|
3183
3297
|
const scroll = history_info[history_index]?.scroll;
|
|
3184
|
-
const state = history_metadata.state;
|
|
3298
|
+
const state = parse(history_metadata.state);
|
|
3185
3299
|
const url = new URL(history_metadata.pageUrl ?? location.href);
|
|
3186
3300
|
const navigation_index = history_metadata.navigationIndex;
|
|
3187
3301
|
const is_hash_change =
|
|
@@ -3343,7 +3457,7 @@ async function _hydrate(
|
|
|
3343
3457
|
} else {
|
|
3344
3458
|
// undefined in case of 404
|
|
3345
3459
|
if (server_route) {
|
|
3346
|
-
parsed_route = route =
|
|
3460
|
+
parsed_route = route = parse_and_cache_server_route(server_route);
|
|
3347
3461
|
} else {
|
|
3348
3462
|
route = { id: null };
|
|
3349
3463
|
params = {};
|
|
@@ -3427,7 +3541,7 @@ async function _hydrate(
|
|
|
3427
3541
|
|
|
3428
3542
|
if (result.props.page) {
|
|
3429
3543
|
const history_metadata = get_history_metadata();
|
|
3430
|
-
result.props.page.state = history_metadata?.persistState ? history_metadata.state : {};
|
|
3544
|
+
result.props.page.state = history_metadata?.persistState ? parse(history_metadata.state) : {};
|
|
3431
3545
|
}
|
|
3432
3546
|
|
|
3433
3547
|
await initialize(result, target, should_hydrate);
|