refactor: rename @pnpm/exe platform packages to @pnpm/exe.<platform>-<arch>[-musl] (#11316)

* refactor: rename @pnpm/exe platform packages to @pnpm/exe.<platform>-<arch>[-musl]

Aligns pnpm's own published platform artifacts with the one naming
convention the rest of the codebase already uses (`process.platform`
values plus an explicit `-musl` libc suffix), matching what `pnpm
pack-app`, `pnpm add --os/--cpu/--libc`, `supportedArchitectures.os`,
and Node.js tarball names all already settled on.

Package renames:
- @pnpm/linux-x64          -> @pnpm/exe.linux-x64
- @pnpm/linux-arm64        -> @pnpm/exe.linux-arm64
- @pnpm/linuxstatic-x64    -> @pnpm/exe.linux-x64-musl   (new dir)
- @pnpm/linuxstatic-arm64  -> @pnpm/exe.linux-arm64-musl
- @pnpm/macos-x64          -> @pnpm/exe.darwin-x64
- @pnpm/macos-arm64        -> @pnpm/exe.darwin-arm64
- @pnpm/win-x64            -> @pnpm/exe.win32-x64
- @pnpm/win-arm64          -> @pnpm/exe.win32-arm64

GitHub release asset names follow suit (`pnpm-linuxstatic-x64.tar.gz`
-> `pnpm-linux-x64-musl.tar.gz`, `pnpm-macos-*` -> `pnpm-darwin-*`,
`pnpm-win-*` -> `pnpm-win32-*`). Internal artifact directories under
`pnpm/artifacts/` renamed to match, which drops the awkward mixed
naming between target and directory.

The umbrella package `@pnpm/exe` keeps its name so that `pnpm
self-update` from v10 and any `npm i -g @pnpm/exe` scripts continue to
resolve. Platform children can be renamed freely because npm/pnpm
filter optional deps by each child's `os`/`cpu`/`libc` manifest
fields, not by package names.

Also updates:
- `@pnpm/exe`'s `setup.js` (preinstall) and the self-updater's
  `linkExePlatformBinary` to look up the platform package by the new
  scheme, using `detect-libc` to append `-musl` on musl Linux hosts.
- `.meta-updater` optional-dependency list for @pnpm/exe.
- `copy-artifacts.ts` target list and Windows detection prefix.
- cspell wordlist (drops `linuxstatic`; it's no longer used anywhere).

Final transition publishes of the old package names (pointing at the
new ones so direct pins keep resolving) are a release-engineering step
handled separately.

Refs #11314.

* chore: keep "linuxstatic" in cspell wordlist for changeset references

* test(pack-app rename): cover the musl branch of platform-package-name lookup

Copilot flagged that the musl -> -musl suffix logic in setup.js's preinstall
and self-updater's linkExePlatformBinary had no regression coverage. Extract
the name-computation from both into small pure helpers and unit-test all
four matrix cases (linux+musl, linux+glibc, darwin, win32) plus the
win32 ia32->x86 arch normalization:

- pnpm/artifacts/exe/platform-pkg-name.js exposes `exePlatformPkgName`
  (returns `@pnpm/exe.<platform>-<arch>[-musl]`). setup.js imports it
  instead of inlining the logic; the new setup.test.ts block covers the
  four-case matrix without having to mock detect-libc or patch
  process.platform.
- engine/pm/commands/src/self-updater/installPnpm.ts exports a new
  `exePlatformPkgDirName` returning `exe.<platform>-<arch>[-musl]` (the
  scope-local dir). linkExePlatformBinary calls it; the new
  selfUpdate.test.ts block covers the same matrix.

Both helpers are deliberately pure so the non-musl CI host can still
exercise the musl code path.
This commit is contained in:
Zoltan Kochan
2026-04-20 15:42:04 +02:00
committed by GitHub
parent 72c1e050e9
commit 5a293d250c
39 changed files with 322 additions and 117 deletions

View File

@@ -0,0 +1,21 @@
---
"@pnpm/exe": major
"pnpm": minor
---
Renamed the platform-specific optional dependencies of `@pnpm/exe` to the new `@pnpm/exe.<platform>-<arch>[-<libc>]` scheme, using `process.platform` values (`linux`, `darwin`, `win32`) for the OS segment. The umbrella package `@pnpm/exe` itself is unchanged so existing `npm i -g @pnpm/exe` and `pnpm self-update` flows keep working.
| before | after |
| --- | --- |
| `@pnpm/linux-x64` | `@pnpm/exe.linux-x64` |
| `@pnpm/linux-arm64` | `@pnpm/exe.linux-arm64` |
| `@pnpm/linuxstatic-x64` | `@pnpm/exe.linux-x64-musl` |
| `@pnpm/linuxstatic-arm64` | `@pnpm/exe.linux-arm64-musl` |
| `@pnpm/macos-x64` | `@pnpm/exe.darwin-x64` |
| `@pnpm/macos-arm64` | `@pnpm/exe.darwin-arm64` |
| `@pnpm/win-x64` | `@pnpm/exe.win32-x64` |
| `@pnpm/win-arm64` | `@pnpm/exe.win32-arm64` |
GitHub release asset filenames follow the same scheme — `pnpm-linuxstatic-x64.tar.gz` becomes `pnpm-linux-x64-musl.tar.gz`, `pnpm-macos-*` becomes `pnpm-darwin-*`, `pnpm-win-*` becomes `pnpm-win32-*`. Anyone downloading releases directly needs to use the new filenames; `get.pnpm.io/install.sh` and `install.ps1` will be updated in lockstep to accept both schemes based on the requested version.
Resolves [#11314](https://github.com/pnpm/pnpm/issues/11314).

View File

@@ -119,7 +119,16 @@ export default async (workspaceDir: string) => { // eslint-disable-line
if (dir.includes('artifacts') || manifest.name === '@pnpm/exe') {
manifest.version = pnpmVersion
if (manifest.name === '@pnpm/exe') {
for (const depName of ['@pnpm/linux-arm64', '@pnpm/linux-x64', '@pnpm/win-x64', '@pnpm/win-arm64', '@pnpm/macos-x64', '@pnpm/macos-arm64']) {
for (const depName of [
'@pnpm/exe.darwin-arm64',
'@pnpm/exe.darwin-x64',
'@pnpm/exe.linux-arm64',
'@pnpm/exe.linux-arm64-musl',
'@pnpm/exe.linux-x64',
'@pnpm/exe.linux-x64-musl',
'@pnpm/exe.win32-arm64',
'@pnpm/exe.win32-x64',
]) {
manifest.optionalDependencies![depName] = 'workspace:*'
}
}

View File

@@ -21,13 +21,13 @@ const pnpmDistDir = path.join(repoRoot, 'pnpm/dist')
})
}
await createArtifactTarball('linux-x64', 'pnpm')
await createArtifactTarball('linuxstatic-x64', 'pnpm')
await createArtifactTarball('linuxstatic-arm64', 'pnpm')
await createArtifactTarball('linux-x64-musl', 'pnpm')
await createArtifactTarball('linux-arm64', 'pnpm')
await createArtifactTarball('macos-x64', 'pnpm')
await createArtifactTarball('macos-arm64', 'pnpm')
await createArtifactTarball('win-x64', 'pnpm.exe')
await createArtifactTarball('win-arm64', 'pnpm.exe')
await createArtifactTarball('linux-arm64-musl', 'pnpm')
await createArtifactTarball('darwin-x64', 'pnpm')
await createArtifactTarball('darwin-arm64', 'pnpm')
await createArtifactTarball('win32-x64', 'pnpm.exe')
await createArtifactTarball('win32-arm64', 'pnpm.exe')
await createSourceMapsArchive()
})().catch((err) => {
console.error(err)
@@ -54,7 +54,7 @@ async function createArtifactTarball (target: string, binaryName: string): Promi
fs.rmSync(path.join(distDest, mapFile))
}
const isWindows = target.startsWith('win-')
const isWindows = target.startsWith('win32-')
const archiveName = isWindows ? `pnpm-${target}.zip` : `pnpm-${target}.tar.gz`
if (isWindows) {
@@ -91,15 +91,17 @@ async function createSourceMapsArchive () {
}
// Reflink platform package names needed for a build target.
// Target format: 'linux-x64', 'linuxstatic-arm64', 'macos-arm64', 'win-x64'.
// Target format: '<platform>-<arch>[-<libc>]' where <platform> is one of
// 'linux', 'darwin', 'win32', <arch> is 'x64' | 'arm64', and <libc> is only
// ever 'musl' (linux-only).
function getReflinkKeepPackages (target: string): string[] {
if (target.startsWith('macos-')) {
return [`@reflink/reflink-darwin-${target.slice('macos-'.length)}`]
if (target.startsWith('darwin-')) {
return [`@reflink/reflink-${target}`]
}
if (target.startsWith('win-')) {
return [`@reflink/reflink-win32-${target.slice('win-'.length)}-msvc`]
if (target.startsWith('win32-')) {
return [`@reflink/reflink-${target}-msvc`]
}
if (target.startsWith('linux')) {
if (target.startsWith('linux-')) {
const arch = target.includes('arm64') ? 'arm64' : 'x64'
return [
`@reflink/reflink-linux-${arch}-gnu`,

View File

@@ -52,6 +52,7 @@
"@pnpm/types": "workspace:*",
"@pnpm/workspace.project-manifest-reader": "workspace:*",
"cross-spawn": "catalog:",
"detect-libc": "catalog:",
"path-name": "catalog:",
"ramda": "catalog:",
"render-help": "catalog:",

View File

@@ -1,4 +1,4 @@
export { selfUpdate } from './self-updater/index.js'
export { installPnpm, installPnpmToStore, linkExePlatformBinary } from './self-updater/installPnpm.js'
export { exePlatformPkgDirName, installPnpm, installPnpmToStore, linkExePlatformBinary } from './self-updater/installPnpm.js'
export { setup } from './setup/index.js'
export { withCmd } from './with/index.js'

View File

@@ -22,6 +22,7 @@ import { headlessInstall } from '@pnpm/installing.deps-restorer'
import type { EnvLockfile, LockfileObject, PackageSnapshot } from '@pnpm/lockfile.types'
import { registerProject, type StoreController } from '@pnpm/store.controller'
import type { DepPath, ProjectId, ProjectRootDir, Registries } from '@pnpm/types'
import { familySync } from 'detect-libc'
import { symlinkDir } from 'symlink-dir'
// @pnpm/exe has platform-specific binaries, so its GVS hash must
@@ -319,31 +320,44 @@ async function installFromResolution (
}, params)
}
// @pnpm/exe bundles Node.js via optional platform-specific packages (e.g. @pnpm/macos-arm64).
/**
* Computes the scope-local directory name of the `@pnpm/exe` platform
* package for a given host: `exe.<platform>-<arch>[-musl]`. Pure so that the
* musl branch is unit-testable without mocking detect-libc or patching
* process.platform.
*/
export function exePlatformPkgDirName (
platform: NodeJS.Platform,
arch: string,
libcFamily: string | null
): string {
const normalizedArch = platform === 'win32' && arch === 'ia32' ? 'x86' : arch
const libcSuffix = platform === 'linux' && libcFamily === 'musl' ? '-musl' : ''
return `exe.${platform}-${normalizedArch}${libcSuffix}`
}
// @pnpm/exe bundles Node.js via optional platform-specific packages
// (e.g. @pnpm/exe.darwin-arm64, @pnpm/exe.linux-x64-musl).
// Its postinstall script links the correct binary into the @pnpm/exe package dir.
// Since scripts are disabled during install (to support systems without Node.js),
// we replicate that linking here.
export function linkExePlatformBinary (installDir: string): void {
const platform = process.platform === 'win32'
? 'win'
: process.platform === 'darwin'
? 'macos'
: process.platform
const arch = platform === 'win' && process.arch === 'ia32' ? 'x86' : process.arch
const platform = process.platform
const pkgDirName = exePlatformPkgDirName(platform, process.arch, familySync())
const exePkgDir = path.join(installDir, 'node_modules', '@pnpm', 'exe')
if (!fs.existsSync(exePkgDir)) return
// In pnpm's symlinked node_modules layout, the platform package is not hoisted
// to the top-level node_modules. It's a dependency of @pnpm/exe and lives as a
// sibling in the virtual store. Resolve through the @pnpm/exe symlink to find it.
const exeRealDir = fs.realpathSync(exePkgDir)
const platformPkgDir = path.join(path.dirname(exeRealDir), `${platform}-${arch}`)
const executable = platform === 'win' ? 'pnpm.exe' : 'pnpm'
const platformPkgDir = path.join(path.dirname(exeRealDir), pkgDirName)
const executable = platform === 'win32' ? 'pnpm.exe' : 'pnpm'
const src = path.join(platformPkgDir, executable)
if (!fs.existsSync(src)) return
const dest = path.join(exePkgDir, executable)
forceLink(src, dest)
if (platform === 'win') {
if (platform === 'win32') {
const exePkgJsonPath = path.join(exePkgDir, 'package.json')
const exePkg = JSON.parse(fs.readFileSync(exePkgJsonPath, 'utf8'))
exePkg.bin.pnpm = 'pnpm.exe'

View File

@@ -23,7 +23,7 @@ jest.unstable_mockModule('@pnpm/cli.meta', () => {
},
}
})
const { selfUpdate, installPnpm, linkExePlatformBinary } = await import('@pnpm/engine.pm.commands')
const { selfUpdate, installPnpm, linkExePlatformBinary, exePlatformPkgDirName } = await import('@pnpm/engine.pm.commands')
beforeEach(async () => {
await setupMockAgent()
@@ -508,14 +508,13 @@ test('installPnpm without env lockfile uses resolution path', async () => {
})
describe('linkExePlatformBinary', () => {
const platform = process.platform === 'win32'
? 'win'
: process.platform === 'darwin'
? 'macos'
: process.platform
const arch = platform === 'win' && process.arch === 'ia32' ? 'x86' : process.arch
const executable = platform === 'win' ? 'pnpm.exe' : 'pnpm'
const platformPkgName = `${platform}-${arch}`
const platform = process.platform
const arch = platform === 'win32' && process.arch === 'ia32' ? 'x86' : process.arch
const executable = platform === 'win32' ? 'pnpm.exe' : 'pnpm'
// NOTE: the test layout doesn't set up a musl libc marker on Linux, so the
// non-musl platform package is what gets linked here. Matching what
// linkExePlatformBinary detects via detect-libc.
const platformPkgName = `exe.${platform}-${arch}`
test('links platform binary in pnpm symlinked node_modules layout', () => {
const dir = tempDir(false)
@@ -601,3 +600,26 @@ describe('linkExePlatformBinary', () => {
expect(result).toBe(placeholder)
})
})
describe('exePlatformPkgDirName', () => {
test('appends -musl for linux + musl libc family', () => {
expect(exePlatformPkgDirName('linux', 'x64', 'musl')).toBe('exe.linux-x64-musl')
expect(exePlatformPkgDirName('linux', 'arm64', 'musl')).toBe('exe.linux-arm64-musl')
})
test('does not append -musl when libc is glibc or unknown', () => {
expect(exePlatformPkgDirName('linux', 'x64', 'glibc')).toBe('exe.linux-x64')
expect(exePlatformPkgDirName('linux', 'arm64', null)).toBe('exe.linux-arm64')
})
test('libc is irrelevant on non-linux platforms', () => {
expect(exePlatformPkgDirName('darwin', 'arm64', 'musl')).toBe('exe.darwin-arm64')
expect(exePlatformPkgDirName('darwin', 'x64', null)).toBe('exe.darwin-x64')
expect(exePlatformPkgDirName('win32', 'x64', 'musl')).toBe('exe.win32-x64')
})
test('normalizes ia32 to x86 on win32 only', () => {
expect(exePlatformPkgDirName('win32', 'ia32', null)).toBe('exe.win32-x86')
expect(exePlatformPkgDirName('linux', 'ia32', null)).toBe('exe.linux-ia32')
})
})

74
pnpm-lock.yaml generated
View File

@@ -3740,6 +3740,9 @@ importers:
cross-spawn:
specifier: 'catalog:'
version: 7.0.6
detect-libc:
specifier: 'catalog:'
version: 2.1.2
path-name:
specifier: 'catalog:'
version: 1.0.0
@@ -7580,11 +7583,26 @@ importers:
specifier: 'catalog:'
version: 6.0.0
pnpm/artifacts/darwin-arm64:
devDependencies:
'@pnpm/exe.darwin-arm64':
specifier: workspace:*
version: 'link:'
pnpm/artifacts/darwin-x64:
devDependencies:
'@pnpm/exe.darwin-x64':
specifier: workspace:*
version: 'link:'
pnpm/artifacts/exe:
dependencies:
'@reflink/reflink':
specifier: 'catalog:'
version: 0.1.19
detect-libc:
specifier: 'catalog:'
version: 2.1.2
devDependencies:
'@jest/globals':
specifier: 'catalog:'
@@ -7602,58 +7620,64 @@ importers:
specifier: 'catalog:'
version: 7.5.13
optionalDependencies:
'@pnpm/linux-arm64':
'@pnpm/exe.darwin-arm64':
specifier: workspace:*
version: link:../darwin-arm64
'@pnpm/exe.darwin-x64':
specifier: workspace:*
version: link:../darwin-x64
'@pnpm/exe.linux-arm64':
specifier: workspace:*
version: link:../linux-arm64
'@pnpm/linux-x64':
'@pnpm/exe.linux-arm64-musl':
specifier: workspace:*
version: link:../linux-arm64-musl
'@pnpm/exe.linux-x64':
specifier: workspace:*
version: link:../linux-x64
'@pnpm/macos-arm64':
'@pnpm/exe.linux-x64-musl':
specifier: workspace:*
version: link:../macos-arm64
'@pnpm/macos-x64':
version: link:../linux-x64-musl
'@pnpm/exe.win32-arm64':
specifier: workspace:*
version: link:../macos-x64
'@pnpm/win-arm64':
version: link:../win32-arm64
'@pnpm/exe.win32-x64':
specifier: workspace:*
version: link:../win-arm64
'@pnpm/win-x64':
specifier: workspace:*
version: link:../win-x64
version: link:../win32-x64
pnpm/artifacts/linux-arm64:
devDependencies:
'@pnpm/linux-arm64':
'@pnpm/exe.linux-arm64':
specifier: workspace:*
version: 'link:'
pnpm/artifacts/linux-arm64-musl:
devDependencies:
'@pnpm/exe.linux-arm64-musl':
specifier: workspace:*
version: 'link:'
pnpm/artifacts/linux-x64:
devDependencies:
'@pnpm/linux-x64':
'@pnpm/exe.linux-x64':
specifier: workspace:*
version: 'link:'
pnpm/artifacts/macos-arm64:
pnpm/artifacts/linux-x64-musl:
devDependencies:
'@pnpm/macos-arm64':
'@pnpm/exe.linux-x64-musl':
specifier: workspace:*
version: 'link:'
pnpm/artifacts/macos-x64:
pnpm/artifacts/win32-arm64:
devDependencies:
'@pnpm/macos-x64':
'@pnpm/exe.win32-arm64':
specifier: workspace:*
version: 'link:'
pnpm/artifacts/win-arm64:
pnpm/artifacts/win32-x64:
devDependencies:
'@pnpm/win-arm64':
specifier: workspace:*
version: 'link:'
pnpm/artifacts/win-x64:
devDependencies:
'@pnpm/win-x64':
'@pnpm/exe.win32-x64':
specifier: workspace:*
version: 'link:'

View File

@@ -1,5 +1,5 @@
{
"name": "@pnpm/macos-arm64",
"name": "@pnpm/exe.darwin-arm64",
"version": "11.0.0-rc.2",
"keywords": [
"pnpm",
@@ -8,8 +8,8 @@
],
"license": "MIT",
"funding": "https://opencollective.com/pnpm",
"repository": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/macos-arm64",
"homepage": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/macos-arm64#readme",
"repository": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/darwin-arm64",
"homepage": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/darwin-arm64#readme",
"bugs": {
"url": "https://github.com/pnpm/pnpm/issues"
},
@@ -20,7 +20,7 @@
"prepublishOnly": "test -f pnpm || (echo 'Error: pnpm is missing' && exit 1)"
},
"devDependencies": {
"@pnpm/macos-arm64": "workspace:*"
"@pnpm/exe.darwin-arm64": "workspace:*"
},
"publishConfig": {
"os": [

View File

@@ -1,5 +1,5 @@
{
"name": "@pnpm/macos-x64",
"name": "@pnpm/exe.darwin-x64",
"version": "11.0.0-rc.2",
"keywords": [
"pnpm",
@@ -8,8 +8,8 @@
],
"license": "MIT",
"funding": "https://opencollective.com/pnpm",
"repository": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/macos-x64",
"homepage": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/macos-x64#readme",
"repository": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/darwin-x64",
"homepage": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/darwin-x64#readme",
"bugs": {
"url": "https://github.com/pnpm/pnpm/issues"
},
@@ -20,7 +20,7 @@
"prepublishOnly": "test -f pnpm || (echo 'Error: pnpm is missing' && exit 1)"
},
"devDependencies": {
"@pnpm/macos-x64": "workspace:*"
"@pnpm/exe.darwin-x64": "workspace:*"
},
"publishConfig": {
"os": [

View File

@@ -17,6 +17,7 @@
"type": "module",
"files": [
"setup.js",
"platform-pkg-name.js",
"prepare.js",
"dist/",
"pn.cmd",
@@ -32,15 +33,18 @@
"prepublishOnly": "pn --filter=pnpm prepublishOnly && node ./scripts/build-artifacts.ts"
},
"dependencies": {
"@reflink/reflink": "catalog:"
"@reflink/reflink": "catalog:",
"detect-libc": "catalog:"
},
"optionalDependencies": {
"@pnpm/linux-arm64": "workspace:*",
"@pnpm/linux-x64": "workspace:*",
"@pnpm/macos-arm64": "workspace:*",
"@pnpm/macos-x64": "workspace:*",
"@pnpm/win-arm64": "workspace:*",
"@pnpm/win-x64": "workspace:*"
"@pnpm/exe.darwin-arm64": "workspace:*",
"@pnpm/exe.darwin-x64": "workspace:*",
"@pnpm/exe.linux-arm64": "workspace:*",
"@pnpm/exe.linux-arm64-musl": "workspace:*",
"@pnpm/exe.linux-x64": "workspace:*",
"@pnpm/exe.linux-x64-musl": "workspace:*",
"@pnpm/exe.win32-arm64": "workspace:*",
"@pnpm/exe.win32-x64": "workspace:*"
},
"devDependencies": {
"@jest/globals": "catalog:",

View File

@@ -0,0 +1,9 @@
// Shared between setup.js (preinstall hook) and the test suite.
// Computes the npm package name of the matching @pnpm/exe platform child for a
// given host: `@pnpm/exe.<platform>-<arch>[-musl]`. Pure — no I/O, no detect-libc
// call — so the musl branch is unit-testable without mocking.
export function exePlatformPkgName(platform, arch, libcFamily) {
const normalizedArch = platform === 'win32' && arch === 'ia32' ? 'x86' : arch
const libcSuffix = platform === 'linux' && libcFamily === 'musl' ? '-musl' : ''
return `@pnpm/exe.${platform}-${normalizedArch}${libcSuffix}`
}

View File

@@ -38,42 +38,42 @@ function getTargets (): Record<string, TargetConfig> {
nodeBinPath: `node-v${v}-linux-arm64/bin/node`,
needsLdidSigning: false,
},
'linuxstatic-x64': {
'linux-x64-musl': {
platform: 'linux',
arch: 'x64',
nodeUrl: `https://unofficial-builds.nodejs.org/download/release/v${v}/node-v${v}-linux-x64-musl.tar.xz`,
nodeBinPath: `node-v${v}-linux-x64-musl/bin/node`,
needsLdidSigning: false,
},
'linuxstatic-arm64': {
'linux-arm64-musl': {
platform: 'linux',
arch: 'arm64',
nodeUrl: `https://unofficial-builds.nodejs.org/download/release/v${v}/node-v${v}-linux-arm64-musl.tar.xz`,
nodeBinPath: `node-v${v}-linux-arm64-musl/bin/node`,
needsLdidSigning: false,
},
'macos-x64': {
'darwin-x64': {
platform: 'darwin',
arch: 'x64',
nodeUrl: `https://nodejs.org/dist/v${v}/node-v${v}-darwin-x64.tar.gz`,
nodeBinPath: `node-v${v}-darwin-x64/bin/node`,
needsLdidSigning: process.platform === 'linux',
},
'macos-arm64': {
'darwin-arm64': {
platform: 'darwin',
arch: 'arm64',
nodeUrl: `https://nodejs.org/dist/v${v}/node-v${v}-darwin-arm64.tar.gz`,
nodeBinPath: `node-v${v}-darwin-arm64/bin/node`,
needsLdidSigning: process.platform === 'linux',
},
'win-x64': {
'win32-x64': {
platform: 'win32',
arch: 'x64',
nodeUrl: `https://nodejs.org/dist/v${v}/node-v${v}-win-x64.zip`,
nodeBinPath: `node-v${v}-win-x64/node.exe`,
needsLdidSigning: false,
},
'win-arm64': {
'win32-arm64': {
platform: 'win32',
arch: 'arm64',
nodeUrl: `https://nodejs.org/dist/v${v}/node-v${v}-win-arm64.zip`,
@@ -229,17 +229,17 @@ async function build (target: string, config: TargetConfig): Promise<void> {
;(async () => {
const targets = getTargets()
await build('win-x64', targets['win-x64'])
await build('win32-x64', targets['win32-x64'])
await build('linux-x64', targets['linux-x64'])
await build('macos-x64', targets['macos-x64'])
await build('darwin-x64', targets['darwin-x64'])
const isM1Mac = process.platform === 'darwin' && process.arch === 'arm64'
if (process.platform === 'linux' || isM1Mac) {
await build('macos-arm64', targets['macos-arm64'])
await build('darwin-arm64', targets['darwin-arm64'])
await build('linux-arm64', targets['linux-arm64'])
await build('win-arm64', targets['win-arm64'])
await build('linuxstatic-x64', targets['linuxstatic-x64'])
await build('linuxstatic-arm64', targets['linuxstatic-arm64'])
await build('win32-arm64', targets['win32-arm64'])
await build('linux-x64-musl', targets['linux-x64-musl'])
await build('linux-arm64-musl', targets['linux-arm64-musl'])
}
// Copy dist/ to the exe directory for npm publishing.

View File

@@ -1,17 +1,18 @@
import { fileURLToPath } from 'url'
import path from 'path'
import fs from 'fs'
import { familySync } from 'detect-libc'
import { exePlatformPkgName } from './platform-pkg-name.js'
const platform = process.platform === 'win32'
? 'win'
: process.platform === 'darwin'
? 'macos'
: process.platform
const arch = platform === 'win' && process.arch === 'ia32' ? 'x86' : process.arch
const pkgName = `@pnpm/${platform}-${arch}`
// Platform names match process.platform (linux | darwin | win32). On linux,
// add a `-musl` libc suffix when detect-libc reports musl, matching the
// @pnpm/exe.linux-<arch>-musl optional-dep naming. The name computation lives
// in platform-pkg-name.js so it can be unit-tested without triggering the
// side effects of this preinstall script.
const platform = process.platform
const pkgName = exePlatformPkgName(platform, process.arch, familySync())
const pkgJson = fileURLToPath(import.meta.resolve(`${pkgName}/package.json`))
const executable = platform === 'win' ? 'pnpm.exe' : 'pnpm'
const executable = platform === 'win32' ? 'pnpm.exe' : 'pnpm'
const platformDir = path.dirname(pkgJson)
const bin = path.resolve(platformDir, executable)
@@ -21,7 +22,7 @@ if (!fs.existsSync(bin)) process.exit(0)
linkSync(bin, path.resolve(ownDir, executable))
if (platform === 'win') {
if (platform === 'win32') {
// On Windows, also hardlink the binary as 'pnpm' (no .exe extension).
// npm's bin shims point to the name from publishConfig.bin, and npm
// does NOT re-read package.json after preinstall, so rewriting the bin

View File

@@ -2,21 +2,46 @@ import { execFileSync } from 'node:child_process'
import fs from 'node:fs'
import path from 'node:path'
const exeDir = path.resolve(import.meta.dirname, '..')
const platform = process.platform === 'win32'
? 'win'
: process.platform === 'darwin'
? 'macos'
: process.platform
const arch = platform === 'win' && process.arch === 'ia32' ? 'x86' : process.arch
const isWindows = platform === 'win'
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error — JS helper without type declarations
import { exePlatformPkgName } from '../platform-pkg-name.js'
const exeDir = path.resolve(import.meta.dirname, '..')
const platform = process.platform
const arch = platform === 'win32' && process.arch === 'ia32' ? 'x86' : process.arch
const isWindows = platform === 'win32'
// The test doesn't create a musl libc marker, so setup.js's detect-libc call
// reports the host's native libc; on a glibc Linux CI box that resolves to the
// non-musl package name. For non-Linux hosts there is no libc suffix.
const platformBin = path.join(
exeDir, 'node_modules', '@pnpm', `${platform}-${arch}`,
exeDir, 'node_modules', '@pnpm', `exe.${platform}-${arch}`,
isWindows ? 'pnpm.exe' : 'pnpm'
)
const hasPlatformBinary = fs.existsSync(platformBin)
describe('exePlatformPkgName', () => {
test('appends -musl for linux + musl libc family', () => {
expect(exePlatformPkgName('linux', 'x64', 'musl')).toBe('@pnpm/exe.linux-x64-musl')
expect(exePlatformPkgName('linux', 'arm64', 'musl')).toBe('@pnpm/exe.linux-arm64-musl')
})
test('does not append -musl when libc is glibc or unknown', () => {
expect(exePlatformPkgName('linux', 'x64', 'glibc')).toBe('@pnpm/exe.linux-x64')
expect(exePlatformPkgName('linux', 'arm64', null)).toBe('@pnpm/exe.linux-arm64')
})
test('libc is irrelevant on non-linux platforms', () => {
expect(exePlatformPkgName('darwin', 'arm64', 'musl')).toBe('@pnpm/exe.darwin-arm64')
expect(exePlatformPkgName('darwin', 'x64', null)).toBe('@pnpm/exe.darwin-x64')
expect(exePlatformPkgName('win32', 'x64', 'musl')).toBe('@pnpm/exe.win32-x64')
})
test('normalizes ia32 to x86 on win32 only', () => {
expect(exePlatformPkgName('win32', 'ia32', null)).toBe('@pnpm/exe.win32-x86')
expect(exePlatformPkgName('linux', 'ia32', null)).toBe('@pnpm/exe.linux-ia32')
})
})
test('prepare writes correct content for all bin files', () => {
execFileSync(process.execPath, [path.join(exeDir, 'prepare.js')], { cwd: exeDir })

View File

@@ -0,0 +1,36 @@
{
"name": "@pnpm/exe.linux-arm64-musl",
"version": "11.0.0-rc.2",
"keywords": [
"pnpm",
"pnpm11",
"pnpm-artifact"
],
"license": "MIT",
"funding": "https://opencollective.com/pnpm",
"repository": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/linux-arm64-musl",
"homepage": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/linux-arm64-musl#readme",
"bugs": {
"url": "https://github.com/pnpm/pnpm/issues"
},
"files": [
"pnpm"
],
"scripts": {
"prepublishOnly": "test -f pnpm || (echo 'Error: pnpm is missing' && exit 1)"
},
"devDependencies": {
"@pnpm/exe.linux-arm64-musl": "workspace:*"
},
"publishConfig": {
"os": [
"linux"
],
"cpu": [
"arm64"
],
"libc": [
"musl"
]
}
}

View File

@@ -1,5 +1,5 @@
{
"name": "@pnpm/linux-arm64",
"name": "@pnpm/exe.linux-arm64",
"version": "11.0.0-rc.2",
"keywords": [
"pnpm",
@@ -20,7 +20,7 @@
"prepublishOnly": "test -f pnpm || (echo 'Error: pnpm is missing' && exit 1)"
},
"devDependencies": {
"@pnpm/linux-arm64": "workspace:*"
"@pnpm/exe.linux-arm64": "workspace:*"
},
"publishConfig": {
"os": [

View File

@@ -0,0 +1 @@
pnpm

View File

@@ -0,0 +1,36 @@
{
"name": "@pnpm/exe.linux-x64-musl",
"version": "11.0.0-rc.2",
"keywords": [
"pnpm",
"pnpm11",
"pnpm-artifact"
],
"license": "MIT",
"funding": "https://opencollective.com/pnpm",
"repository": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/linux-x64-musl",
"homepage": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/linux-x64-musl#readme",
"bugs": {
"url": "https://github.com/pnpm/pnpm/issues"
},
"files": [
"pnpm"
],
"scripts": {
"prepublishOnly": "test -f pnpm || (echo 'Error: pnpm is missing' && exit 1)"
},
"devDependencies": {
"@pnpm/exe.linux-x64-musl": "workspace:*"
},
"publishConfig": {
"os": [
"linux"
],
"cpu": [
"x64"
],
"libc": [
"musl"
]
}
}

View File

@@ -1,5 +1,5 @@
{
"name": "@pnpm/linux-x64",
"name": "@pnpm/exe.linux-x64",
"version": "11.0.0-rc.2",
"keywords": [
"pnpm",
@@ -20,7 +20,7 @@
"prepublishOnly": "test -f pnpm || (echo 'Error: pnpm is missing' && exit 1)"
},
"devDependencies": {
"@pnpm/linux-x64": "workspace:*"
"@pnpm/exe.linux-x64": "workspace:*"
},
"publishConfig": {
"os": [

View File

@@ -1,5 +1,5 @@
{
"name": "@pnpm/win-arm64",
"name": "@pnpm/exe.win32-arm64",
"version": "11.0.0-rc.2",
"keywords": [
"pnpm",
@@ -8,8 +8,8 @@
],
"license": "MIT",
"funding": "https://opencollective.com/pnpm",
"repository": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/win-arm64",
"homepage": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/win-arm64#readme",
"repository": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/win32-arm64",
"homepage": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/win32-arm64#readme",
"bugs": {
"url": "https://github.com/pnpm/pnpm/issues"
},
@@ -20,7 +20,7 @@
"prepublishOnly": "test -f pnpm.exe || (echo 'Error: pnpm.exe is missing' && exit 1)"
},
"devDependencies": {
"@pnpm/win-arm64": "workspace:*"
"@pnpm/exe.win32-arm64": "workspace:*"
},
"publishConfig": {
"os": [

View File

@@ -1,5 +1,5 @@
{
"name": "@pnpm/win-x64",
"name": "@pnpm/exe.win32-x64",
"version": "11.0.0-rc.2",
"keywords": [
"pnpm",
@@ -8,8 +8,8 @@
],
"license": "MIT",
"funding": "https://opencollective.com/pnpm",
"repository": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/win-x64",
"homepage": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/win-x64#readme",
"repository": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/win32-x64",
"homepage": "https://github.com/pnpm/pnpm/tree/main/pnpm/artifacts/win32-x64#readme",
"bugs": {
"url": "https://github.com/pnpm/pnpm/issues"
},
@@ -20,7 +20,7 @@
"prepublishOnly": "test -f pnpm.exe || (echo 'Error: pnpm.exe is missing' && exit 1)"
},
"devDependencies": {
"@pnpm/win-x64": "workspace:*"
"@pnpm/exe.win32-x64": "workspace:*"
},
"publishConfig": {
"os": [