From abdce5c249828dfa42b55db643e6e937615ffaa0 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 23 Nov 2017 08:56:29 -0500 Subject: [PATCH] validate slot attribute placement --- .../shared/utils/isChildOfComponent.ts | 3 -- src/validate/html/validateElement.ts | 45 ++++++++++++------- .../slot-attribute-invalid/errors.json | 8 ++++ .../samples/slot-attribute-invalid/input.html | 1 + 4 files changed, 37 insertions(+), 20 deletions(-) create mode 100644 test/validator/samples/slot-attribute-invalid/errors.json create mode 100644 test/validator/samples/slot-attribute-invalid/input.html diff --git a/src/generators/shared/utils/isChildOfComponent.ts b/src/generators/shared/utils/isChildOfComponent.ts index 3246d27d35..2de64c4860 100644 --- a/src/generators/shared/utils/isChildOfComponent.ts +++ b/src/generators/shared/utils/isChildOfComponent.ts @@ -7,7 +7,4 @@ export default function isChildOfComponent(node: Node, generator: Generator) { 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/src/validate/html/validateElement.ts b/src/validate/html/validateElement.ts index 461b5aa52f..0d67b8002d 100644 --- a/src/validate/html/validateElement.ts +++ b/src/validate/html/validateElement.ts @@ -1,6 +1,6 @@ import * as namespaces from '../../utils/namespaces'; import validateEventHandler from './validateEventHandler'; -import { Validator } from '../index'; +import validate, { Validator } from '../index'; import { Node } from '../../interfaces'; const svg = /^(?:altGlyph|altGlyphDef|altGlyphItem|animate|animateColor|animateMotion|animateTransform|circle|clipPath|color-profile|cursor|defs|desc|discard|ellipse|feBlend|feColorMatrix|feComponentTransfer|feComposite|feConvolveMatrix|feDiffuseLighting|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|feMergeNode|feMorphology|feOffset|fePointLight|feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|foreignObject|g|glyph|glyphRef|hatch|hatchpath|hkern|image|line|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|metadata|missing-glyph|mpath|path|pattern|polygon|polyline|radialGradient|rect|set|solidcolor|stop|switch|symbol|text|textPath|title|tref|tspan|unknown|use|view|vkern)$/; @@ -191,22 +191,7 @@ export default function validateElement( } 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 - ); - } + checkSlotAttribute(validator, node, attribute, stack); } } }); @@ -232,6 +217,32 @@ function checkTypeAttribute(validator: Validator, node: Node) { return attribute.value[0].data; } +function checkSlotAttribute(validator: Validator, node: Node, attribute: Node, stack: Node[]) { + if (isDynamic(attribute)) { + validator.error( + `slot attribute cannot have a dynamic value`, + attribute.start + ); + } + + let i = stack.length; + while (i--) { + const parent = stack[i]; + if (parent.type === 'Element') { + // if we're inside a component or a custom element, gravy + if (validator.components.has(parent.name)) return; + if (/-/.test(parent.name)) return; + } + + 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); + } + } + + validator.error(`Element with a slot='...' attribute must be a descendant of a component or custom element`, attribute.start); +} + function isDynamic(attribute: Node) { return attribute.value.length > 1 || attribute.value[0].type !== 'Text'; } diff --git a/test/validator/samples/slot-attribute-invalid/errors.json b/test/validator/samples/slot-attribute-invalid/errors.json new file mode 100644 index 0000000000..aea1fa7db1 --- /dev/null +++ b/test/validator/samples/slot-attribute-invalid/errors.json @@ -0,0 +1,8 @@ +[{ + "message": "Element with a slot='...' attribute must be a descendant of a component or custom element", + "loc": { + "line": 1, + "column": 5 + }, + "pos": 5 +}] diff --git a/test/validator/samples/slot-attribute-invalid/input.html b/test/validator/samples/slot-attribute-invalid/input.html new file mode 100644 index 0000000000..ed407bb894 --- /dev/null +++ b/test/validator/samples/slot-attribute-invalid/input.html @@ -0,0 +1 @@ +
invalid
\ No newline at end of file