feat: collect local and global styles for noUndeclearedClasses#9297
Conversation
|
| Name | Type |
|---|---|
| @biomejs/biome | Minor |
| @biomejs/cli-win32-x64 | Minor |
| @biomejs/cli-win32-arm64 | Minor |
| @biomejs/cli-darwin-x64 | Minor |
| @biomejs/cli-darwin-arm64 | Minor |
| @biomejs/cli-linux-x64 | Minor |
| @biomejs/cli-linux-arm64 | Minor |
| @biomejs/cli-linux-x64-musl | Minor |
| @biomejs/cli-linux-arm64-musl | Minor |
| @biomejs/wasm-web | Minor |
| @biomejs/wasm-bundler | Minor |
| @biomejs/wasm-nodejs | Minor |
| @biomejs/backend-jsonrpc | Patch |
| @biomejs/js-api | Major |
Click here to learn what changesets are, and how to add one.
Click here if you're a maintainer who wants to add a changeset to this PR
Merging this PR will not alter performance
Comparing Footnotes
|
|
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:
WalkthroughRenames lint rules noUndeclaredStyles → noUndeclaredClasses and noUnusedStyles → noUnusedClasses; introduces EmbeddingStyleApplicability (Local/Global/Unknown) and CssClassDefinition (name + applicability); makes CssFileSource embedding-aware for Vue/Astro/Svelte and exposes embedding APIs; replaces previous class-collection with HTML-aware traversal and import-tree diagnostics (new traverse/build helpers); updates module-graph APIs and formatting to emit HTML-embedded content; and adds/updates tests and snapshots covering linked, scoped and upward-traversal scenarios. Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (9)
crates/biome_html_syntax/src/element_ext.rs (1)
295-297: New public method lacks rustdoc and has a somewhat generic signature.This public method would benefit from a brief rustdoc comment explaining its purpose. The method name
is_style_scopedsuggests checking for thescopedattribute specifically, but the parameter allows any attribute name—consider whether a more descriptive name or dedicated methods (e.g.,is_style_scoped()without params,has_style_attribute(name)) would be clearer.Suggested documentation
+ /// Returns `true` if this is a `<style>` tag that has the specified attribute. + /// + /// This is commonly used to detect scoped styles in frameworks like Vue (`<style scoped>`). pub fn is_style_scoped(&self, attribute_name: &str) -> bool { self.is_style_tag() && self.find_attribute_by_name(attribute_name).is_some() }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_html_syntax/src/element_ext.rs` around lines 295 - 297, The new public function is_style_scoped currently lacks rustdoc and has a generic signature that accepts any attribute name; add a brief Rustdoc above pub fn is_style_scoped(&self, attribute_name: &str) -> bool describing that it checks whether this element is a <style> tag and contains the given attribute, and then either (A) rename the method to has_style_attribute(&self, name: &str) to reflect the general check and add rustdoc, or (B) keep is_style_scoped() as a no-arg method that checks specifically for the "scoped" attribute and implement a new pub fn has_style_attribute(&self, name: &str) -> bool that calls self.is_style_tag() && self.find_attribute_by_name(name).is_some(); update callers accordingly and document both methods; reference the existing helper find_attribute_by_name when writing the docs.crates/biome_html_analyze/tests/spec_tests.rs (1)
216-219: Scope snapshot normalisation more narrowly.Line 218 replaces backslashes across the whole snapshot, which can also alter fixture source text. Prefer normalising only rendered path fragments.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_html_analyze/tests/spec_tests.rs` around lines 216 - 219, The current unconditional snapshot.replace('\\', "/") (inside the needs_module_graph branch) can alter fixture source text; change it to only normalize backslashes inside rendered path fragments by applying the replacement to matched path-like substrings rather than the whole snapshot. Locate the needs_module_graph block and replace the global call on snapshot with a targeted transformation that scans snapshot for path patterns (e.g., Windows drive or path-like regex matches) and replaces backslashes with forward slashes only within those matches, preserving other text in snapshot.crates/biome_module_graph/tests/snap/mod.rs (1)
48-54: Stabilise snapshot file ordering once, then iterate without cloning.Sorting by path in constructors avoids order-dependent snapshots and lets
assert_snapshotiterateself.filesdirectly.Proposed patch
- let files = fs + let mut files: Vec<_> = fs .files .read() .iter() .map(|(file, entry)| { let content = entry.lock(); let content = String::from_utf8_lossy(content.as_slice()).into_owned(); (file.as_str().to_string(), content) }) .collect(); + files.sort_by(|(a, _), (b, _)| a.cmp(b)); Self { module_graph, files, resolver: None, } - pub fn from_files(module_graph: &'a ModuleGraph, files: Vec<(String, String)>) -> Self { + pub fn from_files( + module_graph: &'a ModuleGraph, + mut files: Vec<(String, String)>, + ) -> Self { + files.sort_by(|(a, _), (b, _)| a.cmp(b)); Self { module_graph, files, resolver: None, } } - let files: Vec<_> = self.files.clone(); - let dependency_data = self.module_graph.data(); - for (file_name, source_code) in &files { + for (file_name, source_code) in &self.files {Also applies to: 65-69
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_module_graph/tests/snap/mod.rs` around lines 48 - 54, The constructor from_files should stabilise file ordering by sorting the incoming files Vec by path before storing it (e.g. call files.sort_by(|a,b| a.0.cmp(&b.0)) or equivalent) so snapshots are order-independent, and then store the sorted Vec in the files field (module_graph, files, resolver) rather than leaving it unsorted; apply the same deterministic sort change to the other constructor variant handling files (the one around lines 65-69) and ensure callers/consumers (e.g. tests using assert_snapshot) iterate self.files directly without cloning.crates/biome_module_graph/src/html_module_info/visitor.rs (1)
72-79: Drop the now-unused stylesheet parameter fromvisit_html_element.Since stylesheet extraction moved to
visit_self_closing_element, this parameter is dead weight.Suggested patch
if let Some(element) = HtmlElement::cast(node.clone()) { self.visit_html_element( element, - &mut referenced_classes, - &mut imported_stylesheets, + &mut referenced_classes, ); } else if let Some(element) = HtmlSelfClosingElement::cast(node) { self.visit_self_closing_element(element, &mut imported_stylesheets); } @@ fn visit_html_element( &self, element: HtmlElement, referenced_classes: &mut Vec<CssClassReference>, - _imported_stylesheets: &mut Vec<ResolvedPath>, ) {Also applies to: 131-136
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_module_graph/src/html_module_info/visitor.rs` around lines 72 - 79, The call sites pass imported_stylesheets into visit_html_element but stylesheet extraction was moved to visit_self_closing_element, so remove the now-unused stylesheet parameter from the visit_html_element signature and all its call sites (e.g., change the call inside the HtmlElement branch that currently calls visit_html_element(element, &mut referenced_classes, &mut imported_stylesheets) to only pass element and &mut referenced_classes), and update the other occurrence referenced in the comment (around the second call at the 131-136 region) to match; ensure visit_self_closing_element still accepts &mut imported_stylesheets and that visit_html_element’s implementation no longer expects or uses the stylesheet parameter.crates/biome_service/src/workspace/server.rs (2)
164-167: Gate this test-only accessor behind the testing feature.The doc says “for use in tests”, but Lines 164-167 are exposed in non-testing builds too. Keeping it feature-gated avoids widening the public surface by accident.
Suggested patch
/// Returns the module graph, for use in tests. + #[cfg(feature = "testing")] pub fn module_graph(&self) -> Arc<ModuleGraph> { self.module_graph.clone() }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_service/src/workspace/server.rs` around lines 164 - 167, Gate the test-only accessor module_graph behind the testing feature by adding a conditional compilation attribute to the method (e.g., annotate the pub fn module_graph(&self) with #[cfg(feature = "testing")]) so it is only compiled when that feature is enabled; optionally add #[cfg_attr(docsrs, doc(cfg(feature = "testing")))] above the function to make the feature requirement visible in docs and keep the signature (pub fn module_graph(&self) -> Arc<ModuleGraph>) unchanged.
178-196: Don’t swallow indexing failures in the test helper.Line 184 currently drops the
Result. If parsing/indexing fails, tests can silently proceed with a partial graph.Suggested patch
#[cfg(feature = "testing")] pub fn index_files_for_test( &self, project_key: ProjectKey, files: impl IntoIterator<Item = (BiomePath, DocumentFileSource)>, - ) { + ) -> Result<(), WorkspaceError> { for (path, document_file_source) in files { - let _ = self.open_file_internal( + self.open_file_internal( OpenFileReason::Index(IndexTrigger::InitialScan), OpenFileParams { project_key, path, content: FileContent::FromServer, document_file_source: Some(document_file_source), persist_node_cache: false, inline_config: None, }, - ); + )?; } + Ok(()) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_service/src/workspace/server.rs` around lines 178 - 196, The helper index_files_for_test currently drops the Result from open_file_internal which hides parsing/indexing failures; update index_files_for_test to surface errors by changing its signature to return a Result (e.g., Result<(), E> or anyhow::Result<()>) and propagate the open_file_internal result using the ? operator (or collect and return the first Err), or alternatively call .expect/.unwrap with a clear message to fail fast in tests; ensure you reference the open_file_internal call inside index_files_for_test and propagate errors originating from OpenFileReason::Index / OpenFileParams so tests don't continue on partial indexing.crates/biome_module_graph/src/html_module_info/mod.rs (1)
96-103: Rustdoc forstyle_classesis stale after the type change.The comment still says “Each
TokenText…”, but the field is nowIndexSet<CssClassDefinition>.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_module_graph/src/html_module_info/mod.rs` around lines 96 - 103, The rustdoc for the field style_classes is out of date: it still refers to TokenText even though the field type is now IndexSet<CssClassDefinition>. Update the comment for the style_classes field to describe that it stores CssClassDefinition entries (not TokenText), mention that these are collected from CssClassSelector nodes in embedded CSS ASTs (no re-parsing), and briefly note what a CssClassDefinition represents (e.g., a class name plus any associated metadata like source/span) so readers know why the richer type is used.crates/biome_html_analyze/src/lint/nursery/no_undeclared_classes.rs (1)
139-148: Build the import tree once per attribute, not once per missing class.Lines 139-148 recompute the same tree for every missing class token. Cache it lazily in
runand reuse it for all emitted signals.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_html_analyze/src/lint/nursery/no_undeclared_classes.rs` around lines 139 - 148, The code currently calls module_graph.build_import_tree_for_html(file_path) for every missing class inside the loop (when !found_class), which recomputes the same import tree repeatedly; change this by computing and caching the import tree once per attribute-run: add an Option or lazy variable in run (e.g., let mut cached_import_tree: Option<ImportTree> = None) and inside the loop, when you first need it call module_graph.build_import_tree_for_html(file_path) and store it in cached_import_tree, then reuse cached_import_tree.as_ref() for all subsequent UndeclaredClass signals instead of calling build_import_tree_for_html repeatedly; update references in the block that constructs UndeclaredClass (the import_tree field) to use the cached value.crates/biome_test_utils/src/lib.rs (1)
374-388: Make recursive file collection deterministic for stable snapshots.
read_dirordering is filesystem-dependent; sorting entries here reduces snapshot churn and “works on my machine” noise.Suggested patch
fn collect_all_files_in_dir(dir: &Utf8Path, out: &mut Vec<Utf8PathBuf>) { let Ok(entries) = std::fs::read_dir(dir.as_std_path()) else { return; }; - for entry in entries.flatten() { + let mut entries: Vec<_> = entries.flatten().collect(); + entries.sort_by_key(|entry| entry.path()); + for entry in entries { let Ok(path) = Utf8PathBuf::try_from(entry.path()) else { continue; }; if path.is_dir() { collect_all_files_in_dir(&path, out);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_test_utils/src/lib.rs` around lines 374 - 388, The recursive collector collect_all_files_in_dir uses std::fs::read_dir which yields filesystem-dependent ordering; make it deterministic by collecting entries into a Vec, mapping/filtering to Utf8PathBufs (using the existing try_from logic), sorting that Vec by a stable key (e.g., path.as_str() or path.to_string()) and then iterating the sorted list to recurse or push to out so snapshots are stable across machines; preserve the existing error/continue behavior for entries that fail conversion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/biome_css_syntax/src/file_source.rs`:
- Around line 223-231: The is_applied_locally method currently only considers
Vue local embeddings; update its match to also return true for Astro and Svelte
local embeddings by adding their corresponding patterns (e.g.,
EmbeddingHtmlKind::Astro { applicability: EmbeddingApplicability::Local } and
EmbeddingHtmlKind::Svelte { applicability: EmbeddingApplicability::Local })
under the matches against self.embedding_kind (keeping the existing
EmbeddingKind and EmbeddingHtmlKind/EmbeddingApplicability symbols to locate the
code).
In `@crates/biome_css_syntax/src/selector_ext.rs`:
- Around line 47-51: is_global_pseudo currently compares the token text with
exact "global", which fails for uppercase or mixed-case pseudo names; update the
comparison in is_global_pseudo so the token text is matched case-insensitively
(e.g., use an ASCII case-insensitive comparison like eq_ignore_ascii_case or
lowercasing) when checking token.text_trimmed() == "global" so :GLOBAL and
:Global also match; keep the existing name()/value_token() chain and only change
the final equality check.
In `@crates/biome_html_syntax/src/element_ext.rs`:
- Around line 290-293: The doc and impl disagree: update the is_sass_lang
function to return true for both "scss" and "sass" values; modify the body of
is_sass_lang (which already calls is_style_tag() and has_attribute_with_value)
to check has_attribute_with_value("lang", "scss") ||
has_attribute_with_value("lang", "sass") (or equivalent case-insensitive check)
so the function matches the doc comment.
In `@crates/biome_module_graph/tests/snap/mod.rs`:
- Around line 27-35: The snapshot helper currently calls
std::str::from_utf8(content.as_slice()).unwrap() inside the map over
fs.files.read(), which will panic on non-UTF8 fixture bytes; replace that unwrap
with a lossy conversion such as
String::from_utf8_lossy(content.as_slice()).to_string() (or otherwise handle
invalid UTF-8 without panicking) in the closure where entry.lock() and
content.as_slice() are used so snapshot generation remains resilient.
In `@crates/biome_module_graph/tests/spec_tests.rs`:
- Around line 2937-3198: The test names and inline comments (e.g.,
test_html_inline_style_classes_are_global,
test_vue_unscoped_style_classes_are_global,
test_vue_scoped_style_classes_are_local_and_hidden,
test_vue_mixed_scoped_and_unscoped, test_astro_local_style_classes_are_hidden,
test_astro_global_style_classes_are_visible,
test_svelte_local_style_classes_are_hidden) contradict the asserted behavior
(EmbeddingApplicability::Unknown for plain HTML inline styles and same-file
visibility for local/scoped styles). Rename tests and update their comments to
reflect the actual assertions (e.g., "html inline styles are Unknown", "vue
scoped styles are Local but visible in same-file traversal", "astro/svelte local
styles are Local but appear in same-file traversal", "vue mixed shows both
stored but scoped only applies same-file") so names and commentary match the
asserted applicability and traversal expectations.
In `@crates/biome_rule_options/src/no_undeclared_classes.rs`:
- Line 6: Add an inline rustdoc comment describing the purpose and usage of the
public options type NoUndeclaredClassesOptions (e.g., a one-line summary
explaining what rule options it represents and when to use/modify them) directly
above the pub struct NoUndeclaredClassesOptions {} declaration so the public
type is documented per the project coding guidelines.
In `@crates/biome_service/src/lib.rs`:
- Line 17: Hide the test utilities behind a cargo feature by gating the
test_utils module with the testing feature: replace the unconditional pub mod
test_utils declaration with a conditional compilation attribute so test_utils is
only compiled and exposed when the "testing" feature is enabled (use
#[cfg(feature = "testing")] on the pub mod test_utils declaration, and consider
#[cfg_attr(not(feature = "testing"), allow(dead_code))] or similar only if
needed for warnings).
---
Nitpick comments:
In `@crates/biome_html_analyze/src/lint/nursery/no_undeclared_classes.rs`:
- Around line 139-148: The code currently calls
module_graph.build_import_tree_for_html(file_path) for every missing class
inside the loop (when !found_class), which recomputes the same import tree
repeatedly; change this by computing and caching the import tree once per
attribute-run: add an Option or lazy variable in run (e.g., let mut
cached_import_tree: Option<ImportTree> = None) and inside the loop, when you
first need it call module_graph.build_import_tree_for_html(file_path) and store
it in cached_import_tree, then reuse cached_import_tree.as_ref() for all
subsequent UndeclaredClass signals instead of calling build_import_tree_for_html
repeatedly; update references in the block that constructs UndeclaredClass (the
import_tree field) to use the cached value.
In `@crates/biome_html_analyze/tests/spec_tests.rs`:
- Around line 216-219: The current unconditional snapshot.replace('\\', "/")
(inside the needs_module_graph branch) can alter fixture source text; change it
to only normalize backslashes inside rendered path fragments by applying the
replacement to matched path-like substrings rather than the whole snapshot.
Locate the needs_module_graph block and replace the global call on snapshot with
a targeted transformation that scans snapshot for path patterns (e.g., Windows
drive or path-like regex matches) and replaces backslashes with forward slashes
only within those matches, preserving other text in snapshot.
In `@crates/biome_html_syntax/src/element_ext.rs`:
- Around line 295-297: The new public function is_style_scoped currently lacks
rustdoc and has a generic signature that accepts any attribute name; add a brief
Rustdoc above pub fn is_style_scoped(&self, attribute_name: &str) -> bool
describing that it checks whether this element is a <style> tag and contains the
given attribute, and then either (A) rename the method to
has_style_attribute(&self, name: &str) to reflect the general check and add
rustdoc, or (B) keep is_style_scoped() as a no-arg method that checks
specifically for the "scoped" attribute and implement a new pub fn
has_style_attribute(&self, name: &str) -> bool that calls self.is_style_tag() &&
self.find_attribute_by_name(name).is_some(); update callers accordingly and
document both methods; reference the existing helper find_attribute_by_name when
writing the docs.
In `@crates/biome_module_graph/src/html_module_info/mod.rs`:
- Around line 96-103: The rustdoc for the field style_classes is out of date: it
still refers to TokenText even though the field type is now
IndexSet<CssClassDefinition>. Update the comment for the style_classes field to
describe that it stores CssClassDefinition entries (not TokenText), mention that
these are collected from CssClassSelector nodes in embedded CSS ASTs (no
re-parsing), and briefly note what a CssClassDefinition represents (e.g., a
class name plus any associated metadata like source/span) so readers know why
the richer type is used.
In `@crates/biome_module_graph/src/html_module_info/visitor.rs`:
- Around line 72-79: The call sites pass imported_stylesheets into
visit_html_element but stylesheet extraction was moved to
visit_self_closing_element, so remove the now-unused stylesheet parameter from
the visit_html_element signature and all its call sites (e.g., change the call
inside the HtmlElement branch that currently calls visit_html_element(element,
&mut referenced_classes, &mut imported_stylesheets) to only pass element and
&mut referenced_classes), and update the other occurrence referenced in the
comment (around the second call at the 131-136 region) to match; ensure
visit_self_closing_element still accepts &mut imported_stylesheets and that
visit_html_element’s implementation no longer expects or uses the stylesheet
parameter.
In `@crates/biome_module_graph/tests/snap/mod.rs`:
- Around line 48-54: The constructor from_files should stabilise file ordering
by sorting the incoming files Vec by path before storing it (e.g. call
files.sort_by(|a,b| a.0.cmp(&b.0)) or equivalent) so snapshots are
order-independent, and then store the sorted Vec in the files field
(module_graph, files, resolver) rather than leaving it unsorted; apply the same
deterministic sort change to the other constructor variant handling files (the
one around lines 65-69) and ensure callers/consumers (e.g. tests using
assert_snapshot) iterate self.files directly without cloning.
In `@crates/biome_service/src/workspace/server.rs`:
- Around line 164-167: Gate the test-only accessor module_graph behind the
testing feature by adding a conditional compilation attribute to the method
(e.g., annotate the pub fn module_graph(&self) with #[cfg(feature = "testing")])
so it is only compiled when that feature is enabled; optionally add
#[cfg_attr(docsrs, doc(cfg(feature = "testing")))] above the function to make
the feature requirement visible in docs and keep the signature (pub fn
module_graph(&self) -> Arc<ModuleGraph>) unchanged.
- Around line 178-196: The helper index_files_for_test currently drops the
Result from open_file_internal which hides parsing/indexing failures; update
index_files_for_test to surface errors by changing its signature to return a
Result (e.g., Result<(), E> or anyhow::Result<()>) and propagate the
open_file_internal result using the ? operator (or collect and return the first
Err), or alternatively call .expect/.unwrap with a clear message to fail fast in
tests; ensure you reference the open_file_internal call inside
index_files_for_test and propagate errors originating from OpenFileReason::Index
/ OpenFileParams so tests don't continue on partial indexing.
In `@crates/biome_test_utils/src/lib.rs`:
- Around line 374-388: The recursive collector collect_all_files_in_dir uses
std::fs::read_dir which yields filesystem-dependent ordering; make it
deterministic by collecting entries into a Vec, mapping/filtering to
Utf8PathBufs (using the existing try_from logic), sorting that Vec by a stable
key (e.g., path.as_str() or path.to_string()) and then iterating the sorted list
to recurse or push to out so snapshots are stable across machines; preserve the
existing error/continue behavior for entries that fail conversion.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (56)
Cargo.lockis excluded by!**/*.lockand included by**crates/biome_cli/tests/snapshots/main_cases_handle_css_files/no_undeclared_classes_shows_checked_import_chain.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_css_files/no_unused_classes_component_hierarchy_child_imports_css.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_css_files/no_unused_classes_component_hierarchy_nested_three_levels.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_css_files/no_unused_classes_component_hierarchy_parent_imports_css.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_css_files/no_unused_classes_dynamic_classname_not_collected.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_css_files/no_unused_classes_global_pseudo_class_is_exempt.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_css_files/no_unused_classes_mixed_html_jsx_consumers.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_css_files/no_unused_classes_referenced_class_not_flagged.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_css_files/no_unused_classes_selector_patterns_all_referenced.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_css_files/no_unused_classes_selector_patterns_partial_reference.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_css_files/no_unused_classes_transitive_css_import.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_handle_css_files/no_unused_classes_unreferenced_class_flagged.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_html/no_undeclared_classes_passes_when_declared.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_html/no_undeclared_classes_reports_undeclared_classes.snapis excluded by!**/*.snapand included by**crates/biome_cli/tests/snapshots/main_cases_html/no_undeclared_classes_silent_without_style_info.snapis excluded by!**/*.snapand included by**crates/biome_configuration/src/analyzer/linter/rules.rsis excluded by!**/rules.rsand included by**crates/biome_configuration/src/generated/domain_selector.rsis excluded by!**/generated/**,!**/generated/**and included by**crates/biome_css_analyze/tests/specs/nursery/noUnusedClasses/invalid.css.snapis excluded by!**/*.snapand included by**crates/biome_css_analyze/tests/specs/nursery/noUnusedClasses/partial-invalid/partial_invalid.css.snapis excluded by!**/*.snapand included by**crates/biome_css_analyze/tests/specs/nursery/noUnusedClasses/transitive-valid/transitive_theme.css.snapis excluded by!**/*.snapand included by**crates/biome_css_analyze/tests/specs/nursery/noUnusedClasses/transitive-valid/transitive_valid.css.snapis excluded by!**/*.snapand included by**crates/biome_css_analyze/tests/specs/nursery/noUnusedClasses/tsx_valid.css.snapis excluded by!**/*.snapand included by**crates/biome_css_analyze/tests/specs/nursery/noUnusedClasses/valid.css.snapis excluded by!**/*.snapand included by**crates/biome_css_analyze/tests/specs/nursery/noUnusedStyles/valid.css.snapis excluded by!**/*.snapand included by**crates/biome_diagnostics_categories/src/categories.rsis excluded by!**/categories.rsand included by**crates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-linked-stylesheet/invalid.html.snapis excluded by!**/*.snapand included by**crates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-scoped-styles/invalid.vue.snapis excluded by!**/*.snapand included by**crates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-upward-traversal-astro/App.astro.snapis excluded by!**/*.snapand included by**crates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-upward-traversal-astro/Button.astro.snapis excluded by!**/*.snapand included by**crates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-upward-traversal-astro/Page.astro.snapis excluded by!**/*.snapand included by**crates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-upward-traversal-vue/App.vue.snapis excluded by!**/*.snapand included by**crates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-upward-traversal-vue/Button.vue.snapis excluded by!**/*.snapand included by**crates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-upward-traversal-vue/Page.vue.snapis excluded by!**/*.snapand included by**crates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid.vue.snapis excluded by!**/*.snapand included by**crates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/valid-linked-stylesheet/valid.html.snapis excluded by!**/*.snapand included by**crates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/valid-scoped-styles/valid.vue.snapis excluded by!**/*.snapand included by**crates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/valid.astro.snapis excluded by!**/*.snapand included by**crates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/valid.html.snapis excluded by!**/*.snapand included by**crates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/valid.svelte.snapis excluded by!**/*.snapand included by**crates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/valid.vue.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-direct-import/directImport.jsx.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/invalid.jsx.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/multi-level-chain/level2.jsx.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/multi-level-chain/level3.jsx.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/multi-level-chain/level4.jsx.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/multi-level-chain/multilevel.jsx.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/upward-traversal/App.jsx.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/upward-traversal/Block.jsx.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/upward-traversal/Button.jsx.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/upward-traversal/Page.jsx.snapis excluded by!**/*.snapand included by**crates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/valid/valid.jsx.snapis excluded by!**/*.snapand included by**crates/biome_module_graph/tests/snapshots/test_astro_component_imports_snapshot.snapis excluded by!**/*.snapand included by**crates/biome_module_graph/tests/snapshots/test_jsx_imports_css_file.snapis excluded by!**/*.snapand included by**crates/biome_module_graph/tests/snapshots/test_svelte_component_imports_snapshot.snapis excluded by!**/*.snapand included by**crates/biome_module_graph/tests/snapshots/test_vue_component_imports_snapshot.snapis excluded by!**/*.snapand included by**
📒 Files selected for processing (86)
.changeset/add-no-undeclared-styles.md.changeset/add-no-unused-styles.mdcrates/biome_cli/tests/cases/handle_css_files.rscrates/biome_cli/tests/cases/html.rscrates/biome_css_analyze/src/lint/nursery/no_unused_classes.rscrates/biome_css_analyze/tests/specs/nursery/noUnusedClasses/invalid.csscrates/biome_css_analyze/tests/specs/nursery/noUnusedClasses/partial-invalid/partial_invalid.csscrates/biome_css_analyze/tests/specs/nursery/noUnusedClasses/partial-invalid/partial_invalid.jsxcrates/biome_css_analyze/tests/specs/nursery/noUnusedClasses/transitive-valid/transitive_theme.csscrates/biome_css_analyze/tests/specs/nursery/noUnusedClasses/transitive-valid/transitive_valid.csscrates/biome_css_analyze/tests/specs/nursery/noUnusedClasses/transitive-valid/transitive_valid.jsxcrates/biome_css_analyze/tests/specs/nursery/noUnusedClasses/tsx_valid.csscrates/biome_css_analyze/tests/specs/nursery/noUnusedClasses/tsx_valid.tsxcrates/biome_css_analyze/tests/specs/nursery/noUnusedClasses/valid.csscrates/biome_css_analyze/tests/specs/nursery/noUnusedClasses/valid.jsxcrates/biome_css_analyze/tests/specs/nursery/noUnusedStyles/invalid.csscrates/biome_css_analyze/tests/specs/nursery/noUnusedStyles/valid.csscrates/biome_css_syntax/src/file_source.rscrates/biome_css_syntax/src/lib.rscrates/biome_css_syntax/src/selector_ext.rscrates/biome_html_analyze/src/lint/nursery/no_undeclared_classes.rscrates/biome_html_analyze/tests/spec_tests.rscrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-linked-stylesheet/invalid.htmlcrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-linked-stylesheet/styles.csscrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-scoped-styles/invalid.vuecrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-upward-traversal-astro/App.astrocrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-upward-traversal-astro/Button.astrocrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-upward-traversal-astro/Page.astrocrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-upward-traversal-astro/app.csscrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-upward-traversal-vue/App.vuecrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-upward-traversal-vue/Button.vuecrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-upward-traversal-vue/Page.vuecrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-upward-traversal-vue/app.csscrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/invalid.vuecrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/valid-linked-stylesheet/styles.csscrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/valid-linked-stylesheet/valid.htmlcrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/valid-scoped-styles/valid.vuecrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/valid.astrocrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/valid.htmlcrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/valid.sveltecrates/biome_html_analyze/tests/specs/nursery/noUndeclaredClasses/valid.vuecrates/biome_html_syntax/src/element_ext.rscrates/biome_js_analyze/src/lint/nursery/no_undeclared_classes.rscrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/button.csscrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-direct-import/directImport.jsxcrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/invalid-direct-import/styles.csscrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/invalid.jsxcrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/multi-level-chain/level1.csscrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/multi-level-chain/level2.csscrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/multi-level-chain/level2.jsxcrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/multi-level-chain/level3.csscrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/multi-level-chain/level3.jsxcrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/multi-level-chain/level4.csscrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/multi-level-chain/level4.jsxcrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/multi-level-chain/multilevel.jsxcrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/upward-traversal/App.jsxcrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/upward-traversal/Block.jsxcrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/upward-traversal/Button.jsxcrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/upward-traversal/Page.jsxcrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/upward-traversal/app.csscrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/valid/styles.csscrates/biome_js_analyze/tests/specs/nursery/noUndeclaredClasses/valid/valid.jsxcrates/biome_module_graph/Cargo.tomlcrates/biome_module_graph/src/css_module_info/mod.rscrates/biome_module_graph/src/css_module_info/traverse.rscrates/biome_module_graph/src/css_module_info/visitor.rscrates/biome_module_graph/src/format_module_graph.rscrates/biome_module_graph/src/html_module_info/mod.rscrates/biome_module_graph/src/html_module_info/visitor.rscrates/biome_module_graph/src/js_module_info/visitor.rscrates/biome_module_graph/src/lib.rscrates/biome_module_graph/src/module_graph.rscrates/biome_module_graph/tests/snap/mod.rscrates/biome_module_graph/tests/spec_tests.rscrates/biome_rule_options/src/lib.rscrates/biome_rule_options/src/no_undeclared_classes.rscrates/biome_rule_options/src/no_unused_classes.rscrates/biome_service/Cargo.tomlcrates/biome_service/src/file_handlers/html.rscrates/biome_service/src/lib.rscrates/biome_service/src/test_utils.rscrates/biome_service/src/workspace/document/mod.rscrates/biome_service/src/workspace/server.rscrates/biome_service/src/workspace/server.tests.rscrates/biome_test_utils/Cargo.tomlcrates/biome_test_utils/src/lib.rs
💤 Files with no reviewable changes (2)
- crates/biome_css_analyze/tests/specs/nursery/noUnusedStyles/invalid.css
- crates/biome_css_analyze/tests/specs/nursery/noUnusedStyles/valid.css
dyc3
left a comment
There was a problem hiding this comment.
Tests look good. Just a couple nits
There was a problem hiding this comment.
🧹 Nitpick comments (2)
crates/biome_html_syntax/src/element_ext.rs (1)
297-299: Please add a brief rustdoc foris_style_scoped.Line 297 adds a public helper; a short doc comment would clarify intended attribute names (
scoped,global, framework-specific variants) for callers.Suggested tweak
+ /// Returns `true` when this is a `<style>` tag and `attribute_name` is present + /// on its opening tag (for example, `scoped` or framework-specific style flags). pub fn is_style_scoped(&self, attribute_name: &str) -> bool { self.is_style_tag() && self.find_attribute_by_name(attribute_name).is_some() }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_html_syntax/src/element_ext.rs` around lines 297 - 299, Add a short Rust doc comment for the public method is_style_scoped explaining its purpose and the expected attribute names it checks (e.g., "scoped", "global", and any framework-specific variants), and mention that it returns true only for style tags when find_attribute_by_name(attribute_name) is Some. Update the doc above pub fn is_style_scoped(&self, attribute_name: &str) -> bool to reference related helpers is_style_tag and find_attribute_by_name for context.crates/biome_module_graph/tests/snap/mod.rs (1)
65-69: Stabilise file ordering before rendering snapshots.Snapshot output currently depends on incoming
filesorder. Sorting once here will prevent order-based flakes across callers and environments.Suggested patch
- let files: Vec<_> = self.files.clone(); + let mut files: Vec<_> = self.files.iter().collect(); + files.sort_unstable_by(|(left, _), (right, _)| left.cmp(right)); let dependency_data = self.module_graph.data(); - for (file_name, source_code) in &files { + for (file_name, source_code) in files { let file_name = Utf8PathBuf::from(file_name.as_str());🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_module_graph/tests/snap/mod.rs` around lines 65 - 69, The snapshot flakiness is caused by relying on the incoming order of self.files; fix it by sorting the cloned files Vec before rendering. After let files: Vec<_> = self.files.clone(); sort the files collection (e.g., using files.sort_unstable_by_key(|(name, _)| name.clone()) or a stable sort if needed) so the subsequent loop for (file_name, source_code) in &files produces deterministic ordering for snapshots.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@crates/biome_html_syntax/src/element_ext.rs`:
- Around line 297-299: Add a short Rust doc comment for the public method
is_style_scoped explaining its purpose and the expected attribute names it
checks (e.g., "scoped", "global", and any framework-specific variants), and
mention that it returns true only for style tags when
find_attribute_by_name(attribute_name) is Some. Update the doc above pub fn
is_style_scoped(&self, attribute_name: &str) -> bool to reference related
helpers is_style_tag and find_attribute_by_name for context.
In `@crates/biome_module_graph/tests/snap/mod.rs`:
- Around line 65-69: The snapshot flakiness is caused by relying on the incoming
order of self.files; fix it by sorting the cloned files Vec before rendering.
After let files: Vec<_> = self.files.clone(); sort the files collection (e.g.,
using files.sort_unstable_by_key(|(name, _)| name.clone()) or a stable sort if
needed) so the subsequent loop for (file_name, source_code) in &files produces
deterministic ordering for snapshots.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
packages/@biomejs/backend-jsonrpc/src/workspace.tsis excluded by!**/backend-jsonrpc/src/workspace.tsand included by**packages/@biomejs/biome/configuration_schema.jsonis excluded by!**/configuration_schema.jsonand included by**
📒 Files selected for processing (7)
crates/biome_css_syntax/src/file_source.rscrates/biome_css_syntax/src/selector_ext.rscrates/biome_html_syntax/src/element_ext.rscrates/biome_module_graph/Cargo.tomlcrates/biome_module_graph/tests/snap/mod.rscrates/biome_rule_options/src/no_undeclared_classes.rscrates/biome_service/src/lib.rs
🚧 Files skipped from review as they are similar to previous changes (3)
- crates/biome_css_syntax/src/selector_ext.rs
- crates/biome_service/src/lib.rs
- crates/biome_module_graph/Cargo.toml
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
crates/biome_module_graph/src/html_module_info/mod.rs (1)
96-103: Doc comment is stale after the type change.The text still says each entry is a
TokenText, but the field now storesCssClassDefinition(name + applicability).Suggested doc tweak
- /// Each `TokenText` represents a single class name (e.g., "header" from `.header`). + /// Each `CssClassDefinition` stores a class name (e.g., "header" from `.header`) + /// plus its applicability (Local/Global/Unknown).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_module_graph/src/html_module_info/mod.rs` around lines 96 - 103, The doc comment for the field style_classes is stale: it still claims each entry is a TokenText but the field type is now IndexSet<CssClassDefinition>. Update the comment on style_classes to describe that each entry is a CssClassDefinition containing the class name and applicability (or similar metadata) rather than TokenText, and remove or adjust the references to TokenText so the documentation matches the CssClassDefinition structure.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/biome_module_graph/src/html_module_info/visitor.rs`:
- Around line 68-80: The visitor currently only collects class attribute
references for HtmlElement in visit_html_element, while HtmlSelfClosingElement
nodes (handled by visit_self_closing_element) only handle stylesheets, causing
class="" on self-closing tags to be missed; update visit_self_closing_element
(or extract a shared attribute collector used by both visit_html_element and
visit_self_closing_element) to also extract and add class names into
referenced_classes (and ensure the same parsing logic used for classes in
visit_html_element is reused) — also apply the same change for the second
occurrence handling mentioned around the 167-195 range so self-closing elements
there also contribute to referenced_classes.
---
Nitpick comments:
In `@crates/biome_module_graph/src/html_module_info/mod.rs`:
- Around line 96-103: The doc comment for the field style_classes is stale: it
still claims each entry is a TokenText but the field type is now
IndexSet<CssClassDefinition>. Update the comment on style_classes to describe
that each entry is a CssClassDefinition containing the class name and
applicability (or similar metadata) rather than TokenText, and remove or adjust
the references to TokenText so the documentation matches the CssClassDefinition
structure.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
packages/@biomejs/backend-jsonrpc/src/workspace.tsis excluded by!**/backend-jsonrpc/src/workspace.tsand included by**packages/@biomejs/biome/configuration_schema.jsonis excluded by!**/configuration_schema.jsonand included by**
📒 Files selected for processing (8)
crates/biome_css_syntax/src/file_source.rscrates/biome_css_syntax/src/lib.rscrates/biome_module_graph/src/css_module_info/mod.rscrates/biome_module_graph/src/html_module_info/mod.rscrates/biome_module_graph/src/html_module_info/visitor.rscrates/biome_module_graph/src/lib.rscrates/biome_module_graph/tests/spec_tests.rscrates/biome_service/src/file_handlers/html.rs
🚧 Files skipped from review as they are similar to previous changes (1)
- crates/biome_module_graph/src/css_module_info/mod.rs
db999d4 to
b1dd59b
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
crates/biome_module_graph/tests/spec_tests.rs (1)
2899-3230:⚠️ Potential issue | 🟡 MinorDoc/test wording still disagrees with actual behaviour.
Line 2899 says plain HTML
<style>is “always Global”, but Line 2962 assertsUnknown. Likewise, names like “are_global/hidden” in this block conflict with assertions that local classes appear in same-file traversal (for example Lines 3079, 3131, 3161, 3224). Please align names/comments with what is asserted.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_module_graph/tests/spec_tests.rs` around lines 2899 - 3230, The docstrings/test names/comments misstate behavior: plain HTML inline style applicability is EmbeddingStyleApplicability::Unknown (not "always Global") and many tests that say "hidden" actually assert Local classes still appear in same-file traversal; update the wording to match assertions. Edit the html_css_source doc comment and the test name/comment for test_html_inline_style_classes_are_global to reflect "Unknown" applicability (or rename to test_html_inline_style_classes_are_unknown), and change comments/names that claim "hidden"/"are_hidden" (e.g., test_astro_local_style_classes_are_hidden, test_svelte_local_style_classes_are_hidden, test_vue_scoped_style_classes_are_local_and_hidden) to indicate "Local (hidden from cross-file traversal but present in same-file traversal)" or similar consistent phrasing so the test names and inline comments match the assertions using EmbeddingStyleApplicability and traverse_import_tree_for_html_classes behavior.
🧹 Nitpick comments (1)
crates/biome_module_graph/src/html_module_info/visitor.rs (1)
145-168: Consider a shared class-attribute collector for both element kinds.The class extraction logic is duplicated across
visit_html_elementandvisit_self_closing_element; extracting one helper would reduce drift and prevent this bug class from reappearing.Also applies to: 183-201
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_module_graph/src/html_module_info/visitor.rs` around lines 145 - 168, Duplicate class-attribute extraction appears in visit_html_element and visit_self_closing_element; refactor by extracting a helper (e.g., collect_class_attrs_from_opening) that takes the opening node (the value used in opening.attributes()/opening) plus &self.file_path and referenced_classes and encapsulates the logic currently inside both loops (checking attr.as_html_attribute(), getting name_token via attr.name().ok().and_then(|name| name.value_token().ok()), comparing name_text.eq_ignore_ascii_case("class"), then calling collect_class_attribute_reference on the initializer/value_node). Replace the duplicated blocks in visit_html_element and visit_self_closing_element with a single call to this helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/biome_module_graph/src/html_module_info/visitor.rs`:
- Around line 269-275: The rustdoc applicability table in visitor.rs incorrectly
states that plain HTML `<style>` maps to `Global`; update the documentation in
the comment block in crates::biome_module_graph::html_module_info::visitor (the
rustdoc table starting at the applicability list) to mark "Plain HTML `<style>`"
as `Unknown` to match current behaviour/tests so the docs reflect reality.
---
Duplicate comments:
In `@crates/biome_module_graph/tests/spec_tests.rs`:
- Around line 2899-3230: The docstrings/test names/comments misstate behavior:
plain HTML inline style applicability is EmbeddingStyleApplicability::Unknown
(not "always Global") and many tests that say "hidden" actually assert Local
classes still appear in same-file traversal; update the wording to match
assertions. Edit the html_css_source doc comment and the test name/comment for
test_html_inline_style_classes_are_global to reflect "Unknown" applicability (or
rename to test_html_inline_style_classes_are_unknown), and change comments/names
that claim "hidden"/"are_hidden" (e.g.,
test_astro_local_style_classes_are_hidden,
test_svelte_local_style_classes_are_hidden,
test_vue_scoped_style_classes_are_local_and_hidden) to indicate "Local (hidden
from cross-file traversal but present in same-file traversal)" or similar
consistent phrasing so the test names and inline comments match the assertions
using EmbeddingStyleApplicability and traverse_import_tree_for_html_classes
behavior.
---
Nitpick comments:
In `@crates/biome_module_graph/src/html_module_info/visitor.rs`:
- Around line 145-168: Duplicate class-attribute extraction appears in
visit_html_element and visit_self_closing_element; refactor by extracting a
helper (e.g., collect_class_attrs_from_opening) that takes the opening node (the
value used in opening.attributes()/opening) plus &self.file_path and
referenced_classes and encapsulates the logic currently inside both loops
(checking attr.as_html_attribute(), getting name_token via
attr.name().ok().and_then(|name| name.value_token().ok()), comparing
name_text.eq_ignore_ascii_case("class"), then calling
collect_class_attribute_reference on the initializer/value_node). Replace the
duplicated blocks in visit_html_element and visit_self_closing_element with a
single call to this helper.
ℹ️ Review info
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
packages/@biomejs/backend-jsonrpc/src/workspace.tsis excluded by!**/backend-jsonrpc/src/workspace.tsand included by**
📒 Files selected for processing (2)
crates/biome_module_graph/src/html_module_info/visitor.rscrates/biome_module_graph/tests/spec_tests.rs
| /// - Plain HTML `<style>` → `Global` | ||
| /// - Vue `<style>` (no `scoped`) → `Global` | ||
| /// - Vue `<style scoped>` → `Local` | ||
| /// - Astro `<style>` (default) → `Local` | ||
| /// - Astro `<style is:global>` → `Global` | ||
| /// - Svelte `<style>` (default) → `Local` | ||
| /// |
There was a problem hiding this comment.
Rustdoc applicability table is stale for plain HTML.
Line 269 says plain HTML <style> is Global, but current behaviour/tests treat it as Unknown. Please update the doc to match reality and avoid future confusion.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/biome_module_graph/src/html_module_info/visitor.rs` around lines 269 -
275, The rustdoc applicability table in visitor.rs incorrectly states that plain
HTML `<style>` maps to `Global`; update the documentation in the comment block
in crates::biome_module_graph::html_module_info::visitor (the rustdoc table
starting at the applicability list) to mark "Plain HTML `<style>`" as `Unknown`
to match current behaviour/tests so the docs reflect reality.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
crates/biome_module_graph/src/html_module_info/visitor.rs (1)
135-139: Consider removing the unused_imported_stylesheetsparameter.This parameter is never used because
<link>elements are self-closing and handled invisit_self_closing_element. If kept for API symmetry, a brief comment explaining the rationale would help future readers.♻️ Suggested simplification
fn visit_html_element( &self, element: HtmlElement, referenced_classes: &mut Vec<CssClassReference>, - _imported_stylesheets: &mut Vec<ResolvedPath>, ) {And update the call site at line 73-77:
self.visit_html_element( element, &mut referenced_classes, - &mut imported_stylesheets, );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/biome_module_graph/src/html_module_info/visitor.rs` around lines 135 - 139, The visit_html_element method currently accepts an unused parameter _imported_stylesheets; remove this parameter from the visit_html_element signature in visitor.rs and update all call sites (where Visitor::visit_html_element is invoked) to no longer pass the imported_stylesheets argument, or alternatively if you prefer API symmetry keep the parameter but rename it to _imported_stylesheets and add a short comment inside visit_html_element explaining that imported stylesheets are handled in visit_self_closing_element (e.g., "Imported <link> stylesheets are handled in visit_self_closing_element; parameter kept for symmetry"), and ensure any compiler warnings are resolved by using the underscore-prefixed name.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@crates/biome_module_graph/src/html_module_info/visitor.rs`:
- Around line 135-139: The visit_html_element method currently accepts an unused
parameter _imported_stylesheets; remove this parameter from the
visit_html_element signature in visitor.rs and update all call sites (where
Visitor::visit_html_element is invoked) to no longer pass the
imported_stylesheets argument, or alternatively if you prefer API symmetry keep
the parameter but rename it to _imported_stylesheets and add a short comment
inside visit_html_element explaining that imported stylesheets are handled in
visit_self_closing_element (e.g., "Imported <link> stylesheets are handled in
visit_self_closing_element; parameter kept for symmetry"), and ensure any
compiler warnings are resolved by using the underscore-prefixed name.
Summary
This PR continues the work of #9158
Note
The code has been written in a mixed way:
Rule renaming
The rules have been renamed to
noUndelcaredClassesandnoUnusedClasses. I updated the issues tooModule graph enhancement
The module graph has been updated to track:
Unknownbecause applicability is limited to embedded snippets for now, so I wanted to keep it scoped to that.Formattrait to the new types we've added so farnoUndelcaredClassesenhancementThe rule
noUndelcaredClassesfor HTML-ish languages has been enhanced to use the module graph. It now detects local and global styles that were declared locally and by the imported components.Test Plan
Added many tests
Docs
I will do that in a subsequent PR