mirror of
https://github.com/denoland/deno.git
synced 2024-11-27 16:10:57 -05:00
feat(task): add --eval flag (#26943)
This commit adds `--eval` flag to `deno task` subcommand. This flag allows to evaluate provided "task name" as a task itself, effectively allowing to use `deno_task_shell` from the command line. Also fixes shebang parsing for `node_modules/.bin/` entries to handle `#!/usr/bin/node -S node` in addition to `#!/usr/bin/node node`. Closes https://github.com/denoland/deno/issues/26918
This commit is contained in:
parent
dabb6775f3
commit
318dd3cbc3
10 changed files with 160 additions and 17 deletions
|
@ -380,6 +380,7 @@ pub struct TaskFlags {
|
||||||
pub cwd: Option<String>,
|
pub cwd: Option<String>,
|
||||||
pub task: Option<String>,
|
pub task: Option<String>,
|
||||||
pub is_run: bool,
|
pub is_run: bool,
|
||||||
|
pub eval: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||||
|
@ -1386,7 +1387,7 @@ pub fn flags_from_vec(args: Vec<OsString>) -> clap::error::Result<Flags> {
|
||||||
"repl" => repl_parse(&mut flags, &mut m)?,
|
"repl" => repl_parse(&mut flags, &mut m)?,
|
||||||
"run" => run_parse(&mut flags, &mut m, app, false)?,
|
"run" => run_parse(&mut flags, &mut m, app, false)?,
|
||||||
"serve" => serve_parse(&mut flags, &mut m, app)?,
|
"serve" => serve_parse(&mut flags, &mut m, app)?,
|
||||||
"task" => task_parse(&mut flags, &mut m),
|
"task" => task_parse(&mut flags, &mut m, app)?,
|
||||||
"test" => test_parse(&mut flags, &mut m)?,
|
"test" => test_parse(&mut flags, &mut m)?,
|
||||||
"types" => types_parse(&mut flags, &mut m),
|
"types" => types_parse(&mut flags, &mut m),
|
||||||
"uninstall" => uninstall_parse(&mut flags, &mut m),
|
"uninstall" => uninstall_parse(&mut flags, &mut m),
|
||||||
|
@ -2931,7 +2932,10 @@ fn task_subcommand() -> Command {
|
||||||
<p(245)>deno task build</>
|
<p(245)>deno task build</>
|
||||||
|
|
||||||
List all available tasks:
|
List all available tasks:
|
||||||
<p(245)>deno task</>"
|
<p(245)>deno task</>
|
||||||
|
|
||||||
|
Evaluate a task from string
|
||||||
|
<p(245)>deno task --eval \"echo $(pwd)\"</>"
|
||||||
),
|
),
|
||||||
UnstableArgsConfig::ResolutionAndRuntime,
|
UnstableArgsConfig::ResolutionAndRuntime,
|
||||||
)
|
)
|
||||||
|
@ -2947,6 +2951,13 @@ List all available tasks:
|
||||||
.help("Specify the directory to run the task in")
|
.help("Specify the directory to run the task in")
|
||||||
.value_hint(ValueHint::DirPath),
|
.value_hint(ValueHint::DirPath),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new("eval")
|
||||||
|
.long("eval")
|
||||||
|
.help(
|
||||||
|
"Evaluate the passed value as if, it was a task in a configuration file",
|
||||||
|
).action(ArgAction::SetTrue)
|
||||||
|
)
|
||||||
.arg(node_modules_dir_arg())
|
.arg(node_modules_dir_arg())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -5066,7 +5077,11 @@ fn serve_parse(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn task_parse(flags: &mut Flags, matches: &mut ArgMatches) {
|
fn task_parse(
|
||||||
|
flags: &mut Flags,
|
||||||
|
matches: &mut ArgMatches,
|
||||||
|
mut app: Command,
|
||||||
|
) -> clap::error::Result<()> {
|
||||||
flags.config_flag = matches
|
flags.config_flag = matches
|
||||||
.remove_one::<String>("config")
|
.remove_one::<String>("config")
|
||||||
.map(ConfigFlag::Path)
|
.map(ConfigFlag::Path)
|
||||||
|
@ -5079,6 +5094,7 @@ fn task_parse(flags: &mut Flags, matches: &mut ArgMatches) {
|
||||||
cwd: matches.remove_one::<String>("cwd"),
|
cwd: matches.remove_one::<String>("cwd"),
|
||||||
task: None,
|
task: None,
|
||||||
is_run: false,
|
is_run: false,
|
||||||
|
eval: matches.get_flag("eval"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some((task, mut matches)) = matches.remove_subcommand() {
|
if let Some((task, mut matches)) = matches.remove_subcommand() {
|
||||||
|
@ -5091,9 +5107,15 @@ fn task_parse(flags: &mut Flags, matches: &mut ArgMatches) {
|
||||||
.flatten()
|
.flatten()
|
||||||
.filter_map(|arg| arg.into_string().ok()),
|
.filter_map(|arg| arg.into_string().ok()),
|
||||||
);
|
);
|
||||||
|
} else if task_flags.eval {
|
||||||
|
return Err(app.find_subcommand_mut("task").unwrap().error(
|
||||||
|
clap::error::ErrorKind::MissingRequiredArgument,
|
||||||
|
"[TASK] must be specified when using --eval",
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
flags.subcommand = DenoSubcommand::Task(task_flags);
|
flags.subcommand = DenoSubcommand::Task(task_flags);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parallel_arg_parse(matches: &mut ArgMatches) -> Option<NonZeroUsize> {
|
fn parallel_arg_parse(matches: &mut ArgMatches) -> Option<NonZeroUsize> {
|
||||||
|
@ -10274,6 +10296,7 @@ mod tests {
|
||||||
cwd: None,
|
cwd: None,
|
||||||
task: Some("build".to_string()),
|
task: Some("build".to_string()),
|
||||||
is_run: false,
|
is_run: false,
|
||||||
|
eval: false,
|
||||||
}),
|
}),
|
||||||
argv: svec!["hello", "world"],
|
argv: svec!["hello", "world"],
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
@ -10288,6 +10311,7 @@ mod tests {
|
||||||
cwd: None,
|
cwd: None,
|
||||||
task: Some("build".to_string()),
|
task: Some("build".to_string()),
|
||||||
is_run: false,
|
is_run: false,
|
||||||
|
eval: false,
|
||||||
}),
|
}),
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
}
|
}
|
||||||
|
@ -10301,10 +10325,28 @@ mod tests {
|
||||||
cwd: Some("foo".to_string()),
|
cwd: Some("foo".to_string()),
|
||||||
task: Some("build".to_string()),
|
task: Some("build".to_string()),
|
||||||
is_run: false,
|
is_run: false,
|
||||||
|
eval: false,
|
||||||
}),
|
}),
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let r = flags_from_vec(svec!["deno", "task", "--eval", "echo 1"]);
|
||||||
|
assert_eq!(
|
||||||
|
r.unwrap(),
|
||||||
|
Flags {
|
||||||
|
subcommand: DenoSubcommand::Task(TaskFlags {
|
||||||
|
cwd: None,
|
||||||
|
task: Some("echo 1".to_string()),
|
||||||
|
is_run: false,
|
||||||
|
eval: true,
|
||||||
|
}),
|
||||||
|
..Flags::default()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let r = flags_from_vec(svec!["deno", "task", "--eval"]);
|
||||||
|
assert!(r.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -10326,6 +10368,7 @@ mod tests {
|
||||||
cwd: None,
|
cwd: None,
|
||||||
task: Some("build".to_string()),
|
task: Some("build".to_string()),
|
||||||
is_run: false,
|
is_run: false,
|
||||||
|
eval: false,
|
||||||
}),
|
}),
|
||||||
argv: svec!["--", "hello", "world"],
|
argv: svec!["--", "hello", "world"],
|
||||||
config_flag: ConfigFlag::Path("deno.json".to_owned()),
|
config_flag: ConfigFlag::Path("deno.json".to_owned()),
|
||||||
|
@ -10343,6 +10386,7 @@ mod tests {
|
||||||
cwd: Some("foo".to_string()),
|
cwd: Some("foo".to_string()),
|
||||||
task: Some("build".to_string()),
|
task: Some("build".to_string()),
|
||||||
is_run: false,
|
is_run: false,
|
||||||
|
eval: false,
|
||||||
}),
|
}),
|
||||||
argv: svec!["--", "hello", "world"],
|
argv: svec!["--", "hello", "world"],
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
@ -10361,6 +10405,7 @@ mod tests {
|
||||||
cwd: None,
|
cwd: None,
|
||||||
task: Some("build".to_string()),
|
task: Some("build".to_string()),
|
||||||
is_run: false,
|
is_run: false,
|
||||||
|
eval: false,
|
||||||
}),
|
}),
|
||||||
argv: svec!["--"],
|
argv: svec!["--"],
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
@ -10378,6 +10423,7 @@ mod tests {
|
||||||
cwd: None,
|
cwd: None,
|
||||||
task: Some("build".to_string()),
|
task: Some("build".to_string()),
|
||||||
is_run: false,
|
is_run: false,
|
||||||
|
eval: false,
|
||||||
}),
|
}),
|
||||||
argv: svec!["-1", "--test"],
|
argv: svec!["-1", "--test"],
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
@ -10395,6 +10441,7 @@ mod tests {
|
||||||
cwd: None,
|
cwd: None,
|
||||||
task: Some("build".to_string()),
|
task: Some("build".to_string()),
|
||||||
is_run: false,
|
is_run: false,
|
||||||
|
eval: false,
|
||||||
}),
|
}),
|
||||||
argv: svec!["--test"],
|
argv: svec!["--test"],
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
@ -10413,6 +10460,7 @@ mod tests {
|
||||||
cwd: None,
|
cwd: None,
|
||||||
task: Some("build".to_string()),
|
task: Some("build".to_string()),
|
||||||
is_run: false,
|
is_run: false,
|
||||||
|
eval: false,
|
||||||
}),
|
}),
|
||||||
log_level: Some(log::Level::Error),
|
log_level: Some(log::Level::Error),
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
@ -10430,6 +10478,7 @@ mod tests {
|
||||||
cwd: None,
|
cwd: None,
|
||||||
task: None,
|
task: None,
|
||||||
is_run: false,
|
is_run: false,
|
||||||
|
eval: false,
|
||||||
}),
|
}),
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
}
|
}
|
||||||
|
@ -10446,6 +10495,7 @@ mod tests {
|
||||||
cwd: None,
|
cwd: None,
|
||||||
task: None,
|
task: None,
|
||||||
is_run: false,
|
is_run: false,
|
||||||
|
eval: false,
|
||||||
}),
|
}),
|
||||||
config_flag: ConfigFlag::Path("deno.jsonc".to_string()),
|
config_flag: ConfigFlag::Path("deno.jsonc".to_string()),
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
@ -10463,6 +10513,7 @@ mod tests {
|
||||||
cwd: None,
|
cwd: None,
|
||||||
task: None,
|
task: None,
|
||||||
is_run: false,
|
is_run: false,
|
||||||
|
eval: false,
|
||||||
}),
|
}),
|
||||||
config_flag: ConfigFlag::Path("deno.jsonc".to_string()),
|
config_flag: ConfigFlag::Path("deno.jsonc".to_string()),
|
||||||
..Flags::default()
|
..Flags::default()
|
||||||
|
|
|
@ -238,6 +238,7 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
|
||||||
cwd: None,
|
cwd: None,
|
||||||
task: Some(run_flags.script.clone()),
|
task: Some(run_flags.script.clone()),
|
||||||
is_run: true,
|
is_run: true,
|
||||||
|
eval: false,
|
||||||
};
|
};
|
||||||
new_flags.subcommand = DenoSubcommand::Task(task_flags.clone());
|
new_flags.subcommand = DenoSubcommand::Task(task_flags.clone());
|
||||||
let result = tools::task::execute_script(Arc::new(new_flags), task_flags.clone()).await;
|
let result = tools::task::execute_script(Arc::new(new_flags), task_flags.clone()).await;
|
||||||
|
|
|
@ -483,10 +483,23 @@ fn resolve_execution_path_from_npx_shim(
|
||||||
static SCRIPT_PATH_RE: Lazy<Regex> =
|
static SCRIPT_PATH_RE: Lazy<Regex> =
|
||||||
lazy_regex::lazy_regex!(r#""\$basedir\/([^"]+)" "\$@""#);
|
lazy_regex::lazy_regex!(r#""\$basedir\/([^"]+)" "\$@""#);
|
||||||
|
|
||||||
if text.starts_with("#!/usr/bin/env node") {
|
let maybe_first_line = {
|
||||||
|
let index = text.find("\n")?;
|
||||||
|
Some(&text[0..index])
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(first_line) = maybe_first_line {
|
||||||
|
// NOTE(bartlomieju): this is not perfect, but handle two most common scenarios
|
||||||
|
// where Node is run without any args. If there are args then we use `NodeCommand`
|
||||||
|
// struct.
|
||||||
|
if first_line == "#!/usr/bin/env node"
|
||||||
|
|| first_line == "#!/usr/bin/env -S node"
|
||||||
|
{
|
||||||
// launch this file itself because it's a JS file
|
// launch this file itself because it's a JS file
|
||||||
Some(file_path)
|
return Some(file_path);
|
||||||
} else {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Search for...
|
// Search for...
|
||||||
// > "$basedir/../next/dist/bin/next" "$@"
|
// > "$basedir/../next/dist/bin/next" "$@"
|
||||||
// ...which is what it will look like on Windows
|
// ...which is what it will look like on Windows
|
||||||
|
@ -496,7 +509,6 @@ fn resolve_execution_path_from_npx_shim(
|
||||||
.map(|relative_path| {
|
.map(|relative_path| {
|
||||||
file_path.parent().unwrap().join(relative_path.as_str())
|
file_path.parent().unwrap().join(relative_path.as_str())
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_managed_npm_commands(
|
fn resolve_managed_npm_commands(
|
||||||
|
@ -564,6 +576,16 @@ mod test {
|
||||||
let unix_shim = r#"#!/usr/bin/env node
|
let unix_shim = r#"#!/usr/bin/env node
|
||||||
"use strict";
|
"use strict";
|
||||||
console.log('Hi!');
|
console.log('Hi!');
|
||||||
|
"#;
|
||||||
|
let path = PathBuf::from("/node_modules/.bin/example");
|
||||||
|
assert_eq!(
|
||||||
|
resolve_execution_path_from_npx_shim(path.clone(), unix_shim).unwrap(),
|
||||||
|
path
|
||||||
|
);
|
||||||
|
// example shim on unix
|
||||||
|
let unix_shim = r#"#!/usr/bin/env -S node
|
||||||
|
"use strict";
|
||||||
|
console.log('Hi!');
|
||||||
"#;
|
"#;
|
||||||
let path = PathBuf::from("/node_modules/.bin/example");
|
let path = PathBuf::from("/node_modules/.bin/example");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub async fn execute_script(
|
||||||
let factory = CliFactory::from_flags(flags);
|
let factory = CliFactory::from_flags(flags);
|
||||||
let cli_options = factory.cli_options()?;
|
let cli_options = factory.cli_options()?;
|
||||||
let start_dir = &cli_options.start_dir;
|
let start_dir = &cli_options.start_dir;
|
||||||
if !start_dir.has_deno_or_pkg_json() {
|
if !start_dir.has_deno_or_pkg_json() && !task_flags.eval {
|
||||||
bail!("deno task couldn't find deno.json(c). See https://docs.deno.com/go/config")
|
bail!("deno task couldn't find deno.json(c). See https://docs.deno.com/go/config")
|
||||||
}
|
}
|
||||||
let force_use_pkg_json =
|
let force_use_pkg_json =
|
||||||
|
@ -90,6 +90,19 @@ pub async fn execute_script(
|
||||||
concurrency: no_of_concurrent_tasks.into(),
|
concurrency: no_of_concurrent_tasks.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if task_flags.eval {
|
||||||
|
return task_runner
|
||||||
|
.run_deno_task(
|
||||||
|
&Url::from_directory_path(cli_options.initial_cwd()).unwrap(),
|
||||||
|
&"".to_string(),
|
||||||
|
&TaskDefinition {
|
||||||
|
command: task_flags.task.as_ref().unwrap().to_string(),
|
||||||
|
dependencies: vec![],
|
||||||
|
description: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
task_runner.run_task(task_name).await
|
task_runner.run_task(task_name).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
35
tests/specs/task/eval/__test__.jsonc
Normal file
35
tests/specs/task/eval/__test__.jsonc
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
"tests": {
|
||||||
|
"no_arg": {
|
||||||
|
"args": "task --eval",
|
||||||
|
"output": "no_arg.out",
|
||||||
|
"exitCode": 1
|
||||||
|
},
|
||||||
|
"echo_pwd": {
|
||||||
|
"args": ["task", "--eval", "echo $(pwd)"],
|
||||||
|
"output": "echo_pwd.out"
|
||||||
|
},
|
||||||
|
"piped": {
|
||||||
|
"args": [
|
||||||
|
"task",
|
||||||
|
"--eval",
|
||||||
|
"echo 12345 | (deno eval 'const b = new Uint8Array(1);Deno.stdin.readSync(b);console.log(b)' && deno eval 'const b = new Uint8Array(1);Deno.stdin.readSync(b);console.log(b)')"
|
||||||
|
],
|
||||||
|
"output": "piped.out"
|
||||||
|
},
|
||||||
|
"node_modules_bin": {
|
||||||
|
"tempDir": true,
|
||||||
|
"steps": [{
|
||||||
|
"args": "install",
|
||||||
|
"output": "[WILDCARD]Initialize @denotest/bin[WILDCARD]"
|
||||||
|
}, {
|
||||||
|
"args": [
|
||||||
|
"task",
|
||||||
|
"--eval",
|
||||||
|
"cli-esm hi hello"
|
||||||
|
],
|
||||||
|
"output": "bin.out"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
tests/specs/task/eval/bin.out
Normal file
3
tests/specs/task/eval/bin.out
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Task cli-esm hi hello
|
||||||
|
hi
|
||||||
|
hello
|
2
tests/specs/task/eval/echo_pwd.out
Normal file
2
tests/specs/task/eval/echo_pwd.out
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Task echo $(pwd)
|
||||||
|
[WILDCARD]
|
4
tests/specs/task/eval/no_arg.out
Normal file
4
tests/specs/task/eval/no_arg.out
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
error: [TASK] must be specified when using --eval
|
||||||
|
|
||||||
|
Usage: deno task [OPTIONS] [TASK]
|
||||||
|
|
9
tests/specs/task/eval/package.json
Normal file
9
tests/specs/task/eval/package.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"name": "bin_package",
|
||||||
|
"devDependencies": {
|
||||||
|
"@denotest/bin": "1.0.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"sayhi": "cli-esm hi hello"
|
||||||
|
}
|
||||||
|
}
|
3
tests/specs/task/eval/piped.out
Normal file
3
tests/specs/task/eval/piped.out
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Task echo 12345 | (deno eval 'const b = new Uint8Array(1);Deno.stdin.readSync(b);console.log(b)' && deno eval 'const b = new Uint8Array(1);Deno.stdin.readSync(b);console.log(b)')
|
||||||
|
Uint8Array(1) [ 49 ]
|
||||||
|
Uint8Array(1) [ 50 ]
|
Loading…
Reference in a new issue