ajax

mkYarnModules in 2024

As of today, the documentation for the mkYarnModules function in the official nixpkgs docs leaves something to be desired.

There’s no introduction to the tool, an explanation of how it works, or any holistic examples/function signatures.

Through grepping the nixos/nixpkgs GitHub, I managed to create this a minimum viable usage example:

pkgs.mkYarnModules {
    pname = "myYarnProject";
    packageJSON = ./package.json;
    yarnLock = ./yarn.lock;
    version = "0.0.0";
};

When you build this, you’ll get a result like1:

result
├── deps
│  └── myYarnProject
└── node_modules
   ├── myYarnProject -> ../deps/myYarnProject
   ├── example-dep-1
   ├── example-dep-2
   └── ... more deps ...

The whole function signature here:

mkYarnModules = {
    name ? "${pname}-${version}", # safe name and version, e.g. testcompany-one-modules-1.0.0
    pname, # original name, e.g @testcompany/one
    version,
    packageJSON,
    yarnLock,
    yarnNix ? mkYarnNix { inherit yarnLock; },
    offlineCache ? importOfflineCache yarnNix,
    yarnFlags ? [ ],
    ignoreScripts ? true,
    nodejs ? inputs.nodejs,
    yarn ? inputs.yarn.override { inherit nodejs; },
    pkgConfig ? {},
    preBuild ? "",
    postBuild ? "",
    workspaceDependencies ? [], # List of yarn packages
    packageResolutions ? {},
}: {
    # ...
}

Note that you can pass in a yarnNix if you want to. This would be the nix expression resulting from running yarn2nix in the directory containing your package.json and yarn.lock. mkYarnModules will do this for you, but I could see this information being handy to get me out of a jam one day.

Why this came up

I’m using this to get bun support in my project.

Bun is super fast. It’s honestly hard to use npm now for dependency resolution.

There’s not yet a native bun2nix, and I didn’t want to spend a ton of time learning the complexities of dream2nix. Luckily, though, bun can output yarn.lock files2, in addition to the normal bun.lockb. I have this as part of my build process:

bun install --yarn

which generates a yarn.lock that is then picked up later in my derivation:

# example.nix
{pkgs, ...}:
let
nodeDeps =  pkgs.mkYarnModules {
    pname = "exampleDeps";
    packageJSON = ./package.json;
    yarnLock = ./yarn.lock;
    version = "0.0.0";
};
in
pkgs.stdenv.mkDerivation {
    name = "example";
    src = ./.;
    buildInputs = with pkgs; [hugo git nodePackages.prettier tailwindcss];
    buildPhase = ''
        runHook preBuild

        cp -r ${nodeDeps}/node_modules ./.
        # inject deps here ☝️

        tailwindcss -i assets/css/main.css -o static/css/styles.css
        hugo
        prettier -w public '!**/*.{js,css}'

        runHook postBuild
    '';
    installPhase = "cp -r public $out";
}

Future work

I think that a lot could be done to make the documentation on this better. I’ll see if I can find the time to upstream what I’ve learned.


  1. I only tested this on my blog project, which doesn’t have any JavaScript build steps. So this output might look slightly different, depending on your project structure. However, the node_modules and deps directories will still be the same. ↩︎

  2. Yarn v1, to be precise. ↩︎