Repository: sveltejs/svelte (JavaScript)
Date: 2019-03-15 22:41:02+00:00
Task type: Bug Fix

Issue:
Dependency-less reactive declarations generate invalid code
Compiling

```html
<script>
	$: console.log('foo');
</script>
```

generates code containing

```javascript
		if () {
			console.log('foo');
		}
```

instead of throwing at compile time, which is what I think used to happen.


Issue discussion:
Okay I see a little more what's going on. There _is_ a currently working "Invalid reactive declaration — must depend on local state" compiler error, but the global `console` is being seen as a dependency of the reactive declaration, which it should not.
A potential wrinkle - Consider:

```html
<script>
	let c;
	$: a = b;
	$: b = c;
</script>
```

I haven't checked, but I'm worried there's not currently a way to know that the second reactive declaration is implicitly declaring `b` at the time that we're looking at the first one and need to know whether `b` is a global or not (which will affect whether it's a "real" dependency).
It looks like we already have more info about the vars than I thought by the time we are processing reactive statements. This change seems to be good:

```diff
diff --git a/src/compile/Component.ts b/src/compile/Component.ts
index 9dbfb3b4..b02a836a 100644
--- a/src/compile/Component.ts
+++ b/src/compile/Component.ts
@@ -1034,7 +1034,7 @@ export default class Component {
                                                        if (!assignee_nodes.has(identifier)) {
                                                                const { name } = identifier;
                                                                const owner = scope.findOwner(name);
-                                                               if ((!owner || owner === component.instance_scope) && (name[0] === '$' || component.var_lookup.has(name))) {
+                                                               if ((!owner || owner === component.instance_scope) && (name[0] === '$' || component.var_lookup.has(name) && component.var_lookup.get(name).writable)) {
                                                                        dependencies.add(name);
                                                                }
                                                        }
```

I'll poke around a little more and then hopefully submit a PR with this along with a new test.

Solution patch:
diff --git a/src/compile/Component.ts b/src/compile/Component.ts
--- a/src/compile/Component.ts
+++ b/src/compile/Component.ts
@@ -1034,7 +1034,10 @@ export default class Component {
 							if (!assignee_nodes.has(identifier)) {
 								const { name } = identifier;
 								const owner = scope.findOwner(name);
-								if ((!owner || owner === component.instance_scope) && (name[0] === '$' || component.var_lookup.has(name))) {
+								if (
+									(!owner || owner === component.instance_scope) &&
+									(name[0] === '$' || component.var_lookup.has(name) && component.var_lookup.get(name).writable)
+								) {
 									dependencies.add(name);
 								}
 							}


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
--- a/test/js/samples/reactive-values-non-writable-dependencies/expected.js
+++ b/test/js/samples/reactive-values-non-writable-dependencies/expected.js
@@ -17,11 +17,11 @@ let a = 1;
 let b = 2;
 
 function instance($$self, $$props, $$invalidate) {
-	
+
 
 	let max;
 
-	$$self.$$.update = ($$dirty = { Math: 1, a: 1, b: 1 }) => {
+	$$self.$$.update = ($$dirty = { a: 1, b: 1 }) => {
 		if ($$dirty.a || $$dirty.b) {
 			max = Math.max(a, b); $$invalidate('max', max);
 		}
diff --git a/test/validator/samples/reactive-declaration-no-deps/errors.json b/test/validator/samples/reactive-declaration-no-deps/errors.json
new file mode 100644
--- /dev/null
+++ b/test/validator/samples/reactive-declaration-no-deps/errors.json
@@ -0,0 +1,7 @@
+[{
+	"message": "Invalid reactive declaration — must depend on local state",
+	"code": "invalid-reactive-declaration",
+	"start": { "line": 2, "column": 1, "character": 10 },
+	"end": { "line": 2, "column": 23, "character": 32 },
+	"pos": 10
+}]
diff --git a/test/validator/samples/reactive-declaration-no-deps/input.svelte b/test/validator/samples/reactive-declaration-no-deps/input.svelte
new file mode 100644
--- /dev/null
+++ b/test/validator/samples/reactive-declaration-no-deps/input.svelte
@@ -0,0 +1,3 @@
+<script>
+	$: console.log('foo');
+</script>