fix(lsp): respect DENO_FUTURE for BYONM config (#23207)

This commit is contained in:
Nayeem Rahman 2024-04-05 16:18:48 +01:00 committed by GitHub
parent 049e703331
commit 61f1b8e8dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 111 additions and 29 deletions

View File

@ -1145,6 +1145,7 @@ pub struct ConfigData {
pub lint_options: Arc<LintOptions>, pub lint_options: Arc<LintOptions>,
pub lint_rules: Arc<ConfiguredRules>, pub lint_rules: Arc<ConfiguredRules>,
pub ts_config: Arc<LspTsConfig>, pub ts_config: Arc<LspTsConfig>,
pub byonm: bool,
pub node_modules_dir: Option<PathBuf>, pub node_modules_dir: Option<PathBuf>,
pub vendor_dir: Option<PathBuf>, pub vendor_dir: Option<PathBuf>,
pub lockfile: Option<Arc<Mutex<Lockfile>>>, pub lockfile: Option<Arc<Mutex<Lockfile>>>,
@ -1267,8 +1268,6 @@ impl ConfigData {
let lint_rules = let lint_rules =
get_configured_rules(lint_options.rules.clone(), config_file.as_ref()); get_configured_rules(lint_options.rules.clone(), config_file.as_ref());
let ts_config = LspTsConfig::new(config_file.as_ref()); let ts_config = LspTsConfig::new(config_file.as_ref());
let node_modules_dir =
config_file.as_ref().and_then(resolve_node_modules_dir);
let vendor_dir = config_file.as_ref().and_then(|c| c.vendor_dir_path()); let vendor_dir = config_file.as_ref().and_then(|c| c.vendor_dir_path());
// Load lockfile // Load lockfile
@ -1327,6 +1326,23 @@ impl ConfigData {
} }
} }
} }
let byonm = std::env::var("DENO_UNSTABLE_BYONM").is_ok()
|| config_file
.as_ref()
.map(|c| c.has_unstable("byonm"))
.unwrap_or(false)
|| (std::env::var("DENO_FUTURE").is_ok()
&& package_json.is_some()
&& config_file
.as_ref()
.map(|c| c.json.node_modules_dir.is_none())
.unwrap_or(true));
if byonm {
lsp_log!(" Enabled 'bring your own node_modules'.");
}
let node_modules_dir = config_file
.as_ref()
.and_then(|c| resolve_node_modules_dir(c, byonm));
// Load import map // Load import map
let mut import_map = None; let mut import_map = None;
@ -1427,6 +1443,7 @@ impl ConfigData {
lint_options: Arc::new(lint_options), lint_options: Arc::new(lint_options),
lint_rules: Arc::new(lint_rules), lint_rules: Arc::new(lint_rules),
ts_config: Arc::new(ts_config), ts_config: Arc::new(ts_config),
byonm,
node_modules_dir, node_modules_dir,
vendor_dir, vendor_dir,
lockfile: lockfile.map(Mutex::new).map(Arc::new), lockfile: lockfile.map(Mutex::new).map(Arc::new),
@ -1648,7 +1665,10 @@ fn resolve_lockfile_from_config(config_file: &ConfigFile) -> Option<Lockfile> {
resolve_lockfile_from_path(lockfile_path) resolve_lockfile_from_path(lockfile_path)
} }
fn resolve_node_modules_dir(config_file: &ConfigFile) -> Option<PathBuf> { fn resolve_node_modules_dir(
config_file: &ConfigFile,
byonm: bool,
) -> Option<PathBuf> {
// For the language server, require an explicit opt-in via the // For the language server, require an explicit opt-in via the
// `nodeModulesDir: true` setting in the deno.json file. This is to // `nodeModulesDir: true` setting in the deno.json file. This is to
// reduce the chance of modifying someone's node_modules directory // reduce the chance of modifying someone's node_modules directory
@ -1657,7 +1677,8 @@ fn resolve_node_modules_dir(config_file: &ConfigFile) -> Option<PathBuf> {
if explicitly_disabled { if explicitly_disabled {
return None; return None;
} }
let enabled = config_file.json.node_modules_dir == Some(true) let enabled = byonm
|| config_file.json.node_modules_dir == Some(true)
|| config_file.json.vendor == Some(true); || config_file.json.vendor == Some(true);
if !enabled { if !enabled {
return None; return None;

View File

@ -4,7 +4,6 @@ use base64::Engine;
use deno_ast::MediaType; use deno_ast::MediaType;
use deno_core::anyhow::anyhow; use deno_core::anyhow::anyhow;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
use deno_core::resolve_url; use deno_core::resolve_url;
use deno_core::serde_json; use deno_core::serde_json;
use deno_core::serde_json::json; use deno_core::serde_json::json;
@ -14,7 +13,6 @@ use deno_core::url;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_graph::GraphKind; use deno_graph::GraphKind;
use deno_graph::Resolution; use deno_graph::Resolution;
use deno_lockfile::Lockfile;
use deno_npm::NpmSystemInfo; use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs; use deno_runtime::deno_fs;
use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::NodeResolver;
@ -54,6 +52,7 @@ use super::client::Client;
use super::code_lens; use super::code_lens;
use super::completions; use super::completions;
use super::config::Config; use super::config::Config;
use super::config::ConfigData;
use super::config::ConfigSnapshot; use super::config::ConfigSnapshot;
use super::config::UpdateImportsOnFileMoveEnabled; use super::config::UpdateImportsOnFileMoveEnabled;
use super::config::WorkspaceSettings; use super::config::WorkspaceSettings;
@ -92,7 +91,6 @@ use crate::args::get_root_cert_store;
use crate::args::CaData; use crate::args::CaData;
use crate::args::CacheSetting; use crate::args::CacheSetting;
use crate::args::CliOptions; use crate::args::CliOptions;
use crate::args::ConfigFile;
use crate::args::Flags; use crate::args::Flags;
use crate::cache::DenoDir; use crate::cache::DenoDir;
use crate::cache::FastInsecureHasher; use crate::cache::FastInsecureHasher;
@ -152,10 +150,9 @@ struct LspNpmConfigHash(u64);
impl LspNpmConfigHash { impl LspNpmConfigHash {
pub fn from_inner(inner: &Inner) -> Self { pub fn from_inner(inner: &Inner) -> Self {
let config_data = inner.config.tree.root_data(); let config_data = inner.config.tree.root_data();
let node_modules_dir = config_data let node_modules_dir =
.as_ref() config_data.and_then(|d| d.node_modules_dir.as_ref());
.and_then(|d| d.node_modules_dir.as_ref()); let lockfile = config_data.and_then(|d| d.lockfile.as_ref());
let lockfile = config_data.as_ref().and_then(|d| d.lockfile.as_ref());
let mut hasher = FastInsecureHasher::new(); let mut hasher = FastInsecureHasher::new();
hasher.write_hashable(node_modules_dir); hasher.write_hashable(node_modules_dir);
hasher.write_hashable(&inner.maybe_global_cache_path); hasher.write_hashable(&inner.maybe_global_cache_path);
@ -792,11 +789,7 @@ impl Inner {
&deno_dir, &deno_dir,
&self.initial_cwd, &self.initial_cwd,
&self.http_client, &self.http_client,
config_data.as_ref().and_then(|d| d.config_file.as_deref()), config_data,
config_data.as_ref().and_then(|d| d.lockfile.as_ref()),
config_data
.as_ref()
.and_then(|d| d.node_modules_dir.clone()),
) )
.await; .await;
let node_resolver = Arc::new(NodeResolver::new( let node_resolver = Arc::new(NodeResolver::new(
@ -854,16 +847,10 @@ async fn create_npm_resolver(
deno_dir: &DenoDir, deno_dir: &DenoDir,
initial_cwd: &Path, initial_cwd: &Path,
http_client: &Arc<HttpClient>, http_client: &Arc<HttpClient>,
maybe_config_file: Option<&ConfigFile>, config_data: Option<&ConfigData>,
maybe_lockfile: Option<&Arc<Mutex<Lockfile>>>,
maybe_node_modules_dir_path: Option<PathBuf>,
) -> Arc<dyn CliNpmResolver> { ) -> Arc<dyn CliNpmResolver> {
let is_byonm = std::env::var("DENO_UNSTABLE_BYONM").as_deref() == Ok("1") let byonm = config_data.map(|d| d.byonm).unwrap_or(false);
|| maybe_config_file create_cli_npm_resolver_for_lsp(if byonm {
.as_ref()
.map(|c| c.has_unstable("byonm"))
.unwrap_or(false);
create_cli_npm_resolver_for_lsp(if is_byonm {
CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions { CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions {
fs: Arc::new(deno_fs::RealFs), fs: Arc::new(deno_fs::RealFs),
root_node_modules_dir: initial_cwd.join("node_modules"), root_node_modules_dir: initial_cwd.join("node_modules"),
@ -871,7 +858,7 @@ async fn create_npm_resolver(
} else { } else {
CliNpmResolverCreateOptions::Managed(CliNpmResolverManagedCreateOptions { CliNpmResolverCreateOptions::Managed(CliNpmResolverManagedCreateOptions {
http_client: http_client.clone(), http_client: http_client.clone(),
snapshot: match maybe_lockfile { snapshot: match config_data.and_then(|d| d.lockfile.as_ref()) {
Some(lockfile) => { Some(lockfile) => {
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(
lockfile.clone(), lockfile.clone(),
@ -890,7 +877,8 @@ async fn create_npm_resolver(
// the user is typing. // the user is typing.
cache_setting: CacheSetting::Only, cache_setting: CacheSetting::Only,
text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly), text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly),
maybe_node_modules_path: maybe_node_modules_dir_path, maybe_node_modules_path: config_data
.and_then(|d| d.node_modules_dir.clone()),
// do not install while resolving in the lsp—leave that to the cache command // do not install while resolving in the lsp—leave that to the cache command
package_json_installer: package_json_installer:
CliNpmResolverManagedPackageJsonInstallerOption::NoInstall, CliNpmResolverManagedPackageJsonInstallerOption::NoInstall,

View File

@ -11786,6 +11786,56 @@ fn lsp_jupyter_byonm_diagnostics() {
client.shutdown(); client.shutdown();
} }
#[test]
fn lsp_deno_future_env_byonm() {
let context = TestContextBuilder::for_npm()
.env("DENO_FUTURE", "1")
.use_temp_cwd()
.build();
let temp_dir = context.temp_dir();
temp_dir.path().join("package.json").write_json(&json!({
"dependencies": {
"@denotest/esm-basic": "*",
},
}));
context.run_npm("install");
let mut client = context.new_lsp_command().build();
client.initialize_default();
let diagnostics = client.did_open(json!({
"textDocument": {
"uri": temp_dir.uri().join("file.ts").unwrap(),
"languageId": "typescript",
"version": 1,
"text": r#"
import "npm:chalk";
import "@denotest/esm-basic";
"#,
},
}));
assert_eq!(
json!(diagnostics.all()),
json!([
{
"range": {
"start": {
"line": 1,
"character": 15,
},
"end": {
"line": 1,
"character": 26,
},
},
"severity": 1,
"code": "resolver-error",
"source": "deno",
"message": format!("Could not find a matching package for 'npm:chalk' in '{}'. You must specify this as a package.json dependency when the node_modules folder is not managed by Deno.", temp_dir.path().join("package.json")),
},
])
);
client.shutdown();
}
#[test] #[test]
fn lsp_sloppy_imports_warn() { fn lsp_sloppy_imports_warn() {
let context = TestContextBuilder::new().use_temp_cwd().build(); let context = TestContextBuilder::new().use_temp_cwd().build();

View File

@ -292,9 +292,13 @@ impl TestContext {
} }
pub fn new_lsp_command(&self) -> LspClientBuilder { pub fn new_lsp_command(&self) -> LspClientBuilder {
LspClientBuilder::new_with_dir(self.deno_dir.clone()) let mut builder = LspClientBuilder::new_with_dir(self.deno_dir.clone())
.deno_exe(&self.deno_exe) .deno_exe(&self.deno_exe)
.set_root_dir(self.temp_dir.path().clone()) .set_root_dir(self.temp_dir.path().clone());
for (key, value) in &self.envs {
builder = builder.env(key, value);
}
builder
} }
pub fn run_npm(&self, args: impl AsRef<str>) { pub fn run_npm(&self, args: impl AsRef<str>) {

View File

@ -33,7 +33,10 @@ use serde::Serialize;
use serde_json::json; use serde_json::json;
use serde_json::to_value; use serde_json::to_value;
use serde_json::Value; use serde_json::Value;
use std::collections::HashMap;
use std::collections::HashSet; use std::collections::HashSet;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::io; use std::io;
use std::io::BufRead; use std::io::BufRead;
use std::io::BufReader; use std::io::BufReader;
@ -465,6 +468,7 @@ pub struct LspClientBuilder {
root_dir: PathRef, root_dir: PathRef,
use_diagnostic_sync: bool, use_diagnostic_sync: bool,
deno_dir: TempDir, deno_dir: TempDir,
envs: HashMap<OsString, OsString>,
} }
impl LspClientBuilder { impl LspClientBuilder {
@ -481,6 +485,7 @@ impl LspClientBuilder {
root_dir: deno_dir.path().clone(), root_dir: deno_dir.path().clone(),
use_diagnostic_sync: true, use_diagnostic_sync: true,
deno_dir, deno_dir,
envs: Default::default(),
} }
} }
@ -514,6 +519,17 @@ impl LspClientBuilder {
self self
} }
pub fn env(
mut self,
key: impl AsRef<OsStr>,
value: impl AsRef<OsStr>,
) -> Self {
self
.envs
.insert(key.as_ref().to_owned(), value.as_ref().to_owned());
self
}
pub fn build(&self) -> LspClient { pub fn build(&self) -> LspClient {
self.build_result().unwrap() self.build_result().unwrap()
} }
@ -534,6 +550,9 @@ impl LspClientBuilder {
.arg("lsp") .arg("lsp")
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()); .stdout(Stdio::piped());
for (key, value) in &self.envs {
command.env(key, value);
}
if self.capture_stderr { if self.capture_stderr {
command.stderr(Stdio::piped()); command.stderr(Stdio::piped());
} else if !self.print_stderr { } else if !self.print_stderr {