1
- import type { ErrorPayload , HMRPayload , Update } from 'types/hmrPayload'
2
- import type { ModuleNamespace , ViteHotContext } from 'types/hot'
1
+ import type { ErrorPayload , HMRPayload } from 'types/hmrPayload'
2
+ import type { ViteHotContext } from 'types/hot'
3
3
import type { InferCustomEventPayload } from 'types/customEvent'
4
+ import { HMRClient , HMRContext } from '../shared/hmr'
4
5
import { ErrorOverlay , overlayId } from './overlay'
5
6
import '@vite/env'
6
7
@@ -110,17 +111,6 @@ function setupWebSocket(
110
111
return socket
111
112
}
112
113
113
- function warnFailedFetch ( err : Error , path : string | string [ ] ) {
114
- if ( ! err . message . match ( 'fetch' ) ) {
115
- console . error ( err )
116
- }
117
- console . error (
118
- `[hmr] Failed to reload ${ path } . ` +
119
- `This could be due to syntax errors or importing non-existent ` +
120
- `modules. (see errors above)` ,
121
- )
122
- }
123
-
124
114
function cleanUrl ( pathname : string ) : string {
125
115
const url = new URL ( pathname , location . toString ( ) )
126
116
url . searchParams . delete ( 'direct' )
@@ -144,6 +134,22 @@ const debounceReload = (time: number) => {
144
134
}
145
135
const pageReload = debounceReload ( 50 )
146
136
137
+ const hmrClient = new HMRClient ( console , async function importUpdatedModule ( {
138
+ acceptedPath,
139
+ timestamp,
140
+ explicitImportRequired,
141
+ } ) {
142
+ const [ acceptedPathWithoutQuery , query ] = acceptedPath . split ( `?` )
143
+ return await import (
144
+ /* @vite -ignore */
145
+ base +
146
+ acceptedPathWithoutQuery . slice ( 1 ) +
147
+ `?${ explicitImportRequired ? 'import&' : '' } t=${ timestamp } ${
148
+ query ? `&${ query } ` : ''
149
+ } `
150
+ )
151
+ } )
152
+
147
153
async function handleMessage ( payload : HMRPayload ) {
148
154
switch ( payload . type ) {
149
155
case 'connected' :
@@ -173,7 +179,7 @@ async function handleMessage(payload: HMRPayload) {
173
179
await Promise . all (
174
180
payload . updates . map ( async ( update ) : Promise < void > => {
175
181
if ( update . type === 'js-update' ) {
176
- return queueUpdate ( fetchUpdate ( update ) )
182
+ return queueUpdate ( hmrClient . fetchUpdate ( update ) )
177
183
}
178
184
179
185
// css-update
@@ -245,16 +251,7 @@ async function handleMessage(payload: HMRPayload) {
245
251
break
246
252
case 'prune' :
247
253
notifyListeners ( 'vite:beforePrune' , payload )
248
- // After an HMR update, some modules are no longer imported on the page
249
- // but they may have left behind side effects that need to be cleaned up
250
- // (.e.g style injections)
251
- // TODO Trigger their dispose callbacks.
252
- payload . paths . forEach ( ( path ) => {
253
- const fn = pruneMap . get ( path )
254
- if ( fn ) {
255
- fn ( dataMap . get ( path ) )
256
- }
257
- } )
254
+ hmrClient . prunePaths ( payload . paths )
258
255
break
259
256
case 'error' : {
260
257
notifyListeners ( 'vite:error' , payload )
@@ -280,10 +277,7 @@ function notifyListeners<T extends string>(
280
277
data : InferCustomEventPayload < T > ,
281
278
) : void
282
279
function notifyListeners ( event : string , data : any ) : void {
283
- const cbs = customListenersMap . get ( event )
284
- if ( cbs ) {
285
- cbs . forEach ( ( cb ) => cb ( data ) )
286
- }
280
+ hmrClient . notifyListeners ( event , data )
287
281
}
288
282
289
283
const enableOverlay = __HMR_ENABLE_OVERLAY__
@@ -430,206 +424,22 @@ export function removeStyle(id: string): void {
430
424
}
431
425
}
432
426
433
- async function fetchUpdate ( {
434
- path,
435
- acceptedPath,
436
- timestamp,
437
- explicitImportRequired,
438
- } : Update ) {
439
- const mod = hotModulesMap . get ( path )
440
- if ( ! mod ) {
441
- // In a code-splitting project,
442
- // it is common that the hot-updating module is not loaded yet.
443
- // https://github.com/vitejs/vite/issues/721
444
- return
445
- }
446
-
447
- let fetchedModule : ModuleNamespace | undefined
448
- const isSelfUpdate = path === acceptedPath
449
-
450
- // determine the qualified callbacks before we re-import the modules
451
- const qualifiedCallbacks = mod . callbacks . filter ( ( { deps } ) =>
452
- deps . includes ( acceptedPath ) ,
453
- )
454
-
455
- if ( isSelfUpdate || qualifiedCallbacks . length > 0 ) {
456
- const disposer = disposeMap . get ( acceptedPath )
457
- if ( disposer ) await disposer ( dataMap . get ( acceptedPath ) )
458
- const [ acceptedPathWithoutQuery , query ] = acceptedPath . split ( `?` )
459
- try {
460
- fetchedModule = await import (
461
- /* @vite -ignore */
462
- base +
463
- acceptedPathWithoutQuery . slice ( 1 ) +
464
- `?${ explicitImportRequired ? 'import&' : '' } t=${ timestamp } ${
465
- query ? `&${ query } ` : ''
466
- } `
467
- )
468
- } catch ( e ) {
469
- warnFailedFetch ( e , acceptedPath )
470
- }
471
- }
472
-
473
- return ( ) => {
474
- for ( const { deps, fn } of qualifiedCallbacks ) {
475
- fn ( deps . map ( ( dep ) => ( dep === acceptedPath ? fetchedModule : undefined ) ) )
476
- }
477
- const loggedPath = isSelfUpdate ? path : `${ acceptedPath } via ${ path } `
478
- console . debug ( `[vite] hot updated: ${ loggedPath } ` )
479
- }
480
- }
481
-
482
427
function sendMessageBuffer ( ) {
483
428
if ( socket . readyState === 1 ) {
484
429
messageBuffer . forEach ( ( msg ) => socket . send ( msg ) )
485
430
messageBuffer . length = 0
486
431
}
487
432
}
488
433
489
- interface HotModule {
490
- id : string
491
- callbacks : HotCallback [ ]
492
- }
493
-
494
- interface HotCallback {
495
- // the dependencies must be fetchable paths
496
- deps : string [ ]
497
- fn : ( modules : Array < ModuleNamespace | undefined > ) => void
498
- }
499
-
500
- type CustomListenersMap = Map < string , ( ( data : any ) => void ) [ ] >
501
-
502
- const hotModulesMap = new Map < string , HotModule > ( )
503
- const disposeMap = new Map < string , ( data : any ) => void | Promise < void > > ( )
504
- const pruneMap = new Map < string , ( data : any ) => void | Promise < void > > ( )
505
- const dataMap = new Map < string , any > ( )
506
- const customListenersMap : CustomListenersMap = new Map ( )
507
- const ctxToListenersMap = new Map < string , CustomListenersMap > ( )
508
-
509
434
export function createHotContext ( ownerPath : string ) : ViteHotContext {
510
- if ( ! dataMap . has ( ownerPath ) ) {
511
- dataMap . set ( ownerPath , { } )
512
- }
513
-
514
- // when a file is hot updated, a new context is created
515
- // clear its stale callbacks
516
- const mod = hotModulesMap . get ( ownerPath )
517
- if ( mod ) {
518
- mod . callbacks = [ ]
519
- }
520
-
521
- // clear stale custom event listeners
522
- const staleListeners = ctxToListenersMap . get ( ownerPath )
523
- if ( staleListeners ) {
524
- for ( const [ event , staleFns ] of staleListeners ) {
525
- const listeners = customListenersMap . get ( event )
526
- if ( listeners ) {
527
- customListenersMap . set (
528
- event ,
529
- listeners . filter ( ( l ) => ! staleFns . includes ( l ) ) ,
530
- )
531
- }
532
- }
533
- }
534
-
535
- const newListeners : CustomListenersMap = new Map ( )
536
- ctxToListenersMap . set ( ownerPath , newListeners )
537
-
538
- function acceptDeps ( deps : string [ ] , callback : HotCallback [ 'fn' ] = ( ) => { } ) {
539
- const mod : HotModule = hotModulesMap . get ( ownerPath ) || {
540
- id : ownerPath ,
541
- callbacks : [ ] ,
542
- }
543
- mod . callbacks . push ( {
544
- deps,
545
- fn : callback ,
546
- } )
547
- hotModulesMap . set ( ownerPath , mod )
548
- }
549
-
550
- const hot : ViteHotContext = {
551
- get data ( ) {
552
- return dataMap . get ( ownerPath )
553
- } ,
554
-
555
- accept ( deps ?: any , callback ?: any ) {
556
- if ( typeof deps === 'function' || ! deps ) {
557
- // self-accept: hot.accept(() => {})
558
- acceptDeps ( [ ownerPath ] , ( [ mod ] ) => deps ?.( mod ) )
559
- } else if ( typeof deps === 'string' ) {
560
- // explicit deps
561
- acceptDeps ( [ deps ] , ( [ mod ] ) => callback ?.( mod ) )
562
- } else if ( Array . isArray ( deps ) ) {
563
- acceptDeps ( deps , callback )
564
- } else {
565
- throw new Error ( `invalid hot.accept() usage.` )
566
- }
567
- } ,
568
-
569
- // export names (first arg) are irrelevant on the client side, they're
570
- // extracted in the server for propagation
571
- acceptExports ( _ , callback ) {
572
- acceptDeps ( [ ownerPath ] , ( [ mod ] ) => callback ?.( mod ) )
435
+ return new HMRContext ( ownerPath , hmrClient , {
436
+ addBuffer ( message ) {
437
+ messageBuffer . push ( message )
573
438
} ,
574
-
575
- dispose ( cb ) {
576
- disposeMap . set ( ownerPath , cb )
577
- } ,
578
-
579
- prune ( cb ) {
580
- pruneMap . set ( ownerPath , cb )
581
- } ,
582
-
583
- // Kept for backward compatibility (#11036)
584
- // @ts -expect-error untyped
585
- // eslint-disable-next-line @typescript-eslint/no-empty-function
586
- decline ( ) { } ,
587
-
588
- // tell the server to re-perform hmr propagation from this module as root
589
- invalidate ( message ) {
590
- notifyListeners ( 'vite:invalidate' , { path : ownerPath , message } )
591
- this . send ( 'vite:invalidate' , { path : ownerPath , message } )
592
- console . debug (
593
- `[vite] invalidate ${ ownerPath } ${ message ? `: ${ message } ` : '' } ` ,
594
- )
595
- } ,
596
-
597
- // custom events
598
- on ( event , cb ) {
599
- const addToMap = ( map : Map < string , any [ ] > ) => {
600
- const existing = map . get ( event ) || [ ]
601
- existing . push ( cb )
602
- map . set ( event , existing )
603
- }
604
- addToMap ( customListenersMap )
605
- addToMap ( newListeners )
606
- } ,
607
-
608
- // remove a custom event
609
- off ( event , cb ) {
610
- const removeFromMap = ( map : Map < string , any [ ] > ) => {
611
- const existing = map . get ( event )
612
- if ( existing === undefined ) {
613
- return
614
- }
615
- const pruned = existing . filter ( ( l ) => l !== cb )
616
- if ( pruned . length === 0 ) {
617
- map . delete ( event )
618
- return
619
- }
620
- map . set ( event , pruned )
621
- }
622
- removeFromMap ( customListenersMap )
623
- removeFromMap ( newListeners )
624
- } ,
625
-
626
- send ( event , data ) {
627
- messageBuffer . push ( JSON . stringify ( { type : 'custom' , event, data } ) )
439
+ send ( ) {
628
440
sendMessageBuffer ( )
629
441
} ,
630
- }
631
-
632
- return hot
442
+ } )
633
443
}
634
444
635
445
/**
0 commit comments