Repository: sveltejs/svelte (JavaScript)
Date: 2018-05-30 16:04:34+00:00
Task type: Bug Fix

Issue:
Using {...spread} alongside ternary errors out
[REPL](https://svelte.technology/repl?version=2.7.0&gist=a189e5e7181ed608a837a6a614a4dd25)

If you have some attribute defined by a ternary operator and try to spread some object, this happens:

![image](https://user-images.githubusercontent.com/12702016/40729214-488fffe6-6402-11e8-91d2-708668ba8495.png)



Issue discussion:
It looks like this actually happens when any attribute in an element using spreads (or prop in a component using spreads) has two or more dependencies. It's an order of operations thing. In the code for this REPL example, we're generating `changed.visible || changed.value && { value: ctx.visible ? ctx.value : '****' }` when we ought to be generating `(changed.visible || changed.value) && { value: ctx.visible ? ctx.value : '****' }`. We need to make sure we wrap the conditions [here](https://github.com/sveltejs/svelte/blob/7032ec745cdf17f25c47659ebb989c88fe24a157/src/compile/nodes/Element.ts#L561) and [here](https://github.com/sveltejs/svelte/blob/7032ec745cdf17f25c47659ebb989c88fe24a157/src/compile/nodes/Component.ts#L178) in parentheses as appropriate. PR coming in a bit.

Solution patch:
diff --git a/src/compile/nodes/Component.ts b/src/compile/nodes/Component.ts
--- a/src/compile/nodes/Component.ts
+++ b/src/compile/nodes/Component.ts
@@ -176,7 +176,7 @@ export default class Component extends Node {
 					const { name, dependencies } = attr;
 
 					const condition = dependencies.size > 0 && (dependencies.size !== allDependencies.size)
-						? [...dependencies].map(d => `changed.${d}`).join(' || ')
+						? `(${[...dependencies].map(d => `changed.${d}`).join(' || ')})`
 						: null;
 
 					if (attr.isSpread) {
diff --git a/src/compile/nodes/Element.ts b/src/compile/nodes/Element.ts
--- a/src/compile/nodes/Element.ts
+++ b/src/compile/nodes/Element.ts
@@ -559,7 +559,7 @@ export default class Element extends Node {
 			.filter(attr => attr.type === 'Attribute' || attr.type === 'Spread')
 			.forEach(attr => {
 				const condition = attr.dependencies.size > 0
-					? [...attr.dependencies].map(d => `changed.${d}`).join(' || ')
+					? `(${[...attr.dependencies].map(d => `changed.${d}`).join(' || ')})`
 					: null;
 
 				if (attr.isSpread) {


Test patch:
diff --git a/test/runtime/samples/spread-component-multiple-dependencies/Widget.html b/test/runtime/samples/spread-component-multiple-dependencies/Widget.html
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/spread-component-multiple-dependencies/Widget.html
@@ -0,0 +1 @@
+{foo} {baz}
diff --git a/test/runtime/samples/spread-component-multiple-dependencies/_config.js b/test/runtime/samples/spread-component-multiple-dependencies/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/spread-component-multiple-dependencies/_config.js
@@ -0,0 +1,10 @@
+export default {
+	html: `b baz`,
+	test(assert, component, target) {
+		component.set({ foo: true });
+		assert.htmlEqual(
+			target.innerHTML,
+			`a baz`
+		);
+	},
+};
diff --git a/test/runtime/samples/spread-component-multiple-dependencies/main.html b/test/runtime/samples/spread-component-multiple-dependencies/main.html
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/spread-component-multiple-dependencies/main.html
@@ -0,0 +1,13 @@
+<Widget foo={foo ? a : b} {...bar}/>
+
+<script>
+	export default {
+		components: { Widget: './Widget.html' },
+		data: () => ({
+			foo: false,
+			a: 'a',
+			b: 'b',
+			bar: { baz: 'baz' },
+		}),
+	};
+</script>
diff --git a/test/runtime/samples/spread-element-multiple-dependencies/_config.js b/test/runtime/samples/spread-element-multiple-dependencies/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/spread-element-multiple-dependencies/_config.js
@@ -0,0 +1,10 @@
+export default {
+	html: `<div class='b' title='baz'></div>`,
+	test(assert, component, target) {
+		component.set({ foo: true });
+		assert.htmlEqual(
+			target.innerHTML,
+			`<div class='a' title='baz'></div>`
+		);
+	},
+};
diff --git a/test/runtime/samples/spread-element-multiple-dependencies/main.html b/test/runtime/samples/spread-element-multiple-dependencies/main.html
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/spread-element-multiple-dependencies/main.html
@@ -0,0 +1,12 @@
+<div class={foo ? a : b} {...bar}></div>
+
+<script>
+	export default {
+		data: () => ({
+			foo: false,
+			a: 'a',
+			b: 'b',
+			bar: { title: 'baz' },
+		}),
+	};
+</script>