Skip to content

fix(core): correctly parse vue directives as expressions#9584

Merged
ematipico merged 3 commits intomainfrom
test/more-workspace-tests
Mar 22, 2026
Merged

fix(core): correctly parse vue directives as expressions#9584
ematipico merged 3 commits intomainfrom
test/more-workspace-tests

Conversation

@ematipico
Copy link
Member

@ematipico ematipico commented Mar 22, 2026

Summary

This PR does two things:

  • uses the new analyze_with_workspace method in the HTML analyser. While doing so, I found some files that were incorrect.
  • While working on the porting, I found a few bugs which this PR fixes:
    • Some Vue directives fixed a parsing error when they are empty e.g. v-text=""
    • Some Vue directives couldn't parse v-bind:class="{ isActive: false }"
    • The fix was about introducing an allow_statements flag

Test Plan

The new infrastructure updates the code blocks in the snapshots, which explains the many changes. I also caught:

  • option.json files not correctly declared
  • files that didn't have the necessarary top-level comments

Docs

N/A

@changeset-bot
Copy link

changeset-bot bot commented Mar 22, 2026

🦋 Changeset detected

Latest commit: b6128e5

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

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

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

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

@github-actions github-actions bot added A-Project Area: project A-Linter Area: linter A-Parser Area: parser L-JavaScript Language: JavaScript and super languages L-HTML Language: HTML and super languages labels Mar 22, 2026
@ematipico ematipico requested review from a team March 22, 2026 13:17
@github-actions
Copy link
Contributor

Parser conformance results on

js/262

Test result main count This PR count Difference
Total 53139 53139 0
Passed 51919 51919 0
Failed 1178 1178 0
Panics 42 42 0
Coverage 97.70% 97.70% 0.00%

jsx/babel

Test result main count This PR count Difference
Total 38 38 0
Passed 37 37 0
Failed 1 1 0
Panics 0 0 0
Coverage 97.37% 97.37% 0.00%

markdown/commonmark

Test result main count This PR count Difference
Total 652 652 0
Passed 652 652 0
Failed 0 0 0
Panics 0 0 0
Coverage 100.00% 100.00% 0.00%

symbols/microsoft

Test result main count This PR count Difference
Total 5466 5466 0
Passed 1915 1915 0
Failed 3551 3551 0
Panics 0 0 0
Coverage 35.03% 35.03% 0.00%

ts/babel

Test result main count This PR count Difference
Total 636 636 0
Passed 567 567 0
Failed 69 69 0
Panics 0 0 0
Coverage 89.15% 89.15% 0.00%

ts/microsoft

Test result main count This PR count Difference
Total 18875 18875 0
Passed 13013 13013 0
Failed 5861 5861 0
Panics 1 1 0
Coverage 68.94% 68.94% 0.00%

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 22, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c04fb686-20c4-4b04-828c-367be85f5a8e

📥 Commits

Reviewing files that changed from the base of the PR and between 01a6b1f and b6128e5.

⛔ Files ignored due to path filters (1)
  • packages/@biomejs/backend-jsonrpc/src/workspace.ts is excluded by !**/backend-jsonrpc/src/workspace.ts and included by **
📒 Files selected for processing (1)
  • crates/biome_test_utils/src/lib.rs

Walkthrough

This PR fixes Vue directive attribute value parsing so directive values containing object literals (e.g., v-bind:class="{'dynamic': true}") are parsed as expressions rather than statements. It adds an allow_statements: bool field to EmbeddingKind::Vue to distinguish script-like embeds (statements allowed) from expression-only embeds (directive and text expressions). Test harness calls to analyze_with_workspace were updated to pass source text directly, and multiple test fixture comments were standardised to "should not generate diagnostics".

Possibly related PRs

Suggested labels

A-Core, A-Tooling

Suggested reviewers

  • dyc3
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change—fixing Vue directive parsing to correctly handle expressions like object literals in directives.
Description check ✅ Passed The description clearly outlines the two primary objectives: migration to analyze_with_workspace and fixes for Vue directive parsing bugs, with concrete examples provided.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/more-workspace-tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can generate a title for your PR based on the changes with custom instructions.

Set the reviews.auto_title_instructions setting to generate a title for your PR based on the changes in the PR with custom instructions.

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: 1

