diff --git a/src/validate/html/index.ts b/src/validate/html/index.ts
index d36b00b833..9a3b573505 100644
--- a/src/validate/html/index.ts
+++ b/src/validate/html/index.ts
@@ -11,6 +11,7 @@ const meta = new Map([[':Window', validateWindow]]);
export default function validateHtml(validator: Validator, html: Node) {
const refs = new Map();
const refCallees: Node[] = [];
+ const stack: Node[] = [];
const elementStack: Node[] = [];
function visit(node: Node) {
@@ -21,7 +22,7 @@ export default function validateHtml(validator: Validator, html: Node) {
return meta.get(node.name)(validator, node, refs, refCallees);
}
- validateElement(validator, node, refs, refCallees, elementStack);
+ validateElement(validator, node, refs, refCallees, stack, elementStack);
} else if (node.type === 'EachBlock') {
if (validator.helpers.has(node.context)) {
let c = node.expression.end;
@@ -40,7 +41,9 @@ export default function validateHtml(validator: Validator, html: Node) {
if (node.children) {
if (node.type === 'Element') elementStack.push(node);
+ stack.push(node);
node.children.forEach(visit);
+ stack.pop();
if (node.type === 'Element') elementStack.pop();
}
diff --git a/src/validate/html/validateElement.ts b/src/validate/html/validateElement.ts
index 72ca2a6698..606b0399d8 100644
--- a/src/validate/html/validateElement.ts
+++ b/src/validate/html/validateElement.ts
@@ -10,6 +10,7 @@ export default function validateElement(
node: Node,
refs: Map,
refCallees: Node[],
+ stack: Node[],
elementStack: Node[]
) {
const isComponent =
@@ -189,11 +190,23 @@ export default function validateElement(
}
}
- if (attribute.name === 'slot' && !isComponent && isDynamic(attribute)) {
- validator.error(
- `slot attribute cannot have a dynamic value`,
- attribute.start
- );
+ if (attribute.name === 'slot' && !isComponent) {
+ let i = stack.length;
+ while (i--) {
+ const parent = stack[i];
+ if (parent.type === 'Element' && validator.components.has(parent.name)) break;
+ if (parent.type === 'IfBlock' || parent.type === 'EachBlock') {
+ const message = `Cannot place slotted elements inside an ${parent.type === 'IfBlock' ? 'if' : 'each'}-block`;
+ validator.error(message, attribute.start);
+ }
+ }
+
+ if (isDynamic(attribute)) {
+ validator.error(
+ `slot attribute cannot have a dynamic value`,
+ attribute.start
+ );
+ }
}
}
});
diff --git a/test/validator/samples/component-slotted-each-block/errors.json b/test/validator/samples/component-slotted-each-block/errors.json
new file mode 100644
index 0000000000..eca404b1e2
--- /dev/null
+++ b/test/validator/samples/component-slotted-each-block/errors.json
@@ -0,0 +1,8 @@
+[{
+ "message": "Cannot place slotted elements inside an each-block",
+ "loc": {
+ "line": 3,
+ "column": 7
+ },
+ "pos": 43
+}]
\ No newline at end of file
diff --git a/test/validator/samples/component-slotted-each-block/input.html b/test/validator/samples/component-slotted-each-block/input.html
new file mode 100644
index 0000000000..81166f3e54
--- /dev/null
+++ b/test/validator/samples/component-slotted-each-block/input.html
@@ -0,0 +1,15 @@
+
+ {{#each things as thing}}
+ {{thing}}
+ {{/each}}
+
+
+
\ No newline at end of file
diff --git a/test/validator/samples/component-slotted-if-block/errors.json b/test/validator/samples/component-slotted-if-block/errors.json
new file mode 100644
index 0000000000..7a3a34b9f7
--- /dev/null
+++ b/test/validator/samples/component-slotted-if-block/errors.json
@@ -0,0 +1,8 @@
+[{
+ "message": "Cannot place slotted elements inside an if-block",
+ "loc": {
+ "line": 3,
+ "column": 7
+ },
+ "pos": 31
+}]
\ No newline at end of file
diff --git a/test/validator/samples/component-slotted-if-block/input.html b/test/validator/samples/component-slotted-if-block/input.html
new file mode 100644
index 0000000000..d063ab03aa
--- /dev/null
+++ b/test/validator/samples/component-slotted-if-block/input.html
@@ -0,0 +1,15 @@
+
+ {{#if thing}}
+ {{thing}}
+ {{/if}}
+
+
+
\ No newline at end of file