From 6f3d7beaae00910eec831127b76fd848f94135b0 Mon Sep 17 00:00:00 2001
From: Tan Li Hau <lhtan93@gmail.com>
Date: Fri, 24 Apr 2020 08:13:25 +0800
Subject: [PATCH 1/4] fix mutation to imported variable

---
 src/compiler/compile/Component.ts             | 10 ++++-
 .../reactive-import-statement-2/_config.js    |  5 +++
 .../reactive-import-statement-2/data.js       |  3 ++
 .../reactive-import-statement-2/main.svelte   |  8 ++++
 .../reactive-import-statement/_config.js      | 38 +++++++++++++++++++
 .../samples/reactive-import-statement/data.js |  1 +
 .../reactive-import-statement/main.svelte     | 19 ++++++++++
 7 files changed, 83 insertions(+), 1 deletion(-)
 create mode 100644 test/runtime/samples/reactive-import-statement-2/_config.js
 create mode 100644 test/runtime/samples/reactive-import-statement-2/data.js
 create mode 100644 test/runtime/samples/reactive-import-statement-2/main.svelte
 create mode 100644 test/runtime/samples/reactive-import-statement/_config.js
 create mode 100644 test/runtime/samples/reactive-import-statement/data.js
 create mode 100644 test/runtime/samples/reactive-import-statement/main.svelte

diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts
index bb1546ceee9c..c83f8153a01e 100644
--- a/src/compiler/compile/Component.ts
+++ b/src/compiler/compile/Component.ts
@@ -632,7 +632,6 @@ export default class Component {
 			this.add_var({
 				name,
 				initialised: instance_scope.initialised_declarations.has(name),
-				hoistable: /^Import/.test(node.type),
 				writable
 			});
 
@@ -986,6 +985,7 @@ export default class Component {
 			hoistable_nodes,
 			var_lookup,
 			injected_reactive_declaration_vars,
+			imports,
 		} = this;
 
 		const top_level_function_declarations = new Map();
@@ -1137,6 +1137,14 @@ export default class Component {
 				this.fully_hoisted.push(node);
 			}
 		}
