diff --git a/dist/index.js b/dist/index.js index 350b6b2..0ef9ee8 100644 Binary files a/dist/index.js and b/dist/index.js differ diff --git a/src/install-pnpm/ensureAliasLinks.test.ts b/src/install-pnpm/ensureAliasLinks.test.ts index 4d74684..7e23b62 100644 --- a/src/install-pnpm/ensureAliasLinks.test.ts +++ b/src/install-pnpm/ensureAliasLinks.test.ts @@ -121,25 +121,27 @@ describe('ensureAliasLinks', () => { }) }) - describe('does not overwrite existing links', () => { - it('preserves existing symlinks on unix', async () => { + describe('overwrites existing broken links', () => { + it('replaces existing file with symlink on unix', async () => { await setupStandaloneFixture(binDir) - await writeFile(path.join(binDir, 'pn'), 'existing content') + // Simulate npm's broken shim (points to .tools/ placeholder) + await writeFile(path.join(binDir, 'pn'), '#!/bin/sh\nexec broken\n') await ensureAliasLinks(binDir, true, 'linux') - const content = await readFile(path.join(binDir, 'pn'), 'utf8') - expect(content).toBe('existing content') + // Should be replaced with a symlink to the real target + const target = await readlink(path.join(binDir, 'pn')) + expect(target).toBe(path.join('..', '@pnpm', 'exe', 'pn')) }) - it('preserves existing .cmd shims on windows', async () => { + it('replaces existing .cmd shims on windows', async () => { await setupStandaloneFixture(binDir) - await writeFile(path.join(binDir, 'pn.cmd'), 'existing shim') + await writeFile(path.join(binDir, 'pn.cmd'), 'broken shim') await ensureAliasLinks(binDir, true, 'win32') const content = await readFile(path.join(binDir, 'pn.cmd'), 'utf8') - expect(content).toBe('existing shim') + expect(content).toContain(path.join('..', '@pnpm', 'exe', 'pn')) }) }) }) diff --git a/src/install-pnpm/ensureAliasLinks.ts b/src/install-pnpm/ensureAliasLinks.ts index 097d07a..004f5e0 100644 --- a/src/install-pnpm/ensureAliasLinks.ts +++ b/src/install-pnpm/ensureAliasLinks.ts @@ -1,4 +1,4 @@ -import { writeFile, symlink } from 'fs/promises' +import { unlink, writeFile, symlink } from 'fs/promises' import { existsSync } from 'fs' import path from 'path' @@ -30,10 +30,18 @@ function pwshShim (target: string): string { return `#!/usr/bin/env pwsh\n& "$PSScriptRoot\\${target}" @args\n` } +async function forceSymlink (target: string, linkPath: string): Promise { + try { await unlink(linkPath) } catch {} + await symlink(target, linkPath) +} + /** * Create pn/pnpx/pnx alias links in the bin directory. * On Unix, creates symlinks. On Windows, creates .cmd and .ps1 shims. * Only creates links when the target file actually exists (pnpm v11+). + * + * Existing links are always replaced because npm may have created shims + * pointing to an isolated .tools/ copy that has stale placeholder files. */ export async function ensureAliasLinks (binDir: string, standalone: boolean, platform: NodeJS.Platform = process.platform): Promise { const aliases = getAliases(standalone) @@ -44,19 +52,10 @@ export async function ensureAliasLinks (binDir: string, standalone: boolean, pla if (!existsSync(resolvedTarget)) continue if (isWindows) { - const cmdPath = path.join(binDir, `${name}.cmd`) - if (!existsSync(cmdPath)) { - await writeFile(cmdPath, cmdShim(target)) - } - const ps1Path = path.join(binDir, `${name}.ps1`) - if (!existsSync(ps1Path)) { - await writeFile(ps1Path, pwshShim(target)) - } + await writeFile(path.join(binDir, `${name}.cmd`), cmdShim(target)) + await writeFile(path.join(binDir, `${name}.ps1`), pwshShim(target)) } else { - const link = path.join(binDir, name) - if (!existsSync(link)) { - await symlink(target, link) - } + await forceSymlink(target, path.join(binDir, name)) } } }