diff --git a/apps/oxfmt/conformance/fixtures/edge-cases/html/nested-html-in-script.html b/apps/oxfmt/conformance/fixtures/edge-cases/html/nested-html-in-script.html
new file mode 100644
index 0000000000..cda8581c92
--- /dev/null
+++ b/apps/oxfmt/conformance/fixtures/edge-cases/html/nested-html-in-script.html
@@ -0,0 +1,3 @@
+
diff --git a/apps/oxfmt/conformance/snapshots/conformance.snap.md b/apps/oxfmt/conformance/snapshots/conformance.snap.md
index 232ba9ed4a..8f3fcb8c2a 100644
--- a/apps/oxfmt/conformance/snapshots/conformance.snap.md
+++ b/apps/oxfmt/conformance/snapshots/conformance.snap.md
@@ -370,7 +370,7 @@
## html
-### Option 1: 617/623 (99.04%)
+### Option 1: 618/624 (99.04%)
```json
{"printWidth":80}
@@ -389,7 +389,7 @@
- [externals/vite/html/valid.html](diffs/html/externals__vite__html__valid.html.md)
- crates/oxc_formatter_htmlish/DIVERGENCES.md#unquoted-value-with-slash
-### Option 2: 618/623 (99.20%)
+### Option 2: 619/624 (99.20%)
```json
{"printWidth":100,"htmlWhitespaceSensitivity":"ignore","singleAttributePerLine":true}
@@ -406,7 +406,7 @@
- [externals/vite/html/valid.html](diffs/html/externals__vite__html__valid.html.md)
- crates/oxc_formatter_htmlish/DIVERGENCES.md#unquoted-value-with-slash
-### Option 3: 617/623 (99.04%)
+### Option 3: 618/624 (99.04%)
```json
{"printWidth":120,"htmlWhitespaceSensitivity":"strict","bracketSameLine":true}
diff --git a/apps/oxfmt/src/core/embed/dispatcher.rs b/apps/oxfmt/src/core/embed/dispatcher.rs
index e5860d1c06..9fecdfddfc 100644
--- a/apps/oxfmt/src/core/embed/dispatcher.rs
+++ b/apps/oxfmt/src/core/embed/dispatcher.rs
@@ -26,6 +26,7 @@ use oxc_formatter_markdown::{MarkdownFormatOptions, XxxInMarkdownCodeBlock};
use oxc_formatter_toml::TomlFormatOptions;
use oxc_formatter_yaml::YamlFormatOptions;
+use super::jsdoc_fence::XxxInJsdocFence;
use crate::core::{
language::{Route, route_embedded},
options::{
@@ -341,20 +342,19 @@ pub fn build_dispatcher(
dispatch_config.toml_options(),
),
NativeLanguage::Html(variant) => {
- // Embedded in a JS template literal or a Markdown code block;
- // `None` is a part formatted on its own (a JSDoc code block).
- // An HTML parent added later needs its marker here, or reads as standalone.
- let embedded_in = request
- .parent_context_as::()
- .map(|c| HtmlishEmbeddedIn {
- indent_multiple_roots: c.indent_multiple_roots,
- placeholder: Some(c.placeholder),
- })
- .or_else(|| {
- request
- .parent_context_as::()
- .map(|_| HtmlishEmbeddedIn::default())
- });
+ // Every part is embedded in its parent (Prettier's `parentParser`),
+ // but a JSDoc code block, formatted on its own
+ let embedded_in =
+ if let Some(c) = request.parent_context_as::() {
+ Some(HtmlishEmbeddedIn {
+ indent_multiple_roots: c.indent_multiple_roots,
+ placeholder: Some(c.placeholder),
+ })
+ } else if request.parent_context_as::().is_some() {
+ None
+ } else {
+ Some(HtmlishEmbeddedIn::default())
+ };
oxc_formatter_htmlish::format_to_ir(
session,
text,
diff --git a/apps/oxfmt/src/core/embed/jsdoc_fence.rs b/apps/oxfmt/src/core/embed/jsdoc_fence.rs
index 63baab92fe..ac7a30a240 100644
--- a/apps/oxfmt/src/core/embed/jsdoc_fence.rs
+++ b/apps/oxfmt/src/core/embed/jsdoc_fence.rs
@@ -16,6 +16,11 @@ use oxc_formatter_core::{
use super::dispatcher::ResolvedDispatchConfig;
+/// Parent→child context for a JSDoc fenced code block:
+/// a part formatted on its own, as Prettier formats it (`prettier.format`, no `parentParser`),
+/// where every other dispatched part is embedded in its parent.
+pub struct XxxInJsdocFence;
+
/// The session's string embedder: [`format_native_fence`] over `fence_dispatcher`,
/// a fallback-less registry (a native fence never falls back to Prettier).
/// `sort_tailwind` is the root's pre-bound sorter, for the `@apply` classes a CSS fence collects.
@@ -76,7 +81,7 @@ fn format_native_fence(
language,
text: code,
input_kind: InputKind::Fragment,
- parent_context: None,
+ parent_context: Some(&XxxInJsdocFence),
},
printer_options,
)?