doc/javascript: various fixes (#567490)

This commit is contained in:
Sefa Eyeoglu
2026-10-01 21:00:04 +00:00
committed by GitHub
4 changed files with 62 additions and 229 deletions

View File

@@ -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)

View File

@@ -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.
@@ -288,9 +294,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`:
There is also the [`pnpmBuildHook`](#pnpm-build-hook) for building packages with `pnpm`, as seen in [](#ex-pnpm-build-hook).
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`:
```nix
{
@@ -298,6 +302,7 @@ There is also the [`pnpmBuildHook`](#pnpm-build-hook) for building packages with
nodejs,
pnpm_11,
pnpmConfigHook,
pnpmBuildHook,
stdenv,
}:
let
@@ -319,7 +324,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,49 +337,11 @@ 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:
The example also uses [`pnpmBuildHook`](#javascript-pnpm-pnpmBuildHook), which runs `pnpm run build` in the build phase.
<!-- TODO: Does splicing still work when overriding in nativeBuildInputs here? -->
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`).
```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`).
#### pnpmConfigHook {#javascript-pnpm-pnpmConfigHook}
`pnpmConfigHook` supports adding additional `pnpm install` flags via `pnpmInstallFlags` which can be set to a Nix string array:
@@ -391,6 +359,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 <build-script>`.
```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`.
@@ -603,13 +598,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:
@@ -634,7 +624,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";
@@ -657,6 +646,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`:
@@ -668,14 +658,17 @@ $ yarn-berry-fetcher prefetch </path/to/yarn.lock> [/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:
@@ -689,7 +682,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";

View File

@@ -430,9 +430,6 @@
{
"file": "hooks/pkg-config.section.md"
},
{
"file": "hooks/pnpm.section.md"
},
{
"file": "hooks/postgresql-test-hook.section.md"
},

View File

@@ -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"
@@ -3886,7 +3856,23 @@
"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"
],
"javascript-pnpm-sourceRoot": [
"index.html#javascript-pnpm-sourceRoot"