1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
|
Npm.nix
=======
Npm.nix is a project that organizes JavaScript packages with the
[Nix](https://nixos.org/nix/) package manager.
There was [npm2nix](https://github.com/NixOS/npm2nix), then
[node2nix](https://github.com/svanderburg/node2nix). Both of them designed
to work with the [NPM package registry](https://www.npmjs.com/), and
leave you a little freedom and control. The gravest downside is that NPM
is not a repository of _source code_. Yes, some of the packages are plain
human-written JavaScript, but the others are derived from TypeScript,
Dart, and whatnot. Some require native extensions for the Node.js
runtime, which are usually precompiled and downloaded at _install_ time
(for example [node-sass](https://www.npmjs.com/package/node-sass) and
[fibers](https://www.npmjs.com/package/fibers)).
Npm.nix fuses together the power of Nix and [the Debian Free Software
Guideline](https://www.debian.org/doc/debian-policy/). We build every
JavaScript package from its source code, we can apply any patches, we can
control what is installed.
To help adding and updaing packages there is the
[npm4nix](https://github.com/ip1981/npm4nix) utility.
To be (maybe) done
==================
* Something like `nodeWithPackages`.
* Bundles. For example, a closed set of tarballs, so that NPM can install
them and resolve depenednecies offline. Or simply a bundle with `node_modules`
without symbolic links, deduplicated, etc.
Structure
=========
Entry point in the [npmPackages](./npmPackages) directory. It can be used
in command line as `nix build -f ./npmPackages PACKAGE` or `nix-build ./npmPackages -A PACKAGE`.
It can be used in top-level Nix expressions like this:
```nix
{
#...
node12Packages = import ./npmPackages { nodejs = nodejs-12_x; };
#...
}
```
Each NPM package is placed in separate directory with at least one file -
`default.nix`. Scoped packages are placed into corresponding subdirectories.
For the purpose of Nix expressions, names of the packages are modified:
* all slashes are replaced by dashes (`/` -> `-`);
* all dots are replaced by dashes (`.` -> `-`);
* the at symbol (`@`) is removed.
For example, some imaginary package `@babel/core` would be located under
`./npmPackages/@babel/core/default.nix` and available in Nix expressions as
`babel-core`.
How it works
============
Npm.nix basically replicates setup for Haskell or Python in
[Nixpkgs](https://nixos.org/nixpkgs). There is a predefined set of NPM
packages. The set can be extended. Each package in the set can be modified
or overriden for any particular use case.
When building an NPM package we:
1. Upack its source code.
2. Modify its `package.json` so that all dependencies were resolved to Nix store.
3. Invoke `npm install` to populate the `node_modules` directory.
4. Build the package.
5. Strip `package.json`, e. g. remove `devDependencies` which are not longer needed.
6. Pack the package and install it as a Nix derivation (unpacked, ready to use as is).
7. Invoke `npm install` again to populate `node_modules` with runtime dependencies only.
8. Strip `package.json` even more.
When the package is built, its runtime dependencies are tracked by Nix through
symbolic links under its `node_modules`. Its `package.json` does not refer
any packages. A little surprise, that this approach is in line with
[Node.js recommendations](https://nodejs.org/api/modules.html).
For example:
```
$ nix-build ./npmPackages/ -A _color-convert -o color-convert
these derivations will be built:
/nix/store/s30w2nskd2vk3mwylyja19bzmbwjr70v-color-name-4536ce5.drv
/nix/store/l5bkr89n5kr23fmnfgcsp05dvhl8n015-nodejs-10.16.3-color-name-1.1.4.drv
/nix/store/vk82xx042l1pf99ih4cj8yyna7vipamp-chalk-2.4.2-dummy.drv
/nix/store/wsxc4vgjwm0slq2i7zx0czzxg47y7wgy-xo-0.25.3-dummy.drv
/nix/store/zasx83qzd3dk5d9cfi5kifai3xg9vh8w-2.0.1.tar.gz.drv
/nix/store/q8rr2cgw74z2qdyhxqc12q7dxjzwa16c-nodejs-10.16.3-color-convert-2.0.1.drv
...
nodejs-10.16.3-color-convert-2.0.1 notice === Tarball Contents ===
nodejs-10.16.3-color-convert-2.0.1 notice 782B package.json
nodejs-10.16.3-color-convert-2.0.1 notice 1.4kB CHANGELOG.md
nodejs-10.16.3-color-convert-2.0.1 notice 17.0kB conversions.js
nodejs-10.16.3-color-convert-2.0.1 notice 1.7kB index.js
nodejs-10.16.3-color-convert-2.0.1 notice 1.1kB LICENSE
nodejs-10.16.3-color-convert-2.0.1 notice 2.9kB README.md
nodejs-10.16.3-color-convert-2.0.1 notice 2.3kB route.js
...
shrinking RPATHs of ELF executables and libraries in /nix/store/wz943yq79k76khaglrdlafpy0f3ahiyf-nodejs-10.16.3-color-convert-2.0.1
strip is /nix/store/sc8xmj2am32c8zvc4f7572g8r5cyxw91-binutils-2.31.1/bin/strip
patching script interpreter paths in /nix/store/wz943yq79k76khaglrdlafpy0f3ahiyf-nodejs-10.16.3-color-convert-2.0.1
checking for references to /tmp/nix-build-nodejs-10.16.3-color-convert-2.0.1.drv-0/ in /nix/store/wz943yq79k76khaglrdlafpy0f3ahiyf-nodejs-10.16.3-color-convert-2.0.1...
/nix/store/wz943yq79k76khaglrdlafpy0f3ahiyf-nodejs-10.16.3-color-convert-2.0.1
$ cat color-convert/package.json
{
"name": "color-convert",
"description": "Plain color conversion functions",
"version": "2.0.1",
"author": "Heather Arthur <fayearthur@gmail.com>",
"license": "MIT",
"repository": "Qix-/color-convert",
"files": [
"index.js",
"conversions.js",
"route.js"
]
}
$ ls -lh color-convert/node_modules/
total 4,0K
lrwxrwxrwx 1 pashev pashev 70 ene 1 1970 color-name -> ../../jbdpbcsfam331mqgylbhixsdydcsg7wd-nodejs-10.16.3-color-name-1.1.4
$ node
> var convert = require('./color-convert');
undefined
> convert.keyword.rgb('blue');
[ 0, 0, 255 ]
$ npm i ./color-convert
...
+ color-convert@2.0.1
added 1 package from 1 contributor in 0.838s
$ node
> var convert = require('color-convert');
undefined
> convert.keyword.rgb('blue');
[ 0, 0, 255 ]
```
|