From 65c7bd149cf9024f8026a73426d51c76be543cc4 Mon Sep 17 00:00:00 2001
From: Bjorn Lu <bjornlu.dev@gmail.com>
Date: Wed, 18 Oct 2023 21:23:19 +0800
Subject: [PATCH] Fix Vue HMR for script tags (#8860)

---
 .changeset/seven-brooms-trade.md              |  5 +++++
 .../vue-component/src/components/State.vue    |  9 ++++++++
 .../vue-component/src/pages/index.astro       |  2 ++
 packages/astro/e2e/vue-component.test.js      | 13 +++++++++++
 packages/integrations/vue/client.js           | 22 +++++++++++--------
 5 files changed, 42 insertions(+), 9 deletions(-)
 create mode 100644 .changeset/seven-brooms-trade.md
 create mode 100644 packages/astro/e2e/fixtures/vue-component/src/components/State.vue

diff --git a/.changeset/seven-brooms-trade.md b/.changeset/seven-brooms-trade.md
new file mode 100644
index 000000000000..124df8d06023
--- /dev/null
+++ b/.changeset/seven-brooms-trade.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/vue': patch
+---
+
+Fix Vue component HMR when updating the script tag
diff --git a/packages/astro/e2e/fixtures/vue-component/src/components/State.vue b/packages/astro/e2e/fixtures/vue-component/src/components/State.vue
new file mode 100644
index 000000000000..920cb4594265
--- /dev/null
+++ b/packages/astro/e2e/fixtures/vue-component/src/components/State.vue
@@ -0,0 +1,9 @@
+<script setup>
+import { ref } from 'vue'
+const props = defineProps(['id'])
+const count = ref(1)
+</script>
+
+<template>
+  <span :id="props.id">Count is {{ count }}</span>
+</template>
diff --git a/packages/astro/e2e/fixtures/vue-component/src/pages/index.astro b/packages/astro/e2e/fixtures/vue-component/src/pages/index.astro
index 5ea9823b6376..fb10e60382d8 100644
--- a/packages/astro/e2e/fixtures/vue-component/src/pages/index.astro
+++ b/packages/astro/e2e/fixtures/vue-component/src/pages/index.astro
@@ -2,6 +2,7 @@
 import Counter from '../components/Counter.vue';
 import VueComponent from '../components/VueComponent.vue';
 import AsyncTest from '../components/Test.vue'
+import State from '../components/State.vue'
 
 const someProps = {
 	count: 0,
@@ -35,5 +36,6 @@ const someProps = {
 
 		<VueComponent id="client-only" client:only="vue" />
 		<AsyncTest id="client-test" client:only="vue" />
+		<State id="state" client:load />
 	</body>
 </html>
diff --git a/packages/astro/e2e/vue-component.test.js b/packages/astro/e2e/vue-component.test.js
index b7371cd1b1f0..5d0455dee66c 100644
--- a/packages/astro/e2e/vue-component.test.js
+++ b/packages/astro/e2e/vue-component.test.js
@@ -39,3 +39,16 @@ test('test the async vue component in mdx', async ({ page, astro }) => {
 
 	await expect(label, 'component not hydrated').toHaveText('2');
 });
+
+test('hmr works', async ({ page, astro }) => {
+	await page.goto(astro.resolveUrl('/'));
+
+	const span = page.locator('#state');
+	await expect(span).toHaveText('Count is 1');
+
+	await astro.editFile('./src/components/State.vue', (content) =>
+		content.replace('ref(1)', 'ref(2)')
+	);
+
+	await expect(span).toHaveText('Count is 2');
+});
diff --git a/packages/integrations/vue/client.js b/packages/integrations/vue/client.js
index 8b2a5eede16c..7e1d8090efb1 100644
--- a/packages/integrations/vue/client.js
+++ b/packages/integrations/vue/client.js
@@ -14,16 +14,20 @@ export default (element) =>
 			slots[key] = () => h(StaticHtml, { value, name: key === 'default' ? undefined : key });
 		}
 
-		let content = h(Component, props, slots);
-		// related to https://github.com/withastro/astro/issues/6549
-		// if the component is async, wrap it in a Suspense component
-		if (isAsync(Component.setup)) {
-			content = h(Suspense, null, content);
-		}
-
 		const isHydrate = client !== 'only';
-		const boostrap = isHydrate ? createSSRApp : createApp;
-		const app = boostrap({ name, render: () => content });
+		const bootstrap = isHydrate ? createSSRApp : createApp;
+		const app = bootstrap({
+			name,
+			render() {
+				let content = h(Component, props, slots);
+				// related to https://github.com/withastro/astro/issues/6549
+				// if the component is async, wrap it in a Suspense component
+				if (isAsync(Component.setup)) {
+					content = h(Suspense, null, content);
+				}
+				return content;
+			},
+		});
 		await setup(app);
 		app.mount(element, isHydrate);