From 18fb67eaad5426cf6af155cebf4c4a8802adab02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Tue, 30 Nov 2021 17:07:11 +0100 Subject: [PATCH] [FIX] blockdom: properly handle references Before this commit, there were situations where the reference numbers were not properly set, which caused the blocks generated to crash because the algorithm could not get correct references. --- src/blockdom/block_compiler.ts | 45 ++++++++++++++-------------------- tests/blockdom/block.test.ts | 16 ++++++++++++ 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/blockdom/block_compiler.ts b/src/blockdom/block_compiler.ts index 728fe6b5..76f7df31 100644 --- a/src/blockdom/block_compiler.ts +++ b/src/blockdom/block_compiler.ts @@ -113,7 +113,7 @@ interface IntermediateTree { nextSibling: IntermediateTree | null; el: Node; info: DynamicInfo[]; - forceRef?: boolean; + isRef?: boolean; refIdx?: number; refN: number; currentNS: string | null; @@ -127,7 +127,6 @@ function buildTree( switch (node.nodeType) { case Node.ELEMENT_NODE: { // HTMLElement - let isActive = false; let currentNS = parent && parent.currentNS; const tagName = (node as Element).tagName; let el: Node | undefined = undefined; @@ -136,14 +135,14 @@ function buildTree( const index = parseInt(tagName.slice(11), 10); info.push({ type: "text", idx: index }); el = document.createTextNode(""); - isActive = true; } if (tagName.startsWith("block-child-")) { - domParentTree!.forceRef = true; + if (!domParentTree!.isRef) { + addRef(domParentTree!); + } const index = parseInt(tagName.slice(12), 10); info.push({ type: "child", idx: index }); el = document.createTextNode(""); - isActive = true; } const attrs = (node as Element).attributes; const ns = attrs.getNamedItem("block-ns"); @@ -161,7 +160,6 @@ function buildTree( const attrName = attrs[i].name; const attrValue = attrs[i].value; if (attrName.startsWith("block-handler-")) { - isActive = true; const idx = parseInt(attrName.slice(14), 10); info.push({ type: "handler", @@ -169,7 +167,6 @@ function buildTree( event: attrValue, }); } else if (attrName.startsWith("block-attribute-")) { - isActive = true; const idx = parseInt(attrName.slice(16), 10); info.push({ type: "attribute", @@ -178,13 +175,11 @@ function buildTree( tag: tagName, }); } else if (attrName === "block-attributes") { - isActive = true; info.push({ type: "attributes", idx: parseInt(attrValue, 10), }); } else if (attrName === "block-ref") { - isActive = true; info.push({ type: "ref", idx: parseInt(attrValue, 10), @@ -201,7 +196,7 @@ function buildTree( nextSibling: null, el, info, - refN: isActive ? 1 : 0, + refN: 0, currentNS, }; @@ -215,8 +210,6 @@ function buildTree( const tagName = (childNode as Element).tagName; const index = parseInt(tagName.slice(12), 10); info.push({ idx: index, type: "child", isOnlyChild: true }); - isActive = true; - tree.refN = 1; } else { tree.firstChild = buildTree(node.firstChild, tree, tree); el.appendChild(tree.firstChild.el); @@ -229,11 +222,8 @@ function buildTree( } } } - if (isActive) { - let cur: IntermediateTree | null = tree; - while ((cur = cur.parent)) { - cur.refN++; - } + if (tree.info.length) { + addRef(tree); } return tree; } @@ -258,6 +248,13 @@ function buildTree( throw new Error("boom"); } +function addRef(tree: IntermediateTree) { + tree.isRef = true; + do { + tree.refN++; + } while ((tree = tree.parent as any)); +} + function parentTree(tree: IntermediateTree): IntermediateTree | null { let parent = tree.parent; while (parent && parent.nextSibling === tree) { @@ -304,21 +301,15 @@ interface BlockCtx { cbRefs: number[]; } -function buildContext( - tree: IntermediateTree, - ctx?: BlockCtx, - fromIdx?: number, - toIdx?: number -): BlockCtx { +function buildContext(tree: IntermediateTree, ctx?: BlockCtx, fromIdx?: number): BlockCtx { if (!ctx) { const children = new Array(tree.info.filter((v) => v.type === "child").length); ctx = { collectors: [], locations: [], children, cbRefs: [], refN: tree.refN }; fromIdx = 0; - toIdx = tree.refN - 1; } if (tree.refN) { const initialIdx = fromIdx!; - const isRef = tree.forceRef || tree.info.length > 0; + const isRef = tree.isRef; const firstChild = tree.firstChild ? tree.firstChild.refN : 0; const nextSibling = tree.nextSibling ? tree.nextSibling.refN : 0; @@ -336,13 +327,13 @@ function buildContext( if (nextSibling) { const idx = fromIdx! + firstChild; ctx.collectors.push({ idx, prevIdx: initialIdx, getVal: nodeGetNextSibling }); - buildContext(tree.nextSibling!, ctx, idx, toIdx); + buildContext(tree.nextSibling!, ctx, idx); } // left if (firstChild) { ctx.collectors.push({ idx: fromIdx!, prevIdx: initialIdx, getVal: nodeGetFirstChild }); - buildContext(tree.firstChild!, ctx, fromIdx!, toIdx! - nextSibling); + buildContext(tree.firstChild!, ctx, fromIdx!); } } diff --git a/tests/blockdom/block.test.ts b/tests/blockdom/block.test.ts index ec69d729..98c80df7 100644 --- a/tests/blockdom/block.test.ts +++ b/tests/blockdom/block.test.ts @@ -227,6 +227,22 @@ describe("misc", () => { expect(fixture.innerHTML).toBe("

() => 3tostring

"); }); + test("block with 2 subblocks: variation", async () => { + const block = createBlock("2"); + const tree = block([], [text("1"), text("3")]); + mount(tree, fixture); + expect(fixture.innerHTML).toBe("123"); + }); + + test("block with 2 subblocks: another variation", async () => { + const block = createBlock( + `2` + ); + const tree = block(["world"], [text("1"), text("3")]); + mount(tree, fixture); + expect(fixture.innerHTML).toBe(`123`); + }); + // test.skip("reusing a block skips patching process", async () => { // const block = createBlock('
'); // const foo = block(["foo"]);