Skip to content

Commit fefc761

Browse files
committed
Merge branch 'main' into pr/astralhpi/13427
2 parents 68a214a + 370e9f9 commit fefc761

File tree

22 files changed

+155
-44
lines changed

22 files changed

+155
-44
lines changed

.changeset/angry-ravens-eat.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': patch
3+
---
4+
5+
fix: only call `afterNavigate` once on app start when SSR is disabled

packages/adapter-cloudflare/CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# @sveltejs/adapter-cloudflare
22

3+
## 6.0.1
4+
### Patch Changes
5+
6+
7+
- fix: revert writing server files to the cloudflare build directory ([#13622](https://github.com/sveltejs/kit/pull/13622))
8+
39
## 6.0.0
410
### Major Changes
511

packages/adapter-cloudflare/index.js

+6
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ export default function (options = {}) {
6262
`export const prerendered = new Set(${JSON.stringify(builder.prerendered.paths)});\n\n` +
6363
`export const base_path = ${JSON.stringify(builder.config.kit.paths.base)};\n`
6464
);
65+
builder.copy(`${files}/worker.js`, `${dest}/_worker.js`, {
66+
replace: {
67+
SERVER: `${relative_path}/index.js`,
68+
MANIFEST: `${path.posix.relative(dest, tmp)}/manifest.js`
69+
}
70+
});
6571

6672

6773
// _headers

packages/adapter-cloudflare/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sveltejs/adapter-cloudflare",
3-
"version": "6.0.0",
3+
"version": "6.0.1",
44
"description": "Adapter for building SvelteKit applications on Cloudflare Pages with Workers integration",
55
"keywords": [
66
"adapter",

packages/kit/CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# @sveltejs/kit
22

3+
## 2.20.2
4+
### Patch Changes
5+
6+
7+
- fix: allow non-prerendered API endpoint calls during reroute when prerendering ([#13616](https://github.com/sveltejs/kit/pull/13616))
8+
39
## 2.20.1
410
### Patch Changes
511

packages/kit/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sveltejs/kit",
3-
"version": "2.20.1",
3+
"version": "2.20.2",
44
"description": "SvelteKit is the fastest way to build Svelte apps",
55
"keywords": [
66
"framework",

packages/kit/src/exports/public.d.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1025,7 +1025,7 @@ export interface NavigationTarget {
10251025
}
10261026

10271027
/**
1028-
* - `enter`: The app has hydrated
1028+
* - `enter`: The app has hydrated/started
10291029
* - `form`: The user submitted a `<form>` with a GET method
10301030
* - `leave`: The user is leaving the app by closing the tab or using the back/forward buttons to go to a different document
10311031
* - `link`: Navigation was triggered by a link click
@@ -1101,7 +1101,7 @@ export interface OnNavigate extends Navigation {
11011101
export interface AfterNavigate extends Omit<Navigation, 'type'> {
11021102
/**
11031103
* The type of navigation:
1104-
* - `enter`: The app has hydrated
1104+
* - `enter`: The app has hydrated/started
11051105
* - `form`: The user submitted a `<form>`
11061106
* - `link`: Navigation was triggered by a link click
11071107
* - `goto`: Navigation was triggered by a `goto(...)` call or a redirect

packages/kit/src/runtime/client/client.js

+28-20
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,10 @@ export async function start(_app, _target, hydrate) {
320320
if (hydrate) {
321321
await _hydrate(target, hydrate);
322322
} else {
323-
await goto(app.hash ? decode_hash(new URL(location.href)) : location.href, {
324-
replaceState: true
323+
await navigate({
324+
type: 'enter',
325+
url: resolve_url(app.hash ? decode_hash(new URL(location.href)) : location.href),
326+
replace_state: true
325327
});
326328
}
327329

@@ -479,20 +481,22 @@ function initialize(result, target, hydrate) {
479481

480482
restore_snapshot(current_navigation_index);
481483

482-
/** @type {import('@sveltejs/kit').AfterNavigate} */
483-
const navigation = {
484-
from: null,
485-
to: {
486-
params: current.params,
487-
route: { id: current.route?.id ?? null },
488-
url: new URL(location.href)
489-
},
490-
willUnload: false,
491-
type: 'enter',
492-
complete: Promise.resolve()
493-
};
484+
if (hydrate) {
485+
/** @type {import('@sveltejs/kit').AfterNavigate} */
486+
const navigation = {
487+
from: null,
488+
to: {
489+
params: current.params,
490+
route: { id: current.route?.id ?? null },
491+
url: new URL(location.href)
492+
},
493+
willUnload: false,
494+
type: 'enter',
495+
complete: Promise.resolve()
496+
};
494497

495-
after_navigate_callbacks.forEach((fn) => fn(navigation));
498+
after_navigate_callbacks.forEach((fn) => fn(navigation));
499+
}
496500

497501
started = true;
498502
}
@@ -1373,7 +1377,7 @@ function _before_navigate({ url, type, intent, delta }) {
13731377

13741378
/**
13751379
* @param {{
1376-
* type: import('@sveltejs/kit').Navigation["type"];
1380+
* type: import('@sveltejs/kit').NavigationType;
13771381
* url: URL;
13781382
* popped?: {
13791383
* state: Record<string, any>;
@@ -1407,7 +1411,10 @@ async function navigate({
14071411
token = nav_token;
14081412

14091413
const intent = await get_navigation_intent(url, false);
1410-
const nav = _before_navigate({ url, type, delta: popped?.delta, intent });
1414+
const nav =
1415+
type === 'enter'
1416+
? create_navigation(current, intent, url, type)
1417+
: _before_navigate({ url, type, delta: popped?.delta, intent });
14111418

14121419
if (!nav) {
14131420
block();
@@ -1423,7 +1430,7 @@ async function navigate({
14231430

14241431
is_navigating = true;
14251432

1426-
if (started) {
1433+
if (started && nav.navigation.type !== 'enter') {
14271434
stores.navigating.set((navigating.current = nav.navigation));
14281435
}
14291436

@@ -2847,10 +2854,11 @@ function reset_focus() {
28472854
}
28482855

28492856
/**
2857+
* @template {import('@sveltejs/kit').NavigationType} T
28502858
* @param {import('./types.js').NavigationState} current
28512859
* @param {import('./types.js').NavigationIntent | undefined} intent
28522860
* @param {URL | null} url
2853-
* @param {Exclude<import('@sveltejs/kit').NavigationType, 'enter'>} type
2861+
* @param {T} type
28542862
*/
28552863
function create_navigation(current, intent, url, type) {
28562864
/** @type {(value: any) => void} */
@@ -2867,7 +2875,7 @@ function create_navigation(current, intent, url, type) {
28672875
// Handle any errors off-chain so that it doesn't show up as an unhandled rejection
28682876
complete.catch(() => {});
28692877

2870-
/** @type {import('@sveltejs/kit').Navigation} */
2878+
/** @type {Omit<import('@sveltejs/kit').Navigation, 'type'> & { type: T }} */
28712879
const navigation = {
28722880
from: {
28732881
params: current.params,

packages/kit/src/runtime/server/endpoint.js

+20-7
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export async function render_endpoint(event, mod, state) {
2929
throw new Error('Cannot prerender endpoints that have mutative methods');
3030
}
3131

32-
if (state.prerendering && !prerender) {
32+
if (state.prerendering && !state.prerendering.inside_reroute && !prerender) {
3333
if (state.depth > 0) {
3434
// if request came from a prerendered page, bail
3535
throw new Error(`${event.route.id} is not prerenderable`);
@@ -41,7 +41,7 @@ export async function render_endpoint(event, mod, state) {
4141
}
4242

4343
try {
44-
let response = await with_event(event, () =>
44+
const response = await with_event(event, () =>
4545
handler(/** @type {import('@sveltejs/kit').RequestEvent<Record<string, any>>} */ (event))
4646
);
4747

@@ -51,15 +51,28 @@ export async function render_endpoint(event, mod, state) {
5151
);
5252
}
5353

54-
if (state.prerendering) {
55-
// the returned Response might have immutable Headers
56-
// so we should clone them before trying to mutate them
57-
response = new Response(response.body, {
54+
if (state.prerendering && (!state.prerendering.inside_reroute || prerender)) {
55+
// The returned Response might have immutable Headers
56+
// so we should clone them before trying to mutate them.
57+
// We also need to clone the response body since it may be read twice during prerendering
58+
const cloned = new Response(response.clone().body, {
5859
status: response.status,
5960
statusText: response.statusText,
6061
headers: new Headers(response.headers)
6162
});
62-
response.headers.set('x-sveltekit-prerender', String(prerender));
63+
cloned.headers.set('x-sveltekit-prerender', String(prerender));
64+
65+
if (state.prerendering.inside_reroute && prerender) {
66+
// Without this, the route wouldn't be recorded as prerendered,
67+
// because there's nothing after reroute that would do that.
68+
cloned.headers.set(
69+
'x-sveltekit-routeid',
70+
encodeURI(/** @type {string} */ (event.route.id))
71+
);
72+
state.prerendering.dependencies.set(event.url.pathname, { response: cloned, body: null });
73+
} else {
74+
return cloned;
75+
}
6376
}
6477

6578
return response;

packages/kit/src/runtime/server/respond.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -183,14 +183,21 @@ export async function respond(request, options, manifest, state) {
183183

184184
let resolved_path;
185185

186+
const prerendering_reroute_state = state.prerendering?.inside_reroute;
186187
try {
188+
// For the duration or a reroute, disable the prerendering state as reroute could call API endpoints
189+
// which would end up in the wrong logic path if not disabled.
190+
if (state.prerendering) state.prerendering.inside_reroute = true;
191+
187192
// reroute could alter the given URL, so we pass a copy
188193
resolved_path =
189194
(await options.hooks.reroute({ url: new URL(url), fetch: event.fetch })) ?? url.pathname;
190195
} catch {
191196
return text('Internal Server Error', {
192197
status: 500
193198
});
199+
} finally {
200+
if (state.prerendering) state.prerendering.inside_reroute = prerendering_reroute_state;
194201
}
195202

196203
try {
@@ -349,7 +356,9 @@ export async function respond(request, options, manifest, state) {
349356

350357
set_trailing_slash(trailing_slash);
351358

352-
if (state.prerendering && !state.prerendering.fallback) disable_search(url);
359+
if (state.prerendering && !state.prerendering.fallback && !state.prerendering.inside_reroute) {
360+
disable_search(url);
361+
}
353362

354363
const response = await with_event(event, () =>
355364
options.hooks.handle({

packages/kit/src/types/internal.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ export interface PrerenderOptions {
216216
cache?: string; // including this here is a bit of a hack, but it makes it easy to add <meta http-equiv>
217217
fallback?: boolean;
218218
dependencies: Map<string, PrerenderDependency>;
219+
/** True for the duration of a call to the `reroute` hook */
220+
inside_reroute?: boolean;
219221
}
220222

221223
export type RecursiveRequired<T> = {

packages/kit/src/version.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
// generated during release, do not modify
22

33
/** @type {string} */
4-
export const VERSION = '2.20.1';
4+
export const VERSION = '2.20.2';

packages/kit/test/apps/basics/src/hooks.js

+4
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ export const reroute = ({ url, fetch }) => {
3131
return fetch('/reroute/api').then((r) => r.text());
3232
}
3333

34+
if (url.pathname === '/reroute/async/c') {
35+
return fetch('/reroute/api/prerendered').then((r) => r.text());
36+
}
37+
3438
if (url.pathname === '/reroute/prerendered/to-destination') {
3539
return '/reroute/prerendered/destination';
3640
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<script>
2+
import { afterNavigate } from '$app/navigation';
3+
4+
let count = 0;
5+
let type = '';
6+
7+
afterNavigate((event) => {
8+
count += 1;
9+
type = event.type;
10+
});
11+
</script>
12+
13+
<p>{type.toString()} {count}</p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { text } from '@sveltejs/kit';
2+
3+
// This is inside a route with a route segment to ensure that the route is marked as prerendered
4+
// as part of reroute resolution even when no `entries` is given.
5+
export const prerender = true;
6+
7+
export function GET() {
8+
return text('/reroute/async/b');
9+
}
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
<a href="/reroute/async/a">Go to url that should be rewritten</a>
2+
<a href="/reroute/async/c"
3+
>Go to url that should be rewritten and its reroute api call prerendered</a
4+
>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// through this we test that the crawler during prerendering does not fail on
2+
// reroute calls that call non-prerendered endpoints.
3+
export const prerender = true;

packages/kit/test/apps/basics/svelte.config.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,13 @@ const config = {
2525
'/routing/prerendered/trailing-slash/never',
2626
'/routing/prerendered/trailing-slash/ignore'
2727
],
28-
handleHttpError: 'warn'
28+
handleHttpError: ({ path, message }) => {
29+
if (path.includes('/reroute/async')) {
30+
throw new Error('shouldnt error on ' + path);
31+
}
32+
33+
console.warn(message);
34+
}
2935
},
3036

3137
version: {

0 commit comments

Comments
 (0)