Skip to content

fix: don't mistake type identifier expressions for TS type declarations in tags#18330

Merged
dummdidumm merged 5 commits into
mainfrom
refactor-declaration-tag-parsing
Jun 1, 2026
Merged

fix: don't mistake type identifier expressions for TS type declarations in tags#18330
dummdidumm merged 5 commits into
mainfrom
refactor-declaration-tag-parsing

Conversation

@baseballyama

Copy link
Copy Markdown
Member

Detect TypeScript type declarations by actually parsing instead of by character-class blacklists, so that expressions like {type === 'all' ? a : b} or {type instanceof Foo} aren't misclassified as malformed declarations.

Closes #18328

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. In many cases, features are absent for a reason. For large changes, please create an RFC: https://github.com/sveltejs/rfcs
  • Prefix your PR title with feat:, fix:, chore:, or docs:.
  • This message body should clearly illustrate what problems it solves.
  • Ideally, include a test that fails without this PR but passes with it.
  • If this PR changes code within packages/svelte/src, add a changeset (npx changeset).

Tests and linting

  • Run the tests with pnpm test and lint the project with pnpm lint

…ations in tags

Detect TypeScript `type` declarations by actually parsing instead of by
character-class blacklists, so that expressions like `{type === 'all' ? a : b}`
or `{type instanceof Foo}` aren't misclassified as malformed declarations.

Closes #18328
@changeset-bot

changeset-bot Bot commented May 30, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: cca40b1

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

This PR includes changesets to release 1 package
Name Type
svelte 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

@baseballyama

Copy link
Copy Markdown
Member Author

