diff options
author | Pig Fang <g-plane@hotmail.com> | 2024-08-15 04:58:48 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-14 22:58:48 +0200 |
commit | 3a3315cc7f3466ce229f6f150402d5ccf72b3d1d (patch) | |
tree | a0299679b2c1eb1cee496905caab380a95f22813 /cli/tools/fmt.rs | |
parent | 22a834ff5b4b312b8f91be8991f2b495d49fad2f (diff) |
feat(fmt): support HTML, Svelte, Vue, Astro and Angular (#25019)
This commit adds capability to format HTML, Svelte, Vue, Astro and Angular
files.
"--unstable-html" is required to format HTML files, and "--unstable-component"
flag is needed to format other formats. These can also be specified in the config
file.
Close #25015
Diffstat (limited to 'cli/tools/fmt.rs')
-rw-r--r-- | cli/tools/fmt.rs | 206 |
1 files changed, 205 insertions, 1 deletions
diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index 348e2d862..2d06aafca 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -36,6 +36,7 @@ use deno_core::unsync::spawn_blocking; use log::debug; use log::info; use log::warn; +use std::borrow::Cow; use std::fs; use std::io::stdin; use std::io::stdout; @@ -248,6 +249,10 @@ fn format_markdown( | "scss" | "sass" | "less" + | "html" + | "svelte" + | "vue" + | "astro" | "yml" | "yaml" ) { @@ -274,6 +279,20 @@ fn format_markdown( Ok(None) } } + "html" => { + if unstable_options.html { + format_html(&fake_filename, text, fmt_options) + } else { + Ok(None) + } + } + "svelte" | "vue" | "astro" => { + if unstable_options.component { + format_html(&fake_filename, text, fmt_options) + } else { + Ok(None) + } + } "yml" | "yaml" => { if unstable_options.yaml { pretty_yaml::format_text( @@ -330,6 +349,107 @@ pub fn format_css( .map_err(AnyError::from) } +pub fn format_html( + file_path: &Path, + file_text: &str, + fmt_options: &FmtOptionsConfig, +) -> Result<Option<String>, AnyError> { + markup_fmt::format_text( + file_text, + markup_fmt::detect_language(file_path) + .unwrap_or(markup_fmt::Language::Html), + &get_resolved_markup_fmt_config(fmt_options), + |text, hints| { + let mut file_name = + file_path.file_name().expect("missing file name").to_owned(); + file_name.push("."); + file_name.push(hints.ext); + let path = file_path.with_file_name(file_name); + match hints.ext { + "css" | "scss" | "sass" | "less" => { + let mut malva_config = get_resolved_malva_config(fmt_options); + malva_config.layout.print_width = hints.print_width; + if hints.attr { + malva_config.language.quotes = + if let Some(true) = fmt_options.single_quote { + malva::config::Quotes::AlwaysDouble + } else { + malva::config::Quotes::AlwaysSingle + }; + malva_config.language.single_line_top_level_declarations = true; + } + malva::format_text( + text, + malva::detect_syntax(path).unwrap_or(malva::Syntax::Css), + &malva_config, + ) + .map(Cow::from) + .map_err(AnyError::from) + } + "json" | "jsonc" => { + let mut json_config = get_resolved_json_config(fmt_options); + json_config.line_width = hints.print_width as u32; + dprint_plugin_json::format_text(&path, text, &json_config).map( + |formatted| { + if let Some(formatted) = formatted { + Cow::from(formatted) + } else { + Cow::from(text) + } + }, + ) + } + _ => { + let mut typescript_config = + get_resolved_typescript_config(fmt_options); + typescript_config.line_width = hints.print_width as u32; + if hints.attr { + typescript_config.quote_style = if let Some(true) = + fmt_options.single_quote + { + dprint_plugin_typescript::configuration::QuoteStyle::AlwaysDouble + } else { + dprint_plugin_typescript::configuration::QuoteStyle::AlwaysSingle + }; + } + dprint_plugin_typescript::format_text( + &path, + text.to_string(), + &typescript_config, + ) + .map(|formatted| { + if let Some(formatted) = formatted { + Cow::from(formatted) + } else { + Cow::from(text) + } + }) + } + } + }, + ) + .map(Some) + .map_err(|error| match error { + markup_fmt::FormatError::Syntax(error) => AnyError::from(error), + markup_fmt::FormatError::External(errors) => { + let last = errors.len() - 1; + AnyError::msg( + errors + .into_iter() + .enumerate() + .map(|(i, error)| { + if i == last { + format!("{error}") + } else { + format!("{error}\n\n") + } + }) + .collect::<String>(), + ) + } + }) +} + /// Formats a single TS, TSX, JS, JSX, JSONC, JSON, MD, or IPYNB file. pub fn format_file( file_path: &Path, @@ -351,6 +471,20 @@ pub fn format_file( Ok(None) } } + "html" => { + if unstable_options.html { + format_html(file_path, file_text, fmt_options) + } else { + Ok(None) + } + } + "svelte" | "vue" | "astro" => { + if unstable_options.component { + format_html(file_path, file_text, fmt_options) + } else { + Ok(None) + } + } "yml" | "yaml" => { if unstable_options.yaml { pretty_yaml::format_text( @@ -810,7 +944,7 @@ fn get_resolved_malva_config( less_import_options_prefer_single_line: None, less_mixin_args_prefer_single_line: None, less_mixin_params_prefer_single_line: None, - top_level_declarations_prefer_single_line: None, + single_line_top_level_declarations: false, selector_override_comment_directive: "deno-fmt-selector-override".into(), ignore_comment_directive: "deno-fmt-ignore".into(), }; @@ -821,6 +955,64 @@ fn get_resolved_malva_config( } } +fn get_resolved_markup_fmt_config( + options: &FmtOptionsConfig, +) -> markup_fmt::config::FormatOptions { + use markup_fmt::config::*; + + let layout_options = LayoutOptions { + print_width: options.line_width.unwrap_or(80) as usize, + use_tabs: options.use_tabs.unwrap_or_default(), + indent_width: options.indent_width.unwrap_or(2) as usize, + line_break: LineBreak::Lf, + }; + + let language_options = LanguageOptions { + quotes: Quotes::Double, + format_comments: false, + script_indent: true, + html_script_indent: None, + vue_script_indent: Some(false), + svelte_script_indent: None, + astro_script_indent: None, + style_indent: true, + html_style_indent: None, + vue_style_indent: Some(false), + svelte_style_indent: None, + astro_style_indent: None, + closing_bracket_same_line: false, + closing_tag_line_break_for_empty: ClosingTagLineBreakForEmpty::Fit, + max_attrs_per_line: None, + prefer_attrs_single_line: false, + html_normal_self_closing: None, + html_void_self_closing: Some(true), + component_self_closing: None, + svg_self_closing: None, + mathml_self_closing: None, + whitespace_sensitivity: WhitespaceSensitivity::Css, + component_whitespace_sensitivity: None, + doctype_keyword_case: DoctypeKeywordCase::Upper, + v_bind_style: None, + v_on_style: None, + v_for_delimiter_style: None, + v_slot_style: None, + component_v_slot_style: None, + default_v_slot_style: None, + named_v_slot_style: None, + v_bind_same_name_short_hand: None, + strict_svelte_attr: false, + svelte_attr_shorthand: Some(true), + svelte_directive_shorthand: Some(true), + astro_attr_shorthand: Some(true), + ignore_comment_directive: "deno-fmt-ignore".into(), + }; + + FormatOptions { + layout: layout_options, + language: language_options, + } +} + fn get_resolved_yaml_config( options: &FmtOptionsConfig, ) -> pretty_yaml::config::FormatOptions { @@ -952,6 +1144,10 @@ fn is_supported_ext_fmt(path: &Path) -> bool { | "scss" | "sass" | "less" + | "html" + | "svelte" + | "vue" + | "astro" | "md" | "mkd" | "mkdn" @@ -1002,6 +1198,14 @@ mod test { assert!(is_supported_ext_fmt(Path::new("foo.Sass"))); assert!(is_supported_ext_fmt(Path::new("foo.less"))); assert!(is_supported_ext_fmt(Path::new("foo.LeSS"))); + assert!(is_supported_ext_fmt(Path::new("foo.html"))); + assert!(is_supported_ext_fmt(Path::new("foo.HTML"))); + assert!(is_supported_ext_fmt(Path::new("foo.svelte"))); + assert!(is_supported_ext_fmt(Path::new("foo.Svelte"))); + assert!(is_supported_ext_fmt(Path::new("foo.vue"))); + assert!(is_supported_ext_fmt(Path::new("foo.VUE"))); + assert!(is_supported_ext_fmt(Path::new("foo.astro"))); + assert!(is_supported_ext_fmt(Path::new("foo.AsTrO"))); assert!(is_supported_ext_fmt(Path::new("foo.yml"))); assert!(is_supported_ext_fmt(Path::new("foo.Yml"))); assert!(is_supported_ext_fmt(Path::new("foo.yaml"))); |