feat(task): Task description in the form of comments (#23101)

Closes #22786.

TLDR;
```jsonc
{
  "tasks": {
    // Some comment
    //
    // describing what the task does
    "dev": "deno run -A --watch main.ts"
  }
}
```
```bash
deno task
```
![Screenshot 2024-03-27 at 1 43
49 PM](https://github.com/denoland/deno/assets/17734409/7a14da8c-8e63-45ba-9bfb-590d250b56a9)
This commit is contained in:
Nathan Whitaker 2024-03-27 14:14:27 -07:00 committed by GitHub
parent 68fecc6de4
commit 2dc37f411e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 121 additions and 18 deletions

4
Cargo.lock generated
View file

@ -1236,9 +1236,9 @@ dependencies = [
[[package]] [[package]]
name = "deno_config" name = "deno_config"
version = "0.14.1" version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c801e30b12aa3f15f59d4d4947621eef34d6798a93f6a5037c0efa26f87a8b" checksum = "c29ec5738e6c94de0c71279e09e052cc8827d826165162510949b60caf873339"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"glob", "glob",

View file

@ -64,7 +64,7 @@ winres.workspace = true
[dependencies] [dependencies]
deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] } deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] }
deno_cache_dir = { workspace = true } deno_cache_dir = { workspace = true }
deno_config = "=0.14.1" deno_config = "=0.15.0"
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
deno_doc = { version = "=0.113.1", features = ["html"] } deno_doc = { version = "=0.113.1", features = ["html"] }
deno_emit = "=0.38.2" deno_emit = "=0.38.2"

View file