🧹 Nitpick comments (2)
crates/biome_html_analyze/tests/spec_tests.rs (1)

45-49: Minor observation: filter is now only used in the JSON scripts path.

The rule_filter and filter variables are constructed but only consumed by analyze_and_snap in the scripts_from_json branch. For the main path (line 70), analyze_with_workspace builds its own rule selector internally using group and rule.

This is fine and works correctly, but if you wanted to tidy up, these could be moved inside the if let Some(scripts) block. Not a blocker.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_html_analyze/tests/spec_tests.rs` around lines 45 - 49, The
variables rule_filter and filter (of type AnalysisFilter) are only used when
scripts are loaded from JSON and passed into analyze_and_snap, while
analyze_with_workspace constructs its own selector from group and rule; move the
construction of RuleFilter::Rule (rule_filter) and the AnalysisFilter (filter)
into the scripts_from_json branch (the if let Some(scripts) block) so they are
only created when needed and avoid unused allocations in the main
analyze_with_workspace path.
crates/biome_test_utils/src/lib.rs (1)

931-935: Minor nit: .to_string() on a String.

Line 932 uses input_code.to_string() which clones the string. Semantically correct (the clone is needed since input_code is used later), but .clone() would signal intent more clearly.

Not worth changing - just a style observation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_test_utils/src/lib.rs` around lines 931 - 935, The field
construction for FileContent::FromClient currently calls input_code.to_string(),
which performs a clone of an existing String; replace that with
input_code.clone() to make the intent explicit (i.e., in the struct initializer
for FileContent::FromClient use input_code.clone() for the content field while
leaving version and document_file_source unchanged).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.changeset/fix-vue-directive-expression-parsing.md:
- Line 5: Update the changeset entry text on line 5 of
.changeset/fix-vue-directive-expression-parsing.md to follow the bug-fix prefix
format by replacing the current free-form sentence with a line that begins with
"Fixed [`#NUMBER`](issue link): " followed by the existing description (e.g.,
"Fixed [`#NUMBER`](issue link): Fixed a bug where Vue directive attribute values
like `v-bind:class=\"{'dynamic': true}\"` were incorrectly parsed as JavaScript
statements instead of expressions..."). Ensure you include the issue number and
link in the square-bracket/parenthesis format before the rest of the message.

---

Nitpick comments:
In `@crates/biome_html_analyze/tests/spec_tests.rs`:
- Around line 45-49: The variables rule_filter and filter (of type
AnalysisFilter) are only used when scripts are loaded from JSON and passed into
analyze_and_snap, while analyze_with_workspace constructs its own selector from
group and rule; move the construction of RuleFilter::Rule (rule_filter) and the
AnalysisFilter (filter) into the scripts_from_json branch (the if let
Some(scripts) block) so they are only created when needed and avoid unused
allocations in the main analyze_with_workspace path.

In `@crates/biome_test_utils/src/lib.rs`:
- Around line 931-935: The field construction for FileContent::FromClient
currently calls input_code.to_string(), which performs a clone of an existing
String; replace that with input_code.clone() to make the intent explicit (i.e.,
in the struct initializer for FileContent::FromClient use input_code.clone() for
the content field while leaving version and document_file_source unchanged).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8bd0524c-1692-4275-9024-d7317004dd11

📥 Commits

Reviewing files that changed from the base of the PR and between ccb249e and 01a6b1f.

⛔ Files ignored due to path filters (80)
  • crates/biome_html_analyze/tests/specs/a11y/noRedundantAlt/invalid.html.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/noRedundantAlt/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/noRedundantAlt/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useAltText/vue/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/invalid.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/valid.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/svelte/invalid.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/svelte/valid.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/vue/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/vue/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useButtonType/invalid.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useButtonType/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useButtonType/valid.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useButtonType/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useIframeTitle/astro/invalid.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useIframeTitle/astro/valid.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useIframeTitle/svelte/invalid.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useIframeTitle/svelte/valid.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useIframeTitle/vue/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useIframeTitle/vue/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useMediaCaption/astro/invalid.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useMediaCaption/astro/valid.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useMediaCaption/svelte/invalid.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useMediaCaption/svelte/valid.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useMediaCaption/vue/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useMediaCaption/vue/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useValidLang/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useValidLang/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/noDuplicateAttributes/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/noDuplicateAttributes/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/noScriptUrl/invalid.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/noScriptUrl/invalid.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/noScriptUrl/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/noScriptUrl/valid.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/noScriptUrl/valid.html.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/noScriptUrl/valid.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/noScriptUrl/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/noVueVIfWithVFor/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/noVueVIfWithVFor/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useScopedStyles/invalid.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useScopedStyles/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useScopedStyles/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueConsistentVBindStyle/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueConsistentVBindStyle/longhand-invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueConsistentVBindStyle/longhand-valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueConsistentVBindStyle/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueConsistentVOnStyle/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueConsistentVOnStyle/longhand-invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueConsistentVOnStyle/longhand-valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueConsistentVOnStyle/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueHyphenatedAttributes/ignore/valid-ignored.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueHyphenatedAttributes/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueHyphenatedAttributes/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueVForKey/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueVForKey/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidTemplateRoot/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidTemplateRoot/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVBind/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVBind/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVCloak/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVCloak/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVElse/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVElse/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVElseIf/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVElseIf/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVHtml/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVHtml/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVIf/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVIf/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVOn/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVOn/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVOnce/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVOnce/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVPre/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVPre/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVText/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVText/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueVapor/invalid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/nursery/useVueVapor/valid.vue.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/source/noDuplicateClasses/valid.html.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (24)
  • .changeset/fix-vue-directive-expression-parsing.md
  • crates/biome_html_analyze/tests/spec_tests.rs
  • crates/biome_html_analyze/tests/specs/a11y/noRedundantAlt/invalid.html
  • crates/biome_html_analyze/tests/specs/a11y/noRedundantAlt/invalid.vue
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/valid.astro
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/svelte/valid.svelte
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/vue/valid.vue
  • crates/biome_html_analyze/tests/specs/nursery/noScriptUrl/valid.astro
  • crates/biome_html_analyze/tests/specs/nursery/noScriptUrl/valid.html
  • crates/biome_html_analyze/tests/specs/nursery/noScriptUrl/valid.svelte
  • crates/biome_html_analyze/tests/specs/nursery/noScriptUrl/valid.vue
  • crates/biome_html_analyze/tests/specs/nursery/useVueConsistentVBindStyle/longhand-invalid.options.json
  • crates/biome_html_analyze/tests/specs/nursery/useVueConsistentVBindStyle/longhand-valid.options.json
  • crates/biome_html_analyze/tests/specs/nursery/useVueConsistentVOnStyle/longhand-invalid.options.json
  • crates/biome_html_analyze/tests/specs/nursery/useVueConsistentVOnStyle/longhand-valid.options.json
  • crates/biome_html_analyze/tests/specs/nursery/useVueHyphenatedAttributes/ignore/valid-ignored.options.json
  • crates/biome_html_analyze/tests/specs/nursery/useVueValidVCloak/valid.vue
  • crates/biome_html_analyze/tests/specs/source/noDuplicateClasses/valid.html
  • crates/biome_js_analyze/tests/spec_tests.rs
  • crates/biome_js_parser/src/syntax/program.rs
  • crates/biome_js_syntax/src/file_source.rs
  • crates/biome_service/src/file_handlers/html.rs
  • crates/biome_service/src/file_handlers/vue.rs
  • crates/biome_test_utils/src/lib.rs
💤 Files with no reviewable changes (2)
  • crates/biome_html_analyze/tests/specs/a11y/noRedundantAlt/invalid.vue
  • crates/biome_html_analyze/tests/specs/a11y/noRedundantAlt/invalid.html

@codspeed-hq
Copy link

codspeed-hq bot commented Mar 22, 2026

Merging this PR will not alter performance

✅ 58 untouched benchmarks
⏩ 156 skipped benchmarks1


Comparing test/more-workspace-tests (b6128e5) with main (a62487a)

Open in CodSpeed

Footnotes

  1. 156 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@ematipico ematipico merged commit 956e367 into main Mar 22, 2026
21 of 22 checks passed
@ematipico ematipico deleted the test/more-workspace-tests branch March 22, 2026 14:51
This was referenced Mar 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Linter Area: linter A-Parser Area: parser A-Project Area: project L-HTML Language: HTML and super languages L-JavaScript Language: JavaScript and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants