Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)

pnpm.fetchDeps: init

Signed-off-by: Sefa Eyeoglu <contact@scrumplex.net>

+193 -4
+63
doc/languages-frameworks/javascript.section.md
··· 310 310 - `node2nix` has some [bugs](https://github.com/svanderburg/node2nix/issues/238) related to working with lock files from npm distributed with `nodejs_16`. 311 311 - `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. 312 312 313 + ### pnpm {#javascript-pnpm} 314 + 315 + 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. 316 + 317 + 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: 318 + 319 + ```nix 320 + { 321 + stdenv, 322 + nodejs, 323 + # This is pinned as { pnpm = pnpm_9; } 324 + pnpm 325 + }: 326 + 327 + stdenv.mkDerivation (finalAttrs: { 328 + pname = "foo"; 329 + version = "0-unstable-1980-01-01"; 330 + 331 + src = ...; 332 + 333 + nativeBuildInputs = [ 334 + nodejs 335 + pnpm.configHook 336 + ]; 337 + 338 + pnpmDeps = pnpm.fetchDeps { 339 + inherit (finalAttrs) pname version src; 340 + hash = "..."; 341 + }; 342 + }) 343 + ``` 344 + 345 + 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. 346 + 347 + 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`. 348 + 349 + #### Dealing with `sourceRoot` {#javascript-pnpm-sourceRoot} 350 + 351 + 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. 352 + 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`. 353 + 354 + Assuming the following directory structure, we can define `sourceRoot` and `pnpmRoot` as follows: 355 + 356 + ``` 357 + . 358 + ├── frontend 359 + │   ├── ... 360 + │   ├── package.json 361 + │   └── pnpm-lock.yaml 362 + └── ... 363 + ``` 364 + 365 + ```nix 366 + ... 367 + pnpmDeps = pnpm.fetchDeps { 368 + ... 369 + sourceRoot = "${finalAttrs.src.name}/frontend"; 370 + }; 371 + 372 + # by default the working directory is the extracted source 373 + pnpmRoot = "frontend"; 374 + ``` 375 + 313 376 ### yarn2nix {#javascript-yarn2nix} 314 377 315 378 #### Preparation {#javascript-yarn2nix-preparation}
+83
pkgs/development/tools/pnpm/fetch-deps/default.nix
··· 1 + { 2 + stdenvNoCC, 3 + fetchurl, 4 + jq, 5 + moreutils, 6 + cacert, 7 + makeSetupHook, 8 + pnpm, 9 + }: 10 + { 11 + fetchDeps = 12 + { 13 + src, 14 + hash ? "", 15 + pname, 16 + ... 17 + }@args: 18 + let 19 + args' = builtins.removeAttrs args [ 20 + "hash" 21 + "pname" 22 + ]; 23 + hash' = 24 + if hash != "" then 25 + { outputHash = hash; } 26 + else 27 + { 28 + outputHash = ""; 29 + outputHashAlgo = "sha256"; 30 + }; 31 + in 32 + stdenvNoCC.mkDerivation ( 33 + args' 34 + // { 35 + name = "${pname}-pnpm-deps"; 36 + 37 + nativeBuildInputs = [ 38 + jq 39 + moreutils 40 + pnpm 41 + cacert 42 + ]; 43 + 44 + installPhase = '' 45 + runHook preInstall 46 + 47 + export HOME=$(mktemp -d) 48 + pnpm config set store-dir $out 49 + # Some packages produce platform dependent outputs. We do not want to cache those in the global store 50 + pnpm config set side-effects-cache false 51 + # As we pin pnpm versions, we don't really care about updates 52 + pnpm config set update-notifier false 53 + # pnpm is going to warn us about using --force 54 + # --force allows us to fetch all dependencies including ones that aren't meant for our host platform 55 + pnpm install --frozen-lockfile --ignore-script --force 56 + 57 + runHook postInstall 58 + ''; 59 + 60 + fixupPhase = '' 61 + runHook preFixup 62 + 63 + # Remove timestamp and sort the json files 64 + rm -rf $out/v3/tmp 65 + for f in $(find $out -name "*.json"); do 66 + jq --sort-keys "del(.. | .checkedAt?)" $f | sponge $f 67 + done 68 + 69 + runHook postFixup 70 + ''; 71 + 72 + dontConfigure = true; 73 + dontBuild = true; 74 + outputHashMode = "recursive"; 75 + } 76 + // hash' 77 + ); 78 + 79 + configHook = makeSetupHook { 80 + name = "pnpm-config-hook"; 81 + propagatedBuildInputs = [ pnpm ]; 82 + } ./pnpm-config-hook.sh; 83 + }
+40
pkgs/development/tools/pnpm/fetch-deps/pnpm-config-hook.sh
··· 1 + # shellcheck shell=bash 2 + 3 + pnpmConfigHook() { 4 + echo "Executing pnpmConfigHook" 5 + 6 + if [ -n "${pnpmRoot-}" ]; then 7 + pushd "$pnpmRoot" 8 + fi 9 + 10 + if [ -z "${pnpmDeps-}" ]; then 11 + echo "Error: 'pnpmDeps' must be set when using pnpmConfigHook." 12 + exit 1 13 + fi 14 + 15 + echo "Configuring pnpm store" 16 + 17 + export HOME=$(mktemp -d) 18 + export STORE_PATH=$(mktemp -d) 19 + 20 + cp -Tr "$pnpmDeps" "$STORE_PATH" 21 + chmod -R +w "$STORE_PATH" 22 + 23 + pnpm config set store-dir "$STORE_PATH" 24 + 25 + echo "Installing dependencies" 26 + 27 + pnpm install --offline --frozen-lockfile --ignore-script 28 + 29 + echo "Patching scripts" 30 + 31 + patchShebangs node_modules/{*,.*} 32 + 33 + if [ -n "${pnpmRoot-}" ]; then 34 + popd 35 + fi 36 + 37 + echo "Finished pnpmConfigHook" 38 + } 39 + 40 + postConfigureHooks+=(pnpmConfigHook)
+7 -4
pkgs/development/tools/pnpm/generic.nix
··· 1 1 { 2 2 lib, 3 3 stdenvNoCC, 4 + callPackages, 4 5 fetchurl, 5 6 nodejs, 6 7 testers, ··· 8 9 9 10 version, 10 11 hash, 11 - }: 12 - 13 - stdenvNoCC.mkDerivation (finalAttrs: { 12 + }: stdenvNoCC.mkDerivation (finalAttrs: { 14 13 pname = "pnpm"; 15 14 inherit version; 16 15 ··· 32 31 runHook postInstall 33 32 ''; 34 33 35 - passthru = { 34 + passthru = let 35 + fetchDepsAttrs = callPackages ./fetch-deps { pnpm = finalAttrs.finalPackage; }; 36 + in { 37 + inherit (fetchDepsAttrs) fetchDeps configHook; 38 + 36 39 tests.version = lib.optionalAttrs withNode ( 37 40 testers.testVersion { package = finalAttrs.finalPackage; } 38 41 );