Skip to content

feat(parse/tailwind): parse data attribute selectors as dedicated syntax#8514

Merged
dyc3 merged 1 commit intomainfrom
dyc3/tw-parser-fixes
Dec 20, 2025
Merged

feat(parse/tailwind): parse data attribute selectors as dedicated syntax#8514
dyc3 merged 1 commit intomainfrom
dyc3/tw-parser-fixes

Conversation

@dyc3
Copy link
Contributor

@dyc3 dyc3 commented Dec 19, 2025

Summary

Tailwind allows you to modify styles based on the presence of data attributes and the values assigned to them: https://tailwindcss.com/docs/hover-focus-and-other-states#data-attributes This partially implements dedicated parsing of these as their own syntax nodes. Though the reason I made this PR is to fix the parsing of samples like group-data-[collapsible=icon]:hidden, as shadcn commonly uses this pattern.

Test Plan

snapshots

Docs

@changeset-bot
Copy link

changeset-bot bot commented Dec 19, 2025

⚠️ No Changeset found

Latest commit: 77eeab3

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

Copy link
Contributor Author

dyc3 commented Dec 19, 2025

@github-actions github-actions bot added A-Parser Area: parser A-Tooling Area: internal tools L-Tailwind Language: Tailwind CSS labels Dec 19, 2025
@dyc3 dyc3 marked this pull request as ready for review December 19, 2025 17:04
@dyc3 dyc3 force-pushed the dyc3/tw-parser-whitespace branch from 98f8dc3 to c242dce Compare December 19, 2025 17:07
@dyc3 dyc3 force-pushed the dyc3/tw-parser-fixes branch from 5d2ea90 to cb0ce1b Compare December 19, 2025 17:07
@dyc3 dyc3 changed the base branch from dyc3/tw-parser-whitespace to graphite-base/8514 December 19, 2025 17:21
@dyc3 dyc3 force-pushed the dyc3/tw-parser-fixes branch from cb0ce1b to fdf01d6 Compare December 19, 2025 17:21
@dyc3 dyc3 force-pushed the graphite-base/8514 branch from c242dce to c89cfe2 Compare December 19, 2025 17:21
@graphite-app graphite-app bot changed the base branch from graphite-base/8514 to main December 19, 2025 17:22
@dyc3 dyc3 force-pushed the dyc3/tw-parser-fixes branch from fdf01d6 to 77eeab3 Compare December 19, 2025 17:22
@github-actions
Copy link
Contributor

Parser conformance results on

js/262

Test result main count This PR count Difference
Total 52650 52650 0
Passed 51437 51437 0
Failed 1171 1171 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%

symbols/microsoft

Test result main count This PR count Difference
Total 6339 6339 0
Passed 2110 2110 0
Failed 4229 4229 0
Panics 0 0 0
Coverage 33.29% 33.29% 0.00%

ts/babel

Test result main count This PR count Difference
Total 626 626 0
Passed 561 561 0
Failed 65 65 0
Panics 0 0 0
Coverage 89.62% 89.62% 0.00%

ts/microsoft

Test result main count This PR count Difference
Total 18846 18846 0
Passed 14090 14090 0
Failed 4755 4755 0
Panics 1 1 0
Coverage 74.76% 74.76% 0.00%

@codspeed-hq
Copy link

codspeed-hq bot commented Dec 19, 2025

CodSpeed Performance Report

Merging #8514 will improve performances by 15.31%

Comparing dyc3/tw-parser-fixes (77eeab3) with main (c53f42e)1

Summary

⚡ 4 improvements
✅ 6 untouched
⏩ 145 skipped2

Benchmarks breakdown

Benchmark BASE HEAD Change
cached[extreme_stress.txt] 944.9 µs 872 µs +8.37%
cached[arbitrary_classes.txt] 259.1 µs 244 µs +6.2%
uncached[extreme_stress.txt] 1.2 ms 1.1 ms +15.31%
uncached[arbitrary_classes.txt] 331.5 µs 309.2 µs +7.2%

Footnotes

  1. No successful run was found on main (c89cfe2) during the generation of this report, so c53f42e was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

  2. 145 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.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 19, 2025

Walkthrough

