diff options
| author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2024-05-24 00:43:38 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-05-23 23:43:38 +0000 |
| commit | 92a8d09e498712aec2ba0e54a1ad85194ebd83af (patch) | |
| tree | d26e89bb46850665b2ed357a11718d8148850880 /cli/npm/managed/resolvers/local | |
| parent | 959739f609dddacde3bbe9ecede2f409214fb34c (diff) | |
fix(npm): set up node_modules/.bin/ entries for package that provide bin entrypoints (#23496)
Closes https://github.com/denoland/deno/issues/23036
---------
Co-authored-by: Nathan Whitaker <nathan@deno.com>
Diffstat (limited to 'cli/npm/managed/resolvers/local')
| -rw-r--r-- | cli/npm/managed/resolvers/local/bin_entries.rs | 109 |
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(()) +} |
