Repository: sveltejs/svelte (JavaScript)
Date: 2019-02-07 16:44:22+00:00
Task type: Bug Fix

Issue:
Only account for reassigned/mutated vars in reactive dirty checks
In [this example](https://v3.svelte.technology/repl?version=3.0.0-beta.1&gist=8aaa43f0fe81a2f66ac76a38fc04fb66)...

```html
<script>
	let a = 1;
	let b = 2;
	
	let max;
	$: max = Math.max(a, b);
</script>

<label>
	<input type=range bind:value={a}> a = {a}
</label>

<label>
	<input type=range bind:value={b}> b = {b}
</label>
	
<p>max = {max}</p>
```

...Svelte generates this code:

```js
$$self.$$.update = ($$dirty = { max: 1, Math: 1, a: 1, b: 1 }) => {
	if ($$dirty.max || $$dirty.Math || $$dirty.a || $$dirty.b) {
		max = Math.max(a, b); $$invalidate('max', max);
	}
};
```

There's no need for `$$dirty.Math` — we can see it never changes.


Issue discussion:
Here are two similar cases: (introduced in alpha 21)

https://github.com/thgh/svelte-number-gauge/commit/e0d5a483cc106d75009b6802a809698b2e0ac65d#diff-0b3178d0b21a965441ff7323d2523a23R393
=> `bg` is a simple component function and will never change

https://github.com/thgh/svelte-number-gauge/commit/e0d5a483cc106d75009b6802a809698b2e0ac65d#diff-0b3178d0b21a965441ff7323d2523a23L455 
=> `String` and `parseInt` definitely won't change



Solution patch:
diff --git a/src/compile/render-dom/index.ts b/src/compile/render-dom/index.ts
--- a/src/compile/render-dom/index.ts
+++ b/src/compile/render-dom/index.ts
@@ -314,7 +314,13 @@ export default function dom(
 
 	if (has_definition) {
 		const reactive_declarations = component.reactive_declarations.map(d => {
-			const condition = Array.from(d.dependencies).map(n => `$$dirty.${n}`).join(' || ');
+			const condition = Array.from(d.dependencies)
+				.filter(n => {
+					const variable = component.var_lookup.get(n);
+					return variable && variable.writable;
+				})
+				.map(n => `$$dirty.${n}`).join(' || ');
+
 			const snippet = d.node.body.type === 'BlockStatement'
 				? `[✂${d.node.body.start}-${d.node.end}✂]`
 				: deindent`


Test patch:
diff --git a/test/js/samples/reactive-values-non-writable-dependencies/expected.js b/test/js/samples/reactive-values-non-writable-dependencies/expected.js
new file mode 100644
--- /dev/null
+++ b/test/js/samples/reactive-values-non-writable-dependencies/expected.js
@@ -0,0 +1,40 @@
+/* generated by Svelte vX.Y.Z */
+import { SvelteComponent as SvelteComponent_1, init, noop, safe_not_equal } from "svelte/internal";
+
+function create_fragment(ctx) {
+	return {
+		c: noop,
+		m: noop,
+		p: noop,
+		i: noop,
+		o: noop,
+		d: noop
+	};
+}
+
+let a = 1;
+
+let b = 2;
+
+function instance($$self, $$props, $$invalidate) {
+
+
+	let max;
+
+	$$self.$$.update = ($$dirty = { max: 1, Math: 1, a: 1, b: 1 }) => {
+		if ($$dirty.max || $$dirty.a || $$dirty.b) {
+			max = Math.max(a, b); $$invalidate('max', max);
+		}
+	};
+
+	return {};
+}
+
+class SvelteComponent extends SvelteComponent_1 {
+	constructor(options) {
+		super();
+		init(this, options, instance, create_fragment, safe_not_equal);
+	}
+}
+
+export default SvelteComponent;
\ No newline at end of file
diff --git a/test/js/samples/reactive-values-non-writable-dependencies/input.html b/test/js/samples/reactive-values-non-writable-dependencies/input.html
new file mode 100644
--- /dev/null
+++ b/test/js/samples/reactive-values-non-writable-dependencies/input.html
@@ -0,0 +1,7 @@
+<script>
+	let a = 1;
+	let b = 2;
+
+	let max;
+	$: max = Math.max(a, b);
+</script>
\ No newline at end of file