pnpm / pnpm

Fast, disk space efficient package manager
https://pnpm.io
MIT License
29.98k stars 1.03k forks source link

"Cannot find module" in bin shims for dependencies of globally-installed packages #8704

Open karlhorky opened 1 month ago

karlhorky commented 1 month ago

Verify latest release

pnpm version

9.12.2

Which area(s) of pnpm are affected? (leave empty if unsure)

No response

Link to the code that reproduces this issue or a replay of the bug

No response

Reproduction steps

  1. Install a package globally which depends on another package (eg. pnpm add --global @upleveled/preflight)
  2. From the global installation location, run the ./node_modules/.bin/<bin name> for one of the dependencies (eg. /Users/k/Library/pnpm/global/5/node_modules/@upleveled/preflight/node_modules/.bin/tsx)
  3. 💥 Observe the "Cannot find module" error similar to that shown below
node:internal/modules/cjs/loader:1228
  throw err;
  ^

Error: Cannot find module '/Users/k/Library/pnpm/global/tsx@4.19.1/node_modules/tsx/dist/cli.mjs'
    at Module._resolveFilename (node:internal/modules/cjs/loader:1225:15)
    at Module._load (node:internal/modules/cjs/loader:1051:27)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:174:12)
    at node:internal/main/run_main_module:28:49 {
  code: 'MODULE_NOT_FOUND',
  requireStack: []
}

Node.js v20.18.0

Looking through pnpm/global, I can find this path, which looks like the correct path:

/Users/k/Library/pnpm/global/5/.pnpm/tsx@4.19.1/node_modules/tsx/dist/cli.mjs

Comparison of the incorrect and correct paths:

-/Users/k/Library/pnpm/global/tsx@4.19.1/node_modules/tsx/dist/cli.mjs
+/Users/k/Library/pnpm/global/5/.pnpm/tsx@4.19.1/node_modules/tsx/dist/cli.mjs

Alternative path (symlink):

/Users/k/Library/pnpm/global/5/.pnpm/node_modules/tsx/dist/cli.mjs

Describe the Bug

The shim created in <global module location>/node_modules/.bin/<dependency bin> contains incorrect paths

Expected Behavior

The shim created in <global module location>/node_modules/.bin/<dependency bin> contains only correct paths

Which Node.js version are you using?

v20.18.0

Which operating systems have you used?

If your OS is a Linux based, which one it is? (Include the version if relevant)

No response

Use Case

Executing the bin of a dependency of a global module

Alternatives Considered

Using pnpm exec tsx in the global dependency directory, which resolves correctly, but executes in the global dependency directory, which is not where the script should be executed. I don't see a way to pass to pnpm exec an alternative current working directory for where the script should be executed.

If there was a different pnpm command for getting the absolute paths of dependencies of a global dependency, then that would be an option.

Additional Details

Maybe this is related to the code in https://github.com/pnpm/cmd-shim ? (and if so, maybe I should copy this issue over there instead?)

Related Issues

Potentially related is the issue from @Js-Brecht from 2019:

karlhorky commented 1 month ago

Workaround

Extract the path from the pnpm shim and fix it before executing the script:

Before

#!/usr/bin/env bash

script_dir="$(cd "$(dirname "$0")" && pwd)"
node "$script_dir/../node_modules/bin" "$script_dir/../src/index.ts"

After

#!/usr/bin/env bash

script_dir="$(cd "$(dirname "$0")" && pwd)"
echo "script_dir: $script_dir"

# Workaround for incorrect path in pnpm shim
# https://github.com/pnpm/pnpm/issues/8704#issuecomment-2439618363
#
# TODO: Remove and switch back to "$script_dir/../node_modules/.bin/tsx"
# when the issue above is resolved
tsx_shim="$script_dir/../node_modules/.bin/tsx"
exec_command=$(awk '/^else$/{flag=1;next}/^fi$/{flag=0}flag' "$tsx_shim" | grep 'exec node')
cli_path=$(echo "$exec_command" | sed -E 's/^[[:space:]]*exec node[[:space:]]+"[^"]+(node_modules\/tsx\/[^"]+)".*/\1/')
cli_path="$(pnpm bin --global)/global/5/.pnpm/$cli_path"

node "$cli_path" "$script_dir/../src/index.ts"

Note: Uses alternative, simpler path to the tsx dist/cli.mjs file:

/Users/k/Library/pnpm/global/5/.pnpm/node_modules/tsx/dist/cli.mjs