-
-
Notifications
You must be signed in to change notification settings - Fork 720
feat(format/css): implement formatters for new tailwind nodes #7241
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Name | Type |
---|---|
@biomejs/biome | Minor |
@biomejs/cli-win32-x64 | Minor |
@biomejs/cli-win32-arm64 | Minor |
@biomejs/cli-darwin-x64 | Minor |
@biomejs/cli-darwin-arm64 | Minor |
@biomejs/cli-linux-x64 | Minor |
@biomejs/cli-linux-arm64 | Minor |
@biomejs/cli-linux-x64-musl | Minor |
@biomejs/cli-linux-arm64-musl | Minor |
@biomejs/wasm-web | Minor |
@biomejs/wasm-bundler | Minor |
@biomejs/wasm-nodejs | Minor |
@biomejs/backend-jsonrpc | Patch |
@biomejs/js-api | Major |
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
WalkthroughAdds a boolean CSS parser option Possibly related PRs
Suggested reviewers
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
CodSpeed Performance ReportMerging #7241 will not alter performanceComparing Summary
|
6384fc1
to
fb05c57
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
crates/biome_lsp/src/handlers/analysis.rs (1)
5-5
: Refactoranalysis.rs
to useStdResult
and only importanyhow::Context
• In crates/biome_lsp/src/handlers/analysis.rs (line 5), replace:
- use anyhow::{Context, Result}; + use anyhow::Context; + use std::result::Result as StdResult;• Update your function signatures accordingly:
pub(crate) fn code_actions( session: &Session, params: CodeActionParams, -) -> Result<Option<CodeActionResponse>, LspError> { +) -> StdResult<Option<CodeActionResponse>, LspError> { fn fix_all( session: &Session, url: &Uri, path: BiomePath, line_index: &LineIndex, diagnostics: &[lsp::Diagnostic], offset: Option<u32>, -) -> Result<Option<CodeActionOrCommand>, Error> { +) -> StdResult<Option<CodeActionOrCommand>, Error> {• Verified that
LspError: From<biome_diagnostics::Error>
exists in crates/biome_lsp/src/diagnostics.rs (lines 22–24), so your?
operators will continue to work as expected.Please apply this refactor to avoid any ambiguity between
anyhow::Result
and your locally scoped error types..changeset/resting-rhinos-reverberate.md (1)
1-6
: Changeset content doesn’t match this PR’s scope; propose replacing with a Tailwind formatter feature note and a minor bump.This PR implements Tailwind formatter/parser support, but the changeset talks about a rest-parameter inference fix (#6172) and uses a patch bump. That’s misleading for users and release notes. Replace with a feature-focused entry and bump minor to reflect new behaviour behind a flag.
Apply this diff to align the changeset with the PR:
---- -"@biomejs/biome": patch ---- - -Fixed [#6172](https://github.com/biomejs/biome/issues/6172): Resolved an issue with inferring types for rest parameters. This issue caused rest-parameter types to be incorrect, and in some cases caused extreme performance regressions in files that contained many methods with rest-parameter definitions. +--- +"@biomejs/biome": minor +--- + +Added formatting support for Tailwind CSS v4 directives and nodes behind a parser feature flag. + +When `"css.parser.tailwindDirectives": true`, the parser recognises Tailwind-specific at-rules (for example, `@theme`, `@utility`, and `@variant`) and the formatter prints them with stable whitespace and indentation. + +Example: + +```css +@theme { + --color-*: initial; +} +``` + +This change adds new formatters and test coverage for the above constructs. Behaviour is unchanged when the feature flag is disabled.Notes against the changeset guidelines:
- User-facing, past tense for what we did; present tense for current behaviour.
- Representative code block included.
- Sentences end with periods.
- No headers added.
.github/workflows/bench_cli.yml (1)
22-35
: Fix step-level condition: wrong context for issue_comment eventsThe step “Write a new comment” reads github.event.pull_request..., which isn’t present on issue_comment. This can make the workflow error out when evaluating the if expression. Fold the fork check into the earlier github-script step and gate the final step on that output.
Apply:
- - name: Get PR SHA + - name: Get PR metadata id: sha uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: - result-encoding: string script: | - const response = await github.request(context.payload.issue.pull_request.url); - return response.data.head.sha; + const { owner, repo } = context.repo; + const { data: pr } = await github.request(context.payload.issue.pull_request.url); + core.setOutput('sha', pr.head.sha); + core.setOutput('is_local', pr.head.repo.full_name === `${owner}/${repo}` ? 'true' : 'false'); - name: Checkout PR Branch uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 with: - ref: ${{ steps.sha.outputs.result }} + ref: ${{ steps.sha.outputs.sha }} - name: Write a new comment - # Check if the event is not triggered by a fork - if: github.event.pull_request.head.repo.full_name == github.repository + # Only comment for PRs where the head repo is this repository + if: ${{ steps.sha.outputs.is_local == 'true' }}Also applies to: 31-35, 96-104
xtask/codegen/src/formatter.rs (2)
844-849
: Tailwind node concept misclassification for AtRules.TwAtRule nodes should be classified as statements (to land under tailwind/statements), not auxiliary. As-is, codegen will point wrappers at crate::tailwind::auxiliary::..., which doesn’t match the new file layout (tailwind/statements/).
Apply:
- LanguageKind::Tailwind => match name { - _ if name.ends_with("Value") => NodeConcept::Value, - "TW_CANDIDATE" => NodeConcept::Expression, - _ => NodeConcept::Auxiliary, - }, + LanguageKind::Tailwind => match name { + _ if name.ends_with("AtRule") => NodeConcept::Statement, + _ if name.ends_with("Selector") => NodeConcept::Selector, + _ if name.ends_with("Value") => NodeConcept::Value, + // Keep any special cases first; adjust if the actual node name differs + "TW_CANDIDATE" => NodeConcept::Expression, + _ => NodeConcept::Auxiliary, + },
905-916
: Use existing CSS formatter types for TailwindIt looks like neither
TailwindFormatter
norTailwindFormatContext
are defined, so codegen will emit references to non-existent types and fail to compile. Let’s map Tailwind to the CSS formatter instead:• In
xtask/codegen/src/formatter.rs
lines 905–916, change:- Self::Tailwind => "TailwindFormatter", + Self::Tailwind => "CssFormatter",• In the same file around lines 921–932, change:
- Self::Tailwind => "TailwindFormatContext", + Self::Tailwind => "CssFormatContext",
crates/biome_css_parser/tests/css_test_suite/ok/tailwind/utility/value-literals.css
Show resolved
Hide resolved
crates/biome_js_analyze/src/lint/correctness/use_import_extensions.rs
Outdated
Show resolved
Hide resolved
// Tailwind CSS 4.0 keywords | ||
"theme", | ||
"utility", | ||
"variant", | ||
"custom-variant", | ||
"apply", | ||
"source", | ||
"reference", | ||
"config", | ||
"plugin", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
New Tailwind keywords: confirm lexer tables are updated and gated
These sit above the “HERE” marker (good for range checks). Please ensure consume_identifier
/is_contextual_keyword
in the lexer are updated so these remain contextual and don’t leak into non-Tailwind contexts.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Keywords should be accounted for in lexer identifier handling:
rg -n -C2 -g 'crates/biome_css_parser/src/lexer/**' $'consume_identifier|is_contextual_keyword'
# Sanity-check each Tailwind keyword appears in lexer logic (directly or via table):
for kw in theme utility variant custom-variant apply source reference config plugin; do
rg -n -C1 -g 'crates/biome_css_parser/src/lexer/**' -F "$kw" || echo "MISSING in lexer: $kw"
done
Length of output: 10287
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== is_contextual_keyword occurrences ==="
rg -n -C3 'is_contextual_keyword' crates/biome_css_parser/src/lexer/mod.rs || echo "no is_contextual_keyword found"
echo
echo "=== Tailwind keyword table context (lines 900-980) ==="
sed -n '900,980p' crates/biome_css_parser/src/lexer/mod.rs || true
Length of output: 3814
Gate Tailwind CSS keywords by lexing context
Our verification shows no is_contextual_keyword
hook exists and that in
crates/biome_css_parser/src/lexer/mod.rs
(lines 931–940) the new Tailwind tokens—
theme
, utility
, variant
, custom-variant
, apply
, source
, reference
,
config
, plugin
—are matched unconditionally in consume_identifier
. This will
cause them to be treated as keywords in all CSS, not just Tailwind rules.
• Location:
– File: crates/biome_css_parser/src/lexer/mod.rs
– Function: fn consume_identifier
(around the Tailwind CSS 4.0 keywords table)
• Action:
– Introduce a context check (e.g. if self.context == CssLexContext::TailwindUtility
)
around those keyword arms so they return THEME_KW
, UTILITY_KW
, etc. only when
in a Tailwind-specific lexing context. Otherwise fall back to IDENT
.
– Optionally, factor this into an is_contextual_keyword()
helper for clarity.
– Add tests to ensure plain CSS identifiers like "theme"
still lex as IDENT
outside
of Tailwind contexts.
🤖 Prompt for AI Agents
In xtask/codegen/src/css_kinds_src.rs around lines 120 to 129, the new Tailwind
CSS 4.0 keywords are currently emitted unconditionally which causes them to be
treated as keywords in all CSS; update the generated arms so those tokens are
only returned when the lexer is in a Tailwind lexing context (e.g. check
self.context == CssLexContext::TailwindUtility) and otherwise return IDENT, or
implement a small is_contextual_keyword(self, ident) helper used by
consume_identifier; add unit tests verifying that identifiers like "theme" lex
as IDENT in normal CSS context and as THEME_KW in the Tailwind context.
// Tailwind CSS 4.0 nodes | ||
"TW_THEME_AT_RULE", | ||
"TW_UTILITY_AT_RULE", | ||
"TW_VARIANT_AT_RULE", | ||
"TW_CUSTOM_VARIANT_AT_RULE", | ||
"TW_APPLY_AT_RULE", | ||
"TW_APPLY_CLASS_LIST", | ||
"TW_SOURCE_AT_RULE", | ||
"TW_REFERENCE_AT_RULE", | ||
"TW_CONFIG_AT_RULE", | ||
"TW_PLUGIN_AT_RULE", | ||
"TW_VALUE_THEME_REFERENCE", | ||
"TW_FUNCTIONAL_UTILITY_NAME", | ||
"TW_CUSTOM_VARIANT_SHORTHAND", | ||
// Unknowns |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
New TW_ nodes: verify ungrammar, AST, parser, and formatter coverage*
Great to see the node kinds added. Please verify the full chain exists:
- entries in
xtask/codegen/css.ungram
- generated AST node types
- parser productions
- formatter implementations (FormatTw*).
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Ungrammar contains the new nodes
rg -n -g 'xtask/codegen/**' -F 'TW_' | sort
# 2) Generated AST/formatter references
rg -n -g 'crates/**' -F 'TW_' -S | sort
# 3) Specific formatter impls for at-rules and lists
rg -n -g 'crates/biome_css_formatter/**' -P 'FormatTw_.*AtRule|TW_APPLY_CLASS_LIST|TW_VALUE_THEME_REFERENCE'
# 4) Parser production mentions
rg -n -g 'crates/biome_css_parser/**' -F 'TW_' -S
Length of output: 112707
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Searching for .ungram files in xtask/codegen"
rg -l '\.ungram' -g 'xtask/codegen/**' || echo "No .ungram files found in xtask/codegen"
echo "Checking for TW_ entries in .ungram files"
rg -n 'TW_' -g 'xtask/codegen/**/*.ungram' || echo "No TW_ entries in ungrammar files"
echo "Verifying formatter code generation for new TW nodes in xtask/codegen/src/formatter.rs"
rg -n 'TW_THEME_AT_RULE' -g 'xtask/codegen/src/formatter.rs' || echo "TW_THEME_AT_RULE not found in formatter.rs"
rg -n 'TW_UTILITY_AT_RULE' -g 'xtask/codegen/src/formatter.rs' || echo "TW_UTILITY_AT_RULE not found in formatter.rs"
rg -n 'TW_CUSTOM_VARIANT_AT_RULE' -g 'xtask/codegen/src/formatter.rs' || echo "TW_CUSTOM_VARIANT_AT_RULE not found in formatter.rs"
rg -n 'TW_APPLY_CLASS_LIST' -g 'xtask/codegen/src/formatter.rs' || echo "TW_APPLY_CLASS_LIST not found in formatter.rs"
echo "Searching for FormatTw* implementations in biome_css_formatter crate"
rg -n 'FormatTw' -g 'crates/biome_css_formatter/**' || echo "No FormatTw functions present in biome_css_formatter"
Length of output: 13736
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "1) Locating .ungram grammar files under xtask/codegen"
um_files=$(find xtask/codegen -maxdepth 1 -type f -name '*.ungram' || true)
if [ -z "$um_files" ]; then
echo "⚠️ No .ungram files found at xtask/codegen root"
else
echo "Found grammar files:"
printf '%s\n' "$um_files"
for f in $um_files; do
echo "– Checking for TW_ entries in $f"
rg -n 'TW_' -H "$f" || echo " ↳ No TW_ entries in $f"
done
fi
echo
echo "2) Verifying AST node wrappers in biome_css_syntax"
for node in TwThemeAtRule TwUtilityAtRule TwVariantAtRule TwCustomVariantAtRule TwApplyAtRule TwConfigAtRule TwPluginAtRule TwSourceAtRule TwReferenceAtRule TwValueThemeReference TwApplyClassList; do
echo "– Searching for AST wrapper ${node}"
rg -n "struct ${node}" -g 'crates/biome_css_syntax/**' || echo " ↳ Missing AST struct ${node}"
done
Length of output: 8986
Critical: Missing grammar entries and AST wrappers for new TW_ nodes*
It looks like the new TW_* kinds were added to css_kinds_src.rs
, and the parser and formatter already reference them, but…
• xtask/codegen/*.ungram (css.ungram, tailwind.ungram) have no TW_*
definitions.
• No corresponding struct Tw…
wrappers were generated in crates/biome_css_syntax
.
• xtask/codegen/src/formatter.rs hasn’t been updated with FormatTwThemeAtRule
, FormatTwUtilityAtRule
, etc.
Please:
- Add grammar rules for each of these in
xtask/codegen/css.ungram
(ortailwind.ungram
as appropriate). - Run
xtask codegen
to regenerate
•xtask/codegen/src/css_kinds_src.rs
• AST wrappers incrates/biome_css_syntax
(struct TwThemeAtRule, …)
• formatter generator inxtask/codegen/src/formatter.rs
- Commit the regenerated files so all new TW_* nodes flow through grammar → AST → formatter.
Without these steps, the new nodes will remain unrecognised by the ungrammar toolchain.
🤖 Prompt for AI Agents
In xtask/codegen/src/css_kinds_src.rs around lines 499–513, new TW_* kinds were
added but there are no corresponding grammar rules or generated AST/formatter
artifacts; add TW_* rules to the appropriate xtask/codegen/*.ungram files
(css.ungram or tailwind.ungram) matching each TW_* name, then run the generator
with `xtask codegen` to regenerate css_kinds_src.rs, the AST wrapper structs in
crates/biome_css_syntax (e.g., TwThemeAtRule, TwUtilityAtRule, etc.) and the
formatter generator (xtask/codegen/src/formatter.rs) so FormatTw* entries are
produced; finally stage and commit the regenerated files so the new TW_* nodes
flow through grammar → AST → formatter.
fb05c57
to
2e2b42f
Compare
2e2b42f
to
027cd91
Compare
There was a problem hiding this 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 (3)
crates/biome_css_formatter/src/tailwind/auxiliary/custom_variant_shorthand.rs (2)
20-28
: Tiny nit: add a trailing comma to future-proof diffs.A trailing comma after the last element keeps diffs small when appending new items and matches common style elsewhere in the repo.
l_paren_token.format(), selector.format(), r_paren_token.format(), - semicolon_token.format() + semicolon_token.format(),
13-18
: Semicolon presence: confirm grammar and add tests for both cases.If
semicolon_token
is optional in this position, current formatting is fine. If it’s mandatory, ensure the parser never drops it; otherwise we risk silently omitting it. Please add/confirm tests for:
- with semicolon vs without;
- comments between selector and
)
and after)
before;
.Happy to draft targeted tests if you share the canonical snippets for this node (valid/invalid).
.changeset/tasty-hairs-shop.md (1)
5-5
: Tighten wording; fix tense; note default.Use present tense for behaviour and include the default. Also tidies the “all of Tailwind” phrasing flagged by the linter.
-Added a new CSS parser option `tailwindDirectives`. Enabling this option will allow all of Tailwind v4's syntax additions to be parsed and formatted by Biome. +Added a new CSS parser option `tailwindDirectives` (default: false). When enabled, Biome parses and formats all Tailwind v4 syntax additions.
📜 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.
⛔ Files ignored due to path filters (4)
crates/biome_css_formatter/tests/specs/css/tailwind/long-apply.css.snap
is excluded by!**/*.snap
and included by**
crates/biome_css_formatter/tests/specs/css/tailwind/shadcn-default.css.snap
is excluded by!**/*.snap
and included by**
crates/biome_css_formatter/tests/specs/css/tailwind/utility.css.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**
📒 Files selected for processing (21)
.changeset/tasty-hairs-shop.md
(1 hunks)crates/biome_css_formatter/src/tailwind/auxiliary/custom_variant_shorthand.rs
(2 hunks)crates/biome_css_formatter/src/tailwind/auxiliary/functional_utility_name.rs
(1 hunks)crates/biome_css_formatter/src/tailwind/auxiliary/value_theme_reference.rs
(1 hunks)crates/biome_css_formatter/src/tailwind/lists/apply_class_list.rs
(1 hunks)crates/biome_css_formatter/src/tailwind/statements/apply_at_rule.rs
(1 hunks)crates/biome_css_formatter/src/tailwind/statements/config_at_rule.rs
(1 hunks)crates/biome_css_formatter/src/tailwind/statements/custom_variant_at_rule.rs
(1 hunks)crates/biome_css_formatter/src/tailwind/statements/plugin_at_rule.rs
(1 hunks)crates/biome_css_formatter/src/tailwind/statements/reference_at_rule.rs
(1 hunks)crates/biome_css_formatter/src/tailwind/statements/source_at_rule.rs
(1 hunks)crates/biome_css_formatter/src/tailwind/statements/theme_at_rule.rs
(1 hunks)crates/biome_css_formatter/src/tailwind/statements/utility_at_rule.rs
(1 hunks)crates/biome_css_formatter/src/tailwind/statements/variant_at_rule.rs
(1 hunks)crates/biome_css_formatter/tests/language.rs
(1 hunks)crates/biome_css_formatter/tests/quick_test.rs
(1 hunks)crates/biome_css_formatter/tests/specs/css/tailwind/long-apply.css
(1 hunks)crates/biome_css_formatter/tests/specs/css/tailwind/options.json
(1 hunks)crates/biome_css_formatter/tests/specs/css/tailwind/shadcn-default.css
(1 hunks)crates/biome_css_formatter/tests/specs/css/tailwind/utility.css
(1 hunks)crates/biome_css_syntax/src/file_source.rs
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (19)
- crates/biome_css_formatter/tests/specs/css/tailwind/long-apply.css
- crates/biome_css_formatter/src/tailwind/statements/source_at_rule.rs
- crates/biome_css_formatter/src/tailwind/statements/apply_at_rule.rs
- crates/biome_css_formatter/src/tailwind/lists/apply_class_list.rs
- crates/biome_css_formatter/src/tailwind/auxiliary/value_theme_reference.rs
- crates/biome_css_formatter/tests/specs/css/tailwind/shadcn-default.css
- crates/biome_css_formatter/src/tailwind/statements/plugin_at_rule.rs
- crates/biome_css_formatter/src/tailwind/auxiliary/functional_utility_name.rs
- crates/biome_css_formatter/src/tailwind/statements/custom_variant_at_rule.rs
- crates/biome_css_formatter/tests/quick_test.rs
- crates/biome_css_formatter/src/tailwind/statements/utility_at_rule.rs
- crates/biome_css_formatter/src/tailwind/statements/theme_at_rule.rs
- crates/biome_css_formatter/src/tailwind/statements/config_at_rule.rs
- crates/biome_css_syntax/src/file_source.rs
- crates/biome_css_formatter/src/tailwind/statements/reference_at_rule.rs
- crates/biome_css_formatter/tests/language.rs
- crates/biome_css_formatter/src/tailwind/statements/variant_at_rule.rs
- crates/biome_css_formatter/tests/specs/css/tailwind/options.json
- crates/biome_css_formatter/tests/specs/css/tailwind/utility.css
🧰 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_css_formatter/src/tailwind/auxiliary/custom_variant_shorthand.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_css_formatter/src/tailwind/auxiliary/custom_variant_shorthand.rs
crates/biome_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_css_formatter/src/tailwind/auxiliary/custom_variant_shorthand.rs
.changeset/*.md
📄 CodeRabbit inference engine (CONTRIBUTING.md)
.changeset/*.md
: Create changesets withjust new-changeset
; store them in.changeset/
with correct frontmatter (package keys and change type).
In changeset descriptions, follow content conventions: user-facing changes only; past tense for what you did; present tense for current behavior; link issues for fixes; link rules/assists; include representative code blocks; end every sentence with a period.
When adding headers in a changeset, only use #### or ##### levels.
Files:
.changeset/tasty-hairs-shop.md
🧠 Learnings (10)
📚 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/src/cst.rs : Provide the FormatNodeRule trait wiring (fmt, fmt_fields, is_suppressed, fmt_leading_comments, fmt_dangling_comments, fmt_trailing_comments) used to start node formatting
Applied to files:
crates/biome_css_formatter/src/tailwind/auxiliary/custom_variant_shorthand.rs
📚 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/src/cst.rs : Define FormatHtmlSyntaxNode in cst.rs and implement FormatRule<HtmlSyntaxNode>, plus AsFormat and IntoFormat for HtmlSyntaxNode using the provided mapping code
Applied to files:
crates/biome_css_formatter/src/tailwind/auxiliary/custom_variant_shorthand.rs
📚 Learning: 2025-08-11T11:48:52.001Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-08-11T11:48:52.001Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Use the generic Format trait and FormatNode for AST nodes when implementing the formatter
Applied to files:
crates/biome_css_formatter/src/tailwind/auxiliary/custom_variant_shorthand.rs
📚 Learning: 2025-08-11T11:48:52.001Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-08-11T11:48:52.001Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : When a token is mandatory and present in the AST, use the AST-provided token (e.g., node.l_paren_token().format()) instead of hardcoded tokens
Applied to files:
crates/biome_css_formatter/src/tailwind/auxiliary/custom_variant_shorthand.rs
📚 Learning: 2025-08-11T11:48:52.001Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-08-11T11:48:52.001Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Import the FormatNode trait and implement it for your Node
Applied to files:
crates/biome_css_formatter/src/tailwind/auxiliary/custom_variant_shorthand.rs
📚 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/src/lib.rs : Expose a documented public function format_node(options: HtmlFormatOptions, root: &HtmlSyntaxNode) -> FormatResult<Formatted<HtmlFormatContext>> delegating to biome_formatter::format_node
Applied to files:
crates/biome_css_formatter/src/tailwind/auxiliary/custom_variant_shorthand.rs
📚 Learning: 2025-08-11T11:48:52.001Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-08-11T11:48:52.001Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Use dbg_write! to debug and inspect the written IR elements during formatter development
Applied to files:
crates/biome_css_formatter/src/tailwind/auxiliary/custom_variant_shorthand.rs
📚 Learning: 2025-08-11T11:48:52.001Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-08-11T11:48:52.001Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : Do not attempt to fix code: if a mandatory token/node is missing, return None instead
Applied to files:
crates/biome_css_formatter/src/tailwind/auxiliary/custom_variant_shorthand.rs
📚 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/src/lib.rs : Place the plumbing traits and impls (AsFormat, IntoFormat, FormattedIterExt, and their Iterator adapters) in the biome_html_formatter crate’s lib.rs
Applied to files:
crates/biome_css_formatter/src/tailwind/auxiliary/custom_variant_shorthand.rs
📚 Learning: 2025-08-11T11:48:52.001Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_js_formatter/CONTRIBUTING.md:0-0
Timestamp: 2025-08-11T11:48:52.001Z
Learning: Applies to crates/biome_js_formatter/**/*.rs : For non-mandatory tokens, use the formatter helper functions (e.g., token, space_token) instead of hardcoding
Applied to files:
crates/biome_css_formatter/src/tailwind/auxiliary/custom_variant_shorthand.rs
🪛 LanguageTool
.changeset/tasty-hairs-shop.md
[grammar] ~5-~5: Consider using “all Tailwind” or “all of the Tailwind”.
Context: ...tives`. Enabling this option will allow all of Tailwind v4's syntax additions to be parsed and ...
(ALL_MOST_SOME_OF_NOUN)
🔇 Additional comments (3)
crates/biome_css_formatter/src/tailwind/auxiliary/custom_variant_shorthand.rs (2)
13-18
: Field-based formatter wiring looks right.Correct destructuring of fields and reliance on AST-provided tokens/nodes. Order matches the expected syntax. LGTM.
2-3
: Import style is consistent – no changes neededAll 12 sibling Tailwind formatter modules explicitly import
biome_formatter::write
, including this one, so your import is perfectly aligned..changeset/tasty-hairs-shop.md (1)
2-3
: Changeset scope and bump look correct• Only
@biomejs/biome
is mentioned across all changesets (tasty-hairs-shop, slimy-zoos-search and based-bears-brawl) – no other public packages need bumping.
• You can ignore the “origin/next” error when previewing the release plan; it just means that branch isn’t checked out locally.All set to ship! 🎉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome stuff!
A couple of things as a follow up (another PR if you want):
- create a CLI test and make sure the new parser option allows formatting for a tailwind CSS file
- create a CLI test and make sure the new parser option allows for linting for a tailwind CSS file
- create two LSP test that do the same (linting and formatting)
|
||
```css | ||
.foo { | ||
@apply text-blue-500 hover:text-blue-600 accent-red accent-blue accent-green text-sm text-md hover:underline md:grid; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit of an eyesore 🤣 not sure what prettier does, but I wonder if we should create a group here, and multiline? Would that work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This behavior currently matches prettier. But it also appears that prettier doesn't do any formatting here:
However, whenever I save the file in my IDE, some language server (tailwind? could also be biome) formats it to remove all the newlines and force it on a single line.
I also checked that tailwind's compiler does accept it if we add newlines.
…ed down to the parser and add cli tests
There was a problem hiding this 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 (2)
crates/biome_cli/tests/cases/tailwind_directives.rs (2)
33-37
: Nit: drop redundant formatter flag
formatter.enabled
is already set totrue
in biome.json for this test. The extra--css-formatter-enabled=true
doesn’t change behaviour and adds noise.Apply:
- Args::from(["format", "--css-formatter-enabled=true", css_file.as_str()].as_slice()), + Args::from(["format", css_file.as_str()].as_slice()),
20-31
: Trim duplication: factor a tiny helper to write the config JSONThe three tests inline similar JSON writes. A small helper reduces repetition and makes the intent clearer.
Apply:
use camino::Utf8Path; +fn write_config(fs: &MemoryFileSystem, json: &str) { + let config_path = Utf8Path::new("biome.json"); + fs.insert(config_path.into(), json.as_bytes()); +} + #[test] fn should_parse_tailwind_directive() { @@ - let config_path = Utf8Path::new("biome.json"); - fs.insert( - config_path.into(), - r#"{ + write_config( + &fs, + r#"{ "formatter": { "enabled": true }, "css": { "parser": { "tailwindDirectives": true } } }"# - .as_bytes(), ); @@ - let config_path = Utf8Path::new("biome.json"); - fs.insert( - config_path.into(), - r#"{ + write_config( + &fs, + r#"{ "formatter": { "enabled": true }, "css": { "parser": { "tailwindDirectives": false } } }"# - .as_bytes(), ); @@ - let config_path = Utf8Path::new("biome.json"); - fs.insert( - config_path.into(), - r#"{ + write_config( + &fs, + r#"{ "linter": { "enabled": true, "rules": { "recommended": false, "suspicious": { "noUnknownAtRules": "warn" } } }, "css": { "parser": { "tailwindDirectives": true } } }"# - .as_bytes(), );Also applies to: 63-74, 105-121
📜 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.
⛔ Files ignored due to path filters (3)
crates/biome_cli/tests/snapshots/main_cases_tailwind_directives/should_not_parse_tailwind_directive_when_disabled.snap
is excluded by!**/*.snap
and included by**
crates/biome_cli/tests/snapshots/main_cases_tailwind_directives/should_parse_tailwind_directive.snap
is excluded by!**/*.snap
and included by**
crates/biome_cli/tests/snapshots/main_cases_tailwind_directives/tw_should_not_show_unknown_at_rule_diagnostic.snap
is excluded by!**/*.snap
and included by**
📒 Files selected for processing (3)
crates/biome_cli/tests/cases/mod.rs
(1 hunks)crates/biome_cli/tests/cases/tailwind_directives.rs
(1 hunks)crates/biome_service/src/file_handlers/css.rs
(1 hunks)
🧰 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_cli/tests/cases/tailwind_directives.rs
crates/biome_cli/tests/cases/mod.rs
crates/biome_service/src/file_handlers/css.rs
crates/biome_*/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place core crates under /crates/biome_*/
Files:
crates/biome_cli/tests/cases/tailwind_directives.rs
crates/biome_cli/tests/cases/mod.rs
crates/biome_service/src/file_handlers/css.rs
**/tests/**
📄 CodeRabbit inference engine (CLAUDE.md)
Place test files under a tests/ directory in each crate
Files:
crates/biome_cli/tests/cases/tailwind_directives.rs
crates/biome_cli/tests/cases/mod.rs
🧠 Learnings (6)
📚 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/spec_test.rs : Create tests/spec_test.rs implementing the run(spec_input_file, _expected_file, test_directory, _file_type) function as shown and include!("language.rs")
Applied to files:
crates/biome_cli/tests/cases/tailwind_directives.rs
crates/biome_cli/tests/cases/mod.rs
📚 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/spec_tests.rs : Create tests/spec_tests.rs in the biome_html_formatter crate that generates tests via tests_macros::gen_tests! for all HTML files at tests/specs/html/**/*.html
Applied to files:
crates/biome_cli/tests/cases/tailwind_directives.rs
crates/biome_cli/tests/cases/mod.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/tests/quick_test.rs : Use `biome_js_analyze/tests/quick_test.rs` for quick, ad-hoc testing; un-ignore the test and adjust the rule filter as needed
Applied to files:
crates/biome_cli/tests/cases/tailwind_directives.rs
crates/biome_cli/tests/cases/mod.rs
📚 Learning: 2025-08-11T11:53:15.299Z
Learnt from: CR
PR: biomejs/biome#0
File: crates/biome_service/CONTRIBUTING.md:0-0
Timestamp: 2025-08-11T11:53:15.299Z
Learning: Applies to crates/biome_service/../biome_lsp/src/server.tests.rs : Keep end-to-end LSP tests in biome_lsp’s server.tests.rs
Applied to files:
crates/biome_cli/tests/cases/tailwind_directives.rs
crates/biome_cli/tests/cases/mod.rs
📚 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/language.rs : Create tests/language.rs defining HtmlTestFormatLanguage and implement TestFormatLanguage for it
Applied to files:
crates/biome_cli/tests/cases/tailwind_directives.rs
📚 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/Cargo.toml : Add the specified dev-dependencies (biome_formatter_test, biome_html_factory, biome_html_parser, biome_parser, biome_service, countme with feature enable, iai 0.1.1, quickcheck, quickcheck_macros, tests_macros) to Cargo.toml under [dev-dependencies]
Applied to files:
crates/biome_cli/tests/cases/mod.rs
🧬 Code graph analysis (1)
crates/biome_cli/tests/cases/tailwind_directives.rs (3)
crates/biome_cli/tests/main.rs (1)
run_cli
(332-347)crates/biome_cli/tests/snap_test.rs (1)
assert_cli_snapshot
(407-409)crates/biome_service/src/file_handlers/css.rs (4)
from
(58-67)from
(78-83)from
(93-97)from
(109-115)
⏰ 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). (24)
- GitHub Check: Documentation
- GitHub Check: End-to-end tests
- GitHub Check: Test (depot-windows-2022-16)
- GitHub Check: autofix
- GitHub Check: Test (depot-ubuntu-24.04-arm-16)
- GitHub Check: Check Dependencies
- GitHub Check: Lint project (depot-windows-2022)
- GitHub Check: Lint project (depot-ubuntu-24.04-arm-16)
- GitHub Check: Bench (biome_graphql_formatter)
- GitHub Check: Bench (biome_html_parser)
- GitHub Check: Bench (biome_js_formatter)
- GitHub Check: Bench (biome_html_formatter)
- GitHub Check: Bench (biome_graphql_parser)
- GitHub Check: Bench (biome_module_graph)
- GitHub Check: Bench (biome_json_analyze)
- GitHub Check: Bench (biome_package)
- GitHub Check: Bench (biome_configuration)
- GitHub Check: Bench (biome_json_parser)
- GitHub Check: Bench (biome_json_formatter)
- GitHub Check: Bench (biome_css_analyze)
- GitHub Check: Bench (biome_css_formatter)
- GitHub Check: Bench (biome_js_parser)
- GitHub Check: Bench (biome_css_parser)
- GitHub Check: Bench (biome_js_analyze)
🔇 Additional comments (3)
crates/biome_service/src/file_handlers/css.rs (1)
395-401
: Override propagatestailwind_directives
correctlyI’ve confirmed that in
crates/biome_service/src/settings.rs
theapply_override_css_parser_options
method includes:if let Some(tailwind_directives) = css_parser.tailwind_directives { options.tailwind_directives = tailwind_directives.value(); }so any per-path override for Tailwind directives is indeed applied. No further action required.
crates/biome_cli/tests/cases/mod.rs (1)
34-34
: New CLI test module included — LGTMThe Tailwind directives suite is now part of the CLI tests. No concerns.
crates/biome_cli/tests/cases/tailwind_directives.rs (1)
39-41
: Sanity check: test relies on non‑zero exit fromformat
This assertion depends on
format
exiting non‑zero (file is “unformatted”). That’s currently true because the input lacks a trailing newline andformat
will propose a change. If someone “pretty‑prints” the fixture later, this could flip. Consider adding a comment to keep the no‑newline invariant or switch to an explicit check mode flag if available.
I added the cli tests, but I'll do the LSP tests in another PR. |
@ematipico I've been playing with the LSP tests for this on and off today, and the more I think about it, the value of these tests feels a bit dubious compared to the effort. I feel like the cli tests do enough -- they assert that given the right configuration, biome can emit the right diagnostics. If the LSP can't do that, its a problem in the LSP that affects more areas than just this issue. |
Summary
This is a follow up to #7164 to add formatters, and add the changeset for the feature.
Caution
This PR is stacked on the following PRs. Do not merge it without merging the others and rebasing this branch. To review this PR, look at 3295929 onwards.
Test Plan
Added tests, ci should stay green.
Docs