How to build an NPM project with a code generator in a sibling folder?

Hello!

I have a project that has both a Haskell backend and an Elm frontend part, and they are in a single repo. The backend also has a component that generates some code that is used by the frontend.

(Note: All the code I’m talking about here is available in my github too: GitHub - TheOddler/elmder: Trying out some elm stuff)

So my repo looks something like this (v is an open folder, > a closed one):

v backend
    > src
    > tests
    > elm-gen
    package.yaml
v frontend
    v src
        > Generated
        Main.elm
        ...more elm files
    elm.json
    package.json
flake.nix

When I run stack run elm-gen in the backend folder it’ll generate the needed Elm code in the Generated folder.

And I have a build script that runs parcel build src/index.html in package.json to build the frontend.

Then in my flake.nix I use buildNpmPackage to build the frontend in nix:

packages.frontend = pkgs.buildNpmPackage {
  name = "elmder";
  src = ./frontend;

  preBuild = ''
    (cd ../backend && stack run elm-gen)
  '';

  ... some more stuff that I don't think is relevant for this issue
};

When I run this, it complains ../backend does not exist. I don’t know the details, but it seems to copy whatever folder I specify in src somewhere to then build that.

Fair enough, however when I change src = ./.; it then gives the error that the folder does not contain package-lock.json file.

I found I can also set the sourceRoot, so I tried that, setting sourceRoot = ./frontend;, but then I get a bunch of errors stating chmod: changing permissions of '/nix/store/...-frontend/src/....elm': Operation not permitted

I tried a bunch of other combinations of src, sourceRoot, also tried npmRoot I believe, but nothing seems to work.

Is there a proper way of doing something like this? Do I need to restructure my project so the package-lock.json is in the root? Is there a way to keep it in the frontend folder and keep the frontend and backend separated more like this?

I mentioned it at the start as well, but just in case, all the code is available at GitHub - TheOddler/elmder: Trying out some elm stuff in case I left out relevant parts (but don’t feel obligated to look at it, just asking here is good too)

Thanks!

Did you get this figured out? I’ve been trying to figure out how to compile elm code with ParcelJS as well and am currently stumped after a long day.

Not yet :frowning:

I first had to figure out how to properly build my haskell in Nix :sweat_smile: But I have that now, so possibly I’ll look at it again one of the next weekends when I have time again.

Oh, but if you only need to build parcel code, without generating any code, that did work for me.

What I have is:

A package.json with a simple build script: "build": "parcel build src/index.html"

A currently broken frontend package in nix, but that’s only broken because the generated code isn’t there when nix builds. So I think if you have no generated code this should work:

packages.frontend = pkgs.buildNpmPackage {
  name = "elmder";
  src = ./frontend;
  npmDepsHash = "sha256-SvlklTgqGSoDyjlHRIjlhBuB4dyYl4Ro1Sc2aBgx76I=";
  installPhase = ''
    mkdir $out
    cp -r dist/ $out
  '';

  preBuild = elmParcelNixFix.preBuild;
  nativeBuildInputs = elmParcelNixFix.nativeBuildPackages;
  npmFlags = elmParcelNixFix.npmFlags;
  configurePhase = elmParcelNixFix.configurePhase;
};

And as you can see there are some references to elmParcelNixFix, those are some fixed (and hacks) I had to do to make elm and parcel to work in nix:

elmParcelNixFix = {
  # To make Elm, Parcel and Nix work together we need do some some fixes:

  # 1. The Elm node package tries to download the binary when installing, this doesn't work in Nix's sandbox, so instead we add it to the nativeBuildPackages, set the npmFlag to not run install scripts, and alter this (https://github.com/elm/compiler/blob/047d5026fe6547c842db65f7196fed3f0b4743ee/installers/npm/bin/elm#L8-L30) script to instead of downloading the binary to just use the one we installed as the native build package
  nativeBuildPackages = [ pkgs.elmPackages.elm ];
  npmFlags = [ "--ignore-scripts" ];
  preBuild = ''
  substituteInPlace node_modules/.bin/elm \
  --replace 'var binaryPath = path.resolve' 'var binaryPath = "${pkgs.lib.getExe pkgs.elmPackages.elm}"; runCommand(); return; //'
  '';

  # 2. Manage the Elm dependencies through nix too, otherwise Parcel will try to download them and that is again not allowed in Nix's sandbox.
  # For this we had to use `elm2nix`, see README on how to generate the required files when updating dependencies.
  configurePhase = pkgs.elmPackages.fetchElmDeps {
  elmPackages = import ./frontend/elm-srcs.nix;
  elmVersion = "0.19.1";
  registryDat = ./frontend/registry.dat;
  };
};

I hope that helps!

Seeing your example was so helpful! I combined it with some recent work on mkElmDerivation and posted it back to your repo. I still haven’t figured out how to pull a file into a flake that is in the parent directory, but maybe on another day :frowning: I’d love to see what you discover related to this problem as well.

1 Like

Not sure when the next time is I’ll take time to try and figure it out, maybe later this week.

Thank you for posting as well! Hopefully it’ll make it easier for others in the future :smiley: