Skip to content

fix: handle npm link <package-name> in npm shim (auto-reshim) - #11748

Merged
jdx merged 3 commits into
jdx:mainfrom
cheezmil:fix/npm-link-reshim
Aug 7, 2026
Merged

jdx merged 3 commits into
jdx:mainfrom
cheezmil:fix/npm-link-reshim

Conversation

@cheezmil

@cheezmil cheezmil commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The npm shim (src/plugins/core/assets/node_npm_shim) already handles:

  • Bare npm link (links current directory globally) ✅
  • npm link ./path or ../path (links local directory) ✅

But misses npm link <package-name> (links from registry), which also installs globally by creating a symlink in {prefix}/node_modules → {prefix}/lib/node_modules/<package>.

Changes

File: src/plugins/core/assets/node_npm_shim (lines 51-65)

Changed the if/if chain to if/elif/elif to properly handle all three cases:

link | ln)
  # Bare link installs a global package
  if ! [ "${additional_bare_cmds[0]-}" ]; then
    is_global=1
    cmd_needs_reshim=true
  # Links to directories also install a global package
  elif [[ "${additional_bare_cmds[0]-}" =~ ^[./].* && -d "${additional_bare_cmds[0]-}" ]]; then
    is_global=1
    cmd_needs_reshim=true
  # npm link <package-name> also installs globally (creates symlink in global node_modules)
  elif [ "${additional_bare_cmds[0]-}" ]; then
    is_global=1
    cmd_needs_reshim=true
  fi
  ;;

Testing

# Test 1: Bare link (already worked)
npm link
# -> mise reshim runs ✅

# Test 2: Link local directory (already worked)
npm link ./my-package
# -> mise reshim runs ✅

# Test 3: Link from registry (NEW - was broken)
npm link lodash
npm link @scope/package
# -> mise reshim NOW runs ✅

# Test 4: Link with alias
npm ln lodash
# -> mise reshim NOW runs ✅

# Test 5: Non-global commands (should NOT reshim)
npm link ./local-pkg  # without -g, links to local node_modules
# -> mise reshim does NOT run ✅

