fix(formatter_css): keep css-in-js placeholder as media type (#27639)

Part of #27572.

Not fixed completely, just keeps placeholders in `@media` and `@import`.
This commit is contained in:
leaysgur committed 2026-10-13 06:43:47 +00:00
1 parent 372849ebd1
commit 99bb268da7
5 files changed
+109 -8

No files matched your search

@@ -0,0 +1,19 @@
const mobile = "(max-width: 600px)";
export const A = styled.div`
color: red;
@media ${mobile} {
color: blue;
}
`;
export const B = css`
@media not ${a} { color: blue; }
@media ${a} and (min-width: 1px) { color: blue; }
@media ${a}, ${b} { color: blue; }
`;
export const C = createGlobalStyle`
@import url(x) ${a};
@import url(x) screen, ${b};
`;
@@ -50,7 +50,7 @@
## css-in-js
### Option 1: 21/23 (91.30%)
### Option 1: 22/24 (91.67%)
```json
{"printWidth":80}
@@ -61,7 +61,7 @@
- [externals/prettier/js/multiparser-css/styled-components.js](diffs/css-in-js/externals__prettier__js__multiparser-css__styled-components.js.md)
- apps/oxfmt/DIVERGENCES.md#styled-extend-tag
### Option 2: 21/23 (91.30%)
### Option 2: 22/24 (91.67%)
```json
{"printWidth":100}
+21 -6
View File
@@ -7,8 +7,8 @@ use cow_utils::CowUtils;
use oxc_css_parser::{
ast::{
AtRule, AtRulePrelude, ComponentValue, CustomMediaValue, Ident, ImportPrelude,
ImportPreludeHref, ImportPreludeSupports, ImportPreludeSupportsKind, InterpolableStr,
KeyframesName, LessImportOptions, LessImportPrelude, MediaCondition,
ImportPreludeHref, ImportPreludeSupports, ImportPreludeSupportsKind, InterpolableIdent,
InterpolableStr, KeyframesName, LessImportOptions, LessImportPrelude, MediaCondition,
MediaConditionAfterMediaType, MediaConditionKind, MediaFeature, MediaFeatureComparisonKind,
MediaFeatureName, MediaInParens, MediaInParensKind, MediaQuery, MediaQueryList,
NamespacePreludeUri, SassAtRootKind, SimpleBlock, SupportsAnd, SupportsCondition,
@@ -35,7 +35,7 @@ use crate::{
print::{
CssFormatter, format_with, is_glued, normalize_whitespace, scss, selector, statement,
value::{self, ValueContext},
write_maybe_lowercase,
write_maybe_lowercase, write_placeholder,
},
};
@@ -1488,8 +1488,10 @@ enum ImportWord<'b, 'a> {
LessOptions(&'b LessImportOptions<'a>),
Href(&'b ImportPreludeHref<'a>),
Supports(&'b ImportPreludeSupports<'a>),
/// A media keyword (`screen`, `and`, `not`): lowercased
/// A media keyword (`only`, `and`, `not`): lowercased
Keyword(oxc_span::Span),
/// A media type (`screen`), see `write_media_type`
MediaType(&'b InterpolableIdent<'a>),
InParens(&'b MediaInParens<'a>),
/// `and(...)` / `not(...)`: a function call to Prettier's value parser
/// (keyword glued, the inside printed like any `(cond)`); `@media` reads it as one word
@@ -1520,6 +1522,7 @@ fn write_import_words<'a>(comma_groups: &[Vec<ImportWord<'_, 'a>>], f: &mut CssF
ImportWord::Href(href) => write_import_href(href, f),
ImportWord::Supports(supports) => write_import_supports(supports, f),
ImportWord::Keyword(span) => write_maybe_lowercase(source.text_for(span), f),
ImportWord::MediaType(media_type) => write_media_type(media_type, f),
ImportWord::InParens(in_parens) => write_media_in_parens(in_parens, f),
ImportWord::GluedCall(keyword, in_parens) => {
write_maybe_lowercase(source.text_for(&to_span(keyword.span())), f);
@@ -1604,7 +1607,7 @@ fn push_media_query_list_words<'b, 'a>(
if let Some(modifier) = &with_type.modifier {
words.push(ImportWord::Keyword(to_span(modifier.span())));
}
words.push(ImportWord::Keyword(to_span(with_type.media_type.span())));
words.push(ImportWord::MediaType(&with_type.media_type));
if let Some(condition) = &with_type.condition {
let kinds = &condition.condition.conditions;
// `screen and(...)`: the glued `and` and the first parens are one word
@@ -1948,7 +1951,9 @@ fn write_media_query<'a>(query: &MediaQuery<'a>, f: &mut CssFormatter<'_, 'a>) {
write_word(to_span(modifier.span()), f);
write!(f, space());
}
write_word(to_span(with_type.media_type.span()), f);
value::write_with_comments(to_span(with_type.media_type.span()), query_end, f, |f| {
write_media_type(&with_type.media_type, f);
});
if let Some(condition) = &with_type.condition {
write!(f, space());
// `screen and(max-width: 1px)`: the glued `and(...)` is one word
@@ -1977,6 +1982,16 @@ fn write_media_query<'a>(query: &MediaQuery<'a>, f: &mut CssFormatter<'_, 'a>) {
}
}
/// A css-in-js placeholder stays typed, a keyword is lowercased.
fn write_media_type<'a>(media_type: &InterpolableIdent<'a>, f: &mut CssFormatter<'_, 'a>) {
if let InterpolableIdent::Placeholder(placeholder) = media_type {
write_placeholder(placeholder, f);
} else {
let source = f.context().source_text();
write_maybe_lowercase(source.text_for(&to_span(media_type.span())), f);
}
}
fn write_media_condition<'a>(condition: &MediaCondition<'a>, f: &mut CssFormatter<'_, 'a>) {
write_media_condition_from(condition, 0, f);
}
@@ -0,0 +1,9 @@
@media `PLACEHOLDER-0` {
color: blue;
}
@media not `PLACEHOLDER-1` { color: blue; }
@media `PLACEHOLDER-2` and (min-width: 1px) { color: blue; }
@media `PLACEHOLDER-3`, `PLACEHOLDER-4` { color: blue; }
@media `PLACEHOLDER-5` /* c */ and (min-width: 1px) { color: blue; }
@import url(x) `PLACEHOLDER-6`;
@import url(x) screen, `PLACEHOLDER-7`;
@@ -0,0 +1,58 @@
---
source: crates/oxc_formatter_css/tests/fixtures/mod.rs
---
==================== Input ====================
@media `PLACEHOLDER-0` {
color: blue;
}
@media not `PLACEHOLDER-1` { color: blue; }
@media `PLACEHOLDER-2` and (min-width: 1px) { color: blue; }
@media `PLACEHOLDER-3`, `PLACEHOLDER-4` { color: blue; }
@media `PLACEHOLDER-5` /* c */ and (min-width: 1px) { color: blue; }
@import url(x) `PLACEHOLDER-6`;
@import url(x) screen, `PLACEHOLDER-7`;
==================== Output ====================
------------------
{ printWidth: 80 }
------------------
@media `PLACEHOLDER-0` {
color: blue;
}
@media not `PLACEHOLDER-1` {
color: blue;
}
@media `PLACEHOLDER-2` and (min-width: 1px) {
color: blue;
}
@media `PLACEHOLDER-3`, `PLACEHOLDER-4` {
color: blue;
}
@media `PLACEHOLDER-5` /* c */ and (min-width: 1px) {
color: blue;
}
@import url(x) `PLACEHOLDER-6`;
@import url(x) screen, `PLACEHOLDER-7`;
-------------------
{ printWidth: 100 }
-------------------
@media `PLACEHOLDER-0` {
color: blue;
}
@media not `PLACEHOLDER-1` {
color: blue;
}
@media `PLACEHOLDER-2` and (min-width: 1px) {
color: blue;
}
@media `PLACEHOLDER-3`, `PLACEHOLDER-4` {
color: blue;
}
@media `PLACEHOLDER-5` /* c */ and (min-width: 1px) {
color: blue;
}
@import url(x) `PLACEHOLDER-6`;
@import url(x) screen, `PLACEHOLDER-7`;
===================== End =====================