summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
Diffstat (limited to 'cli')
-rw-r--r--cli/Cargo.toml3
-rw-r--r--cli/flags.rs17
-rw-r--r--cli/fs_util.rs43
-rw-r--r--cli/main.rs6
-rw-r--r--cli/tests/badly_formatted.md28
-rw-r--r--cli/tests/badly_formatted_fixed.md23
-rw-r--r--cli/tests/fmt/expected_fmt_check_formatted_files.out2
-rw-r--r--cli/tests/fmt/expected_fmt_check_ignore.out2
-rw-r--r--cli/tests/fmt/expected_fmt_check_tests_dir.out2
-rw-r--r--cli/tests/fmt/formatted3.md17
-rw-r--r--cli/tests/integration_tests.rs48
-rw-r--r--cli/tools/fmt.rs109
12 files changed, 257 insertions, 43 deletions
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 04861e302..e69d25621 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -46,7 +46,8 @@ base64 = "0.13.0"
byteorder = "1.4.2"
clap = "2.33.3"
dissimilar = "1.0.2"
-dprint-plugin-typescript = "0.38.1"
+dprint-plugin-typescript = "0.38.3"
+dprint-plugin-markdown = "0.5.1"
encoding_rs = "0.8.26"
env_logger = "0.8.2"
filetime = "0.2.13"
diff --git a/cli/flags.rs b/cli/flags.rs
index f018d00b0..e87924491 100644
--- a/cli/flags.rs
+++ b/cli/flags.rs
@@ -50,6 +50,7 @@ pub enum DenoSubcommand {
check: bool,
files: Vec<PathBuf>,
ignore: Vec<PathBuf>,
+ ext: String,
},
Info {
json: bool,
@@ -400,8 +401,11 @@ fn fmt_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
Some(f) => f.map(PathBuf::from).collect(),
None => vec![],
};
+ let ext = matches.value_of("ext").unwrap().to_string();
+
flags.subcommand = DenoSubcommand::Fmt {
check: matches.is_present("check"),
+ ext,
files,
ignore,
}
@@ -810,6 +814,14 @@ Ignore formatting a file by adding an ignore comment at the top of the file:
.takes_value(false),
)
.arg(
+ Arg::with_name("ext")
+ .long("ext")
+ .help("Set standard input (stdin) content type")
+ .takes_value(true)
+ .default_value("ts")
+ .possible_values(&["ts", "tsx", "js", "jsx", "md"]),
+ )
+ .arg(
Arg::with_name("ignore")
.long("ignore")
.takes_value(true)
@@ -1991,6 +2003,7 @@ mod tests {
PathBuf::from("script_1.ts"),
PathBuf::from("script_2.ts")
],
+ ext: "ts".to_string()
},
..Flags::default()
}
@@ -2004,6 +2017,7 @@ mod tests {
ignore: vec![],
check: true,
files: vec![],
+ ext: "ts".to_string(),
},
..Flags::default()
}
@@ -2017,6 +2031,7 @@ mod tests {
ignore: vec![],
check: false,
files: vec![],
+ ext: "ts".to_string(),
},
..Flags::default()
}
@@ -2030,6 +2045,7 @@ mod tests {
ignore: vec![],
check: false,
files: vec![],
+ ext: "ts".to_string(),
},
watch: true,
unstable: true,
@@ -2053,6 +2069,7 @@ mod tests {
ignore: vec![PathBuf::from("bar.js")],
check: true,
files: vec![PathBuf::from("foo.ts")],
+ ext: "ts".to_string(),
},
watch: true,
unstable: true,
diff --git a/cli/fs_util.rs b/cli/fs_util.rs
index 130a209ce..a6cd06e78 100644
--- a/cli/fs_util.rs
+++ b/cli/fs_util.rs
@@ -90,17 +90,30 @@ pub fn resolve_from_cwd(path: &Path) -> Result<PathBuf, AnyError> {
/// Checks if the path has extension Deno supports.
pub fn is_supported_ext(path: &Path) -> bool {
- let lowercase_ext = path
- .extension()
- .and_then(|e| e.to_str())
- .map(|e| e.to_lowercase());
- if let Some(ext) = lowercase_ext {
- ext == "ts" || ext == "tsx" || ext == "js" || ext == "jsx" || ext == "mjs"
+ if let Some(ext) = get_extension(path) {
+ matches!(ext.as_str(), "ts" | "tsx" | "js" | "jsx" | "mjs")
+ } else {
+ false
+ }
+}
+
+/// This function is similar to is_supported_ext but also allows .md extension.
+pub fn is_supported_ext_md(path: &Path) -> bool {
+ if let Some(ext) = get_extension(path) {
+ matches!(ext.as_str(), "ts" | "tsx" | "js" | "jsx" | "mjs" | "md")
} else {
false
}
}
+/// Get the extension of a file in lowercase.
+pub fn get_extension(file_path: &Path) -> Option<String> {
+ return file_path
+ .extension()
+ .and_then(|e| e.to_str())
+ .map(|e| e.to_lowercase());
+}
+
/// Collects file paths that satisfy the given predicate, by recursively walking `files`.
/// If the walker visits a path that is listed in `ignore`, it skips descending into the directory.
pub fn collect_files<P>(
@@ -208,6 +221,24 @@ mod tests {
}
#[test]
+ fn test_is_supported_ext_md() {
+ assert!(!is_supported_ext_md(Path::new("tests/subdir/redirects")));
+ assert!(is_supported_ext_md(Path::new("README.md")));
+ assert!(is_supported_ext_md(Path::new("readme.MD")));
+ assert!(is_supported_ext_md(Path::new("lib/typescript.d.ts")));
+ assert!(is_supported_ext_md(Path::new("cli/tests/001_hello.js")));
+ assert!(is_supported_ext_md(Path::new("cli/tests/002_hello.ts")));
+ assert!(is_supported_ext_md(Path::new("foo.jsx")));
+ assert!(is_supported_ext_md(Path::new("foo.tsx")));
+ assert!(is_supported_ext_md(Path::new("foo.TS")));
+ assert!(is_supported_ext_md(Path::new("foo.TSX")));
+ assert!(is_supported_ext_md(Path::new("foo.JS")));
+ assert!(is_supported_ext_md(Path::new("foo.JSX")));
+ assert!(is_supported_ext_md(Path::new("foo.mjs")));
+ assert!(!is_supported_ext_md(Path::new("foo.mjsx")));
+ }
+
+ #[test]
fn test_collect_files() {
fn create_files(dir_path: &PathBuf, files: &[&str]) {
std::fs::create_dir(dir_path).expect("Failed to create directory");
diff --git a/cli/main.rs b/cli/main.rs
index de0e63e84..d98313f54 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -807,9 +807,10 @@ async fn format_command(
args: Vec<PathBuf>,
ignore: Vec<PathBuf>,
check: bool,
+ ext: String,
) -> Result<(), AnyError> {
if args.len() == 1 && args[0].to_string_lossy() == "-" {
- return tools::fmt::format_stdin(check);
+ return tools::fmt::format_stdin(check, ext);
}
tools::fmt::format(args, ignore, check, flags.watch).await?;
@@ -1179,7 +1180,8 @@ fn get_subcommand(
check,
files,
ignore,
- } => format_command(flags, files, ignore, check).boxed_local(),
+ ext,
+ } => format_command(flags, files, ignore, check, ext).boxed_local(),
DenoSubcommand::Info { file, json } => {
info_command(flags, file, json).boxed_local()
}
diff --git a/cli/tests/badly_formatted.md b/cli/tests/badly_formatted.md
new file mode 100644
index 000000000..adffe3e46
--- /dev/null
+++ b/cli/tests/badly_formatted.md
@@ -0,0 +1,28 @@
+# Hello Markdown
+
+```js
+console.log("Hello World"
+
+)
+```
+
+```javascript
+console.log("Hello World2"
+
+)
+```
+
+```ts
+
+function hello(name: string ) {
+ console.log(name);
+};
+
+hello( "alice");
+```
+
+```typescript
+function foo(): number {
+ return 2;
+}
+``` \ No newline at end of file
diff --git a/cli/tests/badly_formatted_fixed.md b/cli/tests/badly_formatted_fixed.md
new file mode 100644
index 000000000..359a8aead
--- /dev/null
+++ b/cli/tests/badly_formatted_fixed.md
@@ -0,0 +1,23 @@
+# Hello Markdown
+
+```js
+console.log("Hello World");
+```
+
+```javascript
+console.log("Hello World2");
+```
+
+```ts
+function hello(name: string) {
+ console.log(name);
+}
+
+hello("alice");
+```
+
+```typescript
+function foo(): number {
+ return 2;
+}
+```
diff --git a/cli/tests/fmt/expected_fmt_check_formatted_files.out b/cli/tests/fmt/expected_fmt_check_formatted_files.out
index 158c556c2..7c1e471b9 100644
--- a/cli/tests/fmt/expected_fmt_check_formatted_files.out
+++ b/cli/tests/fmt/expected_fmt_check_formatted_files.out
@@ -1 +1 @@
-Checked 2 files
+Checked 3 files
diff --git a/cli/tests/fmt/expected_fmt_check_ignore.out b/cli/tests/fmt/expected_fmt_check_ignore.out
index c05ac45a1..158c556c2 100644
--- a/cli/tests/fmt/expected_fmt_check_ignore.out
+++ b/cli/tests/fmt/expected_fmt_check_ignore.out
@@ -1 +1 @@
-Checked 1 file
+Checked 2 files
diff --git a/cli/tests/fmt/expected_fmt_check_tests_dir.out b/cli/tests/fmt/expected_fmt_check_tests_dir.out
index 00d7cb3fa..e2dc2b4ae 100644
--- a/cli/tests/fmt/expected_fmt_check_tests_dir.out
+++ b/cli/tests/fmt/expected_fmt_check_tests_dir.out
@@ -1,2 +1,2 @@
[WILDCARD]
-error: Found 5 not formatted files in [WILDCARD] files
+error: Found 6 not formatted files in [WILDCARD] files
diff --git a/cli/tests/fmt/formatted3.md b/cli/tests/fmt/formatted3.md
new file mode 100644
index 000000000..e6e616584
--- /dev/null
+++ b/cli/tests/fmt/formatted3.md
@@ -0,0 +1,17 @@
+# Hello
+
+```js
+function foo() {
+ return 42;
+}
+
+foo();
+```
+
+```ts
+function bar(): number {
+ return 42;
+}
+
+bar();
+```
diff --git a/cli/tests/integration_tests.rs b/cli/tests/integration_tests.rs
index 0af4709fb..80a7222e5 100644
--- a/cli/tests/integration_tests.rs
+++ b/cli/tests/integration_tests.rs
@@ -511,20 +511,31 @@ fn cache_invalidation_test_no_check() {
#[test]
fn fmt_test() {
let t = TempDir::new().expect("tempdir fail");
- let fixed = util::root_path().join("cli/tests/badly_formatted_fixed.js");
- let badly_formatted_original =
+ let fixed_js = util::root_path().join("cli/tests/badly_formatted_fixed.js");
+ let fixed_md = util::root_path().join("cli/tests/badly_formatted_fixed.md");
+ let badly_formatted_original_js =
util::root_path().join("cli/tests/badly_formatted.mjs");
- let badly_formatted = t.path().join("badly_formatted.js");
- let badly_formatted_str = badly_formatted.to_str().unwrap();
- std::fs::copy(&badly_formatted_original, &badly_formatted)
+ let badly_formatted_original_md =
+ util::root_path().join("cli/tests/badly_formatted.md");
+ let badly_formatted_js = t.path().join("badly_formatted.js");
+ let badly_formatted_md = t.path().join("badly_formatted.md");
+ let badly_formatted_js_str = badly_formatted_js.to_str().unwrap();
+ let badly_formatted_md_str = badly_formatted_md.to_str().unwrap();
+ std::fs::copy(&badly_formatted_original_js, &badly_formatted_js)
+ .expect("Failed to copy file");
+ std::fs::copy(&badly_formatted_original_md, &badly_formatted_md)
.expect("Failed to copy file");
// First, check formatting by ignoring the badly formatted file.
let status = util::deno_cmd()
.current_dir(util::root_path())
.arg("fmt")
- .arg(format!("--ignore={}", badly_formatted_str))
+ .arg(format!(
+ "--ignore={},{}",
+ badly_formatted_js_str, badly_formatted_md_str
+ ))
.arg("--check")
- .arg(badly_formatted_str)
+ .arg(badly_formatted_js_str)
+ .arg(badly_formatted_md_str)
.spawn()
.expect("Failed to spawn script")
.wait()
@@ -535,7 +546,8 @@ fn fmt_test() {
.current_dir(util::root_path())
.arg("fmt")
.arg("--check")
- .arg(badly_formatted_str)
+ .arg(badly_formatted_js_str)
+ .arg(badly_formatted_md_str)
.spawn()
.expect("Failed to spawn script")
.wait()
@@ -545,15 +557,19 @@ fn fmt_test() {
let status = util::deno_cmd()
.current_dir(util::root_path())
.arg("fmt")
- .arg(badly_formatted_str)
+ .arg(badly_formatted_js_str)
+ .arg(badly_formatted_md_str)
.spawn()
.expect("Failed to spawn script")
.wait()
.expect("Failed to wait for child process");
assert!(status.success());
- let expected = std::fs::read_to_string(fixed).unwrap();
- let actual = std::fs::read_to_string(badly_formatted).unwrap();
- assert_eq!(expected, actual);
+ let expected_js = std::fs::read_to_string(fixed_js).unwrap();
+ let expected_md = std::fs::read_to_string(fixed_md).unwrap();
+ let actual_js = std::fs::read_to_string(badly_formatted_js).unwrap();
+ let actual_md = std::fs::read_to_string(badly_formatted_md).unwrap();
+ assert_eq!(expected_js, actual_js);
+ assert_eq!(expected_md, actual_md);
}
// Helper function to skip watcher output that contains "Restarting"
@@ -2751,7 +2767,7 @@ itest!(fmt_quiet_check_fmt_dir {
});
itest!(fmt_check_formatted_files {
- args: "fmt --check fmt/formatted1.js fmt/formatted2.ts",
+ args: "fmt --check fmt/formatted1.js fmt/formatted2.ts fmt/formatted3.md",
output: "fmt/expected_fmt_check_formatted_files.out",
exit_code: 0,
});
@@ -2768,6 +2784,12 @@ itest!(fmt_stdin {
output_str: Some("const a = 1;\n"),
});
+itest!(fmt_stdin_markdown {
+ args: "fmt --ext=md -",
+ input: Some("# Hello Markdown\n```ts\nconsole.log( \"text\")\n```\n"),
+ output_str: Some("# Hello Markdown\n\n```ts\nconsole.log(\"text\");\n```\n"),
+});
+
itest!(fmt_stdin_check_formatted {
args: "fmt --check -",
input: Some("const a = 1;\n"),
diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs
index d5500a8f2..61875d172 100644
--- a/cli/tools/fmt.rs
+++ b/cli/tools/fmt.rs
@@ -10,13 +10,12 @@
use crate::colors;
use crate::diff::diff;
use crate::file_watcher;
-use crate::fs_util::{collect_files, is_supported_ext};
+use crate::fs_util::{collect_files, get_extension, is_supported_ext_md};
use crate::text_encoding;
use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::futures;
use deno_core::futures::FutureExt;
-use dprint_plugin_typescript as dprint;
use std::fs;
use std::io::stdin;
use std::io::stdout;
@@ -38,11 +37,10 @@ pub async fn format(
) -> Result<(), AnyError> {
let target_file_resolver = || {
// collect the files that are to be formatted
- collect_files(&args, &ignore, is_supported_ext)
+ collect_files(&args, &ignore, is_supported_ext_md)
};
-
let operation = |paths: Vec<PathBuf>| {
- let config = get_config();
+ let config = get_typescript_config();
async move {
if check {
check_source_files(config, paths).await?;
@@ -63,8 +61,48 @@ pub async fn format(
Ok(())
}
+/// Formats markdown (using https://github.com/dprint/dprint-plugin-markdown) and its code blocks
+/// (ts/tsx, js/jsx).
+fn format_markdown(
+ file_text: &str,
+ ts_config: dprint_plugin_typescript::configuration::Configuration,
+) -> Result<String, String> {
+ let md_config = get_markdown_config();
+ dprint_plugin_markdown::format_text(
+ &file_text,
+ &md_config,
+ Box::new(move |tag, text, line_width| {
+ let tag = tag.to_lowercase();
+ if matches!(
+ tag.as_str(),
+ "ts" | "tsx" | "js" | "jsx" | "javascript" | "typescript"
+ ) {
+ // It's important to tell dprint proper file extension, otherwise
+ // it might parse the file twice.
+ let extension = match tag.as_str() {
+ "javascript" => "js",
+ "typescript" => "ts",
+ rest => rest,
+ };
+ let fake_filename =
+ PathBuf::from(format!("deno_fmt_stdin.{}", extension));
+
+ let mut codeblock_config = ts_config.clone();
+ codeblock_config.line_width = line_width;
+ dprint_plugin_typescript::format_text(
+ &fake_filename,
+ &text,
+ &codeblock_config,
+ )
+ } else {
+ Ok(text.to_string())
+ }
+ }),
+ )
+}
+
async fn check_source_files(
- config: dprint::configuration::Configuration,
+ config: dprint_plugin_typescript::configuration::Configuration,
paths: Vec<PathBuf>,
) -> Result<(), AnyError> {
let not_formatted_files_count = Arc::new(AtomicUsize::new(0));
@@ -79,7 +117,12 @@ async fn check_source_files(
move |file_path| {
checked_files_count.fetch_add(1, Ordering::Relaxed);
let file_text = read_file_contents(&file_path)?.text;
- let r = dprint::format_text(&file_path, &file_text, &config);
+ let ext = get_extension(&file_path).unwrap_or_else(String::new);
+ let r = if ext == "md" {
+ format_markdown(&file_text, config.clone())
+ } else {
+ dprint_plugin_typescript::format_text(&file_path, &file_text, &config)
+ };
match r {
Ok(formatted_text) => {
if formatted_text != file_text {
@@ -120,7 +163,7 @@ async fn check_source_files(
}
async fn format_source_files(
- config: dprint::configuration::Configuration,
+ config: dprint_plugin_typescript::configuration::Configuration,
paths: Vec<PathBuf>,
) -> Result<(), AnyError> {
let formatted_files_count = Arc::new(AtomicUsize::new(0));
@@ -133,7 +176,16 @@ async fn format_source_files(
move |file_path| {
checked_files_count.fetch_add(1, Ordering::Relaxed);
let file_contents = read_file_contents(&file_path)?;
- let r = dprint::format_text(&file_path, &file_contents.text, &config);
+ let ext = get_extension(&file_path).unwrap_or_else(String::new);
+ let r = if ext == "md" {
+ format_markdown(&file_contents.text, config.clone())
+ } else {
+ dprint_plugin_typescript::format_text(
+ &file_path,
+ &file_contents.text,
+ &config,
+ )
+ };
match r {
Ok(formatted_text) => {
if formatted_text != file_contents.text {
@@ -178,17 +230,25 @@ async fn format_source_files(
}
/// Format stdin and write result to stdout.
-/// Treats input as TypeScript.
+/// Treats input as TypeScript or as set by `--ext` flag.
/// Compatible with `--check` flag.
-pub fn format_stdin(check: bool) -> Result<(), AnyError> {
+pub fn format_stdin(check: bool, ext: String) -> Result<(), AnyError> {
let mut source = String::new();
if stdin().read_to_string(&mut source).is_err() {
return Err(generic_error("Failed to read from stdin"));
}
- let config = get_config();
-
- // dprint will fallback to jsx parsing if parsing this as a .ts file doesn't work
- match dprint::format_text(&PathBuf::from("_stdin.ts"), &source, &config) {
+ let config = get_typescript_config();
+ let r = if ext.as_str() == "md" {
+ format_markdown(&source, config)
+ } else {
+ // dprint will fallback to jsx parsing if parsing this as a .ts file doesn't work
+ dprint_plugin_typescript::format_text(
+ &PathBuf::from("_stdin.ts"),
+ &source,
+ &config,
+ )
+ };
+ match r {
Ok(formatted_text) => {
if check {
if formatted_text != source {
@@ -213,9 +273,22 @@ fn files_str(len: usize) -> &'static str {
}
}
-fn get_config() -> dprint::configuration::Configuration {
- use dprint::configuration::*;
- ConfigurationBuilder::new().deno().build()
+fn get_typescript_config(
+) -> dprint_plugin_typescript::configuration::Configuration {
+ dprint_plugin_typescript::configuration::ConfigurationBuilder::new()
+ .deno()
+ .build()
+}
+
+fn get_markdown_config() -> dprint_plugin_markdown::configuration::Configuration
+{
+ dprint_plugin_markdown::configuration::ConfigurationBuilder::new()
+ // Matches `.dprintrc.json` in the repository
+ .text_wrap(dprint_plugin_markdown::configuration::TextWrap::Always)
+ .ignore_directive("deno-fmt-ignore")
+ .ignore_start_directive("deno-fmt-ignore-start")
+ .ignore_end_directive("deno-fmt-ignore-end")
+ .build()
}
struct FileContents {