Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pnpm.fetchDeps: init #290715

Merged
merged 12 commits into from
Jun 6, 2024
65 changes: 65 additions & 0 deletions doc/languages-frameworks/javascript.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,71 @@ See `node2nix` [docs](https://github.com/svanderburg/node2nix) for more info.
- `node2nix` has some [bugs](https://github.com/svanderburg/node2nix/issues/238) related to working with lock files from npm distributed with `nodejs_16`.
- `node2nix` does not like missing packages from npm. If you see something like `Cannot resolve version: vue-loader-v16@undefined` then you might want to try another tool. The package might have been pulled off of npm.

### 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.

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 prefetched dependencies store. Here is an example for a package that contains a `package.json` and a `pnpm-lock.yaml` files using the above `pnpm_` attributes:

```nix
{
stdenv,
nodejs,
# This is pinned as { pnpm = pnpm_9; }
pnpm
}:

stdenv.mkDerivation (finalAttrs: {
pname = "foo";
version = "0-unstable-1980-01-01";

src = ...;

nativeBuildInputs = [
nodejs
pnpm.configHook
];

pnpmDeps = pnpm.fetchDeps {
inherit (finalAttrs) pname version src;
hash = "...";
};
})
```

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.

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`.

#### Dealing with `sourceRoot` {#javascript-pnpm-sourceRoot}

NOTE: Nixpkgs pnpm tooling doesn't support building projects with a `pnpm-workspace.yaml`, or building monorepos. It maybe possible to use `pnpm.fetchDeps` for these projects, but it may be hard or impossible to produce a binary from such projects ([an example attempt](https://github.com/NixOS/nixpkgs/pull/290715#issuecomment-2144543728)).

If the pnpm project is in a subdirectory, you can just define `sourceRoot` or `setSourceRoot` for `fetchDeps`. Note, that projects using `pnpm-workspace.yaml` are currently not supported, and will probably not work using this approach.
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`.

Assuming the following directory structure, we can define `sourceRoot` and `pnpmRoot` as follows:

```
.
├── frontend
│   ├── ...
│   ├── package.json
│   └── pnpm-lock.yaml
└── ...
```

```nix
...
pnpmDeps = pnpm.fetchDeps {
...
sourceRoot = "${finalAttrs.src.name}/frontend";
};

# by default the working directory is the extracted source
pnpmRoot = "frontend";
```

### yarn2nix {#javascript-yarn2nix}

#### Preparation {#javascript-yarn2nix-preparation}
Expand Down
52 changes: 6 additions & 46 deletions pkgs/applications/audio/youtube-music/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@
, electron
, python3
, stdenv
, stdenvNoCC
, copyDesktopItems
, moreutils
, cacert
, jq
, nodePackages
, nodejs
, pnpm
, makeDesktopItem
}:

Expand All @@ -24,54 +21,17 @@ stdenv.mkDerivation (finalAttrs: {
hash = "sha256-nxpctEG4XoxW6jOAxGdgTEYr6YnhFRR8+5HUQLxRJB0=";
};

pnpmDeps = stdenvNoCC.mkDerivation {
pname = "${finalAttrs.pname}-pnpm-deps";
inherit (finalAttrs) src version ELECTRON_SKIP_BINARY_DOWNLOAD;

nativeBuildInputs = [ jq moreutils nodePackages.pnpm cacert ];

installPhase = ''
export HOME=$(mktemp -d)

pnpm config set store-dir $out
pnpm install --frozen-lockfile --ignore-script

rm -rf $out/v3/tmp
for f in $(find $out -name "*.json"); do
sed -i -E -e 's/"checkedAt":[0-9]+,//g' $f
jq --sort-keys . $f | sponge $f
done
'';

dontBuild = true;
dontFixup = true;
outputHashMode = "recursive";
outputHash = {
x86_64-linux = "sha256-bujlQxP6Lr3qPUDxYXKyb702ZJY/xbuCsu3wVDhcb+8=";
aarch64-linux = "sha256-0kyjjttpXpFVhdza5NAjGrRn++qc/N5/u2dQl7VufLE=";
x86_64-darwin = "sha256-Q37QJt/mhfpSguOlkJGKFTCrIOrpbG3OBwaD/Bg09Us=";
aarch64-darwin = "sha256-wbfjzoGa/6vIlOOVX3bKNQ2uxzph3WSofo3MGXqA6yQ=";
}.${stdenv.system} or (throw "Unsupported system: ${stdenv.system}");
wegank marked this conversation as resolved.
Show resolved Hide resolved
pnpmDeps = pnpm.fetchDeps {
inherit (finalAttrs) pname version src;
hash = "sha256-8oeloQYiwUy+GDG4R+XtiynT+8Fad4WYFWTO1KANZKQ=";
};

nativeBuildInputs = [ makeWrapper python3 nodePackages.pnpm nodePackages.nodejs ]
nativeBuildInputs = [ makeWrapper python3 nodejs pnpm.configHook ]
++ lib.optionals (!stdenv.isDarwin) [ copyDesktopItems ];


ELECTRON_SKIP_BINARY_DOWNLOAD = 1;

preBuild = ''
export HOME=$(mktemp -d)
export STORE_PATH=$(mktemp -d)

cp -Tr "$pnpmDeps" "$STORE_PATH"
chmod -R +w "$STORE_PATH"

pnpm config set store-dir "$STORE_PATH"
pnpm install --offline --frozen-lockfile --ignore-script
patchShebangs node_modules/{*,.*}
'';

postBuild = lib.optionalString stdenv.isDarwin ''
cp -R ${electron}/Applications/Electron.app Electron.app
chmod -R u+w Electron.app
Expand Down
64 changes: 19 additions & 45 deletions pkgs/applications/networking/geph/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
, fetchFromGitHub
, buildGoModule
, makeWrapper
, nodePackages
, nodejs
, pnpm
, cacert
, esbuild
, jq
Expand Down Expand Up @@ -48,7 +49,7 @@ in
};
};

gui = stdenvNoCC.mkDerivation rec {
gui = stdenvNoCC.mkDerivation (finalAttrs: {
pname = "geph-gui";
inherit version;

Expand All @@ -60,42 +61,11 @@ in
fetchSubmodules = true;
};

pnpm-deps = stdenvNoCC.mkDerivation {
pname = "${pname}-pnpm-deps";
inherit src version;

sourceRoot = "${src.name}/gephgui-wry/gephgui";

nativeBuildInputs = [
jq
moreutils
nodePackages.pnpm
cacert
];

installPhase = ''
export HOME=$(mktemp -d)
pnpm config set store-dir $out
pnpm install --ignore-scripts

# Remove timestamp and sort the json files
rm -rf $out/v3/tmp
for f in $(find $out -name "*.json"); do
sed -i -E -e 's/"checkedAt":[0-9]+,//g' $f
jq --sort-keys . $f | sponge $f
done
'';

dontFixup = true;
outputHashMode = "recursive";
outputHash = "sha256-OKPx5xRI7DWd6m31nYx1biP0k6pcZ7fq7dfVlHda4O0=";
};

gephgui-wry = rustPlatform.buildRustPackage {
pname = "gephgui-wry";
inherit version src;
inherit (finalAttrs) version src;

sourceRoot = "${src.name}/gephgui-wry";
sourceRoot = "${finalAttrs.src.name}/gephgui-wry";

cargoLock = {
lockFile = ./Cargo.lock;
Expand All @@ -105,10 +75,17 @@ in
};
};

pnpmDeps = pnpm.fetchDeps {
inherit (finalAttrs) pname version src;
sourceRoot = "${finalAttrs.src.name}/gephgui-wry/gephgui";
hash = "sha256-0MGlsLEgugQ1wEz07ROIwkanTa8PSKwIaxNahyS1014=";
};

nativeBuildInputs = [
pkg-config
nodePackages.pnpm
pnpm.configHook
makeWrapper
nodejs
];

buildInputs = [
Expand All @@ -132,22 +109,19 @@ in
});
})}";