This PR introduces support for Tailwind data-attribute variants. The lexer is modified to detect and specially handle "data-" sequences by introducing a dedicated consume_after_dash() method. The parser is extended to handle data attributes in both variant and value contexts via a new parse_data_attribute() function. The grammar is updated with TwDataAttribute and AnyTwDataAttributeValue types integrated into existing variant and value unions. Test cases for data-attribute parsing are added.

Possibly related PRs

Suggested reviewers

  • ematipico
  • denbezrukov

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding dedicated parsing for Tailwind data attribute selectors as syntax nodes.
Description check ✅ Passed The description clearly relates to the changeset, explaining the motivation (fixing parsing of patterns like group-data-[collapsible=icon]:hidden) and linking to relevant Tailwind documentation.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dyc3/tw-parser-fixes

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

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

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c89cfe2 and 77eeab3.

⛔ Files ignored due to path filters (8)
  • crates/biome_tailwind_factory/src/generated/node_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_tailwind_factory/src/generated/syntax_factory.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/data-attribute.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/group-data.txt.snap is excluded by !**/*.snap and included by **
  • crates/biome_tailwind_syntax/src/generated/kind.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_tailwind_syntax/src/generated/macros.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_tailwind_syntax/src/generated/nodes.rs is excluded by !**/generated/**, !**/generated/** and included by **
  • crates/biome_tailwind_syntax/src/generated/nodes_mut.rs is excluded by !**/generated/**, !**/generated/** and included by **
📒 Files selected for processing (8)
  • crates/biome_tailwind_parser/src/lexer/mod.rs (3 hunks)
  • crates/biome_tailwind_parser/src/syntax/value.rs (2 hunks)
  • crates/biome_tailwind_parser/src/syntax/variant.rs (2 hunks)
  • crates/biome_tailwind_parser/tests/quick_test.rs (1 hunks)
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/data-attribute.txt (1 hunks)
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/group-data.txt (1 hunks)
  • xtask/codegen/src/tailwind_kinds_src.rs (2 hunks)
  • xtask/codegen/tailwind.ungram (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
crates/**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Update inline rustdoc documentation for rules, assists, and their options when adding new features or changing existing features in Rust crates

Files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/tests/quick_test.rs
  • crates/biome_tailwind_parser/src/lexer/mod.rs
  • crates/biome_tailwind_parser/src/syntax/value.rs
🧠 Learnings (30)
📚 Learning: 2025-12-04T13:29:49.287Z
Learnt from: dyc3
Repo: biomejs/biome PR: 8291
File: crates/biome_html_formatter/tests/specs/prettier/vue/html-vue/elastic-header.html:10-10
Timestamp: 2025-12-04T13:29:49.287Z
Learning: Files under `crates/biome_html_formatter/tests/specs/prettier` are test fixtures synced from Prettier and should not receive detailed code quality reviews (e.g., HTTP vs HTTPS, formatting suggestions, etc.). These files are test data meant to validate formatter behavior and should be preserved as-is.

Applied to files:

  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/group-data.txt
  • crates/biome_tailwind_parser/tests/quick_test.rs
  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/data-attribute.txt
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `p.eat(token)` for optional tokens, `p.expect(token)` for required tokens, `parse_rule(p).ok(p)` for optional nodes, and `parse_rule(p).or_add_diagnostic(p, error)` for required nodes

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/src/lexer/mod.rs
  • crates/biome_tailwind_parser/src/syntax/value.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Parse rules must take a mutable reference to the parser as their only parameter and return a `ParsedSyntax`

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/src/syntax/value.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/language_kind.rs : Add a new variant to `LanguageKind` enum in `language_kind.rs` file and implement all methods for the new language variant

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/src/syntax/value.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Parse rule functions must be prefixed with `parse_` and use the name defined in the grammar file, e.g., `parse_for_statement` or `parse_expression`

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/src/syntax/value.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `ConditionalParsedSyntax` for syntax that is only valid in specific contexts (e.g., strict mode, file types, language versions) and call `or_invalid_to_bogus()` to convert to a bogus node if not supported

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/tests/quick_test.rs
  • crates/biome_tailwind_parser/src/syntax/value.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Parse rules must return `ParsedSyntax::Absent` if the rule can't predict by the next token(s) if they form the expected node, and must not progress the parser in this case

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/src/syntax/value.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : A parser struct must implement the `Parser` trait and save the token source, parser context, and optional parser options

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/src/syntax/value.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/*.ungram : Nodes for enclosing syntax errors must have the `Bogus` word, e.g., `HtmlBogusAttribute`, and must be part of a variant

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/src/syntax/value.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Implement error recovery in list parsing using `or_recover()` to wrap unparseable tokens in a `BOGUS_*` node and consume tokens until a recovery token is found

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/src/lexer/mod.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Use `ParseSeparatedList` and `ParseNodeList` for parsing lists with error recovery to avoid infinite loops

Applied to files:

  • crates/biome_tailwind_parser/src/syntax/variant.rs
  • crates/biome_tailwind_parser/src/syntax/value.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Lines prefixed with `#` in rule documentation code examples will be hidden from output

Applied to files:

  • crates/biome_tailwind_parser/tests/quick_test.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Prefix line with `#` in documentation code examples sparingly; prefer concise complete snippets

Applied to files:

  • crates/biome_tailwind_parser/tests/quick_test.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Assist rules should detect refactoring opportunities and emit code action signals

Applied to files:

  • crates/biome_tailwind_parser/tests/quick_test.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Set rule severity to `info` or `warn` for rules in style group

Applied to files:

  • crates/biome_tailwind_parser/tests/quick_test.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Set rule severity to `error` for rules in correctness, security, and a11y groups

Applied to files:

  • crates/biome_tailwind_parser/tests/quick_test.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Avoid string allocations by comparing against `&str` or using `TokenText`

Applied to files:

  • crates/biome_tailwind_parser/tests/quick_test.rs
  • crates/biome_tailwind_parser/src/lexer/mod.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/Cargo.toml : Include development dependencies in `Cargo.toml` for formatter tests: `biome_formatter_test`, `biome_<language>_factory`, `biome_<language>_parser`, `biome_parser`, `biome_service`, `countme`, `iai`, `quickcheck`, `quickcheck_macros`, and `tests_macros`

Applied to files:

  • crates/biome_tailwind_parser/tests/quick_test.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/tests/language.rs : Implement `TestFormatLanguage` trait in `tests/language.rs` for the formatter's test language

Applied to files:

  • crates/biome_tailwind_parser/tests/quick_test.rs
📚 Learning: 2025-11-24T18:05:20.371Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:20.371Z
Learning: Applies to crates/biome_formatter/**/biome_*_formatter/tests/spec_tests.rs : Use the `tests_macros::gen_tests!` macro in `spec_tests.rs` to generate test functions for each specification file matching the pattern `tests/specs/<language>/**/*.<ext>`

