diff --git a/index.mjs b/index.mjs index 1ba5890537..768b53f626 100644 --- a/index.mjs +++ b/index.mjs @@ -3,6 +3,8 @@ export { onDestroy, beforeUpdate, afterUpdate, + setContext, + getContext, nextTick, createEventDispatcher } from './internal'; diff --git a/package.json b/package.json index 164bf61208..b1307936c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "svelte", - "version": "3.0.0-alpha18", + "version": "3.0.0-alpha19", "description": "The magical disappearing UI framework", "module": "index.mjs", "main": "index.js", diff --git a/src/compile/Component.ts b/src/compile/Component.ts index a0386570e3..c7ca6b7feb 100644 --- a/src/compile/Component.ts +++ b/src/compile/Component.ts @@ -156,7 +156,7 @@ export default class Component { this.tag = options.customElement ? options.customElement === true ? this.meta.tag - : options.customElement + : options.customElement as string : this.name; this.walk_module_js(); @@ -553,7 +553,7 @@ export default class Component { walk_instance_js_post_template() { const script = this.instance_script; if (!script) return; - + this.hoist_instance_declarations(); this.extract_reactive_declarations(); this.extract_reactive_store_references(); diff --git a/src/compile/css/Stylesheet.ts b/src/compile/css/Stylesheet.ts index 2a22782a5a..2cfb960862 100644 --- a/src/compile/css/Stylesheet.ts +++ b/src/compile/css/Stylesheet.ts @@ -316,7 +316,7 @@ export default class Stylesheet { leave: (node: Node) => { if (node.type === 'Rule' || node.type === 'Atrule') stack.pop(); - if (node.type === 'Atrule') currentAtrule = stack[stack.length - 1]; + if (node.type === 'Atrule') currentAtrule = stack[stack.length - 1] as Atrule; } }); } else { @@ -330,7 +330,7 @@ export default class Stylesheet { const stack: Element[] = []; let parent: Node = node; while (parent = parent.parent) { - if (parent.type === 'Element') stack.unshift(parent); + if (parent.type === 'Element') stack.unshift(parent as Element); } for (let i = 0; i < this.children.length; i += 1) { diff --git a/src/compile/nodes/CatchBlock.ts b/src/compile/nodes/CatchBlock.ts index 6486f4a3a8..db202b9517 100644 --- a/src/compile/nodes/CatchBlock.ts +++ b/src/compile/nodes/CatchBlock.ts @@ -12,7 +12,7 @@ export default class CatchBlock extends Node { super(component, parent, scope, info); this.scope = scope.child(); - this.scope.add(parent.error, parent.expression.dependencies); + this.scope.add(parent.error, parent.expression.dependencies, this); this.children = mapChildren(component, parent, this.scope, info.children); this.warnIfEmptyBlock(); diff --git a/src/compile/nodes/EachBlock.ts b/src/compile/nodes/EachBlock.ts index 6770ab8739..5ea1d5e033 100644 --- a/src/compile/nodes/EachBlock.ts +++ b/src/compile/nodes/EachBlock.ts @@ -20,6 +20,7 @@ export default class EachBlock extends Node { scope: TemplateScope; contexts: Array<{ name: string, tail: string }>; hasAnimation: boolean; + has_binding = false; children: Node[]; else?: ElseBlock; @@ -38,7 +39,7 @@ export default class EachBlock extends Node { unpackDestructuring(this.contexts, info.context, ''); this.contexts.forEach(context => { - this.scope.add(context.key.name, this.expression.dependencies); + this.scope.add(context.key.name, this.expression.dependencies, this); }); this.key = info.key @@ -48,7 +49,7 @@ export default class EachBlock extends Node { if (this.index) { // index can only change if this is a keyed each block const dependencies = this.key ? this.expression.dependencies : []; - this.scope.add(this.index, dependencies); + this.scope.add(this.index, dependencies, this); } this.hasAnimation = false; diff --git a/src/compile/nodes/Element.ts b/src/compile/nodes/Element.ts index af2f8ec919..362dbb784f 100644 --- a/src/compile/nodes/Element.ts +++ b/src/compile/nodes/Element.ts @@ -1,5 +1,4 @@ import isVoidElementName from '../../utils/isVoidElementName'; -import { quotePropIfNecessary } from '../../utils/quoteIfNecessary'; import Node from './shared/Node'; import Attribute from './Attribute'; import Binding from './Binding'; @@ -14,6 +13,8 @@ import mapChildren from './shared/mapChildren'; import { dimensions } from '../../utils/patterns'; import fuzzymatch from '../../utils/fuzzymatch'; import list from '../../utils/list'; +import Let from './Let'; +import TemplateScope from './shared/TemplateScope'; const svg = /^(?:altGlyph|altGlyphDef|altGlyphItem|animate|animateColor|animateMotion|animateTransform|circle|clipPath|color-profile|cursor|defs|desc|discard|ellipse|feBlend|feColorMatrix|feComponentTransfer|feComposite|feConvolveMatrix|feDiffuseLighting|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feImage|feMerge|feMergeNode|feMorphology|feOffset|fePointLight|feSpecularLighting|feSpotLight|feTile|feTurbulence|filter|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|foreignObject|g|glyph|glyphRef|hatch|hatchpath|hkern|image|line|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|metadata|missing-glyph|mpath|path|pattern|polygon|polyline|radialGradient|rect|set|solidcolor|stop|switch|symbol|text|textPath|tref|tspan|unknown|use|view|vkern)$/; @@ -75,12 +76,13 @@ const passiveEvents = new Set([ export default class Element extends Node { type: 'Element'; name: string; - scope: any; // TODO + scope: TemplateScope; attributes: Attribute[] = []; actions: Action[] = []; bindings: Binding[] = []; classes: Class[] = []; handlers: EventHandler[] = []; + lets: Let[] = []; intro?: Transition = null; outro?: Transition = null; animation?: Animation = null; @@ -168,6 +170,10 @@ export default class Element extends Node { this.handlers.push(new EventHandler(component, this, scope, node)); break; + case 'Let': + this.lets.push(new Let(component, this, scope, node)); + break; + case 'Transition': const transition = new Transition(component, this, scope, node); if (node.intro) this.intro = transition; @@ -183,7 +189,21 @@ export default class Element extends Node { } }); - this.children = mapChildren(component, this, scope, info.children); + if (this.lets.length > 0) { + this.scope = scope.child(); + + this.lets.forEach(l => { + const dependencies = new Set([l.name]); + + l.names.forEach(name => { + this.scope.add(name, dependencies); + }); + }); + } else { + this.scope = scope; + } + + this.children = mapChildren(component, this, this.scope, info.children); this.validate(); @@ -637,21 +657,11 @@ 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) { if (classAttribute.chunks.length === 1 && classAttribute.chunks[0].type === 'Text') { - (classAttribute.chunks[0]).data += ` ${className}`; + (classAttribute.chunks[0] as Text).data += ` ${className}`; } else { (classAttribute.chunks).push( new Text(this.component, this, this.scope, { diff --git a/src/compile/nodes/InlineComponent.ts b/src/compile/nodes/InlineComponent.ts index 1aa07bdfc2..4d3b7332a6 100644 --- a/src/compile/nodes/InlineComponent.ts +++ b/src/compile/nodes/InlineComponent.ts @@ -5,15 +5,19 @@ import Binding from './Binding'; import EventHandler from './EventHandler'; import Expression from './shared/Expression'; import Component from '../Component'; +import Let from './Let'; +import TemplateScope from './shared/TemplateScope'; export default class InlineComponent extends Node { type: 'InlineComponent'; name: string; expression: Expression; - attributes: Attribute[]; - bindings: Binding[]; - handlers: EventHandler[]; + attributes: Attribute[] = []; + bindings: Binding[] = []; + handlers: EventHandler[] = []; + lets: Let[] = []; children: Node[]; + scope: TemplateScope; constructor(component: Component, parent, scope, info) { super(component, parent, scope, info); @@ -29,10 +33,6 @@ export default class InlineComponent extends Node { ? new Expression(component, this, scope, info.expression) : null; - this.attributes = []; - this.bindings = []; - this.handlers = []; - info.attributes.forEach(node => { switch (node.type) { case 'Action': @@ -60,6 +60,10 @@ export default class InlineComponent extends Node { this.handlers.push(new EventHandler(component, this, scope, node)); break; + case 'Let': + this.lets.push(new Let(component, this, scope, node)); + break; + case 'Transition': component.error(node, { code: `invalid-transition`, @@ -71,6 +75,20 @@ export default class InlineComponent extends Node { } }); - this.children = mapChildren(component, this, scope, info.children); + if (this.lets.length > 0) { + this.scope = scope.child(); + + this.lets.forEach(l => { + const dependencies = new Set([l.name]); + + l.names.forEach(name => { + this.scope.add(name, dependencies, this); + }); + }); + } else { + this.scope = scope; + } + + this.children = mapChildren(component, this, this.scope, info.children); } } diff --git a/src/compile/nodes/Let.ts b/src/compile/nodes/Let.ts new file mode 100644 index 0000000000..d855068c16 --- /dev/null +++ b/src/compile/nodes/Let.ts @@ -0,0 +1,38 @@ +import Node from './shared/Node'; +import Component from '../Component'; +import { walk } from 'estree-walker'; + +const applicable = new Set(['Identifier', 'ObjectExpression', 'ArrayExpression', 'Property']); + +export default class Let extends Node { + type: 'Let'; + name: string; + value: string; + names: string[] = []; + + constructor(component: Component, parent, scope, info) { + super(component, parent, scope, info); + + this.name = info.name; + this.value = info.expression && `[✂${info.expression.start}-${info.expression.end}✂]`; + + if (info.expression) { + walk(info.expression, { + enter: node => { + if (!applicable.has(node.type)) { + component.error(node, { + code: 'invalid-let', + message: `let directive value must be an identifier or an object/array pattern` + }); + } + + if (node.type === 'Identifier') { + this.names.push(node.name); + } + } + }); + } else { + this.names.push(this.name); + } + } +} \ No newline at end of file diff --git a/src/compile/nodes/Slot.ts b/src/compile/nodes/Slot.ts index 028f42852b..d66ec8c6c0 100644 --- a/src/compile/nodes/Slot.ts +++ b/src/compile/nodes/Slot.ts @@ -19,26 +19,28 @@ export default class Slot extends Element { }); } - if (attr.name !== 'name') { - component.error(attr, { - code: `invalid-slot-attribute`, - message: `"name" is the only attribute permitted on elements` - }); - } + // if (attr.name !== 'name') { + // component.error(attr, { + // code: `invalid-slot-attribute`, + // message: `"name" is the only attribute permitted on elements` + // }); + // } - if (attr.value.length !== 1 || attr.value[0].type !== 'Text') { - component.error(attr, { - code: `dynamic-slot-name`, - message: ` name cannot be dynamic` - }); - } + if (attr.name === 'name') { + if (attr.value.length !== 1 || attr.value[0].type !== 'Text') { + component.error(attr, { + code: `dynamic-slot-name`, + message: ` name cannot be dynamic` + }); + } - const slotName = attr.value[0].data; - if (slotName === 'default') { - component.error(attr, { - code: `invalid-slot-name`, - message: `default is a reserved word — it cannot be used as a slot name` - }); + const slotName = attr.value[0].data; + if (slotName === 'default') { + component.error(attr, { + code: `invalid-slot-name`, + message: `default is a reserved word — it cannot be used as a slot name` + }); + } } // TODO should duplicate slots be disallowed? Feels like it's more likely to be a diff --git a/src/compile/nodes/ThenBlock.ts b/src/compile/nodes/ThenBlock.ts index 9921eb77c7..6df8259273 100644 --- a/src/compile/nodes/ThenBlock.ts +++ b/src/compile/nodes/ThenBlock.ts @@ -12,7 +12,7 @@ export default class ThenBlock extends Node { super(component, parent, scope, info); this.scope = scope.child(); - this.scope.add(parent.value, parent.expression.dependencies); + this.scope.add(parent.value, parent.expression.dependencies, this); this.children = mapChildren(component, parent, this.scope, info.children); this.warnIfEmptyBlock(); diff --git a/src/compile/nodes/shared/Expression.ts b/src/compile/nodes/shared/Expression.ts index 852065d98d..9ec0c92e84 100644 --- a/src/compile/nodes/shared/Expression.ts +++ b/src/compile/nodes/shared/Expression.ts @@ -113,7 +113,10 @@ export default class Expression { // function, and it only applies if the dependency is writable // or a sub-path of a non-writable if (component.instance_script) { - if (component.writable_declarations.has(name) || name[0] === '$' || (component.userVars.has(name) && deep)) { + const owner = template_scope.getOwner(name); + const is_let = owner && (owner.type === 'InlineComponent' || owner.type === 'Element'); + + if (is_let || component.writable_declarations.has(name) || name[0] === '$' || (component.userVars.has(name) && deep)) { dynamic_dependencies.add(name); } } else { 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/nodes/shared/TemplateScope.ts b/src/compile/nodes/shared/TemplateScope.ts index 8d31e848e6..df48cc57f9 100644 --- a/src/compile/nodes/shared/TemplateScope.ts +++ b/src/compile/nodes/shared/TemplateScope.ts @@ -1,19 +1,28 @@ +import Node from './Node'; +import EachBlock from '../EachBlock'; +import ThenBlock from '../ThenBlock'; +import CatchBlock from '../CatchBlock'; +import InlineComponent from '../InlineComponent'; + +type NodeWithScope = EachBlock | ThenBlock | CatchBlock | InlineComponent | Element; + export default class TemplateScope { names: Set; dependenciesForName: Map>; - mutables: Set; + mutables: Set = new Set(); + owners: Map = new Map(); parent?: TemplateScope; constructor(parent?: TemplateScope) { this.parent = parent; this.names = new Set(parent ? parent.names : []); this.dependenciesForName = new Map(parent ? parent.dependenciesForName : []); - this.mutables = new Set(); } - add(name, dependencies: Set) { + add(name, dependencies: Set, owner) { this.names.add(name); this.dependenciesForName.set(name, dependencies); + this.owners.set(name, owner); return this; } @@ -32,6 +41,10 @@ export default class TemplateScope { containsMutable(names: Iterable) { for (const name of names) { + const owner = this.getOwner(name); + const is_let = owner && (owner.type === 'InlineComponent' || owner.type === 'Element'); + if (is_let) return true; + if (name[0] === '$') return true; if (this.mutables.has(name)) return true; else if (this.dependenciesForName.has(name) && this.containsMutable(this.dependenciesForName.get(name))) return true; @@ -44,4 +57,8 @@ export default class TemplateScope { isTopLevel(name: string) { return !this.parent || !this.names.has(name) && this.parent.isTopLevel(name); } + + getOwner(name: string): NodeWithScope { + return this.owners.get(name) || (this.parent && this.parent.getOwner(name)); + } } \ No newline at end of file diff --git a/src/compile/render-dom/Block.ts b/src/compile/render-dom/Block.ts index 6d23fe9acb..bf4823f863 100644 --- a/src/compile/render-dom/Block.ts +++ b/src/compile/render-dom/Block.ts @@ -1,9 +1,10 @@ import CodeBuilder from '../../utils/CodeBuilder'; import deindent from '../../utils/deindent'; -import { escape } from '../../utils/stringify'; import Renderer from './Renderer'; import Wrapper from './wrappers/shared/Wrapper'; import EachBlockWrapper from './wrappers/EachBlock'; +import InlineComponentWrapper from './wrappers/InlineComponent'; +import ElementWrapper from './wrappers/Element'; export interface BlockOptions { parent?: Block; @@ -12,7 +13,6 @@ export interface BlockOptions { comment?: string; key?: string; bindings?: Map { object: string, property: string, snippet: string }>; - contextOwners?: Map; dependencies?: Set; } @@ -30,7 +30,6 @@ export default class Block { dependencies: Set; bindings: Map; - contextOwners: Map; builders: { init: CodeBuilder; @@ -61,7 +60,7 @@ export default class Block { variables: Map; getUniqueName: (name: string) => string; - hasUpdateMethod: boolean; + hasUpdateMethod = false; autofocus: string; constructor(options: BlockOptions) { @@ -79,7 +78,6 @@ export default class Block { this.dependencies = new Set(); this.bindings = options.bindings; - this.contextOwners = options.contextOwners; this.builders = { init: new CodeBuilder(), @@ -106,8 +104,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 +147,8 @@ export default class Block { dependencies.forEach(dependency => { this.dependencies.add(dependency); }); + + this.hasUpdateMethod = true; } addElement( @@ -407,7 +405,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/Renderer.ts b/src/compile/render-dom/Renderer.ts index 28a8a90dfb..d2bbcb3f53 100644 --- a/src/compile/render-dom/Renderer.ts +++ b/src/compile/render-dom/Renderer.ts @@ -44,7 +44,6 @@ export default class Renderer { key: null, bindings: new Map(), - contextOwners: new Map(), dependencies: new Set(), }); diff --git a/src/compile/render-dom/index.ts b/src/compile/render-dom/index.ts index fc35e4f317..c7d79fb5d2 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,15 @@ 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'); + } + + if (renderer.bindingGroups.length > 0) { + filtered_declarations.push(`$$binding_groups`); + } + const has_definition = ( component.javascript || filtered_props.length > 0 || @@ -301,6 +315,10 @@ export default function dom( function ${definition}(${args.join(', ')}) { ${user_code} + ${renderer.slots.size && `let { ${[...renderer.slots].map(name => `$$slot_${sanitize(name)}`).join(', ')}, $$scope } = $$props;`} + + ${renderer.bindingGroups.length > 0 && `const $$binding_groups = [${renderer.bindingGroups.map(_ => `[]`).join(', ')}];`} + ${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..39f3407020 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; - } } } @@ -66,7 +62,6 @@ export default class EachBlockWrapper extends Wrapper { indexName: string; var = 'each'; - hasBinding = false; constructor( renderer: Renderer, @@ -85,10 +80,9 @@ export default class EachBlockWrapper extends Wrapper { this.block = block.child({ comment: createDebuggingComment(this.node, this.renderer.component), name: renderer.component.getUniqueName('create_each_block'), - key: node.key, // TODO... + key: node.key as string, - bindings: new Map(block.bindings), - contextOwners: new Map(block.contextOwners) + bindings: new Map(block.bindings) }); // TODO this seems messy @@ -114,8 +108,6 @@ export default class EachBlockWrapper extends Wrapper { }; node.contexts.forEach(prop => { - this.block.contextOwners.set(prop.key.name, this); - this.block.bindings.set(prop.key.name, { object: this.vars.each_block_value, property: this.indexName, @@ -149,7 +141,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(); @@ -172,8 +163,8 @@ export default class EachBlockWrapper extends Wrapper { this.contextProps = this.node.contexts.map(prop => `child_ctx.${prop.key.name} = list[i]${prop.tail};`); - if (this.hasBinding) this.contextProps.push(`child_ctx.${this.vars.each_block_value} = list;`); - if (this.hasBinding || this.node.index) this.contextProps.push(`child_ctx.${this.indexName} = i;`); + if (this.node.has_binding) this.contextProps.push(`child_ctx.${this.vars.each_block_value} = list;`); + if (this.node.has_binding || this.node.index) this.contextProps.push(`child_ctx.${this.indexName} = i;`); const snippet = this.node.expression.render(block); @@ -216,7 +207,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 +225,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 +241,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 +297,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 +333,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 +366,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 +430,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 +490,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..710b6e8c6d 100644 --- a/src/compile/render-dom/wrappers/Element/Binding.ts +++ b/src/compile/render-dom/wrappers/Element/Binding.ts @@ -7,6 +7,7 @@ import Node from '../../../nodes/shared/Node'; import Renderer from '../../Renderer'; import flattenReference from '../../../../utils/flattenReference'; import { get_tail } from '../../../../utils/get_tail_snippet'; +import EachBlock from '../../../nodes/EachBlock'; // TODO this should live in a specific binding const readOnlyMediaAttributes = new Set([ @@ -52,9 +53,9 @@ export default class BindingWrapper { // we need to ensure that the each block creates a context including // the list and the index, if they're not otherwise referenced const { name } = getObject(this.node.expression.node); - const eachBlock = block.contextOwners.get(name); + const eachBlock = this.parent.node.scope.getOwner(name); - eachBlock.hasBinding = true; + (eachBlock as EachBlock).has_binding = true; } this.object = getObject(this.node.expression.node).name; @@ -124,11 +125,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}].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 +279,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($$binding_groups[${bindingGroup}])`; } return `this.__value`; @@ -295,4 +296,4 @@ function getValueFromDom( // everything else return `this.${name}`; -} \ No newline at end of file +} diff --git a/src/compile/render-dom/wrappers/Element/index.ts b/src/compile/render-dom/wrappers/Element/index.ts index 7d0288726e..97654fd19c 100644 --- a/src/compile/render-dom/wrappers/Element/index.ts +++ b/src/compile/render-dom/wrappers/Element/index.ts @@ -19,6 +19,9 @@ 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'; +import { get_context_merger } from '../shared/get_context_merger'; const events = [ { @@ -91,7 +94,7 @@ export default class ElementWrapper extends Wrapper { bindings: Binding[]; classDependencies: string[]; - slotOwner?: InlineComponentWrapper; + slot_block: Block; selectBindingDependencies?: Set; var: string; @@ -126,8 +129,25 @@ export default class ElementWrapper extends Wrapper { } if (owner && owner.node.type === 'InlineComponent') { - this.slotOwner = owner; - owner._slots.add(attribute.getStaticValue()); + const name = attribute.getStaticValue(); + + if (!(owner as InlineComponentWrapper).slots.has(name)) { + const child_block = block.child({ + comment: createDebuggingComment(node, this.renderer.component), + name: this.renderer.component.getUniqueName(`create_${sanitize(name)}_slot`) + }); + + const fn = get_context_merger(this.node.lets); + + (owner as InlineComponentWrapper).slots.set(name, { + block: child_block, + fn + }); + this.renderer.blocks.push(child_block); + } + + this.slot_block = (owner as InlineComponentWrapper).slots.get(name).block; + block = this.slot_block; } } if (attribute.name === 'style') { @@ -179,6 +199,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 +218,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 +241,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 { @@ -317,7 +334,7 @@ export default class ElementWrapper extends Wrapper { let open = `<${wrapper.node.name}`; - (wrapper).attributes.forEach((attr: AttributeWrapper) => { + (wrapper as ElementWrapper).attributes.forEach((attr: AttributeWrapper) => { open += ` ${fixAttributeCasing(attr.node.name)}${attr.stringify()}` }); @@ -755,23 +772,13 @@ 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) { if (classAttribute.chunks.length === 1 && classAttribute.chunks[0].type === 'Text') { - (classAttribute.chunks[0]).data += ` ${className}`; + (classAttribute.chunks[0] as Text).data += ` ${className}`; } else { - (classAttribute.chunks).push( + (classAttribute.chunks as Node[]).push( new Text(this.component, this, this.scope, { type: 'Text', data: ` ${className}` diff --git a/src/compile/render-dom/wrappers/Fragment.ts b/src/compile/render-dom/wrappers/Fragment.ts index aad4c74c80..e4d989f9a5 100644 --- a/src/compile/render-dom/wrappers/Fragment.ts +++ b/src/compile/render-dom/wrappers/Fragment.ts @@ -63,6 +63,10 @@ export default class FragmentWrapper { while (i--) { const child = nodes[i]; + if (!child.type) { + throw new Error(`missing type`) + } + if (!(child.type in wrappers)) { throw new Error(`TODO implement ${child.type}`); } @@ -114,7 +118,7 @@ export default class FragmentWrapper { } if (stripWhitespace) { - const first = this.nodes[0]; + const first = this.nodes[0] as TextWrapper; if (first && first.node.type === 'Text') { first.data = trimStart(first.data); diff --git a/src/compile/render-dom/wrappers/IfBlock.ts b/src/compile/render-dom/wrappers/IfBlock.ts index ab4c697881..3d12c589ac 100644 --- a/src/compile/render-dom/wrappers/IfBlock.ts +++ b/src/compile/render-dom/wrappers/IfBlock.ts @@ -32,12 +32,12 @@ class IfBlockBranch extends Wrapper { ) { super(renderer, block, parent, node); - this.condition = (node).expression && (node).expression.render(block); + this.condition = (node as IfBlock).expression && (node as IfBlock).expression.render(block); this.block = block.child({ comment: createDebuggingComment(node, parent.renderer.component), name: parent.renderer.component.getUniqueName( - (node).expression ? `create_if_block` : `create_else_block` + (node as IfBlock).expression ? `create_if_block` : `create_else_block` ) }); @@ -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..5a97be48eb 100644 --- a/src/compile/render-dom/wrappers/InlineComponent/index.ts +++ b/src/compile/render-dom/wrappers/InlineComponent/index.ts @@ -10,12 +10,15 @@ 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'; +import { get_context_merger } from '../shared/get_context_merger'; +import EachBlock from '../../../nodes/EachBlock'; export default class InlineComponentWrapper extends Wrapper { var: string; - _slots: Set; // TODO lose the underscore + slots: Map = new Map(); node: InlineComponent; fragment: FragmentWrapper; @@ -44,9 +47,9 @@ export default class InlineComponentWrapper extends Wrapper { // we need to ensure that the each block creates a context including // the list and the index, if they're not otherwise referenced const { name } = getObject(binding.expression.node); - const eachBlock = block.contextOwners.get(name); + const eachBlock = this.node.scope.getOwner(name); - eachBlock.hasBinding = true; + (eachBlock as EachBlock).has_binding = true; } block.addDependencies(binding.expression.dynamic_dependencies); @@ -65,8 +68,22 @@ 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); + + const fn = get_context_merger(this.node.lets); + + this.slots.set('default', { + block: default_slot, + fn + }); + this.fragment = new FragmentWrapper(renderer, default_slot, node.children, this, stripWhitespace, nextSibling); + + block.addDependencies(default_slot.dependencies); } block.addOutro(); @@ -84,15 +101,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 +110,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, slot]) => `$$slot_${sanitize(name)}: [${slot.block.name}${slot.fn ? `, ${slot.fn}` : ''}]`); + 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 +128,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.block, 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 +144,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(slot => { + slot.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 +224,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 +407,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 +479,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..ae81910dba 100644 --- a/src/compile/render-dom/wrappers/Slot.ts +++ b/src/compile/render-dom/wrappers/Slot.ts @@ -2,16 +2,20 @@ import Wrapper from './shared/Wrapper'; import Renderer from '../Renderer'; import Block from '../Block'; import Slot from '../../nodes/Slot'; -import { quotePropIfNecessary } from '../../../utils/quoteIfNecessary'; import FragmentWrapper from './Fragment'; import deindent from '../../../utils/deindent'; import sanitize from '../../../utils/sanitize'; +import addToSet from '../../../utils/addToSet'; +import get_slot_data from '../../../utils/get_slot_data'; +import stringifyProps from '../../../utils/stringifyProps'; +import Expression from '../../nodes/shared/Expression'; export default class SlotWrapper extends Wrapper { node: Slot; fragment: FragmentWrapper; var = 'slot'; + dependencies: Set = new Set(['$$scope']); constructor( renderer: Renderer, @@ -32,6 +36,12 @@ export default class SlotWrapper extends Wrapper { stripWhitespace, nextSibling ); + + this.node.attributes.forEach(attribute => { + addToSet(this.dependencies, attribute.dependencies); + }); + + block.addDependencies(this.dependencies); } render( @@ -41,41 +51,66 @@ 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); + + let get_slot_changes; + let get_slot_context; + + const attributes = this.node.attributes.filter(attribute => attribute.name !== 'name'); + + if (attributes.length > 0) { + get_slot_changes = renderer.component.getUniqueName(`get_${slot_name}_slot_changes`); + get_slot_context = renderer.component.getUniqueName(`get_${slot_name}_slot_context`); - const content_name = block.getUniqueName(`slot_content_${sanitize(slotName)}`); - const prop = quotePropIfNecessary(slotName); - block.addVariable(content_name, `$$.slotted${prop}`); + const context_props = get_slot_data(attributes); + const changes_props = []; + + const dependencies = new Set(); + + attributes.forEach(attribute => { + attribute.chunks.forEach(chunk => { + if ((chunk as Expression).dependencies) { + addToSet(dependencies, (chunk as Expression).dependencies); + addToSet(dependencies, (chunk as Expression).contextual_dependencies); + } + }); + + if (attribute.dependencies.size > 0) { + changes_props.push(`${attribute.name}: ${[...attribute.dependencies].join(' || ')}`) + } + }); - // 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 arg = dependencies.size > 0 ? `{ ${[...dependencies].join(', ')} }` : '{}'; - const anchorBefore = needsAnchorBefore - ? block.getUniqueName(`${content_name}_before`) - : (this.prev && this.prev.var) || 'null'; + renderer.blocks.push(deindent` + const ${get_slot_changes} = (${arg}) => (${stringifyProps(changes_props)}); + const ${get_slot_context} = (${arg}) => (${stringifyProps(context_props)}); + `); + } else { + get_slot_context = 'null'; + } - const anchorAfter = needsAnchorAfter - ? block.getUniqueName(`${content_name}_after`) - : (this.next && this.next.var) || 'null'; + const slot = block.getUniqueName(`${sanitize(slot_name)}_slot`); + const slot_definition = block.getUniqueName(`${sanitize(slot_name)}_slot`); - if (needsAnchorBefore) block.addVariable(anchorBefore); - if (needsAnchorAfter) block.addVariable(anchorAfter); + block.builders.init.addBlock(deindent` + const ${slot_definition} = ctx.$$slot_${sanitize(slot_name)}; + const ${slot} = @create_slot(${slot_definition}, ctx, ${get_slot_context}); + `); 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.renderListeners(`_${slot}`); block.event_listeners = listeners; block.builders.create.popCondition(); @@ -84,62 +119,35 @@ 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 (${slot})`; - // 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})`; + block.builders.mount.addBlock(deindent` + ${mountLeadin} { + ${slot}.m(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'}); + } + `); - 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}); - } - `); - } + let update_conditions = [...this.dependencies].map(name => `changed.${name}`).join(' || '); + if (this.dependencies.size > 1) update_conditions = `(${update_conditions})`; + + block.builders.update.addBlock(deindent` + if (${slot} && ${update_conditions}) { + ${slot}.p(@assign(@assign({}, ${get_slot_changes}(changed)), ctx.$$scope.changed), @get_slot_context(${slot_definition}, ctx, ${get_slot_context})); + } + `); + + block.builders.destroy.addLine( + `if (${slot}) ${slot}.d(detach);` + ); } } \ 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 70780dd0d2..f46d626078 100644 --- a/src/compile/render-dom/wrappers/shared/Wrapper.ts +++ b/src/compile/render-dom/wrappers/shared/Wrapper.ts @@ -75,8 +75,4 @@ export default class Wrapper { this.node.type === 'MustacheTag' ); } - - remount(name: string) { - return `${this.var}.m(${name}.$$.slotted.default, null);`; - } } \ No newline at end of file diff --git a/src/compile/render-dom/wrappers/shared/get_context_merger.ts b/src/compile/render-dom/wrappers/shared/get_context_merger.ts new file mode 100644 index 0000000000..bbd14ec87d --- /dev/null +++ b/src/compile/render-dom/wrappers/shared/get_context_merger.ts @@ -0,0 +1,10 @@ +import Let from '../../../nodes/Let'; + +export function get_context_merger(lets: Let[]) { + if (lets.length === 0) return null; + + const input = lets.map(l => l.value ? `${l.name}: ${l.value}` : l.name).join(', '); + const output = lets.map(l => l.names.join(', ')).join(', '); + + return `({ ${input} }) => ({ ${output} })`; +} \ No newline at end of file diff --git a/src/compile/render-ssr/handlers/AwaitBlock.ts b/src/compile/render-ssr/handlers/AwaitBlock.ts index 05da3a4ad6..a79ff18429 100644 --- a/src/compile/render-ssr/handlers/AwaitBlock.ts +++ b/src/compile/render-ssr/handlers/AwaitBlock.ts @@ -1,6 +1,6 @@ import Renderer from '../Renderer'; import { CompileOptions } from '../../../interfaces'; -import { snip } from '../utils'; +import { snip } from '../../../utils/snip'; export default function(node, renderer: Renderer, options: CompileOptions) { renderer.append('${(function(__value) { if(@isPromise(__value)) return `'); diff --git a/src/compile/render-ssr/handlers/EachBlock.ts b/src/compile/render-ssr/handlers/EachBlock.ts index 57b0ddd1f1..a764b151aa 100644 --- a/src/compile/render-ssr/handlers/EachBlock.ts +++ b/src/compile/render-ssr/handlers/EachBlock.ts @@ -1,4 +1,4 @@ -import { snip } from '../utils'; +import { snip } from '../../../utils/snip'; export default function(node, renderer, options) { const snippet = snip(node.expression); diff --git a/src/compile/render-ssr/handlers/Element.ts b/src/compile/render-ssr/handlers/Element.ts index 3a4316abfa..ba5bd998ab 100644 --- a/src/compile/render-ssr/handlers/Element.ts +++ b/src/compile/render-ssr/handlers/Element.ts @@ -2,8 +2,9 @@ import { quotePropIfNecessary, quoteNameIfNecessary } from '../../../utils/quote import isVoidElementName from '../../../utils/isVoidElementName'; import Attribute from '../../nodes/Attribute'; import Node from '../../nodes/shared/Node'; -import { escape, escapeTemplate } from '../../../utils/stringify'; -import { snip } from '../utils'; +import { snip } from '../../../utils/snip'; +import { stringify_attribute } from '../../../utils/stringify_attribute'; +import { get_slot_scope } from './shared/get_slot_scope'; // source: https://gist.github.com/ArjanSchouten/0b8574a6ad7f5065a5e7 const boolean_attributes = new Set([ @@ -57,6 +58,8 @@ export default function(node, renderer, options) { const target = renderer.targets[renderer.targets.length - 1]; target.slotStack.push(slotName); target.slots[slotName] = ''; + + options.slot_scopes.set(slotName, get_slot_scope(node.lets)); } const classExpr = node.classes.map((classDir: Class) => { @@ -75,7 +78,7 @@ export default function(node, renderer, options) { args.push(snip(attribute.expression)); } else { if (attribute.name === 'value' && node.name === 'textarea') { - textareaContents = stringifyAttribute(attribute); + textareaContents = stringify_attribute(attribute); } else if (attribute.isTrue) { args.push(`{ ${quoteNameIfNecessary(attribute.name)}: true }`); } else if ( @@ -86,7 +89,7 @@ export default function(node, renderer, options) { // a boolean attribute with one non-Text chunk args.push(`{ ${quoteNameIfNecessary(attribute.name)}: ${snip(attribute.chunks[0])} }`); } else { - args.push(`{ ${quoteNameIfNecessary(attribute.name)}: \`${stringifyAttribute(attribute)}\` }`); + args.push(`{ ${quoteNameIfNecessary(attribute.name)}: \`${stringify_attribute(attribute)}\` }`); } } }); @@ -97,7 +100,7 @@ export default function(node, renderer, options) { if (attribute.type !== 'Attribute') return; if (attribute.name === 'value' && node.name === 'textarea') { - textareaContents = stringifyAttribute(attribute); + textareaContents = stringify_attribute(attribute); } else if (attribute.isTrue) { openingTag += ` ${attribute.name}`; } else if ( @@ -109,14 +112,14 @@ export default function(node, renderer, options) { openingTag += '${' + snip(attribute.chunks[0]) + ' ? " ' + attribute.name + '" : "" }'; } else if (attribute.name === 'class' && classExpr) { addClassAttribute = false; - openingTag += ` class="\${[\`${stringifyAttribute(attribute)}\`, ${classExpr}].join(' ').trim() }"`; + openingTag += ` class="\${[\`${stringify_attribute(attribute)}\`, ${classExpr}].join(' ').trim() }"`; } else if (attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text') { const { name } = attribute; const snippet = snip(attribute.chunks[0]); openingTag += '${(v => v == null ? "" : ` ' + name + '="${@escape(' + snippet + ')}"`)(' + snippet + ')}'; } else { - openingTag += ` ${attribute.name}="${stringifyAttribute(attribute)}"`; + openingTag += ` ${attribute.name}="${stringify_attribute(attribute)}"`; } }); } @@ -149,16 +152,4 @@ export default function(node, renderer, options) { if (!isVoidElementName(node.name)) { renderer.append(``); } -} - -function stringifyAttribute(attribute: Attribute) { - return attribute.chunks - .map((chunk: Node) => { - if (chunk.type === 'Text') { - return escapeTemplate(escape(chunk.data).replace(/"/g, '"')); - } - - return '${@escape(' + snip(chunk) + ')}'; - }) - .join(''); } \ No newline at end of file diff --git a/src/compile/render-ssr/handlers/HtmlTag.ts b/src/compile/render-ssr/handlers/HtmlTag.ts index ce7c8bf97c..4d6934d892 100644 --- a/src/compile/render-ssr/handlers/HtmlTag.ts +++ b/src/compile/render-ssr/handlers/HtmlTag.ts @@ -1,4 +1,4 @@ -import { snip } from '../utils'; +import { snip } from '../../../utils/snip'; export default function(node, renderer, options) { renderer.append('${' + snip(node.expression) + '}'); diff --git a/src/compile/render-ssr/handlers/IfBlock.ts b/src/compile/render-ssr/handlers/IfBlock.ts index fadaccef98..9063bcc988 100644 --- a/src/compile/render-ssr/handlers/IfBlock.ts +++ b/src/compile/render-ssr/handlers/IfBlock.ts @@ -1,4 +1,4 @@ -import { snip } from '../utils'; +import { snip } from '../../../utils/snip'; export default function(node, renderer, options) { const snippet = snip(node.expression); diff --git a/src/compile/render-ssr/handlers/InlineComponent.ts b/src/compile/render-ssr/handlers/InlineComponent.ts index 408286b6ac..d1776988b5 100644 --- a/src/compile/render-ssr/handlers/InlineComponent.ts +++ b/src/compile/render-ssr/handlers/InlineComponent.ts @@ -1,11 +1,9 @@ import { escape, escapeTemplate, stringify } from '../../../utils/stringify'; -import getObject from '../../../utils/getObject'; -import { get_tail_snippet } from '../../../utils/get_tail_snippet'; -import { quoteNameIfNecessary, quotePropIfNecessary } from '../../../utils/quoteIfNecessary'; -import deindent from '../../../utils/deindent'; -import { snip } from '../utils'; +import { quoteNameIfNecessary } from '../../../utils/quoteIfNecessary'; +import { snip } from '../../../utils/snip'; import Renderer from '../Renderer'; import stringifyProps from '../../../utils/stringifyProps'; +import { get_slot_scope } from './shared/get_slot_scope'; type AppendTarget = any; // TODO @@ -33,12 +31,6 @@ function getAttributeValue(attribute) { return '`' + attribute.chunks.map(stringifyAttribute).join('') + '`'; } -function stringifyObject(props) { - return props.length > 0 - ? `{ ${props.join(', ')} }` - : `{};` -} - export default function(node, renderer: Renderer, options) { const binding_props = []; const binding_fns = []; @@ -98,11 +90,18 @@ export default function(node, renderer: Renderer, options) { renderer.targets.push(target); - renderer.render(node.children, options); + const slot_scopes = new Map(); + slot_scopes.set('default', get_slot_scope(node.lets)); + + renderer.render(node.children, Object.assign({}, options, { + slot_scopes + })); Object.keys(target.slots).forEach(name => { + const slot_scope = slot_scopes.get(name); + slot_fns.push( - `${quoteNameIfNecessary(name)}: () => \`${target.slots[name]}\`` + `${quoteNameIfNecessary(name)}: (${slot_scope}) => \`${target.slots[name]}\`` ); }); diff --git a/src/compile/render-ssr/handlers/Slot.ts b/src/compile/render-ssr/handlers/Slot.ts index 704f17fdc1..6bf2a22cad 100644 --- a/src/compile/render-ssr/handlers/Slot.ts +++ b/src/compile/render-ssr/handlers/Slot.ts @@ -1,4 +1,5 @@ import { quotePropIfNecessary } from '../../../utils/quoteIfNecessary'; +import get_slot_data from '../../../utils/get_slot_data'; export default function(node, renderer, options) { const name = node.attributes.find(attribute => attribute.name === 'name'); @@ -6,7 +7,11 @@ export default function(node, renderer, options) { const slot_name = name && name.chunks[0].data || 'default'; const prop = quotePropIfNecessary(slot_name); - renderer.append(`\${$$slots${prop} ? $$slots${prop}() : \``); + const slot_data = get_slot_data(node.attributes); + + const arg = slot_data.length > 0 ? `{ ${slot_data.join(', ')} }` : ''; + + renderer.append(`\${$$slots${prop} ? $$slots${prop}(${arg}) : \``); renderer.render(node.children, options); diff --git a/src/compile/render-ssr/handlers/Tag.ts b/src/compile/render-ssr/handlers/Tag.ts index f4bebed552..2cfbafd48a 100644 --- a/src/compile/render-ssr/handlers/Tag.ts +++ b/src/compile/render-ssr/handlers/Tag.ts @@ -1,4 +1,4 @@ -import { snip } from '../utils'; +import { snip } from '../../../utils/snip'; export default function(node, renderer, options) { const snippet = snip(node.expression); diff --git a/src/compile/render-ssr/handlers/shared/get_slot_scope.ts b/src/compile/render-ssr/handlers/shared/get_slot_scope.ts new file mode 100644 index 0000000000..52aa70f015 --- /dev/null +++ b/src/compile/render-ssr/handlers/shared/get_slot_scope.ts @@ -0,0 +1,6 @@ +import Let from '../../../nodes/Let'; + +export function get_slot_scope(lets: Let[]) { + if (lets.length === 0) return ''; + return `{ ${lets.map(l => l.value ? `${l.name}: ${l.value}` : l.name).join(', ')} }`; +} \ No newline at end of file diff --git a/src/compile/wrapModule.ts b/src/compile/wrapModule.ts index d4f8198bc0..05d33f7fd4 100644 --- a/src/compile/wrapModule.ts +++ b/src/compile/wrapModule.ts @@ -41,6 +41,12 @@ export default function wrapModule( throw new Error(`options.format is invalid (must be ${list(Object.keys(wrappers))})`); } +function editSource(source, sveltePath) { + return source === 'svelte' || source.startsWith('svelte/') + ? source.replace('svelte', sveltePath) + : source; +} + function esm( code: string, name: string, @@ -60,7 +66,7 @@ function esm( const importBlock = imports.length > 0 && ( imports .map((declaration: Node) => { - const import_source = declaration.source.value === 'svelte' ? sveltePath : declaration.source.value; + const import_source = editSource(declaration.source.value, sveltePath); return ( source.slice(declaration.start, declaration.source.start) + @@ -117,9 +123,7 @@ function cjs( lhs = `{ ${properties.join(', ')} }`; } - const source = node.source.value === 'svelte' - ? sveltePath - : node.source.value; + const source = editSource(node.source.value, sveltePath); return `const ${lhs} = require("${source}");` }); diff --git a/src/internal/Component.js b/src/internal/Component.js index 894f80461b..8a87654eb7 100644 --- a/src/internal/Component.js +++ b/src/internal/Component.js @@ -54,15 +54,16 @@ function make_dirty(component, key) { } export function init(component, options, instance, create_fragment, not_equal) { - const previous_component = current_component; + const parent_component = current_component; set_current_component(component); + const props = options.props || {}; + const $$ = component.$$ = { fragment: null, - ctx: options.props || {}, + ctx: null, // state - set: noop, update: noop, not_equal, bound: blankObject(), @@ -72,18 +73,17 @@ export function init(component, options, instance, create_fragment, not_equal) { on_destroy: [], before_render: [], after_render: [], + context: new Map(parent_component ? parent_component.$$.context : []), // 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,13 @@ export function init(component, options, instance, create_fragment, not_equal) { $$.ctx[key] = value; return changed; } - }); - } + }) + : props; $$.update(); ready = true; run_all($$.before_render); - $$.fragment = create_fragment($$, $$.ctx); + $$.fragment = create_fragment($$.ctx); if (options.target) { if (options.hydrate) { @@ -115,7 +115,7 @@ export function init(component, options, instance, create_fragment, not_equal) { flush(); } - set_current_component(previous_component); + set_current_component(parent_component); } export let SvelteElement; diff --git a/src/internal/await-block.js b/src/internal/await-block.js index e9c25ec370..2e10250b4c 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/internal/lifecycle.js b/src/internal/lifecycle.js index e413f4c8f4..02fc2161e1 100644 --- a/src/internal/lifecycle.js +++ b/src/internal/lifecycle.js @@ -4,20 +4,25 @@ export function set_current_component(component) { current_component = component; } +function get_current_component() { + if (!current_component) throw new Error(`Function called outside component initialization`); + return current_component; +} + export function beforeUpdate(fn) { - current_component.$$.before_render.push(fn); + get_current_component().$$.before_render.push(fn); } export function onMount(fn) { - current_component.$$.on_mount.push(fn); + get_current_component().$$.on_mount.push(fn); } export function afterUpdate(fn) { - current_component.$$.after_render.push(fn); + get_current_component().$$.after_render.push(fn); } export function onDestroy(fn) { - current_component.$$.on_destroy.push(fn); + get_current_component().$$.on_destroy.push(fn); } export function createEventDispatcher() { @@ -37,6 +42,14 @@ export function createEventDispatcher() { }; } +export function setContext(key, context) { + get_current_component().$$.context.set(key, context); +} + +export function getContext(key) { + return get_current_component().$$.context.get(key); +} + // TODO figure out if we still want to support // shorthand events, or if we want to implement // a real bubbling mechanism diff --git a/src/internal/scheduler.js b/src/internal/scheduler.js index 0626db2a97..4d9f9ad388 100644 --- a/src/internal/scheduler.js +++ b/src/internal/scheduler.js @@ -1,4 +1,5 @@ import { run_all } from './utils.js'; +import { set_current_component } from './lifecycle.js'; export let dirty_components = []; export const intros = { enabled: false }; @@ -34,7 +35,9 @@ export function flush() { // first, call beforeUpdate functions // and update components while (dirty_components.length) { - update(dirty_components.shift().$$); + const component = dirty_components.shift(); + set_current_component(component); + update(component.$$); } while (binding_callbacks.length) binding_callbacks.shift()(); diff --git a/src/internal/ssr.js b/src/internal/ssr.js index 170f841ec6..762c01a3c5 100644 --- a/src/internal/ssr.js +++ b/src/internal/ssr.js @@ -1,4 +1,4 @@ -import { set_current_component } from './lifecycle.js'; +import { set_current_component, current_component } from './lifecycle.js'; import { run_all, blankObject } from './utils.js'; export const invalidAttributeNameCharacter = /[\s'">\/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u; @@ -68,6 +68,8 @@ export function debug(file, line, column, values) { export function create_ssr_component($$render) { return { render: (props = {}, options = {}) => { + const parent_component = current_component; + // TODO do we need on_ready, since on_mount, // before_render and after_render don't run? const $$ = { @@ -75,6 +77,7 @@ export function create_ssr_component($$render) { on_destroy: [], before_render: [], after_render: [], + context: new Map(parent_component ? parent_component.$$.context : []), callbacks: blankObject() }; diff --git a/src/internal/utils.js b/src/internal/utils.js index 222e2669e6..5ac71b4fce 100644 --- a/src/internal/utils.js +++ b/src/internal/utils.js @@ -45,4 +45,18 @@ export function validate_store(store, name) { if (!store || typeof store.subscribe !== 'function') { throw new Error(`'${name}' is not a store with a 'subscribe' method`); } -} \ No newline at end of file +} + +export function create_slot(definition, ctx, fn) { + if (definition) { + const slot_ctx = get_slot_context(definition, ctx, fn); + return definition[0](slot_ctx); + } +} + +export function get_slot_context(definition, ctx, fn) { + return definition[1] + ? assign({}, assign(ctx.$$scope.ctx, definition[1](fn ? fn(ctx) : {}))) + : ctx.$$scope.ctx; +} + diff --git a/src/parse/state/tag.ts b/src/parse/state/tag.ts index 6e53ab03e0..c11c660082 100644 --- a/src/parse/state/tag.ts +++ b/src/parse/state/tag.ts @@ -182,18 +182,19 @@ export default function tag(parser: Parser) { } } - if (name === 'slot') { - let i = parser.stack.length; - while (i--) { - const item = parser.stack[i]; - if (item.type === 'EachBlock') { - parser.error({ - code: `invalid-slot-placement`, - message: ` cannot be a child of an each-block` - }, start); - } - } - } + // TODO should this still error in in web component mode? + // if (name === 'slot') { + // let i = parser.stack.length; + // while (i--) { + // const item = parser.stack[i]; + // if (item.type === 'EachBlock') { + // parser.error({ + // code: `invalid-slot-placement`, + // message: ` cannot be a child of an each-block` + // }, start); + // } + // } + // } const uniqueNames = new Set(); @@ -464,6 +465,7 @@ function get_directive_type(name) { if (name === 'bind') return 'Binding'; if (name === 'class') return 'Class'; if (name === 'on') return 'EventHandler'; + if (name === 'let') return 'Let'; if (name === 'ref') return 'Ref'; if (name === 'in' || name === 'out' || name === 'transition') return 'Transition'; } diff --git a/src/utils/createDebuggingComment.ts b/src/utils/createDebuggingComment.ts index 15b8b12267..c90d7cf860 100644 --- a/src/utils/createDebuggingComment.ts +++ b/src/utils/createDebuggingComment.ts @@ -13,9 +13,16 @@ export default function createDebuggingComment( 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})`; diff --git a/src/utils/get_slot_data.ts b/src/utils/get_slot_data.ts new file mode 100644 index 0000000000..d5a5205ac9 --- /dev/null +++ b/src/utils/get_slot_data.ts @@ -0,0 +1,18 @@ +import { snip } from './snip'; +import { stringify_attribute } from './stringify_attribute'; + +export default function(attributes) { + return attributes + .filter(attribute => attribute.name !== 'name') + .map(attribute => { + const value = attribute.isTrue + ? 'true' + : attribute.chunks.length === 0 + ? '""' + : attribute.chunks.length === 1 && attribute.chunks[0].type !== 'Text' + ? snip(attribute.chunks[0]) + : stringify_attribute(attribute); + + return `${attribute.name}: ${value}`; + }); +} \ No newline at end of file diff --git a/src/compile/render-ssr/utils.ts b/src/utils/snip.ts similarity index 100% rename from src/compile/render-ssr/utils.ts rename to src/utils/snip.ts diff --git a/src/utils/stringify_attribute.ts b/src/utils/stringify_attribute.ts new file mode 100644 index 0000000000..933a84ed03 --- /dev/null +++ b/src/utils/stringify_attribute.ts @@ -0,0 +1,16 @@ +import Attribute from '../compile/nodes/Attribute'; +import Node from '../compile/nodes/shared/Node'; +import { escapeTemplate, escape } from './stringify'; +import { snip } from './snip'; + +export function stringify_attribute(attribute: Attribute) { + return attribute.chunks + .map((chunk: Node) => { + if (chunk.type === 'Text') { + return escapeTemplate(escape(chunk.data).replace(/"/g, '"')); + } + + return '${@escape(' + snip(chunk) + ')}'; + }) + .join(''); +} \ No newline at end of file diff --git a/test/js/samples/action-custom-event-handler/expected.js b/test/js/samples/action-custom-event-handler/expected.js index 0c73a79237..d3fe89ca0e 100644 --- a/test/js/samples/action-custom-event-handler/expected.js +++ b/test/js/samples/action-custom-event-handler/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, createElement, detachNode, flush, init, insert, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var button, foo_action; return { diff --git a/test/js/samples/action/expected.js b/test/js/samples/action/expected.js index 23f4bbe90d..07a54681c6 100644 --- a/test/js/samples/action/expected.js +++ b/test/js/samples/action/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, createElement, detachNode, init, insert, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var a, link_action; return { diff --git a/test/js/samples/bind-width-height/expected.js b/test/js/samples/bind-width-height/expected.js index a1b13fedc6..f168db3e72 100644 --- a/test/js/samples/bind-width-height/expected.js +++ b/test/js/samples/bind-width-height/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, addResizeListener, add_render_callback, createElement, detachNode, flush, init, insert, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var div, div_resize_listener; return { diff --git a/test/js/samples/collapses-text-around-comments/expected.js b/test/js/samples/collapses-text-around-comments/expected.js index ff5ce3b048..43982fb97a 100644 --- a/test/js/samples/collapses-text-around-comments/expected.js +++ b/test/js/samples/collapses-text-around-comments/expected.js @@ -8,7 +8,7 @@ function add_css() { append(document.head, style); } -function create_fragment($$, ctx) { +function create_fragment(ctx) { var p, text; return { diff --git a/test/js/samples/component-static-array/expected.js b/test/js/samples/component-static-array/expected.js index ab3dbbc4f6..7fbcc27382 100644 --- a/test/js/samples/component-static-array/expected.js +++ b/test/js/samples/component-static-array/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, init, mount_component, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var current; var nested = new ctx.Nested({ props: { foo: [1, 2, 3] } }); diff --git a/test/js/samples/component-static-immutable/expected.js b/test/js/samples/component-static-immutable/expected.js index 0e8d67ccea..e3dc618115 100644 --- a/test/js/samples/component-static-immutable/expected.js +++ b/test/js/samples/component-static-immutable/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, init, mount_component, noop, not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var current; var nested = new ctx.Nested({ props: { foo: "bar" } }); diff --git a/test/js/samples/component-static-immutable2/expected.js b/test/js/samples/component-static-immutable2/expected.js index 0e8d67ccea..e3dc618115 100644 --- a/test/js/samples/component-static-immutable2/expected.js +++ b/test/js/samples/component-static-immutable2/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, init, mount_component, noop, not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var current; var nested = new ctx.Nested({ props: { foo: "bar" } }); diff --git a/test/js/samples/component-static/expected.js b/test/js/samples/component-static/expected.js index be7357c866..014e1caefb 100644 --- a/test/js/samples/component-static/expected.js +++ b/test/js/samples/component-static/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, init, mount_component, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var current; var nested = new ctx.Nested({ props: { foo: "bar" } }); diff --git a/test/js/samples/computed-collapsed-if/expected.js b/test/js/samples/computed-collapsed-if/expected.js index f9411cf5f6..8a9acf7523 100644 --- a/test/js/samples/computed-collapsed-if/expected.js +++ b/test/js/samples/computed-collapsed-if/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, flush, init, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { return { c: noop, m: noop, diff --git a/test/js/samples/css-media-query/expected.js b/test/js/samples/css-media-query/expected.js index 72944cdaa6..89ae16d0fc 100644 --- a/test/js/samples/css-media-query/expected.js +++ b/test/js/samples/css-media-query/expected.js @@ -8,7 +8,7 @@ function add_css() { append(document.head, style); } -function create_fragment($$, ctx) { +function create_fragment(ctx) { var div; return { diff --git a/test/js/samples/css-shadow-dom-keyframes/expected.js b/test/js/samples/css-shadow-dom-keyframes/expected.js index 9582707de3..b7785963b3 100644 --- a/test/js/samples/css-shadow-dom-keyframes/expected.js +++ b/test/js/samples/css-shadow-dom-keyframes/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteElement, createElement, detachNode, init, insert, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var div; return { diff --git a/test/js/samples/debug-empty/expected.js b/test/js/samples/debug-empty/expected.js index 4ed9b00a85..4a6393388c 100644 --- a/test/js/samples/debug-empty/expected.js +++ b/test/js/samples/debug-empty/expected.js @@ -3,7 +3,7 @@ import { SvelteComponentDev, addLoc, append, createElement, createText, detachNo const file = undefined; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var h1, text0, text1, text2, text3; return { diff --git a/test/js/samples/debug-foo-bar-baz-things/expected.js b/test/js/samples/debug-foo-bar-baz-things/expected.js index 24089b777c..b56d333238 100644 --- a/test/js/samples/debug-foo-bar-baz-things/expected.js +++ b/test/js/samples/debug-foo-bar-baz-things/expected.js @@ -10,7 +10,7 @@ function get_each_context(ctx, list, i) { } // (1:0) {#each things as thing} -function create_each_block($$, ctx) { +function create_each_block(ctx) { var span, text0_value = ctx.thing.name, text0, text1; return { @@ -54,7 +54,7 @@ function create_each_block($$, ctx) { }; } -function create_fragment($$, ctx) { +function create_fragment(ctx) { var text0, p, text1, text2; var each_value = ctx.things; @@ -62,7 +62,7 @@ function create_fragment($$, ctx) { var each_blocks = []; for (var i = 0; i < each_value.length; i += 1) { - each_blocks[i] = create_each_block($$, get_each_context(ctx, each_value, i)); + each_blocks[i] = create_each_block(get_each_context(ctx, each_value, i)); } return { @@ -103,7 +103,7 @@ function create_fragment($$, ctx) { if (each_blocks[i]) { each_blocks[i].p(changed, child_ctx); } else { - each_blocks[i] = create_each_block($$, child_ctx); + each_blocks[i] = create_each_block(child_ctx); each_blocks[i].c(); each_blocks[i].m(text0.parentNode, text0); } diff --git a/test/js/samples/debug-foo/expected.js b/test/js/samples/debug-foo/expected.js index 2c7d3ef2d2..35d0a1f18f 100644 --- a/test/js/samples/debug-foo/expected.js +++ b/test/js/samples/debug-foo/expected.js @@ -10,7 +10,7 @@ function get_each_context(ctx, list, i) { } // (1:0) {#each things as thing} -function create_each_block($$, ctx) { +function create_each_block(ctx) { var span, text0_value = ctx.thing.name, text0, text1; return { @@ -54,7 +54,7 @@ function create_each_block($$, ctx) { }; } -function create_fragment($$, ctx) { +function create_fragment(ctx) { var text0, p, text1, text2; var each_value = ctx.things; @@ -62,7 +62,7 @@ function create_fragment($$, ctx) { var each_blocks = []; for (var i = 0; i < each_value.length; i += 1) { - each_blocks[i] = create_each_block($$, get_each_context(ctx, each_value, i)); + each_blocks[i] = create_each_block(get_each_context(ctx, each_value, i)); } return { @@ -103,7 +103,7 @@ function create_fragment($$, ctx) { if (each_blocks[i]) { each_blocks[i].p(changed, child_ctx); } else { - each_blocks[i] = create_each_block($$, child_ctx); + each_blocks[i] = create_each_block(child_ctx); each_blocks[i].c(); each_blocks[i].m(text0.parentNode, text0); } diff --git a/test/js/samples/deconflict-builtins/expected.js b/test/js/samples/deconflict-builtins/expected.js index d8f2db31ce..7ad954e330 100644 --- a/test/js/samples/deconflict-builtins/expected.js +++ b/test/js/samples/deconflict-builtins/expected.js @@ -8,7 +8,7 @@ function get_each_context(ctx, list, i) { } // (1:0) {#each createElement as node} -function create_each_block($$, ctx) { +function create_each_block(ctx) { var span, text_value = ctx.node, text; return { @@ -36,7 +36,7 @@ function create_each_block($$, ctx) { }; } -function create_fragment($$, ctx) { +function create_fragment(ctx) { var each_anchor; var each_value = ctx.createElement; @@ -44,7 +44,7 @@ function create_fragment($$, ctx) { var each_blocks = []; for (var i = 0; i < each_value.length; i += 1) { - each_blocks[i] = create_each_block($$, get_each_context(ctx, each_value, i)); + each_blocks[i] = create_each_block(get_each_context(ctx, each_value, i)); } return { @@ -74,7 +74,7 @@ function create_fragment($$, ctx) { if (each_blocks[i]) { each_blocks[i].p(changed, child_ctx); } else { - each_blocks[i] = create_each_block($$, child_ctx); + each_blocks[i] = create_each_block(child_ctx); each_blocks[i].c(); each_blocks[i].m(each_anchor.parentNode, each_anchor); } diff --git a/test/js/samples/deconflict-globals/expected.js b/test/js/samples/deconflict-globals/expected.js index b8613b4ff9..f3e8c93ebe 100644 --- a/test/js/samples/deconflict-globals/expected.js +++ b/test/js/samples/deconflict-globals/expected.js @@ -2,7 +2,7 @@ import { SvelteComponent as SvelteComponent_1, flush, init, noop, safe_not_equal } from "svelte/internal"; import { onMount } from "svelte"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { return { c: noop, m: noop, diff --git a/test/js/samples/dev-warning-missing-data-computed/expected.js b/test/js/samples/dev-warning-missing-data-computed/expected.js index 4f64e95350..e3dff9649d 100644 --- a/test/js/samples/dev-warning-missing-data-computed/expected.js +++ b/test/js/samples/dev-warning-missing-data-computed/expected.js @@ -3,7 +3,7 @@ import { SvelteComponentDev, addLoc, append, createElement, createText, detachNo const file = undefined; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var p, text0_value = Math.max(0, ctx.foo), text0, text1, text2; return { diff --git a/test/js/samples/do-use-dataset/expected.js b/test/js/samples/do-use-dataset/expected.js index 343ac22999..f340f71b01 100644 --- a/test/js/samples/do-use-dataset/expected.js +++ b/test/js/samples/do-use-dataset/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, createElement, createText, detachNode, flush, init, insert, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var div0, text, div1; return { diff --git a/test/js/samples/dont-use-dataset-in-legacy/expected.js b/test/js/samples/dont-use-dataset-in-legacy/expected.js index dc787888af..974ed23b28 100644 --- a/test/js/samples/dont-use-dataset-in-legacy/expected.js +++ b/test/js/samples/dont-use-dataset-in-legacy/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, createElement, createText, detachNode, flush, init, insert, noop, safe_not_equal, setAttribute } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var div0, text, div1; return { diff --git a/test/js/samples/dont-use-dataset-in-svg/expected.js b/test/js/samples/dont-use-dataset-in-svg/expected.js index 1a5825a63d..cc29258326 100644 --- a/test/js/samples/dont-use-dataset-in-svg/expected.js +++ b/test/js/samples/dont-use-dataset-in-svg/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, append, createSvgElement, detachNode, flush, init, insert, noop, safe_not_equal, setAttribute } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var svg, g0, g1; return { diff --git a/test/js/samples/dynamic-import/expected.js b/test/js/samples/dynamic-import/expected.js index 7b4a2fc004..8ccacdd398 100644 --- a/test/js/samples/dynamic-import/expected.js +++ b/test/js/samples/dynamic-import/expected.js @@ -2,7 +2,7 @@ import { SvelteComponent as SvelteComponent_1, init, mount_component, noop, safe_not_equal } from "svelte/internal"; import LazyLoad from "./LazyLoad.html"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var current; var lazyload = new LazyLoad({ props: { load: func } }); diff --git a/test/js/samples/each-block-changed-check/expected.js b/test/js/samples/each-block-changed-check/expected.js index 9dcf4b1ad2..0032b1c720 100644 --- a/test/js/samples/each-block-changed-check/expected.js +++ b/test/js/samples/each-block-changed-check/expected.js @@ -9,7 +9,7 @@ function get_each_context(ctx, list, i) { } // (1:0) {#each comments as comment, i} -function create_each_block($$, ctx) { +function create_each_block(ctx) { var div, strong, text0, text1, span, text2_value = ctx.comment.author, text2, text3, text4_value = ctx.elapsed(ctx.comment.time, ctx.time), text4, text5, text6, raw_value = ctx.comment.html, raw_before; return { @@ -67,7 +67,7 @@ function create_each_block($$, ctx) { }; } -function create_fragment($$, ctx) { +function create_fragment(ctx) { var text0, p, text1; var each_value = ctx.comments; @@ -75,7 +75,7 @@ function create_fragment($$, ctx) { var each_blocks = []; for (var i = 0; i < each_value.length; i += 1) { - each_blocks[i] = create_each_block($$, get_each_context(ctx, each_value, i)); + each_blocks[i] = create_each_block(get_each_context(ctx, each_value, i)); } return { @@ -109,7 +109,7 @@ function create_fragment($$, ctx) { if (each_blocks[i]) { each_blocks[i].p(changed, child_ctx); } else { - each_blocks[i] = create_each_block($$, child_ctx); + each_blocks[i] = create_each_block(child_ctx); each_blocks[i].c(); each_blocks[i].m(text0.parentNode, text0); } diff --git a/test/js/samples/each-block-keyed-animated/expected.js b/test/js/samples/each-block-keyed-animated/expected.js index fbd5fa15cc..e7e10208e4 100644 --- a/test/js/samples/each-block-keyed-animated/expected.js +++ b/test/js/samples/each-block-keyed-animated/expected.js @@ -8,7 +8,7 @@ function get_each_context(ctx, list, i) { } // (19:0) {#each things as thing (thing.id)} -function create_each_block($$, key_1, ctx) { +function create_each_block(key_1, ctx) { var div, text_value = ctx.thing.name, text, rect, stop_animation = noop; return { @@ -55,7 +55,7 @@ function create_each_block($$, key_1, ctx) { }; } -function create_fragment($$, ctx) { +function create_fragment(ctx) { var each_blocks = [], each_lookup = blankObject(), each_anchor; var each_value = ctx.things; @@ -65,7 +65,7 @@ function create_fragment($$, ctx) { for (var i = 0; i < each_value.length; i += 1) { let child_ctx = get_each_context(ctx, each_value, i); let key = get_key(child_ctx); - each_blocks[i] = each_lookup[key] = create_each_block($$, key, child_ctx); + each_blocks[i] = each_lookup[key] = create_each_block(key, child_ctx); } return { @@ -84,7 +84,7 @@ function create_fragment($$, ctx) { p(changed, ctx) { const each_value = ctx.things; for (let i = 0; i < each_blocks.length; i += 1) each_blocks[i].r(); - each_blocks = updateKeyedEach(each_blocks, $$, changed, get_key, 1, ctx, each_value, each_lookup, each_anchor.parentNode, fixAndOutroAndDestroyBlock, create_each_block, each_anchor, get_each_context); + each_blocks = updateKeyedEach(each_blocks, changed, get_key, 1, ctx, each_value, each_lookup, each_anchor.parentNode, fixAndOutroAndDestroyBlock, create_each_block, each_anchor, get_each_context); for (let i = 0; i < each_blocks.length; i += 1) each_blocks[i].a(); }, diff --git a/test/js/samples/each-block-keyed/expected.js b/test/js/samples/each-block-keyed/expected.js index 1599362932..c2dd23fb96 100644 --- a/test/js/samples/each-block-keyed/expected.js +++ b/test/js/samples/each-block-keyed/expected.js @@ -8,7 +8,7 @@ function get_each_context(ctx, list, i) { } // (1:0) {#each things as thing (thing.id)} -function create_each_block($$, key_1, ctx) { +function create_each_block(key_1, ctx) { var div, text_value = ctx.thing.name, text; return { @@ -41,7 +41,7 @@ function create_each_block($$, key_1, ctx) { }; } -function create_fragment($$, ctx) { +function create_fragment(ctx) { var each_blocks = [], each_lookup = blankObject(), each_anchor; var each_value = ctx.things; @@ -51,7 +51,7 @@ function create_fragment($$, ctx) { for (var i = 0; i < each_value.length; i += 1) { let child_ctx = get_each_context(ctx, each_value, i); let key = get_key(child_ctx); - each_blocks[i] = each_lookup[key] = create_each_block($$, key, child_ctx); + each_blocks[i] = each_lookup[key] = create_each_block(key, child_ctx); } return { @@ -69,7 +69,7 @@ function create_fragment($$, ctx) { p(changed, ctx) { const each_value = ctx.things; - each_blocks = updateKeyedEach(each_blocks, $$, changed, get_key, 1, ctx, each_value, each_lookup, each_anchor.parentNode, destroyBlock, create_each_block, each_anchor, get_each_context); + each_blocks = updateKeyedEach(each_blocks, changed, get_key, 1, ctx, each_value, each_lookup, each_anchor.parentNode, destroyBlock, create_each_block, each_anchor, get_each_context); }, i: noop, diff --git a/test/js/samples/event-handler-no-passive/expected.js b/test/js/samples/event-handler-no-passive/expected.js index 83b981cc40..19cfed3124 100644 --- a/test/js/samples/event-handler-no-passive/expected.js +++ b/test/js/samples/event-handler-no-passive/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, addListener, createElement, detachNode, init, insert, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var a, dispose; return { diff --git a/test/js/samples/event-modifiers/expected.js b/test/js/samples/event-modifiers/expected.js index 4af2123bf8..b6c554a8b6 100644 --- a/test/js/samples/event-modifiers/expected.js +++ b/test/js/samples/event-modifiers/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, addListener, append, createElement, createText, detachNode, init, insert, noop, preventDefault, run_all, safe_not_equal, stopPropagation } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var div, button0, text1, button1, text3, button2, dispose; return { diff --git a/test/js/samples/head-no-whitespace/expected.js b/test/js/samples/head-no-whitespace/expected.js index 2a356d5939..9e38fe41d3 100644 --- a/test/js/samples/head-no-whitespace/expected.js +++ b/test/js/samples/head-no-whitespace/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, append, createElement, detachNode, init, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var meta0, meta1; return { diff --git a/test/js/samples/hoisted-const/expected.js b/test/js/samples/hoisted-const/expected.js index 1720d67cd9..884f765ba7 100644 --- a/test/js/samples/hoisted-const/expected.js +++ b/test/js/samples/hoisted-const/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, append, createElement, createText, detachNode, init, insert, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var b, text_value = get_answer(), text; return { diff --git a/test/js/samples/hoisted-let/expected.js b/test/js/samples/hoisted-let/expected.js index f206ffb3ba..d94eb2a7b7 100644 --- a/test/js/samples/hoisted-let/expected.js +++ b/test/js/samples/hoisted-let/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, append, createElement, createText, detachNode, init, insert, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var b, text_value = get_answer(), text; return { diff --git a/test/js/samples/if-block-no-update/expected.js b/test/js/samples/if-block-no-update/expected.js index d8763aed51..a4c9fbc163 100644 --- a/test/js/samples/if-block-no-update/expected.js +++ b/test/js/samples/if-block-no-update/expected.js @@ -2,7 +2,7 @@ import { SvelteComponent as SvelteComponent_1, createComment, createElement, detachNode, flush, init, insert, noop, safe_not_equal } from "svelte/internal"; // (3:0) {:else} -function create_else_block($$, ctx) { +function create_else_block(ctx) { var p; return { @@ -24,7 +24,7 @@ function create_else_block($$, ctx) { } // (1:0) {#if foo} -function create_if_block($$, ctx) { +function create_if_block(ctx) { var p; return { @@ -45,7 +45,7 @@ function create_if_block($$, ctx) { }; } -function create_fragment($$, ctx) { +function create_fragment(ctx) { var if_block_anchor; function select_block_type(ctx) { @@ -54,7 +54,7 @@ function create_fragment($$, ctx) { } var current_block_type = select_block_type(ctx); - var if_block = current_block_type($$, ctx); + var if_block = current_block_type(ctx); return { c() { @@ -70,7 +70,7 @@ function create_fragment($$, ctx) { p(changed, ctx) { if (current_block_type !== (current_block_type = select_block_type(ctx))) { if_block.d(1); - if_block = current_block_type($$, ctx); + if_block = current_block_type(ctx); if (if_block) { if_block.c(); if_block.m(if_block_anchor.parentNode, if_block_anchor); diff --git a/test/js/samples/if-block-simple/expected.js b/test/js/samples/if-block-simple/expected.js index bd6528b11c..76c69b000f 100644 --- a/test/js/samples/if-block-simple/expected.js +++ b/test/js/samples/if-block-simple/expected.js @@ -2,7 +2,7 @@ import { SvelteComponent as SvelteComponent_1, createComment, createElement, detachNode, flush, init, insert, noop, safe_not_equal } from "svelte/internal"; // (1:0) {#if foo} -function create_if_block($$, ctx) { +function create_if_block(ctx) { var p; return { @@ -23,10 +23,10 @@ function create_if_block($$, ctx) { }; } -function create_fragment($$, ctx) { +function create_fragment(ctx) { var if_block_anchor; - var if_block = (ctx.foo) && create_if_block($$, ctx); + var if_block = (ctx.foo) && create_if_block(ctx); return { c() { @@ -42,7 +42,7 @@ function create_fragment($$, ctx) { p(changed, ctx) { if (ctx.foo) { if (!if_block) { - if_block = create_if_block($$, ctx); + if_block = create_if_block(ctx); if_block.c(); if_block.m(if_block_anchor.parentNode, if_block_anchor); } diff --git a/test/js/samples/inline-style-optimized-multiple/expected.js b/test/js/samples/inline-style-optimized-multiple/expected.js index b39aaf9d26..5988850ede 100644 --- a/test/js/samples/inline-style-optimized-multiple/expected.js +++ b/test/js/samples/inline-style-optimized-multiple/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, createElement, detachNode, flush, init, insert, noop, safe_not_equal, setStyle } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var div; return { diff --git a/test/js/samples/inline-style-optimized-url/expected.js b/test/js/samples/inline-style-optimized-url/expected.js index 49fb1d3427..c1ed003c83 100644 --- a/test/js/samples/inline-style-optimized-url/expected.js +++ b/test/js/samples/inline-style-optimized-url/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, createElement, detachNode, flush, init, insert, noop, safe_not_equal, setStyle } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var div; return { diff --git a/test/js/samples/inline-style-optimized/expected.js b/test/js/samples/inline-style-optimized/expected.js index bfb7c56436..9d88cb7d35 100644 --- a/test/js/samples/inline-style-optimized/expected.js +++ b/test/js/samples/inline-style-optimized/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, createElement, detachNode, flush, init, insert, noop, safe_not_equal, setStyle } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var div; return { diff --git a/test/js/samples/inline-style-unoptimized/expected.js b/test/js/samples/inline-style-unoptimized/expected.js index 73dd167875..31bda3a217 100644 --- a/test/js/samples/inline-style-unoptimized/expected.js +++ b/test/js/samples/inline-style-unoptimized/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, createElement, createText, detachNode, flush, init, insert, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var div0, text, div1, div1_style_value; return { diff --git a/test/js/samples/input-files/expected.js b/test/js/samples/input-files/expected.js index 507d9fa2a3..80a00a9701 100644 --- a/test/js/samples/input-files/expected.js +++ b/test/js/samples/input-files/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, addListener, createElement, detachNode, flush, init, insert, noop, safe_not_equal, setAttribute } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var input, dispose; return { diff --git a/test/js/samples/input-range/expected.js b/test/js/samples/input-range/expected.js index 393a6bb39c..7a4cf6079a 100644 --- a/test/js/samples/input-range/expected.js +++ b/test/js/samples/input-range/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, addListener, createElement, detachNode, flush, init, insert, noop, run_all, safe_not_equal, setAttribute, toNumber } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var input, dispose; return { diff --git a/test/js/samples/input-without-blowback-guard/expected.js b/test/js/samples/input-without-blowback-guard/expected.js index a0d6065de6..18c1535138 100644 --- a/test/js/samples/input-without-blowback-guard/expected.js +++ b/test/js/samples/input-without-blowback-guard/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, addListener, createElement, detachNode, flush, init, insert, noop, safe_not_equal, setAttribute } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var input, dispose; return { diff --git a/test/js/samples/instrumentation-script-if-no-block/expected.js b/test/js/samples/instrumentation-script-if-no-block/expected.js index 319ccb2637..f5100961fc 100644 --- a/test/js/samples/instrumentation-script-if-no-block/expected.js +++ b/test/js/samples/instrumentation-script-if-no-block/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, addListener, append, createElement, createText, detachNode, init, insert, noop, safe_not_equal, setData } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var button, text1, p, text2, text3, dispose; return { diff --git a/test/js/samples/instrumentation-script-x-equals-x/expected.js b/test/js/samples/instrumentation-script-x-equals-x/expected.js index 21e26a907f..45a968f94f 100644 --- a/test/js/samples/instrumentation-script-x-equals-x/expected.js +++ b/test/js/samples/instrumentation-script-x-equals-x/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, addListener, append, createElement, createText, detachNode, init, insert, noop, safe_not_equal, setData } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var button, text1, p, text2, text3_value = ctx.things.length, text3, dispose; return { diff --git a/test/js/samples/instrumentation-template-if-no-block/expected.js b/test/js/samples/instrumentation-template-if-no-block/expected.js index d403b12fb1..e5248cf5dc 100644 --- a/test/js/samples/instrumentation-template-if-no-block/expected.js +++ b/test/js/samples/instrumentation-template-if-no-block/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, addListener, append, createElement, createText, detachNode, init, insert, noop, safe_not_equal, setData } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var button, text1, p, text2, text3, dispose; return { diff --git a/test/js/samples/instrumentation-template-x-equals-x/expected.js b/test/js/samples/instrumentation-template-x-equals-x/expected.js index a7496d403a..9f7ff64725 100644 --- a/test/js/samples/instrumentation-template-x-equals-x/expected.js +++ b/test/js/samples/instrumentation-template-x-equals-x/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, addListener, append, createElement, createText, detachNode, init, insert, noop, safe_not_equal, setData } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var button, text1, p, text2, text3_value = ctx.things.length, text3, dispose; return { diff --git a/test/js/samples/legacy-input-type/expected.js b/test/js/samples/legacy-input-type/expected.js index 3eefd6cedb..f9e0807314 100644 --- a/test/js/samples/legacy-input-type/expected.js +++ b/test/js/samples/legacy-input-type/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, createElement, detachNode, init, insert, noop, safe_not_equal, setInputType } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var input; return { diff --git a/test/js/samples/media-bindings/expected.js b/test/js/samples/media-bindings/expected.js index 17d87e4a9d..8163a8a947 100644 --- a/test/js/samples/media-bindings/expected.js +++ b/test/js/samples/media-bindings/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, addListener, add_render_callback, createElement, detachNode, flush, init, insert, noop, run_all, safe_not_equal, timeRangesToArray } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var audio, audio_updating = false, audio_animationframe, audio_is_paused = true, dispose; function audio_timeupdate_handler() { diff --git a/test/js/samples/non-imported-component/expected.js b/test/js/samples/non-imported-component/expected.js index 0b91f40560..a917990426 100644 --- a/test/js/samples/non-imported-component/expected.js +++ b/test/js/samples/non-imported-component/expected.js @@ -2,7 +2,7 @@ import { SvelteComponent as SvelteComponent_1, createText, detachNode, init, insert, mount_component, noop, safe_not_equal } from "svelte/internal"; import Imported from "Imported.html"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var text, current; var imported = new Imported({}); diff --git a/test/js/samples/non-mutable-reference/expected.js b/test/js/samples/non-mutable-reference/expected.js index 747f0e1641..d82617fccd 100644 --- a/test/js/samples/non-mutable-reference/expected.js +++ b/test/js/samples/non-mutable-reference/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, append, createElement, createText, detachNode, init, insert, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var h1, text0, text1, text2; return { diff --git a/test/js/samples/select-dynamic-value/expected.js b/test/js/samples/select-dynamic-value/expected.js index a7f49b940e..7f2bc3b55c 100644 --- a/test/js/samples/select-dynamic-value/expected.js +++ b/test/js/samples/select-dynamic-value/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, append, createElement, detachNode, flush, init, insert, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var select, option0, option1, select_value_value; return { diff --git a/test/js/samples/setup-method/expected.js b/test/js/samples/setup-method/expected.js index 07a2dc46db..bd7e2e34b2 100644 --- a/test/js/samples/setup-method/expected.js +++ b/test/js/samples/setup-method/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, init, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { return { c: noop, m: noop, diff --git a/test/js/samples/svg-title/expected.js b/test/js/samples/svg-title/expected.js index 1db3124e9c..c88c0d5a5a 100644 --- a/test/js/samples/svg-title/expected.js +++ b/test/js/samples/svg-title/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, append, createSvgElement, createText, detachNode, init, insert, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var svg, title, text; return { diff --git a/test/js/samples/title/expected.js b/test/js/samples/title/expected.js index 390f11cbcd..8bb0b59894 100644 --- a/test/js/samples/title/expected.js +++ b/test/js/samples/title/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, flush, init, noop, safe_not_equal } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var title_value; document.title = title_value = "a " + ctx.custom + " title"; diff --git a/test/js/samples/use-elements-as-anchors/expected.js b/test/js/samples/use-elements-as-anchors/expected.js index eda6f1f721..3cc4d4a09a 100644 --- a/test/js/samples/use-elements-as-anchors/expected.js +++ b/test/js/samples/use-elements-as-anchors/expected.js @@ -2,7 +2,7 @@ import { SvelteComponent as SvelteComponent_1, append, createComment, createElement, createText, detachNode, flush, init, insert, noop, safe_not_equal } from "svelte/internal"; // (2:1) {#if a} -function create_if_block_4($$, ctx) { +function create_if_block_4(ctx) { var p; return { @@ -24,7 +24,7 @@ function create_if_block_4($$, ctx) { } // (8:1) {#if b} -function create_if_block_3($$, ctx) { +function create_if_block_3(ctx) { var p; return { @@ -46,7 +46,7 @@ function create_if_block_3($$, ctx) { } // (12:1) {#if c} -function create_if_block_2($$, ctx) { +function create_if_block_2(ctx) { var p; return { @@ -68,7 +68,7 @@ function create_if_block_2($$, ctx) { } // (18:1) {#if d} -function create_if_block_1($$, ctx) { +function create_if_block_1(ctx) { var p; return { @@ -90,7 +90,7 @@ function create_if_block_1($$, ctx) { } // (25:0) {#if e} -function create_if_block($$, ctx) { +function create_if_block(ctx) { var p; return { @@ -111,18 +111,18 @@ function create_if_block($$, ctx) { }; } -function create_fragment($$, ctx) { +function create_fragment(ctx) { var div, text0, p0, text2, text3, text4, p1, text6, text7, if_block4_anchor; - var if_block0 = (ctx.a) && create_if_block_4($$, ctx); + var if_block0 = (ctx.a) && create_if_block_4(ctx); - var if_block1 = (ctx.b) && create_if_block_3($$, ctx); + var if_block1 = (ctx.b) && create_if_block_3(ctx); - var if_block2 = (ctx.c) && create_if_block_2($$, ctx); + var if_block2 = (ctx.c) && create_if_block_2(ctx); - var if_block3 = (ctx.d) && create_if_block_1($$, ctx); + var if_block3 = (ctx.d) && create_if_block_1(ctx); - var if_block4 = (ctx.e) && create_if_block($$, ctx); + var if_block4 = (ctx.e) && create_if_block(ctx); return { c() { @@ -166,7 +166,7 @@ function create_fragment($$, ctx) { p(changed, ctx) { if (ctx.a) { if (!if_block0) { - if_block0 = create_if_block_4($$, ctx); + if_block0 = create_if_block_4(ctx); if_block0.c(); if_block0.m(div, text0); } @@ -177,7 +177,7 @@ function create_fragment($$, ctx) { if (ctx.b) { if (!if_block1) { - if_block1 = create_if_block_3($$, ctx); + if_block1 = create_if_block_3(ctx); if_block1.c(); if_block1.m(div, text3); } @@ -188,7 +188,7 @@ function create_fragment($$, ctx) { if (ctx.c) { if (!if_block2) { - if_block2 = create_if_block_2($$, ctx); + if_block2 = create_if_block_2(ctx); if_block2.c(); if_block2.m(div, text4); } @@ -199,7 +199,7 @@ function create_fragment($$, ctx) { if (ctx.d) { if (!if_block3) { - if_block3 = create_if_block_1($$, ctx); + if_block3 = create_if_block_1(ctx); if_block3.c(); if_block3.m(div, null); } @@ -210,7 +210,7 @@ function create_fragment($$, ctx) { if (ctx.e) { if (!if_block4) { - if_block4 = create_if_block($$, ctx); + if_block4 = create_if_block(ctx); if_block4.c(); if_block4.m(if_block4_anchor.parentNode, if_block4_anchor); } diff --git a/test/js/samples/window-binding-scroll/expected.js b/test/js/samples/window-binding-scroll/expected.js index 7a5e291866..2696297623 100644 --- a/test/js/samples/window-binding-scroll/expected.js +++ b/test/js/samples/window-binding-scroll/expected.js @@ -1,7 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent as SvelteComponent_1, addListener, add_render_callback, append, createElement, createText, detachNode, flush, init, insert, noop, safe_not_equal, setData } from "svelte/internal"; -function create_fragment($$, ctx) { +function create_fragment(ctx) { var scrolling = false, clear_scrolling = () => { scrolling = false }, scrolling_timeout, p, text0, text1, dispose; add_render_callback(ctx.onwindowscroll); diff --git a/test/runtime/samples/binding-select-in-yield/_config.js b/test/runtime/samples/binding-select-in-yield/_config.js index 6c206837e1..11dcb3d58c 100644 --- a/test/runtime/samples/binding-select-in-yield/_config.js +++ b/test/runtime/samples/binding-select-in-yield/_config.js @@ -18,7 +18,7 @@ export default { `); - const select = target.querySelector('select'); + let select = target.querySelector('select'); const change = new window.MouseEvent('change'); select.options[2].selected = true; @@ -44,6 +44,8 @@ export default { await component.modal.toggle(); await component.modal.toggle(); + select = target.querySelector('select'); + assert.deepEqual(Array.from(select.options).map(o => o.selected), [ false, false, diff --git a/test/runtime/samples/component-binding-blowback-c/_config.js b/test/runtime/samples/component-binding-blowback-c/_config.js index b179f1c104..5ff0ae34f6 100644 --- a/test/runtime/samples/component-binding-blowback-c/_config.js +++ b/test/runtime/samples/component-binding-blowback-c/_config.js @@ -14,7 +14,7 @@ export default { `, - async test({ assert, component, target, window }) { + async test({ assert, target, window }) { const input = target.querySelector('input'); input.value = 4; diff --git a/test/runtime/samples/component-slot-let-aliased/Nested.html b/test/runtime/samples/component-slot-let-aliased/Nested.html new file mode 100644 index 0000000000..38c6ed6cbe --- /dev/null +++ b/test/runtime/samples/component-slot-let-aliased/Nested.html @@ -0,0 +1,5 @@ +
+ {#each things as thing} + + {/each} +
\ No newline at end of file diff --git a/test/runtime/samples/component-slot-let-aliased/_config.js b/test/runtime/samples/component-slot-let-aliased/_config.js new file mode 100644 index 0000000000..d66f613bb4 --- /dev/null +++ b/test/runtime/samples/component-slot-let-aliased/_config.js @@ -0,0 +1,24 @@ +export default { + props: { + things: [1, 2, 3] + }, + + html: ` +
+ 1 + 2 + 3 +
`, + + test({ assert, component, target }) { + component.things = [1, 2, 3, 4]; + assert.htmlEqual(target.innerHTML, ` +
+ 1 + 2 + 3 + 4 +
+ `); + } +}; diff --git a/test/runtime/samples/component-slot-let-aliased/main.html b/test/runtime/samples/component-slot-let-aliased/main.html new file mode 100644 index 0000000000..1acb7fbcdc --- /dev/null +++ b/test/runtime/samples/component-slot-let-aliased/main.html @@ -0,0 +1,9 @@ + + + + {x} + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-let-b/Nested.html b/test/runtime/samples/component-slot-let-b/Nested.html new file mode 100644 index 0000000000..42963f6d1d --- /dev/null +++ b/test/runtime/samples/component-slot-let-b/Nested.html @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-let-b/_config.js b/test/runtime/samples/component-slot-let-b/_config.js new file mode 100644 index 0000000000..f13a3b2bb6 --- /dev/null +++ b/test/runtime/samples/component-slot-let-b/_config.js @@ -0,0 +1,18 @@ +export default { + html: ` + + 0 + `, + + async test({ assert, target, window }) { + const button = target.querySelector('button'); + const click = new window.MouseEvent('click'); + + await button.dispatchEvent(click); + + assert.htmlEqual(target.innerHTML, ` + + 1 + `); + } +}; diff --git a/test/runtime/samples/component-slot-let-b/main.html b/test/runtime/samples/component-slot-let-b/main.html new file mode 100644 index 0000000000..b4fd5f54c6 --- /dev/null +++ b/test/runtime/samples/component-slot-let-b/main.html @@ -0,0 +1,7 @@ + + + + {count} + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-let-c/Nested.html b/test/runtime/samples/component-slot-let-c/Nested.html new file mode 100644 index 0000000000..d38a1403fb --- /dev/null +++ b/test/runtime/samples/component-slot-let-c/Nested.html @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-let-c/_config.js b/test/runtime/samples/component-slot-let-c/_config.js new file mode 100644 index 0000000000..fe8e68b1ac --- /dev/null +++ b/test/runtime/samples/component-slot-let-c/_config.js @@ -0,0 +1,18 @@ +export default { + html: ` + + 0 (undefined) + `, + + async test({ assert, target, window }) { + const button = target.querySelector('button'); + const click = new window.MouseEvent('click'); + + await button.dispatchEvent(click); + + assert.htmlEqual(target.innerHTML, ` + + 1 (undefined) + `); + } +}; diff --git a/test/runtime/samples/component-slot-let-c/main.html b/test/runtime/samples/component-slot-let-c/main.html new file mode 100644 index 0000000000..b834fd8790 --- /dev/null +++ b/test/runtime/samples/component-slot-let-c/main.html @@ -0,0 +1,7 @@ + + + + {c} ({count}) + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-let-destructured/Nested.html b/test/runtime/samples/component-slot-let-destructured/Nested.html new file mode 100644 index 0000000000..38c6ed6cbe --- /dev/null +++ b/test/runtime/samples/component-slot-let-destructured/Nested.html @@ -0,0 +1,5 @@ +
+ {#each things as thing} + + {/each} +
\ No newline at end of file diff --git a/test/runtime/samples/component-slot-let-destructured/_config.js b/test/runtime/samples/component-slot-let-destructured/_config.js new file mode 100644 index 0000000000..d7aaae2d77 --- /dev/null +++ b/test/runtime/samples/component-slot-let-destructured/_config.js @@ -0,0 +1,34 @@ +export default { + props: { + things: [ + { num: 1 }, + { num: 2 }, + { num: 3 } + ] + }, + + html: ` +
+ 1 + 2 + 3 +
`, + + test({ assert, component, target }) { + component.things = [ + { num: 1 }, + { num: 2 }, + { num: 3 }, + { num: 4 } + ]; + + assert.htmlEqual(target.innerHTML, ` +
+ 1 + 2 + 3 + 4 +
+ `); + } +}; diff --git a/test/runtime/samples/component-slot-let-destructured/main.html b/test/runtime/samples/component-slot-let-destructured/main.html new file mode 100644 index 0000000000..488fd463de --- /dev/null +++ b/test/runtime/samples/component-slot-let-destructured/main.html @@ -0,0 +1,9 @@ + + + + {num} + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-let-named/Nested.html b/test/runtime/samples/component-slot-let-named/Nested.html new file mode 100644 index 0000000000..d95593fc18 --- /dev/null +++ b/test/runtime/samples/component-slot-let-named/Nested.html @@ -0,0 +1,5 @@ +
+ {#each things as thing} + + {/each} +
\ No newline at end of file diff --git a/test/runtime/samples/component-slot-let-named/_config.js b/test/runtime/samples/component-slot-let-named/_config.js new file mode 100644 index 0000000000..f65448af93 --- /dev/null +++ b/test/runtime/samples/component-slot-let-named/_config.js @@ -0,0 +1,24 @@ +export default { + props: { + things: [1, 2, 3] + }, + + html: ` +
+
1
+
2
+
3
+
`, + + test({ assert, component, target }) { + component.things = [1, 2, 3, 4]; + assert.htmlEqual(target.innerHTML, ` +
+
1
+
2
+
3
+
4
+
+ `); + } +}; diff --git a/test/runtime/samples/component-slot-let-named/main.html b/test/runtime/samples/component-slot-let-named/main.html new file mode 100644 index 0000000000..1c3d3365c7 --- /dev/null +++ b/test/runtime/samples/component-slot-let-named/main.html @@ -0,0 +1,11 @@ + + + +
+ {thing} +
+
\ No newline at end of file diff --git a/test/runtime/samples/component-slot-let/Nested.html b/test/runtime/samples/component-slot-let/Nested.html new file mode 100644 index 0000000000..38c6ed6cbe --- /dev/null +++ b/test/runtime/samples/component-slot-let/Nested.html @@ -0,0 +1,5 @@ +
+ {#each things as thing} + + {/each} +
\ No newline at end of file diff --git a/test/runtime/samples/component-slot-let/_config.js b/test/runtime/samples/component-slot-let/_config.js new file mode 100644 index 0000000000..d66f613bb4 --- /dev/null +++ b/test/runtime/samples/component-slot-let/_config.js @@ -0,0 +1,24 @@ +export default { + props: { + things: [1, 2, 3] + }, + + html: ` +
+ 1 + 2 + 3 +
`, + + test({ assert, component, target }) { + component.things = [1, 2, 3, 4]; + assert.htmlEqual(target.innerHTML, ` +
+ 1 + 2 + 3 + 4 +
+ `); + } +}; diff --git a/test/runtime/samples/component-slot-let/main.html b/test/runtime/samples/component-slot-let/main.html new file mode 100644 index 0000000000..76d491389f --- /dev/null +++ b/test/runtime/samples/component-slot-let/main.html @@ -0,0 +1,9 @@ + + + + {thing} + \ No newline at end of file diff --git a/test/runtime/samples/context-api/Tab.html b/test/runtime/samples/context-api/Tab.html new file mode 100644 index 0000000000..2f9793aed8 --- /dev/null +++ b/test/runtime/samples/context-api/Tab.html @@ -0,0 +1,17 @@ + + + \ No newline at end of file diff --git a/test/runtime/samples/context-api/TabList.html b/test/runtime/samples/context-api/TabList.html new file mode 100644 index 0000000000..08c86216ae --- /dev/null +++ b/test/runtime/samples/context-api/TabList.html @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/test/runtime/samples/context-api/TabPanel.html b/test/runtime/samples/context-api/TabPanel.html new file mode 100644 index 0000000000..e15dfca021 --- /dev/null +++ b/test/runtime/samples/context-api/TabPanel.html @@ -0,0 +1,16 @@ + + +{#if $selectedPanel === panel} + +{/if} \ No newline at end of file diff --git a/test/runtime/samples/context-api/Tabs.html b/test/runtime/samples/context-api/Tabs.html new file mode 100644 index 0000000000..6692bb31e9 --- /dev/null +++ b/test/runtime/samples/context-api/Tabs.html @@ -0,0 +1,52 @@ + + + + + + +
+ +
\ No newline at end of file diff --git a/test/runtime/samples/context-api/_config.js b/test/runtime/samples/context-api/_config.js new file mode 100644 index 0000000000..1bc475a156 --- /dev/null +++ b/test/runtime/samples/context-api/_config.js @@ -0,0 +1,73 @@ +export default { + html: ` +
+
+ + +
+ +

Small panel

+
+ `, + + async test({ assert, component, target, window }) { + const click = new window.MouseEvent('click'); + let buttons = target.querySelectorAll('button'); + + await buttons[1].dispatchEvent(click); + + assert.htmlEqual(target.innerHTML, ` +
+
+ + +
+ +

Large panel

+
+ `); + + component.show_medium = true; + + assert.htmlEqual(target.innerHTML, ` +
+
+ + + +
+ +

Large panel

+
+ `); + + buttons = target.querySelectorAll('button'); + + await buttons[1].dispatchEvent(click); + + assert.htmlEqual(target.innerHTML, ` +
+
+ + + +
+ +

Medium panel

+
+ `); + + component.show_medium = false; + + assert.htmlEqual(target.innerHTML, ` +
+
+ + +
+ +

Large panel

+
+ `); + } +}; \ No newline at end of file diff --git a/test/runtime/samples/context-api/main.html b/test/runtime/samples/context-api/main.html new file mode 100644 index 0000000000..ef3ee996d5 --- /dev/null +++ b/test/runtime/samples/context-api/main.html @@ -0,0 +1,30 @@ + + + + + small + {#if show_medium}medium{/if} + large + + + +

Small panel

+
+ + {#if show_medium} + +

Medium panel

+
+ {/if} + + +

Large panel

+
+
\ No newline at end of file diff --git a/test/validator/samples/component-slot-each-block/errors.json b/test/validator/samples/component-slot-each-block/errors.json deleted file mode 100644 index 97f88e4e60..0000000000 --- a/test/validator/samples/component-slot-each-block/errors.json +++ /dev/null @@ -1,15 +0,0 @@ -[{ - "code": "invalid-slot-placement", - "message": " cannot be a child of an each-block", - "start": { - "line": 2, - "column": 1, - "character": 25 - }, - "end": { - "line": 2, - "column": 1, - "character": 25 - }, - "pos": 25 -}] \ No newline at end of file diff --git a/test/validator/samples/component-slot-each-block/input.html b/test/validator/samples/component-slot-each-block/input.html deleted file mode 100644 index ddc27a7d0d..0000000000 --- a/test/validator/samples/component-slot-each-block/input.html +++ /dev/null @@ -1,3 +0,0 @@ -{#each things as thing} - -{/each} \ No newline at end of file