Skip to content

BUG: In npm v9, packages are always installed globallyΒ #6

@sounisi5011

Description

@sounisi5011

As reported in go-task/task#1081, local installation with npm v9 or later always installs binaries in the global directory.

This is because @go-task/go-npm always uses the environment variable npm_config_prefix to get the installation directory instead when the npm bin command fails.
In npm v9 and later, the npm bin command has been removed.

go-npm/src/common.js

Lines 25 to 49 in b3015da

// `npm bin` will output the path where binary files should be installed
exec('npm bin', (err, stdout, stderr) => {
let dir = null;
if (err || stderr || !stdout || stdout.length === 0) {
// We couldn't infer path from `npm bin`. Let's try to get it from
// Environment variables set by NPM when it runs.
// npm_config_prefix points to NPM's installation directory where `bin` folder is available
// Ex: /Users/foo/.nvm/versions/node/v4.3.0
const env = process.env;
// Get the package manager who is running the script
// This is needed since PNPM works in a different way than NPM or YARN.
const packageManager = usedPM();
if (env && env.npm_config_prefix) {
if (process.platform === 'win32') {
// On Windows, use the installation directory itself instead of the `bin` folder.
// See: https://docs.npmjs.com/cli/v6/configuring-npm/folders#executables
dir = env.npm_config_prefix;
} else {
dir = join(env.npm_config_prefix, 'bin');
}
} else if (env && env.npm_config_local_prefix) {

@go-task/go-npm should only use the npm_config_prefix environment variable when in global mode.
In the case of npm and pnpm, global mode can be detected by reading the npm_config_global environment variable.

process.env.npm_config_global === 'true'

However, in the case of Yarn V1, the npm_config_global environment variable does not exist, so the npm_config_argv environment variable should be parsed to determine this instead. I am still investigating the correct logic, but it is still buggy 1.

Note
This needs to be released as a breaking change.
When @go-task/go-npm is upgraded, the user's environment will no longer remove binaries that were previously installed by mistake.

For example, the following version range is specified in @go-task/cli:

"@go-task/go-npm": "^0.1.17"

--- https://github.com/go-task/task/blob/e0d3e33c32cfa0c99afd95b74086852983603a51/package.json#L32

In this case, when @go-task/go-npm is updated to a version such as 0.1.19, the @go-task/cli dependency is implicitly updated. Since postuninstall is not run at this time, task binaries that were incorrectly installed by @go-task/go-npm before this bug was fixed will not be removed.

Therefore, it is necessary to upgrade to a version out of this range (e.g. 0.3.0, 1.0.0).

Footnotes

  1. Currently it compares the order of the global and add arguments in the original property. However, from reading the Yarn v1 source code, it should probably be enough to check that the value of the cooked property is not ["add"]. But I haven't been able to test it with the old Yarn yet. ↩

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions