fix(lint): noUnusedImports/noUnusedVariables now flag truly-unused script bindings in Svelte/Vue/Astro#10473
Conversation
…ript bindings in Svelte/Vue/Astro The two rules consult `EmbeddedExportedBindings` to avoid false positives on imports/vars referenced only in templates. But under `html.experimentalFullSupportEnabled`, the source `<script>` block's own top-level bindings get registered into that set via `visit_js_source_snippet`, so the membership check at no_unused_variables.rs:357 and no_unused_imports.rs:686 self-suppresses every import and let/const in the script — including truly unused ones (biomejs#8590). Gate the embedded-binding suppression on `!file_source.is_embedded_source()`: for `{@const}` / Vue v-for / Svelte snippet params we keep the existing behavior, but for the `<script>` block itself rely on `EmbeddedValueReferences` (precise per-name template references) instead. `noUnusedImports` now consults that service too, mirroring `useImportType`/`noUnusedVariables`. Also extracts `HtmlSpreadAttribute` arguments as non-source snippets so `<input {...props} />` properly registers `props` as a template reference. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: 5052fca The changes in this PR will be included in the next version bump. This PR includes changesets to release 13 packages
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 |
✅ Organic activityNo automation signals detected in the analyzed events. This is an automated analysis by AgentScan |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThis PR completes cross-language support for no-unused rules by threading embedded-template references through the analysis pipeline. HTML parsing now captures Svelte spread attributes, quoted interpolations (e.g. Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.changeset/tasty-kiwis-roll.md:
- Line 5: The changeset sentence ends with a colon after "...unusedLet" but per
guidelines it must end with a full stop; edit the sentence in
.changeset/tasty-kiwis-roll.md to replace the trailing ":" with "." (keep the
following example paragraph/block as-is) so the sentence ending containing
"unusedLet" is terminated with a period.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 94636377-0a75-44dd-9eec-3bd95262d38a
⛔ Files ignored due to path filters (16)
crates/biome_cli/tests/snapshots/main_cases_handle_astro_files/full_support.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_astro_files/return_in_template_expression_should_error.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/full_support.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_svelte_files/full_support_ts.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_vue_files/full_support.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_vue_files/full_support_jsx.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_vue_files/full_support_ts.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_vue_files/full_support_tsx.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_vue_files/lint_vue_should_not_add_extra_newlines_in_embedded_snippet.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_vue_files/unused_suppression_has_correct_span_in_vue_file.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_svelte_cross_language_rules/no_unused_imports_flags_truly_unused_script_bindings.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_svelte_cross_language_rules/no_unused_imports_or_variables_for_template_references.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_svelte_cross_language_rules/spread_attribute_counts_as_template_reference.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_svelte_cross_language_rules/use_import_type_fires_on_type_only_value_import.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_svelte_cross_language_rules/use_import_type_quiet_for_template_referenced_imports.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/valid-vue-embedded-bindings.vue.snapis excluded by!**/*.snapand included by**
📒 Files selected for processing (8)
.changeset/tasty-kiwis-roll.mdcrates/biome_cli/tests/cases/handle_vue_files.rscrates/biome_cli/tests/cases/mod.rscrates/biome_cli/tests/cases/svelte_cross_language_rules.rscrates/biome_js_analyze/src/lint/correctness/no_unused_imports.rscrates/biome_js_analyze/src/lint/correctness/no_unused_variables.rscrates/biome_js_analyze/tests/specs/correctness/noUnusedVariables/valid-vue-embedded-bindings.vuecrates/biome_service/src/file_handlers/html/parse_embedded_nodes.rs
…ation, directives, bind shorthand, snippet params
Removing the source-script over-suppression (previous commit) exposed several
Svelte template constructs whose references the extractor never collected, each
becoming a false positive on noUnusedImports/noUnusedVariables:
- `{expr}` inside quoted attribute values (`style="top: {top}px"`): the parser
stores these as one opaque string token, so a brace/string-aware scanner
(`svelte_interpolations`) pulls the expressions out and parses them as JS.
- Directive names `use:action` / `transition:fn` / `in:` / `out:` / `animate:`
reference imported values; the name token is now registered.
- Shorthand `bind:open` (no initializer) reads the local `open`.
- `{#snippet foo(param)}` params used only in the snippet body — noUnused
FunctionParameters now consults EmbeddedValueReferences like the other rules.
Verified against a real SvelteKit codebase: 70 false positives → 0, while
genuinely-unused script bindings are still reported.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.changeset/tasty-kiwis-roll.md:
- Line 17: Change the sentence that currently ends with "removed:" to end with a
full stop (replace the trailing colon with a period) in the .changeset entry
describing noUnusedImports/noUnusedFunctionParameters and
EmbeddedValueReferences; leave the following bullet list unchanged and ensure
the sentence follows the project guideline that every sentence in a changeset
ends with a full stop.
In
`@crates/biome_js_analyze/src/lint/correctness/no_unused_function_parameters.rs`:
- Around line 145-152: The current early return uses only the identifier text
via refs.is_used_as_value(name), which can incorrectly suppress unused-parameter
diagnostics when an unrelated template binding shares the same name; instead
resolve the parameter's actual symbol/scope before checking
EmbeddedValueReferences: obtain the parameter's symbol (the binding or symbol id
for the identifier `name` from the semantic model or ctx), then ask
EmbeddedValueReferences whether that specific symbol (or symbol id/scope) is
used in templates (e.g., add or use a method like
is_used_as_value_for_symbol(symbol_id) or filter refs by scope) and only return
None when the symbol-matched template usage is found.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 27afbe83-7867-4020-934e-8a163be16416
⛔ Files ignored due to path filters (4)
crates/biome_cli/tests/snapshots/main_cases_svelte_cross_language_rules/attribute_string_interpolation_counts_as_reference.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_svelte_cross_language_rules/bind_shorthand_counts_as_reference.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_svelte_cross_language_rules/directive_names_count_as_references.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_svelte_cross_language_rules/snippet_parameter_used_in_body_is_not_unused.snapis excluded by!**/*.snapand included by**
📒 Files selected for processing (5)
.changeset/tasty-kiwis-roll.mdcrates/biome_cli/tests/cases/svelte_cross_language_rules.rscrates/biome_js_analyze/src/lint/correctness/no_unused_function_parameters.rscrates/biome_service/src/workspace/document/services/embedded_value_references.rscrates/biome_service/src/workspace/server.rs
| // A Svelte `{#snippet foo(param)}` parameter can be referenced only in | ||
| // the snippet's template body (a separate embedded snippet the JS | ||
| // semantic model can't see). Treat template references as a use. | ||
| if let Some(refs) = ctx.get_service::<EmbeddedValueReferences>() | ||
| && refs.is_used_as_value(name) | ||
| { | ||
| return None; | ||
| } |
There was a problem hiding this comment.
Name-only template matching can hide genuinely unused parameters.
At Line 148, this suppression is keyed only by identifier text, not symbol identity/scope. That can silence a real unused parameter when an unrelated template binding happens to share the same name. Please narrow this to actual snippet-parameter symbols (or otherwise scope-aware matching) before returning early.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@crates/biome_js_analyze/src/lint/correctness/no_unused_function_parameters.rs`
around lines 145 - 152, The current early return uses only the identifier text
via refs.is_used_as_value(name), which can incorrectly suppress unused-parameter
diagnostics when an unrelated template binding shares the same name; instead
resolve the parameter's actual symbol/scope before checking
EmbeddedValueReferences: obtain the parameter's symbol (the binding or symbol id
for the identifier `name` from the semantic model or ctx), then ask
EmbeddedValueReferences whether that specific symbol (or symbol id/scope) is
used in templates (e.g., add or use a method like
is_used_as_value_for_symbol(symbol_id) or filter refs by scope) and only return
None when the symbol-matched template usage is found.
There was a problem hiding this comment.
This is a deliberate property of the whole EmbeddedValueReferences mechanism rather than something specific to this rule. Template expressions, {#snippet} headers, directives, etc. are parsed as separate embedded snippets, so there is no shared semantic model — and therefore no symbol identity — spanning the script block and the template. References are necessarily collected as raw token text, and the same name-based check already backs noUnusedImports, noUnusedVariables, and useImportType. Adding this rule to that set keeps it consistent.
The failure mode of a name collision is a false negative (a genuinely-unused parameter goes unreported because an unrelated template binding shares its name), not a false positive. For an unused-* rule that direction is the safe one — it never flags/auto-fixes something that is actually used. The previous behaviour here was strictly worse: it produced false positives on real code (e.g. {#snippet card(label)} using {label} in its body).
True scope-aware matching would require a unified semantic model across embedded-snippet boundaries, which doesn't exist today; that's a larger architectural effort tracked under the broader cross-language work (#8590) rather than this fix. Leaving as-is for now.
There was a problem hiding this comment.
@Mokto Given that, it's best to add this note as a comment. It's knowledge base that must be kept around. Avoid LLM-generated comments, write a comment yourself. These LLM generated comments are verbose, and use useless technical words.
…port rules
An import used only as a type inside a template snippet — e.g. `icon: IconType`
in a `{#snippet}` parameter type — was flagged as unused. `EmbeddedValueReferences`
dropped type-only references (so `useImportType` could distinguish value vs type
usage), leaving them invisible to noUnusedImports/noUnusedVariables.
Track type-only references in a parallel channel. `useImportType` keeps using the
value-only set (unchanged); the unused-* rules now consult `is_used` (value OR
type). A genuinely-unused type import is still reported.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ematipico
left a comment
There was a problem hiding this comment.
Thank you. The architecture of the Svelte interpolation is incorrect. In fact, I fail to see a case where we have e.g. {top}px where top is undefined.
Also remove all those CLI tests. Our analyzer infra already supports those languages, so the cases can be moved there.
| /// Identifiers referenced only as types (e.g. `icon: IconType` in a | ||
| /// `{#snippet}` parameter type). Tracked separately so `useImportType` | ||
| /// can keep distinguishing value vs type usage, while the unused-* rules | ||
| /// can treat either as a use. |
There was a problem hiding this comment.
Let's make comments generics and properly targeted. We don't care about who uses the list, we're interested to its business logic.
Also, how are "types" deemed real types? In typescript, some constructs create both a value and a type e.g. class Foo {}
| /// constructs the parser leaves opaque or unattached: directive names | ||
| /// (`use:action`) and `{expr}` interpolations embedded inside quoted | ||
| /// attribute values (`style="top: {top}px"`). | ||
| pub(crate) fn visit_html_root(&mut self, root: &HtmlRoot, is_svelte: bool) { |
There was a problem hiding this comment.
Passing is_svelte is short-sighted. It's best to send the HtmlFileSource
| /// When `is_svelte` is set, also extracts references from Svelte-only | ||
| /// constructs the parser leaves opaque or unattached: directive names | ||
| /// (`use:action`) and `{expr}` interpolations embedded inside quoted | ||
| /// attribute values (`style="top: {top}px"`). |
There was a problem hiding this comment.
constructs the parser leaves opaque or unattached
What does that mean? Please use language targeted to human developers, be specific and use simple language.
| // we register the directive name itself (`action` / `fn`). `bind:`, | ||
| // `class:`, and `style:` are excluded — their property is a DOM/CSS | ||
| // name, not a JS reference. | ||
| if let Some(directive) = SvelteUseDirective::cast_ref(&node) { |
There was a problem hiding this comment.
Create a new function to visit svelte directives. Use AnySvelteDirective to catch all the nodes. Also, use match instead of if/else. Using a match will make the compiler fail in case we add new nodes.
| // Shorthand `bind:open` (no `={...}`) binds the local variable | ||
| // named by the property. With an explicit initializer | ||
| // (`bind:value={x}`) the property is a DOM/component name and | ||
| // the reference `x` is extracted from the initializer snippet. |
There was a problem hiding this comment.
This comment is superfluous, or it should be moved as docstring of the function register_svelte_binding_property
| /// Identifiers used as values. | ||
| value: Vec<Vec<(TextRange, TokenText)>>, | ||
| /// Identifiers used only as types (e.g. `icon: IconType`). | ||
| type_only: Vec<Vec<(TextRange, TokenText)>>, |
| /// Check if an identifier is referenced at all (value or type) in any | ||
| /// tracked non-source snippet. The unused-* rules use this: a type-only | ||
| /// usage in a template (`icon: IconType`) still counts as a use. |
There was a problem hiding this comment.
| /// Check if an identifier is referenced at all (value or type) in any | |
| /// tracked non-source snippet. The unused-* rules use this: a type-only | |
| /// usage in a template (`icon: IconType`) still counts as a use. | |
| /// Check if an identifier is referenced as value or type in any | |
| /// tracked non-source snippet. |
| /// tracked non-source snippet. The unused-* rules use this: a type-only | ||
| /// usage in a template (`icon: IconType`) still counts as a use. | ||
| pub(crate) fn is_used(&self, identifier: &str) -> bool { | ||
| Self::contains(&self.value, identifier) || Self::contains(&self.type_only, identifier) |
There was a problem hiding this comment.
nit: I think it's best if we don't use the contains function, and just use simple iterators.
self.value.iter().chain(self.type_only).any()| // A Svelte `{#snippet foo(param)}` parameter can be referenced only in | ||
| // the snippet's template body (a separate embedded snippet the JS | ||
| // semantic model can't see). Treat template references as a use. | ||
| if let Some(refs) = ctx.get_service::<EmbeddedValueReferences>() | ||
| && refs.is_used_as_value(name) | ||
| { | ||
| return None; | ||
| } |
There was a problem hiding this comment.
@Mokto Given that, it's best to add this note as a comment. It's knowledge base that must be kept around. Avoid LLM-generated comments, write a comment yourself. These LLM generated comments are verbose, and use useless technical words.
| Fixed [#8590](https://github.com/biomejs/biome/issues/8590): [`noUnusedImports`](https://biomejs.dev/linter/rules/no-unused-imports/) and [`noUnusedVariables`](https://biomejs.dev/linter/rules/no-unused-variables/) now correctly flag truly-unused imports and `let`/`const` declarations inside Svelte, Vue, and Astro `<script>` blocks (when `html.experimentalFullSupportEnabled` is on). Previously, the script's own top-level bindings were registered into the embedded-binding set, which self-suppressed every diagnostic; precise template-reference tracking now drives the suppression instead. The following Svelte component now correctly reports `unusedImport` and `unusedLet`. | ||
|
|
||
| ```svelte | ||
| <script lang="ts"> | ||
| import { unusedImport } from "./other"; | ||
| let unusedLet = 42; | ||
| import Button from "./Button.svelte"; | ||
| let name = "alice"; | ||
| </script> | ||
| <Button>{name}</Button> | ||
| ``` | ||
|
|
||
| [`noUnusedImports`](https://biomejs.dev/linter/rules/no-unused-imports/) and [`noUnusedFunctionParameters`](https://biomejs.dev/linter/rules/no-unused-function-parameters/) now consult `EmbeddedValueReferences` too, so bindings used only in the template stay quiet. Template-reference extraction was extended to cover the Svelte constructs the parser leaves unattached or opaque, all of which previously produced false positives once the over-suppression was removed. | ||
|
|
||
| - Spread attributes — `<input {...props} />` (Svelte and Astro). | ||
| - `{expr}` interpolations inside quoted attribute values — `style="top: {top}px"`, `class="card {cls}"`. | ||
| - Directive names — `use:action`, `transition:fn`, `in:fn`, `out:fn`, `animate:fn`. | ||
| - Shorthand `bind:` — `<Dialog bind:open />` references the local `open`. | ||
| - `{#snippet foo(param)}` parameters referenced only in the snippet body. |
There was a problem hiding this comment.
This changeset can be shorted to something like "improved rules ... for Svelte, Astro and Vue".
Remove the technicalities. This is covered in the contribution guidelines.
…h on directives, move tests
- Move attribute-string `{...}` interpolation extraction into parse_embedded_nodes
so each is a tracked snippet, instead of re-parsing and discarding it in the
references visitor.
- Replace the directive if/else chain with an exhaustive match on AnySvelteDirective.
- visit_html_root takes &HtmlFileSource instead of an is_svelte bool.
- Rename EmbeddedValueReferences fields to values/types, drop the contains helper.
- Move the Svelte CLI cases to analyzer spec fixtures.
- Trim comments and the changeset.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…UnusedVariables Verifies that genuinely-unused script bindings are still flagged after the embedded-reference tracking fix; previously only valid (clean) cases existed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ro templates
When an imported name appears only in type position inside a template
snippet (e.g. `{#snippet card(p: { icon: IconType })}`), the semantic
model of the <script> block has no references to it, so is_only_used_as_type
returned false and the rule stayed silent. Now it falls back to checking
the embedded type_references channel, correctly suggesting `import type`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers the case where `import type { T }` is never referenced in either
the script or the template — it should still be reported by noUnusedImports.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@ematipico thank you for the quick feedback! I can see my first PR was pretty rough. I think it's ready for another round of review now |
ematipico
left a comment
There was a problem hiding this comment.
Much better now, but we still don't have error tests for svelte interpolations e.g. {foo}px where foo isn't defined anywhere
…value_references.rs Co-authored-by: Emanuele Stoppa <my.burning@gmail.com>
…olation test Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Thank you! Happy to hear it's better. I've pushed the latest fix |
| let unused = 0; | ||
| </script> | ||
|
|
||
| <div style="top: {foo}px"></div> |
There was a problem hiding this comment.
That's not what I asked. foo should be flagged by noUndeclaredVariables
There was a problem hiding this comment.
Ah sorry, I misunderstood. i've just added it
…e interpolation
Verifies that undeclared variables used in attribute interpolations like
{foo}px are flagged by noUndeclaredVariables.
| // Interpolations inside a quoted attribute value: | ||
| // `style="top: {top}px"`, `class="card {cls}"`. | ||
| if let Some(string) = HtmlString::cast_ref(&element) { | ||
| for candidate in build_svelte_string_interpolation_candidates(&string) { | ||
| ctx.parse_and_push( | ||
| &candidate, | ||
| &doc_file_source, | ||
| Some(embedded_file_source), | ||
| &mut nodes, | ||
| ); | ||
| } | ||
| } |
There was a problem hiding this comment.
This is a hack for a change that should go in the parser. We would want to emit a new node like SvelteInterpolatedString or something like that so we could just extract the expressions without needing to do string parsing this high in the architecture.
Maybe leave this fix out of this PR to not bloat it further.
There was a problem hiding this comment.
so do you want me to do something about it afterwards, or in a PR to the current PR ?
Or will you create an issue for later
There was a problem hiding this comment.
@Mokto, revert the changes regarding the interpolation, and then send a new PR that probably takes care of the parsing of the interpolated strings in the HTML parser.
You're free to create an issue, but not mandatory.
| /// Returns the range of each `{...}` interpolation's inner expression within | ||
| /// `input`. Brace counting is aware of JS string and template literals, so a | ||
| /// `}` inside a string (`{ok ? 'a}b' : c}`) or a nested object (`{ {x: 1} }`) | ||
| /// does not end the group early. | ||
| fn svelte_interpolation_ranges(input: &str) -> Vec<TextRange> { | ||
| let bytes = input.as_bytes(); | ||
| let mut ranges = Vec::new(); | ||
| let mut i = 0; | ||
| while i < bytes.len() { | ||
| if bytes[i] != b'{' { | ||
| i += 1; | ||
| continue; | ||
| } |
There was a problem hiding this comment.
Per my previous comment, this is the kind of string parsing that we want to defer to the actual parser. Otherwise we're effectively double-processing the same text.
| /// Tracks identifiers referenced from the template that the JS semantic | ||
| /// model of the `<script>` block can't see: component names (`<Button />`) | ||
| /// and, for Svelte, directive names (`use:action`). Interpolations are | ||
| /// parsed as snippets earlier and reach us via [`Self::visit_non_source_snippet`]. | ||
| pub(crate) fn visit_html_root(&mut self, root: &HtmlRoot, file_source: &HtmlFileSource) { | ||
| let is_svelte = file_source.is_svelte(); |
There was a problem hiding this comment.
This is a worse comment than it was previously. Too verbose, not easily glancible, and is missing information that the previous version had.
| /// Registers the directive name when it resolves to an imported binding. | ||
| /// `use`/`transition`/`in`/`out`/`animate` name a function; shorthand | ||
| /// `bind:open` reads the local `open`. `style:`/`class:` name a CSS | ||
| /// property, not a binding. | ||
| fn register_svelte_directive_reference( | ||
| &mut self, | ||
| directive: &AnySvelteDirective, | ||
| ) -> Option<()> { | ||
| let value = match directive { | ||
| AnySvelteDirective::SvelteUseDirective(d) => d.value().ok()?, | ||
| AnySvelteDirective::SvelteTransitionDirective(d) => d.value().ok()?, | ||
| AnySvelteDirective::SvelteInDirective(d) => d.value().ok()?, | ||
| AnySvelteDirective::SvelteOutDirective(d) => d.value().ok()?, |
There was a problem hiding this comment.
I couldn't find any tests that exercise this.
| fn register_svelte_binding_property( | ||
| &mut self, | ||
| property: Option<AnySvelteBindingProperty>, | ||
| ) -> Option<()> { | ||
| let token = match property? { | ||
| AnySvelteBindingProperty::SvelteName(name) => name.ident_token().ok()?, | ||
| AnySvelteBindingProperty::SvelteMemberProperty(_) | ||
| | AnySvelteBindingProperty::SvelteLiteral(_) => return None, | ||
| }; |
There was a problem hiding this comment.
Same here, couldn't find tests.
| self.register_svelte_binding_property(value.property().ok()) | ||
| } | ||
|
|
||
| fn register_svelte_binding_property( |
There was a problem hiding this comment.
add a comment explaining what syntax this touches. code samples are a good way to do that.
Merging this PR will not alter performance
Comparing Footnotes
|
…nt, add bind: tests - Rename EmbeddedValueReferences::references to value_references (pairs cleaner with type_references) - Restore the detailed doc comment on visit_html_root with examples - Add unit tests for bind: shorthand (registers reference) and bind: with initializer (does not register directive name as reference)
|
@Mokto thank you. Can you fix the CI? Also, if comments have been addressed, re-request a review from the right side of the webpage. This will send a notification to the reviewers, notify "this is ready for a new review" |
|
I'm waiting on #10504 to be merged per #10473 (comment) |
…0555 Replace the embedded-node string-parsing hack with consumption of the HtmlAttributeSingleTextExpression nodes that biomejs#10555 now emits inside SvelteTemplateAttributeValue for interpolated quoted attribute values (`style="top: {top}px"`). - Drop build_svelte_string_interpolation_candidates / svelte_interpolation_ranges and the HtmlString branch in the Svelte pass. The interpolation nodes are captured by the existing Pass-4 HtmlAttributeSingleTextExpression branch, whose parent guard already admits them. - Remove the now-unused HtmlString / TextRange imports and the hack's unit test. - visit_html_root doc comment: interpolations flow through the snippet path, not this function. - Collapse a nested if in use_import_type into a let-chain (clippy).
4d704fc to
e1851db
Compare
…s unused
The `parse_embedded_nodes` function was silently skipping `SvelteHtmlBlock`
nodes (`{@html expr}`), so the JS expression inside them was never added to
the embedded snippet list. As a result, any variable referenced only via
`{@html variable}` was incorrectly reported as unused by `noUnusedVariables`.
Now `SvelteHtmlBlock` is handled the same way as other expression-bearing
blocks (`SvelteIfBlock`, `SvelteKeyBlock`, etc.): the expression is parsed as
a JS snippet and visited so its identifier references are tracked.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In Svelte files, <pre> was being parsed the same way as <script>/<style>:
its entire content was consumed as a single raw HTML_LITERAL token inside
HtmlEmbeddedContent. That prevented the expression-tracking passes in
parse_embedded_nodes from seeing any Svelte blocks inside <pre>, so
variables referenced only via <pre>{@html expr}</pre> or <pre>{expr}</pre>
were still incorrectly reported as unused by noUnusedVariables.
The formatter already has an independent HTML_VERBATIM_TAGS list that
includes "pre", which causes it to emit <pre> content verbatim regardless
of how the parser represents the children. Skipping the embedded-language
path in the parser for Svelte is therefore safe: formatting is unchanged,
and {@html} / interpolation nodes inside <pre> are now visible as proper
AST descendants.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
dyc3
left a comment
There was a problem hiding this comment.
This is a great improvement! Just make sure that there are enough snapshot tests for the lint rules to cover all these cases. We plan to eventually replace the EmbeddedBindings/References services with framework specific semantic models for more precise analysis, and we want to make sure there are enough tests in place to prevent regressions.
…snapshot Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
Part of #10103.
With
html.experimentalFullSupportEnabledenabled, supports:noUnusedImports/noUnusedVariables/useImportTypeAI assistance disclosure
Implemented with Claude Code, with manual review and direction at each step (diagnosis, the
is_embedded_sourcegate, enumerating + fixing the extraction gap classes against a real codebase, and the tests).