summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2023-02-22 22:45:35 -0500
committerGitHub <noreply@github.com>2023-02-22 22:45:35 -0500
commitb15f9e60a040e2e450e7ca9971a5fc07dbf8b94c (patch)
tree4290744b0c0a8f8f5d063322a650fdabf2d3150c /cli
parentcc8e4a00aaf4c4fe959944c7400f2e259f7faae8 (diff)
feat(task): support scripts in package.json (#17887)
This is a super basic initial implementation. We don't create a `node_modules/.bin` folder at the moment and add it to the PATH like we should which is necessary to make command name resolution in the subprocess work properly (ex. you run a script that launches another script that then tries to launch an "npx command"... this won't work atm). Closes #17492
Diffstat (limited to 'cli')
-rw-r--r--cli/Cargo.toml4
-rw-r--r--cli/args/config_file.rs39
-rw-r--r--cli/args/flags.rs54
-rw-r--r--cli/args/mod.rs31
-rw-r--r--cli/node/mod.rs30
-rw-r--r--cli/npm/resolvers/mod.rs16
-rw-r--r--cli/tests/integration/run_tests.rs8
-rw-r--r--cli/tests/integration/task_tests.rs159
-rw-r--r--cli/tests/testdata/npm/registry/@denotest/bin/0.5.0/cli.mjs5
-rw-r--r--cli/tests/testdata/npm/registry/@denotest/bin/0.5.0/package.json5
-rw-r--r--cli/tests/testdata/task/both/deno.json6
-rw-r--r--cli/tests/testdata/task/both/deno_selected.out4
-rw-r--r--cli/tests/testdata/task/both/echo.out0
-rw-r--r--cli/tests/testdata/task/both/no_args.out7
-rw-r--r--cli/tests/testdata/task/both/package.json9
-rw-r--r--cli/tests/testdata/task/both/package_json_selected.out7
-rw-r--r--cli/tests/testdata/task/both/prefers_deno.out4
-rw-r--r--cli/tests/testdata/task/deno_json/deno.json (renamed from cli/tests/testdata/task/deno.json)0
-rw-r--r--cli/tests/testdata/task/deno_json/task_additional_args.out (renamed from cli/tests/testdata/task/task_additional_args.out)0
-rw-r--r--cli/tests/testdata/task/deno_json/task_additional_args_nested_strings.out (renamed from cli/tests/testdata/task/task_additional_args_nested_strings.out)0
-rw-r--r--cli/tests/testdata/task/deno_json/task_additional_args_no_logic.out (renamed from cli/tests/testdata/task/task_additional_args_no_logic.out)0
-rw-r--r--cli/tests/testdata/task/deno_json/task_additional_args_no_shell_expansion.out (renamed from cli/tests/testdata/task/task_additional_args_no_shell_expansion.out)0
-rw-r--r--cli/tests/testdata/task/deno_json/task_boolean_logic.out (renamed from cli/tests/testdata/task/task_boolean_logic.out)0
-rw-r--r--cli/tests/testdata/task/deno_json/task_cwd.out (renamed from cli/tests/testdata/task/task_cwd.out)0
-rw-r--r--cli/tests/testdata/task/deno_json/task_deno_exe_no_env.out (renamed from cli/tests/testdata/task/task_deno_exe_no_env.out)0
-rw-r--r--cli/tests/testdata/task/deno_json/task_exit_code_5.out (renamed from cli/tests/testdata/task/task_exit_code_5.out)0
-rw-r--r--cli/tests/testdata/task/deno_json/task_init_cwd.out (renamed from cli/tests/testdata/task/task_init_cwd.out)0
-rw-r--r--cli/tests/testdata/task/deno_json/task_init_cwd_already_set.out (renamed from cli/tests/testdata/task/task_init_cwd_already_set.out)0
-rw-r--r--cli/tests/testdata/task/deno_json/task_no_args.out (renamed from cli/tests/testdata/task/task_no_args.out)20
-rw-r--r--cli/tests/testdata/task/deno_json/task_non_existent.out (renamed from cli/tests/testdata/task/task_non_existent.out)20
-rw-r--r--cli/tests/testdata/task/deno_json/task_piped_stdin.out (renamed from cli/tests/testdata/task/task_piped_stdin.out)0
-rw-r--r--cli/tests/testdata/task/npx/non_existent.out2
-rw-r--r--cli/tests/testdata/task/npx/on_own.out2
-rw-r--r--cli/tests/testdata/task/npx/package.json6
-rw-r--r--cli/tests/testdata/task/package_json/bin.out10
-rw-r--r--cli/tests/testdata/task/package_json/echo.out1
-rw-r--r--cli/tests/testdata/task/package_json/no_args.out5
-rw-r--r--cli/tests/testdata/task/package_json/package.json10
-rw-r--r--cli/tools/task.rs257
-rw-r--r--cli/worker.rs1
40 files changed, 537 insertions, 185 deletions
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 3903d32f1..6c7c8e128 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -50,7 +50,7 @@ deno_graph = "0.44.0"
deno_lint = { version = "0.40.0", features = ["docs"] }
deno_lockfile.workspace = true
deno_runtime = { workspace = true, features = ["dont_create_runtime_snapshot", "include_js_files_for_snapshotting"] }
-deno_task_shell = "0.8.1"
+deno_task_shell = "0.10.0"
napi_sym.workspace = true
async-trait.workspace = true
@@ -75,7 +75,7 @@ fancy-regex = "=0.10.0"
flate2.workspace = true
http.workspace = true
import_map = "=0.15.0"
-indexmap = "=1.9.2"
+indexmap.workspace = true
jsonc-parser = { version = "=0.21.0", features = ["serde"] }
libc.workspace = true
log = { workspace = true, features = ["serde"] }
diff --git a/cli/args/config_file.rs b/cli/args/config_file.rs
index 154692376..f7fd85579 100644
--- a/cli/args/config_file.rs
+++ b/cli/args/config_file.rs
@@ -18,6 +18,7 @@ use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::serde_json::Value;
use deno_core::ModuleSpecifier;
+use indexmap::IndexMap;
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::collections::HashMap;
@@ -760,9 +761,9 @@ impl ConfigFile {
pub fn to_tasks_config(
&self,
- ) -> Result<Option<BTreeMap<String, String>>, AnyError> {
+ ) -> Result<Option<IndexMap<String, String>>, AnyError> {
if let Some(config) = self.json.tasks.clone() {
- let tasks_config: BTreeMap<String, String> =
+ let tasks_config: IndexMap<String, String> =
serde_json::from_value(config)
.context("Failed to parse \"tasks\" configuration")?;
Ok(Some(tasks_config))
@@ -815,25 +816,22 @@ impl ConfigFile {
pub fn resolve_tasks_config(
&self,
- ) -> Result<BTreeMap<String, String>, AnyError> {
+ ) -> Result<IndexMap<String, String>, AnyError> {
let maybe_tasks_config = self.to_tasks_config()?;
- if let Some(tasks_config) = maybe_tasks_config {
- for key in tasks_config.keys() {
- if key.is_empty() {
- bail!("Configuration file task names cannot be empty");
- } else if !key
- .chars()
- .all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '-' | ':'))
- {
- bail!("Configuration file task names must only contain alpha-numeric characters, colons (:), underscores (_), or dashes (-). Task: {}", key);
- } else if !key.chars().next().unwrap().is_ascii_alphabetic() {
- bail!("Configuration file task names must start with an alphabetic character. Task: {}", key);
- }
+ let tasks_config = maybe_tasks_config.unwrap_or_default();
+ for key in tasks_config.keys() {
+ if key.is_empty() {
+ bail!("Configuration file task names cannot be empty");
+ } else if !key
+ .chars()
+ .all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '-' | ':'))
+ {
+ bail!("Configuration file task names must only contain alpha-numeric characters, colons (:), underscores (_), or dashes (-). Task: {}", key);
+ } else if !key.chars().next().unwrap().is_ascii_alphabetic() {
+ bail!("Configuration file task names must start with an alphabetic character. Task: {}", key);
}
- Ok(tasks_config)
- } else {
- bail!("No tasks found in configuration file")
}
+ Ok(tasks_config)
}
pub fn to_lock_config(&self) -> Result<Option<LockConfig>, AnyError> {
@@ -1238,11 +1236,6 @@ mod tests {
}
#[test]
- fn tasks_no_tasks() {
- run_task_error_test(r#"{}"#, "No tasks found in configuration file");
- }
-
- #[test]
fn task_name_invalid_chars() {
run_task_error_test(
r#"{
diff --git a/cli/args/flags.rs b/cli/args/flags.rs
index 2c9f4c09d..825bf96d0 100644
--- a/cli/args/flags.rs
+++ b/cli/args/flags.rs
@@ -193,7 +193,7 @@ impl RunFlags {
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TaskFlags {
pub cwd: Option<String>,
- pub task: String,
+ pub task: Option<String>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
@@ -508,26 +508,34 @@ impl Flags {
/// from the `path` dir.
/// If it returns None, the `package.json` file shouldn't be discovered at
/// all.
- pub fn package_json_arg(&self) -> Option<PathBuf> {
+ pub fn package_json_search_dir(&self) -> Option<PathBuf> {
use DenoSubcommand::*;
- if let Run(RunFlags { script }) = &self.subcommand {
- if let Ok(module_specifier) = deno_core::resolve_url_or_path(script) {
+ match &self.subcommand {
+ Run(RunFlags { script }) => {
+ let module_specifier = deno_core::resolve_url_or_path(script).ok()?;
if module_specifier.scheme() == "file" {
let p = module_specifier
.to_file_path()
.unwrap()
.parent()?
.to_owned();
- return Some(p);
+ Some(p)
} else if module_specifier.scheme() == "npm" {
- let p = std::env::current_dir().unwrap();
- return Some(p);
+ Some(std::env::current_dir().unwrap())
+ } else {
+ None
}
}
+ Task(TaskFlags { cwd: Some(cwd), .. }) => {
+ deno_core::resolve_url_or_path(cwd)
+ .ok()?
+ .to_file_path()
+ .ok()
+ }
+ Task(TaskFlags { cwd: None, .. }) => std::env::current_dir().ok(),
+ _ => None,
}
-
- None
}
pub fn has_permission(&self) -> bool {
@@ -2795,7 +2803,7 @@ fn task_parse(
let mut task_flags = TaskFlags {
cwd: None,
- task: String::new(),
+ task: None,
};
if let Some(cwd) = matches.value_of("cwd") {
@@ -2830,7 +2838,7 @@ fn task_parse(
}
if index < raw_args.len() {
- task_flags.task = raw_args[index].to_string();
+ task_flags.task = Some(raw_args[index].to_string());
index += 1;
if index < raw_args.len() {
@@ -6394,7 +6402,7 @@ mod tests {
Flags {
subcommand: DenoSubcommand::Task(TaskFlags {
cwd: None,
- task: "build".to_string(),
+ task: Some("build".to_string()),
}),
argv: svec!["hello", "world"],
..Flags::default()
@@ -6407,7 +6415,7 @@ mod tests {
Flags {
subcommand: DenoSubcommand::Task(TaskFlags {
cwd: None,
- task: "build".to_string(),
+ task: Some("build".to_string()),
}),
..Flags::default()
}
@@ -6419,7 +6427,7 @@ mod tests {
Flags {
subcommand: DenoSubcommand::Task(TaskFlags {
cwd: Some("foo".to_string()),
- task: "build".to_string(),
+ task: Some("build".to_string()),
}),
..Flags::default()
}
@@ -6443,7 +6451,7 @@ mod tests {
Flags {
subcommand: DenoSubcommand::Task(TaskFlags {
cwd: None,
- task: "build".to_string(),
+ task: Some("build".to_string()),
}),
argv: svec!["--", "hello", "world"],
config_flag: ConfigFlag::Path("deno.json".to_owned()),
@@ -6459,7 +6467,7 @@ mod tests {
Flags {
subcommand: DenoSubcommand::Task(TaskFlags {
cwd: Some("foo".to_string()),
- task: "build".to_string(),
+ task: Some("build".to_string()),
}),
argv: svec!["--", "hello", "world"],
..Flags::default()
@@ -6476,7 +6484,7 @@ mod tests {
Flags {
subcommand: DenoSubcommand::Task(TaskFlags {
cwd: None,
- task: "build".to_string(),
+ task: Some("build".to_string()),
}),
argv: svec!["--"],
..Flags::default()
@@ -6492,7 +6500,7 @@ mod tests {
Flags {
subcommand: DenoSubcommand::Task(TaskFlags {
cwd: None,
- task: "build".to_string(),
+ task: Some("build".to_string()),
}),
argv: svec!["-1", "--test"],
..Flags::default()
@@ -6508,7 +6516,7 @@ mod tests {
Flags {
subcommand: DenoSubcommand::Task(TaskFlags {
cwd: None,
- task: "build".to_string(),
+ task: Some("build".to_string()),
}),
argv: svec!["--test"],
..Flags::default()
@@ -6526,7 +6534,7 @@ mod tests {
Flags {
subcommand: DenoSubcommand::Task(TaskFlags {
cwd: None,
- task: "build".to_string(),
+ task: Some("build".to_string()),
}),
unstable: true,
log_level: Some(log::Level::Error),
@@ -6543,7 +6551,7 @@ mod tests {
Flags {
subcommand: DenoSubcommand::Task(TaskFlags {
cwd: None,
- task: "".to_string(),
+ task: None,
}),
..Flags::default()
}
@@ -6558,7 +6566,7 @@ mod tests {
Flags {
subcommand: DenoSubcommand::Task(TaskFlags {
cwd: None,
- task: "".to_string(),
+ task: None,
}),
config_flag: ConfigFlag::Path("deno.jsonc".to_string()),
..Flags::default()
@@ -6574,7 +6582,7 @@ mod tests {
Flags {
subcommand: DenoSubcommand::Task(TaskFlags {
cwd: None,
- task: "".to_string(),
+ task: None,
}),
config_flag: ConfigFlag::Path("deno.jsonc".to_string()),
..Flags::default()
diff --git a/cli/args/mod.rs b/cli/args/mod.rs
index 3d8a29fe7..aa1781bd8 100644
--- a/cli/args/mod.rs
+++ b/cli/args/mod.rs
@@ -9,9 +9,9 @@ pub mod package_json;
pub use self::import_map::resolve_import_map_from_specifier;
use ::import_map::ImportMap;
+use indexmap::IndexMap;
use crate::npm::NpmResolutionSnapshot;
-use crate::util::fs::canonicalize_path;
pub use config_file::BenchConfig;
pub use config_file::CompilerOptions;
pub use config_file::ConfigFile;
@@ -49,7 +49,6 @@ use deno_runtime::deno_tls::webpki_roots;
use deno_runtime::inspector_server::InspectorServer;
use deno_runtime::permissions::PermissionsOptions;
use once_cell::sync::Lazy;
-use std::collections::BTreeMap;
use std::collections::HashMap;
use std::env;
use std::io::BufReader;
@@ -398,6 +397,7 @@ fn discover_package_json(
) -> Result<Option<PackageJson>, AnyError> {
const PACKAGE_JSON_NAME: &str = "package.json";
+ // note: ancestors() includes the `start` path
for ancestor in start.ancestors() {
let path = ancestor.join(PACKAGE_JSON_NAME);
@@ -430,17 +430,10 @@ fn discover_package_json(
// TODO(bartlomieju): discover for all subcommands, but print warnings that
// `package.json` is ignored in bundle/compile/etc.
- if let crate::args::DenoSubcommand::Task(TaskFlags {
- cwd: Some(path), ..
- }) = &flags.subcommand
- {
- // attempt to resolve the config file from the task subcommand's
- // `--cwd` when specified
- let task_cwd = canonicalize_path(&PathBuf::from(path))?;
- return discover_from(&task_cwd, None);
- } else if let Some(package_json_arg) = flags.package_json_arg() {
- let package_json_arg = canonicalize_path(&package_json_arg)?;
- return discover_from(&package_json_arg, maybe_stop_at);
+ if let Some(package_json_dir) = flags.package_json_search_dir() {
+ let package_json_dir =
+ canonicalize_path_maybe_not_exists(&package_json_dir)?;
+ return discover_from(&package_json_dir, maybe_stop_at);
}
log::debug!("No package.json file found");
@@ -802,9 +795,11 @@ impl CliOptions {
pub fn resolve_tasks_config(
&self,
- ) -> Result<BTreeMap<String, String>, AnyError> {
+ ) -> Result<IndexMap<String, String>, AnyError> {
if let Some(config_file) = &self.maybe_config_file {
config_file.resolve_tasks_config()
+ } else if self.maybe_package_json.is_some() {
+ Ok(Default::default())
} else {
bail!("No config file found")
}
@@ -841,7 +836,13 @@ impl CliOptions {
pub fn maybe_package_json_deps(
&self,
) -> Result<Option<HashMap<String, NpmPackageReq>>, AnyError> {
- if let Some(package_json) = self.maybe_package_json() {
+ if matches!(
+ self.flags.subcommand,
+ DenoSubcommand::Task(TaskFlags { task: None, .. })
+ ) {
+ // don't have any package json dependencies for deno task with no args
+ Ok(None)
+ } else if let Some(package_json) = self.maybe_package_json() {
package_json::get_local_package_json_version_reqs(package_json).map(Some)
} else {
Ok(None)
diff --git a/cli/node/mod.rs b/cli/node/mod.rs
index a6f40c99e..470003057 100644
--- a/cli/node/mod.rs
+++ b/cli/node/mod.rs
@@ -285,17 +285,41 @@ pub fn node_resolve_npm_reference(
Ok(Some(resolve_response))
}
+pub fn node_resolve_binary_commands(
+ pkg_nv: &NpmPackageNv,
+ npm_resolver: &NpmPackageResolver,
+) -> Result<Vec<String>, AnyError> {
+ let package_folder =
+ npm_resolver.resolve_package_folder_from_deno_module(pkg_nv)?;
+ let package_json_path = package_folder.join("package.json");
+ let package_json = PackageJson::load(
+ npm_resolver,
+ &mut PermissionsContainer::allow_all(),
+ package_json_path,
+ )?;
+
+ Ok(match package_json.bin {
+ Some(Value::String(_)) => vec![pkg_nv.name.to_string()],
+ Some(Value::Object(o)) => {
+ o.into_iter().map(|(key, _)| key).collect::<Vec<_>>()
+ }
+ _ => Vec::new(),
+ })
+}
+
pub fn node_resolve_binary_export(
pkg_nv: &NpmPackageNv,
bin_name: Option<&str>,
npm_resolver: &NpmPackageResolver,
- permissions: &mut dyn NodePermissions,
) -> Result<NodeResolution, AnyError> {
let package_folder =
npm_resolver.resolve_package_folder_from_deno_module(pkg_nv)?;
let package_json_path = package_folder.join("package.json");
- let package_json =
- PackageJson::load(npm_resolver, permissions, package_json_path)?;
+ let package_json = PackageJson::load(
+ npm_resolver,
+ &mut PermissionsContainer::allow_all(),
+ package_json_path,
+ )?;
let bin = match &package_json.bin {
Some(bin) => bin,
None => bail!(
diff --git a/cli/npm/resolvers/mod.rs b/cli/npm/resolvers/mod.rs
index 2450638bf..039864c5f 100644
--- a/cli/npm/resolvers/mod.rs
+++ b/cli/npm/resolvers/mod.rs
@@ -150,25 +150,23 @@ impl NpmPackageResolver {
/// Resolves an npm package folder path from a Deno module.
pub fn resolve_package_folder_from_deno_module(
&self,
- package_id: &NpmPackageNv,
+ pkg_nv: &NpmPackageNv,
) -> Result<PathBuf, AnyError> {
- let node_id = self
- .resolution
- .resolve_pkg_id_from_deno_module(package_id)?;
- self.resolve_pkg_folder_from_deno_module_at_node_id(&node_id)
+ let pkg_id = self.resolution.resolve_pkg_id_from_deno_module(pkg_nv)?;
+ self.resolve_pkg_folder_from_deno_module_at_pkg_id(&pkg_id)
}
- fn resolve_pkg_folder_from_deno_module_at_node_id(
+ fn resolve_pkg_folder_from_deno_module_at_pkg_id(
&self,
- package_id: &NpmPackageId,
+ pkg_id: &NpmPackageId,
) -> Result<PathBuf, AnyError> {
let path = self
.fs_resolver
- .resolve_package_folder_from_deno_module(package_id)?;
+ .resolve_package_folder_from_deno_module(pkg_id)?;
let path = canonicalize_path_maybe_not_exists(&path)?;
log::debug!(
"Resolved package folder of {} to {}",
- package_id.as_serialized(),
+ pkg_id.as_serialized(),
path.display()
);
Ok(path)
diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs
index 7dbf9c74c..d89142c21 100644
--- a/cli/tests/integration/run_tests.rs
+++ b/cli/tests/integration/run_tests.rs
@@ -2752,7 +2752,7 @@ itest!(config_not_auto_discovered_for_remote_script {
itest!(package_json_auto_discovered_for_local_script_log {
args: "run -L debug -A no_deno_json/main.ts",
output: "run/with_package_json/no_deno_json/main.out",
- maybe_cwd: Some("run/with_package_json/"),
+ cwd: Some("run/with_package_json/"),
// prevent creating a node_modules dir in the code directory
copy_temp_dir: Some("run/with_package_json/"),
envs: env_vars_for_npm_tests_no_sync_download(),
@@ -2765,7 +2765,7 @@ itest!(
package_json_auto_discovered_for_local_script_log_with_stop {
args: "run -L debug with_stop/some/nested/dir/main.ts",
output: "run/with_package_json/with_stop/main.out",
- maybe_cwd: Some("run/with_package_json/"),
+ cwd: Some("run/with_package_json/"),
copy_temp_dir: Some("run/with_package_json/"),
envs: env_vars_for_npm_tests_no_sync_download(),
http_server: true,
@@ -2777,7 +2777,7 @@ itest!(
package_json_auto_discovered_node_modules_relative_package_json {
args: "run -A main.js",
output: "run/with_package_json/no_deno_json/sub_dir/main.out",
- maybe_cwd: Some("run/with_package_json/no_deno_json/sub_dir"),
+ cwd: Some("run/with_package_json/no_deno_json/sub_dir"),
copy_temp_dir: Some("run/with_package_json/"),
envs: env_vars_for_npm_tests_no_sync_download(),
http_server: true,
@@ -2787,7 +2787,7 @@ itest!(
itest!(package_json_auto_discovered_for_npm_binary {
args: "run -L debug -A npm:@denotest/bin/cli-esm this is a test",
output: "run/with_package_json/npm_binary/main.out",
- maybe_cwd: Some("run/with_package_json/npm_binary/"),
+ cwd: Some("run/with_package_json/npm_binary/"),
copy_temp_dir: Some("run/with_package_json/"),
envs: env_vars_for_npm_tests_no_sync_download(),
http_server: true,
diff --git a/cli/tests/integration/task_tests.rs b/cli/tests/integration/task_tests.rs
index 47c3b6b6b..3dce90a0c 100644
--- a/cli/tests/integration/task_tests.rs
+++ b/cli/tests/integration/task_tests.rs
@@ -3,30 +3,32 @@
// Most of the tests for this are in deno_task_shell.
// These tests are intended to only test integration.
+use test_util::env_vars_for_npm_tests;
+
itest!(task_no_args {
- args: "task -q --config task/deno.json",
- output: "task/task_no_args.out",
+ args: "task -q --config task/deno_json/deno.json",
+ output: "task/deno_json/task_no_args.out",
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
exit_code: 1,
});
itest!(task_cwd {
- args: "task -q --config task/deno.json --cwd .. echo_cwd",
- output: "task/task_cwd.out",
+ args: "task -q --config task/deno_json/deno.json --cwd .. echo_cwd",
+ output: "task/deno_json/task_cwd.out",
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
exit_code: 0,
});
itest!(task_init_cwd {
- args: "task -q --config task/deno.json --cwd .. echo_init_cwd",
- output: "task/task_init_cwd.out",
+ args: "task -q --config task/deno_json/deno.json --cwd .. echo_init_cwd",
+ output: "task/deno_json/task_init_cwd.out",
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
exit_code: 0,
});
itest!(task_init_cwd_already_set {
- args: "task -q --config task/deno.json echo_init_cwd",
- output: "task/task_init_cwd_already_set.out",
+ args: "task -q --config task/deno_json/deno.json echo_init_cwd",
+ output: "task/deno_json/task_init_cwd_already_set.out",
envs: vec![
("NO_COLOR".to_string(), "1".to_string()),
("INIT_CWD".to_string(), "HELLO".to_string())
@@ -35,15 +37,15 @@ itest!(task_init_cwd_already_set {
});
itest!(task_cwd_resolves_config_from_specified_dir {
- args: "task -q --cwd task",
- output: "task/task_no_args.out",
+ args: "task -q --cwd task/deno_json",
+ output: "task/deno_json/task_no_args.out",
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
exit_code: 1,
});
itest!(task_non_existent {
- args: "task --config task/deno.json non_existent",
- output: "task/task_non_existent.out",
+ args: "task --config task/deno_json/deno.json non_existent",
+ output: "task/deno_json/task_non_existent.out",
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
exit_code: 1,
});
@@ -51,27 +53,27 @@ itest!(task_non_existent {
#[test]
fn task_emoji() {
// this bug only appears when using a pty/tty
- let args = "task --config task/deno.json echo_emoji";
+ let args = "task --config task/deno_json/deno.json echo_emoji";
use test_util::PtyData::*;
test_util::test_pty2(args, vec![Output("Task echo_emoji echo šŸ”„\r\nšŸ”„")]);
}
itest!(task_boolean_logic {
- args: "task -q --config task/deno.json boolean_logic",
- output: "task/task_boolean_logic.out",
+ args: "task -q --config task/deno_json/deno.json boolean_logic",
+ output: "task/deno_json/task_boolean_logic.out",
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
});
itest!(task_exit_code_5 {
- args: "task --config task/deno.json exit_code_5",
- output: "task/task_exit_code_5.out",
+ args: "task --config task/deno_json/deno.json exit_code_5",
+ output: "task/deno_json/task_exit_code_5.out",
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
exit_code: 5,
});
itest!(task_additional_args {
- args: "task -q --config task/deno.json echo 2",
- output: "task/task_additional_args.out",
+ args: "task -q --config task/deno_json/deno.json echo 2",
+ output: "task/deno_json/task_additional_args.out",
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
});
@@ -80,11 +82,11 @@ itest!(task_additional_args_no_shell_expansion {
"task",
"-q",
"--config",
- "task/deno.json",
+ "task/deno_json/deno.json",
"echo",
"$(echo 5)"
],
- output: "task/task_additional_args_no_shell_expansion.out",
+ output: "task/deno_json/task_additional_args_no_shell_expansion.out",
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
});
@@ -93,11 +95,11 @@ itest!(task_additional_args_nested_strings {
"task",
"-q",
"--config",
- "task/deno.json",
+ "task/deno_json/deno.json",
"echo",
"string \"quoted string\""
],
- output: "task/task_additional_args_nested_strings.out",
+ output: "task/deno_json/task_additional_args_nested_strings.out",
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
});
@@ -106,25 +108,124 @@ itest!(task_additional_args_no_logic {
"task",
"-q",
"--config",
- "task/deno.json",
+ "task/deno_json/deno.json",
"echo",
"||",
"echo",
"5"
],
- output: "task/task_additional_args_no_logic.out",
+ output: "task/deno_json/task_additional_args_no_logic.out",
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
});
itest!(task_deno_exe_no_env {
- args_vec: vec!["task", "-q", "--config", "task/deno.json", "deno_echo"],
- output: "task/task_deno_exe_no_env.out",
+ args_vec: vec![
+ "task",
+ "-q",
+ "--config",
+ "task/deno_json/deno.json",
+ "deno_echo"
+ ],
+ output: "task/deno_json/task_deno_exe_no_env.out",
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
env_clear: true,
});
itest!(task_piped_stdin {
- args_vec: vec!["task", "-q", "--config", "task/deno.json", "piped"],
- output: "task/task_piped_stdin.out",
+ args_vec: vec![
+ "task",
+ "-q",
+ "--config",
+ "task/deno_json/deno.json",
+ "piped"
+ ],
+ output: "task/deno_json/task_piped_stdin.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+});
+
+itest!(task_package_json_no_arg {
+ args: "task",
+ cwd: Some("task/package_json/"),
+ output: "task/package_json/no_args.out",
+ envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+ exit_code: 1,
+});
+
+itest!(task_package_json_echo {
+ args: "task --quiet echo",
+ cwd: Some("task/package_json/"),
+ output: "task/package_json/echo.out",
+ // use a temp dir because the node_modules folder will be created
+ copy_temp_dir: Some("task/package_json/"),
+ envs: env_vars_for_npm_tests(),
+ exit_code: 0,
+ http_server: true,
+});
+
+itest!(task_package_json_npm_bin {
+ args: "task bin extra",
+ cwd: Some("task/package_json/"),
+ output: "task/package_json/bin.out",
+ copy_temp_dir: Some("task/package_json/"),
+ envs: env_vars_for_npm_tests(),
+ exit_code: 0,
+ http_server: true,
+});
+
+itest!(task_both_no_arg {
+ args: "task",
+ cwd: Some("task/both/"),
+ output: "task/both/no_args.out",
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
+ exit_code: 1,
+});
+
+itest!(task_both_deno_json_selected {
+ args: "task other",
+ cwd: Some("task/both/"),
+ output: "task/both/deno_selected.out",
+ copy_temp_dir: Some("task/both/"),
+ envs: env_vars_for_npm_tests(),
+ exit_code: 0,
+ http_server: true,
+});
+
+itest!(task_both_package_json_selected {
+ args: "task bin asdf",
+ cwd: Some("task/both/"),
+ output: "task/both/package_json_selected.out",
+ copy_temp_dir: Some("task/both/"),
+ envs: env_vars_for_npm_tests(),
+ exit_code: 0,
+ http_server: true,
+});
+
+itest!(task_both_prefers_deno {
+ args: "task output some text",
+ cwd: Some("task/both/"),
+ output: "task/both/prefers_deno.out",
+ copy_temp_dir: Some("task/both/"),
+ envs: env_vars_for_npm_tests(),
+ exit_code: 0,
+ http_server: true,
+});
+
+itest!(task_npx_non_existent {
+ args: "task non-existent",
+ cwd: Some("task/npx/"),
+ output: "task/npx/non_existent.out",
+ copy_temp_dir: Some("task/npx/"),
+ envs: env_vars_for_npm_tests(),
+ exit_code: 1,
+ http_server: true,
+});
+
+itest!(task_npx_on_own {
+ args: "task on-own",
+ cwd: Some("task/npx/"),
+ output: "task/npx/on_own.out",
+ copy_temp_dir: Some("task/npx/"),
+ envs: env_vars_for_npm_tests(),
+ exit_code: 1,
+ http_server: true,
});
diff --git a/cli/tests/testdata/npm/registry/@denotest/bin/0.5.0/cli.mjs b/cli/tests/testdata/npm/registry/@denotest/bin/0.5.0/cli.mjs
new file mode 100644
index 000000000..0ae8e9190
--- /dev/null
+++ b/cli/tests/testdata/npm/registry/@denotest/bin/0.5.0/cli.mjs
@@ -0,0 +1,5 @@
+import process from "node:process";
+
+for (const arg of process.argv.slice(2)) {
+ console.log(arg);
+}
diff --git a/cli/tests/testdata/npm/registry/@denotest/bin/0.5.0/package.json b/cli/tests/testdata/npm/registry/@denotest/bin/0.5.0/package.json
new file mode 100644
index 000000000..caa2ef538
--- /dev/null
+++ b/cli/tests/testdata/npm/registry/@denotest/bin/0.5.0/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "@deno/bin",
+ "version": "0.5.0",
+ "bin": "./cli.mjs"
+}
diff --git a/cli/tests/testdata/task/both/deno.json b/cli/tests/testdata/task/both/deno.json
new file mode 100644
index 000000000..1038609a4
--- /dev/null
+++ b/cli/tests/testdata/task/both/deno.json
@@ -0,0 +1,6 @@
+{
+ "tasks": {
+ "output": "deno eval 'console.log(1)'",
+ "other": "deno eval 'console.log(2)'"
+ }
+}
diff --git a/cli/tests/testdata/task/both/deno_selected.out b/cli/tests/testdata/task/both/deno_selected.out
new file mode 100644
index 000000000..f55a74f5b
--- /dev/null
+++ b/cli/tests/testdata/task/both/deno_selected.out
@@ -0,0 +1,4 @@
+Download http://localhost:4545/npm/registry/@denotest/bin
+Download http://localhost:4545/npm/registry/@denotest/bin/1.0.0.tgz
+Task other deno eval 'console.log(2)'
+2
diff --git a/cli/tests/testdata/task/both/echo.out b/cli/tests/testdata/task/both/echo.out
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/cli/tests/testdata/task/both/echo.out
diff --git a/cli/tests/testdata/task/both/no_args.out b/cli/tests/testdata/task/both/no_args.out
new file mode 100644
index 000000000..fce690b70
--- /dev/null
+++ b/cli/tests/testdata/task/both/no_args.out
@@ -0,0 +1,7 @@
+Available tasks:
+- output
+ deno eval 'console.log(1)'
+- other
+ deno eval 'console.log(2)'
+- bin (package.json)
+ cli-esm testing this out
diff --git a/cli/tests/testdata/task/both/package.json b/cli/tests/testdata/task/both/package.json
new file mode 100644
index 000000000..708ccc6b1
--- /dev/null
+++ b/cli/tests/testdata/task/both/package.json
@@ -0,0 +1,9 @@
+{
+ "scripts": {
+ "bin": "cli-esm testing this out",
+ "output": "echo should never be called or shown"
+ },
+ "dependencies": {
+ "other": "npm:@denotest/bin@1.0"
+ }
+}
diff --git a/cli/tests/testdata/task/both/package_json_selected.out b/cli/tests/testdata/task/both/package_json_selected.out
new file mode 100644
index 000000000..76b3a9227
--- /dev/null
+++ b/cli/tests/testdata/task/both/package_json_selected.out
@@ -0,0 +1,7 @@
+Download http://localhost:4545/npm/registry/@denotest/bin
+Download http://localhost:4545/npm/registry/@denotest/bin/1.0.0.tgz
+Task bin cli-esm testing this out "asdf"
+testing
+this
+out
+asdf
diff --git a/cli/tests/testdata/task/both/prefers_deno.out b/cli/tests/testdata/task/both/prefers_deno.out
new file mode 100644
index 000000000..cd6798e6d
--- /dev/null
+++ b/cli/tests/testdata/task/both/prefers_deno.out
@@ -0,0 +1,4 @@
+Download http://localhost:4545/npm/registry/@denotest/bin
+Download http://localhost:4545/npm/registry/@denotest/bin/1.0.0.tgz
+Task output deno eval 'console.log(1)' "some" "text"
+1
diff --git a/cli/tests/testdata/task/deno.json b/cli/tests/testdata/task/deno_json/deno.json
index c57426d25..c57426d25 100644
--- a/cli/tests/testdata/task/deno.json
+++ b/cli/tests/testdata/task/deno_json/deno.json
diff --git a/cli/tests/testdata/task/task_additional_args.out b/cli/tests/testdata/task/deno_json/task_additional_args.out
index 8d04f961a..8d04f961a 100644
--- a/cli/tests/testdata/task/task_additional_args.out
+++ b/cli/tests/testdata/task/deno_json/task_additional_args.out
diff --git a/cli/tests/testdata/task/task_additional_args_nested_strings.out b/cli/tests/testdata/task/deno_json/task_additional_args_nested_strings.out
index 0e5f35c7f..0e5f35c7f 100644
--- a/cli/tests/testdata/task/task_additional_args_nested_strings.out
+++ b/cli/tests/testdata/task/deno_json/task_additional_args_nested_strings.out
diff --git a/cli/tests/testdata/task/task_additional_args_no_logic.out b/cli/tests/testdata/task/deno_json/task_additional_args_no_logic.out
index a4886a60d..a4886a60d 100644
--- a/cli/tests/testdata/task/task_additional_args_no_logic.out
+++ b/cli/tests/testdata/task/deno_json/task_additional_args_no_logic.out
diff --git a/cli/tests/testdata/task/task_additional_args_no_shell_expansion.out b/cli/tests/testdata/task/deno_json/task_additional_args_no_shell_expansion.out
index 826a3aaf1..826a3aaf1 100644
--- a/cli/tests/testdata/task/task_additional_args_no_shell_expansion.out
+++ b/cli/tests/testdata/task/deno_json/task_additional_args_no_shell_expansion.out
diff --git a/cli/tests/testdata/task/task_boolean_logic.out b/cli/tests/testdata/task/deno_json/task_boolean_logic.out
index 94ebaf900..94ebaf900 100644
--- a/cli/tests/testdata/task/task_boolean_logic.out
+++ b/cli/tests/testdata/task/deno_json/task_boolean_logic.out
diff --git a/cli/tests/testdata/task/task_cwd.out b/cli/tests/testdata/task/deno_json/task_cwd.out
index bfe3e7b11..bfe3e7b11 100644
--- a/cli/tests/testdata/task/task_cwd.out
+++ b/cli/tests/testdata/task/deno_json/task_cwd.out
diff --git a/cli/tests/testdata/task/task_deno_exe_no_env.out b/cli/tests/testdata/task/deno_json/task_deno_exe_no_env.out
index cf4a51b68..cf4a51b68 100644
--- a/cli/tests/testdata/task/task_deno_exe_no_env.out
+++ b/cli/tests/testdata/task/deno_json/task_deno_exe_no_env.out
diff --git a/cli/tests/testdata/task/task_exit_code_5.out b/cli/tests/testdata/task/deno_json/task_exit_code_5.out
index 12d171939..12d171939 100644
--- a/cli/tests/testdata/task/task_exit_code_5.out
+++ b/cli/tests/testdata/task/deno_json/task_exit_code_5.out
diff --git a/cli/tests/testdata/task/task_init_cwd.out b/cli/tests/testdata/task/deno_json/task_init_cwd.out
index 95ea8a545..95ea8a545 100644
--- a/cli/tests/testdata/task/task_init_cwd.out
+++ b/cli/tests/testdata/task/deno_json/task_init_cwd.out
diff --git a/cli/tests/testdata/task/task_init_cwd_already_set.out b/cli/tests/testdata/task/deno_json/task_init_cwd_already_set.out
index e427984d4..e427984d4 100644
--- a/cli/tests/testdata/task/task_init_cwd_already_set.out
+++ b/cli/tests/testdata/task/deno_json/task_init_cwd_already_set.out
diff --git a/cli/tests/testdata/task/task_no_args.out b/cli/tests/testdata/task/deno_json/task_no_args.out
index e41b3edd5..18f86fce6 100644
--- a/cli/tests/testdata/task/task_no_args.out
+++ b/cli/tests/testdata/task/deno_json/task_no_args.out
@@ -1,19 +1,19 @@
Available tasks:
- boolean_logic
sleep 0.1 && echo 3 && echo 4 & echo 1 && echo 2 || echo NOPE
-- deno_echo
- deno eval 'console.log(5)'
- echo
echo 1
+- deno_echo
+ deno eval 'console.log(5)'
+- strings
+ deno run main.ts && deno eval "console.log(\"test\")"
+- piped
+ 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)')
+- exit_code_5
+ echo $(echo 10 ; exit 2) && exit 5
- echo_cwd
echo $(pwd)
-- echo_emoji
- echo šŸ”„
- echo_init_cwd
echo $INIT_CWD
-- exit_code_5
- echo $(echo 10 ; exit 2) && exit 5
-- piped
- 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)')
-- strings
- deno run main.ts && deno eval "console.log(\"test\")"
+- echo_emoji
+ echo šŸ”„
diff --git a/cli/tests/testdata/task/task_non_existent.out b/cli/tests/testdata/task/deno_json/task_non_existent.out
index 0e70f24d9..efe3805f6 100644
--- a/cli/tests/testdata/task/task_non_existent.out
+++ b/cli/tests/testdata/task/deno_json/task_non_existent.out
@@ -2,19 +2,19 @@ Task not found: non_existent
Available tasks:
- boolean_logic
sleep 0.1 && echo 3 && echo 4 & echo 1 && echo 2 || echo NOPE
-- deno_echo
- deno eval 'console.log(5)'
- echo
echo 1
+- deno_echo
+ deno eval 'console.log(5)'
+- strings
+ deno run main.ts && deno eval "console.log(\"test\")"
+- piped
+ 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)')
+- exit_code_5
+ echo $(echo 10 ; exit 2) && exit 5
- echo_cwd
echo $(pwd)
-- echo_emoji
- echo šŸ”„
- echo_init_cwd
echo $INIT_CWD
-- exit_code_5
- echo $(echo 10 ; exit 2) && exit 5
-- piped
- 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)')
-- strings
- deno run main.ts && deno eval "console.log(\"test\")"
+- echo_emoji
+ echo šŸ”„
diff --git a/cli/tests/testdata/task/task_piped_stdin.out b/cli/tests/testdata/task/deno_json/task_piped_stdin.out
index f0a236c86..f0a236c86 100644
--- a/cli/tests/testdata/task/task_piped_stdin.out
+++ b/cli/tests/testdata/task/deno_json/task_piped_stdin.out
diff --git a/cli/tests/testdata/task/npx/non_existent.out b/cli/tests/testdata/task/npx/non_existent.out
new file mode 100644
index 000000000..5df04917e
--- /dev/null
+++ b/cli/tests/testdata/task/npx/non_existent.out
@@ -0,0 +1,2 @@
+Task non-existent npx this-command-should-not-exist-for-you
+npx: could not resolve command 'this-command-should-not-exist-for-you'
diff --git a/cli/tests/testdata/task/npx/on_own.out b/cli/tests/testdata/task/npx/on_own.out
new file mode 100644
index 000000000..67491f7b0
--- /dev/null
+++ b/cli/tests/testdata/task/npx/on_own.out
@@ -0,0 +1,2 @@
+Task on-own npx
+npx: missing command
diff --git a/cli/tests/testdata/task/npx/package.json b/cli/tests/testdata/task/npx/package.json
new file mode 100644
index 000000000..59602b96f
--- /dev/null
+++ b/cli/tests/testdata/task/npx/package.json
@@ -0,0 +1,6 @@
+{
+ "scripts": {
+ "non-existent": "npx this-command-should-not-exist-for-you",
+ "on-own": "npx"
+ }
+}
diff --git a/cli/tests/testdata/task/package_json/bin.out b/cli/tests/testdata/task/package_json/bin.out
new file mode 100644
index 000000000..ed1b6b8a2
--- /dev/null
+++ b/cli/tests/testdata/task/package_json/bin.out
@@ -0,0 +1,10 @@
+Download http://localhost:4545/npm/registry/@denotest/bin
+Download http://localhost:4545/npm/registry/@denotest/bin/0.5.0.tgz
+Download http://localhost:4545/npm/registry/@denotest/bin/1.0.0.tgz
+Task bin @denotest/bin hi && cli-esm testing this out && npx cli-cjs test "extra"
+hi
+testing
+this
+out
+test
+extra
diff --git a/cli/tests/testdata/task/package_json/echo.out b/cli/tests/testdata/task/package_json/echo.out
new file mode 100644
index 000000000..d00491fd7
--- /dev/null
+++ b/cli/tests/testdata/task/package_json/echo.out
@@ -0,0 +1 @@
+1
diff --git a/cli/tests/testdata/task/package_json/no_args.out b/cli/tests/testdata/task/package_json/no_args.out
new file mode 100644
index 000000000..de149ccf9
--- /dev/null
+++ b/cli/tests/testdata/task/package_json/no_args.out
@@ -0,0 +1,5 @@
+Available tasks:
+- echo (package.json)
+ deno eval 'console.log(1)'
+- bin (package.json)
+ @denotest/bin hi && cli-esm testing this out && npx cli-cjs test
diff --git a/cli/tests/testdata/task/package_json/package.json b/cli/tests/testdata/task/package_json/package.json
new file mode 100644
index 000000000..cedbe2d5b
--- /dev/null
+++ b/cli/tests/testdata/task/package_json/package.json
@@ -0,0 +1,10 @@
+{
+ "scripts": {
+ "echo": "deno eval 'console.log(1)'",
+ "bin": "@denotest/bin hi && cli-esm testing this out && npx cli-cjs test"
+ },
+ "dependencies": {
+ "@denotest/bin": "0.5",
+ "other": "npm:@denotest/bin@1.0"
+ }
+}
diff --git a/cli/tools/task.rs b/cli/tools/task.rs
index 0b611b9d3..523f2bc88 100644
--- a/cli/tools/task.rs
+++ b/cli/tools/task.rs
@@ -8,18 +8,16 @@ use crate::util::fs::canonicalize_path;
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
-use std::collections::BTreeMap;
+use deno_core::futures;
+use deno_core::futures::future::LocalBoxFuture;
+use deno_graph::npm::NpmPackageNv;
+use deno_task_shell::ExecuteResult;
+use deno_task_shell::ShellCommand;
+use deno_task_shell::ShellCommandContext;
+use indexmap::IndexMap;
use std::collections::HashMap;
use std::path::PathBuf;
-
-fn print_available_tasks(tasks_config: BTreeMap<String, String>) {
- eprintln!("{}", colors::green("Available tasks:"));
-
- for name in tasks_config.keys() {
- eprintln!("- {}", colors::cyan(name));
- eprintln!(" {}", tasks_config[name])
- }
-}
+use std::rc::Rc;
pub async fn execute_script(
flags: Flags,
@@ -27,62 +25,209 @@ pub async fn execute_script(
) -> Result<i32, AnyError> {
let ps = ProcState::build(flags).await?;
let tasks_config = ps.options.resolve_tasks_config()?;
- let config_file_url = ps.options.maybe_config_file_specifier().unwrap();
- let config_file_path = if config_file_url.scheme() == "file" {
- config_file_url.to_file_path().unwrap()
- } else {
- bail!("Only local configuration files are supported")
+ let maybe_package_json = ps.options.maybe_package_json();
+ let package_json_scripts = maybe_package_json
+ .as_ref()
+ .and_then(|p| p.scripts.clone())
+ .unwrap_or_default();
+
+ let task_name = match &task_flags.task {
+ Some(task) => task,
+ None => {
+ print_available_tasks(&tasks_config, &package_json_scripts);
+ return Ok(1);
+ }
};
- if task_flags.task.is_empty() {
- print_available_tasks(tasks_config);
- return Ok(1);
+ if let Some(script) = tasks_config.get(task_name) {
+ let config_file_url = ps.options.maybe_config_file_specifier().unwrap();
+ let config_file_path = if config_file_url.scheme() == "file" {
+ config_file_url.to_file_path().unwrap()
+ } else {
+ bail!("Only local configuration files are supported")
+ };
+ let cwd = match task_flags.cwd {
+ Some(path) => canonicalize_path(&PathBuf::from(path))?,
+ None => config_file_path.parent().unwrap().to_owned(),
+ };
+ let script = get_script_with_args(script, &ps);
+ output_task(task_name, &script);
+ let seq_list = deno_task_shell::parser::parse(&script)
+ .with_context(|| format!("Error parsing script '{task_name}'."))?;
+ let env_vars = collect_env_vars();
+ let exit_code =
+ deno_task_shell::execute(seq_list, env_vars, &cwd, Default::default())
+ .await;
+ Ok(exit_code)
+ } else if let Some(script) = package_json_scripts.get(task_name) {
+ let cwd = match task_flags.cwd {
+ Some(path) => canonicalize_path(&PathBuf::from(path))?,
+ None => maybe_package_json
+ .as_ref()
+ .unwrap()
+ .path
+ .parent()
+ .unwrap()
+ .to_owned(),
+ };
+ let script = get_script_with_args(script, &ps);
+ output_task(task_name, &script);
+ let seq_list = deno_task_shell::parser::parse(&script)
+ .with_context(|| format!("Error parsing script '{task_name}'."))?;
+ let npx_commands = resolve_npm_commands(&ps)?;
+ let env_vars = collect_env_vars();
+ let exit_code =
+ deno_task_shell::execute(seq_list, env_vars, &cwd, npx_commands).await;
+ Ok(exit_code)
+ } else {
+ eprintln!("Task not found: {task_name}");
+ print_available_tasks(&tasks_config, &package_json_scripts);
+ Ok(1)
}
+}
- let cwd = match task_flags.cwd {
- Some(path) => canonicalize_path(&PathBuf::from(path))?,
- None => config_file_path.parent().unwrap().to_owned(),
- };
- let task_name = task_flags.task;
- let maybe_script = tasks_config.get(&task_name);
+fn get_script_with_args(script: &str, ps: &ProcState) -> String {
+ let additional_args = ps
+ .options
+ .argv()
+ .iter()
+ // surround all the additional arguments in double quotes
+ // and santize any command substition
+ .map(|a| format!("\"{}\"", a.replace('"', "\\\"").replace('$', "\\$")))
+ .collect::<Vec<_>>()
+ .join(" ");
+ let script = format!("{script} {additional_args}");
+ script.trim().to_owned()
+}
- if let Some(script) = maybe_script {
- let additional_args = ps
- .options
- .argv()
+fn output_task(task_name: &str, script: &str) {
+ log::info!(
+ "{} {} {}",
+ colors::green("Task"),
+ colors::cyan(&task_name),
+ script,
+ );
+}
+
+fn collect_env_vars() -> HashMap<String, String> {
+ // get the starting env vars (the PWD env var will be set by deno_task_shell)
+ let mut env_vars = std::env::vars().collect::<HashMap<String, String>>();
+ const INIT_CWD_NAME: &str = "INIT_CWD";
+ if !env_vars.contains_key(INIT_CWD_NAME) {
+ if let Ok(cwd) = std::env::current_dir() {
+ // if not set, set an INIT_CWD env var that has the cwd
+ env_vars
+ .insert(INIT_CWD_NAME.to_string(), cwd.to_string_lossy().to_string());
+ }
+ }
+ env_vars
+}
+
+fn print_available_tasks(
+ // order can be important, so these use an index map
+ tasks_config: &IndexMap<String, String>,
+ 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()
- // surround all the additional arguments in double quotes
- // and santize any command substition
- .map(|a| format!("\"{}\"", a.replace('"', "\\\"").replace('$', "\\$")))
- .collect::<Vec<_>>()
- .join(" ");
- let script = format!("{script} {additional_args}");
- let script = script.trim();
- log::info!(
- "{} {} {}",
- colors::green("Task"),
- colors::cyan(&task_name),
- script,
+ .filter(|(key, _)| !tasks_config.contains_key(*key))
+ .map(|e| (false, e)),
+ ) {
+ eprintln!(
+ "- {}{}",
+ colors::cyan(key),
+ if is_deno {
+ "".to_string()
+ } else {
+ format!(" {}", colors::italic_gray("(package.json)"))
+ }
);
- let seq_list = deno_task_shell::parser::parse(script)
- .with_context(|| format!("Error parsing script '{task_name}'."))?;
+ eprintln!(" {value}");
+ had_task = true;
+ }
+ if !had_task {
+ eprintln!(" {}", colors::red("No tasks found in configuration file"));
+ }
+}
+
+struct NpxCommand;
- // get the starting env vars (the PWD env var will be set by deno_task_shell)
- let mut env_vars = std::env::vars().collect::<HashMap<String, String>>();
- const INIT_CWD_NAME: &str = "INIT_CWD";
- if !env_vars.contains_key(INIT_CWD_NAME) {
- if let Ok(cwd) = std::env::current_dir() {
- // if not set, set an INIT_CWD env var that has the cwd
- env_vars
- .insert(INIT_CWD_NAME.to_string(), cwd.to_string_lossy().to_string());
+impl ShellCommand for NpxCommand {
+ fn execute(
+ &self,
+ mut context: ShellCommandContext,
+ ) -> LocalBoxFuture<'static, ExecuteResult> {
+ if let Some(first_arg) = context.args.get(0).cloned() {
+ if let Some(command) = context.state.resolve_command(&first_arg) {
+ let context = ShellCommandContext {
+ args: context.args.iter().skip(1).cloned().collect::<Vec<_>>(),
+ ..context
+ };
+ command.execute(context)
+ } else {
+ let _ = context
+ .stderr
+ .write_line(&format!("npx: could not resolve command '{first_arg}'"));
+ Box::pin(futures::future::ready(ExecuteResult::from_exit_code(1)))
}
+ } else {
+ let _ = context.stderr.write_line("npx: missing command");
+ Box::pin(futures::future::ready(ExecuteResult::from_exit_code(1)))
}
+ }
+}
- let exit_code = deno_task_shell::execute(seq_list, env_vars, &cwd).await;
- Ok(exit_code)
- } else {
- eprintln!("Task not found: {task_name}");
- print_available_tasks(tasks_config);
- Ok(1)
+#[derive(Clone)]
+struct NpmPackageBinCommand {
+ name: String,
+ npm_package: NpmPackageNv,
+}
+
+impl ShellCommand for NpmPackageBinCommand {
+ fn execute(
+ &self,
+ context: ShellCommandContext,
+ ) -> LocalBoxFuture<'static, ExecuteResult> {
+ let mut args = vec![
+ "run".to_string(),
+ "-A".to_string(),
+ if self.npm_package.name == self.name {
+ format!("npm:{}", self.npm_package)
+ } else {
+ format!("npm:{}/{}", self.npm_package, self.name)
+ },
+ ];
+ args.extend(context.args);
+ let executable_command =
+ deno_task_shell::ExecutableCommand::new("deno".to_string());
+ executable_command.execute(ShellCommandContext { args, ..context })
+ }
+}
+
+fn resolve_npm_commands(
+ ps: &ProcState,
+) -> Result<HashMap<String, Rc<dyn ShellCommand>>, AnyError> {
+ let mut result = HashMap::new();
+ let snapshot = ps.npm_resolver.snapshot();
+ for id in snapshot.top_level_packages() {
+ let bin_commands =
+ crate::node::node_resolve_binary_commands(&id.nv, &ps.npm_resolver)?;
+ for bin_command in bin_commands {
+ result.insert(
+ bin_command.to_string(),
+ Rc::new(NpmPackageBinCommand {
+ name: bin_command,
+ npm_package: id.nv.clone(),
+ }) as Rc<dyn ShellCommand>,
+ );
+ }
+ }
+ if !result.contains_key("npx") {
+ result.insert("npx".to_string(), Rc::new(NpxCommand));
}
+ Ok(result)
}
diff --git a/cli/worker.rs b/cli/worker.rs
index 000216102..4cf4fe862 100644
--- a/cli/worker.rs
+++ b/cli/worker.rs
@@ -457,7 +457,6 @@ async fn create_main_worker_internal(
&pkg_nv,
package_ref.sub_path.as_deref(),
&ps.npm_resolver,
- &mut PermissionsContainer::allow_all(),
)?;
let is_main_cjs =
matches!(node_resolution, node::NodeResolution::CommonJs(_));