Home Explore Blog Models CI



nixpkgs

7th chunk of `doc/languages-frameworks/javascript.section.md`
d9f64d72817cec0a7f7a102d11d33136aeead91a551ef1d8000000010000100a
Commands like `npm install` & `npm add` that write packages & executables need to be used with `--package-lock-only`.

This means `npm` installs dependencies by writing into `package-lock.json` without modifying the `node_modules` folder. Installation happens through reloading the devShell.
This might be best practice since it gives the `nix shell` virtually exclusive ownership over your `node_modules` folder.

It's recommended to set `package-lock-only = true` in your project-local [`.npmrc`](https://docs.npmjs.com/cli/v11/configuring-npm/npmrc).
:::

### corepack {#javascript-corepack}

This package puts the corepack wrappers for pnpm and yarn in your PATH, and they will honor the `packageManager` setting in the `package.json`.

### node2nix {#javascript-node2nix}

#### Preparation {#javascript-node2nix-preparation}

You will need to generate a Nix expression for the dependencies. Don't forget the `-l package-lock.json` if there is a lock file. Most probably you will need the `--development` to include the `devDependencies`

So the command will most likely be:
```sh
node2nix --development -l package-lock.json
```

See `node2nix` [docs](https://github.com/svanderburg/node2nix) for more info.

#### Pitfalls {#javascript-node2nix-pitfalls}

- If upstream package.json does not have a "version" attribute, `node2nix` will crash. You will need to add it like shown in [the package.json section](#javascript-upstream-package-json).
- `node2nix` has some [bugs](https://github.com/svanderburg/node2nix/issues/238) related to working with lock files from npm distributed with `nodejs_16`.
- `node2nix` does not like missing packages from npm. If you see something like `Cannot resolve version: vue-loader-v16@undefined` then you might want to try another tool. The package might have been pulled off of npm.

### pnpm {#javascript-pnpm}

Pnpm is available as the top-level package `pnpm`. Additionally, there are variants pinned to certain major versions, like `pnpm_8` and `pnpm_9`, which support different sets of lock file versions.

When packaging an application that includes a `pnpm-lock.yaml`, you need to fetch the pnpm store for that project using a fixed-output-derivation. The functions `pnpm_8.fetchDeps` and `pnpm_9.fetchDeps` can create this pnpm store derivation. In conjunction, the setup hooks `pnpm_8.configHook` and `pnpm_9.configHook` will prepare the build environment to install the pre-fetched dependencies store. Here is an example for a package that contains `package.json` and a `pnpm-lock.yaml` files using the above `pnpm_` attributes:

```nix
{
  stdenv,
  nodejs,
  # This is pinned as { pnpm = pnpm_9; }
  pnpm,
}:

stdenv.mkDerivation (finalAttrs: {
  pname = "foo";
  version = "0-unstable-1980-01-01";

  src = {
    #...
  };

  nativeBuildInputs = [
    nodejs
    pnpm.configHook
  ];

  pnpmDeps = pnpm.fetchDeps {
    inherit (finalAttrs) pname version src;
    fetcherVersion = 2;
    hash = "...";
  };
})
```

NOTE: It is highly recommended to use a pinned version of pnpm (i.e., `pnpm_8` or `pnpm_9`), to increase future reproducibility. It might also be required to use an older version if the package needs support for a certain lock file version.

In case you are patching `package.json` or `pnpm-lock.yaml`, make sure to pass `finalAttrs.patches` to the function as well (i.e., `inherit (finalAttrs) patches`.

`pnpm.configHook` supports adding additional `pnpm install` flags via `pnpmInstallFlags` which can be set to a Nix string array:

```nix
{ pnpm }:

stdenv.mkDerivation (finalAttrs: {
  pname = "foo";
  version = "0-unstable-1980-01-01";

  src = {
    # ...
  };

  pnpmInstallFlags = [ "--shamefully-hoist" ];

  pnpmDeps = pnpm.fetchDeps { inherit (finalAttrs) pnpmInstallFlags; };
})
```

#### Dealing with `sourceRoot` {#javascript-pnpm-sourceRoot}

If the pnpm project is in a subdirectory, you can just define `sourceRoot` or `setSourceRoot` for `fetchDeps`.
If `sourceRoot` is different between the parent derivation and `fetchDeps`, you will have to set `pnpmRoot` to effectively be the same location as it is in `fetchDeps`.

Title: Nixpkgs Node.js Ecosystem: `npm` Lockfile Best Practices, `corepack`, `node2nix`, and `pnpm` Integration
Summary
This document recommends `npm install --package-lock-only` in dev shells for Nix to exclusively manage `node_modules` via `package-lock.json`. It introduces `corepack` for `pnpm` and `yarn` wrappers, honoring `packageManager`. The `node2nix` section details generating Nix expressions (`--development -l package-lock.json`) and warns of common pitfalls: missing `package.json` 'version', `nodejs_16` lock file bugs, and unresolvable npm packages. Finally, `pnpm` integration is covered, explaining the use of `pnpm` (and versioned `pnpm_X`) with `pnpm_X.fetchDeps` for fetching the pnpm store and `pnpm_X.configHook` for build environment setup. It includes an example and advice on using pinned versions, `pnpmInstallFlags`, and managing `sourceRoot` for subdirectories.