@@ -23,6 +23,7 @@ export const serveHot = (options) => {
23
23
const env = typeof Deno === "object" ? Deno . env . toObject ( ) : process . env ;
24
24
const onFsNotify = fs . watch ( root ) ;
25
25
const contentCache = new Map ( ) ; // todo: use worker `caches` api if possible
26
+ const hotClients = new Map ( ) ;
26
27
27
28
/**
28
29
* Fetcher handles requests for hot applications.
@@ -193,7 +194,9 @@ export const serveHot = (options) => {
193
194
return new Response ( "[]" , { headers } ) ;
194
195
}
195
196
const entries = await fs . ls ( root ) ;
196
- const matched = entries . filter ( ( entry ) => glob . includes ( entry ) || entry . match ( globToRegExp ( glob ) ) ) ;
197
+ const matched = entries . filter ( ( entry ) =>
198
+ glob . includes ( entry ) || entry . match ( globToRegExp ( glob ) )
199
+ ) ;
197
200
if ( ! matched . length ) {
198
201
return new Response ( "[]" , { headers } ) ;
199
202
}
@@ -241,23 +244,47 @@ export const serveHot = (options) => {
241
244
242
245
/** Event stream for HMR */
243
246
case "/@hot-events" : {
247
+ const channelName = url . searchParams . get ( "channel" ) ;
248
+ const devChannel = channelName === "dev" ;
244
249
const disposes = [ ] ;
250
+ if ( req . method === "POST" ) {
251
+ const data = await req . json ( ) ;
252
+ const clients = hotClients . get ( channelName )
253
+ if ( ! clients ) {
254
+ return new Response ( "Channel not found" , { status : 404 } ) ;
255
+ }
256
+ clients . forEach ( ( { sentEvent } ) => sentEvent ( "message" , data ) ) ;
257
+ return new Response ( "Ok" ) ;
258
+ }
245
259
return new Response (
246
260
new ReadableStream ( {
247
261
start ( controller ) {
248
- const sendEvent = ( eventName , data ) => {
249
- controller . enqueue ( "event: " + eventName + "\ndata: " + JSON . stringify ( data ) + "\n\n" ) ;
262
+ const sentEvent = ( eventName , data ) => {
263
+ controller . enqueue (
264
+ "event: " + eventName + "\ndata: " + JSON . stringify ( data ) +
265
+ "\n\n" ,
266
+ ) ;
250
267
} ;
251
- disposes . push ( onFsNotify ( ( type , name ) => {
252
- sendEvent ( "fs-notify" , { type, name } ) ;
253
- } ) ) ;
254
- controller . enqueue ( ": hot notify stream\n\n" ) ;
255
- if ( isLocalHost ( url ) ) {
256
- sendEvent ( "open-devtools" , null ) ;
268
+ controller . enqueue ( ": hot events stream\n\n" ) ;
269
+ if ( devChannel ) {
270
+ disposes . push ( onFsNotify ( ( type , name ) => {
271
+ sentEvent ( "fs-notify" , { type, name } ) ;
272
+ } ) ) ;
273
+ if ( isLocalHost ( url ) ) {
274
+ sentEvent ( "open-devtools" , null ) ;
275
+ }
276
+ } else {
277
+ const map = hotClients . get ( channelName ) ??
278
+ hotClients . set ( channelName , new Map ( ) ) . get ( channelName ) ;
279
+ map . set ( req , { sentEvent } ) ;
257
280
}
258
281
} ,
259
282
cancel ( ) {
260
- disposes . forEach ( ( dispose ) => dispose ( ) ) ;
283
+ if ( devChannel ) {
284
+ disposes . forEach ( ( dispose ) => dispose ( ) ) ;
285
+ } else {
286
+ hotClients . get ( channelName ) ?. delete ( req ) ;
287
+ }
261
288
} ,
262
289
} ) ,
263
290
{
@@ -299,12 +326,10 @@ export const serveHot = (options) => {
299
326
) ;
300
327
}
301
328
default : {
302
- const htmls = [
303
- pathname !== "/" ? pathname + ".html" : null ,
304
- pathname !== "/" ? pathname + "/index.html" : null ,
305
- "/404.html" ,
306
- "/index.html" ,
307
- ] . filter ( Boolean ) ;
329
+ const htmls = [ "/404.html" , "/index.html" ] ;
330
+ if ( pathname !== "/" ) {
331
+ htmls . unshift ( pathname + ".html" , pathname + "/index.html" ) ;
332
+ }
308
333
for ( const path of htmls ) {
309
334
filepath = path ;
310
335
file = await fs . open ( root + filepath ) ;
@@ -376,7 +401,9 @@ export const serveHot = (options) => {
376
401
const { pathname } = new URL ( content , url . origin + filepath ) ;
377
402
const index = await fs . ls ( root + pathname ) ;
378
403
el . replace (
379
- `<script type="applicatin/json" id="@hot/router">${ JSON . stringify ( { index } ) } </script>` ,
404
+ `<script type="applicatin/json" id="@hot/router">${
405
+ JSON . stringify ( { index } )
406
+ } </script>`,
380
407
{ html : true } ,
381
408
) ;
382
409
} ,
@@ -417,7 +444,9 @@ export const serveHot = (options) => {
417
444
async element ( el ) {
418
445
if ( contentMap ) {
419
446
try {
420
- const { contents = { } } = isNEString ( contentMap ) ? ( contentMap = JSON . parse ( contentMap ) ) : contentMap ;
447
+ const { contents = { } } = isNEString ( contentMap )
448
+ ? ( contentMap = JSON . parse ( contentMap ) )
449
+ : contentMap ;
421
450
const name = el . getAttribute ( "from" ) ;
422
451
let content = contents [ name ] ;
423
452
let asterisk = undefined ;
@@ -454,7 +483,9 @@ export const serveHot = (options) => {
454
483
value = new Function ( "return this." + expr ) . call ( data ) ;
455
484
}
456
485
}
457
- return ! isNullish ( value ) ? value . toString ?. ( ) ?? stringify ( value ) : "" ;
486
+ return ! isNullish ( value )
487
+ ? value . toString ?. ( ) ?? stringify ( value )
488
+ : "" ;
458
489
} ;
459
490
const render = ( data ) => {
460
491
el . setInnerContent ( process ( data ) , { html : true } ) ;
0 commit comments