Applied to files:

  • crates/biome_tailwind_parser/tests/quick_test.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Use `ignore` code block property to exclude documentation code examples from automatic validation

Applied to files:

  • crates/biome_tailwind_parser/tests/quick_test.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Rule documentation code blocks should be ordered as language, expect_diagnostic, options/full_options/use_options, ignore, file

Applied to files:

  • crates/biome_tailwind_parser/tests/quick_test.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/src/**/*.rs : Invalid code examples in rule documentation must be marked with `expect_diagnostic` code block property

Applied to files:

  • crates/biome_tailwind_parser/tests/quick_test.rs
📚 Learning: 2025-12-19T12:53:30.399Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_analyze/CONTRIBUTING.md:0-0
Timestamp: 2025-12-19T12:53:30.399Z
Learning: Applies to crates/biome_analyze/**/*analyze/tests/specs/**/*valid* : Create test files prefixed with `valid` for code that should not trigger the rule

Applied to files:

  • crates/biome_tailwind_parser/tests/tailwind_specs/ok/data-attribute.txt
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/lexer/mod.rs : Implement a `Lexer` trait from `biome_parser` crate for the lexer struct that consumes characters from source code and emits tokens

Applied to files:

  • crates/biome_tailwind_parser/src/lexer/mod.rs
  • crates/biome_tailwind_parser/src/syntax/value.rs
📚 Learning: 2025-11-24T18:06:03.545Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_parser/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:06:03.545Z
Learning: Applies to crates/biome_parser/**/src/**/*.rs : Implement a token source struct that wraps the lexer and implements `TokenSourceWithBufferedLexer` and `LexerWithCheckpoint` for lookahead and re-lexing capabilities

