From d99359a03a577fa01e18b7b1adee7bbfe404dc78 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Wed, 25 Mar 2020 08:11:00 -0700 Subject: [PATCH 1/4] Implement with block construct --- src/compiler/compile/nodes/WithBlock.ts | 86 ++++++++ src/compiler/compile/nodes/interfaces.ts | 4 +- .../compile/nodes/shared/map_children.ts | 2 + src/compiler/compile/render_dom/Block.ts | 8 +- .../render_dom/wrappers/Element/Binding.ts | 9 +- .../compile/render_dom/wrappers/Fragment.ts | 4 +- .../wrappers/InlineComponent/index.ts | 4 +- .../compile/render_dom/wrappers/WithBlock.ts | 183 ++++++++++++++++++ src/compiler/compile/render_ssr/Renderer.ts | 4 +- .../compile/render_ssr/handlers/WithBlock.ts | 12 ++ src/compiler/parse/state/mustache.ts | 14 +- .../_config.js | 45 +++++ .../main.svelte | 9 + test/runtime/samples/with-block/_config.js | 16 ++ test/runtime/samples/with-block/main.svelte | 7 + 15 files changed, 391 insertions(+), 16 deletions(-) create mode 100644 src/compiler/compile/nodes/WithBlock.ts create mode 100644 src/compiler/compile/render_dom/wrappers/WithBlock.ts create mode 100644 src/compiler/compile/render_ssr/handlers/WithBlock.ts create mode 100644 test/runtime/samples/with-block-destructured-object-binding/_config.js create mode 100644 test/runtime/samples/with-block-destructured-object-binding/main.svelte create mode 100644 test/runtime/samples/with-block/_config.js create mode 100644 test/runtime/samples/with-block/main.svelte diff --git a/src/compiler/compile/nodes/WithBlock.ts b/src/compiler/compile/nodes/WithBlock.ts new file mode 100644 index 000000000000..0d1a32cfc80a --- /dev/null +++ b/src/compiler/compile/nodes/WithBlock.ts @@ -0,0 +1,86 @@ +import Expression from './shared/Expression'; +import map_children from './shared/map_children'; +import TemplateScope from './shared/TemplateScope'; +import AbstractBlock from './shared/AbstractBlock'; +import { x } from 'code-red'; +import { Node, Identifier, RestElement } from 'estree'; + +interface Context { + key: Identifier; + name?: string; + modifier: (node: Node) => Node; +} + +function unpack_destructuring(contexts: Context[], node: Node, modifier: (node: Node) => Node) { + if (!node) return; + + if (node.type === 'Identifier' || (node as any).type === 'RestIdentifier') { // TODO is this right? not RestElement? + contexts.push({ + key: node as Identifier, + modifier + }); + } else if (node.type === 'ArrayPattern') { + node.elements.forEach((element, i) => { + if (element && (element as any).type === 'RestIdentifier') { + unpack_destructuring(contexts, element, node => x`${modifier(node)}.slice(${i})` as Node); + } else { + unpack_destructuring(contexts, element, node => x`${modifier(node)}[${i}]` as Node); + } + }); + } else if (node.type === 'ObjectPattern') { + const used_properties = []; + + node.properties.forEach((property, i) => { + if ((property as any).kind === 'rest') { // TODO is this right? + const replacement: RestElement = { + type: 'RestElement', + argument: property.key as Identifier + }; + + node.properties[i] = replacement as any; + + unpack_destructuring( + contexts, + property.value, + node => x`@object_without_properties(${modifier(node)}, [${used_properties}])` as Node + ); + } else { + used_properties.push(x`"${(property.key as Identifier).name}"`); + + unpack_destructuring(contexts, property.value, node => x`${modifier(node)}.${(property.key as Identifier).name}` as Node); + } + }); + } +} + +export default class WithBlock extends AbstractBlock { + type: 'WithBlock'; + + expression: Expression; + context_node: Node; + + context: string; + scope: TemplateScope; + contexts: Context[]; + has_binding = false; + + constructor(component, parent, scope, info) { + super(component, parent, scope, info); + + this.expression = new Expression(component, this, scope, info.expression); + this.context = info.context.name || 'with'; // TODO this is used to facilitate binding; currently fails with destructuring + this.context_node = info.context; + this.scope = scope.child(); + + this.contexts = []; + unpack_destructuring(this.contexts, info.context, node => node); + + this.contexts.forEach(context => { + this.scope.add(context.key.name, this.expression.dependencies, this); + }); + + this.children = map_children(component, this, this.scope, info.children); + + this.warn_if_empty_block(); + } +} diff --git a/src/compiler/compile/nodes/interfaces.ts b/src/compiler/compile/nodes/interfaces.ts index 752168a49d41..193dac344669 100644 --- a/src/compiler/compile/nodes/interfaces.ts +++ b/src/compiler/compile/nodes/interfaces.ts @@ -29,6 +29,7 @@ import ThenBlock from './ThenBlock'; import Title from './Title'; import Transition from './Transition'; import Window from './Window'; +import WithBlock from './WithBlock'; // note: to write less types each of types in union below should have type defined as literal // https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions @@ -61,4 +62,5 @@ export type INode = Action | ThenBlock | Title | Transition -| Window; +| Window +| WithBlock; diff --git a/src/compiler/compile/nodes/shared/map_children.ts b/src/compiler/compile/nodes/shared/map_children.ts index dcdc52f86d16..62ebe7643fc8 100644 --- a/src/compiler/compile/nodes/shared/map_children.ts +++ b/src/compiler/compile/nodes/shared/map_children.ts @@ -14,6 +14,7 @@ import Slot from '../Slot'; import Text from '../Text'; import Title from '../Title'; import Window from '../Window'; +import WithBlock from '../WithBlock'; import { TemplateNode } from '../../../interfaces'; export type Children = ReturnType; @@ -36,6 +37,7 @@ function get_constructor(type) { case 'Text': return Text; case 'Title': return Title; case 'Window': return Window; + case 'WithBlock': return WithBlock; default: throw new Error(`Not implemented: ${type}`); } } diff --git a/src/compiler/compile/render_dom/Block.ts b/src/compiler/compile/render_dom/Block.ts index b29b4d4afd95..f8550a0dc7a6 100644 --- a/src/compiler/compile/render_dom/Block.ts +++ b/src/compiler/compile/render_dom/Block.ts @@ -13,10 +13,10 @@ export interface BlockOptions { key?: Identifier; bindings?: Map Node; }>; dependencies?: Set; @@ -38,10 +38,10 @@ export default class Block { bindings: Map Node; }>; diff --git a/src/compiler/compile/render_dom/wrappers/Element/Binding.ts b/src/compiler/compile/render_dom/wrappers/Element/Binding.ts index ac5732eae7b0..68eee55b600c 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/Binding.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/Binding.ts @@ -280,10 +280,13 @@ function get_event_handler( const { object, property, modifier, store } = context; if (lhs.type === 'Identifier') { - lhs = modifier(x`${object}[${property}]`); - contextual_dependencies.add(object.name); - contextual_dependencies.add(property.name); + if (property === undefined) { + lhs = modifier(b`${object}`[0]); + } else { + lhs = modifier(x`${object}[${property}]`); + contextual_dependencies.add(property.name); + } } if (store) { diff --git a/src/compiler/compile/render_dom/wrappers/Fragment.ts b/src/compiler/compile/render_dom/wrappers/Fragment.ts index a0984b69b920..248356b6dea5 100644 --- a/src/compiler/compile/render_dom/wrappers/Fragment.ts +++ b/src/compiler/compile/render_dom/wrappers/Fragment.ts @@ -13,6 +13,7 @@ import Slot from './Slot'; import Text from './Text'; import Title from './Title'; import Window from './Window'; +import WithBlock from './WithBlock'; import { INode } from '../../nodes/interfaces'; import Renderer from '../Renderer'; import Block from '../Block'; @@ -36,7 +37,8 @@ const wrappers = { Slot, Text, Title, - Window + Window, + WithBlock }; function trimmable_at(child: INode, next_sibling: Wrapper): boolean { diff --git a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts index 00f803bbbd48..9115ec3011c0 100644 --- a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts +++ b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts @@ -346,7 +346,9 @@ export default class InlineComponentWrapper extends Wrapper { const { name } = binding.expression.node; const { object, property, snippet } = block.bindings.get(name); lhs = snippet; - contextual_dependencies.push(object.name, property.name); + contextual_dependencies.push(object.name); + if (property !== undefined) + contextual_dependencies.push(property.name); } const params = [x`#value`]; diff --git a/src/compiler/compile/render_dom/wrappers/WithBlock.ts b/src/compiler/compile/render_dom/wrappers/WithBlock.ts new file mode 100644 index 000000000000..3a15628cf811 --- /dev/null +++ b/src/compiler/compile/render_dom/wrappers/WithBlock.ts @@ -0,0 +1,183 @@ +import Renderer from '../Renderer'; +import Block from '../Block'; +import Wrapper from './shared/Wrapper'; +import create_debugging_comment from './shared/create_debugging_comment'; +import FragmentWrapper from './Fragment'; +import { b, x } from 'code-red'; +import WithBlock from '../../nodes/WithBlock'; +import { Node, Identifier } from 'estree'; + +export default class WithBlockWrapper extends Wrapper { + block: Block; + node: WithBlock; + fragment: FragmentWrapper; + vars: { + create_with_block: Identifier; + with_block_value: Identifier; + with_block: Identifier; + get_with_context: Identifier; + } + + dependencies: Set; + + var: Identifier = { type: 'Identifier', name: 'with' }; + + constructor( + renderer: Renderer, + block: Block, + parent: Wrapper, + node: WithBlock, + strip_whitespace: boolean, + next_sibling: Wrapper + ) { + super(renderer, block, parent, node); + this.cannot_use_innerhtml(); + this.not_static_content(); + + block.add_dependencies(node.expression.dependencies); + + this.node.contexts.forEach(context => { + renderer.add_to_context(context.key.name, true); + }); + + this.block = block.child({ + comment: create_debugging_comment(this.node, this.renderer.component), + name: renderer.component.get_unique_name('create_with_block'), + type: 'with', + // @ts-ignore todo: probably error + key: node.key as string, + + bindings: new Map(block.bindings) + }); + + const with_block_value = renderer.component.get_unique_name(`${this.var.name}_value`); + renderer.add_to_context(with_block_value.name, true); + + this.vars = { + create_with_block: this.block.name, + with_block_value, + with_block: block.get_unique_name(`${this.var.name}_block`), + get_with_context: renderer.component.get_unique_name(`get_${this.var.name}_context`), + }; + + const store = + node.expression.node.type === 'Identifier' && + node.expression.node.name[0] === '$' + ? node.expression.node.name.slice(1) + : null; + + node.contexts.forEach(prop => { + this.block.bindings.set(prop.key.name, { + object: with_block_value, + modifier: prop.modifier, + snippet: prop.modifier(x`${with_block_value}` as Node), + store + }); + }); + + renderer.blocks.push(this.block); + + this.fragment = new FragmentWrapper(renderer, this.block, node.children, this, strip_whitespace, next_sibling); + + block.add_dependencies(this.block.dependencies); + } + + render(block: Block, parent_node: Identifier, parent_nodes: Identifier) { + if (this.fragment.nodes.length === 0) return; + + const { renderer } = this; + const { + create_with_block, + with_block, + with_block_value, + get_with_context + } = this.vars; + + const needs_anchor = this.next + ? !this.next.is_dom_node() : + !parent_node || !this.parent.is_dom_node(); + + const context_props = this.node.contexts.map(prop => b`child_ctx[${renderer.context_lookup.get(prop.key.name).index}] = ${prop.modifier(x`value`)};`); + if (this.node.has_binding) context_props.push(b`child_ctx[${renderer.context_lookup.get(with_block_value.name).index}] = value;`); + + const snippet = this.node.expression.manipulate(block); + block.chunks.init.push(b`let ${with_block_value} = ${snippet};`); + + renderer.blocks.push(b` + function ${get_with_context}(#ctx, value) { + const child_ctx = #ctx.slice(); + ${context_props} + return child_ctx; + } + `); + + const initial_anchor_node: Identifier = { type: 'Identifier', name: parent_node ? 'null' : 'anchor' }; + const initial_mount_node: Identifier = parent_node || { type: 'Identifier', name: '#target' }; + const update_anchor_node = needs_anchor + ? block.get_unique_name(`${this.var.name}_anchor`) + : (this.next && this.next.var) || { type: 'Identifier', name: 'null' }; + const update_mount_node: Identifier = this.get_update_mount_node((update_anchor_node as Identifier)); + + const all_dependencies = new Set(this.block.dependencies); // TODO should be dynamic deps only + this.node.expression.dynamic_dependencies().forEach((dependency: string) => { + all_dependencies.add(dependency); + }); + this.dependencies = all_dependencies; + + block.chunks.init.push(b` + let ${with_block} = ${create_with_block}(${get_with_context}(#ctx, ${with_block_value})); + `); + + block.chunks.create.push(b` + ${with_block}.c(); + `); + + if (parent_nodes && this.renderer.options.hydratable) { + block.chunks.claim.push(b` + ${with_block}.l(${parent_nodes}); + `); + } + + block.chunks.mount.push(b` + ${with_block}.m(${initial_mount_node}, ${initial_anchor_node}); + `); + + if (this.dependencies.size) { + const update = this.block.has_update_method + ? b` + if (${with_block}) { + ${with_block}.p(child_ctx, #dirty); + } else { + ${with_block} = ${create_with_block}(child_ctx); + ${with_block}.c(); + ${with_block}.m(${update_mount_node}, ${update_anchor_node}); + }` + : b` + if (!${with_block}) { + ${with_block} = ${create_with_block}(child_ctx); + ${with_block}.c(); + ${with_block}.m(${update_mount_node}, ${update_anchor_node}); + }`; + block.chunks.update.push(b` + if (${block.renderer.dirty(Array.from(all_dependencies))}) { + ${with_block_value} = ${snippet}; + const child_ctx = ${get_with_context}(#ctx, ${with_block_value}); + ${update} + } + `); + } + + block.chunks.destroy.push(b`${with_block}.d(detaching);`); + + if (needs_anchor) { + block.add_element( + update_anchor_node as Identifier, + x`@empty()`, + parent_nodes && x`@empty()`, + parent_node + ); + } + + this.fragment.render(this.block, null, x`#nodes` as Identifier); + } +} diff --git a/src/compiler/compile/render_ssr/Renderer.ts b/src/compiler/compile/render_ssr/Renderer.ts index fb9216327c68..d2558bceeec5 100644 --- a/src/compiler/compile/render_ssr/Renderer.ts +++ b/src/compiler/compile/render_ssr/Renderer.ts @@ -11,6 +11,7 @@ import Slot from './handlers/Slot'; import Tag from './handlers/Tag'; import Text from './handlers/Text'; import Title from './handlers/Title'; +import WithBlock from './handlers/WithBlock'; import { AppendTarget, CompileOptions } from '../../interfaces'; import { INode } from '../nodes/interfaces'; import { Expression, TemplateLiteral, Identifier } from 'estree'; @@ -36,7 +37,8 @@ const handlers: Record = { Slot, Text, Title, - Window: noop + Window: noop, + WithBlock }; export interface RenderOptions extends CompileOptions{ diff --git a/src/compiler/compile/render_ssr/handlers/WithBlock.ts b/src/compiler/compile/render_ssr/handlers/WithBlock.ts new file mode 100644 index 000000000000..b7b3c98991bd --- /dev/null +++ b/src/compiler/compile/render_ssr/handlers/WithBlock.ts @@ -0,0 +1,12 @@ +import Renderer, { RenderOptions } from '../Renderer'; +import WithBlock from '../../nodes/WithBlock'; +import { x } from 'code-red'; + +export default function(node: WithBlock, renderer: Renderer, options: RenderOptions) { + const args = [node.context_node]; + + renderer.push(); + renderer.render(node.children, options); + const result = renderer.pop(); + renderer.add_expression(x`((${args}) => ${result})(${node.expression.node})`); +} diff --git a/src/compiler/parse/state/mustache.ts b/src/compiler/parse/state/mustache.ts index 4e1d5c5fd4cc..a9dea41d0329 100644 --- a/src/compiler/parse/state/mustache.ts +++ b/src/compiler/parse/state/mustache.ts @@ -38,7 +38,7 @@ export default function mustache(parser: Parser) { parser.allow_whitespace(); - // {/if}, {/each} or {/await} + // {/if}, {/each}, {/await}, or {/with} if (parser.eat('/')) { let block = parser.current(); let expected; @@ -63,6 +63,8 @@ export default function mustache(parser: Parser) { expected = 'each'; } else if (block.type === 'AwaitBlock') { expected = 'await'; + } else if (block.type === 'WithBlock') { + expected = 'with'; } else { parser.error({ code: `unexpected-block-close`, @@ -212,7 +214,7 @@ export default function mustache(parser: Parser) { await_block[is_then ? 'then' : 'catch'] = new_block; parser.stack.push(new_block); } else if (parser.eat('#')) { - // {#if foo}, {#each foo} or {#await foo} + // {#if foo}, {#each foo}, {#await foo}, or {#with foo} let type; if (parser.eat('if')) { @@ -221,10 +223,12 @@ export default function mustache(parser: Parser) { type = 'EachBlock'; } else if (parser.eat('await')) { type = 'AwaitBlock'; + } else if (parser.eat('with')) { + type = 'WithBlock'; } else { parser.error({ code: `expected-block-type`, - message: `Expected if, each or await` + message: `Expected if, each, await, or with` }); } @@ -272,8 +276,8 @@ export default function mustache(parser: Parser) { parser.allow_whitespace(); - // {#each} blocks must declare a context – {#each list as item} - if (type === 'EachBlock') { + // {#each} and {#with} blocks must declare a context – {#each list as item} + if (type === 'EachBlock' || type === 'WithBlock') { parser.eat('as', true); parser.require_whitespace(); diff --git a/test/runtime/samples/with-block-destructured-object-binding/_config.js b/test/runtime/samples/with-block-destructured-object-binding/_config.js new file mode 100644 index 000000000000..436b71cfaff5 --- /dev/null +++ b/test/runtime/samples/with-block-destructured-object-binding/_config.js @@ -0,0 +1,45 @@ +export default { + props: { + people: { name: { first: 'Doctor', last: 'Who' } }, + }, + + html: ` + + +

Doctor Who

+ `, + + ssrHtml: ` + + +

Doctor Who

+ `, + + async test({ assert, component, target, window }) { + const inputs = target.querySelectorAll('input'); + + inputs[1].value = 'Oz'; + await inputs[1].dispatchEvent(new window.Event('input')); + + const { people } = component; + + assert.deepEqual(people, { + name: { first: 'Doctor', last: 'Oz' } + }); + + assert.htmlEqual(target.innerHTML, ` + + +

Doctor Oz

+ `); + + people.name.first = 'Frank'; + component.people = people; + + assert.htmlEqual(target.innerHTML, ` + + +

Frank Oz

+ `); + }, +}; diff --git a/test/runtime/samples/with-block-destructured-object-binding/main.svelte b/test/runtime/samples/with-block-destructured-object-binding/main.svelte new file mode 100644 index 000000000000..152c28cbda77 --- /dev/null +++ b/test/runtime/samples/with-block-destructured-object-binding/main.svelte @@ -0,0 +1,9 @@ + + +{#with people as { name: { first: f, last: l } } } + + +

{f} {l}

+{/with} diff --git a/test/runtime/samples/with-block/_config.js b/test/runtime/samples/with-block/_config.js new file mode 100644 index 000000000000..827d1af9b38a --- /dev/null +++ b/test/runtime/samples/with-block/_config.js @@ -0,0 +1,16 @@ +export default { + html: ` + + `, + + async test({ assert, target, window, }) { + const btn = target.querySelector('button'); + const clickEvent = new window.MouseEvent('click'); + + await btn.dispatchEvent(clickEvent); + + assert.htmlEqual(target.innerHTML, ` + + `); + } +}; diff --git a/test/runtime/samples/with-block/main.svelte b/test/runtime/samples/with-block/main.svelte new file mode 100644 index 000000000000..c9dc4249fb2f --- /dev/null +++ b/test/runtime/samples/with-block/main.svelte @@ -0,0 +1,7 @@ + + +{#with a as b } + +{/with} From f2c2ca0a2a1126168310e7b5c4b284e4977588b5 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Thu, 26 Mar 2020 02:39:58 -0700 Subject: [PATCH 2/4] Fix small error --- src/compiler/compile/render_dom/wrappers/Element/Binding.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/compile/render_dom/wrappers/Element/Binding.ts b/src/compiler/compile/render_dom/wrappers/Element/Binding.ts index 68eee55b600c..f3aafe6761c2 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/Binding.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/Binding.ts @@ -282,7 +282,7 @@ function get_event_handler( if (lhs.type === 'Identifier') { contextual_dependencies.add(object.name); if (property === undefined) { - lhs = modifier(b`${object}`[0]); + lhs = modifier(object); } else { lhs = modifier(x`${object}[${property}]`); contextual_dependencies.add(property.name); From 287a7c4abdb53884d4b16c9c2a978fe4971df86e Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Thu, 30 Apr 2020 00:11:08 -0700 Subject: [PATCH 3/4] Don't parse index and key for with block --- src/compiler/parse/state/mustache.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler/parse/state/mustache.ts b/src/compiler/parse/state/mustache.ts index a9dea41d0329..5d9b13385287 100644 --- a/src/compiler/parse/state/mustache.ts +++ b/src/compiler/parse/state/mustache.ts @@ -284,7 +284,9 @@ export default function mustache(parser: Parser) { block.context = read_context(parser); parser.allow_whitespace(); + } + if (type == 'EachBlock') { if (parser.eat(',')) { parser.allow_whitespace(); block.index = parser.read_identifier(); From 4a91e79907965bc58db23e945597713ac8d33b25 Mon Sep 17 00:00:00 2001 From: Timothy Johnson Date: Sun, 17 May 2020 04:34:20 -0700 Subject: [PATCH 4/4] Update to new dry destructure --- src/compiler/compile/nodes/WithBlock.ts | 52 +------------------ .../compile/render_dom/wrappers/WithBlock.ts | 2 +- 2 files changed, 3 insertions(+), 51 deletions(-) diff --git a/src/compiler/compile/nodes/WithBlock.ts b/src/compiler/compile/nodes/WithBlock.ts index 0d1a32cfc80a..45b700f797a8 100644 --- a/src/compiler/compile/nodes/WithBlock.ts +++ b/src/compiler/compile/nodes/WithBlock.ts @@ -2,56 +2,8 @@ import Expression from './shared/Expression'; import map_children from './shared/map_children'; import TemplateScope from './shared/TemplateScope'; import AbstractBlock from './shared/AbstractBlock'; -import { x } from 'code-red'; -import { Node, Identifier, RestElement } from 'estree'; - -interface Context { - key: Identifier; - name?: string; - modifier: (node: Node) => Node; -} - -function unpack_destructuring(contexts: Context[], node: Node, modifier: (node: Node) => Node) { - if (!node) return; - - if (node.type === 'Identifier' || (node as any).type === 'RestIdentifier') { // TODO is this right? not RestElement? - contexts.push({ - key: node as Identifier, - modifier - }); - } else if (node.type === 'ArrayPattern') { - node.elements.forEach((element, i) => { - if (element && (element as any).type === 'RestIdentifier') { - unpack_destructuring(contexts, element, node => x`${modifier(node)}.slice(${i})` as Node); - } else { - unpack_destructuring(contexts, element, node => x`${modifier(node)}[${i}]` as Node); - } - }); - } else if (node.type === 'ObjectPattern') { - const used_properties = []; - - node.properties.forEach((property, i) => { - if ((property as any).kind === 'rest') { // TODO is this right? - const replacement: RestElement = { - type: 'RestElement', - argument: property.key as Identifier - }; - - node.properties[i] = replacement as any; - - unpack_destructuring( - contexts, - property.value, - node => x`@object_without_properties(${modifier(node)}, [${used_properties}])` as Node - ); - } else { - used_properties.push(x`"${(property.key as Identifier).name}"`); - - unpack_destructuring(contexts, property.value, node => x`${modifier(node)}.${(property.key as Identifier).name}` as Node); - } - }); - } -} +import { Context, unpack_destructuring } from './shared/Context'; +import { Node } from 'estree'; export default class WithBlock extends AbstractBlock { type: 'WithBlock'; diff --git a/src/compiler/compile/render_dom/wrappers/WithBlock.ts b/src/compiler/compile/render_dom/wrappers/WithBlock.ts index 3a15628cf811..be2a6253ab3e 100644 --- a/src/compiler/compile/render_dom/wrappers/WithBlock.ts +++ b/src/compiler/compile/render_dom/wrappers/WithBlock.ts @@ -111,7 +111,7 @@ export default class WithBlockWrapper extends Wrapper { } `); - const initial_anchor_node: Identifier = { type: 'Identifier', name: parent_node ? 'null' : 'anchor' }; + const initial_anchor_node: Identifier = { type: 'Identifier', name: parent_node ? 'null' : '#anchor' }; const initial_mount_node: Identifier = parent_node || { type: 'Identifier', name: '#target' }; const update_anchor_node = needs_anchor ? block.get_unique_name(`${this.var.name}_anchor`)