# Copyright 1999-2026 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 EAPI=8 DESCRIPTION="Fast, disk space efficient package manager" HOMEPAGE="https://pnpm.io" # TWO distfiles, and the second one is where pnpm actually lives. # # pnpm 12 is a rewrite: the implementation moved from a JavaScript bundle to a # native Rust binary, and the "pnpm" package on npm stopped carrying it. What # that tarball ships now is a wrapper -- a shebang-less placeholder bin, the # Corepack entry points under bin/, and dist/, which holds node-gyp and the # downloader behind https://get.pnpm.io. There is no JS implementation left in # it to fall back to: run the wrapper without the binary and it fetches one over # the network on first use and writes it next to itself, which is not something # an installed package may do. # # The binary is published per platform as an optional dependency of that # wrapper, so it is fetched here as a distfile instead. The npm basenames # ("exe.linux-x64-12.3.4.tgz") are too generic for a shared DISTDIR, hence the # rename. SRC_URI=" https://registry.npmjs.org/${PN}/-/${P}.tgz amd64? ( elibc_glibc? ( https://registry.npmjs.org/@pnpm/exe.linux-x64/-/exe.linux-x64-${PV}.tgz -> ${PN}-exe.linux-x64-${PV}.tgz ) elibc_musl? ( https://registry.npmjs.org/@pnpm/exe.linux-x64-musl/-/exe.linux-x64-musl-${PV}.tgz -> ${PN}-exe.linux-x64-musl-${PV}.tgz ) ) arm64? ( elibc_glibc? ( https://registry.npmjs.org/@pnpm/exe.linux-arm64/-/exe.linux-arm64-${PV}.tgz -> ${PN}-exe.linux-arm64-${PV}.tgz ) elibc_musl? ( https://registry.npmjs.org/@pnpm/exe.linux-arm64-musl/-/exe.linux-arm64-musl-${PV}.tgz -> ${PN}-exe.linux-arm64-musl-${PV}.tgz ) ) " S="${WORKDIR}/wrapper/package" # The Rust binary vendors its dependency tree; THIRD-PARTY-NOTICES.md ships # alongside it and is installed with the rest of the wrapper. LICENSE="MIT" SLOT="0" KEYWORDS="~amd64 ~arm64" # Prebuilt, and upstream ships it stripped already. QA_PREBUILT="usr/lib*/node_modules/${PN}/${PN}" RESTRICT="strip" # The floor comes from upstream, not from what happens to be installed here: # pnpm 12.3.4's package.json declares engines.node ">=18.*". # # Node.js is no longer what runs pnpm -- the native binary needs nothing -- but # it is what pnpm spawns for a package's lifecycle scripts and for node-gyp, so # a Node.js-less pnpm could install a tree and not build it. This is RDEPEND # only: nothing is compiled here. # # The :* slot operator is load-bearing. Story 005 made net-libs/nodejs slotted # by major (this overlay carries SLOT="24" and SLOT="26"; ::gentoo keeps the # unslotted SLOT="0/"), so a slotless atom matches more than one slot and # pkgcheck rejects it as MissingSlotDep. pnpm runs any node past the floor, so # ":*" -- any slot, no rebuild when it changes -- is the honest constraint. # Pinning one major would be a lie. # # ACCEPTED QA FINDING: pkgcheck reports NonsolvableDepsInDev on the # default/linux/amd64/23.0/x32 dev profile. net-libs/nodejs is unavailable on # amd64/x32 -- ::gentoo states exactly that and package.mask's each consumer # individually there (dev-util/claude-code, net-misc/sunshine, # app-containers/devcontainer, ...). Every nodejs consumer in this overlay # reports it (playwright, lemonade, claude-agent-acp-tui), so it is a property # of the profile, not of this ebuild. The systemic fix is one overlay-wide # profiles/arch/amd64/x32/package.mask covering all of them. RDEPEND=">=net-libs/nodejs-18:*" # Basename of the native-binary distfile for this host, matching the SRC_URI # above. Both axes are USE_EXPAND_IMPLICIT flags (ARCH, ELIBC), which is why # neither appears in IUSE. pnpm_native_distfile() { local arch libc= if use amd64; then arch="x64" elif use arm64; then arch="arm64" else die "no prebuilt pnpm binary for ARCH=${ARCH}" fi use elibc_musl && libc="-musl" echo "${PN}-exe.linux-${arch}${libc}-${PV}.tgz" } src_unpack() { # Both tarballs unpack to "package/", so they cannot share a directory. mkdir -p "${WORKDIR}"/{wrapper,native} || die cd "${WORKDIR}/wrapper" || die unpack "${P}.tgz" cd "${WORKDIR}/native" || die unpack "$(pnpm_native_distfile)" } src_compile() { : } src_install() { local install_dir="/usr/$(get_libdir)/node_modules/${PN}" b # Everything that exists to obtain a binary we already have. install.js # links the native binary over the placeholder (done below, at merge time # instead), bin/ is Corepack's Node.js entry point, and it reaches # dist/node_modules/get-pnpm to download a binary when none is installed. # Left in place, that download path stays reachable in an installed # package; taken out, the only pnpm here is the one Portage merged. rm -r bin install.js native-binary.mjs dist/node_modules/get-pnpm || die insinto "${install_dir}" doins -r . # What upstream's install.js does on Unix: replace the shebang-less `pnpm` # placeholder -- which only exists to hand over to the Node.js wrapper -- # with the native binary. It has to land at this path rather than straight # in /usr/bin because the binary resolves dist/node-gyp-bin relative to # itself when it builds a lifecycle script's PATH. exeinto "${install_dir}" newexe "${WORKDIR}/native/package/${PN}" "${PN}" # pn/pnpx/pnx stay the committed `#!/bin/sh` scripts upstream ships: they # `exec pnpm [dlx]`, and the binary's own launch-name detection does not # substitute for them. Measured: a `pnpx` symlink onto the binary prints # pnpm's help, not dlx's. fperms +x "${install_dir}"/{pn,pnpx,pnx} fperms +x "${install_dir}/dist/node-gyp-bin/node-gyp" fperms +x "${install_dir}/dist/node_modules/node-gyp/bin/node-gyp.js" # No fperms on the symlinks themselves: chmod follows a symlink, so it # would silently alter the target instead, and a symlink's own mode is # meaningless to the kernel. for b in "${PN}" pn pnpx pnx; do dosym "../$(get_libdir)/node_modules/${PN}/${b}" "/usr/bin/${b}" done }