@@ -9,15 +9,17 @@ import {
99import {
1010 getResolvedScript ,
1111 invalidateScript ,
12+ resolveScript ,
1213 setResolvedScript ,
1314} from './script'
1415import type { SFCBlock , SFCDescriptor } from 'vue/compiler-sfc'
1516import type { ResolvedOptions } from '.'
1617
18+ import type * as t from '@babel/types'
19+
1720const debug = _debug ( 'vite:hmr' )
1821
19- // eslint-disable-next-line unicorn/better-regex
20- const directRequestRE = / (?: \? | & ) d i r e c t \b /
22+ const directRequestRE = / [ & ? ] d i r e c t \b /
2123
2224/**
2325 * Vite-specific HMR handling
@@ -40,6 +42,8 @@ export async function handleHotUpdate(
4042 const mainModule = getMainModule ( modules )
4143 const templateModule = modules . find ( ( m ) => / t y p e = t e m p l a t e / . test ( m . url ) )
4244
45+ // trigger resolveScript for descriptor so that we'll have the AST ready
46+ resolveScript ( 'vite' , descriptor , options , false )
4347 const scriptChanged = hasScriptChanged ( prevDescriptor , descriptor )
4448 if ( scriptChanged ) {
4549 affectedModules . add ( getScriptModule ( modules ) || mainModule )
@@ -192,11 +196,88 @@ export function isOnlyTemplateChanged(
192196 )
193197}
194198
199+ function deepEqual ( obj1 : any , obj2 : any , excludeProps : string [ ] = [ ] ) : boolean {
200+ // Check if both objects are of the same type
201+ if ( typeof obj1 !== typeof obj2 ) {
202+ return false
203+ }
204+
205+ // Check if both objects are primitive types or null
206+ if ( obj1 == null || obj2 == null || typeof obj1 !== 'object' ) {
207+ return obj1 === obj2
208+ }
209+
210+ // Get the keys of the objects
211+ const keys1 = Object . keys ( obj1 )
212+ const keys2 = Object . keys ( obj2 )
213+
214+ // Check if the number of keys is the same
215+ if ( keys1 . length !== keys2 . length ) {
216+ return false
217+ }
218+
219+ // Iterate through the keys and recursively compare the values
220+ for ( const key of keys1 ) {
221+ // Check if the current key should be excluded
222+ if ( excludeProps . includes ( key ) ) {
223+ continue
224+ }
225+
226+ if ( ! deepEqual ( obj1 [ key ] , obj2 [ key ] , excludeProps ) ) {
227+ return false
228+ }
229+ }
230+
231+ // If all comparisons passed, the objects are deep equal
232+ return true
233+ }
234+
235+ function isEqualAst ( prev ?: t . Statement [ ] , next ?: t . Statement [ ] ) : boolean {
236+ if ( typeof prev === 'undefined' || typeof next === 'undefined' ) {
237+ return prev === next
238+ }
239+
240+ // deep equal, but ignore start/end/loc/range/leadingComments/trailingComments/innerComments
241+ if ( prev . length !== next . length ) {
242+ return false
243+ }
244+
245+ for ( const [ i , prevNode ] of prev . entries ( ) ) {
246+ const nextNode = next [ i ]
247+ if (
248+ ! deepEqual ( prevNode , nextNode , [
249+ 'start' ,
250+ 'end' ,
251+ 'loc' ,
252+ 'range' ,
253+ 'leadingComments' ,
254+ 'trailingComments' ,
255+ 'innerComments' ,
256+ ] )
257+ ) {
258+ return false
259+ }
260+ }
261+
262+ return true
263+ }
264+
195265function hasScriptChanged ( prev : SFCDescriptor , next : SFCDescriptor ) : boolean {
196- if ( ! isEqualBlock ( prev . script , next . script ) ) {
266+ // check for scriptAst/scriptSetupAst changes
267+ // note that the next ast is not available yet, so we need to trigger parsing
268+ const prevScript = getResolvedScript ( prev , false )
269+ const nextScript = getResolvedScript ( next , false )
270+
271+ if (
272+ ! isEqualBlock ( prev . script , next . script ) &&
273+ ! isEqualAst ( prevScript ?. scriptAst , nextScript ?. scriptAst )
274+ ) {
197275 return true
198276 }
199- if ( ! isEqualBlock ( prev . scriptSetup , next . scriptSetup ) ) {
277+ if (
278+ ! isEqualBlock ( prev . scriptSetup , next . scriptSetup ) &&
279+ ! isEqualAst ( prevScript ?. scriptSetupAst , nextScript ?. scriptSetupAst )
280+ ) {
200281 return true
201282 }
202283
0 commit comments