start making slots lazy

pull/1998/head
Richard Harris 7 years ago
parent ff6e378fcb
commit c3e3a8c8f2

@ -637,16 +637,6 @@ export default class Element extends Node {
return this.name === 'audio' || this.name === 'video'; 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) { addCssClass(className = this.component.stylesheet.id) {
const classAttribute = this.attributes.find(a => a.name === 'class'); const classAttribute = this.attributes.find(a => a.name === 'class');
if (classAttribute && !classAttribute.isTrue) { if (classAttribute && !classAttribute.isTrue) {

@ -48,10 +48,6 @@ export default class Node {
if (this.parent) return this.parent.findNearest(selector); if (this.parent) return this.parent.findNearest(selector);
} }
remount(name: string) {
return `${this.var}.m(${name}.$$.slotted.default, null);`;
}
warnIfEmptyBlock() { warnIfEmptyBlock() {
if (!this.component.options.dev) return; if (!this.component.options.dev) return;
if (!/Block$/.test(this.type) || !this.children) return; if (!/Block$/.test(this.type) || !this.children) return;

@ -61,7 +61,7 @@ export default class Block {
variables: Map<string, string>; variables: Map<string, string>;
getUniqueName: (name: string) => string; getUniqueName: (name: string) => string;
hasUpdateMethod: boolean; hasUpdateMethod = false;
autofocus: string; autofocus: string;
constructor(options: BlockOptions) { constructor(options: BlockOptions) {
@ -106,8 +106,6 @@ export default class Block {
this.aliases = new Map().set('ctx', this.getUniqueName('ctx')); this.aliases = new Map().set('ctx', this.getUniqueName('ctx'));
if (this.key) this.aliases.set('key', this.getUniqueName('key')); if (this.key) this.aliases.set('key', this.getUniqueName('key'));
this.hasUpdateMethod = false; // determined later
} }
assignVariableNames() { assignVariableNames() {
@ -151,6 +149,8 @@ export default class Block {
dependencies.forEach(dependency => { dependencies.forEach(dependency => {
this.dependencies.add(dependency); this.dependencies.add(dependency);
}); });
this.hasUpdateMethod = true;
} }
addElement( addElement(
@ -407,7 +407,7 @@ export default class Block {
return deindent` return deindent`
${this.comment && `// ${this.comment}`} ${this.comment && `// ${this.comment}`}
function ${this.name}($$, ${this.key ? `${localKey}, ` : ''}ctx) { function ${this.name}(${this.key ? `${localKey}, ` : ''}ctx) {
${this.getContents(localKey)} ${this.getContents(localKey)}
} }
`; `;

@ -10,6 +10,7 @@ import addToSet from '../../utils/addToSet';
import getObject from '../../utils/getObject'; import getObject from '../../utils/getObject';
import { extractNames } from '../../utils/annotateWithScopes'; import { extractNames } from '../../utils/annotateWithScopes';
import { nodes_match } from '../../utils/nodes_match'; import { nodes_match } from '../../utils/nodes_match';
import sanitize from '../../utils/sanitize';
export default function dom( export default function dom(
component: Component, component: Component,
@ -71,7 +72,7 @@ export default function dom(
const props = component.props.filter(x => component.writable_declarations.has(x.name)); 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` ? deindent`
$$props => { $$props => {
${component.meta.props && deindent` ${component.meta.props && deindent`
@ -81,6 +82,8 @@ export default function dom(
`} `}
${props.map(prop => ${props.map(prop =>
`if ('${prop.as}' in $$props) $$invalidate('${prop.name}', ${prop.name} = $$props.${prop.as});`)} `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; : null;
@ -235,10 +238,12 @@ export default function dom(
} }
const args = ['$$self']; 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` builder.addBlock(deindent`
function create_fragment($$, ctx) { function create_fragment(ctx) {
${block.getContents()} ${block.getContents()}
} }
@ -264,6 +269,11 @@ export default function dom(
const reactive_stores = Array.from(component.template_references).filter(n => n[0] === '$'); const reactive_stores = Array.from(component.template_references).filter(n => n[0] === '$');
filtered_declarations.push(...reactive_stores); 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 = ( const has_definition = (
component.javascript || component.javascript ||
filtered_props.length > 0 || filtered_props.length > 0 ||
@ -301,6 +311,8 @@ export default function dom(
function ${definition}(${args.join(', ')}) { function ${definition}(${args.join(', ')}) {
${user_code} ${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')} ${component.partly_hoisted.length > 0 && component.partly_hoisted.join('\n\n')}
${reactive_store_subscriptions} ${reactive_store_subscriptions}

@ -135,7 +135,6 @@ export default class AwaitBlockWrapper extends Wrapper {
block.maintainContext = true; block.maintainContext = true;
const infoProps = [ const infoProps = [
'$$',
'ctx', 'ctx',
'current: null', 'current: null',
this.pending.block.name && `pending: ${this.pending.block.name}`, this.pending.block.name && `pending: ${this.pending.block.name}`,

@ -41,10 +41,6 @@ class ElseBlockWrapper extends Wrapper {
); );
this.isDynamic = this.block.dependencies.size > 0; 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); 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)) { if (this.block.hasOutros || (this.else && this.else.block.hasOutros)) {
block.addOutro(); 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 // TODO neaten this up... will end up with an empty line in the block
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
if (!${this.vars.each_block_value}.${this.vars.length}) { 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}.c();
} }
`); `);
@ -234,7 +229,7 @@ export default class EachBlockWrapper extends Wrapper {
if (!${this.vars.each_block_value}.${this.vars.length} && ${each_block_else}) { if (!${this.vars.each_block_value}.${this.vars.length} && ${each_block_else}) {
${each_block_else}.p(changed, ctx); ${each_block_else}.p(changed, ctx);
} else if (!${this.vars.each_block_value}.${this.vars.length}) { } 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}.c();
${each_block_else}.m(${initialMountNode}, ${this.vars.anchor}); ${each_block_else}.m(${initialMountNode}, ${this.vars.anchor});
} else if (${each_block_else}) { } else if (${each_block_else}) {
@ -250,7 +245,7 @@ export default class EachBlockWrapper extends Wrapper {
${each_block_else} = null; ${each_block_else} = null;
} }
} else if (!${each_block_else}) { } 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}.c();
${each_block_else}.m(${initialMountNode}, ${this.vars.anchor}); ${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) { 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 child_ctx = ${this.vars.get_each_context}(ctx, ${this.vars.each_block_value}, #i);
let key = ${get_key}(child_ctx); 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.block.hasOutros && `@group_outros();`}
${this.node.hasAnimation && `for (let #i = 0; #i < ${iterations}.length; #i += 1) ${iterations}[#i].r();`} ${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.node.hasAnimation && `for (let #i = 0; #i < ${iterations}.length; #i += 1) ${iterations}[#i].a();`}
${this.block.hasOutros && `@check_outros();`} ${this.block.hasOutros && `@check_outros();`}
`); `);
@ -375,7 +370,7 @@ export default class EachBlockWrapper extends Wrapper {
var ${iterations} = []; var ${iterations} = [];
for (var #i = 0; #i < ${this.vars.each_block_value}.${length}; #i += 1) { 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]) { if (${iterations}[#i]) {
${iterations}[#i].p(changed, child_ctx); ${iterations}[#i].p(changed, child_ctx);
} else { } else {
${iterations}[#i] = ${create_each_block}($$, child_ctx); ${iterations}[#i] = ${create_each_block}(child_ctx);
${iterations}[#i].c(); ${iterations}[#i].c();
${iterations}[#i].m(${updateMountNode}, ${anchor}); ${iterations}[#i].m(${updateMountNode}, ${anchor});
} }
${has_transitions && `${iterations}[#i].i();`} ${has_transitions && `${iterations}[#i].i();`}
` `
: deindent` : deindent`
${iterations}[#i] = ${create_each_block}($$, child_ctx); ${iterations}[#i] = ${create_each_block}(child_ctx);
${iterations}[#i].c(); ${iterations}[#i].c();
${iterations}[#i].m(${updateMountNode}, ${anchor}); ${iterations}[#i].m(${updateMountNode}, ${anchor});
${has_transitions && `${iterations}[#i].i();`} ${has_transitions && `${iterations}[#i].i();`}
@ -499,9 +494,4 @@ export default class EachBlockWrapper extends Wrapper {
block.builders.destroy.addBlock(`@destroyEach(${iterations}, detach);`); 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);`;
}
} }

@ -124,11 +124,11 @@ export default class BindingWrapper {
const bindingGroup = getBindingGroup(parent.renderer, this.node.expression.node); const bindingGroup = getBindingGroup(parent.renderer, this.node.expression.node);
block.builders.hydrate.addLine( 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( 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; break;
@ -278,7 +278,7 @@ function getValueFromDom(
if (name === 'group') { if (name === 'group') {
const bindingGroup = getBindingGroup(renderer, binding.node.expression.node); const bindingGroup = getBindingGroup(renderer, binding.node.expression.node);
if (type === 'checkbox') { if (type === 'checkbox') {
return `@getBindingGroupValue($$self.$$.binding_groups[${bindingGroup}])`; return `@getBindingGroupValue($$self.ctx.$$binding_groups[${bindingGroup}])`;
} }
return `this.__value`; return `this.__value`;

@ -19,6 +19,8 @@ import InlineComponentWrapper from '../InlineComponent';
import addToSet from '../../../../utils/addToSet'; import addToSet from '../../../../utils/addToSet';
import addEventHandlers from '../shared/addEventHandlers'; import addEventHandlers from '../shared/addEventHandlers';
import addActions from '../shared/addActions'; import addActions from '../shared/addActions';
import createDebuggingComment from '../../../../utils/createDebuggingComment';
import sanitize from '../../../../utils/sanitize';
const events = [ const events = [
{ {
@ -91,7 +93,7 @@ export default class ElementWrapper extends Wrapper {
bindings: Binding[]; bindings: Binding[];
classDependencies: string[]; classDependencies: string[];
slotOwner?: InlineComponentWrapper; slot_block: Block;
selectBindingDependencies?: Set<string>; selectBindingDependencies?: Set<string>;
var: string; var: string;
@ -126,8 +128,17 @@ export default class ElementWrapper extends Wrapper {
} }
if (owner && owner.node.type === 'InlineComponent') { if (owner && owner.node.type === 'InlineComponent') {
this.slotOwner = <InlineComponentWrapper>owner; const name = attribute.getStaticValue();
owner._slots.add(attribute.getStaticValue());
this.slot_block = block.child({
comment: createDebuggingComment(node, this.renderer.component),
name: this.renderer.component.getUniqueName(`create_${sanitize(name)}_slot`)
});
(<InlineComponentWrapper>owner).slots.set(name, this.slot_block);
this.renderer.blocks.push(this.slot_block);
block = this.slot_block;
} }
} }
if (attribute.name === 'style') { 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); 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) { render(block: Block, parentNode: string, parentNodes: string) {
@ -194,15 +209,8 @@ export default class ElementWrapper extends Wrapper {
const node = this.var; const node = this.var;
const nodes = parentNodes && block.getUniqueName(`${this.var}_nodes`) // if we're in unclaimable territory, i.e. <head>, parentNodes is null const nodes = parentNodes && block.getUniqueName(`${this.var}_nodes`) // if we're in unclaimable territory, i.e. <head>, parentNodes is null
const slot = this.node.attributes.find((attribute: Node) => attribute.name === 'slot'); if (this.slot_block) {
const prop = slot && quotePropIfNecessary(slot.chunks[0].data); block = this.slot_block;
let initialMountNode;
if (this.slotOwner) {
initialMountNode = `${this.slotOwner.var}.$$.slotted${prop}`;
} else {
initialMountNode = parentNode;
} }
block.addVariable(node); block.addVariable(node);
@ -224,12 +232,12 @@ export default class ElementWrapper extends Wrapper {
} }
} }
if (initialMountNode) { if (parentNode) {
block.builders.mount.addLine( block.builders.mount.addLine(
`@append(${initialMountNode}, ${node});` `@append(${parentNode}, ${node});`
); );
if (initialMountNode === 'document.head') { if (parentNode === 'document.head') {
block.builders.destroy.addLine(`@detachNode(${node});`); block.builders.destroy.addLine(`@detachNode(${node});`);
} }
} else { } else {
@ -755,16 +763,6 @@ export default class ElementWrapper extends Wrapper {
return null; 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) { addCssClass(className = this.component.stylesheet.id) {
const classAttribute = this.attributes.find(a => a.name === 'class'); const classAttribute = this.attributes.find(a => a.name === 'class');
if (classAttribute && !classAttribute.isTrue) { if (classAttribute && !classAttribute.isTrue) {

@ -222,7 +222,7 @@ export default class IfBlockWrapper extends Wrapper {
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
var ${current_block_type} = ${select_block_type}(ctx); 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'; const initialMountNode = parentNode || '#target';
@ -235,7 +235,7 @@ export default class IfBlockWrapper extends Wrapper {
const changeBlock = deindent` const changeBlock = deindent`
${if_name}${name}.d(1); ${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}) { if (${name}) {
${name}.c(); ${name}.c();
${name}.m(${updateMountNode}, ${anchor}); ${name}.m(${updateMountNode}, ${anchor});
@ -302,12 +302,12 @@ export default class IfBlockWrapper extends Wrapper {
if (hasElse) { if (hasElse) {
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
${current_block_type_index} = ${select_block_type}(ctx); ${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 { } else {
block.builders.init.addBlock(deindent` block.builders.init.addBlock(deindent`
if (~(${current_block_type_index} = ${select_block_type}(ctx))) { 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` const createNewBlock = deindent`
${name} = ${if_blocks}[${current_block_type_index}]; ${name} = ${if_blocks}[${current_block_type_index}];
if (!${name}) { 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}.c();
} }
${name}.m(${updateMountNode}, ${anchor}); ${name}.m(${updateMountNode}, ${anchor});
@ -394,7 +394,7 @@ export default class IfBlockWrapper extends Wrapper {
const branch = this.branches[0]; const branch = this.branches[0];
block.builders.init.addBlock(deindent` 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'; const initialMountNode = parentNode || '#target';
@ -411,7 +411,7 @@ export default class IfBlockWrapper extends Wrapper {
if (${name}) { if (${name}) {
${name}.p(changed, ctx); ${name}.p(changed, ctx);
} else { } else {
${name} = ${branch.block.name}($$, ctx); ${name} = ${branch.block.name}(ctx);
${name}.c(); ${name}.c();
${name}.m(${updateMountNode}, ${anchor}); ${name}.m(${updateMountNode}, ${anchor});
} }
@ -419,7 +419,7 @@ export default class IfBlockWrapper extends Wrapper {
` `
: deindent` : deindent`
if (!${name}) { if (!${name}) {
${name} = ${branch.block.name}($$, ctx); ${name} = ${branch.block.name}(ctx);
${name}.c(); ${name}.c();
${name}.m(${updateMountNode}, ${anchor}); ${name}.m(${updateMountNode}, ${anchor});
} }

@ -10,12 +10,13 @@ import addToSet from '../../../../utils/addToSet';
import deindent from '../../../../utils/deindent'; import deindent from '../../../../utils/deindent';
import Attribute from '../../../nodes/Attribute'; import Attribute from '../../../nodes/Attribute';
import getObject from '../../../../utils/getObject'; import getObject from '../../../../utils/getObject';
import Binding from '../../../nodes/Binding';
import flattenReference from '../../../../utils/flattenReference'; import flattenReference from '../../../../utils/flattenReference';
import createDebuggingComment from '../../../../utils/createDebuggingComment';
import sanitize from '../../../../utils/sanitize';
export default class InlineComponentWrapper extends Wrapper { export default class InlineComponentWrapper extends Wrapper {
var: string; var: string;
_slots: Set<string>; // TODO lose the underscore slots: Map<string, Block> = new Map();
node: InlineComponent; node: InlineComponent;
fragment: FragmentWrapper; fragment: FragmentWrapper;
@ -65,8 +66,15 @@ export default class InlineComponentWrapper extends Wrapper {
).toLowerCase(); ).toLowerCase();
if (this.node.children.length) { if (this.node.children.length) {
this._slots = new Set(['default']); const default_slot = block.child({
this.fragment = new FragmentWrapper(renderer, block, node.children, this, stripWhitespace, nextSibling); 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(); block.addOutro();
@ -84,15 +92,6 @@ export default class InlineComponentWrapper extends Wrapper {
const component_opts = []; 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 statements: string[] = [];
const updates: string[] = []; const updates: string[] = [];
const postupdates: string[] = []; const postupdates: string[] = [];
@ -102,13 +101,16 @@ export default class InlineComponentWrapper extends Wrapper {
const usesSpread = !!this.node.attributes.find(a => a.isSpread); 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 const attributeObject = usesSpread
? '{}' ? stringifyProps(slot_props)
: stringifyProps( : 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) { if (!usesSpread && this.node.bindings.length === 0) {
component_opts.push(`props: ${attributeObject}`); component_opts.push(`props: ${attributeObject}`);
} else { } 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) { if (component.options.dev) {
// TODO this is a terrible hack, but without it the component // TODO this is a terrible hack, but without it the component
// will complain that options.target is missing. This would // will complain that options.target is missing. This would
@ -125,7 +135,16 @@ export default class InlineComponentWrapper extends Wrapper {
component_opts.push(`$$inline: true`); 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} = {};`); 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 => { const munged_bindings = this.node.bindings.map(binding => {
component.has_reactive_assignments = true; component.has_reactive_assignments = true;
@ -375,7 +398,6 @@ export default class InlineComponentWrapper extends Wrapper {
${munged_bindings} ${munged_bindings}
${munged_handlers} ${munged_handlers}
${this.fragment && this.fragment.nodes.map(child => child.remount(name))}
${name}.$$.fragment.c(); ${name}.$$.fragment.c();
@mount_component(${name}, ${updateMountNode}, ${anchor}); @mount_component(${name}, ${updateMountNode}, ${anchor});
${name}.$$.fragment.i(); ${name}.$$.fragment.i();
@ -448,10 +470,6 @@ export default class InlineComponentWrapper extends Wrapper {
`if (${name}) ${name}.$$.fragment.o();` `if (${name}) ${name}.$$.fragment.o();`
); );
} }
remount(name: string) {
return `${this.var}.$$.fragment.m(${name}.$$.slotted.default, null);`;
}
} }
function isComputed(node: Node) { function isComputed(node: Node) {

@ -41,42 +41,24 @@ export default class SlotWrapper extends Wrapper {
) { ) {
const { renderer } = this; const { renderer } = this;
const slotName = this.node.getStaticAttributeValue('name') || 'default'; const slot_name = this.node.getStaticAttributeValue('name') || 'default';
renderer.slots.add(slotName); renderer.slots.add(slot_name);
const content_name = block.getUniqueName(`slot_content_${sanitize(slotName)}`); const slot = block.getUniqueName(`${sanitize(slot_name)}_slot`);
const prop = quotePropIfNecessary(slotName);
block.addVariable(content_name, `$$.slotted${prop}`);
// TODO can we use isDomNode instead of type === 'Element'? block.builders.init.addLine(
const needsAnchorBefore = this.prev ? this.prev.node.type !== 'Element' : !parentNode; `const ${slot} = ctx.$$slot_${sanitize(slot_name)} && ctx.$$slot_${sanitize(slot_name)}(ctx.$$scope.ctx);`
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);
let mountBefore = block.builders.mount.toString(); let mountBefore = block.builders.mount.toString();
let destroyBefore = block.builders.destroy.toString();
block.builders.create.pushCondition(`!${content_name}`); block.builders.create.pushCondition(`!${slot}`);
block.builders.hydrate.pushCondition(`!${content_name}`); block.builders.hydrate.pushCondition(`!${slot}`);
block.builders.mount.pushCondition(`!${content_name}`); block.builders.mount.pushCondition(`!${slot}`);
block.builders.update.pushCondition(`!${content_name}`); block.builders.update.pushCondition(`!${slot}`);
block.builders.destroy.pushCondition(`!${content_name}`); block.builders.destroy.pushCondition(`!${slot}`);
const listeners = block.event_listeners;
block.event_listeners = [];
this.fragment.render(block, parentNode, parentNodes); this.fragment.render(block, parentNode, parentNodes);
block.renderListeners(`_${content_name}`);
block.event_listeners = listeners;
block.builders.create.popCondition(); block.builders.create.popCondition();
block.builders.hydrate.popCondition(); block.builders.hydrate.popCondition();
@ -84,62 +66,26 @@ export default class SlotWrapper extends Wrapper {
block.builders.update.popCondition(); block.builders.update.popCondition();
block.builders.destroy.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 const mountLeadin = block.builders.mount.toString() !== mountBefore
? `else` ? `else`
: `if (${content_name})`; : `if (${slot})`;
if (parentNode) { block.builders.mount.addBlock(deindent`
block.builders.mount.addBlock(deindent` ${mountLeadin} {
${mountLeadin} { ${slot}.m(${parentNode || '#target'}, anchor);
${needsAnchorBefore && `@append(${parentNode}, ${anchorBefore} || (${anchorBefore} = @createComment()));`} }
@append(${parentNode}, ${content_name}); `);
${needsAnchorAfter && `@append(${parentNode}, ${anchorAfter} || (${anchorAfter} = @createComment()));`}
} block.builders.update.addLine(
`); `if (${slot} && changed.$$scope) ${slot}.p(ctx.$$scope.changed, ctx.$$scope.ctx);`
} 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});
}
`);
}
} }
} }

@ -60,8 +60,4 @@ export default class TextWrapper extends Wrapper {
parentNode parentNode
); );
} }
remount(name: string) {
return `@append(${name}.$$.slotted.default, ${this.var});`;
}
} }

@ -51,8 +51,4 @@ export default class Tag extends Wrapper {
return { init: content }; return { init: content };
} }
remount(name: string) {
return `@append(${name}.$$.slotted.default, ${this.var});`;
}
} }

@ -85,8 +85,4 @@ export default class Wrapper {
render(block: Block, parentNode: string, parentNodes: string) { render(block: Block, parentNode: string, parentNodes: string) {
throw new Error(`render method not implemented by subclass ${this.node.type}`); throw new Error(`render method not implemented by subclass ${this.node.type}`);
} }
remount(name: string) {
return `${this.var}.m(${name}.$$.slotted.default, null);`;
}
} }

@ -57,9 +57,11 @@ export function init(component, options, instance, create_fragment, not_equal) {
const previous_component = current_component; const previous_component = current_component;
set_current_component(component); set_current_component(component);
const props = options.props || {};
const $$ = component.$$ = { const $$ = component.$$ = {
fragment: null, fragment: null,
ctx: options.props || {}, ctx: null,
// state // state
set: noop, set: noop,
@ -75,15 +77,13 @@ export function init(component, options, instance, create_fragment, not_equal) {
// everything else // everything else
callbacks: blankObject(), callbacks: blankObject(),
slotted: options.slots || {}, dirty: null
dirty: null,
binding_groups: []
}; };
let ready = false; let ready = false;
if (instance) { $$.ctx = instance
$$.ctx = instance(component, $$.ctx, (key, value) => { ? instance(component, props, (key, value) => {
if ($$.bound[key]) $$.bound[key](value); if ($$.bound[key]) $$.bound[key](value);
if ($$.ctx) { if ($$.ctx) {
@ -95,13 +95,15 @@ export function init(component, options, instance, create_fragment, not_equal) {
$$.ctx[key] = value; $$.ctx[key] = value;
return changed; return changed;
} }
}); })
} : props;
$$.ctx.$$binding_groups = []; // TODO this is awkward and usually unncessary
$$.update(); $$.update();
ready = true; ready = true;
run_all($$.before_render); run_all($$.before_render);
$$.fragment = create_fragment($$, $$.ctx); $$.fragment = create_fragment($$.ctx);
if (options.target) { if (options.target) {
if (options.hydrate) { if (options.hydrate) {

@ -11,7 +11,7 @@ export function handlePromise(promise, info) {
info.resolved = key && { [key]: value }; info.resolved = key && { [key]: value };
const child_ctx = assign(assign({}, info.ctx), info.resolved); 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.block) {
if (info.blocks) { if (info.blocks) {

@ -18,7 +18,7 @@ export function fixAndOutroAndDestroyBlock(block, lookup) {
outroAndDestroyBlock(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 o = old_blocks.length;
var n = list.length; var n = list.length;
@ -37,7 +37,7 @@ export function updateKeyedEach(old_blocks, component, changed, get_key, dynamic
var block = lookup[key]; var block = lookup[key];
if (!block) { if (!block) {
block = create_each_block(component, key, child_ctx); block = create_each_block(key, child_ctx);
block.c(); block.c();
} else if (dynamic) { } else if (dynamic) {
block.p(changed, child_ctx); block.p(changed, child_ctx);

@ -10,12 +10,19 @@ export default function createDebuggingComment(
let c = node.start; let c = node.start;
if (node.type === 'ElseBlock') { if (node.type === 'ElseBlock') {
while (source[c - 1] !== '{') c -= 1; 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; let d;
while (source[d] !== '}') d += 1;
while (source[d] === '}') d += 1; 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 start = locate(c);
const loc = `(${start.line + 1}:${start.column})`; const loc = `(${start.line + 1}:${start.column})`;

Loading…
Cancel
Save