diff --git a/packages/html2/src/html.ts b/packages/html2/src/html.ts index dc14a4d..686a218 100644 --- a/packages/html2/src/html.ts +++ b/packages/html2/src/html.ts @@ -308,17 +308,17 @@ export type HtmlTemplater = => ChildNode[]; export function template(): HtmlTemplater { - let instance: HtmlFragmentInstance | null = null; + let cache: null | { instance: HtmlFragmentInstance, builder: HtmlFragmentBuilder } = null; return (constantParts, ... variableParts) => { let b = templateCache.get(constantParts); if (b === void 0) { b = new HtmlFragmentBuilder(constantParts); templateCache.set(constantParts, b); } - if (instance === null) { - instance = b.clone(); + if (cache === null || cache.builder !== b) { + cache = { instance: b.clone(), builder: b }; } - b.update(instance, variableParts); - return instance.container; + b.update(cache.instance, variableParts); + return cache.instance.container; }; } diff --git a/packages/html2/test/html.test.ts b/packages/html2/test/html.test.ts index 6a85c50..c7c8921 100644 --- a/packages/html2/test/html.test.ts +++ b/packages/html2/test/html.test.ts @@ -1,7 +1,7 @@ /// SPDX-License-Identifier: GPL-3.0-or-later /// SPDX-FileCopyrightText: Copyright © 2024 Tony Garnock-Jones -import { template } from '../src/html'; +import { HtmlTemplater, template } from '../src/html'; import './test-utils'; function compareHTML(nodes: ChildNode[], expected: string) { @@ -89,6 +89,18 @@ describe('basic templating', () => { compareHTML(t`${ps.map(p => p())}`, expected); }); + it('should be callable multiple times with the same syntactic location', () => { + // Because the fixed portion of a template string, the TemplateStringsArray, is hashconsed + const v = () => 'aaa'; + const ps = [() => '123', () => '234', () => '345']; + const expected = '123234345'; + const t = template(); + const f = () => compareHTML(t`${ps.map(p => p())}`, expected); + f(); + f(); + f(); + }); + it('should be callable multiple times with extra items', () => { const v = () => 'aaa'; const t = template(); @@ -99,4 +111,16 @@ describe('basic templating', () => { ps.push(() => '3'); compareHTML(t`${ps.map(p => p())}`, '123'); }); + + it('should be reusable for supplying arguments to itself', () => { + const inner = (t: HtmlTemplater) => t`middle`; + const t = template(); + compareHTML(t`

n${inner(t)}n

`, '

nmiddlen

'); + }); + + it('should accept strings', () => { + const inner = (_t: HtmlTemplater) => `middle`; + const t = template(); + compareHTML(t`

n${inner(t)}n

`, '

n<i>middle</i>n

'); + }); });