1+ use std:: path:: PathBuf ;
2+
3+ use serde:: Deserialize ;
14use serde_json:: Value ;
25use tracing:: { debug, instrument} ;
36
@@ -12,9 +15,11 @@ use oxc_span::SourceType;
1215
1316use crate :: {
1417 core:: {
15- EmbeddedCallbackPayload , ExternalFormatter , JsFormatEmbeddedCb , JsFormatEmbeddedDocCb ,
16- JsFormatFileCb , JsInitExternalFormatterCb , JsSortTailwindClassesCb ,
17- oxfmtrc:: { inject_filepath, inject_tailwind_plugin_payload, to_prettier_options} ,
18+ ExternalFormatter , JsFormatEmbeddedCb , JsFormatEmbeddedDocCb , JsFormatFileCb ,
19+ JsInitExternalFormatterCb , JsSortTailwindClassesCb ,
20+ oxfmtrc:: {
21+ FormatConfig , inject_filepath, inject_tailwind_plugin_payload, to_prettier_options,
22+ } ,
1823 resolve_options_for_embedded_js,
1924 } ,
2025 prettier_compat:: to_prettier_doc,
@@ -107,11 +112,9 @@ fn run_full(
107112) -> Option < Value > {
108113 let num_of_threads = 1 ;
109114
110- // Paths in `_oxfmtPluginOptionsJson` are already resolved to absolute paths
111- // by the host (`format_by_external_formatter` with `supports_oxfmt = true`)
112- // when processing the parent file (e.g., App.vue). No further path resolution
113- // needed here.
114- let payload = parse_payload ( oxfmt_plugin_options_json) ;
115+ // Tailwind paths in the payload are already absolute (resolved by the host before serialization),
116+ // so no `cwd` is threaded through here.
117+ let ( config, parent_filepath) = parse_payload ( oxfmt_plugin_options_json) ;
115118
116119 let external_formatter = ExternalFormatter :: new (
117120 init_external_formatter_cb,
@@ -137,15 +140,12 @@ fn run_full(
137140 . expect ( "source_ext should be a valid JS/TS extension" ) ,
138141 ) ;
139142
140- let resolved = resolve_options_for_embedded_js ( payload )
143+ let resolved = resolve_options_for_embedded_js ( config , parent_filepath )
141144 . expect ( "`_oxfmtPluginOptionsJson` should contain valid config" ) ;
142145
143- // Build Prettier options for nested embedded callbacks (e.g., CSS-in-JS inside
144- // the embedded JS). `oxc_formatter` always supports Tailwind sorting, so the
145- // Tailwind plugin payload is injected unconditionally (the inject helper
146- // no-ops when the user config has it disabled). `filepath` uses the parent
147- // path so Tailwind config resolution works correctly (Prettier overwrites
148- // the inner filepath with `dummy.{ts,tsx}`).
146+ // Prettier options for callbacks that `oxc_formatter` may dispatch (e.g., CSS-in-JS).
147+ // The embedded JS context is treated as always Tailwind-capable, so the inject is unconditional.
148+ // The helper no-ops when user config has Tailwind disabled.
149149 let mut external_options = to_prettier_options ( & resolved. config ) ;
150150 inject_filepath ( & mut external_options, & resolved. parent_filepath ) ;
151151 inject_tailwind_plugin_payload ( & mut external_options, & resolved. config ) ;
@@ -198,11 +198,10 @@ fn run_fragment(
198198 let source_type = SourceType :: from_extension ( source_ext)
199199 . expect ( "source_ext should be a valid JS/TS extension" ) ;
200200
201- // NOTE: Paths are already resolved (see `run_full()` comment).
202201 // `run_fragment()` does not support external formatting, so we discard
203202 // `parent_filepath` and `config` from the resolved bundle.
204- let payload = parse_payload ( oxfmt_plugin_options_json) ;
205- let resolved = resolve_options_for_embedded_js ( payload )
203+ let ( config , parent_filepath ) = parse_payload ( oxfmt_plugin_options_json) ;
204+ let resolved = resolve_options_for_embedded_js ( config , parent_filepath )
206205 . expect ( "`_oxfmtPluginOptionsJson` should contain valid config" ) ;
207206 let format_options = resolved. format_options ;
208207
@@ -286,12 +285,14 @@ fn run_fragment(
286285
287286// ---
288287
289- /// Deserializes the plugin options JSON into a typed [`EmbeddedCallbackPayload`].
290- ///
291- /// The host (`format_by_external_formatter` with `supports_oxfmt = true`)
292- /// serializes the typed `FormatConfig` plus the parent filepath into
293- /// `_oxfmtPluginOptionsJson`, and we recover that exact shape here.
294- fn parse_payload ( oxfmt_plugin_options_json : & str ) -> EmbeddedCallbackPayload {
295- serde_json:: from_str ( oxfmt_plugin_options_json)
296- . expect ( "`_oxfmtPluginOptionsJson` should deserialize as `EmbeddedCallbackPayload`" )
288+ /// Deserialize `_oxfmtPluginOptionsJson` into the typed config + parent filepath.
289+ fn parse_payload ( oxfmt_plugin_options_json : & str ) -> ( FormatConfig , PathBuf ) {
290+ #[ derive( Deserialize ) ]
291+ struct Payload {
292+ config : FormatConfig ,
293+ filepath : String ,
294+ }
295+ let payload: Payload = serde_json:: from_str ( oxfmt_plugin_options_json)
296+ . expect ( "`_oxfmtPluginOptionsJson` should deserialize" ) ;
297+ ( payload. config , PathBuf :: from ( payload. filepath ) )
297298}
0 commit comments