Welcome! We notice you're using an outdated browser, which will result in a degraded experience on this site. Please consider a modern, fully supported browser.

webbureaucrat

The articles are just window-dressing for code snippets I want to keep.

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 @[email protected] 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";
}

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 { };
}

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.

I write to learn, so I welcome your constructive criticism. Report issues on GitLab.

← Home