diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 5bdecca9bcfb35..fe9c279289e285 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -554,12 +554,12 @@ fn discover_package_json( /// /// In the future we will need to support it in user directory or global directory /// as per https://docs.npmjs.com/cli/v10/configuring-npm/npmrc#files. -fn discover_npmrc( +pub fn discover_npmrc( maybe_package_json_path: Option, maybe_deno_json_path: Option, -) -> Result, AnyError> { +) -> Result<(Arc, Option), AnyError> { if !*DENO_FUTURE { - return Ok(create_default_npmrc()); + return Ok((create_default_npmrc(), None)); } const NPMRC_NAME: &str = ".npmrc"; @@ -599,7 +599,7 @@ fn discover_npmrc( if let Some(package_json_path) = maybe_package_json_path { if let Some(package_json_dir) = package_json_path.parent() { if let Some((source, path)) = try_to_read_npmrc(package_json_dir)? { - return try_to_parse_npmrc(source, &path); + return try_to_parse_npmrc(source, &path).map(|r| (r, Some(path))); } } } @@ -607,13 +607,13 @@ fn discover_npmrc( if let Some(deno_json_path) = maybe_deno_json_path { if let Some(deno_json_dir) = deno_json_path.parent() { if let Some((source, path)) = try_to_read_npmrc(deno_json_dir)? { - return try_to_parse_npmrc(source, &path); + return try_to_parse_npmrc(source, &path).map(|r| (r, Some(path))); } } } log::debug!("No .npmrc file found"); - Ok(create_default_npmrc()) + Ok((create_default_npmrc(), None)) } pub fn create_default_npmrc() -> Arc { @@ -938,7 +938,7 @@ impl CliOptions { } else { maybe_package_json = discover_package_json(&flags, None, &initial_cwd)?; } - let npmrc = discover_npmrc( + let (npmrc, _) = discover_npmrc( maybe_package_json.as_ref().map(|p| p.path.clone()), maybe_config_file.as_ref().and_then(|cf| { if cf.specifier.scheme() == "file" { diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 02f3d5afb0770e..d3cdd2a9470f1a 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -1,6 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use super::logging::lsp_log; +use crate::args::discover_npmrc; use crate::args::read_lockfile_at_path; use crate::args::ConfigFile; use crate::args::FmtOptions; @@ -1175,10 +1176,12 @@ impl ConfigData { .entry(config_file.specifier.clone()) .or_insert(ConfigWatchedFileType::DenoJson); } - let config_file_canonicalized_specifier = config_file + let config_file_path = config_file .as_ref() - .and_then(|c| c.specifier.to_file_path().ok()) - .and_then(|p| canonicalize_path_maybe_not_exists(&p).ok()) + .and_then(|c| specifier_to_file_path(&c.specifier).ok()); + let config_file_canonicalized_specifier = config_file_path + .as_ref() + .and_then(|p| canonicalize_path_maybe_not_exists(p).ok()) .and_then(|p| ModuleSpecifier::from_file_path(p).ok()); if let Some(specifier) = config_file_canonicalized_specifier { watched_files @@ -1306,17 +1309,17 @@ impl ConfigData { // Load package.json let mut package_json = None; - // TODO(bartlomieju): support discovering .npmrc - let npmrc = None; - if let Ok(path) = specifier_to_file_path(scope) { - let path = path.join("package.json"); - if let Ok(specifier) = ModuleSpecifier::from_file_path(&path) { + let package_json_path = specifier_to_file_path(scope) + .ok() + .map(|p| p.join("package.json")); + if let Some(path) = &package_json_path { + if let Ok(specifier) = ModuleSpecifier::from_file_path(path) { watched_files .entry(specifier) .or_insert(ConfigWatchedFileType::PackageJson); } let package_json_canonicalized_specifier = - canonicalize_path_maybe_not_exists(&path) + canonicalize_path_maybe_not_exists(path) .ok() .and_then(|p| ModuleSpecifier::from_file_path(p).ok()); if let Some(specifier) = package_json_canonicalized_specifier { @@ -1324,7 +1327,7 @@ impl ConfigData { .entry(specifier) .or_insert(ConfigWatchedFileType::PackageJson); } - if let Ok(source) = std::fs::read_to_string(&path) { + if let Ok(source) = std::fs::read_to_string(path) { match PackageJson::load_from_string(path.clone(), source) { Ok(result) => { lsp_log!(" Resolved package.json: \"{}\"", path.display()); @@ -1340,6 +1343,17 @@ impl ConfigData { } } } + let npmrc = discover_npmrc(package_json_path, config_file_path) + .inspect(|(_, path)| { + if let Some(path) = path { + lsp_log!(" Resolved .npmrc: \"{}\"", path.display()); + } + }) + .inspect_err(|err| { + lsp_warn!(" Couldn't read .npmrc for \"{scope}\": {err}"); + }) + .map(|(r, _)| r) + .ok(); let byonm = std::env::var("DENO_UNSTABLE_BYONM").is_ok() || config_file .as_ref() @@ -1464,7 +1478,7 @@ impl ConfigData { vendor_dir, lockfile: lockfile.map(Mutex::new).map(Arc::new), package_json: package_json.map(Arc::new), - npmrc: npmrc.map(Arc::new), + npmrc, import_map: import_map.map(Arc::new), import_map_from_settings, watched_files, diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 09882a666df60d..51507b0a66f115 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -8860,6 +8860,83 @@ fn lsp_tls_cert() { client.shutdown(); } +#[test] +fn lsp_npmrc() { + let context = TestContextBuilder::new() + .use_http_server() + .use_temp_cwd() + .add_npm_env_vars() + .env("DENO_FUTURE", "1") + .build(); + let temp_dir = context.temp_dir(); + temp_dir.write( + temp_dir.path().join("deno.json"), + json!({ + "nodeModulesDir": true, + }) + .to_string(), + ); + temp_dir.write( + temp_dir.path().join("package.json"), + json!({ + "name": "npmrc_test", + "version": "0.0.1", + "dependencies": { + "@denotest/basic": "1.0.0", + }, + }) + .to_string(), + ); + temp_dir.write( + temp_dir.path().join(".npmrc"), + "\ +@denotest:registry=http://127.0.0.1:4261/ +//127.0.0.1:4261/:_authToken=private-reg-token +", + ); + let file = source_file( + temp_dir.path().join("main.ts"), + r#" + import { getValue, setValue } from "@denotest/basic"; + setValue(42); + const n: string = getValue(); + console.log(n); + "#, + ); + let mut client = context.new_lsp_command().build(); + client.initialize_default(); + client.write_request( + "workspace/executeCommand", + json!({ + "command": "deno.cache", + "arguments": [[], file.uri()], + }), + ); + let diagnostics = client.did_open_file(&file); + assert_eq!( + json!(diagnostics.all()), + json!([ + { + "range": { + "start": { + "line": 3, + "character": 12, + }, + "end": { + "line": 3, + "character": 13, + }, + }, + "severity": 1, + "code": 2322, + "source": "deno-ts", + "message": "Type 'number' is not assignable to type 'string'.", + }, + ]), + ); + client.shutdown(); +} + #[test] fn lsp_diagnostics_warn_redirect() { let context = TestContextBuilder::new()