Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(task): add --eval flag #26943

Merged
merged 9 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 54 additions & 3 deletions cli/args/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ pub struct TaskFlags {
pub cwd: Option<String>,
pub task: Option<String>,
pub is_run: bool,
pub eval: bool,
}

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
Expand Down Expand Up @@ -1386,7 +1387,7 @@ pub fn flags_from_vec(args: Vec<OsString>) -> clap::error::Result<Flags> {
"repl" => repl_parse(&mut flags, &mut m)?,
"run" => run_parse(&mut flags, &mut m, app, false)?,
"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)?,
"types" => types_parse(&mut flags, &mut m),
"uninstall" => uninstall_parse(&mut flags, &mut m),
Expand Down Expand Up @@ -2931,7 +2932,10 @@ fn task_subcommand() -> Command {
<p(245)>deno task build</>

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,
)
Expand All @@ -2947,6 +2951,13 @@ List all available tasks:
.help("Specify the directory to run the task in")
.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())
})
}
Expand Down Expand Up @@ -5066,7 +5077,11 @@ fn serve_parse(
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
.remove_one::<String>("config")
.map(ConfigFlag::Path)
Expand All @@ -5079,6 +5094,7 @@ fn task_parse(flags: &mut Flags, matches: &mut ArgMatches) {
cwd: matches.remove_one::<String>("cwd"),
task: None,
is_run: false,
eval: matches.get_flag("eval"),
};

if let Some((task, mut matches)) = matches.remove_subcommand() {
Expand All @@ -5091,9 +5107,15 @@ fn task_parse(flags: &mut Flags, matches: &mut ArgMatches) {
.flatten()
.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);
Ok(())
}

fn parallel_arg_parse(matches: &mut ArgMatches) -> Option<NonZeroUsize> {
Expand Down Expand Up @@ -10274,6 +10296,7 @@ mod tests {
cwd: None,
task: Some("build".to_string()),
is_run: false,
eval: false,
}),
argv: svec!["hello", "world"],
..Flags::default()
Expand All @@ -10288,6 +10311,7 @@ mod tests {
cwd: None,
task: Some("build".to_string()),
is_run: false,
eval: false,
}),
..Flags::default()
}
Expand All @@ -10301,10 +10325,28 @@ mod tests {
cwd: Some("foo".to_string()),
task: Some("build".to_string()),
is_run: false,
eval: false,
}),
..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]
Expand All @@ -10326,6 +10368,7 @@ mod tests {
cwd: None,
task: Some("build".to_string()),
is_run: false,
eval: false,
}),
argv: svec!["--", "hello", "world"],
config_flag: ConfigFlag::Path("deno.json".to_owned()),
Expand All @@ -10343,6 +10386,7 @@ mod tests {
cwd: Some("foo".to_string()),
task: Some("build".to_string()),
is_run: false,
eval: false,
}),
argv: svec!["--", "hello", "world"],
..Flags::default()
Expand All @@ -10361,6 +10405,7 @@ mod tests {
cwd: None,
task: Some("build".to_string()),
is_run: false,
eval: false,
}),
argv: svec!["--"],
..Flags::default()
Expand All @@ -10378,6 +10423,7 @@ mod tests {
cwd: None,
task: Some("build".to_string()),
is_run: false,
eval: false,
}),
argv: svec!["-1", "--test"],
..Flags::default()
Expand All @@ -10395,6 +10441,7 @@ mod tests {
cwd: None,
task: Some("build".to_string()),
is_run: false,
eval: false,
}),
argv: svec!["--test"],
..Flags::default()
Expand All @@ -10413,6 +10460,7 @@ mod tests {
cwd: None,
task: Some("build".to_string()),
is_run: false,
eval: false,
}),
log_level: Some(log::Level::Error),
..Flags::default()
Expand All @@ -10430,6 +10478,7 @@ mod tests {
cwd: None,
task: None,
is_run: false,
eval: false,
}),
..Flags::default()
}
Expand All @@ -10446,6 +10495,7 @@ mod tests {
cwd: None,
task: None,
is_run: false,
eval: false,
}),
config_flag: ConfigFlag::Path("deno.jsonc".to_string()),
..Flags::default()
Expand All @@ -10463,6 +10513,7 @@ mod tests {
cwd: None,
task: None,
is_run: false,
eval: false,
}),
config_flag: ConfigFlag::Path("deno.jsonc".to_string()),
..Flags::default()
Expand Down
1 change: 1 addition & 0 deletions cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
cwd: None,
task: Some(run_flags.script.clone()),
is_run: true,
eval: false,
};
new_flags.subcommand = DenoSubcommand::Task(task_flags.clone());
let result = tools::task::execute_script(Arc::new(new_flags), task_flags.clone()).await;
Expand Down
48 changes: 35 additions & 13 deletions cli/task_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -483,20 +483,32 @@ fn resolve_execution_path_from_npx_shim(
static SCRIPT_PATH_RE: Lazy<Regex> =
lazy_regex::lazy_regex!(r#""\$basedir\/([^"]+)" "\$@""#);

if text.starts_with("#!/usr/bin/env node") {
// launch this file itself because it's a JS file
Some(file_path)
} else {
// Search for...
// > "$basedir/../next/dist/bin/next" "$@"
// ...which is what it will look like on Windows
SCRIPT_PATH_RE
.captures(text)
.and_then(|c| c.get(1))
.map(|relative_path| {
file_path.parent().unwrap().join(relative_path.as_str())
})
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
return Some(file_path);
}
}

// Search for...
// > "$basedir/../next/dist/bin/next" "$@"
// ...which is what it will look like on Windows
SCRIPT_PATH_RE
.captures(text)
.and_then(|c| c.get(1))
.map(|relative_path| {
file_path.parent().unwrap().join(relative_path.as_str())
})
}

fn resolve_managed_npm_commands(
Expand Down Expand Up @@ -564,6 +576,16 @@ mod test {
let unix_shim = r#"#!/usr/bin/env node
"use strict";
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");
assert_eq!(
Expand Down
15 changes: 14 additions & 1 deletion cli/tools/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub async fn execute_script(
let factory = CliFactory::from_flags(flags);
let cli_options = factory.cli_options()?;
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")
}
let force_use_pkg_json =
Expand Down Expand Up @@ -90,6 +90,19 @@ pub async fn execute_script(
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
}

Expand Down
35 changes: 35 additions & 0 deletions tests/specs/task/eval/__test__.jsonc
Original file line number Diff line number Diff line change
@@ -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 changes: 3 additions & 0 deletions tests/specs/task/eval/bin.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Task cli-esm hi hello
hi
hello
2 changes: 2 additions & 0 deletions tests/specs/task/eval/echo_pwd.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Task echo $(pwd)
[WILDCARD]
4 changes: 4 additions & 0 deletions tests/specs/task/eval/no_arg.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
error: [TASK] must be specified when using --eval

Usage: deno task [OPTIONS] [TASK]

9 changes: 9 additions & 0 deletions tests/specs/task/eval/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "bin_package",
"devDependencies": {
"@denotest/bin": "1.0.0"
},
"scripts": {
"sayhi": "cli-esm hi hello"
}
}
3 changes: 3 additions & 0 deletions tests/specs/task/eval/piped.out
Original file line number Diff line number Diff line change
@@ -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 ]