Applied to files:

  • crates/biome_tailwind_parser/src/lexer/mod.rs
  • crates/biome_tailwind_parser/src/syntax/value.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : When formatting AST nodes, use mandatory tokens from the AST instead of hardcoding token strings (e.g., use `node.l_paren_token().format()` instead of `token("(")`)

Applied to files:

  • crates/biome_tailwind_parser/src/lexer/mod.rs
  • crates/biome_tailwind_parser/src/syntax/value.rs
📚 Learning: 2025-10-25T07:22:18.540Z
Learnt from: ematipico
Repo: biomejs/biome PR: 7852
File: crates/biome_css_parser/src/syntax/property/mod.rs:161-168
Timestamp: 2025-10-25T07:22:18.540Z
Learning: In the Biome CSS parser, lexer token emission should not be gated behind parser options like `is_tailwind_directives_enabled()`. The lexer must emit correct tokens regardless of parser options to enable accurate diagnostics and error messages when the syntax is used incorrectly.

Applied to files:

  • crates/biome_tailwind_parser/src/lexer/mod.rs
📚 Learning: 2025-11-24T18:05:42.356Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_type_info/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:42.356Z
Learning: Applies to crates/biome_js_type_info/**/*.rs : Distinguish between `TypeData::Unknown` and `TypeData::UnknownKeyword` to measure inference effectiveness versus explicit user-provided unknown types

Applied to files:

  • crates/biome_tailwind_parser/src/lexer/mod.rs
📚 Learning: 2025-11-24T18:05:27.810Z
Learnt from: CR
Repo: biomejs/biome PR: 0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-11-24T18:05:27.810Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : For tokens that are not mandatory, use helper functions instead of hardcoding

Applied to files:

  • crates/biome_tailwind_parser/src/lexer/mod.rs
🧬 Code graph analysis (2)
crates/biome_tailwind_parser/src/syntax/variant.rs (1)
crates/biome_tailwind_parser/src/syntax/value.rs (1)
  • parse_value (10-21)
crates/biome_tailwind_parser/src/syntax/value.rs (1)
crates/biome_tailwind_parser/src/syntax/variant.rs (1)
  • parse_data_attribute (142-153)
⏰ 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). (11)
  • GitHub Check: Test Node.js API
  • GitHub Check: End-to-end tests
  • GitHub Check: Parser conformance
  • GitHub Check: autofix
  • GitHub Check: Test (depot-windows-2022-16)
  • GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Check Dependencies
  • GitHub Check: Lint project (depot-windows-2022)
  • GitHub Check: Documentation
  • GitHub Check: Test (depot-ubuntu-24.04-arm-16)
  • GitHub Check: Bench (biome_tailwind_parser)
🔇 Additional comments (15)
crates/biome_tailwind_parser/tests/tailwind_specs/ok/data-attribute.txt (1)

1-1: LGTM! Good test coverage for basic data-attribute variant.

The test case exercises the new data-attribute parsing path with a straightforward example.

crates/biome_tailwind_parser/tests/tailwind_specs/ok/group-data.txt (1)

1-1: LGTM! Excellent test for the shadcn use case.

This test case directly addresses the motivation mentioned in the PR for supporting patterns used by shadcn.

crates/biome_tailwind_parser/tests/quick_test.rs (1)

7-7: LGTM! Test input appropriately updated.

The new test input exercises the data-attribute parsing path introduced in this PR.

xtask/codegen/src/tailwind_kinds_src.rs (2)

17-17: LGTM! Keyword addition aligns with lexer changes.

The "data" keyword is now properly recognised for data-attribute variant parsing.


33-34: Partial implementation noted.

The commented-out TW_DATA_ATTRIBUTE_ARBITRARY_VALUE line (along with the TODO in tailwind.ungram) indicates this is a partial implementation. The current approach treats data-attribute arbitrary values as regular arbitrary values, which appears intentional for this initial implementation.

crates/biome_tailwind_parser/src/syntax/value.rs (2)

2-2: LGTM! Import correctly added.

The import enables value parsing to delegate to the data-attribute parser when encountering a data token.


17-19: LGTM! Data-attribute parsing path integrated correctly.

The check for T![data] appropriately delegates to parse_data_attribute before falling back to named value parsing.

crates/biome_tailwind_parser/src/syntax/variant.rs (1)

86-88: LGTM! Variant parsing correctly routes data attributes.

