Repository: sveltejs/svelte (JavaScript)

Issue:
Improve error message for trying to export a snippet that references variables inside a non module script
### Describe the problem

When I saw the following error messages I did not quickly understand what caused the error

```
TS error (shown when hovering error in editor):
Cannot export 'snippet_name'. Only local declarations can be exported from a module.

vite-plugin-svelte error (shown in dev console or vite-error-overlay):
`snippet_name` is not defined
https://svelte.dev/e/export_undefined
```

Problematic code
```svelte
<script module>
    export { snippet_name }
</script>

<script>
    let state = $state(true)
</script>

{#snippet snippet_name()}
    {#if state} ... {/if}
{/snippet}
```

### Describe the proposed solution

It would be nice if the error message has similar phrasing to https://svelte.dev/docs/svelte/snippet#Exporting-snippets (or better yet links to that part of the documentation)

### Importance

nice to have


Discussion:

Patch:
diff --git a/.changeset/thick-snakes-look.md b/.changeset/thick-snakes-look.md
new file mode 100644
index 000000000000..f5f22e8a6ca4
--- /dev/null
+++ b/.changeset/thick-snakes-look.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: emit `snippet_invalid_export` instead of `undefined_export` for exported snippets
diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js
index cd44fd998aed..ecc843d2f42a 100644
--- a/packages/svelte/src/compiler/phases/2-analyze/index.js
+++ b/packages/svelte/src/compiler/phases/2-analyze/index.js
@@ -526,7 +526,6 @@ export function analyze_component(root, source, options) {
 			has_global: false
 		},
 		source,
-		undefined_exports: new Map(),
 		snippet_renderers: new Map(),
 		snippets: new Set(),
 		async_deriveds: new Set()
@@ -787,9 +786,15 @@ export function analyze_component(root, source, options) {
 		if (node.type === 'ExportNamedDeclaration' && node.specifiers !== null && node.source == null) {
 			for (const specifier of node.specifiers) {
 				if (specifier.local.type !== 'Identifier') continue;
-
-				const binding = analysis.module.scope.get(specifier.local.name);
-				if (!binding) e.export_undefined(specifier, specifier.local.name);
+				const name = specifier.local.name;
+				const binding = analysis.module.scope.get(name);
+				if (!binding) {
+					if ([...analysis.snippets].find((snippet) => snippet.expression.name === name)) {
+						e.snippet_invalid_export(specifier);
+					} else {
+						e.export_undefined(specifier, name);
+					}
+				}
 			}
 		}
 	}
diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/SnippetBlock.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/SnippetBlock.js
index 2f6bbd785a71..7930c2b1a7b8 100644
--- a/packages/svelte/src/compiler/phases/2-analyze/visitors/SnippetBlock.js
+++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/SnippetBlock.js
@@ -35,11 +35,6 @@ export function SnippetBlock(node, context) {
 	if (can_hoist) {
 		const binding = /** @type {Binding} */ (context.state.scope.get(name));
 		context.state.analysis.module.scope.declarations.set(name, binding);
-	} else {
-		const undefined_export = context.state.analysis.undefined_exports.get(name);
-		if (undefined_export) {
-			e.snippet_invalid_export(undefined_export);
-		}
 	}
 
 	node.metadata.can_hoist = can_hoist;
diff --git a/packages/svelte/src/compiler/phases/types.d.ts b/packages/svelte/src/compiler/phases/types.d.ts
index 4cbbc362c7cc..4da78f1482f7 100644
--- a/packages/svelte/src/compiler/phases/types.d.ts
+++ b/packages/svelte/src/compiler/phases/types.d.ts
@@ -95,7 +95,6 @@ export interface ComponentAnalysis extends Analysis {
 	};
 	/** @deprecated use `source` from `state.js` instead */
 	source: string;
-	undefined_exports: Map<string, Node>;
 	/**
 	 * Every render tag/component, and whether it could be definitively resolved or not
 	 */


Test patch:
diff --git a/packages/svelte/tests/compiler-errors/samples/snippet-invalid-export/_config.js b/packages/svelte/tests/compiler-errors/samples/snippet-invalid-export/_config.js
new file mode 100644
index 000000000000..cc0dd388f4bf
--- /dev/null
+++ b/packages/svelte/tests/compiler-errors/samples/snippet-invalid-export/_config.js
@@ -0,0 +1,10 @@
+import { test } from '../../test';
+
+export default test({
+	error: {
+		code: 'snippet_invalid_export',
+		message:
+			'An exported snippet can only reference things declared in a `<script module>`, or other exportable snippets',
+		position: [26, 29]
+	}
+});
diff --git a/packages/svelte/tests/compiler-errors/samples/snippet-invalid-export/main.svelte b/packages/svelte/tests/compiler-errors/samples/snippet-invalid-export/main.svelte
new file mode 100644
index 000000000000..eb776944c3f5
--- /dev/null
+++ b/packages/svelte/tests/compiler-errors/samples/snippet-invalid-export/main.svelte
@@ -0,0 +1,11 @@
+<script module>
+	export { foo }
+</script>
+
+<script>
+	let x = 42;
+</script>
+
+{#snippet foo()}
+	{x}
+{/snippet}