Skip to content

Parse JSX and TSX source files#147

Merged
praveenperera merged 3 commits into
masterfrom
tsx-jsx-parsing
Jul 21, 2026
Merged

Parse JSX and TSX source files#147
praveenperera merged 3 commits into
masterfrom
tsx-jsx-parsing

Conversation

@praveenperera

@praveenperera praveenperera commented Jul 14, 2026

Copy link
Copy Markdown
Member

Summary

  • add a shared Winnow-backed JSX source profile for .jsx and .tsx
  • sort only directly quoted class and className attributes in real JSX elements
  • preserve JavaScript and TypeScript strings, comments, templates, regexes, generics, and dynamic attributes
  • add a pinned Horizon UI JSX corpus and enforce source preservation across every real-world corpus

Why

JSX and TSX previously used the unknown-language regex fallback. That sorted simple attributes, but it could not reliably distinguish JSX attributes from class-like text in the surrounding program. The real-world comparison suite also lacked a JSX corpus and did not verify that formatter changes stayed inside parsed attribute values.

Impact

JSX and TSX files now receive syntax-aware handling through SourceLanguage::Jsx, whether inferred from a filename or selected with --language jsx or --language tsx. Static quoted attributes are sorted while dynamic expressions and other source text remain unchanged.

This adds a public SourceLanguage variant, so downstream exhaustive matches must handle Jsx.

Validation

  • just fmt
  • just clippy
  • cargo test --workspace --no-fail-fast
  • npm test
  • cargo xtask compare run --offline
    • Shadcn UI TSX: 834 attributes
    • Horizon UI JSX: 670 attributes
    • Shadcn Svelte: 713 attributes
    • Astrowind: 362 attributes

All four corpora passed with zero extraction mismatches, known-order mismatches, or source-preservation failures.

Summary by CodeRabbit

  • New Features

    • Added JSX/TSX support for sorting only directly quoted class/className attributes.
    • Added jsx and tsx CLI language options, with .jsx/.tsx files handled automatically.
  • Bug Fixes

    • Improved extraction to avoid touching class-like text in strings, comments, regexes, or dynamic expressions.
    • Strengthened source preservation so only real quoted class attributes are reordered.
  • Documentation

    • Updated usage, CLI help, and comparison docs to reflect JSX/TSX support.
  • Breaking Changes

    • Language detection now includes a Jsx variant shared by .jsx and .tsx, requiring downstream exhaustive handling updates.

@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a87ed305-71fc-4622-9969-f20a2aac5ccc

📥 Commits

Reviewing files that changed from the base of the PR and between 4d9d45a and 2074d98.

📒 Files selected for processing (1)
  • rustywind-core/src/jsx_parser.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • rustywind-core/src/jsx_parser.rs

📝 Walkthrough

Walkthrough

JSX and TSX are recognized as a shared source language. A dedicated parser extracts directly quoted class and className attributes, integrates with sorting, and extends comparison tooling and corpus coverage.

Changes

JSX and TSX parsing

Layer / File(s) Summary
Language profiles and CLI mapping
rustywind-core/src/source.rs, rustywind-cli/src/options.rs, rustywind-cli/src/main.rs, README.md, CHANGELOG.md, rustywind-core/CHANGELOG.md
JSX and TSX paths and CLI values resolve to SourceLanguage::Jsx, with corresponding documentation and changelog updates.
Attribute parser abstraction
rustywind-core/src/attribute_parser.rs, rustywind-core/src/markup_parser.rs, rustywind-core/src/template_parser.rs, rustywind-core/src/lib.rs
A shared parser dispatches between markup and JSX implementations, while markup parsing and lexical helpers adopt the new internal interfaces.
JSX parser implementation
rustywind-core/src/jsx_parser.rs
A Winnow-based parser scans JavaScript and JSX constructs, extracts directly quoted class attributes, and tests nested, dynamic, lexical, and malformed cases.
Sorting integration and regression coverage
rustywind-core/src/app.rs, rustywind-core/tests/template_aware_sorting.rs
Sorting fast paths use attribute-parser profiles, with tests covering quoted JSX/TSX attributes and ignored dynamic or string content.
Comparison attribute extraction and preservation
tests/tailwind-compare/lib.mjs, tests/tailwind-compare/test/lib.test.mjs, tests/tailwind-compare/README.md
Comparison tooling supports JSX and validates that only quoted class attributes may change, with additional Svelte and offset tests.
Comparison runner and JSX corpus
tests/tailwind-compare/compare.mjs, xtask/src/commands/compare.rs, xtask/README.md
The runner records source-preservation failures and enables JSX comparisons; the corpus catalog adds the Horizon UI JSX snapshot.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SourceDocument
  participant attribute_parser
  participant jsx_parser
  participant Sorter
  SourceDocument->>attribute_parser: provide source and JSX profile
  attribute_parser->>jsx_parser: extract quoted class attributes
  jsx_parser-->>attribute_parser: return attribute ranges
  attribute_parser-->>Sorter: provide ClassAttribute ranges
  Sorter->>SourceDocument: reorder class values within ranges
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the main change: adding JSX/TSX source parsing support.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tsx-jsx-parsing

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@greptile-apps

