import * as namespaces from '../../utils/namespaces'; import validateEventHandler from './validateEventHandler'; import { 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)$/; export default function validateElement( validator: Validator, node: Node, refs: Map, refCallees: Node[], elementStack: Node[] ) { const isComponent = node.name === ':Self' || validator.components.has(node.name); if (!isComponent && /^[A-Z]/.test(node.name[0])) { // TODO upgrade to validator.error in v2 validator.warn(`${node.name} component is not defined`, node.start); } if (elementStack.length === 0 && validator.namespace !== namespaces.svg && svg.test(node.name)) { validator.warn( `<${node.name}> is an SVG element – did you forget to add { namespace: 'svg' } ?`, node.start ); } if (node.name === 'slot') { const nameAttribute = node.attributes.find((attribute: Node) => attribute.name === 'name'); if (nameAttribute) { if (nameAttribute.value.length !== 1 || nameAttribute.value[0].type !== 'Text') { validator.error(` name cannot be dynamic`, nameAttribute.start); } const slotName = nameAttribute.value[0].data; if (slotName === 'default') { validator.error(`default is a reserved word — it cannot be used as a slot name`, nameAttribute.start); } // TODO should duplicate slots be disallowed? Feels like it's more likely to be a // bug than anything. Perhaps it should be a warning // if (validator.slots.has(slotName)) { // validator.error(`duplicate '${slotName}' element`, nameAttribute.start); // } // validator.slots.add(slotName); } else { // if (validator.slots.has('default')) { // validator.error(`duplicate default element`, node.start); // } // validator.slots.add('default'); } } let hasIntro: boolean; let hasOutro: boolean; let hasTransition: boolean; node.attributes.forEach((attribute: Node) => { if (attribute.type === 'Ref') { if (!refs.has(attribute.name)) refs.set(attribute.name, []); refs.get(attribute.name).push(node); } if (!isComponent && attribute.type === 'Binding') { const { name } = attribute; if (name === 'value') { if ( node.name !== 'input' && node.name !== 'textarea' && node.name !== 'select' ) { validator.error( `'value' is not a valid binding on <${node.name}> elements`, attribute.start ); } checkTypeAttribute(validator, node); } else if (name === 'checked') { if (node.name !== 'input') { validator.error( `'checked' is not a valid binding on <${node.name}> elements`, attribute.start ); } if (checkTypeAttribute(validator, node) !== 'checkbox') { validator.error( `'checked' binding can only be used with `, attribute.start ); } } else if (name === 'group') { if (node.name !== 'input') { validator.error( `'group' is not a valid binding on <${node.name}> elements`, attribute.start ); } const type = checkTypeAttribute(validator, node); if (type !== 'checkbox' && type !== 'radio') { validator.error( `'checked' binding can only be used with or `, attribute.start ); } } else if ( name === 'currentTime' || name === 'duration' || name === 'paused' ) { if (node.name !== 'audio' && node.name !== 'video') { validator.error( `'${name}' binding can only be used with