+
+		for (const { specifiers } of imports) {
+			for (const specifier of specifiers) {
+				const variable = var_lookup.get(specifier.local.name);
+
+				if (!variable.mutated) variable.hoistable = true;
+			}
+		}
 	}
 
 	extract_reactive_declarations() {
diff --git a/test/runtime/samples/reactive-import-statement-2/_config.js b/test/runtime/samples/reactive-import-statement-2/_config.js
new file mode 100644
index 000000000000..03b5b97e6e2e
--- /dev/null
+++ b/test/runtime/samples/reactive-import-statement-2/_config.js
@@ -0,0 +1,5 @@
+export default {
+	html: `
+		Im a prop
+	`,
+};
diff --git a/test/runtime/samples/reactive-import-statement-2/data.js b/test/runtime/samples/reactive-import-statement-2/data.js
new file mode 100644
index 000000000000..28c7c656026f
--- /dev/null
+++ b/test/runtime/samples/reactive-import-statement-2/data.js
@@ -0,0 +1,3 @@
+export const obj = {
+	prop: 'Im a prop'
+};
\ No newline at end of file
diff --git a/test/runtime/samples/reactive-import-statement-2/main.svelte b/test/runtime/samples/reactive-import-statement-2/main.svelte
new file mode 100644
index 000000000000..4f6f0abf6fa7
--- /dev/null
+++ b/test/runtime/samples/reactive-import-statement-2/main.svelte
@@ -0,0 +1,8 @@
+<script>
+  import { obj } from './data.js'
+	
+	$: prop = obj.prop
+  obj.foo = 'uncomment this line to break the example'
+</script>
+
+{prop}
diff --git a/test/runtime/samples/reactive-import-statement/_config.js b/test/runtime/samples/reactive-import-statement/_config.js
new file mode 100644
index 000000000000..7fb8097ca58a
--- /dev/null
+++ b/test/runtime/samples/reactive-import-statement/_config.js
@@ -0,0 +1,38 @@
+import * as path from 'path';
+
+export default {
+	html: `
+		import
+		<p>1 + 2 + 3 + 4 = 10</p>
+		local
+		<p>1 + 2 + 3 + 4 = 10</p>
+		<button>Add a number</button>
+	`,
+	before_test() {
+		delete require.cache[path.resolve(__dirname, 'data.js')];
+	},
+	async test({ assert, target, window, }) {
+		const btn = target.querySelector('button');
+		const clickEvent = new window.MouseEvent('click');
+
+		await btn.dispatchEvent(clickEvent);
+		
+		assert.htmlEqual(target.innerHTML, `
+			import
+			<p>1 + 2 + 3 + 4 + 5 = 15</p>
+			local
+			<p>1 + 2 + 3 + 4 + 5 = 15</p>
+			<button>Add a number</button>
+		`);
+
+		await btn.dispatchEvent(clickEvent);
+		
+		assert.htmlEqual(target.innerHTML, `
+			import
+			<p>1 + 2 + 3 + 4 + 5 + 6 = 21</p>
+			local
+			<p>1 + 2 + 3 + 4 + 5 + 6 = 21</p>
+			<button>Add a number</button>
+		`);
+	}
+};
diff --git a/test/runtime/samples/reactive-import-statement/data.js b/test/runtime/samples/reactive-import-statement/data.js
new file mode 100644
index 000000000000..ad494c7cb04b
--- /dev/null
+++ b/test/runtime/samples/reactive-import-statement/data.js
@@ -0,0 +1 @@
+export const numbers = [1, 2, 3, 4];
\ No newline at end of file
diff --git a/test/runtime/samples/reactive-import-statement/main.svelte b/test/runtime/samples/reactive-import-statement/main.svelte
new file mode 100644
index 000000000000..0ee627002447
--- /dev/null
+++ b/test/runtime/samples/reactive-import-statement/main.svelte
@@ -0,0 +1,19 @@
+<script>
+	import {numbers, foo} from './data.js'
+	const local_numbers = [1, 2, 3, 4];
+
+	function addNumber() {
+		numbers[numbers.length] = numbers.length + 1;
+		local_numbers[local_numbers.length] = local_numbers.length + 1;
+	}
+
+	$: sum = numbers.reduce((t, n) => t + n, 0);
+	$: local_sum = local_numbers.reduce((t, n) => t + n, 0);
+</script>
+
+import <p>{numbers.join(' + ')} = {sum}</p>
+local <p>{local_numbers.join(' + ')} = {local_sum}</p>
+
+<button on:click={addNumber}>
+	Add a number
+</button>
\ No newline at end of file

From f7b63203105dd52f1a97e4bcdf952606c73f19c8 Mon Sep 17 00:00:00 2001
From: Conduitry <git@chor.date>
Date: Fri, 24 Apr 2020 12:56:27 -0400
Subject: [PATCH 2/4] adjust test

---
 .../samples/reactive-import-statement-2/_config.js     |  4 +---
 .../samples/reactive-import-statement-2/data.js        |  4 ++--
 .../samples/reactive-import-statement-2/main.svelte    | 10 +++++-----
 3 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/test/runtime/samples/reactive-import-statement-2/_config.js b/test/runtime/samples/reactive-import-statement-2/_config.js
index 03b5b97e6e2e..ccc97cd076b2 100644
--- a/test/runtime/samples/reactive-import-statement-2/_config.js
+++ b/test/runtime/samples/reactive-import-statement-2/_config.js
@@ -1,5 +1,3 @@
 export default {
-	html: `
-		Im a prop
-	`,
+	html: `<p>prop value</p>`
 };
diff --git a/test/runtime/samples/reactive-import-statement-2/data.js b/test/runtime/samples/reactive-import-statement-2/data.js
index 28c7c656026f..56fb86982c77 100644
--- a/test/runtime/samples/reactive-import-statement-2/data.js
+++ b/test/runtime/samples/reactive-import-statement-2/data.js
@@ -1,3 +1,3 @@
 export const obj = {
-	prop: 'Im a prop'
-};
\ No newline at end of file
+	prop: 'prop value'
+};
diff --git a/test/runtime/samples/reactive-import-statement-2/main.svelte b/test/runtime/samples/reactive-import-statement-2/main.svelte
index 4f6f0abf6fa7..2582974b71b3 100644
--- a/test/runtime/samples/reactive-import-statement-2/main.svelte
+++ b/test/runtime/samples/reactive-import-statement-2/main.svelte
@@ -1,8 +1,8 @@
 <script>
-  import { obj } from './data.js'
-	
-	$: prop = obj.prop
-  obj.foo = 'uncomment this line to break the example'
+	import { obj } from './data.js';
+
+	$: prop = obj.prop;
+	obj.foo = 'a different prop';
 </script>
 
-{prop}
+<p>{prop}</p>

From 46cc8180996b79614accd0718e2368868fe9c7ec Mon Sep 17 00:00:00 2001
From: Conduitry <git@chor.date>
Date: Fri, 24 Apr 2020 12:58:04 -0400
Subject: [PATCH 3/4] update changelog

---
 CHANGELOG.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index f9b3bb8b9a2b..3fceb5f2fc16 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Svelte changelog
 
+## Unreleased
+
+* Fix reactivity with imported values that are then mutated ([#4555](https://github.com/sveltejs/svelte/issues/4555))
+
 ## 3.21.0
 
 * Support dimension bindings in cross-origin environments ([#2147](https://github.com/sveltejs/svelte/issues/2147))

From bfc91c9b00b8632b5b68200627b47f758a6d6ca4 Mon Sep 17 00:00:00 2001
From: Conduitry <git@chor.date>
Date: Fri, 24 Apr 2020 13:01:51 -0400
Subject: [PATCH 4/4] lint other test

---
 test/runtime/samples/reactive-import-statement/main.svelte | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/runtime/samples/reactive-import-statement/main.svelte b/test/runtime/samples/reactive-import-statement/main.svelte
index 0ee627002447..daded0494f4b 100644
--- a/test/runtime/samples/reactive-import-statement/main.svelte
+++ b/test/runtime/samples/reactive-import-statement/main.svelte
@@ -1,5 +1,5 @@
 <script>
-	import {numbers, foo} from './data.js'
+	import { numbers } from './data.js';
 	const local_numbers = [1, 2, 3, 4];
 
 	function addNumber() {