diff --git a/dist/index.js b/dist/index.js index 30f73dd..7b76f04 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 f4b7d9a..d74fb10 100644 --- a/src/install-pnpm/ensureAliasLinks.test.ts +++ b/src/install-pnpm/ensureAliasLinks.test.ts @@ -125,6 +125,31 @@ describe('ensureAliasLinks', () => { }) }) + describe('self-update bin directory (pnpm shim in same dir)', () => { + it('creates aliases using pnpm shim in the same directory on unix', async () => { + // self-update creates a pnpm shim in $PNPM_HOME/bin/ — no package dir + await writeFile(path.join(binDir, 'pnpm'), '#!/bin/sh\nexec /path/to/real/pnpm "$@"\n', { mode: 0o755 }) + + await ensureAliasLinks(binDir, true, 'linux') + + const pnTarget = await readlink(path.join(binDir, 'pn')) + expect(pnTarget).toBe('pnpm') + + const pnxContent = await readFile(path.join(binDir, 'pnx'), 'utf8') + expect(pnxContent).toContain('pnpm') + expect(pnxContent).toContain('dlx') + }) + + it('creates .cmd shims using pnpm in same dir on windows', async () => { + await writeFile(path.join(binDir, 'pnpm'), 'pnpm binary') + + await ensureAliasLinks(binDir, true, 'win32') + + const cmdContent = await readFile(path.join(binDir, 'pn.cmd'), 'utf8') + expect(cmdContent).toContain('pnpm') + }) + }) + describe('overwrites existing broken shims', () => { it('replaces npm broken shim with symlink on unix', async () => { await setupStandaloneFixture(binDir) diff --git a/src/install-pnpm/ensureAliasLinks.ts b/src/install-pnpm/ensureAliasLinks.ts index 8221e3f..c636eb2 100644 --- a/src/install-pnpm/ensureAliasLinks.ts +++ b/src/install-pnpm/ensureAliasLinks.ts @@ -24,6 +24,28 @@ async function forceWriteFile (filePath: string, content: string, mode?: number) await writeFile(filePath, content, { mode }) } +/** + * Find the pnpm binary/shim relative to binDir. + * Checks the package directory first (node_modules/.bin/../@pnpm/exe/pnpm), + * then falls back to a pnpm shim in binDir itself (e.g. self-update's bin/). + */ +function findPnpmTarget (binDir: string, standalone: boolean): string | undefined { + const packageTarget = standalone + ? path.join('..', '@pnpm', 'exe', 'pnpm') + : path.join('..', 'pnpm', 'bin', 'pnpm.cjs') + + if (existsSync(path.resolve(binDir, packageTarget))) { + return packageTarget + } + + // self-update creates a pnpm shim in $PNPM_HOME/bin/ — use it directly + if (existsSync(path.join(binDir, 'pnpm'))) { + return 'pnpm' + } + + return undefined +} + /** * Create pn/pnpx/pnx alias links in the bin directory. * @@ -34,20 +56,12 @@ async function forceWriteFile (filePath: string, content: string, mode?: number) * pnpm self-update only replaces the pnpm binary — it doesn't update other * files in the package. The aliases are created by pointing pn directly to * the pnpm binary, and pnpx/pnx as scripts that exec "pnpm dlx". - * - * Only creates links when the pnpm binary exists in the expected location - * (i.e. the package has been installed). This is always true after bootstrap. */ export async function ensureAliasLinks (binDir: string, standalone: boolean, platform: NodeJS.Platform = process.platform): Promise { const isWindows = platform === 'win32' - // Determine the pnpm binary path relative to binDir - const pnpmTarget = standalone - ? path.join('..', '@pnpm', 'exe', 'pnpm') - : path.join('..', 'pnpm', 'bin', 'pnpm.cjs') - - const resolvedPnpm = path.resolve(binDir, pnpmTarget) - if (!existsSync(resolvedPnpm)) return + const pnpmTarget = findPnpmTarget(binDir, standalone) + if (!pnpmTarget) return if (isWindows) { // pn → calls pnpm directly