The check for T![data] appropriately delegates to the dedicated data-attribute parser.

crates/biome_tailwind_parser/src/lexer/mod.rs (3)

48-48: LGTM! Dash handling correctly routes to data-attribute detection.

The change enables special handling for "data-" sequences after a dash, which is essential for the lexer to recognise data-attribute variants.


133-137: LGTM! Keyword emission correctly handles "data".

The lexer now emits DATA_KW when it encounters a 4-byte base matching "data", enabling the parser to recognise data-attribute syntax.


160-176: LGTM! Clever implementation with good documentation.

The consume_after_dash method elegantly handles the "data-" detection after a dash. The comments clearly explain the behaviour: advancing past "data" and leaving the following dash to be emitted separately. Well done!

xtask/codegen/tailwind.ungram (4)

103-107: LGTM! Grammar correctly extends variant union.

The addition of TwDataAttribute to AnyTwVariant properly integrates data-attribute variants into the grammar.


133-137: LGTM! Grammar correctly extends value union.

Including TwDataAttribute in AnyTwValue allows data attributes to be used as values in functional candidates (e.g., group-data-active).


168-181: LGTM! Grammar correctly defines data-attribute structure and prevents recursion.

The definitions are well-structured:

  • TwDataAttribute correctly requires the sequence 'data' '-' value
  • AnyTwDataAttributeValue appropriately excludes TwDataAttribute itself, preventing recursive data-attribute nesting (e.g., data-data-...)

This matches the Tailwind specification and prevents parsing issues.


182-188: TODO noted for future refinement.

The commented TODO suggests a future enhancement to parse data-attribute arbitrary values with key=value syntax (e.g., [state=open]). The current implementation treats these as regular arbitrary values, which appears intentional for this initial implementation.