@ -796,11 +796,18 @@ impl CliOptions {
} else { } else {
None None
}; };
let parse_options = deno_config::ParseOptions {
include_task_comments: matches!(
flags.subcommand,
DenoSubcommand::Task(..)
),
};
let maybe_config_file = ConfigFile::discover( let maybe_config_file = ConfigFile::discover(
&flags.config_flag, &flags.config_flag,
flags.config_path_args(&initial_cwd), flags.config_path_args(&initial_cwd),
&initial_cwd, &initial_cwd,
additional_config_file_names, additional_config_file_names,
&parse_options,
)?; )?;
let mut maybe_package_json = None; let mut maybe_package_json = None;
@ -1183,7 +1190,7 @@ impl CliOptions {
pub fn resolve_tasks_config( pub fn resolve_tasks_config(
&self, &self,
) -> Result<IndexMap<String, String>, AnyError> { ) -> Result<IndexMap<String, deno_config::Task>, AnyError> {
if let Some(config_file) = &self.maybe_config_file { if let Some(config_file) = &self.maybe_config_file {
config_file.resolve_tasks_config() config_file.resolve_tasks_config()
} else if self.maybe_package_json.is_some() { } else if self.maybe_package_json.is_some() {
@ -1850,7 +1857,12 @@ mod test {
let cwd = &std::env::current_dir().unwrap(); let cwd = &std::env::current_dir().unwrap();
let config_specifier = let config_specifier =
ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap(); ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap();
let config_file = ConfigFile::new(config_text, config_specifier).unwrap(); let config_file = ConfigFile::new(
config_text,
config_specifier,
&deno_config::ParseOptions::default(),
)
.unwrap();
let actual = resolve_import_map_specifier( let actual = resolve_import_map_specifier(
Some("import-map.json"), Some("import-map.json"),
Some(&config_file), Some(&config_file),
@ -1869,7 +1881,12 @@ mod test {
let config_text = r#"{}"#; let config_text = r#"{}"#;
let config_specifier = let config_specifier =
ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap(); ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap();
let config_file = ConfigFile::new(config_text, config_specifier).unwrap(); let config_file = ConfigFile::new(
config_text,
config_specifier,
&deno_config::ParseOptions::default(),
)
.unwrap();
let actual = resolve_import_map_specifier( let actual = resolve_import_map_specifier(
None, None,
Some(&config_file), Some(&config_file),

View file

@ -1136,7 +1136,10 @@ impl ConfigData {
file_fetcher: Option<&FileFetcher>, file_fetcher: Option<&FileFetcher>,
) -> Self { ) -> Self {
if let Some(specifier) = config_file_specifier { if let Some(specifier) = config_file_specifier {
match ConfigFile::from_specifier(specifier.clone()) { match ConfigFile::from_specifier(
specifier.clone(),
&deno_config::ParseOptions::default(),
) {
Ok(config_file) => { Ok(config_file) => {
lsp_log!( lsp_log!(
" Resolved Deno configuration file: \"{}\"", " Resolved Deno configuration file: \"{}\"",
@ -1949,7 +1952,12 @@ mod tests {
config config
.tree .tree
.inject_config_file( .inject_config_file(
ConfigFile::new("{}", root_uri.join("deno.json").unwrap()).unwrap(), ConfigFile::new(
"{}",
root_uri.join("deno.json").unwrap(),
&deno_config::ParseOptions::default(),
)
.unwrap(),
) )
.await; .await;
assert!(config.specifier_enabled(&root_uri)); assert!(config.specifier_enabled(&root_uri));
@ -1996,6 +2004,7 @@ mod tests {
}) })
.to_string(), .to_string(),
root_uri.join("deno.json").unwrap(), root_uri.join("deno.json").unwrap(),
&deno_config::ParseOptions::default(),
) )
.unwrap(), .unwrap(),
) )
@ -2021,6 +2030,7 @@ mod tests {
}) })
.to_string(), .to_string(),
root_uri.join("deno.json").unwrap(), root_uri.join("deno.json").unwrap(),
&deno_config::ParseOptions::default(),
) )
.unwrap(), .unwrap(),
) )
@ -2038,6 +2048,7 @@ mod tests {
}) })
.to_string(), .to_string(),
root_uri.join("deno.json").unwrap(), root_uri.join("deno.json").unwrap(),
&deno_config::ParseOptions::default(),
) )
.unwrap(), .unwrap(),
) )
@ -2067,6 +2078,7 @@ mod tests {
}) })
.to_string(), .to_string(),
root_uri.join("deno.json").unwrap(), root_uri.join("deno.json").unwrap(),
&deno_config::ParseOptions::default(),
) )
.unwrap(), .unwrap(),
) )

View file

@ -1617,7 +1617,12 @@ mod tests {
let config = Config::new_with_roots([resolve_url("file:///").unwrap()]); let config = Config::new_with_roots([resolve_url("file:///").unwrap()]);
if let Some((base_url, json_string)) = maybe_import_map { if let Some((base_url, json_string)) = maybe_import_map {
let base_url = resolve_url(base_url).unwrap(); let base_url = resolve_url(base_url).unwrap();
let config_file = ConfigFile::new(json_string, base_url).unwrap(); let config_file = ConfigFile::new(
json_string,
base_url,
&deno_config::ParseOptions::default(),
)
.unwrap();
config.tree.inject_config_file(config_file).await; config.tree.inject_config_file(config_file).await;
} }
StateSnapshot { StateSnapshot {

View file

@ -1886,6 +1886,7 @@ console.log(b, "hello deno");
}) })
.to_string(), .to_string(),
config.root_uri().unwrap().join("deno.json").unwrap(), config.root_uri().unwrap().join("deno.json").unwrap(),
&deno_config::ParseOptions::default(),
) )
.unwrap(), .unwrap(),
) )
@ -1926,6 +1927,7 @@ console.log(b, "hello deno");
}) })
.to_string(), .to_string(),
config.root_uri().unwrap().join("deno.json").unwrap(), config.root_uri().unwrap().join("deno.json").unwrap(),
&deno_config::ParseOptions::default(),
) )
.unwrap(), .unwrap(),
) )

View file

@ -4711,6 +4711,7 @@ mod tests {
}) })
.to_string(), .to_string(),
resolve_url("file:///deno.json").unwrap(), resolve_url("file:///deno.json").unwrap(),
&deno_config::ParseOptions::default(),
) )
.unwrap(), .unwrap(),
) )

View file

@ -49,7 +49,13 @@ pub async fn execute_script(
} }
}; };
if let Some(script) = tasks_config.get(task_name) { if let Some(
deno_config::Task::Definition(script)
| deno_config::Task::Commented {
definition: script, ..
},
) = tasks_config.get(task_name)
{
let config_file_url = cli_options.maybe_config_file_specifier().unwrap(); let config_file_url = cli_options.maybe_config_file_specifier().unwrap();
let config_file_path = if config_file_url.scheme() == "file" { let config_file_path = if config_file_url.scheme() == "file" {
config_file_url.to_file_path().unwrap() config_file_url.to_file_path().unwrap()
@ -222,18 +228,22 @@ fn collect_env_vars() -> HashMap<String, String> {
fn print_available_tasks( fn print_available_tasks(
// order can be important, so these use an index map // order can be important, so these use an index map
tasks_config: &IndexMap<String, String>, tasks_config: &IndexMap<String, deno_config::Task>,
package_json_scripts: &IndexMap<String, String>, package_json_scripts: &IndexMap<String, String>,
) { ) {
eprintln!("{}", colors::green("Available tasks:")); eprintln!("{}", colors::green("Available tasks:"));
let mut had_task = false; let mut had_task = false;
for (is_deno, (key, value)) in tasks_config.iter().map(|e| (true, e)).chain( for (is_deno, (key, task)) in tasks_config
package_json_scripts .iter()
.iter() .map(|(k, t)| (true, (k, t.clone())))
.filter(|(key, _)| !tasks_config.contains_key(*key)) .chain(
.map(|e| (false, e)), package_json_scripts
) { .iter()
.filter(|(key, _)| !tasks_config.contains_key(*key))
.map(|(k, v)| (false, (k, deno_config::Task::Definition(v.clone())))),
)
{
eprintln!( eprintln!(
"- {}{}", "- {}{}",
colors::cyan(key), colors::cyan(key),
@ -243,7 +253,17 @@ fn print_available_tasks(
format!(" {}", colors::italic_gray("(package.json)")) format!(" {}", colors::italic_gray("(package.json)"))
} }
); );
eprintln!(" {value}"); let definition = match &task {
deno_config::Task::Definition(definition) => definition,
deno_config::Task::Commented { definition, .. } => definition,
};
if let deno_config::Task::Commented { comments, .. } = &task {
let slash_slash = colors::italic_gray("//");
for comment in comments {
eprintln!(" {slash_slash} {}", colors::italic_gray(comment));
}
}
eprintln!(" {definition}");
had_task = true; had_task = true;
} }
if !had_task { if !had_task {

View file

@ -0,0 +1,6 @@
{
"args": "task doesntexist",
"envs": { "NO_COLOR": "1" },
"output": "task.out",
"exitCode": 1
}

View file

@ -0,0 +1,7 @@
{
"tasks": {
// some docs
// on what this does
"lint": "deno lint"
}
}

View file

@ -0,0 +1,6 @@
Task not found: doesntexist
Available tasks:
- lint
// some docs
// on what this does
deno lint

View file

@ -0,0 +1,6 @@
{
"args": "task",
"envs": { "NO_COLOR": "1" },
"output": "task.out",
"exitCode": 1
}

View file

@ -0,0 +1,12 @@
{
"tasks": {
// this task has documentation
//
// in the form of comments
"lint": "deno lint",
/*
* block comments are fine too
*/
"fmt": "deno fmt"
}
}

View file

@ -0,0 +1,9 @@
Available tasks:
- lint
// this task has documentation
//
// in the form of comments
deno lint
- fmt
// block comments are fine too
deno fmt