diff --git a/src/compiler/compile/nodes/Element.ts b/src/compiler/compile/nodes/Element.ts index 7a70e603a77f..d5ecbe44dadc 100644 --- a/src/compiler/compile/nodes/Element.ts +++ b/src/compiler/compile/nodes/Element.ts @@ -807,6 +807,11 @@ export default class Element extends Node { ); } } + + get slot_template_name() { + // attribute.get_static_value + return this.attributes.find(attribute => attribute.name === 'slot').get_static_value() as string; + } } function should_have_attribute( diff --git a/src/compiler/compile/nodes/SlotTemplate.ts b/src/compiler/compile/nodes/SlotTemplate.ts index 214f081dab59..3f44aeaae497 100644 --- a/src/compiler/compile/nodes/SlotTemplate.ts +++ b/src/compiler/compile/nodes/SlotTemplate.ts @@ -12,7 +12,7 @@ export default class SlotTemplate extends Node { children: INode[]; lets: Let[] = []; slot_attribute: Attribute; - slot_name: string = 'default'; + slot_template_name: string = 'default'; constructor(component: Component, parent: INode, scope: TemplateScope, info: any) { super(component, parent, scope, info); @@ -52,7 +52,7 @@ export default class SlotTemplate extends Node { message: `slot attribute value is missing` }); } - this.slot_name = this.slot_attribute.get_static_value() as string; + this.slot_template_name = this.slot_attribute.get_static_value() as string; break; } throw new Error(`Invalid attribute "${node.name}" in `); @@ -70,7 +70,7 @@ export default class SlotTemplate extends Node { if (this.parent.type !== 'InlineComponent') { this.component.error(this, { code: `invalid-slotted-content`, - message: `Element with a slot='...' attribute must be a child of a component` + message: ` must be a child of a component` }); } } diff --git a/src/compiler/compile/nodes/shared/TemplateScope.ts b/src/compiler/compile/nodes/shared/TemplateScope.ts index 4e087eedf5d5..df694ed45ec7 100644 --- a/src/compiler/compile/nodes/shared/TemplateScope.ts +++ b/src/compiler/compile/nodes/shared/TemplateScope.ts @@ -3,8 +3,9 @@ import ThenBlock from '../ThenBlock'; import CatchBlock from '../CatchBlock'; import InlineComponent from '../InlineComponent'; import Element from '../Element'; +import SlotTemplate from '../SlotTemplate'; -type NodeWithScope = EachBlock | ThenBlock | CatchBlock | InlineComponent | Element; +type NodeWithScope = EachBlock | ThenBlock | CatchBlock | InlineComponent | Element | SlotTemplate; export default class TemplateScope { names: Set; @@ -40,7 +41,7 @@ export default class TemplateScope { is_let(name: string) { const owner = this.get_owner(name); - return owner && (owner.type === 'Element' || owner.type === 'InlineComponent'); + return owner && (owner.type === 'Element' || owner.type === 'InlineComponent' || owner.type === 'SlotTemplate'); } is_await(name: string) { diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index 148f837ee6d7..fc548d7e9523 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -2,7 +2,7 @@ import Renderer from '../../Renderer'; import Element from '../../../nodes/Element'; import Wrapper from '../shared/Wrapper'; import Block from '../../Block'; -import { is_void, sanitize } from '../../../../utils/names'; +import { is_void } from '../../../../utils/names'; import FragmentWrapper from '../Fragment'; import { escape_html, string_literal } from '../../../utils/stringify'; import TextWrapper from '../Text'; @@ -13,12 +13,9 @@ import AttributeWrapper from './Attribute'; import StyleAttributeWrapper from './StyleAttribute'; import { dimensions } from '../../../../utils/patterns'; import Binding from './Binding'; -import InlineComponentWrapper from '../InlineComponent'; import add_to_set from '../../../utils/add_to_set'; import { add_event_handler } from '../shared/add_event_handlers'; import { add_action } from '../shared/add_actions'; -import create_debugging_comment from '../shared/create_debugging_comment'; -import { get_slot_definition } from '../shared/get_slot_definition'; import bind_this from '../shared/bind_this'; import { is_head } from '../shared/is_head'; import { Identifier } from 'estree'; @@ -175,49 +172,6 @@ export default class ElementWrapper extends Wrapper { } this.attributes = this.node.attributes.map(attribute => { - if (attribute.name === 'slot') { - // TODO make separate subclass for this? - let owner = this.parent; - while (owner) { - if (owner.node.type === 'InlineComponent') { - break; - } - - if (owner.node.type === 'Element' && /-/.test(owner.node.name)) { - break; - } - - owner = owner.parent; - } - - if (owner && owner.node.type === 'InlineComponent') { - const name = attribute.get_static_value() as string; - - if (!(owner as unknown as InlineComponentWrapper).slots.has(name)) { - const child_block = block.child({ - comment: create_debugging_comment(node, this.renderer.component), - name: this.renderer.component.get_unique_name(`create_${sanitize(name)}_slot`), - type: 'slot' - }); - - const { scope, lets } = this.node; - const seen = new Set(lets.map(l => l.name.name)); - - (owner as unknown as InlineComponentWrapper).node.lets.forEach(l => { - if (!seen.has(l.name.name)) lets.push(l); - }); - - (owner as unknown as InlineComponentWrapper).slots.set( - name, - get_slot_definition(child_block, scope, lets) - ); - this.renderer.blocks.push(child_block); - } - - this.slot_block = (owner as unknown as InlineComponentWrapper).slots.get(name).block; - block = this.slot_block; - } - } if (attribute.name === 'style') { return new StyleAttributeWrapper(this, block, attribute); } @@ -269,15 +223,6 @@ export default class ElementWrapper extends Wrapper { } this.fragment = new FragmentWrapper(renderer, block, node.children, this, strip_whitespace, next_sibling); - - if (this.slot_block) { - block.parent.add_dependencies(block.dependencies); - - // appalling hack - const index = block.parent.wrappers.indexOf(this); - block.parent.wrappers.splice(index, 1); - block.wrappers.push(this); - } } render(block: Block, parent_node: Identifier, parent_nodes: Identifier) { @@ -285,10 +230,6 @@ export default class ElementWrapper extends Wrapper { if (this.node.name === 'noscript') return; - if (this.slot_block) { - block = this.slot_block; - } - const node = this.var; const nodes = parent_nodes && block.get_unique_name(`${this.var.name}_nodes`); // if we're in unclaimable territory, i.e. , parent_nodes is null const children = x`@children(${this.node.name === 'template' ? x`${node}.content` : node})`; diff --git a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts index 271b3de1e1c3..10e934647f33 100644 --- a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts +++ b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts @@ -4,6 +4,7 @@ import Renderer from '../../Renderer'; import Block from '../../Block'; import InlineComponent from '../../../nodes/InlineComponent'; import FragmentWrapper from '../Fragment'; +import SlotTemplateWrapper from '../SlotTemplate'; import { sanitize } from '../../../../utils/names'; import add_to_set from '../../../utils/add_to_set'; import { b, x, p } from 'code-red'; @@ -18,11 +19,15 @@ import EventHandler from '../Element/EventHandler'; import { extract_names } from 'periscopic'; import mark_each_block_bindings from '../shared/mark_each_block_bindings'; +type SlotDefinition = { block: Block; scope: TemplateScope; get_context?: Node; get_changes?: Node }; + export default class InlineComponentWrapper extends Wrapper { var: Identifier; - slots: Map = new Map(); + slots: Map = new Map(); node: InlineComponent; fragment: FragmentWrapper; + children: Array = []; + default_slot_block: Block; constructor( renderer: Renderer, @@ -75,6 +80,23 @@ export default class InlineComponentWrapper extends Wrapper { }); }); + const children = this.node.children.slice(); + for (let i=children.length - 1; i>=0;) { + const child = children[i]; + if (child.type === 'SlotTemplate' || (child.type === 'Element' && child.attributes.find(attribute => attribute.name === 'slot'))) { + const slot_template = new SlotTemplateWrapper(renderer, block, this, child, strip_whitespace, next_sibling); + this.children.push(slot_template); + children.splice(i, 1); + continue; + } + + i--; + } + + if (this.slots.has('default') && children.filter(node => !(node.type === 'Text' && node.data.trim() === ''))) { + throw new Error('Found elements without slot attribute when using slot="default"'); + } + const default_slot = block.child({ comment: create_debugging_comment(node, renderer.component), name: renderer.component.get_unique_name(`create_default_slot`), @@ -82,9 +104,11 @@ export default class InlineComponentWrapper extends Wrapper { }); this.renderer.blocks.push(default_slot); + this.default_slot_block = default_slot; this.slots.set('default', get_slot_definition(default_slot, this.node.scope, this.node.lets)); - this.fragment = new FragmentWrapper(renderer, default_slot, node.children, this, strip_whitespace, next_sibling); + const fragment = new FragmentWrapper(renderer, default_slot, children, this, strip_whitespace, next_sibling); + this.children.push(fragment); const dependencies: Set = new Set(); @@ -101,6 +125,13 @@ export default class InlineComponentWrapper extends Wrapper { block.add_outro(); } + set_slot(name: string, slot_definition: SlotDefinition) { + if (this.slots.has(name)) { + throw new Error(`Duplicate slot name "${name}" in <${this.node.name}>`); + } + this.slots.set(name, slot_definition); + } + warn_if_reactive() { const { name } = this.node; const variable = this.renderer.component.var_lookup.get(name); @@ -134,14 +165,10 @@ export default class InlineComponentWrapper extends Wrapper { const statements: Array = []; const updates: Array = []; - if (this.fragment) { + this.children.forEach((child) => { this.renderer.add_to_context('$$scope', true); - const default_slot = this.slots.get('default'); - - this.fragment.nodes.forEach((child) => { - child.render(default_slot.block, null, x`#nodes` as unknown as Identifier); - }); - } + child.render(this.default_slot_block, null, x`#nodes` as Identifier); + }); let props; const name_changes = block.get_unique_name(`${name.name}_changes`); @@ -193,7 +220,7 @@ export default class InlineComponentWrapper extends Wrapper { component_opts.properties.push(p`$$inline: true`); } - const fragment_dependencies = new Set(this.fragment ? ['$$scope'] : []); + const fragment_dependencies = new Set(this.slots.size ? ['$$scope'] : []); this.slots.forEach(slot => { slot.block.dependencies.forEach(name => { const is_let = slot.scope.is_let(name); diff --git a/src/compiler/compile/render_dom/wrappers/SlotTemplate.ts b/src/compiler/compile/render_dom/wrappers/SlotTemplate.ts index d7b390f0fcf6..4effe62a249c 100644 --- a/src/compiler/compile/render_dom/wrappers/SlotTemplate.ts +++ b/src/compiler/compile/render_dom/wrappers/SlotTemplate.ts @@ -1,63 +1,77 @@ import Wrapper from "./shared/Wrapper"; import Renderer from "../Renderer"; import Block from "../Block"; -import SlotTemplate from "../../nodes/SlotTemplate"; import FragmentWrapper from "./Fragment"; import create_debugging_comment from "./shared/create_debugging_comment"; -import { get_slot_definition } from './shared/get_slot_definition'; +import { get_slot_definition } from "./shared/get_slot_definition"; import { x } from "code-red"; import { sanitize } from "../../../utils/names"; import { Identifier } from "estree"; import InlineComponentWrapper from "./InlineComponent"; +import { extract_names } from "periscopic"; +import { INode } from "../../nodes/interfaces"; +import Let from "../../nodes/Let"; +import TemplateScope from "../../nodes/shared/TemplateScope"; -export default class SlotWrapper extends Wrapper { - node: SlotTemplate; +type NodeWithLets = INode & { + scope: TemplateScope; + lets: Let[]; + slot_template_name: string; +}; + +export default class SlotTemplateWrapper extends Wrapper { + node: NodeWithLets; fragment: FragmentWrapper; block: Block; + parent: InlineComponentWrapper; constructor( renderer: Renderer, block: Block, parent: Wrapper, - node: SlotTemplate, + node: NodeWithLets, strip_whitespace: boolean, next_sibling: Wrapper ) { super(renderer, block, parent, node); - const { scope, lets, slot_name } = this.node; + const { scope, lets, slot_template_name } = this.node; + + lets.forEach(l => { + extract_names(l.value || l.name).forEach(name => { + renderer.add_to_context(name, true); + }); + }); this.block = block.child({ - comment: create_debugging_comment(node, this.renderer.component), + comment: create_debugging_comment(this.node, this.renderer.component), name: this.renderer.component.get_unique_name( - `create_${sanitize(slot_name)}_slot` + `create_${sanitize(slot_template_name)}_slot` ), type: "slot" }); this.renderer.blocks.push(this.block); - if (this.parent.node.type === "InlineComponent") { - const component = (this.parent as unknown) as InlineComponentWrapper; - const seen = new Set(lets.map(l => l.name.name)); - component.node.lets.forEach(l => { - if (!seen.has(l.name.name)) lets.push(l); - }); - - component.slots.set( - slot_name, - get_slot_definition(this.block, scope, lets) - ); - } + const seen = new Set(lets.map(l => l.name.name)); + this.parent.node.lets.forEach(l => { + if (!seen.has(l.name.name)) lets.push(l); + }); - this.fragment = new FragmentWrapper(renderer, this.block, node.children, this, strip_whitespace, next_sibling); + this.parent.set_slot( + slot_template_name, + get_slot_definition(this.block, scope, lets) + ); + this.fragment = new FragmentWrapper( + renderer, + this.block, + node.type === "SlotTemplate" ? node.children : [node], + this, + strip_whitespace, + next_sibling + ); this.block.parent.add_dependencies(this.block.dependencies); - - // appalling hack, copied from ElementWrapper - const index = this.block.parent.wrappers.indexOf(this); - this.block.parent.wrappers.splice(index, 1); - this.block.wrappers.push(this); } render() { diff --git a/src/compiler/compile/render_ssr/handlers/SlotTemplate.ts b/src/compiler/compile/render_ssr/handlers/SlotTemplate.ts index 688cf3169793..71680494a511 100644 --- a/src/compiler/compile/render_ssr/handlers/SlotTemplate.ts +++ b/src/compiler/compile/render_ssr/handlers/SlotTemplate.ts @@ -19,7 +19,7 @@ export default function(node: SlotTemplate, renderer: Renderer, options: RenderO if (!seen.has(l.name.name)) lets.push(l); }); - options.slot_scopes.set(node.slot_name, { + options.slot_scopes.set(node.slot_template_name, { input: get_slot_scope(node.lets), output: renderer.pop() }); diff --git a/test/parser/samples/error-svelte-selfdestructive/error.json b/test/parser/samples/error-svelte-selfdestructive/error.json index 2443bb982207..dcec676d78ae 100644 --- a/test/parser/samples/error-svelte-selfdestructive/error.json +++ b/test/parser/samples/error-svelte-selfdestructive/error.json @@ -1,6 +1,6 @@ { "code": "invalid-tag-name", - "message": "Valid tag names are svelte:head, svelte:options, svelte:window, svelte:body, svelte:self or svelte:component", + "message": "Valid tag names are svelte:head, svelte:options, svelte:window, svelte:body, svelte:self, svelte:component or svelte:slot", "pos": 10, "start": { "character": 10, diff --git a/test/runtime/samples/component-slot-duplicate-error-2/Nested.svelte b/test/runtime/samples/component-slot-duplicate-error-2/Nested.svelte new file mode 100644 index 000000000000..32eee1534acb --- /dev/null +++ b/test/runtime/samples/component-slot-duplicate-error-2/Nested.svelte @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-duplicate-error-2/_config.js b/test/runtime/samples/component-slot-duplicate-error-2/_config.js new file mode 100644 index 000000000000..1c9fa51dc3d3 --- /dev/null +++ b/test/runtime/samples/component-slot-duplicate-error-2/_config.js @@ -0,0 +1,3 @@ +export default { + error: 'Duplicate slot name "foo" in ' +}; diff --git a/test/runtime/samples/component-slot-duplicate-error-2/main.svelte b/test/runtime/samples/component-slot-duplicate-error-2/main.svelte new file mode 100644 index 000000000000..6e423953badc --- /dev/null +++ b/test/runtime/samples/component-slot-duplicate-error-2/main.svelte @@ -0,0 +1,8 @@ + + + + {value} + {value} + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-duplicate-error-3/Nested.svelte b/test/runtime/samples/component-slot-duplicate-error-3/Nested.svelte new file mode 100644 index 000000000000..32eee1534acb --- /dev/null +++ b/test/runtime/samples/component-slot-duplicate-error-3/Nested.svelte @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-duplicate-error-3/_config.js b/test/runtime/samples/component-slot-duplicate-error-3/_config.js new file mode 100644 index 000000000000..1c9fa51dc3d3 --- /dev/null +++ b/test/runtime/samples/component-slot-duplicate-error-3/_config.js @@ -0,0 +1,3 @@ +export default { + error: 'Duplicate slot name "foo" in ' +}; diff --git a/test/runtime/samples/component-slot-duplicate-error-3/main.svelte b/test/runtime/samples/component-slot-duplicate-error-3/main.svelte new file mode 100644 index 000000000000..22c10d7d4331 --- /dev/null +++ b/test/runtime/samples/component-slot-duplicate-error-3/main.svelte @@ -0,0 +1,8 @@ + + + + {value} +

{value}

+
\ No newline at end of file diff --git a/test/runtime/samples/component-slot-duplicate-error-4/Nested.svelte b/test/runtime/samples/component-slot-duplicate-error-4/Nested.svelte new file mode 100644 index 000000000000..0385342cef1b --- /dev/null +++ b/test/runtime/samples/component-slot-duplicate-error-4/Nested.svelte @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-duplicate-error-4/_config.js b/test/runtime/samples/component-slot-duplicate-error-4/_config.js new file mode 100644 index 000000000000..cfe8e7054e0e --- /dev/null +++ b/test/runtime/samples/component-slot-duplicate-error-4/_config.js @@ -0,0 +1,3 @@ +export default { + error: 'Found elements without slot attribute when using slot="default"' +}; diff --git a/test/runtime/samples/component-slot-duplicate-error-4/main.svelte b/test/runtime/samples/component-slot-duplicate-error-4/main.svelte new file mode 100644 index 000000000000..559e17b86e0e --- /dev/null +++ b/test/runtime/samples/component-slot-duplicate-error-4/main.svelte @@ -0,0 +1,8 @@ + + + + value +

value

+
\ No newline at end of file diff --git a/test/runtime/samples/component-slot-duplicate-error/Nested.svelte b/test/runtime/samples/component-slot-duplicate-error/Nested.svelte new file mode 100644 index 000000000000..32eee1534acb --- /dev/null +++ b/test/runtime/samples/component-slot-duplicate-error/Nested.svelte @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-duplicate-error/_config.js b/test/runtime/samples/component-slot-duplicate-error/_config.js new file mode 100644 index 000000000000..1c9fa51dc3d3 --- /dev/null +++ b/test/runtime/samples/component-slot-duplicate-error/_config.js @@ -0,0 +1,3 @@ +export default { + error: 'Duplicate slot name "foo" in ' +}; diff --git a/test/runtime/samples/component-slot-duplicate-error/main.svelte b/test/runtime/samples/component-slot-duplicate-error/main.svelte new file mode 100644 index 000000000000..9024bcd46f65 --- /dev/null +++ b/test/runtime/samples/component-slot-duplicate-error/main.svelte @@ -0,0 +1,8 @@ + + + +

{value}

+

{value}

+
\ No newline at end of file diff --git a/test/runtime/samples/component-slot-warning/_config.js b/test/runtime/samples/component-slot-warning/_config.js index 6ffe624782e0..c1785f27640e 100644 --- a/test/runtime/samples/component-slot-warning/_config.js +++ b/test/runtime/samples/component-slot-warning/_config.js @@ -3,7 +3,6 @@ export default { dev: true }, warnings: [ - ' received an unexpected slot "default".', ' received an unexpected slot "slot1".' ] }; diff --git a/test/runtime/samples/component-svelte-slot-2/Nested.svelte b/test/runtime/samples/component-svelte-slot-2/Nested.svelte new file mode 100644 index 000000000000..a94392ce5d82 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-2/Nested.svelte @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-2/_config.js b/test/runtime/samples/component-svelte-slot-2/_config.js new file mode 100644 index 000000000000..9dfed6ff531e --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-2/_config.js @@ -0,0 +1,6 @@ +export default { + html: ` + Hello + world + ` +}; diff --git a/test/runtime/samples/component-svelte-slot-2/main.svelte b/test/runtime/samples/component-svelte-slot-2/main.svelte new file mode 100644 index 000000000000..9686ab39f125 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-2/main.svelte @@ -0,0 +1,15 @@ + + + + + Hello + + + + + + world + + \ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-aliased/Nested.svelte b/test/runtime/samples/component-svelte-slot-let-aliased/Nested.svelte new file mode 100644 index 000000000000..6eaf6fe3adcb --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-aliased/Nested.svelte @@ -0,0 +1,9 @@ + + +
+ {#each things as thing} + + {/each} +
\ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-aliased/_config.js b/test/runtime/samples/component-svelte-slot-let-aliased/_config.js new file mode 100644 index 000000000000..d66f613bb4b0 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-aliased/_config.js @@ -0,0 +1,24 @@ +export default { + props: { + things: [1, 2, 3] + }, + + html: ` +
+ 1 + 2 + 3 +
`, + + test({ assert, component, target }) { + component.things = [1, 2, 3, 4]; + assert.htmlEqual(target.innerHTML, ` +
+ 1 + 2 + 3 + 4 +
+ `); + } +}; diff --git a/test/runtime/samples/component-svelte-slot-let-aliased/main.svelte b/test/runtime/samples/component-svelte-slot-let-aliased/main.svelte new file mode 100644 index 000000000000..9362795d57cf --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-aliased/main.svelte @@ -0,0 +1,11 @@ + + + + + {x} + + \ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-b/Nested.svelte b/test/runtime/samples/component-svelte-slot-let-b/Nested.svelte new file mode 100644 index 000000000000..36c10ecbc215 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-b/Nested.svelte @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-b/_config.js b/test/runtime/samples/component-svelte-slot-let-b/_config.js new file mode 100644 index 000000000000..f13a3b2bb662 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-b/_config.js @@ -0,0 +1,18 @@ +export default { + html: ` + + 0 + `, + + async test({ assert, target, window }) { + const button = target.querySelector('button'); + const click = new window.MouseEvent('click'); + + await button.dispatchEvent(click); + + assert.htmlEqual(target.innerHTML, ` + + 1 + `); + } +}; diff --git a/test/runtime/samples/component-svelte-slot-let-b/main.svelte b/test/runtime/samples/component-svelte-slot-let-b/main.svelte new file mode 100644 index 000000000000..c0107a1493f3 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-b/main.svelte @@ -0,0 +1,9 @@ + + + + + {count} + + \ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-c/Nested.svelte b/test/runtime/samples/component-svelte-slot-let-c/Nested.svelte new file mode 100644 index 000000000000..122044059048 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-c/Nested.svelte @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-c/_config.js b/test/runtime/samples/component-svelte-slot-let-c/_config.js new file mode 100644 index 000000000000..fe8e68b1ac1e --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-c/_config.js @@ -0,0 +1,18 @@ +export default { + html: ` + + 0 (undefined) + `, + + async test({ assert, target, window }) { + const button = target.querySelector('button'); + const click = new window.MouseEvent('click'); + + await button.dispatchEvent(click); + + assert.htmlEqual(target.innerHTML, ` + + 1 (undefined) + `); + } +}; diff --git a/test/runtime/samples/component-svelte-slot-let-c/main.svelte b/test/runtime/samples/component-svelte-slot-let-c/main.svelte new file mode 100644 index 000000000000..54ae1bbf295f --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-c/main.svelte @@ -0,0 +1,9 @@ + + + + + {c} ({count}) + + \ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-d/Nested.svelte b/test/runtime/samples/component-svelte-slot-let-d/Nested.svelte new file mode 100644 index 000000000000..9f3a345acdd5 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-d/Nested.svelte @@ -0,0 +1,7 @@ + + +
+ +
\ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-d/_config.js b/test/runtime/samples/component-svelte-slot-let-d/_config.js new file mode 100644 index 000000000000..e293ae136c72 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-d/_config.js @@ -0,0 +1,20 @@ +export default { + html: ` +
+

a

+
+ `, + + async test({ assert, target, window }) { + const div = target.querySelector('div'); + const click = new window.MouseEvent('click'); + + await div.dispatchEvent(click); + + assert.htmlEqual(target.innerHTML, ` +
+

b

+
+ `); + } +}; diff --git a/test/runtime/samples/component-svelte-slot-let-d/main.svelte b/test/runtime/samples/component-svelte-slot-let-d/main.svelte new file mode 100644 index 000000000000..b7003724b036 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-d/main.svelte @@ -0,0 +1,9 @@ + + + + +

{bar}

+
+
\ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-destructured-2/Nested.svelte b/test/runtime/samples/component-svelte-slot-let-destructured-2/Nested.svelte new file mode 100644 index 000000000000..f46046c78e6f --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-destructured-2/Nested.svelte @@ -0,0 +1,5 @@ + + + diff --git a/test/runtime/samples/component-svelte-slot-let-destructured-2/_config.js b/test/runtime/samples/component-svelte-slot-let-destructured-2/_config.js new file mode 100644 index 000000000000..38b04b7b5e06 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-destructured-2/_config.js @@ -0,0 +1,68 @@ +export default { + html: ` +
+ hello world 0 hello + +
+
+ hello world 0 hello + +
+
+ hello world 0 hello + +
+ `, + async test({ assert, component, target, window }) { + const [button1, button2, button3] = target.querySelectorAll('button'); + const event = new window.MouseEvent('click'); + + await button1.dispatchEvent(event); + assert.htmlEqual(target.innerHTML, ` +
+ hello world 1 hello + +
+
+ hello world 0 hello + +
+
+ hello world 0 hello + +
+ `); + + await button2.dispatchEvent(event); + assert.htmlEqual(target.innerHTML, ` +
+ hello world 1 hello + +
+
+ hello world 1 hello + +
+
+ hello world 0 hello + +
+ `); + + await button3.dispatchEvent(event); + assert.htmlEqual(target.innerHTML, ` +
+ hello world 1 hello + +
+
+ hello world 1 hello + +
+
+ hello world 1 hello + +
+ `); + } +}; diff --git a/test/runtime/samples/component-svelte-slot-let-destructured-2/main.svelte b/test/runtime/samples/component-svelte-slot-let-destructured-2/main.svelte new file mode 100644 index 000000000000..734e6b139059 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-destructured-2/main.svelte @@ -0,0 +1,34 @@ + + +
+ + + {pair[0]} {pair[1]} {c} {foo} + + + + +
+ +
+ + + {a} {b} {d} {foo} + + + + +
+ +
+ + + {a} {b} {e} {foo} + + + + +
\ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-destructured/Nested.svelte b/test/runtime/samples/component-svelte-slot-let-destructured/Nested.svelte new file mode 100644 index 000000000000..99afb32aa068 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-destructured/Nested.svelte @@ -0,0 +1,9 @@ + + +
+ {#each things as thing} + + {/each} +
\ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-destructured/_config.js b/test/runtime/samples/component-svelte-slot-let-destructured/_config.js new file mode 100644 index 000000000000..d7aaae2d7735 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-destructured/_config.js @@ -0,0 +1,34 @@ +export default { + props: { + things: [ + { num: 1 }, + { num: 2 }, + { num: 3 } + ] + }, + + html: ` +
+ 1 + 2 + 3 +
`, + + test({ assert, component, target }) { + component.things = [ + { num: 1 }, + { num: 2 }, + { num: 3 }, + { num: 4 } + ]; + + assert.htmlEqual(target.innerHTML, ` +
+ 1 + 2 + 3 + 4 +
+ `); + } +}; diff --git a/test/runtime/samples/component-svelte-slot-let-destructured/main.svelte b/test/runtime/samples/component-svelte-slot-let-destructured/main.svelte new file mode 100644 index 000000000000..1cf53a325546 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-destructured/main.svelte @@ -0,0 +1,11 @@ + + + + + {num} + + \ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-e/A.svelte b/test/runtime/samples/component-svelte-slot-let-e/A.svelte new file mode 100644 index 000000000000..5064635f28fe --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-e/A.svelte @@ -0,0 +1,11 @@ + + + + + {reflected} + + + \ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-e/B.svelte b/test/runtime/samples/component-svelte-slot-let-e/B.svelte new file mode 100644 index 000000000000..352f10a24947 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-e/B.svelte @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-e/_config.js b/test/runtime/samples/component-svelte-slot-let-e/_config.js new file mode 100644 index 000000000000..967955dfd856 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-e/_config.js @@ -0,0 +1,19 @@ +export default { + html: ` + 1 + 1 + 1 + 1 + `, + + async test({ assert, target, component }) { + component.x = 2; + + assert.htmlEqual(target.innerHTML, ` + 2 + 2 + 2 + 2 + `); + } +}; diff --git a/test/runtime/samples/component-svelte-slot-let-e/main.svelte b/test/runtime/samples/component-svelte-slot-let-e/main.svelte new file mode 100644 index 000000000000..2856d393ea62 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-e/main.svelte @@ -0,0 +1,16 @@ + + + + + {reflected} + + + + + + {reflected} + + \ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-f/A.svelte b/test/runtime/samples/component-svelte-slot-let-f/A.svelte new file mode 100644 index 000000000000..4f4ac95014bb --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-f/A.svelte @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-f/_config.js b/test/runtime/samples/component-svelte-slot-let-f/_config.js new file mode 100644 index 000000000000..73b90885b545 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-f/_config.js @@ -0,0 +1,22 @@ +export default { + html: ` + 1 + 0 + `, + async test({ assert, target, component, window }) { + component.x = 2; + + assert.htmlEqual(target.innerHTML, ` + 2 + 0 + `); + + const span = target.querySelector('span'); + await span.dispatchEvent(new window.MouseEvent('click')); + + assert.htmlEqual(target.innerHTML, ` + 2 + 2 + `); + } +}; diff --git a/test/runtime/samples/component-svelte-slot-let-f/main.svelte b/test/runtime/samples/component-svelte-slot-let-f/main.svelte new file mode 100644 index 000000000000..721ca162aa27 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-f/main.svelte @@ -0,0 +1,17 @@ + + + + + y = reflected} + class={reflected} + > + {reflected} + + + +{ y } \ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-in-binding/Nested.svelte b/test/runtime/samples/component-svelte-slot-let-in-binding/Nested.svelte new file mode 100644 index 000000000000..95b4842ee6ea --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-in-binding/Nested.svelte @@ -0,0 +1,9 @@ + + +
+ {#each items as item, index} + + {/each} +
\ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-in-binding/_config.js b/test/runtime/samples/component-svelte-slot-let-in-binding/_config.js new file mode 100644 index 000000000000..41e77a9574eb --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-in-binding/_config.js @@ -0,0 +1,26 @@ +export default { + html: ` +
+ + + +
+ `, + + ssrHtml: ` +
+ + + +
+ `, + + async test({ assert, component, target, window }) { + const inputs = target.querySelectorAll('input'); + + inputs[2].value = 'd'; + await inputs[2].dispatchEvent(new window.Event('input')); + + assert.deepEqual(component.letters, ['a', 'b', 'd']); + } +}; diff --git a/test/runtime/samples/component-svelte-slot-let-in-binding/main.svelte b/test/runtime/samples/component-svelte-slot-let-in-binding/main.svelte new file mode 100644 index 000000000000..54b4f2974896 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-in-binding/main.svelte @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-in-slot/Inner.svelte b/test/runtime/samples/component-svelte-slot-let-in-slot/Inner.svelte new file mode 100644 index 000000000000..50baf3aa93cd --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-in-slot/Inner.svelte @@ -0,0 +1 @@ + diff --git a/test/runtime/samples/component-svelte-slot-let-in-slot/Outer.svelte b/test/runtime/samples/component-svelte-slot-let-in-slot/Outer.svelte new file mode 100644 index 000000000000..d6c86307a5a4 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-in-slot/Outer.svelte @@ -0,0 +1,5 @@ + + + diff --git a/test/runtime/samples/component-svelte-slot-let-in-slot/_config.js b/test/runtime/samples/component-svelte-slot-let-in-slot/_config.js new file mode 100644 index 000000000000..a86d869581fa --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-in-slot/_config.js @@ -0,0 +1,12 @@ +export default { + props: { + prop: 'a', + }, + + html: 'a', + + test({ assert, component, target }) { + component.prop = 'b'; + assert.htmlEqual( target.innerHTML, 'b' ); + } +}; diff --git a/test/runtime/samples/component-svelte-slot-let-in-slot/main.svelte b/test/runtime/samples/component-svelte-slot-let-in-slot/main.svelte new file mode 100644 index 000000000000..11b9a4e91d96 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-in-slot/main.svelte @@ -0,0 +1,16 @@ + + + + + + + {value} + + + + diff --git a/test/runtime/samples/component-svelte-slot-let-named/Nested.svelte b/test/runtime/samples/component-svelte-slot-let-named/Nested.svelte new file mode 100644 index 000000000000..a3b09ff5b213 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-named/Nested.svelte @@ -0,0 +1,9 @@ + + +
+ {#each things as thing} + + {/each} +
\ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-named/_config.js b/test/runtime/samples/component-svelte-slot-let-named/_config.js new file mode 100644 index 000000000000..f65448af93be --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-named/_config.js @@ -0,0 +1,24 @@ +export default { + props: { + things: [1, 2, 3] + }, + + html: ` +
+
1
+
2
+
3
+
`, + + test({ assert, component, target }) { + component.things = [1, 2, 3, 4]; + assert.htmlEqual(target.innerHTML, ` +
+
1
+
2
+
3
+
4
+
+ `); + } +}; diff --git a/test/runtime/samples/component-svelte-slot-let-named/main.svelte b/test/runtime/samples/component-svelte-slot-let-named/main.svelte new file mode 100644 index 000000000000..f9e9e3a10fc6 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-named/main.svelte @@ -0,0 +1,11 @@ + + + +
+ {thing} +
+
\ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-static/Nested.svelte b/test/runtime/samples/component-svelte-slot-let-static/Nested.svelte new file mode 100644 index 000000000000..32eee1534acb --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-static/Nested.svelte @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let-static/_config.js b/test/runtime/samples/component-svelte-slot-let-static/_config.js new file mode 100644 index 000000000000..3067939b88a3 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-static/_config.js @@ -0,0 +1,3 @@ +export default { + html: `

Hi

` +}; diff --git a/test/runtime/samples/component-svelte-slot-let-static/main.svelte b/test/runtime/samples/component-svelte-slot-let-static/main.svelte new file mode 100644 index 000000000000..3eeaba976e1b --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let-static/main.svelte @@ -0,0 +1,7 @@ + + + +

{value}

+
\ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let/Nested.svelte b/test/runtime/samples/component-svelte-slot-let/Nested.svelte new file mode 100644 index 000000000000..3758adbd7ce4 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let/Nested.svelte @@ -0,0 +1,9 @@ + + +
+ {#each things as thing} + + {/each} +
\ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot-let/_config.js b/test/runtime/samples/component-svelte-slot-let/_config.js new file mode 100644 index 000000000000..d66f613bb4b0 --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let/_config.js @@ -0,0 +1,24 @@ +export default { + props: { + things: [1, 2, 3] + }, + + html: ` +
+ 1 + 2 + 3 +
`, + + test({ assert, component, target }) { + component.things = [1, 2, 3, 4]; + assert.htmlEqual(target.innerHTML, ` +
+ 1 + 2 + 3 + 4 +
+ `); + } +}; diff --git a/test/runtime/samples/component-svelte-slot-let/main.svelte b/test/runtime/samples/component-svelte-slot-let/main.svelte new file mode 100644 index 000000000000..d00bb8e41eed --- /dev/null +++ b/test/runtime/samples/component-svelte-slot-let/main.svelte @@ -0,0 +1,9 @@ + + + + {thing} + \ No newline at end of file diff --git a/test/runtime/samples/component-svelte-slot/main.svelte b/test/runtime/samples/component-svelte-slot/main.svelte index a37df9e68308..c5c784c1577a 100644 --- a/test/runtime/samples/component-svelte-slot/main.svelte +++ b/test/runtime/samples/component-svelte-slot/main.svelte @@ -10,6 +10,4 @@ - -
diff --git a/test/validator/samples/svelte-slot-placement-2/errors.json b/test/validator/samples/svelte-slot-placement-2/errors.json new file mode 100644 index 000000000000..e3ee8b744594 --- /dev/null +++ b/test/validator/samples/svelte-slot-placement-2/errors.json @@ -0,0 +1,9 @@ +[ + { + "code": "invalid-slotted-content", + "message": " must be a child of a component", + "start": { "line": 5, "column": 0, "character": 59 }, + "end": { "line": 7, "column": 14, "character": 104 }, + "pos": 59 + } +] diff --git a/test/validator/samples/svelte-slot-placement-2/input.svelte b/test/validator/samples/svelte-slot-placement-2/input.svelte new file mode 100644 index 000000000000..d5a83bf66446 --- /dev/null +++ b/test/validator/samples/svelte-slot-placement-2/input.svelte @@ -0,0 +1,7 @@ + + + +
test
+
\ No newline at end of file diff --git a/test/validator/samples/svelte-slot-placement/errors.json b/test/validator/samples/svelte-slot-placement/errors.json new file mode 100644 index 000000000000..5843663f8ddb --- /dev/null +++ b/test/validator/samples/svelte-slot-placement/errors.json @@ -0,0 +1,9 @@ +[ + { + "code": "invalid-slotted-content", + "message": " must be a child of a component", + "start": { "line": 7, "column": 2, "character": 77 }, + "end": { "line": 9, "column": 16, "character": 126 }, + "pos": 77 + } +] diff --git a/test/validator/samples/svelte-slot-placement/input.svelte b/test/validator/samples/svelte-slot-placement/input.svelte new file mode 100644 index 000000000000..4803e75da932 --- /dev/null +++ b/test/validator/samples/svelte-slot-placement/input.svelte @@ -0,0 +1,11 @@ + + + +
+ +
test
+
+
+
\ No newline at end of file