Skip to content

Commit

Permalink
pnpm.fetchDeps: Add support for multiple workspaces; additional insta…
Browse files Browse the repository at this point in the history
…llFlags support
  • Loading branch information
pyrox0 committed Oct 23, 2024
1 parent 4545ad6 commit 3981d80
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 69 deletions.
31 changes: 25 additions & 6 deletions doc/languages-frameworks/javascript.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,26 @@ NOTE: It is highly recommended to use a pinned version of pnpm (i.e. `pnpm_8` or
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.
`pnpm.configHook` 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 = ...;
pnpmInstallFlags = ["--shamefully-hoist"]
pnpmDeps = pnpm.fetchDeps {
inherit (finalAttrs) pnpmInstallFlags;
};
})
```
#### Dealing with `sourceRoot` {#javascript-pnpm-sourceRoot}
Expand Down Expand Up @@ -459,24 +478,24 @@ 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 `pnpmWorkspace = "<workspace project name>"` in your `pnpm.fetchDeps` call,
which will make PNPM only install dependencies for that workspace package.
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,
which will make PNPM only install dependencies for those workspace packages.
For example:
```nix
...
pnpmWorkspace = "@astrojs/language-server";
pnpmWorkspaces = ["@astrojs/language-server"];
pnpmDeps = pnpm.fetchDeps {
inherit (finalAttrs) pnpmWorkspace;
inherit (finalAttrs) pnpmWorkspaces;
...
}
```
The above would make `pnpm.fetchDeps` 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=$pnpmWorkspace 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:
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:
```nix
buildPhase = ''
Expand Down
126 changes: 66 additions & 60 deletions pkgs/development/tools/pnpm/fetch-deps/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
{
hash ? "",
pname,
pnpmWorkspace ? "",
pnpmWorkspaces ? [ ],
prePnpmInstall ? "",
pnpmInstallFlags ? [ ],
...
}@args:
let
Expand All @@ -32,79 +33,84 @@
outputHash = "";
outputHashAlgo = "sha256";
};
installFlags = lib.optionalString (pnpmWorkspace != "") "--filter=${pnpmWorkspace}";

filterFlags = lib.map (package: "--filter=${package}") pnpmWorkspaces;
in
stdenvNoCC.mkDerivation (
finalAttrs:
lib.throwIf (args.pnpmWorkspace != null)
"pnpm.fetchDeps: `pnpmWorkspace` is no longer supported, please migrate to `pnpmWorkspaces`."
stdenvNoCC.mkDerivation
(
args'
// {
name = "${pname}-pnpm-deps";
finalAttrs:
(
args'
// {
name = "${pname}-pnpm-deps";

nativeBuildInputs = [
cacert
jq
moreutils
pnpm
yq
];
nativeBuildInputs = [
cacert
jq
moreutils
pnpm
yq
];

impureEnvVars = lib.fetchers.proxyImpureEnvVars;
impureEnvVars = lib.fetchers.proxyImpureEnvVars;

installPhase = ''
runHook preInstall
installPhase = ''
runHook preInstall
lockfileVersion="$(yq -r .lockfileVersion pnpm-lock.yaml)"
if [[ ''${lockfileVersion:0:1} -gt ${lib.versions.major pnpm.version} ]]; then
echo "ERROR: lockfileVersion $lockfileVersion in pnpm-lock.yaml is too new for the provided pnpm version ${lib.versions.major pnpm.version}!"
exit 1
fi
lockfileVersion="$(yq -r .lockfileVersion pnpm-lock.yaml)"
if [[ ''${lockfileVersion:0:1} -gt ${lib.versions.major pnpm.version} ]]; then
echo "ERROR: lockfileVersion $lockfileVersion in pnpm-lock.yaml is too new for the provided pnpm version ${lib.versions.major pnpm.version}!"
exit 1
fi
export HOME=$(mktemp -d)
pnpm config set store-dir $out
# Some packages produce platform dependent outputs. We do not want to cache those in the global store
pnpm config set side-effects-cache false
# As we pin pnpm versions, we don't really care about updates
pnpm config set update-notifier false
# Run any additional pnpm configuration commands that users provide.
${prePnpmInstall}
# pnpm is going to warn us about using --force
# --force allows us to fetch all dependencies including ones that aren't meant for our host platform
pnpm install \
--force \
--ignore-scripts \
${installFlags} \
--frozen-lockfile
export HOME=$(mktemp -d)
pnpm config set store-dir $out
# Some packages produce platform dependent outputs. We do not want to cache those in the global store
pnpm config set side-effects-cache false
# As we pin pnpm versions, we don't really care about updates
pnpm config set update-notifier false
# Run any additional pnpm configuration commands that users provide.
${prePnpmInstall}
# pnpm is going to warn us about using --force
# --force allows us to fetch all dependencies including ones that aren't meant for our host platform
pnpm install \
--force \
--ignore-scripts \
${lib.escapeShellArgs filterFlags} \
${lib.escapeShellArgs pnpmInstallFlags} \
--frozen-lockfile
runHook postInstall
'';
runHook postInstall
'';

fixupPhase = ''
runHook preFixup
fixupPhase = ''
runHook preFixup
# Remove timestamp and sort the json files
rm -rf $out/v3/tmp
for f in $(find $out -name "*.json"); do
jq --sort-keys "del(.. | .checkedAt?)" $f | sponge $f
done
# Remove timestamp and sort the json files
rm -rf $out/v3/tmp
for f in $(find $out -name "*.json"); do
jq --sort-keys "del(.. | .checkedAt?)" $f | sponge $f
done
runHook postFixup
'';
runHook postFixup
'';

passthru = {
serve = callPackage ./serve.nix {
inherit pnpm;
pnpmDeps = finalAttrs.finalPackage;
passthru = {
serve = callPackage ./serve.nix {
inherit pnpm;
pnpmDeps = finalAttrs.finalPackage;
};
};
};

dontConfigure = true;
dontBuild = true;
outputHashMode = "recursive";
}
// hash'
)
);
dontConfigure = true;
dontBuild = true;
outputHashMode = "recursive";
}
// hash'
)
);

configHook = makeSetupHook {
name = "pnpm-config-hook";
Expand Down
9 changes: 6 additions & 3 deletions pkgs/development/tools/pnpm/fetch-deps/pnpm-config-hook.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@ pnpmConfigHook() {
pnpm config set store-dir "$STORE_PATH"

echo "Installing dependencies"

if [[ -n "$pnpmWorkspace" ]]; then
pnpmInstallFlags+=("--filter=$pnpmWorkspace")
if [[ -n "$pnpmWorkspaces" ]]; then
local IFS=" "
for ws in $pnpmWorkspaces; do
pnpmInstallFlags+=("--filter=$ws")
done
fi

runHook prePnpmInstall

pnpm install \
Expand Down

0 comments on commit 3981d80

Please sign in to comment.