diff --git a/apps/oxfmt/conformance/fixtures/edge-cases/css-in-js/escaped.js b/apps/oxfmt/conformance/fixtures/edge-cases/css-in-js/escaped.js new file mode 100644 index 0000000000000..798cde37c3ec1 --- /dev/null +++ b/apps/oxfmt/conformance/fixtures/edge-cases/css-in-js/escaped.js @@ -0,0 +1,7 @@ +import styled from 'styled-components'; + +const Example = styled.div` + content: '\\201C'; +`; + +export default Example; diff --git a/apps/oxfmt/conformance/snapshots/conformance.snap.md b/apps/oxfmt/conformance/snapshots/conformance.snap.md index ebf05e57f39bb..1b94a7cb10286 100644 --- a/apps/oxfmt/conformance/snapshots/conformance.snap.md +++ b/apps/oxfmt/conformance/snapshots/conformance.snap.md @@ -44,7 +44,7 @@ ## css-in-js -### Option 1: 16/17 (94.12%) +### Option 1: 17/18 (94.44%) ```json {"printWidth":80} @@ -54,7 +54,7 @@ | :--- | :--- | | [styled-components.js](diffs/css-in-js/styled-components.js.md) | `Xxx.extend` not recognized as tag | -### Option 2: 16/17 (94.12%) +### Option 2: 17/18 (94.44%) ```json {"printWidth":100} diff --git a/apps/oxfmt/src/prettier_compat/from_prettier_doc.rs b/apps/oxfmt/src/prettier_compat/from_prettier_doc.rs index 851faa7bae6cc..aa1ada9ee5412 100644 --- a/apps/oxfmt/src/prettier_compat/from_prettier_doc.rs +++ b/apps/oxfmt/src/prettier_compat/from_prettier_doc.rs @@ -25,11 +25,13 @@ pub fn to_format_elements_for_template<'a>( allocator: &'a Allocator, group_id_builder: &UniqueGroupIdBuilder, ) -> Result, String> { - let convert = |doc_json: &Value| -> Result<(Vec>, usize), String> { + let convert = |doc_json: &Value, + escape_template_chars: bool| + -> Result<(Vec>, usize), String> { let mut ctx = FmtCtx::new(allocator, group_id_builder); let mut out = vec![]; convert_doc(doc_json, &mut out, &mut ctx)?; - let placeholder_count = postprocess(&mut out, allocator); + let placeholder_count = postprocess(&mut out, allocator, escape_template_chars); Ok((out, placeholder_count)) }; @@ -38,14 +40,14 @@ pub fn to_format_elements_for_template<'a>( let doc_json = doc_jsons .first() .ok_or_else(|| "Expected exactly one Doc JSON for CSS".to_string())?; - let (ir, count) = convert(doc_json)?; + let (ir, count) = convert(doc_json, false)?; Ok(EmbeddedDocResult::DocWithPlaceholders(ir, count)) } "tagged-graphql" => { let irs = doc_jsons .iter() .map(|doc_json| { - let (ir, _) = convert(doc_json)?; + let (ir, _) = convert(doc_json, true)?; Ok(ir) }) .collect::, String>>()?; @@ -350,10 +352,16 @@ fn extract_group_id( /// - collapse double-hardlines `[Hard, ExpandParent, Hard, ExpandParent]` → `[Empty, ExpandParent]` /// - merge consecutive Text nodes (SCSS emits split strings like `"@"` + `"prettier-placeholder-0-id"`) /// - escape template characters (`\`, `` ` ``, `${`) +/// - for css-in-js, this is not needed because values are already escaped via `.raw` +/// - for others, `.cooked` is used, so escaping is needed /// - count `@prettier-placeholder-N-id` patterns /// /// Returns the placeholder count (0 for non-CSS languages). -fn postprocess<'a>(ir: &mut Vec>, allocator: &'a Allocator) -> usize { +fn postprocess<'a>( + ir: &mut Vec>, + allocator: &'a Allocator, + escape_template_chars: bool, +) -> usize { const PREFIX: &str = "@prettier-placeholder-"; const SUFFIX: &str = "-id"; @@ -388,9 +396,9 @@ fn postprocess<'a>(ir: &mut Vec>, allocator: &'a Allocator) -> read += 1; } - let escaped = if read - run_start == 1 { + let text = if read - run_start == 1 { let FormatElement::Text { text, .. } = &ir[run_start] else { unreachable!() }; - escape_template_characters(text, allocator) + text } else { let mut sb = StringBuilder::new_in(allocator); for element in &ir[run_start..read] { @@ -398,14 +406,19 @@ fn postprocess<'a>(ir: &mut Vec>, allocator: &'a Allocator) -> sb.push_str(text); } } - escape_template_characters(sb.into_str(), allocator) + sb.into_str() + }; + let text = if escape_template_chars { + escape_template_characters(text, allocator) + } else { + text }; - let width = TextWidth::from_text(escaped, IndentWidth::default()); - ir[write] = FormatElement::Text { text: escaped, width }; + let width = TextWidth::from_text(text, IndentWidth::default()); + ir[write] = FormatElement::Text { text, width }; write += 1; // Count placeholders - let mut remaining = escaped; + let mut remaining = text; while let Some(start) = remaining.find(PREFIX) { let after_prefix = &remaining[start + PREFIX.len()..]; let digit_end = after_prefix