Fix `npm install` error with some languages (#12087)

张小白 and Thorsten Ball created

If you have already installed `node` using `brew install node`, you are
fine. If you did not install `node` on you local machine, it fails.

The `node_binary` path is actually not included in environment variable.
When run `npm install`, some extensions like `eslint`, may run some
commands like `sh -c node .....`. Since `node_binary` path is not
included in `PATH` variable, `sh -c node ...` will fail complaining that
"command not found". If you have installed `node` before, `node` is
already included in `PATH`, so you are fine. If not, it fails.

Closes #11890

Release Notes:

- Fixed Zed's internal Node runtime not being put in `$PATH` correctly
when running language servers and other commands with `node`.
([#11890](https://github.com/zed-industries/zed/issues/11890))

---------

Co-authored-by: Thorsten Ball <mrnugget@gmail.com>

Change summary

crates/node_runtime/src/node_runtime.rs | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)

Detailed changes

crates/node_runtime/src/node_runtime.rs 🔗

@@ -226,18 +226,19 @@ impl NodeRuntime for RealNodeRuntime {
 
             let node_binary = installation_path.join(NODE_PATH);
             let npm_file = installation_path.join(NPM_PATH);
-            let mut env_path = node_binary
+            let mut env_path = vec![node_binary
                 .parent()
                 .expect("invalid node binary path")
-                .to_path_buf();
+                .to_path_buf()];
 
             if let Some(existing_path) = std::env::var_os("PATH") {
-                if !existing_path.is_empty() {
-                    env_path.push(":");
-                    env_path.push(&existing_path);
-                }
+                let mut paths = std::env::split_paths(&existing_path).collect::<Vec<_>>();
+                env_path.append(&mut paths);
             }
 
+            let env_path =
+                std::env::join_paths(env_path).context("failed to create PATH env variable")?;
+
             if smol::fs::metadata(&node_binary).await.is_err() {
                 return Err(anyhow!("missing node binary file"));
             }