Skip to content

Commit

Permalink
fix(plugin-vue): handle circular references in HMR check (#334)
Browse files Browse the repository at this point in the history
Fixes #325
  • Loading branch information
NateScarlet committed Jan 9, 2024
1 parent 0a3b9a5 commit eddcfa8
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 3 deletions.
22 changes: 19 additions & 3 deletions packages/plugin-vue/src/handleHotUpdate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,14 +202,25 @@ export function isOnlyTemplateChanged(
)
}

function deepEqual(obj1: any, obj2: any, excludeProps: string[] = []): boolean {
function deepEqual(
obj1: any,
obj2: any,
excludeProps: string[] = [],
deepParentsOfObj1: any[] = [],
): boolean {
// Check if both objects are of the same type
if (typeof obj1 !== typeof obj2) {
return false
}

// Check if both objects are primitive types or null
if (obj1 == null || obj2 == null || typeof obj1 !== 'object') {
// or circular reference
if (
obj1 == null ||
obj2 == null ||
typeof obj1 !== 'object' ||
deepParentsOfObj1.includes(obj1)
) {
return obj1 === obj2
}

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

if (!deepEqual(obj1[key], obj2[key], excludeProps)) {
if (
!deepEqual(obj1[key], obj2[key], excludeProps, [
...deepParentsOfObj1,
obj1,
])
) {
return false
}
}
Expand Down
24 changes: 24 additions & 0 deletions playground/vue/HmrCircularReference.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<template>
<h2 class="hmr-circular-reference">HMR Circular reference</h2>
<p>Click the button then edit this message. The count should be preserved.</p>
<button class="hmr-circular-reference-inc" @click="count++">
count is {{ count }}
</button>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import Node from './Node.vue'
defineProps<App.User>()
let foo: number = 0
const count = ref(foo)
</script>

<style>
.hmr-circular-reference-inc {
color: red;
}
</style>
8 changes: 8 additions & 0 deletions playground/vue/HmrCircularReferenceFile.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// this file will become a TypeScope through option `script.globalTypeFiles`
// which has a circular reference on `TypeScope._ownerScope` (issue 325)

declare namespace App {
interface User {
name: string
}
}
2 changes: 2 additions & 0 deletions playground/vue/Main.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<div class="hmr-tsx-block">
<HmrTsx />
</div>
<HmrCircularReference name="test" />
<TypeProps msg="msg" bar="bar" :id="123" />
<Syntax />
<PreProcessors />
Expand Down Expand Up @@ -60,6 +61,7 @@ import PreCompiled from './pre-compiled/foo.vue'
import PreCompiledExternalScoped from './pre-compiled/external-scoped.vue'
import PreCompiledExternalCssModules from './pre-compiled/external-cssmodules.vue'
import ParserOptions from './ParserOptions.vue'
import HmrCircularReference from './HmrCircularReference.vue'
const TsGeneric = defineAsyncComponent(() => import('./TsGeneric.vue'))
Expand Down
10 changes: 10 additions & 0 deletions playground/vue/__tests__/vue.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,16 @@ describe('hmr', () => {
'updatedCount is 0',
)
})

test('should handle circular reference (issue 325)', async () => {
editFile('HmrCircularReference.vue', (code) =>
code.replace('let foo: number = 0', 'let foo: number = 100'),
)
await untilUpdated(
() => page.textContent('.hmr-circular-reference-inc'),
'count is 100',
)
})
})

describe('src imports', () => {
Expand Down
4 changes: 4 additions & 0 deletions playground/vue/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { resolve } from 'node:path'
import { defineConfig, splitVendorChunkPlugin } from 'vite'
import vuePlugin from '@vitejs/plugin-vue'
import { vueI18nPlugin } from './CustomBlockPlugin'
Expand All @@ -11,6 +12,9 @@ export default defineConfig({
},
plugins: [
vuePlugin({
script: {
globalTypeFiles: [resolve(__dirname, 'HmrCircularReferenceFile.d.ts')],
},
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('my-'),
Expand Down

0 comments on commit eddcfa8

Please sign in to comment.