From 0b42278de9c2e3694d056ff14f58c619e75332c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Tue, 19 Nov 2024 00:16:34 +0100 Subject: [PATCH 1/8] feat: Support for external formatters --- src/format_text.rs | 5 ++- src/generation/context.rs | 4 +++ src/generation/generate.rs | 11 ++++++ tests/spec_test.rs | 73 +++++++++++++++++++++++--------------- 4 files changed, 64 insertions(+), 29 deletions(-) diff --git a/src/format_text.rs b/src/format_text.rs index 9b386d8c..e382075f 100644 --- a/src/format_text.rs +++ b/src/format_text.rs @@ -47,6 +47,7 @@ pub fn format_text(file_path: &Path, file_extension: Option<&str>, file_text: St let file_text = if had_bom { file_text[3..].to_string() } else { file_text }; let file_text: Arc = file_text.into(); let parsed_source = parse_swc_ast(file_path, file_extension, file_text)?; + eprintln!("parsed source {:#?}", parsed_source); match inner_format(&parsed_source, config)? { Some(new_text) => Ok(Some(new_text)), None => { @@ -111,7 +112,9 @@ mod test { fn strips_bom() { for input_text in ["\u{FEFF}const t = 5;\n", "\u{FEFF}const t = 5;"] { let config = crate::configuration::ConfigurationBuilder::new().build(); - let result = format_text(&std::path::PathBuf::from("test.ts"), None, input_text.into(), &config).unwrap().unwrap(); + let result = format_text(&std::path::PathBuf::from("test.ts"), None, input_text.into(), &config) + .unwrap() + .unwrap(); assert_eq!(result, "const t = 5;\n"); } } diff --git a/src/generation/context.rs b/src/generation/context.rs index 1368f1ce..1262d0cd 100644 --- a/src/generation/context.rs +++ b/src/generation/context.rs @@ -18,11 +18,14 @@ use super::*; use crate::configuration::*; use crate::utils::Stack; +pub type ExternalFormatter = Box String>; + pub struct Context<'a> { pub media_type: MediaType, pub program: Program<'a>, pub config: &'a Configuration, pub comments: CommentTracker<'a>, + pub external_formatter: Option, pub token_finder: TokenFinder<'a>, pub current_node: Node<'a>, pub parent_stack: Stack>, @@ -49,6 +52,7 @@ impl<'a> Context<'a> { program, config, comments: CommentTracker::new(program, tokens), + external_formatter: None, token_finder: TokenFinder::new(program), current_node, parent_stack: Default::default(), diff --git a/src/generation/generate.rs b/src/generation/generate.rs index 6a4cb249..3a172cf5 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -3012,6 +3012,17 @@ fn gen_tagged_tpl<'a>(node: &TaggedTpl<'a>, context: &mut Context<'a>) -> PrintI items.extend(generated_between_comments); } + if let Expr::Ident(ident) = node.tag { + if ident.sym() == "css" && node.tpl.quasis.len() == 1 { + let mut i = PrintItems::new(); + let quasi = node.tpl.quasis[0]; + eprintln!("template {}", quasi.raw()); + let externally_formatted = "`asdfasdf`".to_string(); + i.push_string(externally_formatted); + items.push_condition(conditions::indent_if_start_of_line(i)); + return items; + } + } items.push_condition(conditions::indent_if_start_of_line(gen_node(node.tpl.into(), context))); items } diff --git a/tests/spec_test.rs b/tests/spec_test.rs index c0c429ce..1671a482 100644 --- a/tests/spec_test.rs +++ b/tests/spec_test.rs @@ -14,34 +14,51 @@ fn main() { ..Default::default() }; - run_specs( - &PathBuf::from("./tests/specs"), - &ParseSpecOptions { default_file_name: "file.ts" }, - &RunSpecsOptions { - fix_failures: false, - format_twice: true, - }, - { - let global_config = global_config.clone(); - Arc::new(move |file_name, file_text, spec_config| { - let spec_config: ConfigKeyMap = serde_json::from_value(spec_config.clone().into()).unwrap(); - let config_result = resolve_config(spec_config, &global_config); - ensure_no_diagnostics(&config_result.diagnostics); + let file_text = r#"import css from 'styled-components' - format_text(file_name, None, file_text.into(), &config_result.config) - }) - }, - Arc::new(move |_file_name, _file_text, _spec_config| { - #[cfg(feature = "tracing")] - { - let spec_config: ConfigKeyMap = serde_json::from_value(_spec_config.clone().into()).unwrap(); - let config_result = resolve_config(spec_config, &global_config); - ensure_no_diagnostics(&config_result.diagnostics); - return serde_json::to_string(&trace_file(_file_name, _file_text, &config_result.config)).unwrap(); - } +const Header = css` + height: 40px; padding: 0 15px; + display: flex; align-items: center; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +`"#; + let r = format_text( + &std::path::PathBuf::from("foo.js"), + None, + file_text.into(), + &ConfigurationBuilder::new().build(), + ); + eprintln!("result {:#?}", r); + return; - #[cfg(not(feature = "tracing"))] - panic!("\n====\nPlease run with `cargo test --features tracing` to get trace output\n====\n") - }), - ) + // run_specs( + // &PathBuf::from("./tests/specs"), + // &ParseSpecOptions { default_file_name: "file.ts" }, + // &RunSpecsOptions { + // fix_failures: false, + // format_twice: true, + // }, + // { + // let global_config = global_config.clone(); + // Arc::new(move |file_name, file_text, spec_config| { + // let spec_config: ConfigKeyMap = serde_json::from_value(spec_config.clone().into()).unwrap(); + // let config_result = resolve_config(spec_config, &global_config); + // ensure_no_diagnostics(&config_result.diagnostics); + + // format_text(file_name, None, file_text.into(), &config_result.config) + // }) + // }, + // Arc::new(move |_file_name, _file_text, _spec_config| { + // #[cfg(feature = "tracing")] + // { + // let spec_config: ConfigKeyMap = serde_json::from_value(_spec_config.clone().into()).unwrap(); + // let config_result = resolve_config(spec_config, &global_config); + // ensure_no_diagnostics(&config_result.diagnostics); + // return serde_json::to_string(&trace_file(_file_name, _file_text, &config_result.config)).unwrap(); + // } + + // #[cfg(not(feature = "tracing"))] + // panic!("\n====\nPlease run with `cargo test --features tracing` to get trace output\n====\n") + // }), + // ) } From 8c578d54f277bba0de2bb51cb0e8a1f7f6b77ff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Fri, 29 Nov 2024 00:44:11 +0100 Subject: [PATCH 2/8] format somehow --- src/format_text.rs | 1 - src/generation/context.rs | 2 +- src/generation/generate.rs | 32 +++++++++++++---- tests/spec_test.rs | 73 +++++++++++++++----------------------- 4 files changed, 55 insertions(+), 53 deletions(-) diff --git a/src/format_text.rs b/src/format_text.rs index e382075f..13eea7c9 100644 --- a/src/format_text.rs +++ b/src/format_text.rs @@ -47,7 +47,6 @@ pub fn format_text(file_path: &Path, file_extension: Option<&str>, file_text: St let file_text = if had_bom { file_text[3..].to_string() } else { file_text }; let file_text: Arc = file_text.into(); let parsed_source = parse_swc_ast(file_path, file_extension, file_text)?; - eprintln!("parsed source {:#?}", parsed_source); match inner_format(&parsed_source, config)? { Some(new_text) => Ok(Some(new_text)), None => { diff --git a/src/generation/context.rs b/src/generation/context.rs index 1262d0cd..1b9b7ae2 100644 --- a/src/generation/context.rs +++ b/src/generation/context.rs @@ -18,7 +18,7 @@ use super::*; use crate::configuration::*; use crate::utils::Stack; -pub type ExternalFormatter = Box String>; +pub type ExternalFormatter = Box Option>; pub struct Context<'a> { pub media_type: MediaType, diff --git a/src/generation/generate.rs b/src/generation/generate.rs index 3a172cf5..b1beeb67 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -2996,6 +2996,28 @@ fn gen_spread_element<'a>(node: &SpreadElement<'a>, context: &mut Context<'a>) - items } +fn maybe_format_tagged_tpl_with_external_formatter<'a>(node: &TaggedTpl<'a>, external_formatter: &ExternalFormatter) -> Option { + let Expr::Ident(ident) = node.tag else { + return None; + }; + + // TODO(bartlomieju): support `html` and `sql` template tags + let media_type = match ident.sym().as_str() { + "css" => MediaType::Css, + _ => return None, + }; + + // TODO(bartlomieju): support formatting when there are multiplie quasis, but first need to figure + // out how to put valid "placeholders" for different languages + if node.tpl.quasis.len() != 1 { + return None; + } + + let quasi = node.tpl.quasis[0]; + + external_formatter(media_type, quasi.raw().to_string()) +} + fn gen_tagged_tpl<'a>(node: &TaggedTpl<'a>, context: &mut Context<'a>) -> PrintItems { let use_space = context.config.tagged_template_space_before_literal; let mut items = gen_node(node.tag.into(), context); @@ -3012,17 +3034,15 @@ fn gen_tagged_tpl<'a>(node: &TaggedTpl<'a>, context: &mut Context<'a>) -> PrintI items.extend(generated_between_comments); } - if let Expr::Ident(ident) = node.tag { - if ident.sym() == "css" && node.tpl.quasis.len() == 1 { + if let Some(external_formatter) = context.external_formatter.as_ref() { + if let Some(formatted_tpl) = maybe_format_tagged_tpl_with_external_formatter(node, external_formatter) { let mut i = PrintItems::new(); - let quasi = node.tpl.quasis[0]; - eprintln!("template {}", quasi.raw()); - let externally_formatted = "`asdfasdf`".to_string(); - i.push_string(externally_formatted); + i.push_string(formatted_tpl); items.push_condition(conditions::indent_if_start_of_line(i)); return items; } } + items.push_condition(conditions::indent_if_start_of_line(gen_node(node.tpl.into(), context))); items } diff --git a/tests/spec_test.rs b/tests/spec_test.rs index 1671a482..c0c429ce 100644 --- a/tests/spec_test.rs +++ b/tests/spec_test.rs @@ -14,51 +14,34 @@ fn main() { ..Default::default() }; - let file_text = r#"import css from 'styled-components' + run_specs( + &PathBuf::from("./tests/specs"), + &ParseSpecOptions { default_file_name: "file.ts" }, + &RunSpecsOptions { + fix_failures: false, + format_twice: true, + }, + { + let global_config = global_config.clone(); + Arc::new(move |file_name, file_text, spec_config| { + let spec_config: ConfigKeyMap = serde_json::from_value(spec_config.clone().into()).unwrap(); + let config_result = resolve_config(spec_config, &global_config); + ensure_no_diagnostics(&config_result.diagnostics); -const Header = css` - height: 40px; padding: 0 15px; - display: flex; align-items: center; - border-top-left-radius: 5px; - border-top-right-radius: 5px; -`"#; - let r = format_text( - &std::path::PathBuf::from("foo.js"), - None, - file_text.into(), - &ConfigurationBuilder::new().build(), - ); - eprintln!("result {:#?}", r); - return; + format_text(file_name, None, file_text.into(), &config_result.config) + }) + }, + Arc::new(move |_file_name, _file_text, _spec_config| { + #[cfg(feature = "tracing")] + { + let spec_config: ConfigKeyMap = serde_json::from_value(_spec_config.clone().into()).unwrap(); + let config_result = resolve_config(spec_config, &global_config); + ensure_no_diagnostics(&config_result.diagnostics); + return serde_json::to_string(&trace_file(_file_name, _file_text, &config_result.config)).unwrap(); + } - // run_specs( - // &PathBuf::from("./tests/specs"), - // &ParseSpecOptions { default_file_name: "file.ts" }, - // &RunSpecsOptions { - // fix_failures: false, - // format_twice: true, - // }, - // { - // let global_config = global_config.clone(); - // Arc::new(move |file_name, file_text, spec_config| { - // let spec_config: ConfigKeyMap = serde_json::from_value(spec_config.clone().into()).unwrap(); - // let config_result = resolve_config(spec_config, &global_config); - // ensure_no_diagnostics(&config_result.diagnostics); - - // format_text(file_name, None, file_text.into(), &config_result.config) - // }) - // }, - // Arc::new(move |_file_name, _file_text, _spec_config| { - // #[cfg(feature = "tracing")] - // { - // let spec_config: ConfigKeyMap = serde_json::from_value(_spec_config.clone().into()).unwrap(); - // let config_result = resolve_config(spec_config, &global_config); - // ensure_no_diagnostics(&config_result.diagnostics); - // return serde_json::to_string(&trace_file(_file_name, _file_text, &config_result.config)).unwrap(); - // } - - // #[cfg(not(feature = "tracing"))] - // panic!("\n====\nPlease run with `cargo test --features tracing` to get trace output\n====\n") - // }), - // ) + #[cfg(not(feature = "tracing"))] + panic!("\n====\nPlease run with `cargo test --features tracing` to get trace output\n====\n") + }), + ) } From 01af45f61965c54e1da7a26ad792b7a4477cd8ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Fri, 29 Nov 2024 01:02:41 +0100 Subject: [PATCH 3/8] add a test --- src/format_text.rs | 90 +++++++++++++++++++++++++++++++++++--- src/generation/context.rs | 11 ++++- src/generation/generate.rs | 20 +++++++-- src/generation/mod.rs | 1 + src/lib.rs | 2 + 5 files changed, 114 insertions(+), 10 deletions(-) diff --git a/src/format_text.rs b/src/format_text.rs index 13eea7c9..5b95c376 100644 --- a/src/format_text.rs +++ b/src/format_text.rs @@ -10,6 +10,7 @@ use crate::swc::ensure_no_specific_syntax_errors; use super::configuration::Configuration; use super::generation::generate; +pub use super::generation::ExternalFormatter; use super::swc::parse_swc_ast; /// Formats a file. @@ -40,6 +41,26 @@ use super::swc::parse_swc_ast; /// } /// ``` pub fn format_text(file_path: &Path, file_extension: Option<&str>, file_text: String, config: &Configuration) -> Result> { + format_text_inner(file_path, file_extension, file_text, config, None) +} + +pub fn format_text_with_external_formatter( + file_path: &Path, + file_extension: Option<&str>, + file_text: String, + config: &Configuration, + external_formatter: ExternalFormatter, +) -> Result> { + format_text_inner(file_path, file_extension, file_text, config, Some(external_formatter)) +} + +fn format_text_inner( + file_path: &Path, + file_extension: Option<&str>, + file_text: String, + config: &Configuration, + external_formatter: Option, +) -> Result> { if super::utils::file_text_has_ignore_comment(&file_text, &config.ignore_file_comment_text) { Ok(None) } else { @@ -47,7 +68,7 @@ pub fn format_text(file_path: &Path, file_extension: Option<&str>, file_text: St let file_text = if had_bom { file_text[3..].to_string() } else { file_text }; let file_text: Arc = file_text.into(); let parsed_source = parse_swc_ast(file_path, file_extension, file_text)?; - match inner_format(&parsed_source, config)? { + match inner_format(&parsed_source, config, external_formatter)? { Some(new_text) => Ok(Some(new_text)), None => { if had_bom { @@ -66,15 +87,16 @@ pub fn format_parsed_source(source: &ParsedSource, config: &Configuration) -> Re Ok(None) } else { ensure_no_specific_syntax_errors(source)?; - inner_format(source, config) + // TODO(bartlomieju): support external formatter + inner_format(source, config, None) } } -fn inner_format(parsed_source: &ParsedSource, config: &Configuration) -> Result> { +fn inner_format(parsed_source: &ParsedSource, config: &Configuration, external_formatter: Option) -> Result> { let result = dprint_core::formatting::format( || { #[allow(clippy::let_and_return)] - let print_items = generate(parsed_source, config); + let print_items = generate(parsed_source, config, external_formatter); // println!("{}", print_items.get_as_text()); print_items }, @@ -91,7 +113,7 @@ fn inner_format(parsed_source: &ParsedSource, config: &Configuration) -> Result< pub fn trace_file(file_path: &Path, file_text: &str, config: &Configuration) -> dprint_core::formatting::TracingResult { let parsed_source = parse_swc_ast(file_path, None, file_text.into()).unwrap(); ensure_no_specific_syntax_errors(&parsed_source).unwrap(); - dprint_core::formatting::trace_printing(|| generate(&parsed_source, config), config_to_print_options(file_text, config)) + dprint_core::formatting::trace_printing(|| generate(&parsed_source, config, None), config_to_print_options(file_text, config)) } fn config_to_print_options(file_text: &str, config: &Configuration) -> PrintOptions { @@ -105,6 +127,10 @@ fn config_to_print_options(file_text: &str, config: &Configuration) -> PrintOpti #[cfg(test)] mod test { + use deno_ast::MediaType; + + use crate::configuration::ConfigurationBuilder; + use super::*; #[test] @@ -117,4 +143,58 @@ mod test { assert_eq!(result, "const t = 5;\n"); } } + + #[test] + fn format_with_external_formatter() { + let file_text = r#"import css from 'styled-components' + +const Header = css` +height: 40px; padding: 0 15px; +display: flex; align-items: center; +border-top-left-radius: 5px; +border-top-right-radius: 5px; +`"#; + + fn external_formatter(media_type: MediaType, text: String) -> Option { + assert_eq!(media_type, MediaType::Css); + Some( + text + .split(';') + .filter_map(|val| { + let val = val.trim(); + if val.is_empty() { + None + } else { + Some(format!("{};", val)) + } + }) + .collect::>() + .join("\n"), + ) + } + + let formatted_text = format_text_with_external_formatter( + &std::path::PathBuf::from("foo.js"), + None, + file_text.into(), + &ConfigurationBuilder::new().build(), + Box::new(external_formatter), + ) + .unwrap() + .unwrap(); + + assert_eq!( + formatted_text, + r#"import css from "styled-components"; + +const Header = css`height: 40px; +padding: 0 15px; +display: flex; +align-items: center; +border-top-left-radius: 5px; +border-top-right-radius: 5px; +`; +"# + ); + } } diff --git a/src/generation/context.rs b/src/generation/context.rs index 1b9b7ae2..63981cfb 100644 --- a/src/generation/context.rs +++ b/src/generation/context.rs @@ -46,13 +46,20 @@ pub struct Context<'a> { } impl<'a> Context<'a> { - pub fn new(media_type: MediaType, tokens: &'a [TokenAndSpan], current_node: Node<'a>, program: Program<'a>, config: &'a Configuration) -> Context<'a> { + pub fn new( + media_type: MediaType, + tokens: &'a [TokenAndSpan], + current_node: Node<'a>, + program: Program<'a>, + config: &'a Configuration, + external_formatter: Option, + ) -> Context<'a> { Context { media_type, program, config, comments: CommentTracker::new(program, tokens), - external_formatter: None, + external_formatter, token_finder: TokenFinder::new(program), current_node, parent_stack: Default::default(), diff --git a/src/generation/generate.rs b/src/generation/generate.rs index b1beeb67..89584992 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -25,13 +25,20 @@ use super::*; use crate::configuration::*; use crate::utils; -pub fn generate(parsed_source: &ParsedSource, config: &Configuration) -> PrintItems { +pub fn generate(parsed_source: &ParsedSource, config: &Configuration, external_formatter: Option) -> PrintItems { // eprintln!("Leading: {:?}", parsed_source.comments().leading_map()); // eprintln!("Trailing: {:?}", parsed_source.comments().trailing_map()); parsed_source.with_view(|program| { let program_node = program.into(); - let mut context = Context::new(parsed_source.media_type(), parsed_source.tokens(), program_node, program, config); + let mut context = Context::new( + parsed_source.media_type(), + parsed_source.tokens(), + program_node, + program, + config, + external_formatter, + ); let mut items = gen_node(program_node, &mut context); items.push_condition(if_true( "endOfFileNewLine", @@ -3037,7 +3044,14 @@ fn gen_tagged_tpl<'a>(node: &TaggedTpl<'a>, context: &mut Context<'a>) -> PrintI if let Some(external_formatter) = context.external_formatter.as_ref() { if let Some(formatted_tpl) = maybe_format_tagged_tpl_with_external_formatter(node, external_formatter) { let mut i = PrintItems::new(); - i.push_string(formatted_tpl); + // TODO(bartlomieju): not fully correct, need to handle trailing newlines better + i.push_string("`".to_string()); + for line in formatted_tpl.lines() { + i.push_string(line.to_string()); + i.push_signal(Signal::NewLine); + } + i.push_string("`".to_string()); + // i.push_string(formatted_tpl.trim_end().to_string()); items.push_condition(conditions::indent_if_start_of_line(i)); return items; } diff --git a/src/generation/mod.rs b/src/generation/mod.rs index 09e0434a..7696bcb8 100644 --- a/src/generation/mod.rs +++ b/src/generation/mod.rs @@ -12,4 +12,5 @@ use context::*; use generate_types::*; use tokens::*; +pub use context::ExternalFormatter; pub use generate::generate; diff --git a/src/lib.rs b/src/lib.rs index 38c5a479..4b0fb016 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,8 @@ mod utils; pub use format_text::format_parsed_source; pub use format_text::format_text; +pub use format_text::format_text_with_external_formatter; +pub use format_text::ExternalFormatter; #[cfg(feature = "tracing")] pub use format_text::trace_file; From 46b32c1143ae7095cf882502fea214792a49067c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Fri, 29 Nov 2024 01:05:03 +0100 Subject: [PATCH 4/8] another test --- src/format_text.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/format_text.rs b/src/format_text.rs index 5b95c376..7ebc0751 100644 --- a/src/format_text.rs +++ b/src/format_text.rs @@ -196,5 +196,19 @@ border-top-right-radius: 5px; `; "# ); + + let file_text = "const htmlString = html`Hello there!`;\n"; + + let formatted_text = format_text_with_external_formatter( + &std::path::PathBuf::from("foo.js"), + None, + file_text.into(), + &ConfigurationBuilder::new().build(), + Box::new(external_formatter), + ) + .unwrap() + .unwrap(); + + assert_eq!(formatted_text, file_text); } } From 98ecc93b92aba48557df428e611422beb9b57072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Fri, 29 Nov 2024 01:13:18 +0100 Subject: [PATCH 5/8] fix a test --- src/format_text.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/format_text.rs b/src/format_text.rs index 7ebc0751..1b36f9a2 100644 --- a/src/format_text.rs +++ b/src/format_text.rs @@ -206,9 +206,9 @@ border-top-right-radius: 5px; &ConfigurationBuilder::new().build(), Box::new(external_formatter), ) - .unwrap() .unwrap(); - assert_eq!(formatted_text, file_text); + // No changes, so returns None - no changes because `html` template tag is currently not understood. + assert!(formatted_text.is_none()); } } From 9a9b855a3b6f7fb56bd8155689375be7d5793f21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Fri, 29 Nov 2024 01:28:08 +0100 Subject: [PATCH 6/8] move to spec tests --- src/format_text.rs | 72 ------------------- src/generation/generate.rs | 27 +++---- tests/spec_test.rs | 22 +++++- tests/specs/externalFormatter/css.txt | 21 ++++++ .../externalFormatter/css_many_quasis.txt | 16 +++++ tests/specs/externalFormatter/html.txt | 6 ++ tests/specs/externalFormatter/sql.txt | 6 ++ 7 files changed, 85 insertions(+), 85 deletions(-) create mode 100644 tests/specs/externalFormatter/css.txt create mode 100644 tests/specs/externalFormatter/css_many_quasis.txt create mode 100644 tests/specs/externalFormatter/html.txt create mode 100644 tests/specs/externalFormatter/sql.txt diff --git a/src/format_text.rs b/src/format_text.rs index 1b36f9a2..d1c3323e 100644 --- a/src/format_text.rs +++ b/src/format_text.rs @@ -127,10 +127,6 @@ fn config_to_print_options(file_text: &str, config: &Configuration) -> PrintOpti #[cfg(test)] mod test { - use deno_ast::MediaType; - - use crate::configuration::ConfigurationBuilder; - use super::*; #[test] @@ -143,72 +139,4 @@ mod test { assert_eq!(result, "const t = 5;\n"); } } - - #[test] - fn format_with_external_formatter() { - let file_text = r#"import css from 'styled-components' - -const Header = css` -height: 40px; padding: 0 15px; -display: flex; align-items: center; -border-top-left-radius: 5px; -border-top-right-radius: 5px; -`"#; - - fn external_formatter(media_type: MediaType, text: String) -> Option { - assert_eq!(media_type, MediaType::Css); - Some( - text - .split(';') - .filter_map(|val| { - let val = val.trim(); - if val.is_empty() { - None - } else { - Some(format!("{};", val)) - } - }) - .collect::>() - .join("\n"), - ) - } - - let formatted_text = format_text_with_external_formatter( - &std::path::PathBuf::from("foo.js"), - None, - file_text.into(), - &ConfigurationBuilder::new().build(), - Box::new(external_formatter), - ) - .unwrap() - .unwrap(); - - assert_eq!( - formatted_text, - r#"import css from "styled-components"; - -const Header = css`height: 40px; -padding: 0 15px; -display: flex; -align-items: center; -border-top-left-radius: 5px; -border-top-right-radius: 5px; -`; -"# - ); - - let file_text = "const htmlString = html`Hello there!`;\n"; - - let formatted_text = format_text_with_external_formatter( - &std::path::PathBuf::from("foo.js"), - None, - file_text.into(), - &ConfigurationBuilder::new().build(), - Box::new(external_formatter), - ) - .unwrap(); - - // No changes, so returns None - no changes because `html` template tag is currently not understood. - assert!(formatted_text.is_none()); - } } diff --git a/src/generation/generate.rs b/src/generation/generate.rs index 89584992..a3b8a73b 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -3003,7 +3003,7 @@ fn gen_spread_element<'a>(node: &SpreadElement<'a>, context: &mut Context<'a>) - items } -fn maybe_format_tagged_tpl_with_external_formatter<'a>(node: &TaggedTpl<'a>, external_formatter: &ExternalFormatter) -> Option { +fn maybe_format_tagged_tpl_with_external_formatter<'a>(node: &TaggedTpl<'a>, external_formatter: &ExternalFormatter) -> Option { let Expr::Ident(ident) = node.tag else { return None; }; @@ -3022,7 +3022,19 @@ fn maybe_format_tagged_tpl_with_external_formatter<'a>(node: &TaggedTpl<'a>, ext let quasi = node.tpl.quasis[0]; - external_formatter(media_type, quasi.raw().to_string()) + let Some(formatted_tpl) = external_formatter(media_type, quasi.raw().to_string()) else { + return None; + }; + + let mut items = PrintItems::new(); + // TODO(bartlomieju): might not be fully correct, need to better handle trailing newlines? + items.push_string("`".to_string()); + for line in formatted_tpl.lines() { + items.push_string(line.to_string()); + items.push_signal(Signal::NewLine); + } + items.push_string("`".to_string()); + Some(items) } fn gen_tagged_tpl<'a>(node: &TaggedTpl<'a>, context: &mut Context<'a>) -> PrintItems { @@ -3043,16 +3055,7 @@ fn gen_tagged_tpl<'a>(node: &TaggedTpl<'a>, context: &mut Context<'a>) -> PrintI if let Some(external_formatter) = context.external_formatter.as_ref() { if let Some(formatted_tpl) = maybe_format_tagged_tpl_with_external_formatter(node, external_formatter) { - let mut i = PrintItems::new(); - // TODO(bartlomieju): not fully correct, need to handle trailing newlines better - i.push_string("`".to_string()); - for line in formatted_tpl.lines() { - i.push_string(line.to_string()); - i.push_signal(Signal::NewLine); - } - i.push_string("`".to_string()); - // i.push_string(formatted_tpl.trim_end().to_string()); - items.push_condition(conditions::indent_if_start_of_line(i)); + items.push_condition(conditions::indent_if_start_of_line(formatted_tpl)); return items; } } diff --git a/tests/spec_test.rs b/tests/spec_test.rs index c0c429ce..b0d5d35e 100644 --- a/tests/spec_test.rs +++ b/tests/spec_test.rs @@ -1,11 +1,31 @@ use std::path::PathBuf; use std::sync::Arc; +use deno_ast::MediaType; use dprint_core::configuration::*; use dprint_development::*; use dprint_plugin_typescript::configuration::*; use dprint_plugin_typescript::*; +fn external_formatter(media_type: MediaType, text: String) -> Option { + assert_eq!(media_type, MediaType::Css); + // Put each rule on a separate line. + Some( + text + .split(';') + .filter_map(|val| { + let val = val.trim(); + if val.is_empty() { + None + } else { + Some(format!("{};", val)) + } + }) + .collect::>() + .join("\n"), + ) +} + fn main() { //debug_here!(); let global_config = GlobalConfiguration { @@ -28,7 +48,7 @@ fn main() { let config_result = resolve_config(spec_config, &global_config); ensure_no_diagnostics(&config_result.diagnostics); - format_text(file_name, None, file_text.into(), &config_result.config) + format_text_with_external_formatter(file_name, None, file_text.into(), &config_result.config, Box::new(external_formatter)) }) }, Arc::new(move |_file_name, _file_text, _spec_config| { diff --git a/tests/specs/externalFormatter/css.txt b/tests/specs/externalFormatter/css.txt new file mode 100644 index 00000000..beb65810 --- /dev/null +++ b/tests/specs/externalFormatter/css.txt @@ -0,0 +1,21 @@ +-- file.js -- +== should format CSS tagged template with a single quasi == +import css from 'styled-components' + +const Header = css` +height: 40px; padding: 0 15px; +display: flex; align-items: center; +border-top-left-radius: 5px; +border-top-right-radius: 5px; +`; + +[expect] +import css from "styled-components"; + +const Header = css`height: 40px; +padding: 0 15px; +display: flex; +align-items: center; +border-top-left-radius: 5px; +border-top-right-radius: 5px; +`; diff --git a/tests/specs/externalFormatter/css_many_quasis.txt b/tests/specs/externalFormatter/css_many_quasis.txt new file mode 100644 index 00000000..79bb7f2b --- /dev/null +++ b/tests/specs/externalFormatter/css_many_quasis.txt @@ -0,0 +1,16 @@ +-- file.js -- +== should not format CSS tagged template with multiple quasis == +import css from 'styled-components' + +const height = 50; +const Header = css` +height: ${height}px; +`; + +[expect] +import css from "styled-components"; + +const height = 50; +const Header = css` +height: ${height}px; +`; diff --git a/tests/specs/externalFormatter/html.txt b/tests/specs/externalFormatter/html.txt new file mode 100644 index 00000000..5e945d46 --- /dev/null +++ b/tests/specs/externalFormatter/html.txt @@ -0,0 +1,6 @@ +-- file.js -- +== should not format HTML tagged template == +const htmlString = html`Hello there!`; + +[expect] +const htmlString = html`Hello there!`; diff --git a/tests/specs/externalFormatter/sql.txt b/tests/specs/externalFormatter/sql.txt new file mode 100644 index 00000000..0c202ae4 --- /dev/null +++ b/tests/specs/externalFormatter/sql.txt @@ -0,0 +1,6 @@ +-- file.js -- +== should not format SQL tagged template == +const query = sql`SELECT * FROM users;`; + +[expect] +const query = sql`SELECT * FROM users;`; From 09d6ed27b9f00f69903a0764a5c7ccfd7b61b8fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Fri, 29 Nov 2024 01:31:55 +0100 Subject: [PATCH 7/8] add a doc string --- src/generation/context.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/generation/context.rs b/src/generation/context.rs index 63981cfb..da2a1248 100644 --- a/src/generation/context.rs +++ b/src/generation/context.rs @@ -18,6 +18,33 @@ use super::*; use crate::configuration::*; use crate::utils::Stack; +/// A callback that will be called when encountering certain tagged templates. +/// +/// Currently supports `css`, `html` and `sql` tagged templated. +/// +/// Examples: +/// ``` +/// const styles = css`color: red;`; +/// +/// const markup = html` +/// +///

Hello!

+/// +/// `; +/// +/// const query = sql` +/// SELECT +/// * +/// FROM +/// users +/// WHERE +/// active IS TRUE; +/// ``` +/// +/// External formatter should return `None` if it doesn't understand given `MediaType`, in such +/// cases the templates will be left as they are. +/// +/// Only templates with no interpolation are supported. pub type ExternalFormatter = Box Option>; pub struct Context<'a> { From 0b1eee40112057b108d4e1d9a47187de18a04692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Fri, 29 Nov 2024 01:36:41 +0100 Subject: [PATCH 8/8] ignore doctest --- src/generation/context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generation/context.rs b/src/generation/context.rs index da2a1248..96981c1b 100644 --- a/src/generation/context.rs +++ b/src/generation/context.rs @@ -23,7 +23,7 @@ use crate::utils::Stack; /// Currently supports `css`, `html` and `sql` tagged templated. /// /// Examples: -/// ``` +/// ```ignore /// const styles = css`color: red;`; /// /// const markup = html`