From 866654d08f1c089c0c9876f3504257c22202b90e Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Fri, 24 Jul 2020 23:49:29 +0800 Subject: [PATCH] fix bind:group contextual reference --- src/compiler/compile/nodes/Binding.ts | 5 +- .../compile/nodes/shared/Expression.ts | 5 +- .../shared/mark_each_block_bindings.ts | 10 ++- .../_config.js | 1 + .../binding-input-group-each-6/_config.js | 87 +++++++++++++++++++ .../binding-input-group-each-6/main.svelte | 22 +++++ 6 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 test/runtime/samples/binding-input-group-each-6/_config.js create mode 100644 test/runtime/samples/binding-input-group-each-6/main.svelte diff --git a/src/compiler/compile/nodes/Binding.ts b/src/compiler/compile/nodes/Binding.ts index bcb07280b0..c561e72b99 100644 --- a/src/compiler/compile/nodes/Binding.ts +++ b/src/compiler/compile/nodes/Binding.ts @@ -41,7 +41,8 @@ export default class Binding extends Node { this.raw_expression = JSON.parse(JSON.stringify(info.expression)); const { name } = get_object(this.expression.node); - this.is_contextual = scope.names.has(name); + + this.is_contextual = Array.from(this.expression.references).some(name => scope.names.has(name)); // make sure we track this as a mutable ref if (scope.is_let(name)) { @@ -49,7 +50,7 @@ export default class Binding extends Node { code: 'invalid-binding', message: 'Cannot bind to a variable declared with the let: directive' }); - } else if (this.is_contextual) { + } else if (scope.names.has(name)) { if (scope.is_await(name)) { component.error(this, { code: 'invalid-binding', diff --git a/src/compiler/compile/nodes/shared/Expression.ts b/src/compiler/compile/nodes/shared/Expression.ts index 8bfc2bffce..bbbc1b2f2d 100644 --- a/src/compiler/compile/nodes/shared/Expression.ts +++ b/src/compiler/compile/nodes/shared/Expression.ts @@ -24,7 +24,7 @@ export default class Expression { component: Component; owner: Owner; node: any; - references: Set; + references: Set = new Set(); dependencies: Set = new Set(); contextual_dependencies: Set = new Set(); @@ -50,7 +50,7 @@ export default class Expression { this.template_scope = template_scope; this.owner = owner; - const { dependencies, contextual_dependencies } = this; + const { dependencies, contextual_dependencies, references } = this; let { map, scope } = create_scopes(info); this.scope = scope; @@ -75,6 +75,7 @@ export default class Expression { if (is_reference(node, parent)) { const { name, nodes } = flatten_reference(node); + references.add(name); if (scope.has(name)) return; diff --git a/src/compiler/compile/render_dom/wrappers/shared/mark_each_block_bindings.ts b/src/compiler/compile/render_dom/wrappers/shared/mark_each_block_bindings.ts index 490d52bd9d..884d4b88b8 100644 --- a/src/compiler/compile/render_dom/wrappers/shared/mark_each_block_bindings.ts +++ b/src/compiler/compile/render_dom/wrappers/shared/mark_each_block_bindings.ts @@ -2,7 +2,6 @@ import EachBlock from "../../../nodes/EachBlock"; import InlineComponentWrapper from "../InlineComponent"; import ElementWrapper from "../Element"; import Binding from "../../../nodes/Binding"; -import get_object from "../../../utils/get_object"; export default function mark_each_block_bindings( parent: ElementWrapper | InlineComponentWrapper, @@ -10,9 +9,12 @@ export default function mark_each_block_bindings( ) { // we need to ensure that the each block creates a context including // the list and the index, if they're not otherwise referenced - const object = get_object(binding.expression.node).name; - const each_block = parent.node.scope.get_owner(object); - (each_block as EachBlock).has_binding = true; + binding.expression.references.forEach(name => { + const each_block = parent.node.scope.get_owner(name); + if (each_block) { + (each_block as EachBlock).has_binding = true; + } + }); if (binding.name === "group") { // for ``, we make sure that all the each blocks creates context with `index` diff --git a/test/runtime/samples/binding-input-checkbox-deep-contextual/_config.js b/test/runtime/samples/binding-input-checkbox-deep-contextual/_config.js index 959b17777b..09b0807c8f 100644 --- a/test/runtime/samples/binding-input-checkbox-deep-contextual/_config.js +++ b/test/runtime/samples/binding-input-checkbox-deep-contextual/_config.js @@ -43,6 +43,7 @@ export default { const event = new window.Event('change'); inputs[1].checked = true; + console.log('@@', event, inputs[1]); await inputs[1].dispatchEvent(event); assert.equal(component.numCompleted, 2); diff --git a/test/runtime/samples/binding-input-group-each-6/_config.js b/test/runtime/samples/binding-input-group-each-6/_config.js new file mode 100644 index 0000000000..9eb251bf5d --- /dev/null +++ b/test/runtime/samples/binding-input-group-each-6/_config.js @@ -0,0 +1,87 @@ +export default { + html: ` + + + +

+ + + +

+ + + +

+ `, + + async test({ assert, component, target, window }) { + const inputs = target.querySelectorAll('input'); + assert.equal(inputs[0].checked, false); + assert.equal(inputs[1].checked, false); + assert.equal(inputs[2].checked, false); + + assert.equal(inputs[3].checked, false); + assert.equal(inputs[4].checked, false); + assert.equal(inputs[5].checked, false); + + assert.equal(inputs[6].checked, false); + assert.equal(inputs[7].checked, false); + assert.equal(inputs[8].checked, false); + + const event = new window.Event('change'); + + inputs[2].checked = true; + await inputs[2].dispatchEvent(event); + + assert.htmlEqual(target.innerHTML, ` + + + +

z

+ + + +

+ + + +

+ `); + + inputs[4].checked = true; + await inputs[4].dispatchEvent(event); + + assert.htmlEqual(target.innerHTML, ` + + + +

z

+ + + +

y

+ + + +

+ `); + + inputs[5].checked = true; + await inputs[5].dispatchEvent(event); + + assert.htmlEqual(target.innerHTML, ` + + + +

z

+ + + +

y, z

+ + + +

+ `); + } +}; diff --git a/test/runtime/samples/binding-input-group-each-6/main.svelte b/test/runtime/samples/binding-input-group-each-6/main.svelte new file mode 100644 index 0000000000..85be939e8a --- /dev/null +++ b/test/runtime/samples/binding-input-group-each-6/main.svelte @@ -0,0 +1,22 @@ + + +{#each Object.keys(list) as key} + {#each values as value} + + {/each} +

{list[key].join(', ')}

+{/each}