@@ -43,7 +43,22 @@ pub fn resolve_editorconfig_path(cwd: &Path) -> Option<PathBuf> {
4343 cwd. ancestors ( ) . map ( |dir| dir. join ( ".editorconfig" ) ) . find ( |p| p. exists ( ) )
4444}
4545
46- /// Resolve options for a pre-classified file and build a [`FormatStrategy`].
46+ // ---
47+
48+ /// Outcome of resolving a [`FileKind`] against a [`FormatConfig`],
49+ /// constructed by [`ConfigResolver::resolve`] / [`resolve_for_api`].
50+ #[ derive( Debug ) ]
51+ pub enum ResolveOutcome {
52+ /// Ready to format with this strategy.
53+ Format ( FormatStrategy ) ,
54+ /// The file's parser requires a plugin that the resolved config did NOT enable.
55+ /// The payload carries the missing config key (e.g. `"svelte"`)
56+ /// so callers can construct a friendly error or log message.
57+ #[ cfg_attr( not( feature = "napi" ) , expect( dead_code) ) ]
58+ MissingPlugin ( & ' static str ) ,
59+ }
60+
61+ /// Resolve options for a pre-classified file and build a [`ResolveOutcome`].
4762///
4863/// This is the simplified path for the NAPI `format()` API,
4964/// which doesn't need `.oxfmtrc` overrides, `.editorconfig`, or ignore patterns.
@@ -56,14 +71,17 @@ pub fn resolve_for_api(
5671 raw_config : Value ,
5772 kind : FileKind ,
5873 cwd : & Path ,
59- ) -> Result < FormatStrategy , String > {
74+ ) -> Result < ResolveOutcome , String > {
6075 let mut format_config: FormatConfig =
6176 serde_json:: from_value ( raw_config) . map_err ( |err| err. to_string ( ) ) ?;
6277 format_config. resolve_tailwind_paths ( cwd) ;
6378 // Validate eagerly: `from_format_config` skips validation for `ExternalFormatter*` kinds,
6479 // so range-out values (e.g., `printWidth: 1000`) would otherwise silently reach Prettier.
6580 let _ = to_oxc_formatter ( & format_config) ?;
66- FormatStrategy :: from_format_config ( format_config, kind)
81+ if let Some ( plugin) = kind. requires_plugin ( & format_config) {
82+ return Ok ( ResolveOutcome :: MissingPlugin ( plugin) ) ;
83+ }
84+ FormatStrategy :: from_format_config ( format_config, kind) . map ( ResolveOutcome :: Format )
6785}
6886
6987/// Resolved options ready for the embedded callback to drive `oxc_formatter`.
@@ -389,13 +407,17 @@ impl ConfigResolver {
389407 Ok ( ( ) )
390408 }
391409
392- /// Resolve options for a pre-classified file and build a [`FormatStrategy `].
410+ /// Resolve options for a pre-classified file and build a [`ResolveOutcome `].
393411 ///
394412 /// Returns `Err` only when the merged config (after override application) fails validation.
395413 #[ instrument( level = "debug" , name = "oxfmt::config::resolve" , skip_all, fields( path = %kind. path( ) . display( ) ) ) ]
396- pub fn resolve ( & self , kind : FileKind ) -> Result < FormatStrategy , String > {
414+ pub fn resolve ( & self , kind : FileKind ) -> Result < ResolveOutcome , String > {
397415 let format_config = self . resolve_options ( kind. path ( ) ) ?;
398- FormatStrategy :: from_format_config ( format_config, kind)
416+ #[ cfg( feature = "napi" ) ]
417+ if let Some ( plugin) = kind. requires_plugin ( & format_config) {
418+ return Ok ( ResolveOutcome :: MissingPlugin ( plugin) ) ;
419+ }
420+ FormatStrategy :: from_format_config ( format_config, kind) . map ( ResolveOutcome :: Format )
399421 }
400422
401423 /// Resolve `FormatConfig` for a specific file path.
@@ -731,6 +753,7 @@ mod tests_slow_path_validation {
731753 parser_name : "json" ,
732754 supports_tailwind : false ,
733755 supports_oxfmt : false ,
756+ supports_svelte : false ,
734757 } ;
735758 let err = resolver. resolve ( kind) . unwrap_err ( ) ;
736759 assert ! ( err. contains( "printWidth" ) , "expected printWidth validation error, got: {err}" ) ;
@@ -779,6 +802,7 @@ mod tests_slow_path_validation {
779802 parser_name : "css" ,
780803 supports_tailwind : true ,
781804 supports_oxfmt : false ,
805+ supports_svelte : false ,
782806 } ;
783807 let err = resolve_for_api ( serde_json:: json!( { "printWidth" : 1000 } ) , kind, Path :: new ( "." ) )
784808 . unwrap_err ( ) ;
0 commit comments