@@ -33,11 +33,13 @@ export const html = (statics, ...dynamics) => new BoundTemplateInstance(statics,
3333
3434const singlePartTemplate = part => html `${ part } `
3535
36+ /* v8 ignore start */
3637/** @return {asserts value } */
3738const assert = ( value , message = 'assertion failed' ) => {
3839 if ( ! DEV ) return
3940 if ( ! value ) throw new Error ( message )
4041}
42+ /* v8 ignore stop */
4143
4244/** @implements {SpanInstance} */
4345class Span {
@@ -322,35 +324,47 @@ function compileTemplate(statics) {
322324}
323325
324326/** @type {WeakMap<object, {
327+ _mounted: boolean
325328 _invalidateQueued: Promise<void> | null
326329 _invalidate: () => void
327- _unmountCallbacks: Set<( ) => void> | null
330+ _unmountCallbacks: Set<void | (( ) => void) > | null
328331 _parentNode: Node
329332}>} */
330333const controllers = new WeakMap ( )
334+
331335export function invalidate ( renderable ) {
332336 const controller = controllers . get ( renderable )
333- // TODO: if no controller, check again in a microtask?
334- // just in case the renderable was created between invalidation and rerendering
335337 assert ( controller , 'the renderable has not been rendered' )
336-
337- // TODO: cancel this invalidation if a higher up one comes along
338338 return ( controller . _invalidateQueued ??= Promise . resolve ( ) . then ( ( ) => {
339339 controller . _invalidateQueued = null
340340 controller . _invalidate ( )
341341 } ) )
342342}
343- export function onUnmount ( renderable , callback ) {
343+
344+ /** @type {WeakMap<Renderable, Set<() => void | (() => void)>> } */
345+ const mountCallbacks = new WeakMap ( )
346+
347+ export function onMount ( renderable , callback ) {
348+ DEV: assert ( isRenderable ( renderable ) , 'expected a renderable' )
349+
344350 const controller = controllers . get ( renderable )
345- assert ( controller , 'the renderable has not been rendered' )
351+ if ( controller ?. _mounted ) {
352+ ; ( controller . _unmountCallbacks ??= new Set ( ) ) . add ( callback ( ) )
353+ return
354+ }
346355
347- controller . _unmountCallbacks ??= new Set ( )
348- controller . _unmountCallbacks . add ( callback )
356+ let cb = mountCallbacks . get ( renderable )
357+ if ( ! cb ) mountCallbacks . set ( renderable , ( cb = new Set ( ) ) )
358+ cb . add ( callback )
349359}
360+
361+ export function onUnmount ( renderable , callback ) {
362+ onMount ( renderable , ( ) => callback )
363+ }
364+
350365export function getParentNode ( renderable ) {
351366 const controller = controllers . get ( renderable )
352367 assert ( controller , 'the renderable has not been rendered' )
353-
354368 return controller . _parentNode
355369}
356370
@@ -408,7 +422,7 @@ class ChildPart {
408422 #switchRenderable( next ) {
409423 if ( this . #renderable && this . #renderable !== next ) {
410424 const controller = controllers . get ( this . #renderable)
411- if ( controller ?. _unmountCallbacks ) for ( const callback of controller . _unmountCallbacks ) callback ( )
425+ if ( controller ?. _unmountCallbacks ) for ( const callback of controller . _unmountCallbacks ) callback ?. ( )
412426 controllers . delete ( this . #renderable)
413427 }
414428 this . #renderable = next
@@ -433,6 +447,7 @@ class ChildPart {
433447
434448 if ( ! controllers . has ( renderable ) )
435449 controllers . set ( renderable , {
450+ _mounted : false ,
436451 _invalidateQueued : null ,
437452 _invalidate : ( ) => {
438453 DEV: assert ( this . #renderable === renderable , 'could not invalidate an outdated renderable' )
@@ -511,6 +526,18 @@ class ChildPart {
511526 }
512527
513528 this . #span. _end = end
529+
530+ const controller = controllers . get ( this . #renderable)
531+ if ( controller ) {
532+ controller . _mounted = true
533+ // @ts -expect-error -- WeakMap lookups of null always return undefined, which is fine
534+ for ( const callback of mountCallbacks . get ( this . #renderable) ?? [ ] ) {
535+ ; ( controller . _unmountCallbacks ??= new Set ( ) ) . add ( callback ( ) )
536+ }
537+ // @ts -expect-error -- WeakMap lookups of null always return undefined, which is fine
538+ mountCallbacks . delete ( this . #renderable)
539+ }
540+
514541 if ( endsWereEqual ) this . #parentSpan. _end = this . #span. _end
515542
516543 return
@@ -541,9 +568,20 @@ class ChildPart {
541568 }
542569 }
543570
544- if ( endsWereEqual ) this . #parentSpan. _end = this . #span. _end
545-
546571 this . #value = value
572+
573+ const controller = controllers . get ( this . #renderable)
574+ if ( controller ) {
575+ controller . _mounted = true
576+ // @ts -expect-error -- WeakMap lookups of null always return undefined, which is fine
577+ for ( const callback of mountCallbacks . get ( this . #renderable) ?? [ ] ) {
578+ ; ( controller . _unmountCallbacks ??= new Set ( ) ) . add ( callback ( ) )
579+ }
580+ // @ts -expect-error -- WeakMap lookups of null always return undefined, which is fine
581+ mountCallbacks . delete ( this . #renderable)
582+ }
583+
584+ if ( endsWereEqual ) this . #parentSpan. _end = this . #span. _end
547585 }
548586
549587 detach ( ) {
0 commit comments