Comment on lines +142 to +153
pub(crate) fn parse_data_attribute(p: &mut TailwindParser) -> ParsedSyntax {
if !p.at(T![data]) {
return Absent;
}

let m = p.start();
p.bump(T![data]);
p.expect(T![-]);
parse_value(p).or_add_diagnostic(p, expected_value);

Present(m.complete(p, TW_DATA_ATTRIBUTE))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Fix recursion bug by implementing separate parse function for AnyTwDataAttributeValue.

The implementation doesn't match the grammar specification. Line 150 calls parse_value, which parses AnyTwValue (including TwDataAttribute). However, the grammar defines AnyTwDataAttributeValue as:

AnyTwDataAttributeValue =
    TwNamedValue |
    TwArbitraryValue |
    TwBogusValue

This creates infinite recursion for inputs like data-data-active:

  1. Parser sees DATA_KW, calls parse_data_attribute
  2. Bumps data, expects -, calls parse_value
  3. parse_value sees DATA_KW again, calls parse_data_attribute
  4. → Infinite recursion

The lexer will emit DATA_KW for "data" following a dash (via consume_after_dash), enabling this recursion.

🔎 Recommended fix

Implement a separate parsing function that matches the grammar:

 pub(crate) fn parse_data_attribute(p: &mut TailwindParser) -> ParsedSyntax {
     if !p.at(T![data]) {
         return Absent;
     }

     let m = p.start();
     p.bump(T![data]);
     p.expect(T![-]);
-    parse_value(p).or_add_diagnostic(p, expected_value);
+    parse_data_attribute_value(p).or_add_diagnostic(p, expected_value);

     Present(m.complete(p, TW_DATA_ATTRIBUTE))
 }
+
+fn parse_data_attribute_value(p: &mut TailwindParser) -> ParsedSyntax {
+    if p.at(T!['[']) {
+        return parse_arbitrary_value(p);
+    }
+    parse_named_value(p)
+}

You'll need to make parse_arbitrary_value and parse_named_value crate-visible or move this helper into value.rs.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In crates/biome_tailwind_parser/src/syntax/variant.rs around lines 142 to 153,
the current parse_data_attribute calls parse_value which includes
TwDataAttribute and leads to infinite recursion for inputs like
"data-data-active"; replace that call with a new function that implements the
grammar AnyTwDataAttributeValue = TwNamedValue | TwArbitraryValue | TwBogusValue
(e.g., parse_any_tw_data_attribute_value) that tries parse_named_value, then
parse_arbitrary_value, then produces a bogus value on failure; make
parse_named_value and parse_arbitrary_value crate-visible (pub(crate)) or move
the helper into value.rs so parse_data_attribute can call only this
non-recursive helper and avoid re-entering parse_data_attribute via parse_value.

@dyc3 dyc3 requested review from a team December 20, 2025 19:23
Copy link
Contributor Author

dyc3 commented Dec 20, 2025

Merge activity

  • Dec 20, 8:51 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Dec 20, 8:51 PM UTC: @dyc3 merged this pull request with Graphite.

@dyc3 dyc3 merged commit b8d3bcb into main Dec 20, 2025
16 checks passed
@dyc3 dyc3 deleted the dyc3/tw-parser-fixes branch December 20, 2025 20:51
l0ngvh pushed a commit to l0ngvh/biome that referenced this pull request Dec 21, 2025
…tax (biomejs#8514)

<!--
  IMPORTANT!!
  If you generated this PR with the help of any AI assistance, please disclose it in the PR.
  https://github.com/biomejs/biome/blob/main/CONTRIBUTING.md#ai-assistance-notice
-->

<!--
	Thanks for submitting a Pull Request! We appreciate you spending the time to work on these changes.
	Please provide enough information so that others can review your PR.
	Once created, your PR will be automatically labeled according to changed files.
	Learn more about contributing: https://github.com/biomejs/biome/blob/main/CONTRIBUTING.md
-->

## Summary

<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve?-->
Tailwind allows you to modify styles based on the presence of data attributes and the values assigned to them: https://tailwindcss.com/docs/hover-focus-and-other-states#data-attributes This partially implements dedicated parsing of these as their own syntax nodes. Though the reason I made this PR is to fix the parsing of samples like `group-data-[collapsible=icon]:hidden`, as shadcn commonly uses this pattern.

<!-- Link any relevant issues if necessary or include a transcript of any Discord discussion. -->

<!-- If you create a user-facing change, please write a changeset: https://github.com/biomejs/biome/blob/main/CONTRIBUTING.md#writing-a-changeset (your changeset is often a good starting point for this summary as well) -->

## Test Plan

<!-- What demonstrates that your implementation is correct? -->
snapshots

## Docs

<!-- If you're submitting a new rule or action (or an option for them), the documentation is part of the code. Make sure rules and actions have example usages, and that all options are documented. -->

<!-- For other features, please submit a documentation PR to the `next` branch of our website: https://github.com/biomejs/website/. Link the PR here once it's ready. -->
dibashthapa pushed a commit to dibashthapa/biome that referenced this pull request Dec 21, 2025
…tax (biomejs#8514)

<!--
  IMPORTANT!!
  If you generated this PR with the help of any AI assistance, please disclose it in the PR.
  https://github.com/biomejs/biome/blob/main/CONTRIBUTING.md#ai-assistance-notice
-->

<!--
	Thanks for submitting a Pull Request! We appreciate you spending the time to work on these changes.
	Please provide enough information so that others can review your PR.
	Once created, your PR will be automatically labeled according to changed files.
	Learn more about contributing: https://github.com/biomejs/biome/blob/main/CONTRIBUTING.md
-->

## Summary

<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve?-->
Tailwind allows you to modify styles based on the presence of data attributes and the values assigned to them: https://tailwindcss.com/docs/hover-focus-and-other-states#data-attributes This partially implements dedicated parsing of these as their own syntax nodes. Though the reason I made this PR is to fix the parsing of samples like `group-data-[collapsible=icon]:hidden`, as shadcn commonly uses this pattern.

<!-- Link any relevant issues if necessary or include a transcript of any Discord discussion. -->

<!-- If you create a user-facing change, please write a changeset: https://github.com/biomejs/biome/blob/main/CONTRIBUTING.md#writing-a-changeset (your changeset is often a good starting point for this summary as well) -->

## Test Plan

<!-- What demonstrates that your implementation is correct? -->
snapshots

## Docs

<!-- If you're submitting a new rule or action (or an option for them), the documentation is part of the code. Make sure rules and actions have example usages, and that all options are documented. -->

<!-- For other features, please submit a documentation PR to the `next` branch of our website: https://github.com/biomejs/website/. Link the PR here once it's ready. -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Parser Area: parser A-Tooling Area: internal tools L-Tailwind Language: Tailwind CSS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants