mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-28 20:11:48 -04:00
This PR overhauls `pnpm env` use to route through pnpm's own install machinery instead of maintaining a parallel code path with manual symlink/shim/hardlink logic. ``` pnpm env use -g <version> ``` now runs: ``` pnpm add --global node@runtime:<version> ``` via `@pnpm/exec.pnpm-cli-runner`. All manual symlink, hardlink, and cmd-shim code in `envUse.ts` is gone (~1000 lines removed across the package). ### Changes **npm and npx shims on all platforms** Added `getNodeBinsForCurrentOS(platform)` to `@pnpm/constants`, returning a `Record<string, string>` with the correct relative paths for `node`, `npm`, and `npx` inside a Node.js distribution. `BinaryResolution.bin` is widened from `string` to `string | Record<string, string>` in `@pnpm/resolver-base` and `@pnpm/lockfile.types`, so the node resolver can set all three entries and pnpm's bin-linker creates shims for each automatically. **Windows npm/npx fix** `addFilesFromDir` was skipping root-level `node_modules/` (to avoid storing a package's own dependencies), which stripped the bundled `npm` from Node.js Windows zip archives. Added an `includeNodeModules` option and enabled it from the binary fetcher so Windows distributions keep their full contents. **Removed subcommands** `pnpm env add` and `pnpm env remove` are removed. `pnpm env use` handles both installing and activating a version. `pnpm env list` now always lists remote versions (the `--remote` flag is no longer required, though it is kept for backwards compatibility). **musl support** On Alpine Linux and other musl-based systems, the musl variant of Node.js is automatically downloaded from [unofficial-builds.nodejs.org](https://unofficial-builds.nodejs.org).
87 lines
2.5 KiB
TypeScript
87 lines
2.5 KiB
TypeScript
import { jest } from '@jest/globals'
|
|
import { PnpmError } from '@pnpm/error'
|
|
|
|
const mockRunPnpmCli = jest.fn()
|
|
jest.unstable_mockModule('@pnpm/exec.pnpm-cli-runner', () => ({
|
|
runPnpmCli: mockRunPnpmCli,
|
|
}))
|
|
|
|
const { env } = await import('@pnpm/plugin-commands-env')
|
|
|
|
beforeEach(() => {
|
|
mockRunPnpmCli.mockClear()
|
|
})
|
|
|
|
test('env use calls pnpm add with the correct arguments', async () => {
|
|
await env.handler({
|
|
bin: '/usr/local/bin',
|
|
cacheDir: '/tmp/cache',
|
|
global: true,
|
|
pnpmHomeDir: '/tmp/pnpm-home',
|
|
rawConfig: {},
|
|
storeDir: '/tmp/store',
|
|
}, ['use', '18'])
|
|
|
|
expect(mockRunPnpmCli).toHaveBeenCalledWith(
|
|
['add', '--global', 'node@runtime:18', '--global-bin-dir', '/usr/local/bin', '--store-dir', '/tmp/store', '--cache-dir', '/tmp/cache'],
|
|
{ cwd: '/tmp/pnpm-home' }
|
|
)
|
|
})
|
|
|
|
test('env use passes lts specifier through unchanged', async () => {
|
|
await env.handler({
|
|
bin: '/usr/local/bin',
|
|
global: true,
|
|
pnpmHomeDir: '/tmp/pnpm-home',
|
|
rawConfig: {},
|
|
storeDir: '/tmp/store',
|
|
}, ['use', 'lts'])
|
|
|
|
expect(mockRunPnpmCli).toHaveBeenCalledWith(
|
|
['add', '--global', 'node@runtime:lts', '--global-bin-dir', '/usr/local/bin', '--store-dir', '/tmp/store'],
|
|
{ cwd: '/tmp/pnpm-home' }
|
|
)
|
|
})
|
|
|
|
test('env use passes codename specifier through unchanged', async () => {
|
|
await env.handler({
|
|
bin: '/usr/local/bin',
|
|
global: true,
|
|
pnpmHomeDir: '/tmp/pnpm-home',
|
|
rawConfig: {},
|
|
storeDir: '/tmp/store',
|
|
}, ['use', 'argon'])
|
|
|
|
expect(mockRunPnpmCli).toHaveBeenCalledWith(
|
|
['add', '--global', 'node@runtime:argon', '--global-bin-dir', '/usr/local/bin', '--store-dir', '/tmp/store'],
|
|
{ cwd: '/tmp/pnpm-home' }
|
|
)
|
|
})
|
|
|
|
test('fail if not run with --global', async () => {
|
|
await expect(
|
|
env.handler({
|
|
bin: '/usr/local/bin',
|
|
global: false,
|
|
pnpmHomeDir: '/tmp/pnpm-home',
|
|
rawConfig: {},
|
|
}, ['use', '18'])
|
|
).rejects.toEqual(new PnpmError('NOT_IMPLEMENTED_YET', '"pnpm env use <version>" can only be used with the "--global" option currently'))
|
|
|
|
expect(mockRunPnpmCli).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('fail if there is no global bin directory', async () => {
|
|
await expect(
|
|
env.handler({
|
|
// @ts-expect-error
|
|
bin: undefined,
|
|
global: true,
|
|
pnpmHomeDir: '/tmp/pnpm-home',
|
|
rawConfig: {},
|
|
}, ['use', 'lts'])
|
|
).rejects.toEqual(new PnpmError('CANNOT_MANAGE_NODE', 'Unable to manage Node.js because pnpm was not installed using the standalone installation script'))
|
|
|
|
expect(mockRunPnpmCli).not.toHaveBeenCalled()
|
|
})
|