Skip to content

Commit 1a35ac2

Browse files
committed
Split functions shared between v1 and v2 to util
- fetchGitWrapped - throw - buildTgzFromGitHub - nodeSource - add_node_modules_to_cwd - get_node_modules_attrs
1 parent 330f8d5 commit 1a35ac2

File tree

4 files changed

+159
-237
lines changed

4 files changed

+159
-237
lines changed

default.nix

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{ pkgs ? import ./nix { }, lib ? pkgs.lib }:
22
let
3-
v1_internal = pkgs.callPackage ./internal-v1.nix { };
4-
v2_internal = pkgs.callPackage ./internal-v2.nix { };
3+
util = pkgs.callPackage ./util.nix { };
4+
v1_internal = pkgs.callPackage ./internal-v1.nix { inherit util; };
5+
v2_internal = pkgs.callPackage ./internal-v2.nix { inherit util; };
56
separatePublicAndInternalAPI = api: {
67
inherit (api) shell build node_modules;
78

internal-v1.nix

Lines changed: 28 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,14 @@
1-
{ nodejs-14_x, jq, openssl, stdenv, mkShell, lib, fetchurl, writeText, writeTextFile, runCommand, fetchFromGitHub }:
1+
{ util, nodejs-14_x, jq, openssl, stdenv, mkShell, lib, fetchurl, writeText, writeTextFile, runCommand, fetchFromGitHub }:
22
rec {
33
# Versions >= 15 use npm >= 7, which uses npm lockfile version 2, which we don't support yet
44
# See the assertion in the node_modules function
55
default_nodejs = nodejs-14_x;
66

7-
8-
# builtins.fetchGit wrapper that ensures compatibility with Nix 2.3 and Nix 2.4
9-
# Type: Attrset -> Path
10-
fetchGitWrapped =
11-
let
12-
is24OrNewer = lib.versionAtLeast builtins.nixVersion "2.4";
13-
in
14-
if is24OrNewer then
15-
# remove the now unsupported / insufficient `ref` paramter
16-
args: builtins.fetchGit (builtins.removeAttrs args [ "ref" ])
17-
else
18-
# for 2.3 (and older?) we remove the unsupported `allRefs` parameter
19-
args: builtins.fetchGit (builtins.removeAttrs args [ "allRefs" ])
20-
;
21-
22-
# Description: Custom throw function that ensures our error messages have a common prefix.
23-
# Type: String -> Throw
24-
throw = str: builtins.throw "[npmlock2nix] ${str}";
25-
267
# Description: Turns an npm lockfile dependency into an attribute set as needed by fetchurl
278
# Type: String -> Set -> Set
289
makeSourceAttrs = name: dependency:
29-
assert !(dependency ? resolved) -> throw "Missing `resolved` attribute for dependency `${name}`.";
30-
assert !(dependency ? integrity) -> throw "Missing `integrity` attribute for dependency `${name}`.";
10+
assert !(dependency ? resolved) -> util.throw "Missing `resolved` attribute for dependency `${name}`.";
11+
assert !(dependency ? integrity) -> util.throw "Missing `integrity` attribute for dependency `${name}`.";
3112
{
3213
url = dependency.resolved;
3314
# FIXME: for backwards compatibility we should probably set the
@@ -49,44 +30,14 @@ rec {
4930
parts = builtins.split "[:#/]" str;
5031
in
5132
assert !(builtins.length parts == 7) ->
52-
throw "failed to parse GitHub reference `${str}`. Expected a string of format `github:org/repo#revision`";
33+
util.throw "failed to parse GitHub reference `${str}`. Expected a string of format `github:org/repo#revision`";
5334
rec {
5435
inherit parts;
5536
org = builtins.elemAt parts 2;
5637
repo = builtins.elemAt parts 4;
5738
rev = builtins.elemAt parts 6;
5839
};
5940

60-
# Description: Takes an attribute set describing a git dependency and returns
61-
# a .tgz of the repository as store path. If the attribute hash contains a
62-
# hash attribute it will provide the value to `fetchFromGitHub` which will
63-
# also work in restricted evaluation.
64-
# Type: Set -> Path
65-
buildTgzFromGitHub = { name, org, repo, rev, ref, hash ? null, sourceOptions ? { } }:
66-
let
67-
src =
68-
if hash != null then
69-
fetchFromGitHub
70-
{
71-
owner = org;
72-
inherit repo;
73-
inherit rev;
74-
sha256 = hash; # FIXME: what if sha3?
75-
} else
76-
fetchGitWrapped {
77-
url = "https://github.com/${org}/${repo}";
78-
inherit rev ref;
79-
allRefs = true;
80-
};
81-
82-
sourceInfo = {
83-
github = { inherit org repo rev ref; };
84-
};
85-
drv = packTgz sourceOptions.nodejs name ref src;
86-
in
87-
if sourceOptions ? sourceOverrides.${name}
88-
then sourceOptions.sourceOverrides.${name} sourceInfo drv
89-
else drv;
9041

9142
# Description: Packs a source directory into a .tgz tar archive. If the
9243
# source is an archive, it gets unpacked first.
@@ -120,16 +71,17 @@ rec {
12071
v = parseGitHubRef dependency.version;
12172
f = parseGitHubRef dependency.from;
12273
in
123-
assert v.org != f.org -> throw "version and from of `${name}` disagree on the GitHub org to fetch from: `${v.org}` vs `${f.org}`";
124-
assert v.repo != f.repo -> throw "version and from of `${name}` disagree on the GitHub repo to fetch from: `${v.repo}` vs `${f.repo}`";
125-
assert !isGitRev v.rev -> throw "version of `${name}` does not specify a valid git rev: `${v.rev}`";
74+
assert v.org != f.org -> util.throw "version and from of `${name}` disagree on the GitHub org to fetch from: `${v.org}` vs `${f.org}`";
75+
assert v.repo != f.repo -> util.throw "version and from of `${name}` disagree on the GitHub repo to fetch from: `${v.repo}` vs `${f.repo}`";
76+
assert !isGitRev v.rev -> util.throw "version of `${name}` does not specify a valid git rev: `${v.rev}`";
12677
let
127-
src = buildTgzFromGitHub {
78+
src = util.buildTgzFromGitHub {
12879
name = "${name}.tgz";
12980
ref = f.rev;
13081
inherit (v) org repo rev;
13182
hash = sourceHashFunc { type = "github"; value = v; };
13283
inherit sourceOptions;
84+
pack = packTgz;
13385
};
13486
in
13587
(builtins.removeAttrs dependency [ "from" ]) // {
@@ -139,7 +91,7 @@ rec {
13991
# Description: Checks if the given string looks like a vila HTTP or HTTPS url
14092
# Type: String -> Bool
14193
looksLikeUrl = s:
142-
assert (builtins.typeOf s != "string") -> throw "can only check strings if they are URL-like";
94+
assert (builtins.typeOf s != "string") -> util.throw "can only check strings if they are URL-like";
14395
lib.hasPrefix "http://" s || lib.hasPrefix "https://" s;
14496

14597
# Description: Checks the given dependency spec if its version field should
@@ -179,16 +131,16 @@ rec {
179131
# Type: { sourceHashFunc :: Fn } -> String -> Set -> Derivation
180132
makeSource = sourceOptions: name: dependency:
181133
assert (builtins.typeOf name != "string") ->
182-
throw "Name of dependency ${toString name} must be a string";
134+
util.throw "Name of dependency ${toString name} must be a string";
183135
assert (builtins.typeOf dependency != "set") ->
184-
throw "Specification of dependency ${toString name} must be a set";
136+
util.throw "Specification of dependency ${toString name} must be a set";
185137
if dependency ? resolved && dependency ? integrity then
186138
makeUrlSource sourceOptions name dependency
187139
else if dependency ? from && dependency ? version then
188140
makeGithubSource sourceOptions name dependency
189141
else if shouldUseVersionAsUrl dependency then
190142
makeSource sourceOptions name (dependency // { resolved = dependency.version; })
191-
else throw "A valid dependency consists of at least the resolved and integrity field. Missing one or both of them for `${name}`. The object I got looks like this: ${builtins.toJSON dependency}";
143+
else util.throw "A valid dependency consists of at least the resolved and integrity field. Missing one or both of them for `${name}`. The object I got looks like this: ${builtins.toJSON dependency}";
192144

193145
# Description: Parses the lock file as json and returns an attribute set
194146
# Type: Path -> Set
@@ -199,7 +151,7 @@ rec {
199151
in
200152
assert
201153
builtins.typeOf json != "set" ->
202-
throw "The NPM lockfile must be a valid JSON object";
154+
util.throw "The NPM lockfile must be a valid JSON object";
203155
# if a lockfile doesn't declare dependencies ensure that we have an empty
204156
# set. This makes the consuming code eaiser.
205157
if json ? dependencies then json else json // { dependencies = { }; };
@@ -210,12 +162,13 @@ rec {
210162
let
211163
gitAttrs = parseGitHubRef str;
212164
in
213-
buildTgzFromGitHub {
165+
util.buildTgzFromGitHub {
214166
name = "${name}.tgz";
215167
ref = gitAttrs.rev;
216168
inherit (gitAttrs) org repo rev;
217169
hash = sourceHashFunc { type = "github"; value = gitAttrs; };
218170
inherit sourceOptions;
171+
pack = packTgz;
219172
};
220173

221174
# Description: Patch the `requires` attributes of a dependency spec to refer to paths in the store
@@ -231,9 +184,9 @@ rec {
231184
# Type: List String -> { sourceHashFunc :: Fn } -> String -> Set -> { result :: Set, integrityUpdates :: List { path, file } }
232185
patchDependency = path: sourceOptions: name: spec:
233186
assert (builtins.typeOf name != "string") ->
234-
throw "Name of dependency ${toString name} must be a string";
187+
util.throw "Name of dependency ${toString name} must be a string";
235188
assert (builtins.typeOf spec != "set") ->
236-
throw "spec of dependency ${toString name} must be a set";
189+
util.throw "spec of dependency ${toString name} must be a set";
237190
let
238191
isBundled = spec ? bundled && spec.bundled == true;
239192
hasGitHubRequires = spec: (spec ? requires) && (lib.any (x: lib.hasPrefix "github:" x) (lib.attrValues spec.requires));
@@ -261,7 +214,7 @@ rec {
261214
# Type: { sourceHashFunc :: Fn } -> Path -> { result :: Set, integrityUpdates :: List { path, file } }
262215
patchLockfile = sourceOptions: file:
263216
assert (builtins.typeOf file != "path" && builtins.typeOf file != "string") ->
264-
throw "file ${toString file} must be a path or string";
217+
util.throw "file ${toString file} must be a path or string";
265218
let
266219
content = readLockfile file;
267220
dependencies = lib.mapAttrs (name: patchDependency [ name ] sourceOptions name) content.dependencies;
@@ -277,7 +230,7 @@ rec {
277230
# Type: Path -> Set
278231
patchPackagefile = file:
279232
assert (builtins.typeOf file != "path" && builtins.typeOf file != "string") ->
280-
throw "file ${toString file} must be a path or string";
233+
util.throw "file ${toString file} must be a path or string";
281234
let
282235
# Read the file but also add empty `devDependencies` and `dependencies`
283236
# if either are missing
@@ -315,51 +268,7 @@ rec {
315268
integrityUpdates = patched.integrityUpdates;
316269
};
317270

318-
# Description: Turn a derivation (with name & src attribute) into a directory containing the unpacked sources
319-
# Type: Derivation -> Derivation
320-
nodeSource = nodejs: runCommand "node-sources-${nodejs.version}"
321-
{ } ''
322-
tar --no-same-owner --no-same-permissions -xf ${nodejs.src}
323-
mv node-* $out
324-
'';
325-
326-
# Description: Creates shell scripts to provide node_modules to the environment supporting
327-
# two different modes: "symlink" and "copy"
328-
# Type: Derivation -> String -> String
329-
add_node_modules_to_cwd = node_modules: mode:
330-
''
331-
# If node_modules is a managed symlink we can safely remove it and install a new one
332-
${lib.optionalString (mode == "symlink") ''
333-
if [[ "$(readlink -f node_modules)" == ${builtins.storeDir}* ]]; then
334-
rm -f node_modules
335-
fi
336-
''}
337-
if test -e node_modules; then
338-
echo '[npmlock2nix] There is already a `node_modules` directory. Not replacing it.' >&2
339-
exit 1
340-
fi
341-
'' +
342-
(
343-
if mode == "copy" then ''
344-
cp --no-preserve=mode -r ${node_modules}/node_modules node_modules
345-
chmod -R u+rw node_modules
346-
'' else if mode == "symlink" then ''
347-
ln -s ${node_modules}/node_modules node_modules
348-
'' else throw "node_modules_mode must be either `copy` or `symlink`"
349-
) + ''
350-
export NODE_PATH="$(pwd)/node_modules:$NODE_PATH"
351-
'';
352271

353-
# Description: Extract the attributes that are relevant for building node_modules and use
354-
# them as defaults in case the node_modules_attrs attribute doesn't have
355-
# them.
356-
# Type: Set -> Set
357-
get_node_modules_attrs = { node_modules_attrs ? { }, ... }@attrs:
358-
let
359-
getAttr = name: from: lib.optionalAttrs (builtins.hasAttr name from) { "${name}" = from.${name}; };
360-
getAttrs = names: from: lib.foldl (a: b: a // (getAttr b from)) { } names;
361-
in
362-
(getAttrs [ "src" "nodejs" ] attrs // node_modules_attrs);
363272

364273
# Description: Takes a dependency spec and a map of github sources/hashes and returns either the map or 'null'
365274
# Type: Set -> Set -> Set | null
@@ -374,7 +283,7 @@ rec {
374283
)
375284
githubSourceHashMap
376285
else
377-
throw "sourceHashFunc: spec.type '${spec.type}' is not supported. Supported types: 'github'";
286+
util.throw "sourceHashFunc: spec.type '${spec.type}' is not supported. Supported types: 'github'";
378287

379288
node_modules =
380289
{ src
@@ -392,9 +301,9 @@ rec {
392301
, ...
393302
}@args:
394303
assert lib.versionAtLeast nodejs.version "15.0" ->
395-
throw "npmlock2nix is called with nodejs version ${nodejs.version}, which is currently not supported, see https://github.com/nix-community/npmlock2nix/issues/153 for more information";
304+
util.throw "npmlock2nix is called with nodejs version ${nodejs.version}, which is currently not supported, see https://github.com/nix-community/npmlock2nix/issues/153 for more information";
396305
assert (builtins.typeOf preInstallLinks != "set") ->
397-
throw "`preInstallLinks` must be an attributeset of attributesets";
306+
util.throw "`preInstallLinks` must be an attributeset of attributesets";
398307
let
399308
cleanArgs = builtins.removeAttrs args [ "src" "packageJson" "packageLockJson" "buildInputs" "nativeBuildInputs" "nodejs" "preBuild" "postBuild" "preInstallLinks" "sourceOverrides" "githubSourceHashMap" ];
400309
lockfile = readLockfile packageLockJson;
@@ -506,7 +415,7 @@ rec {
506415
declare -pf > $TMP/preinstall-env
507416
ln -s ${preinstall_node_modules}/node_modules/.hooks/prepare node_modules/.hooks/preinstall
508417
export HOME=.
509-
npm install --offline --nodedir=${nodeSource nodejs}
418+
npm install --offline --nodedir=${util.nodeSource nodejs}
510419
test -d node_modules/.bin && patchShebangs node_modules/.bin
511420
rm -rf node_modules/.hooks
512421
runHook postBuild
@@ -540,14 +449,14 @@ rec {
540449
, ...
541450
}@attrs:
542451
let
543-
nm = node_modules (get_node_modules_attrs attrs);
452+
nm = node_modules (util.get_node_modules_attrs attrs);
544453
extraAttrs = builtins.removeAttrs attrs [ "node_modules_attrs" "passthru" "shellHook" "buildInputs" ];
545454
in
546455
mkShell ({
547456
buildInputs = buildInputs ++ [ nm.nodejs nm ];
548457
shellHook = ''
549458
# FIXME: we should somehow register a GC root here in case of a symlink?
550-
${add_node_modules_to_cwd nm node_modules_mode}
459+
${util.add_node_modules_to_cwd nm node_modules_mode}
551460
'' + shellHook;
552461
passthru = passthru // {
553462
node_modules = nm;
@@ -565,7 +474,7 @@ rec {
565474
, ...
566475
}@attrs:
567476
let
568-
nm = node_modules (get_node_modules_attrs attrs);
477+
nm = node_modules (util.get_node_modules_attrs attrs);
569478
extraAttrs = builtins.removeAttrs attrs [ "node_modules_attrs" "passthru" "buildInputs" ];
570479
in
571480
stdenv.mkDerivation ({
@@ -574,7 +483,7 @@ rec {
574483
buildInputs = [ nm ] ++ buildInputs;
575484
inherit src installPhase;
576485

577-
preConfigure = add_node_modules_to_cwd nm node_modules_mode;
486+
preConfigure = util.add_node_modules_to_cwd nm node_modules_mode;
578487

579488
buildPhase = ''
580489
runHook preBuild

0 commit comments

Comments
 (0)