@@ -323,7 +323,123 @@ function hasErrorOverlay() {
323
323
return document . querySelectorAll ( overlayId ) . length
324
324
}
325
325
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
+
327
443
async function ping ( ) {
328
444
const socket = new WebSocket ( socketUrl , 'vite-ping' )
329
445
return new Promise < boolean > ( ( resolve ) => {
@@ -345,39 +461,35 @@ async function waitForSuccessfulPing(socketUrl: string, ms = 1000) {
345
461
} )
346
462
}
347
463
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
+
348
476
if ( await ping ( ) ) {
349
477
return
350
478
}
351
479
await wait ( ms )
352
480
353
481
while ( true ) {
354
- if ( document . visibilityState === 'visible' ) {
482
+ if ( visibilityManager . currentState === 'visible' ) {
355
483
if ( await ping ( ) ) {
356
484
break
357
485
}
358
486
await wait ( ms )
359
487
} else {
360
- await waitForWindowShow ( )
488
+ await waitForWindowShow ( visibilityManager )
361
489
}
362
490
}
363
491
}
364
492
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
-
381
493
const sheetsMap = new Map < string , HTMLStyleElement > ( )
382
494
383
495
// collect existing style elements that may have been inserted during SSR
0 commit comments