From 0e26a8cd9fdb50ae5a323f42769baa1e2be93d42 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Sun, 3 Dec 2023 23:30:52 +0100 Subject: [PATCH] Hoist actions --- packages/html2/src/html.ts | 43 +++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/packages/html2/src/html.ts b/packages/html2/src/html.ts index 934e630..e32a51a 100644 --- a/packages/html2/src/html.ts +++ b/packages/html2/src/html.ts @@ -57,11 +57,10 @@ function followPath(topNodes: ChildNode[], path: number[]): Node { return n; } -type PlaceholderAction = (variableParts: HtmlFragment[], topNodes: ChildNode[]) => void; +type PlaceholderAction = (variableParts: HtmlFragment[], node: Node) => void; -function nodeInserter(n: number, path: number[]): PlaceholderAction { - return (vs, topNodes) => { - const node = followPath(topNodes, path); +function nodeInserter(n: number): PlaceholderAction { + return (vs, node) => { function walk(f: HtmlFragment) { if (Array.isArray(f)) { f.forEach(walk); @@ -80,9 +79,8 @@ function nodeInserter(n: number, path: number[]): PlaceholderAction { }; } -function attributesInserter(n: number, path: number[]): PlaceholderAction { - return (vs, topNodes) => { - const node = followPath(topNodes, path); +function attributesInserter(n: number): PlaceholderAction { + return (vs, node) => { const e = document.createElement('template'); e.innerHTML = ``; Array.from(e.attributes).forEach(a => @@ -94,10 +92,8 @@ function attributeValueInserter( attrName: string, constantParts: string[], placeholders: number[], - path: number[], ): PlaceholderAction { - return (vs, topNodes) => { - const node = followPath(topNodes, path); + return (vs, node) => { const pieces = [constantParts[0]]; placeholders.forEach((n, i) => { pieces.push(...renderFragment(vs[n], false)); @@ -109,7 +105,7 @@ function attributeValueInserter( export class HtmlFragmentBuilder { template: HTMLTemplateElement = document.createElement('template'); - placeholderActions: PlaceholderAction[] = []; + placeholderActions: { path: number[], action: PlaceholderAction }[] = []; constructor(constantParts: TemplateStringsArray) { const pieces: string[] = []; @@ -145,15 +141,17 @@ export class HtmlFragmentBuilder { placeholders.forEach((n, i) => { const currentPath = path.slice(); currentPath[currentPath.length - 1] = i * 2 + 1; - this.placeholderActions.push(nodeInserter(n, currentPath)); + this.placeholderActions.push({ + path: currentPath, + action: nodeInserter(n), + }); }); bump(constantParts.length + placeholders.length); break; } case Node.ELEMENT_NODE: { - const currentPath = path.slice(); const e = n as Element; - // TODO: hoist all actions for this node into a single action + const actions: PlaceholderAction[] = []; for (let i = 0; i < e.attributes.length; i++) { const attr = e.attributes[i]; const attrName = attr.name; @@ -162,19 +160,24 @@ export class HtmlFragmentBuilder { e.removeAttributeNode(attr); i--; const n = parseInt(nameIsPlaceholder[1], 10); - this.placeholderActions.push(attributesInserter(n, currentPath)); + actions.push(attributesInserter(n)); } else { const { constantParts, placeholders } = splitByPlaceholders(attr.value); if (constantParts.length !== 1) { - this.placeholderActions.push(attributeValueInserter( + actions.push(attributeValueInserter( attrName, constantParts, - placeholders, - currentPath)); + placeholders)); } } } + if (actions.length) { + this.placeholderActions.push({ + path: path.slice(), + action: (vs, n) => actions.forEach(a => a(vs, n)), + }); + } walk(e); nextN = e.nextSibling; bump(1); @@ -197,7 +200,9 @@ export class HtmlFragmentBuilder { } update(template: ChildNode[], variableParts: Array) { - this.placeholderActions.forEach(a => a(variableParts, template)); + this.placeholderActions.forEach(({ path, action }) => { + action(variableParts, followPath(template, path)); + }); } }