Skip to content

feat(oxfmt): Support html-in-js substitution#20193

Merged
graphite-app[bot] merged 1 commit intomainfrom
03-10-feat_oxfmt_support_html-in-js_substitution
Mar 23, 2026
Merged

feat(oxfmt): Support html-in-js substitution#20193
graphite-app[bot] merged 1 commit intomainfrom
03-10-feat_oxfmt_support_html-in-js_substitution

Conversation

@leaysgur
Copy link
Member

@leaysgur leaysgur commented Mar 10, 2026

Part of #15180

The basic flow is the same as css-in-js: replace ${expr} with placeholder strings, let Prettier format the HTML, then substitute back with formatted exprs.

However, there are a few key differences:

options.htmlWhitespaceSensitivity

This HTML-specific option also affects html-in-js (template wrapping strategy, hug detection), so it needed to be held in oxc_formatter::FormatOptions.

prettier.__internal.formatOptionsHiddenDefaults

Prettier adds hidden fields to the options object during its internal print phase for embedded formatters.

However, __debug.printToDoc() runs normalizeFormatOptions() which strips unknown keys. Unlike internal textToDoc() (which uses passThrough: true), printToDoc() does not preserve them.

To work around this, we pre-register keys in formatOptionsHiddenDefaults so they survive normalization.

NOTE: Not fully supported cases

js-in-html-in-js

Currently, the process of converting Docs to our IR does not support conditionalGroup.

Since Prettier only uses this for YAML and JS formatting.
YAML is never embedded, and we would like to use oxc_formatter for JS.

However, applying oxc_formatter to the js-in-html-in-js case was not straightforward. Therefore, I have implemented a fallback to a simpler formatting process for that case for now.

label({ embed, hug }) equivalent

With current implementation, is_huggable_html_embed_single_arg() always hugs via early-return, ignoring line width.

Prettier uses label({ embed, hug }) + conditionalGroup to try hugged first and fall back to expanded.

We have the equivalent BestFitting 3-variant flow in write_grouped_arguments, but ExpandParent from HTML template's hardline propagates through will_break / propagate_expands / fit check, forcing the expanded variant to always be chosen.

To fix, we need to add new IR for boundary (prevents ExpandParent propagation in will_break / propagate_expands / fit check), then add HTML embeds to can_group_expression_argument so they enter the existing BestFitting 3-variant flow.

However, I'm not sure this is worth to add. So just leave it for now.

Copy link
Member Author

leaysgur commented Mar 10, 2026


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • 0-merge - adds this PR to the back of the merge queue
  • hotfix - for urgent changes, fast-track this PR to the front of the merge queue

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions github-actions bot added the C-enhancement Category - New feature or request label Mar 10, 2026
@codspeed-hq
Copy link

codspeed-hq bot commented Mar 10, 2026

Merging this PR will not alter performance

✅ 49 untouched benchmarks
⏩ 7 skipped benchmarks1


Comparing 03-10-feat_oxfmt_support_html-in-js_substitution (dcac45f) with main (5e893d7)2

Open in CodSpeed

Footnotes

  1. 7 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (ff8422a) during the generation of this report, so 5e893d7 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@graphite-app graphite-app bot changed the base branch from 03-09-fix_oxfmt_handle_literalline_for_script-in-vue to graphite-base/20193 March 12, 2026 23:53
@leaysgur leaysgur force-pushed the graphite-base/20193 branch from 325026b to 4ae3f3f Compare March 18, 2026 04:22
@leaysgur leaysgur force-pushed the 03-10-feat_oxfmt_support_html-in-js_substitution branch from 8bf4a03 to eab10a9 Compare March 18, 2026 04:22
@leaysgur leaysgur changed the base branch from graphite-base/20193 to main March 18, 2026 04:22
@leaysgur leaysgur force-pushed the 03-10-feat_oxfmt_support_html-in-js_substitution branch 15 times, most recently from 96f52d7 to 4524119 Compare March 19, 2026 06:56
@leaysgur
Copy link
Member Author

