Invoke npm from downloaded Node

Julia created

Change summary

crates/collab/src/tests.rs               |  4 +---
crates/zed/src/languages/html.rs         | 10 +++++++---
crates/zed/src/languages/installation.rs | 23 ++++++++++++++++++-----
crates/zed/src/languages/python.rs       |  8 ++++----
crates/zed/src/languages/typescript.rs   |  9 +++++----
crates/zed/src/languages/yaml.rs         | 14 +++++++++-----
crates/zed/src/zed.rs                    |  2 +-
7 files changed, 45 insertions(+), 25 deletions(-)

Detailed changes

crates/collab/src/tests.rs 🔗

@@ -13,9 +13,7 @@ use client::{
 use collections::{HashMap, HashSet};
 use fs::FakeFs;
 use futures::{channel::oneshot, StreamExt as _};
-use gpui::{
-    executor::Deterministic, test::EmptyView, ModelHandle, Task, TestAppContext, ViewHandle,
-};
+use gpui::{executor::Deterministic, test::EmptyView, ModelHandle, TestAppContext, ViewHandle};
 use language::LanguageRegistry;
 use parking_lot::Mutex;
 use project::{Project, WorktreeId};

crates/zed/src/languages/html.rs 🔗

@@ -32,15 +32,18 @@ impl LspAdapter for HtmlLspAdapter {
 
     async fn fetch_latest_server_version(
         &self,
-        _: Arc<dyn HttpClient>,
+        http: Arc<dyn HttpClient>,
     ) -> Result<Box<dyn 'static + Any + Send>> {
-        Ok(Box::new(npm_package_latest_version("vscode-langservers-extracted").await?) as Box<_>)
+        Ok(
+            Box::new(npm_package_latest_version(http, "vscode-langservers-extracted").await?)
+                as Box<_>,
+        )
     }
 
     async fn fetch_server_binary(
         &self,
         version: Box<dyn 'static + Send + Any>,
-        _: Arc<dyn HttpClient>,
+        http: Arc<dyn HttpClient>,
         container_dir: PathBuf,
     ) -> Result<PathBuf> {
         let version = version.downcast::<String>().unwrap();
@@ -52,6 +55,7 @@ impl LspAdapter for HtmlLspAdapter {
 
         if fs::metadata(&binary_path).await.is_err() {
             npm_install_packages(
+                http,
                 [("vscode-langservers-extracted", version.as_str())],
                 &version_dir,
             )

crates/zed/src/languages/installation.rs 🔗

@@ -2,9 +2,9 @@ use anyhow::{anyhow, Context, Result};
 use async_compression::futures::bufread::GzipDecoder;
 use async_tar::Archive;
 use client::http::HttpClient;
-use futures::{io::BufReader, StreamExt};
+use futures::io::BufReader;
 use serde::Deserialize;
-use smol::fs::{self, File};
+use smol::fs::{self};
 use smol::io::AsyncReadExt;
 use std::{
     path::{Path, PathBuf},
@@ -75,10 +75,16 @@ pub async fn ensure_node_installation_dir(http: Arc<dyn HttpClient>) -> Result<P
     Ok(dbg!(node_dir))
 }
 
-pub async fn npm_package_latest_version(name: &str) -> Result<String> {
-    let output = smol::process::Command::new("npm")
+pub async fn npm_package_latest_version(http: Arc<dyn HttpClient>, name: &str) -> Result<String> {
+    let node_dir = ensure_node_installation_dir(http).await?;
+    let node_binary = node_dir.join("bin/npm");
+    let npm_file = node_dir.join("bin/npm");
+
+    let output = smol::process::Command::new(node_binary)
+        .arg(npm_file)
         .args(["-fetch-retry-mintimeout", "2000"])
         .args(["-fetch-retry-maxtimeout", "5000"])
+        .args(["-fetch-timeout", "5000"])
         .args(["info", name, "--json"])
         .output()
         .await
@@ -98,12 +104,19 @@ pub async fn npm_package_latest_version(name: &str) -> Result<String> {
 }
 
 pub async fn npm_install_packages(
+    http: Arc<dyn HttpClient>,
     packages: impl IntoIterator<Item = (&str, &str)>,
     directory: &Path,
 ) -> Result<()> {
-    let output = smol::process::Command::new("npm")
+    let node_dir = ensure_node_installation_dir(http).await?;
+    let node_binary = node_dir.join("bin/npm");
+    let npm_file = node_dir.join("bin/npm");
+
+    let output = smol::process::Command::new(node_binary)
+        .arg(npm_file)
         .args(["-fetch-retry-mintimeout", "2000"])
         .args(["-fetch-retry-maxtimeout", "5000"])
+        .args(["-fetch-timeout", "5000"])
         .arg("install")
         .arg("--prefix")
         .arg(directory)

crates/zed/src/languages/python.rs 🔗

@@ -30,15 +30,15 @@ impl LspAdapter for PythonLspAdapter {
 
     async fn fetch_latest_server_version(
         &self,
-        _: Arc<dyn HttpClient>,
+        http: Arc<dyn HttpClient>,
     ) -> Result<Box<dyn 'static + Any + Send>> {
-        Ok(Box::new(npm_package_latest_version("pyright").await?) as Box<_>)
+        Ok(Box::new(npm_package_latest_version(http, "pyright").await?) as Box<_>)
     }
 
     async fn fetch_server_binary(
         &self,
         version: Box<dyn 'static + Send + Any>,
-        _: Arc<dyn HttpClient>,
+        http: Arc<dyn HttpClient>,
         container_dir: PathBuf,
     ) -> Result<PathBuf> {
         let version = version.downcast::<String>().unwrap();
@@ -49,7 +49,7 @@ impl LspAdapter for PythonLspAdapter {
         let binary_path = version_dir.join(Self::BIN_PATH);
 
         if fs::metadata(&binary_path).await.is_err() {
-            npm_install_packages([("pyright", version.as_str())], &version_dir).await?;
+            npm_install_packages(http, [("pyright", version.as_str())], &version_dir).await?;
 
             if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() {
                 while let Some(entry) = entries.next().await {

crates/zed/src/languages/typescript.rs 🔗

@@ -40,18 +40,18 @@ impl LspAdapter for TypeScriptLspAdapter {
 
     async fn fetch_latest_server_version(
         &self,
-        _: Arc<dyn HttpClient>,
+        http: Arc<dyn HttpClient>,
     ) -> Result<Box<dyn 'static + Send + Any>> {
         Ok(Box::new(Versions {
-            typescript_version: npm_package_latest_version("typescript").await?,
-            server_version: npm_package_latest_version("typescript-language-server").await?,
+            typescript_version: npm_package_latest_version(http.clone(), "typescript").await?,
+            server_version: npm_package_latest_version(http, "typescript-language-server").await?,
         }) as Box<_>)
     }
 
     async fn fetch_server_binary(
         &self,
         versions: Box<dyn 'static + Send + Any>,
-        _: Arc<dyn HttpClient>,
+        http: Arc<dyn HttpClient>,
         container_dir: PathBuf,
     ) -> Result<PathBuf> {
         let versions = versions.downcast::<Versions>().unwrap();
@@ -66,6 +66,7 @@ impl LspAdapter for TypeScriptLspAdapter {
 
         if fs::metadata(&binary_path).await.is_err() {
             npm_install_packages(
+                http,
                 [
                     ("typescript", versions.typescript_version.as_str()),
                     (

crates/zed/src/languages/yaml.rs 🔗

@@ -34,15 +34,15 @@ impl LspAdapter for YamlLspAdapter {
 
     async fn fetch_latest_server_version(
         &self,
-        _: Arc<dyn HttpClient>,
+        http: Arc<dyn HttpClient>,
     ) -> Result<Box<dyn 'static + Any + Send>> {
-        Ok(Box::new(npm_package_latest_version("yaml-language-server").await?) as Box<_>)
+        Ok(Box::new(npm_package_latest_version(http, "yaml-language-server").await?) as Box<_>)
     }
 
     async fn fetch_server_binary(
         &self,
         version: Box<dyn 'static + Send + Any>,
-        _: Arc<dyn HttpClient>,
+        http: Arc<dyn HttpClient>,
         container_dir: PathBuf,
     ) -> Result<PathBuf> {
         let version = version.downcast::<String>().unwrap();
@@ -53,8 +53,12 @@ impl LspAdapter for YamlLspAdapter {
         let binary_path = version_dir.join(Self::BIN_PATH);
 
         if fs::metadata(&binary_path).await.is_err() {
-            npm_install_packages([("yaml-language-server", version.as_str())], &version_dir)
-                .await?;
+            npm_install_packages(
+                http,
+                [("yaml-language-server", version.as_str())],
+                &version_dir,
+            )
+            .await?;
 
             if let Some(mut entries) = fs::read_dir(&container_dir).await.log_err() {
                 while let Some(entry) = entries.next().await {

crates/zed/src/zed.rs 🔗

@@ -654,7 +654,7 @@ mod tests {
     use assets::Assets;
     use editor::{scroll::autoscroll::Autoscroll, DisplayPoint, Editor};
     use gpui::{
-        executor::Deterministic, AssetSource, MutableAppContext, Task, TestAppContext, ViewHandle,
+        executor::Deterministic, AssetSource, MutableAppContext, TestAppContext, ViewHandle,
     };
     use language::LanguageRegistry;
     use project::{Project, ProjectPath};