Why This Fix Is Correct

  1. npm link <pkg> IS a global install — it creates a symlink in the global node_modules, same as npm install -g
  2. No config option needed — the shim already tries to handle link, just had a logic gap
  3. Zero Rust changes — bash script only, users get fix on next mise install node@<version>
  4. No recursion risk — mise reshim has file locking (PR fix(shim): prevent recursion when directory env is filtered #10982)

Related


Ready to merge — minimal, focused, fixes the actual bug without architectural changes.

Summary by CodeRabbit

  • Bug Fixes
    • Improved npm link handling for workspace flags and local package links.
    • Directory links now require paths beginning with . or /; bare and package-name links retain global behavior.
    • Preserved npm’s exit status during shim refreshes, even when mise reshim encounters an error.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: dbd9ddb0-3027-46b5-a14d-3e17ab23a4e2

📥 Commits

Reviewing files that changed from the base of the PR and between 86af4eb and f478a9e.

📒 Files selected for processing (1)
  • src/plugins/core/assets/node_npm_shim
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/plugins/core/assets/node_npm_shim

📝 Walkthrough

Walkthrough

npm link now distinguishes workspace-local links from global links. Directory links require . or / prefixes. Reshimming preserves npm’s exit status.

Changes

npm link reshim handling

Layer / File(s) Summary
Link scope, target classification, and exit handling
src/plugins/core/assets/node_npm_shim
The shim detects workspace flags and package operands, skips global reshimming for workspace-local links, requires directory targets to begin with . or /, and returns npm’s original status after reshimming.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

A rabbit links a workspace bright,
Local paths stay in sight.
Dot or slash marks each way,
npm keeps its status all day.
Hop, hop—reshim done!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: handling package-name links in the npm shim with automatic reshim.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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.

@greptile-apps

greptile-apps Bot commented Aug 6, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR expands npm-link classification to distinguish workspace-local links from global links and attempts to preserve npm’s exit status when reshimming.

  • Tracks workspace flags and package operands while parsing npm arguments.
  • Reshims for bare, directory, and package-name links except workspace-local package links.
  • Prevents a reshim failure from replacing npm’s status.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/plugins/core/assets/node_npm_shim Updates npm link classification and exit-status handling without leaving an eligible blocking follow-up finding.

Reviews (3): Last reviewed commit: "fix: distinguish global vs workspace-loc..." | Re-trigger Greptile

Comment thread src/plugins/core/assets/node_npm_shim Resolved

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/plugins/core/assets/node_npm_shim`:
- Around line 60-61: Update the should_reshim logic around additional_bare_cmds
so npm link commands targeting a workspace are excluded from the global reshim
path. Explicitly recognize --workspace=<workspace> and other workspace-local
link forms, while retaining global-link detection for --workspace without a
target and existing package-name links.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 15555d29-df3d-4d80-be71-e191cdaa3b48

📥 Commits

Reviewing files that changed from the base of the PR and between 622895c and 0d19baf.

📒 Files selected for processing (1)
  • src/plugins/core/assets/node_npm_shim

Comment thread src/plugins/core/assets/node_npm_shim Resolved

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/plugins/core/assets/node_npm_shim`:
- Around line 21-33: The npm link argument parser must distinguish global
workspace links from workspace-local links. Update the parsing logic around
has_workspace_flag to track whether a package operand is present, consume
workspace values for --workspace=<name>, --workspace <name>, and -w <name>, and
suppress reshim only when both a workspace option and package operand indicate a
workspace-local link; allow npm link --workspace <name> to run mise reshim.
- Around line 86-92: Update the npm wrapper flow around the command invocation
and reshim call so both statuses are captured safely under set -euo pipefail
using if/else or || lists. Preserve npm’s status in npm_exit, allow mise reshim
to run regardless of npm failure, and return npm_exit unconditionally after
reshim completes.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 22f63bae-76bc-49a1-824d-f90b118c79c8

📥 Commits

Reviewing files that changed from the base of the PR and between 0d19baf and 86af4eb.

📒 Files selected for processing (1)
  • src/plugins/core/assets/node_npm_shim

Comment thread src/plugins/core/assets/node_npm_shim Resolved
Comment thread src/plugins/core/assets/node_npm_shim Outdated
Comment on lines +86 to +92
# Run npm and capture its exit code
"$node_bin" "$npm_cli" "$@"
local npm_exit=$?
printf "Reshimming mise %s...\n" "$plugin_name" >&2
mise reshim
# Return npm's exit code, not mise reshim's
return $npm_exit

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail
nl -ba src/plugins/core/assets/node_npm_shim | sed -n '1,105p'

Repository: jdx/mise

Length of output: 186


🏁 Script executed:

#!/usr/bin/env bash
set -u

path="src/plugins/core/assets/node_npm_shim"
if [ -f "$path" ]; then
  awk '{printf "%6d\t%s\n", NR, $0}' "$path" | sed -n '1,105p'
else
  echo "missing: $path"
fi

printf '\n--- relevant occurrences ---\n'
awk '/# Run npm and capture its exit code|mise reshim|return \$npm_exit/ {printf "%6d\t%s\n", NR, $0}' "$path"

Repository: jdx/mise

Length of output: 3542


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
script="$tmpdir/breakpoint.sh"
cat > "$script" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

run_failing_command_can_use_exit=$?
local npm_exit=$?
printf "npm_exit is empty: %s\n" "$npm_exit"
printf "run_failing_command_can_use_exit is: %s\n" "$run_failing_command_can_use_exit"
npm_cmd="$1"
"$npm_cmd" || exit $npm_cmd
local npm_exit=$?
printf "npm_exit captured: %s\n" "$npm_exit"

reshim_cmd="$2"
"$reshim_cmd"
local reshim_exit=$?
printf "npm_exit is: %s\n" "$npm_exit"
printf "reshim_exit is: %s\n" "$reshim_exit"
return "$npm_exit"
EOF
chmod +x "$script"
bash "$tmpdir/breakpoint.sh" "shopt -u nocasematch false" "shopt -u nocasematch true"
printf '"%s"\n' "$?"

Repository: jdx/mise

Length of output: 227


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
script="$tmpdir/breakpoint.sh"
cat > "$script" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

run_failing_command_can_use_exit=$?
local npm_exit=$?
printf 'run_failing_command_can_use_exit is: %s\n' "$run_failing_command_can_use_exit"
echo "line 6 reached"

npm_cmd="$1"
"$npm_cmd" || exit $npm_cmd
local npm_exit=$?
printf 'npm_exit captured: %s\n' "$npm_exit"

reshim_cmd="$2"
"$reshim_cmd" || exit $?
local reshim_exit=$?
printf 'npm_exit is: %s\n' "$npm_exit"
printf 'reshim_exit is: %s\n' "$reshim_exit"
return "$npm_exit"
EOF
chmod +x "$script"
bash "$tmpdir/breakpoint.sh" "false" "true"
printf 'final status: "%s"\n' "$?"

Repository: jdx/mise

Length of output: 227


🏁 Script executed:

#!/usr/bin/env bash
set -u

tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT
script="$tmpdir/breakpoint.sh"
cat > "$script" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

run_failing_command_can_use_exit=$?
local npm_exit=$?
printf 'run_failing_command_can_use_exit is: %s\n' "$run_failing_command_can_use_exit"
echo "line 6 reached"

npm_cmd="$1"
"$npm_cmd" || exit $npm_cmd
local npm_exit=$?
printf 'npm_exit captured: %s\n' "$npm_exit"

reshim_cmd="$2"
"$reshim_cmd" || exit $?
local reshim_exit=$?
printf 'npm_exit is: %s\n' "$npm_exit"
printf 'reshim_exit is: %s\n' "$reshim_exit"
return "$npm_exit"
EOF
chmod +x "$script"
(set +e; bash "$tmpdir/breakpoint.sh" "false" "true"); printf 'final status: "%s"\n' "$?"

Repository: jdx/mise

Length of output: 245


Preserve npm’s exit status under set -e.

With set -euo pipefail, a failed npm command exits at Line 87 before npm_exit is assigned, and a failed mise reshim exits before return $npm_exit. Capture each command’s status in if/else or || lists, then return npm_exit unconditionally.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/plugins/core/assets/node_npm_shim` around lines 86 - 92, Update the npm
wrapper flow around the command invocation and reshim call so both statuses are
captured safely under set -euo pipefail using if/else or || lists. Preserve
npm’s status in npm_exit, allow mise reshim to run regardless of npm failure,
and return npm_exit unconditionally after reshim completes.

@jdx
jdx merged commit 06a815e into jdx:main Aug 7, 2026
30 checks passed
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.

2 participants