Skip to content

feat(lint/vue): implement useVueMultiWordComponentNames#7373

Merged
dyc3 merged 5 commits intomainfrom
vue-multi-word-component-names
Sep 3, 2025
Merged

feat(lint/vue): implement useVueMultiWordComponentNames#7373
dyc3 merged 5 commits intomainfrom
vue-multi-word-component-names

Conversation

@dyc3
Copy link
Contributor

@dyc3 dyc3 commented Sep 1, 2025

Summary

This PR implements a new rule useVueMultiWordComponentNames based on https://eslint.vuejs.org/rules/multi-word-component-names.html

I opted to include the ignore option so that users have an escape hatch, and the logic is trivial to implement.

closes #6298

Test Plan

Added tests, based on https://github.com/vuejs/eslint-plugin-vue/blob/master/tests/lib/rules/multi-word-component-names.js

I couldn't just reuse all the test cases because our vue component query doesn't cover some cases that these do.

Docs

@changeset-bot
Copy link

changeset-bot bot commented Sep 1, 2025

⚠️ No Changeset found

Latest commit: 70484c8

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 1, 2025

Walkthrough

Refactors Vue component representation into an enum AnyVueComponent plus a path-carrying wrapper VueComponent<'a>, adds a component_name module exposing VueComponentName, moves from_potential_component onto AnyVueComponent and provides a wrapper constructor that accepts a file path, updates existing Vue lints to pass ctx.file_path(), and adds a new nursery lint UseVueMultiWordComponentNames with options, diagnostics and tests.

Assessment against linked issues