pnpmRoot = "gephgui";

preBuild = ''
cd gephgui
export HOME=$(mktemp -d)
pnpm config set store-dir ${pnpm-deps}
pnpm install --ignore-scripts --offline
chmod -R +w node_modules
pnpm rebuild
pushd gephgui
pnpm build
cd ..
popd
'';
};

dontBuild = true;

installPhase = ''
install -Dt $out/bin ${gephgui-wry}/bin/gephgui-wry
install -Dt $out/bin ${finalAttrs.gephgui-wry}/bin/gephgui-wry
install -d $out/share/icons/hicolor
for i in '16' '32' '64' '128' '256'
do
Expand All @@ -163,5 +137,5 @@ in
meta = geph-meta // {
license = with lib.licenses; [ unfree ];
};
};
});
}
60 changes: 10 additions & 50 deletions pkgs/by-name/ki/kiwitalk/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
, fetchFromGitHub
, copyDesktopItems
, stdenv
, stdenvNoCC
, rustc
, rustPlatform
, cargo
Expand All @@ -12,20 +11,18 @@
, webkitgtk
, pkg-config
, makeDesktopItem
, jq
, moreutils
, nodePackages
, cacert
, pnpm
, nodejs
}:

stdenv.mkDerivation rec {
stdenv.mkDerivation (finalAttrs: {
pname = "kiwitalk";
version = "0.5.1";

src = fetchFromGitHub {
owner = "KiwiTalk";
repo = "KiwiTalk";
rev = "v${version}";
rev = "v${finalAttrs.version}";
hash = "sha256-Th8q+Zbc102fIk2v7O3OOeSriUV/ydz60QwxzmS7AY8=";
};

Expand All @@ -34,43 +31,9 @@ stdenv.mkDerivation rec {
--replace "libayatana-appindicator3.so.1" "${libayatana-appindicator}/lib/libayatana-appindicator3.so.1"
'';

pnpm-deps = stdenvNoCC.mkDerivation {
pname = "${pname}-pnpm-deps";
inherit src version;

nativeBuildInputs = [
jq
moreutils
nodePackages.pnpm
cacert
];

installPhase = ''
export HOME=$(mktemp -d)
pnpm config set store-dir $out
# This version of the package has different versions of esbuild as a dependency.
# You can use the command below to get esbuild binaries for a specific platform and calculate hashes for that platforms. (linux, darwin for os, and x86, arm64, ia32 for cpu)
# cat package.json | jq '.pnpm.supportedArchitectures += { "os": ["linux"], "cpu": ["arm64"] }' | sponge package.json
pnpm install --frozen-lockfile --ignore-script

# Remove timestamp and sort the json files.
rm -rf $out/v3/tmp
for f in $(find $out -name "*.json"); do
sed -i -E -e 's/"checkedAt":[0-9]+,//g' $f
jq --sort-keys . $f | sponge $f
done
'';

dontBuild = true;
dontFixup = true;
outputHashMode = "recursive";
outputHash = {
x86_64-linux = "sha256-LJPjWNpVfdUu8F5BMhAzpTo/h6ax7lxY2EESHj5P390=";
aarch64-linux = "sha256-N1K4pV5rbWmO/KonvYegzBoWa6TYQIqhQyxH/sWjOJQ=";
i686-linux = "sha256-/Q7VZahYhLdKVFB25CanROYxD2etQOcRg+4bXZUMqTc=";
x86_64-darwin = "sha256-9biFAbFD7Bva7KPKztgCvcaoX8E6AlJBKkjlDQdP6Zw=";
aarch64-darwin = "sha256-to5Y0R9tm9b7jUQAK3eBylLhpu+w5oDd63FbBCBAvd8=";
}.${stdenv.system} or (throw "Unsupported system: ${stdenv.system}");
pnpmDeps = pnpm.fetchDeps {
inherit (finalAttrs) pname version src;
hash = "sha256-gf3vmKUta8KksUOxyhQS4UO6ycAJDfEicyXVGMW8+4c=";
};

cargoDeps = rustPlatform.importCargoLock {
Expand All @@ -86,7 +49,8 @@ stdenv.mkDerivation rec {
cargo
rustc
cargo-tauri
nodePackages.pnpm
nodejs
pnpm.configHook
copyDesktopItems
pkg-config
];
Expand All @@ -98,10 +62,6 @@ stdenv.mkDerivation rec {
];

preBuild = ''
export HOME=$(mktemp -d)
pnpm config set store-dir ${pnpm-deps}
pnpm install --offline --frozen-lockfile --ignore-script
pnpm rebuild
cargo tauri build -b deb
'';

Expand Down Expand Up @@ -131,4 +91,4 @@ stdenv.mkDerivation rec {
platforms = platforms.linux ++ platforms.darwin;
mainProgram = "kiwi-talk";
};
}
})
Loading