import Node from './shared/Node'; import Attribute from './Attribute'; import map_children from './shared/map_children'; import Binding from './Binding'; import EventHandler from './EventHandler'; import Expression from './shared/Expression'; import Component from '../Component'; import Let from './Let'; import TemplateScope from './shared/TemplateScope'; import { INode } from './interfaces'; import { TemplateNode } from '../../interfaces'; export default class InlineComponent extends Node { type: 'InlineComponent'; name: string; expression: Expression; attributes: Attribute[] = []; bindings: Binding[] = []; handlers: EventHandler[] = []; lets: Let[] = []; children: INode[]; scope: TemplateScope; constructor(component: Component, parent: Node, scope: TemplateScope, info: TemplateNode) { super(component, parent, scope, info); if (info.name !== 'svelte:component' && info.name !== 'svelte:self') { const name = info.name.split('.')[0]; // accommodate namespaces component.warn_if_undefined(name, info, scope); component.add_reference(name); } this.name = info.name; this.expression = this.name === 'svelte:component' ? new Expression(component, this, scope, info.expression) : null; info.attributes.forEach(node => { /* eslint-disable no-fallthrough */ switch (node.type) { case 'Action': component.error(node, { code: 'invalid-action', message: 'Actions can only be applied to DOM elements, not components' }); case 'Attribute': if (node.name === 'slot') { component.error(node, { code: 'invalid-prop', message: "'slot' is reserved for future use in named slots" }); } // fallthrough case 'Spread': this.attributes.push(new Attribute(component, this, scope, node)); break; case 'Binding': this.bindings.push(new Binding(component, this, scope, node)); break; case 'Class': component.error(node, { code: 'invalid-class', message: 'Classes can only be applied to DOM elements, not components' }); case 'EventHandler': this.handlers.push(new EventHandler(component, this, scope, node)); break; case 'Let': this.lets.push(new Let(component, this, scope, node)); break; case 'Transition': component.error(node, { code: 'invalid-transition', message: 'Transitions can only be applied to DOM elements, not components' }); default: throw new Error(`Not implemented: ${node.type}`); } /* eslint-enable no-fallthrough */ }); if (this.lets.length > 0) { this.scope = scope.child(); this.lets.forEach(l => { const dependencies = new Set([l.name.name]); l.names.forEach(name => { this.scope.add(name, dependencies, this); }); }); } else { this.scope = scope; } this.handlers.forEach(handler => { handler.modifiers.forEach(modifier => { if (modifier !== 'once') { component.error(handler, { code: 'invalid-event-modifier', message: "Event modifiers other than 'once' can only be used on DOM elements" }); } }); }); this.children = map_children(component, this, this.scope, info.children); } }