diff --git a/Cargo.lock b/Cargo.lock index 38b81fc24c..0b80bb356f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1194,6 +1194,7 @@ dependencies = [ "deno_core", "digest 0.10.6", "idna 0.3.0", + "indexmap", "md-5", "md4", "once_cell", @@ -1279,9 +1280,9 @@ dependencies = [ [[package]] name = "deno_task_shell" -version = "0.8.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532b383a071a05144c712614d62f08a2f9fad48dd62d6d457ed3884b049357da" +checksum = "a7068bd49521a7b22dc6df8937097a7ac285ea320cbd78582b4155d31f0d5049" dependencies = [ "anyhow", "futures", diff --git a/Cargo.toml b/Cargo.toml index 57063a8691..afb994f25e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,6 +87,7 @@ flate2 = "=1.0.24" futures = "0.3.21" http = "=0.2.8" hyper = "0.14.18" +indexmap = "1.9.2" libc = "0.2.126" log = "=0.4.17" lzzzz = "1.0" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 3903d32f1c..6c7c8e1282 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 1546923767..f7fd855796 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>, AnyError> { + ) -> Result>, AnyError> { if let Some(config) = self.json.tasks.clone() { - let tasks_config: BTreeMap = + let tasks_config: IndexMap = 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, AnyError> { + ) -> Result, 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, AnyError> { @@ -1237,11 +1235,6 @@ mod tests { assert!(err.to_string().contains("Unable to parse config file")); } - #[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( diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 2c9f4c09dd..825bf96d03 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, - pub task: String, + pub task: Option, } #[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 { + pub fn package_json_search_dir(&self) -> Option { 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 3d8a29fe70..aa1781bd8b 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, 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, AnyError> { + ) -> Result, 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>, 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 a6f40c99ee..4700030570 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, 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::new(), + }) +} + pub fn node_resolve_binary_export( pkg_nv: &NpmPackageNv, bin_name: Option<&str>, npm_resolver: &NpmPackageResolver, - permissions: &mut dyn NodePermissions, ) -> Result { 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 2450638bfa..039864c5ff 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 { - 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 { 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 7dbf9c74c0..d89142c212 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 47c3b6b6b0..3dce90a0cb 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 0000000000..0ae8e91903 --- /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 0000000000..caa2ef5388 --- /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 0000000000..1038609a4a --- /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 0000000000..f55a74f5b5 --- /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 0000000000..e69de29bb2 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 0000000000..fce690b705 --- /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 0000000000..708ccc6b19 --- /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 0000000000..76b3a9227b --- /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 0000000000..cd6798e6d9 --- /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 similarity index 100% rename from cli/tests/testdata/task/deno.json rename to 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 similarity index 100% rename from cli/tests/testdata/task/task_additional_args.out rename to 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 similarity index 100% rename from cli/tests/testdata/task/task_additional_args_nested_strings.out rename to 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 similarity index 100% rename from cli/tests/testdata/task/task_additional_args_no_logic.out rename to 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 similarity index 100% rename from cli/tests/testdata/task/task_additional_args_no_shell_expansion.out rename to 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 similarity index 100% rename from cli/tests/testdata/task/task_boolean_logic.out rename to 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 similarity index 100% rename from cli/tests/testdata/task/task_cwd.out rename to 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 similarity index 100% rename from cli/tests/testdata/task/task_deno_exe_no_env.out rename to 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 similarity index 100% rename from cli/tests/testdata/task/task_exit_code_5.out rename to 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 similarity index 100% rename from cli/tests/testdata/task/task_init_cwd.out rename to 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 similarity index 100% rename from cli/tests/testdata/task/task_init_cwd_already_set.out rename to 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 similarity index 100% rename from cli/tests/testdata/task/task_no_args.out rename to cli/tests/testdata/task/deno_json/task_no_args.out index e41b3edd5b..18f86fce6c 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 -- 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)') +- 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_init_cwd + echo $INIT_CWD +- 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 similarity index 100% rename from cli/tests/testdata/task/task_non_existent.out rename to cli/tests/testdata/task/deno_json/task_non_existent.out index 0e70f24d9b..efe3805f68 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 -- 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)') +- 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_init_cwd + echo $INIT_CWD +- 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 similarity index 100% rename from cli/tests/testdata/task/task_piped_stdin.out rename to 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 0000000000..5df04917e6 --- /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 0000000000..67491f7b0a --- /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 0000000000..59602b96fe --- /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 0000000000..ed1b6b8a2d --- /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 0000000000..d00491fd7e --- /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 0000000000..de149ccf96 --- /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 0000000000..cedbe2d5bd --- /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 0b611b9d32..523f2bc888 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) { - 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 { 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(); - if task_flags.task.is_empty() { - print_available_tasks(tasks_config); - return 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); - - if let Some(script) = maybe_script { - 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::>() - .join(" "); - let script = format!("{script} {additional_args}"); - let script = script.trim(); - log::info!( - "{} {} {}", - colors::green("Task"), - colors::cyan(&task_name), - script, - ); - let seq_list = deno_task_shell::parser::parse(script) - .with_context(|| format!("Error parsing script '{task_name}'."))?; - - // get the starting env vars (the PWD env var will be set by deno_task_shell) - let mut env_vars = std::env::vars().collect::>(); - 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()); - } + let task_name = match &task_flags.task { + Some(task) => task, + None => { + print_available_tasks(&tasks_config, &package_json_scripts); + return Ok(1); } + }; - let exit_code = deno_task_shell::execute(seq_list, env_vars, &cwd).await; + 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); + print_available_tasks(&tasks_config, &package_json_scripts); Ok(1) } } + +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::>() + .join(" "); + let script = format!("{script} {additional_args}"); + script.trim().to_owned() +} + +fn output_task(task_name: &str, script: &str) { + log::info!( + "{} {} {}", + colors::green("Task"), + colors::cyan(&task_name), + script, + ); +} + +fn collect_env_vars() -> HashMap { + // get the starting env vars (the PWD env var will be set by deno_task_shell) + let mut env_vars = std::env::vars().collect::>(); + 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, + package_json_scripts: &IndexMap, +) { + 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)), + ) { + eprintln!( + "- {}{}", + colors::cyan(key), + if is_deno { + "".to_string() + } else { + format!(" {}", colors::italic_gray("(package.json)")) + } + ); + eprintln!(" {value}"); + had_task = true; + } + if !had_task { + eprintln!(" {}", colors::red("No tasks found in configuration file")); + } +} + +struct NpxCommand; + +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::>(), + ..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))) + } + } +} + +#[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>, 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, + ); + } + } + 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 0002161029..4cf4fe862f 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(_)); diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 600e4fb820..1a5f4a615f 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -17,6 +17,7 @@ path = "lib.rs" deno_core.workspace = true digest = { version = "0.10.5", features = ["core-api", "std"] } idna = "0.3.0" +indexmap.workspace = true md-5 = "0.10.5" md4 = "0.10.2" once_cell.workspace = true diff --git a/ext/node/package_json.rs b/ext/node/package_json.rs index b053628900..4fa3025bf9 100644 --- a/ext/node/package_json.rs +++ b/ext/node/package_json.rs @@ -4,12 +4,14 @@ use crate::NodeModuleKind; use crate::NodePermissions; use super::RequireNpmResolver; + use deno_core::anyhow; use deno_core::anyhow::bail; use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::serde_json::Map; use deno_core::serde_json::Value; +use indexmap::IndexMap; use serde::Serialize; use std::cell::RefCell; use std::collections::HashMap; @@ -35,6 +37,7 @@ pub struct PackageJson { pub types: Option, pub dependencies: Option>, pub dev_dependencies: Option>, + pub scripts: Option>, } impl PackageJson { @@ -53,6 +56,7 @@ impl PackageJson { types: None, dependencies: None, dev_dependencies: None, + scripts: None, } } @@ -144,6 +148,10 @@ impl PackageJson { } }); + let scripts: Option> = package_json + .get("scripts") + .and_then(|d| serde_json::from_value(d.to_owned()).ok()); + // Ignore unknown types for forwards compatibility let typ = if let Some(t) = type_val { if let Some(t) = t.as_str() { @@ -179,6 +187,7 @@ impl PackageJson { bin, dependencies, dev_dependencies, + scripts, }; CACHE.with(|cache| { diff --git a/test_util/src/lib.rs b/test_util/src/lib.rs index 25faa1633e..4e97fee3b9 100644 --- a/test_util/src/lib.rs +++ b/test_util/src/lib.rs @@ -1929,7 +1929,7 @@ pub struct CheckOutputIntegrationTest<'a> { /// the test creates files in the testdata directory (ex. a node_modules folder) pub copy_temp_dir: Option<&'a str>, /// Relative to "testdata" directory - pub maybe_cwd: Option<&'a str>, + pub cwd: Option<&'a str>, } impl<'a> CheckOutputIntegrationTest<'a> { @@ -1970,7 +1970,7 @@ impl<'a> CheckOutputIntegrationTest<'a> { let mut command = deno_cmd_with_deno_dir(&deno_dir); let cwd = if self.temp_cwd { deno_dir.path().to_owned() - } else if let Some(cwd_) = &self.maybe_cwd { + } else if let Some(cwd_) = &self.cwd { testdata_dir.join(cwd_) } else { testdata_dir.clone()