node2nix icon indicating copy to clipboard operation
node2nix copied to clipboard

Is there a way to get just a package's dependencies from package.json?

Open ersinakinci opened this issue 5 years ago • 7 comments

I'd like to use node2nix to install all of my web app's dependencies inside of a custom Nix shell. Right now, node2nix supports a few flows for getting close but not quite there:

  • You can run node2nix on a standard package.json file, then the resulting set in node-packages.nix has a shell attribute that points to a derivation for a nix-shell that contains all the dependencies.
    • Problem: you're forced to use node2nix's generated shell, doesn't work with custom shells. Perhaps you could use some overrideAttrs hackery or something, but this isn't a clean approach.
  • You can run node2nix on a standard package.json file, then the resulting set in node-packages.nix has a package attribute that points to an NPM package for the whole project, including dependencies.
    • Problem: this isn't the same as importing just the dependencies. NODE_PATH in the generated shell doesn't contain the project's dependencies.
  • You can run node2nix on a JSON file that contains an array with all your dependencies in it, and the resulting set in node-packages.nix will contain each of your dependencies. You can then pass these dependencies into mkShell { buildInputs = [...] }.
    • Problem: you need to maintain a file separate from the standard package.json with just the dependencies in an array.

Ideally, I'd like to run node2nix on a standard package.json and be able to access dependencies and devDependencies as attributes on node-packages.nix. In lieu of that, is there a way to achieve what I'm trying to achieve?

ersinakinci avatar Feb 23 '20 20:02 ersinakinci

Here's an example of my custom shell.nix:

{ pkgs ? import <nixpkgs> {} }:

with pkgs;

let
  nodeEnv = callPackage ./node-env.nix {};
  nodePackages = callPackage ./node-packages.nix { inherit nodeEnv; };
in mkShell {
  buildInputs = [
    nodejs
    # other deps
  ]; # ++ nodePackages.dependencies (???)
}

ersinakinci avatar Feb 23 '20 20:02 ersinakinci

I've dug into the generated code a bit more and I see a couple of things:

  1. node-env.nix's buildNodeShell does more than just package dependencies and append them to buildInputs, it also generates a dummy package.json for the dependencies package that gets generated and sets PATH and NODE_PATH for the shell to use the NPM dependencies. These features are non-trivial to replicate; i.e., my initial assumption that I could just simply "add to buildInputs" was wrong.
  2. I can achieve my particular use case by passing globalBuildInputs to my callPackage invocation:
{ pkgs ? import <nixpkgs> {} }:

with pkgs;

let
  nodeEnv = callPackage ./node-env.nix {};
  nodePackages = callPackage ./node-packages.nix {
    globalBuildInputs = [
      nodejs # not technically necessary since nodejs gets pulled in anyway
      # other deps
    ];
    inherit nodeEnv;
  };
in nodePackages.shell

That said, I'm not crazy about extending the shell derivation in this way. The syntax feels inelegant. I would prefer something more modular, like being able to grab a dependencies derivation and handle setting PATH and NODE_PATH in a different way, to exercise a more granular level of control.

ersinakinci avatar Feb 23 '20 22:02 ersinakinci

EDIT: I probably should play a bit more with the node2nix before commenting as I'm still discovering more ways to achieve what I wanted by using overrides.

OLD: I want to second @earksiinni suggestion that it would be much easier for custom development shells but also deployments to have direct access to some partial products of package buildup like sources (to make overrides) or args from node-packages.nix.

paluh avatar Feb 24 '20 15:02 paluh

@paluh can you share some of your discoveries regarding how to use overrides and what your goals are? Would be great to compile some info on best practices.

ersinakinci avatar Feb 24 '20 18:02 ersinakinci

@earksiinni I think I should drop my EDIT from previous comment - I'm stuck with my deployment again :-( Sorry for my previous messy comment.

I also think that your suggestion to expand on a given problem solutions is great. I'm going to try to provide a minimal example of my particular package build problem and discuss this in the context of nix-shell and the final deployment expression and their potential consistency and simplicity.

paluh avatar Feb 26 '20 13:02 paluh

It may be worth mentioning here that it's pretty non-obvious, but you can access nodePackages.shell.nodeDependencies as a derivation you can throw into a buildInputs or use to set up $PATH or $NODE_PATH directly (as I just discovered and mentioned in https://github.com/svanderburg/node2nix/issues/168#issuecomment-611108882).

ryanartecona avatar Apr 08 '20 19:04 ryanartecona

Something like this will set up a simple dev shell:

{
          devShell = pkgs.mkShell {
            buildInputs = with pkgs;
              with nodePackages; [
                nodejs
                node2nix

                nodeEnv.shell.nodeDependencies
              ];

            shellHook = ''
              export NODE_PATH=${nodeEnv.shell.nodeDependencies}/lib/node_modules
            '';
          };
}

The nodeEnv should be created like so:

{
            nodeEnv = pkgs.callPackage ./default.nix { };
}

YMMV, various npm packages rely on node_modules being in the package root. If you really want to make this work, maybe create a symlink with the shellHook instead of overriding the unreliable NODE_PATH variable, but I imagine it being read-only will annoy some npm packages as well.

TLATER avatar Apr 06 '21 22:04 TLATER