1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-21 21:50:00 -05:00

feat(npm): support running non-bin scripts in npm pkgs via deno run (#19975)

Closes https://github.com/denoland/deno/issues/19967
This commit is contained in:
David Sherret 2023-08-01 18:47:57 -04:00 committed by GitHub
parent 51ceed860b
commit 00b5fe8ba3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 110 additions and 1 deletions

View file

@ -775,6 +775,20 @@ itest!(deno_run_bin_esm {
http_server: true,
});
itest!(deno_run_bin_esm_no_bin_entrypoint {
args: "run -A --quiet npm:@denotest/bin@0.6.0/cli.mjs this is a test",
output: "npm/deno_run_esm.out",
envs: env_vars_for_npm_tests(),
http_server: true,
});
itest!(deno_run_bin_cjs_no_bin_entrypoint {
args: "run -A --quiet npm:@denotest/bin@0.6.0/cli-cjs.js this is a test",
output: "npm/deno_run_cjs.out",
envs: env_vars_for_npm_tests(),
http_server: true,
});
itest!(deno_run_bin_special_chars {
args: "run -A --quiet npm:@denotest/special-chars-in-bin-name/\\foo\" this is a test",
output: "npm/deno_run_special_chars_in_bin_name.out",
@ -817,6 +831,22 @@ itest!(deno_run_non_existent {
exit_code: 1,
});
itest!(deno_run_no_bin_entrypoint {
args: "run -A --quiet npm:@denotest/esm-basic",
output: "npm/deno_run_no_bin_entrypoint.out",
envs: env_vars_for_npm_tests(),
http_server: true,
exit_code: 1,
});
itest!(deno_run_no_bin_entrypoint_non_existent_subpath {
args: "run -A --quiet npm:@denotest/esm-basic/non-existent.js",
output: "npm/deno_run_no_bin_entrypoint_non_existent_subpath.out",
envs: env_vars_for_npm_tests(),
http_server: true,
exit_code: 1,
});
itest!(directory_import_folder_index_js {
args: "run npm/directory_import/folder_index_js.ts",
output: "npm/directory_import/folder_index_js.out",

View file

@ -0,0 +1 @@
error: package '@denotest/esm-basic' did not have a bin property in its package.json

View file

@ -0,0 +1,3 @@
error: package '@denotest/esm-basic' did not have a bin property in its package.json
Fallback failed: Cannot find module 'file:///[WILDCARD]/non-existent.js'

View file

@ -0,0 +1,5 @@
const process = require("process");
for (const arg of process.argv.slice(2)) {
console.log(arg);
}

View file

@ -0,0 +1,5 @@
import process from "node:process";
for (const arg of process.argv.slice(2)) {
console.log(arg);
}

View file

@ -0,0 +1,4 @@
{
"name": "@deno/bin",
"version": "0.6.0"
}

View file

@ -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