diff --git a/src/compile/nodes/Element.ts b/src/compile/nodes/Element.ts index af2f8ec919..552e30f614 100644 --- a/src/compile/nodes/Element.ts +++ b/src/compile/nodes/Element.ts @@ -637,16 +637,6 @@ export default class Element extends Node { return this.name === 'audio' || this.name === 'video'; } - remount(name: string) { - const slot = this.attributes.find(attribute => attribute.name === 'slot'); - if (slot) { - const prop = quotePropIfNecessary(slot.chunks[0].data); - return `@append(${name}.$$.slotted${prop}, ${this.var});`; - } - - return `@append(${name}.$$.slotted.default, ${this.var});`; - } - addCssClass(className = this.component.stylesheet.id) { const classAttribute = this.attributes.find(a => a.name === 'class'); if (classAttribute && !classAttribute.isTrue) { diff --git a/src/compile/nodes/shared/Node.ts b/src/compile/nodes/shared/Node.ts index 893d4e25a5..5d8435965c 100644 --- a/src/compile/nodes/shared/Node.ts +++ b/src/compile/nodes/shared/Node.ts @@ -48,10 +48,6 @@ export default class Node { if (this.parent) return this.parent.findNearest(selector); } - remount(name: string) { - return `${this.var}.m(${name}.$$.slotted.default, null);`; - } - warnIfEmptyBlock() { if (!this.component.options.dev) return; if (!/Block$/.test(this.type) || !this.children) return; diff --git a/src/compile/render-dom/Block.ts b/src/compile/render-dom/Block.ts index 6d23fe9acb..299ba637c5 100644 --- a/src/compile/render-dom/Block.ts +++ b/src/compile/render-dom/Block.ts @@ -61,7 +61,7 @@ export default class Block { variables: Map; getUniqueName: (name: string) => string; - hasUpdateMethod: boolean; + hasUpdateMethod = false; autofocus: string; constructor(options: BlockOptions) { @@ -106,8 +106,6 @@ export default class Block { this.aliases = new Map().set('ctx', this.getUniqueName('ctx')); if (this.key) this.aliases.set('key', this.getUniqueName('key')); - - this.hasUpdateMethod = false; // determined later } assignVariableNames() { @@ -151,6 +149,8 @@ export default class Block { dependencies.forEach(dependency => { this.dependencies.add(dependency); }); + + this.hasUpdateMethod = true; } addElement( @@ -407,7 +407,7 @@ export default class Block { return deindent` ${this.comment && `// ${this.comment}`} - function ${this.name}($$, ${this.key ? `${localKey}, ` : ''}ctx) { + function ${this.name}(${this.key ? `${localKey}, ` : ''}ctx) { ${this.getContents(localKey)} } `; diff --git a/src/compile/render-dom/index.ts b/src/compile/render-dom/index.ts index fc35e4f317..da6573e456 100644 --- a/src/compile/render-dom/index.ts +++ b/src/compile/render-dom/index.ts @@ -10,6 +10,7 @@ import addToSet from '../../utils/addToSet'; import getObject from '../../utils/getObject'; import { extractNames } from '../../utils/annotateWithScopes'; import { nodes_match } from '../../utils/nodes_match'; +import sanitize from '../../utils/sanitize'; export default function dom( component: Component, @@ -71,7 +72,7 @@ export default function dom( const props = component.props.filter(x => component.writable_declarations.has(x.name)); - const set = component.meta.props || props.length > 0 + const set = (component.meta.props || props.length > 0 || renderer.slots.size > 0) ? deindent` $$props => { ${component.meta.props && deindent` @@ -81,6 +82,8 @@ export default function dom( `} ${props.map(prop => `if ('${prop.as}' in $$props) $$invalidate('${prop.name}', ${prop.name} = $$props.${prop.as});`)} + ${renderer.slots.size > 0 && + `if ('$$scope' in $$props) $$invalidate('$$scope', $$scope = $$props.$$scope);`} } ` : null; @@ -235,10 +238,12 @@ export default function dom( } const args = ['$$self']; - if (component.props.length > 0 || component.has_reactive_assignments) args.push('$$props', '$$invalidate'); + if (component.props.length > 0 || component.has_reactive_assignments || renderer.slots.size > 0) { + args.push('$$props', '$$invalidate'); + } builder.addBlock(deindent` - function create_fragment($$, ctx) { + function create_fragment(ctx) { ${block.getContents()} } @@ -264,6 +269,11 @@ export default function dom( const reactive_stores = Array.from(component.template_references).filter(n => n[0] === '$'); filtered_declarations.push(...reactive_stores); + if (renderer.slots.size > 0) { + const arr = Array.from(renderer.slots); + filtered_declarations.push(...arr.map(name => `$$slot_${sanitize(name)}`), '$$scope'); + } + const has_definition = ( component.javascript || filtered_props.length > 0 || @@ -301,6 +311,8 @@ export default function dom( function ${definition}(${args.join(', ')}) { ${user_code} + ${renderer.slots.size && `let { ${[...renderer.slots].map(name => `$$slot_${sanitize(name)}`).join(', ')}, $$scope } = $$props;`} + ${component.partly_hoisted.length > 0 && component.partly_hoisted.join('\n\n')} ${reactive_store_subscriptions} diff --git a/src/compile/render-dom/wrappers/AwaitBlock.ts b/src/compile/render-dom/wrappers/AwaitBlock.ts index c8ce02ab20..a5926fa8d7 100644 --- a/src/compile/render-dom/wrappers/AwaitBlock.ts +++ b/src/compile/render-dom/wrappers/AwaitBlock.ts @@ -135,7 +135,6 @@ export default class AwaitBlockWrapper extends Wrapper { block.maintainContext = true; const infoProps = [ - '$$', 'ctx', 'current: null', this.pending.block.name && `pending: ${this.pending.block.name}`, diff --git a/src/compile/render-dom/wrappers/EachBlock.ts b/src/compile/render-dom/wrappers/EachBlock.ts index c713ea5f19..490b0929a1 100644 --- a/src/compile/render-dom/wrappers/EachBlock.ts +++ b/src/compile/render-dom/wrappers/EachBlock.ts @@ -41,10 +41,6 @@ class ElseBlockWrapper extends Wrapper { ); this.isDynamic = this.block.dependencies.size > 0; - if (this.isDynamic) { - // TODO this can't be right - this.block.hasUpdateMethod = true; - } } } @@ -149,7 +145,6 @@ export default class EachBlockWrapper extends Wrapper { } block.addDependencies(this.block.dependencies); - this.block.hasUpdateMethod = this.block.dependencies.size > 0; // TODO should this logic be in Block? if (this.block.hasOutros || (this.else && this.else.block.hasOutros)) { block.addOutro(); @@ -216,7 +211,7 @@ export default class EachBlockWrapper extends Wrapper { // TODO neaten this up... will end up with an empty line in the block block.builders.init.addBlock(deindent` if (!${this.vars.each_block_value}.${this.vars.length}) { - ${each_block_else} = ${this.else.block.name}($$, ctx); + ${each_block_else} = ${this.else.block.name}(ctx); ${each_block_else}.c(); } `); @@ -234,7 +229,7 @@ export default class EachBlockWrapper extends Wrapper { if (!${this.vars.each_block_value}.${this.vars.length} && ${each_block_else}) { ${each_block_else}.p(changed, ctx); } else if (!${this.vars.each_block_value}.${this.vars.length}) { - ${each_block_else} = ${this.else.block.name}($$, ctx); + ${each_block_else} = ${this.else.block.name}(ctx); ${each_block_else}.c(); ${each_block_else}.m(${initialMountNode}, ${this.vars.anchor}); } else if (${each_block_else}) { @@ -250,7 +245,7 @@ export default class EachBlockWrapper extends Wrapper { ${each_block_else} = null; } } else if (!${each_block_else}) { - ${each_block_else} = ${this.else.block.name}($$, ctx); + ${each_block_else} = ${this.else.block.name}(ctx); ${each_block_else}.c(); ${each_block_else}.m(${initialMountNode}, ${this.vars.anchor}); } @@ -306,7 +301,7 @@ export default class EachBlockWrapper extends Wrapper { for (var #i = 0; #i < ${this.vars.each_block_value}.${length}; #i += 1) { let child_ctx = ${this.vars.get_each_context}(ctx, ${this.vars.each_block_value}, #i); let key = ${get_key}(child_ctx); - ${iterations}[#i] = ${lookup}[key] = ${create_each_block}($$, key, child_ctx); + ${iterations}[#i] = ${lookup}[key] = ${create_each_block}(key, child_ctx); } `); @@ -342,7 +337,7 @@ export default class EachBlockWrapper extends Wrapper { ${this.block.hasOutros && `@group_outros();`} ${this.node.hasAnimation && `for (let #i = 0; #i < ${iterations}.length; #i += 1) ${iterations}[#i].r();`} - ${iterations} = @updateKeyedEach(${iterations}, $$, changed, ${get_key}, ${dynamic ? '1' : '0'}, ctx, ${this.vars.each_block_value}, ${lookup}, ${updateMountNode}, ${destroy}, ${create_each_block}, ${anchor}, ${this.vars.get_each_context}); + ${iterations} = @updateKeyedEach(${iterations}, changed, ${get_key}, ${dynamic ? '1' : '0'}, ctx, ${this.vars.each_block_value}, ${lookup}, ${updateMountNode}, ${destroy}, ${create_each_block}, ${anchor}, ${this.vars.get_each_context}); ${this.node.hasAnimation && `for (let #i = 0; #i < ${iterations}.length; #i += 1) ${iterations}[#i].a();`} ${this.block.hasOutros && `@check_outros();`} `); @@ -375,7 +370,7 @@ export default class EachBlockWrapper extends Wrapper { var ${iterations} = []; for (var #i = 0; #i < ${this.vars.each_block_value}.${length}; #i += 1) { - ${iterations}[#i] = ${create_each_block}($$, ${this.vars.get_each_context}(ctx, ${this.vars.each_block_value}, #i)); + ${iterations}[#i] = ${create_each_block}(${this.vars.get_each_context}(ctx, ${this.vars.each_block_value}, #i)); } `); @@ -439,14 +434,14 @@ export default class EachBlockWrapper extends Wrapper { if (${iterations}[#i]) { ${iterations}[#i].p(changed, child_ctx); } else { - ${iterations}[#i] = ${create_each_block}($$, child_ctx); + ${iterations}[#i] = ${create_each_block}(child_ctx); ${iterations}[#i].c(); ${iterations}[#i].m(${updateMountNode}, ${anchor}); } ${has_transitions && `${iterations}[#i].i();`} ` : deindent` - ${iterations}[#i] = ${create_each_block}($$, child_ctx); + ${iterations}[#i] = ${create_each_block}(child_ctx); ${iterations}[#i].c(); ${iterations}[#i].m(${updateMountNode}, ${anchor}); ${has_transitions && `${iterations}[#i].i();`} @@ -499,9 +494,4 @@ export default class EachBlockWrapper extends Wrapper { block.builders.destroy.addBlock(`@destroyEach(${iterations}, detach);`); } - - remount(name: string) { - // TODO consider keyed blocks - return `for (var #i = 0; #i < ${this.vars.iterations}.length; #i += 1) ${this.vars.iterations}[#i].m(${name}.$$.slotted.default, null);`; - } } \ No newline at end of file diff --git a/src/compile/render-dom/wrappers/Element/Binding.ts b/src/compile/render-dom/wrappers/Element/Binding.ts index 8b34682f8c..a9fa106def 100644 --- a/src/compile/render-dom/wrappers/Element/Binding.ts +++ b/src/compile/render-dom/wrappers/Element/Binding.ts @@ -124,11 +124,11 @@ export default class BindingWrapper { const bindingGroup = getBindingGroup(parent.renderer, this.node.expression.node); block.builders.hydrate.addLine( - `($$.binding_groups[${bindingGroup}] || ($$.binding_groups[${bindingGroup}] = [])).push(${parent.var});` + `(ctx.$$binding_groups[${bindingGroup}] || (ctx.$$binding_groups[${bindingGroup}] = [])).push(${parent.var});` ); block.builders.destroy.addLine( - `$$.binding_groups[${bindingGroup}].splice($$.binding_groups[${bindingGroup}].indexOf(${parent.var}), 1);` + `ctx.$$binding_groups[${bindingGroup}].splice(ctx.$$binding_groups[${bindingGroup}].indexOf(${parent.var}), 1);` ); break; @@ -278,7 +278,7 @@ function getValueFromDom( if (name === 'group') { const bindingGroup = getBindingGroup(renderer, binding.node.expression.node); if (type === 'checkbox') { - return `@getBindingGroupValue($$self.$$.binding_groups[${bindingGroup}])`; + return `@getBindingGroupValue($$self.ctx.$$binding_groups[${bindingGroup}])`; } return `this.__value`; diff --git a/src/compile/render-dom/wrappers/Element/index.ts b/src/compile/render-dom/wrappers/Element/index.ts index 7d0288726e..2bd4d7f13d 100644 --- a/src/compile/render-dom/wrappers/Element/index.ts +++ b/src/compile/render-dom/wrappers/Element/index.ts @@ -19,6 +19,8 @@ import InlineComponentWrapper from '../InlineComponent'; import addToSet from '../../../../utils/addToSet'; import addEventHandlers from '../shared/addEventHandlers'; import addActions from '../shared/addActions'; +import createDebuggingComment from '../../../../utils/createDebuggingComment'; +import sanitize from '../../../../utils/sanitize'; const events = [ { @@ -91,7 +93,7 @@ export default class ElementWrapper extends Wrapper { bindings: Binding[]; classDependencies: string[]; - slotOwner?: InlineComponentWrapper; + slot_block: Block; selectBindingDependencies?: Set; var: string; @@ -126,8 +128,17 @@ export default class ElementWrapper extends Wrapper { } if (owner && owner.node.type === 'InlineComponent') { - this.slotOwner = owner; - owner._slots.add(attribute.getStaticValue()); + const name = attribute.getStaticValue(); + + this.slot_block = block.child({ + comment: createDebuggingComment(node, this.renderer.component), + name: this.renderer.component.getUniqueName(`create_${sanitize(name)}_slot`) + }); + + (owner).slots.set(name, this.slot_block); + this.renderer.blocks.push(this.slot_block); + + block = this.slot_block; } } if (attribute.name === 'style') { @@ -179,6 +190,10 @@ export default class ElementWrapper extends Wrapper { } this.fragment = new FragmentWrapper(renderer, block, node.children, this, stripWhitespace, nextSibling); + + if (this.slot_block) { + block.parent.addDependencies(block.dependencies); + } } render(block: Block, parentNode: string, parentNodes: string) { @@ -194,15 +209,8 @@ export default class ElementWrapper extends Wrapper { const node = this.var; const nodes = parentNodes && block.getUniqueName(`${this.var}_nodes`) // if we're in unclaimable territory, i.e. , parentNodes is null - const slot = this.node.attributes.find((attribute: Node) => attribute.name === 'slot'); - const prop = slot && quotePropIfNecessary(slot.chunks[0].data); - - let initialMountNode; - - if (this.slotOwner) { - initialMountNode = `${this.slotOwner.var}.$$.slotted${prop}`; - } else { - initialMountNode = parentNode; + if (this.slot_block) { + block = this.slot_block; } block.addVariable(node); @@ -224,12 +232,12 @@ export default class ElementWrapper extends Wrapper { } } - if (initialMountNode) { + if (parentNode) { block.builders.mount.addLine( - `@append(${initialMountNode}, ${node});` + `@append(${parentNode}, ${node});` ); - if (initialMountNode === 'document.head') { + if (parentNode === 'document.head') { block.builders.destroy.addLine(`@detachNode(${node});`); } } else { @@ -755,16 +763,6 @@ export default class ElementWrapper extends Wrapper { return null; } - remount(name: string) { - const slot = this.attributes.find(attribute => attribute.node.name === 'slot'); - if (slot) { - const prop = quotePropIfNecessary(slot.node.chunks[0].data); - return `@append(${name}.$$.slotted${prop}, ${this.var});`; - } - - return `@append(${name}.$$.slotted.default, ${this.var});`; - } - addCssClass(className = this.component.stylesheet.id) { const classAttribute = this.attributes.find(a => a.name === 'class'); if (classAttribute && !classAttribute.isTrue) { diff --git a/src/compile/render-dom/wrappers/IfBlock.ts b/src/compile/render-dom/wrappers/IfBlock.ts index ab4c697881..026f668b28 100644 --- a/src/compile/render-dom/wrappers/IfBlock.ts +++ b/src/compile/render-dom/wrappers/IfBlock.ts @@ -222,7 +222,7 @@ export default class IfBlockWrapper extends Wrapper { block.builders.init.addBlock(deindent` var ${current_block_type} = ${select_block_type}(ctx); - var ${name} = ${current_block_type_and}${current_block_type}($$, ctx); + var ${name} = ${current_block_type_and}${current_block_type}(ctx); `); const initialMountNode = parentNode || '#target'; @@ -235,7 +235,7 @@ export default class IfBlockWrapper extends Wrapper { const changeBlock = deindent` ${if_name}${name}.d(1); - ${name} = ${current_block_type_and}${current_block_type}($$, ctx); + ${name} = ${current_block_type_and}${current_block_type}(ctx); if (${name}) { ${name}.c(); ${name}.m(${updateMountNode}, ${anchor}); @@ -302,12 +302,12 @@ export default class IfBlockWrapper extends Wrapper { if (hasElse) { block.builders.init.addBlock(deindent` ${current_block_type_index} = ${select_block_type}(ctx); - ${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}]($$, ctx); + ${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](ctx); `); } else { block.builders.init.addBlock(deindent` if (~(${current_block_type_index} = ${select_block_type}(ctx))) { - ${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}]($$, ctx); + ${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](ctx); } `); } @@ -334,7 +334,7 @@ export default class IfBlockWrapper extends Wrapper { const createNewBlock = deindent` ${name} = ${if_blocks}[${current_block_type_index}]; if (!${name}) { - ${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}]($$, ctx); + ${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](ctx); ${name}.c(); } ${name}.m(${updateMountNode}, ${anchor}); @@ -394,7 +394,7 @@ export default class IfBlockWrapper extends Wrapper { const branch = this.branches[0]; block.builders.init.addBlock(deindent` - var ${name} = (${branch.condition}) && ${branch.block.name}($$, ctx); + var ${name} = (${branch.condition}) && ${branch.block.name}(ctx); `); const initialMountNode = parentNode || '#target'; @@ -411,7 +411,7 @@ export default class IfBlockWrapper extends Wrapper { if (${name}) { ${name}.p(changed, ctx); } else { - ${name} = ${branch.block.name}($$, ctx); + ${name} = ${branch.block.name}(ctx); ${name}.c(); ${name}.m(${updateMountNode}, ${anchor}); } @@ -419,7 +419,7 @@ export default class IfBlockWrapper extends Wrapper { ` : deindent` if (!${name}) { - ${name} = ${branch.block.name}($$, ctx); + ${name} = ${branch.block.name}(ctx); ${name}.c(); ${name}.m(${updateMountNode}, ${anchor}); } diff --git a/src/compile/render-dom/wrappers/InlineComponent/index.ts b/src/compile/render-dom/wrappers/InlineComponent/index.ts index 9669910d06..77b00fb9a8 100644 --- a/src/compile/render-dom/wrappers/InlineComponent/index.ts +++ b/src/compile/render-dom/wrappers/InlineComponent/index.ts @@ -10,12 +10,13 @@ import addToSet from '../../../../utils/addToSet'; import deindent from '../../../../utils/deindent'; import Attribute from '../../../nodes/Attribute'; import getObject from '../../../../utils/getObject'; -import Binding from '../../../nodes/Binding'; import flattenReference from '../../../../utils/flattenReference'; +import createDebuggingComment from '../../../../utils/createDebuggingComment'; +import sanitize from '../../../../utils/sanitize'; export default class InlineComponentWrapper extends Wrapper { var: string; - _slots: Set; // TODO lose the underscore + slots: Map = new Map(); node: InlineComponent; fragment: FragmentWrapper; @@ -65,8 +66,15 @@ export default class InlineComponentWrapper extends Wrapper { ).toLowerCase(); if (this.node.children.length) { - this._slots = new Set(['default']); - this.fragment = new FragmentWrapper(renderer, block, node.children, this, stripWhitespace, nextSibling); + const default_slot = block.child({ + comment: createDebuggingComment(node, renderer.component), + name: renderer.component.getUniqueName(`create_default_slot`) + }); + this.renderer.blocks.push(default_slot); + this.slots.set('default', default_slot); + this.fragment = new FragmentWrapper(renderer, default_slot, node.children, this, stripWhitespace, nextSibling); + + block.addDependencies(default_slot.dependencies); } block.addOutro(); @@ -84,15 +92,6 @@ export default class InlineComponentWrapper extends Wrapper { const component_opts = []; - if (this.fragment) { - const slots = Array.from(this._slots).map(name => `${quoteNameIfNecessary(name)}: @createFragment()`); - component_opts.push(`slots: { ${slots.join(', ')} }`); - - this.fragment.nodes.forEach((child: Wrapper) => { - child.render(block, `${this.var}.$$.slotted.default`, 'nodes'); - }); - } - const statements: string[] = []; const updates: string[] = []; const postupdates: string[] = []; @@ -102,13 +101,16 @@ export default class InlineComponentWrapper extends Wrapper { const usesSpread = !!this.node.attributes.find(a => a.isSpread); + const slot_props = Array.from(this.slots).map(([name, block]) => `$$slot_${sanitize(name)}: ${block.name}`); + if (slot_props.length > 0) slot_props.push(`$$scope: { ctx }`); + const attributeObject = usesSpread - ? '{}' + ? stringifyProps(slot_props) : stringifyProps( - this.node.attributes.map(attr => `${quoteNameIfNecessary(attr.name)}: ${attr.getValue()}`) + this.node.attributes.map(attr => `${quoteNameIfNecessary(attr.name)}: ${attr.getValue()}`).concat(slot_props) ); - if (this.node.attributes.length || this.node.bindings.length) { + if (this.node.attributes.length || this.node.bindings.length || slot_props.length) { if (!usesSpread && this.node.bindings.length === 0) { component_opts.push(`props: ${attributeObject}`); } else { @@ -117,6 +119,14 @@ export default class InlineComponentWrapper extends Wrapper { } } + if (this.fragment) { + const default_slot = this.slots.get('default'); + + this.fragment.nodes.forEach((child: Wrapper) => { + child.render(default_slot, null, 'nodes'); + }); + } + if (component.options.dev) { // TODO this is a terrible hack, but without it the component // will complain that options.target is missing. This would @@ -125,7 +135,16 @@ export default class InlineComponentWrapper extends Wrapper { component_opts.push(`$$inline: true`); } - if (!usesSpread && (this.node.attributes.filter(a => a.isDynamic).length || this.node.bindings.length)) { + const fragment_dependencies = new Set(); + this.slots.forEach(block => { + block.dependencies.forEach(name => { + if (renderer.component.mutable_props.has(name)) { + fragment_dependencies.add(name); + } + }); + }); + + if (!usesSpread && (this.node.attributes.filter(a => a.isDynamic).length || this.node.bindings.length || fragment_dependencies.size > 0)) { updates.push(`var ${name_changes} = {};`); } @@ -196,6 +215,10 @@ export default class InlineComponentWrapper extends Wrapper { } } + if (fragment_dependencies.size > 0) { + updates.push(`if (${[...fragment_dependencies].map(n => `changed.${n}`).join(' || ')}) ${name_changes}.$$scope = { changed, ctx };`); + } + const munged_bindings = this.node.bindings.map(binding => { component.has_reactive_assignments = true; @@ -375,7 +398,6 @@ export default class InlineComponentWrapper extends Wrapper { ${munged_bindings} ${munged_handlers} - ${this.fragment && this.fragment.nodes.map(child => child.remount(name))} ${name}.$$.fragment.c(); @mount_component(${name}, ${updateMountNode}, ${anchor}); ${name}.$$.fragment.i(); @@ -448,10 +470,6 @@ export default class InlineComponentWrapper extends Wrapper { `if (${name}) ${name}.$$.fragment.o();` ); } - - remount(name: string) { - return `${this.var}.$$.fragment.m(${name}.$$.slotted.default, null);`; - } } function isComputed(node: Node) { diff --git a/src/compile/render-dom/wrappers/Slot.ts b/src/compile/render-dom/wrappers/Slot.ts index 2788fc075b..aa9a3d5b20 100644 --- a/src/compile/render-dom/wrappers/Slot.ts +++ b/src/compile/render-dom/wrappers/Slot.ts @@ -41,42 +41,24 @@ export default class SlotWrapper extends Wrapper { ) { const { renderer } = this; - const slotName = this.node.getStaticAttributeValue('name') || 'default'; - renderer.slots.add(slotName); + const slot_name = this.node.getStaticAttributeValue('name') || 'default'; + renderer.slots.add(slot_name); - const content_name = block.getUniqueName(`slot_content_${sanitize(slotName)}`); - const prop = quotePropIfNecessary(slotName); - block.addVariable(content_name, `$$.slotted${prop}`); + const slot = block.getUniqueName(`${sanitize(slot_name)}_slot`); - // TODO can we use isDomNode instead of type === 'Element'? - const needsAnchorBefore = this.prev ? this.prev.node.type !== 'Element' : !parentNode; - const needsAnchorAfter = this.next ? this.next.node.type !== 'Element' : !parentNode; - - const anchorBefore = needsAnchorBefore - ? block.getUniqueName(`${content_name}_before`) - : (this.prev && this.prev.var) || 'null'; - - const anchorAfter = needsAnchorAfter - ? block.getUniqueName(`${content_name}_after`) - : (this.next && this.next.var) || 'null'; - - if (needsAnchorBefore) block.addVariable(anchorBefore); - if (needsAnchorAfter) block.addVariable(anchorAfter); + block.builders.init.addLine( + `const ${slot} = ctx.$$slot_${sanitize(slot_name)} && ctx.$$slot_${sanitize(slot_name)}(ctx.$$scope.ctx);` + ); let mountBefore = block.builders.mount.toString(); - let destroyBefore = block.builders.destroy.toString(); - block.builders.create.pushCondition(`!${content_name}`); - block.builders.hydrate.pushCondition(`!${content_name}`); - block.builders.mount.pushCondition(`!${content_name}`); - block.builders.update.pushCondition(`!${content_name}`); - block.builders.destroy.pushCondition(`!${content_name}`); + block.builders.create.pushCondition(`!${slot}`); + block.builders.hydrate.pushCondition(`!${slot}`); + block.builders.mount.pushCondition(`!${slot}`); + block.builders.update.pushCondition(`!${slot}`); + block.builders.destroy.pushCondition(`!${slot}`); - const listeners = block.event_listeners; - block.event_listeners = []; this.fragment.render(block, parentNode, parentNodes); - block.renderListeners(`_${content_name}`); - block.event_listeners = listeners; block.builders.create.popCondition(); block.builders.hydrate.popCondition(); @@ -84,62 +66,26 @@ export default class SlotWrapper extends Wrapper { block.builders.update.popCondition(); block.builders.destroy.popCondition(); + block.builders.create.addLine( + `if (${slot}) ${slot}.c();` + ); + + block.builders.claim.addLine( + `if (${slot}) ${slot}.l(${parentNodes});` + ); + const mountLeadin = block.builders.mount.toString() !== mountBefore ? `else` - : `if (${content_name})`; - - if (parentNode) { - block.builders.mount.addBlock(deindent` - ${mountLeadin} { - ${needsAnchorBefore && `@append(${parentNode}, ${anchorBefore} || (${anchorBefore} = @createComment()));`} - @append(${parentNode}, ${content_name}); - ${needsAnchorAfter && `@append(${parentNode}, ${anchorAfter} || (${anchorAfter} = @createComment()));`} - } - `); - } else { - block.builders.mount.addBlock(deindent` - ${mountLeadin} { - ${needsAnchorBefore && `@insert(#target, ${anchorBefore} || (${anchorBefore} = @createComment()), anchor);`} - @insert(#target, ${content_name}, anchor); - ${needsAnchorAfter && `@insert(#target, ${anchorAfter} || (${anchorAfter} = @createComment()), anchor);`} - } - `); - } - - // if the slot is unmounted, move nodes back into the document fragment, - // so that it can be reinserted later - // TODO so that this can work with public API, component.$$.slotted should - // be all fragments, derived from options.slots. Not === options.slots - const unmountLeadin = block.builders.destroy.toString() !== destroyBefore - ? `else` - : `if (${content_name})`; - - if (anchorBefore === 'null' && anchorAfter === 'null') { - block.builders.destroy.addBlock(deindent` - ${unmountLeadin} { - @reinsertChildren(${parentNode}, ${content_name}); - } - `); - } else if (anchorBefore === 'null') { - block.builders.destroy.addBlock(deindent` - ${unmountLeadin} { - @reinsertBefore(${anchorAfter}, ${content_name}); - } - `); - } else if (anchorAfter === 'null') { - block.builders.destroy.addBlock(deindent` - ${unmountLeadin} { - @reinsertAfter(${anchorBefore}, ${content_name}); - } - `); - } else { - block.builders.destroy.addBlock(deindent` - ${unmountLeadin} { - @reinsertBetween(${anchorBefore}, ${anchorAfter}, ${content_name}); - @detachNode(${anchorBefore}); - @detachNode(${anchorAfter}); - } - `); - } + : `if (${slot})`; + + block.builders.mount.addBlock(deindent` + ${mountLeadin} { + ${slot}.m(${parentNode || '#target'}, anchor); + } + `); + + block.builders.update.addLine( + `if (${slot} && changed.$$scope) ${slot}.p(ctx.$$scope.changed, ctx.$$scope.ctx);` + ); } } \ No newline at end of file diff --git a/src/compile/render-dom/wrappers/Text.ts b/src/compile/render-dom/wrappers/Text.ts index fea357f1d5..00028df67c 100644 --- a/src/compile/render-dom/wrappers/Text.ts +++ b/src/compile/render-dom/wrappers/Text.ts @@ -60,8 +60,4 @@ export default class TextWrapper extends Wrapper { parentNode ); } - - remount(name: string) { - return `@append(${name}.$$.slotted.default, ${this.var});`; - } } \ No newline at end of file diff --git a/src/compile/render-dom/wrappers/shared/Tag.ts b/src/compile/render-dom/wrappers/shared/Tag.ts index d44a53ffde..475c0d8a70 100644 --- a/src/compile/render-dom/wrappers/shared/Tag.ts +++ b/src/compile/render-dom/wrappers/shared/Tag.ts @@ -51,8 +51,4 @@ export default class Tag extends Wrapper { return { init: content }; } - - remount(name: string) { - return `@append(${name}.$$.slotted.default, ${this.var});`; - } } \ No newline at end of file diff --git a/src/compile/render-dom/wrappers/shared/Wrapper.ts b/src/compile/render-dom/wrappers/shared/Wrapper.ts index 3ee2bbbb9f..daa97f36fc 100644 --- a/src/compile/render-dom/wrappers/shared/Wrapper.ts +++ b/src/compile/render-dom/wrappers/shared/Wrapper.ts @@ -85,8 +85,4 @@ export default class Wrapper { render(block: Block, parentNode: string, parentNodes: string) { throw new Error(`render method not implemented by subclass ${this.node.type}`); } - - remount(name: string) { - return `${this.var}.m(${name}.$$.slotted.default, null);`; - } } \ No newline at end of file diff --git a/src/internal/Component.js b/src/internal/Component.js index 894f80461b..63350bbd1c 100644 --- a/src/internal/Component.js +++ b/src/internal/Component.js @@ -57,9 +57,11 @@ export function init(component, options, instance, create_fragment, not_equal) { const previous_component = current_component; set_current_component(component); + const props = options.props || {}; + const $$ = component.$$ = { fragment: null, - ctx: options.props || {}, + ctx: null, // state set: noop, @@ -75,15 +77,13 @@ export function init(component, options, instance, create_fragment, not_equal) { // everything else callbacks: blankObject(), - slotted: options.slots || {}, - dirty: null, - binding_groups: [] + dirty: null }; let ready = false; - if (instance) { - $$.ctx = instance(component, $$.ctx, (key, value) => { + $$.ctx = instance + ? instance(component, props, (key, value) => { if ($$.bound[key]) $$.bound[key](value); if ($$.ctx) { @@ -95,13 +95,15 @@ export function init(component, options, instance, create_fragment, not_equal) { $$.ctx[key] = value; return changed; } - }); - } + }) + : props; + + $$.ctx.$$binding_groups = []; // TODO this is awkward and usually unncessary $$.update(); ready = true; run_all($$.before_render); - $$.fragment = create_fragment($$, $$.ctx); + $$.fragment = create_fragment($$.ctx); if (options.target) { if (options.hydrate) { diff --git a/src/internal/await-block.js b/src/internal/await-block.js index 3d8be35edc..0ec254ee4b 100644 --- a/src/internal/await-block.js +++ b/src/internal/await-block.js @@ -11,7 +11,7 @@ export function handlePromise(promise, info) { info.resolved = key && { [key]: value }; const child_ctx = assign(assign({}, info.ctx), info.resolved); - const block = type && (info.current = type)(info.$$, child_ctx); + const block = type && (info.current = type)(child_ctx); if (info.block) { if (info.blocks) { diff --git a/src/internal/keyed-each.js b/src/internal/keyed-each.js index ad5eb243d2..76e233f2ed 100644 --- a/src/internal/keyed-each.js +++ b/src/internal/keyed-each.js @@ -18,7 +18,7 @@ export function fixAndOutroAndDestroyBlock(block, lookup) { outroAndDestroyBlock(block, lookup); } -export function updateKeyedEach(old_blocks, component, changed, get_key, dynamic, ctx, list, lookup, node, destroy, create_each_block, next, get_context) { +export function updateKeyedEach(old_blocks, changed, get_key, dynamic, ctx, list, lookup, node, destroy, create_each_block, next, get_context) { var o = old_blocks.length; var n = list.length; @@ -37,7 +37,7 @@ export function updateKeyedEach(old_blocks, component, changed, get_key, dynamic var block = lookup[key]; if (!block) { - block = create_each_block(component, key, child_ctx); + block = create_each_block(key, child_ctx); block.c(); } else if (dynamic) { block.p(changed, child_ctx); diff --git a/src/utils/createDebuggingComment.ts b/src/utils/createDebuggingComment.ts index 15b8b12267..37e27aab4a 100644 --- a/src/utils/createDebuggingComment.ts +++ b/src/utils/createDebuggingComment.ts @@ -10,12 +10,19 @@ export default function createDebuggingComment( let c = node.start; if (node.type === 'ElseBlock') { while (source[c - 1] !== '{') c -= 1; - while (source[c - 1] === '{') c -= 1; + // while (source[c - 1] === '{') c -= 1; } - let d = node.expression ? node.expression.node.end : c; - while (source[d] !== '}') d += 1; - while (source[d] === '}') d += 1; + let d; + + if (node.type === 'InlineComponent' || node.type === 'Element') { + d = node.children[0].start; + while (source[d - 1] !== '>') d -= 1; + } else { + d = node.expression ? node.expression.node.end : c; + while (source[d] !== '}') d += 1; + // while (source[d] === '}') d += 1; + } const start = locate(c); const loc = `(${start.line + 1}:${start.column})`;