diff --git a/src/compiler/compile/render_dom/wrappers/Element/create_slot_block.ts b/src/compiler/compile/render_dom/wrappers/Element/create_slot_block.ts new file mode 100644 index 000000000000..f9ed43bb3ea2 --- /dev/null +++ b/src/compiler/compile/render_dom/wrappers/Element/create_slot_block.ts @@ -0,0 +1,61 @@ +import ElementWrapper from './index'; +import SlotWrapper from '../Slot'; +import Block from '../../Block'; +import { sanitize } from '../../../../utils/names'; +import InlineComponentWrapper from '../InlineComponent'; +import create_debugging_comment from '../shared/create_debugging_comment'; +import { get_slot_definition } from '../shared/get_slot_definition'; + +export default function create_slot_block(attribute, element: ElementWrapper | SlotWrapper, block: Block) { + const owner = find_slot_owner(element.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(element.node, element.renderer.component), + name: element.renderer.component.get_unique_name( + `create_${sanitize(name)}_slot` + ), + type: 'slot', + }); + + const { scope, lets } = element.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) + ); + element.renderer.blocks.push(child_block); + } + + element.slot_block = ((owner as unknown) as InlineComponentWrapper).slots.get( + name + ).block; + + return element.slot_block; + } + + return block; +} + +function find_slot_owner(owner) { + while (owner) { + if (owner.node.type === 'InlineComponent') { + break; + } + + if (owner.node.type === 'Element' && /-/.test(owner.node.name)) { + break; + } + + owner = owner.parent; + } + return owner; +} diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index ef330224027e..4ae2be793267 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'; @@ -14,18 +14,16 @@ 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'; import EventHandler from './EventHandler'; import { extract_names } from 'periscopic'; import Action from '../../../nodes/Action'; +import create_slot_block from './create_slot_block'; const events = [ { @@ -170,47 +168,7 @@ 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; - } + block = create_slot_block(attribute, this, block); } if (attribute.name === 'style') { return new StyleAttributeWrapper(this, block, attribute); diff --git a/src/compiler/compile/render_dom/wrappers/Slot.ts b/src/compiler/compile/render_dom/wrappers/Slot.ts index 5f2d3318397e..d44d5bdd131a 100644 --- a/src/compiler/compile/render_dom/wrappers/Slot.ts +++ b/src/compiler/compile/render_dom/wrappers/Slot.ts @@ -10,10 +10,12 @@ import get_slot_data from '../../utils/get_slot_data'; import Expression from '../../nodes/shared/Expression'; import is_dynamic from './shared/is_dynamic'; import { Identifier, ObjectExpression } from 'estree'; +import create_slot_block from './Element/create_slot_block'; export default class SlotWrapper extends Wrapper { node: Slot; fragment: FragmentWrapper; + slot_block: Block; var: Identifier = { type: 'Identifier', name: 'slot' }; dependencies: Set = new Set(['$$scope']); @@ -30,6 +32,10 @@ export default class SlotWrapper extends Wrapper { this.cannot_use_innerhtml(); this.not_static_content(); + if (this.node.values.has('slot')) { + block = create_slot_block(this.node.values.get('slot'), this, block); + } + this.fragment = new FragmentWrapper( renderer, block, @@ -59,6 +65,10 @@ export default class SlotWrapper extends Wrapper { const { slot_name } = this.node; + if (this.slot_block) { + block = this.slot_block; + } + let get_slot_changes_fn; let get_slot_context_fn; diff --git a/src/compiler/compile/render_ssr/handlers/Slot.ts b/src/compiler/compile/render_ssr/handlers/Slot.ts index 3b1e199c75ed..d15f6b88d631 100644 --- a/src/compiler/compile/render_ssr/handlers/Slot.ts +++ b/src/compiler/compile/render_ssr/handlers/Slot.ts @@ -2,9 +2,18 @@ import Renderer, { RenderOptions } from '../Renderer'; import Slot from '../../nodes/Slot'; import { x } from 'code-red'; import get_slot_data from '../../utils/get_slot_data'; +import { get_slot_scope } from './shared/get_slot_scope'; -export default function(node: Slot, renderer: Renderer, options: RenderOptions) { +export default function(node: Slot, renderer: Renderer, options: RenderOptions & { + slot_scopes: Map; +}) { const slot_data = get_slot_data(node.values); + const slot = node.get_static_attribute_value('slot'); + const nearest_inline_component = node.find_nearest(/InlineComponent/); + + if (slot && nearest_inline_component) { + renderer.push(); + } renderer.push(); renderer.render(node.children, options); @@ -15,4 +24,17 @@ export default function(node: Slot, renderer: Renderer, options: RenderOptions) ? $$slots.${node.slot_name}(${slot_data}) : ${result} `); + + if (slot && nearest_inline_component) { + const lets = node.lets; + const seen = new Set(lets.map(l => l.name.name)); + + nearest_inline_component.lets.forEach(l => { + if (!seen.has(l.name.name)) lets.push(l); + }); + options.slot_scopes.set(slot, { + input: get_slot_scope(node.lets), + output: renderer.pop() + }); + } } diff --git a/test/runtime/samples/component-slot-nested-in-slot/One.svelte b/test/runtime/samples/component-slot-nested-in-slot/One.svelte new file mode 100644 index 000000000000..e27437c450fc --- /dev/null +++ b/test/runtime/samples/component-slot-nested-in-slot/One.svelte @@ -0,0 +1,8 @@ + + + + + diff --git a/test/runtime/samples/component-slot-nested-in-slot/Two.svelte b/test/runtime/samples/component-slot-nested-in-slot/Two.svelte new file mode 100644 index 000000000000..3f21e2d16f21 --- /dev/null +++ b/test/runtime/samples/component-slot-nested-in-slot/Two.svelte @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-nested-in-slot/_config.js b/test/runtime/samples/component-slot-nested-in-slot/_config.js new file mode 100644 index 000000000000..56a9ef2668b6 --- /dev/null +++ b/test/runtime/samples/component-slot-nested-in-slot/_config.js @@ -0,0 +1,18 @@ +export default { + html: ` +

one: 1 two: 2

+ `, + test({ assert, component, target }) { + component.a = 3; + component.b = 4; + assert.htmlEqual(target.innerHTML, ` +

one: 3 two: 4

+ `); + + component.a = 5; + component.b = 6; + assert.htmlEqual(target.innerHTML, ` +

one: 5 two: 6

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

one: {one} two: {two}

+
\ No newline at end of file