@@ -323,7 +323,123 @@ function hasErrorOverlay() {
323323 return document . querySelectorAll ( overlayId ) . length
324324}
325325
326- async function waitForSuccessfulPing ( socketUrl : string , ms = 1000 ) {
326+ function waitForSuccessfulPing ( socketUrl : string ) {
327+ if ( typeof SharedWorker === 'undefined' ) {
328+ const visibilityManager : VisibilityManager = {
329+ currentState : document . visibilityState ,
330+ listeners : new Set ( ) ,
331+ }
332+ const onVisibilityChange = ( ) => {
333+ visibilityManager . currentState = document . visibilityState
334+ for ( const listener of visibilityManager . listeners ) {
335+ listener ( visibilityManager . currentState )
336+ }
337+ }
338+ document . addEventListener ( 'visibilitychange' , onVisibilityChange )
339+ return waitForSuccessfulPingInternal ( socketUrl , visibilityManager )
340+ }
341+
342+ // needs to be inlined to
343+ // - load the worker after the server is closed
344+ // - make it work with backend integrations
345+ const blob = new Blob (
346+ [
347+ '"use strict";' ,
348+ `const waitForSuccessfulPingInternal = ${ waitForSuccessfulPingInternal . toString ( ) } ;` ,
349+ `const fn = ${ pingWorkerContentMain . toString ( ) } ;` ,
350+ `fn(${ JSON . stringify ( socketUrl ) } )` ,
351+ ] ,
352+ { type : 'application/javascript' } ,
353+ )
354+ const objURL = URL . createObjectURL ( blob )
355+ const sharedWorker = new SharedWorker ( objURL )
356+ return new Promise < void > ( ( resolve , reject ) => {
357+ const onVisibilityChange = ( ) => {
358+ sharedWorker . port . postMessage ( { visibility : document . visibilityState } )
359+ }
360+ document . addEventListener ( 'visibilitychange' , onVisibilityChange )
361+
362+ sharedWorker . port . addEventListener ( 'message' , ( event ) => {
363+ document . removeEventListener ( 'visibilitychange' , onVisibilityChange )
364+ sharedWorker . port . close ( )
365+
366+ const data : { type : 'success' } | { type : 'error' ; error : unknown } =
367+ event . data
368+ if ( data . type === 'error' ) {
369+ reject ( data . error )
370+ return
371+ }
372+ resolve ( )
373+ } )
374+
375+ onVisibilityChange ( )
376+ sharedWorker . port . start ( )
377+ } )
378+ }
379+
380+ type VisibilityManager = {
381+ currentState : DocumentVisibilityState
382+ listeners : Set < ( newVisibility : DocumentVisibilityState ) => void >
383+ }
384+
385+ function pingWorkerContentMain ( socketUrl : string ) {
386+ self . addEventListener ( 'connect' , ( _event ) => {
387+ const event = _event as MessageEvent
388+ const port = event . ports [ 0 ]
389+
390+ if ( ! socketUrl ) {
391+ port . postMessage ( {
392+ type : 'error' ,
393+ error : new Error ( 'socketUrl not found' ) ,
394+ } )
395+ return
396+ }
397+
398+ const visibilityManager : VisibilityManager = {
399+ currentState : 'visible' ,
400+ listeners : new Set ( ) ,
401+ }
402+ port . addEventListener ( 'message' , ( event ) => {
403+ const { visibility } = event . data
404+ visibilityManager . currentState = visibility
405+ console . debug ( 'new window visibility' , visibility )
406+ for ( const listener of visibilityManager . listeners ) {
407+ listener ( visibility )
408+ }
409+ } )
410+ port . start ( )
411+
412+ console . debug ( 'connected from window' )
413+ waitForSuccessfulPingInternal ( socketUrl , visibilityManager ) . then (
414+ ( ) => {
415+ console . debug ( 'ping successful' )
416+ try {
417+ port . postMessage ( { type : 'success' } )
418+ } catch ( error ) {
419+ port . postMessage ( { type : 'error' , error } )
420+ }
421+ } ,
422+ ( error ) => {
423+ console . debug ( 'error happened' , error )
424+ try {
425+ port . postMessage ( { type : 'error' , error } )
426+ } catch ( error ) {
427+ port . postMessage ( { type : 'error' , error } )
428+ }
429+ } ,
430+ )
431+ } )
432+ }
433+
434+ async function waitForSuccessfulPingInternal (
435+ socketUrl : string ,
436+ visibilityManager : VisibilityManager ,
437+ ms = 1000 ,
438+ ) {
439+ function wait ( ms : number ) {
440+ return new Promise ( ( resolve ) => setTimeout ( resolve , ms ) )
441+ }
442+
327443 async function ping ( ) {
328444 const socket = new WebSocket ( socketUrl , 'vite-ping' )
329445 return new Promise < boolean > ( ( resolve ) => {
@@ -345,39 +461,35 @@ async function waitForSuccessfulPing(socketUrl: string, ms = 1000) {
345461 } )
346462 }
347463
464+ function waitForWindowShow ( visibilityManager : VisibilityManager ) {
465+ return new Promise < void > ( ( resolve ) => {
466+ const onChange = ( newVisibility : DocumentVisibilityState ) => {
467+ if ( newVisibility === 'visible' ) {
468+ resolve ( )
469+ visibilityManager . listeners . delete ( onChange )
470+ }
471+ }
472+ visibilityManager . listeners . add ( onChange )
473+ } )
474+ }
475+
348476 if ( await ping ( ) ) {
349477 return
350478 }
351479 await wait ( ms )
352480
353481 while ( true ) {
354- if ( document . visibilityState === 'visible' ) {
482+ if ( visibilityManager . currentState === 'visible' ) {
355483 if ( await ping ( ) ) {
356484 break
357485 }
358486 await wait ( ms )
359487 } else {
360- await waitForWindowShow ( )
488+ await waitForWindowShow ( visibilityManager )
361489 }
362490 }
363491}
364492
365- function wait ( ms : number ) {
366- return new Promise ( ( resolve ) => setTimeout ( resolve , ms ) )
367- }
368-
369- function waitForWindowShow ( ) {
370- return new Promise < void > ( ( resolve ) => {
371- const onChange = async ( ) => {
372- if ( document . visibilityState === 'visible' ) {
373- resolve ( )
374- document . removeEventListener ( 'visibilitychange' , onChange )
375- }
376- }
377- document . addEventListener ( 'visibilitychange' , onChange )
378- } )
379- }
380-
381493const sheetsMap = new Map < string , HTMLStyleElement > ( )
382494
383495// collect existing style elements that may have been inserted during SSR
0 commit comments