Skip to content

Commit eddcfa8

Browse files
authoredJan 9, 2024
fix(plugin-vue): handle circular references in HMR check (#334)
Fixes #325
1 parent 0a3b9a5 commit eddcfa8

File tree

6 files changed

+67
-3
lines changed

6 files changed

+67
-3
lines changed
 

‎packages/plugin-vue/src/handleHotUpdate.ts

+19-3
Original file line numberDiff line numberDiff line change
@@ -202,14 +202,25 @@ export function isOnlyTemplateChanged(
202202
)
203203
}
204204

205-
function deepEqual(obj1: any, obj2: any, excludeProps: string[] = []): boolean {
205+
function deepEqual(
206+
obj1: any,
207+
obj2: any,
208+
excludeProps: string[] = [],
209+
deepParentsOfObj1: any[] = [],
210+
): boolean {
206211
// Check if both objects are of the same type
207212
if (typeof obj1 !== typeof obj2) {
208213
return false
209214
}
210215

211216
// Check if both objects are primitive types or null
212-
if (obj1 == null || obj2 == null || typeof obj1 !== 'object') {
217+
// or circular reference
218+
if (
219+
obj1 == null ||
220+
obj2 == null ||
221+
typeof obj1 !== 'object' ||
222+
deepParentsOfObj1.includes(obj1)
223+
) {
213224
return obj1 === obj2
214225
}
215226

@@ -229,7 +240,12 @@ function deepEqual(obj1: any, obj2: any, excludeProps: string[] = []): boolean {
229240
continue
230241
}
231242

232-
if (!deepEqual(obj1[key], obj2[key], excludeProps)) {
243+
if (
244+
!deepEqual(obj1[key], obj2[key], excludeProps, [
245+
...deepParentsOfObj1,
246+
obj1,
247+
])
248+
) {
233249
return false
234250
}
235251
}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<template>
2+
<h2 class="hmr-circular-reference">HMR Circular reference</h2>
3+
<p>Click the button then edit this message. The count should be preserved.</p>
4+
<button class="hmr-circular-reference-inc" @click="count++">
5+
count is {{ count }}
6+
</button>
7+
</template>
8+
9+
<script setup lang="ts">
10+
import { ref } from 'vue'
11+
import Node from './Node.vue'
12+
13+
defineProps<App.User>()
14+
15+
let foo: number = 0
16+
17+
const count = ref(foo)
18+
</script>
19+
20+
<style>
21+
.hmr-circular-reference-inc {
22+
color: red;
23+
}
24+
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// this file will become a TypeScope through option `script.globalTypeFiles`
2+
// which has a circular reference on `TypeScope._ownerScope` (issue 325)
3+
4+
declare namespace App {
5+
interface User {
6+
name: string
7+
}
8+
}

‎playground/vue/Main.vue

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<div class="hmr-tsx-block">
99
<HmrTsx />
1010
</div>
11+
<HmrCircularReference name="test" />
1112
<TypeProps msg="msg" bar="bar" :id="123" />
1213
<Syntax />
1314
<PreProcessors />
@@ -60,6 +61,7 @@ import PreCompiled from './pre-compiled/foo.vue'
6061
import PreCompiledExternalScoped from './pre-compiled/external-scoped.vue'
6162
import PreCompiledExternalCssModules from './pre-compiled/external-cssmodules.vue'
6263
import ParserOptions from './ParserOptions.vue'
64+
import HmrCircularReference from './HmrCircularReference.vue'
6365
6466
const TsGeneric = defineAsyncComponent(() => import('./TsGeneric.vue'))
6567

‎playground/vue/__tests__/vue.spec.ts

+10
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,16 @@ describe('hmr', () => {
225225
'updatedCount is 0',
226226
)
227227
})
228+
229+
test('should handle circular reference (issue 325)', async () => {
230+
editFile('HmrCircularReference.vue', (code) =>
231+
code.replace('let foo: number = 0', 'let foo: number = 100'),
232+
)
233+
await untilUpdated(
234+
() => page.textContent('.hmr-circular-reference-inc'),
235+
'count is 100',
236+
)
237+
})
228238
})
229239

230240
describe('src imports', () => {

‎playground/vue/vite.config.ts

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { resolve } from 'node:path'
12
import { defineConfig, splitVendorChunkPlugin } from 'vite'
23
import vuePlugin from '@vitejs/plugin-vue'
34
import { vueI18nPlugin } from './CustomBlockPlugin'
@@ -11,6 +12,9 @@ export default defineConfig({
1112
},
1213
plugins: [
1314
vuePlugin({
15+
script: {
16+
globalTypeFiles: [resolve(__dirname, 'HmrCircularReferenceFile.d.ts')],
17+
},
1418
template: {
1519
compilerOptions: {
1620
isCustomElement: (tag) => tag.startsWith('my-'),

0 commit comments

Comments
 (0)
Please sign in to comment.