|
import Node from './shared/Node.js'; |
|
import Attribute from './Attribute.js'; |
|
import map_children from './shared/map_children.js'; |
|
import Binding from './Binding.js'; |
|
import EventHandler from './EventHandler.js'; |
|
import Expression from './shared/Expression.js'; |
|
import compiler_errors from '../compiler_errors.js'; |
|
import { regex_only_whitespaces } from '../../utils/patterns.js'; |
|
|
|
|
|
export default class InlineComponent extends Node { |
|
|
|
name; |
|
|
|
|
|
expression; |
|
|
|
|
|
bindings = []; |
|
|
|
|
|
handlers = []; |
|
|
|
|
|
css_custom_properties = []; |
|
|
|
|
|
children; |
|
|
|
|
|
scope; |
|
|
|
|
|
namespace; |
|
|
|
|
|
let_attributes; |
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor(component, parent, scope, info) { |
|
super(component, parent, scope, info); |
|
this.cannot_use_innerhtml(); |
|
this.not_static_content(); |
|
if (info.name !== 'svelte:component' && info.name !== 'svelte:self') { |
|
const name = info.name.split('.')[0]; |
|
component.warn_if_undefined(name, info, scope); |
|
component.add_reference( (this), name); |
|
} |
|
this.name = info.name; |
|
this.namespace = get_namespace(parent, component.namespace); |
|
this.expression = |
|
this.name === 'svelte:component' |
|
? new Expression(component, this, scope, info.expression) |
|
: null; |
|
|
|
const let_attributes = (this.let_attributes = []); |
|
info.attributes.forEach( |
|
( |
|
node |
|
) => { |
|
|
|
switch (node.type) { |
|
case 'Action': |
|
return component.error(node, compiler_errors.invalid_action); |
|
case 'Attribute': |
|
if (node.name.startsWith('--')) { |
|
this.css_custom_properties.push(new Attribute(component, this, scope, node)); |
|
break; |
|
} |
|
|
|
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': |
|
return component.error(node, compiler_errors.invalid_class); |
|
case 'EventHandler': |
|
this.handlers.push(new EventHandler(component, this, scope, node)); |
|
break; |
|
case 'Let': |
|
let_attributes.push(node); |
|
break; |
|
case 'Transition': |
|
return component.error(node, compiler_errors.invalid_transition); |
|
case 'StyleDirective': |
|
return component.error(node, compiler_errors.invalid_component_style_directive); |
|
case 'Animation': |
|
return component.error(node, compiler_errors.invalid_animation); |
|
default: |
|
throw new Error(`Not implemented: ${node.type}`); |
|
} |
|
|
|
} |
|
); |
|
|
|
this.scope = scope; |
|
|
|
this.handlers.forEach((handler) => { |
|
handler.modifiers.forEach((modifier) => { |
|
if (modifier !== 'once') { |
|
return component.error(handler, compiler_errors.invalid_event_modifier_component); |
|
} |
|
}); |
|
}); |
|
const children = []; |
|
for (let i = info.children.length - 1; i >= 0; i--) { |
|
const child = info.children[i]; |
|
if (child.type === 'SlotTemplate') { |
|
children.push(child); |
|
info.children.splice(i, 1); |
|
} else if ( |
|
(child.type === 'Element' || child.type === 'InlineComponent' || child.type === 'Slot') && |
|
child.attributes.find((attribute) => attribute.name === 'slot') |
|
) { |
|
const slot_template = { |
|
start: child.start, |
|
end: child.end, |
|
type: 'SlotTemplate', |
|
name: 'svelte:fragment', |
|
attributes: [], |
|
children: [child] |
|
}; |
|
|
|
for (let i = child.attributes.length - 1; i >= 0; i--) { |
|
const attribute = child.attributes[i]; |
|
if (attribute.type === 'Let') { |
|
slot_template.attributes.push(attribute); |
|
child.attributes.splice(i, 1); |
|
} else if (attribute.type === 'Attribute' && attribute.name === 'slot') { |
|
slot_template.attributes.push(attribute); |
|
} |
|
} |
|
|
|
for (let i = child.children.length - 1; i >= 0; i--) { |
|
const child_child = child.children[i]; |
|
if (child_child.type === 'ConstTag') { |
|
slot_template.children.push(child_child); |
|
child.children.splice(i, 1); |
|
} |
|
} |
|
children.push(slot_template); |
|
info.children.splice(i, 1); |
|
} else if (child.type === 'Comment' && children.length > 0) { |
|
children[children.length - 1].children.unshift(child); |
|
} |
|
} |
|
if (info.children.some((node) => not_whitespace_text(node))) { |
|
children.push({ |
|
start: info.start, |
|
end: info.end, |
|
type: 'SlotTemplate', |
|
name: 'svelte:fragment', |
|
attributes: [], |
|
children: info.children |
|
}); |
|
} |
|
|
|
if (let_attributes.length) { |
|
|
|
|
|
children.forEach((child) => { |
|
const slot = child.attributes.find((attribute) => attribute.name === 'slot'); |
|
if (!slot || slot.value[0].data === 'default') { |
|
child.attributes.push(...let_attributes); |
|
} |
|
}); |
|
} |
|
|
|
this.children = map_children(component, this, this.scope, children); |
|
} |
|
get slot_template_name() { |
|
return ( |
|
this.attributes.find((attribute) => attribute.name === 'slot').get_static_value() |
|
); |
|
} |
|
} |
|
|
|
|
|
function not_whitespace_text(node) { |
|
return !(node.type === 'Text' && regex_only_whitespaces.test(node.data)); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function get_namespace(parent, explicit_namespace) { |
|
const parent_element = parent.find_nearest(/^Element/); |
|
if (!parent_element) { |
|
return explicit_namespace; |
|
} |
|
return parent_element.namespace; |
|
} |
|
|