diff --git a/.changeset/tasty-hairs-shop.md b/.changeset/tasty-hairs-shop.md index fbbcf857fd8e..87aade5d6853 100644 --- a/.changeset/tasty-hairs-shop.md +++ b/.changeset/tasty-hairs-shop.md @@ -2,7 +2,7 @@ "@biomejs/biome": minor --- -Added a new CSS parser option `tailwindDirectives`. Enabling this option will allow all of Tailwind v4's syntax additions to be parsed by Biome. +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. You can enable this by setting `css.parser.tailwindDirectives` to `true` in your Biome configuration. diff --git a/crates/biome_cli/tests/cases/mod.rs b/crates/biome_cli/tests/cases/mod.rs index 3616fba4755c..098ef5787d72 100644 --- a/crates/biome_cli/tests/cases/mod.rs +++ b/crates/biome_cli/tests/cases/mod.rs @@ -31,5 +31,6 @@ mod reporter_summary; mod reporter_terminal; mod rules_via_dependencies; mod suppressions; +mod tailwind_directives; mod unknown_files; mod vcs_ignored_files; diff --git a/crates/biome_cli/tests/cases/tailwind_directives.rs b/crates/biome_cli/tests/cases/tailwind_directives.rs new file mode 100644 index 000000000000..d07496c62421 --- /dev/null +++ b/crates/biome_cli/tests/cases/tailwind_directives.rs @@ -0,0 +1,139 @@ +use crate::run_cli; +use crate::snap_test::{SnapshotPayload, assert_cli_snapshot}; +use biome_console::BufferConsole; +use biome_fs::MemoryFileSystem; +use bpaf::Args; +use camino::Utf8Path; + +#[test] +fn should_parse_tailwind_directive() { + let fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + + let css_file_content = r#"@theme {}"#; + let css_file = Utf8Path::new("input.css"); + fs.insert(css_file.into(), css_file_content.as_bytes()); + + let config_path = Utf8Path::new("biome.json"); + fs.insert( + config_path.into(), + r#"{ + "formatter": { + "enabled": true + }, + "css": { + "parser": { + "tailwindDirectives": true + } + } +}"# + .as_bytes(), + ); + + let (fs, result) = run_cli( + fs, + &mut console, + Args::from(["format", "--css-formatter-enabled=true", css_file.as_str()].as_slice()), + ); + + // should format the file + assert!(result.is_err(), "run_cli returned {result:?}"); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "should_parse_tailwind_directive", + fs, + console, + result, + )); +} + +#[test] +fn should_not_parse_tailwind_directive_when_disabled() { + let fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + + let css_file_content = r#"@theme {}"#; + let css_file = Utf8Path::new("input.css"); + fs.insert(css_file.into(), css_file_content.as_bytes()); + + let config_path = Utf8Path::new("biome.json"); + fs.insert( + config_path.into(), + r#"{ + "formatter": { + "enabled": true + }, + "css": { + "parser": { + "tailwindDirectives": false + } + } +}"# + .as_bytes(), + ); + + let (fs, result) = run_cli( + fs, + &mut console, + Args::from(["format", css_file.as_str()].as_slice()), + ); + + assert!(result.is_err(), "run_cli returned {result:?}"); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "should_not_parse_tailwind_directive_when_disabled", + fs, + console, + result, + )); +} + +#[test] +fn tw_should_not_show_unknown_at_rule_diagnostic() { + let fs = MemoryFileSystem::default(); + let mut console = BufferConsole::default(); + + let css_file_content = r#"@theme {}"#; + let css_file = Utf8Path::new("input.css"); + fs.insert(css_file.into(), css_file_content.as_bytes()); + + let config_path = Utf8Path::new("biome.json"); + fs.insert( + config_path.into(), + r#"{ + "linter": { + "enabled": true, + "rules": { + "recommended": false, + "suspicious": { + "noUnknownAtRules": "warn" + } + } + }, + "css": { + "parser": { + "tailwindDirectives": true + } + } +}"# + .as_bytes(), + ); + + let (fs, result) = run_cli( + fs, + &mut console, + Args::from(["lint", css_file.as_str()].as_slice()), + ); + + assert!(result.is_ok(), "run_cli returned {result:?}"); + + assert_cli_snapshot(SnapshotPayload::new( + module_path!(), + "tw_should_not_show_unknown_at_rule_diagnostic", + fs, + console, + result, + )); +} diff --git a/crates/biome_cli/tests/snapshots/main_cases_tailwind_directives/should_not_parse_tailwind_directive_when_disabled.snap b/crates/biome_cli/tests/snapshots/main_cases_tailwind_directives/should_not_parse_tailwind_directive_when_disabled.snap new file mode 100644 index 000000000000..338076f2ffe3 --- /dev/null +++ b/crates/biome_cli/tests/snapshots/main_cases_tailwind_directives/should_not_parse_tailwind_directive_when_disabled.snap @@ -0,0 +1,69 @@ +--- +source: crates/biome_cli/tests/snap_test.rs +expression: redactor(content) +--- +## `biome.json` + +```json +{ + "formatter": { + "enabled": true + }, + "css": { + "parser": { + "tailwindDirectives": false + } + } +} +``` + +## `input.css` + +```css +@theme {} +``` + +# Termination Message + +```block +format ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × No files were processed in the specified paths. + + i Check your biome.json or biome.jsonc to ensure the paths are not ignored by the configuration. + + i These paths were provided but ignored: + + - input.css + + + +``` + +# Emitted Messages + +```block +input.css:1:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Tailwind-specific syntax is disabled. + + > 1 │ @theme {} + │ ^^^^^^^^ + + i Enable `tailwindDirectives` in the css parser options, or remove this if you are not using Tailwind CSS. + + +``` + +```block +input.css format ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Code formatting aborted due to parsing errors. To format code with errors, enable the 'formatter.formatWithErrors' option. + + +``` + +```block +Checked 1 file in