From d730fdd17c644a2a048af32c47d2cbfc6a997ee7 Mon Sep 17 00:00:00 2001 From: leaysgur <6259812+leaysgur@users.noreply.github.com> Date: Tue, 6 Oct 2026 02:57:14 +0000 Subject: [PATCH] fix(formatter_core): leave blank frontmatter without dispatcher (#27359) Always leave FM (in any form) as-is when `embeddedLanguageFormatting: off` (or Rust CLI). --- apps/oxfmt/test/api/front_matter.test.ts | 7 +++++++ crates/oxc_formatter_core/src/envelope/mod.rs | 12 ++++++------ crates/oxc_formatter_css/src/format.rs | 2 +- crates/oxc_formatter_css/tests/conformance.rs | 3 ++- .../tests/snapshots/conformance__prettier-css.snap | 2 +- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/apps/oxfmt/test/api/front_matter.test.ts b/apps/oxfmt/test/api/front_matter.test.ts index 2aa4e83089..4be0de8364 100644 --- a/apps/oxfmt/test/api/front_matter.test.ts +++ b/apps/oxfmt/test/api/front_matter.test.ts @@ -68,6 +68,13 @@ describe("CSS front matter", () => { }); expect(result.errors).toStrictEqual([]); expect(result.code).toBe("---\ntitle: Home\n---\n\nb {\n}\n"); + + // Even an empty block is not normalized + const empty = await format("a.css", "---\n\n---\nb {}\n", { + embeddedLanguageFormatting: "off", + }); + expect(empty.errors).toStrictEqual([]); + expect(empty.code).toBe("---\n\n---\n\nb {\n}\n"); }); it("keeps a physical BOM at byte 0, before the block", async () => { diff --git a/crates/oxc_formatter_core/src/envelope/mod.rs b/crates/oxc_formatter_core/src/envelope/mod.rs index cdd5197f6c..3520fb0028 100644 --- a/crates/oxc_formatter_core/src/envelope/mod.rs +++ b/crates/oxc_formatter_core/src/envelope/mod.rs @@ -18,13 +18,13 @@ use crate::{ /// Writes a front matter block. /// -/// The body dispatches through the session when its resolved language is in `embeddable_languages`, -/// the whole block stays verbatim otherwise. +/// The body dispatches through the session when a dispatcher is installed +/// and its resolved language is in `embeddable_languages`, the whole block stays verbatim otherwise. /// Membership means "gets the frame treatment", not "gets formatted": /// a member language without a serving formatter keeps its body verbatim (`PreserveOriginal`), -/// but its EMPTY block still normalizes through the frame. -/// Never fails; any refusal (`PreserveOriginal`, operational error, non-embeddable language) keeps the block's bytes as-is -/// while the host document still formats. +/// but its EMPTY block still normalizes through the frame when a dispatcher is installed. +/// Never fails; any refusal (`PreserveOriginal`, operational error, no dispatcher, non-embeddable language) +/// keeps the block's bytes as-is while the host document still formats. /// /// The composed shape (Prettier `embed.js` + its css/markdown printers): /// opening delimiter with the explicit language re-emitted (`---yaml`), @@ -36,7 +36,7 @@ pub fn write_front_matter<'a, C: FormatContext>( f: &mut Formatter<'_, 'a, C>, ) { let language = fm.language(); - if embeddable_languages.contains(&language) { + if f.session().has_dispatcher() && embeddable_languages.contains(&language) { let body = fm.value.trim(); if body.is_empty() { write_frame(fm, None, f); diff --git a/crates/oxc_formatter_css/src/format.rs b/crates/oxc_formatter_css/src/format.rs index e02098a038..8389efcd38 100644 --- a/crates/oxc_formatter_css/src/format.rs +++ b/crates/oxc_formatter_css/src/format.rs @@ -31,7 +31,7 @@ pub fn format<'a>( options: CssFormatOptions, ) -> Result>, OxcDiagnostic> { // NOTE: this wrapper labels the run `PhysicalFile` with NO services: - // front matter is detected but its body degrades to verbatim (`PreserveOriginal`), + // front matter is detected but kept verbatim (no dispatcher), // and `@apply` Tailwind classes print unsorted. // Hosts that want them use `format_with_session` with the services installed. format_with_session( diff --git a/crates/oxc_formatter_css/tests/conformance.rs b/crates/oxc_formatter_css/tests/conformance.rs index 7d6a742882..aae856aa49 100644 --- a/crates/oxc_formatter_css/tests/conformance.rs +++ b/crates/oxc_formatter_css/tests/conformance.rs @@ -26,8 +26,9 @@ const CSS: ConformanceConfig = ConformanceConfig { ignore: &[ // postcss-conditionals (archived: https://github.com/andyjansson/postcss-conditionals). "css/atrule/if-else.css", - // YAML frontmatter + // YAML frontmatter, needs the dispatcher this harness doesn't install "css/yaml/dirty.css", + "css/yaml/empty_newlines.css", // range formatting / IDE cursor, not whole-file formatting "css/range/", "css/cursor/", diff --git a/crates/oxc_formatter_css/tests/snapshots/conformance__prettier-css.snap b/crates/oxc_formatter_css/tests/snapshots/conformance__prettier-css.snap index 09c58f8cc8..0dc69637df 100644 --- a/crates/oxc_formatter_css/tests/snapshots/conformance__prettier-css.snap +++ b/crates/oxc_formatter_css/tests/snapshots/conformance__prettier-css.snap @@ -2,7 +2,7 @@ source: crates/oxc_formatter_css/tests/conformance.rs expression: report --- -css compatibility: 142/151 (94.04%), 13 files skipped +css compatibility: 141/150 (94.00%), 13 files skipped # Failed