diff --git a/src/generators/dom/preprocess.ts b/src/generators/dom/preprocess.ts
index 1e71361f31..1fdfababeb 100644
--- a/src/generators/dom/preprocess.ts
+++ b/src/generators/dom/preprocess.ts
@@ -2,6 +2,7 @@ import Block from './Block';
import { trimStart, trimEnd } from '../../utils/trim';
import { assign } from '../../shared/index.js';
import getStaticAttributeValue from '../../utils/getStaticAttributeValue';
+import isChildOfComponent from '../shared/utils/isChildOfComponent';
import { DomGenerator } from './index';
import { Node } from '../../interfaces';
import { State } from './interfaces';
@@ -340,7 +341,8 @@ const preprocessors = {
});
} else {
const slot = getStaticAttributeValue(node, 'slot');
- if (slot) {
+ if (slot && isChildOfComponent(node, generator)) {
+ node.slotted = true;
// TODO validate slots — no nesting, no dynamic names...
const component = componentStack[componentStack.length - 1];
component._slots.add(slot);
diff --git a/src/generators/dom/visitors/Element/Element.ts b/src/generators/dom/visitors/Element/Element.ts
index 4fa5a04b18..70e8ff280e 100644
--- a/src/generators/dom/visitors/Element/Element.ts
+++ b/src/generators/dom/visitors/Element/Element.ts
@@ -63,7 +63,7 @@ export default function visitElement(
const name = childState.parentNode;
const slot = node.attributes.find((attribute: Node) => attribute.name === 'slot');
- const parentNode = slot ?
+ const parentNode = node.slotted ?
`${componentStack[componentStack.length - 1].var}._slotted.${slot.value[0].data}` : // TODO this looks bonkers
state.parentNode;
diff --git a/src/generators/server-side-rendering/preprocess.ts b/src/generators/server-side-rendering/preprocess.ts
index 1f6effc5f7..a8a790f754 100644
--- a/src/generators/server-side-rendering/preprocess.ts
+++ b/src/generators/server-side-rendering/preprocess.ts
@@ -1,3 +1,5 @@
+import getStaticAttributeValue from '../../utils/getStaticAttributeValue';
+import isChildOfComponent from '../shared/utils/isChildOfComponent';
import { SsrGenerator } from './index';
import { Node } from '../../interfaces';
@@ -62,6 +64,11 @@ const preprocessors = {
if (!isComponent) {
generator.stylesheet.apply(node, elementStack);
+
+ const slot = getStaticAttributeValue(node, 'slot');
+ if (slot && isChildOfComponent(node, generator)) {
+ node.slotted = true;
+ }
}
if (node.children.length) {
@@ -80,6 +87,8 @@ function preprocessChildren(
elementStack: Node[]
) {
node.children.forEach((child: Node, i: number) => {
+ child.parent = node;
+
const preprocessor = preprocessors[child.type];
if (preprocessor) preprocessor(generator, child, elementStack);
});
diff --git a/src/generators/server-side-rendering/visitors/Element.ts b/src/generators/server-side-rendering/visitors/Element.ts
index 3a1c070f26..058ba7a0a7 100644
--- a/src/generators/server-side-rendering/visitors/Element.ts
+++ b/src/generators/server-side-rendering/visitors/Element.ts
@@ -47,8 +47,8 @@ export default function visitElement(
let openingTag = `<${node.name}`;
let textareaContents; // awkward special case
- const slot = node.attributes.find((attribute: Node) => attribute.name === 'slot');
- if (slot) {
+ if (node.slotted) {
+ const slot = node.attributes.find((attribute: Node) => attribute.name === 'slot');
const slotName = slot.value[0].data;
const appendTarget = generator.appendTargets[generator.appendTargets.length - 1];
appendTarget.slotStack.push(slotName);
diff --git a/src/generators/shared/utils/isChildOfComponent.ts b/src/generators/shared/utils/isChildOfComponent.ts
new file mode 100644
index 0000000000..3246d27d35
--- /dev/null
+++ b/src/generators/shared/utils/isChildOfComponent.ts
@@ -0,0 +1,13 @@
+import { Node } from '../../../interfaces';
+import Generator from '../../Generator';
+
+export default function isChildOfComponent(node: Node, generator: Generator) {
+ while (node = node.parent) {
+ if (node.type !== 'Element') continue;
+ if (generator.components.has(node.name)) return true;
+ if (/-/.test(node.name)) return false;
+ }
+
+ // TODO do this in validation
+ throw new Error(`Element with a slot='...' attribute must be a descendant of a component or custom element`);
+}
\ No newline at end of file
diff --git a/test/runtime/samples/slot-in-custom-element/_config.js b/test/runtime/samples/slot-in-custom-element/_config.js
new file mode 100644
index 0000000000..f17cc9adcb
--- /dev/null
+++ b/test/runtime/samples/slot-in-custom-element/_config.js
@@ -0,0 +1,7 @@
+export default {
+ html: `
+
+
+
+ `
+};
\ No newline at end of file
diff --git a/test/runtime/samples/slot-in-custom-element/main.html b/test/runtime/samples/slot-in-custom-element/main.html
new file mode 100644
index 0000000000..fca685a6aa
--- /dev/null
+++ b/test/runtime/samples/slot-in-custom-element/main.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file