diff options
Diffstat (limited to 'user/packages/node2nix/node-env.nix')
-rw-r--r-- | user/packages/node2nix/node-env.nix | 696 |
1 files changed, 354 insertions, 342 deletions
diff --git a/user/packages/node2nix/node-env.nix b/user/packages/node2nix/node-env.nix index b81347da..16720d90 100644 --- a/user/packages/node2nix/node-env.nix +++ b/user/packages/node2nix/node-env.nix @@ -1,15 +1,16 @@ # This file originates from node2nix -{ - lib, - stdenv, - nodejs, - python2, - pkgs, - libtool, - runCommand, - writeTextFile, - writeShellScript, -}: let +{ 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 @@ -22,7 +23,7 @@ else python2; # Create a tar wrapper that filters all the 'Ignoring unknown extended header keyword' noise - tarWrapper = runCommand "tarWrapper" {} '' + tarWrapper = runCommand "tarWrapper" { } '' mkdir -p $out/bin cat > $out/bin/tar <<EOF @@ -34,16 +35,16 @@ ''; # Function that generates a TGZ file from a NPM project - buildNodeSourceDist = { - name, - version, - src, - ... - }: + buildNodeSourceDist = + { name + , version + , src + , ... + }: stdenv.mkDerivation { name = "node-tarball-${name}-${version}"; inherit src; - buildInputs = [nodejs]; + 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) @@ -106,13 +107,14 @@ # 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 != []) ( + includeDependencies = { dependencies }: + lib.optionalString (dependencies != [ ]) ( '' mkdir -p node_modules cd node_modules '' - + (lib.concatMapStrings ( + + (lib.concatMapStrings + ( dependency: '' if [ ! -e "${dependency.packageName}" ]; then ${composePackage dependency} @@ -126,13 +128,13 @@ ); # Recursively composes the dependencies of a package - composePackage = { - name, - packageName, - src, - dependencies ? [], - ... - } @ args: + composePackage = + { name + , packageName + , src + , dependencies ? [ ] + , ... + } @ args: builtins.addErrorContext "while evaluating node package '${packageName}'" '' installPackage "${packageName}" "${src}" ${includeDependencies {inherit dependencies;}} @@ -140,106 +142,109 @@ ${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); - } - } - } + 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); + 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; - } - } - } - } + 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')); + /* 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); + /* 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" - } + /* 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 - ''} - ''; + ${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 - ''; + 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" {} '' + nodeSources = runCommand "node-sources" { } '' tar --no-same-owner --no-same-permissions -xf ${nodejs.src} mv node-* $out ''; @@ -446,293 +451,300 @@ ''; }; - 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 + 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 ${reconstructPackageLock} + node ${addIntegrityFieldsScript} ''} - node ${addIntegrityFieldsScript} - ''} + npm ${forceOfflineFlag} --nodedir=${nodeSources} ${npmFlags} ${lib.optionalString production "--production"} rebuild - npm ${forceOfflineFlag} --nodedir=${nodeSources} ${npmFlags} ${lib.optionalString production "--production"} rebuild + runHook postRebuild - 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 - 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 - npm ${forceOfflineFlag} --nodedir=${nodeSources} --no-bin-links --ignore-scripts ${npmFlags} ${lib.optionalString production "--production"} install - fi - - # Link executables defined in package.json - node ${linkBinsScript} - ''; + # 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 + 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}${ + name = "${name}${ if version == null then "" else "-${version}" }"; - buildInputs = - [tarWrapper python nodejs] + buildInputs = + [ tarWrapper python nodejs ] ++ lib.optional (stdenv.isLinux) utillinux ++ lib.optional (stdenv.isDarwin) libtool ++ buildInputs; - inherit nodejs; + inherit nodejs; - inherit dontStrip; # Stripping may fail a build for some package deployments - inherit dontNpmInstall preRebuild unpackPhase buildPhase; + inherit dontStrip; # Stripping may fail a build for some package deployments + inherit dontNpmInstall preRebuild unpackPhase buildPhase; - compositionScript = composePackage args; - pinpointDependenciesScript = pinpointDependenciesOfPackage args; + compositionScript = composePackage args; + pinpointDependenciesScript = pinpointDependenciesOfPackage args; - passAsFile = ["compositionScript" "pinpointDependenciesScript"]; + passAsFile = [ "compositionScript" "pinpointDependenciesScript" ]; - installPhase = '' - source ${installPackage} + installPhase = '' + source ${installPackage} - # Create and enter a root node_modules/ folder - mkdir -p $out/lib/node_modules - cd $out/lib/node_modules + # 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 + # Compose the package and all its dependencies + source $compositionScriptPath - ${prepareAndInvokeNPM {inherit packageName bypassCache reconstructLock npmFlags production;}} + ${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 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 + # 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 - ''; + # Run post install hook, if provided + runHook postInstall + ''; - meta = - { - # default to Node.js' platforms - platforms = nodejs.meta.platforms; - } - // meta; - } - // extraArgs); + 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 + 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}${ + name = "node-dependencies-${name}${ if version == null then "" else "-${version}" }"; - buildInputs = - [tarWrapper python nodejs] + 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; + inherit dontStrip; # Stripping may fail a build for some package deployments + inherit dontNpmInstall unpackPhase buildPhase; - includeScript = includeDependencies {inherit dependencies;}; - pinpointDependenciesScript = pinpointDependenciesOfPackage args; + includeScript = includeDependencies { inherit dependencies; }; + pinpointDependenciesScript = pinpointDependenciesOfPackage args; - passAsFile = ["includeScript" "pinpointDependenciesScript"]; + passAsFile = [ "includeScript" "pinpointDependenciesScript" ]; - installPhase = '' - source ${installPackage} + installPhase = '' + source ${installPackage} - mkdir -p $out/${packageName} - cd $out/${packageName} + mkdir -p $out/${packageName} + cd $out/${packageName} - source $includeScriptPath + 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 - ''} + # 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 .."} + # 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;}} + ${prepareAndInvokeNPM {inherit packageName bypassCache reconstructLock npmFlags production;}} - # Expose the executables that were installed - cd .. - ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} + # 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); + 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 + 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}${ + 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 - ''; + 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 { + # 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; |