From f41e59e474b28d6cb96da818fd23843a922d9ecb Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 14 Apr 2019 16:25:45 -0400 Subject: [PATCH 1/9] failing test for #2320 --- .../Nested.svelte | 17 +++++++++++++ .../_config.js | 25 +++++++++++++++++++ .../main.svelte | 17 +++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 test/runtime/samples/component-slot-named-inherits-default-lets/Nested.svelte create mode 100644 test/runtime/samples/component-slot-named-inherits-default-lets/_config.js create mode 100644 test/runtime/samples/component-slot-named-inherits-default-lets/main.svelte diff --git a/test/runtime/samples/component-slot-named-inherits-default-lets/Nested.svelte b/test/runtime/samples/component-slot-named-inherits-default-lets/Nested.svelte new file mode 100644 index 0000000000..472af6278e --- /dev/null +++ b/test/runtime/samples/component-slot-named-inherits-default-lets/Nested.svelte @@ -0,0 +1,17 @@ + + +
+ + + + + +
\ No newline at end of file diff --git a/test/runtime/samples/component-slot-named-inherits-default-lets/_config.js b/test/runtime/samples/component-slot-named-inherits-default-lets/_config.js new file mode 100644 index 0000000000..212c57308a --- /dev/null +++ b/test/runtime/samples/component-slot-named-inherits-default-lets/_config.js @@ -0,0 +1,25 @@ +export default { + html: ` +
+

count in default slot: 0

+

count in foo slot: 0

+

count in bar slot: 0

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

count in default slot: 1

+

count in foo slot: 1

+

count in bar slot: 1

+ +
+ `); + } +} \ No newline at end of file diff --git a/test/runtime/samples/component-slot-named-inherits-default-lets/main.svelte b/test/runtime/samples/component-slot-named-inherits-default-lets/main.svelte new file mode 100644 index 0000000000..51dd28b855 --- /dev/null +++ b/test/runtime/samples/component-slot-named-inherits-default-lets/main.svelte @@ -0,0 +1,17 @@ + + + +

+ count in default slot: {count} +

+ +

+ count in foo slot: {count} +

+ +

+ count in bar slot: {count} +

