summaryrefslogtreecommitdiff
path: root/cli/lsp
diff options
context:
space:
mode:
Diffstat (limited to 'cli/lsp')
-rw-r--r--cli/lsp/cache.rs2
-rw-r--r--cli/lsp/diagnostics.rs29
-rw-r--r--cli/lsp/documents.rs69
-rw-r--r--cli/lsp/tsc.rs58
4 files changed, 118 insertions, 40 deletions
diff --git a/cli/lsp/cache.rs b/cli/lsp/cache.rs
index 47f6e44ba..23469a583 100644
--- a/cli/lsp/cache.rs
+++ b/cli/lsp/cache.rs
@@ -68,7 +68,7 @@ impl CacheMetadata {
&self,
specifier: &ModuleSpecifier,
) -> Option<Arc<HashMap<MetadataKey, String>>> {
- if specifier.scheme() == "file" || specifier.scheme() == "npm" {
+ if matches!(specifier.scheme(), "file" | "npm" | "node") {
return None;
}
let version = self
diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs
index b4be63a55..4faff2fae 100644
--- a/cli/lsp/diagnostics.rs
+++ b/cli/lsp/diagnostics.rs
@@ -13,6 +13,7 @@ use super::tsc;
use super::tsc::TsServer;
use crate::args::LintOptions;
+use crate::node;
use crate::npm::NpmPackageReference;
use crate::tools::lint::get_configured_rules;
@@ -614,6 +615,8 @@ pub enum DenoDiagnostic {
},
/// An error occurred when resolving the specifier string.
ResolutionError(deno_graph::ResolutionError),
+ /// Invalid `node:` specifier.
+ InvalidNodeSpecifier(ModuleSpecifier),
}
impl DenoDiagnostic {
@@ -641,6 +644,7 @@ impl DenoDiagnostic {
},
ResolutionError::ResolverError { .. } => "resolver-error",
},
+ Self::InvalidNodeSpecifier(_) => "resolver-error",
}
}
@@ -791,6 +795,7 @@ impl DenoDiagnostic {
Self::NoLocal(specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Unable to load a local module: \"{}\".\n Please check the file path.", specifier), None),
Self::Redirect { from, to} => (lsp::DiagnosticSeverity::INFORMATION, format!("The import of \"{}\" was redirected to \"{}\".", from, to), Some(json!({ "specifier": from, "redirect": to }))),
Self::ResolutionError(err) => (lsp::DiagnosticSeverity::ERROR, err.to_string(), None),
+ Self::InvalidNodeSpecifier(specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Unknown Node built-in module: {}", specifier.path()), None),
};
lsp::Diagnostic {
range: *range,
@@ -872,6 +877,30 @@ fn diagnose_resolved(
);
}
}
+ } else if let Some(module_name) = specifier.as_str().strip_prefix("node:")
+ {
+ if node::resolve_builtin_node_module(module_name).is_err() {
+ diagnostics.push(
+ DenoDiagnostic::InvalidNodeSpecifier(specifier.clone())
+ .to_lsp_diagnostic(&range),
+ );
+ } else if let Some(npm_resolver) = &snapshot.maybe_npm_resolver {
+ // check that a @types/node package exists in the resolver
+ let types_node_ref =
+ NpmPackageReference::from_str("npm:@types/node").unwrap();
+ if npm_resolver
+ .resolve_package_folder_from_deno_module(&types_node_ref.req)
+ .is_err()
+ {
+ diagnostics.push(
+ DenoDiagnostic::NoCacheNpm(
+ types_node_ref,
+ ModuleSpecifier::parse("npm:@types/node").unwrap(),
+ )
+ .to_lsp_diagnostic(&range),
+ );
+ }
+ }
} else {
// When the document is not available, it means that it cannot be found
// in the cache or locally on the disk, so we want to issue a diagnostic
diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs
index bc1e4a808..b99b64bfe 100644
--- a/cli/lsp/documents.rs
+++ b/cli/lsp/documents.rs
@@ -770,6 +770,9 @@ pub struct Documents {
maybe_resolver: Option<CliResolver>,
/// The npm package requirements.
npm_reqs: Arc<HashSet<NpmPackageReq>>,
+ /// Gets if any document had a node: specifier such that a @types/node package
+ /// should be injected.
+ has_injected_types_node_package: bool,
/// Resolves a specifier to its final redirected to specifier.
specifier_resolver: Arc<SpecifierResolver>,
}
@@ -785,6 +788,7 @@ impl Documents {
imports: Default::default(),
maybe_resolver: None,
npm_reqs: Default::default(),
+ has_injected_types_node_package: false,
specifier_resolver: Arc::new(SpecifierResolver::new(location)),
}
}
@@ -925,6 +929,12 @@ impl Documents {
(*self.npm_reqs).clone()
}
+ /// Returns if a @types/node package was injected into the npm
+ /// resolver based on the state of the documents.
+ pub fn has_injected_types_node_package(&self) -> bool {
+ self.has_injected_types_node_package
+ }
+
/// Return a document for the specifier.
pub fn get(&self, original_specifier: &ModuleSpecifier) -> Option<Document> {
let specifier = self.specifier_resolver.resolve(original_specifier)?;
@@ -985,11 +995,15 @@ impl Documents {
/// tsc when type checking.
pub fn resolve(
&self,
- specifiers: &[String],
- referrer: &ModuleSpecifier,
+ specifiers: Vec<String>,
+ referrer_doc: &AssetOrDocument,
maybe_npm_resolver: Option<&NpmPackageResolver>,
- ) -> Option<Vec<Option<(ModuleSpecifier, MediaType)>>> {
- let dependencies = self.get(referrer)?.0.dependencies.clone();
+ ) -> Vec<Option<(ModuleSpecifier, MediaType)>> {
+ let referrer = referrer_doc.specifier();
+ let dependencies = match referrer_doc {
+ AssetOrDocument::Asset(_) => None,
+ AssetOrDocument::Document(doc) => Some(doc.0.dependencies.clone()),
+ };
let mut results = Vec::new();
for specifier in specifiers {
if let Some(npm_resolver) = maybe_npm_resolver {
@@ -997,7 +1011,7 @@ impl Documents {
// we're in an npm package, so use node resolution
results.push(Some(NodeResolution::into_specifier_and_media_type(
node::node_resolve(
- specifier,
+ &specifier,
referrer,
NodeResolutionMode::Types,
npm_resolver,
@@ -1009,15 +1023,28 @@ impl Documents {
continue;
}
}
- // handle npm:<package> urls
+ if let Some(module_name) = specifier.strip_prefix("node:") {
+ if crate::node::resolve_builtin_node_module(module_name).is_ok() {
+ // return itself for node: specifiers because during type checking
+ // we resolve to the ambient modules in the @types/node package
+ // rather than deno_std/node
+ results.push(Some((
+ ModuleSpecifier::parse(&specifier).unwrap(),
+ MediaType::Dts,
+ )));
+ continue;
+ }
+ }
if specifier.starts_with("asset:") {
- if let Ok(specifier) = ModuleSpecifier::parse(specifier) {
+ if let Ok(specifier) = ModuleSpecifier::parse(&specifier) {
let media_type = MediaType::from(&specifier);
results.push(Some((specifier, media_type)));
} else {
results.push(None);
}
- } else if let Some(dep) = dependencies.deps.get(specifier) {
+ } else if let Some(dep) =
+ dependencies.as_ref().and_then(|d| d.deps.get(&specifier))
+ {
if let Resolved::Ok { specifier, .. } = &dep.maybe_type {
results.push(self.resolve_dependency(specifier, maybe_npm_resolver));
} else if let Resolved::Ok { specifier, .. } = &dep.maybe_code {
@@ -1026,12 +1053,12 @@ impl Documents {
results.push(None);
}
} else if let Some(Resolved::Ok { specifier, .. }) =
- self.resolve_imports_dependency(specifier)
+ self.resolve_imports_dependency(&specifier)
{
// clone here to avoid double borrow of self
let specifier = specifier.clone();
results.push(self.resolve_dependency(&specifier, maybe_npm_resolver));
- } else if let Ok(npm_ref) = NpmPackageReference::from_str(specifier) {
+ } else if let Ok(npm_ref) = NpmPackageReference::from_str(&specifier) {
results.push(maybe_npm_resolver.map(|npm_resolver| {
NodeResolution::into_specifier_and_media_type(
node_resolve_npm_reference(
@@ -1048,7 +1075,7 @@ impl Documents {
results.push(None);
}
}
- Some(results)
+ results
}
/// Update the location of the on disk cache for the document store.
@@ -1125,6 +1152,7 @@ impl Documents {
analyzed_specifiers: HashSet<ModuleSpecifier>,
pending_specifiers: VecDeque<ModuleSpecifier>,
npm_reqs: HashSet<NpmPackageReq>,
+ has_node_builtin_specifier: bool,
}
impl DocAnalyzer {
@@ -1148,7 +1176,11 @@ impl Documents {
fn analyze_doc(&mut self, specifier: &ModuleSpecifier, doc: &Document) {
self.analyzed_specifiers.insert(specifier.clone());
- for dependency in doc.dependencies().values() {
+ for (name, dependency) in doc.dependencies() {
+ if !self.has_node_builtin_specifier && name.starts_with("node:") {
+ self.has_node_builtin_specifier = true;
+ }
+
if let Some(dep) = dependency.get_code() {
self.add(dep, specifier);
}
@@ -1185,8 +1217,19 @@ impl Documents {
}
}
+ let mut npm_reqs = doc_analyzer.npm_reqs;
+ // Ensure a @types/node package exists when any module uses a node: specifier.
+ // Unlike on the command line, here we just add @types/node to the npm package
+ // requirements since this won't end up in the lockfile.
+ self.has_injected_types_node_package = doc_analyzer
+ .has_node_builtin_specifier
+ && !npm_reqs.iter().any(|r| r.name == "@types/node");
+ if self.has_injected_types_node_package {
+ npm_reqs.insert(NpmPackageReq::from_str("@types/node").unwrap());
+ }
+
self.dependents_map = Arc::new(doc_analyzer.dependents_map);
- self.npm_reqs = Arc::new(doc_analyzer.npm_reqs);
+ self.npm_reqs = Arc::new(npm_reqs);
self.dirty = false;
file_system_docs.dirty = false;
}
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index ced8fb699..3619f529c 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -216,7 +216,7 @@ fn new_assets_map() -> Arc<Mutex<AssetsMap>> {
let asset = AssetDocument::new(specifier.clone(), v);
(specifier, asset)
})
- .collect();
+ .collect::<AssetsMap>();
Arc::new(Mutex::new(assets))
}
@@ -2728,28 +2728,29 @@ fn op_resolve(
let state = state.borrow_mut::<State>();
let mark = state.performance.mark("op_resolve", Some(&args));
let referrer = state.normalize_specifier(&args.base)?;
-
- let result = if let Some(resolved) = state.state_snapshot.documents.resolve(
- &args.specifiers,
- &referrer,
- state.state_snapshot.maybe_npm_resolver.as_ref(),
- ) {
- Ok(
- resolved
- .into_iter()
- .map(|o| {
- o.map(|(s, mt)| (s.to_string(), mt.as_ts_extension().to_string()))
- })
- .collect(),
- )
- } else {
- Err(custom_error(
+ let result = match state.get_asset_or_document(&referrer) {
+ Some(referrer_doc) => {
+ let resolved = state.state_snapshot.documents.resolve(
+ args.specifiers,
+ &referrer_doc,
+ state.state_snapshot.maybe_npm_resolver.as_ref(),
+ );
+ Ok(
+ resolved
+ .into_iter()
+ .map(|o| {
+ o.map(|(s, mt)| (s.to_string(), mt.as_ts_extension().to_string()))
+ })
+ .collect(),
+ )
+ }
+ None => Err(custom_error(
"NotFound",
format!(
"Error resolving. Referring specifier \"{}\" was not found.",
args.base
),
- ))
+ )),
};
state.performance.measure(mark);
@@ -2764,15 +2765,20 @@ fn op_respond(state: &mut OpState, args: Response) -> bool {
}
#[op]
-fn op_script_names(state: &mut OpState) -> Vec<ModuleSpecifier> {
+fn op_script_names(state: &mut OpState) -> Vec<String> {
let state = state.borrow_mut::<State>();
- state
- .state_snapshot
- .documents
- .documents(true, true)
- .into_iter()
- .map(|d| d.specifier().clone())
- .collect()
+ let documents = &state.state_snapshot.documents;
+ let open_docs = documents.documents(true, true);
+
+ let mut result = Vec::with_capacity(open_docs.len() + 1);
+
+ if documents.has_injected_types_node_package() {
+ // ensure this is first so it resolves the node types first
+ result.push("asset:///node_types.d.ts".to_string());
+ }
+
+ result.extend(open_docs.into_iter().map(|d| d.specifier().to_string()));
+ result
}
#[derive(Debug, Deserialize, Serialize)]