leaysgur commented Mar 19, 2026

Oxfmt Ecosystem CI

suite oxfmt@latest 4524119 branch
formatjs/formatjs
AmanVarshney01/create-better-t-stack
monkeytypegame/monkeytype
eggjs/egg
vuejs/core
huggingface/huggingface.js
cnpm/cnpmcore
lichess-org/lila
Comfy-Org/ComfyUI_frontend
aidenybai/react-grab
npmx-dev/npmx.dev
rolldown/rolldown
fastify/fastify-vite
vuejs/pinia
dyad-sh/dyad
cloudflare/agents
actualbudget/actual
tale/headplane
mastodon/mastodon
getsentry/sentry-javascript
fuma-nama/fumadocs
vercel/turborepo
openclaw/openclaw

@leaysgur leaysgur force-pushed the 03-10-feat_oxfmt_support_html-in-js_substitution branch 2 times, most recently from 93c97af to 11341da Compare March 19, 2026 09:19
@leaysgur leaysgur marked this pull request as ready for review March 19, 2026 09:52
@Dunqing Dunqing added the 0-merge Merge with Graphite Merge Queue label Mar 23, 2026
Copy link
Member

Dunqing commented Mar 23, 2026

Merge activity

Part of #15180

The basic flow is the same as css-in-js: replace `${expr}` with placeholder strings, let Prettier format the HTML, then substitute back with formatted `expr`s.

However, there are a few key differences:

### `options.htmlWhitespaceSensitivity`

This HTML-specific option also affects html-in-js (template wrapping strategy, hug detection), so it needed to be held in `oxc_formatter::FormatOptions`.

### `prettier.__internal.formatOptionsHiddenDefaults`

Prettier adds hidden fields to the `options` object during its internal print phase for embedded formatters.

However, `__debug.printToDoc()` runs `normalizeFormatOptions()` which strips unknown keys. Unlike internal `textToDoc()` (which uses `passThrough: true`), `printToDoc()` does not preserve them.

To work around this, we pre-register keys in `formatOptionsHiddenDefaults` so they survive normalization.

### NOTE: Not fully supported cases

#### js-in-html-in-js

Currently, the process of converting Docs to our IR does not support `conditionalGroup`.

Since Prettier only uses this for YAML and JS formatting.
YAML is never embedded, and we would like to use `oxc_formatter` for JS.

However, applying `oxc_formatter` to the **js**-in-html-in-js case was not straightforward. Therefore, I have implemented a fallback to a simpler formatting process for that case for now.

#### `label({ embed, hug })` equivalent

With current implementation, `is_huggable_html_embed_single_arg()` always hugs via early-return, ignoring line width.

Prettier uses `label({ embed, hug })` + `conditionalGroup` to try hugged first and fall back to expanded.

We have the equivalent `BestFitting` 3-variant flow in `write_grouped_arguments`, but `ExpandParent` from HTML template's `hardline` propagates through `will_break` / `propagate_expands` / fit check, forcing the expanded variant to always be chosen.

To fix, we need to add new IR for boundary (prevents `ExpandParent` propagation in `will_break` / `propagate_expands` / fit check), then add HTML embeds to `can_group_expression_argument` so they enter the existing `BestFitting` 3-variant flow.

However, I'm not sure this is worth to add. So just leave it for now.
@graphite-app graphite-app bot force-pushed the 03-10-feat_oxfmt_support_html-in-js_substitution branch from dcac45f to c21c5a7 Compare March 23, 2026 07:30
@graphite-app graphite-app bot merged commit c21c5a7 into main Mar 23, 2026
26 checks passed
@graphite-app graphite-app bot removed the 0-merge Merge with Graphite Merge Queue label Mar 23, 2026
@graphite-app graphite-app bot deleted the 03-10-feat_oxfmt_support_html-in-js_substitution branch March 23, 2026 07:33
@leaysgur leaysgur mentioned this pull request Mar 23, 2026
21 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-cli Area - CLI A-formatter Area - Formatter C-enhancement Category - New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants