node.jsnix

How do I build a Node-based derivation that outputs a directory?


I'm pretty new to Nix, and I'm trying to build a derivation around an Eleventy static site project.

I have a build script in my package.json file defined as "npx eleventy". When I run npm run build, it compiles all the source files into a folder of static assets called public/.

I have a derivation defined like:

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;
  };
  version = "0.0.1";
}

and default.nix

let
  # channel 24.11-darwin
  # 2025-02-20
  nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/archive/d27f39bcb57207b9b891f7beaed84bd50e757201.tar.gz";
  pkgs = import nixpkgs { config = {}; overlays = []; };
in
{
  mysite = pkgs.callPackage ./mysite.nix { };
}

(Tip: this is a nixpkgs hash that for some cursed reason builds Node's entire runtime from source, and it takes several hours. I really don't think my issue is specific to the particular version of the runtime I'm using, so if you're trying to reproduce my issue, it would be a good idea to pin to a different commit.)

I can build the site using nix-build --attr mysite and it builds just fine, and I can even see the output from the Eleventy command claiming to be writing files out too a folder called "public", so I know it's working roughly how I expect, but it doesn't output a directory called public/ anywhere in the result symlinked directory.

And perhaps this makes sense, right? Because there's no "bin" attribute to my package.json (Okay, I admit having tried that despite it making no sense.)

So I guess what I'm asking is: Is there a good way of making buildNpmPackage derivations that output a directory instead of a single bin? Or are derivations just a bad tool for this?


Solution

  • Hat tip to @BryanBennett@tilde.zone who gave me the answer on Mastodon.

    buildNpmPackage inherits from mkDerivation, so you can just get the directory by overriding postInstall.

    buildNpmPackage rec {
      npmDepsHash = "sha256-+4lLBQ+UQ2XT0wwE6jADxG1UNZjLkQCLvvN1SdiUwZY=";
      pname = "mysite";
      src = fs.toSource {
        root = ./.;
        fileset = sourceFiles;
      };
      postInstall = ''
    cp -rv public/ $out
    '';
      version = "0.0.1";