Repository: sveltejs/kit (js)

Issue:
Page contains data from a previous page if that had a server load function and new one doesnt
### Describe the bug

Just experimenting with the new layout groups and noticed something slightly odd / inconsistent.

Basically, a couple of layout groups, one with layout data, one without. Navigating between them shows some data lingers across the groups, but then disappears if you refresh the page. It does it in both dev and build mode. Probably no biggie but who knows ... if someone is iterating keys or something it might bite them?

https://user-images.githubusercontent.com/304910/186784633-00098258-cd9d-4193-870b-fd32b066d8d7.mov


### Reproduction

https://github.com/CaptainCodeman/sveltekit-routing-layouts

### Logs

_No response_

### System Info

```shell
System:
    OS: macOS 12.5.1
    CPU: (6) x64 Intel(R) Core(TM) i5-8500B CPU @ 3.00GHz
    Memory: 4.63 GB / 32.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 16.14.0 - /usr/local/bin/node
    npm: 8.3.1 - /usr/local/bin/npm
  Browsers:
    Brave Browser: 102.1.39.111
    Chrome: 104.0.5112.101
    Chrome Canary: 107.0.5261.0
    Firefox: 103.0.2
    Safari: 15.6.1
    Safari Technology Preview: 16.0
  npmPackages:
    @sveltejs/adapter-auto: next => 1.0.0-next.66 
    @sveltejs/adapter-node: 1.0.0-next.87 => 1.0.0-next.87 
    @sveltejs/kit: next => 1.0.0-next.442 
    svelte: ^3.44.0 => 3.49.0 
    vite: ^3.0.4 => 3.0.9
```


### Severity

annoyance

### Additional Information

_No response_

Pull request:
[fix] wrong reuse of server load data
Don't reuse server load data from previous page if current doesn't have a load function

Fixes #6300

### Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
- [x] It's really useful if your PR references an issue where it is discussed ahead of time. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
- [x] This message body should clearly illustrate what problems it solves.
- [x] Ideally, include a test that fails without this PR but passes with it.

### Tests
- [x] Run the tests with `pnpm test` and lint the project with `pnpm lint` and `pnpm check`

### Changesets
- [x] If your PR makes a change that should be noted in one or more packages' changelogs, generate a changeset by running `pnpm changeset` and following the prompts. All changesets should be `patch` until SvelteKit 1.0


Patch:
diff --git a/.changeset/neat-cameras-swim.md b/.changeset/neat-cameras-swim.md
new file mode 100644
index 000000000..010d28451
--- /dev/null
+++ b/.changeset/neat-cameras-swim.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[fix] prevent unused types warning
diff --git a/.changeset/rotten-mice-compete.md b/.changeset/rotten-mice-compete.md
new file mode 100644
index 000000000..1ab5aefa1
--- /dev/null
+++ b/.changeset/rotten-mice-compete.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+[fix] don't reuse server load data from previous page if current doesn't have a load function
diff --git a/packages/kit/src/core/sync/write_types/index.js b/packages/kit/src/core/sync/write_types/index.js
index 238e7ffd2..fe8574657 100644
--- a/packages/kit/src/core/sync/write_types/index.js
+++ b/packages/kit/src/core/sync/write_types/index.js
@@ -559,7 +559,13 @@ export function tweak_types(content, names) {
 										arg.name.end,
 										`: Parameters<${type}>[0]` + (add_parens ? ')' : '')
 									);
+								} else {
+									// prevent "type X is imported but not used" (isn't silenced by @ts-nocheck) when svelte-check runs
+									code.append(`;${type};`);
 								}
+							} else {
+								// prevent "type X is imported but not used" (isn't silenced by @ts-nocheck) when svelte-check runs
+								code.append(`;${type};`);
 							}
 
 							modified = true;
@@ -569,6 +575,16 @@ export function tweak_types(content, names) {
 			}
 		});
 
