Repository: sveltejs/svelte (JavaScript)
Date: 2022-04-08 17:38:00+00:00
Task type: Bug Fix

Issue:
@const declaration inside components ignored
### Describe the bug

The docs state that

> `{@const}` is only allowed as direct child of `{#each}`, `{:then}`, `{:catch}`,**` <Component />`** or` <svelte:fragment />`.

but currently if you use it inside a component slot the compiler fails to emit the assignment and the code throws at runtime.

```svelte
<script>
	import Heading from './Heading.svelte';
</script>
<Heading>
	{@const heading = "Hello world"}
	{heading}
</Heading>
```

If this is not the intended usage of `@const` (I hope it is though) the compiler should produce an error, otherwise it should emit the assignment.

### Reproduction

https://svelte.dev/repl/d01b574992e04be294ba081cddbf6859?version=3.46.2

### Logs

_No response_

### System Info

```shell
System:
    OS: macOS 12.1
    CPU: (8) arm64 Apple M1
    Memory: 108.55 MB / 8.00 GB
    Shell: 5.8 - /bin/zsh
  Binaries:
    Node: 17.3.0 - /opt/homebrew/bin/node
    npm: 8.3.0 - /opt/homebrew/bin/npm
  Browsers:
    Chrome: 97.0.4692.99
    Safari: 15.2
    Safari Technology Preview: 15.4
  npmPackages:
    rollup: ^2.3.4 => 2.66.1
    svelte: ^3.0.0 => 3.46.2
```


### Severity

annoyance


Issue discussion:
Looking at the tests for this aspect of this feature, I suspect it was intended only to work with `let:` variables from the component. If that's so, the docs should be made a bit clearer and the compiler should throw an exception rather than ignoring the `{@const}`.

Solution patch:
diff --git a/src/compiler/compile/nodes/SlotTemplate.ts b/src/compiler/compile/nodes/SlotTemplate.ts
--- a/src/compiler/compile/nodes/SlotTemplate.ts
+++ b/src/compiler/compile/nodes/SlotTemplate.ts
@@ -27,10 +27,7 @@ export default class SlotTemplate extends Node {
 
 		this.validate_slot_template_placement();
 
-		const has_let = info.attributes.some((node) => node.type === 'Let');
-		if (has_let) {
-			scope = scope.child();
-		}
+		scope = scope.child();
 
 		info.attributes.forEach((node) => {
 			switch (node.type) {


Test patch:
diff --git a/test/runtime/samples/const-tag-component-without-let/Component.svelte b/test/runtime/samples/const-tag-component-without-let/Component.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/const-tag-component-without-let/Component.svelte
@@ -0,0 +1,2 @@
+<slot name="box1" />
+<slot />
diff --git a/test/runtime/samples/const-tag-component-without-let/_config.js b/test/runtime/samples/const-tag-component-without-let/_config.js
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/const-tag-component-without-let/_config.js
@@ -0,0 +1,15 @@
+export default {
+	html: `
+		<div>static dynamic</div>
+		<div>static dynamic</div>
+		<div>static dynamic</div>
+	`,
+	async test({ component, target, assert }) {
+		component.props = 'xxx';
+		assert.htmlEqual(target.innerHTML, `
+			<div>static xxx</div>
+			<div>static xxx</div>
+			<div>static xxx</div>
+		`);
+	}
+};
diff --git a/test/runtime/samples/const-tag-component-without-let/main.svelte b/test/runtime/samples/const-tag-component-without-let/main.svelte
new file mode 100644
--- /dev/null
+++ b/test/runtime/samples/const-tag-component-without-let/main.svelte
@@ -0,0 +1,24 @@
+<script>
+	import Component from './Component.svelte';
+	export let props = "dynamic";
+</script>
+
+<Component>
+	<svelte:fragment slot="box1">
+		{@const foo = "static"}
+		{@const bar = props}
+		<div>{foo} {bar}</div>
+	</svelte:fragment>
+
+	<svelte:fragment>
+		{@const foo = "static"}
+		{@const bar = props}
+		<div>{foo} {bar}</div>
+	</svelte:fragment>
+</Component>
+
+<Component>
+	{@const foo = "static"}
+	{@const bar = props}
+	<div>{foo} {bar}</div>
+</Component>