Skip to content

Commit 80e36b5

Browse files
authored
fix(install): rewrite node_modules .bin shim generation for windows to be like npm (#31494)
Previously we were just generating a little cmd file that did `deno run npm:/..`..., but we were the odd one out in that most other package managers do .cmd, a sh script, and a powershell script. This ports the shim logic from https://github.com/npm/cmd-shim and uses that instead. Parts of the port were done by claude, mostly the string handling nonsense and the tests
1 parent 1317a36 commit 80e36b5

File tree

4 files changed

+646
-29
lines changed

4 files changed

+646
-29
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libs/npm_installer/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ deno_terminal.workspace = true
3535
deno_unsync.workspace = true
3636
dyn-clone.workspace = true
3737
futures.workspace = true
38+
lazy-regex.workspace = true
3839
log.workspace = true
3940
once_cell.workspace = true
4041
parking_lot.workspace = true

libs/npm_installer/bin_entries.rs

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// Copyright 2018-2025 the Deno authors. MIT license.
22

3+
mod windows_shim;
4+
35
use std::borrow::Cow;
46
use std::collections::HashMap;
57
use std::collections::HashSet;
@@ -378,14 +380,20 @@ pub trait SetupBinEntrySys:
378380
pub fn set_up_bin_entry<'a>(
379381
sys: &impl SetupBinEntrySys,
380382
package: &'a NpmResolutionPackage,
381-
#[allow(unused_variables)] extra: &'a NpmPackageExtraInfo,
383+
extra: &'a NpmPackageExtraInfo,
382384
bin_name: &'a str,
383-
#[allow(unused_variables)] bin_script: &str,
384-
#[allow(unused_variables)] package_path: &'a Path,
385+
bin_script: &str,
386+
package_path: &'a Path,
385387
bin_node_modules_dir_path: &Path,
386388
) -> Result<EntrySetupOutcome<'a>, BinEntriesError> {
387389
if sys_traits::impls::is_windows() {
388-
set_up_bin_shim(sys, package, bin_name, bin_node_modules_dir_path)?;
390+
windows_shim::set_up_bin_shim(
391+
sys,
392+
bin_name,
393+
bin_script,
394+
package_path,
395+
bin_node_modules_dir_path,
396+
)?;
389397
Ok(EntrySetupOutcome::Success)
390398
} else {
391399
symlink_bin_entry(
@@ -400,27 +408,6 @@ pub fn set_up_bin_entry<'a>(
400408
}
401409
}
402410

403-
fn set_up_bin_shim(
404-
sys: &impl FsWrite,
405-
package: &NpmResolutionPackage,
406-
bin_name: &str,
407-
bin_node_modules_dir_path: &Path,
408-
) -> Result<(), BinEntriesError> {
409-
let mut cmd_shim = bin_node_modules_dir_path.join(bin_name);
410-
411-
cmd_shim.set_extension("cmd");
412-
let shim = format!("@deno run -A npm:{}/{bin_name} %*", package.id.nv);
413-
sys
414-
.fs_write(&cmd_shim, shim)
415-
.map_err(|err| BinEntriesError::SetUpBin {
416-
name: bin_name.to_string(),
417-
path: cmd_shim.clone(),
418-
source: Box::new(err.into()),
419-
})?;
420-
421-
Ok(())
422-
}
423-
424411
/// Make the file at `path` executable if it exists.
425412
/// Returns `true` if the file exists, `false` otherwise.
426413
fn make_executable_if_exists(
@@ -479,6 +466,10 @@ impl EntrySetupOutcome<'_> {
479466
}
480467
}
481468

469+
fn relative_path(from: &Path, to: &Path) -> Option<PathBuf> {
470+
pathdiff::diff_paths(to, from)
471+
}
472+
482473
fn symlink_bin_entry<'a>(
483474
sys: &(impl FsOpen + FsSymlinkFile + FsRemoveFile + FsReadLink),
484475
package: &'a NpmResolutionPackage,
@@ -491,10 +482,6 @@ fn symlink_bin_entry<'a>(
491482
let link = bin_node_modules_dir_path.join(bin_name);
492483
let original = package_path.join(bin_script);
493484

494-
fn relative_path(from: &Path, to: &Path) -> Option<PathBuf> {
495-
pathdiff::diff_paths(to, from)
496-
}
497-
498485
let original_relative = relative_path(bin_node_modules_dir_path, &original)
499486
.map(Cow::Owned)
500487
.unwrap_or_else(|| Cow::Borrowed(&original));

0 commit comments

Comments
 (0)