+
\ No newline at end of file From 4c86701f925a8e65d100fa466815cf4fd74891c2 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 14 Apr 2019 16:26:17 -0400 Subject: [PATCH 2/9] pass default slot values through to named slots --- src/compile/render-dom/Renderer.ts | 20 +++------ .../render-dom/wrappers/Element/index.ts | 4 -- src/compile/render-dom/wrappers/Slot.ts | 41 ++++++++++++++++--- 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/compile/render-dom/Renderer.ts b/src/compile/render-dom/Renderer.ts index ae986315a9..f899d16fcb 100644 --- a/src/compile/render-dom/Renderer.ts +++ b/src/compile/render-dom/Renderer.ts @@ -3,16 +3,17 @@ import { CompileOptions } from '../../interfaces'; import Component from '../Component'; import FragmentWrapper from './wrappers/Fragment'; import CodeBuilder from '../utils/CodeBuilder'; +import SlotWrapper from './wrappers/Slot'; export default class Renderer { component: Component; // TODO Maybe Renderer shouldn't know about Component? options: CompileOptions; - blocks: (Block | string)[]; - readonly: Set; - slots: Set; - meta_bindings: CodeBuilder; - binding_groups: string[]; + blocks: (Block | string)[] = []; + readonly: Set = new Set(); + slots: Map = new Map(); + meta_bindings: CodeBuilder = new CodeBuilder(); // initial values for e.g. window.innerWidth, if there's a meta tag + binding_groups: string[] = []; block: Block; fragment: FragmentWrapper; @@ -24,16 +25,8 @@ export default class Renderer { this.options = options; this.locate = component.locate; // TODO messy - this.readonly = new Set(); - this.slots = new Set(); - this.file_var = options.dev && this.component.get_unique_name('file'); - // initial values for e.g. window.innerWidth, if there's a meta tag - this.meta_bindings = new CodeBuilder(); - - this.binding_groups = []; - // main block this.block = new Block({ renderer: this, @@ -46,7 +39,6 @@ export default class Renderer { }); this.block.has_update_method = true; - this.blocks = []; this.fragment = new FragmentWrapper( this, diff --git a/src/compile/render-dom/wrappers/Element/index.ts b/src/compile/render-dom/wrappers/Element/index.ts index 80a8308b93..50752c021b 100644 --- a/src/compile/render-dom/wrappers/Element/index.ts +++ b/src/compile/render-dom/wrappers/Element/index.ts @@ -220,10 +220,6 @@ export default class ElementWrapper extends Wrapper { render(block: Block, parent_node: string, parent_nodes: string) { const { renderer } = this; - if (this.node.name === 'slot') { - renderer.slots.add((this.node as Slot).slot_name); - } - if (this.node.name === 'noscript') return; if (this.slot_block) { diff --git a/src/compile/render-dom/wrappers/Slot.ts b/src/compile/render-dom/wrappers/Slot.ts index 89a575910f..c4a8a83bb4 100644 --- a/src/compile/render-dom/wrappers/Slot.ts +++ b/src/compile/render-dom/wrappers/Slot.ts @@ -9,10 +9,12 @@ import add_to_set from '../../utils/add_to_set'; import get_slot_data from '../../utils/get_slot_data'; import { stringify_props } from '../../utils/stringify_props'; import Expression from '../../nodes/shared/Expression'; +import Attribute from '../../nodes/Attribute'; export default class SlotWrapper extends Wrapper { node: Slot; fragment: FragmentWrapper; + slot_values: Map = new Map(); var = 'slot'; dependencies: Set = new Set(['$$scope']); @@ -38,14 +40,44 @@ export default class SlotWrapper extends Wrapper { ); this.node.attributes.forEach(attribute => { - add_to_set(this.dependencies, attribute.dependencies); + if (attribute.name !== 'name') this.slot_values.set(attribute.name, attribute); }); - block.add_dependencies(this.dependencies); + if (this.node.slot_name === 'default') { + // if this is the default slot, add our dependencies to any + // other slots (which inherit our slot values) that were + // previously encountered + renderer.slots.forEach(({ slot, block }) => { + this.slot_values.forEach((attribute, name) => { + if (!slot.slot_values.has(name)) { + slot.slot_values.set(name, attribute); + + add_to_set(slot.dependencies, attribute.dependencies); + block.add_dependencies(attribute.dependencies); + } + }); + }); + } else if (renderer.slots.has('default')) { + // otherwise, go the other way — inherit values from + // a previously encountered default slot + const { slot: default_slot } = renderer.slots.get('default'); + default_slot.slot_values.forEach((attribute, name) => { + if (!this.slot_values.has(name)) { + this.slot_values.set(name, attribute); + } + }); + } + + this.slot_values.forEach(attribute => { + add_to_set(this.dependencies, attribute.dependencies); + block.add_dependencies(attribute.dependencies); + }); // we have to do this, just in case block.add_intro(); block.add_outro(); + + renderer.slots.set(this.node.slot_name, { slot: this, block }); } render( @@ -56,14 +88,13 @@ export default class SlotWrapper extends Wrapper { const { renderer } = this; const { slot_name } = this.node; - renderer.slots.add(slot_name); let get_slot_changes; let get_slot_context; - const attributes = this.node.attributes.filter(attribute => attribute.name !== 'name'); + if (this.slot_values.size > 0) { + const attributes = Array.from(this.slot_values.values()); - if (attributes.length > 0) { get_slot_changes = renderer.component.get_unique_name(`get_${slot_name}_slot_changes`); get_slot_context = renderer.component.get_unique_name(`get_${slot_name}_slot_context`); From cf2bf0581276af5c11998762d072bc79985ec17c Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 14 Apr 2019 16:36:16 -0400 Subject: [PATCH 3/9] receive default slot values in named slots --- src/compile/render-dom/wrappers/Element/index.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/compile/render-dom/wrappers/Element/index.ts b/src/compile/render-dom/wrappers/Element/index.ts index 50752c021b..22ea7a78cd 100644 --- a/src/compile/render-dom/wrappers/Element/index.ts +++ b/src/compile/render-dom/wrappers/Element/index.ts @@ -143,7 +143,14 @@ export default class ElementWrapper extends Wrapper { name: this.renderer.component.get_unique_name(`create_${sanitize(name)}_slot`) }); - const fn = get_context_merger(this.node.lets); + const lets = this.node.lets; + const seen = new Set(lets.map(l => l.name)); + + (owner as InlineComponentWrapper).node.lets.forEach(l => { + if (!seen.has(l.name)) lets.push(l); + }); + + const fn = get_context_merger(lets); (owner as InlineComponentWrapper).slots.set(name, { block: child_block, From b6ca514188c0bf78a1b553d5c0b10cdf69630889 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 14 Apr 2019 17:14:08 -0400 Subject: [PATCH 4/9] inherit default slot values in named slots in SSR mode --- src/compile/Component.ts | 3 ++ src/compile/nodes/Slot.ts | 34 ++++++++++++++++-- src/compile/render-dom/wrappers/Slot.ts | 40 +++------------------- src/compile/render-ssr/handlers/Element.ts | 10 +++++- src/compile/render-ssr/handlers/Slot.ts | 2 +- src/compile/utils/get_slot_data.ts | 5 +-- 6 files changed, 52 insertions(+), 42 deletions(-) diff --git a/src/compile/Component.ts b/src/compile/Component.ts index f1bf634786..41d6c3e3d5 100644 --- a/src/compile/Component.ts +++ b/src/compile/Component.ts @@ -20,6 +20,7 @@ import fuzzymatch from '../utils/fuzzymatch'; import { remove_indentation, add_indentation } from '../utils/indentation'; import get_object from './utils/get_object'; import unwrap_parens from './utils/unwrap_parens'; +import Slot from './nodes/Slot'; type ComponentOptions = { namespace?: string; @@ -117,6 +118,8 @@ export default class Component { used_names: Set = new Set(); globally_used_names: Set = new Set(); + slots: Map = new Map(); + constructor( ast: Ast, source: string, diff --git a/src/compile/nodes/Slot.ts b/src/compile/nodes/Slot.ts index 1540fa6271..396b53ea94 100644 --- a/src/compile/nodes/Slot.ts +++ b/src/compile/nodes/Slot.ts @@ -1,15 +1,17 @@ import Node from './shared/Node'; import Element from './Element'; import Attribute from './Attribute'; +import Component from '../Component'; +import TemplateScope from './shared/TemplateScope'; export default class Slot extends Element { type: 'Element'; name: string; - slot_name: string; - attributes: Attribute[]; children: Node[]; + slot_name: string; + values: Map = new Map(); - constructor(component, parent, scope, info) { + constructor(component: Component, parent: Node, scope: TemplateScope, info: any) { super(component, parent, scope, info); info.attributes.forEach(attr => { @@ -37,6 +39,8 @@ export default class Slot extends Element { } } + this.values.set(attr.name, new Attribute(component, this, scope, attr)); + // TODO should duplicate slots be disallowed? Feels like it's more likely to be a // bug than anything. Perhaps it should be a warning @@ -49,6 +53,30 @@ export default class Slot extends Element { if (!this.slot_name) this.slot_name = 'default'; + if (this.slot_name === 'default') { + // if this is the default slot, add our dependencies to any + // other slots (which inherit our slot values) that were + // previously encountered + component.slots.forEach((slot) => { + this.values.forEach((attribute, name) => { + if (!slot.values.has(name)) { + slot.values.set(name, attribute); + } + }); + }); + } else if (component.slots.has('default')) { + // otherwise, go the other way — inherit values from + // a previously encountered default slot + const default_slot = component.slots.get('default'); + default_slot.values.forEach((attribute, name) => { + if (!this.values.has(name)) { + this.values.set(name, attribute); + } + }); + } + + component.slots.set(this.slot_name, this); + // if (node.attributes.length === 0) && validator.slots.has('default')) { // validator.error(node, { // code: `duplicate-slot`, diff --git a/src/compile/render-dom/wrappers/Slot.ts b/src/compile/render-dom/wrappers/Slot.ts index c4a8a83bb4..69fa570d7d 100644 --- a/src/compile/render-dom/wrappers/Slot.ts +++ b/src/compile/render-dom/wrappers/Slot.ts @@ -14,7 +14,6 @@ import Attribute from '../../nodes/Attribute'; export default class SlotWrapper extends Wrapper { node: Slot; fragment: FragmentWrapper; - slot_values: Map = new Map(); var = 'slot'; dependencies: Set = new Set(['$$scope']); @@ -39,36 +38,7 @@ export default class SlotWrapper extends Wrapper { next_sibling ); - this.node.attributes.forEach(attribute => { - if (attribute.name !== 'name') this.slot_values.set(attribute.name, attribute); - }); - - if (this.node.slot_name === 'default') { - // if this is the default slot, add our dependencies to any - // other slots (which inherit our slot values) that were - // previously encountered - renderer.slots.forEach(({ slot, block }) => { - this.slot_values.forEach((attribute, name) => { - if (!slot.slot_values.has(name)) { - slot.slot_values.set(name, attribute); - - add_to_set(slot.dependencies, attribute.dependencies); - block.add_dependencies(attribute.dependencies); - } - }); - }); - } else if (renderer.slots.has('default')) { - // otherwise, go the other way — inherit values from - // a previously encountered default slot - const { slot: default_slot } = renderer.slots.get('default'); - default_slot.slot_values.forEach((attribute, name) => { - if (!this.slot_values.has(name)) { - this.slot_values.set(name, attribute); - } - }); - } - - this.slot_values.forEach(attribute => { + this.node.values.forEach(attribute => { add_to_set(this.dependencies, attribute.dependencies); block.add_dependencies(attribute.dependencies); }); @@ -92,18 +62,18 @@ export default class SlotWrapper extends Wrapper { let get_slot_changes; let get_slot_context; - if (this.slot_values.size > 0) { - const attributes = Array.from(this.slot_values.values()); + if (this.node.values.size > 0) { + const attributes = Array.from(this.node.values.values()); get_slot_changes = renderer.component.get_unique_name(`get_${slot_name}_slot_changes`); get_slot_context = renderer.component.get_unique_name(`get_${slot_name}_slot_context`); - const context_props = get_slot_data(attributes, false); + const context_props = get_slot_data(this.node.values, false); const changes_props = []; const dependencies = new Set(); - attributes.forEach(attribute => { + this.node.values.forEach(attribute => { attribute.chunks.forEach(chunk => { if ((chunk as Expression).dependencies) { add_to_set(dependencies, (chunk as Expression).dependencies); diff --git a/src/compile/render-ssr/handlers/Element.ts b/src/compile/render-ssr/handlers/Element.ts index 0c2cdc489e..4c48c85133 100644 --- a/src/compile/render-ssr/handlers/Element.ts +++ b/src/compile/render-ssr/handlers/Element.ts @@ -51,13 +51,21 @@ export default function(node, renderer, options) { let textarea_contents; // awkward special case const slot = node.get_static_attribute_value('slot'); - if (slot && node.has_ancestor('InlineComponent')) { + const component = node.find_nearest(/InlineComponent/); + if (slot && component) { const slot = node.attributes.find((attribute: Node) => attribute.name === 'slot'); const slot_name = slot.chunks[0].data; const target = renderer.targets[renderer.targets.length - 1]; target.slot_stack.push(slot_name); target.slots[slot_name] = ''; + const lets = node.lets; + const seen = new Set(lets.map(l => l.name)); + + component.lets.forEach(l => { + if (!seen.has(l.name)) lets.push(l); + }); + options.slot_scopes.set(slot_name, get_slot_scope(node.lets)); } diff --git a/src/compile/render-ssr/handlers/Slot.ts b/src/compile/render-ssr/handlers/Slot.ts index 77273e8009..b2e67f9e79 100644 --- a/src/compile/render-ssr/handlers/Slot.ts +++ b/src/compile/render-ssr/handlers/Slot.ts @@ -4,7 +4,7 @@ import get_slot_data from '../../utils/get_slot_data'; export default function(node, renderer, options) { const prop = quote_prop_if_necessary(node.slot_name); - const slot_data = get_slot_data(node.attributes, true); + const slot_data = get_slot_data(node.values, true); const arg = slot_data.length > 0 ? `{ ${slot_data.join(', ')} }` : ''; diff --git a/src/compile/utils/get_slot_data.ts b/src/compile/utils/get_slot_data.ts index e0a85aa348..ee64d8f1a0 100644 --- a/src/compile/utils/get_slot_data.ts +++ b/src/compile/utils/get_slot_data.ts @@ -1,8 +1,9 @@ import { snip } from './snip'; import { stringify_attribute } from './stringify_attribute'; +import Attribute from '../nodes/Attribute'; -export default function get_slot_data(attributes, is_ssr: boolean) { - return attributes +export default function get_slot_data(values: Map, is_ssr: boolean) { + return Array.from(values.values()) .filter(attribute => attribute.name !== 'name') .map(attribute => { const value = attribute.is_true From 394556941ab0909b4123720efa94ebc08f6db6fb Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 14 Apr 2019 17:32:28 -0400 Subject: [PATCH 5/9] fix --- src/compile/render-dom/wrappers/Slot.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compile/render-dom/wrappers/Slot.ts b/src/compile/render-dom/wrappers/Slot.ts index 69fa570d7d..07ee48ab73 100644 --- a/src/compile/render-dom/wrappers/Slot.ts +++ b/src/compile/render-dom/wrappers/Slot.ts @@ -40,9 +40,10 @@ export default class SlotWrapper extends Wrapper { this.node.values.forEach(attribute => { add_to_set(this.dependencies, attribute.dependencies); - block.add_dependencies(attribute.dependencies); }); + block.add_dependencies(this.dependencies); + // we have to do this, just in case block.add_intro(); block.add_outro(); From 0cf04988c6153c21d2e9530e0fec596427ff3198 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 14 Apr 2019 17:36:03 -0400 Subject: [PATCH 6/9] remove unused code --- src/compile/render-dom/wrappers/Slot.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/compile/render-dom/wrappers/Slot.ts b/src/compile/render-dom/wrappers/Slot.ts index 07ee48ab73..4e7635c84d 100644 --- a/src/compile/render-dom/wrappers/Slot.ts +++ b/src/compile/render-dom/wrappers/Slot.ts @@ -64,8 +64,6 @@ export default class SlotWrapper extends Wrapper { let get_slot_context; if (this.node.values.size > 0) { - const attributes = Array.from(this.node.values.values()); - get_slot_changes = renderer.component.get_unique_name(`get_${slot_name}_slot_changes`); get_slot_context = renderer.component.get_unique_name(`get_${slot_name}_slot_context`); From c08cfc2bf8a775e601f18864f19d98f11d8e037f Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 14 Apr 2019 17:50:25 -0400 Subject: [PATCH 7/9] sanitize names --- src/compile/render-dom/wrappers/Slot.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compile/render-dom/wrappers/Slot.ts b/src/compile/render-dom/wrappers/Slot.ts index 4e7635c84d..e48c74d7f5 100644 --- a/src/compile/render-dom/wrappers/Slot.ts +++ b/src/compile/render-dom/wrappers/Slot.ts @@ -64,8 +64,8 @@ export default class SlotWrapper extends Wrapper { let get_slot_context; if (this.node.values.size > 0) { - get_slot_changes = renderer.component.get_unique_name(`get_${slot_name}_slot_changes`); - get_slot_context = renderer.component.get_unique_name(`get_${slot_name}_slot_context`); + get_slot_changes = renderer.component.get_unique_name(`get_${sanitize(slot_name)}_slot_changes`); + get_slot_context = renderer.component.get_unique_name(`get_${sanitize(slot_name)}_slot_context`); const context_props = get_slot_data(this.node.values, false); const changes_props = []; From e44293aa640bda4ea7fd93dd0b563698ea3a3d94 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 14 Apr 2019 17:53:45 -0400 Subject: [PATCH 8/9] remove renderer.slots --- src/compile/render-dom/Renderer.ts | 1 - src/compile/render-dom/index.ts | 10 +++++----- src/compile/render-dom/wrappers/Slot.ts | 2 -- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/compile/render-dom/Renderer.ts b/src/compile/render-dom/Renderer.ts index f899d16fcb..1d766c6345 100644 --- a/src/compile/render-dom/Renderer.ts +++ b/src/compile/render-dom/Renderer.ts @@ -11,7 +11,6 @@ export default class Renderer { blocks: (Block | string)[] = []; readonly: Set = new Set(); - slots: Map = new Map(); meta_bindings: CodeBuilder = new CodeBuilder(); // initial values for e.g. window.innerWidth, if there's a meta tag binding_groups: string[] = []; diff --git a/src/compile/render-dom/index.ts b/src/compile/render-dom/index.ts index 8ab5d25668..a6ef0ac69c 100644 --- a/src/compile/render-dom/index.ts +++ b/src/compile/render-dom/index.ts @@ -74,14 +74,14 @@ export default function dom( const props = component.vars.filter(variable => !variable.module && variable.export_name); const writable_props = props.filter(variable => variable.writable); - const set = (uses_props || writable_props.length > 0 || renderer.slots.size > 0) + const set = (uses_props || writable_props.length > 0 || component.slots.size > 0) ? deindent` ${$$props} => { ${uses_props && component.invalidate('$$props', `$$props = @assign(@assign({}, $$props), $$new_props)`)} ${writable_props.map(prop => `if ('${prop.export_name}' in $$props) ${component.invalidate(prop.name, `${prop.name} = $$props.${prop.export_name}`)};` )} - ${renderer.slots.size > 0 && + ${component.slots.size > 0 && `if ('$$scope' in ${$$props}) ${component.invalidate('$$scope', `$$scope = ${$$props}.$$scope`)};`} } ` @@ -285,7 +285,7 @@ export default function dom( } const args = ['$$self']; - if (props.length > 0 || component.has_reactive_assignments || renderer.slots.size > 0) { + if (props.length > 0 || component.has_reactive_assignments || component.slots.size > 0) { args.push('$$props', '$$invalidate'); } @@ -315,7 +315,7 @@ export default function dom( const reactive_stores = component.vars.filter(variable => variable.name[0] === '$' && variable.name[1] !== '$'); - if (renderer.slots.size > 0) { + if (component.slots.size > 0) { filtered_declarations.push('$$slots', '$$scope'); } @@ -415,7 +415,7 @@ export default function dom( ${component.javascript} - ${renderer.slots.size && `let { $$slots = {}, $$scope } = $$props;`} + ${component.slots.size && `let { $$slots = {}, $$scope } = $$props;`} ${renderer.binding_groups.length > 0 && `const $$binding_groups = [${renderer.binding_groups.map(_ => `[]`).join(', ')}];`} diff --git a/src/compile/render-dom/wrappers/Slot.ts b/src/compile/render-dom/wrappers/Slot.ts index e48c74d7f5..ec1b7d4b59 100644 --- a/src/compile/render-dom/wrappers/Slot.ts +++ b/src/compile/render-dom/wrappers/Slot.ts @@ -47,8 +47,6 @@ export default class SlotWrapper extends Wrapper { // we have to do this, just in case block.add_intro(); block.add_outro(); - - renderer.slots.set(this.node.slot_name, { slot: this, block }); } render( From a1d35d868d1de8efbbb7023ca2820dfabf899928 Mon Sep 17 00:00:00 2001 From: Richard Harris Date: Sun, 14 Apr 2019 18:06:24 -0400 Subject: [PATCH 9/9] disallow duplicate slot="xxx" elements - fixes second half of #2320 --- src/compile/Component.ts | 1 + src/compile/nodes/Element.ts | 9 +++++++++ src/compile/nodes/Slot.ts | 16 ---------------- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/compile/Component.ts b/src/compile/Component.ts index 41d6c3e3d5..bf2fdf9a5a 100644 --- a/src/compile/Component.ts +++ b/src/compile/Component.ts @@ -119,6 +119,7 @@ export default class Component { globally_used_names: Set = new Set(); slots: Map = new Map(); + slot_outlets: Set = new Set(); constructor( ast: Ast, diff --git a/src/compile/nodes/Element.ts b/src/compile/nodes/Element.ts index bc1991c57e..b38ee5b6fb 100644 --- a/src/compile/nodes/Element.ts +++ b/src/compile/nodes/Element.ts @@ -361,6 +361,15 @@ export default class Element extends Node { }); } + if (component.slot_outlets.has(name)) { + component.error(attribute, { + code: `duplicate-slot-attribute`, + message: `Duplicate '${name}' slot` + }); + + component.slot_outlets.add(name); + } + let ancestor = this.parent; do { if (ancestor.type === 'InlineComponent') break; diff --git a/src/compile/nodes/Slot.ts b/src/compile/nodes/Slot.ts index 396b53ea94..dbb502b41a 100644 --- a/src/compile/nodes/Slot.ts +++ b/src/compile/nodes/Slot.ts @@ -40,15 +40,6 @@ export default class Slot extends Element { } this.values.set(attr.name, new Attribute(component, this, scope, attr)); - - // 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(slot_name)) { - // validator.error(`duplicate '${slot_name}' element`, nameAttribute.start); - // } - - // validator.slots.add(slot_name); }); if (!this.slot_name) this.slot_name = 'default'; @@ -76,12 +67,5 @@ export default class Slot extends Element { } component.slots.set(this.slot_name, this); - - // if (node.attributes.length === 0) && validator.slots.has('default')) { - // validator.error(node, { - // code: `duplicate-slot`, - // message: `duplicate default element` - // }); - // } } } \ No newline at end of file