Skip to content

fix(install): rewrite node_modules .bin shim generation for windows to be like npm - #31494

Merged
nathanwhit merged 5 commits into
denoland:mainfrom
nathanwhit:windows-bin-shim
Dec 9, 2025
Merged

nathanwhit merged 5 commits into
denoland:mainfrom
nathanwhit:windows-bin-shim

Conversation

@nathanwhit

Copy link
Copy Markdown
Member

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

@coderabbitai

coderabbitai Bot commented Dec 3, 2025 •

Copy link
Copy Markdown

Walkthrough

Adds lazy-regex to the workspace. Refactors npm bin-entry setup: extracts Windows shim logic into a new windows_shim module and replaces the inline shim implementation. set_up_bin_entry signature is simplified (removed #[allow(unused_variables)] attrs) and delegates Windows handling to windows_shim::set_up_bin_shim. Introduces relative_path(from,to) helper and updates symlink handling to use it. Implements shebang parsing, ShimData, and generators for cmd, sh, and pwsh shims; adds set_up_bin_shim and extensive unit tests for shim generation and parsing.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
βœ… Passed checks (2 passed)
Check name Status Explanation
Title check βœ… Passed The title clearly describes the main change: rewriting Windows .bin shim generation to match npm's approach.
Description check βœ… Passed The description is directly related to the changeset, explaining the rationale for replacing simple .cmd shims with multi-format shims (cmd, sh, pwsh) based on npm's cmd-shim.
✨ Finishing touches
  • πŸ“ Generate docstrings
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
libs/npm_installer/bin_entries.rs (1)

430-436: Consider renaming this macro to avoid shadowing std::writeln!.

The macro works correctly since writing to a String is infallible, but shadowing the standard library's writeln! can be confusing for future maintainers. A name like writeln_unchecked! or a brief comment explaining the rationale would help.

πŸ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 49fa02d and 56fd947.

β›” Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
πŸ“’ Files selected for processing (2)
  • libs/npm_installer/Cargo.toml (1 hunks)
  • libs/npm_installer/bin_entries.rs (5 hunks)
🧰 Additional context used
πŸ““ Path-based instructions (1)
**/*.rs

πŸ“„ CodeRabbit inference engine (CLAUDE.md)

**/*.rs: For debugging Rust code, set breakpoints in IDE debuggers (VS Code with rust-analyzer, IntelliJ IDEA) or use lldb directly
Use eprintln!() or dbg!() macros for debug prints in Rust code

Files:

  • libs/npm_installer/bin_entries.rs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: test debug macos-x86_64
  • GitHub Check: test release linux-x86_64
  • GitHub Check: test release windows-x86_64
  • GitHub Check: test debug macos-aarch64
  • GitHub Check: test debug windows-x86_64
  • GitHub Check: build libs
  • GitHub Check: lint debug linux-x86_64
  • GitHub Check: test debug linux-x86_64
  • GitHub Check: lint debug windows-x86_64
  • GitHub Check: test debug linux-aarch64
  • GitHub Check: lint debug macos-x86_64
πŸ”‡ Additional comments (8)
libs/npm_installer/Cargo.toml (1)

38-38: LGTM!

The lazy-regex dependency is appropriate for the shebang parsing logic in bin_entries.rs. It provides compile-time regex validation which is preferable to runtime regex compilation.

libs/npm_installer/bin_entries.rs (7)

3-20: LGTM!

Proper attribution and license inclusion for the ported npm/cmd-shim code.


446-467: LGTM!

The shebang parsing regex is correctly ported from npm/cmd-shim and the implementation handles the various shebang formats (env-based, direct path, with args/vars) properly. Good test coverage validates the behavior.


469-477: LGTM!

Efficient first-line-only reading for shebang detection. Treating all errors as "no shebang" is appropriate since a missing or unreadable file will be handled elsewhere.


787-1086: LGTM!

Comprehensive test coverage for shebang parsing and all three shim formats. The trim_indent helpers make assertions readable. Good job covering edge cases like env vars and arguments.


716-718: LGTM!

Simple wrapper around pathdiff::diff_paths.


408-416: Verify platform-specific parameter usage in set_up_bin_shim.

The concern about removing #[allow(unused_variables)] requires confirming whether the package and extra parameters are actually unused when set_up_bin_shim is called on Windows. If these parameters remain unused after the refactoring, compiler warnings would occur without the attribute.


571-575: Double spaces in exec statements are intentional npm/cmd-shim behavior and functionally harmless.

Lines 572 and 574 may produce output like exec NODE_ENV=prod "$basedir/node" "--experimental" ... when vars is non-empty. These double spaces come from how npm's cmd-shim template joins optional arguments and environment variables. POSIX shells treat multiple consecutive spaces as a single separator, so this spacing has no functional impact and is expected cross-platform compatibility behavior.

Comment thread libs/npm_installer/bin_entries.rs Resolved
@nathanwhit
nathanwhit enabled auto-merge (squash) December 5, 2025 18:41
@dsherret
dsherret disabled auto-merge December 5, 2025 19:18
Comment thread libs/npm_installer/bin_entries.rs Resolved
Comment thread libs/npm_installer/bin_entries.rs Resolved
Comment thread libs/npm_installer/bin_entries.rs Outdated
})
}

