From b02c199b3d222908a4b1d759f79976081e7258c8 Mon Sep 17 00:00:00 2001 From: Diogo Correia Date: Sun, 27 Sep 2026 13:23:37 +0100 Subject: [PATCH 1/5] doc/javascript: add heading to pnpmConfigHook --- doc/languages-frameworks/javascript.section.md | 4 +++- doc/redirects.json | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/languages-frameworks/javascript.section.md b/doc/languages-frameworks/javascript.section.md index 8fd33230573a..1629b381f224 100644 --- a/doc/languages-frameworks/javascript.section.md +++ b/doc/languages-frameworks/javascript.section.md @@ -288,7 +288,7 @@ This package puts the corepack wrappers for pnpm and yarn in your PATH, and they pnpm is available as the top-level package `pnpm`. Additionally, there are variants pinned to certain major versions, like `pnpm_9`, `pnpm_10`, `pnpm_10_29_2` and `pnpm_11`, which support different sets of lock file versions. -When packaging an application that includes a `pnpm-lock.yaml`, you need to fetch the pnpm store for that project using a fixed-output-derivation. The function `fetchPnpmDeps` can create this pnpm store derivation. In conjunction, the setup hook `pnpmConfigHook` prepares the build environment to install the pre-fetched dependencies store. The example below uses the fetcher and setup hook for a package that has `package.json` and `pnpm-lock.yaml`: +When packaging an application that includes a `pnpm-lock.yaml`, you need to fetch the pnpm store for that project using a fixed-output-derivation. The function `fetchPnpmDeps` can create this pnpm store derivation. In conjunction, the setup hook [`pnpmConfigHook`](#javascript-pnpm-pnpmConfigHook) prepares the build environment to install the pre-fetched dependencies store. The example below uses the fetcher and setup hook for a package that has `package.json` and `pnpm-lock.yaml`: There is also the [`pnpmBuildHook`](#pnpm-build-hook) for building packages with `pnpm`, as seen in [](#ex-pnpm-build-hook). @@ -375,6 +375,8 @@ Use a pinned version of pnpm (for example `pnpm_9` or `pnpm_10`) to increase rep In case you are patching `package.json` or `pnpm-lock.yaml`, make sure to pass `finalAttrs.patches` to the function as well (i.e., `inherit (finalAttrs) patches`). +#### pnpmConfigHook {#javascript-pnpm-pnpmConfigHook} + `pnpmConfigHook` supports adding additional `pnpm install` flags via `pnpmInstallFlags` which can be set to a Nix string array: ```nix diff --git a/doc/redirects.json b/doc/redirects.json index c247c05dcd9b..4f31228a2271 100644 --- a/doc/redirects.json +++ b/doc/redirects.json @@ -3882,6 +3882,9 @@ "javascript-pnpm": [ "index.html#javascript-pnpm" ], + "javascript-pnpm-pnpmConfigHook": [ + "index.html#javascript-pnpm-pnpmConfigHook" + ], "javascript-pnpm-sourceRoot": [ "index.html#javascript-pnpm-sourceRoot" ], From 887b9e1a5d2891385d314b0731f2509a4fb9bc37 Mon Sep 17 00:00:00 2001 From: Diogo Correia Date: Sun, 27 Sep 2026 13:24:27 +0100 Subject: [PATCH 2/5] doc/javascript: remove superfluous section about pinning pnpm The main example already pins pnpm. This section had been removed before in 7d318dfe3b95f6ce5f97db40a3b2dee704d75227, but was (accidentally?) added back in d925565179c8ca824dd3436f311476341ffc2fcc. --- .../javascript.section.md | 44 +------------------ 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/doc/languages-frameworks/javascript.section.md b/doc/languages-frameworks/javascript.section.md index 1629b381f224..43c49e32cc6d 100644 --- a/doc/languages-frameworks/javascript.section.md +++ b/doc/languages-frameworks/javascript.section.md @@ -331,49 +331,7 @@ stdenv.mkDerivation (finalAttrs: { }) ``` -Use a pinned version of pnpm (for example `pnpm_9` or `pnpm_10`) to increase reproducibility. An older version may be required if the package needs a certain lock file version. To do so, pass the `pnpm` argument to `fetchPnpmDeps`. Then override the `pnpm` arg in `pnpmConfigHook`. Here are the changes in the example above to use a pinned pnpm version: - - - -```diff - { - fetchPnpmDeps, - nodejs, -- pnpm, -+ pnpm_10, - pnpmConfigHook, - stdenv, - }: -+let -+ # Optionally override pnpm to use a custom nodejs version -+ # Make sure that the same nodejs version is referenced in nativeBuildInputs -+ # pnpm = pnpm_10.override { nodejs-slim = nodejs-slim_22; }; -+in - stdenv.mkDerivation (finalAttrs: { - pname = "foo"; - version = "0-unstable-1980-01-01"; - - src = { - #... - }; - - nativeBuildInputs = [ - nodejs # in case scripts are run outside of a pnpm call - pnpmConfigHook -- pnpm # At least required by pnpmConfigHook, if not other (custom) phases -+ pnpm_10 # At least required by pnpmConfigHook, if not other (custom) phases - ]; - - pnpmDeps = fetchPnpmDeps { - inherit (finalAttrs) pname version src; -+ pnpm = pnpm_10; - fetcherVersion = 4; - hash = "..."; - }; - }) -``` - -In case you are patching `package.json` or `pnpm-lock.yaml`, make sure to pass `finalAttrs.patches` to the function as well (i.e., `inherit (finalAttrs) patches`). +In case you are patching `package.json` or `pnpm-lock.yaml`, make sure to pass `finalAttrs.patches` to the `fetchPnpmDeps` function as well (i.e., `inherit (finalAttrs) patches`). #### pnpmConfigHook {#javascript-pnpm-pnpmConfigHook} From 3827d932a5b454c8a4b1fa7359876b3325dd3261 Mon Sep 17 00:00:00 2001 From: Diogo Correia Date: Sun, 27 Sep 2026 13:26:35 +0100 Subject: [PATCH 3/5] doc/javascript: small cleanup of yarn berry section --- doc/languages-frameworks/javascript.section.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/doc/languages-frameworks/javascript.section.md b/doc/languages-frameworks/javascript.section.md index 43c49e32cc6d..c6a4ded46554 100644 --- a/doc/languages-frameworks/javascript.section.md +++ b/doc/languages-frameworks/javascript.section.md @@ -563,13 +563,8 @@ To install the package, `yarnInstallHook` uses both `npm` and `yarn` to clean up - `yarnKeepDevDeps`: Disables the removal of devDependencies from `node_modules` before installation. #### Yarn Berry v3/v4 {#javascript-yarn-v3-v4} -Yarn Berry (v3 / v4) versions have similar formats. They start with blocks like these: -```yaml -__metadata: - version: 6 - cacheKey: 8[cX] -``` +Yarn Berry (v3 / v4) versions have similar formats. The `yarn.lock` file starts with blocks like these: ```yaml __metadata: @@ -594,7 +589,6 @@ Explicitly pin the major version. For example, capture the `yarn-berry_Xn` argum let yarn-berry = yarn-berry_4; - in stdenv.mkDerivation (finalAttrs: { pname = "foo"; @@ -617,6 +611,7 @@ stdenv.mkDerivation (finalAttrs: { ``` ##### `yarn-berry_X.fetchYarnBerryDeps` {#javascript-fetchYarnBerryDeps} + `fetchYarnBerryDeps` runs `yarn-berry-fetcher fetch` in a fixed-output-derivation. It is a custom fetcher designed to reproducibly download all files in the `yarn.lock` file, validating their hashes in the process. For git dependencies, it creates a checkout at `${offlineCache}/checkouts/<40-character-commit-hash>` (relying on the git commit hash to describe the contents of the checkout). To produce the `hash` argument for the `fetchYarnBerryDeps` call, run `yarn-berry-fetcher prefetch`: @@ -628,14 +623,17 @@ $ yarn-berry-fetcher prefetch [/path/to/missing-hashes.json This prints the hash to stdout. Use it in update scripts to recalculate the hash for a new `yarn.lock`. ##### `yarn-berry_X.yarnBerryConfigHook` {#javascript-yarnBerryConfigHook} + `yarnBerryConfigHook` uses the store path `offlineCache` points to, to run a `yarn install` during the build, producing a usable `node_modules` directory from the downloaded dependencies. Internally, this uses a patched version of Yarn to ensure git dependencies are re-packed and any attempted downloads fail immediately. ##### Patching the project's `package.json` or `yarn.lock` files {#javascript-yarnBerry-patching} + In case patching the project's `package.json` or `yarn.lock` is needed, it's important to pass `finalAttrs.patches` to `fetchYarnBerryDeps` as well, so the patched variants are picked up (i.e., `inherit (finalAttrs) patches`). ##### Missing hashes in the `yarn.lock` file {#javascript-yarnBerry-missing-hashes} + Unfortunately, `yarn.lock` files do not include hashes for optional/platform-specific dependencies. This is [by design](https://github.com/yarnpkg/berry/issues/6759). To compensate for this, run the `yarn-berry-fetcher missing-hashes` subcommand to produce all missing hashes. These are stored in a `missing-hashes.json` file, which needs to be passed to both the build itself, as well as the `fetchYarnBerryDeps` helper: @@ -649,7 +647,6 @@ To compensate for this, run the `yarn-berry-fetcher missing-hashes` subcommand t let yarn-berry = yarn-berry_4; - in stdenv.mkDerivation (finalAttrs: { pname = "foo"; From d0702e9a4ed93103906a5c6c2f5fb62a8a87b609 Mon Sep 17 00:00:00 2001 From: Diogo Correia Date: Sun, 27 Sep 2026 14:08:12 +0100 Subject: [PATCH 4/5] doc/hooks/pnpm: move to pnpm section in docs/javascript --- doc/hooks/pnpm.section.md | 142 ------------------ .../javascript.section.md | 35 ++++- doc/nav.json | 3 - doc/redirects.json | 45 ++---- 4 files changed, 46 insertions(+), 179 deletions(-) delete mode 100644 doc/hooks/pnpm.section.md diff --git a/doc/hooks/pnpm.section.md b/doc/hooks/pnpm.section.md deleted file mode 100644 index e8871c6c2398..000000000000 --- a/doc/hooks/pnpm.section.md +++ /dev/null @@ -1,142 +0,0 @@ -# pnpmBuildHook {#pnpm-build-hook} - -[pnpm](https://pnpm.io/) is a an NPM-compatible package manager focused on increasing managment speeds, and reducing disk space. - -The `pnpmBuildHook` in Nixpkgs overrides the default build phase for building packages that use pnpm. - -:::{.example #ex-pnpm-build-hook} -## pnpmBuildHook example code snippet {#pnpm-build-hook-code-snippet} - -```nix -{ - lib, - stdenv, - fetchFromGitHub, - fetchPnpmDeps, - pnpmConfigHook, - pnpmBuildHook, - makeBinaryWrapper, - pnpm_10, -}: -let - pnpm = pnpm_10; -in -stdenv.mkDerivation (finalAttrs: { - pname = "coolPackages"; - version = "1.0"; - - src = fetchFromGitHub { - owner = "JaneCool"; - repo = "coolpackage"; - tag = finalAttrs.version; - hash = lib.fakeHash; - }; - - __structuredAttrs = true; - strictDeps = true; - - pnpmDeps = fetchPnpmDeps { - inherit (finalAttrs) pname version src; - inherit pnpm; - fetcherVersion = 4; - hash = lib.fakeHash; - }; - - nativeBuildInputs = [ - pnpmConfigHook - pnpmBuildHook - makeBinaryWrapper - ]; - - pnpmBuildScript = "build"; - pnpmBuildFlags = [ - "--mode" - "production" - ]; - pnpmWorkspaces = [ - "test" - ]; - - installPhase = '' - runHook preInstall - - mkdir "$out" - cp -r dist/. "$out" - - runHook postInstall - ''; - - meta = { - description = "very cool package that does cool things"; - mainProgram = "cool"; - }; -}) -``` -::: - -## Variables controlling pnpmBuildHook {#pnpm-build-hook-variables} - -### pnpm Exclusive Variables {#pnpm-build-hook-exclusive-variables} - -#### `pnpmBuildScript` {#pnpm-build-hook-script} - -Controls the script ran to build the package, by default the script is `build`. - -#### `pnpmFlags` {#pnpm-build-hook-flags} - -Controls flags used for all invocations of pnpm across all hooks local to this derivation. - -#### `pnpmBuildFlags` {#pnpm-build-hook-build-flags} - -Controls the flags pass only to the pnpm build script invocation. - -#### `dontPnpmBuild` {#pnpm-build-hook-dont} - -Disables automatically running `pnpmBuildHook`. The build can still be run manually if needed, for example: - -```nix -{ - lib, - rustPlatform, - pnpmBuildHook, - pnpmConfigHook, - fetchPnpmDeps, - emptyDirectory, - pnpm_10, -}: -let - pnpm = pnpm_10; -in -rustPlatform.buildRustPackage (finalAttrs: { - pname = "super-fast-application"; - version = "1.0"; - - src = emptyDirectory; - - cargoHash = lib.fakeHash; - - nativeBuildInputs = [ - pnpmBuildHook - pnpmConfigHook - ]; - - pnpmDeps = fetchPnpmDeps { - inherit (finalAttrs) pname version src; - inherit pnpm; - fetcherVersion = 4; - hash = lib.fakeHash; - }; - - dontPnpmBuild = true; - postBuild = '' - pnpmBuildHook - ''; -}) -``` - -### Honored Variables {#pnpm-build-hook-honored-variables} - -The following variables are honored by `pnpmBuildHook`. - -* [`pnpmRoot`](#javascript-pnpm-sourceRoot) -* [`pnpmWorkspaces`](#javascript-pnpm-workspaces) diff --git a/doc/languages-frameworks/javascript.section.md b/doc/languages-frameworks/javascript.section.md index c6a4ded46554..4077d10b02e0 100644 --- a/doc/languages-frameworks/javascript.section.md +++ b/doc/languages-frameworks/javascript.section.md @@ -290,14 +290,13 @@ pnpm is available as the top-level package `pnpm`. Additionally, there are varia When packaging an application that includes a `pnpm-lock.yaml`, you need to fetch the pnpm store for that project using a fixed-output-derivation. The function `fetchPnpmDeps` can create this pnpm store derivation. In conjunction, the setup hook [`pnpmConfigHook`](#javascript-pnpm-pnpmConfigHook) prepares the build environment to install the pre-fetched dependencies store. The example below uses the fetcher and setup hook for a package that has `package.json` and `pnpm-lock.yaml`: -There is also the [`pnpmBuildHook`](#pnpm-build-hook) for building packages with `pnpm`, as seen in [](#ex-pnpm-build-hook). - ```nix { fetchPnpmDeps, nodejs, pnpm_11, pnpmConfigHook, + pnpmBuildHook, stdenv, }: let @@ -319,7 +318,8 @@ stdenv.mkDerivation (finalAttrs: { nativeBuildInputs = [ nodejs # in case scripts are run outside of a pnpm call pnpmConfigHook - pnpm # At least required by pnpmConfigHook, if not other (custom) phases + pnpmBuildHook + pnpm # At least required by pnpmConfigHook and pnpmBuildHook, if not other (custom) phases ]; pnpmDeps = fetchPnpmDeps { @@ -331,6 +331,8 @@ stdenv.mkDerivation (finalAttrs: { }) ``` +The example also uses [`pnpmBuildHook`](#javascript-pnpm-pnpmBuildHook), which runs `pnpm run build` in the build phase. + In case you are patching `package.json` or `pnpm-lock.yaml`, make sure to pass `finalAttrs.patches` to the `fetchPnpmDeps` function as well (i.e., `inherit (finalAttrs) patches`). #### pnpmConfigHook {#javascript-pnpm-pnpmConfigHook} @@ -351,6 +353,33 @@ In case you are patching `package.json` or `pnpm-lock.yaml`, make sure to pass ` If needed, set `dontPnpmConfigure = true;` to fully disable `pnpmConfigHook` without removing it from inputs manually. +#### pnpmBuildHook {#javascript-pnpm-pnpmBuildHook} + +The `pnpmBuildHook` in overrides the default build phase with `pnpm run `. + +```nix +{ + nativeBuildInputs = [ + pnpmBuildHook + ]; + + pnpmBuildScript = "build-ui"; + pnpmBuildFlags = [ + "--mode" + "production" + ]; +} +``` + +Available options: + +- `pnpmBuildScript`: select which script from `package.json` to run. Defaults to `build`. +- `pnpmBuildFlags`: array of flags to pass to the build script. +- `pnpmFlags`: currently the same as `pnpmBuildFlags`, but might be used by other hooks in the future. +- `dontPnpmBuild`: disable this hook from running automatically. The hook can still be invoked manually. + +Both [`pnpmRoot`](#javascript-pnpm-sourceRoot) and [`pnpmWorkspaces`](#javascript-pnpm-workspaces) are honored by this hook. + #### Dealing with `sourceRoot` {#javascript-pnpm-sourceRoot} If the pnpm project is in a subdirectory, you can define `sourceRoot` or `setSourceRoot` for `fetchPnpmDeps`. diff --git a/doc/nav.json b/doc/nav.json index cc361e87593f..4fd503ed9d8f 100644 --- a/doc/nav.json +++ b/doc/nav.json @@ -430,9 +430,6 @@ { "file": "hooks/pkg-config.section.md" }, - { - "file": "hooks/pnpm.section.md" - }, { "file": "hooks/postgresql-test-hook.section.md" }, diff --git a/doc/redirects.json b/doc/redirects.json index 4f31228a2271..2527e1ce366b 100644 --- a/doc/redirects.json +++ b/doc/redirects.json @@ -128,9 +128,6 @@ "ex-pkgs-replace-vars-with": [ "index.html#ex-pkgs-replace-vars-with" ], - "ex-pnpm-build-hook": [ - "index.html#ex-pnpm-build-hook" - ], "ex-shfmt": [ "index.html#ex-shfmt" ], @@ -406,33 +403,6 @@ "pkgs.treefmt.withConfig": [ "index.html#pkgs.treefmt.withConfig" ], - "pnpm-build-hook": [ - "index.html#pnpm-build-hook" - ], - "pnpm-build-hook-build-flags": [ - "index.html#pnpm-build-hook-build-flags" - ], - "pnpm-build-hook-code-snippet": [ - "index.html#pnpm-build-hook-code-snippet" - ], - "pnpm-build-hook-dont": [ - "index.html#pnpm-build-hook-dont" - ], - "pnpm-build-hook-exclusive-variables": [ - "index.html#pnpm-build-hook-exclusive-variables" - ], - "pnpm-build-hook-flags": [ - "index.html#pnpm-build-hook-flags" - ], - "pnpm-build-hook-script": [ - "index.html#pnpm-build-hook-script" - ], - "pnpm-build-hook-variables": [ - "index.html#pnpm-build-hook-variables" - ], - "pnpm-build-hook-honored-variables": [ - "index.html#pnpm-build-hook-honored-variables" - ], "preface": [ "index.html#preface", "index.html#overview-of-nixpkgs" @@ -3880,7 +3850,20 @@ "index.html#javascript-corepack" ], "javascript-pnpm": [ - "index.html#javascript-pnpm" + "index.html#javascript-pnpm", + "index.html#ex-pnpm-build-hook" + ], + "javascript-pnpm-pnpmBuildHook": [ + "index.html#javascript-pnpm-pnpmBuildHook", + "index.html#pnpm-build-hook", + "index.html#pnpm-build-hook-build-flags", + "index.html#pnpm-build-hook-code-snippet", + "index.html#pnpm-build-hook-dont", + "index.html#pnpm-build-hook-exclusive-variables", + "index.html#pnpm-build-hook-flags", + "index.html#pnpm-build-hook-script", + "index.html#pnpm-build-hook-variables", + "index.html#pnpm-build-hook-honored-variables" ], "javascript-pnpm-pnpmConfigHook": [ "index.html#javascript-pnpm-pnpmConfigHook" From be10a55ced9f617db7b90d2522f4084581017a81 Mon Sep 17 00:00:00 2001 From: Diogo Correia Date: Sun, 27 Sep 2026 14:08:37 +0100 Subject: [PATCH 5/5] doc/javascript: list available builders and hooks in tools overview --- doc/languages-frameworks/javascript.section.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/languages-frameworks/javascript.section.md b/doc/languages-frameworks/javascript.section.md index 4077d10b02e0..ed1cf59945ac 100644 --- a/doc/languages-frameworks/javascript.section.md +++ b/doc/languages-frameworks/javascript.section.md @@ -6,6 +6,12 @@ Package JavaScript applications with the tools below. ## Tools overview {#javascript-tools-overview} +- **npm**: [`buildNpmPackage`](#javascript-buildNpmPackage), [`prefetch-npm-deps` (CLI)](#javascript-buildNpmPackage-prefetch-npm-deps), [`fetchNpmDeps`](#javascript-buildNpmPackage-fetchNpmDeps), [`importNpmLock`](#javascript-buildNpmPackage-importNpmLock) +- [**corepack**](#javascript-corepack) +- **pnpm**: [`fetchPnpmDeps`](#javascript-pnpm), [`pnpmConfigHook`](#javascript-pnpm-pnpmConfigHook), [`pnpmBuildHook`](#javascript-pnpm-pnpmBuildHook) +- [**Yarn v1**](#javascript-yarn-v1): [`fetchYarnDeps`](#javascript-fetchyarndeps), [`yarnConfigHook`](#javascript-yarnconfighook), [`yarnBuildHook`](#javascript-yarnbuildhook), [`yarnInstallHook`](#javascript-yarninstallhook) +- [**Yarn Berry (v3/v4)**](#javascript-yarn-v3-v4): [`fetchYarnBerryDeps`](#javascript-fetchYarnBerryDeps), [`yarnBerryConfigHook`](#javascript-yarnBerryConfigHook) + ## General principles {#javascript-general-principles} The principles below are ordered by importance.