@sveltejs/kit 1.0.0-next.222 → 1.0.0-next.227
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/assets/kit.js +97 -84
- package/assets/runtime/app/navigation.js +17 -1
- package/assets/runtime/internal/start.js +167 -35
- package/dist/chunks/cert.js +882 -1983
- package/dist/chunks/index.js +5 -3
- package/dist/chunks/index2.js +1 -15
- package/dist/chunks/index3.js +40 -17
- package/dist/chunks/index5.js +193 -72
- package/dist/cli.js +19 -2
- package/dist/ssr.js +97 -84
- package/package.json +2 -2
- package/types/ambient-modules.d.ts +13 -0
- package/types/config.d.ts +5 -0
- package/types/helper.d.ts +1 -1
- package/types/internal.d.ts +6 -1
package/assets/kit.js
CHANGED
|
@@ -567,7 +567,8 @@ async function render_response({
|
|
|
567
567
|
}) {
|
|
568
568
|
const css = new Set(options.manifest._.entry.css);
|
|
569
569
|
const js = new Set(options.manifest._.entry.js);
|
|
570
|
-
|
|
570
|
+
/** @type {Map<string, string>} */
|
|
571
|
+
const styles = new Map();
|
|
571
572
|
|
|
572
573
|
/** @type {Array<{ url: string, body: string, json: string }>} */
|
|
573
574
|
const serialized_data = [];
|
|
@@ -585,7 +586,7 @@ async function render_response({
|
|
|
585
586
|
branch.forEach(({ node, loaded, fetched, uses_credentials }) => {
|
|
586
587
|
if (node.css) node.css.forEach((url) => css.add(url));
|
|
587
588
|
if (node.js) node.js.forEach((url) => js.add(url));
|
|
588
|
-
if (node.styles) node.styles.forEach((
|
|
589
|
+
if (node.styles) Object.entries(node.styles).forEach(([k, v]) => styles.set(k, v));
|
|
589
590
|
|
|
590
591
|
// TODO probably better if `fetched` wasn't populated unless `hydrate`
|
|
591
592
|
if (fetched && page_config.hydrate) serialized_data.push(...fetched);
|
|
@@ -646,94 +647,80 @@ async function render_response({
|
|
|
646
647
|
rendered = { head: '', html: '', css: { code: '', map: null } };
|
|
647
648
|
}
|
|
648
649
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
// TODO strip the AMP stuff out of the build if not relevant
|
|
653
|
-
const links = options.amp
|
|
654
|
-
? styles.size > 0 || rendered.css.code.length > 0
|
|
655
|
-
? `<style amp-custom>${Array.from(styles).concat(rendered.css.code).join('\n')}</style>`
|
|
656
|
-
: ''
|
|
657
|
-
: [
|
|
658
|
-
// From https://web.dev/priority-hints/:
|
|
659
|
-
// Generally, preloads will load in the order the parser gets to them for anything above "Medium" priority
|
|
660
|
-
// Thus, we should list CSS first
|
|
661
|
-
...Array.from(css).map((dep) => `<link rel="stylesheet" href="${options.prefix}${dep}">`),
|
|
662
|
-
...Array.from(js).map((dep) => `<link rel="modulepreload" href="${options.prefix}${dep}">`)
|
|
663
|
-
].join('\n\t\t');
|
|
664
|
-
|
|
665
|
-
/** @type {string} */
|
|
666
|
-
let init = '';
|
|
650
|
+
let { head, html: body } = rendered;
|
|
651
|
+
|
|
652
|
+
const inlined_style = Array.from(styles.values()).join('\n');
|
|
667
653
|
|
|
668
654
|
if (options.amp) {
|
|
669
|
-
|
|
655
|
+
head += `
|
|
670
656
|
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style>
|
|
671
657
|
<noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
|
|
672
|
-
<script async src="https://cdn.ampproject.org/v0.js"></script
|
|
673
|
-
init += options.service_worker
|
|
674
|
-
? '<script async custom-element="amp-install-serviceworker" src="https://cdn.ampproject.org/v0/amp-install-serviceworker-0.1.js"></script>'
|
|
675
|
-
: '';
|
|
676
|
-
} else if (include_js) {
|
|
677
|
-
// prettier-ignore
|
|
678
|
-
init = `<script type="module">
|
|
679
|
-
import { start } from ${s(options.prefix + options.manifest._.entry.file)};
|
|
680
|
-
start({
|
|
681
|
-
target: ${options.target ? `document.querySelector(${s(options.target)})` : 'document.body'},
|
|
682
|
-
paths: ${s(options.paths)},
|
|
683
|
-
session: ${try_serialize($session, (error) => {
|
|
684
|
-
throw new Error(`Failed to serialize session data: ${error.message}`);
|
|
685
|
-
})},
|
|
686
|
-
route: ${!!page_config.router},
|
|
687
|
-
spa: ${!ssr},
|
|
688
|
-
trailing_slash: ${s(options.trailing_slash)},
|
|
689
|
-
hydrate: ${ssr && page_config.hydrate ? `{
|
|
690
|
-
status: ${status},
|
|
691
|
-
error: ${serialize_error(error)},
|
|
692
|
-
nodes: [
|
|
693
|
-
${(branch || [])
|
|
694
|
-
.map(({ node }) => `import(${s(options.prefix + node.entry)})`)
|
|
695
|
-
.join(',\n\t\t\t\t\t\t')}
|
|
696
|
-
],
|
|
697
|
-
url: new URL(${s(url.href)}),
|
|
698
|
-
params: ${devalue(params)}
|
|
699
|
-
}` : 'null'}
|
|
700
|
-
});
|
|
701
|
-
</script>`;
|
|
702
|
-
}
|
|
703
|
-
|
|
704
|
-
if (options.service_worker && !options.amp) {
|
|
705
|
-
init += `<script>
|
|
706
|
-
if ('serviceWorker' in navigator) {
|
|
707
|
-
navigator.serviceWorker.register('${options.service_worker}');
|
|
708
|
-
}
|
|
709
|
-
</script>`;
|
|
710
|
-
}
|
|
658
|
+
<script async src="https://cdn.ampproject.org/v0.js"></script>
|
|
711
659
|
|
|
712
|
-
|
|
713
|
-
rendered.head,
|
|
714
|
-
styles.size && !options.amp
|
|
715
|
-
? `<style data-svelte>${Array.from(styles).join('\n')}</style>`
|
|
716
|
-
: '',
|
|
717
|
-
links,
|
|
718
|
-
init
|
|
719
|
-
].join('\n\n\t\t');
|
|
660
|
+
<style amp-custom>${inlined_style}\n${rendered.css.code}</style>`;
|
|
720
661
|
|
|
721
|
-
let body = rendered.html;
|
|
722
|
-
if (options.amp) {
|
|
723
662
|
if (options.service_worker) {
|
|
663
|
+
head +=
|
|
664
|
+
'<script async custom-element="amp-install-serviceworker" src="https://cdn.ampproject.org/v0/amp-install-serviceworker-0.1.js"></script>';
|
|
665
|
+
|
|
724
666
|
body += `<amp-install-serviceworker src="${options.service_worker}" layout="nodisplay"></amp-install-serviceworker>`;
|
|
725
667
|
}
|
|
726
668
|
} else {
|
|
727
|
-
|
|
728
|
-
.
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
.
|
|
669
|
+
if (inlined_style) {
|
|
670
|
+
head += `\n\t<style${options.dev ? ' data-svelte' : ''}>${inlined_style}</style>`;
|
|
671
|
+
}
|
|
672
|
+
// prettier-ignore
|
|
673
|
+
head += Array.from(css)
|
|
674
|
+
.map((dep) => `\n\t<link${styles.has(dep) ? ' disabled' : ''} rel="stylesheet" href="${options.prefix + dep}">`)
|
|
675
|
+
.join('');
|
|
676
|
+
|
|
677
|
+
if (page_config.router || page_config.hydrate) {
|
|
678
|
+
head += Array.from(js)
|
|
679
|
+
.map((dep) => `\n\t<link rel="modulepreload" href="${options.prefix + dep}">`)
|
|
680
|
+
.join('');
|
|
681
|
+
// prettier-ignore
|
|
682
|
+
head += `
|
|
683
|
+
<script type="module">
|
|
684
|
+
import { start } from ${s(options.prefix + options.manifest._.entry.file)};
|
|
685
|
+
start({
|
|
686
|
+
target: ${options.target ? `document.querySelector(${s(options.target)})` : 'document.body'},
|
|
687
|
+
paths: ${s(options.paths)},
|
|
688
|
+
session: ${try_serialize($session, (error) => {
|
|
689
|
+
throw new Error(`Failed to serialize session data: ${error.message}`);
|
|
690
|
+
})},
|
|
691
|
+
route: ${!!page_config.router},
|
|
692
|
+
spa: ${!ssr},
|
|
693
|
+
trailing_slash: ${s(options.trailing_slash)},
|
|
694
|
+
hydrate: ${ssr && page_config.hydrate ? `{
|
|
695
|
+
status: ${status},
|
|
696
|
+
error: ${serialize_error(error)},
|
|
697
|
+
nodes: [
|
|
698
|
+
${(branch || [])
|
|
699
|
+
.map(({ node }) => `import(${s(options.prefix + node.entry)})`)
|
|
700
|
+
.join(',\n\t\t\t\t\t\t')}
|
|
701
|
+
],
|
|
702
|
+
url: new URL(${s(url.href)}),
|
|
703
|
+
params: ${devalue(params)}
|
|
704
|
+
}` : 'null'}
|
|
705
|
+
});
|
|
706
|
+
</script>${options.service_worker ? `
|
|
707
|
+
<script>
|
|
708
|
+
if ('serviceWorker' in navigator) {
|
|
709
|
+
navigator.serviceWorker.register('${options.service_worker}');
|
|
710
|
+
}
|
|
711
|
+
</script>` : ''}`;
|
|
712
|
+
|
|
713
|
+
body += serialized_data
|
|
714
|
+
.map(({ url, body, json }) => {
|
|
715
|
+
let attributes = `type="application/json" data-type="svelte-data" data-url=${escape_html_attr(
|
|
716
|
+
url
|
|
717
|
+
)}`;
|
|
718
|
+
if (body) attributes += ` data-body="${hash(body)}"`;
|
|
719
|
+
|
|
720
|
+
return `<script ${attributes}>${json}</script>`;
|
|
721
|
+
})
|
|
722
|
+
.join('\n\n\t');
|
|
723
|
+
}
|
|
737
724
|
}
|
|
738
725
|
|
|
739
726
|
/** @type {import('types/helper').ResponseHeaders} */
|
|
@@ -1641,8 +1628,9 @@ function read_only_form_data() {
|
|
|
1641
1628
|
* @param {string} value
|
|
1642
1629
|
*/
|
|
1643
1630
|
append(key, value) {
|
|
1644
|
-
|
|
1645
|
-
|
|
1631
|
+
const existing_values = map.get(key);
|
|
1632
|
+
if (existing_values) {
|
|
1633
|
+
existing_values.push(value);
|
|
1646
1634
|
} else {
|
|
1647
1635
|
map.set(key, [value]);
|
|
1648
1636
|
}
|
|
@@ -1664,12 +1652,15 @@ class ReadOnlyFormData {
|
|
|
1664
1652
|
/** @param {string} key */
|
|
1665
1653
|
get(key) {
|
|
1666
1654
|
const value = this.#map.get(key);
|
|
1667
|
-
|
|
1655
|
+
if (!value) {
|
|
1656
|
+
return null;
|
|
1657
|
+
}
|
|
1658
|
+
return value[0];
|
|
1668
1659
|
}
|
|
1669
1660
|
|
|
1670
1661
|
/** @param {string} key */
|
|
1671
1662
|
getAll(key) {
|
|
1672
|
-
return this.#map.get(key);
|
|
1663
|
+
return this.#map.get(key) || [];
|
|
1673
1664
|
}
|
|
1674
1665
|
|
|
1675
1666
|
/** @param {string} key */
|
|
@@ -1849,6 +1840,28 @@ async function respond(incoming, options, state = {}) {
|
|
|
1849
1840
|
locals: {}
|
|
1850
1841
|
};
|
|
1851
1842
|
|
|
1843
|
+
const { parameter, allowed } = options.method_override;
|
|
1844
|
+
const method_override = incoming.url.searchParams.get(parameter)?.toUpperCase();
|
|
1845
|
+
|
|
1846
|
+
if (method_override) {
|
|
1847
|
+
if (request.method.toUpperCase() === 'POST') {
|
|
1848
|
+
if (allowed.includes(method_override)) {
|
|
1849
|
+
request.method = method_override;
|
|
1850
|
+
} else {
|
|
1851
|
+
const verb = allowed.length === 0 ? 'enabled' : 'allowed';
|
|
1852
|
+
const body = `${parameter}=${method_override} is not ${verb}. See https://kit.svelte.dev/docs#configuration-methodoverride`;
|
|
1853
|
+
|
|
1854
|
+
return {
|
|
1855
|
+
status: 400,
|
|
1856
|
+
headers: {},
|
|
1857
|
+
body
|
|
1858
|
+
};
|
|
1859
|
+
}
|
|
1860
|
+
} else {
|
|
1861
|
+
throw new Error(`${parameter}=${method_override} is only allowed with POST requests`);
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1852
1865
|
// TODO remove this for 1.0
|
|
1853
1866
|
/**
|
|
1854
1867
|
* @param {string} property
|
|
@@ -19,6 +19,8 @@ const goto = import.meta.env.SSR ? guard('goto') : goto_;
|
|
|
19
19
|
const invalidate = import.meta.env.SSR ? guard('invalidate') : invalidate_;
|
|
20
20
|
const prefetch = import.meta.env.SSR ? guard('prefetch') : prefetch_;
|
|
21
21
|
const prefetchRoutes = import.meta.env.SSR ? guard('prefetchRoutes') : prefetchRoutes_;
|
|
22
|
+
const beforeNavigate = import.meta.env.SSR ? () => {} : beforeNavigate_;
|
|
23
|
+
const afterNavigate = import.meta.env.SSR ? () => {} : afterNavigate_;
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* @type {import('$app/navigation').goto}
|
|
@@ -62,4 +64,18 @@ async function prefetchRoutes_(pathnames) {
|
|
|
62
64
|
await Promise.all(promises);
|
|
63
65
|
}
|
|
64
66
|
|
|
65
|
-
|
|
67
|
+
/**
|
|
68
|
+
* @type {import('$app/navigation').beforeNavigate}
|
|
69
|
+
*/
|
|
70
|
+
function beforeNavigate_(fn) {
|
|
71
|
+
if (router) router.before_navigate(fn);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @type {import('$app/navigation').afterNavigate}
|
|
76
|
+
*/
|
|
77
|
+
function afterNavigate_(fn) {
|
|
78
|
+
if (router) router.after_navigate(fn);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export { afterNavigate, beforeNavigate, disableScrollHandling, goto, invalidate, prefetch, prefetchRoutes };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Root from '../../generated/root.svelte';
|
|
2
2
|
import { fallback, routes } from '../../generated/manifest.js';
|
|
3
|
+
import { onMount, tick } from 'svelte';
|
|
3
4
|
import { g as get_base_uri } from '../chunks/utils.js';
|
|
4
|
-
import { tick } from 'svelte';
|
|
5
5
|
import { writable } from 'svelte/store';
|
|
6
6
|
import { init } from './singletons.js';
|
|
7
7
|
import { set_paths } from '../paths.js';
|
|
@@ -59,8 +59,21 @@ class Router {
|
|
|
59
59
|
// make it possible to reset focus
|
|
60
60
|
document.body.setAttribute('tabindex', '-1');
|
|
61
61
|
|
|
62
|
-
//
|
|
63
|
-
|
|
62
|
+
// keeping track of the history index in order to prevent popstate navigation events if needed
|
|
63
|
+
this.current_history_index = history.state?.['sveltekit:index'] ?? 0;
|
|
64
|
+
|
|
65
|
+
if (this.current_history_index === 0) {
|
|
66
|
+
// create initial history entry, so we can return here
|
|
67
|
+
history.replaceState({ ...history.state, 'sveltekit:index': 0 }, '', location.href);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.callbacks = {
|
|
71
|
+
/** @type {Array<({ from, to, cancel }: { from: URL, to: URL | null, cancel: () => void }) => void>} */
|
|
72
|
+
before_navigate: [],
|
|
73
|
+
|
|
74
|
+
/** @type {Array<({ from, to }: { from: URL | null, to: URL }) => void>} */
|
|
75
|
+
after_navigate: []
|
|
76
|
+
};
|
|
64
77
|
}
|
|
65
78
|
|
|
66
79
|
init_listeners() {
|
|
@@ -72,8 +85,23 @@ class Router {
|
|
|
72
85
|
// Reset scrollRestoration to auto when leaving page, allowing page reload
|
|
73
86
|
// and back-navigation from other pages to use the browser to restore the
|
|
74
87
|
// scrolling position.
|
|
75
|
-
addEventListener('beforeunload', () => {
|
|
76
|
-
|
|
88
|
+
addEventListener('beforeunload', (e) => {
|
|
89
|
+
let should_block = false;
|
|
90
|
+
|
|
91
|
+
const intent = {
|
|
92
|
+
from: this.renderer.current.url,
|
|
93
|
+
to: null,
|
|
94
|
+
cancel: () => (should_block = true)
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
this.callbacks.before_navigate.forEach((fn) => fn(intent));
|
|
98
|
+
|
|
99
|
+
if (should_block) {
|
|
100
|
+
e.preventDefault();
|
|
101
|
+
e.returnValue = '';
|
|
102
|
+
} else {
|
|
103
|
+
history.scrollRestoration = 'auto';
|
|
104
|
+
}
|
|
77
105
|
});
|
|
78
106
|
|
|
79
107
|
// Setting scrollRestoration to manual again when returning to this page.
|
|
@@ -128,7 +156,7 @@ class Router {
|
|
|
128
156
|
addEventListener('sveltekit:trigger_prefetch', trigger_prefetch);
|
|
129
157
|
|
|
130
158
|
/** @param {MouseEvent} event */
|
|
131
|
-
addEventListener('click', (event) => {
|
|
159
|
+
addEventListener('click', async (event) => {
|
|
132
160
|
if (!this.enabled) return;
|
|
133
161
|
|
|
134
162
|
// Adapted from https://github.com/visionmedia/page.js
|
|
@@ -161,8 +189,6 @@ class Router {
|
|
|
161
189
|
// Ignore if <a> has a target
|
|
162
190
|
if (a instanceof SVGAElement ? a.target.baseVal : a.target) return;
|
|
163
191
|
|
|
164
|
-
if (!this.owns(url)) return;
|
|
165
|
-
|
|
166
192
|
// Check if new url only differs by hash
|
|
167
193
|
if (url.href.split('#')[0] === location.href.split('#')[0]) {
|
|
168
194
|
// Call `pushState` to add url to history so going back works.
|
|
@@ -175,22 +201,48 @@ class Router {
|
|
|
175
201
|
return;
|
|
176
202
|
}
|
|
177
203
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
204
|
+
this._navigate({
|
|
205
|
+
url,
|
|
206
|
+
scroll: a.hasAttribute('sveltekit:noscroll') ? scroll_state() : null,
|
|
207
|
+
keepfocus: false,
|
|
208
|
+
chain: [],
|
|
209
|
+
details: {
|
|
210
|
+
state: {},
|
|
211
|
+
replaceState: false
|
|
212
|
+
},
|
|
213
|
+
accepted: () => event.preventDefault(),
|
|
214
|
+
blocked: () => event.preventDefault()
|
|
215
|
+
});
|
|
183
216
|
});
|
|
184
217
|
|
|
185
218
|
addEventListener('popstate', (event) => {
|
|
186
219
|
if (event.state && this.enabled) {
|
|
187
|
-
|
|
188
|
-
|
|
220
|
+
// if a popstate-driven navigation is cancelled, we need to counteract it
|
|
221
|
+
// with history.go, which means we end up back here, hence this check
|
|
222
|
+
if (event.state['sveltekit:index'] === this.current_history_index) return;
|
|
223
|
+
|
|
224
|
+
this._navigate({
|
|
225
|
+
url: new URL(location.href),
|
|
226
|
+
scroll: event.state['sveltekit:scroll'],
|
|
227
|
+
keepfocus: false,
|
|
228
|
+
chain: [],
|
|
229
|
+
details: null,
|
|
230
|
+
accepted: () => {
|
|
231
|
+
this.current_history_index = event.state['sveltekit:index'];
|
|
232
|
+
},
|
|
233
|
+
blocked: () => {
|
|
234
|
+
const delta = this.current_history_index - event.state['sveltekit:index'];
|
|
235
|
+
history.go(delta);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
189
238
|
}
|
|
190
239
|
});
|
|
191
240
|
}
|
|
192
241
|
|
|
193
|
-
/**
|
|
242
|
+
/**
|
|
243
|
+
* Returns true if `url` has the same origin and basepath as the app
|
|
244
|
+
* @param {URL} url
|
|
245
|
+
*/
|
|
194
246
|
owns(url) {
|
|
195
247
|
return url.origin === location.origin && url.pathname.startsWith(this.base);
|
|
196
248
|
}
|
|
@@ -226,9 +278,19 @@ class Router {
|
|
|
226
278
|
) {
|
|
227
279
|
const url = new URL(href, get_base_uri(document));
|
|
228
280
|
|
|
229
|
-
if (this.enabled
|
|
230
|
-
|
|
231
|
-
|
|
281
|
+
if (this.enabled) {
|
|
282
|
+
return this._navigate({
|
|
283
|
+
url,
|
|
284
|
+
scroll: noscroll ? scroll_state() : null,
|
|
285
|
+
keepfocus,
|
|
286
|
+
chain,
|
|
287
|
+
details: {
|
|
288
|
+
state,
|
|
289
|
+
replaceState
|
|
290
|
+
},
|
|
291
|
+
accepted: () => {},
|
|
292
|
+
blocked: () => {}
|
|
293
|
+
});
|
|
232
294
|
}
|
|
233
295
|
|
|
234
296
|
location.href = url.href;
|
|
@@ -259,20 +321,73 @@ class Router {
|
|
|
259
321
|
return this.renderer.load(info);
|
|
260
322
|
}
|
|
261
323
|
|
|
324
|
+
/** @param {({ from, to }: { from: URL | null, to: URL }) => void} fn */
|
|
325
|
+
after_navigate(fn) {
|
|
326
|
+
onMount(() => {
|
|
327
|
+
this.callbacks.after_navigate.push(fn);
|
|
328
|
+
|
|
329
|
+
return () => {
|
|
330
|
+
const i = this.callbacks.after_navigate.indexOf(fn);
|
|
331
|
+
this.callbacks.after_navigate.splice(i, 1);
|
|
332
|
+
};
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
262
336
|
/**
|
|
263
|
-
* @param {URL}
|
|
264
|
-
* @param {{ x: number, y: number }?} scroll
|
|
265
|
-
* @param {boolean} keepfocus
|
|
266
|
-
* @param {string[]} chain
|
|
267
|
-
* @param {string} [hash]
|
|
337
|
+
* @param {({ from, to, cancel }: { from: URL, to: URL | null, cancel: () => void }) => void} fn
|
|
268
338
|
*/
|
|
269
|
-
|
|
270
|
-
|
|
339
|
+
before_navigate(fn) {
|
|
340
|
+
onMount(() => {
|
|
341
|
+
this.callbacks.before_navigate.push(fn);
|
|
342
|
+
|
|
343
|
+
return () => {
|
|
344
|
+
const i = this.callbacks.before_navigate.indexOf(fn);
|
|
345
|
+
this.callbacks.before_navigate.splice(i, 1);
|
|
346
|
+
};
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* @param {{
|
|
352
|
+
* url: URL;
|
|
353
|
+
* scroll: { x: number, y: number } | null;
|
|
354
|
+
* keepfocus: boolean;
|
|
355
|
+
* chain: string[];
|
|
356
|
+
* details: {
|
|
357
|
+
* replaceState: boolean;
|
|
358
|
+
* state: any;
|
|
359
|
+
* } | null;
|
|
360
|
+
* accepted: () => void;
|
|
361
|
+
* blocked: () => void;
|
|
362
|
+
* }} opts
|
|
363
|
+
*/
|
|
364
|
+
async _navigate({ url, scroll, keepfocus, chain, details, accepted, blocked }) {
|
|
365
|
+
const from = this.renderer.current.url;
|
|
366
|
+
let should_block = false;
|
|
367
|
+
|
|
368
|
+
const intent = {
|
|
369
|
+
from,
|
|
370
|
+
to: url,
|
|
371
|
+
cancel: () => (should_block = true)
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
this.callbacks.before_navigate.forEach((fn) => fn(intent));
|
|
271
375
|
|
|
376
|
+
if (should_block) {
|
|
377
|
+
blocked();
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const info = this.parse(url);
|
|
272
382
|
if (!info) {
|
|
273
|
-
|
|
383
|
+
location.href = url.href;
|
|
384
|
+
return new Promise(() => {
|
|
385
|
+
// never resolves
|
|
386
|
+
});
|
|
274
387
|
}
|
|
275
388
|
|
|
389
|
+
accepted();
|
|
390
|
+
|
|
276
391
|
if (!this.navigating) {
|
|
277
392
|
dispatchEvent(new CustomEvent('sveltekit:navigation-start'));
|
|
278
393
|
}
|
|
@@ -288,13 +403,24 @@ class Router {
|
|
|
288
403
|
}
|
|
289
404
|
|
|
290
405
|
info.url = new URL(url.origin + pathname + url.search + url.hash);
|
|
291
|
-
history.replaceState({}, '', info.url);
|
|
292
406
|
|
|
293
|
-
|
|
407
|
+
if (details) {
|
|
408
|
+
const change = details.replaceState ? 0 : 1;
|
|
409
|
+
details.state['sveltekit:index'] = this.current_history_index += change;
|
|
410
|
+
history[details.replaceState ? 'replaceState' : 'pushState'](details.state, '', info.url);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
await this.renderer.handle_navigation(info, chain, false, {
|
|
414
|
+
scroll,
|
|
415
|
+
keepfocus
|
|
416
|
+
});
|
|
294
417
|
|
|
295
418
|
this.navigating--;
|
|
296
419
|
if (!this.navigating) {
|
|
297
420
|
dispatchEvent(new CustomEvent('sveltekit:navigation-end'));
|
|
421
|
+
|
|
422
|
+
const navigation = { from, to: url };
|
|
423
|
+
this.callbacks.after_navigate.forEach((fn) => fn(navigation));
|
|
298
424
|
}
|
|
299
425
|
}
|
|
300
426
|
}
|
|
@@ -653,10 +779,11 @@ class Renderer {
|
|
|
653
779
|
});
|
|
654
780
|
} else {
|
|
655
781
|
if (this.router) {
|
|
656
|
-
this.router.goto(
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
782
|
+
this.router.goto(
|
|
783
|
+
new URL(navigation_result.redirect, info.url).href,
|
|
784
|
+
{ replaceState: true },
|
|
785
|
+
[...chain, info.url.pathname]
|
|
786
|
+
);
|
|
660
787
|
} else {
|
|
661
788
|
location.href = new URL(navigation_result.redirect, location.href).href;
|
|
662
789
|
}
|
|
@@ -678,7 +805,7 @@ class Renderer {
|
|
|
678
805
|
|
|
679
806
|
// opts must be passed if we're navigating
|
|
680
807
|
if (opts) {
|
|
681
|
-
const {
|
|
808
|
+
const { scroll, keepfocus } = opts;
|
|
682
809
|
|
|
683
810
|
if (!keepfocus) {
|
|
684
811
|
getSelection()?.removeAllRanges();
|
|
@@ -689,7 +816,7 @@ class Renderer {
|
|
|
689
816
|
await tick();
|
|
690
817
|
|
|
691
818
|
if (this.autoscroll) {
|
|
692
|
-
const deep_linked = hash && document.getElementById(hash.slice(1));
|
|
819
|
+
const deep_linked = info.url.hash && document.getElementById(info.url.hash.slice(1));
|
|
693
820
|
if (scroll) {
|
|
694
821
|
scrollTo(scroll.x, scroll.y);
|
|
695
822
|
} else if (deep_linked) {
|
|
@@ -765,6 +892,11 @@ class Renderer {
|
|
|
765
892
|
});
|
|
766
893
|
|
|
767
894
|
this.started = true;
|
|
895
|
+
|
|
896
|
+
if (this.router) {
|
|
897
|
+
const navigation = { from: null, to: new URL(location.href) };
|
|
898
|
+
this.router.callbacks.after_navigate.forEach((fn) => fn(navigation));
|
|
899
|
+
}
|
|
768
900
|
}
|
|
769
901
|
|
|
770
902
|
/**
|