diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2023-08-01 18:47:57 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-01 18:47:57 -0400 |
commit | 00b5fe8ba31240f6e6abf668ebda8ed0c40fb524 (patch) | |
tree | 466a5940ef057e616a63a3d6936f0d31cd48d2b4 /cli/worker.rs | |
parent | 51ceed860b6f562c70fb09ae1c7173a9eb14473c (diff) |
feat(npm): support running non-bin scripts in npm pkgs via `deno run` (#19975)
Closes https://github.com/denoland/deno/issues/19967
Diffstat (limited to 'cli/worker.rs')
-rw-r--r-- | cli/worker.rs | 63 |
1 files changed, 62 insertions, 1 deletions
diff --git a/cli/worker.rs b/cli/worker.rs index 1516c58da..235e9a225 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -5,6 +5,7 @@ use std::rc::Rc; use std::sync::Arc; use deno_ast::ModuleSpecifier; +use deno_core::anyhow::bail; use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::futures::task::LocalFutureObj; @@ -24,6 +25,7 @@ use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel; use deno_runtime::deno_fs; use deno_runtime::deno_node; use deno_runtime::deno_node::NodeResolution; +use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_tls::RootCertStoreProvider; use deno_runtime::deno_web::BlobStore; @@ -370,7 +372,7 @@ impl CliMainWorkerFactory { .add_package_reqs(&[package_ref.req.clone()]) .await?; let node_resolution = - shared.node_resolver.resolve_binary_export(&package_ref)?; + self.resolve_binary_entrypoint(&package_ref, &permissions)?; let is_main_cjs = matches!(node_resolution, NodeResolution::CommonJs(_)); if let Some(lockfile) = &shared.maybe_lockfile { @@ -492,6 +494,65 @@ impl CliMainWorkerFactory { shared: shared.clone(), }) } + + fn resolve_binary_entrypoint( + &self, + package_ref: &NpmPackageReqReference, + permissions: &PermissionsContainer, + ) -> Result<NodeResolution, AnyError> { + match self.shared.node_resolver.resolve_binary_export(package_ref) { + Ok(node_resolution) => Ok(node_resolution), + Err(original_err) => { + // if the binary entrypoint was not found, fallback to regular node resolution + let result = + self.resolve_binary_entrypoint_fallback(package_ref, permissions); + match result { + Ok(Some(resolution)) => Ok(resolution), + Ok(None) => Err(original_err), + Err(fallback_err) => { + bail!("{:#}\n\nFallback failed: {:#}", original_err, fallback_err) + } + } + } + } + } + + /// resolve the binary entrypoint using regular node resolution + fn resolve_binary_entrypoint_fallback( + &self, + package_ref: &NpmPackageReqReference, + permissions: &PermissionsContainer, + ) -> Result<Option<NodeResolution>, AnyError> { + // only fallback if the user specified a sub path + if package_ref.sub_path.is_none() { + // it's confusing to users if the package doesn't have any binary + // entrypoint and we just execute the main script which will likely + // have blank output, so do not resolve the entrypoint in this case + return Ok(None); + } + + let Some(resolution) = self.shared.node_resolver.resolve_npm_req_reference( + package_ref, + NodeResolutionMode::Execution, + permissions, + )? else { + return Ok(None); + }; + match &resolution { + NodeResolution::BuiltIn(_) => Ok(None), + NodeResolution::CommonJs(specifier) | NodeResolution::Esm(specifier) => { + if specifier + .to_file_path() + .map(|p| p.exists()) + .unwrap_or(false) + { + Ok(Some(resolution)) + } else { + bail!("Cannot find module '{}'", specifier) + } + } + } + } } // TODO(bartlomieju): this callback could have default value |