2024-01-01 19:58:21 +00:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2021-01-22 10:03:16 +00:00
|
|
|
|
2022-02-02 02:04:26 +00:00
|
|
|
use super::cache::calculate_fs_version;
|
2023-07-08 20:06:45 +00:00
|
|
|
use super::cache::calculate_fs_version_at_path;
|
2024-02-15 19:49:35 +00:00
|
|
|
use super::cache::LSP_DISALLOW_GLOBAL_TO_LOCAL_COPY;
|
2024-03-21 04:29:52 +00:00
|
|
|
use super::config::Config;
|
2023-09-28 20:43:45 +00:00
|
|
|
use super::language_server::StateNpmSnapshot;
|
2021-01-22 10:03:16 +00:00
|
|
|
use super::text::LineIndex;
|
2021-06-04 21:31:44 +00:00
|
|
|
use super::tsc;
|
2021-11-12 16:42:04 +00:00
|
|
|
use super::tsc::AssetDocument;
|
2021-01-22 10:03:16 +00:00
|
|
|
|
2023-02-23 15:58:10 +00:00
|
|
|
use crate::args::package_json;
|
2022-11-28 22:28:54 +00:00
|
|
|
use crate::cache::HttpCache;
|
2024-03-01 21:34:13 +00:00
|
|
|
use crate::jsr::JsrCacheResolver;
|
2024-04-15 21:50:52 +00:00
|
|
|
use crate::lsp::logging::lsp_warn;
|
2023-09-30 16:06:38 +00:00
|
|
|
use crate::npm::CliNpmResolver;
|
2023-02-15 16:30:54 +00:00
|
|
|
use crate::resolver::CliGraphResolver;
|
2023-08-17 16:14:22 +00:00
|
|
|
use crate::resolver::CliGraphResolverOptions;
|
2024-03-06 00:23:51 +00:00
|
|
|
use crate::resolver::CliNodeResolver;
|
2023-12-07 20:59:13 +00:00
|
|
|
use crate::resolver::SloppyImportsFsEntry;
|
|
|
|
use crate::resolver::SloppyImportsResolution;
|
|
|
|
use crate::resolver::SloppyImportsResolver;
|
2024-04-17 14:19:55 +00:00
|
|
|
use deno_runtime::fs_util::specifier_to_file_path;
|
2021-10-28 23:56:01 +00:00
|
|
|
|
2024-04-09 22:12:55 +00:00
|
|
|
use dashmap::DashMap;
|
2021-09-07 14:39:32 +00:00
|
|
|
use deno_ast::MediaType;
|
2022-08-22 16:14:59 +00:00
|
|
|
use deno_ast::ParsedSource;
|
2021-10-28 23:56:01 +00:00
|
|
|
use deno_ast::SourceTextInfo;
|
2021-01-22 10:03:16 +00:00
|
|
|
use deno_core::error::custom_error;
|
|
|
|
use deno_core::error::AnyError;
|
2022-10-20 17:23:21 +00:00
|
|
|
use deno_core::futures::future;
|
2023-09-07 13:09:16 +00:00
|
|
|
use deno_core::futures::FutureExt;
|
2021-10-28 23:56:01 +00:00
|
|
|
use deno_core::parking_lot::Mutex;
|
2021-01-22 10:03:16 +00:00
|
|
|
use deno_core::ModuleSpecifier;
|
2023-10-24 13:37:02 +00:00
|
|
|
use deno_graph::source::ResolutionMode;
|
2022-08-09 21:27:22 +00:00
|
|
|
use deno_graph::GraphImport;
|
2023-02-10 03:00:23 +00:00
|
|
|
use deno_graph::Resolution;
|
2024-02-12 22:12:49 +00:00
|
|
|
use deno_lockfile::Lockfile;
|
2023-04-22 01:02:46 +00:00
|
|
|
use deno_runtime::deno_node;
|
|
|
|
use deno_runtime::deno_node::NodeResolution;
|
2022-11-30 23:07:32 +00:00
|
|
|
use deno_runtime::deno_node::NodeResolutionMode;
|
2023-01-10 13:35:44 +00:00
|
|
|
use deno_runtime::permissions::PermissionsContainer;
|
2024-03-04 15:48:23 +00:00
|
|
|
use deno_semver::jsr::JsrPackageReqReference;
|
2023-04-06 22:46:44 +00:00
|
|
|
use deno_semver::npm::NpmPackageReqReference;
|
2023-08-21 09:53:52 +00:00
|
|
|
use deno_semver::package::PackageReq;
|
2023-09-07 13:09:16 +00:00
|
|
|
use indexmap::IndexMap;
|
2023-05-11 00:06:59 +00:00
|
|
|
use package_json::PackageJsonDepsProvider;
|
2023-12-07 00:03:18 +00:00
|
|
|
use std::borrow::Cow;
|
2024-03-21 04:29:52 +00:00
|
|
|
use std::collections::BTreeSet;
|
2021-01-22 10:03:16 +00:00
|
|
|
use std::collections::HashMap;
|
2021-06-03 11:13:53 +00:00
|
|
|
use std::collections::HashSet;
|
2021-10-28 23:56:01 +00:00
|
|
|
use std::fs;
|
2021-01-22 10:03:16 +00:00
|
|
|
use std::ops::Range;
|
2021-06-02 10:29:58 +00:00
|
|
|
use std::str::FromStr;
|
2024-04-09 22:12:55 +00:00
|
|
|
use std::sync::atomic::AtomicBool;
|
|
|
|
use std::sync::atomic::Ordering;
|
2021-09-07 14:39:32 +00:00
|
|
|
use std::sync::Arc;
|
2022-04-03 04:17:30 +00:00
|
|
|
use tower_lsp::lsp_types as lsp;
|
2021-10-28 23:56:01 +00:00
|
|
|
|
2023-11-24 22:35:33 +00:00
|
|
|
pub const DOCUMENT_SCHEMES: [&str; 5] =
|
|
|
|
["data", "blob", "file", "http", "https"];
|
2023-09-29 19:44:59 +00:00
|
|
|
|
2022-12-20 20:19:35 +00:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
2022-03-23 13:54:22 +00:00
|
|
|
pub enum LanguageId {
|
2021-06-02 10:29:58 +00:00
|
|
|
JavaScript,
|
|
|
|
Jsx,
|
|
|
|
TypeScript,
|
|
|
|
Tsx,
|
|
|
|
Json,
|
|
|
|
JsonC,
|
|
|
|
Markdown,
|
2021-08-19 03:19:12 +00:00
|
|
|
Unknown,
|
2021-06-02 10:29:58 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 23:56:01 +00:00
|
|
|
impl LanguageId {
|
2022-12-20 20:19:35 +00:00
|
|
|
pub fn as_extension(&self) -> Option<&'static str> {
|
|
|
|
match self {
|
|
|
|
LanguageId::JavaScript => Some("js"),
|
|
|
|
LanguageId::Jsx => Some("jsx"),
|
|
|
|
LanguageId::TypeScript => Some("ts"),
|
|
|
|
LanguageId::Tsx => Some("tsx"),
|
|
|
|
LanguageId::Json => Some("json"),
|
|
|
|
LanguageId::JsonC => Some("jsonc"),
|
|
|
|
LanguageId::Markdown => Some("md"),
|
|
|
|
LanguageId::Unknown => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-15 21:50:52 +00:00
|
|
|
pub fn as_content_type(&self) -> Option<&'static str> {
|
2021-10-28 23:56:01 +00:00
|
|
|
match self {
|
2024-04-15 21:50:52 +00:00
|
|
|
LanguageId::JavaScript => Some("application/javascript"),
|
|
|
|
LanguageId::Jsx => Some("text/jsx"),
|
|
|
|
LanguageId::TypeScript => Some("application/typescript"),
|
|
|
|
LanguageId::Tsx => Some("text/tsx"),
|
|
|
|
LanguageId::Json | LanguageId::JsonC => Some("application/json"),
|
|
|
|
LanguageId::Markdown => Some("text/markdown"),
|
|
|
|
LanguageId::Unknown => None,
|
2021-10-28 23:56:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_diagnosable(&self) -> bool {
|
|
|
|
matches!(
|
|
|
|
self,
|
|
|
|
Self::JavaScript | Self::Jsx | Self::TypeScript | Self::Tsx
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-02 10:29:58 +00:00
|
|
|
impl FromStr for LanguageId {
|
|
|
|
type Err = AnyError;
|
|
|
|
|
2021-08-19 03:19:12 +00:00
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
2021-06-02 10:29:58 +00:00
|
|
|
match s {
|
|
|
|
"javascript" => Ok(Self::JavaScript),
|
2021-10-28 23:56:01 +00:00
|
|
|
"javascriptreact" | "jsx" => Ok(Self::Jsx),
|
2021-06-02 10:29:58 +00:00
|
|
|
"typescript" => Ok(Self::TypeScript),
|
2021-10-28 23:56:01 +00:00
|
|
|
"typescriptreact" | "tsx" => Ok(Self::Tsx),
|
2021-06-02 10:29:58 +00:00
|
|
|
"json" => Ok(Self::Json),
|
|
|
|
"jsonc" => Ok(Self::JsonC),
|
|
|
|
"markdown" => Ok(Self::Markdown),
|
2021-08-19 03:19:12 +00:00
|
|
|
_ => Ok(Self::Unknown),
|
2021-06-02 10:29:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-01-22 10:03:16 +00:00
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
|
|
enum IndexValid {
|
|
|
|
All,
|
|
|
|
UpTo(u32),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IndexValid {
|
|
|
|
fn covers(&self, line: u32) -> bool {
|
|
|
|
match *self {
|
|
|
|
IndexValid::UpTo(to) => to > line,
|
|
|
|
IndexValid::All => true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-12 16:42:04 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2022-03-02 21:06:38 +00:00
|
|
|
pub enum AssetOrDocument {
|
2024-04-06 14:36:43 +00:00
|
|
|
Document(Arc<Document>),
|
2021-11-12 16:42:04 +00:00
|
|
|
Asset(AssetDocument),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AssetOrDocument {
|
2022-03-02 21:06:38 +00:00
|
|
|
pub fn specifier(&self) -> &ModuleSpecifier {
|
|
|
|
match self {
|
|
|
|
AssetOrDocument::Asset(asset) => asset.specifier(),
|
|
|
|
AssetOrDocument::Document(doc) => doc.specifier(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-06 14:36:43 +00:00
|
|
|
pub fn document(&self) -> Option<&Arc<Document>> {
|
2021-11-12 16:42:04 +00:00
|
|
|
match self {
|
|
|
|
AssetOrDocument::Asset(_) => None,
|
|
|
|
AssetOrDocument::Document(doc) => Some(doc),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-20 20:40:55 +00:00
|
|
|
pub fn text(&self) -> Arc<str> {
|
2021-11-12 16:42:04 +00:00
|
|
|
match self {
|
|
|
|
AssetOrDocument::Asset(a) => a.text(),
|
2024-04-06 14:36:43 +00:00
|
|
|
AssetOrDocument::Document(d) => d.text_info.text(),
|
2021-11-12 16:42:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn line_index(&self) -> Arc<LineIndex> {
|
|
|
|
match self {
|
|
|
|
AssetOrDocument::Asset(a) => a.line_index(),
|
|
|
|
AssetOrDocument::Document(d) => d.line_index(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn maybe_navigation_tree(&self) -> Option<Arc<tsc::NavigationTree>> {
|
|
|
|
match self {
|
|
|
|
AssetOrDocument::Asset(a) => a.maybe_navigation_tree(),
|
|
|
|
AssetOrDocument::Document(d) => d.maybe_navigation_tree(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-15 18:41:37 +00:00
|
|
|
pub fn media_type(&self) -> MediaType {
|
|
|
|
match self {
|
|
|
|
AssetOrDocument::Asset(_) => MediaType::TypeScript, // assets are always TypeScript
|
|
|
|
AssetOrDocument::Document(d) => d.media_type(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-12 16:42:04 +00:00
|
|
|
pub fn get_maybe_dependency(
|
|
|
|
&self,
|
|
|
|
position: &lsp::Position,
|
|
|
|
) -> Option<(String, deno_graph::Dependency, deno_graph::Range)> {
|
|
|
|
self
|
|
|
|
.document()
|
2022-02-25 01:03:12 +00:00
|
|
|
.and_then(|d| d.get_maybe_dependency(position))
|
2021-11-12 16:42:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn maybe_parsed_source(
|
|
|
|
&self,
|
2024-02-09 01:40:26 +00:00
|
|
|
) -> Option<Result<deno_ast::ParsedSource, deno_ast::ParseDiagnostic>> {
|
2022-02-25 01:03:12 +00:00
|
|
|
self.document().and_then(|d| d.maybe_parsed_source())
|
2021-11-12 16:42:04 +00:00
|
|
|
}
|
|
|
|
|
2021-11-18 18:50:24 +00:00
|
|
|
pub fn document_lsp_version(&self) -> Option<i32> {
|
2022-02-25 01:03:12 +00:00
|
|
|
self.document().and_then(|d| d.maybe_lsp_version())
|
2021-11-12 16:42:04 +00:00
|
|
|
}
|
2022-03-02 21:06:38 +00:00
|
|
|
|
|
|
|
pub fn is_open(&self) -> bool {
|
|
|
|
self.document().map(|d| d.is_open()).unwrap_or(false)
|
|
|
|
}
|
2021-11-12 16:42:04 +00:00
|
|
|
}
|
|
|
|
|
2024-02-01 03:15:22 +00:00
|
|
|
type ModuleResult = Result<deno_graph::JsModule, deno_graph::ModuleGraphError>;
|
2024-02-09 01:40:26 +00:00
|
|
|
type ParsedSourceResult = Result<ParsedSource, deno_ast::ParseDiagnostic>;
|
2022-08-22 16:14:59 +00:00
|
|
|
|
2022-12-20 01:22:17 +00:00
|
|
|
#[derive(Debug)]
|
2024-04-06 14:36:43 +00:00
|
|
|
pub struct Document {
|
2022-12-20 01:22:17 +00:00
|
|
|
/// Contains the last-known-good set of dependencies from parsing the module.
|
2024-04-19 00:51:16 +00:00
|
|
|
dependencies: Arc<IndexMap<String, deno_graph::Dependency>>,
|
|
|
|
maybe_types_dependency: Option<Arc<deno_graph::TypesDependency>>,
|
2021-11-12 16:42:04 +00:00
|
|
|
fs_version: String,
|
2021-10-28 23:56:01 +00:00
|
|
|
line_index: Arc<LineIndex>,
|
2023-01-28 15:18:32 +00:00
|
|
|
maybe_headers: Option<HashMap<String, String>>,
|
2021-10-28 23:56:01 +00:00
|
|
|
maybe_language_id: Option<LanguageId>,
|
|
|
|
maybe_lsp_version: Option<i32>,
|
2022-12-20 01:22:17 +00:00
|
|
|
// this is a lazily constructed value based on the state of the document,
|
|
|
|
// so having a mutex to hold it is ok
|
|
|
|
maybe_navigation_tree: Mutex<Option<Arc<tsc::NavigationTree>>>,
|
2023-01-28 15:18:32 +00:00
|
|
|
maybe_parsed_source: Option<ParsedSourceResult>,
|
2024-04-15 21:50:52 +00:00
|
|
|
media_type: MediaType,
|
2021-03-25 00:13:37 +00:00
|
|
|
specifier: ModuleSpecifier,
|
2021-11-12 16:42:04 +00:00
|
|
|
text_info: SourceTextInfo,
|
2021-01-22 10:03:16 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 23:56:01 +00:00
|
|
|
impl Document {
|
|
|
|
fn new(
|
2021-06-02 10:29:58 +00:00
|
|
|
specifier: ModuleSpecifier,
|
2021-11-12 16:42:04 +00:00
|
|
|
fs_version: String,
|
2023-01-28 15:18:32 +00:00
|
|
|
maybe_headers: Option<HashMap<String, String>>,
|
|
|
|
text_info: SourceTextInfo,
|
2023-02-15 16:30:54 +00:00
|
|
|
resolver: &dyn deno_graph::source::Resolver,
|
2024-04-15 21:50:52 +00:00
|
|
|
maybe_node_resolver: Option<&CliNodeResolver>,
|
2023-10-20 04:02:08 +00:00
|
|
|
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
2024-04-06 14:36:43 +00:00
|
|
|
) -> Arc<Self> {
|
2024-04-12 14:33:43 +00:00
|
|
|
// we only ever do `Document::new` on disk resources that are supposed to
|
2021-10-28 23:56:01 +00:00
|
|
|
// be diagnosable, unlike `Document::open`, so it is safe to unconditionally
|
|
|
|
// parse the module.
|
2024-04-15 21:50:52 +00:00
|
|
|
let media_type = resolve_media_type(
|
|
|
|
&specifier,
|
|
|
|
maybe_headers.as_ref(),
|
|
|
|
None,
|
|
|
|
maybe_node_resolver,
|
|
|
|
);
|
2023-01-28 15:18:32 +00:00
|
|
|
let (maybe_parsed_source, maybe_module) = parse_and_analyze_module(
|
2021-10-28 23:56:01 +00:00
|
|
|
&specifier,
|
2023-01-28 15:18:32 +00:00
|
|
|
text_info.clone(),
|
|
|
|
maybe_headers.as_ref(),
|
2024-04-15 21:50:52 +00:00
|
|
|
media_type,
|
2023-02-15 16:30:54 +00:00
|
|
|
resolver,
|
2023-10-20 04:02:08 +00:00
|
|
|
npm_resolver,
|
2022-08-22 16:14:59 +00:00
|
|
|
);
|
2024-04-19 00:51:16 +00:00
|
|
|
let maybe_module = maybe_module.and_then(Result::ok);
|
|
|
|
let dependencies = maybe_module
|
|
|
|
.as_ref()
|
|
|
|
.map(|m| Arc::new(m.dependencies.clone()))
|
|
|
|
.unwrap_or_default();
|
|
|
|
let maybe_types_dependency = maybe_module
|
|
|
|
.as_ref()
|
|
|
|
.and_then(|m| Some(Arc::new(m.maybe_types_dependency.clone()?)));
|
2021-11-12 16:42:04 +00:00
|
|
|
let line_index = Arc::new(LineIndex::new(text_info.text_str()));
|
2024-04-06 14:36:43 +00:00
|
|
|
Arc::new(Document {
|
2021-11-16 22:23:25 +00:00
|
|
|
dependencies,
|
2024-04-19 00:51:16 +00:00
|
|
|
maybe_types_dependency,
|
2021-11-12 16:42:04 +00:00
|
|
|
fs_version,
|
2021-10-28 23:56:01 +00:00
|
|
|
line_index,
|
2023-01-28 15:18:32 +00:00
|
|
|
maybe_headers,
|
2021-10-28 23:56:01 +00:00
|
|
|
maybe_language_id: None,
|
|
|
|
maybe_lsp_version: None,
|
2022-12-20 01:22:17 +00:00
|
|
|
maybe_navigation_tree: Mutex::new(None),
|
2024-04-04 22:39:17 +00:00
|
|
|
maybe_parsed_source: maybe_parsed_source
|
|
|
|
.filter(|_| specifier.scheme() == "file"),
|
2024-04-15 21:50:52 +00:00
|
|
|
media_type,
|
2021-11-12 16:42:04 +00:00
|
|
|
text_info,
|
2021-03-25 00:13:37 +00:00
|
|
|
specifier,
|
2024-04-06 14:36:43 +00:00
|
|
|
})
|
2021-03-25 00:13:37 +00:00
|
|
|
}
|
|
|
|
|
2023-01-28 15:18:32 +00:00
|
|
|
fn maybe_with_new_resolver(
|
|
|
|
&self,
|
2023-02-15 16:30:54 +00:00
|
|
|
resolver: &dyn deno_graph::source::Resolver,
|
2024-04-15 21:50:52 +00:00
|
|
|
maybe_node_resolver: Option<&CliNodeResolver>,
|
2023-10-20 04:02:08 +00:00
|
|
|
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
2024-04-06 14:36:43 +00:00
|
|
|
) -> Option<Arc<Self>> {
|
2024-04-15 21:50:52 +00:00
|
|
|
let media_type = resolve_media_type(
|
|
|
|
&self.specifier,
|
|
|
|
self.maybe_headers.as_ref(),
|
|
|
|
self.maybe_language_id,
|
|
|
|
maybe_node_resolver,
|
|
|
|
);
|
2024-04-19 00:51:16 +00:00
|
|
|
let dependencies;
|
|
|
|
let maybe_types_dependency;
|
|
|
|
let maybe_parsed_source;
|
|
|
|
if media_type != self.media_type {
|
|
|
|
let parsed_source_result =
|
|
|
|
parse_source(&self.specifier, self.text_info.clone(), media_type);
|
|
|
|
let maybe_module = analyze_module(
|
|
|
|
&self.specifier,
|
|
|
|
&parsed_source_result,
|
|
|
|
self.maybe_headers.as_ref(),
|
|
|
|
resolver,
|
|
|
|
npm_resolver,
|
|
|
|
)
|
|
|
|
.ok();
|
|
|
|
dependencies = maybe_module
|
|
|
|
.as_ref()
|
|
|
|
.map(|m| Arc::new(m.dependencies.clone()))
|
|
|
|
.unwrap_or_default();
|
|
|
|
maybe_types_dependency = maybe_module
|
|
|
|
.as_ref()
|
|
|
|
.and_then(|m| Some(Arc::new(m.maybe_types_dependency.clone()?)));
|
|
|
|
maybe_parsed_source = Some(parsed_source_result);
|
|
|
|
} else {
|
|
|
|
dependencies = Arc::new(
|
|
|
|
self
|
|
|
|
.dependencies
|
|
|
|
.iter()
|
|
|
|
.map(|(s, d)| {
|
|
|
|
(
|
|
|
|
s.clone(),
|
|
|
|
d.with_new_resolver(s, Some(resolver), Some(npm_resolver)),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
);
|
|
|
|
maybe_types_dependency = self.maybe_types_dependency.as_ref().map(|d| {
|
|
|
|
Arc::new(d.with_new_resolver(Some(resolver), Some(npm_resolver)))
|
|
|
|
});
|
|
|
|
maybe_parsed_source = self.maybe_parsed_source.clone();
|
2024-04-15 21:50:52 +00:00
|
|
|
}
|
2024-04-06 14:36:43 +00:00
|
|
|
Some(Arc::new(Self {
|
2023-01-28 15:18:32 +00:00
|
|
|
// updated properties
|
|
|
|
dependencies,
|
2024-04-19 00:51:16 +00:00
|
|
|
maybe_types_dependency,
|
2023-01-28 15:18:32 +00:00
|
|
|
maybe_navigation_tree: Mutex::new(None),
|
2024-04-19 00:51:16 +00:00
|
|
|
maybe_parsed_source: maybe_parsed_source
|
|
|
|
.filter(|_| self.specifier.scheme() == "file"),
|
2023-01-28 15:18:32 +00:00
|
|
|
// maintain - this should all be copies/clones
|
2024-04-06 14:36:43 +00:00
|
|
|
fs_version: self.fs_version.clone(),
|
|
|
|
line_index: self.line_index.clone(),
|
|
|
|
maybe_headers: self.maybe_headers.clone(),
|
|
|
|
maybe_language_id: self.maybe_language_id,
|
|
|
|
maybe_lsp_version: self.maybe_lsp_version,
|
2024-04-15 21:50:52 +00:00
|
|
|
media_type,
|
2024-04-06 14:36:43 +00:00
|
|
|
text_info: self.text_info.clone(),
|
|
|
|
specifier: self.specifier.clone(),
|
|
|
|
}))
|
2023-01-28 15:18:32 +00:00
|
|
|
}
|
|
|
|
|
2024-04-15 21:50:52 +00:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2021-11-12 16:42:04 +00:00
|
|
|
fn open(
|
|
|
|
specifier: ModuleSpecifier,
|
|
|
|
version: i32,
|
|
|
|
language_id: LanguageId,
|
2024-04-15 21:50:52 +00:00
|
|
|
maybe_headers: Option<HashMap<String, String>>,
|
2022-05-20 20:40:55 +00:00
|
|
|
content: Arc<str>,
|
2023-10-12 14:37:56 +00:00
|
|
|
cache: &Arc<dyn HttpCache>,
|
2023-02-15 16:30:54 +00:00
|
|
|
resolver: &dyn deno_graph::source::Resolver,
|
2024-04-15 21:50:52 +00:00
|
|
|
maybe_node_resolver: Option<&CliNodeResolver>,
|
2023-10-20 04:02:08 +00:00
|
|
|
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
2024-04-06 14:36:43 +00:00
|
|
|
) -> Arc<Self> {
|
2023-01-28 15:18:32 +00:00
|
|
|
let text_info = SourceTextInfo::new(content);
|
2024-04-15 21:50:52 +00:00
|
|
|
let media_type = resolve_media_type(
|
|
|
|
&specifier,
|
|
|
|
None,
|
|
|
|
Some(language_id),
|
|
|
|
maybe_node_resolver,
|
|
|
|
);
|
2023-01-28 15:18:32 +00:00
|
|
|
let (maybe_parsed_source, maybe_module) = if language_id.is_diagnosable() {
|
|
|
|
parse_and_analyze_module(
|
2021-11-12 16:42:04 +00:00
|
|
|
&specifier,
|
2023-01-28 15:18:32 +00:00
|
|
|
text_info.clone(),
|
2024-04-15 21:50:52 +00:00
|
|
|
maybe_headers.as_ref(),
|
|
|
|
media_type,
|
2023-02-15 16:30:54 +00:00
|
|
|
resolver,
|
2023-10-20 04:02:08 +00:00
|
|
|
npm_resolver,
|
2022-08-22 16:14:59 +00:00
|
|
|
)
|
2021-11-12 16:42:04 +00:00
|
|
|
} else {
|
2022-08-22 16:14:59 +00:00
|
|
|
(None, None)
|
2021-11-12 16:42:04 +00:00
|
|
|
};
|
2024-04-19 00:51:16 +00:00
|
|
|
let maybe_module = maybe_module.and_then(Result::ok);
|
|
|
|
let dependencies = maybe_module
|
|
|
|
.as_ref()
|
|
|
|
.map(|m| Arc::new(m.dependencies.clone()))
|
|
|
|
.unwrap_or_default();
|
|
|
|
let maybe_types_dependency = maybe_module
|
|
|
|
.as_ref()
|
|
|
|
.and_then(|m| Some(Arc::new(m.maybe_types_dependency.clone()?)));
|
2023-01-28 15:18:32 +00:00
|
|
|
let line_index = Arc::new(LineIndex::new(text_info.text_str()));
|
2024-04-06 14:36:43 +00:00
|
|
|
Arc::new(Self {
|
2021-11-16 22:23:25 +00:00
|
|
|
dependencies,
|
2024-04-19 00:51:16 +00:00
|
|
|
maybe_types_dependency,
|
2023-10-12 14:37:56 +00:00
|
|
|
fs_version: calculate_fs_version(cache, &specifier)
|
|
|
|
.unwrap_or_else(|| "1".to_string()),
|
2021-11-12 16:42:04 +00:00
|
|
|
line_index,
|
|
|
|
maybe_language_id: Some(language_id),
|
|
|
|
maybe_lsp_version: Some(version),
|
2024-04-15 21:50:52 +00:00
|
|
|
maybe_headers,
|
2022-12-20 01:22:17 +00:00
|
|
|
maybe_navigation_tree: Mutex::new(None),
|
2024-04-04 22:39:17 +00:00
|
|
|
maybe_parsed_source: maybe_parsed_source
|
|
|
|
.filter(|_| specifier.scheme() == "file"),
|
2024-04-15 21:50:52 +00:00
|
|
|
media_type,
|
2023-01-28 15:18:32 +00:00
|
|
|
text_info,
|
2021-11-12 16:42:04 +00:00
|
|
|
specifier,
|
2024-04-06 14:36:43 +00:00
|
|
|
})
|
2021-11-12 16:42:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn with_change(
|
|
|
|
&self,
|
2021-10-28 23:56:01 +00:00
|
|
|
version: i32,
|
|
|
|
changes: Vec<lsp::TextDocumentContentChangeEvent>,
|
2023-02-15 16:30:54 +00:00
|
|
|
resolver: &dyn deno_graph::source::Resolver,
|
2023-10-20 04:02:08 +00:00
|
|
|
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
2024-04-06 14:36:43 +00:00
|
|
|
) -> Result<Arc<Self>, AnyError> {
|
|
|
|
let mut content = self.text_info.text_str().to_string();
|
|
|
|
let mut line_index = self.line_index.clone();
|
2021-01-22 10:03:16 +00:00
|
|
|
let mut index_valid = IndexValid::All;
|
2021-10-28 23:56:01 +00:00
|
|
|
for change in changes {
|
2021-01-22 10:03:16 +00:00
|
|
|
if let Some(range) = change.range {
|
|
|
|
if !index_valid.covers(range.start.line) {
|
2021-10-28 23:56:01 +00:00
|
|
|
line_index = Arc::new(LineIndex::new(&content));
|
2021-01-22 10:03:16 +00:00
|
|
|
}
|
|
|
|
index_valid = IndexValid::UpTo(range.start.line);
|
|
|
|
let range = line_index.get_text_range(range)?;
|
|
|
|
content.replace_range(Range::<usize>::from(range), &change.text);
|
|
|
|
} else {
|
2021-09-07 14:39:32 +00:00
|
|
|
content = change.text;
|
2021-01-22 10:03:16 +00:00
|
|
|
index_valid = IndexValid::UpTo(0);
|
|
|
|
}
|
|
|
|
}
|
2023-01-28 15:18:32 +00:00
|
|
|
let text_info = SourceTextInfo::from_string(content);
|
2024-04-15 21:50:52 +00:00
|
|
|
let media_type = self.media_type;
|
2023-01-28 15:18:32 +00:00
|
|
|
let (maybe_parsed_source, maybe_module) = if self
|
2021-10-28 23:56:01 +00:00
|
|
|
.maybe_language_id
|
|
|
|
.as_ref()
|
|
|
|
.map(|li| li.is_diagnosable())
|
|
|
|
.unwrap_or(false)
|
|
|
|
{
|
2023-01-28 15:18:32 +00:00
|
|
|
parse_and_analyze_module(
|
2024-04-06 14:36:43 +00:00
|
|
|
&self.specifier,
|
2023-01-28 15:18:32 +00:00
|
|
|
text_info.clone(),
|
2024-04-15 21:50:52 +00:00
|
|
|
self.maybe_headers.as_ref(),
|
|
|
|
media_type,
|
2023-02-15 16:30:54 +00:00
|
|
|
resolver,
|
2023-10-20 04:02:08 +00:00
|
|
|
npm_resolver,
|
2022-08-22 16:14:59 +00:00
|
|
|
)
|
2021-10-28 23:56:01 +00:00
|
|
|
} else {
|
2022-08-22 16:14:59 +00:00
|
|
|
(None, None)
|
2021-11-12 16:42:04 +00:00
|
|
|
};
|
2024-04-19 00:51:16 +00:00
|
|
|
let maybe_module = maybe_module.and_then(Result::ok);
|
|
|
|
let dependencies = maybe_module
|
|
|
|
.as_ref()
|
|
|
|
.map(|m| Arc::new(m.dependencies.clone()))
|
|
|
|
.unwrap_or_else(|| self.dependencies.clone());
|
|
|
|
let maybe_types_dependency = maybe_module
|
|
|
|
.as_ref()
|
|
|
|
.and_then(|m| Some(Arc::new(m.maybe_types_dependency.clone()?)))
|
|
|
|
.or_else(|| self.maybe_types_dependency.clone());
|
2021-11-12 16:42:04 +00:00
|
|
|
let line_index = if index_valid == IndexValid::All {
|
2021-09-07 14:39:32 +00:00
|
|
|
line_index
|
2021-01-22 10:03:16 +00:00
|
|
|
} else {
|
2021-11-16 22:23:25 +00:00
|
|
|
Arc::new(LineIndex::new(text_info.text_str()))
|
2021-01-22 10:03:16 +00:00
|
|
|
};
|
2024-04-06 14:36:43 +00:00
|
|
|
Ok(Arc::new(Self {
|
|
|
|
specifier: self.specifier.clone(),
|
|
|
|
fs_version: self.fs_version.clone(),
|
|
|
|
maybe_language_id: self.maybe_language_id,
|
2021-11-16 22:23:25 +00:00
|
|
|
dependencies,
|
2024-04-19 00:51:16 +00:00
|
|
|
maybe_types_dependency,
|
2021-11-16 22:23:25 +00:00
|
|
|
text_info,
|
2021-11-12 16:42:04 +00:00
|
|
|
line_index,
|
2024-04-06 14:36:43 +00:00
|
|
|
maybe_headers: self.maybe_headers.clone(),
|
2024-04-04 22:39:17 +00:00
|
|
|
maybe_parsed_source: maybe_parsed_source
|
2024-04-06 14:36:43 +00:00
|
|
|
.filter(|_| self.specifier.scheme() == "file"),
|
2021-11-12 16:42:04 +00:00
|
|
|
maybe_lsp_version: Some(version),
|
2022-12-20 01:22:17 +00:00
|
|
|
maybe_navigation_tree: Mutex::new(None),
|
2024-04-15 21:50:52 +00:00
|
|
|
media_type,
|
2024-04-06 14:36:43 +00:00
|
|
|
}))
|
2021-11-12 16:42:04 +00:00
|
|
|
}
|
|
|
|
|
2024-04-06 14:36:43 +00:00
|
|
|
pub fn saved(&self, cache: &Arc<dyn HttpCache>) -> Arc<Self> {
|
|
|
|
Arc::new(Self {
|
|
|
|
specifier: self.specifier.clone(),
|
|
|
|
fs_version: calculate_fs_version(cache, &self.specifier)
|
2023-10-12 14:37:56 +00:00
|
|
|
.unwrap_or_else(|| "1".to_string()),
|
2024-04-06 14:36:43 +00:00
|
|
|
maybe_language_id: self.maybe_language_id,
|
|
|
|
dependencies: self.dependencies.clone(),
|
2024-04-19 00:51:16 +00:00
|
|
|
maybe_types_dependency: self.maybe_types_dependency.clone(),
|
2024-04-06 14:36:43 +00:00
|
|
|
text_info: self.text_info.clone(),
|
|
|
|
line_index: self.line_index.clone(),
|
|
|
|
maybe_headers: self.maybe_headers.clone(),
|
|
|
|
maybe_parsed_source: self.maybe_parsed_source.clone(),
|
|
|
|
maybe_lsp_version: self.maybe_lsp_version,
|
2023-10-12 14:37:56 +00:00
|
|
|
maybe_navigation_tree: Mutex::new(None),
|
2024-04-15 21:50:52 +00:00
|
|
|
media_type: self.media_type,
|
2024-04-06 14:36:43 +00:00
|
|
|
})
|
2023-10-12 14:37:56 +00:00
|
|
|
}
|
|
|
|
|
2021-11-12 16:42:04 +00:00
|
|
|
pub fn specifier(&self) -> &ModuleSpecifier {
|
2024-04-06 14:36:43 +00:00
|
|
|
&self.specifier
|
2021-06-21 06:43:35 +00:00
|
|
|
}
|
2021-06-24 23:06:51 +00:00
|
|
|
|
2022-05-20 20:40:55 +00:00
|
|
|
pub fn content(&self) -> Arc<str> {
|
2024-04-06 14:36:43 +00:00
|
|
|
self.text_info.text()
|
2021-10-28 23:56:01 +00:00
|
|
|
}
|
|
|
|
|
2021-11-12 16:42:04 +00:00
|
|
|
pub fn text_info(&self) -> SourceTextInfo {
|
2024-04-06 14:36:43 +00:00
|
|
|
self.text_info.clone()
|
2021-11-12 16:42:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn line_index(&self) -> Arc<LineIndex> {
|
2024-04-06 14:36:43 +00:00
|
|
|
self.line_index.clone()
|
2021-11-12 16:42:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn fs_version(&self) -> &str {
|
2024-04-06 14:36:43 +00:00
|
|
|
self.fs_version.as_str()
|
2021-11-12 16:42:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn script_version(&self) -> String {
|
|
|
|
self
|
|
|
|
.maybe_lsp_version()
|
2023-10-17 04:13:06 +00:00
|
|
|
.map(|v| format!("{}+{v}", self.fs_version()))
|
2023-03-15 21:46:36 +00:00
|
|
|
.unwrap_or_else(|| self.fs_version().to_string())
|
2021-11-12 16:42:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_diagnosable(&self) -> bool {
|
2021-10-28 23:56:01 +00:00
|
|
|
matches!(
|
|
|
|
self.media_type(),
|
|
|
|
MediaType::JavaScript
|
|
|
|
| MediaType::Jsx
|
2021-12-09 22:12:21 +00:00
|
|
|
| MediaType::Mjs
|
|
|
|
| MediaType::Cjs
|
2021-10-28 23:56:01 +00:00
|
|
|
| MediaType::TypeScript
|
|
|
|
| MediaType::Tsx
|
2021-12-09 22:12:21 +00:00
|
|
|
| MediaType::Mts
|
|
|
|
| MediaType::Cts
|
2021-10-28 23:56:01 +00:00
|
|
|
| MediaType::Dts
|
2021-12-09 22:12:21 +00:00
|
|
|
| MediaType::Dmts
|
|
|
|
| MediaType::Dcts
|
2021-10-28 23:56:01 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-11-12 16:42:04 +00:00
|
|
|
pub fn is_open(&self) -> bool {
|
2024-04-06 14:36:43 +00:00
|
|
|
self.maybe_lsp_version.is_some()
|
2021-10-28 23:56:01 +00:00
|
|
|
}
|
|
|
|
|
2024-04-19 00:51:16 +00:00
|
|
|
pub fn maybe_types_dependency(&self) -> &Resolution {
|
|
|
|
if let Some(types_dep) = self.maybe_types_dependency.as_deref() {
|
|
|
|
&types_dep.dependency
|
2022-01-31 22:33:57 +00:00
|
|
|
} else {
|
2024-04-19 00:51:16 +00:00
|
|
|
&Resolution::None
|
2022-01-31 22:33:57 +00:00
|
|
|
}
|
2021-11-08 00:50:48 +00:00
|
|
|
}
|
|
|
|
|
2021-11-12 16:42:04 +00:00
|
|
|
pub fn media_type(&self) -> MediaType {
|
2024-04-15 21:50:52 +00:00
|
|
|
self.media_type
|
2022-12-20 20:19:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn maybe_language_id(&self) -> Option<LanguageId> {
|
2024-04-06 14:36:43 +00:00
|
|
|
self.maybe_language_id
|
2021-10-28 23:56:01 +00:00
|
|
|
}
|
|
|
|
|
2021-11-12 16:42:04 +00:00
|
|
|
/// Returns the current language server client version if any.
|
|
|
|
pub fn maybe_lsp_version(&self) -> Option<i32> {
|
2024-04-06 14:36:43 +00:00
|
|
|
self.maybe_lsp_version
|
2021-11-12 16:42:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn maybe_parsed_source(
|
|
|
|
&self,
|
2024-02-09 01:40:26 +00:00
|
|
|
) -> Option<Result<deno_ast::ParsedSource, deno_ast::ParseDiagnostic>> {
|
2024-04-06 14:36:43 +00:00
|
|
|
self.maybe_parsed_source.clone()
|
2021-11-12 16:42:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn maybe_navigation_tree(&self) -> Option<Arc<tsc::NavigationTree>> {
|
2024-04-06 14:36:43 +00:00
|
|
|
self.maybe_navigation_tree.lock().clone()
|
2022-12-20 01:22:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn update_navigation_tree_if_version(
|
|
|
|
&self,
|
|
|
|
tree: Arc<tsc::NavigationTree>,
|
|
|
|
script_version: &str,
|
|
|
|
) {
|
|
|
|
// Ensure we are updating the same document that the navigation tree was
|
|
|
|
// created for. Note: this should not be racy between the version check
|
|
|
|
// and setting the navigation tree, because the document is immutable
|
|
|
|
// and this is enforced by it being wrapped in an Arc.
|
|
|
|
if self.script_version() == script_version {
|
2024-04-06 14:36:43 +00:00
|
|
|
*self.maybe_navigation_tree.lock() = Some(tree);
|
2022-12-20 01:22:17 +00:00
|
|
|
}
|
2021-11-12 16:42:04 +00:00
|
|
|
}
|
|
|
|
|
2023-02-22 19:15:25 +00:00
|
|
|
pub fn dependencies(&self) -> &IndexMap<String, deno_graph::Dependency> {
|
2024-04-19 00:51:16 +00:00
|
|
|
self.dependencies.as_ref()
|
2021-11-12 16:42:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// If the supplied position is within a dependency range, return the resolved
|
|
|
|
/// string specifier for the dependency, the resolved dependency and the range
|
|
|
|
/// in the source document of the specifier.
|
|
|
|
pub fn get_maybe_dependency(
|
|
|
|
&self,
|
|
|
|
position: &lsp::Position,
|
|
|
|
) -> Option<(String, deno_graph::Dependency, deno_graph::Range)> {
|
|
|
|
let position = deno_graph::Position {
|
|
|
|
line: position.line as usize,
|
|
|
|
character: position.character as usize,
|
2021-10-28 23:56:01 +00:00
|
|
|
};
|
2024-04-19 00:51:16 +00:00
|
|
|
self.dependencies().iter().find_map(|(s, dep)| {
|
2021-11-12 16:42:04 +00:00
|
|
|
dep
|
|
|
|
.includes(&position)
|
|
|
|
.map(|r| (s.clone(), dep.clone(), r.clone()))
|
|
|
|
})
|
2021-06-24 23:06:51 +00:00
|
|
|
}
|
2021-01-22 10:03:16 +00:00
|
|
|
}
|
|
|
|
|
2024-04-15 21:50:52 +00:00
|
|
|
fn resolve_media_type(
|
|
|
|
specifier: &ModuleSpecifier,
|
|
|
|
maybe_headers: Option<&HashMap<String, String>>,
|
|
|
|
maybe_language_id: Option<LanguageId>,
|
|
|
|
maybe_node_resolver: Option<&CliNodeResolver>,
|
|
|
|
) -> MediaType {
|
|
|
|
if let Some(node_resolver) = maybe_node_resolver {
|
|
|
|
if node_resolver.in_npm_package(specifier) {
|
|
|
|
match node_resolver.url_to_node_resolution(specifier.clone()) {
|
|
|
|
Ok(resolution) => {
|
|
|
|
let (_, media_type) =
|
|
|
|
NodeResolution::into_specifier_and_media_type(Some(resolution));
|
|
|
|
return media_type;
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
lsp_warn!("Node resolution failed for '{}': {}", specifier, err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if maybe_headers.is_some() {
|
|
|
|
return MediaType::from_specifier_and_headers(specifier, maybe_headers);
|
|
|
|
}
|
|
|
|
|
|
|
|
// LanguageId is a subset of MediaType, so get its content type and
|
|
|
|
// also use the specifier to inform its media type
|
|
|
|
MediaType::from_specifier_and_content_type(
|
|
|
|
specifier,
|
|
|
|
maybe_language_id.and_then(|id| id.as_content_type()),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-03-23 13:54:22 +00:00
|
|
|
pub fn to_lsp_range(range: &deno_graph::Range) -> lsp::Range {
|
2021-10-28 23:56:01 +00:00
|
|
|
lsp::Range {
|
|
|
|
start: lsp::Position {
|
|
|
|
line: range.start.line as u32,
|
|
|
|
character: range.start.character as u32,
|
|
|
|
},
|
|
|
|
end: lsp::Position {
|
|
|
|
line: range.end.line as u32,
|
|
|
|
character: range.end.character as u32,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-08 20:06:45 +00:00
|
|
|
#[derive(Debug)]
|
2023-12-07 00:03:18 +00:00
|
|
|
struct RedirectResolver {
|
2023-08-02 00:49:09 +00:00
|
|
|
cache: Arc<dyn HttpCache>,
|
2021-11-18 18:50:24 +00:00
|
|
|
redirects: Mutex<HashMap<ModuleSpecifier, ModuleSpecifier>>,
|
2021-10-28 23:56:01 +00:00
|
|
|
}
|
|
|
|
|
2023-12-07 00:03:18 +00:00
|
|
|
impl RedirectResolver {
|
2023-08-02 00:49:09 +00:00
|
|
|
pub fn new(cache: Arc<dyn HttpCache>) -> Self {
|
2021-10-28 23:56:01 +00:00
|
|
|
Self {
|
2023-06-10 15:09:45 +00:00
|
|
|
cache,
|
2021-11-18 18:50:24 +00:00
|
|
|
redirects: Mutex::new(HashMap::new()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn resolve(
|
|
|
|
&self,
|
|
|
|
specifier: &ModuleSpecifier,
|
|
|
|
) -> Option<ModuleSpecifier> {
|
|
|
|
let scheme = specifier.scheme();
|
2023-09-29 19:44:59 +00:00
|
|
|
if !DOCUMENT_SCHEMES.contains(&scheme) {
|
2021-11-18 18:50:24 +00:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2023-09-29 19:44:59 +00:00
|
|
|
if scheme == "http" || scheme == "https" {
|
2021-11-18 18:50:24 +00:00
|
|
|
let mut redirects = self.redirects.lock();
|
|
|
|
if let Some(specifier) = redirects.get(specifier) {
|
|
|
|
Some(specifier.clone())
|
|
|
|
} else {
|
|
|
|
let redirect = self.resolve_remote(specifier, 10)?;
|
|
|
|
redirects.insert(specifier.clone(), redirect.clone());
|
|
|
|
Some(redirect)
|
|
|
|
}
|
2023-09-29 19:44:59 +00:00
|
|
|
} else {
|
|
|
|
Some(specifier.clone())
|
2021-11-18 18:50:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn resolve_remote(
|
|
|
|
&self,
|
|
|
|
specifier: &ModuleSpecifier,
|
|
|
|
redirect_limit: usize,
|
|
|
|
) -> Option<ModuleSpecifier> {
|
2023-07-08 20:06:45 +00:00
|
|
|
if redirect_limit > 0 {
|
2023-08-02 00:49:09 +00:00
|
|
|
let cache_key = self.cache.cache_item_key(specifier).ok()?;
|
2024-02-15 19:49:35 +00:00
|
|
|
let headers = self.cache.read_headers(&cache_key).ok().flatten()?;
|
2021-11-18 18:50:24 +00:00
|
|
|
if let Some(location) = headers.get("location") {
|
|
|
|
let redirect =
|
|
|
|
deno_core::resolve_import(location, specifier.as_str()).ok()?;
|
|
|
|
self.resolve_remote(&redirect, redirect_limit - 1)
|
|
|
|
} else {
|
|
|
|
Some(specifier.clone())
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
2021-10-28 23:56:01 +00:00
|
|
|
}
|
|
|
|
}
|
2021-11-18 18:50:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Default)]
|
|
|
|
struct FileSystemDocuments {
|
2024-04-09 22:12:55 +00:00
|
|
|
docs: DashMap<ModuleSpecifier, Arc<Document>>,
|
|
|
|
dirty: AtomicBool,
|
2021-11-18 18:50:24 +00:00
|
|
|
}
|
2021-10-28 23:56:01 +00:00
|
|
|
|
2021-11-18 18:50:24 +00:00
|
|
|
impl FileSystemDocuments {
|
2022-11-30 19:19:32 +00:00
|
|
|
pub fn get(
|
2024-04-09 22:12:55 +00:00
|
|
|
&self,
|
2023-08-02 00:49:09 +00:00
|
|
|
cache: &Arc<dyn HttpCache>,
|
2023-02-15 16:30:54 +00:00
|
|
|
resolver: &dyn deno_graph::source::Resolver,
|
2022-11-30 00:32:18 +00:00
|
|
|
specifier: &ModuleSpecifier,
|
2024-04-15 21:50:52 +00:00
|
|
|
maybe_node_resolver: Option<&CliNodeResolver>,
|
2023-10-20 04:02:08 +00:00
|
|
|
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
2024-04-06 14:36:43 +00:00
|
|
|
) -> Option<Arc<Document>> {
|
2023-07-01 22:52:30 +00:00
|
|
|
let fs_version = if specifier.scheme() == "data" {
|
|
|
|
Some("1".to_string())
|
|
|
|
} else {
|
2023-07-08 20:06:45 +00:00
|
|
|
calculate_fs_version(cache, specifier)
|
2023-07-01 22:52:30 +00:00
|
|
|
};
|
2024-04-09 22:12:55 +00:00
|
|
|
let file_system_doc = self.docs.get(specifier).map(|v| v.value().clone());
|
|
|
|
if file_system_doc.as_ref().map(|d| d.fs_version().to_string())
|
|
|
|
!= fs_version
|
|
|
|
{
|
2022-11-30 00:32:18 +00:00
|
|
|
// attempt to update the file on the file system
|
2024-04-15 21:50:52 +00:00
|
|
|
self.refresh_document(
|
|
|
|
cache,
|
|
|
|
resolver,
|
|
|
|
specifier,
|
|
|
|
maybe_node_resolver,
|
|
|
|
npm_resolver,
|
|
|
|
)
|
2022-11-30 00:32:18 +00:00
|
|
|
} else {
|
2024-04-09 22:12:55 +00:00
|
|
|
file_system_doc
|
2022-11-30 00:32:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-25 14:21:59 +00:00
|
|
|
/// Adds or updates a document by reading the document from the file system
|
|
|
|
/// returning the document.
|
2021-11-18 18:50:24 +00:00
|
|
|
fn refresh_document(
|
2024-04-09 22:12:55 +00:00
|
|
|
&self,
|
2023-08-02 00:49:09 +00:00
|
|
|
cache: &Arc<dyn HttpCache>,
|
2023-02-15 16:30:54 +00:00
|
|
|
resolver: &dyn deno_graph::source::Resolver,
|
2022-01-24 08:01:33 +00:00
|
|
|
specifier: &ModuleSpecifier,
|
2024-04-15 21:50:52 +00:00
|
|
|
maybe_node_resolver: Option<&CliNodeResolver>,
|
2023-10-20 04:02:08 +00:00
|
|
|
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
2024-04-06 14:36:43 +00:00
|
|
|
) -> Option<Arc<Document>> {
|
2021-10-28 23:56:01 +00:00
|
|
|
let doc = if specifier.scheme() == "file" {
|
2023-07-08 20:06:45 +00:00
|
|
|
let path = specifier_to_file_path(specifier).ok()?;
|
|
|
|
let fs_version = calculate_fs_version_at_path(&path)?;
|
2023-07-01 22:52:30 +00:00
|
|
|
let bytes = fs::read(path).ok()?;
|
2024-02-01 03:15:22 +00:00
|
|
|
let content =
|
|
|
|
deno_graph::source::decode_owned_source(specifier, bytes, None).ok()?;
|
2021-10-28 23:56:01 +00:00
|
|
|
Document::new(
|
|
|
|
specifier.clone(),
|
2021-11-12 16:42:04 +00:00
|
|
|
fs_version,
|
2021-10-28 23:56:01 +00:00
|
|
|
None,
|
2023-01-28 15:18:32 +00:00
|
|
|
SourceTextInfo::from_string(content),
|
2023-02-15 16:30:54 +00:00
|
|
|
resolver,
|
2024-04-15 21:50:52 +00:00
|
|
|
maybe_node_resolver,
|
2023-10-20 04:02:08 +00:00
|
|
|
npm_resolver,
|
2021-10-28 23:56:01 +00:00
|
|
|
)
|
2023-07-01 22:52:30 +00:00
|
|
|
} else if specifier.scheme() == "data" {
|
2024-02-01 03:15:22 +00:00
|
|
|
let source = deno_graph::source::RawDataUrl::parse(specifier)
|
|
|
|
.ok()?
|
|
|
|
.decode()
|
|
|
|
.ok()?;
|
2023-07-01 22:52:30 +00:00
|
|
|
Document::new(
|
|
|
|
specifier.clone(),
|
|
|
|
"1".to_string(),
|
|
|
|
None,
|
|
|
|
SourceTextInfo::from_string(source),
|
|
|
|
resolver,
|
2024-04-15 21:50:52 +00:00
|
|
|
maybe_node_resolver,
|
2023-10-20 04:02:08 +00:00
|
|
|
npm_resolver,
|
2023-07-01 22:52:30 +00:00
|
|
|
)
|
2021-10-28 23:56:01 +00:00
|
|
|
} else {
|
2023-07-08 20:06:45 +00:00
|
|
|
let fs_version = calculate_fs_version(cache, specifier)?;
|
2023-08-02 00:49:09 +00:00
|
|
|
let cache_key = cache.cache_item_key(specifier).ok()?;
|
2024-02-15 19:49:35 +00:00
|
|
|
let bytes = cache
|
|
|
|
.read_file_bytes(&cache_key, None, LSP_DISALLOW_GLOBAL_TO_LOCAL_COPY)
|
|
|
|
.ok()??;
|
|
|
|
let specifier_headers = cache.read_headers(&cache_key).ok()??;
|
2024-02-01 03:15:22 +00:00
|
|
|
let (_, maybe_charset) =
|
|
|
|
deno_graph::source::resolve_media_type_and_charset_from_headers(
|
|
|
|
specifier,
|
2024-02-15 19:49:35 +00:00
|
|
|
Some(&specifier_headers),
|
2024-02-01 03:15:22 +00:00
|
|
|
);
|
|
|
|
let content = deno_graph::source::decode_owned_source(
|
|
|
|
specifier,
|
|
|
|
bytes,
|
|
|
|
maybe_charset,
|
|
|
|
)
|
|
|
|
.ok()?;
|
2024-02-15 19:49:35 +00:00
|
|
|
let maybe_headers = Some(specifier_headers);
|
2021-10-28 23:56:01 +00:00
|
|
|
Document::new(
|
|
|
|
specifier.clone(),
|
2021-11-12 16:42:04 +00:00
|
|
|
fs_version,
|
2021-10-28 23:56:01 +00:00
|
|
|
maybe_headers,
|
2023-01-28 15:18:32 +00:00
|
|
|
SourceTextInfo::from_string(content),
|
2023-02-15 16:30:54 +00:00
|
|
|
resolver,
|
2024-04-15 21:50:52 +00:00
|
|
|
maybe_node_resolver,
|
2023-10-20 04:02:08 +00:00
|
|
|
npm_resolver,
|
2021-10-28 23:56:01 +00:00
|
|
|
)
|
|
|
|
};
|
2022-01-25 14:21:59 +00:00
|
|
|
self.docs.insert(specifier.clone(), doc.clone());
|
2024-04-09 22:12:55 +00:00
|
|
|
self.set_dirty(true);
|
2022-01-25 14:21:59 +00:00
|
|
|
Some(doc)
|
2021-10-28 23:56:01 +00:00
|
|
|
}
|
2024-04-09 22:12:55 +00:00
|
|
|
|
|
|
|
pub fn remove_document(
|
|
|
|
&self,
|
|
|
|
specifier: &ModuleSpecifier,
|
|
|
|
) -> Option<Arc<Document>> {
|
|
|
|
Some(self.docs.remove(specifier)?.1)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the dirty flag to the provided value and returns the previous value.
|
|
|
|
pub fn set_dirty(&self, dirty: bool) -> bool {
|
|
|
|
self.dirty.swap(dirty, Ordering::Relaxed)
|
|
|
|
}
|
2021-11-18 18:50:24 +00:00
|
|
|
}
|
2021-10-28 23:56:01 +00:00
|
|
|
|
2023-03-29 20:25:48 +00:00
|
|
|
/// Specify the documents to include on a `documents.documents(...)` call.
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
pub enum DocumentsFilter {
|
|
|
|
/// Includes all the documents (diagnosable & non-diagnosable, open & file system).
|
|
|
|
All,
|
|
|
|
/// Includes all the diagnosable documents (open & file system).
|
|
|
|
AllDiagnosable,
|
|
|
|
/// Includes only the diagnosable documents that are open.
|
|
|
|
OpenDiagnosable,
|
|
|
|
}
|
|
|
|
|
2023-07-08 20:06:45 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2022-03-23 13:54:22 +00:00
|
|
|
pub struct Documents {
|
2021-11-18 18:50:24 +00:00
|
|
|
/// The DENO_DIR that the documents looks for non-file based modules.
|
2023-08-02 00:49:09 +00:00
|
|
|
cache: Arc<dyn HttpCache>,
|
2021-11-18 18:50:24 +00:00
|
|
|
/// A flag that indicates that stated data is potentially invalid and needs to
|
|
|
|
/// be recalculated before being considered valid.
|
|
|
|
dirty: bool,
|
|
|
|
/// A map of documents that are "open" in the language server.
|
2024-04-06 14:36:43 +00:00
|
|
|
open_docs: HashMap<ModuleSpecifier, Arc<Document>>,
|
2021-11-18 18:50:24 +00:00
|
|
|
/// Documents stored on the file system.
|
2024-04-09 22:12:55 +00:00
|
|
|
file_system_docs: Arc<FileSystemDocuments>,
|
2021-11-18 18:50:24 +00:00
|
|
|
/// Any imports to the context supplied by configuration files. This is like
|
|
|
|
/// the imports into the a module graph in CLI.
|
2023-03-11 16:43:45 +00:00
|
|
|
imports: Arc<IndexMap<ModuleSpecifier, GraphImport>>,
|
2024-04-15 21:50:52 +00:00
|
|
|
/// Resolver for node_modules.
|
|
|
|
maybe_node_resolver: Option<Arc<CliNodeResolver>>,
|
2022-11-02 14:47:02 +00:00
|
|
|
/// A resolver that takes into account currently loaded import map and JSX
|
|
|
|
/// settings.
|
2023-04-14 20:22:33 +00:00
|
|
|
resolver: Arc<CliGraphResolver>,
|
2024-03-01 21:34:13 +00:00
|
|
|
jsr_resolver: Arc<JsrCacheResolver>,
|
2024-04-05 22:33:01 +00:00
|
|
|
lockfile: Option<Arc<Mutex<Lockfile>>>,
|
2023-02-23 15:58:10 +00:00
|
|
|
/// The npm package requirements found in npm specifiers.
|
2023-08-21 09:53:52 +00:00
|
|
|
npm_specifier_reqs: Arc<Vec<PackageReq>>,
|
2023-01-24 14:05:54 +00:00
|
|
|
/// Gets if any document had a node: specifier such that a @types/node package
|
|
|
|
/// should be injected.
|
|
|
|
has_injected_types_node_package: bool,
|
2021-11-18 18:50:24 +00:00
|
|
|
/// Resolves a specifier to its final redirected to specifier.
|
2023-12-07 00:03:18 +00:00
|
|
|
redirect_resolver: Arc<RedirectResolver>,
|
|
|
|
/// If --unstable-sloppy-imports is enabled.
|
|
|
|
unstable_sloppy_imports: bool,
|
2021-11-18 18:50:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Documents {
|
2023-08-02 00:49:09 +00:00
|
|
|
pub fn new(cache: Arc<dyn HttpCache>) -> Self {
|
2021-11-18 18:50:24 +00:00
|
|
|
Self {
|
2023-06-10 15:09:45 +00:00
|
|
|
cache: cache.clone(),
|
2021-11-18 18:50:24 +00:00
|
|
|
dirty: true,
|
|
|
|
open_docs: HashMap::default(),
|
|
|
|
file_system_docs: Default::default(),
|
|
|
|
imports: Default::default(),
|
2024-04-15 21:50:52 +00:00
|
|
|
maybe_node_resolver: None,
|
2023-10-25 18:39:00 +00:00
|
|
|
resolver: Arc::new(CliGraphResolver::new(CliGraphResolverOptions {
|
|
|
|
node_resolver: None,
|
|
|
|
npm_resolver: None,
|
|
|
|
package_json_deps_provider: Arc::new(PackageJsonDepsProvider::default()),
|
|
|
|
maybe_jsx_import_source_config: None,
|
|
|
|
maybe_import_map: None,
|
|
|
|
maybe_vendor_dir: None,
|
|
|
|
bare_node_builtins_enabled: false,
|
2023-12-07 00:03:18 +00:00
|
|
|
sloppy_imports_resolver: None,
|
2023-10-25 18:39:00 +00:00
|
|
|
})),
|
2024-03-01 21:34:13 +00:00
|
|
|
jsr_resolver: Arc::new(JsrCacheResolver::new(cache.clone(), None)),
|
2024-04-05 22:33:01 +00:00
|
|
|
lockfile: None,
|
2023-02-23 15:58:10 +00:00
|
|
|
npm_specifier_reqs: Default::default(),
|
2023-01-24 14:05:54 +00:00
|
|
|
has_injected_types_node_package: false,
|
2023-12-07 00:03:18 +00:00
|
|
|
redirect_resolver: Arc::new(RedirectResolver::new(cache)),
|
|
|
|
unstable_sloppy_imports: false,
|
2021-10-28 23:56:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-11 16:43:45 +00:00
|
|
|
pub fn module_graph_imports(&self) -> impl Iterator<Item = &ModuleSpecifier> {
|
|
|
|
self
|
|
|
|
.imports
|
|
|
|
.values()
|
|
|
|
.flat_map(|i| i.dependencies.values())
|
|
|
|
.flat_map(|value| value.get_type().or_else(|| value.get_code()))
|
|
|
|
}
|
|
|
|
|
2021-11-18 18:50:24 +00:00
|
|
|
/// "Open" a document from the perspective of the editor, meaning that
|
|
|
|
/// requests for information from the document will come from the in-memory
|
|
|
|
/// representation received from the language server client, versus reading
|
|
|
|
/// information from the disk.
|
|
|
|
pub fn open(
|
|
|
|
&mut self,
|
|
|
|
specifier: ModuleSpecifier,
|
|
|
|
version: i32,
|
|
|
|
language_id: LanguageId,
|
2022-05-20 20:40:55 +00:00
|
|
|
content: Arc<str>,
|
2024-04-06 14:36:43 +00:00
|
|
|
) -> Arc<Document> {
|
2023-02-15 16:30:54 +00:00
|
|
|
let resolver = self.get_resolver();
|
2023-10-20 04:02:08 +00:00
|
|
|
let npm_resolver = self.get_npm_resolver();
|
2021-11-18 18:50:24 +00:00
|
|
|
let document = Document::open(
|
|
|
|
specifier.clone(),
|
|
|
|
version,
|
|
|
|
language_id,
|
2024-04-15 21:50:52 +00:00
|
|
|
// todo(dsherret): don't we want to pass in the headers from
|
|
|
|
// the cache for remote modules here in order to get the
|
|
|
|
// x-typescript-types?
|
|
|
|
None,
|
2021-11-18 18:50:24 +00:00
|
|
|
content,
|
2023-10-12 14:37:56 +00:00
|
|
|
&self.cache,
|
2023-02-15 16:30:54 +00:00
|
|
|
resolver,
|
2024-04-15 21:50:52 +00:00
|
|
|
self.maybe_node_resolver.as_deref(),
|
2023-10-20 04:02:08 +00:00
|
|
|
npm_resolver,
|
2021-11-18 18:50:24 +00:00
|
|
|
);
|
2024-04-09 22:12:55 +00:00
|
|
|
|
|
|
|
self.file_system_docs.remove_document(&specifier);
|
|
|
|
self.file_system_docs.set_dirty(true);
|
|
|
|
|
2021-11-18 18:50:24 +00:00
|
|
|
self.open_docs.insert(specifier, document.clone());
|
|
|
|
self.dirty = true;
|
|
|
|
document
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Apply language server content changes to an open document.
|
|
|
|
pub fn change(
|
2021-01-22 10:03:16 +00:00
|
|
|
&mut self,
|
|
|
|
specifier: &ModuleSpecifier,
|
|
|
|
version: i32,
|
2021-10-28 23:56:01 +00:00
|
|
|
changes: Vec<lsp::TextDocumentContentChangeEvent>,
|
2024-04-06 14:36:43 +00:00
|
|
|
) -> Result<Arc<Document>, AnyError> {
|
2021-11-18 18:50:24 +00:00
|
|
|
let doc = self
|
|
|
|
.open_docs
|
|
|
|
.get(specifier)
|
|
|
|
.cloned()
|
2024-04-09 22:12:55 +00:00
|
|
|
.or_else(|| self.file_system_docs.remove_document(specifier))
|
2023-03-15 21:46:36 +00:00
|
|
|
.map(Ok)
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
Err(custom_error(
|
|
|
|
"NotFound",
|
|
|
|
format!("The specifier \"{specifier}\" was not found."),
|
|
|
|
))
|
|
|
|
})?;
|
2021-10-28 23:56:01 +00:00
|
|
|
self.dirty = true;
|
2023-10-20 04:02:08 +00:00
|
|
|
let doc = doc.with_change(
|
|
|
|
version,
|
|
|
|
changes,
|
|
|
|
self.get_resolver(),
|
|
|
|
self.get_npm_resolver(),
|
|
|
|
)?;
|
2021-11-18 18:50:24 +00:00
|
|
|
self.open_docs.insert(doc.specifier().clone(), doc.clone());
|
2021-11-12 16:42:04 +00:00
|
|
|
Ok(doc)
|
2021-10-28 23:56:01 +00:00
|
|
|
}
|
2021-01-22 10:03:16 +00:00
|
|
|
|
2023-10-12 14:37:56 +00:00
|
|
|
pub fn save(&mut self, specifier: &ModuleSpecifier) {
|
2024-04-09 22:12:55 +00:00
|
|
|
let doc = self
|
|
|
|
.open_docs
|
|
|
|
.get(specifier)
|
|
|
|
.cloned()
|
|
|
|
.or_else(|| self.file_system_docs.remove_document(specifier));
|
2023-10-12 14:37:56 +00:00
|
|
|
let Some(doc) = doc else {
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
self.dirty = true;
|
|
|
|
let doc = doc.saved(&self.cache);
|
|
|
|
self.open_docs.insert(doc.specifier().clone(), doc.clone());
|
|
|
|
}
|
|
|
|
|
2021-11-18 18:50:24 +00:00
|
|
|
/// Close an open document, this essentially clears any editor state that is
|
|
|
|
/// being held, and the document store will revert to the file system if
|
|
|
|
/// information about the document is required.
|
|
|
|
pub fn close(&mut self, specifier: &ModuleSpecifier) -> Result<(), AnyError> {
|
2023-10-12 14:37:56 +00:00
|
|
|
if let Some(document) = self.open_docs.remove(specifier) {
|
2024-04-09 22:12:55 +00:00
|
|
|
self
|
|
|
|
.file_system_docs
|
|
|
|
.docs
|
|
|
|
.insert(specifier.clone(), document);
|
|
|
|
|
2023-10-12 14:37:56 +00:00
|
|
|
self.dirty = true;
|
2021-11-18 18:50:24 +00:00
|
|
|
}
|
2021-09-07 14:39:32 +00:00
|
|
|
Ok(())
|
2021-01-22 10:03:16 +00:00
|
|
|
}
|
|
|
|
|
2024-04-17 20:40:42 +00:00
|
|
|
pub fn release(&self, specifier: &ModuleSpecifier) {
|
|
|
|
self.file_system_docs.remove_document(specifier);
|
|
|
|
self.file_system_docs.set_dirty(true);
|
|
|
|
}
|
|
|
|
|
2021-11-18 18:50:24 +00:00
|
|
|
/// Return `true` if the provided specifier can be resolved to a document,
|
|
|
|
/// otherwise `false`.
|
|
|
|
pub fn contains_import(
|
|
|
|
&self,
|
2021-10-28 23:56:01 +00:00
|
|
|
specifier: &str,
|
|
|
|
referrer: &ModuleSpecifier,
|
|
|
|
) -> bool {
|
2023-10-24 13:37:02 +00:00
|
|
|
let maybe_specifier = self
|
|
|
|
.get_resolver()
|
2023-12-07 00:03:18 +00:00
|
|
|
.resolve(
|
|
|
|
specifier,
|
|
|
|
&deno_graph::Range {
|
|
|
|
specifier: referrer.clone(),
|
|
|
|
start: deno_graph::Position::zeroed(),
|
|
|
|
end: deno_graph::Position::zeroed(),
|
|
|
|
},
|
|
|
|
ResolutionMode::Types,
|
|
|
|
)
|
2023-10-24 13:37:02 +00:00
|
|
|
.ok();
|
2021-10-28 23:56:01 +00:00
|
|
|
if let Some(import_specifier) = maybe_specifier {
|
2022-01-25 14:21:59 +00:00
|
|
|
self.exists(&import_specifier)
|
2021-10-28 23:56:01 +00:00
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
2021-01-22 10:03:16 +00:00
|
|
|
}
|
|
|
|
|
2023-12-07 00:03:18 +00:00
|
|
|
pub fn resolve_specifier(
|
2023-08-02 20:57:25 +00:00
|
|
|
&self,
|
|
|
|
specifier: &ModuleSpecifier,
|
|
|
|
) -> Option<ModuleSpecifier> {
|
2023-12-07 00:03:18 +00:00
|
|
|
if self.unstable_sloppy_imports && specifier.scheme() == "file" {
|
|
|
|
Some(
|
|
|
|
self
|
|
|
|
.resolve_unstable_sloppy_import(specifier)
|
2023-12-08 14:57:06 +00:00
|
|
|
.into_specifier()
|
|
|
|
.into_owned(),
|
2023-12-07 00:03:18 +00:00
|
|
|
)
|
|
|
|
} else {
|
2024-03-04 15:48:23 +00:00
|
|
|
let specifier = if let Ok(jsr_req_ref) =
|
|
|
|
JsrPackageReqReference::from_specifier(specifier)
|
|
|
|
{
|
|
|
|
Cow::Owned(self.jsr_resolver.jsr_to_registry_url(&jsr_req_ref)?)
|
|
|
|
} else {
|
|
|
|
Cow::Borrowed(specifier)
|
2024-02-12 22:12:49 +00:00
|
|
|
};
|
|
|
|
self.redirect_resolver.resolve(&specifier)
|
2023-12-07 00:03:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn resolve_unstable_sloppy_import<'a>(
|
|
|
|
&self,
|
|
|
|
specifier: &'a ModuleSpecifier,
|
2023-12-07 20:59:13 +00:00
|
|
|
) -> SloppyImportsResolution<'a> {
|
2024-02-27 18:30:21 +00:00
|
|
|
SloppyImportsResolver::resolve_with_stat_sync(
|
|
|
|
specifier,
|
|
|
|
ResolutionMode::Types,
|
|
|
|
|path| {
|
|
|
|
if let Ok(specifier) = ModuleSpecifier::from_file_path(path) {
|
|
|
|
if self.open_docs.contains_key(&specifier)
|
|
|
|
|| self.cache.contains(&specifier)
|
|
|
|
{
|
|
|
|
return Some(SloppyImportsFsEntry::File);
|
|
|
|
}
|
2023-12-07 00:03:18 +00:00
|
|
|
}
|
2024-02-27 18:30:21 +00:00
|
|
|
path.metadata().ok().and_then(|m| {
|
|
|
|
if m.is_file() {
|
|
|
|
Some(SloppyImportsFsEntry::File)
|
|
|
|
} else if m.is_dir() {
|
|
|
|
Some(SloppyImportsFsEntry::Dir)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
)
|
2023-08-02 20:57:25 +00:00
|
|
|
}
|
|
|
|
|
2021-11-18 18:50:24 +00:00
|
|
|
/// Return `true` if the specifier can be resolved to a document.
|
2022-01-25 14:21:59 +00:00
|
|
|
pub fn exists(&self, specifier: &ModuleSpecifier) -> bool {
|
2023-12-07 00:03:18 +00:00
|
|
|
let specifier = self.resolve_specifier(specifier);
|
2022-01-25 14:21:59 +00:00
|
|
|
if let Some(specifier) = specifier {
|
|
|
|
if self.open_docs.contains_key(&specifier) {
|
|
|
|
return true;
|
|
|
|
}
|
2023-07-01 22:52:30 +00:00
|
|
|
if specifier.scheme() == "data" {
|
|
|
|
return true;
|
|
|
|
}
|
2023-07-08 20:06:45 +00:00
|
|
|
if specifier.scheme() == "file" {
|
|
|
|
return specifier_to_file_path(&specifier)
|
|
|
|
.map(|p| p.is_file())
|
|
|
|
.unwrap_or(false);
|
|
|
|
}
|
|
|
|
if self.cache.contains(&specifier) {
|
|
|
|
return true;
|
2022-01-25 14:21:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
2021-01-22 10:03:16 +00:00
|
|
|
}
|
|
|
|
|
2022-10-21 15:20:18 +00:00
|
|
|
/// Returns a collection of npm package requirements.
|
2023-08-21 09:53:52 +00:00
|
|
|
pub fn npm_package_reqs(&mut self) -> Arc<Vec<PackageReq>> {
|
2024-04-10 18:58:49 +00:00
|
|
|
self.calculate_npm_reqs_if_dirty();
|
2023-03-30 14:43:16 +00:00
|
|
|
self.npm_specifier_reqs.clone()
|
2022-10-21 15:20:18 +00:00
|
|
|
}
|
|
|
|
|
2023-01-24 14:05:54 +00:00
|
|
|
/// 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
|
|
|
|
}
|
|
|
|
|
2021-11-18 18:50:24 +00:00
|
|
|
/// Return a document for the specifier.
|
2024-04-06 14:36:43 +00:00
|
|
|
pub fn get(
|
|
|
|
&self,
|
|
|
|
original_specifier: &ModuleSpecifier,
|
|
|
|
) -> Option<Arc<Document>> {
|
2023-12-07 00:03:18 +00:00
|
|
|
let specifier = self.resolve_specifier(original_specifier)?;
|
2021-11-18 18:50:24 +00:00
|
|
|
if let Some(document) = self.open_docs.get(&specifier) {
|
|
|
|
Some(document.clone())
|
2021-10-28 23:56:01 +00:00
|
|
|
} else {
|
2024-04-09 22:12:55 +00:00
|
|
|
self.file_system_docs.get(
|
2023-10-20 04:02:08 +00:00
|
|
|
&self.cache,
|
|
|
|
self.get_resolver(),
|
|
|
|
&specifier,
|
2024-04-15 21:50:52 +00:00
|
|
|
self.maybe_node_resolver.as_deref(),
|
2023-10-20 04:02:08 +00:00
|
|
|
self.get_npm_resolver(),
|
|
|
|
)
|
2021-10-28 23:56:01 +00:00
|
|
|
}
|
2021-01-22 10:03:16 +00:00
|
|
|
}
|
|
|
|
|
2024-04-08 18:45:20 +00:00
|
|
|
pub fn is_open(&self, specifier: &ModuleSpecifier) -> bool {
|
|
|
|
let Some(specifier) = self.resolve_specifier(specifier) else {
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
self.open_docs.contains_key(&specifier)
|
|
|
|
}
|
|
|
|
|
2023-03-29 20:25:48 +00:00
|
|
|
/// Return a collection of documents that are contained in the document store
|
|
|
|
/// based on the provided filter.
|
2024-04-06 14:36:43 +00:00
|
|
|
pub fn documents(&self, filter: DocumentsFilter) -> Vec<Arc<Document>> {
|
2023-03-29 20:25:48 +00:00
|
|
|
match filter {
|
|
|
|
DocumentsFilter::OpenDiagnosable => self
|
2021-11-18 18:50:24 +00:00
|
|
|
.open_docs
|
|
|
|
.values()
|
|
|
|
.filter_map(|doc| {
|
2023-03-29 20:25:48 +00:00
|
|
|
if doc.is_diagnosable() {
|
2021-11-18 18:50:24 +00:00
|
|
|
Some(doc.clone())
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
2023-03-29 20:25:48 +00:00
|
|
|
.collect(),
|
|
|
|
DocumentsFilter::AllDiagnosable | DocumentsFilter::All => {
|
|
|
|
let diagnosable_only =
|
|
|
|
matches!(filter, DocumentsFilter::AllDiagnosable);
|
|
|
|
// it is technically possible for a Document to end up in both the open
|
|
|
|
// and closed documents so we need to ensure we don't return duplicates
|
|
|
|
let mut seen_documents = HashSet::new();
|
|
|
|
self
|
|
|
|
.open_docs
|
|
|
|
.values()
|
2024-04-09 22:12:55 +00:00
|
|
|
.cloned()
|
|
|
|
.chain(self.file_system_docs.docs.iter().map(|v| v.value().clone()))
|
2023-03-29 20:25:48 +00:00
|
|
|
.filter_map(|doc| {
|
|
|
|
// this prefers the open documents
|
|
|
|
if seen_documents.insert(doc.specifier().clone())
|
|
|
|
&& (!diagnosable_only || doc.is_diagnosable())
|
|
|
|
{
|
2024-04-09 22:12:55 +00:00
|
|
|
Some(doc)
|
2023-03-29 20:25:48 +00:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
2021-11-18 18:50:24 +00:00
|
|
|
}
|
2021-10-28 23:56:01 +00:00
|
|
|
}
|
|
|
|
|
2021-11-18 18:50:24 +00:00
|
|
|
/// For a given set of string specifiers, resolve each one from the graph,
|
|
|
|
/// for a given referrer. This is used to provide resolution information to
|
|
|
|
/// tsc when type checking.
|
|
|
|
pub fn resolve(
|
|
|
|
&self,
|
2024-04-14 21:42:58 +00:00
|
|
|
specifiers: &[String],
|
|
|
|
referrer: &ModuleSpecifier,
|
2023-09-28 20:43:45 +00:00
|
|
|
maybe_npm: Option<&StateNpmSnapshot>,
|
2023-01-24 14:05:54 +00:00
|
|
|
) -> Vec<Option<(ModuleSpecifier, MediaType)>> {
|
2024-04-19 00:51:16 +00:00
|
|
|
let document = self.get(referrer);
|
|
|
|
let dependencies = document.as_ref().map(|d| d.dependencies());
|
2021-10-28 23:56:01 +00:00
|
|
|
let mut results = Vec::new();
|
2021-11-16 22:23:25 +00:00
|
|
|
for specifier in specifiers {
|
2023-09-28 20:43:45 +00:00
|
|
|
if let Some(npm) = maybe_npm {
|
|
|
|
if npm.node_resolver.in_npm_package(referrer) {
|
2022-10-21 15:20:18 +00:00
|
|
|
// we're in an npm package, so use node resolution
|
|
|
|
results.push(Some(NodeResolution::into_specifier_and_media_type(
|
2023-09-28 20:43:45 +00:00
|
|
|
npm
|
|
|
|
.node_resolver
|
2023-04-24 23:44:35 +00:00
|
|
|
.resolve(
|
2024-04-14 21:42:58 +00:00
|
|
|
specifier,
|
2023-04-17 19:36:23 +00:00
|
|
|
referrer,
|
|
|
|
NodeResolutionMode::Types,
|
2023-04-25 01:07:48 +00:00
|
|
|
&PermissionsContainer::allow_all(),
|
2023-04-17 19:36:23 +00:00
|
|
|
)
|
|
|
|
.ok()
|
|
|
|
.flatten(),
|
2022-10-21 15:20:18 +00:00
|
|
|
)));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2021-11-16 22:23:25 +00:00
|
|
|
if specifier.starts_with("asset:") {
|
2024-04-14 21:42:58 +00:00
|
|
|
if let Ok(specifier) = ModuleSpecifier::parse(specifier) {
|
2023-03-21 15:46:40 +00:00
|
|
|
let media_type = MediaType::from_specifier(&specifier);
|
2021-11-16 22:23:25 +00:00
|
|
|
results.push(Some((specifier, media_type)));
|
|
|
|
} else {
|
|
|
|
results.push(None);
|
|
|
|
}
|
2023-01-24 14:05:54 +00:00
|
|
|
} else if let Some(dep) =
|
2024-04-19 00:51:16 +00:00
|
|
|
dependencies.as_ref().and_then(|d| d.get(specifier))
|
2023-01-24 14:05:54 +00:00
|
|
|
{
|
2023-02-10 03:00:23 +00:00
|
|
|
if let Some(specifier) = dep.maybe_type.maybe_specifier() {
|
2023-10-03 23:05:06 +00:00
|
|
|
results.push(self.resolve_dependency(specifier, maybe_npm, referrer));
|
2023-02-10 03:00:23 +00:00
|
|
|
} else if let Some(specifier) = dep.maybe_code.maybe_specifier() {
|
2023-10-03 23:05:06 +00:00
|
|
|
results.push(self.resolve_dependency(specifier, maybe_npm, referrer));
|
2021-01-22 10:03:16 +00:00
|
|
|
} else {
|
2021-10-28 23:56:01 +00:00
|
|
|
results.push(None);
|
2021-01-22 10:03:16 +00:00
|
|
|
}
|
2023-02-10 03:00:23 +00:00
|
|
|
} else if let Some(specifier) = self
|
2024-04-14 21:42:58 +00:00
|
|
|
.resolve_imports_dependency(specifier)
|
2023-02-10 03:00:23 +00:00
|
|
|
.and_then(|r| r.maybe_specifier())
|
2021-11-16 22:23:25 +00:00
|
|
|
{
|
2023-10-03 23:05:06 +00:00
|
|
|
results.push(self.resolve_dependency(specifier, maybe_npm, referrer));
|
2023-02-22 19:15:25 +00:00
|
|
|
} else if let Ok(npm_req_ref) =
|
2024-04-14 21:42:58 +00:00
|
|
|
NpmPackageReqReference::from_str(specifier)
|
2023-02-22 19:15:25 +00:00
|
|
|
{
|
2023-10-03 23:05:06 +00:00
|
|
|
results.push(node_resolve_npm_req_ref(
|
2024-03-06 00:23:51 +00:00
|
|
|
&npm_req_ref,
|
2023-10-03 23:05:06 +00:00
|
|
|
maybe_npm,
|
|
|
|
referrer,
|
|
|
|
));
|
2024-04-14 21:42:58 +00:00
|
|
|
} else if let Ok(specifier) = self.get_resolver().resolve(
|
|
|
|
specifier,
|
|
|
|
&deno_graph::Range {
|
|
|
|
specifier: referrer.clone(),
|
|
|
|
start: deno_graph::Position::zeroed(),
|
|
|
|
end: deno_graph::Position::zeroed(),
|
|
|
|
},
|
|
|
|
ResolutionMode::Types,
|
|
|
|
) {
|
|
|
|
results.push(self.resolve_dependency(&specifier, maybe_npm, referrer));
|
2021-11-16 22:23:25 +00:00
|
|
|
} else {
|
|
|
|
results.push(None);
|
2021-10-28 23:56:01 +00:00
|
|
|
}
|
|
|
|
}
|
2023-01-24 14:05:54 +00:00
|
|
|
results
|
2021-10-28 23:56:01 +00:00
|
|
|
}
|
|
|
|
|
2021-11-18 18:50:24 +00:00
|
|
|
/// Update the location of the on disk cache for the document store.
|
2023-08-02 00:49:09 +00:00
|
|
|
pub fn set_cache(&mut self, cache: Arc<dyn HttpCache>) {
|
2021-10-28 23:56:01 +00:00
|
|
|
// TODO update resolved dependencies?
|
2023-06-10 15:09:45 +00:00
|
|
|
self.cache = cache.clone();
|
2023-12-07 00:03:18 +00:00
|
|
|
self.redirect_resolver = Arc::new(RedirectResolver::new(cache));
|
2021-10-28 23:56:01 +00:00
|
|
|
self.dirty = true;
|
|
|
|
}
|
|
|
|
|
2021-11-18 18:50:24 +00:00
|
|
|
/// Tries to cache a navigation tree that is associated with the provided specifier
|
|
|
|
/// if the document stored has the same script version.
|
|
|
|
pub fn try_cache_navigation_tree(
|
2022-12-20 01:22:17 +00:00
|
|
|
&self,
|
2021-06-04 21:31:44 +00:00
|
|
|
specifier: &ModuleSpecifier,
|
2021-11-18 18:50:24 +00:00
|
|
|
script_version: &str,
|
2021-10-28 23:56:01 +00:00
|
|
|
navigation_tree: Arc<tsc::NavigationTree>,
|
2021-06-04 21:31:44 +00:00
|
|
|
) -> Result<(), AnyError> {
|
2022-12-20 01:22:17 +00:00
|
|
|
if let Some(doc) = self.open_docs.get(specifier) {
|
|
|
|
doc.update_navigation_tree_if_version(navigation_tree, script_version)
|
2024-04-09 22:12:55 +00:00
|
|
|
} else if let Some(doc) = self.file_system_docs.docs.get_mut(specifier) {
|
|
|
|
doc.update_navigation_tree_if_version(navigation_tree, script_version);
|
2021-11-18 18:50:24 +00:00
|
|
|
} else {
|
2024-04-09 22:12:55 +00:00
|
|
|
return Err(custom_error(
|
|
|
|
"NotFound",
|
|
|
|
format!("Specifier not found {specifier}"),
|
|
|
|
));
|
2021-11-18 18:50:24 +00:00
|
|
|
}
|
2024-04-09 22:12:55 +00:00
|
|
|
|
2021-10-28 23:56:01 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-03-01 21:34:13 +00:00
|
|
|
pub fn get_jsr_resolver(&self) -> &Arc<JsrCacheResolver> {
|
2024-02-21 02:45:00 +00:00
|
|
|
&self.jsr_resolver
|
|
|
|
}
|
|
|
|
|
2024-04-05 22:33:01 +00:00
|
|
|
pub fn refresh_lockfile(&mut self, lockfile: Option<Arc<Mutex<Lockfile>>>) {
|
2024-03-01 21:34:13 +00:00
|
|
|
self.jsr_resolver =
|
2024-04-05 22:33:01 +00:00
|
|
|
Arc::new(JsrCacheResolver::new(self.cache.clone(), lockfile.clone()));
|
|
|
|
self.lockfile = lockfile;
|
2024-02-14 22:48:39 +00:00
|
|
|
}
|
|
|
|
|
2024-03-26 15:52:20 +00:00
|
|
|
pub fn update_config(
|
|
|
|
&mut self,
|
|
|
|
config: &Config,
|
|
|
|
node_resolver: Option<Arc<CliNodeResolver>>,
|
|
|
|
npm_resolver: Option<Arc<dyn CliNpmResolver>>,
|
|
|
|
workspace_files: &BTreeSet<ModuleSpecifier>,
|
|
|
|
) {
|
|
|
|
let config_data = config.tree.root_data();
|
2024-04-02 22:02:50 +00:00
|
|
|
let config_file = config_data.and_then(|d| d.config_file.as_deref());
|
2024-04-15 21:50:52 +00:00
|
|
|
self.maybe_node_resolver = node_resolver.clone();
|
2023-10-25 18:39:00 +00:00
|
|
|
self.resolver = Arc::new(CliGraphResolver::new(CliGraphResolverOptions {
|
2024-03-26 15:52:20 +00:00
|
|
|
node_resolver,
|
|
|
|
npm_resolver,
|
|
|
|
package_json_deps_provider: Arc::new(PackageJsonDepsProvider::new(
|
|
|
|
config_data
|
|
|
|
.and_then(|d| d.package_json.as_ref())
|
|
|
|
.map(|package_json| {
|
|
|
|
package_json::get_local_package_json_version_reqs(package_json)
|
|
|
|
}),
|
|
|
|
)),
|
|
|
|
maybe_jsx_import_source_config: config_file
|
|
|
|
.and_then(|cf| cf.to_maybe_jsx_import_source_config().ok().flatten()),
|
2024-04-02 22:02:50 +00:00
|
|
|
maybe_import_map: config_data.and_then(|d| d.import_map.clone()),
|
|
|
|
maybe_vendor_dir: config_data.and_then(|d| d.vendor_dir.as_ref()),
|
2024-03-26 15:52:20 +00:00
|
|
|
bare_node_builtins_enabled: config_file
|
2023-12-07 00:03:18 +00:00
|
|
|
.map(|config| config.has_unstable("bare-node-builtins"))
|
2023-10-25 18:39:00 +00:00
|
|
|
.unwrap_or(false),
|
2023-12-07 00:03:18 +00:00
|
|
|
// Don't set this for the LSP because instead we'll use the OpenDocumentsLoader
|
|
|
|
// because it's much easier and we get diagnostics/quick fixes about a redirected
|
|
|
|
// specifier for free.
|
|
|
|
sloppy_imports_resolver: None,
|
2023-10-25 18:39:00 +00:00
|
|
|
}));
|
2024-03-01 21:34:13 +00:00
|
|
|
self.jsr_resolver = Arc::new(JsrCacheResolver::new(
|
2024-02-12 22:12:49 +00:00
|
|
|
self.cache.clone(),
|
2024-04-02 22:02:50 +00:00
|
|
|
config.tree.root_lockfile().cloned(),
|
2024-02-12 22:12:49 +00:00
|
|
|
));
|
2024-04-05 22:33:01 +00:00
|
|
|
self.lockfile = config.tree.root_lockfile().cloned();
|
2023-12-07 00:03:18 +00:00
|
|
|
self.redirect_resolver =
|
|
|
|
Arc::new(RedirectResolver::new(self.cache.clone()));
|
2024-03-21 04:29:52 +00:00
|
|
|
let resolver = self.resolver.as_graph_resolver();
|
|
|
|
let npm_resolver = self.resolver.as_graph_npm_resolver();
|
2021-11-18 18:50:24 +00:00
|
|
|
self.imports = Arc::new(
|
2024-03-26 15:52:20 +00:00
|
|
|
if let Some(Ok(imports)) = config_file.map(|cf| cf.to_maybe_imports()) {
|
2021-11-18 18:50:24 +00:00
|
|
|
imports
|
|
|
|
.into_iter()
|
2023-08-24 09:21:34 +00:00
|
|
|
.map(|(referrer, imports)| {
|
2023-10-20 04:02:08 +00:00
|
|
|
let graph_import = GraphImport::new(
|
|
|
|
&referrer,
|
|
|
|
imports,
|
2024-03-21 04:29:52 +00:00
|
|
|
Some(resolver),
|
|
|
|
Some(npm_resolver),
|
2023-10-20 04:02:08 +00:00
|
|
|
);
|
2023-08-24 09:21:34 +00:00
|
|
|
(referrer, graph_import)
|
2021-11-18 18:50:24 +00:00
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
} else {
|
2023-03-11 16:43:45 +00:00
|
|
|
IndexMap::new()
|
2021-11-18 18:50:24 +00:00
|
|
|
},
|
|
|
|
);
|
2024-03-26 15:52:20 +00:00
|
|
|
self.unstable_sloppy_imports = config_file
|
2023-12-07 00:03:18 +00:00
|
|
|
.map(|c| c.has_unstable("sloppy-imports"))
|
|
|
|
.unwrap_or(false);
|
2024-03-21 04:29:52 +00:00
|
|
|
{
|
2024-04-09 22:12:55 +00:00
|
|
|
let fs_docs = &self.file_system_docs;
|
2024-03-21 04:29:52 +00:00
|
|
|
// Clean up non-existent documents.
|
|
|
|
fs_docs.docs.retain(|specifier, _| {
|
|
|
|
let Ok(path) = specifier_to_file_path(specifier) else {
|
|
|
|
// Remove non-file schemed docs (deps). They may not be dependencies
|
|
|
|
// anymore after updating resolvers.
|
|
|
|
return false;
|
|
|
|
};
|
2024-03-26 15:52:20 +00:00
|
|
|
if !config.specifier_enabled(specifier) {
|
2024-03-21 04:29:52 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
path.is_file()
|
|
|
|
});
|
|
|
|
let mut open_docs = std::mem::take(&mut self.open_docs);
|
2024-04-09 22:12:55 +00:00
|
|
|
for doc in open_docs.values_mut() {
|
|
|
|
if !config.specifier_enabled(doc.specifier()) {
|
|
|
|
continue;
|
|
|
|
}
|
2024-04-15 21:50:52 +00:00
|
|
|
if let Some(new_doc) = doc.maybe_with_new_resolver(
|
|
|
|
resolver,
|
|
|
|
self.maybe_node_resolver.as_deref(),
|
|
|
|
npm_resolver,
|
|
|
|
) {
|
2024-04-09 22:12:55 +00:00
|
|
|
*doc = new_doc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for mut doc in self.file_system_docs.docs.iter_mut() {
|
|
|
|
if !config.specifier_enabled(doc.specifier()) {
|
|
|
|
continue;
|
|
|
|
}
|
2024-04-15 21:50:52 +00:00
|
|
|
if let Some(new_doc) = doc.maybe_with_new_resolver(
|
|
|
|
resolver,
|
|
|
|
self.maybe_node_resolver.as_deref(),
|
|
|
|
npm_resolver,
|
|
|
|
) {
|
2024-04-09 22:12:55 +00:00
|
|
|
*doc.value_mut() = new_doc;
|
2024-03-21 04:29:52 +00:00
|
|
|
}
|
2023-01-28 15:18:32 +00:00
|
|
|
}
|
2024-03-21 04:29:52 +00:00
|
|
|
self.open_docs = open_docs;
|
2024-03-31 20:39:23 +00:00
|
|
|
let mut preload_count = 0;
|
2024-03-26 15:52:20 +00:00
|
|
|
for specifier in workspace_files {
|
|
|
|
if !config.specifier_enabled(specifier) {
|
2024-03-21 04:29:52 +00:00
|
|
|
continue;
|
|
|
|
}
|
2024-03-31 20:39:23 +00:00
|
|
|
if preload_count >= config.settings.unscoped.document_preload_limit {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
preload_count += 1;
|
2024-03-21 04:29:52 +00:00
|
|
|
if !self.open_docs.contains_key(specifier)
|
|
|
|
&& !fs_docs.docs.contains_key(specifier)
|
2023-05-11 21:17:14 +00:00
|
|
|
{
|
2023-10-20 04:02:08 +00:00
|
|
|
fs_docs.refresh_document(
|
|
|
|
&self.cache,
|
|
|
|
resolver,
|
2024-03-21 04:29:52 +00:00
|
|
|
specifier,
|
2024-04-15 21:50:52 +00:00
|
|
|
self.maybe_node_resolver.as_deref(),
|
2023-10-20 04:02:08 +00:00
|
|
|
npm_resolver,
|
|
|
|
);
|
2023-03-30 21:47:53 +00:00
|
|
|
}
|
|
|
|
}
|
2024-04-09 22:12:55 +00:00
|
|
|
fs_docs.set_dirty(true);
|
2023-03-30 21:47:53 +00:00
|
|
|
}
|
2024-03-21 04:29:52 +00:00
|
|
|
self.dirty = true;
|
2023-01-28 15:18:32 +00:00
|
|
|
}
|
|
|
|
|
2021-11-18 18:50:24 +00:00
|
|
|
/// Iterate through the documents, building a map where the key is a unique
|
|
|
|
/// document and the value is a set of specifiers that depend on that
|
|
|
|
/// document.
|
2024-04-10 18:58:49 +00:00
|
|
|
fn calculate_npm_reqs_if_dirty(&mut self) {
|
|
|
|
let mut npm_reqs = HashSet::new();
|
|
|
|
let mut has_node_builtin_specifier = false;
|
|
|
|
let is_fs_docs_dirty = self.file_system_docs.set_dirty(false);
|
|
|
|
if !is_fs_docs_dirty && !self.dirty {
|
|
|
|
return;
|
2021-11-18 18:50:24 +00:00
|
|
|
}
|
2024-04-10 18:58:49 +00:00
|
|
|
let mut visit_doc = |doc: &Arc<Document>| {
|
|
|
|
for dependency in doc.dependencies().values() {
|
|
|
|
if let Some(dep) = dependency.get_code() {
|
|
|
|
if dep.scheme() == "node" {
|
|
|
|
has_node_builtin_specifier = true;
|
|
|
|
}
|
2023-02-21 17:03:48 +00:00
|
|
|
if let Ok(reference) = NpmPackageReqReference::from_specifier(dep) {
|
2024-04-10 18:58:49 +00:00
|
|
|
npm_reqs.insert(reference.into_inner().req);
|
2022-11-30 00:32:18 +00:00
|
|
|
}
|
2021-11-18 18:50:24 +00:00
|
|
|
}
|
2024-04-10 18:58:49 +00:00
|
|
|
if let Some(dep) = dependency.get_type() {
|
|
|
|
if let Ok(reference) = NpmPackageReqReference::from_specifier(dep) {
|
|
|
|
npm_reqs.insert(reference.into_inner().req);
|
2022-11-30 00:32:18 +00:00
|
|
|
}
|
|
|
|
}
|
2024-04-10 18:58:49 +00:00
|
|
|
}
|
|
|
|
if let Some(dep) = doc.maybe_types_dependency().maybe_specifier() {
|
|
|
|
if let Ok(reference) = NpmPackageReqReference::from_specifier(dep) {
|
|
|
|
npm_reqs.insert(reference.into_inner().req);
|
2022-11-30 00:32:18 +00:00
|
|
|
}
|
|
|
|
}
|
2024-04-10 18:58:49 +00:00
|
|
|
};
|
2024-04-09 22:12:55 +00:00
|
|
|
for entry in self.file_system_docs.docs.iter() {
|
2024-04-10 18:58:49 +00:00
|
|
|
visit_doc(entry.value())
|
2024-04-09 22:12:55 +00:00
|
|
|
}
|
2024-04-10 18:58:49 +00:00
|
|
|
for doc in self.open_docs.values() {
|
|
|
|
visit_doc(doc);
|
2022-10-21 15:20:18 +00:00
|
|
|
}
|
2022-11-30 00:32:18 +00:00
|
|
|
|
2024-04-05 22:33:01 +00:00
|
|
|
// fill the reqs from the lockfile
|
|
|
|
if let Some(lockfile) = self.lockfile.as_ref() {
|
|
|
|
let lockfile = lockfile.lock();
|
|
|
|
for key in lockfile.content.packages.specifiers.keys() {
|
|
|
|
if let Some(key) = key.strip_prefix("npm:") {
|
|
|
|
if let Ok(req) = PackageReq::from_str(key) {
|
|
|
|
npm_reqs.insert(req);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-24 14:05:54 +00:00
|
|
|
// 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.
|
2024-04-10 18:58:49 +00:00
|
|
|
self.has_injected_types_node_package = has_node_builtin_specifier
|
2023-01-24 14:05:54 +00:00
|
|
|
&& !npm_reqs.iter().any(|r| r.name == "@types/node");
|
|
|
|
if self.has_injected_types_node_package {
|
2023-08-21 09:53:52 +00:00
|
|
|
npm_reqs.insert(PackageReq::from_str("@types/node").unwrap());
|
2023-01-24 14:05:54 +00:00
|
|
|
}
|
|
|
|
|
2023-02-23 15:58:10 +00:00
|
|
|
self.npm_specifier_reqs = Arc::new({
|
|
|
|
let mut reqs = npm_reqs.into_iter().collect::<Vec<_>>();
|
|
|
|
reqs.sort();
|
|
|
|
reqs
|
|
|
|
});
|
2021-11-18 18:50:24 +00:00
|
|
|
self.dirty = false;
|
2021-11-12 16:42:04 +00:00
|
|
|
}
|
|
|
|
|
2023-02-15 16:30:54 +00:00
|
|
|
fn get_resolver(&self) -> &dyn deno_graph::source::Resolver {
|
|
|
|
self.resolver.as_graph_resolver()
|
2021-10-28 23:56:01 +00:00
|
|
|
}
|
|
|
|
|
2023-10-20 04:02:08 +00:00
|
|
|
fn get_npm_resolver(&self) -> &dyn deno_graph::source::NpmResolver {
|
|
|
|
self.resolver.as_graph_npm_resolver()
|
|
|
|
}
|
|
|
|
|
2021-11-18 18:50:24 +00:00
|
|
|
fn resolve_dependency(
|
2021-10-28 23:56:01 +00:00
|
|
|
&self,
|
|
|
|
specifier: &ModuleSpecifier,
|
2023-09-28 20:43:45 +00:00
|
|
|
maybe_npm: Option<&StateNpmSnapshot>,
|
2023-10-03 23:05:06 +00:00
|
|
|
referrer: &ModuleSpecifier,
|
2021-11-18 18:50:24 +00:00
|
|
|
) -> Option<(ModuleSpecifier, MediaType)> {
|
2023-07-26 22:52:31 +00:00
|
|
|
if let Some(module_name) = specifier.as_str().strip_prefix("node:") {
|
|
|
|
if deno_node::is_builtin_node_module(module_name) {
|
|
|
|
// 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
|
|
|
|
return Some((specifier.clone(), MediaType::Dts));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-21 17:03:48 +00:00
|
|
|
if let Ok(npm_ref) = NpmPackageReqReference::from_specifier(specifier) {
|
2024-03-06 00:23:51 +00:00
|
|
|
return node_resolve_npm_req_ref(&npm_ref, maybe_npm, referrer);
|
2022-10-21 15:20:18 +00:00
|
|
|
}
|
2024-04-14 21:42:58 +00:00
|
|
|
let Some(doc) = self.get(specifier) else {
|
|
|
|
return Some((specifier.clone(), MediaType::from_specifier(specifier)));
|
|
|
|
};
|
2024-04-19 00:51:16 +00:00
|
|
|
if let Some(specifier) = doc.maybe_types_dependency().maybe_specifier() {
|
2023-10-03 23:05:06 +00:00
|
|
|
self.resolve_dependency(specifier, maybe_npm, referrer)
|
2021-11-18 18:50:24 +00:00
|
|
|
} else {
|
|
|
|
let media_type = doc.media_type();
|
2023-08-02 20:57:25 +00:00
|
|
|
Some((doc.specifier().clone(), media_type))
|
2021-11-18 18:50:24 +00:00
|
|
|
}
|
2021-10-28 23:56:01 +00:00
|
|
|
}
|
|
|
|
|
2021-11-18 18:50:24 +00:00
|
|
|
/// Iterate through any "imported" modules, checking to see if a dependency
|
|
|
|
/// is available. This is used to provide "global" imports like the JSX import
|
|
|
|
/// source.
|
2023-02-10 03:00:23 +00:00
|
|
|
fn resolve_imports_dependency(&self, specifier: &str) -> Option<&Resolution> {
|
2022-08-09 21:27:22 +00:00
|
|
|
for graph_imports in self.imports.values() {
|
|
|
|
let maybe_dep = graph_imports.dependencies.get(specifier);
|
2021-11-18 18:50:24 +00:00
|
|
|
if maybe_dep.is_some() {
|
2022-01-31 22:33:57 +00:00
|
|
|
return maybe_dep.map(|d| &d.maybe_type);
|
2021-11-18 18:50:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
2021-11-09 01:26:39 +00:00
|
|
|
}
|
2021-01-22 10:03:16 +00:00
|
|
|
}
|
|
|
|
|
2023-02-22 19:15:25 +00:00
|
|
|
fn node_resolve_npm_req_ref(
|
2024-03-06 00:23:51 +00:00
|
|
|
npm_req_ref: &NpmPackageReqReference,
|
2023-09-28 20:43:45 +00:00
|
|
|
maybe_npm: Option<&StateNpmSnapshot>,
|
2023-10-03 23:05:06 +00:00
|
|
|
referrer: &ModuleSpecifier,
|
2023-02-22 19:15:25 +00:00
|
|
|
) -> Option<(ModuleSpecifier, MediaType)> {
|
2023-09-28 20:43:45 +00:00
|
|
|
maybe_npm.map(|npm| {
|
2023-02-22 19:15:25 +00:00
|
|
|
NodeResolution::into_specifier_and_media_type(
|
2023-09-28 20:43:45 +00:00
|
|
|
npm
|
2024-03-06 00:23:51 +00:00
|
|
|
.node_resolver
|
|
|
|
.resolve_req_reference(
|
|
|
|
npm_req_ref,
|
|
|
|
&PermissionsContainer::allow_all(),
|
|
|
|
referrer,
|
|
|
|
NodeResolutionMode::Types,
|
|
|
|
)
|
|
|
|
.ok(),
|
2023-02-22 19:15:25 +00:00
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-10-20 17:23:21 +00:00
|
|
|
/// Loader that will look at the open documents.
|
2022-10-21 15:20:18 +00:00
|
|
|
pub struct OpenDocumentsGraphLoader<'a> {
|
2022-10-20 17:23:21 +00:00
|
|
|
pub inner_loader: &'a mut dyn deno_graph::source::Loader,
|
2024-04-06 14:36:43 +00:00
|
|
|
pub open_docs: &'a HashMap<ModuleSpecifier, Arc<Document>>,
|
2023-12-07 00:03:18 +00:00
|
|
|
pub unstable_sloppy_imports: bool,
|
2022-10-20 17:23:21 +00:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:09:16 +00:00
|
|
|
impl<'a> OpenDocumentsGraphLoader<'a> {
|
|
|
|
fn load_from_docs(
|
|
|
|
&self,
|
2022-10-20 17:23:21 +00:00
|
|
|
specifier: &ModuleSpecifier,
|
2023-09-07 13:09:16 +00:00
|
|
|
) -> Option<deno_graph::source::LoadFuture> {
|
2022-10-20 17:23:21 +00:00
|
|
|
if specifier.scheme() == "file" {
|
|
|
|
if let Some(doc) = self.open_docs.get(specifier) {
|
2023-09-07 13:09:16 +00:00
|
|
|
return Some(
|
|
|
|
future::ready(Ok(Some(deno_graph::source::LoadResponse::Module {
|
2024-02-01 03:15:22 +00:00
|
|
|
content: Arc::from(doc.content()),
|
2022-10-20 17:23:21 +00:00
|
|
|
specifier: doc.specifier().clone(),
|
|
|
|
maybe_headers: None,
|
2023-09-07 13:09:16 +00:00
|
|
|
})))
|
|
|
|
.boxed_local(),
|
|
|
|
);
|
2022-10-20 17:23:21 +00:00
|
|
|
}
|
|
|
|
}
|
2023-09-07 13:09:16 +00:00
|
|
|
None
|
|
|
|
}
|
2023-12-07 00:03:18 +00:00
|
|
|
|
|
|
|
fn resolve_unstable_sloppy_import<'b>(
|
|
|
|
&self,
|
|
|
|
specifier: &'b ModuleSpecifier,
|
2023-12-07 20:59:13 +00:00
|
|
|
) -> SloppyImportsResolution<'b> {
|
2024-02-27 18:30:21 +00:00
|
|
|
SloppyImportsResolver::resolve_with_stat_sync(
|
|
|
|
specifier,
|
|
|
|
ResolutionMode::Types,
|
|
|
|
|path| {
|
|
|
|
if let Ok(specifier) = ModuleSpecifier::from_file_path(path) {
|
|
|
|
if self.open_docs.contains_key(&specifier) {
|
|
|
|
return Some(SloppyImportsFsEntry::File);
|
|
|
|
}
|
2023-12-07 00:03:18 +00:00
|
|
|
}
|
2024-02-27 18:30:21 +00:00
|
|
|
path.metadata().ok().and_then(|m| {
|
|
|
|
if m.is_file() {
|
|
|
|
Some(SloppyImportsFsEntry::File)
|
|
|
|
} else if m.is_dir() {
|
|
|
|
Some(SloppyImportsFsEntry::Dir)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})
|
|
|
|
},
|
|
|
|
)
|
2023-12-07 00:03:18 +00:00
|
|
|
}
|
2023-09-07 13:09:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> deno_graph::source::Loader for OpenDocumentsGraphLoader<'a> {
|
|
|
|
fn load(
|
2024-04-19 00:51:16 +00:00
|
|
|
&self,
|
2023-09-07 13:09:16 +00:00
|
|
|
specifier: &ModuleSpecifier,
|
2024-02-15 19:49:35 +00:00
|
|
|
options: deno_graph::source::LoadOptions,
|
2023-09-07 13:09:16 +00:00
|
|
|
) -> deno_graph::source::LoadFuture {
|
2023-12-07 00:03:18 +00:00
|
|
|
let specifier = if self.unstable_sloppy_imports {
|
|
|
|
self
|
|
|
|
.resolve_unstable_sloppy_import(specifier)
|
|
|
|
.into_specifier()
|
|
|
|
} else {
|
|
|
|
Cow::Borrowed(specifier)
|
|
|
|
};
|
|
|
|
|
|
|
|
match self.load_from_docs(&specifier) {
|
2023-09-07 13:09:16 +00:00
|
|
|
Some(fut) => fut,
|
2024-02-15 19:49:35 +00:00
|
|
|
None => self.inner_loader.load(&specifier, options),
|
2023-09-07 13:09:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn cache_module_info(
|
2024-04-19 00:51:16 +00:00
|
|
|
&self,
|
2023-09-07 13:09:16 +00:00
|
|
|
specifier: &deno_ast::ModuleSpecifier,
|
2024-02-01 03:15:22 +00:00
|
|
|
source: &Arc<[u8]>,
|
2023-09-07 13:09:16 +00:00
|
|
|
module_info: &deno_graph::ModuleInfo,
|
|
|
|
) {
|
|
|
|
self
|
|
|
|
.inner_loader
|
|
|
|
.cache_module_info(specifier, source, module_info)
|
2022-10-20 17:23:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-28 15:18:32 +00:00
|
|
|
fn parse_and_analyze_module(
|
|
|
|
specifier: &ModuleSpecifier,
|
|
|
|
text_info: SourceTextInfo,
|
|
|
|
maybe_headers: Option<&HashMap<String, String>>,
|
2024-04-15 21:50:52 +00:00
|
|
|
media_type: MediaType,
|
2023-02-15 16:30:54 +00:00
|
|
|
resolver: &dyn deno_graph::source::Resolver,
|
2023-10-20 04:02:08 +00:00
|
|
|
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
2023-01-28 15:18:32 +00:00
|
|
|
) -> (Option<ParsedSourceResult>, Option<ModuleResult>) {
|
2024-04-15 21:50:52 +00:00
|
|
|
let parsed_source_result = parse_source(specifier, text_info, media_type);
|
2023-10-20 04:02:08 +00:00
|
|
|
let module_result = analyze_module(
|
|
|
|
specifier,
|
|
|
|
&parsed_source_result,
|
|
|
|
maybe_headers,
|
|
|
|
resolver,
|
|
|
|
npm_resolver,
|
|
|
|
);
|
2023-01-28 15:18:32 +00:00
|
|
|
(Some(parsed_source_result), Some(module_result))
|
|
|
|
}
|
2022-08-22 16:14:59 +00:00
|
|
|
|
2023-01-28 15:18:32 +00:00
|
|
|
fn parse_source(
|
|
|
|
specifier: &ModuleSpecifier,
|
|
|
|
text_info: SourceTextInfo,
|
2024-04-15 21:50:52 +00:00
|
|
|
media_type: MediaType,
|
2023-01-28 15:18:32 +00:00
|
|
|
) -> ParsedSourceResult {
|
|
|
|
deno_ast::parse_module(deno_ast::ParseParams {
|
2024-02-09 01:40:26 +00:00
|
|
|
specifier: specifier.clone(),
|
2023-01-28 15:18:32 +00:00
|
|
|
text_info,
|
2024-04-15 21:50:52 +00:00
|
|
|
media_type,
|
2023-01-28 15:18:32 +00:00
|
|
|
capture_tokens: true,
|
|
|
|
scope_analysis: true,
|
|
|
|
maybe_syntax: None,
|
|
|
|
})
|
2022-08-22 16:14:59 +00:00
|
|
|
}
|
|
|
|
|
2023-01-28 15:18:32 +00:00
|
|
|
fn analyze_module(
|
2022-08-22 16:14:59 +00:00
|
|
|
specifier: &ModuleSpecifier,
|
2023-01-28 15:18:32 +00:00
|
|
|
parsed_source_result: &ParsedSourceResult,
|
2022-08-22 16:14:59 +00:00
|
|
|
maybe_headers: Option<&HashMap<String, String>>,
|
2023-02-15 16:30:54 +00:00
|
|
|
resolver: &dyn deno_graph::source::Resolver,
|
2023-10-20 04:02:08 +00:00
|
|
|
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
2023-01-28 15:18:32 +00:00
|
|
|
) -> ModuleResult {
|
|
|
|
match parsed_source_result {
|
2023-02-03 19:10:51 +00:00
|
|
|
Ok(parsed_source) => Ok(deno_graph::parse_module_from_ast(
|
2023-12-01 20:12:10 +00:00
|
|
|
deno_graph::ParseModuleFromAstOptions {
|
2024-04-19 00:51:16 +00:00
|
|
|
graph_kind: deno_graph::GraphKind::TypesOnly,
|
2023-12-01 20:12:10 +00:00
|
|
|
specifier,
|
|
|
|
maybe_headers,
|
|
|
|
parsed_source,
|
|
|
|
// use a null file system because there's no need to bother resolving
|
|
|
|
// dynamic imports like import(`./dir/${something}`) in the LSP
|
|
|
|
file_system: &deno_graph::source::NullFileSystem,
|
|
|
|
maybe_resolver: Some(resolver),
|
|
|
|
maybe_npm_resolver: Some(npm_resolver),
|
|
|
|
},
|
2023-02-03 19:10:51 +00:00
|
|
|
)),
|
2023-03-21 15:46:40 +00:00
|
|
|
Err(err) => Err(deno_graph::ModuleGraphError::ModuleError(
|
|
|
|
deno_graph::ModuleError::ParseErr(specifier.clone(), err.clone()),
|
2022-08-22 16:14:59 +00:00
|
|
|
)),
|
2023-01-28 15:18:32 +00:00
|
|
|
}
|
2022-08-22 16:14:59 +00:00
|
|
|
}
|
|
|
|
|
2021-01-22 10:03:16 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2023-08-02 00:49:09 +00:00
|
|
|
use crate::cache::GlobalHttpCache;
|
2023-08-08 14:23:02 +00:00
|
|
|
use crate::cache::RealDenoCacheEnv;
|
2023-02-22 19:15:25 +00:00
|
|
|
|
2021-01-22 10:03:16 +00:00
|
|
|
use super::*;
|
2024-03-26 15:52:20 +00:00
|
|
|
use deno_config::ConfigFile;
|
2024-03-21 04:29:52 +00:00
|
|
|
use deno_core::serde_json;
|
2024-03-26 15:52:20 +00:00
|
|
|
use deno_core::serde_json::json;
|
2023-03-30 21:47:53 +00:00
|
|
|
use pretty_assertions::assert_eq;
|
2023-06-10 15:09:45 +00:00
|
|
|
use test_util::PathRef;
|
2022-04-01 15:15:37 +00:00
|
|
|
use test_util::TempDir;
|
2021-01-22 10:03:16 +00:00
|
|
|
|
2023-06-10 15:09:45 +00:00
|
|
|
fn setup(temp_dir: &TempDir) -> (Documents, PathRef) {
|
2021-10-28 23:56:01 +00:00
|
|
|
let location = temp_dir.path().join("deps");
|
2023-08-08 14:23:02 +00:00
|
|
|
let cache = Arc::new(GlobalHttpCache::new(
|
|
|
|
location.to_path_buf(),
|
|
|
|
RealDenoCacheEnv,
|
|
|
|
));
|
2023-07-08 20:06:45 +00:00
|
|
|
let documents = Documents::new(cache);
|
2021-10-28 23:56:01 +00:00
|
|
|
(documents, location)
|
2021-08-19 03:19:12 +00:00
|
|
|
}
|
|
|
|
|
2021-01-22 10:03:16 +00:00
|
|
|
#[test]
|
2021-10-28 23:56:01 +00:00
|
|
|
fn test_documents_open() {
|
2022-04-01 15:15:37 +00:00
|
|
|
let temp_dir = TempDir::new();
|
|
|
|
let (mut documents, _) = setup(&temp_dir);
|
2021-10-28 23:56:01 +00:00
|
|
|
let specifier = ModuleSpecifier::parse("file:///a.ts").unwrap();
|
2022-05-20 20:40:55 +00:00
|
|
|
let content = r#"import * as b from "./b.ts";
|
2021-10-28 23:56:01 +00:00
|
|
|
console.log(b);
|
2022-05-20 20:40:55 +00:00
|
|
|
"#;
|
|
|
|
let document = documents.open(
|
|
|
|
specifier,
|
|
|
|
1,
|
|
|
|
"javascript".parse().unwrap(),
|
|
|
|
content.into(),
|
2021-06-02 10:29:58 +00:00
|
|
|
);
|
2021-11-12 16:42:04 +00:00
|
|
|
assert!(document.is_open());
|
|
|
|
assert!(document.is_diagnosable());
|
2021-01-22 10:03:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-10-28 23:56:01 +00:00
|
|
|
fn test_documents_change() {
|
2022-04-01 15:15:37 +00:00
|
|
|
let temp_dir = TempDir::new();
|
|
|
|
let (mut documents, _) = setup(&temp_dir);
|
2021-10-28 23:56:01 +00:00
|
|
|
let specifier = ModuleSpecifier::parse("file:///a.ts").unwrap();
|
2022-05-20 20:40:55 +00:00
|
|
|
let content = r#"import * as b from "./b.ts";
|
2021-10-28 23:56:01 +00:00
|
|
|
console.log(b);
|
2022-05-20 20:40:55 +00:00
|
|
|
"#;
|
2021-10-28 23:56:01 +00:00
|
|
|
documents.open(
|
2021-06-02 10:29:58 +00:00
|
|
|
specifier.clone(),
|
|
|
|
1,
|
2021-10-28 23:56:01 +00:00
|
|
|
"javascript".parse().unwrap(),
|
2022-05-20 20:40:55 +00:00
|
|
|
content.into(),
|
2021-06-02 10:29:58 +00:00
|
|
|
);
|
2021-10-28 23:56:01 +00:00
|
|
|
documents
|
2021-01-22 10:03:16 +00:00
|
|
|
.change(
|
|
|
|
&specifier,
|
|
|
|
2,
|
2021-01-29 19:34:33 +00:00
|
|
|
vec![lsp::TextDocumentContentChangeEvent {
|
|
|
|
range: Some(lsp::Range {
|
|
|
|
start: lsp::Position {
|
2021-10-28 23:56:01 +00:00
|
|
|
line: 1,
|
|
|
|
character: 13,
|
2021-01-22 10:03:16 +00:00
|
|
|
},
|
2021-01-29 19:34:33 +00:00
|
|
|
end: lsp::Position {
|
2021-10-28 23:56:01 +00:00
|
|
|
line: 1,
|
|
|
|
character: 13,
|
2021-01-22 10:03:16 +00:00
|
|
|
},
|
|
|
|
}),
|
2021-10-28 23:56:01 +00:00
|
|
|
range_length: None,
|
|
|
|
text: r#", "hello deno""#.to_string(),
|
2021-01-22 10:03:16 +00:00
|
|
|
}],
|
|
|
|
)
|
2021-10-28 23:56:01 +00:00
|
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
2022-05-20 20:40:55 +00:00
|
|
|
&*documents.get(&specifier).unwrap().content(),
|
2021-10-28 23:56:01 +00:00
|
|
|
r#"import * as b from "./b.ts";
|
|
|
|
console.log(b, "hello deno");
|
|
|
|
"#
|
2021-08-19 03:19:12 +00:00
|
|
|
);
|
2021-06-02 10:29:58 +00:00
|
|
|
}
|
2021-11-18 18:50:24 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_documents_ensure_no_duplicates() {
|
|
|
|
// it should never happen that a user of this API causes this to happen,
|
|
|
|
// but we'll guard against it anyway
|
2022-04-01 15:15:37 +00:00
|
|
|
let temp_dir = TempDir::new();
|
|
|
|
let (mut documents, documents_path) = setup(&temp_dir);
|
2021-11-18 18:50:24 +00:00
|
|
|
let file_path = documents_path.join("file.ts");
|
|
|
|
let file_specifier = ModuleSpecifier::from_file_path(&file_path).unwrap();
|
2023-06-10 15:09:45 +00:00
|
|
|
documents_path.create_dir_all();
|
|
|
|
file_path.write("");
|
2021-11-18 18:50:24 +00:00
|
|
|
|
|
|
|
// open the document
|
|
|
|
documents.open(
|
|
|
|
file_specifier.clone(),
|
|
|
|
1,
|
|
|
|
LanguageId::TypeScript,
|
2022-05-20 20:40:55 +00:00
|
|
|
"".into(),
|
2021-11-18 18:50:24 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
// make a clone of the document store and close the document in that one
|
|
|
|
let mut documents2 = documents.clone();
|
|
|
|
documents2.close(&file_specifier).unwrap();
|
|
|
|
|
|
|
|
// At this point the document will be in both documents and the shared file system documents.
|
|
|
|
// Now make sure that the original documents doesn't return both copies
|
2023-03-29 20:25:48 +00:00
|
|
|
assert_eq!(documents.documents(DocumentsFilter::All).len(), 1);
|
2021-11-18 18:50:24 +00:00
|
|
|
}
|
2023-01-28 15:18:32 +00:00
|
|
|
|
2024-03-26 15:52:20 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn test_documents_refresh_dependencies_config_change() {
|
2023-01-28 15:18:32 +00:00
|
|
|
// it should never happen that a user of this API causes this to happen,
|
|
|
|
// but we'll guard against it anyway
|
|
|
|
let temp_dir = TempDir::new();
|
|
|
|
let (mut documents, documents_path) = setup(&temp_dir);
|
|
|
|
fs::create_dir_all(&documents_path).unwrap();
|
|
|
|
|
|
|
|
let file1_path = documents_path.join("file1.ts");
|
|
|
|
let file1_specifier = ModuleSpecifier::from_file_path(&file1_path).unwrap();
|
|
|
|
fs::write(&file1_path, "").unwrap();
|
|
|
|
|
|
|
|
let file2_path = documents_path.join("file2.ts");
|
|
|
|
let file2_specifier = ModuleSpecifier::from_file_path(&file2_path).unwrap();
|
|
|
|
fs::write(&file2_path, "").unwrap();
|
|
|
|
|
|
|
|
let file3_path = documents_path.join("file3.ts");
|
|
|
|
let file3_specifier = ModuleSpecifier::from_file_path(&file3_path).unwrap();
|
|
|
|
fs::write(&file3_path, "").unwrap();
|
|
|
|
|
2024-03-21 04:29:52 +00:00
|
|
|
let mut config =
|
|
|
|
Config::new_with_roots(vec![ModuleSpecifier::from_directory_path(
|
|
|
|
&documents_path,
|
|
|
|
)
|
|
|
|
.unwrap()]);
|
|
|
|
let workspace_settings =
|
|
|
|
serde_json::from_str(r#"{ "enable": true }"#).unwrap();
|
|
|
|
config.set_workspace_settings(workspace_settings, vec![]);
|
|
|
|
let workspace_files =
|
|
|
|
[&file1_specifier, &file2_specifier, &file3_specifier]
|
|
|
|
.into_iter()
|
|
|
|
.cloned()
|
|
|
|
.collect::<BTreeSet<_>>();
|
|
|
|
|
2023-01-28 15:18:32 +00:00
|
|
|
// set the initial import map and point to file 2
|
|
|
|
{
|
2024-03-26 15:52:20 +00:00
|
|
|
config
|
|
|
|
.tree
|
|
|
|
.inject_config_file(
|
|
|
|
ConfigFile::new(
|
|
|
|
&json!({
|
|
|
|
"imports": {
|
|
|
|
"test": "./file2.ts",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
.to_string(),
|
|
|
|
config.root_uri().unwrap().join("deno.json").unwrap(),
|
2024-03-27 21:14:27 +00:00
|
|
|
&deno_config::ParseOptions::default(),
|
2024-03-26 15:52:20 +00:00
|
|
|
)
|
2023-01-28 15:18:32 +00:00
|
|
|
.unwrap(),
|
2024-03-26 15:52:20 +00:00
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
documents.update_config(&config, None, None, &workspace_files);
|
2023-01-28 15:18:32 +00:00
|
|
|
|
|
|
|
// open the document
|
|
|
|
let document = documents.open(
|
|
|
|
file1_specifier.clone(),
|
|
|
|
1,
|
|
|
|
LanguageId::TypeScript,
|
|
|
|
"import {} from 'test';".into(),
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
document
|
|
|
|
.dependencies()
|
|
|
|
.get("test")
|
|
|
|
.unwrap()
|
|
|
|
.maybe_code
|
|
|
|
.maybe_specifier()
|
|
|
|
.map(ToOwned::to_owned),
|
|
|
|
Some(file2_specifier),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// now point at file 3
|
|
|
|
{
|
2024-03-26 15:52:20 +00:00
|
|
|
config
|
|
|
|
.tree
|
|
|
|
.inject_config_file(
|
|
|
|
ConfigFile::new(
|
|
|
|
&json!({
|
|
|
|
"imports": {
|
|
|
|
"test": "./file3.ts",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
.to_string(),
|
|
|
|
config.root_uri().unwrap().join("deno.json").unwrap(),
|
2024-03-27 21:14:27 +00:00
|
|
|
&deno_config::ParseOptions::default(),
|
2024-03-26 15:52:20 +00:00
|
|
|
)
|
2023-01-28 15:18:32 +00:00
|
|
|
.unwrap(),
|
2024-03-26 15:52:20 +00:00
|
|
|
)
|
|
|
|
.await;
|
|
|
|
|
|
|
|
documents.update_config(&config, None, None, &workspace_files);
|
2023-01-28 15:18:32 +00:00
|
|
|
|
|
|
|
// check the document's dependencies
|
|
|
|
let document = documents.get(&file1_specifier).unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
document
|
|
|
|
.dependencies()
|
|
|
|
.get("test")
|
|
|
|
.unwrap()
|
|
|
|
.maybe_code
|
|
|
|
.maybe_specifier()
|
|
|
|
.map(ToOwned::to_owned),
|
|
|
|
Some(file3_specifier),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2021-01-22 10:03:16 +00:00
|
|
|
}
|