Skip to content

Commit

Permalink
Merge pull request #1 from mojotech/kb/pnpm-workspace
Browse files Browse the repository at this point in the history
Add support for pnpm workspaces as single derivation
  • Loading branch information
iteratee authored Mar 4, 2024
2 parents 0366b73 + 71a9968 commit c3cfff8
Showing 1 changed file with 108 additions and 19 deletions.
127 changes: 108 additions & 19 deletions derivation.nix
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{ lib
, stdenv
, nodejs
, rsync
, pkg-config
, callPackage
, writeText
Expand All @@ -15,17 +16,29 @@ let
in
{
mkPnpmPackage =
{ src
{ workspace ? null
, components ? []
, src ? if (workspace != null && components != []) then workspace else null
, packageJSON ? src + "/package.json"
, componentPackageJSONs ? map (c: {
name = "${c}/package.json";
value = src + "/${c}/package.json";
}) components
, pnpmLockYaml ? src + "/pnpm-lock.yaml"
, pnpmWorkspaceYaml ? (if workspace == null then null else workspace + "/pnpm-workspace.yaml")
, pname ? (fromJSON (readFile packageJSON)).name
, version ? (fromJSON (readFile packageJSON)).version or null
, name ? if version != null then "${pname}-${version}" else pname
, registry ? "https://registry.npmjs.org"
, script ? "build"
, distDir ? "dist"
, distDirs ? (if workspace == null then [distDir] else (map (c: "${c}/dist") components))
, distDirIsOut ? true
, installNodeModules ? false
, installPackageFiles ? false
, installInPlace ? false
, installEnv ? { }
, buildEnv ? { }
, noDevDependencies ? false
, extraNodeModuleSources ? [ ]
, copyPnpmStore ? true
Expand All @@ -37,17 +50,73 @@ in
, ...
}@attrs:
let
# Flag that can be computed from arguments, indicating a workspace was
# supplied. Only used in these let bindings.
isWorkspace = workspace != null && components != [];
# Utility functions
forEachConcat = f: xs: concatStringsSep "\n" (map f xs);
forEachComponent = f: forEachConcat f components;
# Computed values used below that don't loop
nativeBuildInputs = [
nodejs
pnpm
pkg-config
] ++ extraBuildInputs;
] ++ extraBuildInputs ++ (optional copyNodeModules rsync);
copyLink =
if copyNodeModules
then "rsync -a --chmod=u+w"
else "ln -s";
rsyncSlash = optionalString copyNodeModules "/";
packageFilesWithoutLockfile =
[
{ name = "package.json"; value = packageJSON; }
] ++ componentPackageJSONs ++ computedNodeModuleSources;
computedNodeModuleSources =
(if pnpmWorkspaceYaml == null
then []
else [
{name = "pnpm-workspace.yaml"; value = pnpmWorkspaceYaml;}
]
) ++ extraNodeModuleSources;
# Computed values that loop over something
computedDistFiles =
let
packageFileNames = ["pnpm-lock.yaml"] ++
map ({ name, ... }: name) packageFilesWithoutLockfile;
in
distDirs ++
optionals installNodeModules nodeModulesDirs ++
optionals installPackageFiles packageFileNames;
nodeModulesDirs =
if isWorkspace then
["node_modules"] ++ (map (c: "${c}/node_modules") components)
else ["node_modules"];
filterString = concatStringsSep " " (
["--recursive" "--stream"] ++
map (c: "--filter ./${c}") components
) + " ";
buildScripts = ''
pnpm run ${optionalString isWorkspace filterString}${script}
'';
# Flag derived from value computed above, indicating the single dist
# should be copied as $out directly, rather than $out/${distDir}
computedDistDirIsOut =
length computedDistFiles == 1 && distDirIsOut && !isWorkspace;
in
stdenv.mkDerivation (
recursiveUpdate
(rec {
inherit src name nativeBuildInputs;

postUnpack = ''
${optionalString (pnpmWorkspaceYaml != null) ''
cp ${pnpmWorkspaceYaml} pnpm-workspace.yaml
''}
${forEachComponent (component:
''mkdir -p "${component}"'')
}
'';

configurePhase = ''
export HOME=$NIX_BUILD_TOP # Some packages need a writable HOME
export npm_config_nodedir=${nodejs}
Expand All @@ -56,29 +125,44 @@ in
${if installInPlace
then passthru.nodeModules.buildPhase
else ''
${if !copyNodeModules
then "ln -s"
else "cp -r"
} ${passthru.nodeModules}/node_modules node_modules
''
else
forEachConcat (
nodeModulesDir: ''
${copyLink} ${passthru.nodeModules}/${nodeModulesDir}${rsyncSlash} ${nodeModulesDir}
'') nodeModulesDirs
}
runHook postConfigure
'';

buildPhase = ''
${concatStringsSep "\n" (
mapAttrsToList
(n: v: ''export ${n}="${v}"'')
buildEnv
)}
runHook preBuild
pnpm run ${script}
${buildScripts}
runHook postBuild
'';

installPhase = ''
runHook preInstall
${if distDir == "." then "cp -r" else "mv"} ${distDir} $out
${if computedDistDirIsOut then ''
${if distDir == "." then "cp -r" else "mv"} ${distDir} $out
''
else ''
mkdir -p $out
${forEachConcat (dDir: ''
cp -r --parents ${dDir} $out
'') computedDistFiles
}
''
}
runHook postInstall
'';
Expand Down Expand Up @@ -112,18 +196,19 @@ in
inherit nativeBuildInputs;

unpackPhase = concatStringsSep "\n"
(
( [ # components is an empty list for non workspace builds
(forEachComponent (component: ''
mkdir -p "${component}"
'')) ] ++
map
(v:
let
nv = if isAttrs v then v else { name = "."; value = v; };
in
"cp -vr ${nv.value} ${nv.name}"
"cp -vr \"${nv.value}\" \"${nv.name}\""
)
([
{ name = "package.json"; value = packageJSON; }
{ name = "pnpm-lock.yaml"; value = passthru.patchedLockfileYaml; }
] ++ extraNodeModuleSources)
([{ name = "pnpm-lock.yaml"; value = passthru.patchedLockfileYaml; }]
++ packageFilesWithoutLockfile)
);

buildPhase = ''
Expand All @@ -140,25 +225,29 @@ in
else "cp -RL"
} ${passthru.pnpmStore} $(pnpm store path)
${lib.optionalString copyPnpmStore "chmod -R +w $(pnpm store path)"}
${optionalString copyPnpmStore "chmod -R +w $(pnpm store path)"}
${concatStringsSep "\n" (
mapAttrsToList
(n: v: ''export ${n}="${v}"'')
installEnv
)}
pnpm install ${optionalString noDevDependencies "--prod "}--frozen-lockfile --offline
pnpm install --stream ${optionalString noDevDependencies "--prod "}--frozen-lockfile --offline
'';

installPhase = ''
mkdir -p $out
cp -r node_modules/. $out/node_modules
${forEachComponent (component: ''
mkdir -p $out/"${component}"
cp -r "${component}/node_modules" $out/"${component}/node_modules"
'')}
'';
};
};

})
(attrs // { extraNodeModuleSources = null; installEnv = null; })
(attrs // { extraNodeModuleSources = null; installEnv = null; buildEnv = null;})
);
}

0 comments on commit c3cfff8

Please sign in to comment.