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

Issue:
Reactive declarations are emitted twice
If reactive declarations [aren't written in topological order](https://v3.svelte.technology/repl?version=3.0.0-beta.1&gist=e4ac1a9530cfdfd53016c46191b342c7)...

```html
<script>
	export let x;
	
	let a;
	let b;
	
	$: a = b;
	$: b = x;
</script>
```

...they get duplicated:

```js
$$self.$$.update = ($$dirty = { b: 1, x: 1, a: 1 }) => {
	if ($$dirty.b || $$dirty.x) {
		b = x; $$invalidate('b', b);
	}
	if ($$dirty.a || $$dirty.b) {
		a = b; $$invalidate('a', a);
	}
	if ($$dirty.b || $$dirty.x) {
		b = x; $$invalidate('b', b);
	}
};
```


Issue discussion:

Solution patch:
diff --git a/src/compile/Component.ts b/src/compile/Component.ts
--- a/src/compile/Component.ts
+++ b/src/compile/Component.ts
@@ -1056,6 +1056,10 @@ export default class Component {
 				});
 			}
 
+			if (this.reactive_declarations.indexOf(declaration) !== -1) {
+				return;
+			}
+
 			seen.add(declaration);
 
 			if (declaration.dependencies.size === 0) {
@@ -1069,9 +1073,7 @@ export default class Component {
 				if (declaration.assignees.has(name)) return;
 				const earlier_declarations = lookup.get(name);
 				if (earlier_declarations) earlier_declarations.forEach(declaration => {
-					if (this.reactive_declarations.indexOf(declaration) === -1) {
-						add_declaration(declaration);
-					}
+					add_declaration(declaration);
 				});
 			});
 


Test patch:
diff --git a/test/js/samples/reactive-values-non-topologically-ordered/expected.js b/test/js/samples/reactive-values-non-topologically-ordered/expected.js
new file mode 100644
--- /dev/null
+++ b/test/js/samples/reactive-values-non-topologically-ordered/expected.js
@@ -0,0 +1,53 @@
+/* generated by Svelte vX.Y.Z */
+import { SvelteComponent as SvelteComponent_1, flush, 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
+	};
+}
+
+function instance($$self, $$props, $$invalidate) {
+	let { x } = $$props;
+
+	let a;
+	let b;
+
+	$$self.$set = $$props => {
+		if ('x' in $$props) $$invalidate('x', x = $$props.x);
+	};
+
+	$$self.$$.update = ($$dirty = { b: 1, x: 1, a: 1 }) => {
+		if ($$dirty.b || $$dirty.x) {
+			b = x; $$invalidate('b', b);
+		}
+		if ($$dirty.a || $$dirty.b) {
+			a = b; $$invalidate('a', a);
+		}
+	};
+
+	return { x };
+}
+
+class SvelteComponent extends SvelteComponent_1 {
+	constructor(options) {
+		super();
+		init(this, options, instance, create_fragment, safe_not_equal);
+	}
+
+	get x() {
+		return this.$$.ctx.x;
+	}
+
+	set x(x) {
+		this.$set({ x });
+		flush();
+	}
+}
+
+export default SvelteComponent;
\ No newline at end of file
diff --git a/test/js/samples/reactive-values-non-topologically-ordered/input.html b/test/js/samples/reactive-values-non-topologically-ordered/input.html
new file mode 100644
--- /dev/null
+++ b/test/js/samples/reactive-values-non-topologically-ordered/input.html
@@ -0,0 +1,9 @@
+<script>
+	export let x;
+
+	let a;
+	let b;
+
+	$: a = b;
+	$: b = x;
+</script>
\ No newline at end of file