javascriptnode.jsnix

Why does this javascript import fail in a nix build but succeed in a nix devshell?


My goal is that the following command should run generate.js which will use d3.js to build a svg file.

$ nix build 'github:MatrixManAtYrService/buildsvg#svg'
$ ls result
    output.svg

That repo (https://github.com/MatrixManAtYrService/buildsvg) has a flake.nix with this derivation in the outputs:

packages.svg = pkgs.stdenv.mkDerivation rec {
  name = "generate-svg";
  src = self;
  buildInputs = [ pkgs.nodePackages.nodejs ];
  buildPhase = ''
    export NODE_PATH=${src}/node_modules
    node ${src}/generate.js
  '';

  installPhase = ''
    mkdir -p $out
    cp output.svg $out/
  '';
};

But instead of completing, the node command fails with this error:

Error: Cannot find package '/nix/store/a76p6qgrxlx0safvxrqx5aknhszvg703-source/node_modules/opentype.js/package.json' imported from /nix/store/a76p6qgrxlx0safvxrqx5aknhszvg703-source/generate.js
    at legacyMainResolve (node:internal/modules/esm/resolve:215:26)
    at packageResolve (node:internal/modules/esm/resolve:841:14)
    at moduleResolve (node:internal/modules/esm/resolve:927:18)
    at defaultResolve (node:internal/modules/esm/resolve:1157:11)
    at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:390:12)
    at ModuleLoader.resolve (node:internal/modules/esm/loader:359:25)
    at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:234:38)
    at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:87:39)
    at link (node:internal/modules/esm/module_job:86:36) {
  code: 'ERR_MODULE_NOT_FOUND'
}
Node.js v20.12.2

The top of generate.js looks like this:

import fs from 'fs';
import * as d3 from 'd3';
import opentype from 'opentype.js';  // <--- failure is here
import D3Node from 'd3-node';

So I know that some of my modules are being imported correctly. Also, If I use a nix devshell, it works fine:

devShells.default = pkgs.mkShell {
  buildInputs = [
    pkgs.nodePackages.nodejs
  ];
  shellHook = ''
    export NODE_PATH=$PWD/node_modules
  '';
};

$ nix develop
$ node generate.js
SVG generated successfully.

My package.json indicates these dependencies, and I've run npm install which caused them to be downloaded into node_modules (pardon if this is obvious, I'm a nodejs newbie).

I have sprinkled ls statements into the buildPhase and verified that /nix/store/a76p6qgrxlx0safvxrqx5aknhszvg703-source/node_modules/opentype.js/package.json does indeed exist, despite what the error says.

Why is my nix build failing, while the same command works fine in a devshell?


Solution

  • I have a habit of copying .gitignore files from other projects. In this case I mistakenly copied a .gitignore from a python project which included these lines:

    dist/
    bin/
    

    This caused nix to omit certain files when it copied my project into the nix store for the build:

    node_modules/opentype.js/bin/ot
    node_modules/opentype.js/bin/server.js
    node_modules/opentype.js/bin/test-render
    node_modules/opentype.js/dist/opentype.js
    node_modules/opentype.js/dist/opentype.js.map
    node_modules/opentype.js/dist/opentype.min.js
    node_modules/opentype.js/dist/opentype.min.js.map
    node_modules/opentype.js/dist/opentype.module.js
    node_modules/opentype.js/dist/opentype.module.js.map
    

    So opentype.js's package.json was present in the nix store, but the above parts of that package were missing. This caused the import to fail.

    It's unfortunate that the error message did not indicate the files which were actually missing, but instead points the reader at package.json which exists.