feat(oxfmt): Support html-in-js substitution#20193
Conversation
How to use the Graphite Merge QueueAdd either label to this PR to merge it via 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. |
Merging this PR will not alter performance
Comparing Footnotes
|
325026b to
4ae3f3f
Compare
8bf4a03 to
eab10a9
Compare
96f52d7 to
4524119
Compare
Oxfmt Ecosystem CI
|
93c97af to
11341da
Compare
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.
dcac45f to
c21c5a7
Compare

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 formattedexprs.However, there are a few key differences:
options.htmlWhitespaceSensitivityThis 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.formatOptionsHiddenDefaultsPrettier adds hidden fields to the
optionsobject during its internal print phase for embedded formatters.However,
__debug.printToDoc()runsnormalizeFormatOptions()which strips unknown keys. Unlike internaltextToDoc()(which usespassThrough: true),printToDoc()does not preserve them.To work around this, we pre-register keys in
formatOptionsHiddenDefaultsso 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_formatterfor JS.However, applying
oxc_formatterto 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 })equivalentWith current implementation,
is_huggable_html_embed_single_arg()always hugs via early-return, ignoring line width.Prettier uses
label({ embed, hug })+conditionalGroupto try hugged first and fall back to expanded.We have the equivalent
BestFitting3-variant flow inwrite_grouped_arguments, butExpandParentfrom HTML template'shardlinepropagates throughwill_break/propagate_expands/ fit check, forcing the expanded variant to always be chosen.To fix, we need to add new IR for boundary (prevents
ExpandParentpropagation inwill_break/propagate_expands/ fit check), then add HTML embeds tocan_group_expression_argumentso they enter the existingBestFitting3-variant flow.However, I'm not sure this is worth to add. So just leave it for now.