diff --git a/apps/oxfmt/test/cli/embedded_languages/__snapshots__/embedded_languages.test.ts.snap b/apps/oxfmt/test/cli/embedded_languages/__snapshots__/embedded_languages.test.ts.snap index d43a0a2667fd1..a1657de4c47df 100644 --- a/apps/oxfmt/test/cli/embedded_languages/__snapshots__/embedded_languages.test.ts.snap +++ b/apps/oxfmt/test/cli/embedded_languages/__snapshots__/embedded_languages.test.ts.snap @@ -16,6 +16,19 @@ const mixedTemplate = html\`

Title

\`; const mixedDocs = md\`#Documentation This is **important**.\`; +// Empty - Regular template literals retain newlines and spaces, but embedded ones are condensed +const empty = css\`\`; +const empty2 = styled\` +\`; +const empty3 = styled.div\` \`; +const empty4 = gql\` \`; +const empty5 = html\` + +\`; +const empty6 = md\` + +\`; + --- AFTER ---------- // Multiple embedded languages in one file const mixedStyles = css\` @@ -45,6 +58,14 @@ const mixedDocs = md\` This is **important**. \`; +// Empty - Regular template literals retain newlines and spaces, but embedded ones are condensed +const empty = css\`\`; +const empty2 = styled\`\`; +const empty3 = styled.div\`\`; +const empty4 = gql\`\`; +const empty5 = html\`\`; +const empty6 = md\`\`; + --------------------" `; @@ -144,7 +165,7 @@ var(--color), --------------------" `; -exports[`embedded_languages > angular.ts > should format (auto) 1`] = ` +exports[`embedded_languages > should format angular.ts (auto) 1`] = ` "--- FILE ----------- angular.ts --- BEFORE --------- @@ -250,7 +271,7 @@ export class AppComponent2 {} --------------------" `; -exports[`embedded_languages > css.js > should format (auto) 1`] = ` +exports[`embedded_languages > should format css.js (auto) 1`] = ` "--- FILE ----------- css.js --- BEFORE --------- @@ -333,7 +354,7 @@ const styledJsx = ( --------------------" `; -exports[`embedded_languages > graphql.js > should format (auto) 1`] = ` +exports[`embedded_languages > should format graphql.js (auto) 1`] = ` "--- FILE ----------- graphql.js --- BEFORE --------- @@ -365,7 +386,7 @@ const mutation = graphql\` --------------------" `; -exports[`embedded_languages > html.js > should format (auto) 1`] = ` +exports[`embedded_languages > should format html.js (auto) 1`] = ` "--- FILE ----------- html.js --- BEFORE --------- @@ -390,7 +411,7 @@ const component = html\` --------------------" `; -exports[`embedded_languages > markdown.js > should format (auto) 1`] = ` +exports[`embedded_languages > should format markdown.js (auto) 1`] = ` "--- FILE ----------- markdown.js --- BEFORE --------- diff --git a/apps/oxfmt/test/cli/embedded_languages/embedded_languages.test.ts b/apps/oxfmt/test/cli/embedded_languages/embedded_languages.test.ts index dbe9dd9d2e630..6d852e4854397 100644 --- a/apps/oxfmt/test/cli/embedded_languages/embedded_languages.test.ts +++ b/apps/oxfmt/test/cli/embedded_languages/embedded_languages.test.ts @@ -6,11 +6,9 @@ const fixturesDir = join(import.meta.dirname, "fixtures"); const languages = ["css.js", "graphql.js", "html.js", "markdown.js", "angular.ts"]; describe("embedded_languages", () => { - describe.each(languages)("%s", (lang) => { - it("should format (auto)", async () => { - const snapshot = await runWriteModeAndSnapshot(fixturesDir, [lang]); - expect(snapshot).toMatchSnapshot(); - }); + it.each(languages)(`should format %s (auto)`, async (lang) => { + const snapshot = await runWriteModeAndSnapshot(fixturesDir, [lang]); + expect(snapshot).toMatchSnapshot(); }); it("should not format any language (off)", async () => { diff --git a/apps/oxfmt/test/cli/embedded_languages/fixtures/mixed.js b/apps/oxfmt/test/cli/embedded_languages/fixtures/mixed.js index 72c4c44d543ea..1c5ec4ef9df01 100644 --- a/apps/oxfmt/test/cli/embedded_languages/fixtures/mixed.js +++ b/apps/oxfmt/test/cli/embedded_languages/fixtures/mixed.js @@ -9,3 +9,16 @@ const mixedTemplate = html`

Title

`; const mixedDocs = md`#Documentation This is **important**.`; + +// Empty - Regular template literals retain newlines and spaces, but embedded ones are condensed +const empty = css``; +const empty2 = styled` +`; +const empty3 = styled.div` `; +const empty4 = gql` `; +const empty5 = html` + +`; +const empty6 = md` + +`; diff --git a/crates/oxc_formatter/src/print/template.rs b/crates/oxc_formatter/src/print/template.rs index 406566fbb4585..23cf5cd0156cf 100644 --- a/crates/oxc_formatter/src/print/template.rs +++ b/crates/oxc_formatter/src/print/template.rs @@ -782,6 +782,15 @@ fn format_embedded_template<'a>( language: &str, template_content: &str, ) -> bool { + // If the content is whitespace only, + // just trim it and skip calling the embedded formatter + if template_content.trim().is_empty() { + write!(f, ["``"]); + // Return `true` (mark as formatted), + // since whitespace-only regular template literals are preserved as-is + return true; + } + let Some(Ok(formatted)) = f.context().external_callbacks().format_embedded(language, template_content) else {