Managing multiple versions of Node.js with nix?

I normally use n or nvm to manage Node.js versions, but that seems to work against the grain of NixOS. This will be my first time packaging anything on NixOS; any pointers would be helpful.

I have some unpublished work in a similar vein that I will try to polish up and gist - I needed to offer multiple, concurrent, historical versions of both Elixir and Erlang. I started it before the link below got published and I still hope to rework it slightly based on what I learned from that.

In particular, if you look in that project at /node.nix you can maybe glean some idea for how to provide multiple derivations of different historical versions of a project.

I hope to post more tonight this or this weekend, but work may get the best of me. Hopefully that gives you a good jumping off point.

1 Like

I actually do this quiet a lot. I have a bundling of NodeJS 10.15.3 here: https://github.com/luminescent-dreams/luminescent-dreams-nixpkgs/blob/04535a1cc3a67d1f1cdd1d01d1256bd9a4b6d85a/nixpkgs/pkgs/dev/node10.nix

And then here’s an example of me using it (note that luminescent-dreams is my own nix channel): https://github.com/luminescent-dreams/fitnesstrax/blob/0a48e2c6e11479f82d200caf8c01c8f31158d2c1/shell.nix

Shouldn’t be too hard to just grab that default.nix file and integrate it with your developer shell.

1 Like

@shanesveller @savannidgerinel Thanks you two, I’m getting the idea. Can I write something like these in my configuration.nix instead of in a separate file (and organize it later)?

EDIT: Yep, just read up 6.1.2. Adding Custom Packages , I could use a let expression in systemPackages to define it. Sweet.

@shanesveller In your example, I see it calls configure.py. Where does this file come from? Is it in the tar?

Yep, you can definetly do that. I generally wouldn’t, though. I’d put it into a shell.nix file at the root of your project directory. That way you can have different versions of node.js associated with different projects without risking conflicts.

1 Like

Cool, I also see nixpkgs has function for building Node based on a supplied version number.

How would you recommend I consume that function (without switching to that version of nixpkgs)?

I’m thinking to copy-paste the file somewhere, and run it with nix-shell.

How would you manage this? I’m hoping it can be as easy as things like n 10 were.

I’m not sure how to make the shell: I copied v13.nix and nodejs.nix to my home folder. Then I tried to make a shell with it and got an error:

$ nix-shell ./v13.nix
error: cannot auto-call a function that has an argument without a default value ('callPackage')

What am I missing?

EDIT: I Googled for the error, and I found this mailing list thread, which basically explains to use with import <nixpkgs> {}; instead of using parameters. For example I can successfully run nix-build on the file when it looks like this:

with import <nixpkgs> {};

let
  buildNodejs = callPackage ./nodejs.nix { inherit openssl icu; };
in
  buildNodejs {
    enableNpm = true;
    version = "13.6.0";
    sha256 = "0jf9nn5i1bijmrcgjvkp37fyz63lwwmxjh7nxipn2vw2qdx6ngsm";
  }

It took really long to compile, and ended with an error:

../src/node_util.cc: In function 'void node::util::Sleep(const v8::FunctionCallbackInfo<v8::Value>&)':
../src/node_util.cc:176:3: error: 'uv_sleep' was not declared in this scope
   uv_sleep(msec);
   ^~~~~~~~
../src/node_util.cc:176:3: note: suggested alternative: 'uv_stop'
   uv_sleep(msec);
   ^~~~~~~~
   uv_stop

I want to figure hot to do it without compiling, just patching the binaries.


If I run nix-shell v13.nix, then it drops me into a new shell, but I still see the system node from before; node -v shows 12.x.x. How do I run the nix-shell command so that it’ll build and use Node v13 in this case?

Alright! I finally learned how to install it from the tarball release. This is what I have:

default.nix:

let
  pkgs = import <nixpkgs> {};
  installNodeJS = import ./nodejs.nix;

in installNodeJS {
  inherit pkgs;
  version = "13.6.0";
  sha256 = "00f01315a867da16d1638f7a02966c608e344ac6c5b7d04d1fdae3138fa9d798";
}

nodejs.nix:

{ pkgs ? import <nixpkgs> {}, version, sha256 }:

  let
    inherit (pkgs) stdenv autoPatchelfHook platforms fetchurl;
    inherit (stdenv) mkDerivation lib;
  
  in mkDerivation {
    inherit version;
  
    name = "nodejs-${version}";
  
    src = fetchurl {
      url = "https://nodejs.org/dist/v${version}/node-v${version}-linux-x64.tar.xz";
      inherit sha256;
    };

    # QUESTION: put glib and autoPatchelfHook in nativeBuildInputs or buildInputs?
    nativeBuildInputs = with pkgs; [autoPatchelfHook];
    buildInputs = with pkgs; [glib];

    installPhase = ''
      echo "joe is installing nodejs"
      mkdir -p $out
      cp -R ./ $out/
    '';
  
    meta = {
      description = "Event-driven I/O framework for the V8 JavaScript engine";
      homepage = https://nodejs.org;
      license = lib.licenses.mit;
      platforms = lib.platforms.linux;
    };

    #TODO do I need this?
    #passthru.python = python2; # to ensure nodeEnv uses the same version
  }

Then in that folder I can run nix-build to build it, then ./result/bin/node to run it.

@shanesveller @savannidgerinel I’m not sure how to make a new shell with the new node in my PATH. How might I do that with nix?

For now I just set export PATH=/path/to/result/bin/:$PATH locally in my current shell, which is easy enough to get working with differing versions.

1 Like

I opened a new topic specifically for this at How to use a nix derivation from a local folder?

You can actually just do callPackage on it and call it a day. The full setup can be found here but the tldr version is just

let pkgs = import <nixpkgs> {};

    buildNodejs = pkgs.callPackage <nixpkgs/pkgs/development/web/nodejs/nodejs.nix> {};
    
    nodejs-8 = buildNodejs {
      enableNpm = true;
      version = "8.17.0";
      sha256 = "1zzn7s9wpz1cr4vzrr8n6l1mvg6gdvcfm6f24h1ky9rb93drc3av";
    };

in pkgs.mkShell rec {
  name = "webdev";
  
  buildInputs = with pkgs; [
    nodejs-8
    (yarn.override { nodejs = nodejs-8; })
  ];
}

FWIW this can also just be written as <nixpkgs/pkgs/development/web/nodejs/nodejs.nix>.

1 Like

Thanks a lot. Revised accordingly.