fetchPnpmDeps,pnpmConfigHook: move to top-level attribute

move pnpm.{fetchDeps,configHook} to fetchPnpmDeps and pnpmConfigHook
respectively. Also adds aliases to the former package-level attributes.

Additionally, pnpmConfigHook does not propagate pnpm anymore, to make
changing pnpm versions easier.

This brings pnpm in line with the other Node.js/JavaScript tooling in
nixpkgs.

This doesn't cause any rebuilds.

Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>
This commit is contained in:
Sefa Eyeoglu
2025-12-09 23:32:05 +01:00
parent 97f6e6fd60
commit d925565179
7 changed files with 107 additions and 49 deletions
+70 -33
View File
@@ -327,16 +327,17 @@ See `node2nix` [docs](https://github.com/svanderburg/node2nix) for more info.
### pnpm {#javascript-pnpm}
Pnpm is available as the top-level package `pnpm`. Additionally, there are variants pinned to certain major versions, like `pnpm_8` and `pnpm_9`, which support different sets of lock file versions.
pnpm is available as the top-level package `pnpm`. Additionally, there are variants pinned to certain major versions, like `pnpm_8`, `pnpm_9` and `pnpm_10`, 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 functions `pnpm_8.fetchDeps` and `pnpm_9.fetchDeps` can create this pnpm store derivation. In conjunction, the setup hooks `pnpm_8.configHook` and `pnpm_9.configHook` will prepare the build environment to install the pre-fetched dependencies store. Here is an example for a package that contains `package.json` and a `pnpm-lock.yaml` files using the above `pnpm_` attributes:
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` will prepare the build environment to install the pre-fetched dependencies store. Here is an example for a package that contains `package.json` and a `pnpm-lock.yaml` files using the fetcher and setup hook above:
```nix
{
stdenv,
fetchPnpmDeps,
nodejs,
# This is pinned as { pnpm = pnpm_9; }
pnpm,
pnpmConfigHook,
stdenv,
}:
stdenv.mkDerivation (finalAttrs: {
@@ -348,11 +349,12 @@ stdenv.mkDerivation (finalAttrs: {
};
nativeBuildInputs = [
nodejs
pnpm.configHook
nodejs # in case scripts are run outside of a pnpm call
pnpmConfigHook
pnpm # At least required by pnpmConfigHook, if not other (custom) phases
];
pnpmDeps = pnpm.fetchDeps {
pnpmDeps = fetchPnpmDeps {
inherit (finalAttrs) pname version src;
fetcherVersion = 3;
hash = "...";
@@ -360,33 +362,68 @@ stdenv.mkDerivation (finalAttrs: {
})
```
NOTE: It is highly recommended to use a pinned version of pnpm (i.e., `pnpm_8` or `pnpm_9`), to increase future reproducibility. It might also be required to use an older version if the package needs support for a certain lock file version.
It is highly recommended to use a pinned version of pnpm (i.e., `pnpm_9` or `pnpm_10`), to increase future reproducibility. It might also be required to use an older version if the package needs support for a certain lock file version. To do so, you can pass the `pnpm` argument to `fetchPnpmDeps` and override the `pnpm` arg in `pnpmConfigHook`. Here are the changes in the example above to use a pinned pnpm version:
<!-- TODO: Does splicing still work when overriding in nativeBuildInputs here? -->
```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 = nodejs_20; };
+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 = 3;
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`.
`pnpm.configHook` supports adding additional `pnpm install` flags via `pnpmInstallFlags` which can be set to a Nix string array:
`pnpmConfigHook` supports adding additional `pnpm install` flags via `pnpmInstallFlags` which can be set to a Nix string array:
```nix
{ pnpm }:
stdenv.mkDerivation (finalAttrs: {
pname = "foo";
version = "0-unstable-1980-01-01";
src = {
{
# ...
pnpmDeps = fetchPnpmDeps {
# ...
inherit (finalAttrs) pnpmInstallFlags;
};
pnpmInstallFlags = [ "--shamefully-hoist" ];
pnpmDeps = pnpm.fetchDeps { inherit (finalAttrs) pnpmInstallFlags; };
})
}
```
#### Dealing with `sourceRoot` {#javascript-pnpm-sourceRoot}
If the pnpm project is in a subdirectory, you can just define `sourceRoot` or `setSourceRoot` for `fetchDeps`.
If `sourceRoot` is different between the parent derivation and `fetchDeps`, you will have to set `pnpmRoot` to effectively be the same location as it is in `fetchDeps`.
If the pnpm project is in a subdirectory, you can just define `sourceRoot` or `setSourceRoot` for `fetchPnpmDeps`.
If `sourceRoot` is different between the parent derivation and `fetchPnpmDeps`, you will have to set `pnpmRoot` to effectively be the same location as it is in `fetchPnpmDeps`.
Assuming the following directory structure, we can define `sourceRoot` and `pnpmRoot` as follows:
@@ -402,7 +439,7 @@ Assuming the following directory structure, we can define `sourceRoot` and `pnpm
```nix
{
# ...
pnpmDeps = pnpm.fetchDeps {
pnpmDeps = fetchPnpmDeps {
# ...
sourceRoot = "${finalAttrs.src.name}/frontend";
};
@@ -414,7 +451,7 @@ Assuming the following directory structure, we can define `sourceRoot` and `pnpm
#### PNPM Workspaces {#javascript-pnpm-workspaces}
If you need to use a PNPM workspace for your project, then set `pnpmWorkspaces = [ "<workspace project name 1>" "<workspace project name 2>" ]`, etc, in your `pnpm.fetchDeps` call,
If you need to use a PNPM workspace for your project, then set `pnpmWorkspaces = [ "<workspace project name 1>" "<workspace project name 2>" ]`, etc, in your `fetchPnpmDeps` call,
which will make PNPM only install dependencies for those workspace packages.
For example:
@@ -423,14 +460,14 @@ For example:
{
# ...
pnpmWorkspaces = [ "@astrojs/language-server" ];
pnpmDeps = pnpm.fetchDeps {
inherit (finalAttrs) pnpmWorkspaces;
pnpmDeps = fetchPnpmDeps {
#...
inherit (finalAttrs) pnpmWorkspaces;
};
}
```
The above would make `pnpm.fetchDeps` call only install dependencies for the `@astrojs/language-server` workspace package.
The above would make `fetchPnpmDeps` call only install dependencies for the `@astrojs/language-server` workspace package.
Note that you do not need to set `sourceRoot` to make this work.
Usually, in such cases, you'd want to use `pnpm --filter=<pnpm workspace name> build` to build your project, as `npmHooks.npmBuildHook` probably won't work. A `buildPhase` based on the following example will probably fit most workspace projects:
@@ -457,23 +494,23 @@ set `prePnpmInstall` to the right commands to run. For example:
prePnpmInstall = ''
pnpm config set dedupe-peer-dependents false
'';
pnpmDeps = pnpm.fetchDeps {
pnpmDeps = fetchPnpmDeps {
inherit (finalAttrs) prePnpmInstall;
# ...
};
}
```
In this example, `prePnpmInstall` will be run by both `pnpm.configHook` and by the `pnpm.fetchDeps` builder.
In this example, `prePnpmInstall` will be run by both `pnpmConfigHook` and by the `fetchPnpmDeps` builder.
#### PNPM `fetcherVersion` {#javascript-pnpm-fetcherVersion}
#### pnpm `fetcherVersion` {#javascript-pnpm-fetcherVersion}
This is the version of the output of `pnpm.fetchDeps`, if you haven't set it already, you can use `1` with your current hash:
This is the version of the output of `fetchPnpmDeps`, if you haven't set it already, you can use `1` with your current hash:
```nix
{
# ...
pnpmDeps = pnpm.fetchDeps {
pnpmDeps = fetchPnpmDeps {
# ...
fetcherVersion = 1;
hash = "..."; # you can use your already set hash here
@@ -486,7 +523,7 @@ After upgrading to a newer `fetcherVersion`, you need to regenerate the hash:
```nix
{
# ...
pnpmDeps = pnpm.fetchDeps {
pnpmDeps = fetchPnpmDeps {
# ...
fetcherVersion = 2;
hash = "..."; # clear this hash and generate a new one
@@ -494,7 +531,7 @@ After upgrading to a newer `fetcherVersion`, you need to regenerate the hash:
}
```
This variable ensures that we can make changes to the output of `pnpm.fetchDeps` without breaking existing hashes.
This variable ensures that we can make changes to the output of `fetchPnpmDeps` without breaking existing hashes.
Changes can include workarounds or bug fixes to existing PNPM issues.
##### Version history {#javascript-pnpm-fetcherVersion-versionHistory}
+2
View File
@@ -54,6 +54,8 @@
If your SQLite database is corrupted, the migration might fail and require [manual intervention](https://github.com/louislam/uptime-kuma/issues/5281).
See the [migration guide](https://github.com/louislam/uptime-kuma/wiki/Migration-From-v1-To-v2) for more information.
- `fetchPnpmDeps` and `pnpmConfigHook` were added as top-level attributes, replacing the now deprecated `pnpm.fetchDeps` and `pnpm.configHook` attributes.
- Added `dell-bios-fan-control` package and service.
- We now use the upstream wrapper script for Gradle, supporting both the `JAVA_HOME` and `GRADLE_OPTS` environment variables.
@@ -10,9 +10,8 @@
yq,
zstd,
}:
let
pnpm' = pnpm;
pnpmLatest = pnpm;
supportedFetcherVersions = [
1 # First version. Here to preserve backwards compatibility
@@ -21,11 +20,11 @@ let
];
in
{
fetchDeps = lib.makeOverridable (
fetchPnpmDeps = lib.makeOverridable (
{
hash ? "",
pname,
pnpm ? pnpm',
pnpm ? pnpmLatest,
pnpmWorkspaces ? [ ],
prePnpmInstall ? "",
pnpmInstallFlags ? [ ],
@@ -50,15 +49,15 @@ in
in
# pnpmWorkspace was deprecated, so throw if it's used.
assert (lib.throwIf (args ? pnpmWorkspace)
"pnpm.fetchDeps: `pnpmWorkspace` is no longer supported, please migrate to `pnpmWorkspaces`."
"fetchPnpmDeps: `pnpmWorkspace` is no longer supported, please migrate to `pnpmWorkspaces`."
) true;
assert (lib.throwIf (fetcherVersion == null)
"pnpm.fetchDeps: `fetcherVersion` is not set, see https://nixos.org/manual/nixpkgs/stable/#javascript-pnpm-fetcherVersion."
"fetchPnpmDeps: `fetcherVersion` is not set, see https://nixos.org/manual/nixpkgs/stable/#javascript-pnpm-fetcherVersion."
) true;
assert (lib.throwIf (!(builtins.elem fetcherVersion supportedFetcherVersions))
"pnpm.fetchDeps `fetcherVersion` is not set to a supported value (${lib.concatStringsSep ", " (map toString supportedFetcherVersions)}), see https://nixos.org/manual/nixpkgs/stable/#javascript-pnpm-fetcherVersion."
"fetchPnpmDeps `fetcherVersion` is not set to a supported value (${lib.concatStringsSep ", " (map toString supportedFetcherVersions)}), see https://nixos.org/manual/nixpkgs/stable/#javascript-pnpm-fetcherVersion."
) true;
stdenvNoCC.mkDerivation (
@@ -72,7 +71,7 @@ in
cacert
jq
moreutils
args.pnpm or pnpm'
pnpm # from args
yq
zstd
];
@@ -176,7 +175,7 @@ in
passthru = {
inherit fetcherVersion;
serve = callPackage ./serve.nix {
pnpm = args.pnpm or pnpm';
inherit pnpm; # from args
pnpmDeps = finalAttrs.finalPackage;
};
};
@@ -190,10 +189,9 @@ in
)
);
configHook = makeSetupHook {
pnpmConfigHook = makeSetupHook {
name = "pnpm-config-hook";
propagatedBuildInputs = [
pnpm
zstd
];
substitutions = {
@@ -12,6 +12,13 @@ pnpmConfigHook() {
exit 1
fi
if ! command -v "pnpm" &> /dev/null; then
echo "Error: 'pnpm' binary not found in PATH. Consider adding 'pkgs.pnpm' to 'nativeBuildInputs'." >&2
exit 1
fi
echo "Found 'pnpm' with version '$(pnpm --version)'"
fetcherVersion=$(cat "${pnpmDeps}/.fetcher-version" || echo 1)
echo "Using fetcherVersion: $fetcherVersion"
+14 -5
View File
@@ -2,7 +2,7 @@
lib,
stdenvNoCC,
writeScript,
callPackages,
fetchPnpmDeps,
fetchurl,
installShellFiles,
nodejs,
@@ -11,6 +11,7 @@
version,
hash,
buildPackages,
pnpmConfigHook,
}:
let
majorVersion = lib.versions.major version;
@@ -71,12 +72,20 @@ stdenvNoCC.mkDerivation (finalAttrs: {
passthru =
let
fetchDepsAttrs = callPackages ./fetch-deps {
pnpm = buildPackages."pnpm_${lib.versions.major version}";
};
pnpm' = buildPackages."pnpm_${lib.versions.major version}";
in
{
inherit (fetchDepsAttrs) fetchDeps configHook;
fetchDeps =
{ ... }@args:
fetchPnpmDeps args
// {
pnpm = pnpm';
};
configHook = pnpmConfigHook.overrideAttrs (prevAttrs: {
propagatedBuildInputs = prevAttrs.propagatedBuildInputs or [ ] ++ [
pnpm'
];
});
inherit majorVersion;
tests.version = lib.optionalAttrs withNode (
+5
View File
@@ -3606,6 +3606,11 @@ with pkgs;
;
pnpm = pnpm_10;
inherit (callPackages ../build-support/node/fetch-pnpm-deps { })
fetchPnpmDeps
pnpmConfigHook
;
po4a = perlPackages.Po4a;
podman-compose = python3Packages.callPackage ../applications/virtualization/podman-compose { };