mirror of
https://github.com/pnpm/pnpm.git
synced 2026-01-08 15:08:27 -05:00
fix: remove .exe files from the target .bin directory (#5450)
This commit is contained in:
8
.changeset/tough-dingos-divide.md
Normal file
8
.changeset/tough-dingos-divide.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
"@pnpm/link-bins": patch
|
||||
"pnpm": patch
|
||||
---
|
||||
|
||||
When linking commands to a directory, remove any .exe files that are already present in that target directory by the same name.
|
||||
|
||||
This fixes an issue with pnpm global update on Windows. If pnpm was installed with the standalone script and then updated with pnpm using `pnpm add --global pnpm`, the exe file initially created by the standalone script should be removed.
|
||||
@@ -39,6 +39,7 @@
|
||||
"@pnpm/read-project-manifest": "workspace:*",
|
||||
"@pnpm/types": "workspace:*",
|
||||
"@zkochan/cmd-shim": "^5.3.1",
|
||||
"@zkochan/rimraf": "^2.1.2",
|
||||
"bin-links": "^3.0.3",
|
||||
"is-subdir": "^1.2.0",
|
||||
"is-windows": "^1.0.2",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { promises as fs } from 'fs'
|
||||
import { promises as fs, existsSync } from 'fs'
|
||||
import path from 'path'
|
||||
import PnpmError from '@pnpm/error'
|
||||
import logger, { globalWarn } from '@pnpm/logger'
|
||||
@@ -9,6 +9,7 @@ import { fromDir as readPackageJsonFromDir } from '@pnpm/read-package-json'
|
||||
import { safeReadProjectManifestOnly } from '@pnpm/read-project-manifest'
|
||||
import { DependencyManifest, ProjectManifest } from '@pnpm/types'
|
||||
import cmdShim from '@zkochan/cmd-shim'
|
||||
import rimraf from '@zkochan/rimraf'
|
||||
import isSubdir from 'is-subdir'
|
||||
import isWindows from 'is-windows'
|
||||
import normalizePath from 'normalize-path'
|
||||
@@ -200,6 +201,13 @@ export interface LinkBinOptions {
|
||||
|
||||
async function linkBin (cmd: CommandInfo, binsDir: string, opts?: LinkBinOptions) {
|
||||
const externalBinPath = path.join(binsDir, cmd.name)
|
||||
if (IS_WINDOWS) {
|
||||
const exePath = path.join(binsDir, `${cmd.name}${getExeExtension()}`)
|
||||
if (existsSync(exePath)) {
|
||||
globalWarn(`The target bin directory already contains an exe called ${cmd.name}, so removing ${exePath}`)
|
||||
await rimraf(exePath)
|
||||
}
|
||||
}
|
||||
|
||||
if (opts?.preferSymlinkedExecutables && !IS_WINDOWS && cmd.nodeExecPath == null) {
|
||||
await symlinkDir(cmd.path, externalBinPath)
|
||||
@@ -226,6 +234,18 @@ async function linkBin (cmd: CommandInfo, binsDir: string, opts?: LinkBinOptions
|
||||
}
|
||||
}
|
||||
|
||||
function getExeExtension (): string {
|
||||
let cmdExtension
|
||||
|
||||
if (process.env.PATHEXT) {
|
||||
cmdExtension = process.env.PATHEXT
|
||||
.split(path.delimiter)
|
||||
.find(ext => ext.toUpperCase() === '.EXE')
|
||||
}
|
||||
|
||||
return cmdExtension ?? '.exe'
|
||||
}
|
||||
|
||||
async function safeReadPkgJson (pkgDir: string): Promise<DependencyManifest | null> {
|
||||
try {
|
||||
return await readPackageJsonFromDir(pkgDir) as DependencyManifest
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/// <reference path="../../../typings/index.d.ts"/>
|
||||
import { promises as fs } from 'fs'
|
||||
import { promises as fs, writeFileSync } from 'fs'
|
||||
import path from 'path'
|
||||
import logger, { globalWarn } from '@pnpm/logger'
|
||||
import linkBins, {
|
||||
@@ -36,6 +36,8 @@ const POWER_SHELL_IS_SUPPORTED = isWindows()
|
||||
const IS_WINDOWS = isWindows()
|
||||
const EXECUTABLE_SHEBANG_SUPPORTED = !IS_WINDOWS
|
||||
|
||||
const testOnWindows = IS_WINDOWS ? test : test.skip
|
||||
|
||||
function getExpectedBins (bins: string[]) {
|
||||
const expectedBins = [...bins]
|
||||
if (POWER_SHELL_IS_SUPPORTED) {
|
||||
@@ -371,3 +373,14 @@ test("linkBins() emits global warning when bin points to path that doesn't exist
|
||||
globalWarn
|
||||
).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
testOnWindows('linkBins() shoud remove an existing .exe file from the target directory', async () => {
|
||||
const binTarget = tempy.directory()
|
||||
writeFileSync(path.join(binTarget, 'simple.exe'), '', 'utf8')
|
||||
const warn = jest.fn()
|
||||
const simpleFixture = f.prepare('simple-fixture')
|
||||
|
||||
await linkBins(path.join(simpleFixture, 'node_modules'), binTarget, { warn })
|
||||
|
||||
expect(await fs.readdir(binTarget)).toEqual(getExpectedBins(['simple']))
|
||||
})
|
||||
|
||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -1853,6 +1853,9 @@ importers:
|
||||
'@zkochan/cmd-shim':
|
||||
specifier: ^5.3.1
|
||||
version: 5.3.1
|
||||
'@zkochan/rimraf':
|
||||
specifier: ^2.1.2
|
||||
version: 2.1.2
|
||||
bin-links:
|
||||
specifier: ^3.0.3
|
||||
version: 3.0.3
|
||||
|
||||
Reference in New Issue
Block a user