Skip to content

feat(js_analyze): implement noJsxLeakedSemicolon#9912

Open
Netail wants to merge 3 commits intobiomejs:mainfrom
Netail:feat/no-jsx-leaked-semicolon
Open

feat(js_analyze): implement noJsxLeakedSemicolon#9912
Netail wants to merge 3 commits intobiomejs:mainfrom
Netail:feat/no-jsx-leaked-semicolon

Conversation

@Netail
Copy link
Copy Markdown
Member

@Netail Netail commented Apr 11, 2026

Summary

Port https://www.eslint-react.xyz/docs/rules/jsx-no-leaked-semicolon, which disallows leaked semicolons in JSX text nodes.

Partially generated by co-pilot

Test Plan

Unit tests

Docs

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 11, 2026

🦋 Changeset detected

Latest commit: cb1cd6e

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-Linter Area: linter L-JavaScript Language: JavaScript and super languages A-Diagnostic Area: diagnostocis labels Apr 11, 2026
@Netail
Copy link
Copy Markdown
Member Author

Netail commented Apr 11, 2026

Unsure if this is also a common problem in svelte, astro or vue, else we can remove the JSX prefix

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 11, 2026

Walkthrough

Adds a new nursery lint rule noJsxLeakedSemicolon that detects leading semicolons leaked into JsxText nodes when the text begins with ";\n" or ";\r". The rule emits a warning diagnostic and supplies an Unsafe automatic fix that removes the leading semicolon by replacing the JSX text token. Also adds an empty NoJsxLeakedSemicolonOptions type and module export, a changeset entry for @biomejs/biome, and test fixtures covering valid and invalid JSX cases.

Suggested reviewers

  • dyc3
  • ematipico
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: implementation of a new JSX linting rule, noJsxLeakedSemicolon, which aligns perfectly with the changeset.
Description check ✅ Passed The description directly relates to the changeset by explaining the rule being ported from ESLint React and mentions unit tests as the test plan.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@Netail Netail marked this pull request as draft April 11, 2026 00:42
@github-actions github-actions bot added A-CLI Area: CLI A-Project Area: project labels Apr 11, 2026
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Apr 11, 2026

Merging this PR will not alter performance

✅ 58 untouched benchmarks
⏩ 196 skipped benchmarks1


Comparing Netail:feat/no-jsx-leaked-semicolon (cb1cd6e) with main (19ff706)

Open in CodSpeed

Footnotes

  1. 196 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.

@Netail Netail force-pushed the feat/no-jsx-leaked-semicolon branch from 500ad01 to 20bf6b5 Compare April 11, 2026 00:59
@Netail Netail marked this pull request as ready for review April 11, 2026 01:23
)
}

fn action(ctx: &RuleContext<Self>, _state: &Self::State) -> Option<JsRuleAction> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should we even bother providing a fix for this? does the source rule?

Copy link
Copy Markdown
Member Author

@Netail Netail Apr 11, 2026

Choose a reason for hiding this comment

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

@dyc3
Copy link
Copy Markdown
Contributor

dyc3 commented Apr 11, 2026

Also the concern this rule covers is pretty much only a jsx problem, because of the nature of jsx. Not a problem in HTML and HTML-ish langs.

Copy link
Copy Markdown
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

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

Thank you! I believe the rule requires a bit more care:

  • all unsafe fixes are incorrect, check the snapshots. Did you check them?
  • I believe the configuration that triggers the rule is too naive. I believe the following case triggers the rule
<>
  <br />;

Some Tex 
</>

use biome_rule_options::no_jsx_leaked_semicolon::NoJsxLeakedSemicolonOptions;

