diff --git a/apps/oxfmt/conformance/snapshots/prettier.snap.md b/apps/oxfmt/conformance/snapshots/prettier.snap.md index c0b1cabfe5..99d3d1d29f 100644 --- a/apps/oxfmt/conformance/snapshots/prettier.snap.md +++ b/apps/oxfmt/conformance/snapshots/prettier.snap.md @@ -294,32 +294,32 @@ ### Error -- externals/prettier/css/atrule/charset.css: Syntax error: expect token ``, but found `` - - ❓unclassified -- externals/prettier/css/atrule/import.css: Syntax error: expect token ``, but found `(` - - ❓unclassified - externals/prettier/css/atrule/supports.css: Syntax error: expect token `)`, but found `<` - ❓unclassified +- externals/prettier/css/atrule/charset.css: Syntax error: expect token ``, but found `` + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected +- externals/prettier/css/atrule/import.css: Syntax error: expect token ``, but found `(` + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected - externals/prettier/css/attribute/quotes.css: Syntax error: expect token `]`, but found `(` - - ❓unclassified + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected - externals/prettier/css/combinator/combinator.css: Syntax error: expect token `{`, but found `/` - - ❓unclassified + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected - externals/prettier/css/comments/selectors.css: Syntax error: simple selector is expected - - ❓unclassified + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected - externals/prettier/css/custom-properties/emoji.css: Syntax error: declaration at top level is disallowed - - ❓unclassified + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected - externals/prettier/css/no-semicolon/url.css: Syntax error: URL is expected - - ❓unclassified + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected - externals/prettier/css/postcss-plugins/postcss-mixins.css: Syntax error: expect token `{`, but found `` - - ❓unclassified + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected - externals/prettier/css/postcss-plugins/postcss-simple-vars.css: Syntax error: declaration at top level is disallowed - - ❓unclassified + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected - externals/prettier/css/quotes/quotes.css: Syntax error: expect token `{`, but found `` - - ❓unclassified + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected - externals/prettier/css/selector-list/selectors.css: Syntax error: simple selector is expected - - ❓unclassified + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected - externals/prettier/css/selector-string/string.css: Syntax error: simple selector is expected - - ❓unclassified + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected ## scss @@ -366,20 +366,20 @@ ### Error -- externals/prettier/css/custom-properties/emoji.css: Syntax error: declaration at top level is disallowed - - ❓unclassified -- externals/prettier/scss/atrule/for.scss: Syntax error: Sass keyword `to` is expected - - ❓unclassified - externals/prettier/scss/case/case.scss: Syntax error: expect token ``, but found `` - ❓unclassified +- externals/prettier/css/custom-properties/emoji.css: Syntax error: declaration at top level is disallowed + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected +- externals/prettier/scss/atrule/for.scss: Syntax error: Sass keyword `to` is expected + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected - externals/prettier/scss/comments/CRLF.scss: Syntax error: component value is expected - - ❓unclassified + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected - externals/prettier/scss/math/3945.scss: Syntax error: declaration at top level is disallowed - - ❓unclassified + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected - externals/prettier/scss/no-semicolon/url.scss: Syntax error: expect token ``, but found `` - - ❓unclassified + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected - externals/prettier/scss/parens/parens.scss: Syntax error: component value is expected - - ❓unclassified + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected ## less @@ -423,20 +423,20 @@ ### Error -- externals/prettier/less/case/case.less: Syntax error: expect token `(`, but found `` - - ❓unclassified -- externals/prettier/less/comments/in-value.less: Syntax error: expect token `;`, but found `{` - - ❓unclassified -- externals/prettier/less/interpolation/interpolation.less: Syntax error: expect token ``, but found `@{` - - ❓unclassified - externals/prettier/less/less/less.less: Syntax error: expect one of `` or `@{`, but found `(` - ❓unclassified -- externals/prettier/less/lookup/lookup-1.less: Syntax error: expect token `;`, but found `` - - ❓unclassified -- externals/prettier/less/no-semicolon/url.less: Syntax error: expect token `(`, but found `` - - ❓unclassified - externals/prettier/less/parens/parens.less: Syntax error: expect token `;`, but found `{` - ❓unclassified +- externals/prettier/less/case/case.less: Syntax error: expect token `(`, but found `` + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected +- externals/prettier/less/comments/in-value.less: Syntax error: expect token `;`, but found `{` + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected +- externals/prettier/less/interpolation/interpolation.less: Syntax error: expect token ``, but found `@{` + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected +- externals/prettier/less/lookup/lookup-1.less: Syntax error: expect token `;`, but found `` + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected +- externals/prettier/less/no-semicolon/url.less: Syntax error: expect token `(`, but found `` + - crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected ## graphql diff --git a/crates/oxc_formatter_css/AGENTS.md b/crates/oxc_formatter_css/AGENTS.md index 6e74d29b7e..6ef180cc8c 100644 --- a/crates/oxc_formatter_css/AGENTS.md +++ b/crates/oxc_formatter_css/AGENTS.md @@ -193,7 +193,7 @@ and a host-supplied `TailwindSorter` does the ordering and dedup outside this cr Covered: `$var: value !important;` declarations (root and inside rules), `$var` references in property values and at-rule preludes, `$(var)` interpolation in property names. -NOT covered: `$(var)` interpolation inside values and selectors (`.icon.is-$(network)`), selector-position bare `$var` (`.$prefix`), comment substitutions (`<<$(var)>>`). +NOT covered: `$(var)` interpolation inside values and selectors (`.icon.is-$(network)`, rejected: DIVERGENCES.md#grammar-owner-rejected), selector-position bare `$var` (`.$prefix`), comment substitutions (`<<$(var)>>`). ## Verification diff --git a/crates/oxc_formatter_css/DIVERGENCES.md b/crates/oxc_formatter_css/DIVERGENCES.md index 4ff1220f7e..9324fef4a3 100644 --- a/crates/oxc_formatter_css/DIVERGENCES.md +++ b/crates/oxc_formatter_css/DIVERGENCES.md @@ -1026,10 +1026,33 @@ our core `fill` (biome semantics) breaks only at the last fitting separator. Layout-only, the principled fix is the shared core-fill fit-check change (needs a JS-conformance impact experiment first). A spaced `/` is not part of this: it glues to its left operand and breaks after, as in Prettier (`tests/fixtures/format/css/fill-math-chunk-break.css`). -## less-value-interpolation-rejected +## grammar-owner-rejected -- Why: uniform-rule (error semantics) +- Why: uniform-rule (acceptance: the grammar owner decides) - Pin: `tests/fixtures/mod.rs::parse_error_is_err` (parser-level: the rejection is asserted directly) +- Conformance: + - `externals/prettier/css/atrule/charset.css` + - `externals/prettier/css/atrule/import.css` + - `externals/prettier/css/attribute/quotes.css` + - `externals/prettier/css/combinator/combinator.css` + - `externals/prettier/css/comments/selectors.css` + - `externals/prettier/css/custom-properties/emoji.css` + - `externals/prettier/css/no-semicolon/url.css` + - `externals/prettier/css/postcss-plugins/postcss-mixins.css` + - `externals/prettier/css/postcss-plugins/postcss-simple-vars.css` + - `externals/prettier/css/quotes/quotes.css` + - `externals/prettier/css/selector-list/selectors.css` + - `externals/prettier/css/selector-string/string.css` + - `externals/prettier/scss/atrule/for.scss` + - `externals/prettier/scss/comments/CRLF.scss` + - `externals/prettier/scss/math/3945.scss` + - `externals/prettier/scss/no-semicolon/url.scss` + - `externals/prettier/scss/parens/parens.scss` + - `externals/prettier/less/case/case.less` + - `externals/prettier/less/comments/in-value.less` + - `externals/prettier/less/interpolation/interpolation.less` + - `externals/prettier/less/lookup/lookup-1.less` + - `externals/prettier/less/no-semicolon/url.less` ```less /* input */ @@ -1043,9 +1066,18 @@ A spaced `/` is not part of this: it glues to its left operand and breaks after, } ``` -Value-position `@{var}` interpolation: `oxc-css-parser` rejects it matching `lessc`; -Prettier (postcss) accepts and prints verbatim. Per the policy's "Error semantics", -we never format what the reference compiler rejects (a parse error is the SAFE failure). +`oxc-css-parser` rejects what the variant's grammar owner rejects (oxc-css-parser AGENTS.md "Acceptance"), +so per the policy's "Error semantics" we leave it as-is (a parse error is the SAFE failure); +Prettier (postcss) accepts these and prints them, mostly verbatim. + +- CSS, a browser drops the rule (Chrome; lightningcss, built on `cssparser`): + - invalid selectors: `a[id=func("foo")]`, `-Option/root .a`, `>>>`, `.is-$(var)`, `:not(".a")`, `one "two" three` + - invalid `@import` preludes: `@import (multiple) "a";`, `@import "a" {}` + - root declarations, `--foo: { }` included (a custom property, not the raw-prelude `x: { }` rule) + - `@charset UTF-8;`: only the exact `@charset "...";` form means anything (css-syntax-3); a browser drops both forms, so this one rests on the spec +- SCSS, dart-sass rejects: `@for $i from 1 end 3`, a Less variable (`@var`) in a value, root declarations +- Less, lessc rejects: value-position `@{var}`, SCSS interpolation (`#{$var}`), `@IMPORT a;`, a spaced lookup (`@c [ a ]`), `++1` +- Every owner rejects `@import ur l(a.css);` ## media-query-operator-spacing diff --git a/crates/oxc_formatter_css/tests/fixtures/mod.rs b/crates/oxc_formatter_css/tests/fixtures/mod.rs index 20e23cc8c0..bacd10bc49 100644 --- a/crates/oxc_formatter_css/tests/fixtures/mod.rs +++ b/crates/oxc_formatter_css/tests/fixtures/mod.rs @@ -184,8 +184,31 @@ fn parse_error_is_err() { // when a declaration's value hits a top-level `{` (name + `:` parsed); // a selector error with no such head stays a hard error. ("a, { color: red; }", css), - // Pin for crates/oxc_formatter_css/DIVERGENCES.md#less-value-interpolation-rejected + // Pin for crates/oxc_formatter_css/DIVERGENCES.md#grammar-owner-rejected, one input per shape. + // CSS: a browser drops the rule + ("a[id=func(\"foo\")] {}", css), + ("-Option/root .a {}", css), + (".a >>> .b {}", css), + (".icon.is-$(network) {}", css), + (":not(\".a\") {}", css), + ("one \"two\" three {}", css), + ("@import (multiple) \"a.less\";", css), + ("@import \"a.css\" {}", css), + ("@import ur l(a.css);", css), + ("color: red;", css), + ("--foo: { color: red; }", css), + ("@charset UTF-8;", css), + // SCSS: dart-sass rejects (root declarations: `display: flex;` above) + ("@for $i from 1 end 3 {}", scss), + ("a { color: @var; }", scss), + ("@import ur l(a.css);", scss), + // Less: lessc rejects (".a { width: @{min-width}; }", less), + (".a { width: #{$var}; }", less), + ("@IMPORT a;", less), + ("@c: { a: true; }\n.m() when (@c [ a ] = true) {}", less), + (".a { b: ++1; }", less), + ("@import ur l(a.css);", less), ] { assert!(format(&allocator, source, options).is_err(), "{source:?} should fail to format"); }