Dealing with Node packages in shells and derivations for static front-end distribution

Newbie here.

I’m trying to set up a flake.nix file for a projects. I see a couple of different conflicting things online on how to best tackle the situation. The two normal-est I see right now are to either use node2nix or leverage your package-lock.json and just install via npm as a part of mkDerivation. Neither of these solution seem to be working for me and I don’t really have my bearings on how to read the error outputs or effectively use the right keywords on a web search. I’m also curious if making node_modules a separate “app” in the Flake world is what makes the most sense.

In my case, I’m trying to use parcel@2.0.0-beta.2 (v1.x in nixpkgs is missing features I need) to compile and bundle various sources together for distribution for a static site.

When I tried node2nix I ran into issues with node-gyp-build for native modules in Parcel. Following the documentation and adding pkgs.nodePackages.node-gyp-build to the buildInputs did not help and the error NPM wants to give me is in /build/.npm/_logs/xxx which I couldn’t figure out where it was to read since the build failed to produce the derivation. I also don’t see in this manner what node2nix really gives me as it seems to be reimplementing what NPM does but is slower.

Trying to run NPM as a part of any of the build steps just ran into sandboxing issues I wasn’t understanding and again the build failed so the NPM logs are then gone(?). Do I want to have node_modules and the relevant binaries as a part of my Nix store or is it not needed or valuable?

Previously I was just using Nix+Flakes to make dev shells and it’s been really nice and Flakes has made it fast and easy to upgrade. I’ve just been using NPM (or Yarn) and doing my work as I was previously using asdf, but now I’d like to actually use nix build but it hasn’t been to straightforward to me on how use Node packages and their binaries for this step. I didn’t have much luck searching other open source projects to understand either.

Any higher-level reading material on the subject or existing, Node-using projects to look at would be greatly appreciated.

1 Like

This is sadly rather difficult to do, and not terribly well documented.

You’ll have to use the shell attribute that node2nix generates, and override its inputs. Easiest way is to create an override.nix file like so:

{ pkgs ? import <nixpkgs> { inherit system; }, system ? builtins.currentSystem
}:

let nodePackages = import ./default.nix { inherit pkgs system; };
in nodePackages // {
  shell = nodePackages.shell.override {
    buildInputs = with pkgs; [
      pkgs.nodePackages.node-gyp-build
      pkgconfig
    ];
  };
}

If any of your dependencies need more native inputs you’ll want them there too.

From there you can import the shell and use it for a derivation like so:

{ stdenv, pkgs, nodejs }:

let
    nodeDeps = import ./nix/override.nix { inherit pkgs };

in stdenv.mkDerivation {
    name = "parcel-templates";
    src = ./.;

    nativeBuildInputs = [ nodejs ];
    buildPhase = ''
         ln -s ${nodeDeps}/lib/node_modules ./node_modules
         export PATH="${nodeDeps}/bin:$PATH"
         npm run build
    '';

    installPhase = ''
         cp -r dist $out/
    '';
};

In theory the shell can also be used in a shell.nix, in practice I need to fix parcel’s module management.

Now for the why… Well, I want to build my whole system with nix, so there it’s valuable; no other way to do this (except importing a tarball, which is awful).

If you just develop on a project, the only thing you get from this is the ability to build your frontend without installing npm explicitly on your system. If anyone ever fixes their module management, that will extend to development.

I like this, especially because it’ll mean no more ~/.npm stuff, but npm is decent at reproducibility now, so this isn’t really necessary unless you want to use the larger nix ecosystem (which is a good use!).

1 Like