DEV Community

webbureaucrat
webbureaucrat

Posted on • Originally published at webbureaucrat.dev on

Building a Directory with `buildNpmPackage` in a Nix Derivation

Candidly, I'm very surprised it seems like no one has written this article yet, but I looked and it seems like no one has. The question I set out to answer is how to use a Nix derivation to build an npm package where the build product is not a single "bin" executable file but rather a directory.

Surely, this is a wildly common use cases in JavaScript, right? People compile TypeScript projects into directories all the time. (I prefer ReScript, but it's much the same.) Surely, not everyone uses a script bundler, right? (Please don't disillusion me!)

But I had more trouble with this than I expected. Fortunately, the Nix community is excellent, and @BryanBennett@tilde.zone on Mastodon gave me the answer.

For my setup, I'm building a public/ directory using an npm "build" script set to npx eleventy. This builds the static website.

This is my derivative:

mysite.nix

{ buildNpmPackage, lib }:
let
  fs = lib.fileset;
  sourceFiles = fs.gitTracked ./.;
in
buildNpmPackage rec {
  npmDepsHash = "sha256-+4lLBQ+UQ2XT0wwE6jADxG1UNZjLkQCLvvN1SdiUwZY=";
  pname = "mysite";
  src = fs.toSource {
    root = ./.;
    fileset = sourceFiles;
  };
  postInstall = ''
cp -rv public/ $out
'';
  version = "0.0.1";
}
Enter fullscreen mode Exit fullscreen mode

The key here of course is the postInstall script. The folder is there for us and we just have to go get it.

(For completeness, if you're a beginner wondering how I got the npmDepsHash, a good trick is just to leave the string empty so that the error message you get tells you what the string should be. The rest of this should look at least somewhat intuitive even if the Nix syntax terrifies you as much as it does me.)

There's no magic in the default.nix, here. Just pass in some packages and reference mysite.nix.

default.nix

let
  # nixpkgs-24.11-darwin
  # 2025-03-01
  nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/archive/26a98b6560fe0526b324d76619ffc55e61b4625e.tar.gz";
  pkgs = import nixpkgs { config = {}; overlays = []; };
in
{
  mysite = pkgs.callPackage ./mysite.nix { };
}
Enter fullscreen mode Exit fullscreen mode

And that's really all there is to it! Nix is often criticized for not having good documentation, so I figure every example of someone doing something in Nix is bound to help the community a little.

Top comments (0)