diff options
Diffstat (limited to 'cli')
-rw-r--r-- | cli/Cargo.toml | 2 | ||||
-rw-r--r-- | cli/args/flags.rs | 253 | ||||
-rw-r--r-- | cli/args/mod.rs | 102 | ||||
-rw-r--r-- | cli/cache/mod.rs | 42 | ||||
-rw-r--r-- | cli/factory.rs | 25 | ||||
-rw-r--r-- | cli/file_fetcher.rs | 48 | ||||
-rw-r--r-- | cli/graph_container.rs | 7 | ||||
-rw-r--r-- | cli/graph_util.rs | 38 | ||||
-rw-r--r-- | cli/lsp/language_server.rs | 5 | ||||
-rw-r--r-- | cli/module_loader.rs | 41 | ||||
-rw-r--r-- | cli/standalone/mod.rs | 19 | ||||
-rw-r--r-- | cli/tools/check.rs | 3 | ||||
-rw-r--r-- | cli/tools/compile.rs | 2 | ||||
-rw-r--r-- | cli/tools/info.rs | 17 | ||||
-rw-r--r-- | cli/tools/registry/diagnostics.rs | 14 | ||||
-rw-r--r-- | cli/tools/registry/mod.rs | 6 | ||||
-rw-r--r-- | cli/tools/registry/pm/cache_deps.rs | 3 | ||||
-rw-r--r-- | cli/tools/repl/mod.rs | 6 | ||||
-rw-r--r-- | cli/tools/run/mod.rs | 12 | ||||
-rw-r--r-- | cli/tools/serve.rs | 17 | ||||
-rw-r--r-- | cli/worker.rs | 21 |
21 files changed, 466 insertions, 217 deletions
diff --git a/cli/Cargo.toml b/cli/Cargo.toml index ec2243a81..ede5cc15c 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -68,7 +68,7 @@ deno_cache_dir = { workspace = true } deno_config = { version = "=0.35.0", features = ["workspace", "sync"] } deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_doc = { version = "0.148.0", features = ["html", "syntect"] } -deno_graph = { version = "=0.82.2" } +deno_graph = { version = "=0.82.3" } deno_lint = { version = "=0.67.0", features = ["docs"] } deno_lockfile.workspace = true deno_npm = "=0.25.2" diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 10fa07bed..74ccb512f 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -1,5 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::borrow::Cow; use std::collections::HashSet; use std::env; use std::ffi::OsString; @@ -44,6 +45,7 @@ use crate::args::resolve_no_prompt; use crate::util::fs::canonicalize_path; use super::flags_net; +use super::jsr_url; #[derive(Clone, Debug, Default, Eq, PartialEq)] pub enum ConfigFlag { @@ -639,6 +641,7 @@ pub struct PermissionFlags { pub allow_write: Option<Vec<String>>, pub deny_write: Option<Vec<String>>, pub no_prompt: bool, + pub allow_import: Option<Vec<String>>, } impl PermissionFlags { @@ -658,9 +661,10 @@ impl PermissionFlags { || self.deny_sys.is_some() || self.allow_write.is_some() || self.deny_write.is_some() + || self.allow_import.is_some() } - pub fn to_options(&self) -> PermissionsOptions { + pub fn to_options(&self, cli_arg_urls: &[Cow<Url>]) -> PermissionsOptions { fn handle_allow<T: Default>( allow_all: bool, value: Option<T>, @@ -673,6 +677,41 @@ impl PermissionFlags { } } + fn handle_imports( + cli_arg_urls: &[Cow<Url>], + imports: Option<Vec<String>>, + ) -> Option<Vec<String>> { + if imports.is_some() { + return imports; + } + + let builtin_allowed_import_hosts = [ + "deno.land:443", + "esm.sh:443", + "jsr.io:443", + "raw.githubusercontent.com:443", + "gist.githubusercontent.com:443", + ]; + + let mut imports = + Vec::with_capacity(builtin_allowed_import_hosts.len() + 1); + imports + .extend(builtin_allowed_import_hosts.iter().map(|s| s.to_string())); + + // also add the JSR_URL env var + if let Some(jsr_host) = allow_import_host_from_url(jsr_url()) { + imports.push(jsr_host); + } + // include the cli arg urls + for url in cli_arg_urls { + if let Some(host) = allow_import_host_from_url(url) { + imports.push(host); + } + } + + Some(imports) + } + PermissionsOptions { allow_all: self.allow_all, allow_env: handle_allow(self.allow_all, self.allow_env.clone()), @@ -689,11 +728,33 @@ impl PermissionFlags { deny_sys: self.deny_sys.clone(), allow_write: handle_allow(self.allow_all, self.allow_write.clone()), deny_write: self.deny_write.clone(), + allow_import: handle_imports( + cli_arg_urls, + handle_allow(self.allow_all, self.allow_import.clone()), + ), prompt: !resolve_no_prompt(self), } } } +/// Gets the --allow-import host from the provided url +fn allow_import_host_from_url(url: &Url) -> Option<String> { + let host = url.host()?; + if let Some(port) = url.port() { + Some(format!("{}:{}", host, port)) + } else { + use deno_core::url::Host::*; + match host { + Domain(domain) if domain == "jsr.io" && url.scheme() == "https" => None, + _ => match url.scheme() { + "https" => Some(format!("{}:443", host)), + "http" => Some(format!("{}:80", host)), + _ => None, + }, + } + } +} + fn join_paths(allowlist: &[String], d: &str) -> String { allowlist .iter() @@ -881,6 +942,17 @@ impl Flags { _ => {} } + match &self.permissions.allow_import { + Some(allowlist) if allowlist.is_empty() => { + args.push("--allow-import".to_string()); + } + Some(allowlist) => { + let s = format!("--allow-import={}", allowlist.join(",")); + args.push(s); + } + _ => {} + } + args } @@ -991,6 +1063,7 @@ impl Flags { self.permissions.allow_write = None; self.permissions.allow_sys = None; self.permissions.allow_ffi = None; + self.permissions.allow_import = None; } pub fn resolve_watch_exclude_set( @@ -1707,6 +1780,7 @@ Future runs of this module will trigger no downloads or compilation unless --rel ) .arg(frozen_lockfile_arg()) .arg(allow_scripts_arg()) + .arg(allow_import_arg()) }) } @@ -1766,6 +1840,7 @@ Unless --reload is specified, this command will not re-download already cached d .required_unless_present("help") .value_hint(ValueHint::FilePath), ) + .arg(allow_import_arg()) } ) } @@ -1994,6 +2069,7 @@ Show documentation for runtime built-ins: .arg(no_lock_arg()) .arg(no_npm_arg()) .arg(no_remote_arg()) + .arg(allow_import_arg()) .arg( Arg::new("json") .long("json") @@ -2358,6 +2434,7 @@ The following information is shown: .help("UNSTABLE: Outputs the information in JSON format") .action(ArgAction::SetTrue), )) + .arg(allow_import_arg()) } fn install_subcommand() -> Command { @@ -3151,47 +3228,44 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command { .after_help(cstr!(r#"<y>Permission options:</> <y>Docs</>: <c>https://docs.deno.com/go/permissions</> - <g>-A, --allow-all</> Allow all permissions. - <g>--no-prompt</> Always throw if required permission wasn't passed. - <p(245)>Can also be set via the DENO_NO_PROMPT environment variable.</> - <g>-R, --allow-read[=<<PATH>...]</> Allow file system read access. Optionally specify allowed paths. - <p(245)>--allow-read | --allow-read="/etc,/var/log.txt"</> - <g>-W, --allow-write[=<<PATH>...]</> Allow file system write access. Optionally specify allowed paths. - <p(245)>--allow-write | --allow-write="/etc,/var/log.txt"</> - <g>-N, --allow-net[=<<IP_OR_HOSTNAME>...]</> Allow network access. Optionally specify allowed IP addresses and host names, with ports as necessary. - <p(245)>--allow-net | --allow-net="localhost:8080,deno.land"</> - <g>-E, --allow-env[=<<VARIABLE_NAME>...]</> Allow access to environment variables. Optionally specify accessible environment variables. - <p(245)>--allow-env | --allow-env="PORT,HOME,PATH"</> - <g>-S, --allow-sys[=<<API_NAME>...]</> Allow access to OS information. Optionally allow specific APIs by function name. - <p(245)>--allow-sys | --allow-sys="systemMemoryInfo,osRelease"</> - <g>--allow-run[=<<PROGRAM_NAME>...]</> Allow running subprocesses. Optionally specify allowed runnable program names. - <p(245)>--allow-run | --allow-run="whoami,ps"</> - <g>--allow-ffi[=<<PATH>...]</> (Unstable) Allow loading dynamic libraries. Optionally specify allowed directories or files. - <p(245)>--allow-ffi | --allow-ffi="./libfoo.so"</> - <g> --deny-read[=<<PATH>...]</> Deny file system read access. Optionally specify denied paths. - <p(245)>--deny-read | --deny-read="/etc,/var/log.txt"</> - <g> --deny-write[=<<PATH>...]</> Deny file system write access. Optionally specify denied paths. - <p(245)>--deny-write | --deny-write="/etc,/var/log.txt"</> - <g> --deny-net[=<<IP_OR_HOSTNAME>...]</> Deny network access. Optionally specify defined IP addresses and host names, with ports as necessary. - <p(245)>--deny-net | --deny-net="localhost:8080,deno.land"</> - <g> --deny-env[=<<VARIABLE_NAME>...]</> Deny access to environment variables. Optionally specify inacessible environment variables. - <p(245)>--deny-env | --deny-env="PORT,HOME,PATH"</> - <g>-S, --deny-sys[=<<API_NAME>...]</> Deny access to OS information. Optionally deny specific APIs by function name. - <p(245)>--deny-sys | --deny-sys="systemMemoryInfo,osRelease"</> - <g>--deny-run[=<<PROGRAM_NAME>...]</> Deny running subprocesses. Optionally specify denied runnable program names. - <p(245)>--deny-run | --deny-run="whoami,ps"</> - <g>--deny-ffi[=<<PATH>...]</> (Unstable) Deny loading dynamic libraries. Optionally specify denied directories or files. - <p(245)>--deny-ffi | --deny-ffi="./libfoo.so"</> + <g>-A, --allow-all</> Allow all permissions. + <g>--no-prompt</> Always throw if required permission wasn't passed. + <p(245)>Can also be set via the DENO_NO_PROMPT environment variable.</> + <g>-R, --allow-read[=<<PATH>...]</> Allow file system read access. Optionally specify allowed paths. + <p(245)>--allow-read | --allow-read="/etc,/var/log.txt"</> + <g>-W, --allow-write[=<<PATH>...]</> Allow file system write access. Optionally specify allowed paths. + <p(245)>--allow-write | --allow-write="/etc,/var/log.txt"</> + <g>-I, --allow-import[=<<IP_OR_HOSTNAME>...]</> Allow importing from remote hosts. Optionally specify allowed IP addresses and host names, with ports as necessary. + Default value: <p(245)>deno.land:443,jsr.io:443,esm.sh:443,raw.githubusercontent.com:443,user.githubusercontent.com:443</> + <p(245)>--allow-import | --allow-import="example.com,github.com"</> + <g>-N, --allow-net[=<<IP_OR_HOSTNAME>...]</> Allow network access. Optionally specify allowed IP addresses and host names, with ports as necessary. + <p(245)>--allow-net | --allow-net="localhost:8080,deno.land"</> + <g>-E, --allow-env[=<<VARIABLE_NAME>...]</> Allow access to environment variables. Optionally specify accessible environment variables. + <p(245)>--allow-env | --allow-env="PORT,HOME,PATH"</> + <g>-S, --allow-sys[=<<API_NAME>...]</> Allow access to OS information. Optionally allow specific APIs by function name. + <p(245)>--allow-sys | --allow-sys="systemMemoryInfo,osRelease"</> + <g>--allow-run[=<<PROGRAM_NAME>...]</> Allow running subprocesses. Optionally specify allowed runnable program names. + <p(245)>--allow-run | --allow-run="whoami,ps"</> + <g>--allow-ffi[=<<PATH>...]</> (Unstable) Allow loading dynamic libraries. Optionally specify allowed directories or files. + <p(245)>--allow-ffi | --allow-ffi="./libfoo.so"</> + <g> --deny-read[=<<PATH>...]</> Deny file system read access. Optionally specify denied paths. + <p(245)>--deny-read | --deny-read="/etc,/var/log.txt"</> + <g> --deny-write[=<<PATH>...]</> Deny file system write access. Optionally specify denied paths. + <p(245)>--deny-write | --deny-write="/etc,/var/log.txt"</> + <g> --deny-net[=<<IP_OR_HOSTNAME>...]</> Deny network access. Optionally specify defined IP addresses and host names, with ports as necessary. + <p(245)>--deny-net | --deny-net="localhost:8080,deno.land"</> + <g> --deny-env[=<<VARIABLE_NAME>...]</> Deny access to environment variables. Optionally specify inacessible environment variables. + <p(245)>--deny-env | --deny-env="PORT,HOME,PATH"</> + <g>-S, --deny-sys[=<<API_NAME>...]</> Deny access to OS information. Optionally deny specific APIs by function name. + <p(245)>--deny-sys | --deny-sys="systemMemoryInfo,osRelease"</> + <g>--deny-run[=<<PROGRAM_NAME>...]</> Deny running subprocesses. Optionally specify denied runnable program names. + <p(245)>--deny-run | --deny-run="whoami,ps"</> + <g>--deny-ffi[=<<PATH>...]</> (Unstable) Deny loading dynamic libraries. Optionally specify denied directories or files. + <p(245)>--deny-ffi | --deny-ffi="./libfoo.so"</> "#)) .arg( { - let mut arg = Arg::new("allow-all") - .short('A') - .long("allow-all") - .action(ArgAction::SetTrue) - .help("Allow all permissions") - .hide(true) - ; + let mut arg = allow_all_arg().hide(true); if let Some(requires) = requires { arg = arg.requires(requires) } @@ -3200,7 +3274,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command { ) .arg( { - let mut arg = Arg::new("allow-read") + let mut arg = Arg::new("allow-read") .long("allow-read") .short('R') .num_args(0..) @@ -3218,7 +3292,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command { ) .arg( { - let mut arg = Arg::new("deny-read") + let mut arg = Arg::new("deny-read") .long("deny-read") .num_args(0..) .action(ArgAction::Append) @@ -3235,7 +3309,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command { ) .arg( { - let mut arg = Arg::new("allow-write") + let mut arg = Arg::new("allow-write") .long("allow-write") .short('W') .num_args(0..) @@ -3253,7 +3327,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command { ) .arg( { - let mut arg = Arg::new("deny-write") + let mut arg = Arg::new("deny-write") .long("deny-write") .num_args(0..) .action(ArgAction::Append) @@ -3270,7 +3344,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command { ) .arg( { - let mut arg = Arg::new("allow-net") + let mut arg = Arg::new("allow-net") .long("allow-net") .short('N') .num_args(0..) @@ -3289,7 +3363,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command { ) .arg( { - let mut arg = Arg::new("deny-net") + let mut arg = Arg::new("deny-net") .long("deny-net") .num_args(0..) .use_value_delimiter(true) @@ -3383,7 +3457,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command { ) .arg( { - let mut arg = Arg::new("deny-sys") + let mut arg = Arg::new("deny-sys") .long("deny-sys") .num_args(0..) .use_value_delimiter(true) @@ -3418,7 +3492,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command { ) .arg( { - let mut arg = Arg::new("deny-run") + let mut arg = Arg::new("deny-run") .long("deny-run") .num_args(0..) .use_value_delimiter(true) @@ -3509,6 +3583,26 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command { arg } ) + .arg( + { + let mut arg = allow_import_arg().hide(true); + if let Some(requires) = requires { + // allow this for install --global + if requires != "global" { + arg = arg.requires(requires) + } + } + arg + } + ) +} + +fn allow_all_arg() -> Arg { + Arg::new("allow-all") + .short('A') + .long("allow-all") + .action(ArgAction::SetTrue) + .help("Allow all permissions") } fn runtime_args( @@ -3537,6 +3631,20 @@ fn runtime_args( .arg(strace_ops_arg()) } +fn allow_import_arg() -> Arg { + Arg::new("allow-import") + .long("allow-import") + .short('I') + .num_args(0..) + .use_value_delimiter(true) + .require_equals(true) + .value_name("IP_OR_HOSTNAME") + .help(cstr!( + "Allow importing from remote hosts. Optionally specify allowed IP addresses and host names, with ports as necessary. Default value: <p(245)>deno.land:443,jsr.io:443,esm.sh:443,raw.githubusercontent.com:443,user.githubusercontent.com:443</>" + )) + .value_parser(flags_net::validator) +} + fn inspect_args(app: Command) -> Command { app .arg( @@ -4174,6 +4282,7 @@ fn cache_parse( unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionOnly); frozen_lockfile_arg_parse(flags, matches); allow_scripts_arg_parse(flags, matches)?; + allow_import_parse(flags, matches); let files = matches.remove_many::<String>("file").unwrap().collect(); flags.subcommand = DenoSubcommand::Cache(CacheFlags { files }); Ok(()) @@ -4195,6 +4304,7 @@ fn check_parse( doc: matches.get_flag("doc"), doc_only: matches.get_flag("doc-only"), }); + allow_import_parse(flags, matches); Ok(()) } @@ -4320,6 +4430,7 @@ fn doc_parse( no_lock_arg_parse(flags, matches); no_npm_arg_parse(flags, matches); no_remote_arg_parse(flags, matches); + allow_import_parse(flags, matches); let source_files_val = matches.remove_many::<String>("source_file"); let source_files = if let Some(val) = source_files_val { @@ -4460,6 +4571,7 @@ fn info_parse( lock_args_parse(flags, matches); no_remote_arg_parse(flags, matches); no_npm_arg_parse(flags, matches); + allow_import_parse(flags, matches); let json = matches.get_flag("json"); flags.subcommand = DenoSubcommand::Info(InfoFlags { file: matches.remove_one::<String>("file"), @@ -4495,6 +4607,7 @@ fn install_parse( force, }), }); + return Ok(()); } @@ -5175,13 +5288,22 @@ fn permission_args_parse( } if matches.get_flag("allow-hrtime") || matches.get_flag("deny-hrtime") { - log::warn!("⚠️ Warning: `allow-hrtime` and `deny-hrtime` have been removed in Deno 2, as high resolution time is now always allowed."); + // use eprintln instead of log::warn because logging hasn't been initialized yet + #[allow(clippy::print_stderr)] + { + eprintln!( + "{} `allow-hrtime` and `deny-hrtime` have been removed in Deno 2, as high resolution time is now always allowed", + deno_runtime::colors::yellow("Warning") + ); + } } if matches.get_flag("allow-all") { flags.allow_all(); } + allow_import_parse(flags, matches); + if matches.get_flag("no-prompt") { flags.permissions.no_prompt = true; } @@ -5189,6 +5311,13 @@ fn permission_args_parse( Ok(()) } +fn allow_import_parse(flags: &mut Flags, matches: &mut ArgMatches) { + if let Some(imports_wl) = matches.remove_many::<String>("allow-import") { + let imports_allowlist = flags_net::parse(imports_wl.collect()).unwrap(); + flags.permissions.allow_import = Some(imports_allowlist); + } +} + fn unsafely_ignore_certificate_errors_parse( flags: &mut Flags, matches: &mut ArgMatches, @@ -6215,7 +6344,7 @@ mod tests { #[test] fn short_permission_flags() { - let r = flags_from_vec(svec!["deno", "run", "-RNESW", "gist.ts"]); + let r = flags_from_vec(svec!["deno", "run", "-RNESWI", "gist.ts"]); assert_eq!( r.unwrap(), Flags { @@ -6226,6 +6355,7 @@ mod tests { allow_read: Some(vec![]), allow_write: Some(vec![]), allow_env: Some(vec![]), + allow_import: Some(vec![]), allow_net: Some(vec![]), allow_sys: Some(vec![]), ..Default::default() @@ -10777,7 +10907,7 @@ mod tests { } ); // just make sure this doesn't panic - let _ = flags.permissions.to_options(); + let _ = flags.permissions.to_options(&[]); } #[test] @@ -10852,4 +10982,27 @@ mod tests { Usage: deno repl [OPTIONS] [-- [ARGS]...]\n" ) } + + #[test] + fn test_allow_import_host_from_url() { + fn parse(text: &str) -> Option<String> { + allow_import_host_from_url(&Url::parse(text).unwrap()) + } + + assert_eq!(parse("https://jsr.io"), None); + assert_eq!( + parse("http://127.0.0.1:4250"), + Some("127.0.0.1:4250".to_string()) + ); + assert_eq!(parse("http://jsr.io"), Some("jsr.io:80".to_string())); + assert_eq!( + parse("https://example.com"), + Some("example.com:443".to_string()) + ); + assert_eq!( + parse("http://example.com"), + Some("example.com:80".to_string()) + ); + assert_eq!(parse("file:///example.com"), None); + } } diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 1c92777ae..80b9afb24 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -769,6 +769,7 @@ pub struct CliOptions { // application need not concern itself with, so keep these private flags: Arc<Flags>, initial_cwd: PathBuf, + main_module_cell: std::sync::OnceLock<Result<ModuleSpecifier, AnyError>>, maybe_node_modules_folder: Option<PathBuf>, npmrc: Arc<ResolvedNpmRc>, maybe_lockfile: Option<Arc<CliLockfile>>, @@ -825,6 +826,7 @@ impl CliOptions { npmrc, maybe_node_modules_folder, overrides: Default::default(), + main_module_cell: std::sync::OnceLock::new(), start_dir, deno_dir_provider, }) @@ -1105,40 +1107,43 @@ impl CliOptions { self.flags.env_file.as_ref() } - pub fn resolve_main_module(&self) -> Result<ModuleSpecifier, AnyError> { - let main_module = match &self.flags.subcommand { - DenoSubcommand::Compile(compile_flags) => { - resolve_url_or_path(&compile_flags.source_file, self.initial_cwd())? - } - DenoSubcommand::Eval(_) => { - resolve_url_or_path("./$deno$eval.ts", self.initial_cwd())? - } - DenoSubcommand::Repl(_) => { - resolve_url_or_path("./$deno$repl.ts", self.initial_cwd())? - } - DenoSubcommand::Run(run_flags) => { - if run_flags.is_stdin() { - std::env::current_dir() - .context("Unable to get CWD") - .and_then(|cwd| { - resolve_url_or_path("./$deno$stdin.ts", &cwd) - .map_err(AnyError::from) - })? - } else if NpmPackageReqReference::from_str(&run_flags.script).is_ok() { - ModuleSpecifier::parse(&run_flags.script)? - } else { - resolve_url_or_path(&run_flags.script, self.initial_cwd())? - } - } - DenoSubcommand::Serve(run_flags) => { - resolve_url_or_path(&run_flags.script, self.initial_cwd())? - } - _ => { - bail!("No main module.") - } - }; + pub fn resolve_main_module(&self) -> Result<&ModuleSpecifier, AnyError> { + self + .main_module_cell + .get_or_init(|| { + let main_module = match &self.flags.subcommand { + DenoSubcommand::Compile(compile_flags) => { + resolve_url_or_path(&compile_flags.source_file, self.initial_cwd())? + } + DenoSubcommand::Eval(_) => { + resolve_url_or_path("./$deno$eval.ts", self.initial_cwd())? + } + DenoSubcommand::Repl(_) => { + resolve_url_or_path("./$deno$repl.ts", self.initial_cwd())? + } + DenoSubcommand::Run(run_flags) => { + if run_flags.is_stdin() { + resolve_url_or_path("./$deno$stdin.ts", self.initial_cwd())? + } else if NpmPackageReqReference::from_str(&run_flags.script) + .is_ok() + { + ModuleSpecifier::parse(&run_flags.script)? + } else { + resolve_url_or_path(&run_flags.script, self.initial_cwd())? + } + } + DenoSubcommand::Serve(run_flags) => { + resolve_url_or_path(&run_flags.script, self.initial_cwd())? + } + _ => { + bail!("No main module.") + } + }; - Ok(main_module) + Ok(main_module) + }) + .as_ref() + .map_err(|err| deno_core::anyhow::anyhow!("{}", err)) } pub fn resolve_file_header_overrides( @@ -1159,7 +1164,7 @@ impl CliOptions { (maybe_main_specifier, maybe_content_type) { HashMap::from([( - main_specifier, + main_specifier.clone(), HashMap::from([("content-type".to_string(), content_type.to_string())]), )]) } else { @@ -1480,7 +1485,34 @@ impl CliOptions { } pub fn permissions_options(&self) -> PermissionsOptions { - self.flags.permissions.to_options() + fn files_to_urls(files: &[String]) -> Vec<Cow<'_, Url>> { + files + .iter() + .filter_map(|f| Url::parse(f).ok().map(Cow::Owned)) + .collect() + } + + // get a list of urls to imply for --allow-import + let cli_arg_urls = self + .resolve_main_module() + .ok() + .map(|url| vec![Cow::Borrowed(url)]) + .or_else(|| match &self.flags.subcommand { + DenoSubcommand::Cache(cache_flags) => { + Some(files_to_urls(&cache_flags.files)) + } + DenoSubcommand::Check(check_flags) => { + Some(files_to_urls(&check_flags.files)) + } + DenoSubcommand::Install(InstallFlags { + kind: InstallKind::Global(flags), + }) => Url::parse(&flags.module_url) + .ok() + .map(|url| vec![Cow::Owned(url)]), + _ => None, + }) + .unwrap_or_default(); + self.flags.permissions.to_options(&cli_arg_urls) } pub fn reload_flag(&self) -> bool { diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs index a95c35086..2d8813716 100644 --- a/cli/cache/mod.rs +++ b/cli/cache/mod.rs @@ -1,10 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use crate::args::jsr_url; use crate::args::CacheSetting; use crate::errors::get_error_class_name; use crate::file_fetcher::FetchNoFollowOptions; use crate::file_fetcher::FetchOptions; -use crate::file_fetcher::FetchPermissionsOption; +use crate::file_fetcher::FetchPermissionsOptionRef; use crate::file_fetcher::FileFetcher; use crate::file_fetcher::FileOrRedirect; use crate::npm::CliNpmResolver; @@ -19,6 +20,7 @@ use deno_graph::source::CacheInfo; use deno_graph::source::LoadFuture; use deno_graph::source::LoadResponse; use deno_graph::source::Loader; +use deno_runtime::deno_permissions::PermissionsContainer; use std::collections::HashMap; use std::path::Path; use std::path::PathBuf; @@ -104,6 +106,13 @@ pub type LocalLspHttpCache = deno_cache_dir::LocalLspHttpCache<RealDenoCacheEnv>; pub use deno_cache_dir::HttpCache; +pub struct FetchCacherOptions { + pub file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>, + pub permissions: PermissionsContainer, + /// If we're publishing for `deno publish`. + pub is_deno_publish: bool, +} + /// A "wrapper" for the FileFetcher and DiskCache for the Deno CLI that provides /// a concise interface to the DENO_DIR when building module graphs. pub struct FetchCacher { @@ -112,26 +121,27 @@ pub struct FetchCacher { global_http_cache: Arc<GlobalHttpCache>, npm_resolver: Arc<dyn CliNpmResolver>, module_info_cache: Arc<ModuleInfoCache>, - permissions: FetchPermissionsOption, + permissions: PermissionsContainer, cache_info_enabled: bool, + is_deno_publish: bool, } impl FetchCacher { pub fn new( file_fetcher: Arc<FileFetcher>, - file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>, global_http_cache: Arc<GlobalHttpCache>, npm_resolver: Arc<dyn CliNpmResolver>, module_info_cache: Arc<ModuleInfoCache>, - permissions: FetchPermissionsOption, + options: FetchCacherOptions, ) -> Self { Self { file_fetcher, - file_header_overrides, global_http_cache, npm_resolver, module_info_cache, - permissions, + file_header_overrides: options.file_header_overrides, + permissions: options.permissions, + is_deno_publish: options.is_deno_publish, cache_info_enabled: false, } } @@ -208,10 +218,24 @@ impl Loader for FetchCacher { } } + if self.is_deno_publish + && matches!(specifier.scheme(), "http" | "https") + && !specifier.as_str().starts_with(jsr_url().as_str()) + { + // mark non-JSR remote modules as external so we don't need --allow-import + // permissions as these will error out later when publishing + return Box::pin(futures::future::ready(Ok(Some( + LoadResponse::External { + specifier: specifier.clone(), + }, + )))); + } + let file_fetcher = self.file_fetcher.clone(); let file_header_overrides = self.file_header_overrides.clone(); let permissions = self.permissions.clone(); let specifier = specifier.clone(); + let is_statically_analyzable = !options.was_dynamic_root; async move { let maybe_cache_setting = match options.cache_setting { @@ -230,7 +254,11 @@ impl Loader for FetchCacher { .fetch_no_follow_with_options(FetchNoFollowOptions { fetch_options: FetchOptions { specifier: &specifier, - permissions: permissions.as_ref(), + permissions: if is_statically_analyzable { + FetchPermissionsOptionRef::StaticContainer(&permissions) + } else { + FetchPermissionsOptionRef::DynamicContainer(&permissions) + }, maybe_accept: None, maybe_cache_setting: maybe_cache_setting.as_ref(), }, diff --git a/cli/factory.rs b/cli/factory.rs index 0f49546d0..8aea635d2 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -185,6 +185,7 @@ struct CliFactoryServices { node_resolver: Deferred<Arc<NodeResolver>>, npm_resolver: Deferred<Arc<dyn CliNpmResolver>>, permission_desc_parser: Deferred<Arc<RuntimePermissionDescriptorParser>>, + root_permissions_container: Deferred<PermissionsContainer>, sloppy_imports_resolver: Deferred<Option<Arc<SloppyImportsResolver>>>, text_only_progress_bar: Deferred<ProgressBar>, type_checker: Deferred<Arc<TypeChecker>>, @@ -626,6 +627,7 @@ impl CliFactory { self.maybe_file_watcher_reporter().clone(), self.file_fetcher()?.clone(), self.global_http_cache()?.clone(), + self.root_permissions_container()?.clone(), ))) }) .await @@ -659,6 +661,7 @@ impl CliFactory { Ok(Arc::new(MainModuleGraphContainer::new( self.cli_options()?.clone(), self.module_load_preparer().await?.clone(), + self.root_permissions_container()?.clone(), ))) }) .await @@ -755,15 +758,20 @@ impl CliFactory { )) } - pub fn create_permissions_container( + pub fn root_permissions_container( &self, - ) -> Result<PermissionsContainer, AnyError> { - let desc_parser = self.permission_desc_parser()?.clone(); - let permissions = Permissions::from_options( - desc_parser.as_ref(), - &self.cli_options()?.permissions_options(), - )?; - Ok(PermissionsContainer::new(desc_parser, permissions)) + ) -> Result<&PermissionsContainer, AnyError> { + self + .services + .root_permissions_container + .get_or_try_init(|| { + let desc_parser = self.permission_desc_parser()?.clone(); + let permissions = Permissions::from_options( + desc_parser.as_ref(), + &self.cli_options()?.permissions_options(), + )?; + Ok(PermissionsContainer::new(desc_parser, permissions)) + }) } pub async fn create_cli_main_worker_factory( @@ -816,6 +824,7 @@ impl CliFactory { npm_resolver.clone(), self.permission_desc_parser()?.clone(), self.root_cert_store_provider().clone(), + self.root_permissions_container()?.clone(), StorageKeyResolver::from_options(cli_options), cli_options.sub_command().clone(), self.create_cli_main_worker_options()?, diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index 2f4b0b3dc..ca1144939 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -23,6 +23,7 @@ use deno_graph::source::LoaderChecksum; use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_web::BlobStore; +use deno_runtime::fs_util::specifier_to_file_path; use log::debug; use std::borrow::Cow; use std::collections::HashMap; @@ -135,7 +136,7 @@ impl MemoryFiles { /// Fetch a source file from the local file system. fn fetch_local(specifier: &ModuleSpecifier) -> Result<File, AnyError> { - let local = specifier.to_file_path().map_err(|_| { + let local = specifier_to_file_path(specifier).map_err(|_| { uri_error(format!("Invalid file path.\n Specifier: {specifier}")) })?; // If it doesnt have a extension, we want to treat it as typescript by default @@ -173,30 +174,8 @@ fn get_validated_scheme( #[derive(Debug, Copy, Clone)] pub enum FetchPermissionsOptionRef<'a> { AllowAll, - Container(&'a PermissionsContainer), -} - -#[derive(Debug, Clone)] -pub enum FetchPermissionsOption { - AllowAll, - Container(PermissionsContainer), -} - -impl FetchPermissionsOption { - pub fn as_ref(&self) -> FetchPermissionsOptionRef { - match self { - FetchPermissionsOption::AllowAll => FetchPermissionsOptionRef::AllowAll, - FetchPermissionsOption::Container(container) => { - FetchPermissionsOptionRef::Container(container) - } - } - } -} - -impl From<PermissionsContainer> for FetchPermissionsOption { - fn from(value: PermissionsContainer) -> Self { - Self::Container(value) - } + DynamicContainer(&'a PermissionsContainer), + StaticContainer(&'a PermissionsContainer), } pub struct FetchOptions<'a> { @@ -564,7 +543,6 @@ impl FileFetcher { } /// Fetch a source file and asynchronously return it. - #[allow(dead_code)] // todo(25469): undo when merging #[inline(always)] pub async fn fetch( &self, @@ -572,7 +550,10 @@ impl FileFetcher { permissions: &PermissionsContainer, ) -> Result<File, AnyError> { self - .fetch_inner(specifier, FetchPermissionsOptionRef::Container(permissions)) + .fetch_inner( + specifier, + FetchPermissionsOptionRef::StaticContainer(permissions), + ) .await } @@ -647,8 +628,17 @@ impl FileFetcher { FetchPermissionsOptionRef::AllowAll => { // allow } - FetchPermissionsOptionRef::Container(permissions) => { - permissions.check_specifier(specifier)?; + FetchPermissionsOptionRef::StaticContainer(permissions) => { + permissions.check_specifier( + specifier, + deno_runtime::deno_permissions::CheckSpecifierKind::Static, + )?; + } + FetchPermissionsOptionRef::DynamicContainer(permissions) => { + permissions.check_specifier( + specifier, + deno_runtime::deno_permissions::CheckSpecifierKind::Dynamic, + )?; } } if let Some(file) = self.memory_files.get(specifier) { diff --git a/cli/graph_container.rs b/cli/graph_container.rs index 211b278e0..c463d71a6 100644 --- a/cli/graph_container.rs +++ b/cli/graph_container.rs @@ -9,9 +9,9 @@ use deno_core::error::AnyError; use deno_core::parking_lot::RwLock; use deno_graph::ModuleGraph; use deno_runtime::colors; +use deno_runtime::deno_permissions::PermissionsContainer; use crate::args::CliOptions; -use crate::file_fetcher::FetchPermissionsOption; use crate::module_loader::ModuleLoadPreparer; use crate::util::fs::collect_specifiers; use crate::util::path::is_script_ext; @@ -45,12 +45,14 @@ pub struct MainModuleGraphContainer { inner: Arc<RwLock<Arc<ModuleGraph>>>, cli_options: Arc<CliOptions>, module_load_preparer: Arc<ModuleLoadPreparer>, + root_permissions: PermissionsContainer, } impl MainModuleGraphContainer { pub fn new( cli_options: Arc<CliOptions>, module_load_preparer: Arc<ModuleLoadPreparer>, + root_permissions: PermissionsContainer, ) -> Self { Self { update_queue: Default::default(), @@ -59,6 +61,7 @@ impl MainModuleGraphContainer { )))), cli_options, module_load_preparer, + root_permissions, } } @@ -76,7 +79,7 @@ impl MainModuleGraphContainer { specifiers, false, self.cli_options.ts_type_lib_window(), - FetchPermissionsOption::AllowAll, + self.root_permissions.clone(), ext_overwrite, ) .await?; diff --git a/cli/graph_util.rs b/cli/graph_util.rs index 1add83eb9..d8f7ae749 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -11,7 +11,6 @@ use crate::cache::ModuleInfoCache; use crate::cache::ParsedSourceCache; use crate::colors; use crate::errors::get_error_class_name; -use crate::file_fetcher::FetchPermissionsOption; use crate::file_fetcher::FileFetcher; use crate::npm::CliNpmResolver; use crate::resolver::CliGraphResolver; @@ -41,6 +40,7 @@ use deno_graph::ResolutionError; use deno_graph::SpecifierError; use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_node; +use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::fs_util::specifier_to_file_path; use deno_semver::jsr::JsrDepPackageReq; use deno_semver::package::PackageNv; @@ -249,6 +249,19 @@ impl ModuleGraphCreator { package_configs: &[JsrPackageConfig], build_fast_check_graph: bool, ) -> Result<ModuleGraph, AnyError> { + fn graph_has_external_remote(graph: &ModuleGraph) -> bool { + // Earlier on, we marked external non-JSR modules as external. + // If the graph contains any of those, it would cause type checking + // to crash, so since publishing is going to fail anyway, skip type + // checking. + graph.modules().any(|module| match module { + deno_graph::Module::External(external_module) => { + matches!(external_module.specifier.scheme(), "http" | "https") + } + _ => false, + }) + } + let mut roots = Vec::new(); for package_config in package_configs { roots.extend(package_config.config_file.resolve_export_value_urls()?); @@ -262,9 +275,12 @@ impl ModuleGraphCreator { }) .await?; self.graph_valid(&graph)?; - if self.options.type_check_mode().is_true() { + if self.options.type_check_mode().is_true() + && !graph_has_external_remote(&graph) + { self.type_check_graph(graph.clone()).await?; } + if build_fast_check_graph { let fast_check_workspace_members = package_configs .iter() @@ -279,6 +295,7 @@ impl ModuleGraphCreator { }, )?; } + Ok(graph) } @@ -370,6 +387,7 @@ pub struct ModuleGraphBuilder { maybe_file_watcher_reporter: Option<FileWatcherReporter>, file_fetcher: Arc<FileFetcher>, global_http_cache: Arc<GlobalHttpCache>, + root_permissions_container: PermissionsContainer, } impl ModuleGraphBuilder { @@ -386,6 +404,7 @@ impl ModuleGraphBuilder { maybe_file_watcher_reporter: Option<FileWatcherReporter>, file_fetcher: Arc<FileFetcher>, global_http_cache: Arc<GlobalHttpCache>, + root_permissions_container: PermissionsContainer, ) -> Self { Self { options, @@ -399,6 +418,7 @@ impl ModuleGraphBuilder { maybe_file_watcher_reporter, file_fetcher, global_http_cache, + root_permissions_container, } } @@ -670,20 +690,26 @@ impl ModuleGraphBuilder { /// Creates the default loader used for creating a graph. pub fn create_graph_loader(&self) -> cache::FetchCacher { - self.create_fetch_cacher(FetchPermissionsOption::AllowAll) + self.create_fetch_cacher(self.root_permissions_container.clone()) } pub fn create_fetch_cacher( &self, - permissions: FetchPermissionsOption, + permissions: PermissionsContainer, ) -> cache::FetchCacher { cache::FetchCacher::new( self.file_fetcher.clone(), - self.options.resolve_file_header_overrides(), self.global_http_cache.clone(), self.npm_resolver.clone(), self.module_info_cache.clone(), - permissions, + cache::FetchCacherOptions { + file_header_overrides: self.options.resolve_file_header_overrides(), + permissions, + is_deno_publish: matches!( + self.options.sub_command(), + crate::args::DenoSubcommand::Publish { .. } + ), + }, ) } diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 85daa4e28..aed4b47ef 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -3614,6 +3614,11 @@ impl Inner { }), // bit of a hack to force the lsp to cache the @types/node package type_check_mode: crate::args::TypeCheckMode::Local, + permissions: crate::args::PermissionFlags { + // allow remote import permissions in the lsp for now + allow_import: Some(vec![]), + ..Default::default() + }, ..Default::default() }), initial_cwd, diff --git a/cli/module_loader.rs b/cli/module_loader.rs index a81c5a0aa..f4e219bea 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -104,7 +104,7 @@ impl ModuleLoadPreparer { roots: &[ModuleSpecifier], is_dynamic: bool, lib: TsTypeLib, - permissions: crate::file_fetcher::FetchPermissionsOption, + permissions: PermissionsContainer, ext_overwrite: Option<&String>, ) -> Result<(), AnyError> { log::debug!("Preparing module load."); @@ -252,13 +252,15 @@ impl CliModuleLoaderFactory { &self, graph_container: TGraphContainer, lib: TsTypeLib, - root_permissions: PermissionsContainer, - dynamic_permissions: PermissionsContainer, + is_worker: bool, + parent_permissions: PermissionsContainer, + permissions: PermissionsContainer, ) -> ModuleLoaderAndSourceMapGetter { let loader = Rc::new(CliModuleLoader(Rc::new(CliModuleLoaderInner { lib, - root_permissions, - dynamic_permissions, + is_worker, + parent_permissions, + permissions, graph_container, emitter: self.shared.emitter.clone(), parsed_source_cache: self.shared.parsed_source_cache.clone(), @@ -274,20 +276,20 @@ impl ModuleLoaderFactory for CliModuleLoaderFactory { fn create_for_main( &self, root_permissions: PermissionsContainer, - dynamic_permissions: PermissionsContainer, ) -> ModuleLoaderAndSourceMapGetter { self.create_with_lib( (*self.shared.main_module_graph_container).clone(), self.shared.lib_window, + /* is worker */ false, + root_permissions.clone(), root_permissions, - dynamic_permissions, ) } fn create_for_worker( &self, - root_permissions: PermissionsContainer, - dynamic_permissions: PermissionsContainer, + parent_permissions: PermissionsContainer, + permissions: PermissionsContainer, ) -> ModuleLoaderAndSourceMapGetter { self.create_with_lib( // create a fresh module graph for the worker @@ -295,21 +297,21 @@ impl ModuleLoaderFactory for CliModuleLoaderFactory { self.shared.graph_kind, ))), self.shared.lib_worker, - root_permissions, - dynamic_permissions, + /* is worker */ true, + parent_permissions, + permissions, ) } } struct CliModuleLoaderInner<TGraphContainer: ModuleGraphContainer> { lib: TsTypeLib, + is_worker: bool, /// The initial set of permissions used to resolve the static imports in the /// worker. These are "allow all" for main worker, and parent thread /// permissions for Web Worker. - root_permissions: PermissionsContainer, - /// Permissions used to resolve dynamic imports, these get passed as - /// "root permissions" for Web Worker. - dynamic_permissions: PermissionsContainer, + parent_permissions: PermissionsContainer, + permissions: PermissionsContainer, shared: Arc<SharedCliModuleLoaderState>, emitter: Arc<Emitter>, parsed_source_cache: Arc<ParsedSourceCache>, @@ -769,11 +771,12 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader } } - let root_permissions = if is_dynamic { - inner.dynamic_permissions.clone() + let permissions = if is_dynamic { + inner.permissions.clone() } else { - inner.root_permissions.clone() + inner.parent_permissions.clone() }; + let is_dynamic = is_dynamic || inner.is_worker; // consider workers as dynamic for permissions let lib = inner.lib; let mut update_permit = graph_container.acquire_update_permit().await; let graph = update_permit.graph_mut(); @@ -783,7 +786,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader &[specifier], is_dynamic, lib, - root_permissions.into(), + permissions, None, ) .await?; diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index bab266734..c501d2d6b 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -130,8 +130,6 @@ struct SharedModuleLoaderState { #[derive(Clone)] struct EmbeddedModuleLoader { shared: Arc<SharedModuleLoaderState>, - root_permissions: PermissionsContainer, - dynamic_permissions: PermissionsContainer, } pub const MODULE_NOT_FOUND: &str = "Module not found"; @@ -402,28 +400,23 @@ struct StandaloneModuleLoaderFactory { impl ModuleLoaderFactory for StandaloneModuleLoaderFactory { fn create_for_main( &self, - root_permissions: PermissionsContainer, - dynamic_permissions: PermissionsContainer, + _root_permissions: PermissionsContainer, ) -> ModuleLoaderAndSourceMapGetter { ModuleLoaderAndSourceMapGetter { module_loader: Rc::new(EmbeddedModuleLoader { shared: self.shared.clone(), - root_permissions, - dynamic_permissions, }), } } fn create_for_worker( &self, - root_permissions: PermissionsContainer, - dynamic_permissions: PermissionsContainer, + _parent_permissions: PermissionsContainer, + _permissions: PermissionsContainer, ) -> ModuleLoaderAndSourceMapGetter { ModuleLoaderAndSourceMapGetter { module_loader: Rc::new(EmbeddedModuleLoader { shared: self.shared.clone(), - root_permissions, - dynamic_permissions, }), } } @@ -664,7 +657,8 @@ pub async fn run( }; let permissions = { - let mut permissions = metadata.permissions.to_options(); + let mut permissions = + metadata.permissions.to_options(/* cli_arg_urls */ &[]); // if running with an npm vfs, grant read access to it if let Some(vfs_root) = maybe_vfs_root { match &mut permissions.allow_read { @@ -713,6 +707,7 @@ pub async fn run( npm_resolver, permission_desc_parser, root_cert_store_provider, + permissions, StorageKeyResolver::empty(), crate::args::DenoSubcommand::Run(Default::default()), CliMainWorkerOptions { @@ -752,7 +747,7 @@ pub async fn run( deno_core::JsRuntime::init_platform(None, true); let mut worker = worker_factory - .create_main_worker(WorkerExecutionMode::Run, main_module, permissions) + .create_main_worker(WorkerExecutionMode::Run, main_module) .await?; let exit_code = worker.run().await?; diff --git a/cli/tools/check.rs b/cli/tools/check.rs index c22afbb9a..7edb392d4 100644 --- a/cli/tools/check.rs +++ b/cli/tools/check.rs @@ -51,6 +51,7 @@ pub async fn check( let specifiers_for_typecheck = if check_flags.doc || check_flags.doc_only { let file_fetcher = factory.file_fetcher()?; + let root_permissions = factory.root_permissions_container()?; let mut specifiers_for_typecheck = if check_flags.doc { specifiers.clone() @@ -59,7 +60,7 @@ pub async fn check( }; for s in specifiers { - let file = file_fetcher.fetch_bypass_permissions(&s).await?; + let file = file_fetcher.fetch(&s, root_permissions).await?; let snippet_files = extract::extract_snippet_files(file)?; for snippet_file in snippet_files { specifiers_for_typecheck.push(snippet_file.specifier.clone()); diff --git a/cli/tools/compile.rs b/cli/tools/compile.rs index b9620cfde..c1f98bc08 100644 --- a/cli/tools/compile.rs +++ b/cli/tools/compile.rs @@ -135,7 +135,7 @@ pub async fn compile( file, eszip, root_dir_url, - &module_specifier, + module_specifier, &compile_flags, cli_options, ) diff --git a/cli/tools/info.rs b/cli/tools/info.rs index d78b83cbe..174785631 100644 --- a/cli/tools/info.rs +++ b/cli/tools/info.rs @@ -644,8 +644,21 @@ impl<'a> GraphDisplayContext<'a> { ModuleError::InvalidTypeAssertion { .. } => { self.build_error_msg(specifier, "(invalid import attribute)") } - ModuleError::LoadingErr(_, _, _) => { - self.build_error_msg(specifier, "(loading error)") + ModuleError::LoadingErr(_, _, err) => { + use deno_graph::ModuleLoadError::*; + let message = match err { + HttpsChecksumIntegrity(_) => "(checksum integrity error)", + Decode(_) => "(loading decode error)", + Loader(err) => match deno_core::error::get_custom_error_class(err) { + Some("NotCapable") => "(not capable, requires --allow-import)", + _ => "(loading error)", + }, + Jsr(_) => "(loading error)", + NodeUnknownBuiltinModule(_) => "(unknown node built-in error)", + Npm(_) => "(npm loading error)", + TooManyRedirects => "(too many redirects error)", + }; + self.build_error_msg(specifier, message.as_ref()) } ModuleError::ParseErr(_, _) => { self.build_error_msg(specifier, "(parsing error)") diff --git a/cli/tools/registry/diagnostics.rs b/cli/tools/registry/diagnostics.rs index c53a39683..733a78dda 100644 --- a/cli/tools/registry/diagnostics.rs +++ b/cli/tools/registry/diagnostics.rs @@ -3,7 +3,6 @@ use std::borrow::Cow; use std::path::PathBuf; use std::sync::Arc; -use std::sync::Mutex; use deno_ast::diagnostics::Diagnostic; use deno_ast::diagnostics::DiagnosticLevel; @@ -21,6 +20,7 @@ use deno_ast::SourceRanged; use deno_ast::SourceTextInfo; use deno_core::anyhow::anyhow; use deno_core::error::AnyError; +use deno_core::parking_lot::Mutex; use deno_core::url::Url; use deno_graph::FastCheckDiagnostic; use deno_semver::Version; @@ -36,7 +36,7 @@ impl PublishDiagnosticsCollector { pub fn print_and_error(&self) -> Result<(), AnyError> { let mut errors = 0; let mut has_slow_types_errors = false; - let mut diagnostics = self.diagnostics.lock().unwrap().take(); + let mut diagnostics = self.diagnostics.lock().take(); diagnostics.sort_by_cached_key(|d| d.sorting_key()); @@ -75,8 +75,16 @@ impl PublishDiagnosticsCollector { } } + pub fn has_error(&self) -> bool { + self + .diagnostics + .lock() + .iter() + .any(|d| matches!(d.level(), DiagnosticLevel::Error)) + } + pub fn push(&self, diagnostic: PublishDiagnostic) { - self.diagnostics.lock().unwrap().push(diagnostic); + self.diagnostics.lock().push(diagnostic); } } diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs index fbdcd9e77..941514b04 100644 --- a/cli/tools/registry/mod.rs +++ b/cli/tools/registry/mod.rs @@ -341,13 +341,11 @@ impl PublishPreparer { bail!("Exiting due to DENO_INTERNAL_FAST_CHECK_OVERWRITE") } else { log::info!("Checking for slow types in the public API..."); - let mut any_pkg_had_diagnostics = false; for package in package_configs { let export_urls = package.config_file.resolve_export_value_urls()?; let diagnostics = collect_no_slow_type_diagnostics(&graph, &export_urls); if !diagnostics.is_empty() { - any_pkg_had_diagnostics = true; for diagnostic in diagnostics { diagnostics_collector .push(PublishDiagnostic::FastCheck(diagnostic)); @@ -355,7 +353,9 @@ impl PublishPreparer { } } - if any_pkg_had_diagnostics { + // skip type checking the slow type graph if there are any errors because + // errors like remote modules existing will cause type checking to crash + if diagnostics_collector.has_error() { Ok(Arc::new(graph)) } else { // fast check passed, type check the output as a temporary measure diff --git a/cli/tools/registry/pm/cache_deps.rs b/cli/tools/registry/pm/cache_deps.rs index a59817055..7d1773b34 100644 --- a/cli/tools/registry/pm/cache_deps.rs +++ b/cli/tools/registry/pm/cache_deps.rs @@ -16,6 +16,7 @@ pub async fn cache_top_level_deps( ) -> Result<(), AnyError> { let npm_resolver = factory.npm_resolver().await?; let cli_options = factory.cli_options()?; + let root_permissions = factory.root_permissions_container()?; if let Some(npm_resolver) = npm_resolver.as_managed() { if !npm_resolver.ensure_top_level_package_json_install().await? { if let Some(lockfile) = cli_options.maybe_lockfile() { @@ -106,7 +107,7 @@ pub async fn cache_top_level_deps( &roots, false, deno_config::deno_json::TsTypeLib::DenoWorker, - crate::file_fetcher::FetchPermissionsOption::AllowAll, + root_permissions.clone(), None, ) .await?; diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs index 24bc8e30a..a30304687 100644 --- a/cli/tools/repl/mod.rs +++ b/cli/tools/repl/mod.rs @@ -162,7 +162,7 @@ pub async fn run( let factory = CliFactory::from_flags(flags); let cli_options = factory.cli_options()?; let main_module = cli_options.resolve_main_module()?; - let permissions = factory.create_permissions_container()?; + let permissions = factory.root_permissions_container()?; let npm_resolver = factory.npm_resolver().await?.clone(); let resolver = factory.resolver().await?.clone(); let file_fetcher = factory.file_fetcher()?; @@ -177,7 +177,7 @@ pub async fn run( .create_custom_worker( WorkerExecutionMode::Repl, main_module.clone(), - permissions, + permissions.clone(), vec![crate::ops::testing::deno_test::init_ops(test_event_sender)], Default::default(), ) @@ -189,7 +189,7 @@ pub async fn run( npm_resolver, resolver, worker, - main_module, + main_module.clone(), test_event_receiver, ) .await?; diff --git a/cli/tools/run/mod.rs b/cli/tools/run/mod.rs index 200644490..152e2650b 100644 --- a/cli/tools/run/mod.rs +++ b/cli/tools/run/mod.rs @@ -60,10 +60,9 @@ pub async fn run_script( maybe_npm_install(&factory).await?; - let permissions = factory.create_permissions_container()?; let worker_factory = factory.create_cli_main_worker_factory().await?; let mut worker = worker_factory - .create_main_worker(mode, main_module, permissions) + .create_main_worker(mode, main_module.clone()) .await?; let exit_code = worker.run().await?; @@ -79,7 +78,6 @@ pub async fn run_from_stdin(flags: Arc<Flags>) -> Result<i32, AnyError> { let file_fetcher = factory.file_fetcher()?; let worker_factory = factory.create_cli_main_worker_factory().await?; - let permissions = factory.create_permissions_container()?; let mut source = Vec::new(); std::io::stdin().read_to_end(&mut source)?; // Save a fake file into file fetcher cache @@ -91,7 +89,7 @@ pub async fn run_from_stdin(flags: Arc<Flags>) -> Result<i32, AnyError> { }); let mut worker = worker_factory - .create_main_worker(WorkerExecutionMode::Run, main_module, permissions) + .create_main_worker(WorkerExecutionMode::Run, main_module.clone()) .await?; let exit_code = worker.run().await?; Ok(exit_code) @@ -125,11 +123,10 @@ async fn run_with_watch( let _ = watcher_communicator.watch_paths(cli_options.watch_paths()); - let permissions = factory.create_permissions_container()?; let mut worker = factory .create_cli_main_worker_factory() .await? - .create_main_worker(mode, main_module, permissions) + .create_main_worker(mode, main_module.clone()) .await?; if watch_flags.hmr { @@ -173,10 +170,9 @@ pub async fn eval_command( source: source_code.into_bytes().into(), }); - let permissions = factory.create_permissions_container()?; let worker_factory = factory.create_cli_main_worker_factory().await?; let mut worker = worker_factory - .create_main_worker(WorkerExecutionMode::Eval, main_module, permissions) + .create_main_worker(WorkerExecutionMode::Eval, main_module.clone()) .await?; let exit_code = worker.run().await?; Ok(exit_code) diff --git a/cli/tools/serve.rs b/cli/tools/serve.rs index 2f553cf1e..4ce1cad6f 100644 --- a/cli/tools/serve.rs +++ b/cli/tools/serve.rs @@ -5,7 +5,6 @@ use std::sync::Arc; use deno_core::error::AnyError; use deno_core::futures::TryFutureExt; use deno_core::ModuleSpecifier; -use deno_runtime::deno_permissions::PermissionsContainer; use super::run::check_permission_before_script; use super::run::maybe_npm_install; @@ -44,13 +43,11 @@ pub async fn serve( maybe_npm_install(&factory).await?; - let permissions = factory.create_permissions_container()?; let worker_factory = factory.create_cli_main_worker_factory().await?; do_serve( worker_factory, - main_module, - permissions, + main_module.clone(), serve_flags.worker_count, false, ) @@ -60,7 +57,6 @@ pub async fn serve( async fn do_serve( worker_factory: CliMainWorkerFactory, main_module: ModuleSpecifier, - permissions: PermissionsContainer, worker_count: Option<usize>, hmr: bool, ) -> Result<i32, AnyError> { @@ -71,7 +67,6 @@ async fn do_serve( worker_count, }, main_module.clone(), - permissions.clone(), ) .await?; let worker_count = match worker_count { @@ -87,15 +82,13 @@ async fn do_serve( for i in 0..extra_workers { let worker_factory = worker_factory.clone(); let main_module = main_module.clone(); - let permissions = permissions.clone(); let (tx, rx) = tokio::sync::oneshot::channel(); channels.push(rx); std::thread::Builder::new() .name(format!("serve-worker-{i}")) .spawn(move || { deno_runtime::tokio_util::create_and_run_current_thread(async move { - let result = - run_worker(i, worker_factory, main_module, permissions, hmr).await; + let result = run_worker(i, worker_factory, main_module, hmr).await; let _ = tx.send(result); }); })?; @@ -124,7 +117,6 @@ async fn run_worker( worker_count: usize, worker_factory: CliMainWorkerFactory, main_module: ModuleSpecifier, - permissions: PermissionsContainer, hmr: bool, ) -> Result<i32, AnyError> { let mut worker = worker_factory @@ -134,7 +126,6 @@ async fn run_worker( worker_count: Some(worker_count), }, main_module, - permissions, ) .await?; if hmr { @@ -171,11 +162,9 @@ async fn serve_with_watch( maybe_npm_install(&factory).await?; let _ = watcher_communicator.watch_paths(cli_options.watch_paths()); - - let permissions = factory.create_permissions_container()?; let worker_factory = factory.create_cli_main_worker_factory().await?; - do_serve(worker_factory, main_module, permissions, worker_count, hmr) + do_serve(worker_factory, main_module.clone(), worker_count, hmr) .await?; Ok(()) diff --git a/cli/worker.rs b/cli/worker.rs index 6176398d5..861419f1e 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -62,13 +62,12 @@ pub trait ModuleLoaderFactory: Send + Sync { fn create_for_main( &self, root_permissions: PermissionsContainer, - dynamic_permissions: PermissionsContainer, ) -> ModuleLoaderAndSourceMapGetter; fn create_for_worker( &self, - root_permissions: PermissionsContainer, - dynamic_permissions: PermissionsContainer, + parent_permissions: PermissionsContainer, + permissions: PermissionsContainer, ) -> ModuleLoaderAndSourceMapGetter; } @@ -136,6 +135,7 @@ struct SharedWorkerState { npm_resolver: Arc<dyn CliNpmResolver>, permission_desc_parser: Arc<RuntimePermissionDescriptorParser>, root_cert_store_provider: Arc<dyn RootCertStoreProvider>, + root_permissions: PermissionsContainer, shared_array_buffer_store: SharedArrayBufferStore, storage_key_resolver: StorageKeyResolver, options: CliMainWorkerOptions, @@ -432,6 +432,7 @@ impl CliMainWorkerFactory { npm_resolver: Arc<dyn CliNpmResolver>, permission_parser: Arc<RuntimePermissionDescriptorParser>, root_cert_store_provider: Arc<dyn RootCertStoreProvider>, + root_permissions: PermissionsContainer, storage_key_resolver: StorageKeyResolver, subcommand: DenoSubcommand, options: CliMainWorkerOptions, @@ -452,6 +453,7 @@ impl CliMainWorkerFactory { npm_resolver, permission_desc_parser: permission_parser, root_cert_store_provider, + root_permissions, shared_array_buffer_store: Default::default(), storage_key_resolver, options, @@ -464,13 +466,12 @@ impl CliMainWorkerFactory { &self, mode: WorkerExecutionMode, main_module: ModuleSpecifier, - permissions: PermissionsContainer, ) -> Result<CliMainWorker, AnyError> { self .create_custom_worker( mode, main_module, - permissions, + self.shared.root_permissions.clone(), vec![], Default::default(), ) @@ -530,13 +531,9 @@ impl CliMainWorkerFactory { (main_module, is_cjs) }; - let ModuleLoaderAndSourceMapGetter { module_loader } = - shared.module_loader_factory.create_for_main( - PermissionsContainer::allow_all( - self.shared.permission_desc_parser.clone(), - ), - permissions.clone(), - ); + let ModuleLoaderAndSourceMapGetter { module_loader } = shared + .module_loader_factory + .create_for_main(permissions.clone()); let maybe_inspector_server = shared.maybe_inspector_server.clone(); let create_web_worker_cb = |