summaryrefslogtreecommitdiff
path: root/cli/npm/managed/resolvers/local/bin_entries.rs
blob: 8e43cf98b6ac72b80c78787352e8aa1b21e87153 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
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(())
}