@sveltejs/kit 1.0.0-next.19 → 1.0.0-next.193
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/README.md +12 -9
- package/assets/components/error.svelte +19 -3
- package/assets/runtime/app/env.js +20 -0
- package/assets/runtime/app/navigation.js +45 -13
- package/assets/runtime/app/paths.js +1 -2
- package/assets/runtime/app/stores.js +15 -9
- package/assets/runtime/chunks/utils.js +13 -0
- package/assets/runtime/env.js +8 -0
- package/assets/runtime/internal/singletons.js +5 -11
- package/assets/runtime/internal/start.js +954 -350
- package/assets/runtime/paths.js +13 -0
- package/dist/chunks/cert.js +29254 -0
- package/dist/chunks/constants.js +8 -0
- package/dist/chunks/error.js +21 -0
- package/dist/chunks/http.js +22 -0
- package/dist/chunks/index.js +4745 -0
- package/dist/chunks/index2.js +700 -0
- package/dist/chunks/index3.js +1065 -0
- package/dist/chunks/index4.js +393 -0
- package/dist/chunks/index5.js +413 -0
- package/dist/chunks/index6.js +15560 -0
- package/dist/{standard.js → chunks/standard.js} +16 -18
- package/dist/cli.js +936 -67
- package/dist/hooks.js +28 -0
- package/dist/install-fetch.js +6070 -0
- package/dist/node.js +51 -0
- package/dist/ssr.js +1791 -0
- package/package.json +89 -54
- package/svelte-kit.js +2 -0
- package/types/ambient-modules.d.ts +173 -0
- package/types/app.d.ts +21 -0
- package/types/config.d.ts +96 -0
- package/types/endpoint.d.ts +18 -0
- package/types/helper.d.ts +41 -0
- package/types/hooks.d.ts +36 -0
- package/types/index.d.ts +17 -0
- package/types/internal.d.ts +219 -0
- package/types/page.d.ts +77 -0
- package/CHANGELOG.md +0 -270
- package/assets/runtime/app/navigation.js.map +0 -1
- package/assets/runtime/app/paths.js.map +0 -1
- package/assets/runtime/app/stores.js.map +0 -1
- package/assets/runtime/internal/singletons.js.map +0 -1
- package/assets/runtime/internal/start.js.map +0 -1
- package/assets/runtime/utils-85ebcc60.js +0 -18
- package/assets/runtime/utils-85ebcc60.js.map +0 -1
- package/dist/api.js +0 -44
- package/dist/api.js.map +0 -1
- package/dist/build.js +0 -246
- package/dist/build.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/colors.js +0 -37
- package/dist/colors.js.map +0 -1
- package/dist/create_app.js +0 -578
- package/dist/create_app.js.map +0 -1
- package/dist/index.js +0 -12009
- package/dist/index.js.map +0 -1
- package/dist/index2.js +0 -544
- package/dist/index2.js.map +0 -1
- package/dist/index3.js +0 -71
- package/dist/index3.js.map +0 -1
- package/dist/index4.js +0 -466
- package/dist/index4.js.map +0 -1
- package/dist/index5.js +0 -717
- package/dist/index5.js.map +0 -1
- package/dist/index6.js +0 -713
- package/dist/index6.js.map +0 -1
- package/dist/logging.js +0 -43
- package/dist/logging.js.map +0 -1
- package/dist/package.js +0 -406
- package/dist/package.js.map +0 -1
- package/dist/renderer.js +0 -2391
- package/dist/renderer.js.map +0 -1
- package/dist/standard.js.map +0 -1
- package/dist/utils.js +0 -54
- package/dist/utils.js.map +0 -1
- package/svelte-kit +0 -3
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import Root from '../../generated/root.svelte';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { fallback, routes } from '../../generated/manifest.js';
|
|
3
|
+
import { g as get_base_uri } from '../chunks/utils.js';
|
|
4
4
|
import { writable } from 'svelte/store';
|
|
5
|
-
import { init
|
|
6
|
-
|
|
7
|
-
function which(event) {
|
|
8
|
-
return event.which === null ? event.button : event.which;
|
|
9
|
-
}
|
|
5
|
+
import { init } from './singletons.js';
|
|
6
|
+
import { set_paths } from '../paths.js';
|
|
10
7
|
|
|
11
8
|
function scroll_state() {
|
|
12
9
|
return {
|
|
@@ -15,26 +12,57 @@ function scroll_state() {
|
|
|
15
12
|
};
|
|
16
13
|
}
|
|
17
14
|
|
|
15
|
+
/**
|
|
16
|
+
* @param {Node | null} node
|
|
17
|
+
* @returns {HTMLAnchorElement | SVGAElement | null}
|
|
18
|
+
*/
|
|
19
|
+
function find_anchor(node) {
|
|
20
|
+
while (node && node.nodeName.toUpperCase() !== 'A') node = node.parentNode; // SVG <a> elements have a lowercase name
|
|
21
|
+
return /** @type {HTMLAnchorElement | SVGAElement} */ (node);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @param {HTMLAnchorElement | SVGAElement} node
|
|
26
|
+
* @returns {URL}
|
|
27
|
+
*/
|
|
28
|
+
function get_href(node) {
|
|
29
|
+
return node instanceof SVGAElement
|
|
30
|
+
? new URL(node.href.baseVal, document.baseURI)
|
|
31
|
+
: new URL(node.href);
|
|
32
|
+
}
|
|
33
|
+
|
|
18
34
|
class Router {
|
|
19
|
-
|
|
35
|
+
/**
|
|
36
|
+
* @param {{
|
|
37
|
+
* base: string;
|
|
38
|
+
* routes: import('types/internal').CSRRoute[];
|
|
39
|
+
* trailing_slash: import('types/internal').TrailingSlash;
|
|
40
|
+
* renderer: import('./renderer').Renderer
|
|
41
|
+
* }} opts
|
|
42
|
+
*/
|
|
43
|
+
constructor({ base, routes, trailing_slash, renderer }) {
|
|
20
44
|
this.base = base;
|
|
21
|
-
this.
|
|
22
|
-
this.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
this.history = window.history || {
|
|
26
|
-
pushState: () => {},
|
|
27
|
-
replaceState: () => {},
|
|
28
|
-
scrollRestoration: 'auto'
|
|
29
|
-
};
|
|
30
|
-
}
|
|
45
|
+
this.routes = routes;
|
|
46
|
+
this.trailing_slash = trailing_slash;
|
|
47
|
+
/** Keeps tracks of multiple navigations caused by redirects during rendering */
|
|
48
|
+
this.navigating = 0;
|
|
31
49
|
|
|
32
|
-
|
|
50
|
+
/** @type {import('./renderer').Renderer} */
|
|
33
51
|
this.renderer = renderer;
|
|
34
52
|
renderer.router = this;
|
|
35
53
|
|
|
36
|
-
|
|
37
|
-
|
|
54
|
+
this.enabled = true;
|
|
55
|
+
|
|
56
|
+
// make it possible to reset focus
|
|
57
|
+
document.body.setAttribute('tabindex', '-1');
|
|
58
|
+
|
|
59
|
+
// create initial history entry, so we can return here
|
|
60
|
+
history.replaceState(history.state || {}, '', location.href);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
init_listeners() {
|
|
64
|
+
if ('scrollRestoration' in history) {
|
|
65
|
+
history.scrollRestoration = 'manual';
|
|
38
66
|
}
|
|
39
67
|
|
|
40
68
|
// Adopted from Nuxt.js
|
|
@@ -42,16 +70,18 @@ class Router {
|
|
|
42
70
|
// and back-navigation from other pages to use the browser to restore the
|
|
43
71
|
// scrolling position.
|
|
44
72
|
addEventListener('beforeunload', () => {
|
|
45
|
-
|
|
73
|
+
history.scrollRestoration = 'auto';
|
|
46
74
|
});
|
|
47
75
|
|
|
48
76
|
// Setting scrollRestoration to manual again when returning to this page.
|
|
49
77
|
addEventListener('load', () => {
|
|
50
|
-
|
|
78
|
+
history.scrollRestoration = 'manual';
|
|
51
79
|
});
|
|
52
80
|
|
|
53
81
|
// There's no API to capture the scroll location right before the user
|
|
54
82
|
// hits the back/forward button, so we listen for scroll events
|
|
83
|
+
|
|
84
|
+
/** @type {NodeJS.Timeout} */
|
|
55
85
|
let scroll_timer;
|
|
56
86
|
addEventListener('scroll', () => {
|
|
57
87
|
clearTimeout(scroll_timer);
|
|
@@ -62,145 +92,305 @@ class Router {
|
|
|
62
92
|
...(history.state || {}),
|
|
63
93
|
'sveltekit:scroll': scroll_state()
|
|
64
94
|
};
|
|
65
|
-
history.replaceState(new_state, document.title, window.location);
|
|
95
|
+
history.replaceState(new_state, document.title, window.location.href);
|
|
66
96
|
}, 50);
|
|
67
97
|
});
|
|
68
98
|
|
|
99
|
+
/** @param {MouseEvent|TouchEvent} event */
|
|
100
|
+
const trigger_prefetch = (event) => {
|
|
101
|
+
const a = find_anchor(/** @type {Node} */ (event.target));
|
|
102
|
+
if (a && a.href && a.hasAttribute('sveltekit:prefetch')) {
|
|
103
|
+
this.prefetch(get_href(a));
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/** @type {NodeJS.Timeout} */
|
|
108
|
+
let mousemove_timeout;
|
|
109
|
+
|
|
110
|
+
/** @param {MouseEvent|TouchEvent} event */
|
|
111
|
+
const handle_mousemove = (event) => {
|
|
112
|
+
clearTimeout(mousemove_timeout);
|
|
113
|
+
mousemove_timeout = setTimeout(() => {
|
|
114
|
+
trigger_prefetch(event);
|
|
115
|
+
}, 20);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
addEventListener('touchstart', trigger_prefetch);
|
|
119
|
+
addEventListener('mousemove', handle_mousemove);
|
|
120
|
+
|
|
121
|
+
/** @param {MouseEvent} event */
|
|
69
122
|
addEventListener('click', (event) => {
|
|
123
|
+
if (!this.enabled) return;
|
|
124
|
+
|
|
70
125
|
// Adapted from https://github.com/visionmedia/page.js
|
|
71
126
|
// MIT license https://github.com/visionmedia/page.js#license
|
|
72
|
-
if (
|
|
127
|
+
if (event.button || event.which !== 1) return;
|
|
73
128
|
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
74
129
|
if (event.defaultPrevented) return;
|
|
75
130
|
|
|
76
|
-
const a = find_anchor(event.target);
|
|
131
|
+
const a = find_anchor(/** @type {Node} */ (event.target));
|
|
77
132
|
if (!a) return;
|
|
78
133
|
|
|
79
134
|
if (!a.href) return;
|
|
80
135
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const href = String(svg ? a.href.baseVal : a.href);
|
|
85
|
-
|
|
86
|
-
if (href === location.href) {
|
|
136
|
+
const url = get_href(a);
|
|
137
|
+
const url_string = url.toString();
|
|
138
|
+
if (url_string === location.href) {
|
|
87
139
|
if (!location.hash) event.preventDefault();
|
|
88
140
|
return;
|
|
89
141
|
}
|
|
90
142
|
|
|
91
143
|
// Ignore if tag has
|
|
92
144
|
// 1. 'download' attribute
|
|
93
|
-
// 2. rel
|
|
94
|
-
|
|
145
|
+
// 2. 'rel' attribute includes external
|
|
146
|
+
const rel = (a.getAttribute('rel') || '').split(/\s+/);
|
|
147
|
+
|
|
148
|
+
if (a.hasAttribute('download') || (rel && rel.includes('external'))) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
95
151
|
|
|
96
152
|
// Ignore if <a> has a target
|
|
97
|
-
if (
|
|
153
|
+
if (a instanceof SVGAElement ? a.target.baseVal : a.target) return;
|
|
98
154
|
|
|
99
|
-
|
|
155
|
+
if (!this.owns(url)) return;
|
|
100
156
|
|
|
101
|
-
|
|
102
|
-
if (url.pathname === location.pathname && url.search === location.search) return;
|
|
157
|
+
const noscroll = a.hasAttribute('sveltekit:noscroll');
|
|
103
158
|
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
159
|
+
const i1 = url_string.indexOf('#');
|
|
160
|
+
const i2 = location.href.indexOf('#');
|
|
161
|
+
const u1 = i1 >= 0 ? url_string.substring(0, i1) : url_string;
|
|
162
|
+
const u2 = i2 >= 0 ? location.href.substring(0, i2) : location.href;
|
|
163
|
+
history.pushState({}, '', url.href);
|
|
164
|
+
if (u1 === u2) {
|
|
165
|
+
window.dispatchEvent(new HashChangeEvent('hashchange'));
|
|
111
166
|
}
|
|
167
|
+
this._navigate(url, noscroll ? scroll_state() : null, false, [], url.hash);
|
|
168
|
+
event.preventDefault();
|
|
112
169
|
});
|
|
113
170
|
|
|
114
171
|
addEventListener('popstate', (event) => {
|
|
115
|
-
if (event.state) {
|
|
172
|
+
if (event.state && this.enabled) {
|
|
116
173
|
const url = new URL(location.href);
|
|
117
|
-
|
|
118
|
-
if (selected) {
|
|
119
|
-
this.navigate(selected, event.state['sveltekit:scroll']);
|
|
120
|
-
} else {
|
|
121
|
-
// eslint-disable-next-line
|
|
122
|
-
location.href = location.href; // nosonar
|
|
123
|
-
}
|
|
174
|
+
this._navigate(url, event.state['sveltekit:scroll'], false, []);
|
|
124
175
|
}
|
|
125
176
|
});
|
|
177
|
+
}
|
|
126
178
|
|
|
127
|
-
|
|
128
|
-
|
|
179
|
+
/** @param {URL} url */
|
|
180
|
+
owns(url) {
|
|
181
|
+
return url.origin === location.origin && url.pathname.startsWith(this.base);
|
|
182
|
+
}
|
|
129
183
|
|
|
130
|
-
|
|
131
|
-
|
|
184
|
+
/**
|
|
185
|
+
* @param {URL} url
|
|
186
|
+
* @returns {import('./types').NavigationInfo | undefined}
|
|
187
|
+
*/
|
|
188
|
+
parse(url) {
|
|
189
|
+
if (this.owns(url)) {
|
|
190
|
+
const path = url.pathname.slice(this.base.length) || '/';
|
|
132
191
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
192
|
+
const decoded_path = decodeURI(path);
|
|
193
|
+
const routes = this.routes.filter(([pattern]) => pattern.test(decoded_path));
|
|
194
|
+
|
|
195
|
+
const query = new URLSearchParams(url.search);
|
|
196
|
+
const id = `${path}?${query}`;
|
|
136
197
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
198
|
+
return { id, routes, path, decoded_path, query };
|
|
199
|
+
}
|
|
200
|
+
}
|
|
140
201
|
|
|
141
|
-
|
|
202
|
+
/**
|
|
203
|
+
* @typedef {Parameters<typeof import('$app/navigation').goto>} GotoParams
|
|
204
|
+
*
|
|
205
|
+
* @param {GotoParams[0]} href
|
|
206
|
+
* @param {GotoParams[1]} opts
|
|
207
|
+
* @param {string[]} chain
|
|
208
|
+
*/
|
|
209
|
+
async goto(
|
|
210
|
+
href,
|
|
211
|
+
{ noscroll = false, replaceState = false, keepfocus = false, state = {} } = {},
|
|
212
|
+
chain
|
|
213
|
+
) {
|
|
214
|
+
const url = new URL(href, get_base_uri(document));
|
|
142
215
|
|
|
143
|
-
if (
|
|
144
|
-
|
|
216
|
+
if (this.enabled && this.owns(url)) {
|
|
217
|
+
history[replaceState ? 'replaceState' : 'pushState'](state, '', href);
|
|
218
|
+
return this._navigate(url, noscroll ? scroll_state() : null, keepfocus, chain, url.hash);
|
|
145
219
|
}
|
|
146
220
|
|
|
147
|
-
|
|
148
|
-
|
|
221
|
+
location.href = url.href;
|
|
222
|
+
return new Promise(() => {
|
|
223
|
+
/* never resolves */
|
|
224
|
+
});
|
|
225
|
+
}
|
|
149
226
|
|
|
150
|
-
|
|
151
|
-
|
|
227
|
+
enable() {
|
|
228
|
+
this.enabled = true;
|
|
229
|
+
}
|
|
152
230
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
231
|
+
disable() {
|
|
232
|
+
this.enabled = false;
|
|
233
|
+
}
|
|
156
234
|
|
|
157
|
-
|
|
235
|
+
/**
|
|
236
|
+
* @param {URL} url
|
|
237
|
+
* @returns {Promise<import('./types').NavigationResult>}
|
|
238
|
+
*/
|
|
239
|
+
async prefetch(url) {
|
|
240
|
+
const info = this.parse(url);
|
|
158
241
|
|
|
159
|
-
|
|
160
|
-
|
|
242
|
+
if (!info) {
|
|
243
|
+
throw new Error('Attempted to prefetch a URL that does not belong to this app');
|
|
161
244
|
}
|
|
245
|
+
|
|
246
|
+
return this.renderer.load(info);
|
|
162
247
|
}
|
|
163
248
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
249
|
+
/**
|
|
250
|
+
* @param {URL} url
|
|
251
|
+
* @param {{ x: number, y: number }?} scroll
|
|
252
|
+
* @param {boolean} keepfocus
|
|
253
|
+
* @param {string[]} chain
|
|
254
|
+
* @param {string} [hash]
|
|
255
|
+
*/
|
|
256
|
+
async _navigate(url, scroll, keepfocus, chain, hash) {
|
|
257
|
+
const info = this.parse(url);
|
|
258
|
+
|
|
259
|
+
if (!info) {
|
|
260
|
+
throw new Error('Attempted to navigate to a URL that does not belong to this app');
|
|
261
|
+
}
|
|
167
262
|
|
|
168
|
-
if (
|
|
169
|
-
|
|
263
|
+
if (!this.navigating) {
|
|
264
|
+
dispatchEvent(new CustomEvent('sveltekit:navigation-start'));
|
|
265
|
+
}
|
|
266
|
+
this.navigating++;
|
|
170
267
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
268
|
+
// remove trailing slashes
|
|
269
|
+
if (info.path !== '/') {
|
|
270
|
+
const has_trailing_slash = info.path.endsWith('/');
|
|
271
|
+
|
|
272
|
+
const incorrect =
|
|
273
|
+
(has_trailing_slash && this.trailing_slash === 'never') ||
|
|
274
|
+
(!has_trailing_slash &&
|
|
275
|
+
this.trailing_slash === 'always' &&
|
|
276
|
+
!(info.path.split('/').pop() || '').includes('.'));
|
|
277
|
+
|
|
278
|
+
if (incorrect) {
|
|
279
|
+
info.path = has_trailing_slash ? info.path.slice(0, -1) : info.path + '/';
|
|
280
|
+
history.replaceState({}, '', `${this.base}${info.path}${location.search}`);
|
|
281
|
+
}
|
|
174
282
|
}
|
|
175
283
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
284
|
+
await this.renderer.handle_navigation(info, chain, false, { hash, scroll, keepfocus });
|
|
285
|
+
|
|
286
|
+
this.navigating--;
|
|
287
|
+
if (!this.navigating) {
|
|
288
|
+
dispatchEvent(new CustomEvent('sveltekit:navigation-end'));
|
|
289
|
+
}
|
|
180
290
|
}
|
|
291
|
+
}
|
|
181
292
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
293
|
+
/**
|
|
294
|
+
* @param {unknown} err
|
|
295
|
+
* @return {Error}
|
|
296
|
+
*/
|
|
297
|
+
function coalesce_to_error(err) {
|
|
298
|
+
return err instanceof Error ||
|
|
299
|
+
(err && /** @type {any} */ (err).name && /** @type {any} */ (err).message)
|
|
300
|
+
? /** @type {Error} */ (err)
|
|
301
|
+
: new Error(JSON.stringify(err));
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Hash using djb2
|
|
306
|
+
* @param {import('types/hooks').StrictBody} value
|
|
307
|
+
*/
|
|
308
|
+
function hash(value) {
|
|
309
|
+
let hash = 5381;
|
|
310
|
+
let i = value.length;
|
|
311
|
+
|
|
312
|
+
if (typeof value === 'string') {
|
|
313
|
+
while (i) hash = (hash * 33) ^ value.charCodeAt(--i);
|
|
314
|
+
} else {
|
|
315
|
+
while (i) hash = (hash * 33) ^ value[--i];
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return (hash >>> 0).toString(36);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* @param {import('types/page').LoadOutput} loaded
|
|
323
|
+
* @returns {import('types/internal').NormalizedLoadOutput}
|
|
324
|
+
*/
|
|
325
|
+
function normalize(loaded) {
|
|
326
|
+
const has_error_status =
|
|
327
|
+
loaded.status && loaded.status >= 400 && loaded.status <= 599 && !loaded.redirect;
|
|
328
|
+
if (loaded.error || has_error_status) {
|
|
329
|
+
const status = loaded.status;
|
|
330
|
+
|
|
331
|
+
if (!loaded.error && has_error_status) {
|
|
332
|
+
return {
|
|
333
|
+
status: status || 500,
|
|
334
|
+
error: new Error()
|
|
335
|
+
};
|
|
186
336
|
}
|
|
187
337
|
|
|
188
|
-
|
|
338
|
+
const error = typeof loaded.error === 'string' ? new Error(loaded.error) : loaded.error;
|
|
189
339
|
|
|
190
|
-
|
|
340
|
+
if (!(error instanceof Error)) {
|
|
341
|
+
return {
|
|
342
|
+
status: 500,
|
|
343
|
+
error: new Error(
|
|
344
|
+
`"error" property returned from load() must be a string or instance of Error, received type "${typeof error}"`
|
|
345
|
+
)
|
|
346
|
+
};
|
|
347
|
+
}
|
|
191
348
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
349
|
+
if (!status || status < 400 || status > 599) {
|
|
350
|
+
console.warn('"error" returned from load() without a valid status code — defaulting to 500');
|
|
351
|
+
return { status: 500, error };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return { status, error };
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (loaded.redirect) {
|
|
358
|
+
if (!loaded.status || Math.floor(loaded.status / 100) !== 3) {
|
|
359
|
+
return {
|
|
360
|
+
status: 500,
|
|
361
|
+
error: new Error(
|
|
362
|
+
'"redirect" property returned from load() must be accompanied by a 3xx status code'
|
|
363
|
+
)
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (typeof loaded.redirect !== 'string') {
|
|
368
|
+
return {
|
|
369
|
+
status: 500,
|
|
370
|
+
error: new Error('"redirect" property returned from load() must be a string')
|
|
371
|
+
};
|
|
200
372
|
}
|
|
201
373
|
}
|
|
374
|
+
|
|
375
|
+
// TODO remove before 1.0
|
|
376
|
+
if (/** @type {any} */ (loaded).context) {
|
|
377
|
+
throw new Error(
|
|
378
|
+
'You are returning "context" from a load function. ' +
|
|
379
|
+
'"context" was renamed to "stuff", please adjust your code accordingly.'
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return /** @type {import('types/internal').NormalizedLoadOutput} */ (loaded);
|
|
202
384
|
}
|
|
203
385
|
|
|
386
|
+
/**
|
|
387
|
+
* @typedef {import('types/internal').CSRComponent} CSRComponent
|
|
388
|
+
*
|
|
389
|
+
* @typedef {Partial<import('types/page').Page>} Page
|
|
390
|
+
* @typedef {{ from: Page; to: Page }} Navigating
|
|
391
|
+
*/
|
|
392
|
+
|
|
393
|
+
/** @param {any} value */
|
|
204
394
|
function page_store(value) {
|
|
205
395
|
const store = writable(value);
|
|
206
396
|
let ready = true;
|
|
@@ -210,12 +400,15 @@ function page_store(value) {
|
|
|
210
400
|
store.update((val) => val);
|
|
211
401
|
}
|
|
212
402
|
|
|
403
|
+
/** @param {any} new_value */
|
|
213
404
|
function set(new_value) {
|
|
214
405
|
ready = false;
|
|
215
406
|
store.set(new_value);
|
|
216
407
|
}
|
|
217
408
|
|
|
409
|
+
/** @param {(value: any) => void} run */
|
|
218
410
|
function subscribe(run) {
|
|
411
|
+
/** @type {any} */
|
|
219
412
|
let old_value;
|
|
220
413
|
return store.subscribe((new_value) => {
|
|
221
414
|
if (old_value === undefined || (ready && new_value !== old_value)) {
|
|
@@ -227,38 +420,74 @@ function page_store(value) {
|
|
|
227
420
|
return { notify, set, subscribe };
|
|
228
421
|
}
|
|
229
422
|
|
|
423
|
+
/**
|
|
424
|
+
* @param {RequestInfo} resource
|
|
425
|
+
* @param {RequestInit} [opts]
|
|
426
|
+
*/
|
|
427
|
+
function initial_fetch(resource, opts) {
|
|
428
|
+
const url = typeof resource === 'string' ? resource : resource.url;
|
|
429
|
+
|
|
430
|
+
let selector = `script[data-type="svelte-data"][data-url=${JSON.stringify(url)}]`;
|
|
431
|
+
|
|
432
|
+
if (opts && typeof opts.body === 'string') {
|
|
433
|
+
selector += `[data-body="${hash(opts.body)}"]`;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const script = document.querySelector(selector);
|
|
437
|
+
if (script && script.textContent) {
|
|
438
|
+
const { body, ...init } = JSON.parse(script.textContent);
|
|
439
|
+
return Promise.resolve(new Response(body, init));
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return fetch(resource, opts);
|
|
443
|
+
}
|
|
444
|
+
|
|
230
445
|
class Renderer {
|
|
231
|
-
|
|
446
|
+
/**
|
|
447
|
+
* @param {{
|
|
448
|
+
* Root: CSRComponent;
|
|
449
|
+
* fallback: [CSRComponent, CSRComponent];
|
|
450
|
+
* target: Node;
|
|
451
|
+
* session: any;
|
|
452
|
+
* host: string;
|
|
453
|
+
* }} opts
|
|
454
|
+
*/
|
|
455
|
+
constructor({ Root, fallback, target, session, host }) {
|
|
232
456
|
this.Root = Root;
|
|
233
|
-
this.
|
|
234
|
-
this.
|
|
457
|
+
this.fallback = fallback;
|
|
458
|
+
this.host = host;
|
|
459
|
+
|
|
460
|
+
/** @type {import('./router').Router | undefined} */
|
|
461
|
+
this.router;
|
|
235
462
|
|
|
236
|
-
// TODO ideally we wouldn't need to store these...
|
|
237
463
|
this.target = target;
|
|
238
464
|
|
|
239
|
-
this.
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
465
|
+
this.started = false;
|
|
466
|
+
|
|
467
|
+
this.session_id = 1;
|
|
468
|
+
this.invalid = new Set();
|
|
469
|
+
this.invalidating = null;
|
|
244
470
|
|
|
471
|
+
/** @type {import('./types').NavigationState} */
|
|
245
472
|
this.current = {
|
|
473
|
+
// @ts-ignore - we need the initial value to be null
|
|
246
474
|
page: null,
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
nodes: []
|
|
475
|
+
session_id: 0,
|
|
476
|
+
branch: []
|
|
250
477
|
};
|
|
251
478
|
|
|
252
|
-
|
|
479
|
+
/** @type {Map<string, import('./types').NavigationResult>} */
|
|
480
|
+
this.cache = new Map();
|
|
253
481
|
|
|
254
|
-
|
|
255
|
-
|
|
482
|
+
/** @type {{id: string | null, promise: Promise<import('./types').NavigationResult> | null}} */
|
|
483
|
+
this.loading = {
|
|
484
|
+
id: null,
|
|
256
485
|
promise: null
|
|
257
486
|
};
|
|
258
487
|
|
|
259
488
|
this.stores = {
|
|
260
489
|
page: page_store({}),
|
|
261
|
-
navigating: writable(null),
|
|
490
|
+
navigating: writable(/** @type {Navigating | null} */ (null)),
|
|
262
491
|
session: writable(session)
|
|
263
492
|
};
|
|
264
493
|
|
|
@@ -266,326 +495,701 @@ class Renderer {
|
|
|
266
495
|
|
|
267
496
|
this.root = null;
|
|
268
497
|
|
|
269
|
-
const trigger_prefetch = (event) => {
|
|
270
|
-
const a = find_anchor(event.target);
|
|
271
|
-
|
|
272
|
-
if (a && a.hasAttribute('sveltekit:prefetch')) {
|
|
273
|
-
this.prefetch(new URL(a.href));
|
|
274
|
-
}
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
let mousemove_timeout;
|
|
278
|
-
const handle_mousemove = (event) => {
|
|
279
|
-
clearTimeout(mousemove_timeout);
|
|
280
|
-
mousemove_timeout = setTimeout(() => {
|
|
281
|
-
trigger_prefetch(event);
|
|
282
|
-
}, 20);
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
addEventListener('touchstart', trigger_prefetch);
|
|
286
|
-
addEventListener('mousemove', handle_mousemove);
|
|
287
|
-
|
|
288
498
|
let ready = false;
|
|
289
499
|
this.stores.session.subscribe(async (value) => {
|
|
290
500
|
this.$session = value;
|
|
291
501
|
|
|
292
|
-
if (!ready) return;
|
|
293
|
-
this.
|
|
502
|
+
if (!ready || !this.router) return;
|
|
503
|
+
this.session_id += 1;
|
|
294
504
|
|
|
295
|
-
const
|
|
296
|
-
this.
|
|
505
|
+
const info = this.router.parse(new URL(location.href));
|
|
506
|
+
if (info) this.update(info, [], true);
|
|
297
507
|
});
|
|
298
508
|
ready = true;
|
|
299
509
|
}
|
|
300
510
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
511
|
+
/**
|
|
512
|
+
* @param {{
|
|
513
|
+
* status: number;
|
|
514
|
+
* error: Error;
|
|
515
|
+
* nodes: Array<Promise<CSRComponent>>;
|
|
516
|
+
* page: import('types/page').Page;
|
|
517
|
+
* }} selected
|
|
518
|
+
*/
|
|
519
|
+
async start({ status, error, nodes, page }) {
|
|
520
|
+
/** @type {Array<import('./types').BranchNode | undefined>} */
|
|
521
|
+
const branch = [];
|
|
522
|
+
|
|
523
|
+
/** @type {Record<string, any>} */
|
|
524
|
+
let stuff = {};
|
|
525
|
+
|
|
526
|
+
/** @type {import('./types').NavigationResult | undefined} */
|
|
527
|
+
let result;
|
|
528
|
+
|
|
529
|
+
let error_args;
|
|
530
|
+
|
|
531
|
+
try {
|
|
532
|
+
for (let i = 0; i < nodes.length; i += 1) {
|
|
533
|
+
const is_leaf = i === nodes.length - 1;
|
|
534
|
+
|
|
535
|
+
const node = await this._load_node({
|
|
536
|
+
module: await nodes[i],
|
|
537
|
+
page,
|
|
538
|
+
stuff,
|
|
539
|
+
status: is_leaf ? status : undefined,
|
|
540
|
+
error: is_leaf ? error : undefined
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
branch.push(node);
|
|
544
|
+
|
|
545
|
+
if (node && node.loaded) {
|
|
546
|
+
if (node.loaded.error) {
|
|
547
|
+
if (error) throw node.loaded.error;
|
|
548
|
+
error_args = {
|
|
549
|
+
status: node.loaded.status,
|
|
550
|
+
error: node.loaded.error,
|
|
551
|
+
path: page.path,
|
|
552
|
+
query: page.query
|
|
553
|
+
};
|
|
554
|
+
} else if (node.loaded.stuff) {
|
|
555
|
+
stuff = {
|
|
556
|
+
...stuff,
|
|
557
|
+
...node.loaded.stuff
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
result = error_args
|
|
564
|
+
? await this._load_error(error_args)
|
|
565
|
+
: await this._get_navigation_result_from_branch({ page, branch });
|
|
566
|
+
} catch (e) {
|
|
567
|
+
if (error) throw e;
|
|
568
|
+
|
|
569
|
+
result = await this._load_error({
|
|
570
|
+
status: 500,
|
|
571
|
+
error: coalesce_to_error(e),
|
|
572
|
+
path: page.path,
|
|
573
|
+
query: page.query
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (result.redirect) {
|
|
578
|
+
// this is a real edge case — `load` would need to return
|
|
579
|
+
// a redirect but only in the browser
|
|
580
|
+
location.href = new URL(result.redirect, location.href).href;
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
this._init(result);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* @param {import('./types').NavigationInfo} info
|
|
589
|
+
* @param {string[]} chain
|
|
590
|
+
* @param {boolean} no_cache
|
|
591
|
+
* @param {{hash?: string, scroll: { x: number, y: number } | null, keepfocus: boolean}} [opts]
|
|
592
|
+
*/
|
|
593
|
+
async handle_navigation(info, chain, no_cache, opts) {
|
|
594
|
+
if (this.started) {
|
|
595
|
+
this.stores.navigating.set({
|
|
596
|
+
from: {
|
|
597
|
+
path: this.current.page.path,
|
|
598
|
+
query: this.current.page.query
|
|
599
|
+
},
|
|
600
|
+
to: {
|
|
601
|
+
path: info.path,
|
|
602
|
+
query: info.query
|
|
603
|
+
}
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
await this.update(info, chain, no_cache, opts);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* @param {import('./types').NavigationInfo} info
|
|
612
|
+
* @param {string[]} chain
|
|
613
|
+
* @param {boolean} no_cache
|
|
614
|
+
* @param {{hash?: string, scroll: { x: number, y: number } | null, keepfocus: boolean}} [opts]
|
|
615
|
+
*/
|
|
616
|
+
async update(info, chain, no_cache, opts) {
|
|
617
|
+
const token = (this.token = {});
|
|
618
|
+
let navigation_result = await this._get_navigation_result(info, no_cache);
|
|
619
|
+
|
|
620
|
+
// abort if user navigated during update
|
|
621
|
+
if (token !== this.token) return;
|
|
622
|
+
|
|
623
|
+
this.invalid.clear();
|
|
624
|
+
|
|
625
|
+
if (navigation_result.redirect) {
|
|
626
|
+
if (chain.length > 10 || chain.includes(info.path)) {
|
|
627
|
+
navigation_result = await this._load_error({
|
|
628
|
+
status: 500,
|
|
629
|
+
error: new Error('Redirect loop'),
|
|
630
|
+
path: info.path,
|
|
631
|
+
query: info.query
|
|
632
|
+
});
|
|
633
|
+
} else {
|
|
634
|
+
if (this.router) {
|
|
635
|
+
this.router.goto(navigation_result.redirect, { replaceState: true }, [
|
|
636
|
+
...chain,
|
|
637
|
+
info.path
|
|
638
|
+
]);
|
|
639
|
+
} else {
|
|
640
|
+
location.href = new URL(navigation_result.redirect, location.href).href;
|
|
641
|
+
}
|
|
308
642
|
|
|
309
|
-
|
|
310
|
-
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
if (navigation_result.reload) {
|
|
648
|
+
location.reload();
|
|
649
|
+
} else if (this.started) {
|
|
650
|
+
this.current = navigation_result.state;
|
|
651
|
+
|
|
652
|
+
this.root.$set(navigation_result.props);
|
|
653
|
+
this.stores.navigating.set(null);
|
|
311
654
|
} else {
|
|
312
|
-
|
|
655
|
+
this._init(navigation_result);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
if (!opts?.keepfocus) {
|
|
659
|
+
document.body.focus();
|
|
660
|
+
}
|
|
313
661
|
|
|
314
|
-
|
|
315
|
-
|
|
662
|
+
await 0;
|
|
663
|
+
|
|
664
|
+
// After `await 0`, the onMount() function in the component executed.
|
|
665
|
+
// If there was no scrolling happening (checked via pageYOffset),
|
|
666
|
+
// continue on our custom scroll handling
|
|
667
|
+
if (pageYOffset === 0 && opts) {
|
|
668
|
+
const { hash, scroll } = opts;
|
|
669
|
+
|
|
670
|
+
const deep_linked = hash && document.getElementById(hash.slice(1));
|
|
671
|
+
if (scroll) {
|
|
672
|
+
scrollTo(scroll.x, scroll.y);
|
|
673
|
+
} else if (deep_linked) {
|
|
674
|
+
// Here we use `scrollIntoView` on the element instead of `scrollTo`
|
|
675
|
+
// because it natively supports the `scroll-margin` and `scroll-behavior`
|
|
676
|
+
// CSS properties.
|
|
677
|
+
deep_linked.scrollIntoView();
|
|
678
|
+
} else {
|
|
679
|
+
scrollTo(0, 0);
|
|
316
680
|
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
this.loading.promise = null;
|
|
684
|
+
this.loading.id = null;
|
|
685
|
+
|
|
686
|
+
if (!this.router) return;
|
|
687
|
+
|
|
688
|
+
const leaf_node = navigation_result.state.branch[navigation_result.state.branch.length - 1];
|
|
689
|
+
if (leaf_node && leaf_node.module.router === false) {
|
|
690
|
+
this.router.disable();
|
|
691
|
+
} else {
|
|
692
|
+
this.router.enable();
|
|
693
|
+
}
|
|
694
|
+
}
|
|
317
695
|
|
|
318
|
-
|
|
319
|
-
|
|
696
|
+
/**
|
|
697
|
+
* @param {import('./types').NavigationInfo} info
|
|
698
|
+
* @returns {Promise<import('./types').NavigationResult>}
|
|
699
|
+
*/
|
|
700
|
+
load(info) {
|
|
701
|
+
this.loading.promise = this._get_navigation_result(info, false);
|
|
702
|
+
this.loading.id = info.id;
|
|
703
|
+
|
|
704
|
+
return this.loading.promise;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/** @param {string} href */
|
|
708
|
+
invalidate(href) {
|
|
709
|
+
this.invalid.add(href);
|
|
710
|
+
|
|
711
|
+
if (!this.invalidating) {
|
|
712
|
+
this.invalidating = Promise.resolve().then(async () => {
|
|
713
|
+
const info = this.router && this.router.parse(new URL(location.href));
|
|
714
|
+
if (info) await this.update(info, [], true);
|
|
715
|
+
|
|
716
|
+
this.invalidating = null;
|
|
717
|
+
});
|
|
320
718
|
}
|
|
321
719
|
|
|
720
|
+
return this.invalidating;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/** @param {import('./types').NavigationResult} result */
|
|
724
|
+
_init(result) {
|
|
725
|
+
this.current = result.state;
|
|
726
|
+
|
|
727
|
+
const style = document.querySelector('style[data-svelte]');
|
|
728
|
+
if (style) style.remove();
|
|
729
|
+
|
|
322
730
|
this.root = new this.Root({
|
|
323
731
|
target: this.target,
|
|
324
|
-
props
|
|
732
|
+
props: {
|
|
733
|
+
stores: this.stores,
|
|
734
|
+
...result.props
|
|
735
|
+
},
|
|
325
736
|
hydrate: true
|
|
326
737
|
});
|
|
327
738
|
|
|
328
|
-
this.
|
|
739
|
+
this.started = true;
|
|
329
740
|
}
|
|
330
741
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
742
|
+
/**
|
|
743
|
+
* @param {import('./types').NavigationInfo} info
|
|
744
|
+
* @param {boolean} no_cache
|
|
745
|
+
* @returns {Promise<import('./types').NavigationResult>}
|
|
746
|
+
*/
|
|
747
|
+
async _get_navigation_result(info, no_cache) {
|
|
748
|
+
if (this.loading.id === info.id && this.loading.promise) {
|
|
749
|
+
return this.loading.promise;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
for (let i = 0; i < info.routes.length; i += 1) {
|
|
753
|
+
const route = info.routes[i];
|
|
754
|
+
|
|
755
|
+
// load code for subsequent routes immediately, if they are as
|
|
756
|
+
// likely to match the current path/query as the current one
|
|
757
|
+
let j = i + 1;
|
|
758
|
+
while (j < info.routes.length) {
|
|
759
|
+
const next = info.routes[j];
|
|
760
|
+
if (next[0].toString() === route[0].toString()) {
|
|
761
|
+
next[1].forEach((loader) => loader());
|
|
762
|
+
j += 1;
|
|
763
|
+
} else {
|
|
764
|
+
break;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
const result = await this._load(
|
|
769
|
+
{
|
|
770
|
+
route,
|
|
771
|
+
info
|
|
772
|
+
},
|
|
773
|
+
no_cache
|
|
774
|
+
);
|
|
775
|
+
if (result) return result;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
return await this._load_error({
|
|
779
|
+
status: 404,
|
|
780
|
+
error: new Error(`Not found: ${info.path}`),
|
|
781
|
+
path: info.path,
|
|
782
|
+
query: info.query
|
|
335
783
|
});
|
|
336
784
|
}
|
|
337
785
|
|
|
338
|
-
|
|
339
|
-
|
|
786
|
+
/**
|
|
787
|
+
*
|
|
788
|
+
* @param {{
|
|
789
|
+
* page: import('types/page').Page;
|
|
790
|
+
* branch: Array<import('./types').BranchNode | undefined>
|
|
791
|
+
* }} opts
|
|
792
|
+
*/
|
|
793
|
+
async _get_navigation_result_from_branch({ page, branch }) {
|
|
794
|
+
const filtered = /** @type {import('./types').BranchNode[] } */ (branch.filter(Boolean));
|
|
795
|
+
const redirect = filtered.find((f) => f.loaded && f.loaded.redirect);
|
|
796
|
+
|
|
797
|
+
/** @type {import('./types').NavigationResult} */
|
|
798
|
+
const result = {
|
|
799
|
+
redirect: redirect && redirect.loaded ? redirect.loaded.redirect : undefined,
|
|
800
|
+
state: {
|
|
801
|
+
page,
|
|
802
|
+
branch,
|
|
803
|
+
session_id: this.session_id
|
|
804
|
+
},
|
|
805
|
+
props: {
|
|
806
|
+
components: filtered.map((node) => node.module.default)
|
|
807
|
+
}
|
|
808
|
+
};
|
|
340
809
|
|
|
341
|
-
|
|
810
|
+
for (let i = 0; i < filtered.length; i += 1) {
|
|
811
|
+
const loaded = filtered[i].loaded;
|
|
812
|
+
result.props[`props_${i}`] = loaded ? await loaded.props : null;
|
|
813
|
+
}
|
|
342
814
|
|
|
343
|
-
if (
|
|
344
|
-
|
|
345
|
-
this.current
|
|
815
|
+
if (
|
|
816
|
+
!this.current.page ||
|
|
817
|
+
page.path !== this.current.page.path ||
|
|
818
|
+
page.query.toString() !== this.current.page.query.toString()
|
|
819
|
+
) {
|
|
820
|
+
result.props.page = page;
|
|
821
|
+
}
|
|
346
822
|
|
|
347
|
-
|
|
348
|
-
|
|
823
|
+
const leaf = filtered[filtered.length - 1];
|
|
824
|
+
const maxage = leaf.loaded && leaf.loaded.maxage;
|
|
825
|
+
|
|
826
|
+
if (maxage) {
|
|
827
|
+
const key = `${page.path}?${page.query}`;
|
|
828
|
+
let ready = false;
|
|
829
|
+
|
|
830
|
+
const clear = () => {
|
|
831
|
+
if (this.cache.get(key) === result) {
|
|
832
|
+
this.cache.delete(key);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
unsubscribe();
|
|
836
|
+
clearTimeout(timeout);
|
|
837
|
+
};
|
|
838
|
+
|
|
839
|
+
const timeout = setTimeout(clear, maxage * 1000);
|
|
840
|
+
|
|
841
|
+
const unsubscribe = this.stores.session.subscribe(() => {
|
|
842
|
+
if (ready) clear();
|
|
843
|
+
});
|
|
844
|
+
|
|
845
|
+
ready = true;
|
|
846
|
+
|
|
847
|
+
this.cache.set(key, result);
|
|
349
848
|
}
|
|
849
|
+
|
|
850
|
+
return result;
|
|
350
851
|
}
|
|
351
852
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
853
|
+
/**
|
|
854
|
+
* @param {{
|
|
855
|
+
* status?: number;
|
|
856
|
+
* error?: Error;
|
|
857
|
+
* module: CSRComponent;
|
|
858
|
+
* page: import('types/page').Page;
|
|
859
|
+
* stuff: Record<string, any>;
|
|
860
|
+
* }} options
|
|
861
|
+
* @returns
|
|
862
|
+
*/
|
|
863
|
+
async _load_node({ status, error, module, page, stuff }) {
|
|
864
|
+
/** @type {import('./types').BranchNode} */
|
|
865
|
+
const node = {
|
|
866
|
+
module,
|
|
867
|
+
uses: {
|
|
868
|
+
params: new Set(),
|
|
869
|
+
path: false,
|
|
870
|
+
query: false,
|
|
871
|
+
session: false,
|
|
872
|
+
stuff: false,
|
|
873
|
+
dependencies: []
|
|
874
|
+
},
|
|
875
|
+
loaded: null,
|
|
876
|
+
stuff
|
|
357
877
|
};
|
|
358
878
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
879
|
+
/** @type {Record<string, string>} */
|
|
880
|
+
const params = {};
|
|
881
|
+
for (const key in page.params) {
|
|
882
|
+
Object.defineProperty(params, key, {
|
|
883
|
+
get() {
|
|
884
|
+
node.uses.params.add(key);
|
|
885
|
+
return page.params[key];
|
|
886
|
+
},
|
|
887
|
+
enumerable: true
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
const session = this.$session;
|
|
892
|
+
|
|
893
|
+
if (module.load) {
|
|
894
|
+
const { started } = this;
|
|
895
|
+
|
|
896
|
+
/** @type {import('types/page').LoadInput | import('types/page').ErrorLoadInput} */
|
|
897
|
+
const load_input = {
|
|
898
|
+
page: {
|
|
899
|
+
host: page.host,
|
|
900
|
+
params,
|
|
901
|
+
get path() {
|
|
902
|
+
node.uses.path = true;
|
|
903
|
+
return page.path;
|
|
904
|
+
},
|
|
905
|
+
get query() {
|
|
906
|
+
node.uses.query = true;
|
|
907
|
+
return page.query;
|
|
908
|
+
}
|
|
909
|
+
},
|
|
910
|
+
get session() {
|
|
911
|
+
node.uses.session = true;
|
|
912
|
+
return session;
|
|
913
|
+
},
|
|
914
|
+
get stuff() {
|
|
915
|
+
node.uses.stuff = true;
|
|
916
|
+
return { ...stuff };
|
|
917
|
+
},
|
|
918
|
+
fetch(resource, info) {
|
|
919
|
+
const url = typeof resource === 'string' ? resource : resource.url;
|
|
920
|
+
const { href } = new URL(url, new URL(page.path, document.baseURI));
|
|
921
|
+
node.uses.dependencies.push(href);
|
|
922
|
+
|
|
923
|
+
return started ? fetch(resource, info) : initial_fetch(resource, info);
|
|
365
924
|
}
|
|
925
|
+
};
|
|
926
|
+
|
|
927
|
+
if (error) {
|
|
928
|
+
/** @type {import('types/page').ErrorLoadInput} */ (load_input).status = status;
|
|
929
|
+
/** @type {import('types/page').ErrorLoadInput} */ (load_input).error = error;
|
|
366
930
|
}
|
|
367
931
|
|
|
368
|
-
|
|
369
|
-
};
|
|
932
|
+
const loaded = await module.load.call(null, load_input);
|
|
370
933
|
|
|
371
|
-
|
|
934
|
+
// if the page component returns nothing from load, fall through
|
|
935
|
+
if (!loaded) return;
|
|
372
936
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
session_changed: false,
|
|
377
|
-
nodes: []
|
|
378
|
-
};
|
|
937
|
+
node.loaded = normalize(loaded);
|
|
938
|
+
if (node.loaded.stuff) node.stuff = node.loaded.stuff;
|
|
939
|
+
}
|
|
379
940
|
|
|
380
|
-
|
|
381
|
-
|
|
941
|
+
return node;
|
|
942
|
+
}
|
|
382
943
|
|
|
383
|
-
|
|
384
|
-
|
|
944
|
+
/**
|
|
945
|
+
* @param {import('./types').NavigationCandidate} selected
|
|
946
|
+
* @param {boolean} no_cache
|
|
947
|
+
* @returns {Promise<import('./types').NavigationResult | undefined>} undefined if fallthrough
|
|
948
|
+
*/
|
|
949
|
+
async _load({ route, info: { path, decoded_path, query } }, no_cache) {
|
|
950
|
+
const key = `${decoded_path}?${query}`;
|
|
951
|
+
|
|
952
|
+
if (!no_cache) {
|
|
953
|
+
const cached = this.cache.get(key);
|
|
954
|
+
if (cached) return cached;
|
|
955
|
+
}
|
|
385
956
|
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
957
|
+
const [pattern, a, b, get_params] = route;
|
|
958
|
+
const params = get_params
|
|
959
|
+
? // the pattern is for the route which we've already matched to this path
|
|
960
|
+
get_params(/** @type {RegExpExecArray} */ (pattern.exec(decoded_path)))
|
|
961
|
+
: {};
|
|
962
|
+
|
|
963
|
+
const changed = this.current.page && {
|
|
964
|
+
path: path !== this.current.page.path,
|
|
965
|
+
params: Object.keys(params).filter((key) => this.current.page.params[key] !== params[key]),
|
|
966
|
+
query: query.toString() !== this.current.page.query.toString(),
|
|
967
|
+
session: this.session_id !== this.current.session_id
|
|
393
968
|
};
|
|
394
969
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
970
|
+
/** @type {import('types/page').Page} */
|
|
971
|
+
const page = { host: this.host, path, query, params };
|
|
972
|
+
|
|
973
|
+
/** @type {Array<import('./types').BranchNode | undefined>} */
|
|
974
|
+
let branch = [];
|
|
398
975
|
|
|
399
|
-
|
|
400
|
-
|
|
976
|
+
/** @type {Record<string, any>} */
|
|
977
|
+
let stuff = {};
|
|
978
|
+
let stuff_changed = false;
|
|
979
|
+
|
|
980
|
+
/** @type {number | undefined} */
|
|
981
|
+
let status = 200;
|
|
982
|
+
|
|
983
|
+
/** @type {Error | undefined} */
|
|
984
|
+
let error;
|
|
985
|
+
|
|
986
|
+
// preload modules
|
|
987
|
+
a.forEach((loader) => loader());
|
|
988
|
+
|
|
989
|
+
load: for (let i = 0; i < a.length; i += 1) {
|
|
990
|
+
/** @type {import('./types').BranchNode | undefined} */
|
|
991
|
+
let node;
|
|
992
|
+
|
|
993
|
+
try {
|
|
994
|
+
if (!a[i]) continue;
|
|
995
|
+
|
|
996
|
+
const module = await a[i]();
|
|
997
|
+
const previous = this.current.branch[i];
|
|
401
998
|
|
|
402
999
|
const changed_since_last_render =
|
|
403
1000
|
!previous ||
|
|
404
|
-
|
|
1001
|
+
module !== previous.module ||
|
|
1002
|
+
(changed.path && previous.uses.path) ||
|
|
405
1003
|
changed.params.some((param) => previous.uses.params.has(param)) ||
|
|
406
1004
|
(changed.query && previous.uses.query) ||
|
|
407
1005
|
(changed.session && previous.uses.session) ||
|
|
408
|
-
(
|
|
1006
|
+
previous.uses.dependencies.some((dep) => this.invalid.has(dep)) ||
|
|
1007
|
+
(stuff_changed && previous.uses.stuff);
|
|
409
1008
|
|
|
410
1009
|
if (changed_since_last_render) {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
node = {
|
|
424
|
-
component,
|
|
425
|
-
uses: {
|
|
426
|
-
params: new Set(),
|
|
427
|
-
query: false,
|
|
428
|
-
session: false,
|
|
429
|
-
context: false
|
|
430
|
-
}
|
|
431
|
-
};
|
|
432
|
-
|
|
433
|
-
const params = {};
|
|
434
|
-
for (const key in page.params) {
|
|
435
|
-
Object.defineProperty(params, key, {
|
|
436
|
-
get() {
|
|
437
|
-
node.uses.params.add(key);
|
|
438
|
-
return page.params[key];
|
|
439
|
-
},
|
|
440
|
-
enumerable: true
|
|
441
|
-
});
|
|
1010
|
+
node = await this._load_node({
|
|
1011
|
+
module,
|
|
1012
|
+
page,
|
|
1013
|
+
stuff
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
const is_leaf = i === a.length - 1;
|
|
1017
|
+
|
|
1018
|
+
if (node && node.loaded) {
|
|
1019
|
+
if (node.loaded.error) {
|
|
1020
|
+
status = node.loaded.status;
|
|
1021
|
+
error = node.loaded.error;
|
|
442
1022
|
}
|
|
443
1023
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
...page,
|
|
451
|
-
params,
|
|
452
|
-
get query() {
|
|
453
|
-
node.uses.query = true;
|
|
454
|
-
return page.query;
|
|
455
|
-
}
|
|
456
|
-
},
|
|
457
|
-
get session() {
|
|
458
|
-
node.uses.session = true;
|
|
459
|
-
return session;
|
|
460
|
-
},
|
|
461
|
-
get context() {
|
|
462
|
-
node.uses.context = true;
|
|
463
|
-
return { ...context };
|
|
464
|
-
},
|
|
465
|
-
fetch: fetcher
|
|
466
|
-
}));
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
if (loaded) {
|
|
470
|
-
if (loaded.error) {
|
|
471
|
-
const error = new Error(loaded.error.message);
|
|
472
|
-
error.status = loaded.error.status;
|
|
473
|
-
throw error;
|
|
1024
|
+
if (node.loaded.redirect) {
|
|
1025
|
+
return {
|
|
1026
|
+
redirect: node.loaded.redirect,
|
|
1027
|
+
props: {},
|
|
1028
|
+
state: this.current
|
|
1029
|
+
};
|
|
474
1030
|
}
|
|
475
1031
|
|
|
476
|
-
if (loaded.
|
|
477
|
-
|
|
478
|
-
break;
|
|
1032
|
+
if (node.loaded.stuff) {
|
|
1033
|
+
stuff_changed = true;
|
|
479
1034
|
}
|
|
1035
|
+
} else if (is_leaf && module.load) {
|
|
1036
|
+
// if the leaf node has a `load` function
|
|
1037
|
+
// that returns nothing, fall through
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
} else {
|
|
1041
|
+
node = previous;
|
|
1042
|
+
}
|
|
1043
|
+
} catch (e) {
|
|
1044
|
+
status = 500;
|
|
1045
|
+
error = coalesce_to_error(e);
|
|
1046
|
+
}
|
|
480
1047
|
|
|
481
|
-
|
|
482
|
-
|
|
1048
|
+
if (error) {
|
|
1049
|
+
while (i--) {
|
|
1050
|
+
if (b[i]) {
|
|
1051
|
+
let error_loaded;
|
|
483
1052
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
1053
|
+
/** @type {import('./types').BranchNode | undefined} */
|
|
1054
|
+
let node_loaded;
|
|
1055
|
+
let j = i;
|
|
1056
|
+
while (!(node_loaded = branch[j])) {
|
|
1057
|
+
j -= 1;
|
|
488
1058
|
}
|
|
489
1059
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
cache.set(hash, cached);
|
|
499
|
-
|
|
500
|
-
let ready = false;
|
|
501
|
-
|
|
502
|
-
const timeout = setTimeout(() => {
|
|
503
|
-
clear();
|
|
504
|
-
}, loaded.maxage * 1000);
|
|
505
|
-
|
|
506
|
-
const clear = () => {
|
|
507
|
-
if (cache.get(hash) === cached) {
|
|
508
|
-
cache.delete(hash);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
unsubscribe();
|
|
512
|
-
clearTimeout(timeout);
|
|
513
|
-
};
|
|
514
|
-
|
|
515
|
-
const unsubscribe = this.stores.session.subscribe(() => {
|
|
516
|
-
if (ready) clear();
|
|
1060
|
+
try {
|
|
1061
|
+
error_loaded = await this._load_node({
|
|
1062
|
+
status,
|
|
1063
|
+
error,
|
|
1064
|
+
module: await b[i](),
|
|
1065
|
+
page,
|
|
1066
|
+
stuff: node_loaded.stuff
|
|
517
1067
|
});
|
|
518
1068
|
|
|
519
|
-
|
|
520
|
-
|
|
1069
|
+
if (error_loaded && error_loaded.loaded && error_loaded.loaded.error) {
|
|
1070
|
+
continue;
|
|
1071
|
+
}
|
|
521
1072
|
|
|
522
|
-
|
|
1073
|
+
branch = branch.slice(0, j + 1).concat(error_loaded);
|
|
1074
|
+
break load;
|
|
1075
|
+
} catch (e) {
|
|
1076
|
+
continue;
|
|
1077
|
+
}
|
|
523
1078
|
}
|
|
524
|
-
|
|
525
|
-
state.nodes[i] = node;
|
|
526
|
-
} else {
|
|
527
|
-
state.nodes[i] = previous;
|
|
528
1079
|
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
const new_props = await Promise.all(props_promises);
|
|
532
1080
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
1081
|
+
return await this._load_error({
|
|
1082
|
+
status,
|
|
1083
|
+
error,
|
|
1084
|
+
path,
|
|
1085
|
+
query
|
|
1086
|
+
});
|
|
1087
|
+
} else {
|
|
1088
|
+
if (node && node.loaded && node.loaded.stuff) {
|
|
1089
|
+
stuff = {
|
|
1090
|
+
...stuff,
|
|
1091
|
+
...node.loaded.stuff
|
|
1092
|
+
};
|
|
536
1093
|
}
|
|
537
|
-
});
|
|
538
1094
|
|
|
539
|
-
|
|
540
|
-
props.page = page;
|
|
1095
|
+
branch.push(node);
|
|
541
1096
|
}
|
|
542
|
-
} catch (error) {
|
|
543
|
-
props.error = error;
|
|
544
|
-
props.status = 500;
|
|
545
|
-
state.nodes = [];
|
|
546
1097
|
}
|
|
547
1098
|
|
|
548
|
-
return {
|
|
1099
|
+
return await this._get_navigation_result_from_branch({ page, branch });
|
|
549
1100
|
}
|
|
550
1101
|
|
|
551
|
-
|
|
552
|
-
|
|
1102
|
+
/**
|
|
1103
|
+
* @param {{
|
|
1104
|
+
* status?: number;
|
|
1105
|
+
* error: Error;
|
|
1106
|
+
* path: string;
|
|
1107
|
+
* query: URLSearchParams
|
|
1108
|
+
* }} opts
|
|
1109
|
+
*/
|
|
1110
|
+
async _load_error({ status, error, path, query }) {
|
|
1111
|
+
const page = {
|
|
1112
|
+
host: this.host,
|
|
1113
|
+
path,
|
|
1114
|
+
query,
|
|
1115
|
+
params: {}
|
|
1116
|
+
};
|
|
553
1117
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
}
|
|
1118
|
+
const node = await this._load_node({
|
|
1119
|
+
module: await this.fallback[0],
|
|
1120
|
+
page,
|
|
1121
|
+
stuff: {}
|
|
1122
|
+
});
|
|
558
1123
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
1124
|
+
const branch = [
|
|
1125
|
+
node,
|
|
1126
|
+
await this._load_node({
|
|
1127
|
+
status,
|
|
1128
|
+
error,
|
|
1129
|
+
module: await this.fallback[1],
|
|
1130
|
+
page,
|
|
1131
|
+
stuff: (node && node.loaded && node.loaded.stuff) || {}
|
|
1132
|
+
})
|
|
1133
|
+
];
|
|
1134
|
+
|
|
1135
|
+
return await this._get_navigation_result_from_branch({ page, branch });
|
|
563
1136
|
}
|
|
564
1137
|
}
|
|
565
1138
|
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
1139
|
+
// @ts-expect-error - doesn't exist yet. generated by Rollup
|
|
1140
|
+
|
|
1141
|
+
/**
|
|
1142
|
+
* @param {{
|
|
1143
|
+
* paths: {
|
|
1144
|
+
* assets: string;
|
|
1145
|
+
* base: string;
|
|
1146
|
+
* },
|
|
1147
|
+
* target: Node;
|
|
1148
|
+
* session: any;
|
|
1149
|
+
* host: string;
|
|
1150
|
+
* route: boolean;
|
|
1151
|
+
* spa: boolean;
|
|
1152
|
+
* trailing_slash: import('types/internal').TrailingSlash;
|
|
1153
|
+
* hydrate: {
|
|
1154
|
+
* status: number;
|
|
1155
|
+
* error: Error;
|
|
1156
|
+
* nodes: Array<Promise<import('types/internal').CSRComponent>>;
|
|
1157
|
+
* page: import('types/page').Page;
|
|
1158
|
+
* };
|
|
1159
|
+
* }} opts
|
|
1160
|
+
*/
|
|
1161
|
+
async function start({ paths, target, session, host, route, spa, trailing_slash, hydrate }) {
|
|
1162
|
+
if (import.meta.env.DEV && !target) {
|
|
1163
|
+
throw new Error('Missing target element. See https://kit.svelte.dev/docs#configuration-target');
|
|
1164
|
+
}
|
|
573
1165
|
|
|
574
1166
|
const renderer = new Renderer({
|
|
575
1167
|
Root,
|
|
576
|
-
|
|
1168
|
+
fallback,
|
|
577
1169
|
target,
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
status,
|
|
581
|
-
session
|
|
1170
|
+
session,
|
|
1171
|
+
host
|
|
582
1172
|
});
|
|
583
1173
|
|
|
584
|
-
|
|
1174
|
+
const router = route
|
|
1175
|
+
? new Router({
|
|
1176
|
+
base: paths.base,
|
|
1177
|
+
routes,
|
|
1178
|
+
trailing_slash,
|
|
1179
|
+
renderer
|
|
1180
|
+
})
|
|
1181
|
+
: null;
|
|
1182
|
+
|
|
1183
|
+
init(router);
|
|
585
1184
|
set_paths(paths);
|
|
586
1185
|
|
|
587
|
-
await
|
|
1186
|
+
if (hydrate) await renderer.start(hydrate);
|
|
1187
|
+
if (router) {
|
|
1188
|
+
if (spa) router.goto(location.href, { replaceState: true }, []);
|
|
1189
|
+
router.init_listeners();
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
dispatchEvent(new CustomEvent('sveltekit:start'));
|
|
588
1193
|
}
|
|
589
1194
|
|
|
590
1195
|
export { start };
|
|
591
|
-
//# sourceMappingURL=start.js.map
|