mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-12-28 16:43:58 +00:00
ae83b9c7c6
Updates netlify-cli to 12.2.4, while switching to NodeJS 16. According to its issue page, there seems to be problems running this tool with NodeJS 18, so we'd better stick to NodeJS 16 instead.
687 lines
24 KiB
Nix
687 lines
24 KiB
Nix
# This file originates from node2nix
|
|
|
|
{lib, stdenv, nodejs, python2, pkgs, libtool, runCommand, writeTextFile, writeShellScript}:
|
|
|
|
let
|
|
# Workaround to cope with utillinux in Nixpkgs 20.09 and util-linux in Nixpkgs master
|
|
utillinux = if pkgs ? utillinux then pkgs.utillinux else pkgs.util-linux;
|
|
|
|
python = if nodejs ? python then nodejs.python else python2;
|
|
|
|
# Create a tar wrapper that filters all the 'Ignoring unknown extended header keyword' noise
|
|
tarWrapper = runCommand "tarWrapper" {} ''
|
|
mkdir -p $out/bin
|
|
|
|
cat > $out/bin/tar <<EOF
|
|
#! ${stdenv.shell} -e
|
|
$(type -p tar) "\$@" --warning=no-unknown-keyword --delay-directory-restore
|
|
EOF
|
|
|
|
chmod +x $out/bin/tar
|
|
'';
|
|
|
|
# Function that generates a TGZ file from a NPM project
|
|
buildNodeSourceDist =
|
|
{ name, version, src, ... }:
|
|
|
|
stdenv.mkDerivation {
|
|
name = "node-tarball-${name}-${version}";
|
|
inherit src;
|
|
buildInputs = [ nodejs ];
|
|
buildPhase = ''
|
|
export HOME=$TMPDIR
|
|
tgzFile=$(npm pack | tail -n 1) # Hooks to the pack command will add output (https://docs.npmjs.com/misc/scripts)
|
|
'';
|
|
installPhase = ''
|
|
mkdir -p $out/tarballs
|
|
mv $tgzFile $out/tarballs
|
|
mkdir -p $out/nix-support
|
|
echo "file source-dist $out/tarballs/$tgzFile" >> $out/nix-support/hydra-build-products
|
|
'';
|
|
};
|
|
|
|
# Common shell logic
|
|
installPackage = writeShellScript "install-package" ''
|
|
installPackage() {
|
|
local packageName=$1 src=$2
|
|
|
|
local strippedName
|
|
|
|
local DIR=$PWD
|
|
cd $TMPDIR
|
|
|
|
unpackFile $src
|
|
|
|
# Make the base dir in which the target dependency resides first
|
|
mkdir -p "$(dirname "$DIR/$packageName")"
|
|
|
|
if [ -f "$src" ]
|
|
then
|
|
# Figure out what directory has been unpacked
|
|
packageDir="$(find . -maxdepth 1 -type d | tail -1)"
|
|
|
|
# Restore write permissions to make building work
|
|
find "$packageDir" -type d -exec chmod u+x {} \;
|
|
chmod -R u+w "$packageDir"
|
|
|
|
# Move the extracted tarball into the output folder
|
|
mv "$packageDir" "$DIR/$packageName"
|
|
elif [ -d "$src" ]
|
|
then
|
|
# Get a stripped name (without hash) of the source directory.
|
|
# On old nixpkgs it's already set internally.
|
|
if [ -z "$strippedName" ]
|
|
then
|
|
strippedName="$(stripHash $src)"
|
|
fi
|
|
|
|
# Restore write permissions to make building work
|
|
chmod -R u+w "$strippedName"
|
|
|
|
# Move the extracted directory into the output folder
|
|
mv "$strippedName" "$DIR/$packageName"
|
|
fi
|
|
|
|
# Change to the package directory to install dependencies
|
|
cd "$DIR/$packageName"
|
|
}
|
|
'';
|
|
|
|
# Bundle the dependencies of the package
|
|
#
|
|
# Only include dependencies if they don't exist. They may also be bundled in the package.
|
|
includeDependencies = {dependencies}:
|
|
lib.optionalString (dependencies != []) (
|
|
''
|
|
mkdir -p node_modules
|
|
cd node_modules
|
|
''
|
|
+ (lib.concatMapStrings (dependency:
|
|
''
|
|
if [ ! -e "${dependency.packageName}" ]; then
|
|
${composePackage dependency}
|
|
fi
|
|
''
|
|
) dependencies)
|
|
+ ''
|
|
cd ..
|
|
''
|
|
);
|
|
|
|
# Recursively composes the dependencies of a package
|
|
composePackage = { name, packageName, src, dependencies ? [], ... }@args:
|
|
builtins.addErrorContext "while evaluating node package '${packageName}'" ''
|
|
installPackage "${packageName}" "${src}"
|
|
${includeDependencies { inherit dependencies; }}
|
|
cd ..
|
|
${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
|
|
'';
|
|
|
|
pinpointDependencies = {dependencies, production}:
|
|
let
|
|
pinpointDependenciesFromPackageJSON = writeTextFile {
|
|
name = "pinpointDependencies.js";
|
|
text = ''
|
|
var fs = require('fs');
|
|
var path = require('path');
|
|
|
|
function resolveDependencyVersion(location, name) {
|
|
if(location == process.env['NIX_STORE']) {
|
|
return null;
|
|
} else {
|
|
var dependencyPackageJSON = path.join(location, "node_modules", name, "package.json");
|
|
|
|
if(fs.existsSync(dependencyPackageJSON)) {
|
|
var dependencyPackageObj = JSON.parse(fs.readFileSync(dependencyPackageJSON));
|
|
|
|
if(dependencyPackageObj.name == name) {
|
|
return dependencyPackageObj.version;
|
|
}
|
|
} else {
|
|
return resolveDependencyVersion(path.resolve(location, ".."), name);
|
|
}
|
|
}
|
|
}
|
|
|
|
function replaceDependencies(dependencies) {
|
|
if(typeof dependencies == "object" && dependencies !== null) {
|
|
for(var dependency in dependencies) {
|
|
var resolvedVersion = resolveDependencyVersion(process.cwd(), dependency);
|
|
|
|
if(resolvedVersion === null) {
|
|
process.stderr.write("WARNING: cannot pinpoint dependency: "+dependency+", context: "+process.cwd()+"\n");
|
|
} else {
|
|
dependencies[dependency] = resolvedVersion;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Read the package.json configuration */
|
|
var packageObj = JSON.parse(fs.readFileSync('./package.json'));
|
|
|
|
/* Pinpoint all dependencies */
|
|
replaceDependencies(packageObj.dependencies);
|
|
if(process.argv[2] == "development") {
|
|
replaceDependencies(packageObj.devDependencies);
|
|
}
|
|
else {
|
|
packageObj.devDependencies = {};
|
|
}
|
|
replaceDependencies(packageObj.optionalDependencies);
|
|
replaceDependencies(packageObj.peerDependencies);
|
|
|
|
/* Write the fixed package.json file */
|
|
fs.writeFileSync("package.json", JSON.stringify(packageObj, null, 2));
|
|
'';
|
|
};
|
|
in
|
|
''
|
|
node ${pinpointDependenciesFromPackageJSON} ${if production then "production" else "development"}
|
|
|
|
${lib.optionalString (dependencies != [])
|
|
''
|
|
if [ -d node_modules ]
|
|
then
|
|
cd node_modules
|
|
${lib.concatMapStrings (dependency: pinpointDependenciesOfPackage dependency) dependencies}
|
|
cd ..
|
|
fi
|
|
''}
|
|
'';
|
|
|
|
# Recursively traverses all dependencies of a package and pinpoints all
|
|
# dependencies in the package.json file to the versions that are actually
|
|
# being used.
|
|
|
|
pinpointDependenciesOfPackage = { packageName, dependencies ? [], production ? true, ... }@args:
|
|
''
|
|
if [ -d "${packageName}" ]
|
|
then
|
|
cd "${packageName}"
|
|
${pinpointDependencies { inherit dependencies production; }}
|
|
cd ..
|
|
${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
|
|
fi
|
|
'';
|
|
|
|
# Extract the Node.js source code which is used to compile packages with
|
|
# native bindings
|
|
nodeSources = runCommand "node-sources" {} ''
|
|
tar --no-same-owner --no-same-permissions -xf ${nodejs.src}
|
|
mv node-* $out
|
|
'';
|
|
|
|
# Script that adds _integrity fields to all package.json files to prevent NPM from consulting the cache (that is empty)
|
|
addIntegrityFieldsScript = writeTextFile {
|
|
name = "addintegrityfields.js";
|
|
text = ''
|
|
var fs = require('fs');
|
|
var path = require('path');
|
|
|
|
function augmentDependencies(baseDir, dependencies) {
|
|
for(var dependencyName in dependencies) {
|
|
var dependency = dependencies[dependencyName];
|
|
|
|
// Open package.json and augment metadata fields
|
|
var packageJSONDir = path.join(baseDir, "node_modules", dependencyName);
|
|
var packageJSONPath = path.join(packageJSONDir, "package.json");
|
|
|
|
if(fs.existsSync(packageJSONPath)) { // Only augment packages that exist. Sometimes we may have production installs in which development dependencies can be ignored
|
|
console.log("Adding metadata fields to: "+packageJSONPath);
|
|
var packageObj = JSON.parse(fs.readFileSync(packageJSONPath));
|
|
|
|
if(dependency.integrity) {
|
|
packageObj["_integrity"] = dependency.integrity;
|
|
} else {
|
|
packageObj["_integrity"] = "sha1-000000000000000000000000000="; // When no _integrity string has been provided (e.g. by Git dependencies), add a dummy one. It does not seem to harm and it bypasses downloads.
|
|
}
|
|
|
|
if(dependency.resolved) {
|
|
packageObj["_resolved"] = dependency.resolved; // Adopt the resolved property if one has been provided
|
|
} else {
|
|
packageObj["_resolved"] = dependency.version; // Set the resolved version to the version identifier. This prevents NPM from cloning Git repositories.
|
|
}
|
|
|
|
if(dependency.from !== undefined) { // Adopt from property if one has been provided
|
|
packageObj["_from"] = dependency.from;
|
|
}
|
|
|
|
fs.writeFileSync(packageJSONPath, JSON.stringify(packageObj, null, 2));
|
|
}
|
|
|
|
// Augment transitive dependencies
|
|
if(dependency.dependencies !== undefined) {
|
|
augmentDependencies(packageJSONDir, dependency.dependencies);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(fs.existsSync("./package-lock.json")) {
|
|
var packageLock = JSON.parse(fs.readFileSync("./package-lock.json"));
|
|
|
|
if(![1, 2].includes(packageLock.lockfileVersion)) {
|
|
process.stderr.write("Sorry, I only understand lock file versions 1 and 2!\n");
|
|
process.exit(1);
|
|
}
|
|
|
|
if(packageLock.dependencies !== undefined) {
|
|
augmentDependencies(".", packageLock.dependencies);
|
|
}
|
|
}
|
|
'';
|
|
};
|
|
|
|
# Reconstructs a package-lock file from the node_modules/ folder structure and package.json files with dummy sha1 hashes
|
|
reconstructPackageLock = writeTextFile {
|
|
name = "reconstructpackagelock.js";
|
|
text = ''
|
|
var fs = require('fs');
|
|
var path = require('path');
|
|
|
|
var packageObj = JSON.parse(fs.readFileSync("package.json"));
|
|
|
|
var lockObj = {
|
|
name: packageObj.name,
|
|
version: packageObj.version,
|
|
lockfileVersion: 2,
|
|
requires: true,
|
|
packages: {
|
|
"": {
|
|
name: packageObj.name,
|
|
version: packageObj.version,
|
|
license: packageObj.license,
|
|
bin: packageObj.bin,
|
|
dependencies: packageObj.dependencies,
|
|
engines: packageObj.engines,
|
|
optionalDependencies: packageObj.optionalDependencies
|
|
}
|
|
},
|
|
dependencies: {}
|
|
};
|
|
|
|
function augmentPackageJSON(filePath, packages, dependencies) {
|
|
var packageJSON = path.join(filePath, "package.json");
|
|
if(fs.existsSync(packageJSON)) {
|
|
var packageObj = JSON.parse(fs.readFileSync(packageJSON));
|
|
packages[filePath] = {
|
|
version: packageObj.version,
|
|
integrity: "sha1-000000000000000000000000000=",
|
|
dependencies: packageObj.dependencies,
|
|
engines: packageObj.engines,
|
|
optionalDependencies: packageObj.optionalDependencies
|
|
};
|
|
dependencies[packageObj.name] = {
|
|
version: packageObj.version,
|
|
integrity: "sha1-000000000000000000000000000=",
|
|
dependencies: {}
|
|
};
|
|
processDependencies(path.join(filePath, "node_modules"), packages, dependencies[packageObj.name].dependencies);
|
|
}
|
|
}
|
|
|
|
function processDependencies(dir, packages, dependencies) {
|
|
if(fs.existsSync(dir)) {
|
|
var files = fs.readdirSync(dir);
|
|
|
|
files.forEach(function(entry) {
|
|
var filePath = path.join(dir, entry);
|
|
var stats = fs.statSync(filePath);
|
|
|
|
if(stats.isDirectory()) {
|
|
if(entry.substr(0, 1) == "@") {
|
|
// When we encounter a namespace folder, augment all packages belonging to the scope
|
|
var pkgFiles = fs.readdirSync(filePath);
|
|
|
|
pkgFiles.forEach(function(entry) {
|
|
if(stats.isDirectory()) {
|
|
var pkgFilePath = path.join(filePath, entry);
|
|
augmentPackageJSON(pkgFilePath, packages, dependencies);
|
|
}
|
|
});
|
|
} else {
|
|
augmentPackageJSON(filePath, packages, dependencies);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
processDependencies("node_modules", lockObj.packages, lockObj.dependencies);
|
|
|
|
fs.writeFileSync("package-lock.json", JSON.stringify(lockObj, null, 2));
|
|
'';
|
|
};
|
|
|
|
# Script that links bins defined in package.json to the node_modules bin directory
|
|
# NPM does not do this for top-level packages itself anymore as of v7
|
|
linkBinsScript = writeTextFile {
|
|
name = "linkbins.js";
|
|
text = ''
|
|
var fs = require('fs');
|
|
var path = require('path');
|
|
|
|
var packageObj = JSON.parse(fs.readFileSync("package.json"));
|
|
|
|
var nodeModules = Array(packageObj.name.split("/").length).fill("..").join(path.sep);
|
|
|
|
if(packageObj.bin !== undefined) {
|
|
fs.mkdirSync(path.join(nodeModules, ".bin"))
|
|
|
|
if(typeof packageObj.bin == "object") {
|
|
Object.keys(packageObj.bin).forEach(function(exe) {
|
|
if(fs.existsSync(packageObj.bin[exe])) {
|
|
console.log("linking bin '" + exe + "'");
|
|
fs.symlinkSync(
|
|
path.join("..", packageObj.name, packageObj.bin[exe]),
|
|
path.join(nodeModules, ".bin", exe)
|
|
);
|
|
}
|
|
else {
|
|
console.log("skipping non-existent bin '" + exe + "'");
|
|
}
|
|
})
|
|
}
|
|
else {
|
|
if(fs.existsSync(packageObj.bin)) {
|
|
console.log("linking bin '" + packageObj.bin + "'");
|
|
fs.symlinkSync(
|
|
path.join("..", packageObj.name, packageObj.bin),
|
|
path.join(nodeModules, ".bin", packageObj.name.split("/").pop())
|
|
);
|
|
}
|
|
else {
|
|
console.log("skipping non-existent bin '" + packageObj.bin + "'");
|
|
}
|
|
}
|
|
}
|
|
else if(packageObj.directories !== undefined && packageObj.directories.bin !== undefined) {
|
|
fs.mkdirSync(path.join(nodeModules, ".bin"))
|
|
|
|
fs.readdirSync(packageObj.directories.bin).forEach(function(exe) {
|
|
if(fs.existsSync(path.join(packageObj.directories.bin, exe))) {
|
|
console.log("linking bin '" + exe + "'");
|
|
fs.symlinkSync(
|
|
path.join("..", packageObj.name, packageObj.directories.bin, exe),
|
|
path.join(nodeModules, ".bin", exe)
|
|
);
|
|
}
|
|
else {
|
|
console.log("skipping non-existent bin '" + exe + "'");
|
|
}
|
|
})
|
|
}
|
|
'';
|
|
};
|
|
|
|
prepareAndInvokeNPM = {packageName, bypassCache, reconstructLock, npmFlags, production}:
|
|
let
|
|
forceOfflineFlag = if bypassCache then "--offline" else "--registry http://www.example.com";
|
|
in
|
|
''
|
|
# Pinpoint the versions of all dependencies to the ones that are actually being used
|
|
echo "pinpointing versions of dependencies..."
|
|
source $pinpointDependenciesScriptPath
|
|
|
|
# Patch the shebangs of the bundled modules to prevent them from
|
|
# calling executables outside the Nix store as much as possible
|
|
patchShebangs .
|
|
|
|
# Deploy the Node.js package by running npm install. Since the
|
|
# dependencies have been provided already by ourselves, it should not
|
|
# attempt to install them again, which is good, because we want to make
|
|
# it Nix's responsibility. If it needs to install any dependencies
|
|
# anyway (e.g. because the dependency parameters are
|
|
# incomplete/incorrect), it fails.
|
|
#
|
|
# The other responsibilities of NPM are kept -- version checks, build
|
|
# steps, postprocessing etc.
|
|
|
|
export HOME=$TMPDIR
|
|
cd "${packageName}"
|
|
runHook preRebuild
|
|
|
|
${lib.optionalString bypassCache ''
|
|
${lib.optionalString reconstructLock ''
|
|
if [ -f package-lock.json ]
|
|
then
|
|
echo "WARNING: Reconstruct lock option enabled, but a lock file already exists!"
|
|
echo "This will most likely result in version mismatches! We will remove the lock file and regenerate it!"
|
|
rm package-lock.json
|
|
else
|
|
echo "No package-lock.json file found, reconstructing..."
|
|
fi
|
|
|
|
node ${reconstructPackageLock}
|
|
''}
|
|
|
|
node ${addIntegrityFieldsScript}
|
|
''}
|
|
|
|
npm ${forceOfflineFlag} --nodedir=${nodeSources} ${npmFlags} ${lib.optionalString production "--production"} rebuild
|
|
|
|
runHook postRebuild
|
|
|
|
if [ "''${dontNpmInstall-}" != "1" ]
|
|
then
|
|
# NPM tries to download packages even when they already exist if npm-shrinkwrap is used.
|
|
rm -f npm-shrinkwrap.json
|
|
|
|
npm ${forceOfflineFlag} --nodedir=${nodeSources} --no-bin-links --ignore-scripts ${npmFlags} ${lib.optionalString production "--production"} install
|
|
fi
|
|
|
|
# Link executables defined in package.json
|
|
node ${linkBinsScript}
|
|
'';
|
|
|
|
# Builds and composes an NPM package including all its dependencies
|
|
buildNodePackage =
|
|
{ name
|
|
, packageName
|
|
, version ? null
|
|
, dependencies ? []
|
|
, buildInputs ? []
|
|
, production ? true
|
|
, npmFlags ? ""
|
|
, dontNpmInstall ? false
|
|
, bypassCache ? false
|
|
, reconstructLock ? false
|
|
, preRebuild ? ""
|
|
, dontStrip ? true
|
|
, unpackPhase ? "true"
|
|
, buildPhase ? "true"
|
|
, meta ? {}
|
|
, ... }@args:
|
|
|
|
let
|
|
extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" "dontStrip" "dontNpmInstall" "preRebuild" "unpackPhase" "buildPhase" "meta" ];
|
|
in
|
|
stdenv.mkDerivation ({
|
|
name = "${name}${if version == null then "" else "-${version}"}";
|
|
buildInputs = [ tarWrapper python nodejs ]
|
|
++ lib.optional (stdenv.isLinux) utillinux
|
|
++ lib.optional (stdenv.isDarwin) libtool
|
|
++ buildInputs;
|
|
|
|
inherit nodejs;
|
|
|
|
inherit dontStrip; # Stripping may fail a build for some package deployments
|
|
inherit dontNpmInstall preRebuild unpackPhase buildPhase;
|
|
|
|
compositionScript = composePackage args;
|
|
pinpointDependenciesScript = pinpointDependenciesOfPackage args;
|
|
|
|
passAsFile = [ "compositionScript" "pinpointDependenciesScript" ];
|
|
|
|
installPhase = ''
|
|
source ${installPackage}
|
|
|
|
# Create and enter a root node_modules/ folder
|
|
mkdir -p $out/lib/node_modules
|
|
cd $out/lib/node_modules
|
|
|
|
# Compose the package and all its dependencies
|
|
source $compositionScriptPath
|
|
|
|
${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }}
|
|
|
|
# Create symlink to the deployed executable folder, if applicable
|
|
if [ -d "$out/lib/node_modules/.bin" ]
|
|
then
|
|
ln -s $out/lib/node_modules/.bin $out/bin
|
|
|
|
# Patch the shebang lines of all the executables
|
|
ls $out/bin/* | while read i
|
|
do
|
|
file="$(readlink -f "$i")"
|
|
chmod u+rwx "$file"
|
|
patchShebangs "$file"
|
|
done
|
|
fi
|
|
|
|
# Create symlinks to the deployed manual page folders, if applicable
|
|
if [ -d "$out/lib/node_modules/${packageName}/man" ]
|
|
then
|
|
mkdir -p $out/share
|
|
for dir in "$out/lib/node_modules/${packageName}/man/"*
|
|
do
|
|
mkdir -p $out/share/man/$(basename "$dir")
|
|
for page in "$dir"/*
|
|
do
|
|
ln -s $page $out/share/man/$(basename "$dir")
|
|
done
|
|
done
|
|
fi
|
|
|
|
# Run post install hook, if provided
|
|
runHook postInstall
|
|
'';
|
|
|
|
meta = {
|
|
# default to Node.js' platforms
|
|
platforms = nodejs.meta.platforms;
|
|
} // meta;
|
|
} // extraArgs);
|
|
|
|
# Builds a node environment (a node_modules folder and a set of binaries)
|
|
buildNodeDependencies =
|
|
{ name
|
|
, packageName
|
|
, version ? null
|
|
, src
|
|
, dependencies ? []
|
|
, buildInputs ? []
|
|
, production ? true
|
|
, npmFlags ? ""
|
|
, dontNpmInstall ? false
|
|
, bypassCache ? false
|
|
, reconstructLock ? false
|
|
, dontStrip ? true
|
|
, unpackPhase ? "true"
|
|
, buildPhase ? "true"
|
|
, ... }@args:
|
|
|
|
let
|
|
extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" ];
|
|
in
|
|
stdenv.mkDerivation ({
|
|
name = "node-dependencies-${name}${if version == null then "" else "-${version}"}";
|
|
|
|
buildInputs = [ tarWrapper python nodejs ]
|
|
++ lib.optional (stdenv.isLinux) utillinux
|
|
++ lib.optional (stdenv.isDarwin) libtool
|
|
++ buildInputs;
|
|
|
|
inherit dontStrip; # Stripping may fail a build for some package deployments
|
|
inherit dontNpmInstall unpackPhase buildPhase;
|
|
|
|
includeScript = includeDependencies { inherit dependencies; };
|
|
pinpointDependenciesScript = pinpointDependenciesOfPackage args;
|
|
|
|
passAsFile = [ "includeScript" "pinpointDependenciesScript" ];
|
|
|
|
installPhase = ''
|
|
source ${installPackage}
|
|
|
|
mkdir -p $out/${packageName}
|
|
cd $out/${packageName}
|
|
|
|
source $includeScriptPath
|
|
|
|
# Create fake package.json to make the npm commands work properly
|
|
cp ${src}/package.json .
|
|
chmod 644 package.json
|
|
${lib.optionalString bypassCache ''
|
|
if [ -f ${src}/package-lock.json ]
|
|
then
|
|
cp ${src}/package-lock.json .
|
|
chmod 644 package-lock.json
|
|
fi
|
|
''}
|
|
|
|
# Go to the parent folder to make sure that all packages are pinpointed
|
|
cd ..
|
|
${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
|
|
|
|
${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }}
|
|
|
|
# Expose the executables that were installed
|
|
cd ..
|
|
${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."}
|
|
|
|
mv ${packageName} lib
|
|
ln -s $out/lib/node_modules/.bin $out/bin
|
|
'';
|
|
} // extraArgs);
|
|
|
|
# Builds a development shell
|
|
buildNodeShell =
|
|
{ name
|
|
, packageName
|
|
, version ? null
|
|
, src
|
|
, dependencies ? []
|
|
, buildInputs ? []
|
|
, production ? true
|
|
, npmFlags ? ""
|
|
, dontNpmInstall ? false
|
|
, bypassCache ? false
|
|
, reconstructLock ? false
|
|
, dontStrip ? true
|
|
, unpackPhase ? "true"
|
|
, buildPhase ? "true"
|
|
, ... }@args:
|
|
|
|
let
|
|
nodeDependencies = buildNodeDependencies args;
|
|
extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" "dontStrip" "dontNpmInstall" "unpackPhase" "buildPhase" ];
|
|
in
|
|
stdenv.mkDerivation ({
|
|
name = "node-shell-${name}${if version == null then "" else "-${version}"}";
|
|
|
|
buildInputs = [ python nodejs ] ++ lib.optional (stdenv.isLinux) utillinux ++ buildInputs;
|
|
buildCommand = ''
|
|
mkdir -p $out/bin
|
|
cat > $out/bin/shell <<EOF
|
|
#! ${stdenv.shell} -e
|
|
$shellHook
|
|
exec ${stdenv.shell}
|
|
EOF
|
|
chmod +x $out/bin/shell
|
|
'';
|
|
|
|
# Provide the dependencies in a development shell through the NODE_PATH environment variable
|
|
inherit nodeDependencies;
|
|
shellHook = lib.optionalString (dependencies != []) ''
|
|
export NODE_PATH=${nodeDependencies}/lib/node_modules
|
|
export PATH="${nodeDependencies}/bin:$PATH"
|
|
'';
|
|
} // extraArgs);
|
|
in
|
|
{
|
|
buildNodeSourceDist = lib.makeOverridable buildNodeSourceDist;
|
|
buildNodePackage = lib.makeOverridable buildNodePackage;
|
|
buildNodeDependencies = lib.makeOverridable buildNodeDependencies;
|
|
buildNodeShell = lib.makeOverridable buildNodeShell;
|
|
}
|