declare_lint_rule! {
/// Disallows leaked semicolons in JSX text nodes.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

"Leaked" doesn't mean anything without context.

Here's a suggestion

"Flags semicolons that could be rendered as content inside JSX tags."

Copy link
Copy Markdown
Member Author

@Netail Netail Apr 11, 2026

Choose a reason for hiding this comment

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

What about "Flags text nodes with a leading ; after a JSX element"?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You used "text nodes" which something related to parsing. See if you can find a plain language, beginner friendly

@Netail Netail force-pushed the feat/no-jsx-leaked-semicolon branch from 1497522 to e93576e Compare April 11, 2026 23:29
@Netail Netail force-pushed the feat/no-jsx-leaked-semicolon branch from e93576e to cb1cd6e Compare April 11, 2026 23:29
@Netail Netail requested review from dyc3 and ematipico April 11, 2026 23:30
@Netail
Copy link
Copy Markdown
Member Author

Netail commented Apr 11, 2026

Thank you! I believe the rule requires a bit more care:

  • all unsafe fixes are incorrect, check the snapshots. Did you check them?
  • I believe the configuration that triggers the rule is too naive. I believe the following case triggers the rule
<>
  <br />;

Some Tex 
</>

They seem correct to me, the delete is under the ; 🤔
And the case you mentioned is correct to be flagged, why wouldn't it?

Am I overlooking something?

Copy link
Copy Markdown
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 (1)
.changeset/ten-walls-throw.md (1)

6-6: Tiny copy polish (optional).

You could tighten this to: “This is often accidental and renders ; as text in the output.”

As per coding guidelines: “Write changeset descriptions concisely and clearly (1-3 sentences) …”

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

In @.changeset/ten-walls-throw.md at line 6, Replace the current sentence "This
could be an unintentional mistake, resulting in a ';' being rendered as text in
the output." with the tighter phrasing "This is often accidental and renders `;`
as text in the output." in the changeset description—use backticks around the
semicolon to ensure it's rendered as inline code and keep the changeset
description concise (1–3 sentences).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/biome_js_analyze/src/lint/nursery/no_jsx_leaked_semicolon.rs`:
- Around line 112-113: The matcher currently only detects text nodes that start
with ";\n" or ";\r", missing lone ";" or ";" followed directly by EOF/closing
tag; update the condition so it also treats a text of exactly ";" or any text
that starts with ";" (e.g., text == ";" || text.starts_with(';')) as a leaked
semicolon and return None accordingly; apply the same fix to the analogous check
later in the file (the second occurrence around the other matcher) so both
locations handle lone semicolons and semicolons without a newline.

---

Nitpick comments:
In @.changeset/ten-walls-throw.md:
- Line 6: Replace the current sentence "This could be an unintentional mistake,
resulting in a ';' being rendered as text in the output." with the tighter
phrasing "This is often accidental and renders `;` as text in the output." in
the changeset description—use backticks around the semicolon to ensure it's
rendered as inline code and keep the changeset description concise (1–3
sentences).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4e9b712d-beb7-4061-9c40-30f1c8d54c69

📥 Commits

Reviewing files that changed from the base of the PR and between 20bf6b5 and cb1cd6e.

⛔ Files ignored due to path filters (6)
  • 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_configuration/src/generated/domain_selector.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_configuration/src/generated/linter_options_check.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_diagnostics_categories/src/categories.rs is excluded by !**/categories.rs and included by **
  • crates/biome_js_analyze/tests/specs/nursery/noJsxLeakedSemicolon/invalid.jsx.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (2)
  • .changeset/ten-walls-throw.md
  • crates/biome_js_analyze/src/lint/nursery/no_jsx_leaked_semicolon.rs

Comment on lines +112 to +113
if !(text.starts_with(";\n") || text.starts_with(";\r")) {
return None;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Leaked ; without newline is currently missed.

The matcher only catches ";\n" / ";\r", so a lone ";" text node (for example inline before a closing tag) is not reported and not fixed.

Proposed patch
-        if !(text.starts_with(";\n") || text.starts_with(";\r")) {
+        if !text.starts_with(';') {
+            return None;
+        }
+        let rest = &text[1..];
+        if !(rest.is_empty() || rest.starts_with('\n') || rest.starts_with('\r')) {
             return None;
         }
@@
-        // Remove the leading `;`
-        let new_text = text[1..].to_string();
+        let new_text = text.strip_prefix(';')?.to_string();

Also applies to: 142-143

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

In `@crates/biome_js_analyze/src/lint/nursery/no_jsx_leaked_semicolon.rs` around
lines 112 - 113, The matcher currently only detects text nodes that start with
";\n" or ";\r", missing lone ";" or ";" followed directly by EOF/closing tag;
update the condition so it also treats a text of exactly ";" or any text that
starts with ";" (e.g., text == ";" || text.starts_with(';')) as a leaked
semicolon and return None accordingly; apply the same fix to the analogous check
later in the file (the second occurrence around the other matcher) so both
locations handle lone semicolons and semicolons without a newline.

@Netail
Copy link
Copy Markdown
Member Author

Netail commented Apr 11, 2026

I just realized this is pretty much noSuspiciousSemicolonInJsx 🤦‍♂️, however name-wise & implementation I think this one is better perhaps? (noJSX... seems bit more up to the frameworks specific rule standard + there's a quick fix) 🤔

@ematipico
Copy link
Copy Markdown
Member

Thank you! I believe the rule requires a bit more care:

  • all unsafe fixes are incorrect, check the snapshots. Did you check them?
  • I believe the configuration that triggers the rule is too naive. I believe the following case triggers the rule
<>
  <br />;

Some Tex 
</>

They seem correct to me, the delete is under the ; 🤔
And the case you mentioned is correct to be flagged, why wouldn't it?

Am I overlooking something?

Sorry, my bad. The rule should flag that example, but it doesn't. Or it does, but there aren't any tests. I believe it doesn't, in fact code rabbit says the same

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.

3 participants