Repository: sveltejs/svelte (JavaScript)
Date: 2021-05-01 15:03:00+00:00
Task type: Bug Fix

Issue:
Duplication of elements on hydration in Svelte 3.38.0
**Describe the bug**
Under certain conditions, hydration appends elements to the DOM alongside prerendered parts, leading to element duplication. This seems to specifically affect routes/components using `svelte:head` and depends heavily on components’ structure. There is no duplication after navigating away and coming back.

This behaviour is almost certainly introduced by a recent change in Svelte, but it doesn’t seem to affect much beyond SvelteKit (obviously not REPL, but also not Sapper), so may warrant a look at this side.

**To Reproduce**
Clone and run the demo: https://github.com/intrikate/sveltekit-duplication-issue.

**Expected behavior**
CSR should overwrite, not duplicate, SSR.

**Information about your SvelteKit Installation:**

<details>
  <summary>Diagnostics</summary>

  System:
    OS: macOS 11.2.3
    CPU: (4) x64 Intel(R) Core(TM) i5-4288U CPU @ 2.60GHz
    Memory: 130.07 MB / 8.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 14.16.0 - /usr/local/bin/node
    Yarn: 1.22.10 - /usr/local/bin/yarn
    npm: 6.14.11 - /usr/local/bin/npm
  Browsers:
    Chrome: 90.0.4430.93
    Firefox Developer Edition: 89.0
    Safari: 14.0.3
  npmPackages:
    @sveltejs/kit: next => 1.0.0-next.94 
    svelte: ^3.38.0 => 3.38.0 
    vite: ^2.2.3 => 2.2.3
</details>

**Severity**
High.


Issue discussion:
I just upgraded my project from version 71 to 94 and met the same issue. I can confirm that it does no longer occur when I remove the `<svelte:head>` from my page components.

edit: downgrading to svelte@3.37.0 seems to fix the issue as well.
Experiencing the same issue while using with `<svelte:head>`.

@intrikate you should move this issue to [sveltejs/svelte](https://github.com/sveltejs/svelte/)
@DhyeyMoliya I decided against it. Whatever change they introduced over there apparently didn’t break anything but SvelteKit. If that’s the case, it may be a better course of action to fix the beta to accommodate a stable dependency, rather than the other way round.
@intrikate it's broken, just not so many people using a hydration and looks like we skip it during tests
Thank you for the excellent set of reproductions! I am moving this to the Svelte repro, as this is almost certainly a bug caused by the hydration changes in 3.38.0.
I was seeing the same thing. I set up a reproduction repository with minimal modification from the demo app:
https://github.com/lukasIO/kit-svelte-3.38/commit/688833c1443004ea73b3a2bce642377058621dfc

edit: ah, there was already one... 
I'm seeing this as well. I didn't notice this issue already, but I also put together a repo that reproduces it. This is from skelton project, not the demo. `<svelte:head>` was also what I was seeing. Here's the repo in case you need it.

[Repo reproducing bug](https://github.com/johngrasty/svelte-3.38-duplication)

When clicking the link, the test page will not have the duplication. If reloaded, it will
I have this issue in my project without svelte:head being in the picture at all. On my end it depended on what I set as the hydration target. No element (or body) doesn't cause any duplicated items at all. 
@benmccann it is not duplication of head elements, the actual elements of the affected pages are getting duplicated, like forms, divs, etc.

**Update :** If you mean `head elements` as the `first html element`, then ignore this comment. I was understanding `head elements` as `<svelte:head>`
> @benmccann it is not duplication of head elements, the actual elements of the affected pages are getting duplicated, like forms, divs, etc.

Right. That’s what I’m seeing as well, but in my minimal repo, it seemed that just the first element was being duplicated. For example, if there are two divs that are siblings. If there is a `<svelte:head>`, the first div will be duplicated but not the second. 
Suprisingly, if I remove `<div id="svelte">%svelte.body%</div>` from `app.html` and set `target: 'body'`,  everything works well. Similar to @UltraCakeBakery .
I'm curious why the existing tests didn't catch this. https://github.com/sveltejs/svelte/tree/master/test/hydration/samples/head-meta-hydrate-duplicate

Also, I have a SvelteKit app deployed to production using Svelte 3.38.0 at http://c3.ventures/ and don't see this

It'd be nice if someone was able to turn one of the reproductions into a test that fails with https://github.com/sveltejs/svelte/pull/6204 and passes without it



I tried for a little while this morning to produce a failing test, but without success. While the SvelteKit app that reproduces this is quite simple, there's a bit that's tucked inside `.svelte/dev/generated/root.svelte` that's presumably an important part of reproducing the bug, which makes producing a Svelte-only hydration repro a bit trickier.

Solution patch:
diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts
--- a/src/runtime/internal/dom.ts
+++ b/src/runtime/internal/dom.ts
@@ -216,7 +216,7 @@ export function claim_text(nodes, data) {
 		const node = nodes[i];
 		if (node.nodeType === 3) {
 			node.data = '' + data;
-			return nodes.shift();
+			return nodes.splice(i, 1)[0];
 		}
 	}
 


Test patch:
diff --git a/test/hydration/samples/claim-text/Component.svelte b/test/hydration/samples/claim-text/Component.svelte
new file mode 100644
--- /dev/null
+++ b/test/hydration/samples/claim-text/Component.svelte
@@ -0,0 +1,5 @@
+<svelte:head>
+	<title>Title</title>
+</svelte:head>
+
+<main>There should be one</main>
diff --git a/test/hydration/samples/claim-text/Layout.svelte b/test/hydration/samples/claim-text/Layout.svelte
new file mode 100644
--- /dev/null
+++ b/test/hydration/samples/claim-text/Layout.svelte
@@ -0,0 +1,2 @@
+<p>This <code>p</code> and the <code>slot</code> below are direct children of the root.</p>
+<slot />
diff --git a/test/hydration/samples/claim-text/_after.html b/test/hydration/samples/claim-text/_after.html
new file mode 100644
--- /dev/null
+++ b/test/hydration/samples/claim-text/_after.html
@@ -0,0 +1,3 @@
+<p>This <code>p</code> and the <code>slot</code> below are direct children of the root.</p>
+		
+<main>There should be one</main>
\ No newline at end of file
diff --git a/test/hydration/samples/claim-text/_before.html b/test/hydration/samples/claim-text/_before.html
new file mode 100644
--- /dev/null
+++ b/test/hydration/samples/claim-text/_before.html
@@ -0,0 +1,5 @@
+<p>This <code>p</code> and the <code>slot</code> below are direct children of the root.</p>
+
+<main>There should be one</main>
+
+<!-- need extra space after <main> -->
\ No newline at end of file
diff --git a/test/hydration/samples/claim-text/main.svelte b/test/hydration/samples/claim-text/main.svelte
new file mode 100644
--- /dev/null
+++ b/test/hydration/samples/claim-text/main.svelte
@@ -0,0 +1,8 @@
+<script>
+	import Layout from './Layout.svelte';
+	import Component from './Component.svelte';
+</script>
+
+<Layout>
+	<Component />
+</Layout>
\ No newline at end of file