summaryrefslogtreecommitdiff
path: root/cli/npm/managed/resolvers/local/bin_entries.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/npm/managed/resolvers/local/bin_entries.rs')
-rw-r--r--cli/npm/managed/resolvers/local/bin_entries.rs109
1 files changed, 109 insertions, 0 deletions
diff --git a/cli/npm/managed/resolvers/local/bin_entries.rs b/cli/npm/managed/resolvers/local/bin_entries.rs
new file mode 100644
index 000000000..8e43cf98b
--- /dev/null
+++ b/cli/npm/managed/resolvers/local/bin_entries.rs
@@ -0,0 +1,109 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+use crate::npm::managed::NpmResolutionPackage;
+use deno_core::anyhow::Context;
+use deno_core::error::AnyError;
+use std::path::Path;
+
+pub(super) fn set_up_bin_entry(
+ package: &NpmResolutionPackage,
+ bin_name: &str,
+ #[allow(unused_variables)] bin_script: &str,
+ #[allow(unused_variables)] package_path: &Path,
+ bin_node_modules_dir_path: &Path,
+) -> Result<(), AnyError> {
+ #[cfg(windows)]
+ {
+ set_up_bin_shim(package, bin_name, bin_node_modules_dir_path)?;
+ }
+ #[cfg(unix)]
+ {
+ symlink_bin_entry(
+ package,
+ bin_name,
+ bin_script,
+ package_path,
+ bin_node_modules_dir_path,
+ )?;
+ }
+ Ok(())
+}
+
+#[cfg(windows)]
+fn set_up_bin_shim(
+ package: &NpmResolutionPackage,
+ bin_name: &str,
+ bin_node_modules_dir_path: &Path,
+) -> Result<(), AnyError> {
+ use std::fs;
+ let mut cmd_shim = bin_node_modules_dir_path.join(bin_name);
+
+ cmd_shim.set_extension("cmd");
+ let shim = format!("@deno run -A npm:{}/{bin_name} %*", package.id.nv);
+ if cmd_shim.exists() {
+ if let Ok(contents) = fs::read_to_string(cmd_shim) {
+ if contents == shim {
+ // up to date
+ return Ok(());
+ }
+ }
+ return Ok(());
+ }
+ fs::write(&cmd_shim, shim).with_context(|| {
+ format!("Can't set up '{}' bin at {}", bin_name, cmd_shim.display())
+ })?;
+
+ Ok(())
+}
+
+#[cfg(unix)]
+fn symlink_bin_entry(
+ _package: &NpmResolutionPackage,
+ bin_name: &str,
+ bin_script: &str,
+ package_path: &Path,
+ bin_node_modules_dir_path: &Path,
+) -> Result<(), AnyError> {
+ use std::os::unix::fs::symlink;
+ let link = bin_node_modules_dir_path.join(bin_name);
+ let original = package_path.join(bin_script);
+
+ // Don't bother setting up another link if it already exists
+ if link.exists() {
+ let resolved = std::fs::read_link(&link).ok();
+ if let Some(resolved) = resolved {
+ if resolved != original {
+ log::warn!(
+ "{} Trying to set up '{}' bin for \"{}\", but an entry pointing to \"{}\" already exists. Skipping...",
+ deno_terminal::colors::yellow("Warning"),
+ bin_name,
+ resolved.display(),
+ original.display()
+ );
+ }
+ return Ok(());
+ }
+ }
+
+ use std::os::unix::fs::PermissionsExt;
+ let mut perms = std::fs::metadata(&original).unwrap().permissions();
+ if perms.mode() & 0o111 == 0 {
+ // if the original file is not executable, make it executable
+ perms.set_mode(perms.mode() | 0o111);
+ std::fs::set_permissions(&original, perms).with_context(|| {
+ format!("Setting permissions on '{}'", original.display())
+ })?;
+ }
+ let original_relative =
+ crate::util::path::relative_path(bin_node_modules_dir_path, &original)
+ .unwrap_or(original);
+ symlink(&original_relative, &link).with_context(|| {
+ format!(
+ "Can't set up '{}' bin at {}",
+ bin_name,
+ original_relative.display()
+ )
+ })?;
+
+ Ok(())
+}