diff --git a/pkgs/build-support/node/build-npm-package/hooks/default.nix b/pkgs/build-support/node/build-npm-package/hooks/default.nix
index 36f0319e3d23..7a4fed30f5d5 100644
--- a/pkgs/build-support/node/build-npm-package/hooks/default.nix
+++ b/pkgs/build-support/node/build-npm-package/hooks/default.nix
@@ -7,6 +7,8 @@
 , prefetch-npm-deps
 , diffutils
 , installShellFiles
+, nodejsInstallManuals
+, nodejsInstallExecutables
 }:
 
 {
@@ -39,9 +41,10 @@
       propagatedBuildInputs = [
         installShellFiles
         makeWrapper
+        nodejsInstallManuals
+        nodejsInstallExecutables
       ];
       substitutions = {
-        hostNode = "${nodejs}/bin/node";
         jq = "${jq}/bin/jq";
       };
     } ./npm-install-hook.sh;
diff --git a/pkgs/build-support/node/build-npm-package/hooks/npm-install-hook.sh b/pkgs/build-support/node/build-npm-package/hooks/npm-install-hook.sh
index 750ed421789f..d495caf2d048 100644
--- a/pkgs/build-support/node/build-npm-package/hooks/npm-install-hook.sh
+++ b/pkgs/build-support/node/build-npm-package/hooks/npm-install-hook.sh
@@ -14,31 +14,9 @@ npmInstallHook() {
         cp "${npmWorkspace-.}/$file" "$dest"
     done < <(@jq@ --raw-output '.[0].files | map(.path | select(. | startswith("node_modules/") | not)) | join("\n")' <<< "$(npm_config_cache="$HOME/.npm" npm pack --json --dry-run --loglevel=warn --no-foreground-scripts ${npmWorkspace+--workspace=$npmWorkspace} $npmPackFlags "${npmPackFlagsArray[@]}" $npmFlags "${npmFlagsArray[@]}")")
 
-    # Based on code from Python's buildPythonPackage wrap.sh script, for
-    # supporting both the case when makeWrapperArgs is an array and a
-    # IFS-separated string.
-    #
-    # TODO: remove the string branch when __structuredAttrs are used.
-    if [[ "${makeWrapperArgs+defined}" == "defined" && "$(declare -p makeWrapperArgs)" =~ ^'declare -a makeWrapperArgs=' ]]; then
-        local -a user_args=("${makeWrapperArgs[@]}")
-    else
-        local -a user_args="(${makeWrapperArgs:-})"
-    fi
-    while IFS=" " read -ra bin; do
-        mkdir -p "$out/bin"
-        makeWrapper @hostNode@ "$out/bin/${bin[0]}" --add-flags "$packageOut/${bin[1]}" "${user_args[@]}"
-    done < <(@jq@ --raw-output '(.bin | type) as $typ | if $typ == "string" then
-        .name + " " + .bin
-        elif $typ == "object" then .bin | to_entries | map(.key + " " + .value) | join("\n")
-        elif $typ == "null" then empty
-        else "invalid type " + $typ | halt_error end' "${npmWorkspace-.}/package.json")
+    nodejsInstallExecutables "${npmWorkspace-.}/package.json"
 
-    while IFS= read -r man; do
-        installManPage "$packageOut/$man"
-    done < <(@jq@ --raw-output '(.man | type) as $typ | if $typ == "string" then .man
-        elif $typ == "list" then .man | join("\n")
-        elif $typ == "null" then empty
-        else "invalid type " + $typ | halt_error end' "${npmWorkspace-.}/package.json")
+    nodejsInstallManuals "${npmWorkspace-.}/package.json"
 
     local -r nodeModulesPath="$packageOut/node_modules"
 
diff --git a/pkgs/by-name/no/nodejsInstallExecutables/hook.sh b/pkgs/by-name/no/nodejsInstallExecutables/hook.sh
new file mode 100644
index 000000000000..ab2a8e372da4
--- /dev/null
+++ b/pkgs/by-name/no/nodejsInstallExecutables/hook.sh
@@ -0,0 +1,27 @@
+# shellcheck shell=bash
+
+nodejsInstallExecutables() {
+    local -r packageJson="${1-./package.json}"
+
+    local -r packageOut="$out/lib/node_modules/$(@jq@ --raw-output '.name' package.json)"
+
+    # Based on code from Python's buildPythonPackage wrap.sh script, for
+    # supporting both the case when makeWrapperArgs is an array and a
+    # IFS-separated string.
+    #
+    # TODO: remove the string branch when __structuredAttrs are used.
+    if [[ "${makeWrapperArgs+defined}" == "defined" && "$(declare -p makeWrapperArgs)" =~ ^'declare -a makeWrapperArgs=' ]]; then
+        local -a user_args=("${makeWrapperArgs[@]}")
+    else
+        local -a user_args="(${makeWrapperArgs:-})"
+    fi
+
+    while IFS=" " read -ra bin; do
+        mkdir -p "$out/bin"
+        makeWrapper @hostNode@ "$out/bin/${bin[0]}" --add-flags "$packageOut/${bin[1]}" "${user_args[@]}"
+    done < <(@jq@ --raw-output '(.bin | type) as $typ | if $typ == "string" then
+        .name + " " + .bin
+        elif $typ == "object" then .bin | to_entries | map(.key + " " + .value) | join("\n")
+        elif $typ == "null" then empty
+        else "invalid type " + $typ | halt_error end' "$packageJson")
+}
diff --git a/pkgs/by-name/no/nodejsInstallExecutables/package.nix b/pkgs/by-name/no/nodejsInstallExecutables/package.nix
new file mode 100644
index 000000000000..8403cf8881d6
--- /dev/null
+++ b/pkgs/by-name/no/nodejsInstallExecutables/package.nix
@@ -0,0 +1,19 @@
+{
+  makeSetupHook,
+  installShellFiles,
+  makeWrapper,
+  nodejs,
+  jq,
+}:
+
+makeSetupHook {
+  name = "nodejs-install-executables";
+  propagatedBuildInputs = [
+    installShellFiles
+    makeWrapper
+  ];
+  substitutions = {
+    hostNode = "${nodejs}/bin/node";
+    jq = "${jq}/bin/jq";
+  };
+} ./hook.sh
diff --git a/pkgs/by-name/no/nodejsInstallManuals/hook.sh b/pkgs/by-name/no/nodejsInstallManuals/hook.sh
new file mode 100644
index 000000000000..f0ff209c6226
--- /dev/null
+++ b/pkgs/by-name/no/nodejsInstallManuals/hook.sh
@@ -0,0 +1,14 @@
+# shellcheck shell=bash
+
+nodejsInstallManuals() {
+    local -r packageJson="${1-./package.json}"
+
+    local -r packageOut="$out/lib/node_modules/$(@jq@ --raw-output '.name' package.json)"
+
+    while IFS= read -r man; do
+        installManPage "$packageOut/$man"
+    done < <(@jq@ --raw-output '(.man | type) as $typ | if $typ == "string" then .man
+        elif $typ == "list" then .man | join("\n")
+        elif $typ == "null" then empty
+        else "invalid type " + $typ | halt_error end' "$packageJson")
+}
diff --git a/pkgs/by-name/no/nodejsInstallManuals/package.nix b/pkgs/by-name/no/nodejsInstallManuals/package.nix
new file mode 100644
index 000000000000..9f74cf8538da
--- /dev/null
+++ b/pkgs/by-name/no/nodejsInstallManuals/package.nix
@@ -0,0 +1,13 @@
+{
+  makeSetupHook,
+  installShellFiles,
+  jq,
+}:
+
+makeSetupHook {
+  name = "nodejs-install-manuals";
+  propagatedBuildInputs = [ installShellFiles ];
+  substitutions = {
+    jq = "${jq}/bin/jq";
+  };
+} ./hook.sh