+		if (modified) {
+			// Ignore all type errors so they don't show up twice when svelte-check runs
+			// Account for possible @ts-check which would overwrite @ts-nocheck
+			if (code.original.startsWith('// @ts-check')) {
+				code.prependLeft('// @ts-check'.length, '\n// @ts-nocheck\n');
+			} else {
+				code.prepend('// @ts-nocheck\n');
+			}
+		}
+
 		return {
 			modified,
 			code: code.toString(),
diff --git a/packages/kit/src/core/sync/write_types/index.spec.js b/packages/kit/src/core/sync/write_types/index.spec.js
index 83d4221fb..51862ff97 100644
--- a/packages/kit/src/core/sync/write_types/index.spec.js
+++ b/packages/kit/src/core/sync/write_types/index.spec.js
@@ -1,3 +1,4 @@
+// @ts-nocheck
 import path from 'path';
 import fs from 'fs';
 import { format } from 'prettier';
@@ -101,7 +102,8 @@ test('Rewrites types for a TypeScript module', () => {
 	assert.equal(rewritten?.exports, ['GET']);
 	assert.equal(
 		rewritten?.code,
-		`
+		`// @ts-nocheck
+
 		export const GET = ({ params }: Parameters<Get>[0]) => {
 			return {
 				a: 1
@@ -125,13 +127,14 @@ test('Rewrites types for a TypeScript module without param', () => {
 	assert.equal(rewritten?.exports, ['GET']);
 	assert.equal(
 		rewritten?.code,
-		`
+		`// @ts-nocheck
+
 		export const GET = () => {
 			return {
 				a: 1
 			};
 		};
-	`
+	;Get;`
 	);
 });
 
@@ -150,7 +153,8 @@ test('Rewrites types for a JavaScript module with `function`', () => {
 	assert.equal(rewritten?.exports, ['GET']);
 	assert.equal(
 		rewritten?.code,
-		`
+		`// @ts-nocheck
+
 		/** @param {Parameters<import('./$types').Get>[0]} event */
 		export function GET({ params }) {
 			return {
@@ -176,7 +180,36 @@ test('Rewrites types for a JavaScript module with `const`', () => {
 	assert.equal(rewritten?.exports, ['GET']);
 	assert.equal(
 		rewritten?.code,
-		`
+		`// @ts-nocheck
+
+		/** @param {Parameters<import('./$types').Get>[0]} event */
+		export const GET = ({ params }) => {
+			return {
+				a: 1
+			};
+		};
+	`
+	);
+});
+
+test('Appends @ts-nocheck after @ts-check', () => {
+	const source = `// @ts-check
+		/** @type {import('./$types').Get} */
+		export const GET = ({ params }) => {
+			return {
+				a: 1
+			};
+		};
+	`;
+
+	const rewritten = tweak_types(source, new Set(['GET']));
+
+	assert.equal(rewritten?.exports, ['GET']);
+	assert.equal(
+		rewritten?.code,
+		`// @ts-check
+// @ts-nocheck
+
 		/** @param {Parameters<import('./$types').Get>[0]} event */
 		export const GET = ({ params }) => {
 			return {
diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js
index 0fa99bd6f..f5c64bf35 100644
--- a/packages/kit/src/runtime/client/client.js
+++ b/packages/kit/src/runtime/client/client.js
@@ -638,9 +638,10 @@ export function create_client({ target, base, trailing_slash }) {
 
 	/**
 	 * @param {import('types').ServerDataNode | import('types').ServerDataSkippedNode | null} node
+	 * @param {import('./types').DataNode | null} [previous]
 	 * @returns {import('./types').DataNode | null}
 	 */
-	function create_data_node(node) {
+	function create_data_node(node, previous) {
 		if (node?.type === 'data') {
 			return {
 				type: 'data',
@@ -652,6 +653,8 @@ export function create_client({ target, base, trailing_slash }) {
 					url: !!node.uses.url
 				}
 			};
+		} else if (node?.type === 'skip') {
+			return previous ?? null;
 		}
 		return null;
 	}
@@ -765,7 +768,7 @@ export function create_client({ target, base, trailing_slash }) {
 					}
 					return data;
 				},
-				server_data_node: create_data_node(server_data_node) ?? previous?.server ?? null
+				server_data_node: create_data_node(server_data_node, previous?.server)
 			});
 		});
 
diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts
index 932220f00..3fc11b752 100644
--- a/packages/kit/types/internal.d.ts
+++ b/packages/kit/types/internal.d.ts
@@ -194,6 +194,9 @@ export type ServerData =
 	  }
 	| {
 			type: 'data';
+			/**
+			 * If `null`, then there was no load function
+			 */
 			nodes: Array<ServerDataNode | ServerDataSkippedNode | ServerErrorNode | null>;
 	  };
 


Test patch:
diff --git a/packages/kit/test/apps/basics/src/routes/load/server-data-reuse/no-load/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/server-data-reuse/no-load/+page.svelte
new file mode 100644
index 000000000..592b0da7c
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/load/server-data-reuse/no-load/+page.svelte
@@ -0,0 +1,7 @@
+<script>
+	export let data 
+</script>
+
+<p>Page without load</p>
+
+<pre>{JSON.stringify(data)}</pre>
diff --git a/packages/kit/test/apps/basics/src/routes/load/server-data-reuse/with-server-load/+page.server.js b/packages/kit/test/apps/basics/src/routes/load/server-data-reuse/with-server-load/+page.server.js
new file mode 100644
index 000000000..bbfe5178f
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/load/server-data-reuse/with-server-load/+page.server.js
@@ -0,0 +1,4 @@
+/** @type {import('.$/types').ServerLoad} */
+export function load() {
+	return { server: true };
+}
diff --git a/packages/kit/test/apps/basics/src/routes/load/server-data-reuse/with-server-load/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/server-data-reuse/with-server-load/+page.svelte
new file mode 100644
index 000000000..4812446f3
--- /dev/null
+++ b/packages/kit/test/apps/basics/src/routes/load/server-data-reuse/with-server-load/+page.svelte
@@ -0,0 +1,8 @@
+<script lang="ts">
+	/** @type {import('./$types').PageData} */
+	export let data 
+</script>
+
+<p>Page with server load</p>
+
+<pre>{JSON.stringify(data)}</pre>
diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js
index cbb3fb4de..a49bdca0a 100644
--- a/packages/kit/test/apps/basics/test/client.test.js
+++ b/packages/kit/test/apps/basics/test/client.test.js
@@ -360,6 +360,18 @@ test.describe('Load', () => {
 		expect(await page.textContent('h1')).toBe("I didn't break!");
 	});
 
+	test('server data from previous is not reused if next page has no load function', async ({
+		page,
+		app
+	}) => {
+		await page.goto('/load/server-data-reuse/with-server-load');
+		expect(await page.textContent('pre')).toBe(
+			JSON.stringify({ foo: { bar: 'Custom layout' }, server: true })
+		);
+		await app.goto('/load/server-data-reuse/no-load');
+		expect(await page.textContent('pre')).toBe(JSON.stringify({ foo: { bar: 'Custom layout' } }));
+	});
+
 	if (process.env.DEV) {
 		test('using window.fetch causes a warning', async ({ page }) => {
 			const port = 5173;