fix(jsx): mark <pre> body comments as handled to avoid duplication#796
Open
divybot wants to merge 1 commit into
Open
fix(jsx): mark <pre> body comments as handled to avoid duplication#796divybot wants to merge 1 commit into
divybot wants to merge 1 commit into
Conversation
`<pre>` element bodies are emitted verbatim via `gen_from_raw_string_trim_line_ends` instead of going through `gen_jsx_with_opening_and_closing`. This bypasses the normal child generation path that advances the comment tracker, so any comments inside the `<pre>` body remain unhandled. When `gen_node` is then called for the closing `</pre>` tag, the unhandled comments are picked up by `leading_comments_with_previous` and emitted again as leading comments of the closing tag - duplicating them in the output. Because each pass adds another copy, dprint never reaches a stable format and the embedding formatter (e.g. `deno fmt`) panics with "Formatting not stable". Minimal repro reported as denoland/deno#20403: ```tsx function ErrorPage500(props: ErrorPageProps) { return ( <pre> {props.url} {/* {props.url} */} </pre> ); } ``` The fix advances the comment tracker past the `<pre>` body and marks all comments inside it as handled, so the closing tag's leading-comment pass finds nothing left to emit. Co-Authored-By: Divy Srivastava <me@littledivy.com>
littledivy
added a commit
to denoland/deno
that referenced
this pull request
Jun 13, 2026
`deno fmt` panics with `Formatting not stable. Bailed after 5 tries` when a
`<pre>` JSX element contains a `{/* … */}` expression-container comment, e.g.
```tsx
function ErrorPage500(props: ErrorPageProps) {
return (
<pre>
{props.url}
{/* {props.url} */}
</pre>
);
}
```
`<pre>` element bodies in `dprint-plugin-typescript` are emitted verbatim as
raw text instead of going through the normal JSX child generation path. That
bypass skips the bookkeeping that marks comments inside the body as handled,
so when `gen_node` is later called for `</pre>` the comment is picked up
again as a leading comment of the closing tag and emitted as a JS block
comment outside the expression container. Each formatting pass adds one
more copy of the comment, so the formatter never stabilises.
The fix is in `dprint-plugin-typescript`
(dprint/dprint-plugin-typescript#796) and is consumed here via a
`[patch.crates-io]` git entry until the upstream change ships in a new
crates.io release. A spec regression test in
`tests/specs/fmt/jsx_pre_with_comment` uses the exact input from the issue
and asserts the formatter produces the same text back.
Closes #20403
Closes denoland/orchid#300
Co-Authored-By: Divy Srivastava <me@littledivy.com>
littledivy
added a commit
to denoland/deno
that referenced
this pull request
Jun 13, 2026
`deno fmt` panics with `Formatting not stable. Bailed after 5 tries` when a
`<pre>` JSX element contains a `{/* … */}` expression-container comment, e.g.
```tsx
function ErrorPage500(props: ErrorPageProps) {
return (
<pre>
{props.url}
{/* {props.url} */}
</pre>
);
}
```
`<pre>` element bodies in `dprint-plugin-typescript` are emitted verbatim as
raw text instead of going through the normal JSX child generation path. That
bypass skips the bookkeeping that marks comments inside the body as handled,
so when `gen_node` is later called for `</pre>` the comment is picked up
again as a leading comment of the closing tag and emitted as a JS block
comment outside the expression container. Each formatting pass adds one
more copy of the comment, so the formatter never stabilises.
The fix is in `dprint-plugin-typescript`
(dprint/dprint-plugin-typescript#796) and is consumed here via a
`[patch.crates-io]` git entry until the upstream change ships in a new
crates.io release. A spec regression test in
`tests/specs/fmt/jsx_pre_with_comment` uses the exact input from the issue
and asserts the formatter produces the same text back.
Closes #20403
Closes denoland/orchid#300
Co-Authored-By: Divy Srivastava <me@littledivy.com>
littledivy
added a commit
to denoland/deno
that referenced
this pull request
Jun 15, 2026
`deno fmt` panics with `Formatting not stable. Bailed after 5 tries` when a
`<pre>` JSX element contains a `{/* … */}` expression-container comment, e.g.
```tsx
function ErrorPage500(props: ErrorPageProps) {
return (
<pre>
{props.url}
{/* {props.url} */}
</pre>
);
}
```
`<pre>` element bodies in `dprint-plugin-typescript` are emitted verbatim as
raw text instead of going through the normal JSX child generation path. That
bypass skips the bookkeeping that marks comments inside the body as handled,
so when `gen_node` is later called for `</pre>` the comment is picked up
again as a leading comment of the closing tag and emitted as a JS block
comment outside the expression container. Each formatting pass adds one
more copy of the comment, so the formatter never stabilises.
The fix is in `dprint-plugin-typescript`
(dprint/dprint-plugin-typescript#796) and is consumed here via a
`[patch.crates-io]` git entry until the upstream change ships in a new
crates.io release. A spec regression test in
`tests/specs/fmt/jsx_pre_with_comment` uses the exact input from the issue
and asserts the formatter produces the same text back.
Closes #20403
Closes denoland/orchid#300
Co-Authored-By: Divy Srivastava <me@littledivy.com>
littledivy
added a commit
to denoland/deno
that referenced
this pull request
Jun 15, 2026
`deno fmt` panics with `Formatting not stable. Bailed after 5 tries` when a
`<pre>` JSX element contains a `{/* … */}` expression-container comment, e.g.
```tsx
function ErrorPage500(props: ErrorPageProps) {
return (
<pre>
{props.url}
{/* {props.url} */}
</pre>
);
}
```
`<pre>` element bodies in `dprint-plugin-typescript` are emitted verbatim as
raw text instead of going through the normal JSX child generation path. That
bypass skips the bookkeeping that marks comments inside the body as handled,
so when `gen_node` is later called for `</pre>` the comment is picked up
again as a leading comment of the closing tag and emitted as a JS block
comment outside the expression container. Each formatting pass adds one
more copy of the comment, so the formatter never stabilises.
The fix is in `dprint-plugin-typescript`
(dprint/dprint-plugin-typescript#796) and is consumed here via a
`[patch.crates-io]` git entry until the upstream change ships in a new
crates.io release. A spec regression test in
`tests/specs/fmt/jsx_pre_with_comment` uses the exact input from the issue
and asserts the formatter produces the same text back.
Closes #20403
Closes denoland/orchid#300
Co-Authored-By: Divy Srivastava <me@littledivy.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
<pre>element bodies are emitted verbatim viagen_from_raw_string_trim_line_endsinstead of going throughgen_jsx_with_opening_and_closing. This bypasses the normal child generation path that advances the comment tracker, so any comments inside the<pre>body remain unhandled.When
gen_nodeis then called for the closing</pre>tag, the unhandled comments are picked up byleading_comments_with_previousand emitted again as leading comments of the closing tag — duplicating them in the output. Because each pass adds another copy, the formatter never reaches a stable format and embedding hosts (e.g.deno fmt) panic withFormatting not stable. Bailed after 5 tries.Reproduction
This is reported as denoland/deno#20403:
The first formatting pass produces:
Each subsequent pass adds another
/* {props.url} */line until the embedding formatter gives up.Fix
In the
<pre>branch ofgen_jsx_element, after emitting the body as raw text, advance the comment tracker past the body range and mark every comment it visited as handled. The closing tag's leading-comment pass then finds nothing left to emit.Test plan
tests/specs/jsx/JsxElement/JsxElement_Pre_With_Comments.txtcovering the original repro plus an adjacent-comments variant.