Node.js: Difference between revisions

m Fix google font override
DHCP (talk | contribs)
m Setup: suggest search.nixos.org instead of cli nix search
 
(13 intermediate revisions by 10 users not shown)
Line 1: Line 1:
__TOC__
__TOC__
{{expansion}}


== Install ==
[https://nodejs.org Node.js] is an open-source, cross-platform [[JavaScript]] runtime environment that allows developers to execute JavaScript code on the server side. Built on the V8 JavaScript engine, it enables the creation of scalable and high-performance applications, particularly for real-time web services.


<syntaxhighlight lang="nix>
== Setup ==
  environment.systemPackages = with pkgs; [ nodejs ];
Adapt or add following line to your system configuration:<syntaxhighlight lang="nix>
environment.systemPackages = with pkgs; [ nodejs ];
</syntaxhighlight>
</syntaxhighlight>


See <code>nix search nixpkgs nodejs</code> for additional versions like <code>nodejs-12_x</code>, etc.
See [https://search.nixos.org/packages?&query=nodejs NixOS Package Search] for additional versions like <code>nodejs_20</code>, etc.
 
== Development environment ==
Also see [[Development environment with nix-shell]] on this wiki.
 
=== Nixpkgs example ===
{{file|shell.nix|nix|3=
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
  nativeBuildInputs = with pkgs.buildPackages; [
    nodejs_22
    yarn
  ];
}
}}
 
=== Corepack example ===
To use specific/pinned versions of your runtime & package manager, a combination of corepack & steam-run can be used
{{file|shell.nix|nix|3=
{ pkgs ? import <nixpkgs> {} }:
 
pkgs.mkShell {
  buildInputs = with pkgs; [
    corepack
    steam-run-free
  ];
  shellHook = ''
    alias deno="steam-run pnpm deno"
  '';
}
}}
 
{{file|package.json|json|3=
{
  "packageManager": "pnpm@11.4.0",
  "devEngines": {
    "runtime": {
      "name": "deno",
      "version": "^2.7.14",
      "onFail": "download"
    }
  }
}
}}


== Packaging ==
== Packaging ==
=== Packaging with <code>buildNpmPackage</code> ===
=== Packaging with <code>buildNpmPackage</code> ===
From the [https://nixos.org/manual/nixpkgs/stable/#javascript-tool-specific Nixpkgs manual]: "<code>buildNpmPackage</code> allows you to package npm-based projects in Nixpkgs without the use of an auto-generated dependencies file (as used in 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."
From the [https://nixos.org/manual/nixpkgs/stable/#javascript-tool-specific Nixpkgs manual]: "<code>buildNpmPackage</code> allows you to package npm-based projects in Nixpkgs without the use of an auto-generated dependencies file (as used in 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."


'''To better understand what happens under the hood and see the latest features see'''
To better understand what happens under the hood and see the latest features [https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/node/build-npm-package/default.nix see the build-npm-package source].


https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/node/build-npm-package/default.nix
Here's a <code>flake.nix</code> example to build a node package from the current directory.
 
<syntaxhighlight lang="nix">
{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
  };
 
  outputs = {
    self,
    nixpkgs,
  }: let
    pkgs = nixpkgs.legacyPackages."x86_64-linux";
  in {
    packages."x86_64-linux".default = pkgs.buildNpmPackage {
      pname = "my-node-script";
      version = "0.1.0";
      src = ./.;
      npmDepsHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
    };
  };
}
</syntaxhighlight>
 
By default, the build phase runs the <code>build</code> script defined in <code>package.json</code>.
 
<syntaxhighlight lang="json">
{
  "scripts": {
    "build": "npm install"
  }
}
</syntaxhighlight>
 
The binaries created in this package are defined by the <code>bin</code> key in <code>package.json</code>. This example will result in a <code>my-node-script</code> binary being created that basically runs <code>node main.js</code>.
 
<syntaxhighlight lang="json">
{
  "bin": {
    "my-node-script": "main.js"
  }
}
</syntaxhighlight>


==== Packaging electron applications ====
==== Packaging electron applications ====
Line 26: Line 113:
yarn2nix uses the yarn nodejs tool to create a file called yarn.lock, which in return can be used by yarn2nix to generate a usable yarn expression.
yarn2nix uses the yarn nodejs tool to create a file called yarn.lock, which in return can be used by yarn2nix to generate a usable yarn expression.
This is what was needed to convert a small application server  [https://git.shackspace.de/rz/muellshack/tree/3be09715911628b164fa1cf346387555ca26a5b1 shackspace muellshack]:
This is what was needed to convert a small application server  [https://git.shackspace.de/rz/muellshack/tree/3be09715911628b164fa1cf346387555ca26a5b1 shackspace muellshack]:
<syntaxHighlight lang=console>
<syntaxhighlight lang="console">
$ nix-shell -p yarn yarn2nix
$ nix-shell -p yarn yarn2nix
$ yarn install
$ yarn install # creates yarn.lock
# creates yarn.lock
$ yarn2nix > yarn.nix
$ yarn2nix > yarn.nix
$ vim package.json
$ vim package.json # add: "bin": "app.js",
# add:   "bin": "app.js",
$ cat > default.nix <<EOF
$ cat > default.nix <<EOF
with (import <nixpkgs> {});
with (import <nixpkgs> {});
Line 50: Line 135:


$ result/bin/muellshack
$ result/bin/muellshack
</syntaxHighlight>
</syntaxhighlight>


The complete diff can be found at [https://git.shackspace.de/rz/muellshack/commit/f5e498acd47695c4947dc1b5ddebfad2eee8d653 the respective diff]
The complete diff can be found at [https://git.shackspace.de/rz/muellshack/commit/f5e498acd47695c4947dc1b5ddebfad2eee8d653 the respective diff]


== FAQ ==
== Troubleshooting ==


=== Using <code>npm install -g</code> fails ===
=== Using <code>npm install -g</code> fails ===
Line 81: Line 166:
This is done through configuring npm and amending your <tt>PATH</tt>.<ref>[https://logs.nix.samueldr.com/nixos/2018-07-09#1358500 joepie91 on #nixos, 2018-07-09]</ref>
This is done through configuring npm and amending your <tt>PATH</tt>.<ref>[https://logs.nix.samueldr.com/nixos/2018-07-09#1358500 joepie91 on #nixos, 2018-07-09]</ref>


<pre>
<syntaxhighlight lang="console">
$ npm set prefix ~/.npm-global
$ npm set prefix ~/.npm-global
</pre>
</syntaxhighlight>


Then, amend your <tt>PATH</tt> so it looks into <tt>$HOME/.npm-global</tt>.
Then, amend your <tt>PATH</tt> so it looks into <tt>$HOME/.npm-global</tt>.
Line 91: Line 176:
This is a bit harder to implement, but creates a bit more strictness in your environment; it will be impossible accidentally make use of what would have been a globally installed package. The idea is to install it to either a temporary transitory folder or to the project folder, then run the locally installed instance of the package, the binaries are found under <tt>node_packages/.bin/</tt>.<ref>[https://logs.nix.samueldr.com/nixos/2018-07-17#1386090; samueldr on #nixos, 2018-07-17]</ref>
This is a bit harder to implement, but creates a bit more strictness in your environment; it will be impossible accidentally make use of what would have been a globally installed package. The idea is to install it to either a temporary transitory folder or to the project folder, then run the locally installed instance of the package, the binaries are found under <tt>node_packages/.bin/</tt>.<ref>[https://logs.nix.samueldr.com/nixos/2018-07-17#1386090; samueldr on #nixos, 2018-07-17]</ref>


<pre>
<syntaxhighlight lang="console">
$ npm install uglify-es
$ npm install uglify-es
[ ... ]
[ ... ]


$ ls -l node_modules/.bin/
$ ls -l node_modules/.bin/
total 0
total 0
lrwxrwxrwx 1 user users 25 Jul 17 15:34 uglifyjs -> ../uglify-es/bin/uglifyjs
lrwxrwxrwx 1 user users 25 Jul 17 15:34 uglifyjs -> ../uglify-es/bin/uglifyjs


$ node_modules/.bin/uglifyjs --help
$ node_modules/.bin/uglifyjs --help
   Usage: uglifyjs [options] [files...]
   Usage: uglifyjs [options] [files...]
</pre>
</syntaxhighlight>


===== direnv =====
===== direnv =====
Line 115: Line 200:
==== Using <code>npx</code> ====
==== Using <code>npx</code> ====


<pre>
<syntaxhighlight lang="console">
$ nix-shell -p nodejs-8_x
$ nix-shell -p nodejs-8_x


$ npx create-react-app --help
$ npx create-react-app --help
npx: installed 67 in 1.671s
npx: installed 67 in 1.671s
   Usage: create-react-app <project-directory> [options]
   Usage: create-react-app <project-directory> [options]
[...]
[...]
</pre>
</syntaxhighlight>


==== Using <code>npx</code> with binaries ====
==== Using <code>npx</code> with binaries ====
Line 131: Line 216:
For example, <code>npx cypress open</code> might give an error like:
For example, <code>npx cypress open</code> might give an error like:


<pre>
<syntaxhighlight lang="console">
$ npx cypress open
$ npx cypress open


Line 141: Line 226:
----------
----------
spawn /home/rkb/.cache/Cypress/4.10.0/Cypress/Cypress ENOENT
spawn /home/rkb/.cache/Cypress/4.10.0/Cypress/Cypress ENOENT
</pre>
</syntaxhighlight>


One quick workaround for this is [[Steam#FHS environment only| to use <code>steam-run</code>]] to provide a placeholder FHS environment that *should* work; e.g. for the Cypress example above:
One quick workaround for this is [[Steam#FHS environment only| to use <code>steam-run</code>]] to provide a placeholder FHS environment that *should* work; e.g. for the Cypress example above:


<pre>
<syntaxhighlight lang="console">
$ nix-env -iA nixos.steam-run
$ nix-env -iA nixos.steam-run


Line 151: Line 236:


-- Cypress opens successfully!
-- Cypress opens successfully!
</pre>
</syntaxhighlight>


(Inspired by [https://discourse.nixos.org/t/how-to-make-nixos-so-easy-that-people-can-be-productive-up-front-without-having-to-first-learn-the-nix-language/5625 this discussion on discourse.nixos.org])
(Inspired by [https://discourse.nixos.org/t/how-to-make-nixos-so-easy-that-people-can-be-productive-up-front-without-having-to-first-learn-the-nix-language/5625 this discussion on discourse.nixos.org])
Line 157: Line 242:
'''Google-fonts fetch failure with NextJS'''
'''Google-fonts fetch failure with NextJS'''


Nextjs is a popular React framework and comes with built-in support with support for Google fonts. If a NPM project uses it, <syntaxhighlight lang="shell">
Nextjs is a popular React framework and comes with built-in support with support for Google fonts. If a NPM project uses it, <syntaxhighlight lang="console">
npm run build # which calls "next build"
$ npm run build # which calls "next build"
</syntaxhighlight>will try to fetch and optimize the Google fonts during a  nix build run, which will fail in Nix's isolated sandbox without internet:<syntaxhighlight lang="shell">
</syntaxhighlight>will try to fetch and optimize the Google fonts during a  nix build run, which will fail in Nix's isolated sandbox without internet:<syntaxhighlight lang="shell">
...
...
Line 192: Line 277:
ERROR: `npm build` failed
ERROR: `npm build` failed
</syntaxhighlight>You have to patch the Javascript code <syntaxhighlight lang="javascript">
</syntaxhighlight>You have to patch the Javascript code <syntaxhighlight lang="javascript">
# In layout.tsx file replace
// In layout.tsx file replace
#
//
# import {Inter} from "next/font/google"; #or any other Google font like Inter
// import {Inter} from "next/font/google"; // or any other Google font like Inter
# const inter = Inter({ subsets: ["latin"] });
// const inter = Inter({ subsets: ["latin"] });
#
//
# with ("src:" must be relative to the src/app/layout.tsx file):
// with ("src:" must be relative to the src/app/layout.tsx file):
import localFont from "next/font/local";
import localFont from "next/font/local";
const inter = localFont({ src: './Inter.ttf' });
const inter = localFont({ src: './Inter.ttf' });
Line 215: Line 300:
   ...
   ...
}
}
</syntaxhighlight>You can take a look at what fonts are available in the Nix <code>google-fonts</code> package by calling:<syntaxhighlight lang="shell">
</syntaxhighlight>You can take a look at what fonts are available in the Nix <code>google-fonts</code> package by calling:<syntaxhighlight lang="console">
ls -ahl $(nix build --no-link --print-out-paths nixpkgs#google-fonts)/share/fonts/truetype/
$ ls -ahl $(nix build --no-link --print-out-paths nixpkgs#google-fonts)/share/fonts/truetype/
</syntaxhighlight>
</syntaxhighlight>Take a look at [https://github.com/NixOS/nixpkgs/blob/8358fd43a66594d8b3445d87006185fa76d4be6e/pkgs/by-name/ho/homepage-dashboard/package.nix homepage-dashboard package in nixpkgs] for further workarounds for Nextjs in Nix.


== Example nix shell for Node.js development ==
== Tips and tricks ==
 
=== Example nix flake shell for Node.js development ===
`shell.nix` example:
[[Flake]] example: (Note: the `${&amp;lt;nixpkgs&amp;gt;}` needs to be replaced by `${<nixpkgs>}`
<syntaxhighlight lang="nix>
{{file|flake.nix|nix|
{ pkgs ? import <nixpkgs> {} }:
<nowiki>
 
let
  lib = import <nixpkgs/lib>;
  buildNodeJs = pkgs.callPackage "${<nixpkgs>}/pkgs/development/web/nodejs/nodejs.nix" {
    python = pkgs.python3;
  };
 
  nodejsVersion = lib.fileContents ./.nvmrc;
 
  nodejs = buildNodeJs {
    enableNpm = false;
    version = nodejsVersion;
    sha256 = "1a0zj505nhpfcj19qvjy2hvc5a7gadykv51y0rc6032qhzzsgca2";
  };
 
  NPM_CONFIG_PREFIX = toString ./npm_config_prefix;
 
in pkgs.mkShell {
  packages = with pkgs; [
    nodejs
    nodePackages.npm
  ];
 
  inherit NPM_CONFIG_PREFIX;
 
  shellHook = ''
    export PATH="${NPM_CONFIG_PREFIX}/bin:$PATH"
  '';
}
 
</syntaxhighlight>
 
== Example nix flake shell for Node.js development ==
 
`flake.nix` example:
<syntaxhighlight lang="nix>
{
{
   description = "example-node-js-flake";
   description = "example-node-js-flake";
Line 297: Line 346:
}
}


</syntaxhighlight>
</nowiki>
}}


== Using nodePackages with a different node version ==
=== Using nodePackages with a different node version ===
Packages in {{ic|nixpkgs.nodePackages}} are built using {{ic|nixpkgs.nodejs}}, so if you [[Overlays|overlay that package]] to a different version, the {{ic|nodePackages}} will be built using that:
Packages in {{ic|nixpkgs.nodePackages}} are built using {{ic|nixpkgs.nodejs}}, so if you [[Overlays|overlay that package]] to a different version, the {{ic|nodePackages}} will be built using that:
<syntaxhighlight lang="nix>
<syntaxhighlight lang="nix>
final: prev: {
final: prev: {
      nodejs = prev.nodejs-16_x;
  nodejs = prev.nodejs-16_x;
}
}
</syntaxhighlight>
</syntaxhighlight>
<pre>
<syntaxhighlight lang="console">
$ pnpm node --version
$ pnpm node --version
v16.17.1
v16.17.1
</pre>
</syntaxhighlight>
 
=== Override NodeJS package ===
Overriding a Nix package which is based on ''buildNpmPackage'' can be challeging because not only the source hash has to get changed but sometimes also the ''package-lock.json'' file and the ''npmDepsHash''.
 
Unfortunately it is not possible to directly access and change ''npmDepsHash'' inside ''overrideAttrs'', so this is an example workaround for changing the version, ''package-lock.json'' and hashes of the package ''eslint'':<syntaxhighlight lang="nix">
environment.systemPackages = [
  (eslint.overrideAttrs (oldAttrs: rec {
    version = "8.57.0";
    src = fetchFromGitHub {
      owner = "eslint";
      repo = "eslint";
      rev = "refs/tags/v${version}";
      hash = "sha256-nXlS+k8FiN7rbxhMmRPb3OplHpl+8fWdn1nY0cjL75c=";
    };
    postPatch = ''
      cp ${./package-lock.json} package-lock.json
    '';
    npmDepsHash = "sha256-DiXgAD0PvIIBxPAsdU8OOJIyvYI0JyPqu6sj7XN94hE=";
    npmDeps = pkgs.fetchNpmDeps {
      src = lib.fileset.toSource {
        root = ./.;
        fileset = lib.fileset.unions [
          ./package-lock.json
          ./package.json
        ];
      };
      name = "eslint-${version}-npm-deps";
      hash = npmDepsHash;
    };
  }))
];
</syntaxhighlight>


== External Links ==
== External Links ==
Line 315: Line 397:


=== References ===
=== References ===
[[Category:JavaScript]]