Skip to content

feat: twoslash-svelte#2028

Draft
Hugos68 wants to merge 7 commits into
sveltejs:mainfrom
Hugos68:feature/twoslash-svelte
Draft

feat: twoslash-svelte#2028
Hugos68 wants to merge 7 commits into
sveltejs:mainfrom
Hugos68:feature/twoslash-svelte

Conversation

@Hugos68

@Hugos68 Hugos68 commented Jun 6, 2026

Copy link
Copy Markdown

Issue

Closes #649

Description

NOTE: twoslash-svelte@0.0.0 has a blocking bug, wait for twoslashes/twoslash#90 until we can merge.

Replaces the default twoslasher instance to twoslash-svelte which supports "twoslashing" .svelte snippets:

image

Before submitting the PR, please make sure you do the following

  • It's really useful if your PR references an issue where it is discussed ahead of time.
  • Prefix your PR title with feat:, fix:, chore:, or docs:.
  • This message body should clearly illustrate what problems it solves.

@vercel

vercel Bot commented Jun 6, 2026

Copy link
Copy Markdown

@Hugos68 is attempting to deploy a commit to the Svelte Team on Vercel.

A member of the Team first needs to authorize it.

@vercel

vercel Bot commented Jun 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
svelte-dev Error Error Jun 6, 2026 9:46am

@Hugos68 Hugos68 mentioned this pull request Jun 6, 2026
3 tasks
@Hugos68

Hugos68 commented Jun 6, 2026

Copy link
Copy Markdown
Author

While most snippets work out of the box by simply replacing the import for createTwoSlasher there are 2 bugs I will need help with:

<style> blocks break hovers

When a <style> block is inside a snippet like this snippet: https://svelte.dev/docs/svelte/overview for some reason twoslash will completely not work for these snippets. renderer.ts has a lot of manual string manipulation going on so I assume something there requires some fixing.

Snippets referencing non existing variables

Take this snippet:

<button onclick={todo.reset}>
	reset
</button>

This will currently throw an error because todo is undefined. My suggestion are one of 3:

  1. Disable twoslash on these snippets, as it doesn't really add anything
  2. Add a "phantom" variable like so:
<script>
    const todo = { reset: () => {} }
    // ---cut-before---
</script>

<button onclick={todo.reset}>
	reset
</button>

See: https://twoslash.netlify.app/refs/notations#cutting-a-code-sample

  1. Simply add a todo variable and leave it in there, although this does bloat the examples a bit on some occasions.

@teemingc teemingc left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably need to enable banners for Svelte codeblocks so that we can easily inject types/expected error statements https://github.com/hugos68/svelte.dev/blob/feature/twoslash-svelte/packages/site-kit/src/lib/markdown/renderer.ts#L448-L449 However, it seems like the twoslash syntax only works inside the script blocks, so we can't just prepend the banner like we do in js/ts files.

The docs in each repo also need to be modified with the correct ambient types or // @errors:.

I've tried ensuring the banners get prepended but it seems to make the hover behaviour break.

@Hugos68 Hugos68 marked this pull request as draft June 6, 2026 12:43
@Hugos68

Hugos68 commented Jun 6, 2026

Copy link
Copy Markdown
Author

Hey @teemingc, thanks for your swift response!

So essentially looking for <script> tag and injecting the banners there? And if a <script> tag cannot be found create one and put the banners in there?

@teemingc

teemingc commented Jun 6, 2026

Copy link
Copy Markdown
Member

Hey @teemingc, thanks for your swift response!

So essentially looking for <script> tag and injecting the banners there? And if a <script> tag cannot be found create one and put the banners in there?

Yeah, that’s what I tried but it didn’t seem to work similarly to the original twoslash. It might be better to fix it from that side so that it recognises the syntax from outside the script block, allowing us to declare multiple files at a time

@Hugos68

Hugos68 commented Jun 6, 2026

Copy link
Copy Markdown
Author

