Repository: sveltejs/svelte (JavaScript)
Date: 2019-02-04 01:04:08+00:00
Task type: Bug Fix

Issue:
Can't use same global in instance and module scripts
```html
<script context="module">
  console.log(1);
</script>

<script>
  console.log(2);
</script>
```

I think this is a simple fix — just need to check that `console` hasn't already been declared with `add_var` before adding it a second time


Issue discussion:
Yeah it looks like this takes care of this specific issue.

```diff
diff --git a/src/compile/Component.ts b/src/compile/Component.ts
index c9304ee5..e8672477 100644
--- a/src/compile/Component.ts
+++ b/src/compile/Component.ts
@@ -538,7 +538,7 @@ export default class Component {
                globals.forEach(name => {
                        if (name[0] === '$') {
                                // TODO should this be possible?
-                       } else {
+                       } else if (!this.var_lookup.has(name)) {
                                this.add_var({
                                        name,
                                        global: true
@@ -604,7 +604,7 @@ export default class Component {
                                });

                                this.add_reference(name.slice(1));
-                       } else {
+                       } else if (!this.var_lookup.has(name)) {
                                this.add_var({
                                        name,
                                        global: true
```

But there are other situations where we probably do want to have a more helpful error. E.g., declaring a variable in the module script and again in the instance script, or using a global in the module script and then declaring the same one as a local in the instance script. I see the `throw` in `add_var` has a comment `// TODO remove this` but I don't think that's quite correct either. We can add this more careful error checking in each place where we call `add_var`, or we can send another argument to `add_var` everywhere we call it that's the AST node we should point to for dupe errors.
I think it's probably ok to have dupes...

```html
<script context="module">
  var foo = 1;
  console.log(foo);
</script>

<script>
  var foo = 2;
  console.log(foo);
</script>
```

...we might just need to track them separately (`component.instance_vars` and `component.module_vars`?) and prevent hoisting of instance vars that conflict with module vars.

Solution patch:
diff --git a/src/compile/Component.ts b/src/compile/Component.ts
--- a/src/compile/Component.ts
+++ b/src/compile/Component.ts
@@ -142,11 +142,6 @@ export default class Component {
 	}
 
 	add_var(variable: Var) {
-		// TODO remove this
-		if (this.var_lookup.has(variable.name)) {
-			throw new Error(`dupe: ${variable.name}`);
-		}
-
 		this.vars.push(variable);
 		this.var_lookup.set(variable.name, variable);
 	}
@@ -530,7 +525,8 @@ export default class Component {
 				this.add_var({
 					name,
 					module: true,
-					hoistable: true
+					hoistable: true,
+					writable: kind === 'var' || kind === 'let'
 				});
 			}
 		});
@@ -840,7 +836,16 @@ export default class Component {
 
 		this.ast.instance.content.body.forEach(node => {
 			if (node.type === 'VariableDeclaration') {
-				if (node.declarations.every(d => d.init && d.init.type === 'Literal' && !this.var_lookup.get(d.id.name).reassigned)) {
+				const all_hoistable = node.declarations.every(d => {
+					if (!d.init) return false;
+					if (d.init.type !== 'Literal') return false;
+					if (this.var_lookup.get(d.id.name).reassigned) return false;
+					if (this.vars.find(variable => variable.name === d.id.name && variable.module)) return false;
+
+					return true;
+				});
+
+				if (all_hoistable) {
 					node.declarations.forEach(d => {
 						const variable = this.var_lookup.get(d.id.name);
 						variable.hoistable = true;


Test patch:
diff --git a/test/stats/samples/duplicate-globals/_config.js b/test/stats/samples/duplicate-globals/_config.js
new file mode 100644
--- /dev/null
+++ b/test/stats/samples/duplicate-globals/_config.js
@@ -0,0 +1,5 @@
+export default {
+	test(assert, stats) {
+		assert.deepEqual(stats.vars, []);
+	},
+};
diff --git a/test/stats/samples/duplicate-globals/input.html b/test/stats/samples/duplicate-globals/input.html
new file mode 100644
--- /dev/null
+++ b/test/stats/samples/duplicate-globals/input.html
@@ -0,0 +1,7 @@
+<script context="module">
+	console.log(1);
+</script>
+
+<script>
+	console.log(2);
+</script>
\ No newline at end of file
diff --git a/test/stats/samples/duplicate-non-hoistable/_config.js b/test/stats/samples/duplicate-non-hoistable/_config.js
new file mode 100644
--- /dev/null
+++ b/test/stats/samples/duplicate-non-hoistable/_config.js
@@ -0,0 +1,16 @@
+export default {
+	test(assert, stats) {
+		assert.deepEqual(stats.vars, [
+			{
+				name: 'console',
+				injected: false,
+				export_name: null,
+				module: false,
+				mutated: false,
+				reassigned: false,
+				referenced: true,
+				writable: true
+			}
+		]);
+	},
+};
diff --git a/test/stats/samples/duplicate-non-hoistable/input.html b/test/stats/samples/duplicate-non-hoistable/input.html
new file mode 100644
--- /dev/null
+++ b/test/stats/samples/duplicate-non-hoistable/input.html
@@ -0,0 +1,9 @@
+<script context="module">
+	console.log(1);
+</script>
+
+<script>
+	var console = 42;
+</script>
+
+<p>{console}</p>
\ No newline at end of file
diff --git a/test/stats/samples/duplicate-vars/_config.js b/test/stats/samples/duplicate-vars/_config.js
new file mode 100644
--- /dev/null
+++ b/test/stats/samples/duplicate-vars/_config.js
@@ -0,0 +1,26 @@
+export default {
+	test(assert, stats) {
+		assert.deepEqual(stats.vars, [
+			{
+				name: 'foo',
+				injected: false,
+				export_name: null,
+				module: true,
+				mutated: false,
+				reassigned: false,
+				referenced: false,
+				writable: true
+			},
+			{
+				name: 'foo',
+				injected: false,
+				export_name: null,
+				module: false,
+				mutated: false,
+				reassigned: false,
+				referenced: true,
+				writable: true
+			}
+		]);
+	},
+};
diff --git a/test/stats/samples/duplicate-vars/input.html b/test/stats/samples/duplicate-vars/input.html
new file mode 100644
--- /dev/null
+++ b/test/stats/samples/duplicate-vars/input.html
@@ -0,0 +1,9 @@
+<script context="module">
+	var foo = 1;
+</script>
+
+<script>
+	var foo = 2;
+</script>
+
+<p>{foo}</p>
\ No newline at end of file