Home

Awesome

Napalm

This project is looking for a new maintainer, see

When faced with a JavaScript codebase, napalm is just what you need.

-- anonymous

Table of contents

Building npm packages in Nix with Napalm

Basic Napalm usage

Use the buildPackage function provided in the default.nix for building npm packages (replace <napalm> with the path to napalm; with niv: niv add nmattia/napalm):

let
    napalm = pkgs.callPackage <napalm> {};
in napalm.buildPackage ./. {}

All executables provided by the npm package will be available in the derivation's bin directory.

NOTE: napalm uses the package's package-lock.json (or npm-shrinkwrap.json) for building a package database. Make sure there is either a package-lock.json or npm-shrinkwrap.json in the source. Alternatively provide the path to the package-lock file:

let
    napalm = pkgs.callPackage <napalm> {};
in napalm.buildPackage ./. { packageLock = <path/to/package-lock>; }

Napalm with Nix flakes

If you want to use Napalm in your flake project, you can do that by adding it to your inputs and either passing napalm.overlays.default to your Nixpkgs instance, or by using the napalm.legacyPackages buildPackage output. To configure the latter's environment, be sure to look at the complicated scenarios and potentially set the nixpkgs input of napalm with follows.

Example flake.nix

{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
  inputs.napalm.url = "github:nix-community/napalm";

  # NOTE: This is optional, but is how to configure napalm's env
  inputs.napalm.inputs.nixpkgs.follows = "nixpkgs";

  outputs = { self, nixpkgs, napalm }: 
  let
    system = "x86_64-linux";
    pkgs = nixpkgs.legacyPackages."${system}";
  in {
    # Assuming the flake is in the same directory as package-lock.json
    packages."${system}".package-name = napalm.legacyPackages."${system}".buildPackage ./. { };

    devShells."${system}".shell-name = pkgs.mkShell {
      nativeBuildInputs = with pkgs; [ nodejs ];
    };
  };
}

Flake Template

There is also a template that can help you use napalm in your project. You can use it in a new, empty directory by running:

nix flake init -t "github:nix-community/napalm"

Handling complicated scenarios with Napalm

Examples below assume that you have imported napalm in some way.

Custom node.js version

Napalm makes it quite simple to use custom node.js (with npm) version. This is controlled via nodejs argument.

Example 1

Changing node.js version to the one that is supplied in nixpkgs:

{ napalm, nodejs-16_x, ... }:
napalm.buildPackage ./. {
	nodejs = nodejs-16_x;
}

Example 2

Changing node.js version to some custom version (just an idea):

{ napalm, nodejs-12_x, ... }:
let
	nodejs = nodejs-12_x.overrideAttrs (old: rec {
		pname = "nodejs";
		version = "12.19.0";
		sha256 = "1qainpkakkl3xip9xz2wbs74g95gvc6125cc05z6vyckqi2iqrrv";
		name = "${pname}-${version}";

		src = builtins.fetchurl {
			url =
			"https://nodejs.org/dist/v${version}/node-v${version}.tar.xz";
			inherit sha256;
		};
	});
in
napalm.buildPackage ./. {
	inherit nodejs;
}

Pre/Post Npm hooks

Napalm allows to specify commands that are run before and after every npm call. These hooks work also for nested npm calls thanks to npm override mechanism.

Example

Patching some folder with executable scripts containing shebangs (that may be generated by npm script):

{ napalm, ... }:
napalm.buildPackage ./. {
	postNpmHook = ''
	patchShebangs tools
	'';
}

Multiple package locks

Napalms allows to specify multiple package locks. This may be useful for some project which consist of some smaller projects.

Example

{ napalm, ... }:
napalm.buildPackage ./. {
	# package-lock.json that is in the root of the project
	# is not required to be specified in `additionalpackagelocks`
	# If you want to specify it, you can use `packageLock` argument.
	additionalPackageLocks = [
	./frontend/package-lock.json
	./tests/package-lock.json
	];
}

Patching npm packages (before fetching them with npm)

This is very useful for errors like: Invalid interpreter

Napalm has an ability to patch fetched npm packages before serving them to the npm. By default patching fixes shebangs and binaries that are localized and the tarballs. Napalm also updates package-lock.json with new integrity hashes.

Example

To enable patching, just use:

{ napalm, ... }:
napalm.buildPackage ./. {
	patchPackages = true;
}

This will force repacking of all dependencies, though, so you might want to patch only specific dependencies by passing an empty attribute set to the next method.

Customizing patching mechanism of npm packages

Sometimes it is required to manually patch some package. Napalm allows that via customPatchPackages attribute. This attribute is a set of that overrides for packages that will be patched.

Example

{ napalm, ... }:
napalm.buildPackage ./. {
	# Arguments that are passed to the overrider:
	# `pkgs` - Nixpkgs used by Napalm
	# `prev` - Current set that will be passed to mkDerivation
	customPatchPackages = {
		"react-native" = {
			"0.65.0" = pkgs: prev: {
				EXAMPLE_ENV_VAR = "XYZ";
				dontBuild = false;
				buildPhase = ''
				# You can copy some stuff here or run some custom stuff
				'';
			};
		};

		# Version is not required. When it is not specified it
		# applies override to all packages with that name.
		"node-gyp-builder" = pkgs: prev: { };
	};
}

How does Napalm work ?

These are general steps that Napalm makes when building packages (if you want to learn more, see source code of default.nix):

  1. Napalm loads all package-lock.json files and parses them. Then it fetches all specified packages into the Nix Store.
  2. (optional) Napalm patches npm packages and stores their output in new location. Then uses this location as default package location in Nix Store.
  3. Napalm creates snapshot that consists of packages names, version and paths to locations in Nix Store that contain them.
  4. (optional) Napalm patches package-lock.json integrity if the packages were patched, so that they will work with npm install.
  5. Napalm sets up napalm-registry which as a main argument accepts snapshot of npm packages and them serves them as if it was npm registry server.
  6. Napalm sets up npm so that it thinks napalm-registry server is default npm registry server.
  7. Napalm overrides npm which allows using custom npm hooks (every time it is called) as well as some other default patching activities.
  8. Napalm calls all the npm commands.
  9. Napalm installs everything automatically or based on what was specified in installPhase.

Napalm - a lightweight npm registry

Under the hood napalm uses its own package registry. The registry is available in default.nix as napalm-registry.

Usage: napalm-registry [-v|--verbose] [--endpoint ARG] [--port ARG] --snapshot ARG

Available options:
  -v,--verbose             Print information about requests
  --endpoint ARG           The endpoint of this server, used in the Tarball URL
  --port ARG               The to serve on, also used in the Tarball URL
  --snapshot ARG           Path to the snapshot file. The snapshot is a JSON
                           file. The top-level keys are the package names. The
                           top-level values are objects mapping from version to
                           the path of the package tarball. Example: { "lodash":
                           { "1.0.0": "/path/to/lodash-1.0.0.tgz" } }
  -h,--help                Show this help text

Similar projects