Skip to content

fix(svelte2tsx): don't treat <style>-like text in a script comment as a real style tag#3079

Open
Socialpranker wants to merge 2 commits into
sveltejs:masterfrom
Socialpranker:fix/3071-style-tag-in-script-comment
Open

fix(svelte2tsx): don't treat <style>-like text in a script comment as a real style tag#3079
Socialpranker wants to merge 2 commits into
sveltejs:masterfrom
Socialpranker:fix/3071-style-tag-in-script-comment

Conversation

@Socialpranker

Copy link
Copy Markdown

Fixes #3071

Problem

svelte-check (and the VS Code extension, same engine) reports a false element_unclosed: <script> was left open for a file whose <script> is correctly closed. The trigger is the literal token <style> appearing inside the <script> (e.g. in a // comment) when the file also contains a real <style> block. svelte/compiler's compile() accepts the exact same source without error, so this is a svelte2tsx false positive.

Minimal reproduction:

<script lang="ts">
  // see the <style> block
</script>
<style>p{color:red}</style>

Root cause

In packages/svelte2tsx/src/utils/htmlxparser.ts, findVerbatimElements uses a non-greedy styleRegex that can start matching at a <style> substring inside the script and run past the real </script> up to the next </style>.

The existing insideScript guard was meant to drop such false style matches, but it only skipped matches that are strictly nested inside a script tag (tag.start < style.start && tag.end > style.end). In the issue's case the false style match ends after the script tag, so the guard didn't fire. blankVerbatimContent then blanked out the real </script> along with the fake style content, and the compiler correctly complained about an unclosed <script> in the now-broken text.

Fix

Relax the guard to also skip a style match that merely starts inside a script tag:

- (tag) => tag.start < styleTag.start && tag.end > styleTag.end
+ (tag) => tag.start < styleTag.start && styleTag.start < tag.end

Test plan

  • Added test/svelte2tsx/samples/style-tag-in-script-comment-with-real-style covering the exact reproduction (a <style> token in a script comment plus a real separate <style> block). It fails before the fix (<script> must have a closing tag) and passes after, emitting correct TSX with both the script comment and the real <style> preserved.
  • Full svelte2tsx sample suite passes (371 tests), including the neighbouring style-in-script (a <style>...</style> string literal inside a script with no real style block — still correctly treated as inside-script) and ts-style-and-script (a normal script followed by a separate style block — unaffected).
  • pnpm lint (Prettier) passes.

…as a real style tag

svelte-check reported a false `element_unclosed: <script> was left open`
for a file whose `<script>` is correctly closed, when the literal token
`<style>` appears inside the script (e.g. in a comment) *and* the file also
contains a real `<style>` block. svelte/compiler accepts the same source.

Root cause: the non-greedy `styleRegex` in `findVerbatimElements` can start
matching at a `<style>` substring inside the script and run past the real
`</script>` up to the next `</style>`. The existing `insideScript` guard only
skipped style matches *strictly nested* in a script tag, so this partially
overlapping match slipped through and `blankVerbatimContent` then blanked out
the real `</script>`, making the compiler see an unclosed script.

Relax the guard to also skip a style match that merely *starts* inside a
script tag.

Fixes sveltejs#3071

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a26f7b8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
svelte2tsx Patch
svelte-check Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@jasonlyu123

Copy link
Copy Markdown
Member

This won't work. The actual style tag should be removed. So the proper fix isn't to filter it out, but to properly recognise the actual start tag.

…ing the false match

The previous approach relaxed the insideScript guard to drop a style match that
starts inside a script. That removed the false match but also dropped the real
`<style>` block, because the non-greedy style regex had already swallowed
everything from the `<style>` token in the script comment up to the real
`</style>` into a single match.

Instead, find the script containers first, blank them out (newlines preserved so
character offsets stay put), then run the style regex on that masked text. The
style regex can no longer see a `<style>` token sitting inside a script, so it
matches the actual start tag.

This covers the script-to-style direction from sveltejs#3071. The mirror case (a
`<script>` token inside a `<style>` comment) has the same shape and is left as a
follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Socialpranker

Copy link
Copy Markdown
Author

You're right, filtering it out dropped the real <style> block along with the false match. I reworked it: find the script containers first, blank them out (newlines kept so offsets don't shift), then run the style regex on that. Now the style match can only land on the actual start tag, never a <style> token sitting inside a script comment. Pushed the new version with a recalculated snapshot; the full svelte2tsx suite is green.

One thing I hit while doing this: the mirror case breaks the same way on main today, where a <script> token inside a <style> comment makes the script regex run past the real </style>. My change only covers the script-to-style direction from this issue. Want me to handle the symmetric case here too, or keep this PR scoped to #3071 and file the other one separately?

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

Labels

None yet

Projects

None yet

3 participants