fn resolve_shebang(sys: &impl FsOpen, path: &Path) -> Option<Shebang> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nathanwhit nathanwhit Dec 6, 2025 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels a little weird for deno_npm_installer to depend on deno_task_shell but maybe fine

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm I didn't think about it much. Maybe this could be feature flagged in that crate? I'm actually not sure it's worth consolidating btw. It might be though.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I'm not sure it's worth it. I think I'd prefer to land this as is to unblock dx, and then maybe consolidate

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
libs/npm_installer/bin_entries/windows_shim.rs (1)

307-308: Handle None from relative_path instead of panicking.

The unwrap() can panic if pathdiff::diff_paths returns None (e.g., different drive letters on Windows). This was flagged in a previous review.

🧹 Nitpick comments (2)
libs/npm_installer/bin_entries/windows_shim.rs (1)

233-244: Inconsistent indentation in generated PowerShell for the None branch.

The None branch produces a script where the if ($MyInvocation.ExpectingInput) block is indented 2 extra spaces relative to the surrounding code, while the Some branch starts $ret=0 at the base level. This creates inconsistent output between the two cases.

       match &shebang_data {
         None => {
-          builder.append("  if ($MyInvocation.ExpectingInput) {\n");
-          builder.append("    $input | & \"$basedir/");
+          builder.append("if ($MyInvocation.ExpectingInput) {\n");
+          builder.append("  $input | & \"$basedir/");
           builder.append(&self.target);
-          builder.append("\"   $args\n");
-          builder.append("  } else {\n");
-          builder.append("    & \"$basedir/");
+          builder.append("\" $args\n");
+          builder.append("} else {\n");
+          builder.append("  & \"$basedir/");
           builder.append(&self.target);
-          builder.append("\"   $args\n");
-          builder.append("  }\n");
+          builder.append("\" $args\n");
+          builder.append("}\n");
           builder.append("exit $LASTEXITCODE\n");

Note: This would require updating the corresponding test expectations as well.

libs/npm_installer/bin_entries.rs (1)

389-397: Redundant return after ? operator.

set_up_bin_shim already returns Result<EntrySetupOutcome, BinEntriesError>. Using ? extracts the outcome but then you discard it and return Ok(EntrySetupOutcome::Success) again.

   if sys_traits::impls::is_windows() {
-    windows_shim::set_up_bin_shim(
+    return windows_shim::set_up_bin_shim(
       sys,
       bin_name,
       bin_script,
       package_path,
       bin_node_modules_dir_path,
-    )?;
-    Ok(EntrySetupOutcome::Success)
+    );
   } else {
πŸ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 56fd947 and d0be0fa.

πŸ“’ Files selected for processing (2)
  • libs/npm_installer/bin_entries.rs (3 hunks)
  • libs/npm_installer/bin_entries/windows_shim.rs (1 hunks)
🧰 Additional context used
πŸ““ Path-based instructions (1)
**/*.rs

πŸ“„ CodeRabbit inference engine (CLAUDE.md)

**/*.rs: For debugging Rust code, set breakpoints in IDE debuggers (VS Code with rust-analyzer, IntelliJ IDEA) or use lldb directly
Use eprintln!() or dbg!() macros for debug prints in Rust code

Files:

  • libs/npm_installer/bin_entries/windows_shim.rs
  • libs/npm_installer/bin_entries.rs

βš™οΈ CodeRabbit configuration file

Don't worry about coverage of Rust docstrings. Don't be nitpicky about it. Leave it to the author's judgement if such a documentation is necessary.

Files:

  • libs/npm_installer/bin_entries/windows_shim.rs
  • libs/npm_installer/bin_entries.rs
🧬 Code graph analysis (2)
libs/npm_installer/bin_entries/windows_shim.rs (1)
libs/npm_installer/bin_entries.rs (1)
  • relative_path (469-471)
libs/npm_installer/bin_entries.rs (2)
libs/npm_installer/bin_entries/windows_shim.rs (1)
  • set_up_bin_shim (297-327)
libs/npm_installer/local.rs (1)
  • relative_path (1352-1354)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: test debug linux-x86_64
  • GitHub Check: test debug macos-aarch64
  • GitHub Check: test debug macos-x86_64
  • GitHub Check: test debug windows-x86_64
  • GitHub Check: lint debug windows-x86_64
  • GitHub Check: build libs
  • GitHub Check: lint debug linux-x86_64
  • GitHub Check: lint debug macos-x86_64
πŸ”‡ Additional comments (5)
libs/npm_installer/bin_entries/windows_shim.rs (3)

41-62: Shebang parsing looks correct.

The regex pattern handles the common shebang formats well (direct paths, /usr/bin/env, and -S flag with environment variables). Good use of lazy_regex for compile-once semantics.


64-72: LGTM.

Silently returning None on file open/read errors is appropriate here since shebang is optional and the shim will work without it.


329-628: Good test coverage.

Tests comprehensively cover shebang parsing edge cases and all three shim formats. The trim_indent helpers make expected values readable.

libs/npm_installer/bin_entries.rs (2)

3-4: Good modular separation.

Moving the Windows shim logic to a separate module cleanly separates the ISC-licensed code from npm/cmd-shim.


469-471: Extract relative_path helper to a shared utility module.

This function duplicates an identical definition in libs/npm_installer/local.rs. Consolidating to a shared utility would eliminate duplication and improve maintainability.

@nathanwhit
nathanwhit merged commit 80e36b5 into denoland:main Dec 9, 2025
20 checks passed
@nathanwhit
nathanwhit deleted the windows-bin-shim branch December 9, 2025 18:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants