Skip to content

fix(jsx): mark <pre> body comments as handled to avoid duplication#796

Open
divybot wants to merge 1 commit into
dprint:mainfrom
divybot:fix/jsx-pre-comment-duplication
Open

fix(jsx): mark <pre> body comments as handled to avoid duplication#796
divybot wants to merge 1 commit into
dprint:mainfrom
divybot:fix/jsx-pre-comment-duplication

Conversation

@divybot
Copy link
Copy Markdown

@divybot divybot commented May 30, 2026

Summary

<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, the formatter never reaches a stable format and embedding hosts (e.g. deno fmt) panic with Formatting not stable. Bailed after 5 tries.

Reproduction

This is reported as denoland/deno#20403:

function ErrorPage500(props: ErrorPageProps) {
  return (
    <pre>
      {props.url}
      {/* {props.url} */}
    </pre>
  );
}

The first formatting pass produces:

function ErrorPage500(props: ErrorPageProps) {
  return (
    <pre>
      {props.url}
      {/* {props.url} */}
    /* {props.url} */
    </pre>
  );
}

Each subsequent pass adds another /* {props.url} */ line until the embedding formatter gives up.

Fix

In the <pre> branch of gen_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

  • Added tests/specs/jsx/JsxElement/JsxElement_Pre_With_Comments.txt covering the original repro plus an adjacent-comments variant.
  • Existing JSX/JsxEmptyExpression specs still pass.

`<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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant