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]]
name = "deno_config"
version = "0.14.1"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c801e30b12aa3f15f59d4d4947621eef34d6798a93f6a5037c0efa26f87a8b"
checksum = "c29ec5738e6c94de0c71279e09e052cc8827d826165162510949b60caf873339"
dependencies = [
"anyhow",
"glob",

View File

@ -64,7 +64,7 @@ winres.workspace = true
[dependencies]
deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] }
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_doc = { version = "=0.113.1", features = ["html"] }
deno_emit = "=0.38.2"

View File

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

View File

@ -1136,7 +1136,10 @@ impl ConfigData {
file_fetcher: Option<&FileFetcher>,
) -> Self {
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) => {
lsp_log!(
" Resolved Deno configuration file: \"{}\"",
@ -1949,7 +1952,12 @@ mod tests {
config
.tree
.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;
assert!(config.specifier_enabled(&root_uri));
@ -1996,6 +2004,7 @@ mod tests {
})
.to_string(),
root_uri.join("deno.json").unwrap(),
&deno_config::ParseOptions::default(),
)
.unwrap(),
)
@ -2021,6 +2030,7 @@ mod tests {
})
.to_string(),
root_uri.join("deno.json").unwrap(),
&deno_config::ParseOptions::default(),
)
.unwrap(),
)
@ -2038,6 +2048,7 @@ mod tests {
})
.to_string(),
root_uri.join("deno.json").unwrap(),
&deno_config::ParseOptions::default(),
)
.unwrap(),
)
@ -2067,6 +2078,7 @@ mod tests {
})
.to_string(),
root_uri.join("deno.json").unwrap(),
&deno_config::ParseOptions::default(),
)
.unwrap(),
)

View File

@ -1617,7 +1617,12 @@ mod tests {
let config = Config::new_with_roots([resolve_url("file:///").unwrap()]);
if let Some((base_url, json_string)) = maybe_import_map {
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;
}
StateSnapshot {

View File

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

View File

@ -4711,6 +4711,7 @@ mod tests {
})
.to_string(),
resolve_url("file:///deno.json").unwrap(),
&deno_config::ParseOptions::default(),
)
.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_path = if config_file_url.scheme() == "file" {
config_file_url.to_file_path().unwrap()
@ -222,18 +228,22 @@ fn collect_env_vars() -> HashMap<String, String> {
fn print_available_tasks(
// 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>,
) {
eprintln!("{}", colors::green("Available tasks:"));
let mut had_task = false;
for (is_deno, (key, value)) in tasks_config.iter().map(|e| (true, e)).chain(
package_json_scripts
.iter()
.filter(|(key, _)| !tasks_config.contains_key(*key))
.map(|e| (false, e)),
) {
for (is_deno, (key, task)) in tasks_config
.iter()
.map(|(k, t)| (true, (k, t.clone())))
.chain(
package_json_scripts
.iter()
.filter(|(key, _)| !tasks_config.contains_key(*key))
.map(|(k, v)| (false, (k, deno_config::Task::Definition(v.clone())))),
)
{
eprintln!(
"- {}{}",
colors::cyan(key),
@ -243,7 +253,17 @@ fn print_available_tasks(
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;
}
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