I also submitted a PR to improve the regular expression (#18329), but I realized that handling edge cases such as generics would be difficult with a regex-based approach alone. As a result, I changed the implementation to first perform a lightweight check using a regular expression and then use Acorn for a more accurate determination.

@svelte-docs-bot

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

Playground

pnpm add https://pkg.pr.new/svelte@18330

@dummdidumm dummdidumm 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.

Goddammit I feared something would slip through. Left a few comments but overall solution makes sense

{type / 2}
{type > 1}
{type instanceof Foo}
{type in foo}

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.

These additions to this test should be enough we don't need all the other tests

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

If we’re going with an AST-based solution, then the change at parser.root.comments.length = initial_comment_count; (https://github.com/sveltejs/svelte/pull/18330/changes#diff-07a9d6689d63c982b2e3a45ae4bd624240d27824e8c5bdce8a539b4d2a7c36f1R96) is necessary. Without it, the test at {type instanceof /* probe */ Object} (https://github.com/sveltejs/svelte/pull/18330/changes#diff-37911caa74a576888755175e439f6605c76c5c17695b44de73e2f028295ee3a2R1) fails.

But we can delete packages/svelte/tests/runtime-runes/samples/declaration-tag-type-identifier test. 9826361

// discard probe comments so `read_expression` doesn't re-add them
parser.root.comments.length = initial_comment_count;
return null;
}

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.

This feels overcomplicated. If you add type to the list of unsupported in the regex and then in the "if unsupported" condition check if it's a usage of type as a reference (using your new regex) then don't throw the error it should come down to the same result

@baseballyama baseballyama May 30, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Are you saying that you think the approach in #18329 is preferable to using an AST-based solution?

I feel the AST-based approach is more robust. With the approach in #18329, we’d likely keep running into edge cases and need to add support for them individually—for example, patterns involving generics such as {type<string>('generic-call')}.

Comment thread packages/svelte/src/compiler/phases/1-parse/state/tag.js Outdated
@baseballyama baseballyama force-pushed the refactor-declaration-tag-parsing branch from 0b69c5d to 9826361 Compare May 30, 2026 10:02
Coverage is already provided by the validator samples (declaration-tag-invalid-type-2
exercises the fall-through path; declaration-tag-invalid-type-unicode/-generic exercise
the parse-confirmed type-alias error path).
@baseballyama baseballyama force-pushed the refactor-declaration-tag-parsing branch from 9826361 to b48ad46 Compare May 30, 2026 10:04
@@ -0,0 +1 @@
{type instanceof /* probe */ Object}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@baseballyama baseballyama force-pushed the refactor-declaration-tag-parsing branch from 014cb4b to b48ad46 Compare May 30, 2026 10:13
@baseballyama baseballyama requested a review from dummdidumm May 30, 2026 10:13
Copilot AI review requested due to automatic review settings June 1, 2026 00:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Fixes #18328: expressions starting with type (e.g. {type === 'all' ? a : b}, {type instanceof Foo}) were being misclassified as malformed TypeScript type declarations because of a regex character-class blacklist that couldn't tell identifiers from declaration keywords. The fix replaces the blacklist with a "maybe" hint and confirms by actually parsing the contents, falling back to expression parsing when the result is an ExpressionStatement.

Changes:

  • In state/tag.js, split type out of the unsupported-declaration regex into a regex_maybe_type_declaration hint, then disambiguate via parse_statement_at; if the result is an ExpressionStatement, drop the speculative comments and fall through to expression parsing, otherwise (a TS TSTypeAliasDeclaration) raise declaration_tag_invalid_type.
  • In utils/bracket.js, make find_regex_end only treat a / as the start of a regex literal when a closing / is found before the next newline, and avoid jumping i to Infinity in find_matching_bracket when no closing slash is present (fixes loose recovery for cases like {let x = a / }).
  • Added validator coverage for many {type ...} expressions plus a TS type alias, a parser-modern fixture asserting comment handling on the speculative parse, and an extended loose-declaration-tag fixture.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/svelte/src/compiler/phases/1-parse/state/tag.js Speculatively parse {type ...}, fall back to expression on ExpressionStatement, restore comment count
packages/svelte/src/compiler/phases/1-parse/utils/bracket.js Only treat / as regex when closing slash precedes newline; guard against Infinity cursor advance
packages/svelte/tests/validator/samples/declaration-tag-invalid-type-2/input.svelte Add <script lang="ts"> and many {type ...} expression cases
packages/svelte/tests/validator/samples/declaration-tag-invalid-type-2/errors.json Update expected error location to the remaining real type-alias case
packages/svelte/tests/parser-modern/samples/tag-type-identifier-probe-comment/{input.svelte,output.json} Snapshot showing comments aren't duplicated when speculative parse falls through
packages/svelte/tests/parser-modern/samples/loose-declaration-tag/{input.svelte,output.json} Cover loose recovery for an unterminated division-like /
.changeset/fix-type-identifier-in-tag.md Patch changeset entry

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@dummdidumm dummdidumm 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.

thank you!

Comment thread .changeset/fix-type-identifier-in-tag.md Outdated
@dummdidumm dummdidumm merged commit c74f44f into main Jun 1, 2026
14 of 15 checks passed
@dummdidumm dummdidumm deleted the refactor-declaration-tag-parsing branch June 1, 2026 08:03
@github-actions github-actions Bot mentioned this pull request Jun 1, 2026
Rich-Harris pushed a commit that referenced this pull request Jun 1, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## svelte@5.56.1

### Patch Changes

- fix: error at compile time on duplicate snippet/declaration tag
definitions ([#18351](#18351))

- fix: parse declaration tag contents more robustly
([#18353](#18353))

- fix: correctly transform references to earlier declarators in a
declaration tag (e.g. `{let a = $state(0), b = $derived(a * 2)}`)
([#18348](#18348))

- fix: avoid spurious `state_referenced_locally` warnings for `$derived`
declarations in declaration tags
([#18348](#18348))

- fix: tolerate whitespace before `let`/`const` in declaration tags
([#18348](#18348))

- fix: prevent infinite loop when a tag's expression ends with a
trailing `/` at the end of the input
([#18350](#18350))

- fix: more robust parsing of declaration tags with regards to `type`
([#18330](#18330))

- fix: preserve newlines in spread input values when the `type`
attribute is applied after `value`
([#18345](#18345))

- fix: update `SvelteURLSearchParams` when setting duplicate keys to the
same joined value
([#18336](#18336))

- fix: check references for blockers on server, too
([#18352](#18352))

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
huskas-2189 added a commit to huskas-2189/Bookmark that referenced this pull request Jun 14, 2026
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [svelte](https://svelte.dev) ([source](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte)) | [`5.56.0` → `5.56.1`](https://renovatebot.com/diffs/npm/svelte/5.56.0/5.56.1) | ![age](https://developer.mend.io/api/mc/badges/age/npm/svelte/5.56.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/svelte/5.56.0/5.56.1?slim=true) |

---

### Release Notes

<details>
<summary>sveltejs/svelte (svelte)</summary>

### [`v5.56.1`](https://github.com/sveltejs/svelte/blob/HEAD/packages/svelte/CHANGELOG.md#5561)

[Compare Source](https://github.com/sveltejs/svelte/compare/svelte@5.56.0...svelte@5.56.1)

##### Patch Changes

- fix: error at compile time on duplicate snippet/declaration tag definitions ([#&#8203;18351](sveltejs/svelte#18351))

- fix: parse declaration tag contents more robustly ([#&#8203;18353](sveltejs/svelte#18353))

- fix: correctly transform references to earlier declarators in a declaration tag (e.g. `{let a = $state(0), b = $derived(a * 2)}`) ([#&#8203;18348](sveltejs/svelte#18348))

- fix: avoid spurious `state_referenced_locally` warnings for `$derived` declarations in declaration tags ([#&#8203;18348](sveltejs/svelte#18348))

- fix: tolerate whitespace before `let`/`const` in declaration tags ([#&#8203;18348](sveltejs/svelte#18348))

- fix: prevent infinite loop when a tag's expression ends with a trailing `/` at the end of the input ([#&#8203;18350](sveltejs/svelte#18350))

- fix: more robust parsing of declaration tags with regards to `type` ([#&#8203;18330](sveltejs/svelte#18330))

- fix: preserve newlines in spread input values when the `type` attribute is applied after `value` ([#&#8203;18345](sveltejs/svelte#18345))

- fix: update `SvelteURLSearchParams` when setting duplicate keys to the same joined value ([#&#8203;18336](sveltejs/svelte#18336))

- fix: check references for blockers on server, too ([#&#8203;18352](sveltejs/svelte#18352))

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled because a matching PR was automerged previously.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xOTUuMCIsInVwZGF0ZWRJblZlciI6IjQzLjE5NS4wIiwidGFyZ2V0QnJhbmNoIjoiZGV2ZWxvcCIsImxhYmVscyI6W119-->

Co-authored-by: huskas-2189 <huskas-2189@noreply.codeberg.org>
Reviewed-on: https://codeberg.org/huskas-2189/Bookmark/pulls/82
huskas-2189 added a commit to huskas-2189/Bookmark that referenced this pull request Jun 19, 2026
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [svelte](https://svelte.dev) ([source](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte)) | [`5.56.0` → `5.56.1`](https://renovatebot.com/diffs/npm/svelte/5.56.0/5.56.1) | ![age](https://developer.mend.io/api/mc/badges/age/npm/svelte/5.56.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/svelte/5.56.0/5.56.1?slim=true) |

---

### Release Notes

<details>
<summary>sveltejs/svelte (svelte)</summary>

### [`v5.56.1`](https://github.com/sveltejs/svelte/blob/HEAD/packages/svelte/CHANGELOG.md#5561)

[Compare Source](https://github.com/sveltejs/svelte/compare/svelte@5.56.0...svelte@5.56.1)

##### Patch Changes

- fix: error at compile time on duplicate snippet/declaration tag definitions ([#&#8203;18351](sveltejs/svelte#18351))

- fix: parse declaration tag contents more robustly ([#&#8203;18353](sveltejs/svelte#18353))

- fix: correctly transform references to earlier declarators in a declaration tag (e.g. `{let a = $state(0), b = $derived(a * 2)}`) ([#&#8203;18348](sveltejs/svelte#18348))

- fix: avoid spurious `state_referenced_locally` warnings for `$derived` declarations in declaration tags ([#&#8203;18348](sveltejs/svelte#18348))

- fix: tolerate whitespace before `let`/`const` in declaration tags ([#&#8203;18348](sveltejs/svelte#18348))

- fix: prevent infinite loop when a tag's expression ends with a trailing `/` at the end of the input ([#&#8203;18350](sveltejs/svelte#18350))

- fix: more robust parsing of declaration tags with regards to `type` ([#&#8203;18330](sveltejs/svelte#18330))

- fix: preserve newlines in spread input values when the `type` attribute is applied after `value` ([#&#8203;18345](sveltejs/svelte#18345))

- fix: update `SvelteURLSearchParams` when setting duplicate keys to the same joined value ([#&#8203;18336](sveltejs/svelte#18336))

- fix: check references for blockers on server, too ([#&#8203;18352](sveltejs/svelte#18352))

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled because a matching PR was automerged previously.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xOTUuMCIsInVwZGF0ZWRJblZlciI6IjQzLjE5NS4wIiwidGFyZ2V0QnJhbmNoIjoiZGV2ZWxvcCIsImxhYmVscyI6W119-->

Co-authored-by: huskas-2189 <huskas-2189@noreply.codeberg.org>
Reviewed-on: https://codeberg.org/huskas-2189/Bookmark/pulls/82
huskas-2189 added a commit to huskas-2189/Bookmark that referenced this pull request Jun 19, 2026
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [svelte](https://svelte.dev) ([source](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte)) | [`5.56.0` → `5.56.1`](https://renovatebot.com/diffs/npm/svelte/5.56.0/5.56.1) | ![age](https://developer.mend.io/api/mc/badges/age/npm/svelte/5.56.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/svelte/5.56.0/5.56.1?slim=true) |

---

### Release Notes

<details>
<summary>sveltejs/svelte (svelte)</summary>

### [`v5.56.1`](https://github.com/sveltejs/svelte/blob/HEAD/packages/svelte/CHANGELOG.md#5561)

[Compare Source](https://github.com/sveltejs/svelte/compare/svelte@5.56.0...svelte@5.56.1)

##### Patch Changes

- fix: error at compile time on duplicate snippet/declaration tag definitions ([#&#8203;18351](sveltejs/svelte#18351))

- fix: parse declaration tag contents more robustly ([#&#8203;18353](sveltejs/svelte#18353))

- fix: correctly transform references to earlier declarators in a declaration tag (e.g. `{let a = $state(0), b = $derived(a * 2)}`) ([#&#8203;18348](sveltejs/svelte#18348))

- fix: avoid spurious `state_referenced_locally` warnings for `$derived` declarations in declaration tags ([#&#8203;18348](sveltejs/svelte#18348))

- fix: tolerate whitespace before `let`/`const` in declaration tags ([#&#8203;18348](sveltejs/svelte#18348))

- fix: prevent infinite loop when a tag's expression ends with a trailing `/` at the end of the input ([#&#8203;18350](sveltejs/svelte#18350))

- fix: more robust parsing of declaration tags with regards to `type` ([#&#8203;18330](sveltejs/svelte#18330))

- fix: preserve newlines in spread input values when the `type` attribute is applied after `value` ([#&#8203;18345](sveltejs/svelte#18345))

- fix: update `SvelteURLSearchParams` when setting duplicate keys to the same joined value ([#&#8203;18336](sveltejs/svelte#18336))

- fix: check references for blockers on server, too ([#&#8203;18352](sveltejs/svelte#18352))

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled because a matching PR was automerged previously.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xOTUuMCIsInVwZGF0ZWRJblZlciI6IjQzLjE5NS4wIiwidGFyZ2V0QnJhbmNoIjoiZGV2ZWxvcCIsImxhYmVscyI6W119-->

Co-authored-by: huskas-2189 <huskas-2189@noreply.codeberg.org>
Reviewed-on: https://codeberg.org/huskas-2189/Bookmark/pulls/82
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.

Cannot use ternary expressions in template text content

3 participants