Commit 16722903 authored by Winter's avatar Winter Committed by Yt
Browse files

buildNpmPackage: init

parent e1221120
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -289,3 +289,8 @@
# Dotnet
/pkgs/build-support/dotnet          @IvarWithoutBones
/pkgs/development/compilers/dotnet  @IvarWithoutBones

# Node.js
/pkgs/build-support/node/build-npm-package      @winterqt
/pkgs/build-support/node/fetch-npm-deps         @winterqt
/doc/languages-frameworks/javascript.section.md @winterqt
+55 −0
Original line number Diff line number Diff line
@@ -157,6 +157,61 @@ git config --global url."https://github.com/".insteadOf git://github.com/

## Tool specific instructions {#javascript-tool-specific}

### buildNpmPackage {#javascript-buildNpmPackage}

`buildNpmPackage` allows you to package npm-based projects in Nixpkgs without the use of an auto-generated dependencies file (as used in [node2nix](#javascript-node2nix)). It works by utilizing npm's cache functionality -- creating a reproducible cache that contains the dependencies of a project, and pointing npm to it.

```nix
{ lib, buildNpmPackage, fetchFromGitHub }:

buildNpmPackage rec {
  pname = "flood";
  version = "4.7.0";

  src = fetchFromGitHub {
    owner = "jesec";
    repo = pname;
    rev = "v${version}";
    hash = "sha256-BR+ZGkBBfd0dSQqAvujsbgsEPFYw/ThrylxUbOksYxM=";
  };

  patches = [ ./remove-prepack-script.patch ];

  npmDepsHash = "sha256-s8SpZY/1tKZVd3vt7sA9vsqHvEaNORQBMrSyhWpj048=";

  NODE_OPTIONS = "--openssl-legacy-provider";

  meta = with lib; {
    description = "A modern web UI for various torrent clients with a Node.js backend and React frontend";
    homepage = "https://flood.js.org";
    license = licenses.gpl3Only;
    maintainers = with maintainers; [ winter ];
  };
}
```

#### Arguments {#javascript-buildNpmPackage-arguments}

* `npmDepsHash`: The output hash of the dependencies for this project. Can be calculated in advance with [`prefetch-npm-deps`](#javascript-buildNpmPackage-prefetch-npm-deps).
* `makeCacheWritable`: Whether to make the cache writable prior to installing dependencies. Don't set this unless npm tries to write to the cache directory, as it can slow down the build.
* `npmBuildScript`: The script to run to build the project. Defaults to `"build"`.
* `npmFlags`: Flags to pass to all npm commands.
* `npmInstallFlags`: Flags to pass to `npm ci`.
* `npmBuildFlags`: Flags to pass to `npm run ${npmBuildScript}`.
* `npmPackFlags`: Flags to pass to `npm pack`.

#### prefetch-npm-deps {#javascript-buildNpmPackage-prefetch-npm-deps}

`prefetch-npm-deps` can calculate the hash of the dependencies of an npm project ahead of time.

```console
$ ls
package.json package-lock.json index.js
$ prefetch-npm-deps package-lock.json
...
sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
```

### node2nix {#javascript-node2nix}

#### Preparation {#javascript-node2nix-preparation}
+54 −0
Original line number Diff line number Diff line
{ lib, stdenv, fetchNpmDeps, npmHooks, nodejs }:

{ name ? "${args.pname}-${args.version}"
, src ? null
, srcs ? null
, sourceRoot ? null
, patches ? [ ]
, nativeBuildInputs ? [ ]
, buildInputs ? [ ]
  # The output hash of the dependencies for this project.
  # Can be calculated in advance with prefetch-npm-deps.
, npmDepsHash ? ""
  # Whether to make the cache writable prior to installing dependencies.
  # Don't set this unless npm tries to write to the cache directory, as it can slow down the build.
, makeCacheWritable ? false
  # The script to run to build the project.
, npmBuildScript ? "build"
  # Flags to pass to all npm commands.
, npmFlags ? [ ]
  # Flags to pass to `npm ci`.
, npmInstallFlags ? [ ]
  # Flags to pass to `npm rebuild`.
, npmRebuildFlags ? [ ]
  # Flags to pass to `npm run ${npmBuildScript}`.
, npmBuildFlags ? [ ]
  # Flags to pass to `npm pack`.
, npmPackFlags ? [ ]
, ...
} @ args:

let
  npmDeps = fetchNpmDeps {
    inherit src srcs sourceRoot patches;
    name = "${name}-npm-deps";
    hash = npmDepsHash;
  };

  inherit (npmHooks.override { inherit nodejs; }) npmConfigHook npmBuildHook npmInstallHook;
in
stdenv.mkDerivation (args // {
  inherit npmDeps npmBuildScript;

  nativeBuildInputs = nativeBuildInputs ++ [ nodejs npmConfigHook npmBuildHook npmInstallHook ];
  buildInputs = buildInputs ++ [ nodejs ];

  strictDeps = true;

  # Stripping takes way too long with the amount of files required by a typical Node.js project.
  dontStrip = args.dontStrip or true;

  passthru = { inherit npmDeps; } // (args.passthru or { });

  meta = (args.meta or { }) // { platforms = args.meta.platforms or nodejs.meta.platforms; };
})
+35 −0
Original line number Diff line number Diff line
{ lib, makeSetupHook, nodejs, srcOnly, diffutils, jq, makeWrapper }:

{
  npmConfigHook = makeSetupHook
    {
      name = "npm-config-hook";
      substitutions = {
        nodeSrc = srcOnly nodejs;

        # Specify the stdenv's `diff` and `jq` by abspath to ensure that the user's build
        # inputs do not cause us to find the wrong binaries.
        # The `.nativeDrv` stanza works like nativeBuildInputs and ensures cross-compiling has the right version available.
        diff = "${diffutils.nativeDrv or diffutils}/bin/diff";
        jq = "${jq.nativeDrv or jq}/bin/jq";

        nodeVersion = nodejs.version;
        nodeVersionMajor = lib.versions.major nodejs.version;
      };
    } ./npm-config-hook.sh;

  npmBuildHook = makeSetupHook
    {
      name = "npm-build-hook";
    } ./npm-build-hook.sh;

  npmInstallHook = makeSetupHook
    {
      name = "npm-install-hook";
      deps = [ makeWrapper ];
      substitutions = {
        hostNode = "${nodejs}/bin/node";
        jq = "${jq.nativeDrv or jq}/bin/jq";
      };
    } ./npm-install-hook.sh;
}
+37 −0
Original line number Diff line number Diff line
# shellcheck shell=bash

npmBuildHook() {
    echo "Executing npmBuildHook"

    runHook preBuild

    if [ -z "${npmBuildScript-}" ]; then
        echo
        echo "ERROR: no build script was specified"
        echo 'Hint: set `npmBuildScript`, override `buildPhase`, or set `dontNpmBuild = true`.'
        echo

        exit 1
    fi

    if ! npm run "$npmBuildScript" $npmBuildFlags "${npmBuildFlagsArray[@]}" $npmFlags "${npmFlagsArray[@]}"; then
        echo
        echo 'ERROR: `npm build` failed'
        echo
        echo "Here are a few things you can try, depending on the error:"
        echo "1. Make sure your build script ($npmBuildScript) exists"
        echo '2. If the error being thrown is something similar to "error:0308010C:digital envelope routines::unsupported", add `NODE_OPTIONS = "--openssl-legacy-provider"` to your derivation'
        echo "  See https://github.com/webpack/webpack/issues/14532 for more information."
        echo

        exit 1
    fi

    runHook postBuild

    echo "Finished npmBuildHook"
}

if [ -z "${dontNpmBuild-}" ] && [ -z "${buildPhase-}" ]; then
    buildPhase=npmBuildHook
fi
Loading