feat(lsp): jsr support first pass (#22382)

This implementation heavily depends on there being a lockfile, meaning
JSR specifiers will always diagnose as uncached unless it's there. In
practice this affects cases where a `deno.json` isn't being used. Our
NPM specifier support isn't subject to this.

The reason for this is that the version constraint solving code is
currently buried in `deno_graph` and not usable from the LSP, so the
only way to reuse that logic is the solved-version map in the lockfile's
`packages.specifiers`.
This commit is contained in:
Nayeem Rahman 2024-02-12 22:12:49 +00:00 committed by GitHub
parent f60720090c
commit 49d82e609f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 265 additions and 15 deletions

View file

@ -42,6 +42,7 @@ use deno_lint::rules::LintRule;
use deno_runtime::deno_fs;
use deno_runtime::deno_node;
use deno_runtime::tokio_util::create_basic_runtime;
use deno_semver::jsr::JsrPackageReqReference;
use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageReq;
use log::error;
@ -328,6 +329,8 @@ impl DiagnosticsState {
for diagnostic in diagnostics {
if diagnostic.code
== Some(lsp::NumberOrString::String("no-cache".to_string()))
|| diagnostic.code
== Some(lsp::NumberOrString::String("no-cache-jsr".to_string()))
|| diagnostic.code
== Some(lsp::NumberOrString::String("no-cache-npm".to_string()))
{
@ -968,6 +971,8 @@ pub enum DenoDiagnostic {
NoAttributeType,
/// A remote module was not found in the cache.
NoCache(ModuleSpecifier),
/// A remote jsr package reference was not found in the cache.
NoCacheJsr(PackageReq, ModuleSpecifier),
/// A remote npm package reference was not found in the cache.
NoCacheNpm(PackageReq, ModuleSpecifier),
/// A local module was not found on the local file system.
@ -994,6 +999,7 @@ impl DenoDiagnostic {
Self::InvalidAttributeType(_) => "invalid-attribute-type",
Self::NoAttributeType => "no-attribute-type",
Self::NoCache(_) => "no-cache",
Self::NoCacheJsr(_, _) => "no-cache-jsr",
Self::NoCacheNpm(_, _) => "no-cache-npm",
Self::NoLocal(_) => "no-local",
Self::Redirect { .. } => "redirect",
@ -1072,7 +1078,7 @@ impl DenoDiagnostic {
}),
..Default::default()
},
"no-cache" | "no-cache-npm" => {
"no-cache" | "no-cache-jsr" | "no-cache-npm" => {
let data = diagnostic
.data
.clone()
@ -1188,6 +1194,7 @@ impl DenoDiagnostic {
match code.as_str() {
"import-map-remap"
| "no-cache"
| "no-cache-jsr"
| "no-cache-npm"
| "no-attribute-type"
| "redirect"
@ -1226,6 +1233,7 @@ impl DenoDiagnostic {
Self::InvalidAttributeType(assert_type) => (lsp::DiagnosticSeverity::ERROR, format!("The module is a JSON module and expected an attribute type of \"json\". Instead got \"{assert_type}\"."), None),
Self::NoAttributeType => (lsp::DiagnosticSeverity::ERROR, "The module is a JSON module and not being imported with an import attribute. Consider adding `with { type: \"json\" }` to the import statement.".to_string(), None),
Self::NoCache(specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Uncached or missing remote URL: {specifier}"), Some(json!({ "specifier": specifier }))),
Self::NoCacheJsr(pkg_req, specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Uncached or missing jsr package: {}", pkg_req), Some(json!({ "specifier": specifier }))),
Self::NoCacheNpm(pkg_req, specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Uncached or missing npm package: {}", pkg_req), Some(json!({ "specifier": specifier }))),
Self::NoLocal(specifier) => {
let sloppy_resolution = SloppyImportsResolver::resolve_with_fs(&deno_fs::RealFs, specifier);
@ -1310,7 +1318,7 @@ fn diagnose_resolution(
// If the module was redirected, we want to issue an informational
// diagnostic that indicates this. This then allows us to issue a code
// action to replace the specifier with the final redirected one.
if doc_specifier != specifier {
if specifier.scheme() != "jsr" && doc_specifier != specifier {
diagnostics.push(DenoDiagnostic::Redirect {
from: specifier.clone(),
to: doc_specifier.clone(),
@ -1332,8 +1340,11 @@ fn diagnose_resolution(
None => diagnostics.push(DenoDiagnostic::NoAttributeType),
}
}
} else if specifier.scheme() == "jsr" {
// TODO(nayeemrmn): Check if jsr specifiers are cached.
} else if let Ok(pkg_ref) =
JsrPackageReqReference::from_specifier(specifier)
{
let req = pkg_ref.into_inner().req;
diagnostics.push(DenoDiagnostic::NoCacheJsr(req, specifier.clone()));
} else if let Ok(pkg_ref) =
NpmPackageReqReference::from_specifier(specifier)
{