It might be better to fix it from that side so that it recognises the syntax from outside the script block, allowing us to declare multiple files at a time

I think that should be the svelte2tsx's job in that case. Although allowing top level ts style comments in the markdown bit of svelte seems quote odd no?

@teemingc

teemingc commented Jun 6, 2026

Copy link
Copy Markdown
Member

It might be better to fix it from that side so that it recognises the syntax from outside the script block, allowing us to declare multiple files at a time

I think that should be the svelte2tsx's job in that case. Although allowing top level ts style comments in the markdown bit of svelte seems quote odd no?

I guess twoslash was originally designed for js/ts so for twoslash-svelte it could expect html comment syntax instead? Or for consistency, stick to ts style comments

@Hugos68

Hugos68 commented Jun 7, 2026

Copy link
Copy Markdown
Author

I've partially solved some issues by creating a PR for allowing cuts: twoslashes/twoslash#91

This still leaves us to implement the banner stuff, but it does help with some of the errors while implementing this.

@Hugos68

Hugos68 commented Jun 7, 2026

Copy link
Copy Markdown
Author

@teemingc Is it maybe a good idea to add a injectScript variable that twoslash-svelte can accept so that the nitty gritty parsing stuff is done by twoslash svelte so you can "easily" inject script into the snippets to aid typechecking? What do you think?

@teemingc

teemingc commented Jun 7, 2026

Copy link
Copy Markdown
Member

How would that work if we had multiple svelte files declared in a single code block using cut? Would it inject the same banners into each of the svelte script blocks?

@Hugos68

Hugos68 commented Jun 7, 2026

Copy link
Copy Markdown
Author

How would that work if we had multiple svelte files declared in a single code block using cut? Would it inject the same banners into each of the svelte script blocks?

Svelte are Single File Components, how do you add multiple svelte files to a single code block?

@teemingc

teemingc commented Jun 7, 2026

Copy link
Copy Markdown
Member

The docs in svelte.dev have a common pattern of declaring multiple files to avoid twoslash typescript errors in the same codeblock with // @filename: and then using cut to hide them. If we can’t do the same in svelte code blocks it might not be worth adding typescript support since we’d have to expect lots of typescript errors and they end up defaulting to type any

@Hugos68

Hugos68 commented Jun 8, 2026

Copy link
Copy Markdown
Author

The docs in svelte.dev have a common pattern of declaring multiple files to avoid twoslash typescript errors in the same codeblock with // @filename: and then using cut to hide them

I'm unfamiliar with @filename, is this documented somewhere? Would love to see what I can do to support this.

@teemingc

teemingc commented Jun 8, 2026

Copy link
Copy Markdown
Member

The docs in svelte.dev have a common pattern of declaring multiple files to avoid twoslash typescript errors in the same codeblock with // @filename: and then using cut to hide them

I'm unfamiliar with @filename, is this documented somewhere? Would love to see what I can do to support this.

The first mention of it is at https://twoslash.netlify.app/refs/notations#cut-before

@Hugos68

Hugos68 commented Jun 8, 2026

Copy link
Copy Markdown
Author

@teemingc I've now used this snippet:

function injectPrelude({ prelude, source, language }: { prelude: string; source: string; language: string }) {
	if (language === 'svelte') {
		const scriptTag = /<script\b[^>]*>/;
		if (scriptTag.test(source)) {
			return source.replace(
				scriptTag,
				match => `${match}\n${prelude}\n`
			);
		}
		return `<script>\n${prelude}\n</script>\n\n${source}`;
	}
	return prelude + source;
}

To inject any prelude content into snippets. I'm assuming there are more edges cases I'm not thinking of when you tried, but as far as I know this works fine.

TS/JS -> just inject normal at start of file
Svelte (with script) -> match and inject
Svelte (without script) -> create and inject

All the errors I'm seeing are still from my original concern titled "Snippets referencing non existing variables". I'm not sure how we want to tackle that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use shiki-twoslash for .svelte code blocks

2 participants