Repository: sveltejs/svelte (JavaScript)
Date: 2020-12-30 10:23:19+00:00
Task type: Bug Fix

Issue:
Cannot access 'variableName' before initialization...
In this REPL:
https://svelte.dev/repl/0cf61fcb843c47b7a9ba4bd6d6a891f7?version=3.31.0

this code:
```svelte
let someData = {
  Home: "HomeData",
  // ...
};
$: currentData = someData["NotHome"] || "DefaultData";

let myText = "Bugged ;("
function fetch() {
  if(currentData != "DefaultData") console.log(myText)
};
fetch();
```

compiles into this code, where `currentData` declared after `fetch()`:

```svelte
let someData = { Home: "HomeData" }; // ...
function fetch() {
  if (currentData != "DefaultData") console.log(myText);
}
fetch();
let currentData;
$: currentData = someData["NotHome"] || "DefaultData";
```

And leads to an error `Cannot access 'currentData' before initialization'


Issue discussion:
Here's another example:

```
	import store from "./store";
	let name = 'world';
	
	$: theValue = $store;
	
	let numbersSeen = {
		[theValue]: true
	}
```

REPL: https://svelte.dev/repl/1753fac08700444985259933d4b58518?version=3.31.0 

That code's obviously foolish and silly - the goal is to do something with that initial, original value of "theValue" which, in this circumstance, leads to a ReferenceError 

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
 
 ## Unreleased
 
+* Fix location of automatically declared reactive variables ([#5749](https://github.com/sveltejs/svelte/issues/5749))
 * Warn when using `className` or `htmlFor` attributes ([#5777](https://github.com/sveltejs/svelte/issues/5777))
 * Fix checkbox `bind:group` in nested `{#each}` contexts ([#5811](https://github.com/sveltejs/svelte/issues/5811))
 * Add graphics roles as known ARIA roles ([#5822](https://github.com/sveltejs/svelte/pull/5822))
diff --git a/src/compiler/compile/render_dom/index.ts b/src/compiler/compile/render_dom/index.ts
--- a/src/compiler/compile/render_dom/index.ts
+++ b/src/compiler/compile/render_dom/index.ts
@@ -414,6 +414,8 @@ export default function dom(
 
 		body.push(b`
 			function ${definition}(${args}) {
+				${injected.map(name => b`let ${name};`)}
+
 				${rest}
 
 				${reactive_store_declarations}
@@ -440,8 +442,6 @@ export default function dom(
 
 				${inject_state && b`$$self.$inject_state = ${inject_state};`}
 
-				${injected.map(name => b`let ${name};`)}
-
 				${/* before reactive declarations */ props_inject}
 
 				${reactive_declarations.length > 0 && b`
diff --git a/src/compiler/compile/render_ssr/index.ts b/src/compiler/compile/render_ssr/index.ts
--- a/src/compiler/compile/render_ssr/index.ts
+++ b/src/compiler/compile/render_ssr/index.ts
@@ -101,8 +101,6 @@ export default function ssr(
 
 				${reactive_store_values}
 
-				${injected.map(name => b`let ${name};`)}
-
 				${reactive_declarations}
 
 				$$rendered = ${literal};
@@ -113,13 +111,12 @@ export default function ssr(
 		: b`
 			${reactive_store_values}
 
-			${injected.map(name => b`let ${name};`)}
-
 			${reactive_declarations}
 
 			return ${literal};`;
 
 	const blocks = [
+		...injected.map(name => b`let ${name};`),
 		rest,
 		slots,
 		...reactive_stores.map(({ name }) => {


Test patch:
diff --git a/test/js/samples/capture-inject-state/expected.js b/test/js/samples/capture-inject-state/expected.js
--- a/test/js/samples/capture-inject-state/expected.js
+++ b/test/js/samples/capture-inject-state/expected.js
@@ -98,6 +98,8 @@ let shadowedByModule;
 const priv = "priv";
 
 function instance($$self, $$props, $$invalidate) {
+	let computed;
+
 	let $prop,
 		$$unsubscribe_prop = noop,
 		$$subscribe_prop = () => ($$unsubscribe_prop(), $$unsubscribe_prop = subscribe(prop, $$value => $$invalidate(2, $prop = $$value)), prop);
@@ -145,8 +147,6 @@ function instance($$self, $$props, $$invalidate) {
 		if ("computed" in $$props) computed = $$props.computed;
 	};
 
-	let computed;
-
 	if ($$props && "$$inject" in $$props) {
 		$$self.$inject_state($$props.$$inject);
 	}
diff --git a/test/js/samples/unreferenced-state-not-invalidated/expected.js b/test/js/samples/unreferenced-state-not-invalidated/expected.js
--- a/test/js/samples/unreferenced-state-not-invalidated/expected.js
+++ b/test/js/samples/unreferenced-state-not-invalidated/expected.js
@@ -39,6 +39,8 @@ function create_fragment(ctx) {
 }
 
 function instance($$self, $$props, $$invalidate) {
+	let x;
+	let y;
 	let a = 1, b = 2, c = 3;
 
 	onMount(() => {
@@ -54,9 +56,6 @@ function instance($$self, $$props, $$invalidate) {
 		return () => clearInterval(interval);
 	});
 
-	let x;
-	let y;
-
 	$$self.$$.update = () => {
 		if ($$self.$$.dirty & /*b*/ 2) {
 			$: $$invalidate(0, y = b * 2);
diff --git a/test/runtime/samples/reactive-values-uninitialised/_config.js b/test/runtime/samples/reactive-values-uninitialised/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/reactive-values-uninitialised/_config.js
@@ -0,0 +1,3 @@
+export default {
+	html: '<p>aca</p>'
+};
diff --git a/test/runtime/samples/reactive-values-uninitialised/main.svelte b/test/runtime/samples/reactive-values-uninitialised/main.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/reactive-values-uninitialised/main.svelte
@@ -0,0 +1,12 @@
+<script>
+	export let a = 'a';
+	let b;
+	$: c = a;
+
+	function foo() {
+		b = c === 'a' ? 'b' : 'c';
+	}
+	foo();
+</script>
+
+<p>{a}{b}{c}</p>