diff --git a/.changeset/ten-rats-spend.md b/.changeset/ten-rats-spend.md new file mode 100644 index 000000000000..8e5a0b35b408 --- /dev/null +++ b/.changeset/ten-rats-spend.md @@ -0,0 +1,6 @@ +--- +'@sveltejs/kit': patch +--- + +fix: `afterNavigate` callback not running after hydration when experimental async is enabled +fix: Snapshot `restore` method not called after reload when experimental async is enabled diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index 768ee347ee4c..c33a2be38b14 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -545,7 +545,7 @@ async function _preload_code(url) { * @param {HTMLElement} target * @param {boolean} hydrate */ -function initialize(result, target, hydrate) { +async function initialize(result, target, hydrate) { if (DEV && result.state.error && document.querySelector('vite-error-overlay')) return; current = result.state; @@ -563,6 +563,10 @@ function initialize(result, target, hydrate) { sync: false }); + // Wait for a microtask in case svelte experimental async is enabled, + // which causes component script blocks to run asynchronously + void (await Promise.resolve()); + restore_snapshot(current_navigation_index); if (hydrate) { @@ -1696,7 +1700,7 @@ async function navigate({ update(navigation_result.props.page); has_navigated = true; } else { - initialize(navigation_result, target, false); + await initialize(navigation_result, target, false); } const { activeElement } = document; @@ -2799,7 +2803,7 @@ async function _hydrate( result.props.page.state = {}; } - initialize(result, target, hydrate); + await initialize(result, target, hydrate); } /** diff --git a/packages/kit/test/apps/async/package.json b/packages/kit/test/apps/async/package.json new file mode 100644 index 000000000000..4fc2f1636248 --- /dev/null +++ b/packages/kit/test/apps/async/package.json @@ -0,0 +1,24 @@ +{ + "name": "test-async", + "private": true, + "version": "0.0.1", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync", + "check": "svelte-kit sync && tsc && svelte-check", + "test": "pnpm test:dev && pnpm test:build", + "test:dev": "DEV=true playwright test", + "test:build": "playwright test" + }, + "devDependencies": { + "@sveltejs/kit": "workspace:^", + "@sveltejs/vite-plugin-svelte": "catalog:", + "svelte": "catalog:", + "svelte-check": "catalog:", + "typescript": "^5.5.4", + "vite": "catalog:" + }, + "type": "module" +} diff --git a/packages/kit/test/apps/async/playwright.config.js b/packages/kit/test/apps/async/playwright.config.js new file mode 100644 index 000000000000..33d36b651014 --- /dev/null +++ b/packages/kit/test/apps/async/playwright.config.js @@ -0,0 +1 @@ +export { config as default } from '../../utils.js'; diff --git a/packages/kit/test/apps/async/src/app.html b/packages/kit/test/apps/async/src/app.html new file mode 100644 index 000000000000..79d946ed86a3 --- /dev/null +++ b/packages/kit/test/apps/async/src/app.html @@ -0,0 +1,12 @@ + + +
+ + + + %sveltekit.head% + + +home
diff --git a/packages/kit/test/apps/async/src/routes/after_navigate/+page.svelte b/packages/kit/test/apps/async/src/routes/after_navigate/+page.svelte new file mode 100644 index 000000000000..2e7d06b97444 --- /dev/null +++ b/packages/kit/test/apps/async/src/routes/after_navigate/+page.svelte @@ -0,0 +1,10 @@ + + +{runs}
diff --git a/packages/kit/test/apps/async/src/routes/snapshot/+page.svelte b/packages/kit/test/apps/async/src/routes/snapshot/+page.svelte
new file mode 100644
index 000000000000..160079383b48
--- /dev/null
+++ b/packages/kit/test/apps/async/src/routes/snapshot/+page.svelte
@@ -0,0 +1,14 @@
+
+
+{JSON.stringify(restored)}
diff --git a/packages/kit/test/apps/async/static/favicon.png b/packages/kit/test/apps/async/static/favicon.png
new file mode 100644
index 000000000000..825b9e65af7c
Binary files /dev/null and b/packages/kit/test/apps/async/static/favicon.png differ
diff --git a/packages/kit/test/apps/async/svelte.config.js b/packages/kit/test/apps/async/svelte.config.js
new file mode 100644
index 000000000000..04b3a15e32bb
--- /dev/null
+++ b/packages/kit/test/apps/async/svelte.config.js
@@ -0,0 +1,11 @@
+/** @type {import('@sveltejs/kit').Config} */
+const config = {
+ kit: {},
+ compilerOptions: {
+ experimental: {
+ async: true
+ }
+ }
+};
+
+export default config;
diff --git a/packages/kit/test/apps/async/test/test.js b/packages/kit/test/apps/async/test/test.js
new file mode 100644
index 000000000000..3fce3a062373
--- /dev/null
+++ b/packages/kit/test/apps/async/test/test.js
@@ -0,0 +1,20 @@
+import { expect } from '@playwright/test';
+import { test } from '../../../utils.js';
+
+/** @typedef {import('@playwright/test').Response} Response */
+
+test.skip(({ javaScriptEnabled }) => !javaScriptEnabled);
+
+test.describe.configure({ mode: 'parallel' });
+
+test('afterNavigate runs after hydration', async ({ page }) => {
+ await page.goto('/after_navigate');
+
+ expect(await page.innerText('pre')).toBe('1');
+});
+
+test('snapshots are restored after reload', async ({ page }) => {
+ await page.goto('/snapshot');
+ await page.reload();
+ expect(await page.innerText('pre')).toBe('{"restored":"yes"}');
+});
diff --git a/packages/kit/test/apps/async/tsconfig.json b/packages/kit/test/apps/async/tsconfig.json
new file mode 100644
index 000000000000..1d665886266b
--- /dev/null
+++ b/packages/kit/test/apps/async/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "compilerOptions": {
+ "allowJs": true,
+ "checkJs": true,
+ "esModuleInterop": true,
+ "noEmit": true,
+ "resolveJsonModule": true
+ },
+ "extends": "./.svelte-kit/tsconfig.json"
+}
diff --git a/packages/kit/test/apps/async/vite.config.js b/packages/kit/test/apps/async/vite.config.js
new file mode 100644
index 000000000000..9640847b2704
--- /dev/null
+++ b/packages/kit/test/apps/async/vite.config.js
@@ -0,0 +1,21 @@
+import * as path from 'node:path';
+import { sveltekit } from '@sveltejs/kit/vite';
+
+/** @type {import('vite').UserConfig} */
+const config = {
+ build: {
+ minify: false
+ },
+ clearScreen: false,
+ plugins: [sveltekit()],
+ server: {
+ fs: {
+ allow: [path.resolve('../../../src')]
+ }
+ },
+ optimizeDeps: {
+ exclude: ['svelte']
+ }
+};
+
+export default config;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 74004538fd1d..deec0387ba63 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -618,6 +618,27 @@ importers:
specifier: 'catalog:'
version: 6.3.6(@types/node@18.19.119)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)
+ packages/kit/test/apps/async:
+ devDependencies:
+ '@sveltejs/kit':
+ specifier: workspace:^
+ version: link:../../..
+ '@sveltejs/vite-plugin-svelte':
+ specifier: 'catalog:'
+ version: 6.0.0-next.3(svelte@5.39.8)(vite@6.3.6(@types/node@18.19.119)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0))
+ svelte:
+ specifier: 'catalog:'
+ version: 5.39.8
+ svelte-check:
+ specifier: 'catalog:'
+ version: 4.1.1(picomatch@4.0.3)(svelte@5.39.8)(typescript@5.8.3)
+ typescript:
+ specifier: ^5.5.4
+ version: 5.8.3
+ vite:
+ specifier: 'catalog:'
+ version: 6.3.6(@types/node@18.19.119)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)
+
packages/kit/test/apps/basics:
devDependencies:
'@opentelemetry/api':