greptile-apps Bot commented Jul 14, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds syntax-aware JSX and TSX class sorting. The main changes are:

  • New shared JSX parser for .jsx and .tsx inputs.
  • CLI and filename detection support for JSX and TSX.
  • Structured attribute parsing dispatch shared with markup languages.
  • Comparison-suite updates for JSX corpora and source preservation.

Confidence Score: 5/5

This looks safe to merge from this follow-up review.

  • No new publishable issues were found in the changed code.
  • The follow-up scope did not identify a separate failure that needs a new comment.

Important Files Changed

Filename Overview
rustywind-core/src/jsx_parser.rs Adds the Winnow-backed JSX parser for quoted class and className attributes.
rustywind-core/src/source.rs Adds the shared JSX source language profile for .jsx and .tsx files.
rustywind-core/src/app.rs Routes structured attribute parsing through the new shared parser profile.
tests/tailwind-compare/lib.mjs Extends the comparison helpers to extract JSX attributes and verify source preservation.

Reviews (3): Last reviewed commit: "Add recursion limit to JSX parser" | Re-trigger Greptile

Comment thread rustywind-core/src/jsx_parser.rs
Add a shared Winnow-backed source profile that isolates quoted class attributes from JavaScript and TypeScript syntax.

Extend the pinned parity harness with JSX coverage and enforce source preservation outside parsed attribute values.
Accept comments between JSX attributes so valid TSX files do not fall back to unchanged output.

Keep dynamic quoted Svelte attributes in source-preservation checks without treating them as static scrambling candidates.
@praveenperera
praveenperera marked this pull request as ready for review July 14, 2026 18:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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)
rustywind-core/src/jsx_parser.rs (1)

383-395: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate consume_character instead of reusing template_parser's.

This file redefines consume_character with the same body as the existing private consume_character in template_parser.rs. This PR already promotes five sibling helpers (consume_line_comment, consume_block_comment, consume_quoted, consume_escape, consume_regex, expression_keyword_allows_regex) to pub(crate) specifically for reuse here — consume_character should follow the same pattern instead of being duplicated.

♻️ Proposed fix
-fn consume_character(input: &mut Input<'_>, expected: char) -> Option<()> {
-    let character = any::<_, winnow::error::ContextError>
-        .parse_next(input)
-        .ok()?;
-    (character == expected).then_some(())
-}
+// removed; use `crate::template_parser::consume_character` instead

And in template_parser.rs:

-fn consume_character(input: &mut Input<'_>, expected: char) -> Option<()> {
+pub(crate) fn consume_character(input: &mut Input<'_>, expected: char) -> Option<()> {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rustywind-core/src/jsx_parser.rs` around lines 383 - 395, Remove the
duplicate consume_character helper from jsx_parser.rs and reuse
template_parser::consume_character instead. Promote the existing
consume_character in template_parser.rs to pub(crate), then update JSX parser
references and imports to call that shared helper while preserving its current
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rustywind-core/src/jsx_parser.rs`:
- Around line 41-136: Add a recursion-depth guard covering the mutually
recursive JavaScript and JSX traversal rooted at parse_javascript, including
nested group parsing, template-expression parsing, and
parse_jsx/parse_jsx_children descent. Track depth through each recursive call,
reject or gracefully stop parsing when a configured maximum is reached, and
ensure depth is restored after returning so normal shallow input behavior
remains unchanged.

---

Nitpick comments:
In `@rustywind-core/src/jsx_parser.rs`:
- Around line 383-395: Remove the duplicate consume_character helper from
jsx_parser.rs and reuse template_parser::consume_character instead. Promote the
existing consume_character in template_parser.rs to pub(crate), then update JSX
parser references and imports to call that shared helper while preserving its
current behavior.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fe78c1e4-b928-4a18-99cb-9ee44467199f

📥 Commits

Reviewing files that changed from the base of the PR and between ba418c2 and 4d9d45a.

📒 Files selected for processing (19)
  • CHANGELOG.md
  • README.md
  • rustywind-cli/src/main.rs
  • rustywind-cli/src/options.rs
  • rustywind-core/CHANGELOG.md
  • rustywind-core/src/app.rs
  • rustywind-core/src/attribute_parser.rs
  • rustywind-core/src/jsx_parser.rs
  • rustywind-core/src/lib.rs
  • rustywind-core/src/markup_parser.rs
  • rustywind-core/src/source.rs
  • rustywind-core/src/template_parser.rs
  • rustywind-core/tests/template_aware_sorting.rs
  • tests/tailwind-compare/README.md
  • tests/tailwind-compare/compare.mjs
  • tests/tailwind-compare/lib.mjs
  • tests/tailwind-compare/test/lib.test.mjs
  • xtask/README.md
  • xtask/src/commands/compare.rs

Comment thread rustywind-core/src/jsx_parser.rs Outdated
Prevent unbounded recursive parsing when extracting JSX class names by
adding a depth limit and failing safely when exceeded.

This hardens parsing for deep nested groups, JSX, and template
literals while preserving valid behavior for normal inputs.
@praveenperera

Copy link
Copy Markdown
Member Author

@greptileai

@praveenperera
praveenperera merged commit f553aee into master Jul 21, 2026
32 checks passed
@praveenperera
praveenperera deleted the tsx-jsx-parsing branch July 21, 2026 15:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant