feat(oxfmt): Support gql-in-js substitution#19670
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
|
54ac372 to
c46f385
Compare
c46f385 to
b898419
Compare
b898419 to
bf5105e
Compare
717dd35 to
1d19661
Compare
94448c6 to
b0ec516
Compare
1d19661 to
87abbe8
Compare
b0ec516 to
188eba2
Compare
Oxfmt Ecosystem CI
|
188eba2 to
ff5e58b
Compare
87abbe8 to
a447840
Compare
|
In the oxfmt-ecosystem-ci, it seemed only However, the fact that no differences were detected there means there's at least no issue there. Benched with https://github.com/redwoodjs/graphql shows: Since it performs more operations than the previous code path, a performance decrease is expected. The result doesn't seem to be too bad. |
ff5e58b to
afeb919
Compare
afeb919 to
204128f
Compare
There was a problem hiding this comment.
Pull request overview
This PR implements GraphQL-in-JS template literal formatting as part of the broader xxx-in-js support effort (#15180). It introduces a new Doc→IR conversion path that allows oxc_formatter to properly respect printWidth for embedded languages, addressing a key limitation of the previous string-based approach.
Changes:
- Adds GraphQL template literal formatting via a new Doc→IR path that handles both no-substitution templates and templates with expressions
- Implements bidirectional conversion between Prettier's Doc format and oxc_formatter's IR format
- Refactors the embedded language formatting architecture with new
format_embedded_doccallback andwrite_embedded_irhelper - Updates all NAPI bindings and JavaScript integration code to support the new callback
Reviewed changes
Copilot reviewed 17 out of 18 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| crates/oxc_formatter/src/print/template/embed/graphql.rs | New module implementing GraphQL template formatting with quasi analysis, comment handling, and expression interpolation |
| crates/oxc_formatter/src/print/template/embed/mod.rs | Refactored embed module with new IR writing infrastructure and template character escaping |
| apps/oxfmt/src/prettier_compat/from_prettier_doc.rs | New module converting Prettier Doc JSON to EmbeddedIR with comprehensive test coverage |
| apps/oxfmt/src/core/external_formatter.rs | Extended with new format_embedded_doc callback supporting batch Doc formatting |
| apps/oxfmt/src-js/libs/apis.ts | Added formatEmbeddedDoc function using Prettier's internal printToDoc API |
| apps/oxfmt/src-js/index.ts, cli.ts, cli-worker.ts, worker-proxy.ts | Integration updates for new callback across all entry points |
| apps/oxfmt/src/main_napi.rs, api/*.rs | NAPI binding updates for new callback signature |
| apps/oxfmt/cmp.js | Comparison script for testing against Prettier's GraphQL formatting |
| crates/oxc_formatter/src/lib.rs, external_formatter.rs | Public API updates exposing new EmbeddedIR types |
Dunqing
left a comment
There was a problem hiding this comment.
Nice! You also solved the printWidth issue!
|
One thing I don't know is why we need |
Merge activity
|
Part of #15180 gql-in-js is the simplest among other xxx-in-js cases. It works by: - splitting the template with `${}`, converting each part into an IR using: - `prettier({ parser:"graphql" })` and `oxc_formatter` respectively - and then interleaving them However, for this to work, two things are essential: - Obtaining a formatted `Doc` from Prettier, rather than just a formatted `string`. - Converting the `Doc` JSON into `oxc_formatter`'s IR. The diffs primarily arise from the implementation of these two. ### TODOs - [x] Implement by Claude - [x] Self review and fix up - [x] Check bench, ecosystem
204128f to
ca68ea6
Compare
I believe it was because the lifetimes However, I feel like I can do it now, so I'll try it as a refactoring. 👌🏻 |
…` directly (#19800) Follow up on #19670 (comment) It seems it was achievable without requiring such major changes. Thank you for your suggestion! 🙏🏻
# Oxlint ### 🚀 Features - 2e0e1d0 linter/no-unused-vars: Add experimental fix mode controls (off|suggestion|fix) (#19774) (camc314) - f34f6fa linter: Introduce typeCheck config option (#19764) (camc314) - 694be7d linter: Introduce typeAware as config options (#19614) (camc314) - 655c38f semantic: Add "did you mean?" suggestions to undefined name errors (#19102) (copilot-swe-agent) - e97a57e linter/id-length: Use serde to deserialize rule options (#19636) (camc314) - c4a3677 parser: Report error for initializer in ambient context (#19187) (camc314) - 346045a linter/id-length: Add `checkGeneric` option (#19634) (camc314) ### 🐛 Bug Fixes - 1b7a937 linter: Correct double-comparisons fix with swapped operands (#19846) (camc314) - c308857 linter/consistent_type_imports: Add missing help and notes to diagnostics (#19827) (Daniel Osmond) - 7682e5a linter/plugins: Decode escapes in identifier tokens (#19838) (overlookmotel) - f368fcd linter/consistent_type_assertions: Add missing with_help and with_note to diagnostics (#19826) (Daniel Osmond) - 04e6223 npm: Add `preferUnplugged` for Yarn PnP compatibility (#19829) (Boshen) - 86d5037 linter: Add help text to no-extend-native, no-useless-backreference (#19733) (Anthony Amaro) - 50e8eff linter: Add .with_help() to operator-assignment, no-nonoctal-decimal-escape (#19732) (Anthony Amaro) - 1417bdc linter/no-wrapper-object-types: Add help messages to missing diagnostics (#19771) (Daniel Osmond) - 0838477 linter/ban_ts_comment: Add help and notes to missing diagnostics (#19781) (Daniel Osmond) - e8c77cf linter/adjacent_overload_signatures: Add missing diagnostics (#19780) (Daniel Osmond) - 28834ac linter/ban_types: Add missing help and note to diagnostics (#19782) (Daniel Osmond) - fd938d3 linter/prefer-enum-initializers: Add help messages to missing diagnostics (#19772) (Daniel Osmond) - eb928ee linter/no-dynamic-delete: Add help messages to missing diagnostics (#19768) (Daniel Osmond) - a985666 linter/no-empty-interface: Add help messages to missing diagnostics (#19769) (Daniel Osmond) - 2dc0ceb linter/no-extra-non-null-assertion: Add help messages to missing diagnostics (#19770) (Daniel Osmond) - 95d5d66 linter/no-dupe-keys: Handle `__proto__` proto setters in (#19762) (camc314) - 24ff0db linter/exhaustive-deps: False positive for member expressions in IIFEs (#19751) (Dennis Chen) - 7243a58 linter/no-use-before-define: Honor `ignoreTypeReferences` when value and type name collisions (#19747) (Dimava) - eefd818 linter/explicit-module-boundary-types: Add help messages to missing diagnostics (#19736) (Daniel Osmond) - 0440e9a linter: Add help text to no_control_regex, no_fallthrough, no_param_reassign (#19655) (Anthony Amaro) - e84cb2f react/display-name: Handle merged type+value context symbols (#19608) (camc314) - ce7e253 linter/prefer-object-from-entries: Require exact path match in unicorn helper (#19687) (camc314) - f5694ce estree/tokens: Reverse field order of `regex` object in tokens (#19679) (overlookmotel) - b2b7a55 estree/tokens: Generate tokens for files with BOM (#19535) (overlookmotel) - 0722721 linter/jsx-curly-brace-presence: False positive with prop & mixed quotes (#19674) (camc314) - 3496acd linter: Enhance diagnostic help messages for eslint rules (#19653) (Anthony Amaro) - e384e94 linter: Enhance help diagnostic messages for more eslint rules (#19658) (Anthony Amaro) - a4d5b34 linter: Avoid non-promise catch false positives (#19574) (camc314) - 5706f38 linter: `unicorn/no-array-callback-reference` skip `Effect.*` array-like methods name. (#19633) (Said Atrahouch) ### ⚡ Performance - 05ccf9f linter/plugins: Transfer tokens via raw transfer (#19893) (overlookmotel) - 4b0611a estree/tokens: Introduce `ESTreeTokenConfig` trait (#19842) (overlookmotel) - ec88f6a estree/tokens: Serialize tokens while visiting AST (#19726) (overlookmotel) - d4dcf26 linter/plugins: Remove `typescript` from bundle (#19531) (overlookmotel) - 6a6513c linter/plugins: Use Oxc tokens in plugins (#19498) (camc314) ### 📚 Documentation - d86f59e linter: Improve docs for no-useless-concat, mark as pending fixer. (#19859) (connorshea) - caa091d linter/plugins: Correct doc comments for `initTokens` (#19530) (overlookmotel) - 2fa936f README.md: Map npm package links to npmx.dev (#19666) (Boshen) - dc0ff73 linter/no-useless-constructor: Warn for parameter properties as well (#19638) (Ole Asteo) # Oxfmt ### 🚀 Features - 5141bc2 formatter: Support trailing ignore comments (#19304) (Andreas Lubbe) - 4888a99 oxfmt/lsp: Support other schemes beside `file://` and `untitled://` (#19872) (Sysix) - 14a0181 oxfmt: Support `graphql()` variant for gql-in-js (#19703) (leaysgur) - ca68ea6 oxfmt: Support gql-in-js substitution (#19670) (leaysgur) - 035933c formatter,oxfmt: Support js-in-vue (partially) (#19514) (leaysgur) - 9e11dc6 parser,estree,coverage: Collect tokens in parser and convert to ESTree format (#19497) (camc314) ### 🐛 Bug Fixes - 8e3842d oxfmt: Avoid embedded TSFN crash by returning errors as data (take2) (#19806) (Yuji Sugiura) - 04e6223 npm: Add `preferUnplugged` for Yarn PnP compatibility (#19829) (Boshen) - e540585 oxfmt: Support tailwind sort for CSS/LESS/SCSS (#19803) (leaysgur) - 93bb861 formatter: Trim trailing whitespace before breaking line (#19740) (leaysgur) - b85f97b formatter: Drop blank line between terminal call and first chain member (#19659) (Dunqing) ### ⚡ Performance - b3b2d30 parser: Introduce `ParserConfig` (#19637) (overlookmotel) ### 📚 Documentation - 2fa936f README.md: Map npm package links to npmx.dev (#19666) (Boshen) Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>

Part of #15180
gql-in-js is the simplest among other xxx-in-js cases.
It works by:
${}, converting each part into an IR using:prettier({ parser:"graphql" })andoxc_formatterrespectivelyHowever, for this to work, two things are essential:
Docfrom Prettier, rather than just a formattedstring.DocJSON intooxc_formatter's IR.The diffs primarily arise from the implementation of these two.
TODOs