Node.js: Difference between revisions
imported>R-k-b m add links to source docs, use correct inline code syntax |
Turbotimon (talk | contribs) m →Example nix flake shell for Node.js development: is displayed wrong. Couldn't fix it but added this note |
||
| (24 intermediate revisions by 18 users not shown) | |||
| Line 1: | Line 1: | ||
__TOC__ | __TOC__ | ||
[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 == | ||
Adapt or add following line to your system configuration:<syntaxhighlight lang="nix> | |||
environment.systemPackages = with pkgs; [ nodejs ]; | environment.systemPackages = with pkgs; [ nodejs ]; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
See <code> | See <code>nix search nixpkgs nodejs</code> for additional versions like <code>nodejs-12_x</code>, etc. | ||
== Packaging == | == Packaging == | ||
=== | |||
=== 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." | |||
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]. | |||
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 ==== | |||
When trying to <code>npm i</code> electron, by default electron will try to download binaries from the internet, which does not work in the Nix sandbox. To stop it from trying to access the internet, set the environment variable <code>ELECTRON_SKIP_BINARY_DOWNLOAD = "1";</code>. | |||
For examples of packaging electron applications, search [https://github.com/search?q=buildNpmPackage+electron+repo%3ANixOS%2Fnixpkgs&type=code&l=NixOS%2Fnixpkgs Nixpkgs] for the terms "buildNpmPackage" and "electron". | |||
=== Packaging with <code>yarn2nix</code> === | |||
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"> | ||
$ nix-shell -p yarn yarn2nix | $ nix-shell -p yarn yarn2nix | ||
$ yarn install | $ yarn install | ||
# creates yarn.lock | # creates yarn.lock | ||
| Line 24: | Line 80: | ||
with (import <nixpkgs> {}); | with (import <nixpkgs> {}); | ||
rec { | rec { | ||
muellshack = | muellshack = mkYarnPackage { | ||
name = "muellshack"; | name = "muellshack"; | ||
src = ./.; | src = ./.; | ||
| Line 38: | Line 94: | ||
$ result/bin/muellshack | $ result/bin/muellshack | ||
</ | </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] | ||
== | == Troubleshooting == | ||
=== Using <code>npm install -g</code> fails === | === Using <code>npm install -g</code> fails === | ||
| Line 131: | Line 187: | ||
</pre> | </pre> | ||
One quick workaround for this is [ | 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> | <pre> | ||
| Line 142: | Line 198: | ||
(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]) | ||
'''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"> | |||
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"> | |||
... | |||
Running phase: buildPhase | |||
Executing npmBuildHook | |||
> nextjs-ollama-local-ai@0.1.0 build | |||
> next build | |||
▲ Next.js 14.1.0 | |||
Creating an optimized production build ... | |||
request to https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap failed, reason: getaddrinfo EAI_AGAIN fonts.googleapis.com | |||
at ClientRequest.<anonymous> (/build/source/node_modules/next/dist/compiled/node-fetch/index.js:1:66160) | |||
at ClientRequest.emit (node:events:518:28) | |||
at TLSSocket.socketErrorListener (node:_http_client:500:9) | |||
at TLSSocket.emit (node:events:518:28) | |||
at emitErrorNT (node:internal/streams/destroy:169:8) | |||
at emitErrorCloseNT (node:internal/streams/destroy:128:3) | |||
at process.processTicksAndRejections (node:internal/process/task_queues:82:21) { | |||
type: 'system', | |||
errno: 'EAI_AGAIN', | |||
code: 'EAI_AGAIN' | |||
} | |||
Failed to compile. | |||
src/app/layout.tsx | |||
`next/font` error: | |||
Failed to fetch `Inter` from Google Fonts. | |||
> Build failed because of webpack errors | |||
ERROR: `npm build` failed | |||
</syntaxhighlight>You have to patch the Javascript code <syntaxhighlight lang="javascript"> | |||
# In layout.tsx file replace | |||
# | |||
# import {Inter} from "next/font/google"; #or any other Google font like Inter | |||
# const inter = Inter({ subsets: ["latin"] }); | |||
# | |||
# with ("src:" must be relative to the src/app/layout.tsx file): | |||
import localFont from "next/font/local"; | |||
const inter = localFont({ src: './Inter.ttf' }); | |||
</syntaxhighlight>and place the Google fonts from Nixpkgs into the project, e.g. in a <code>preBuild</code> phase:<syntaxhighlight lang="nix"> | |||
buildNpmPackage { | |||
pname = "myproject"; | |||
version = "1.0.0"; | |||
... | |||
# see https://github.com/NixOS/nixpkgs/blob/master/pkgs/by-name/cr/crabfit-frontend/package.nix | |||
preBuild = '' | |||
cp "${ | |||
google-fonts.override { fonts = [ "Inter" ]; } | |||
}/share/fonts/truetype/Inter[slnt,wght].ttf" src/app/Inter.ttf | |||
''; | |||
... | |||
} | |||
</syntaxhighlight>You can take a look at what fonts are available in the Nix <code>google-fonts</code> package by calling:<syntaxhighlight lang="shell"> | |||
ls -ahl $(nix build --no-link --print-out-paths nixpkgs#google-fonts)/share/fonts/truetype/ | |||
</syntaxhighlight>Take a look at https://github.com/NixOS/nixpkgs/blob/master/pkgs/servers/homepage-dashboard/default.nix for further workarounds for Nextjs in Nix. | |||
== Tips and tricks == | |||
=== Example nix flake shell for Node.js development === | |||
[[Flake]] example: (Note: the `${&lt;nixpkgs&gt;}` needs to be replaced by `${<nixpkgs>}` | |||
{{file|flake.nix|nix| | |||
<nowiki> | |||
{ | |||
description = "example-node-js-flake"; | |||
inputs = { | |||
flake-utils.url = "github:numtide/flake-utils"; | |||
}; | |||
outputs = { self, nixpkgs, flake-utils }: | |||
flake-utils.lib.eachDefaultSystem (system: | |||
let | |||
pkgs = import nixpkgs { | |||
inherit system; | |||
}; | |||
buildNodeJs = pkgs.callPackage "${<nixpkgs>}/pkgs/development/web/nodejs/nodejs.nix" { | |||
python = pkgs.python3; | |||
}; | |||
nodejs = buildNodeJs { | |||
enableNpm = true; | |||
version = "20.5.1"; | |||
sha256 = "sha256-Q5xxqi84woYWV7+lOOmRkaVxJYBmy/1FSFhgScgTQZA="; | |||
}; | |||
in rec { | |||
flakedPkgs = pkgs; | |||
# enables use of `nix shell` | |||
devShell = pkgs.mkShell { | |||
# add things you want in your shell here | |||
buildInputs = with pkgs; [ | |||
nodejs | |||
]; | |||
}; | |||
} | |||
); | |||
} | |||
</nowiki> | |||
}} | |||
=== 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: | |||
<syntaxhighlight lang="nix> | |||
final: prev: { | |||
nodejs = prev.nodejs-16_x; | |||
} | |||
</syntaxhighlight> | |||
<pre> | |||
$ pnpm node --version | |||
v16.17.1 | |||
</pre> | |||
=== 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 147: | Line 356: | ||
=== References === | === References === | ||
[[Category:JavaScript]] | |||