Objective Addressed Explanation
Implement vue/multi-word-component-names rule in Vue domain with diagnostics [#6298]
Infer component name from explicit name or from .vue file name when absent [#6298]
Support configuration to ignore specific single-word names [#6298]
Include built-in ignores consistent with rule behaviour [#6298]
Add tests covering valid/invalid cases and options [#6298]

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Rename/reshape core Vue component union to AnyVueComponent and introduce VueComponent<'a> wrapper (crates/biome_js_analyze/src/frameworks/vue/vue_component.rs) Public API refactor beyond the single-rule implementation; not required by issue #6298.
Add component_name module and public re-export VueComponentName (crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs) General naming utility that broadens public API surface; not strictly required for the rule.
Update other lints to pass ctx.file_path() (e.g. crates/biome_js_analyze/src/lint/nursery/no_vue_data_object_declaration.rs) Cross-cutting call-site changes due to API refactor, unrelated to the multi-word rule objective.

Possibly related PRs

Suggested labels

D-Vue

Suggested reviewers

  • ematipico
  • arendjr

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 71c20e6 and 70484c8.

📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (26)
  • GitHub Check: Documentation
  • GitHub Check: Check Dependencies
  • GitHub Check: End-to-end tests
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Test Node.js API
  • GitHub Check: Bench (biome_css_analyze)
  • GitHub Check: Check JS Files
  • GitHub Check: Bench (biome_graphql_formatter)
  • GitHub Check: Bench (biome_json_analyze)
  • GitHub Check: Bench (biome_package)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Bench (biome_html_parser)
  • GitHub Check: Bench (biome_html_formatter)
  • GitHub Check: Bench (biome_module_graph)
  • GitHub Check: Bench (biome_json_parser)
  • GitHub Check: Bench (biome_graphql_parser)
  • GitHub Check: Bench (biome_configuration)
  • GitHub Check: Bench (biome_css_formatter)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_css_parser)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_json_formatter)
  • GitHub Check: autofix
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch vue-multi-word-component-names

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added A-Core Area: core A-Project Area: project A-Linter Area: linter L-JavaScript Language: JavaScript and super languages A-Diagnostic Area: diagnostocis labels Sep 1, 2025
@dyc3 dyc3 marked this pull request as draft September 1, 2025 19:34
@github-actions
Copy link
Contributor

github-actions bot commented Sep 1, 2025

Parser conformance results on

js/262

Test result main count This PR count Difference
Total 50734 50734 0
Passed 49530 49530 0
Failed 1162 1162 0
Panics 42 42 0
Coverage 97.63% 97.63% 0.00%

jsx/babel

Test result main count This PR count Difference
Total 40 40 0
Passed 37 37 0
Failed 3 3 0
Panics 0 0 0
Coverage 92.50% 92.50% 0.00%

symbols/microsoft

Test result main count This PR count Difference
Total 6684 6684 0
Passed 2231 2231 0
Failed 4453 4453 0
Panics 0 0 0
Coverage 33.38% 33.38% 0.00%

ts/babel

Test result main count This PR count Difference
Total 825 825 0
Passed 732 732 0
Failed 93 93 0
Panics 0 0 0
Coverage 88.73% 88.73% 0.00%

ts/microsoft

Test result main count This PR count Difference
Total 18787 18787 0
Passed 14389 14389 0
Failed 4397 4397 0
Panics 1 1 0
Coverage 76.59% 76.59% 0.00%

@github-actions github-actions bot added the A-CLI Area: CLI label Sep 1, 2025
@codspeed-hq
Copy link

codspeed-hq bot commented Sep 1, 2025

CodSpeed Performance Report

Merging #7373 will not alter performance

Comparing vue-multi-word-component-names (70484c8) with main (099507e)

Summary

✅ 133 untouched benchmarks

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (8)
crates/biome_rowan/src/token_text.rs (1)

60-63: Clarify that the returned range is relative; add inline hint.

Small doc tweak for accuracy and a tiny nudge to the optimiser.

-    /// Returns the range of the text
-    pub fn range(&self) -> TextRange {
+    /// Returns the relative range of the selected text within this token
+    #[inline]
+    pub fn range(&self) -> TextRange {
         self.range
     }
crates/biome_rule_options/src/use_vue_multi_word_component_names.rs (1)

7-12: Expose default in generated schema.

So downstream tooling sees the default ["App"], add schemars default on the field.

     /// Defaults to ["App"], mirroring the common root component naming convention.
-    #[serde(skip_serializing_if = "Vec::is_empty")]
+    #[serde(skip_serializing_if = "Vec::is_empty")]
+    #[cfg_attr(feature = "schema", schemars(default = "default_ignores"))]
     pub ignores: Vec<String>,
crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.vue (1)

1-4: Nice negative case; add one for the default ignore.

Consider adding App.vue to assert the built‑in ignore works as expected.

Example:

<!-- crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/App.vue -->
<script>
export default {}
</script>
crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid-kebab-case.vue (1)

1-5: LGTM: kebab-case FromPath happy-path

Optional: add an App.vue fixture to assert the default ignore works end-to-end.

crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid.js (1)

1-22: Solid coverage of “no diagnostic” cases.

Optional: add KeepAlive and Teleport to exercise more built-in exemptions.

crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs (1)

248-258: Minor: this assertion passes due to multi-word, not the built-in list.

"transition-group" is multi-word, so it wouldn’t be reported regardless of builtin ignores. Consider adding a single-word built-in (e.g. "Transition", "Teleport") here for a stronger signal.

crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (2)

23-26: Module split and re-export look tidy

If VueComponentName isn’t needed outside this crate, consider pub(crate) to keep the public surface lean.


65-69: Consider deriving Debug for VueComponent

Handy for diagnostics and tests; no behavioural change.

Apply:

+#[derive(Debug)]
 pub struct VueComponent<'a> {
     kind: AnyVueComponent,
     path: &'a Utf8Path,
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 963a246 and cb51735.

⛔ Files ignored due to path filters (11)
  • crates/biome_configuration/src/analyzer/linter/rules.rs is excluded by !**/rules.rs and included by **
  • crates/biome_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_js_analyze/src/lint/nursery.rs is excluded by !**/nursery.rs and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/ValidPascalCase.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid-kebab-case.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid.js.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (15)
  • crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (4 hunks)
  • crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_data_object_declaration.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_keys.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_props.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/ValidPascalCase.vue (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.vue (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid-kebab-case.vue (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid.js (1 hunks)
  • crates/biome_rowan/src/token_text.rs (1 hunks)
  • crates/biome_rule_options/src/lib.rs (1 hunks)
  • crates/biome_rule_options/src/use_vue_multi_word_component_names.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.js
  • crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_keys.rs
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid.js
  • crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_props.rs
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid-kebab-case.vue
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.vue
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/ValidPascalCase.vue
  • crates/biome_js_analyze/src/lint/nursery/no_vue_data_object_declaration.rs
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue
  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
  • crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs
  • crates/biome_js_analyze/src/frameworks/vue/vue_component.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.js
  • crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_keys.rs
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid.js
  • crates/biome_rowan/src/token_text.rs
  • crates/biome_rule_options/src/use_vue_multi_word_component_names.rs
  • crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_props.rs
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid-kebab-case.vue
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.vue
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/ValidPascalCase.vue
  • crates/biome_js_analyze/src/lint/nursery/no_vue_data_object_declaration.rs
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue
  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
  • crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs
  • crates/biome_js_analyze/src/frameworks/vue/vue_component.rs
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.js
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid.js
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid-kebab-case.vue
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.vue
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/ValidPascalCase.vue
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format Rust and TOML files before committing (use just f/just format).

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_keys.rs
  • crates/biome_rowan/src/token_text.rs
  • crates/biome_rule_options/src/use_vue_multi_word_component_names.rs
  • crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_props.rs
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_js_analyze/src/lint/nursery/no_vue_data_object_declaration.rs
  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
  • crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs
  • crates/biome_js_analyze/src/frameworks/vue/vue_component.rs
🧠 Learnings (7)
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/tests/specs/**/{invalid*,valid*}.{js,jsx,ts,tsx,css,graphql,jsonc} : Place snapshot test cases under `tests/specs/<group>/<ruleName>/` using files prefixed with `invalid` and `valid`

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.js
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid.js
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid-kebab-case.vue
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.vue
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/ValidPascalCase.vue
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/biome_rule_options/lib/**/*.rs : Define per-rule options in the `biome_rule_options` crate under `lib/`, with serde- and schemars-compatible derives and `#[serde(rename_all = "camelCase", deny_unknown_fields, default)]`

Applied to files:

  • crates/biome_rule_options/src/use_vue_multi_word_component_names.rs
  • crates/biome_rule_options/src/lib.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/lint/nursery/*.rs : For new JavaScript lint rules generated by `just new-js-lintrule`, implement the rule in the generated file under `biome_js_analyze/lib/src/lint/nursery/`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : In `declare_lint_rule!` declarations, set `version: "next"`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : Use `domains` in `declare_lint_rule!` when applicable; recommended rules with domains enable only when the domain is active

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : If a rule provides a code action, add `fix_kind` in `declare_lint_rule!` and use `ctx.action_category(ctx.category(), ctx.group())` and `ctx.metadata().applicability()` when constructing actions

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : When porting from other linters, declare `sources: &[RuleSource::<...>]` in `declare_lint_rule!` using `.same()` or `.inspired()` as appropriate

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
🧬 Code graph analysis (5)
crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_keys.rs (1)
crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (2)
  • from_potential_component (79-91)
  • from_potential_component (132-185)
crates/biome_rowan/src/token_text.rs (3)
crates/biome_js_analyze/src/lint/nursery/no_unresolved_imports.rs (1)
  • range (75-80)
crates/biome_deserialize/src/json.rs (2)
  • range (83-85)
  • range (238-240)
crates/biome_module_graph/src/js_module_info/scope.rs (1)
  • range (408-410)
crates/biome_js_analyze/src/lint/nursery/no_vue_data_object_declaration.rs (1)
crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_props.rs (1)
  • ctx (121-121)
crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs (4)
crates/biome_html_syntax/src/file_source.rs (1)
  • vue (85-89)
crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (4)
  • name (96-102)
  • from_potential_component (79-91)
  • from_potential_component (132-185)
  • new (71-73)
crates/biome_rowan/src/token_text.rs (2)
  • range (61-63)
  • new (40-43)
crates/biome_rule_options/src/use_vue_multi_word_component_names.rs (1)
  • default (19-23)
crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (2)
crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_keys.rs (1)
  • component (127-145)
crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_props.rs (1)
  • component (127-138)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (25)
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Check Dependencies
  • GitHub Check: End-to-end tests
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Documentation
  • GitHub Check: Test Node.js API
  • GitHub Check: Bench (biome_package)
  • GitHub Check: Bench (biome_configuration)
  • GitHub Check: Bench (biome_graphql_parser)
  • GitHub Check: Bench (biome_html_formatter)
  • GitHub Check: Bench (biome_json_parser)
  • GitHub Check: Bench (biome_json_analyze)
  • GitHub Check: Bench (biome_graphql_formatter)
  • GitHub Check: Bench (biome_module_graph)
  • GitHub Check: Bench (biome_css_parser)
  • GitHub Check: Bench (biome_html_parser)
  • GitHub Check: Bench (biome_json_formatter)
  • GitHub Check: Check JS Files
  • GitHub Check: Bench (biome_css_formatter)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_css_analyze)
  • GitHub Check: autofix
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Parser conformance
🔇 Additional comments (14)
crates/biome_rule_options/src/use_vue_multi_word_component_names.rs (1)

18-24: Default + serde(default) interplay – confirm intent.

With struct-level #[serde(default)] and this Default impl, deserialising without ignores yields ["App"]. That also means skip_serializing_if = "Vec::is_empty" won’t omit the field when serialising defaults. If you prefer minimal serialised configs, consider keeping the rule’s built-in default in the analyser and leaving the option default empty. Otherwise, all good—just double‑check this matches Biome’s conventions.

Would you like me to add a tiny deserialisation round‑trip test to lock this behaviour in?

crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_props.rs (1)

122-123: API usage is consistent with the new signature.

Passing ctx.file_path() as the fourth argument matches the updated VueComponent::from_potential_component API. Ship it.

crates/biome_rule_options/src/lib.rs (1)

356-356: Options module matches conventions
All checks passed: UseVueMultiWordComponentNamesOptions is present, derives include Serialize, serde attrs (rename_all = "camelCase", deny_unknown_fields, default) are correct, and default_ignores includes "App". Codegen should pick it up.

crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/ValidPascalCase.vue (1)

1-5: LGTM: verifies PascalCase filename inference

Good coverage for FromPath + PascalCase treated as multi-word.

crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue (1)

1-5: Solid negative case; consider asserting highlight spans

Nice explicit single-word name. If snapshots support it, assert the diagnostic points at the "invalid" token (now that TokenText::range is exposed).

crates/biome_js_analyze/src/lint/nursery/no_vue_data_object_declaration.rs (1)

123-128: Good call passing the file path into VueComponent::from_potential_component.

This aligns the call-site with the path-aware component model and keeps name inference consistent across rules.

crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs (2)

235-246: Update the test to reflect the improved acronym handling.

With the revised heuristic, MYComponent is correctly treated as multi-word.

-        assert!(!is_multi_word("MYComponent")); // Treated as single segment (no lower->upper boundary until after first lowercase)
+        assert!(is_multi_word("MYComponent")); // Acronym + word is multi-word

117-123: Remove TokenText::range() check
TokenText::range() computes its span by adding the stored offset to token.text_range(), yielding a source-absolute TextRange, so no change is needed.

crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (6)

17-17: Good call using Utf8Path

Avoids lossy conversions and matches the repo’s cross‑platform path story.


70-78: LGTM on ctor/accessor

Straightforward and readable.


107-129: Enum rename to AnyVueComponent reads clearer

Nice separation between “wrapper with path” and the inner “any kind” enum.


131-186: Component detection logic looks sound

Conservative checks, and defineProps gated by Vue embedding is a good touch. Clones are cheap on rowan nodes, so fine here.


255-266: Delegation impl is clean

Keeps VueComponent thin; zero behavioural drift.


268-289: Dispatch over variants: steady

Matches prior pattern; nothing alarming.

@dyc3 dyc3 force-pushed the vue-multi-word-component-names branch 3 times, most recently from 6dc4b13 to b483760 Compare September 2, 2025 18:36
@dyc3 dyc3 force-pushed the vue-multi-word-component-names branch from 3097e2f to c032373 Compare September 2, 2025 19:38
@dyc3 dyc3 marked this pull request as ready for review September 3, 2025 00:01
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
crates/biome_rule_options/src/use_vue_multi_word_component_names.rs (1)

7-9: Tighten the doc comment for clarity (tiny nit)

Clarify matching semantics to pre-empt user confusion.

Apply this doc-only tweak:

-    /// Component names to ignore (allowed to be single-word).
+    /// Component names to ignore (allowed to be single-word names).
+    /// Matches exact component names as derived by the rule (not glob/pattern).
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between cb51735 and fafa668.

⛔ Files ignored due to path filters (12)
  • crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs is excluded by !**/migrate/eslint_any_rule_to_biome.rs and included by **
  • crates/biome_configuration/src/analyzer/linter/rules.rs is excluded by !**/rules.rs and included by **
  • crates/biome_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_js_analyze/src/lint/nursery.rs is excluded by !**/nursery.rs and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/ValidPascalCase.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid-kebab-case.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid.js.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (15)
  • crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (4 hunks)
  • crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_data_object_declaration.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_keys.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_props.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/ValidPascalCase.vue (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.vue (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid-kebab-case.vue (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid.js (1 hunks)
  • crates/biome_rowan/src/token_text.rs (1 hunks)
  • crates/biome_rule_options/src/lib.rs (1 hunks)
  • crates/biome_rule_options/src/use_vue_multi_word_component_names.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (13)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid.js
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/ValidPascalCase.vue
  • crates/biome_rowan/src/token_text.rs
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.js
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.vue
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid-kebab-case.vue
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs
  • crates/biome_js_analyze/src/lint/nursery/no_vue_data_object_declaration.rs
  • crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_keys.rs
  • crates/biome_js_analyze/src/frameworks/vue/vue_component.rs
  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format Rust and TOML files before committing (use just f/just format).

Files:

  • crates/biome_rule_options/src/use_vue_multi_word_component_names.rs
  • crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_props.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_rule_options/src/use_vue_multi_word_component_names.rs
  • crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_props.rs
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_props.rs
🧠 Learnings (1)
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/biome_rule_options/lib/**/*.rs : Define per-rule options in the `biome_rule_options` crate under `lib/`, with serde- and schemars-compatible derives and `#[serde(rename_all = "camelCase", deny_unknown_fields, default)]`

Applied to files:

  • crates/biome_rule_options/src/use_vue_multi_word_component_names.rs
🔇 Additional comments (2)
crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_props.rs (1)

118-123: Passing ctx.file_path() into the component resolver — LGTM

This unblocks file-name–based name derivation and aligns this rule with the new Vue component APIs. Nicely plumbed.

crates/biome_rule_options/src/use_vue_multi_word_component_names.rs (1)

1-10: Options struct looks correct and matches project conventions

Derives, serde config (camelCase, deny_unknown_fields, default), and visibility are spot on. Good to ship.

@dyc3 dyc3 requested review from a team September 3, 2025 11:33
@dyc3

This comment was marked as resolved.

Copy link
Contributor

@mdevils mdevils left a comment

Choose a reason for hiding this comment

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

Great job!

@dyc3 dyc3 force-pushed the vue-multi-word-component-names branch from fafa668 to 03c5c85 Compare September 3, 2025 11:44
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (1)

519-529: Methods are being classified as Computed.

collect_computed_and_method_declarations always wraps as Computed, so methods end up mislabelled. This can break filters and downstream rules.

Apply:

- fn collect_computed_and_method_declarations(expression: &AnyJsExpression) -> Vec<VueDeclaration> {
+ fn collect_object_member_methods(
+     expression: &AnyJsExpression,
+     wrap: fn(AnyVueMethod) -> VueDeclaration,
+ ) -> Vec<VueDeclaration> {
@@
-         .filter_map(|member| match member {
-             AnyJsObjectMember::JsPropertyObjectMember(property) => Some(VueDeclaration::Computed(
-                 AnyVueMethod::JsPropertyObjectMember(property),
-             )),
-             AnyJsObjectMember::JsMethodObjectMember(method) => Some(VueDeclaration::Computed(
-                 AnyVueMethod::JsMethodObjectMember(method),
-             )),
+         .filter_map(|member| match member {
+             AnyJsObjectMember::JsPropertyObjectMember(property) => {
+                 Some(wrap(AnyVueMethod::JsPropertyObjectMember(property)))
+             }
+             AnyJsObjectMember::JsMethodObjectMember(method) => {
+                 Some(wrap(AnyVueMethod::JsMethodObjectMember(method)))
+             }
              _ => None,
          })

And update call sites:

@@
-                    if let Ok(expression) = property.value() {
-                        result.extend(collect_computed_and_method_declarations(&expression));
+                    if let Ok(expression) = property.value() {
+                        result.extend(
+                            collect_object_member_methods(&expression, VueDeclaration::Computed)
+                        );
                     }
@@
-                    if let Ok(expression) = property.value() {
-                        result.extend(collect_computed_and_method_declarations(&expression));
+                    if let Ok(expression) = property.value() {
+                        result.extend(
+                            collect_object_member_methods(&expression, VueDeclaration::Method)
+                        );
                     }

Also applies to: 531-541, 910-928

♻️ Duplicate comments (1)
crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (1)

90-105: Tests for name precedence and JS/TS fallback (dup of earlier request).

If not already in this PR, please add/confirm tests: explicit name > component def > SFC filename; and no filename fallback for .js/.ts.

🧹 Nitpick comments (4)
crates/biome_rowan/src/token_text.rs (1)

60-64: Clarify that this range is token-relative (avoid confusion with absolute ranges)

Across the codebase, range() often means an absolute document range. A tiny doc tweak here will save future head‑scratching.

Apply this diff to the doc comment:

-    /// Returns the range of the text
+    /// Returns the relative range within the underlying token's text.
+    ///
+    /// Note: This is not an absolute document range. To obtain an absolute range,
+    /// offset this by the containing `SyntaxToken`'s `text_range().start()`.
crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/with-ignore/options.json (2)

10-13: Add a kebab-case ignore test for completeness

If the rule normalises names, consider a case using foo.vue or name: 'foo' to confirm "Foo" in ignores also covers kebab/file-derived names (or explicitly doesn’t).

Happy to draft an extra fixture (valid/invalid) if you confirm expected behaviour.


3-5: Minor: linter.enabled may be redundant in specs

If the test harness enables linting by default, this field can be omitted to keep the spec minimal. No need to change if you prefer explicitness.

crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (1)

90-105: Make the .vue extension check case-insensitive (tiny nit).

A small guard against odd casing on some filesystems.

Apply:

-                if self.path.extension() == Some("vue") {
+                if self
+                    .path
+                    .extension()
+                    .is_some_and(|ext| ext.eq_ignore_ascii_case("vue"))
+                {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between fafa668 and 03c5c85.

⛔ Files ignored due to path filters (13)
  • crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs is excluded by !**/migrate/eslint_any_rule_to_biome.rs and included by **
  • crates/biome_configuration/src/analyzer/linter/rules.rs is excluded by !**/rules.rs and included by **
  • crates/biome_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_js_analyze/src/lint/nursery.rs is excluded by !**/nursery.rs and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/ValidPascalCase.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid-kebab-case.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/with-ignore/valid-ignored.js.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (13)
  • crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (4 hunks)
  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/ValidPascalCase.vue (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.vue (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid-kebab-case.vue (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/with-ignore/options.json (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/with-ignore/valid-ignored.js (1 hunks)
  • crates/biome_rowan/src/token_text.rs (1 hunks)
  • crates/biome_rule_options/src/lib.rs (1 hunks)
  • crates/biome_rule_options/src/use_vue_multi_word_component_names.rs (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/with-ignore/valid-ignored.js
🚧 Files skipped from review as they are similar to previous changes (9)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid-kebab-case.vue
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.js
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid.js
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.vue
  • crates/biome_rule_options/src/use_vue_multi_word_component_names.rs
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/ValidPascalCase.vue
  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format Rust and TOML files before committing (use just f/just format).

Files:

  • crates/biome_rowan/src/token_text.rs
  • crates/biome_js_analyze/src/frameworks/vue/vue_component.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_rowan/src/token_text.rs
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/with-ignore/options.json
  • crates/biome_js_analyze/src/frameworks/vue/vue_component.rs
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/with-ignore/options.json
  • crates/biome_js_analyze/src/frameworks/vue/vue_component.rs
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/with-ignore/options.json
🧠 Learnings (3)
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/lint/nursery/*.rs : For new JavaScript lint rules generated by `just new-js-lintrule`, implement the rule in the generated file under `biome_js_analyze/lib/src/lint/nursery/`

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/with-ignore/options.json
📚 Learning: 2025-08-11T11:48:27.774Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-08-11T11:48:27.774Z
Learning: Applies to crates/biome_formatter/biome_html_formatter/tests/specs/html/**/options.json : When non-default formatting options are needed for a test group, place an options.json (biome.json format) alongside the .html files in that folder

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/with-ignore/options.json
📚 Learning: 2025-08-05T14:43:29.581Z
Learnt from: dyc3
PR: biomejs/biome#7081
File: packages/@biomejs/biome/configuration_schema.json:7765-7781
Timestamp: 2025-08-05T14:43:29.581Z
Learning: The file `packages/biomejs/biome/configuration_schema.json` is auto-generated and should not be manually edited or reviewed for schema issues; any changes should be made at the code generation source.

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/with-ignore/options.json
🧬 Code graph analysis (2)
crates/biome_rowan/src/token_text.rs (3)
crates/biome_js_analyze/src/lint/nursery/no_unresolved_imports.rs (1)
  • range (75-80)
crates/biome_deserialize/src/json.rs (2)
  • range (83-85)
  • range (238-240)
crates/biome_module_graph/src/js_module_info/scope.rs (1)
  • range (408-410)
crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (3)
crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs (1)
  • component_name (7-49)
crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_props.rs (1)
  • component (127-138)
crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_keys.rs (1)
  • component (127-145)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (26)
  • GitHub Check: Check JS Files
  • GitHub Check: Documentation
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Bench (biome_package)
  • GitHub Check: Check Dependencies
  • GitHub Check: Bench (biome_graphql_formatter)
  • GitHub Check: Bench (biome_configuration)
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Test Node.js API
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Bench (biome_json_analyze)
  • GitHub Check: Bench (biome_module_graph)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_html_formatter)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_json_formatter)
  • GitHub Check: Bench (biome_html_parser)
  • GitHub Check: Bench (biome_css_formatter)
  • GitHub Check: Bench (biome_css_analyze)
  • GitHub Check: Bench (biome_graphql_parser)
  • GitHub Check: Bench (biome_css_parser)
  • GitHub Check: Bench (biome_json_parser)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: autofix
  • GitHub Check: Parser conformance
🔇 Additional comments (6)
crates/biome_rowan/src/token_text.rs (1)

60-64: LGTM: exposing range() unblocks precise diagnostics

This is a handy accessor and aligns with how TokenText is already used downstream.

crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/with-ignore/options.json (2)

7-14: Rule wiring and options look correct

Key name, level, and ignores structure align with the rule intent. Nice.


2-2: Ensure schema generation before tests
The $schema path correctly climbs seven levels to packages/@biomejs/biome/configuration_schema.json. That file is auto-generated (and so isn’t in source), not a path typo. No change needed here—just confirm your build/test pipeline produces configuration_schema.json prior to running these specs.

crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (3)

23-26: Nice split and re-export.

Good move isolating component_name and re-exporting VueComponentName.


65-88: Wrapper looks solid.

Carrying path alongside AnyVueComponent simplifies name resolution and keeps call-sites tidy.


134-189: Factory refactor reads well.

Centralising detection in AnyVueComponent::from_potential_component is cleaner and reduces duplication at call sites.

@ematipico
Copy link
Member

Approved it, but the docs must be addressed

@dyc3 dyc3 force-pushed the vue-multi-word-component-names branch from 2f7eed3 to 32a186b Compare September 3, 2025 14:08
@github-actions github-actions bot removed the A-Core Area: core label Sep 3, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (2)

518-541: Bug: methods are emitted as Computed

In both “computed” and “methods” branches you call the same collector, which returns VueDeclaration::Computed for everything. Methods end up misclassified.

Apply this diff to split collectors and tag correctly:

-                "computed" => {
+                "computed" => {
                     if !filter.contains(VueDeclarationCollectionFilter::Computed) {
                         continue;
                     }
                     let AnyJsObjectMember::JsPropertyObjectMember(property) = group_object_member
                     else {
                         continue;
                     };
                     if let Ok(expression) = property.value() {
-                        result.extend(collect_computed_and_method_declarations(&expression));
+                        result.extend(collect_computed_declarations(&expression));
                     }
                 }
                 "methods" => {
                     if !filter.contains(VueDeclarationCollectionFilter::Method) {
                         continue;
                     }
                     let AnyJsObjectMember::JsPropertyObjectMember(property) = group_object_member
                     else {
                         continue;
                     };
                     if let Ok(expression) = property.value() {
-                        result.extend(collect_computed_and_method_declarations(&expression));
+                        result.extend(collect_method_declarations(&expression));
                     }
                 }

910-928: Follow-up for the misclassification: split the collector

Replace the shared collector with two dedicated helpers.

-fn collect_computed_and_method_declarations(expression: &AnyJsExpression) -> Vec<VueDeclaration> {
+fn collect_computed_declarations(expression: &AnyJsExpression) -> Vec<VueDeclaration> {
     let AnyJsExpression::JsObjectExpression(object_expression) = expression else {
         return vec![];
     };
     object_expression
         .members()
         .iter()
         .flatten()
         .filter_map(|member| match member {
-            AnyJsObjectMember::JsPropertyObjectMember(property) => Some(VueDeclaration::Computed(
-                AnyVueMethod::JsPropertyObjectMember(property),
-            )),
-            AnyJsObjectMember::JsMethodObjectMember(method) => Some(VueDeclaration::Computed(
-                AnyVueMethod::JsMethodObjectMember(method),
-            )),
+            AnyJsObjectMember::JsPropertyObjectMember(property) => {
+                Some(VueDeclaration::Computed(AnyVueMethod::JsPropertyObjectMember(property)))
+            }
+            AnyJsObjectMember::JsMethodObjectMember(method) => {
+                Some(VueDeclaration::Computed(AnyVueMethod::JsMethodObjectMember(method)))
+            }
             _ => None,
         })
         .collect()
 }
+
+fn collect_method_declarations(expression: &AnyJsExpression) -> Vec<VueDeclaration> {
+    let AnyJsExpression::JsObjectExpression(object_expression) = expression else {
+        return vec![];
+    };
+    object_expression
+        .members()
+        .iter()
+        .flatten()
+        .filter_map(|member| match member {
+            AnyJsObjectMember::JsPropertyObjectMember(property) => {
+                Some(VueDeclaration::Method(AnyVueMethod::JsPropertyObjectMember(property)))
+            }
+            AnyJsObjectMember::JsMethodObjectMember(method) => {
+                Some(VueDeclaration::Method(AnyVueMethod::JsMethodObjectMember(method)))
+            }
+            _ => None,
+        })
+        .collect()
+}
♻️ Duplicate comments (5)
crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs (1)

27-37: Good fix: only literal name keys are inspected

The guard now correctly skips computed keys and non-name members. This addresses the earlier concern.

crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (1)

93-105: SFC-only filename fallback looks right

Nice gating of filename fallback to .vue. Prevents JS/TS files from being misnamed.

crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs (3)

219-286: Reuse the normaliser in is_multi_word for simpler, more robust logic.

This covers acronym→word splits and keeps underscore/hyphen edge cases consistent with tests.

-fn is_multi_word(name: &str) -> bool {
-    let mut segments = 0u8;
-    let mut chars = name.chars().peekable();
-
-    while let Some(ch) = chars.next() {
-        match ch {
-            '-' | '_' => {
-                // Explicit separators don't count as segments themselves
-
-                // because there is a separator, we know there is a segment after this
-                // no need to keep going
-                return true;
-            }
-            _ => {
-                // Start a new segment
-                segments += 1;
-                if segments > 1 {
-                    return true;
-                }
-
-                // Skip to the end of this segment
-                let mut prev_was_upper = ch.is_ascii_uppercase();
-                while let Some(&next_ch) = chars.peek() {
-                    match next_ch {
-                        '-' | '_' => {
-                            // End of segment due to separator
-                            break;
-                        }
-                        c if c.is_ascii_uppercase() => {
-                            if !prev_was_upper {
-                                // lowercase/digit -> uppercase: start new segment
-                                break;
-                            }
-                            // Check if this uppercase is followed by lowercase (like 'B' in "UIButton")
-                            chars.next(); // consume this uppercase char
-                            if let Some(&after_upper) = chars.peek()
-                                && after_upper.is_ascii_lowercase()
-                                && prev_was_upper
-                            {
-                                // This uppercase starts a new word (UI|Button pattern)
-                                segments += 1;
-                                if segments > 1 {
-                                    return true;
-                                }
-                                prev_was_upper = true;
-                                continue;
-                            }
-                            prev_was_upper = true;
-                        }
-                        _ => {
-                            // lowercase or digit - continue in same segment
-                            chars.next();
-                            prev_was_upper = false;
-                        }
-                    }
-                }
-            }
-        }
-        if segments > 1 {
-            return true;
-        }
-    }
-    false
-}
+fn is_multi_word(name: &str) -> bool {
+    // After normalisation, multi-word iff there is at least one hyphen.
+    // Note: we intentionally do not trim leading/trailing hyphens so inputs like "_Foo" / "Foo_" still count,
+    // matching the current tests.
+    normalise_name(name).contains('-')
+}

198-217: Fix ignores: normalise kebab/Pascal/snake before comparing (current tests will fail).

eq_ignore_ascii_case won’t match "FooBar" with "foo-bar". Canonicalise both sides first, then compare. This also future‑proofs acronym/underscore variants.

Apply this diff (adds a small normaliser and uses it in the comparisons):

@@
-/// Determines if the given component name should be reported (i.e. is invalid single-word).
-fn should_report(name: &str, options: &UseVueMultiWordComponentNamesOptions) -> bool {
+fn normalise_name(name: &str) -> String {
+    // Convert Camel/PascalCase and underscores to a kebab-like lowercase form.
+    let bytes = name.as_bytes();
+    let mut out = String::with_capacity(bytes.len() + 4);
+    for i in 0..bytes.len() {
+        let b = bytes[i];
+        match b {
+            b'-' | b'_' => {
+                if !out.ends_with('-') {
+                    out.push('-');
+                }
+            }
+            b'A'..=b'Z' => {
+                let prev = if i > 0 { Some(bytes[i - 1]) } else { None };
+                let next = if i + 1 < bytes.len() { Some(bytes[i + 1]) } else { None };
+                let prev_is_lower_or_digit =
+                    prev.map(|p| p.is_ascii_lowercase() || p.is_ascii_digit()).unwrap_or(false);
+                let prev_is_upper = prev.map(|p| p.is_ascii_uppercase()).unwrap_or(false);
+                let next_is_lower = next.map(|n| n.is_ascii_lowercase()).unwrap_or(false);
+                if !out.is_empty() && (prev_is_lower_or_digit || (prev_is_upper && next_is_lower)) && !out.ends_with('-') {
+                    out.push('-');
+                }
+                out.push((b as char).to_ascii_lowercase());
+            }
+            other => out.push((other as char).to_ascii_lowercase()),
+        }
+    }
+    out
+}
+
+/// Determines if the given component name should be reported (i.e. is invalid single-word).
+fn should_report(name: &str, options: &UseVueMultiWordComponentNamesOptions) -> bool {
     if name.is_empty() {
         return true; // invalid, not covered by ignores
     }
 
-    // We could binary search, but the list is so short that linear scan is probably faster
-    if BUILTIN_IGNORES.iter().any(|s| s.eq_ignore_ascii_case(name)) {
+    // We could binary search, but the list is so short that linear scan is probably faster
+    let canon = normalise_name(name);
+    if BUILTIN_IGNORES.iter().any(|s| normalise_name(s) == canon) {
         return false;
     }
 
     for user in &options.ignores {
-        if name.eq_ignore_ascii_case(user) {
+        if normalise_name(user) == canon {
             return false;
         }
     }
 
     // Report if NOT multi-word
     !is_multi_word(name)
 }

19-24: Doc precision: describe the actual heuristic (separators + case boundaries) and mention diagnostic note.

The “two capitals” rule misleads for myComponent. Let’s reflect the implemented behaviour.

-    /// A name is considered multi-word when:
-    /// - Kebab-case: contains at least one hyphen (`my-component`)
-    /// - PascalCase / CamelCase: contains at least two capital letters (`MyComponent`); single-cap names like `App` or `Foo` are rejected
+    /// A name is considered multi-word when:
+    /// - Kebab-/snake-case: contains at least one separator (`my-component`, `my_component`)
+    /// - PascalCase/CamelCase: a case boundary or acronym→word transition exists (e.g. `MyComponent`, `myComponent`, `MYComponent`)
@@
-    /// Component names are extracted from the `name` property in Options API components, or inferred from the file name if not explicitly set.
+    /// Component names are extracted from the `name` property in Options API components, or inferred from the file name if not explicitly set. Diagnostics indicate when the name was inferred.
🧹 Nitpick comments (6)
crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs (1)

39-49: Handle static template literals for name

Consider supporting backticked, no-substitution names (e.g. name: MyComp) alongside string literals, so we catch more valid cases. Safe to treat template-without-expressions as a string.

crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue (1)

1-5: Test case reads clearly

Good negative fixture with explicit name. If possible, assert the diagnostic range points to the string literal to keep UX crisp.

crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs (4)

61-71: Docs: add a CamelCase example (myComponent) to the Valid section.

Keeps examples aligned with the heuristic.

     /// ```js
     /// export default {
     ///   name: "my-component"
     /// };
     /// ```
+    ///
+    /// ```js
+    /// export default {
+    ///   name: "myComponent"
+    /// };
+    /// ```

81-82: Option docs: clarify matching is case- and style-insensitive.

Ignores will match Pascal/kebab/snake variants after your normalisation.

-    /// Additional single-word component names to ignore (case-insensitive). The rule already ignores Vue built-in components and `App` by default.
+    /// Additional single-word component names to ignore. Matching is case- and style-insensitive (PascalCase / kebab-case / snake_case). The rule already ignores Vue built-in components and `App` by default.

165-173: Diagnostic copy: small grammar/polish tweak.

Lower-case “component” and “contains only” reads better; “two or more” instead of “2 or more”.

-                "This Component's name "<Emphasis>"\""{component_name}"\""</Emphasis>" only contains one word."
+                "This component's name "<Emphasis>"\""{component_name}"\""</Emphasis>" contains only one word."
@@
-            "Rename the component to have 2 or more words (e.g. \"FooItem\", or \"BarView\")."
+            "Rename the component to use two or more words (e.g. \"FooItem\" or \"BarView\")."

288-350: Nice test coverage; add one for snake_case ignore to lock in normalisation.

Optional but cheap win.

    #[test]
    fn test_should_report_user_ignores_snake_case() {
        let mut options = UseVueMultiWordComponentNamesOptions::default();
        options.ignores.push("foo_bar".to_string()); // snake_case
        assert!(!should_report("FooBar", &options));   // PascalCase variant
        assert!(!should_report("foo-bar", &options));  // kebab-case variant
        assert!(!should_report("foo_bar", &options));  // snake_case itself
    }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2f7eed3 and 32a186b.

⛔ Files ignored due to path filters (13)
  • crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs is excluded by !**/migrate/eslint_any_rule_to_biome.rs and included by **
  • crates/biome_configuration/src/analyzer/linter/rules.rs is excluded by !**/rules.rs and included by **
  • crates/biome_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_js_analyze/src/lint/nursery.rs is excluded by !**/nursery.rs and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/ValidPascalCase.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid-kebab-case.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid.js.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/with-ignore/valid-ignored.js.snap is excluded by !**/*.snap and included by **
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
  • packages/@biomejs/biome/configuration_schema.json is excluded by !**/configuration_schema.json and included by **
📒 Files selected for processing (13)
  • crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (4 hunks)
  • crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/ValidPascalCase.vue (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.vue (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid-kebab-case.vue (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid.js (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/with-ignore/options.json (1 hunks)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/with-ignore/valid-ignored.js (1 hunks)
  • crates/biome_rule_options/src/lib.rs (1 hunks)
  • crates/biome_rule_options/src/use_vue_multi_word_component_names.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (9)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/ValidPascalCase.vue
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid-kebab-case.vue
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/valid.js
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/with-ignore/valid-ignored.js
  • crates/biome_rule_options/src/lib.rs
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.vue
  • crates/biome_rule_options/src/use_vue_multi_word_component_names.rs
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.js
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/with-ignore/options.json
🧰 Additional context used
📓 Path-based instructions (4)
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue
  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
  • crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs
  • crates/biome_js_analyze/src/frameworks/vue/vue_component.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue
  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
  • crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs
  • crates/biome_js_analyze/src/frameworks/vue/vue_component.rs
**/tests/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place test files under a tests/ directory in each crate

Files:

  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format Rust and TOML files before committing (use just f/just format).

Files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
  • crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs
  • crates/biome_js_analyze/src/frameworks/vue/vue_component.rs
🧠 Learnings (8)
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/tests/specs/**/{invalid*,valid*}.{js,jsx,ts,tsx,css,graphql,jsonc} : Place snapshot test cases under `tests/specs/<group>/<ruleName>/` using files prefixed with `invalid` and `valid`

Applied to files:

  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/lint/nursery/*.rs : For new JavaScript lint rules generated by `just new-js-lintrule`, implement the rule in the generated file under `biome_js_analyze/lib/src/lint/nursery/`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : In `declare_lint_rule!` declarations, set `version: "next"`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : When porting from other linters, declare `sources: &[RuleSource::<...>]` in `declare_lint_rule!` using `.same()` or `.inspired()` as appropriate

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : Use `domains` in `declare_lint_rule!` when applicable; recommended rules with domains enable only when the domain is active

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : If a rule provides a code action, add `fix_kind` in `declare_lint_rule!` and use `ctx.action_category(ctx.category(), ctx.group())` and `ctx.metadata().applicability()` when constructing actions

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : Place all new rules in the nursery group (implement new rule files under a `nursery` directory)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : If a rule is deprecated, set the `deprecated:` field in `declare_lint_rule!` with a reason and alternative

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
🧬 Code graph analysis (3)
crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs (3)
crates/biome_html_syntax/src/file_source.rs (1)
  • vue (85-89)
crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (4)
  • name (93-105)
  • from_potential_component (79-88)
  • from_potential_component (135-188)
  • new (71-73)
crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs (1)
  • component_name (9-53)
crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs (2)
crates/biome_rowan/src/token_text.rs (7)
  • eq (98-100)
  • eq (104-106)
  • eq (110-112)
  • partial_cmp (29-31)
  • cmp (23-25)
  • deref (80-82)
  • as_ref (116-118)
crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (1)
  • name (93-105)
crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (3)
crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs (1)
  • component_name (9-53)
crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_props.rs (1)
  • component (127-138)
crates/biome_js_analyze/src/lint/nursery/no_vue_reserved_keys.rs (1)
  • component (127-145)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (22)
  • GitHub Check: Check JS Files
  • GitHub Check: Bench (biome_graphql_parser)
  • GitHub Check: Bench (biome_module_graph)
  • GitHub Check: Bench (biome_configuration)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_package)
  • GitHub Check: Bench (biome_graphql_formatter)
  • GitHub Check: Bench (biome_html_parser)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Bench (biome_json_analyze)
  • GitHub Check: Bench (biome_html_formatter)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_json_parser)
  • GitHub Check: Bench (biome_json_formatter)
  • GitHub Check: Bench (biome_css_formatter)
  • GitHub Check: Bench (biome_css_analyze)
  • GitHub Check: Bench (biome_css_parser)
  • GitHub Check: Test Node.js API
  • GitHub Check: Documentation
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: autofix
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
🔇 Additional comments (2)
crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs (2)

107-114: Rule declaration looks solid.

Version, domain, and source attribution are correct for a nursery rule; recommending it under the Vue domain makes sense.


123-142: Signal/state handling LGTM.

Clean separation between precise token vs file-stem fallback; good use of ctx.file_path().

@dyc3 dyc3 force-pushed the vue-multi-word-component-names branch from 32a186b to 0213bfa Compare September 3, 2025 14:18
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs (3)

19-24: Docs: clarify the heuristic (case boundaries, underscores) and keep it aligned with the implementation.

The current “two capitals” description is inaccurate and omits underscores. Please describe separators and case-boundary splitting.

Apply this diff:

-    /// A name is considered multi-word when:
-    /// - Kebab-case: contains at least one hyphen (`my-component`)
-    /// - PascalCase / CamelCase: contains at least two capital letters (`MyComponent`); single-cap names like `App` or `Foo` are rejected
+    /// A name is considered multi-word when:
+    /// - It contains an explicit separator: hyphen (`my-component`) or underscore (`my_component`), or
+    /// - It contains a case boundary: transitions from lowercase/digit to uppercase, and acronym→word splits (e.g. `UI` + `Button` in `UIButton`).

198-217: Ignores should match kebab/Pascal variants via normalisation.

eq_ignore_ascii_case won’t match "FooBar" vs "foo-bar". Normalise both sides before comparing.

Apply this diff:

+fn normalise_name(name: &str) -> String {
+    let bytes = name.as_bytes();
+    let mut out = String::with_capacity(bytes.len() + 4);
+    for i in 0..bytes.len() {
+        let b = bytes[i];
+        match b {
+            b'-' | b'_' => {
+                if !out.ends_with('-') { out.push('-'); }
+            }
+            b'A'..=b'Z' => {
+                let prev = i.checked_sub(1).map(|j| bytes[j]);
+                let next = (i + 1 < bytes.len()).then_some(bytes[i + 1]);
+                let prev_is_lower_or_digit = prev.map(|p| p.is_ascii_lowercase() || p.is_ascii_digit()).unwrap_or(false);
+                let prev_is_upper = prev.map(|p| p.is_ascii_uppercase()).unwrap_or(false);
+                let next_is_lower = next.map(|n| n.is_ascii_lowercase()).unwrap_or(false);
+                if !out.is_empty() && (prev_is_lower_or_digit || (prev_is_upper && next_is_lower)) && !out.ends_with('-') {
+                    out.push('-');
+                }
+                out.push((b as char).to_ascii_lowercase());
+            }
+            other => out.push((other as char).to_ascii_lowercase()),
+        }
+    }
+    out
+}
+
 fn should_report(name: &str, options: &UseVueMultiWordComponentNamesOptions) -> bool {
     if name.is_empty() {
         return true; // invalid, not covered by ignores
     }
 
-    // We could binary search, but the list is so short that linear scan is probably faster
-    if BUILTIN_IGNORES.iter().any(|s| s.eq_ignore_ascii_case(name)) {
+    // We could binary search, but the list is tiny; linear is fine.
+    let canon = normalise_name(name);
+    if BUILTIN_IGNORES.iter().any(|s| normalise_name(s) == canon) {
         return false;
     }
 
-    for user in &options.ignores {
-        if name.eq_ignore_ascii_case(user) {
+    for user in &options.ignores {
+        if normalise_name(user) == canon {
             return false;
         }
     }
 
     // Report if NOT multi-word
     !is_multi_word(name)
 }

219-286: Simplify multi-word detection by reusing the same normalisation logic.

This avoids edge-case drift (e.g. acronym→word splits) and keeps ignores and detection consistent.

Apply this diff:

-fn is_multi_word(name: &str) -> bool {
-    let mut segments = 0u8;
-    let mut chars = name.chars().peekable();
-
-    while let Some(ch) = chars.next() {
-        match ch {
-            '-' | '_' => {
-                // Explicit separators don't count as segments themselves
-
-                // because there is a separator, we know there is a segment after this
-                // no need to keep going
-                return true;
-            }
-            _ => {
-                // Start a new segment
-                segments += 1;
-                if segments > 1 {
-                    return true;
-                }
-
-                // Skip to the end of this segment
-                let mut prev_was_upper = ch.is_ascii_uppercase();
-                while let Some(&next_ch) = chars.peek() {
-                    match next_ch {
-                        '-' | '_' => {
-                            // End of segment due to separator
-                            break;
-                        }
-                        c if c.is_ascii_uppercase() => {
-                            if !prev_was_upper {
-                                // lowercase/digit -> uppercase: start new segment
-                                break;
-                            }
-                            // Check if this uppercase is followed by lowercase (like 'B' in "UIButton")
-                            chars.next(); // consume this uppercase char
-                            if let Some(&after_upper) = chars.peek()
-                                && after_upper.is_ascii_lowercase()
-                                && prev_was_upper
-                            {
-                                // This uppercase starts a new word (UI|Button pattern)
-                                segments += 1;
-                                if segments > 1 {
-                                    return true;
-                                }
-                                prev_was_upper = true;
-                                continue;
-                            }
-                            prev_was_upper = true;
-                        }
-                        _ => {
-                            // lowercase or digit - continue in same segment
-                            chars.next();
-                            prev_was_upper = false;
-                        }
-                    }
-                }
-            }
-        }
-        if segments > 1 {
-            return true;
-        }
-    }
-    false
-}
+fn is_multi_word(name: &str) -> bool {
+    // After normalisation, multi-word iff there is at least one hyphen.
+    // Note: we don't trim leading/trailing hyphens so `_Foo`/`Foo_` still count as multi-word,
+    // matching the tests in this file.
+    normalise_name(name).contains('-')
+}
🧹 Nitpick comments (3)
crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs (3)

106-114: Rule metadata looks solid; consider severity “warning” for a style rule.

This is a style/recommendation; error might be too harsh for many codebases.

Apply this diff if you agree:

-        severity: Severity::Error,
+        severity: Severity::Warning,

162-173: Tweak diagnostic phrasing.

Lowercase “component” and tighten the sentence.

Apply this diff:

-                "This Component's name "<Emphasis>"\""{component_name}"\""</Emphasis>" only contains one word."
+                "The component name "<Emphasis>"\""{component_name}"\""</Emphasis>" contains only one word."

292-350: Add tests to prove ignore normalisation and more edge cases.

Cover kebab↔Pascal ignore matching and a few boundaries.

Here’s a small extension:

@@
     fn test_should_report_user_ignores() {
         let mut options = UseVueMultiWordComponentNamesOptions::default();
         options.ignores.push("FooBar".to_string()); // PascalCase
         options.ignores.push("widget".to_string()); // lowercase
 
         // PascalCase ignore and its kebab-case variant
         assert!(!should_report("FooBar", &options));
-        assert!(!should_report("foo-bar", &options));
+        assert!(!should_report("foo-bar", &options)); // kebab should match Pascal ignore
 
         // Lowercase ignore
         assert!(!should_report("widget", &options));
+
+        // Kebab ignore matches Pascal input
+        options.ignores.push("keep-alive".to_string());
+        assert!(!should_report("KeepAlive", &options));
@@
     fn test_is_multi_word() {
+        // kebab↔Pascal boundaries
         assert!(is_multi_word("MyComponent"));
         assert!(is_multi_word("my-component"));
         assert!(is_multi_word("myComponent"));
         assert!(is_multi_word("MyAppRoot"));
         assert!(is_multi_word("MYComponent"));
+        assert!(is_multi_word("AAa")); // acronym + word
     }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 32a186b and 0213bfa.

⛔ Files ignored due to path filters (2)
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid-has-name.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_js_analyze/tests/specs/nursery/useVueMultiWordComponentNames/invalid.js.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (2)
  • crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs (1 hunks)
  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{rs,toml}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Format Rust and TOML files before committing (use just f/just format).

Files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
crates/biome_*_{syntax,parser,formatter,analyze,factory,semantic}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Maintain the per-language crate structure: biome_{lang}_{syntax,parser,formatter,analyze,factory,semantic}

Files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
crates/biome_*/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place core crates under /crates/biome_*/

Files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
🧠 Learnings (10)
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/biome_js_analyze/lib/src/lint/nursery/*.rs : For new JavaScript lint rules generated by `just new-js-lintrule`, implement the rule in the generated file under `biome_js_analyze/lib/src/lint/nursery/`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : In `declare_lint_rule!` declarations, set `version: "next"`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : When porting from other linters, declare `sources: &[RuleSource::<...>]` in `declare_lint_rule!` using `.same()` or `.inspired()` as appropriate

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : Use `domains` in `declare_lint_rule!` when applicable; recommended rules with domains enable only when the domain is active

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : If a rule provides a code action, add `fix_kind` in `declare_lint_rule!` and use `ctx.action_category(ctx.category(), ctx.group())` and `ctx.metadata().applicability()` when constructing actions

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : Place all new rules in the nursery group (implement new rule files under a `nursery` directory)

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : Choose an appropriate `severity` in `declare_lint_rule!` (default is info); use `Severity::Error` for hard errors, `Warning` for potential issues, `Information` for style

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : If a rule is deprecated, set the `deprecated:` field in `declare_lint_rule!` with a reason and alternative

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/**/lib/src/**/nursery/**/*.rs : Use the local `rule_category!()` macro in diagnostics for the rule’s category, not string-parsed categories

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
📚 Learning: 2025-08-17T08:56:30.831Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-08-17T08:56:30.831Z
Learning: Applies to crates/biome_analyze/crates/biome_rule_options/lib/**/*.rs : Define per-rule options in the `biome_rule_options` crate under `lib/`, with serde- and schemars-compatible derives and `#[serde(rename_all = "camelCase", deny_unknown_fields, default)]`

Applied to files:

  • crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs
🧬 Code graph analysis (1)
crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs (4)
crates/biome_html_syntax/src/file_source.rs (1)
  • vue (85-89)
crates/biome_js_analyze/src/frameworks/vue/vue_component.rs (4)
  • name (93-105)
  • from_potential_component (79-88)
  • from_potential_component (135-188)
  • new (71-73)
crates/biome_js_analyze/src/frameworks/vue/vue_component/component_name.rs (1)
  • component_name (9-51)
crates/biome_rowan/src/token_text.rs (1)
  • new (40-43)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (25)
  • GitHub Check: Test Node.js API
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: End-to-end tests
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Check Dependencies
  • GitHub Check: Bench (biome_package)
  • GitHub Check: Bench (biome_configuration)
  • GitHub Check: Bench (biome_json_analyze)
  • GitHub Check: Bench (biome_graphql_parser)
  • GitHub Check: Bench (biome_module_graph)
  • GitHub Check: Bench (biome_html_parser)
  • GitHub Check: Bench (biome_js_analyze)
  • GitHub Check: Bench (biome_graphql_formatter)
  • GitHub Check: Bench (biome_html_formatter)
  • GitHub Check: Bench (biome_css_analyze)
  • GitHub Check: Bench (biome_json_formatter)
  • GitHub Check: Bench (biome_js_formatter)
  • GitHub Check: Bench (biome_js_parser)
  • GitHub Check: Bench (biome_css_formatter)
  • GitHub Check: Bench (biome_json_parser)
  • GitHub Check: Bench (biome_css_parser)
  • GitHub Check: Check JS Files
  • GitHub Check: autofix
🔇 Additional comments (2)
crates/biome_js_analyze/src/lint/nursery/use_vue_multi_word_component_names.rs (2)

123-142: LGTM: query and name resolution flow is correct.

Path-aware VueComponent construction, name extraction, and state shaping are sound.


188-196: Verify built-in ignores match Vue’s built-ins.

Double-check parity with eslint-plugin-vue (e.g. whether template should be here, and whether anything is missing). It won’t change behaviour for multi-word names, but it avoids surprises.

I can cross-check upstream lists and open a follow-up if you want.

@dyc3 dyc3 force-pushed the vue-multi-word-component-names branch from 0084b2f to 71c20e6 Compare September 3, 2025 14:39
@dyc3 dyc3 merged commit 4416573 into main Sep 3, 2025
30 checks passed
@dyc3 dyc3 deleted the vue-multi-word-component-names branch September 3, 2025 15:14
dyc3 added a commit that referenced this pull request Oct 3, 2025
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-CLI Area: CLI A-Diagnostic Area: diagnostocis A-Linter Area: linter A-Project Area: project L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

📎 Port vue/multi-word-component-names from eslint-plugin-vue

3 participants