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