@sveltejs/kit 2.17.3 → 2.19.0
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 +2 -2
- package/src/core/generate_manifest/find_server_assets.js +1 -0
- package/src/core/generate_manifest/index.js +2 -4
- package/src/core/postbuild/analyse.js +6 -20
- package/src/exports/index.js +55 -0
- package/src/exports/public.d.ts +11 -10
- package/src/exports/vite/dev/index.js +6 -9
- package/src/runtime/client/client.js +116 -66
- package/src/runtime/server/cookie.js +23 -4
- package/src/runtime/server/page/actions.js +1 -1
- package/src/runtime/server/page/index.js +16 -22
- package/src/runtime/server/page/load_data.js +1 -1
- package/src/runtime/server/page/render.js +9 -6
- package/src/runtime/server/page/respond_with_error.js +4 -3
- package/src/runtime/server/respond.js +125 -134
- package/src/types/internal.d.ts +56 -44
- package/src/utils/page_nodes.js +95 -0
- package/src/version.js +1 -1
- package/types/index.d.ts +73 -46
- package/types/index.d.ts.map +4 -1
- package/src/runtime/server/page/load_page_nodes.js +0 -11
- package/src/utils/options.js +0 -16
- package/src/utils/route_config.js +0 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sveltejs/kit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.19.0",
|
|
4
4
|
"description": "SvelteKit is the fastest way to build Svelte apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"@types/connect": "^3.4.38",
|
|
37
37
|
"@types/node": "^18.19.48",
|
|
38
38
|
"@types/set-cookie-parser": "^2.4.7",
|
|
39
|
-
"dts-buddy": "^0.5.
|
|
39
|
+
"dts-buddy": "^0.5.5",
|
|
40
40
|
"rollup": "^4.14.2",
|
|
41
41
|
"svelte": "^5.2.9",
|
|
42
42
|
"svelte-preprocess": "^6.0.0",
|
|
@@ -27,12 +27,10 @@ export function generate_manifest({ build_data, prerendered, relative_path, rout
|
|
|
27
27
|
const reindexed = new Map();
|
|
28
28
|
/**
|
|
29
29
|
* All nodes actually used in the routes definition (prerendered routes are omitted).
|
|
30
|
-
*
|
|
31
|
-
* and the root layout/error is therefore not needed.
|
|
32
|
-
* Else, root layout/error is always included as they are needed for 404 and root errors.
|
|
30
|
+
* Root layout/error is always included as they are needed for 404 and root errors.
|
|
33
31
|
* @type {Set<any>}
|
|
34
32
|
*/
|
|
35
|
-
const used_nodes = new Set(
|
|
33
|
+
const used_nodes = new Set([0, 1]);
|
|
36
34
|
|
|
37
35
|
const server_assets = find_server_assets(build_data, routes);
|
|
38
36
|
|
|
@@ -1,22 +1,15 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
2
|
import { pathToFileURL } from 'node:url';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
validate_layout_exports,
|
|
6
|
-
validate_layout_server_exports,
|
|
7
|
-
validate_page_exports,
|
|
8
|
-
validate_page_server_exports,
|
|
9
|
-
validate_server_exports
|
|
10
|
-
} from '../../utils/exports.js';
|
|
3
|
+
import { validate_server_exports } from '../../utils/exports.js';
|
|
11
4
|
import { load_config } from '../config/index.js';
|
|
12
5
|
import { forked } from '../../utils/fork.js';
|
|
13
6
|
import { installPolyfills } from '../../exports/node/polyfills.js';
|
|
14
7
|
import { ENDPOINT_METHODS } from '../../constants.js';
|
|
15
8
|
import { filter_private_env, filter_public_env } from '../../utils/env.js';
|
|
16
9
|
import { has_server_load, resolve_route } from '../../utils/routing.js';
|
|
17
|
-
import { get_page_config } from '../../utils/route_config.js';
|
|
18
10
|
import { check_feature } from '../../utils/features.js';
|
|
19
11
|
import { createReadableStream } from '@sveltejs/kit/node';
|
|
12
|
+
import { PageNodes } from '../../utils/page_nodes.js';
|
|
20
13
|
|
|
21
14
|
export default forked(import.meta.url, analyse);
|
|
22
15
|
|
|
@@ -190,25 +183,18 @@ function analyse_endpoint(route, mod) {
|
|
|
190
183
|
* @param {import('types').SSRNode} leaf
|
|
191
184
|
*/
|
|
192
185
|
function analyse_page(layouts, leaf) {
|
|
193
|
-
for (const layout of layouts) {
|
|
194
|
-
if (layout) {
|
|
195
|
-
validate_layout_server_exports(layout.server, layout.server_id);
|
|
196
|
-
validate_layout_exports(layout.universal, layout.universal_id);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
186
|
/** @type {Array<'GET' | 'POST'>} */
|
|
201
187
|
const methods = ['GET'];
|
|
202
188
|
if (leaf.server?.actions) methods.push('POST');
|
|
203
189
|
|
|
204
|
-
|
|
205
|
-
|
|
190
|
+
const nodes = new PageNodes([...layouts, leaf]);
|
|
191
|
+
nodes.validate();
|
|
206
192
|
|
|
207
193
|
return {
|
|
208
|
-
config:
|
|
194
|
+
config: nodes.get_config(),
|
|
209
195
|
entries: leaf.universal?.entries ?? leaf.server?.entries,
|
|
210
196
|
methods,
|
|
211
|
-
prerender:
|
|
197
|
+
prerender: nodes.prerender()
|
|
212
198
|
};
|
|
213
199
|
}
|
|
214
200
|
|
package/src/exports/index.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { HttpError, Redirect, ActionFailure } from '../runtime/control.js';
|
|
2
2
|
import { BROWSER, DEV } from 'esm-env';
|
|
3
|
+
import {
|
|
4
|
+
add_data_suffix,
|
|
5
|
+
add_resolution_suffix,
|
|
6
|
+
has_data_suffix,
|
|
7
|
+
has_resolution_suffix,
|
|
8
|
+
strip_data_suffix,
|
|
9
|
+
strip_resolution_suffix
|
|
10
|
+
} from '../runtime/pathname.js';
|
|
3
11
|
|
|
4
12
|
export { VERSION } from '../version.js';
|
|
5
13
|
|
|
@@ -207,3 +215,50 @@ export function fail(status, data) {
|
|
|
207
215
|
export function isActionFailure(e) {
|
|
208
216
|
return e instanceof ActionFailure;
|
|
209
217
|
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Strips possible SvelteKit-internal suffixes and trailing slashes from the URL pathname.
|
|
221
|
+
* Returns the normalized URL as well as a method for adding the potential suffix back
|
|
222
|
+
* based on a new pathname (possibly including search) or URL.
|
|
223
|
+
* ```js
|
|
224
|
+
* import { normalizeUrl } from '@sveltejs/kit';
|
|
225
|
+
*
|
|
226
|
+
* const { url, denormalize } = normalizeUrl('/blog/post/__data.json');
|
|
227
|
+
* console.log(url.pathname); // /blog/post
|
|
228
|
+
* console.log(denormalize('/blog/post/a')); // /blog/post/a/__data.json
|
|
229
|
+
* ```
|
|
230
|
+
* @param {URL | string} url
|
|
231
|
+
* @returns {{ url: URL, wasNormalized: boolean, denormalize: (url?: string | URL) => URL }}
|
|
232
|
+
* @since 2.18.0
|
|
233
|
+
*/
|
|
234
|
+
export function normalizeUrl(url) {
|
|
235
|
+
url = new URL(url, 'http://internal');
|
|
236
|
+
|
|
237
|
+
const is_route_resolution = has_resolution_suffix(url.pathname);
|
|
238
|
+
const is_data_request = has_data_suffix(url.pathname);
|
|
239
|
+
const has_trailing_slash = url.pathname !== '/' && url.pathname.endsWith('/');
|
|
240
|
+
|
|
241
|
+
if (is_route_resolution) {
|
|
242
|
+
url.pathname = strip_resolution_suffix(url.pathname);
|
|
243
|
+
} else if (is_data_request) {
|
|
244
|
+
url.pathname = strip_data_suffix(url.pathname);
|
|
245
|
+
} else if (has_trailing_slash) {
|
|
246
|
+
url.pathname = url.pathname.slice(0, -1);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
url,
|
|
251
|
+
wasNormalized: is_data_request || is_route_resolution || has_trailing_slash,
|
|
252
|
+
denormalize: (new_url = url) => {
|
|
253
|
+
new_url = new URL(new_url, url);
|
|
254
|
+
if (is_route_resolution) {
|
|
255
|
+
new_url.pathname = add_resolution_suffix(new_url.pathname);
|
|
256
|
+
} else if (is_data_request) {
|
|
257
|
+
new_url.pathname = add_data_suffix(new_url.pathname);
|
|
258
|
+
} else if (has_trailing_slash && !new_url.pathname.endsWith('/')) {
|
|
259
|
+
new_url.pathname += '/';
|
|
260
|
+
}
|
|
261
|
+
return new_url;
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
}
|
package/src/exports/public.d.ts
CHANGED
|
@@ -814,7 +814,7 @@ export type ClientInit = () => MaybePromise<void>;
|
|
|
814
814
|
* The [`reroute`](https://svelte.dev/docs/kit/hooks#Universal-hooks-reroute) hook allows you to modify the URL before it is used to determine which route to render.
|
|
815
815
|
* @since 2.3.0
|
|
816
816
|
*/
|
|
817
|
-
export type Reroute = (event: { url: URL }) => void | string
|
|
817
|
+
export type Reroute = (event: { url: URL; fetch: typeof fetch }) => MaybePromise<void | string>;
|
|
818
818
|
|
|
819
819
|
/**
|
|
820
820
|
* The [`transport`](https://svelte.dev/docs/kit/hooks#Universal-hooks-transport) hook allows you to transport custom types across the server/client boundary.
|
|
@@ -1177,7 +1177,7 @@ export interface RequestEvent<
|
|
|
1177
1177
|
* - During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the `text` and `json` methods of the `Response` object. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](https://svelte.dev/docs/kit/hooks#Server-hooks-handle)
|
|
1178
1178
|
* - During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request.
|
|
1179
1179
|
*
|
|
1180
|
-
* You can learn more about making credentialed requests with cookies [here](https://svelte.dev/docs/kit/load#Cookies)
|
|
1180
|
+
* You can learn more about making credentialed requests with cookies [here](https://svelte.dev/docs/kit/load#Cookies).
|
|
1181
1181
|
*/
|
|
1182
1182
|
fetch: typeof fetch;
|
|
1183
1183
|
/**
|
|
@@ -1189,7 +1189,7 @@ export interface RequestEvent<
|
|
|
1189
1189
|
*/
|
|
1190
1190
|
locals: App.Locals;
|
|
1191
1191
|
/**
|
|
1192
|
-
* The parameters of the current route - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object
|
|
1192
|
+
* The parameters of the current route - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object.
|
|
1193
1193
|
*/
|
|
1194
1194
|
params: Params;
|
|
1195
1195
|
/**
|
|
@@ -1197,15 +1197,15 @@ export interface RequestEvent<
|
|
|
1197
1197
|
*/
|
|
1198
1198
|
platform: Readonly<App.Platform> | undefined;
|
|
1199
1199
|
/**
|
|
1200
|
-
* The original request object
|
|
1200
|
+
* The original request object.
|
|
1201
1201
|
*/
|
|
1202
1202
|
request: Request;
|
|
1203
1203
|
/**
|
|
1204
|
-
* Info about the current route
|
|
1204
|
+
* Info about the current route.
|
|
1205
1205
|
*/
|
|
1206
1206
|
route: {
|
|
1207
1207
|
/**
|
|
1208
|
-
* The ID of the current route - e.g. for `src/routes/blog/[slug]`, it would be `/blog/[slug]
|
|
1208
|
+
* The ID of the current route - e.g. for `src/routes/blog/[slug]`, it would be `/blog/[slug]`.
|
|
1209
1209
|
*/
|
|
1210
1210
|
id: RouteId;
|
|
1211
1211
|
};
|
|
@@ -1302,15 +1302,16 @@ export class Server {
|
|
|
1302
1302
|
}
|
|
1303
1303
|
|
|
1304
1304
|
export interface ServerInitOptions {
|
|
1305
|
-
/** A map of environment variables */
|
|
1305
|
+
/** A map of environment variables. */
|
|
1306
1306
|
env: Record<string, string>;
|
|
1307
|
-
/** A function that turns an asset filename into a `ReadableStream`. Required for the `read` export from `$app/server` to work */
|
|
1307
|
+
/** A function that turns an asset filename into a `ReadableStream`. Required for the `read` export from `$app/server` to work. */
|
|
1308
1308
|
read?: (file: string) => ReadableStream;
|
|
1309
1309
|
}
|
|
1310
1310
|
|
|
1311
1311
|
export interface SSRManifest {
|
|
1312
1312
|
appDir: string;
|
|
1313
1313
|
appPath: string;
|
|
1314
|
+
/** Static files from `kit.config.files.assets` and the service worker (if any). */
|
|
1314
1315
|
assets: Set<string>;
|
|
1315
1316
|
mimeTypes: Record<string, string>;
|
|
1316
1317
|
|
|
@@ -1321,7 +1322,7 @@ export interface SSRManifest {
|
|
|
1321
1322
|
routes: SSRRoute[];
|
|
1322
1323
|
prerendered_routes: Set<string>;
|
|
1323
1324
|
matchers: () => Promise<Record<string, ParamMatcher>>;
|
|
1324
|
-
/** A `[file]: size` map of all assets imported by server code */
|
|
1325
|
+
/** A `[file]: size` map of all assets imported by server code. */
|
|
1325
1326
|
server_assets: Record<string, number>;
|
|
1326
1327
|
};
|
|
1327
1328
|
}
|
|
@@ -1452,7 +1453,7 @@ export interface HttpError {
|
|
|
1452
1453
|
}
|
|
1453
1454
|
|
|
1454
1455
|
/**
|
|
1455
|
-
* The object returned by the [`redirect`](https://svelte.dev/docs/kit/@sveltejs-kit#redirect) function
|
|
1456
|
+
* The object returned by the [`redirect`](https://svelte.dev/docs/kit/@sveltejs-kit#redirect) function.
|
|
1456
1457
|
*/
|
|
1457
1458
|
export interface Redirect {
|
|
1458
1459
|
/** The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages), in the range 300-308. */
|
|
@@ -177,17 +177,18 @@ export async function dev(vite, vite_config, svelte_config) {
|
|
|
177
177
|
return async () => {
|
|
178
178
|
/** @type {import('types').SSRNode} */
|
|
179
179
|
const result = {};
|
|
180
|
-
|
|
181
|
-
/** @type {import('vite').ModuleNode[]} */
|
|
182
|
-
const module_nodes = [];
|
|
183
|
-
|
|
184
180
|
result.index = index;
|
|
181
|
+
result.universal_id = node.universal;
|
|
182
|
+
result.server_id = node.server;
|
|
185
183
|
|
|
186
|
-
// these are unused in dev, it's easier to include them
|
|
184
|
+
// these are unused in dev, but it's easier to include them
|
|
187
185
|
result.imports = [];
|
|
188
186
|
result.stylesheets = [];
|
|
189
187
|
result.fonts = [];
|
|
190
188
|
|
|
189
|
+
/** @type {import('vite').ModuleNode[]} */
|
|
190
|
+
const module_nodes = [];
|
|
191
|
+
|
|
191
192
|
if (node.component) {
|
|
192
193
|
result.component = async () => {
|
|
193
194
|
const { module_node, module } = await resolve(
|
|
@@ -202,17 +203,13 @@ export async function dev(vite, vite_config, svelte_config) {
|
|
|
202
203
|
|
|
203
204
|
if (node.universal) {
|
|
204
205
|
const { module, module_node } = await resolve(node.universal);
|
|
205
|
-
|
|
206
206
|
module_nodes.push(module_node);
|
|
207
|
-
|
|
208
207
|
result.universal = module;
|
|
209
|
-
result.universal_id = node.universal;
|
|
210
208
|
}
|
|
211
209
|
|
|
212
210
|
if (node.server) {
|
|
213
211
|
const { module } = await resolve(node.server);
|
|
214
212
|
result.server = module;
|
|
215
|
-
result.server_id = node.server;
|
|
216
213
|
}
|
|
217
214
|
|
|
218
215
|
// in dev we inline all styles to avoid FOUC. this gets populated lazily so that
|
|
@@ -188,6 +188,17 @@ const components = [];
|
|
|
188
188
|
/** @type {{id: string, token: {}, promise: Promise<import('./types.js').NavigationResult>} | null} */
|
|
189
189
|
let load_cache = null;
|
|
190
190
|
|
|
191
|
+
/**
|
|
192
|
+
* @type {Map<string, Promise<URL>>}
|
|
193
|
+
* Cache for client-side rerouting, since it could contain async calls which we want to
|
|
194
|
+
* avoid running multiple times which would slow down navigations (e.g. else preloading
|
|
195
|
+
* wouldn't help because on navigation it would be called again). Since `reroute` should be
|
|
196
|
+
* a pure function (i.e. always return the same) value it's safe to cache across navigations.
|
|
197
|
+
* The server reroute calls don't need to be cached because they are called using `import(...)`
|
|
198
|
+
* which is cached per the JS spec.
|
|
199
|
+
*/
|
|
200
|
+
const reroute_cache = new Map();
|
|
201
|
+
|
|
191
202
|
/**
|
|
192
203
|
* Note on before_navigate_callbacks, on_navigate_callbacks and after_navigate_callbacks:
|
|
193
204
|
* do not re-assign as some closures keep references to these Sets
|
|
@@ -679,12 +690,7 @@ async function load_node({ loader, parent, url, params, route, server_data_node
|
|
|
679
690
|
app.hash
|
|
680
691
|
),
|
|
681
692
|
async fetch(resource, init) {
|
|
682
|
-
/** @type {URL | string} */
|
|
683
|
-
let requested;
|
|
684
|
-
|
|
685
693
|
if (resource instanceof Request) {
|
|
686
|
-
requested = resource.url;
|
|
687
|
-
|
|
688
694
|
// we're not allowed to modify the received `Request` object, so in order
|
|
689
695
|
// to fixup relative urls we create a new equivalent `init` object instead
|
|
690
696
|
init = {
|
|
@@ -709,25 +715,15 @@ async function load_node({ loader, parent, url, params, route, server_data_node
|
|
|
709
715
|
signal: resource.signal,
|
|
710
716
|
...init
|
|
711
717
|
};
|
|
712
|
-
} else {
|
|
713
|
-
requested = resource;
|
|
714
718
|
}
|
|
715
719
|
|
|
716
|
-
|
|
717
|
-
|
|
720
|
+
const { resolved, promise } = resolve_fetch_url(resource, init, url);
|
|
721
|
+
|
|
718
722
|
if (is_tracking) {
|
|
719
723
|
depends(resolved.href);
|
|
720
724
|
}
|
|
721
725
|
|
|
722
|
-
|
|
723
|
-
if (resolved.origin === url.origin) {
|
|
724
|
-
requested = resolved.href.slice(url.origin.length);
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
// prerendered pages may be served from any origin, so `initial_fetch` urls shouldn't be resolved
|
|
728
|
-
return started
|
|
729
|
-
? subsequent_fetch(requested, resolved.href, init)
|
|
730
|
-
: initial_fetch(requested, init);
|
|
726
|
+
return promise;
|
|
731
727
|
},
|
|
732
728
|
setHeaders: () => {}, // noop
|
|
733
729
|
depends,
|
|
@@ -782,6 +778,30 @@ async function load_node({ loader, parent, url, params, route, server_data_node
|
|
|
782
778
|
};
|
|
783
779
|
}
|
|
784
780
|
|
|
781
|
+
/**
|
|
782
|
+
* @param {Request | string | URL} input
|
|
783
|
+
* @param {RequestInit | undefined} init
|
|
784
|
+
* @param {URL} url
|
|
785
|
+
*/
|
|
786
|
+
function resolve_fetch_url(input, init, url) {
|
|
787
|
+
let requested = input instanceof Request ? input.url : input;
|
|
788
|
+
|
|
789
|
+
// we must fixup relative urls so they are resolved from the target page
|
|
790
|
+
const resolved = new URL(requested, url);
|
|
791
|
+
|
|
792
|
+
// match ssr serialized data url, which is important to find cached responses
|
|
793
|
+
if (resolved.origin === url.origin) {
|
|
794
|
+
requested = resolved.href.slice(url.origin.length);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// prerendered pages may be served from any origin, so `initial_fetch` urls shouldn't be resolved
|
|
798
|
+
const promise = started
|
|
799
|
+
? subsequent_fetch(requested, resolved.href, init)
|
|
800
|
+
: initial_fetch(requested, init);
|
|
801
|
+
|
|
802
|
+
return { resolved, promise };
|
|
803
|
+
}
|
|
804
|
+
|
|
785
805
|
/**
|
|
786
806
|
* @param {boolean} parent_changed
|
|
787
807
|
* @param {boolean} route_changed
|
|
@@ -1198,26 +1218,47 @@ async function load_root_error_page({ status, error, url, route }) {
|
|
|
1198
1218
|
/**
|
|
1199
1219
|
* Resolve the relative rerouted URL for a client-side navigation
|
|
1200
1220
|
* @param {URL} url
|
|
1201
|
-
* @returns {URL | undefined}
|
|
1221
|
+
* @returns {Promise<URL | undefined>}
|
|
1202
1222
|
*/
|
|
1203
|
-
function get_rerouted_url(url) {
|
|
1204
|
-
|
|
1223
|
+
async function get_rerouted_url(url) {
|
|
1224
|
+
const href = url.href;
|
|
1225
|
+
|
|
1226
|
+
if (reroute_cache.has(href)) {
|
|
1227
|
+
return reroute_cache.get(href);
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1205
1230
|
let rerouted;
|
|
1231
|
+
|
|
1206
1232
|
try {
|
|
1207
|
-
|
|
1233
|
+
const promise = (async () => {
|
|
1234
|
+
// reroute could alter the given URL, so we pass a copy
|
|
1235
|
+
let rerouted =
|
|
1236
|
+
(await app.hooks.reroute({
|
|
1237
|
+
url: new URL(url),
|
|
1238
|
+
fetch: async (input, init) => {
|
|
1239
|
+
return resolve_fetch_url(input, init, url).promise;
|
|
1240
|
+
}
|
|
1241
|
+
})) ?? url;
|
|
1208
1242
|
|
|
1209
|
-
|
|
1210
|
-
|
|
1243
|
+
if (typeof rerouted === 'string') {
|
|
1244
|
+
const tmp = new URL(url); // do not mutate the incoming URL
|
|
1211
1245
|
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1246
|
+
if (app.hash) {
|
|
1247
|
+
tmp.hash = rerouted;
|
|
1248
|
+
} else {
|
|
1249
|
+
tmp.pathname = rerouted;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
rerouted = tmp;
|
|
1216
1253
|
}
|
|
1217
1254
|
|
|
1218
|
-
rerouted
|
|
1219
|
-
}
|
|
1255
|
+
return rerouted;
|
|
1256
|
+
})();
|
|
1257
|
+
|
|
1258
|
+
reroute_cache.set(href, promise);
|
|
1259
|
+
rerouted = await promise;
|
|
1220
1260
|
} catch (e) {
|
|
1261
|
+
reroute_cache.delete(href);
|
|
1221
1262
|
if (DEV) {
|
|
1222
1263
|
// in development, print the error...
|
|
1223
1264
|
console.error(e);
|
|
@@ -1246,7 +1287,7 @@ async function get_navigation_intent(url, invalidating) {
|
|
|
1246
1287
|
if (is_external_url(url, base, app.hash)) return;
|
|
1247
1288
|
|
|
1248
1289
|
if (__SVELTEKIT_CLIENT_ROUTING__) {
|
|
1249
|
-
const rerouted = get_rerouted_url(url);
|
|
1290
|
+
const rerouted = await get_rerouted_url(url);
|
|
1250
1291
|
if (!rerouted) return;
|
|
1251
1292
|
|
|
1252
1293
|
const path = get_url_path(rerouted);
|
|
@@ -1636,25 +1677,29 @@ if (import.meta.hot) {
|
|
|
1636
1677
|
});
|
|
1637
1678
|
}
|
|
1638
1679
|
|
|
1680
|
+
/** @typedef {(typeof PRELOAD_PRIORITIES)['hover'] | (typeof PRELOAD_PRIORITIES)['tap']} PreloadDataPriority */
|
|
1681
|
+
|
|
1639
1682
|
function setup_preload() {
|
|
1640
1683
|
/** @type {NodeJS.Timeout} */
|
|
1641
1684
|
let mousemove_timeout;
|
|
1642
1685
|
/** @type {Element} */
|
|
1643
1686
|
let current_a;
|
|
1687
|
+
/** @type {PreloadDataPriority} */
|
|
1688
|
+
let current_priority;
|
|
1644
1689
|
|
|
1645
1690
|
container.addEventListener('mousemove', (event) => {
|
|
1646
1691
|
const target = /** @type {Element} */ (event.target);
|
|
1647
1692
|
|
|
1648
1693
|
clearTimeout(mousemove_timeout);
|
|
1649
1694
|
mousemove_timeout = setTimeout(() => {
|
|
1650
|
-
void preload(target,
|
|
1695
|
+
void preload(target, PRELOAD_PRIORITIES.hover);
|
|
1651
1696
|
}, 20);
|
|
1652
1697
|
});
|
|
1653
1698
|
|
|
1654
1699
|
/** @param {Event} event */
|
|
1655
1700
|
function tap(event) {
|
|
1656
1701
|
if (event.defaultPrevented) return;
|
|
1657
|
-
void preload(/** @type {Element} */ (event.composedPath()[0]),
|
|
1702
|
+
void preload(/** @type {Element} */ (event.composedPath()[0]), PRELOAD_PRIORITIES.tap);
|
|
1658
1703
|
}
|
|
1659
1704
|
|
|
1660
1705
|
container.addEventListener('mousedown', tap);
|
|
@@ -1674,11 +1719,14 @@ function setup_preload() {
|
|
|
1674
1719
|
|
|
1675
1720
|
/**
|
|
1676
1721
|
* @param {Element} element
|
|
1677
|
-
* @param {
|
|
1722
|
+
* @param {PreloadDataPriority} priority
|
|
1678
1723
|
*/
|
|
1679
1724
|
async function preload(element, priority) {
|
|
1680
1725
|
const a = find_anchor(element, container);
|
|
1681
|
-
|
|
1726
|
+
|
|
1727
|
+
// we don't want to preload data again if the user has already hovered/tapped
|
|
1728
|
+
const interacted = a === current_a && priority >= current_priority;
|
|
1729
|
+
if (!a || interacted) return;
|
|
1682
1730
|
|
|
1683
1731
|
const { url, external, download } = get_link_info(a, base, app.hash);
|
|
1684
1732
|
if (external || download) return;
|
|
@@ -1687,31 +1735,34 @@ function setup_preload() {
|
|
|
1687
1735
|
|
|
1688
1736
|
// we don't want to preload data for a page we're already on
|
|
1689
1737
|
const same_url = url && get_page_key(current.url) === get_page_key(url);
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1738
|
+
if (options.reload || same_url) return;
|
|
1739
|
+
|
|
1740
|
+
if (priority <= options.preload_data) {
|
|
1741
|
+
current_a = a;
|
|
1742
|
+
// we don't want to preload data again on tap if we've already preloaded it on hover
|
|
1743
|
+
current_priority = PRELOAD_PRIORITIES.tap;
|
|
1744
|
+
|
|
1745
|
+
const intent = await get_navigation_intent(url, false);
|
|
1746
|
+
if (!intent) return;
|
|
1747
|
+
|
|
1748
|
+
if (DEV) {
|
|
1749
|
+
void _preload_data(intent).then((result) => {
|
|
1750
|
+
if (result.type === 'loaded' && result.state.error) {
|
|
1751
|
+
console.warn(
|
|
1752
|
+
`Preloading data for ${intent.url.pathname} failed with the following error: ${result.state.error.message}\n` +
|
|
1753
|
+
'If this error is transient, you can ignore it. Otherwise, consider disabling preloading for this route. ' +
|
|
1754
|
+
'This route was preloaded due to a data-sveltekit-preload-data attribute. ' +
|
|
1755
|
+
'See https://svelte.dev/docs/kit/link-options for more info'
|
|
1756
|
+
);
|
|
1709
1757
|
}
|
|
1710
|
-
}
|
|
1711
|
-
} else
|
|
1712
|
-
|
|
1713
|
-
void _preload_code(/** @type {URL} */ (url));
|
|
1758
|
+
});
|
|
1759
|
+
} else {
|
|
1760
|
+
void _preload_data(intent);
|
|
1714
1761
|
}
|
|
1762
|
+
} else if (priority <= options.preload_code) {
|
|
1763
|
+
current_a = a;
|
|
1764
|
+
current_priority = priority;
|
|
1765
|
+
void _preload_code(/** @type {URL} */ (url));
|
|
1715
1766
|
}
|
|
1716
1767
|
}
|
|
1717
1768
|
|
|
@@ -1997,7 +2048,7 @@ export async function preloadCode(pathname) {
|
|
|
1997
2048
|
}
|
|
1998
2049
|
|
|
1999
2050
|
if (__SVELTEKIT_CLIENT_ROUTING__) {
|
|
2000
|
-
const rerouted = get_rerouted_url(url);
|
|
2051
|
+
const rerouted = await get_rerouted_url(url);
|
|
2001
2052
|
if (!rerouted || !routes.find((route) => route.exec(get_url_path(rerouted)))) {
|
|
2002
2053
|
throw new Error(`'${pathname}' did not match any routes`);
|
|
2003
2054
|
}
|
|
@@ -2435,6 +2486,12 @@ function _start_router() {
|
|
|
2435
2486
|
if (!hash_navigating) {
|
|
2436
2487
|
const url = new URL(location.href);
|
|
2437
2488
|
update_url(url);
|
|
2489
|
+
|
|
2490
|
+
// if the user edits the hash via the browser URL bar, trigger a full-page
|
|
2491
|
+
// reload to align with pathname router behavior
|
|
2492
|
+
if (app.hash) {
|
|
2493
|
+
location.reload();
|
|
2494
|
+
}
|
|
2438
2495
|
}
|
|
2439
2496
|
}
|
|
2440
2497
|
});
|
|
@@ -2453,13 +2510,6 @@ function _start_router() {
|
|
|
2453
2510
|
'',
|
|
2454
2511
|
location.href
|
|
2455
2512
|
);
|
|
2456
|
-
} else if (app.hash) {
|
|
2457
|
-
// If the user edits the hash via the browser URL bar, it
|
|
2458
|
-
// (surprisingly!) mutates `current.url`, allowing us to
|
|
2459
|
-
// detect it and trigger a navigation
|
|
2460
|
-
if (current.url.hash === location.hash) {
|
|
2461
|
-
void navigate({ type: 'goto', url: decode_hash(current.url) });
|
|
2462
|
-
}
|
|
2463
2513
|
}
|
|
2464
2514
|
});
|
|
2465
2515
|
|
|
@@ -29,13 +29,13 @@ function validate_options(options) {
|
|
|
29
29
|
/**
|
|
30
30
|
* @param {Request} request
|
|
31
31
|
* @param {URL} url
|
|
32
|
-
* @param {import('types').TrailingSlash} trailing_slash
|
|
33
32
|
*/
|
|
34
|
-
export function get_cookies(request, url
|
|
33
|
+
export function get_cookies(request, url) {
|
|
35
34
|
const header = request.headers.get('cookie') ?? '';
|
|
36
35
|
const initial_cookies = parse(header, { decode: (value) => value });
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
/** @type {string | undefined} */
|
|
38
|
+
let normalized_url;
|
|
39
39
|
|
|
40
40
|
/** @type {Record<string, import('./page/types.js').Cookie>} */
|
|
41
41
|
const new_cookies = {};
|
|
@@ -149,6 +149,9 @@ export function get_cookies(request, url, trailing_slash) {
|
|
|
149
149
|
let path = options.path;
|
|
150
150
|
|
|
151
151
|
if (!options.domain || options.domain === url.hostname) {
|
|
152
|
+
if (!normalized_url) {
|
|
153
|
+
throw new Error('Cannot serialize cookies until after the route is determined');
|
|
154
|
+
}
|
|
152
155
|
path = resolve(normalized_url, path);
|
|
153
156
|
}
|
|
154
157
|
|
|
@@ -190,12 +193,20 @@ export function get_cookies(request, url, trailing_slash) {
|
|
|
190
193
|
.join('; ');
|
|
191
194
|
}
|
|
192
195
|
|
|
196
|
+
/** @type {Array<() => void>} */
|
|
197
|
+
const internal_queue = [];
|
|
198
|
+
|
|
193
199
|
/**
|
|
194
200
|
* @param {string} name
|
|
195
201
|
* @param {string} value
|
|
196
202
|
* @param {import('./page/types.js').Cookie['options']} options
|
|
197
203
|
*/
|
|
198
204
|
function set_internal(name, value, options) {
|
|
205
|
+
if (!normalized_url) {
|
|
206
|
+
internal_queue.push(() => set_internal(name, value, options));
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
199
210
|
let path = options.path;
|
|
200
211
|
|
|
201
212
|
if (!options.domain || options.domain === url.hostname) {
|
|
@@ -220,7 +231,15 @@ export function get_cookies(request, url, trailing_slash) {
|
|
|
220
231
|
}
|
|
221
232
|
}
|
|
222
233
|
|
|
223
|
-
|
|
234
|
+
/**
|
|
235
|
+
* @param {import('types').TrailingSlash} trailing_slash
|
|
236
|
+
*/
|
|
237
|
+
function set_trailing_slash(trailing_slash) {
|
|
238
|
+
normalized_url = normalize_path(url.pathname, trailing_slash);
|
|
239
|
+
internal_queue.forEach((fn) => fn());
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return { cookies, new_cookies, get_cookie_header, set_internal, set_trailing_slash };
|
|
224
243
|
}
|
|
225
244
|
|
|
226
245
|
/**
|
|
@@ -214,7 +214,7 @@ function check_named_default_separate(actions) {
|
|
|
214
214
|
|
|
215
215
|
/**
|
|
216
216
|
* @param {import('@sveltejs/kit').RequestEvent} event
|
|
217
|
-
* @param {NonNullable<import('types').
|
|
217
|
+
* @param {NonNullable<import('types').ServerNode['actions']>} actions
|
|
218
218
|
* @throws {Redirect | HttpError | SvelteKitError | Error}
|
|
219
219
|
*/
|
|
220
220
|
async function call_action(event, actions) {
|