From a53e42518f96267893a7d02f2b19fc0a70d8ec3b Mon Sep 17 00:00:00 2001 From: Samuel Degueldre Date: Wed, 15 Nov 2023 12:53:15 +0100 Subject: [PATCH] [FIX] compiler: compile named slot in t-component in named slot Previously, if a t-set-slot was inside a t-component itself inside a t-set-slot the parser would crash, because the slot is removed from the template before compiling its content, causing a further check's assumption to be broken (the t-set-slot remains connected to the component's xml node) --- src/compiler/parser.ts | 4 +- .../__snapshots__/slots.test.ts.snap | 39 +++++++++++++++++++ tests/components/slots.test.ts | 25 ++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 3a4cbb0ed..ed31ea2a4 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -740,14 +740,14 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null { // be ignored) let el = slotNode.parentElement!; let isInSubComponent = false; - while (el !== clone) { + while (el && el !== clone) { if (el!.hasAttribute("t-component") || el!.tagName[0] === el!.tagName[0].toUpperCase()) { isInSubComponent = true; break; } el = el.parentElement!; } - if (isInSubComponent) { + if (isInSubComponent || !el) { continue; } diff --git a/tests/components/__snapshots__/slots.test.ts.snap b/tests/components/__snapshots__/slots.test.ts.snap index 2034ed5d5..068d76fd4 100644 --- a/tests/components/__snapshots__/slots.test.ts.snap +++ b/tests/components/__snapshots__/slots.test.ts.snap @@ -1066,6 +1066,45 @@ exports[`slots multiple slots containing components 3`] = ` }" `; +exports[`slots named slot inside named slot in t-component 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, markRaw } = helpers; + const comp1 = app.createComponent(null, false, true, false, []); + const comp2 = app.createComponent(\`Child\`, true, true, false, []); + + function slot1(ctx, node, key = \\"\\") { + const b2 = text(\` outer \`); + const ctx2 = capture(ctx); + const Comp1 = ctx['Child']; + const b4 = toggler(Comp1, comp1({slots: markRaw({'brol': {__render: slot2.bind(this), __ctx: ctx2}})}, (Comp1).name + key + \`__1\`, node, this, Comp1)); + return multi([b2, b4]); + } + + function slot2(ctx, node, key = \\"\\") { + return text(ctx['value']); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + return comp2({slots: markRaw({'brol': {__render: slot1.bind(this), __ctx: ctx1}})}, key + \`__2\`, node, this, null); + } +}" +`; + +exports[`slots named slot inside named slot in t-component 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { callSlot } = helpers; + + return function template(ctx, node, key = \\"\\") { + return callSlot(ctx, node, key, 'brol', false, {}); + } +}" +`; + exports[`slots named slot inside slot 1`] = ` "function anonymous(app, bdom, helpers ) { diff --git a/tests/components/slots.test.ts b/tests/components/slots.test.ts index f36ef6d07..167358579 100644 --- a/tests/components/slots.test.ts +++ b/tests/components/slots.test.ts @@ -1673,6 +1673,31 @@ describe("slots", () => { expect(fixture.innerHTML).toBe("

Ablip

Bblip

"); }); + test("named slot inside named slot in t-component", async () => { + class Child extends Component { + static template = xml``; + } + class Parent extends Component { + static template = xml` + + + outer + + + + + + + `; + static components = { Child }; + Child = Child; + value = "inner"; + } + await mount(Parent, fixture); + + expect(fixture.innerHTML).toBe(" outer inner"); + }); + test("can render only empty slot", async () => { class Parent extends Component { static template = xml``;