From 10808d6b60be556f96c937d0f416ff29b3bd5d7b Mon Sep 17 00:00:00 2001 From: Thomas Stokes Date: Tue, 29 Jul 2025 21:29:45 +0800 Subject: [PATCH] try/finally around directive cleanup --- src/client/parts.ts | 7 +++++-- src/client/tests/directives.test.ts | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/client/parts.ts b/src/client/parts.ts index 8e561a3e..9168032c 100644 --- a/src/client/parts.ts +++ b/src/client/parts.ts @@ -296,8 +296,11 @@ export function create_directive_part(node: Node): Part { return fn => { if (prev_fn === fn) return assert(typeof fn === 'function' || fn == null) - cleanup?.() - cleanup = fn?.(node) + try { + cleanup?.() + } finally { + cleanup = fn?.(node) + } prev_fn = fn } } diff --git a/src/client/tests/directives.test.ts b/src/client/tests/directives.test.ts index 11888072..b324ca52 100644 --- a/src/client/tests/directives.test.ts +++ b/src/client/tests/directives.test.ts @@ -243,3 +243,31 @@ test('same directive function is not re-invoked or cleaned up', () => { root.render(template(null, null)) assert_deep_eq(sequence, ['stable cleanup', 'unstable cleanup']) }) + +test('cleanup can throw and next directive will still run', () => { + const { root, el } = setup() + + const template = (d: Directive | null) => html`
Hello, world!
` + + const oops = new Error('oops') + + root.render( + template(() => () => { + throw oops + }), + ) + + let caught + try { + root.render( + template(node => { + ;(node as HTMLElement).style.color = 'red' + }), + ) + } catch (error) { + caught = error + } + + assert_eq(caught, oops) + assert_eq(el.querySelector('div')!.style.cssText, 'color: red;') +})