mirror of
https://github.com/denoland/deno.git
synced 2024-12-24 16:19:12 -05:00
feat(unstable/npm): initial type checking of npm specifiers (#16332)
This commit is contained in:
parent
0e1a71fec6
commit
bcfe279fba
64 changed files with 2135 additions and 280 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4752,6 +4752,7 @@ dependencies = [
|
|||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-tungstenite",
|
||||
"url",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
|
|
|
@ -201,6 +201,11 @@ fn create_compiler_snapshot(
|
|||
false
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_is_node_file() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_script_version(
|
||||
_state: &mut OpState,
|
||||
|
@ -266,6 +271,7 @@ fn create_compiler_snapshot(
|
|||
op_build_info::decl(),
|
||||
op_cwd::decl(),
|
||||
op_exists::decl(),
|
||||
op_is_node_file::decl(),
|
||||
op_load::decl(),
|
||||
op_script_version::decl(),
|
||||
])
|
||||
|
|
18
cli/dts/README.md
vendored
18
cli/dts/README.md
vendored
|
@ -4,16 +4,26 @@ The files in this directory are mostly from the TypeScript repository. We
|
|||
currently (unfortunately) have a rather manual process for upgrading TypeScript.
|
||||
It works like this currently:
|
||||
|
||||
1. Checkout typescript repo in a separate directory.
|
||||
2. Copy typescript.js into Deno repo.
|
||||
3. Copy d.ts files into dts directory.
|
||||
1. Checkout denoland/TypeScript repo in a separate directory.
|
||||
1. Add Microsoft/TypeScript as a remote and fetch its latest tags
|
||||
1. Checkout a new branch based on this tag.
|
||||
1. Cherry pick the custom commit we made in a previous release to the new one.
|
||||
1. This commit has a "deno.ts" file in it. Read the instructions in it.
|
||||
1. Copy typescript.js into Deno repo.
|
||||
1. Copy d.ts files into dts directory.
|
||||
|
||||
So that might look something like this:
|
||||
|
||||
```
|
||||
git clone https://github.com/microsoft/TypeScript.git
|
||||
git clone https://github.com/denoland/TypeScript.git
|
||||
cd typescript
|
||||
git remote add upstream https://github.com/Microsoft/TypeScript
|
||||
git fetch upstream
|
||||
git checkout v3.9.7
|
||||
git checkout -b branch_v3.9.7
|
||||
git cherry pick <previous-release-branch-commit-we-did>
|
||||
npm install
|
||||
gulp local
|
||||
rsync lib/typescript.js ~/src/deno/cli/tsc/00_typescript.js
|
||||
rsync --exclude=protocol.d.ts --exclude=tsserverlibrary.d.ts --exclude=typescriptServices.d.ts lib/*.d.ts ~/src/deno/cli/dts/
|
||||
```
|
||||
|
|
|
@ -458,6 +458,13 @@ async fn generate_lint_diagnostics(
|
|||
break;
|
||||
}
|
||||
|
||||
// ignore any npm package files
|
||||
if let Some(npm_resolver) = &snapshot.maybe_npm_resolver {
|
||||
if npm_resolver.in_npm_package(document.specifier()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let version = document.maybe_lsp_version();
|
||||
diagnostics_vec.push((
|
||||
document.specifier().clone(),
|
||||
|
@ -597,6 +604,8 @@ pub enum DenoDiagnostic {
|
|||
NoCacheBlob,
|
||||
/// A data module was not found in the cache.
|
||||
NoCacheData(ModuleSpecifier),
|
||||
/// A remote npm package reference was not found in the cache.
|
||||
NoCacheNpm(NpmPackageReference, ModuleSpecifier),
|
||||
/// A local module was not found on the local file system.
|
||||
NoLocal(ModuleSpecifier),
|
||||
/// The specifier resolved to a remote specifier that was redirected to
|
||||
|
@ -622,6 +631,7 @@ impl DenoDiagnostic {
|
|||
Self::NoCache(_) => "no-cache",
|
||||
Self::NoCacheBlob => "no-cache-blob",
|
||||
Self::NoCacheData(_) => "no-cache-data",
|
||||
Self::NoCacheNpm(_, _) => "no-cache-npm",
|
||||
Self::NoLocal(_) => "no-local",
|
||||
Self::Redirect { .. } => "redirect",
|
||||
Self::ResolutionError(err) => match err {
|
||||
|
@ -690,16 +700,17 @@ impl DenoDiagnostic {
|
|||
}),
|
||||
..Default::default()
|
||||
},
|
||||
"no-cache" | "no-cache-data" => {
|
||||
"no-cache" | "no-cache-data" | "no-cache-npm" => {
|
||||
let data = diagnostic
|
||||
.data
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("Diagnostic is missing data"))?;
|
||||
let data: DiagnosticDataSpecifier = serde_json::from_value(data)?;
|
||||
let title = if code == "no-cache" {
|
||||
format!("Cache \"{}\" and its dependencies.", data.specifier)
|
||||
} else {
|
||||
"Cache the data URL and its dependencies.".to_string()
|
||||
let title = match code.as_str() {
|
||||
"no-cache" | "no-cache-npm" => {
|
||||
format!("Cache \"{}\" and its dependencies.", data.specifier)
|
||||
}
|
||||
_ => "Cache the data URL and its dependencies.".to_string(),
|
||||
};
|
||||
lsp::CodeAction {
|
||||
title,
|
||||
|
@ -757,6 +768,7 @@ impl DenoDiagnostic {
|
|||
code.as_str(),
|
||||
"import-map-remap"
|
||||
| "no-cache"
|
||||
| "no-cache-npm"
|
||||
| "no-cache-data"
|
||||
| "no-assert-type"
|
||||
| "redirect"
|
||||
|
@ -777,6 +789,7 @@ impl DenoDiagnostic {
|
|||
Self::NoCache(specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Uncached or missing remote URL: \"{}\".", specifier), Some(json!({ "specifier": specifier }))),
|
||||
Self::NoCacheBlob => (lsp::DiagnosticSeverity::ERROR, "Uncached blob URL.".to_string(), None),
|
||||
Self::NoCacheData(specifier) => (lsp::DiagnosticSeverity::ERROR, "Uncached data URL.".to_string(), Some(json!({ "specifier": specifier }))),
|
||||
Self::NoCacheNpm(pkg_ref, specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Uncached or missing npm package: \"{}\".", pkg_ref.req), Some(json!({ "specifier": specifier }))),
|
||||
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),
|
||||
|
@ -847,8 +860,20 @@ fn diagnose_resolved(
|
|||
.push(DenoDiagnostic::NoAssertType.to_lsp_diagnostic(&range)),
|
||||
}
|
||||
}
|
||||
} else if NpmPackageReference::from_specifier(specifier).is_ok() {
|
||||
// ignore npm specifiers for now
|
||||
} else if let Ok(pkg_ref) = NpmPackageReference::from_specifier(specifier)
|
||||
{
|
||||
if let Some(npm_resolver) = &snapshot.maybe_npm_resolver {
|
||||
// show diagnostics for npm package references that aren't cached
|
||||
if npm_resolver
|
||||
.resolve_package_folder_from_deno_module(&pkg_ref.req)
|
||||
.is_err()
|
||||
{
|
||||
diagnostics.push(
|
||||
DenoDiagnostic::NoCacheNpm(pkg_ref, specifier.clone())
|
||||
.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
|
||||
|
@ -882,6 +907,12 @@ fn diagnose_dependency(
|
|||
dependency_key: &str,
|
||||
dependency: &deno_graph::Dependency,
|
||||
) {
|
||||
if let Some(npm_resolver) = &snapshot.maybe_npm_resolver {
|
||||
if npm_resolver.in_npm_package(referrer) {
|
||||
return; // ignore, surface typescript errors instead
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(import_map) = &snapshot.maybe_import_map {
|
||||
if let Resolved::Ok {
|
||||
specifier, range, ..
|
||||
|
@ -938,8 +969,8 @@ async fn generate_deno_diagnostics(
|
|||
&mut diagnostics,
|
||||
snapshot,
|
||||
specifier,
|
||||
&dependency_key,
|
||||
&dependency,
|
||||
dependency_key,
|
||||
dependency,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,13 @@ use crate::file_fetcher::SUPPORTED_SCHEMES;
|
|||
use crate::fs_util::specifier_to_file_path;
|
||||
use crate::http_cache;
|
||||
use crate::http_cache::HttpCache;
|
||||
use crate::node;
|
||||
use crate::node::node_resolve_npm_reference;
|
||||
use crate::node::NodeResolution;
|
||||
use crate::node::NodeResolutionMode;
|
||||
use crate::npm::NpmPackageReference;
|
||||
use crate::npm::NpmPackageReq;
|
||||
use crate::npm::NpmPackageResolver;
|
||||
use crate::resolver::ImportMapResolver;
|
||||
use crate::resolver::JsxResolver;
|
||||
use crate::text_encoding;
|
||||
|
@ -209,6 +216,29 @@ impl AssetOrDocument {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct DocumentDependencies {
|
||||
deps: BTreeMap<String, deno_graph::Dependency>,
|
||||
maybe_types_dependency: Option<(String, Resolved)>,
|
||||
}
|
||||
|
||||
impl DocumentDependencies {
|
||||
pub fn from_maybe_module(maybe_module: &MaybeModuleResult) -> Self {
|
||||
if let Some(Ok(module)) = &maybe_module {
|
||||
Self::from_module(module)
|
||||
} else {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_module(module: &deno_graph::Module) -> Self {
|
||||
Self {
|
||||
deps: module.dependencies.clone(),
|
||||
maybe_types_dependency: module.maybe_types_dependency.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type MaybeModuleResult =
|
||||
Option<Result<deno_graph::Module, deno_graph::ModuleGraphError>>;
|
||||
type MaybeParsedSourceResult =
|
||||
|
@ -217,7 +247,7 @@ type MaybeParsedSourceResult =
|
|||
#[derive(Debug, Clone)]
|
||||
struct DocumentInner {
|
||||
/// contains the last-known-good set of dependencies from parsing the module
|
||||
dependencies: Arc<BTreeMap<String, deno_graph::Dependency>>,
|
||||
dependencies: Arc<DocumentDependencies>,
|
||||
fs_version: String,
|
||||
line_index: Arc<LineIndex>,
|
||||
maybe_language_id: Option<LanguageId>,
|
||||
|
@ -249,12 +279,9 @@ impl Document {
|
|||
maybe_headers,
|
||||
maybe_resolver,
|
||||
);
|
||||
let dependencies = if let Some(Ok(module)) = &maybe_module {
|
||||
Arc::new(module.dependencies.clone())
|
||||
} else {
|
||||
Arc::new(BTreeMap::new())
|
||||
};
|
||||
// todo(dsherret): retrieve this from the parsed source if it
|
||||
let dependencies =
|
||||
Arc::new(DocumentDependencies::from_maybe_module(&maybe_module));
|
||||
// todo(dsherret): retrieve this from the parsed source if it exists
|
||||
let text_info = SourceTextInfo::new(content);
|
||||
let line_index = Arc::new(LineIndex::new(text_info.text_str()));
|
||||
Self(Arc::new(DocumentInner {
|
||||
|
@ -289,11 +316,8 @@ impl Document {
|
|||
} else {
|
||||
(None, None)
|
||||
};
|
||||
let dependencies = if let Some(Ok(module)) = &maybe_module {
|
||||
Arc::new(module.dependencies.clone())
|
||||
} else {
|
||||
Arc::new(BTreeMap::new())
|
||||
};
|
||||
let dependencies =
|
||||
Arc::new(DocumentDependencies::from_maybe_module(&maybe_module));
|
||||
let source = SourceTextInfo::new(content);
|
||||
let line_index = Arc::new(LineIndex::new(source.text_str()));
|
||||
Self(Arc::new(DocumentInner {
|
||||
|
@ -355,9 +379,9 @@ impl Document {
|
|||
(None, None)
|
||||
};
|
||||
let dependencies = if let Some(Ok(module)) = &maybe_module {
|
||||
Arc::new(module.dependencies.clone())
|
||||
Arc::new(DocumentDependencies::from_module(module))
|
||||
} else {
|
||||
self.0.dependencies.clone()
|
||||
self.0.dependencies.clone() // use the last known good
|
||||
};
|
||||
let text_info = SourceTextInfo::new(content);
|
||||
let line_index = if index_valid == IndexValid::All {
|
||||
|
@ -435,15 +459,9 @@ impl Document {
|
|||
}
|
||||
|
||||
pub fn maybe_types_dependency(&self) -> deno_graph::Resolved {
|
||||
let module_result = match self.0.maybe_module.as_ref() {
|
||||
Some(module_result) => module_result,
|
||||
_ => return deno_graph::Resolved::None,
|
||||
};
|
||||
let module = match module_result.as_ref() {
|
||||
Ok(module) => module,
|
||||
Err(_) => return deno_graph::Resolved::None,
|
||||
};
|
||||
if let Some((_, maybe_dep)) = module.maybe_types_dependency.as_ref() {
|
||||
if let Some((_, maybe_dep)) =
|
||||
self.0.dependencies.maybe_types_dependency.as_ref()
|
||||
{
|
||||
maybe_dep.clone()
|
||||
} else {
|
||||
deno_graph::Resolved::None
|
||||
|
@ -479,13 +497,8 @@ impl Document {
|
|||
self.0.maybe_navigation_tree.clone()
|
||||
}
|
||||
|
||||
pub fn dependencies(&self) -> Vec<(String, deno_graph::Dependency)> {
|
||||
self
|
||||
.0
|
||||
.dependencies
|
||||
.iter()
|
||||
.map(|(s, d)| (s.clone(), d.clone()))
|
||||
.collect()
|
||||
pub fn dependencies(&self) -> &BTreeMap<String, deno_graph::Dependency> {
|
||||
&self.0.dependencies.deps
|
||||
}
|
||||
|
||||
/// If the supplied position is within a dependency range, return the resolved
|
||||
|
@ -698,6 +711,8 @@ pub struct Documents {
|
|||
maybe_import_map: Option<ImportMapResolver>,
|
||||
/// The optional JSX resolver, which is used when JSX imports are configured.
|
||||
maybe_jsx_resolver: Option<JsxResolver>,
|
||||
/// The npm package requirements.
|
||||
npm_reqs: HashSet<NpmPackageReq>,
|
||||
/// Resolves a specifier to its final redirected to specifier.
|
||||
specifier_resolver: Arc<SpecifierResolver>,
|
||||
}
|
||||
|
@ -713,6 +728,7 @@ impl Documents {
|
|||
imports: Default::default(),
|
||||
maybe_import_map: None,
|
||||
maybe_jsx_resolver: None,
|
||||
npm_reqs: HashSet::new(),
|
||||
specifier_resolver: Arc::new(SpecifierResolver::new(location)),
|
||||
}
|
||||
}
|
||||
|
@ -847,6 +863,12 @@ impl Documents {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns a collection of npm package requirements.
|
||||
pub fn npm_package_reqs(&mut self) -> HashSet<NpmPackageReq> {
|
||||
self.calculate_dependents_if_dirty();
|
||||
self.npm_reqs.clone()
|
||||
}
|
||||
|
||||
/// Return a document for the specifier.
|
||||
pub fn get(&self, original_specifier: &ModuleSpecifier) -> Option<Document> {
|
||||
let specifier = self.specifier_resolver.resolve(original_specifier)?;
|
||||
|
@ -921,10 +943,28 @@ impl Documents {
|
|||
&self,
|
||||
specifiers: Vec<String>,
|
||||
referrer: &ModuleSpecifier,
|
||||
maybe_npm_resolver: Option<&NpmPackageResolver>,
|
||||
) -> Option<Vec<Option<(ModuleSpecifier, MediaType)>>> {
|
||||
let dependencies = self.get(referrer)?.0.dependencies.clone();
|
||||
let mut results = Vec::new();
|
||||
for specifier in specifiers {
|
||||
if let Some(npm_resolver) = maybe_npm_resolver {
|
||||
if npm_resolver.in_npm_package(referrer) {
|
||||
// we're in an npm package, so use node resolution
|
||||
results.push(Some(NodeResolution::into_specifier_and_media_type(
|
||||
node::node_resolve(
|
||||
&specifier,
|
||||
referrer,
|
||||
node::NodeResolutionMode::Types,
|
||||
npm_resolver,
|
||||
)
|
||||
.ok()
|
||||
.flatten(),
|
||||
)));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// handle npm:<package> urls
|
||||
if specifier.starts_with("asset:") {
|
||||
if let Ok(specifier) = ModuleSpecifier::parse(&specifier) {
|
||||
let media_type = MediaType::from(&specifier);
|
||||
|
@ -932,11 +972,11 @@ impl Documents {
|
|||
} else {
|
||||
results.push(None);
|
||||
}
|
||||
} else if let Some(dep) = dependencies.get(&specifier) {
|
||||
} else if let Some(dep) = dependencies.deps.get(&specifier) {
|
||||
if let Resolved::Ok { specifier, .. } = &dep.maybe_type {
|
||||
results.push(self.resolve_dependency(specifier));
|
||||
results.push(self.resolve_dependency(specifier, maybe_npm_resolver));
|
||||
} else if let Resolved::Ok { specifier, .. } = &dep.maybe_code {
|
||||
results.push(self.resolve_dependency(specifier));
|
||||
results.push(self.resolve_dependency(specifier, maybe_npm_resolver));
|
||||
} else {
|
||||
results.push(None);
|
||||
}
|
||||
|
@ -945,7 +985,19 @@ impl Documents {
|
|||
{
|
||||
// clone here to avoid double borrow of self
|
||||
let specifier = specifier.clone();
|
||||
results.push(self.resolve_dependency(&specifier));
|
||||
results.push(self.resolve_dependency(&specifier, maybe_npm_resolver));
|
||||
} 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(
|
||||
&npm_ref,
|
||||
NodeResolutionMode::Types,
|
||||
npm_resolver,
|
||||
)
|
||||
.ok()
|
||||
.flatten(),
|
||||
)
|
||||
}));
|
||||
} else {
|
||||
results.push(None);
|
||||
}
|
||||
|
@ -1038,32 +1090,36 @@ impl Documents {
|
|||
// favour documents that are open in case a document exists in both collections
|
||||
let documents = file_system_docs.docs.iter().chain(self.open_docs.iter());
|
||||
for (specifier, doc) in documents {
|
||||
if let Some(Ok(module)) = doc.maybe_module() {
|
||||
for dependency in module.dependencies.values() {
|
||||
if let Some(dep) = dependency.get_code() {
|
||||
dependents_map
|
||||
.entry(dep.clone())
|
||||
.or_default()
|
||||
.insert(specifier.clone());
|
||||
}
|
||||
if let Some(dep) = dependency.get_type() {
|
||||
dependents_map
|
||||
.entry(dep.clone())
|
||||
.or_default()
|
||||
.insert(specifier.clone());
|
||||
}
|
||||
for dependency in doc.dependencies().values() {
|
||||
if let Some(dep) = dependency.get_code() {
|
||||
dependents_map
|
||||
.entry(dep.clone())
|
||||
.or_default()
|
||||
.insert(specifier.clone());
|
||||
}
|
||||
if let Some((_, Resolved::Ok { specifier: dep, .. })) =
|
||||
&module.maybe_types_dependency
|
||||
{
|
||||
if let Some(dep) = dependency.get_type() {
|
||||
dependents_map
|
||||
.entry(dep.clone())
|
||||
.or_default()
|
||||
.insert(specifier.clone());
|
||||
}
|
||||
}
|
||||
if let Resolved::Ok { specifier: dep, .. } = doc.maybe_types_dependency()
|
||||
{
|
||||
dependents_map
|
||||
.entry(dep.clone())
|
||||
.or_default()
|
||||
.insert(specifier.clone());
|
||||
}
|
||||
}
|
||||
let mut npm_reqs = HashSet::new();
|
||||
for specifier in dependents_map.keys() {
|
||||
if let Ok(reference) = NpmPackageReference::from_specifier(specifier) {
|
||||
npm_reqs.insert(reference.req);
|
||||
}
|
||||
}
|
||||
self.dependents_map = Arc::new(dependents_map);
|
||||
self.npm_reqs = npm_reqs;
|
||||
self.dirty = false;
|
||||
file_system_docs.dirty = false;
|
||||
}
|
||||
|
@ -1079,7 +1135,21 @@ impl Documents {
|
|||
fn resolve_dependency(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_npm_resolver: Option<&NpmPackageResolver>,
|
||||
) -> Option<(ModuleSpecifier, MediaType)> {
|
||||
if let Ok(npm_ref) = NpmPackageReference::from_specifier(specifier) {
|
||||
return maybe_npm_resolver.map(|npm_resolver| {
|
||||
NodeResolution::into_specifier_and_media_type(
|
||||
node_resolve_npm_reference(
|
||||
&npm_ref,
|
||||
NodeResolutionMode::Types,
|
||||
npm_resolver,
|
||||
)
|
||||
.ok()
|
||||
.flatten(),
|
||||
)
|
||||
});
|
||||
}
|
||||
let doc = self.get(specifier)?;
|
||||
let maybe_module = doc.maybe_module().and_then(|r| r.as_ref().ok());
|
||||
let maybe_types_dependency = maybe_module.and_then(|m| {
|
||||
|
@ -1088,7 +1158,7 @@ impl Documents {
|
|||
.map(|(_, resolved)| resolved.clone())
|
||||
});
|
||||
if let Some(Resolved::Ok { specifier, .. }) = maybe_types_dependency {
|
||||
self.resolve_dependency(&specifier)
|
||||
self.resolve_dependency(&specifier, maybe_npm_resolver)
|
||||
} else {
|
||||
let media_type = doc.media_type();
|
||||
Some((specifier.clone(), media_type))
|
||||
|
@ -1113,12 +1183,12 @@ impl Documents {
|
|||
}
|
||||
|
||||
/// Loader that will look at the open documents.
|
||||
pub struct DocumentsDenoGraphLoader<'a> {
|
||||
pub struct OpenDocumentsGraphLoader<'a> {
|
||||
pub inner_loader: &'a mut dyn deno_graph::source::Loader,
|
||||
pub open_docs: &'a HashMap<ModuleSpecifier, Document>,
|
||||
}
|
||||
|
||||
impl<'a> deno_graph::source::Loader for DocumentsDenoGraphLoader<'a> {
|
||||
impl<'a> deno_graph::source::Loader for OpenDocumentsGraphLoader<'a> {
|
||||
fn load(
|
||||
&mut self,
|
||||
specifier: &ModuleSpecifier,
|
||||
|
|
|
@ -66,10 +66,15 @@ use crate::args::LintConfig;
|
|||
use crate::args::TsConfig;
|
||||
use crate::deno_dir;
|
||||
use crate::file_fetcher::get_source_from_data_url;
|
||||
use crate::file_fetcher::CacheSetting;
|
||||
use crate::fs_util;
|
||||
use crate::graph_util::graph_valid;
|
||||
use crate::npm::NpmCache;
|
||||
use crate::npm::NpmPackageResolver;
|
||||
use crate::npm::NpmRegistryApi;
|
||||
use crate::proc_state::import_map_from_text;
|
||||
use crate::proc_state::ProcState;
|
||||
use crate::progress_bar::ProgressBar;
|
||||
use crate::tools::fmt::format_file;
|
||||
use crate::tools::fmt::format_parsed_source;
|
||||
|
||||
|
@ -87,6 +92,7 @@ pub struct StateSnapshot {
|
|||
pub documents: Documents,
|
||||
pub maybe_import_map: Option<Arc<ImportMap>>,
|
||||
pub root_uri: Option<Url>,
|
||||
pub maybe_npm_resolver: Option<NpmPackageResolver>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -125,6 +131,8 @@ pub struct Inner {
|
|||
pub maybe_lint_config: Option<LintConfig>,
|
||||
/// A lazily create "server" for handling test run requests.
|
||||
maybe_testing_server: Option<testing::TestServer>,
|
||||
/// Resolver for npm packages.
|
||||
npm_resolver: NpmPackageResolver,
|
||||
/// A collection of measurements which instrument that performance of the LSP.
|
||||
performance: Arc<Performance>,
|
||||
/// A memoized version of fixable diagnostic codes retrieved from TypeScript.
|
||||
|
@ -250,6 +258,26 @@ impl Inner {
|
|||
ts_server.clone(),
|
||||
);
|
||||
let assets = Assets::new(ts_server.clone());
|
||||
let registry_url = NpmRegistryApi::default_url();
|
||||
// Use an "only" cache setting in order to make the
|
||||
// user do an explicit "cache" command and prevent
|
||||
// the cache from being filled with lots of packages while
|
||||
// the user is typing.
|
||||
let cache_setting = CacheSetting::Only;
|
||||
let progress_bar = ProgressBar::default();
|
||||
let npm_cache = NpmCache::from_deno_dir(
|
||||
&dir,
|
||||
cache_setting.clone(),
|
||||
progress_bar.clone(),
|
||||
);
|
||||
let api = NpmRegistryApi::new(
|
||||
registry_url,
|
||||
npm_cache.clone(),
|
||||
cache_setting,
|
||||
progress_bar,
|
||||
);
|
||||
let npm_resolver =
|
||||
NpmPackageResolver::new(npm_cache, api, true, false, None);
|
||||
|
||||
Self {
|
||||
assets,
|
||||
|
@ -267,6 +295,7 @@ impl Inner {
|
|||
maybe_testing_server: None,
|
||||
module_registries,
|
||||
module_registries_location,
|
||||
npm_resolver,
|
||||
performance,
|
||||
ts_fixable_diagnostics: Default::default(),
|
||||
ts_server,
|
||||
|
@ -435,6 +464,7 @@ impl Inner {
|
|||
cache_metadata: self.cache_metadata.clone(),
|
||||
documents: self.documents.clone(),
|
||||
maybe_import_map: self.maybe_import_map.clone(),
|
||||
maybe_npm_resolver: Some(self.npm_resolver.snapshotted()),
|
||||
root_uri: self.config.root_uri.clone(),
|
||||
})
|
||||
}
|
||||
|
@ -828,7 +858,7 @@ impl Inner {
|
|||
if let Err(err) =
|
||||
self.client.register_capability(vec![registration]).await
|
||||
{
|
||||
warn!("Client errored on capabilities.\n{}", err);
|
||||
warn!("Client errored on capabilities.\n{:#}", err);
|
||||
}
|
||||
}
|
||||
self.config.update_enabled_paths(self.client.clone()).await;
|
||||
|
@ -891,6 +921,7 @@ impl Inner {
|
|||
) {
|
||||
Ok(document) => {
|
||||
if document.is_diagnosable() {
|
||||
self.refresh_npm_specifiers().await;
|
||||
self
|
||||
.diagnostics_server
|
||||
.invalidate(&self.documents.dependents(&specifier));
|
||||
|
@ -903,6 +934,13 @@ impl Inner {
|
|||
self.performance.measure(mark);
|
||||
}
|
||||
|
||||
async fn refresh_npm_specifiers(&mut self) {
|
||||
let package_reqs = self.documents.npm_package_reqs();
|
||||
if let Err(err) = self.npm_resolver.set_package_reqs(package_reqs).await {
|
||||
warn!("Could not set npm package requirements. {:#}", err);
|
||||
}
|
||||
}
|
||||
|
||||
async fn did_close(&mut self, params: DidCloseTextDocumentParams) {
|
||||
let mark = self.performance.mark("did_close", Some(¶ms));
|
||||
if params.text_document.uri.scheme() == "deno" {
|
||||
|
@ -917,6 +955,7 @@ impl Inner {
|
|||
error!("{}", err);
|
||||
}
|
||||
if self.is_diagnosable(&specifier) {
|
||||
self.refresh_npm_specifiers().await;
|
||||
let mut specifiers = self.documents.dependents(&specifier);
|
||||
specifiers.push(specifier.clone());
|
||||
self.diagnostics_server.invalidate(&specifiers);
|
||||
|
@ -1135,7 +1174,7 @@ impl Inner {
|
|||
Ok(None) => Some(Vec::new()),
|
||||
Err(err) => {
|
||||
// TODO(lucacasonato): handle error properly
|
||||
warn!("Format error: {}", err);
|
||||
warn!("Format error: {:#}", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -2476,6 +2515,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
|
|||
let has_specifier_settings =
|
||||
inner.config.has_specifier_settings(&specifier);
|
||||
if document.is_diagnosable() {
|
||||
inner.refresh_npm_specifiers().await;
|
||||
let specifiers = inner.documents.dependents(&specifier);
|
||||
inner.diagnostics_server.invalidate(&specifiers);
|
||||
// don't send diagnostics yet if we don't have the specifier settings
|
||||
|
@ -2834,7 +2874,7 @@ impl Inner {
|
|||
.collect::<HashMap<_, _>>();
|
||||
let ps = ProcState::from_options(Arc::new(cli_options)).await?;
|
||||
let mut inner_loader = ps.create_graph_loader();
|
||||
let mut loader = crate::lsp::documents::DocumentsDenoGraphLoader {
|
||||
let mut loader = crate::lsp::documents::OpenDocumentsGraphLoader {
|
||||
inner_loader: &mut inner_loader,
|
||||
open_docs: &open_docs,
|
||||
};
|
||||
|
@ -2870,6 +2910,9 @@ impl Inner {
|
|||
ca_stores: None,
|
||||
ca_file: None,
|
||||
unsafely_ignore_certificate_errors: None,
|
||||
// this is to allow loading npm specifiers, so we can remove this
|
||||
// once stabilizing them
|
||||
unstable: true,
|
||||
..Default::default()
|
||||
},
|
||||
self.maybe_config_file.clone(),
|
||||
|
@ -2892,6 +2935,7 @@ impl Inner {
|
|||
// For that we're invalidating all the existing diagnostics and restarting
|
||||
// the language server for TypeScript (as it might hold to some stale
|
||||
// documents).
|
||||
self.refresh_npm_specifiers().await;
|
||||
self.diagnostics_server.invalidate_all();
|
||||
let _: bool = self
|
||||
.ts_server
|
||||
|
|
|
@ -2678,6 +2678,20 @@ fn op_is_cancelled(state: &mut OpState) -> bool {
|
|||
state.token.is_cancelled()
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_is_node_file(state: &mut OpState, path: String) -> bool {
|
||||
let state = state.borrow::<State>();
|
||||
match ModuleSpecifier::parse(&path) {
|
||||
Ok(specifier) => state
|
||||
.state_snapshot
|
||||
.maybe_npm_resolver
|
||||
.as_ref()
|
||||
.map(|r| r.in_npm_package(&specifier))
|
||||
.unwrap_or(false),
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_load(
|
||||
state: &mut OpState,
|
||||
|
@ -2692,7 +2706,7 @@ fn op_load(
|
|||
Some(doc) => {
|
||||
json!({
|
||||
"data": doc.text(),
|
||||
"scriptKind": crate::tsc::as_ts_script_kind(&doc.media_type()),
|
||||
"scriptKind": crate::tsc::as_ts_script_kind(doc.media_type()),
|
||||
"version": state.script_version(&specifier),
|
||||
})
|
||||
}
|
||||
|
@ -2709,11 +2723,11 @@ fn op_resolve(
|
|||
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)
|
||||
{
|
||||
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()
|
||||
|
@ -2789,6 +2803,7 @@ fn init_extension(performance: Arc<Performance>) -> Extension {
|
|||
.ops(vec![
|
||||
op_exists::decl(),
|
||||
op_is_cancelled::decl(),
|
||||
op_is_node_file::decl(),
|
||||
op_load::decl(),
|
||||
op_resolve::decl(),
|
||||
op_respond::decl(),
|
||||
|
|
|
@ -525,6 +525,7 @@ async fn create_graph_and_maybe_check(
|
|||
&graph.roots,
|
||||
Arc::new(RwLock::new(graph.as_ref().into())),
|
||||
&cache,
|
||||
ps.npm_resolver.clone(),
|
||||
check::CheckOptions {
|
||||
type_check_mode: ps.options.type_check_mode(),
|
||||
debug,
|
||||
|
|
180
cli/node/mod.rs
180
cli/node/mod.rs
|
@ -31,6 +31,7 @@ use deno_runtime::deno_node::PathClean;
|
|||
use deno_runtime::deno_node::RequireNpmResolver;
|
||||
use deno_runtime::deno_node::DEFAULT_CONDITIONS;
|
||||
use deno_runtime::deno_node::NODE_GLOBAL_THIS_NAME;
|
||||
use deno_runtime::deno_node::TYPES_CONDITIONS;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
|
||||
|
@ -55,9 +56,61 @@ impl NodeResolution {
|
|||
match self {
|
||||
Self::Esm(u) => u,
|
||||
Self::CommonJs(u) => u,
|
||||
_ => unreachable!(),
|
||||
Self::BuiltIn(specifier) => {
|
||||
if specifier.starts_with("node:") {
|
||||
ModuleSpecifier::parse(&specifier).unwrap()
|
||||
} else {
|
||||
ModuleSpecifier::parse(&format!("node:{}", specifier)).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_specifier_and_media_type(
|
||||
resolution: Option<Self>,
|
||||
) -> (ModuleSpecifier, MediaType) {
|
||||
match resolution {
|
||||
Some(NodeResolution::CommonJs(specifier)) => {
|
||||
let media_type = MediaType::from(&specifier);
|
||||
(
|
||||
specifier,
|
||||
match media_type {
|
||||
MediaType::JavaScript | MediaType::Jsx => MediaType::Cjs,
|
||||
MediaType::TypeScript | MediaType::Tsx => MediaType::Cts,
|
||||
MediaType::Dts => MediaType::Dcts,
|
||||
_ => media_type,
|
||||
},
|
||||
)
|
||||
}
|
||||
Some(NodeResolution::Esm(specifier)) => {
|
||||
let media_type = MediaType::from(&specifier);
|
||||
(
|
||||
specifier,
|
||||
match media_type {
|
||||
MediaType::JavaScript | MediaType::Jsx => MediaType::Mjs,
|
||||
MediaType::TypeScript | MediaType::Tsx => MediaType::Mts,
|
||||
MediaType::Dts => MediaType::Dmts,
|
||||
_ => media_type,
|
||||
},
|
||||
)
|
||||
}
|
||||
maybe_response => {
|
||||
let specifier = match maybe_response {
|
||||
Some(response) => response.into_url(),
|
||||
None => {
|
||||
ModuleSpecifier::parse("deno:///missing_dependency.d.ts").unwrap()
|
||||
}
|
||||
};
|
||||
(specifier, MediaType::Dts)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum NodeResolutionMode {
|
||||
Execution,
|
||||
Types,
|
||||
}
|
||||
|
||||
struct NodeModulePolyfill {
|
||||
|
@ -389,6 +442,7 @@ pub async fn initialize_binary_command(
|
|||
pub fn node_resolve(
|
||||
specifier: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
mode: NodeResolutionMode,
|
||||
npm_resolver: &dyn RequireNpmResolver,
|
||||
) -> Result<Option<NodeResolution>, AnyError> {
|
||||
// Note: if we are here, then the referrer is an esm module
|
||||
|
@ -425,12 +479,22 @@ pub fn node_resolve(
|
|||
}
|
||||
}
|
||||
|
||||
let conditions = DEFAULT_CONDITIONS;
|
||||
let conditions = mode_conditions(mode);
|
||||
let url = module_resolve(specifier, referrer, conditions, npm_resolver)?;
|
||||
let url = match url {
|
||||
Some(url) => url,
|
||||
None => return Ok(None),
|
||||
};
|
||||
let url = match mode {
|
||||
NodeResolutionMode::Execution => url,
|
||||
NodeResolutionMode::Types => {
|
||||
let path = url.to_file_path().unwrap();
|
||||
// todo(16370): the module kind is not correct here. I think we need
|
||||
// typescript to tell us if the referrer is esm or cjs
|
||||
let path = path_to_declaration_path(path, NodeModuleKind::Esm);
|
||||
ModuleSpecifier::from_file_path(path).unwrap()
|
||||
}
|
||||
};
|
||||
|
||||
let resolve_response = url_to_node_resolution(url, npm_resolver)?;
|
||||
// TODO(bartlomieju): skipped checking errors for commonJS resolution and
|
||||
|
@ -440,24 +504,36 @@ pub fn node_resolve(
|
|||
|
||||
pub fn node_resolve_npm_reference(
|
||||
reference: &NpmPackageReference,
|
||||
mode: NodeResolutionMode,
|
||||
npm_resolver: &NpmPackageResolver,
|
||||
) -> Result<Option<NodeResolution>, AnyError> {
|
||||
let package_folder =
|
||||
npm_resolver.resolve_package_folder_from_deno_module(&reference.req)?;
|
||||
let resolved_path = package_config_resolve(
|
||||
let node_module_kind = NodeModuleKind::Esm;
|
||||
let maybe_resolved_path = package_config_resolve(
|
||||
&reference
|
||||
.sub_path
|
||||
.as_ref()
|
||||
.map(|s| format!("./{}", s))
|
||||
.unwrap_or_else(|| ".".to_string()),
|
||||
&package_folder,
|
||||
node_module_kind,
|
||||
mode_conditions(mode),
|
||||
npm_resolver,
|
||||
NodeModuleKind::Esm,
|
||||
)
|
||||
.with_context(|| {
|
||||
format!("Error resolving package config for '{}'.", reference)
|
||||
})?;
|
||||
|
||||
let resolved_path = match maybe_resolved_path {
|
||||
Some(resolved_path) => resolved_path,
|
||||
None => return Ok(None),
|
||||
};
|
||||
let resolved_path = match mode {
|
||||
NodeResolutionMode::Execution => resolved_path,
|
||||
NodeResolutionMode::Types => {
|
||||
path_to_declaration_path(resolved_path, node_module_kind)
|
||||
}
|
||||
};
|
||||
let url = ModuleSpecifier::from_file_path(resolved_path).unwrap();
|
||||
let resolve_response = url_to_node_resolution(url, npm_resolver)?;
|
||||
// TODO(bartlomieju): skipped checking errors for commonJS resolution and
|
||||
|
@ -465,6 +541,41 @@ pub fn node_resolve_npm_reference(
|
|||
Ok(Some(resolve_response))
|
||||
}
|
||||
|
||||
fn mode_conditions(mode: NodeResolutionMode) -> &'static [&'static str] {
|
||||
match mode {
|
||||
NodeResolutionMode::Execution => DEFAULT_CONDITIONS,
|
||||
NodeResolutionMode::Types => TYPES_CONDITIONS,
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the resolved file has a corresponding declaration file.
|
||||
fn path_to_declaration_path(
|
||||
path: PathBuf,
|
||||
referrer_kind: NodeModuleKind,
|
||||
) -> PathBuf {
|
||||
let lowercase_path = path.to_string_lossy().to_lowercase();
|
||||
if lowercase_path.ends_with(".d.ts")
|
||||
|| lowercase_path.ends_with(".d.cts")
|
||||
|| lowercase_path.ends_with(".d.ts")
|
||||
{
|
||||
return path;
|
||||
}
|
||||
let specific_dts_path = match referrer_kind {
|
||||
NodeModuleKind::Cjs => path.with_extension("d.cts"),
|
||||
NodeModuleKind::Esm => path.with_extension("d.mts"),
|
||||
};
|
||||
if specific_dts_path.exists() {
|
||||
specific_dts_path
|
||||
} else {
|
||||
let dts_path = path.with_extension("d.ts");
|
||||
if dts_path.exists() {
|
||||
dts_path
|
||||
} else {
|
||||
path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node_resolve_binary_export(
|
||||
pkg_req: &NpmPackageReq,
|
||||
bin_name: Option<&str>,
|
||||
|
@ -562,45 +673,56 @@ pub fn load_cjs_module_from_ext_node(
|
|||
fn package_config_resolve(
|
||||
package_subpath: &str,
|
||||
package_dir: &Path,
|
||||
npm_resolver: &dyn RequireNpmResolver,
|
||||
referrer_kind: NodeModuleKind,
|
||||
) -> Result<PathBuf, AnyError> {
|
||||
conditions: &[&str],
|
||||
npm_resolver: &dyn RequireNpmResolver,
|
||||
) -> Result<Option<PathBuf>, AnyError> {
|
||||
let package_json_path = package_dir.join("package.json");
|
||||
let referrer = ModuleSpecifier::from_directory_path(package_dir).unwrap();
|
||||
let package_config =
|
||||
PackageJson::load(npm_resolver, package_json_path.clone())?;
|
||||
if let Some(exports) = &package_config.exports {
|
||||
let is_types = conditions == TYPES_CONDITIONS;
|
||||
if is_types && package_subpath == "." {
|
||||
if let Ok(Some(path)) =
|
||||
legacy_main_resolve(&package_config, referrer_kind, conditions)
|
||||
{
|
||||
return Ok(Some(path));
|
||||
}
|
||||
}
|
||||
return package_exports_resolve(
|
||||
&package_json_path,
|
||||
package_subpath.to_string(),
|
||||
exports,
|
||||
&referrer,
|
||||
referrer_kind,
|
||||
DEFAULT_CONDITIONS,
|
||||
conditions,
|
||||
npm_resolver,
|
||||
);
|
||||
)
|
||||
.map(Some);
|
||||
}
|
||||
if package_subpath == "." {
|
||||
return legacy_main_resolve(&package_config, referrer_kind);
|
||||
return legacy_main_resolve(&package_config, referrer_kind, conditions);
|
||||
}
|
||||
|
||||
Ok(package_dir.join(package_subpath))
|
||||
Ok(Some(package_dir.join(package_subpath)))
|
||||
}
|
||||
|
||||
pub fn url_to_node_resolution(
|
||||
url: ModuleSpecifier,
|
||||
npm_resolver: &dyn RequireNpmResolver,
|
||||
) -> Result<NodeResolution, AnyError> {
|
||||
Ok(if url.as_str().starts_with("http") {
|
||||
let url_str = url.as_str().to_lowercase();
|
||||
Ok(if url_str.starts_with("http") {
|
||||
NodeResolution::Esm(url)
|
||||
} else if url.as_str().ends_with(".js") {
|
||||
} else if url_str.ends_with(".js") || url_str.ends_with(".d.ts") {
|
||||
let package_config = get_closest_package_json(&url, npm_resolver)?;
|
||||
if package_config.typ == "module" {
|
||||
NodeResolution::Esm(url)
|
||||
} else {
|
||||
NodeResolution::CommonJs(url)
|
||||
}
|
||||
} else if url.as_str().ends_with(".mjs") {
|
||||
} else if url_str.ends_with(".mjs") || url_str.ends_with(".d.mts") {
|
||||
NodeResolution::Esm(url)
|
||||
} else {
|
||||
NodeResolution::CommonJs(url)
|
||||
|
@ -666,7 +788,16 @@ fn module_resolve(
|
|||
// note: if we're here, the referrer is an esm module
|
||||
let url = if should_be_treated_as_relative_or_absolute_path(specifier) {
|
||||
let resolved_specifier = referrer.join(specifier)?;
|
||||
Some(resolved_specifier)
|
||||
if conditions == TYPES_CONDITIONS {
|
||||
let file_path = to_file_path(&resolved_specifier);
|
||||
// todo(dsherret): the node module kind is not correct and we
|
||||
// should use the value provided by typescript instead
|
||||
let declaration_path =
|
||||
path_to_declaration_path(file_path, NodeModuleKind::Esm);
|
||||
Some(ModuleSpecifier::from_file_path(declaration_path).unwrap())
|
||||
} else {
|
||||
Some(resolved_specifier)
|
||||
}
|
||||
} else if specifier.starts_with('#') {
|
||||
Some(
|
||||
package_imports_resolve(
|
||||
|
@ -681,16 +812,14 @@ fn module_resolve(
|
|||
} else if let Ok(resolved) = Url::parse(specifier) {
|
||||
Some(resolved)
|
||||
} else {
|
||||
Some(
|
||||
package_resolve(
|
||||
specifier,
|
||||
referrer,
|
||||
NodeModuleKind::Esm,
|
||||
conditions,
|
||||
npm_resolver,
|
||||
)
|
||||
.map(|p| ModuleSpecifier::from_file_path(p).unwrap())?,
|
||||
)
|
||||
package_resolve(
|
||||
specifier,
|
||||
referrer,
|
||||
NodeModuleKind::Esm,
|
||||
conditions,
|
||||
npm_resolver,
|
||||
)?
|
||||
.map(|p| ModuleSpecifier::from_file_path(p).unwrap())
|
||||
};
|
||||
Ok(match url {
|
||||
Some(url) => Some(finalize_resolution(url, referrer)?),
|
||||
|
@ -913,6 +1042,7 @@ fn resolve(
|
|||
let module_dir = npm_resolver.resolve_package_folder_from_package(
|
||||
package_specifier.as_str(),
|
||||
&referrer_path,
|
||||
conditions,
|
||||
)?;
|
||||
|
||||
let package_json_path = module_dir.join("package.json");
|
||||
|
|
|
@ -160,13 +160,14 @@ impl ReadonlyNpmCache {
|
|||
.take(if is_scoped_package { 3 } else { 2 })
|
||||
.map(|(_, part)| part)
|
||||
.collect::<Vec<_>>();
|
||||
if parts.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
let version = parts.pop().unwrap();
|
||||
let name = parts.join("/");
|
||||
|
||||
Some(NpmPackageId {
|
||||
name,
|
||||
version: NpmVersion::parse(version).unwrap(),
|
||||
})
|
||||
NpmVersion::parse(version)
|
||||
.ok()
|
||||
.map(|version| NpmPackageId { name, version })
|
||||
}
|
||||
|
||||
pub fn get_cache_location(&self) -> PathBuf {
|
||||
|
|
|
@ -13,4 +13,5 @@ pub use resolution::NpmPackageId;
|
|||
pub use resolution::NpmPackageReference;
|
||||
pub use resolution::NpmPackageReq;
|
||||
pub use resolution::NpmResolutionPackage;
|
||||
pub use resolution::NpmResolutionSnapshot;
|
||||
pub use resolvers::NpmPackageResolver;
|
||||
|
|
|
@ -21,6 +21,7 @@ use super::registry::NpmPackageVersionDistInfo;
|
|||
use super::registry::NpmPackageVersionInfo;
|
||||
use super::registry::NpmRegistryApi;
|
||||
use super::semver::NpmVersion;
|
||||
use super::semver::NpmVersionReq;
|
||||
use super::semver::SpecifierVersionReq;
|
||||
|
||||
/// The version matcher used for npm schemed urls is more strict than
|
||||
|
@ -375,15 +376,57 @@ impl NpmResolution {
|
|||
|
||||
pub async fn add_package_reqs(
|
||||
&self,
|
||||
mut package_reqs: Vec<NpmPackageReq>,
|
||||
package_reqs: Vec<NpmPackageReq>,
|
||||
) -> Result<(), AnyError> {
|
||||
// multiple packages are resolved in alphabetical order
|
||||
package_reqs.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
|
||||
// only allow one thread in here at a time
|
||||
let _permit = self.update_sempahore.acquire().await.unwrap();
|
||||
let mut snapshot = self.snapshot.read().clone();
|
||||
let mut pending_dependencies = VecDeque::new();
|
||||
let snapshot = self.snapshot.read().clone();
|
||||
|
||||
let snapshot = self
|
||||
.add_package_reqs_to_snapshot(package_reqs, snapshot)
|
||||
.await?;
|
||||
|
||||
*self.snapshot.write() = snapshot;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn set_package_reqs(
|
||||
&self,
|
||||
package_reqs: HashSet<NpmPackageReq>,
|
||||
) -> Result<(), AnyError> {
|
||||
// only allow one thread in here at a time
|
||||
let _permit = self.update_sempahore.acquire().await.unwrap();
|
||||
let snapshot = self.snapshot.read().clone();
|
||||
|
||||
let has_removed_package = !snapshot
|
||||
.package_reqs
|
||||
.keys()
|
||||
.all(|req| package_reqs.contains(req));
|
||||
// if any packages were removed, we need to completely recreate the npm resolution snapshot
|
||||
let snapshot = if has_removed_package {
|
||||
NpmResolutionSnapshot::default()
|
||||
} else {
|
||||
snapshot
|
||||
};
|
||||
let snapshot = self
|
||||
.add_package_reqs_to_snapshot(
|
||||
package_reqs.into_iter().collect(),
|
||||
snapshot,
|
||||
)
|
||||
.await?;
|
||||
|
||||
*self.snapshot.write() = snapshot;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn add_package_reqs_to_snapshot(
|
||||
&self,
|
||||
mut package_reqs: Vec<NpmPackageReq>,
|
||||
mut snapshot: NpmResolutionSnapshot,
|
||||
) -> Result<NpmResolutionSnapshot, AnyError> {
|
||||
// multiple packages are resolved in alphabetical order
|
||||
package_reqs.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
|
||||
// go over the top level packages first, then down the
|
||||
// tree one level at a time through all the branches
|
||||
|
@ -418,6 +461,7 @@ impl NpmResolution {
|
|||
}));
|
||||
}
|
||||
|
||||
let mut pending_dependencies = VecDeque::new();
|
||||
for result in futures::future::join_all(unresolved_tasks).await {
|
||||
let (package_req, info) = result??;
|
||||
let version_and_info = get_resolved_package_version_and_info(
|
||||
|
@ -546,8 +590,7 @@ impl NpmResolution {
|
|||
}
|
||||
}
|
||||
|
||||
*self.snapshot.write() = snapshot;
|
||||
Ok(())
|
||||
Ok(snapshot)
|
||||
}
|
||||
|
||||
pub fn resolve_package_from_package(
|
||||
|
@ -601,6 +644,22 @@ fn get_resolved_package_version_and_info(
|
|||
) -> Result<VersionAndInfo, AnyError> {
|
||||
let mut maybe_best_version: Option<VersionAndInfo> = None;
|
||||
if let Some(tag) = version_matcher.tag() {
|
||||
// For when someone just specifies @types/node, we want to pull in a
|
||||
// "known good" version of @types/node that works well with Deno and
|
||||
// not necessarily the latest version. For example, we might only be
|
||||
// compatible with Node vX, but then Node vY is published so we wouldn't
|
||||
// want to pull that in.
|
||||
// Note: If the user doesn't want this behavior, then they can specify an
|
||||
// explicit version.
|
||||
if tag == "latest" && pkg_name == "@types/node" {
|
||||
return get_resolved_package_version_and_info(
|
||||
pkg_name,
|
||||
&NpmVersionReq::parse("18.0.0 - 18.8.2").unwrap(),
|
||||
info,
|
||||
parent,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(version) = info.dist_tags.get(tag) {
|
||||
match info.versions.get(version) {
|
||||
Some(info) => {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::io::ErrorKind;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
@ -26,6 +27,7 @@ pub trait InnerNpmPackageResolver: Send + Sync {
|
|||
&self,
|
||||
name: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
conditions: &[&str],
|
||||
) -> Result<PathBuf, AnyError>;
|
||||
|
||||
fn resolve_package_folder_from_specifier(
|
||||
|
@ -40,6 +42,11 @@ pub trait InnerNpmPackageResolver: Send + Sync {
|
|||
packages: Vec<NpmPackageReq>,
|
||||
) -> BoxFuture<'static, Result<(), AnyError>>;
|
||||
|
||||
fn set_package_reqs(
|
||||
&self,
|
||||
packages: HashSet<NpmPackageReq>,
|
||||
) -> BoxFuture<'static, Result<(), AnyError>>;
|
||||
|
||||
fn ensure_read_permission(&self, path: &Path) -> Result<(), AnyError>;
|
||||
|
||||
fn snapshot(&self) -> NpmResolutionSnapshot;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
//! Code for global npm cache resolution.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
@ -11,6 +12,8 @@ use deno_core::error::AnyError;
|
|||
use deno_core::futures::future::BoxFuture;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::url::Url;
|
||||
use deno_runtime::deno_node::PackageJson;
|
||||
use deno_runtime::deno_node::TYPES_CONDITIONS;
|
||||
|
||||
use crate::npm::resolution::NpmResolution;
|
||||
use crate::npm::resolution::NpmResolutionSnapshot;
|
||||
|
@ -65,14 +68,35 @@ impl InnerNpmPackageResolver for GlobalNpmPackageResolver {
|
|||
&self,
|
||||
name: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
conditions: &[&str],
|
||||
) -> Result<PathBuf, AnyError> {
|
||||
let referrer_pkg_id = self
|
||||
.cache
|
||||
.resolve_package_id_from_specifier(referrer, &self.registry_url)?;
|
||||
let pkg = self
|
||||
let pkg_result = self
|
||||
.resolution
|
||||
.resolve_package_from_package(name, &referrer_pkg_id)?;
|
||||
Ok(self.package_folder(&pkg.id))
|
||||
.resolve_package_from_package(name, &referrer_pkg_id);
|
||||
if conditions == TYPES_CONDITIONS && !name.starts_with("@types/") {
|
||||
// When doing types resolution, the package must contain a "types"
|
||||
// entry, or else it will then search for a @types package
|
||||
if let Ok(pkg) = pkg_result {
|
||||
let package_folder = self.package_folder(&pkg.id);
|
||||
let package_json = PackageJson::load_skip_read_permission(
|
||||
package_folder.join("package.json"),
|
||||
)?;
|
||||
if package_json.types.is_some() {
|
||||
return Ok(package_folder);
|
||||
}
|
||||
}
|
||||
|
||||
let name = format!("@types/{}", name);
|
||||
let pkg = self
|
||||
.resolution
|
||||
.resolve_package_from_package(&name, &referrer_pkg_id)?;
|
||||
Ok(self.package_folder(&pkg.id))
|
||||
} else {
|
||||
Ok(self.package_folder(&pkg_result?.id))
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_package_folder_from_specifier(
|
||||
|
@ -96,12 +120,19 @@ impl InnerNpmPackageResolver for GlobalNpmPackageResolver {
|
|||
let resolver = self.clone();
|
||||
async move {
|
||||
resolver.resolution.add_package_reqs(packages).await?;
|
||||
cache_packages(
|
||||
resolver.resolution.all_packages(),
|
||||
&resolver.cache,
|
||||
&resolver.registry_url,
|
||||
)
|
||||
.await
|
||||
cache_packages_in_resolver(&resolver).await
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
fn set_package_reqs(
|
||||
&self,
|
||||
packages: HashSet<NpmPackageReq>,
|
||||
) -> BoxFuture<'static, Result<(), AnyError>> {
|
||||
let resolver = self.clone();
|
||||
async move {
|
||||
resolver.resolution.set_package_reqs(packages).await?;
|
||||
cache_packages_in_resolver(&resolver).await
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
@ -115,3 +146,14 @@ impl InnerNpmPackageResolver for GlobalNpmPackageResolver {
|
|||
self.resolution.snapshot()
|
||||
}
|
||||
}
|
||||
|
||||
async fn cache_packages_in_resolver(
|
||||
resolver: &GlobalNpmPackageResolver,
|
||||
) -> Result<(), AnyError> {
|
||||
cache_packages(
|
||||
resolver.resolution.all_packages(),
|
||||
&resolver.cache,
|
||||
&resolver.registry_url,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ use deno_core::futures::future::BoxFuture;
|
|||
use deno_core::futures::FutureExt;
|
||||
use deno_core::url::Url;
|
||||
use deno_runtime::deno_core::futures;
|
||||
use deno_runtime::deno_node::PackageJson;
|
||||
use deno_runtime::deno_node::TYPES_CONDITIONS;
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
use crate::fs_util;
|
||||
|
@ -124,6 +126,7 @@ impl InnerNpmPackageResolver for LocalNpmPackageResolver {
|
|||
&self,
|
||||
name: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
conditions: &[&str],
|
||||
) -> Result<PathBuf, AnyError> {
|
||||
let local_path = self.resolve_folder_for_specifier(referrer)?;
|
||||
let package_root_path = self.resolve_package_root(&local_path);
|
||||
|
@ -132,8 +135,28 @@ impl InnerNpmPackageResolver for LocalNpmPackageResolver {
|
|||
current_folder = get_next_node_modules_ancestor(current_folder);
|
||||
let sub_dir = join_package_name(current_folder, name);
|
||||
if sub_dir.is_dir() {
|
||||
return Ok(sub_dir);
|
||||
// if doing types resolution, only resolve the package if it specifies a types property
|
||||
if conditions == TYPES_CONDITIONS && !name.starts_with("@types/") {
|
||||
let package_json = PackageJson::load_skip_read_permission(
|
||||
sub_dir.join("package.json"),
|
||||
)?;
|
||||
if package_json.types.is_some() {
|
||||
return Ok(sub_dir);
|
||||
}
|
||||
} else {
|
||||
return Ok(sub_dir);
|
||||
}
|
||||
}
|
||||
|
||||
// if doing type resolution, check for the existance of a @types package
|
||||
if conditions == TYPES_CONDITIONS && !name.starts_with("@types/") {
|
||||
let sub_dir =
|
||||
join_package_name(current_folder, &format!("@types/{}", name));
|
||||
if sub_dir.is_dir() {
|
||||
return Ok(sub_dir);
|
||||
}
|
||||
}
|
||||
|
||||
if current_folder == self.root_node_modules_path {
|
||||
bail!(
|
||||
"could not find package '{}' from referrer '{}'.",
|
||||
|
@ -164,15 +187,20 @@ impl InnerNpmPackageResolver for LocalNpmPackageResolver {
|
|||
let resolver = self.clone();
|
||||
async move {
|
||||
resolver.resolution.add_package_reqs(packages).await?;
|
||||
sync_resolver_with_fs(&resolver).await?;
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
}
|
||||
|
||||
sync_resolution_with_fs(
|
||||
&resolver.resolution.snapshot(),
|
||||
&resolver.cache,
|
||||
&resolver.registry_url,
|
||||
&resolver.root_node_modules_path,
|
||||
)
|
||||
.await?;
|
||||
|
||||
fn set_package_reqs(
|
||||
&self,
|
||||
packages: HashSet<NpmPackageReq>,
|
||||
) -> BoxFuture<'static, Result<(), AnyError>> {
|
||||
let resolver = self.clone();
|
||||
async move {
|
||||
resolver.resolution.set_package_reqs(packages).await?;
|
||||
sync_resolver_with_fs(&resolver).await?;
|
||||
Ok(())
|
||||
}
|
||||
.boxed()
|
||||
|
@ -187,6 +215,18 @@ impl InnerNpmPackageResolver for LocalNpmPackageResolver {
|
|||
}
|
||||
}
|
||||
|
||||
async fn sync_resolver_with_fs(
|
||||
resolver: &LocalNpmPackageResolver,
|
||||
) -> Result<(), AnyError> {
|
||||
sync_resolution_with_fs(
|
||||
&resolver.resolution.snapshot(),
|
||||
&resolver.cache,
|
||||
&resolver.registry_url,
|
||||
&resolver.root_node_modules_path,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Creates a pnpm style folder structure.
|
||||
async fn sync_resolution_with_fs(
|
||||
snapshot: &NpmResolutionSnapshot,
|
||||
|
|
|
@ -15,6 +15,7 @@ use global::GlobalNpmPackageResolver;
|
|||
use once_cell::sync::Lazy;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
@ -23,10 +24,10 @@ use crate::fs_util;
|
|||
|
||||
use self::common::InnerNpmPackageResolver;
|
||||
use self::local::LocalNpmPackageResolver;
|
||||
use super::resolution::NpmResolutionSnapshot;
|
||||
use super::NpmCache;
|
||||
use super::NpmPackageReq;
|
||||
use super::NpmRegistryApi;
|
||||
use super::NpmResolutionSnapshot;
|
||||
|
||||
const RESOLUTION_STATE_ENV_VAR_NAME: &str =
|
||||
"DENO_DONT_USE_INTERNAL_NODE_COMPAT_STATE";
|
||||
|
@ -67,6 +68,19 @@ pub struct NpmPackageResolver {
|
|||
no_npm: bool,
|
||||
inner: Arc<dyn InnerNpmPackageResolver>,
|
||||
local_node_modules_path: Option<PathBuf>,
|
||||
api: NpmRegistryApi,
|
||||
cache: NpmCache,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for NpmPackageResolver {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("NpmPackageResolver")
|
||||
.field("unstable", &self.unstable)
|
||||
.field("no_npm", &self.no_npm)
|
||||
.field("inner", &"<omitted>")
|
||||
.field("local_node_modules_path", &self.local_node_modules_path)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl NpmPackageResolver {
|
||||
|
@ -76,6 +90,24 @@ impl NpmPackageResolver {
|
|||
unstable: bool,
|
||||
no_npm: bool,
|
||||
local_node_modules_path: Option<PathBuf>,
|
||||
) -> Self {
|
||||
Self::new_with_maybe_snapshot(
|
||||
cache,
|
||||
api,
|
||||
unstable,
|
||||
no_npm,
|
||||
local_node_modules_path,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
fn new_with_maybe_snapshot(
|
||||
cache: NpmCache,
|
||||
api: NpmRegistryApi,
|
||||
unstable: bool,
|
||||
no_npm: bool,
|
||||
local_node_modules_path: Option<PathBuf>,
|
||||
initial_snapshot: Option<NpmResolutionSnapshot>,
|
||||
) -> Self {
|
||||
let process_npm_state = NpmProcessState::take();
|
||||
let local_node_modules_path = local_node_modules_path.or_else(|| {
|
||||
|
@ -83,24 +115,29 @@ impl NpmPackageResolver {
|
|||
.as_ref()
|
||||
.and_then(|s| s.local_node_modules_path.as_ref().map(PathBuf::from))
|
||||
});
|
||||
let maybe_snapshot = process_npm_state.map(|s| s.snapshot);
|
||||
let maybe_snapshot =
|
||||
initial_snapshot.or_else(|| process_npm_state.map(|s| s.snapshot));
|
||||
let inner: Arc<dyn InnerNpmPackageResolver> = match &local_node_modules_path
|
||||
{
|
||||
Some(node_modules_folder) => Arc::new(LocalNpmPackageResolver::new(
|
||||
cache,
|
||||
api,
|
||||
cache.clone(),
|
||||
api.clone(),
|
||||
node_modules_folder.clone(),
|
||||
maybe_snapshot,
|
||||
)),
|
||||
None => {
|
||||
Arc::new(GlobalNpmPackageResolver::new(cache, api, maybe_snapshot))
|
||||
}
|
||||
None => Arc::new(GlobalNpmPackageResolver::new(
|
||||
cache.clone(),
|
||||
api.clone(),
|
||||
maybe_snapshot,
|
||||
)),
|
||||
};
|
||||
Self {
|
||||
unstable,
|
||||
no_npm,
|
||||
inner,
|
||||
local_node_modules_path,
|
||||
api,
|
||||
cache,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,10 +159,11 @@ impl NpmPackageResolver {
|
|||
&self,
|
||||
name: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
conditions: &[&str],
|
||||
) -> Result<PathBuf, AnyError> {
|
||||
let path = self
|
||||
.inner
|
||||
.resolve_package_folder_from_package(name, referrer)?;
|
||||
.resolve_package_folder_from_package(name, referrer, conditions)?;
|
||||
log::debug!("Resolved {} from {} to {}", name, referrer, path.display());
|
||||
Ok(path)
|
||||
}
|
||||
|
@ -156,12 +194,14 @@ impl NpmPackageResolver {
|
|||
self.inner.has_packages()
|
||||
}
|
||||
|
||||
/// Adds a package requirement to the resolver and ensures everything is setup.
|
||||
/// Adds package requirements to the resolver and ensures everything is setup.
|
||||
pub async fn add_package_reqs(
|
||||
&self,
|
||||
packages: Vec<NpmPackageReq>,
|
||||
) -> Result<(), AnyError> {
|
||||
assert!(!packages.is_empty());
|
||||
if packages.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if !self.unstable {
|
||||
bail!(
|
||||
|
@ -187,6 +227,14 @@ impl NpmPackageResolver {
|
|||
self.inner.add_package_reqs(packages).await
|
||||
}
|
||||
|
||||
/// Sets package requirements to the resolver, removing old requirements and adding new ones.
|
||||
pub async fn set_package_reqs(
|
||||
&self,
|
||||
packages: HashSet<NpmPackageReq>,
|
||||
) -> Result<(), AnyError> {
|
||||
self.inner.set_package_reqs(packages).await
|
||||
}
|
||||
|
||||
// If the main module should be treated as being in an npm package.
|
||||
// This is triggered via a secret environment variable which is used
|
||||
// for functionality like child_process.fork. Users should NOT depend
|
||||
|
@ -206,6 +254,18 @@ impl NpmPackageResolver {
|
|||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Gets a new resolver with a new snapshotted state.
|
||||
pub fn snapshotted(&self) -> Self {
|
||||
Self::new_with_maybe_snapshot(
|
||||
self.cache.clone(),
|
||||
self.api.clone(),
|
||||
self.unstable,
|
||||
self.no_npm,
|
||||
self.local_node_modules_path.clone(),
|
||||
Some(self.inner.snapshot()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl RequireNpmResolver for NpmPackageResolver {
|
||||
|
@ -213,9 +273,10 @@ impl RequireNpmResolver for NpmPackageResolver {
|
|||
&self,
|
||||
specifier: &str,
|
||||
referrer: &std::path::Path,
|
||||
conditions: &[&str],
|
||||
) -> Result<PathBuf, AnyError> {
|
||||
let referrer = path_to_specifier(referrer)?;
|
||||
self.resolve_package_folder_from_package(specifier, &referrer)
|
||||
self.resolve_package_folder_from_package(specifier, &referrer, conditions)
|
||||
}
|
||||
|
||||
fn resolve_package_folder_from_path(
|
||||
|
|
|
@ -34,9 +34,9 @@ use crate::tools::check;
|
|||
|
||||
use deno_ast::MediaType;
|
||||
use deno_core::anyhow::anyhow;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::generic_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
|
@ -433,8 +433,13 @@ impl ProcState {
|
|||
let check_cache =
|
||||
TypeCheckCache::new(&self.dir.type_checking_cache_db_file_path());
|
||||
let graph_data = self.graph_data.clone();
|
||||
let check_result =
|
||||
check::check(&roots, graph_data, &check_cache, options)?;
|
||||
let check_result = check::check(
|
||||
&roots,
|
||||
graph_data,
|
||||
&check_cache,
|
||||
self.npm_resolver.clone(),
|
||||
options,
|
||||
)?;
|
||||
if !check_result.diagnostics.is_empty() {
|
||||
return Err(anyhow!(check_result.diagnostics));
|
||||
}
|
||||
|
@ -470,7 +475,7 @@ impl ProcState {
|
|||
) -> Result<ModuleSpecifier, AnyError> {
|
||||
let response = match result? {
|
||||
Some(response) => response,
|
||||
None => bail!("Not found."),
|
||||
None => return Err(generic_error("not found")),
|
||||
};
|
||||
if let NodeResolution::CommonJs(specifier) = &response {
|
||||
// remember that this was a common js resolution
|
||||
|
@ -493,6 +498,7 @@ impl ProcState {
|
|||
.handle_node_resolve_result(node::node_resolve(
|
||||
specifier,
|
||||
&referrer,
|
||||
node::NodeResolutionMode::Execution,
|
||||
&self.npm_resolver,
|
||||
))
|
||||
.with_context(|| {
|
||||
|
@ -516,6 +522,7 @@ impl ProcState {
|
|||
return self
|
||||
.handle_node_resolve_result(node::node_resolve_npm_reference(
|
||||
&reference,
|
||||
node::NodeResolutionMode::Execution,
|
||||
&self.npm_resolver,
|
||||
))
|
||||
.with_context(|| format!("Could not resolve '{}'.", reference));
|
||||
|
|
|
@ -50,6 +50,13 @@ itest!(declaration_header_file_with_no_exports {
|
|||
output_str: Some(""),
|
||||
});
|
||||
|
||||
itest!(check_npm_install_diagnostics {
|
||||
args: "check --quiet check/npm_install_diagnostics/main.ts",
|
||||
output: "check/npm_install_diagnostics/main.out",
|
||||
envs: vec![("NO_COLOR".to_string(), "1".to_string())],
|
||||
exit_code: 1,
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn cache_switching_config_then_no_config() {
|
||||
let deno_dir = util::new_deno_dir();
|
||||
|
|
|
@ -3346,6 +3346,37 @@ fn lsp_code_actions_deno_cache() {
|
|||
session.shutdown_and_exit();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_code_actions_deno_cache_npm() {
|
||||
let mut session = TestSession::from_file("initialize_params.json");
|
||||
let diagnostics = session.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "import chalk from \"npm:chalk\";\n\nconsole.log(chalk.green);\n"
|
||||
}
|
||||
}));
|
||||
assert_eq!(
|
||||
diagnostics.with_source("deno"),
|
||||
load_fixture_as("code_actions/cache_npm/diagnostics.json")
|
||||
);
|
||||
|
||||
let (maybe_res, maybe_err) = session
|
||||
.client
|
||||
.write_request(
|
||||
"textDocument/codeAction",
|
||||
load_fixture("code_actions/cache_npm/cache_action.json"),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(maybe_err.is_none());
|
||||
assert_eq!(
|
||||
maybe_res,
|
||||
Some(load_fixture("code_actions/cache_npm/cache_response.json"))
|
||||
);
|
||||
session.shutdown_and_exit();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_code_actions_imports() {
|
||||
let mut session = TestSession::from_file("initialize_params.json");
|
||||
|
@ -4046,6 +4077,169 @@ fn lsp_completions_no_snippet() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_completions_npm() {
|
||||
let _g = http_server();
|
||||
let mut client = init("initialize_params.json");
|
||||
did_open(
|
||||
&mut client,
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": "import cjsDefault from 'npm:@denotest/cjs-default-export';import chalk from 'npm:chalk';\n\n",
|
||||
}
|
||||
}),
|
||||
);
|
||||
let (maybe_res, maybe_err) = client
|
||||
.write_request::<_, _, Value>(
|
||||
"deno/cache",
|
||||
json!({
|
||||
"referrer": {
|
||||
"uri": "file:///a/file.ts",
|
||||
},
|
||||
"uris": [
|
||||
{
|
||||
"uri": "npm:@denotest/cjs-default-export",
|
||||
},
|
||||
{
|
||||
"uri": "npm:chalk",
|
||||
}
|
||||
]
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(maybe_err.is_none());
|
||||
assert!(maybe_res.is_some());
|
||||
|
||||
// check importing a cjs default import
|
||||
client
|
||||
.write_notification(
|
||||
"textDocument/didChange",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"version": 2
|
||||
},
|
||||
"contentChanges": [
|
||||
{
|
||||
"range": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"character": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"character": 0
|
||||
}
|
||||
},
|
||||
"text": "cjsDefault."
|
||||
}
|
||||
]
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
read_diagnostics(&mut client);
|
||||
|
||||
let (maybe_res, maybe_err) = client
|
||||
.write_request(
|
||||
"textDocument/completion",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts"
|
||||
},
|
||||
"position": {
|
||||
"line": 2,
|
||||
"character": 11
|
||||
},
|
||||
"context": {
|
||||
"triggerKind": 2,
|
||||
"triggerCharacter": "."
|
||||
}
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(maybe_err.is_none());
|
||||
if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
|
||||
assert!(!list.is_incomplete);
|
||||
assert_eq!(list.items.len(), 3);
|
||||
assert!(list.items.iter().any(|i| i.label == "default"));
|
||||
assert!(list.items.iter().any(|i| i.label == "MyClass"));
|
||||
} else {
|
||||
panic!("unexpected response");
|
||||
}
|
||||
let (maybe_res, maybe_err) = client
|
||||
.write_request(
|
||||
"completionItem/resolve",
|
||||
load_fixture("completions/npm/resolve_params.json"),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(maybe_err.is_none());
|
||||
assert_eq!(
|
||||
maybe_res,
|
||||
Some(load_fixture("completions/npm/resolve_response.json"))
|
||||
);
|
||||
|
||||
// now check chalk, which is esm
|
||||
client
|
||||
.write_notification(
|
||||
"textDocument/didChange",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"version": 3
|
||||
},
|
||||
"contentChanges": [
|
||||
{
|
||||
"range": {
|
||||
"start": {
|
||||
"line": 2,
|
||||
"character": 0
|
||||
},
|
||||
"end": {
|
||||
"line": 2,
|
||||
"character": 11
|
||||
}
|
||||
},
|
||||
"text": "chalk."
|
||||
}
|
||||
]
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
read_diagnostics(&mut client);
|
||||
|
||||
let (maybe_res, maybe_err) = client
|
||||
.write_request(
|
||||
"textDocument/completion",
|
||||
json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts"
|
||||
},
|
||||
"position": {
|
||||
"line": 2,
|
||||
"character": 6
|
||||
},
|
||||
"context": {
|
||||
"triggerKind": 2,
|
||||
"triggerCharacter": "."
|
||||
}
|
||||
}),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(maybe_err.is_none());
|
||||
if let Some(lsp::CompletionResponse::List(list)) = maybe_res {
|
||||
assert!(!list.is_incomplete);
|
||||
assert!(list.items.iter().any(|i| i.label == "green"));
|
||||
assert!(list.items.iter().any(|i| i.label == "red"));
|
||||
} else {
|
||||
panic!("unexpected response");
|
||||
}
|
||||
|
||||
shutdown(&mut client);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_completions_registry() {
|
||||
let _g = http_server();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::url::Url;
|
||||
use std::process::Stdio;
|
||||
use test_util as util;
|
||||
use util::assert_contains;
|
||||
|
@ -34,7 +33,7 @@ itest!(esm_module_deno_test {
|
|||
});
|
||||
|
||||
itest!(esm_import_cjs_default {
|
||||
args: "run --allow-read --allow-env --unstable --quiet npm/esm_import_cjs_default/main.js",
|
||||
args: "run --allow-read --allow-env --unstable --quiet --check=all npm/esm_import_cjs_default/main.ts",
|
||||
output: "npm/esm_import_cjs_default/main.out",
|
||||
envs: env_vars(),
|
||||
http_server: true,
|
||||
|
@ -84,7 +83,7 @@ itest!(translate_cjs_to_esm {
|
|||
});
|
||||
|
||||
itest!(compare_globals {
|
||||
args: "run --allow-read --unstable npm/compare_globals/main.js",
|
||||
args: "run --allow-read --unstable --check=all npm/compare_globals/main.ts",
|
||||
output: "npm/compare_globals/main.out",
|
||||
envs: env_vars(),
|
||||
http_server: true,
|
||||
|
@ -210,6 +209,38 @@ itest!(deno_cache {
|
|||
http_server: true,
|
||||
});
|
||||
|
||||
itest!(check_all {
|
||||
args: "check --unstable --remote npm/check_errors/main.ts",
|
||||
output: "npm/check_errors/main_all.out",
|
||||
envs: env_vars(),
|
||||
http_server: true,
|
||||
exit_code: 1,
|
||||
});
|
||||
|
||||
itest!(check_local {
|
||||
args: "check --unstable npm/check_errors/main.ts",
|
||||
output: "npm/check_errors/main_local.out",
|
||||
envs: env_vars(),
|
||||
http_server: true,
|
||||
exit_code: 1,
|
||||
});
|
||||
|
||||
itest!(types_ambient_module {
|
||||
args: "check --unstable --quiet npm/types_ambient_module/main.ts",
|
||||
output: "npm/types_ambient_module/main.out",
|
||||
envs: env_vars(),
|
||||
http_server: true,
|
||||
exit_code: 1,
|
||||
});
|
||||
|
||||
itest!(types_ambient_module_import_map {
|
||||
args: "check --unstable --quiet --import-map=npm/types_ambient_module/import_map.json npm/types_ambient_module/main_import_map.ts",
|
||||
output: "npm/types_ambient_module/main_import_map.out",
|
||||
envs: env_vars(),
|
||||
http_server: true,
|
||||
exit_code: 1,
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn parallel_downloading() {
|
||||
let (out, _err) = util::run_and_collect_output_with_args(
|
||||
|
@ -672,18 +703,10 @@ fn ensure_registry_files_local() {
|
|||
}
|
||||
}
|
||||
|
||||
fn std_file_url() -> String {
|
||||
let u = Url::from_directory_path(util::std_path()).unwrap();
|
||||
u.to_string()
|
||||
}
|
||||
|
||||
fn env_vars_no_sync_download() -> Vec<(String, String)> {
|
||||
vec![
|
||||
("DENO_NODE_COMPAT_URL".to_string(), std_file_url()),
|
||||
(
|
||||
"DENO_NPM_REGISTRY".to_string(),
|
||||
"http://localhost:4545/npm/registry/".to_string(),
|
||||
),
|
||||
("DENO_NODE_COMPAT_URL".to_string(), util::std_file_url()),
|
||||
("DENO_NPM_REGISTRY".to_string(), util::npm_registry_url()),
|
||||
("NO_COLOR".to_string(), "1".to_string()),
|
||||
]
|
||||
}
|
||||
|
|
11
cli/tests/testdata/check/npm_install_diagnostics/main.out
vendored
Normal file
11
cli/tests/testdata/check/npm_install_diagnostics/main.out
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
error: TS2581 [ERROR]: Cannot find name '$'. Did you mean to import jQuery? Try adding `import $ from "npm:jquery";`.
|
||||
$;
|
||||
^
|
||||
at file:///[WILDCARD]/npm_install_diagnostics/main.ts:1:1
|
||||
|
||||
TS2580 [ERROR]: Cannot find name 'process'.
|
||||
process;
|
||||
~~~~~~~
|
||||
at file:///[WILDCARD]/npm_install_diagnostics/main.ts:2:1
|
||||
|
||||
Found 2 errors.
|
2
cli/tests/testdata/check/npm_install_diagnostics/main.ts
vendored
Normal file
2
cli/tests/testdata/check/npm_install_diagnostics/main.ts
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
$;
|
||||
process;
|
41
cli/tests/testdata/lsp/code_actions/cache_npm/cache_action.json
vendored
Normal file
41
cli/tests/testdata/lsp/code_actions/cache_npm/cache_action.json
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts"
|
||||
},
|
||||
"range": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 18
|
||||
},
|
||||
"end": {
|
||||
"line": 0,
|
||||
"character": 29
|
||||
}
|
||||
},
|
||||
"context": {
|
||||
"diagnostics": [
|
||||
{
|
||||
"range": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 18
|
||||
},
|
||||
"end": {
|
||||
"line": 0,
|
||||
"character": 29
|
||||
}
|
||||
},
|
||||
"severity": 1,
|
||||
"code": "no-cache-npm",
|
||||
"source": "deno",
|
||||
"message": "Uncached or missing npm package: \"chalk\".",
|
||||
"data": {
|
||||
"specifier": "npm:chalk"
|
||||
}
|
||||
}
|
||||
],
|
||||
"only": [
|
||||
"quickfix"
|
||||
]
|
||||
}
|
||||
}
|
36
cli/tests/testdata/lsp/code_actions/cache_npm/cache_response.json
vendored
Normal file
36
cli/tests/testdata/lsp/code_actions/cache_npm/cache_response.json
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
[
|
||||
{
|
||||
"title": "Cache \"npm:chalk\" and its dependencies.",
|
||||
"kind": "quickfix",
|
||||
"diagnostics": [
|
||||
{
|
||||
"range": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 18
|
||||
},
|
||||
"end": {
|
||||
"line": 0,
|
||||
"character": 29
|
||||
}
|
||||
},
|
||||
"severity": 1,
|
||||
"code": "no-cache-npm",
|
||||
"source": "deno",
|
||||
"message": "Uncached or missing npm package: \"chalk\".",
|
||||
"data": {
|
||||
"specifier": "npm:chalk"
|
||||
}
|
||||
}
|
||||
],
|
||||
"command": {
|
||||
"title": "",
|
||||
"command": "deno.cache",
|
||||
"arguments": [
|
||||
[
|
||||
"npm:chalk"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
25
cli/tests/testdata/lsp/code_actions/cache_npm/diagnostics.json
vendored
Normal file
25
cli/tests/testdata/lsp/code_actions/cache_npm/diagnostics.json
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"uri": "file:///a/file.ts",
|
||||
"diagnostics": [
|
||||
{
|
||||
"range": {
|
||||
"start": {
|
||||
"line": 0,
|
||||
"character": 18
|
||||
},
|
||||
"end": {
|
||||
"line": 0,
|
||||
"character": 29
|
||||
}
|
||||
},
|
||||
"severity": 1,
|
||||
"code": "no-cache-npm",
|
||||
"source": "deno",
|
||||
"message": "Uncached or missing npm package: \"chalk\".",
|
||||
"data": {
|
||||
"specifier": "npm:chalk"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": 1
|
||||
}
|
14
cli/tests/testdata/lsp/completions/npm/resolve_params.json
vendored
Normal file
14
cli/tests/testdata/lsp/completions/npm/resolve_params.json
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"label": "MyClass",
|
||||
"kind": 6,
|
||||
"sortText": "1",
|
||||
"insertTextFormat": 1,
|
||||
"data": {
|
||||
"tsc": {
|
||||
"specifier": "file:///a/file.ts",
|
||||
"position": 69,
|
||||
"name": "MyClass",
|
||||
"useCodeSnippet": false
|
||||
}
|
||||
}
|
||||
}
|
14
cli/tests/testdata/lsp/completions/npm/resolve_response.json
vendored
Normal file
14
cli/tests/testdata/lsp/completions/npm/resolve_response.json
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"label": "MyClass",
|
||||
"kind": 6,
|
||||
"sortText": "1",
|
||||
"insertTextFormat": 1,
|
||||
"data": {
|
||||
"tsc": {
|
||||
"specifier": "file:///a/file.ts",
|
||||
"position": 69,
|
||||
"name": "MyClass",
|
||||
"useCodeSnippet": false
|
||||
}
|
||||
}
|
||||
}
|
3
cli/tests/testdata/npm/check_errors/main.ts
vendored
Normal file
3
cli/tests/testdata/npm/check_errors/main.ts
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
import * as test from "npm:@denotest/check-error";
|
||||
|
||||
console.log(test.Asdf); // should error
|
19
cli/tests/testdata/npm/check_errors/main_all.out
vendored
Normal file
19
cli/tests/testdata/npm/check_errors/main_all.out
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
Download http://localhost:4545/npm/registry/@denotest/check-error
|
||||
Download http://localhost:4545/npm/registry/@denotest/check-error/1.0.0.tgz
|
||||
Check file:///[WILDCARD]/check_errors/main.ts
|
||||
error: TS2506 [ERROR]: 'Class1' is referenced directly or indirectly in its own base expression.
|
||||
export class Class1 extends Class2 {
|
||||
~~~~~~
|
||||
at file:///[WILDCARD]/check-error/1.0.0/index.d.ts:2:14
|
||||
|
||||
TS2506 [ERROR]: 'Class2' is referenced directly or indirectly in its own base expression.
|
||||
export class Class2 extends Class1 {
|
||||
~~~~~~
|
||||
at file:///[WILDCARD]/check-error/1.0.0/index.d.ts:5:14
|
||||
|
||||
TS2339 [ERROR]: Property 'Asdf' does not exist on type 'typeof import("file:///[WILDCARD]/@denotest/check-error/1.0.0/index.d.ts")'.
|
||||
console.log(test.Asdf); // should error
|
||||
~~~~
|
||||
at file:///[WILDCARD]/check_errors/main.ts:3:18
|
||||
|
||||
Found 3 errors.
|
7
cli/tests/testdata/npm/check_errors/main_local.out
vendored
Normal file
7
cli/tests/testdata/npm/check_errors/main_local.out
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
Download http://localhost:4545/npm/registry/@denotest/check-error
|
||||
Download http://localhost:4545/npm/registry/@denotest/check-error/1.0.0.tgz
|
||||
Check file:///[WILDCARD]/check_errors/main.ts
|
||||
error: TS2339 [ERROR]: Property 'Asdf' does not exist on type 'typeof import("file:///[WILDCARD]/@denotest/check-error/1.0.0/index.d.ts")'.
|
||||
console.log(test.Asdf); // should error
|
||||
~~~~
|
||||
at file:///[WILDCARD]/npm/check_errors/main.ts:3:18
|
|
@ -1,2 +0,0 @@
|
|||
import * as globals from "npm:@denotest/globals";
|
||||
console.log(globals.global === globals.globalThis);
|
|
@ -1,3 +1,8 @@
|
|||
Download http://localhost:4545/npm/registry/@denotest/globals
|
||||
Download http://localhost:4545/npm/registry/@types/node
|
||||
Download http://localhost:4545/npm/registry/@denotest/globals/1.0.0.tgz
|
||||
Download http://localhost:4545/npm/registry/@types/node/node-18.8.2.tgz
|
||||
Check file:///[WILDCARD]/npm/compare_globals/main.ts
|
||||
Check file:///[WILDCARD]/std/node/module_all.ts
|
||||
true
|
||||
[]
|
||||
|
|
14
cli/tests/testdata/npm/compare_globals/main.ts
vendored
Normal file
14
cli/tests/testdata/npm/compare_globals/main.ts
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
/// <reference types="npm:@types/node" />
|
||||
|
||||
import * as globals from "npm:@denotest/globals";
|
||||
console.log(globals.global === globals.globalThis);
|
||||
console.log(globals.process.execArgv);
|
||||
|
||||
type AssertTrue<T extends true> = never;
|
||||
type _TestNoProcessGlobal = AssertTrue<
|
||||
typeof globalThis extends { process: any } ? false : true
|
||||
>;
|
||||
type _TestHasNodeJsGlobal = NodeJS.Architecture;
|
||||
|
||||
const controller = new AbortController();
|
||||
controller.abort("reason"); // in the NodeJS declaration it doesn't have a reason
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-check
|
||||
import cjsDefault, {
|
||||
MyClass as MyCjsClass,
|
||||
} from "npm:@denotest/cjs-default-export";
|
6
cli/tests/testdata/npm/registry/@denotest/check-error/1.0.0/index.d.ts
vendored
Normal file
6
cli/tests/testdata/npm/registry/@denotest/check-error/1.0.0/index.d.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
// intentional type checking errors
|
||||
export class Class1 extends Class2 {
|
||||
}
|
||||
|
||||
export class Class2 extends Class1 {
|
||||
}
|
6
cli/tests/testdata/npm/registry/@denotest/check-error/1.0.0/index.js
vendored
Normal file
6
cli/tests/testdata/npm/registry/@denotest/check-error/1.0.0/index.js
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
Class1: class {
|
||||
},
|
||||
Class2: class {
|
||||
},
|
||||
};
|
5
cli/tests/testdata/npm/registry/@denotest/check-error/1.0.0/package.json
vendored
Normal file
5
cli/tests/testdata/npm/registry/@denotest/check-error/1.0.0/package.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "@denotest/check-error",
|
||||
"version": "1.0.0",
|
||||
"types": "./index.d.ts"
|
||||
}
|
6
cli/tests/testdata/npm/registry/@denotest/cjs-default-export/1.0.0/index.d.ts
vendored
Normal file
6
cli/tests/testdata/npm/registry/@denotest/cjs-default-export/1.0.0/index.d.ts
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
export default function (): number;
|
||||
export declare function named(): number;
|
||||
declare class MyClass {
|
||||
static someStaticMethod(): string;
|
||||
}
|
||||
export { MyClass };
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"name": "@denotest/cjs-default-export",
|
||||
"version": "1.0.0"
|
||||
"version": "1.0.0",
|
||||
"types": "./index.d.ts"
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"name": "@denotest/esm-import-cjs-default",
|
||||
"version": "1.0.0",
|
||||
"main": "index.mjs",
|
||||
"dependencies": {
|
||||
"@denotest/cjs-default-export": "^1.0.0"
|
||||
}
|
||||
|
|
13
cli/tests/testdata/npm/registry/@denotest/globals/1.0.0/index.d.ts
vendored
Normal file
13
cli/tests/testdata/npm/registry/@denotest/globals/1.0.0/index.d.ts
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
declare const tempGlobalThis: typeof globalThis;
|
||||
declare const tempGlobal: typeof global;
|
||||
declare const tempProcess: typeof process;
|
||||
export {
|
||||
tempGlobalThis as globalThis,
|
||||
tempGlobal as global,
|
||||
tempProcess as process,
|
||||
};
|
||||
|
||||
type AssertTrue<T extends true> = never;
|
||||
type _TestHasProcessGlobal = AssertTrue<
|
||||
typeof globalThis extends { process: any } ? true : false
|
||||
>;
|
|
@ -1,2 +1,3 @@
|
|||
exports.globalThis = globalThis;
|
||||
exports.global = global;
|
||||
exports.process = process;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"name": "@denotest/globals",
|
||||
"version": "1.0.0"
|
||||
"version": "1.0.0",
|
||||
"types": "index.d.ts"
|
||||
}
|
||||
|
|
10
cli/tests/testdata/npm/registry/@denotest/types-ambient/1.0.0/index.d.ts
vendored
Normal file
10
cli/tests/testdata/npm/registry/@denotest/types-ambient/1.0.0/index.d.ts
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
// Some packages do this. It's really not ideal because instead of allowing
|
||||
// the package to be resolved at any specifier, it instead expects the package
|
||||
// to be resolved via a "@denotest/types-ambient" specifier. To make this work,
|
||||
// we've currently modified the typescript compiler to check for any "<package-name>"
|
||||
// ambient modules when resolving an npm specifier at "npm:<package-name>"
|
||||
declare module "@denotest/types-ambient" {
|
||||
class Test {
|
||||
prop: number;
|
||||
}
|
||||
}
|
3
cli/tests/testdata/npm/registry/@denotest/types-ambient/1.0.0/index.js
vendored
Normal file
3
cli/tests/testdata/npm/registry/@denotest/types-ambient/1.0.0/index.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
export class Test {
|
||||
prop = 5;
|
||||
}
|
5
cli/tests/testdata/npm/registry/@denotest/types-ambient/1.0.0/package.json
vendored
Normal file
5
cli/tests/testdata/npm/registry/@denotest/types-ambient/1.0.0/package.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "@denotest/types-ambient",
|
||||
"version": "1.0.0",
|
||||
"types": "./index.d.ts"
|
||||
}
|
BIN
cli/tests/testdata/npm/registry/@types/node/node-18.8.2.tgz
vendored
Normal file
BIN
cli/tests/testdata/npm/registry/@types/node/node-18.8.2.tgz
vendored
Normal file
Binary file not shown.
73
cli/tests/testdata/npm/registry/@types/node/registry.json
vendored
Normal file
73
cli/tests/testdata/npm/registry/@types/node/registry.json
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
{
|
||||
"_id": "@types/node",
|
||||
"_rev": "8944-025a921c7561ec7339c70b87995cb358",
|
||||
"name": "@types/node",
|
||||
"description": "TypeScript definitions for Node.js",
|
||||
"dist-tags": {
|
||||
"latest": "18.8.2"
|
||||
},
|
||||
"versions": {
|
||||
"18.8.2": {
|
||||
"name": "@types/node",
|
||||
"version": "18.8.2",
|
||||
"description": "TypeScript definitions for Node.js",
|
||||
"homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node",
|
||||
"license": "MIT",
|
||||
"contributors": [
|
||||
],
|
||||
"main": "",
|
||||
"types": "index.d.ts",
|
||||
"typesVersions": { "<4.9.0-0": { "*": ["ts4.8/*"] } },
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
|
||||
"directory": "types/node"
|
||||
},
|
||||
"scripts": {},
|
||||
"dependencies": {},
|
||||
"typesPublisherContentHash": "034172ea945b66afc6502e6be34d6fb957c596091e39cf43672e8aca563a8c66",
|
||||
"typeScriptVersion": "4.1",
|
||||
"_id": "@types/node@18.8.2",
|
||||
"dist": {
|
||||
"integrity": "sha512-cRMwIgdDN43GO4xMWAfJAecYn8wV4JbsOGHNfNUIDiuYkUYAR5ec4Rj7IO2SAhFPEfpPtLtUTbbny/TCT7aDwA==",
|
||||
"shasum": "17d42c6322d917764dd3d2d3a10d7884925de067",
|
||||
"tarball": "http://localhost:4545/npm/registry/@types/node/node-18.8.2.tgz",
|
||||
"fileCount": 124,
|
||||
"unpackedSize": 3524549,
|
||||
"signatures": [
|
||||
{
|
||||
"keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA",
|
||||
"sig": "MEYCIQCAqI3XibndhBD647C/13AFb58Fhmg4WmfCoGrIYrgtzwIhAIB0b0D58Tigwb3qKaOVsKnuYOOr0strAmprZSCi/+oq"
|
||||
}
|
||||
],
|
||||
"npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjPFItACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmrKAg/+IwaUWPgePlO4IxW7CVhFEEFiyhjEH3FHe0ogC3YmreoBFv/A\r\nPwQrwObdskbGWrBzsAOVFvhzYktzP3kc857HtU2ia9FXeaEYvsSQBqh6jZfA\r\njR1+h+jn+W5JnmbnwRGfNn2riCo/un4tYoZ4o/bKiMdNd9WrdIs0Oii1Dd4N\r\nnsBXPb05UPPP4Uu8cz68u1bj+QQ6aslr6keGNyNeILf8bJsEfcfVkEO3l4cu\r\njyTIrxiD+tM8jrUE9CDeodF8CZNuvLh3hqdMPJeH3U47qkDtPDKEOvZTbyYm\r\ngodto6mcnuBr8F9vmikAQfGiXV0U2cg2XRjWc1lI8HT4X0kESTIo+nzNuliD\r\niTpfjyZHdKBGGIuHP1Ou9dVvx4t5XZ1JsK9EK5WTilvAlu/qZrynxXxAV3Rc\r\nvL9UhIacISprMWB3Sohl9ZtfcmGnV/KMRpM+yPZOWp39gQQCKaKF/j2f/mir\r\n8YFbFUnySZkXKzxgsgjrSsh9QiK2dYAzcqHlazITeFN9jOYRzYUjAFj3qOFm\r\n7o1eJpW0qM5vgR+fPq30WxcdcQ4PaWgHSlb/ll8hiwQG1ZUihO/1RU7FtDoc\r\n1KwcfrGOAJPL6vBSLPReUkhDIUTSBwfmvfMxzqD1aDp6YV5WX7h03B0dWbPJ\r\nmPJmJZjjZje4Trk9jBJ5/ZLpB8/zkrDKvhE=\r\n=LPWa\r\n-----END PGP SIGNATURE-----\r\n"
|
||||
},
|
||||
"_npmUser": { "name": "types", "email": "ts-npm-types@microsoft.com" },
|
||||
"directories": {},
|
||||
"maintainers": [
|
||||
{ "name": "types", "email": "ts-npm-types@microsoft.com" }
|
||||
],
|
||||
"_npmOperationalInternal": {
|
||||
"host": "s3://npm-registry-packages",
|
||||
"tmp": "tmp/node_18.8.2_1664897581729_0.9746861340465625"
|
||||
},
|
||||
"_hasShrinkwrap": false
|
||||
}
|
||||
},
|
||||
"readme": "[object Object]",
|
||||
"maintainers": [{ "name": "types", "email": "ts-npm-types@microsoft.com" }],
|
||||
"time": {
|
||||
"18.8.2": "2022-10-04T15:33:01.913Z"
|
||||
},
|
||||
"license": "MIT",
|
||||
"readmeFilename": "",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
|
||||
"directory": "types/node"
|
||||
},
|
||||
"users": {
|
||||
},
|
||||
"contributors": [],
|
||||
"homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node"
|
||||
}
|
5
cli/tests/testdata/npm/types_ambient_module/import_map.json
vendored
Normal file
5
cli/tests/testdata/npm/types_ambient_module/import_map.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"imports": {
|
||||
"types-ambient": "npm:@denotest/types-ambient"
|
||||
}
|
||||
}
|
21
cli/tests/testdata/npm/types_ambient_module/main.out
vendored
Normal file
21
cli/tests/testdata/npm/types_ambient_module/main.out
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
error: TS2551 [ERROR]: Property 'Test2' does not exist on type 'typeof import("@denotest/types-ambient")'. Did you mean 'Test'?
|
||||
console.log(import1.Test2); // should error
|
||||
~~~~~
|
||||
at file:///[WILDCARD]/types_ambient_module/main.ts:5:21
|
||||
|
||||
'Test' is declared here.
|
||||
class Test {
|
||||
~~~~
|
||||
at file:///[WILDCARD]/@denotest/types-ambient/1.0.0/index.d.ts:7:9
|
||||
|
||||
TS2551 [ERROR]: Property 'Test2' does not exist on type 'typeof import("@denotest/types-ambient")'. Did you mean 'Test'?
|
||||
console.log(import2.Test2); // should error
|
||||
~~~~~
|
||||
at file:///[WILDCARD]/types_ambient_module/main.ts:7:21
|
||||
|
||||
'Test' is declared here.
|
||||
class Test {
|
||||
~~~~
|
||||
at file:///[WILDCARD]/@denotest/types-ambient/1.0.0/index.d.ts:7:9
|
||||
|
||||
Found 2 errors.
|
7
cli/tests/testdata/npm/types_ambient_module/main.ts
vendored
Normal file
7
cli/tests/testdata/npm/types_ambient_module/main.ts
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
import * as import1 from "npm:@denotest/types-ambient";
|
||||
import * as import2 from "npm:@denotest/types-ambient@1";
|
||||
|
||||
console.log(import1.Test);
|
||||
console.log(import1.Test2); // should error
|
||||
console.log(import2.Test);
|
||||
console.log(import2.Test2); // should error
|
9
cli/tests/testdata/npm/types_ambient_module/main_import_map.out
vendored
Normal file
9
cli/tests/testdata/npm/types_ambient_module/main_import_map.out
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
error: TS2551 [ERROR]: Property 'Test2' does not exist on type 'typeof import("@denotest/types-ambient")'. Did you mean 'Test'?
|
||||
console.log(mod.Test2); // should error
|
||||
~~~~~
|
||||
at file:///[WILDCARD]/main_import_map.ts:4:17
|
||||
|
||||
'Test' is declared here.
|
||||
class Test {
|
||||
~~~~
|
||||
at file:///[WILDCARD]/@denotest/types-ambient/1.0.0/index.d.ts:7:9
|
4
cli/tests/testdata/npm/types_ambient_module/main_import_map.ts
vendored
Normal file
4
cli/tests/testdata/npm/types_ambient_module/main_import_map.ts
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
import * as mod from "npm:@denotest/types-ambient";
|
||||
|
||||
console.log(mod.Test);
|
||||
console.log(mod.Test2); // should error
|
|
@ -18,6 +18,7 @@ use crate::cache::TypeCheckCache;
|
|||
use crate::diagnostics::Diagnostics;
|
||||
use crate::graph_util::GraphData;
|
||||
use crate::graph_util::ModuleEntry;
|
||||
use crate::npm::NpmPackageResolver;
|
||||
use crate::tsc;
|
||||
use crate::tsc::Stats;
|
||||
use crate::version;
|
||||
|
@ -57,6 +58,7 @@ pub fn check(
|
|||
roots: &[(ModuleSpecifier, ModuleKind)],
|
||||
graph_data: Arc<RwLock<GraphData>>,
|
||||
cache: &TypeCheckCache,
|
||||
npm_resolver: NpmPackageResolver,
|
||||
options: CheckOptions,
|
||||
) -> Result<CheckResult, AnyError> {
|
||||
let check_js = options.ts_config.get_check_js();
|
||||
|
@ -106,6 +108,7 @@ pub fn check(
|
|||
graph_data,
|
||||
hash_data,
|
||||
maybe_config_specifier: options.maybe_config_specifier,
|
||||
maybe_npm_resolver: Some(npm_resolver.clone()),
|
||||
maybe_tsbuildinfo,
|
||||
root_names,
|
||||
})?;
|
||||
|
@ -114,6 +117,9 @@ pub fn check(
|
|||
response.diagnostics.filter(|d| {
|
||||
if let Some(file_name) = &d.file_name {
|
||||
!file_name.starts_with("http")
|
||||
&& ModuleSpecifier::parse(file_name)
|
||||
.map(|specifier| !npm_resolver.in_npm_package(&specifier))
|
||||
.unwrap_or(true)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
|
132
cli/tsc.rs
132
cli/tsc.rs
|
@ -4,6 +4,12 @@ use crate::args::TsConfig;
|
|||
use crate::diagnostics::Diagnostics;
|
||||
use crate::graph_util::GraphData;
|
||||
use crate::graph_util::ModuleEntry;
|
||||
use crate::node;
|
||||
use crate::node::node_resolve_npm_reference;
|
||||
use crate::node::NodeResolution;
|
||||
use crate::node::NodeResolutionMode;
|
||||
use crate::npm::NpmPackageReference;
|
||||
use crate::npm::NpmPackageResolver;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_core::anyhow::anyhow;
|
||||
|
@ -28,6 +34,7 @@ use deno_core::RuntimeOptions;
|
|||
use deno_core::Snapshot;
|
||||
use deno_graph::Resolved;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
|
@ -171,7 +178,7 @@ fn get_maybe_hash(
|
|||
}
|
||||
|
||||
/// Hash the URL so it can be sent to `tsc` in a supportable way
|
||||
fn hash_url(specifier: &ModuleSpecifier, media_type: &MediaType) -> String {
|
||||
fn hash_url(specifier: &ModuleSpecifier, media_type: MediaType) -> String {
|
||||
let hash = crate::checksum::gen(&[specifier.path().as_bytes()]);
|
||||
format!(
|
||||
"{}:///{}{}",
|
||||
|
@ -187,7 +194,7 @@ fn hash_url(specifier: &ModuleSpecifier, media_type: &MediaType) -> String {
|
|||
/// think a `.js` version exists, when it doesn't.
|
||||
fn maybe_remap_specifier(
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: &MediaType,
|
||||
media_type: MediaType,
|
||||
) -> Option<String> {
|
||||
let path = if specifier.scheme() == "file" {
|
||||
if let Ok(path) = specifier.to_file_path() {
|
||||
|
@ -279,6 +286,7 @@ pub struct Request {
|
|||
pub graph_data: Arc<RwLock<GraphData>>,
|
||||
pub hash_data: Vec<Vec<u8>>,
|
||||
pub maybe_config_specifier: Option<ModuleSpecifier>,
|
||||
pub maybe_npm_resolver: Option<NpmPackageResolver>,
|
||||
pub maybe_tsbuildinfo: Option<String>,
|
||||
/// A vector of strings that represent the root/entry point modules for the
|
||||
/// program.
|
||||
|
@ -295,13 +303,14 @@ pub struct Response {
|
|||
pub stats: Stats,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
struct State {
|
||||
hash_data: Vec<Vec<u8>>,
|
||||
graph_data: Arc<RwLock<GraphData>>,
|
||||
maybe_config_specifier: Option<ModuleSpecifier>,
|
||||
maybe_tsbuildinfo: Option<String>,
|
||||
maybe_response: Option<RespondArgs>,
|
||||
maybe_npm_resolver: Option<NpmPackageResolver>,
|
||||
remapped_specifiers: HashMap<String, ModuleSpecifier>,
|
||||
root_map: HashMap<String, ModuleSpecifier>,
|
||||
}
|
||||
|
@ -311,6 +320,7 @@ impl State {
|
|||
graph_data: Arc<RwLock<GraphData>>,
|
||||
hash_data: Vec<Vec<u8>>,
|
||||
maybe_config_specifier: Option<ModuleSpecifier>,
|
||||
maybe_npm_resolver: Option<NpmPackageResolver>,
|
||||
maybe_tsbuildinfo: Option<String>,
|
||||
root_map: HashMap<String, ModuleSpecifier>,
|
||||
remapped_specifiers: HashMap<String, ModuleSpecifier>,
|
||||
|
@ -319,6 +329,7 @@ impl State {
|
|||
hash_data,
|
||||
graph_data,
|
||||
maybe_config_specifier,
|
||||
maybe_npm_resolver,
|
||||
maybe_tsbuildinfo,
|
||||
maybe_response: None,
|
||||
remapped_specifiers,
|
||||
|
@ -417,7 +428,7 @@ struct LoadArgs {
|
|||
specifier: String,
|
||||
}
|
||||
|
||||
pub fn as_ts_script_kind(media_type: &MediaType) -> i32 {
|
||||
pub fn as_ts_script_kind(media_type: MediaType) -> i32 {
|
||||
match media_type {
|
||||
MediaType::JavaScript => 1,
|
||||
MediaType::Jsx => 2,
|
||||
|
@ -431,7 +442,10 @@ pub fn as_ts_script_kind(media_type: &MediaType) -> i32 {
|
|||
MediaType::Dcts => 3,
|
||||
MediaType::Tsx => 4,
|
||||
MediaType::Json => 6,
|
||||
_ => 0,
|
||||
MediaType::SourceMap
|
||||
| MediaType::TsBuildInfo
|
||||
| MediaType::Wasm
|
||||
| MediaType::Unknown => 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -446,19 +460,19 @@ fn op_load(state: &mut OpState, args: Value) -> Result<Value, AnyError> {
|
|||
let mut media_type = MediaType::Unknown;
|
||||
let graph_data = state.graph_data.read();
|
||||
let data = if &v.specifier == "deno:///.tsbuildinfo" {
|
||||
state.maybe_tsbuildinfo.as_deref()
|
||||
state.maybe_tsbuildinfo.as_deref().map(Cow::Borrowed)
|
||||
// in certain situations we return a "blank" module to tsc and we need to
|
||||
// handle the request for that module here.
|
||||
} else if &v.specifier == "deno:///missing_dependency.d.ts" {
|
||||
hash = Some("1".to_string());
|
||||
media_type = MediaType::Dts;
|
||||
Some("declare const __: any;\nexport = __;\n")
|
||||
Some(Cow::Borrowed("declare const __: any;\nexport = __;\n"))
|
||||
} else if v.specifier.starts_with("asset:///") {
|
||||
let name = v.specifier.replace("asset:///", "");
|
||||
let maybe_source = get_asset(&name);
|
||||
hash = get_maybe_hash(maybe_source, &state.hash_data);
|
||||
media_type = MediaType::from(&v.specifier);
|
||||
maybe_source
|
||||
maybe_source.map(Cow::Borrowed)
|
||||
} else {
|
||||
let specifier = if let Some(remapped_specifier) =
|
||||
state.remapped_specifiers.get(&v.specifier)
|
||||
|
@ -477,18 +491,31 @@ fn op_load(state: &mut OpState, args: Value) -> Result<Value, AnyError> {
|
|||
graph_data.get(&graph_data.follow_redirect(&specifier))
|
||||
{
|
||||
media_type = *mt;
|
||||
Some(code as &str)
|
||||
Some(Cow::Borrowed(code as &str))
|
||||
} else if state
|
||||
.maybe_npm_resolver
|
||||
.as_ref()
|
||||
.map(|resolver| resolver.in_npm_package(&specifier))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
media_type = MediaType::from(&specifier);
|
||||
let file_path = specifier.to_file_path().unwrap();
|
||||
let code = std::fs::read_to_string(&file_path)
|
||||
.with_context(|| format!("Unable to load {}", file_path.display()))?;
|
||||
Some(Cow::Owned(code))
|
||||
} else {
|
||||
media_type = MediaType::Unknown;
|
||||
None
|
||||
};
|
||||
hash = get_maybe_hash(maybe_source, &state.hash_data);
|
||||
hash = get_maybe_hash(maybe_source.as_deref(), &state.hash_data);
|
||||
maybe_source
|
||||
};
|
||||
|
||||
Ok(
|
||||
json!({ "data": data, "version": hash, "scriptKind": as_ts_script_kind(&media_type) }),
|
||||
)
|
||||
Ok(json!({
|
||||
"data": data,
|
||||
"version": hash,
|
||||
"scriptKind": as_ts_script_kind(media_type),
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
|
@ -550,17 +577,51 @@ fn op_resolve(
|
|||
let types = graph_data.follow_redirect(specifier);
|
||||
match graph_data.get(&types) {
|
||||
Some(ModuleEntry::Module { media_type, .. }) => {
|
||||
Some((types, media_type))
|
||||
Some((types, *media_type))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
_ => Some((specifier, media_type)),
|
||||
_ => Some((specifier, *media_type)),
|
||||
},
|
||||
_ => None,
|
||||
_ => {
|
||||
// handle npm:<package> urls
|
||||
if let Ok(npm_ref) =
|
||||
NpmPackageReference::from_specifier(&specifier)
|
||||
{
|
||||
if let Some(npm_resolver) = &state.maybe_npm_resolver {
|
||||
Some(resolve_npm_package_reference_types(
|
||||
&npm_ref,
|
||||
npm_resolver,
|
||||
)?)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
_ => {
|
||||
state.maybe_npm_resolver.as_ref().and_then(|npm_resolver| {
|
||||
if npm_resolver.in_npm_package(&referrer) {
|
||||
// we're in an npm package, so use node resolution
|
||||
Some(NodeResolution::into_specifier_and_media_type(
|
||||
node::node_resolve(
|
||||
specifier,
|
||||
&referrer,
|
||||
node::NodeResolutionMode::Types,
|
||||
npm_resolver,
|
||||
)
|
||||
.ok()
|
||||
.flatten(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
let result = match maybe_result {
|
||||
Some((specifier, media_type)) => {
|
||||
|
@ -599,6 +660,33 @@ fn op_resolve(
|
|||
Ok(resolved)
|
||||
}
|
||||
|
||||
pub fn resolve_npm_package_reference_types(
|
||||
npm_ref: &NpmPackageReference,
|
||||
npm_resolver: &NpmPackageResolver,
|
||||
) -> Result<(ModuleSpecifier, MediaType), AnyError> {
|
||||
let maybe_resolution = node_resolve_npm_reference(
|
||||
npm_ref,
|
||||
NodeResolutionMode::Types,
|
||||
npm_resolver,
|
||||
)?;
|
||||
Ok(NodeResolution::into_specifier_and_media_type(
|
||||
maybe_resolution,
|
||||
))
|
||||
}
|
||||
|
||||
#[op]
|
||||
fn op_is_node_file(state: &mut OpState, path: String) -> bool {
|
||||
let state = state.borrow::<State>();
|
||||
match ModuleSpecifier::parse(&path) {
|
||||
Ok(specifier) => state
|
||||
.maybe_npm_resolver
|
||||
.as_ref()
|
||||
.map(|r| r.in_npm_package(&specifier))
|
||||
.unwrap_or(false),
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Eq, PartialEq)]
|
||||
struct RespondArgs {
|
||||
pub diagnostics: Diagnostics,
|
||||
|
@ -629,13 +717,13 @@ pub fn exec(request: Request) -> Result<Response, AnyError> {
|
|||
.iter()
|
||||
.map(|(s, mt)| match s.scheme() {
|
||||
"data" | "blob" => {
|
||||
let specifier_str = hash_url(s, mt);
|
||||
let specifier_str = hash_url(s, *mt);
|
||||
remapped_specifiers.insert(specifier_str.clone(), s.clone());
|
||||
specifier_str
|
||||
}
|
||||
_ => {
|
||||
let ext_media_type = get_tsc_media_type(s);
|
||||
if mt != &ext_media_type {
|
||||
if *mt != ext_media_type {
|
||||
let new_specifier = format!("{}{}", s, mt.as_ts_extension());
|
||||
root_map.insert(new_specifier.clone(), s.clone());
|
||||
new_specifier
|
||||
|
@ -653,6 +741,7 @@ pub fn exec(request: Request) -> Result<Response, AnyError> {
|
|||
op_create_hash::decl(),
|
||||
op_emit::decl(),
|
||||
op_exists::decl(),
|
||||
op_is_node_file::decl(),
|
||||
op_load::decl(),
|
||||
op_resolve::decl(),
|
||||
op_respond::decl(),
|
||||
|
@ -662,6 +751,7 @@ pub fn exec(request: Request) -> Result<Response, AnyError> {
|
|||
request.graph_data.clone(),
|
||||
request.hash_data.clone(),
|
||||
request.maybe_config_specifier.clone(),
|
||||
request.maybe_npm_resolver.clone(),
|
||||
request.maybe_tsbuildinfo.clone(),
|
||||
root_map.clone(),
|
||||
remapped_specifiers.clone(),
|
||||
|
@ -771,6 +861,7 @@ mod tests {
|
|||
Arc::new(RwLock::new((&graph).into())),
|
||||
hash_data,
|
||||
None,
|
||||
None,
|
||||
maybe_tsbuildinfo,
|
||||
HashMap::new(),
|
||||
HashMap::new(),
|
||||
|
@ -820,6 +911,7 @@ mod tests {
|
|||
graph_data: Arc::new(RwLock::new((&graph).into())),
|
||||
hash_data,
|
||||
maybe_config_specifier: None,
|
||||
maybe_npm_resolver: None,
|
||||
maybe_tsbuildinfo: None,
|
||||
root_names: vec![(specifier.clone(), MediaType::TypeScript)],
|
||||
};
|
||||
|
@ -865,7 +957,7 @@ mod tests {
|
|||
"data:application/javascript,console.log(\"Hello%20Deno\");",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(hash_url(&specifier, &MediaType::JavaScript), "data:///d300ea0796bd72b08df10348e0b70514c021f2e45bfe59cec24e12e97cd79c58.js");
|
||||
assert_eq!(hash_url(&specifier, MediaType::JavaScript), "data:///d300ea0796bd72b08df10348e0b70514c021f2e45bfe59cec24e12e97cd79c58.js");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
384
cli/tsc/00_typescript.js
vendored
384
cli/tsc/00_typescript.js
vendored
|
@ -48949,12 +48949,23 @@ var ts;
|
|||
var emitResolver = createResolver();
|
||||
var nodeBuilder = createNodeBuilder();
|
||||
var globals = ts.createSymbolTable();
|
||||
var nodeGlobals = ts.createSymbolTable();
|
||||
var undefinedSymbol = createSymbol(4 /* SymbolFlags.Property */, "undefined");
|
||||
undefinedSymbol.declarations = [];
|
||||
var globalThisSymbol = createSymbol(1536 /* SymbolFlags.Module */, "globalThis", 8 /* CheckFlags.Readonly */);
|
||||
globalThisSymbol.exports = globals;
|
||||
globalThisSymbol.declarations = [];
|
||||
globals.set(globalThisSymbol.escapedName, globalThisSymbol);
|
||||
var denoContext = ts.deno.createDenoForkContext({
|
||||
globals: globals,
|
||||
nodeGlobals: nodeGlobals,
|
||||
mergeSymbol: mergeSymbol,
|
||||
ambientModuleSymbolRegex: ambientModuleSymbolRegex,
|
||||
});
|
||||
var nodeGlobalThisSymbol = createSymbol(1536 /* SymbolFlags.Module */, "globalThis", 8 /* CheckFlags.Readonly */);
|
||||
nodeGlobalThisSymbol.exports = denoContext.combinedGlobals;
|
||||
nodeGlobalThisSymbol.declarations = [];
|
||||
nodeGlobals.set(nodeGlobalThisSymbol.escapedName, nodeGlobalThisSymbol);
|
||||
var argumentsSymbol = createSymbol(4 /* SymbolFlags.Property */, "arguments");
|
||||
var requireSymbol = createSymbol(4 /* SymbolFlags.Property */, "require");
|
||||
/** This will be set during calls to `getResolvedSignature` where services determines an apparent number of arguments greater than what is actually provided. */
|
||||
|
@ -49464,6 +49475,7 @@ var ts;
|
|||
var reverseMappedCache = new ts.Map();
|
||||
var inInferTypeForHomomorphicMappedType = false;
|
||||
var ambientModulesCache;
|
||||
var nodeAmbientModulesCache;
|
||||
/**
|
||||
* List of every ambient module with a "*" wildcard.
|
||||
* Unlike other ambient modules, these can't be stored in `globals` because symbol tables only deal with exact matches.
|
||||
|
@ -49851,7 +49863,7 @@ var ts;
|
|||
// Do not report an error when merging `var globalThis` with the built-in `globalThis`,
|
||||
// as we will already report a "Declaration name conflicts..." error, and this error
|
||||
// won't make much sense.
|
||||
if (target !== globalThisSymbol) {
|
||||
if (target !== globalThisSymbol && target !== nodeGlobalThisSymbol) {
|
||||
error(source.declarations && ts.getNameOfDeclaration(source.declarations[0]), ts.Diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, symbolToString(target));
|
||||
}
|
||||
}
|
||||
|
@ -49950,7 +49962,7 @@ var ts;
|
|||
return;
|
||||
}
|
||||
if (ts.isGlobalScopeAugmentation(moduleAugmentation)) {
|
||||
mergeSymbolTable(globals, moduleAugmentation.symbol.exports);
|
||||
denoContext.mergeGlobalSymbolTable(moduleAugmentation, moduleAugmentation.symbol.exports);
|
||||
}
|
||||
else {
|
||||
// find a module that about to be augmented
|
||||
|
@ -50631,7 +50643,12 @@ var ts;
|
|||
}
|
||||
}
|
||||
if (!excludeGlobals) {
|
||||
result = lookup(globals, name, meaning);
|
||||
if (denoContext.hasNodeSourceFile(lastLocation)) {
|
||||
result = lookup(nodeGlobals, name, meaning);
|
||||
}
|
||||
if (!result) {
|
||||
result = lookup(globals, name, meaning);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
|
@ -51888,6 +51905,24 @@ var ts;
|
|||
: undefined;
|
||||
}
|
||||
function resolveExternalModule(location, moduleReference, moduleNotFoundError, errorNode, isForAugmentation) {
|
||||
var _a;
|
||||
if (isForAugmentation === void 0) { isForAugmentation = false; }
|
||||
var result = resolveExternalModuleInner(location, moduleReference, moduleNotFoundError, errorNode, isForAugmentation);
|
||||
// deno: attempt to resolve an npm package reference to its bare specifier w/ path ambient module
|
||||
// when not found and the symbol has zero exports
|
||||
if (moduleReference.startsWith("npm:") && (result == null || ((_a = result === null || result === void 0 ? void 0 : result.exports) === null || _a === void 0 ? void 0 : _a.size) === 0)) {
|
||||
var npmPackageRef = ts.deno.tryParseNpmPackageReference(moduleReference);
|
||||
if (npmPackageRef) {
|
||||
var bareSpecifier = npmPackageRef.name + (npmPackageRef.subPath == null ? "" : "/" + npmPackageRef.subPath);
|
||||
var ambientModule = tryFindAmbientModule(bareSpecifier, /*withAugmentations*/ true);
|
||||
if (ambientModule) {
|
||||
return ambientModule;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function resolveExternalModuleInner(location, moduleReference, moduleNotFoundError, errorNode, isForAugmentation) {
|
||||
var _a, _b, _c, _d, _e, _f, _g, _h;
|
||||
if (isForAugmentation === void 0) { isForAugmentation = false; }
|
||||
if (ts.startsWith(moduleReference, "@types/")) {
|
||||
|
@ -52630,6 +52665,12 @@ var ts;
|
|||
if (typeof state_2 === "object")
|
||||
return state_2.value;
|
||||
}
|
||||
if (denoContext.hasNodeSourceFile(enclosingDeclaration)) {
|
||||
result = callback(nodeGlobals, /*ignoreQualification*/ undefined, /*isLocalNameLookup*/ true);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return callback(globals, /*ignoreQualification*/ undefined, /*isLocalNameLookup*/ true);
|
||||
}
|
||||
function getQualifiedLeftMeaning(rightMeaning) {
|
||||
|
@ -52713,7 +52754,11 @@ var ts;
|
|||
}
|
||||
});
|
||||
// If there's no result and we're looking at the global symbol table, treat `globalThis` like an alias and try to lookup thru that
|
||||
return result || (symbols === globals ? getCandidateListForSymbol(globalThisSymbol, globalThisSymbol, ignoreQualification) : undefined);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
var globalSymbol = symbols === nodeGlobals ? nodeGlobalThisSymbol : symbols === globals ? globalThisSymbol : undefined;
|
||||
return globalSymbol != null ? getCandidateListForSymbol(globalSymbol, globalSymbol, ignoreQualification) : undefined;
|
||||
}
|
||||
function getCandidateListForSymbol(symbolFromSymbolTable, resolvedImportedSymbol, ignoreQualification) {
|
||||
if (isAccessible(symbolFromSymbolTable, resolvedImportedSymbol, ignoreQualification)) {
|
||||
|
@ -59159,7 +59204,7 @@ var ts;
|
|||
var indexInfos;
|
||||
if (symbol.exports) {
|
||||
members = getExportsOfSymbol(symbol);
|
||||
if (symbol === globalThisSymbol) {
|
||||
if (symbol === globalThisSymbol || symbol === nodeGlobalThisSymbol) {
|
||||
var varsOnly_1 = new ts.Map();
|
||||
members.forEach(function (p) {
|
||||
var _a;
|
||||
|
@ -60321,7 +60366,7 @@ var ts;
|
|||
if (ts.isExternalModuleNameRelative(moduleName)) {
|
||||
return undefined;
|
||||
}
|
||||
var symbol = getSymbol(globals, '"' + moduleName + '"', 512 /* SymbolFlags.ValueModule */);
|
||||
var symbol = getSymbol(denoContext.combinedGlobals, '"' + moduleName + '"', 512 /* SymbolFlags.ValueModule */);
|
||||
// merged symbol is module declaration symbol combined with all augmentations
|
||||
return symbol && withAugmentations ? getMergedSymbol(symbol) : symbol;
|
||||
}
|
||||
|
@ -62988,6 +63033,10 @@ var ts;
|
|||
if (objectType.symbol === globalThisSymbol && propName !== undefined && globalThisSymbol.exports.has(propName) && (globalThisSymbol.exports.get(propName).flags & 418 /* SymbolFlags.BlockScoped */)) {
|
||||
error(accessExpression, ts.Diagnostics.Property_0_does_not_exist_on_type_1, ts.unescapeLeadingUnderscores(propName), typeToString(objectType));
|
||||
}
|
||||
// deno: ensure condition and body match the above
|
||||
else if (objectType.symbol === nodeGlobalThisSymbol && propName !== undefined && nodeGlobalThisSymbol.exports.has(propName) && (nodeGlobalThisSymbol.exports.get(propName).flags & 418 /* SymbolFlags.BlockScoped */)) {
|
||||
error(accessExpression, ts.Diagnostics.Property_0_does_not_exist_on_type_1, ts.unescapeLeadingUnderscores(propName), typeToString(objectType));
|
||||
}
|
||||
else if (noImplicitAny && !compilerOptions.suppressImplicitAnyIndexErrors && !(accessFlags & 128 /* AccessFlags.SuppressNoImplicitAnyError */)) {
|
||||
if (propName !== undefined && typeHasStaticProperty(propName, objectType)) {
|
||||
var typeName = typeToString(objectType);
|
||||
|
@ -71860,7 +71909,7 @@ var ts;
|
|||
if (type.flags & 1048576 /* TypeFlags.Union */
|
||||
|| type.flags & 524288 /* TypeFlags.Object */ && declaredType !== type && !(declaredType === unknownType && isEmptyAnonymousObjectType(type))
|
||||
|| ts.isThisTypeParameter(type)
|
||||
|| type.flags & 2097152 /* TypeFlags.Intersection */ && ts.every(type.types, function (t) { return t.symbol !== globalThisSymbol; })) {
|
||||
|| type.flags & 2097152 /* TypeFlags.Intersection */ && ts.every(type.types, function (t) { return t.symbol !== globalThisSymbol && t.symbol !== nodeGlobalThisSymbol; })) {
|
||||
return filterType(type, function (t) { return isTypePresencePossible(t, name, assumeTrue); });
|
||||
}
|
||||
return type;
|
||||
|
@ -73019,6 +73068,9 @@ var ts;
|
|||
return undefinedType;
|
||||
}
|
||||
else if (includeGlobalThis) {
|
||||
if (denoContext.hasNodeSourceFile(container)) {
|
||||
return getTypeOfSymbol(nodeGlobalThisSymbol);
|
||||
}
|
||||
return getTypeOfSymbol(globalThisSymbol);
|
||||
}
|
||||
}
|
||||
|
@ -75690,6 +75742,11 @@ var ts;
|
|||
}
|
||||
return anyType;
|
||||
}
|
||||
// deno: ensure condition matches above
|
||||
if (leftType.symbol === nodeGlobalThisSymbol) {
|
||||
// deno: don't bother with errors like above for simplicity
|
||||
return anyType;
|
||||
}
|
||||
if (right.escapedText && !checkAndReportErrorForExtendingInterface(node)) {
|
||||
reportNonexistentProperty(right, ts.isThisTypeParameter(leftType) ? apparentType : leftType, isUncheckedJS);
|
||||
}
|
||||
|
@ -75997,7 +76054,7 @@ var ts;
|
|||
if (symbol)
|
||||
return symbol;
|
||||
var candidates;
|
||||
if (symbols === globals) {
|
||||
if (symbols === globals || symbols === nodeGlobals) {
|
||||
var primitives = ts.mapDefined(["string", "number", "boolean", "object", "bigint", "symbol"], function (s) { return symbols.has((s.charAt(0).toUpperCase() + s.slice(1)))
|
||||
? createSymbol(524288 /* SymbolFlags.TypeAlias */, s)
|
||||
: undefined; });
|
||||
|
@ -86656,7 +86713,7 @@ var ts;
|
|||
// find immediate value referenced by exported name (SymbolFlags.Alias is set so we don't chase down aliases)
|
||||
var symbol = resolveName(exportedName, exportedName.escapedText, 111551 /* SymbolFlags.Value */ | 788968 /* SymbolFlags.Type */ | 1920 /* SymbolFlags.Namespace */ | 2097152 /* SymbolFlags.Alias */,
|
||||
/*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ true);
|
||||
if (symbol && (symbol === undefinedSymbol || symbol === globalThisSymbol || symbol.declarations && isGlobalSourceFile(getDeclarationContainer(symbol.declarations[0])))) {
|
||||
if (symbol && (symbol === undefinedSymbol || symbol === globalThisSymbol || symbol === nodeGlobalThisSymbol || symbol.declarations && isGlobalSourceFile(getDeclarationContainer(symbol.declarations[0])))) {
|
||||
error(exportedName, ts.Diagnostics.Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, ts.idText(exportedName));
|
||||
}
|
||||
else {
|
||||
|
@ -87343,6 +87400,9 @@ var ts;
|
|||
isStaticSymbol = ts.isStatic(location);
|
||||
location = location.parent;
|
||||
}
|
||||
if (denoContext.hasNodeSourceFile(location)) {
|
||||
copySymbols(nodeGlobals, meaning);
|
||||
}
|
||||
copySymbols(globals, meaning);
|
||||
}
|
||||
/**
|
||||
|
@ -88717,25 +88777,24 @@ var ts;
|
|||
amalgamatedDuplicates = new ts.Map();
|
||||
// Initialize global symbol table
|
||||
var augmentations;
|
||||
for (var _b = 0, _c = host.getSourceFiles(); _b < _c.length; _b++) {
|
||||
var file = _c[_b];
|
||||
var _loop_35 = function (file) {
|
||||
if (file.redirectInfo) {
|
||||
continue;
|
||||
return "continue";
|
||||
}
|
||||
if (!ts.isExternalOrCommonJsModule(file)) {
|
||||
// It is an error for a non-external-module (i.e. script) to declare its own `globalThis`.
|
||||
// We can't use `builtinGlobals` for this due to synthetic expando-namespace generation in JS files.
|
||||
var fileGlobalThisSymbol = file.locals.get("globalThis");
|
||||
if (fileGlobalThisSymbol === null || fileGlobalThisSymbol === void 0 ? void 0 : fileGlobalThisSymbol.declarations) {
|
||||
for (var _d = 0, _e = fileGlobalThisSymbol.declarations; _d < _e.length; _d++) {
|
||||
var declaration = _e[_d];
|
||||
for (var _h = 0, _j = fileGlobalThisSymbol.declarations; _h < _j.length; _h++) {
|
||||
var declaration = _j[_h];
|
||||
diagnostics.add(ts.createDiagnosticForNode(declaration, ts.Diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0, "globalThis"));
|
||||
}
|
||||
}
|
||||
mergeSymbolTable(globals, file.locals);
|
||||
denoContext.mergeGlobalSymbolTable(file, file.locals);
|
||||
}
|
||||
if (file.jsGlobalAugmentations) {
|
||||
mergeSymbolTable(globals, file.jsGlobalAugmentations);
|
||||
denoContext.mergeGlobalSymbolTable(file, file.jsGlobalAugmentations);
|
||||
}
|
||||
if (file.patternAmbientModules && file.patternAmbientModules.length) {
|
||||
patternAmbientModules = ts.concatenate(patternAmbientModules, file.patternAmbientModules);
|
||||
|
@ -88746,12 +88805,18 @@ var ts;
|
|||
if (file.symbol && file.symbol.globalExports) {
|
||||
// Merge in UMD exports with first-in-wins semantics (see #9771)
|
||||
var source = file.symbol.globalExports;
|
||||
var isNodeFile_1 = denoContext.hasNodeSourceFile(file);
|
||||
source.forEach(function (sourceSymbol, id) {
|
||||
if (!globals.has(id)) {
|
||||
globals.set(id, sourceSymbol);
|
||||
var envGlobals = isNodeFile_1 ? denoContext.getGlobalsForName(id) : globals;
|
||||
if (!envGlobals.has(id)) {
|
||||
envGlobals.set(id, sourceSymbol);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
for (var _b = 0, _c = host.getSourceFiles(); _b < _c.length; _b++) {
|
||||
var file = _c[_b];
|
||||
_loop_35(file);
|
||||
}
|
||||
// We do global augmentations separately from module augmentations (and before creating global types) because they
|
||||
// 1. Affect global types. We won't have the correct global types until global augmentations are merged. Also,
|
||||
|
@ -88762,10 +88827,10 @@ var ts;
|
|||
if (augmentations) {
|
||||
// merge _global_ module augmentations.
|
||||
// this needs to be done after global symbol table is initialized to make sure that all ambient modules are indexed
|
||||
for (var _f = 0, augmentations_1 = augmentations; _f < augmentations_1.length; _f++) {
|
||||
var list = augmentations_1[_f];
|
||||
for (var _g = 0, list_1 = list; _g < list_1.length; _g++) {
|
||||
var augmentation = list_1[_g];
|
||||
for (var _d = 0, augmentations_1 = augmentations; _d < augmentations_1.length; _d++) {
|
||||
var list = augmentations_1[_d];
|
||||
for (var _e = 0, list_1 = list; _e < list_1.length; _e++) {
|
||||
var augmentation = list_1[_e];
|
||||
if (!ts.isGlobalScopeAugmentation(augmentation.parent))
|
||||
continue;
|
||||
mergeModuleAugmentation(augmentation);
|
||||
|
@ -88778,6 +88843,7 @@ var ts;
|
|||
getSymbolLinks(argumentsSymbol).type = getGlobalType("IArguments", /*arity*/ 0, /*reportErrors*/ true);
|
||||
getSymbolLinks(unknownSymbol).type = errorType;
|
||||
getSymbolLinks(globalThisSymbol).type = createObjectType(16 /* ObjectFlags.Anonymous */, globalThisSymbol);
|
||||
getSymbolLinks(nodeGlobalThisSymbol).type = createObjectType(16 /* ObjectFlags.Anonymous */, nodeGlobalThisSymbol);
|
||||
// Initialize special types
|
||||
globalArrayType = getGlobalType("Array", /*arity*/ 1, /*reportErrors*/ true);
|
||||
globalObjectType = getGlobalType("Object", /*arity*/ 0, /*reportErrors*/ true);
|
||||
|
@ -88800,10 +88866,10 @@ var ts;
|
|||
if (augmentations) {
|
||||
// merge _nonglobal_ module augmentations.
|
||||
// this needs to be done after global symbol table is initialized to make sure that all ambient modules are indexed
|
||||
for (var _h = 0, augmentations_2 = augmentations; _h < augmentations_2.length; _h++) {
|
||||
var list = augmentations_2[_h];
|
||||
for (var _j = 0, list_2 = list; _j < list_2.length; _j++) {
|
||||
var augmentation = list_2[_j];
|
||||
for (var _f = 0, augmentations_2 = augmentations; _f < augmentations_2.length; _f++) {
|
||||
var list = augmentations_2[_f];
|
||||
for (var _g = 0, list_2 = list; _g < list_2.length; _g++) {
|
||||
var augmentation = list_2[_g];
|
||||
if (ts.isGlobalScopeAugmentation(augmentation.parent))
|
||||
continue;
|
||||
mergeModuleAugmentation(augmentation);
|
||||
|
@ -90373,17 +90439,30 @@ var ts;
|
|||
}
|
||||
return false;
|
||||
}
|
||||
function getAmbientModules() {
|
||||
if (!ambientModulesCache) {
|
||||
ambientModulesCache = [];
|
||||
globals.forEach(function (global, sym) {
|
||||
function getAmbientModules(sourceFile) {
|
||||
var isNode = denoContext.hasNodeSourceFile(sourceFile);
|
||||
if (isNode) {
|
||||
if (!nodeAmbientModulesCache) {
|
||||
nodeAmbientModulesCache = getAmbientModules(denoContext.combinedGlobals);
|
||||
}
|
||||
return nodeAmbientModulesCache;
|
||||
}
|
||||
else {
|
||||
if (!ambientModulesCache) {
|
||||
ambientModulesCache = getAmbientModules(globals);
|
||||
}
|
||||
return ambientModulesCache;
|
||||
}
|
||||
function getAmbientModules(envGlobals) {
|
||||
var cache = [];
|
||||
envGlobals.forEach(function (global, sym) {
|
||||
// No need to `unescapeLeadingUnderscores`, an escaped symbol is never an ambient module.
|
||||
if (ambientModuleSymbolRegex.test(sym)) {
|
||||
ambientModulesCache.push(global);
|
||||
cache.push(global);
|
||||
}
|
||||
});
|
||||
return cache;
|
||||
}
|
||||
return ambientModulesCache;
|
||||
}
|
||||
function checkGrammarImportClause(node) {
|
||||
var _a;
|
||||
|
@ -90562,6 +90641,234 @@ var ts;
|
|||
}
|
||||
ts.signatureHasLiteralTypes = signatureHasLiteralTypes;
|
||||
})(ts || (ts = {}));
|
||||
/* @internal */
|
||||
var ts;
|
||||
(function (ts) {
|
||||
var deno;
|
||||
(function (deno) {
|
||||
var isNodeSourceFile = function () { return false; };
|
||||
function setIsNodeSourceFileCallback(callback) {
|
||||
isNodeSourceFile = callback;
|
||||
}
|
||||
deno.setIsNodeSourceFileCallback = setIsNodeSourceFileCallback;
|
||||
// When upgrading:
|
||||
// 1. Inspect all usages of "globals" and "globalThisSymbol" in checker.ts
|
||||
// - Beware that `globalThisType` might refer to the global `this` type
|
||||
// and not the global `globalThis` type
|
||||
// 2. Inspect the types in @types/node for anything that might need to go below
|
||||
// as well.
|
||||
var nodeOnlyGlobalNames = new ts.Set([
|
||||
"NodeRequire",
|
||||
"RequireResolve",
|
||||
"RequireResolve",
|
||||
"process",
|
||||
"console",
|
||||
"__filename",
|
||||
"__dirname",
|
||||
"require",
|
||||
"module",
|
||||
"exports",
|
||||
"gc",
|
||||
"BufferEncoding",
|
||||
"BufferConstructor",
|
||||
"WithImplicitCoercion",
|
||||
"Buffer",
|
||||
"Console",
|
||||
"ImportMeta",
|
||||
"setTimeout",
|
||||
"setInterval",
|
||||
"setImmediate",
|
||||
"Global",
|
||||
"AbortController",
|
||||
"AbortSignal",
|
||||
"Blob",
|
||||
"BroadcastChannel",
|
||||
"MessageChannel",
|
||||
"MessagePort",
|
||||
"Event",
|
||||
"EventTarget",
|
||||
"performance",
|
||||
"TextDecoder",
|
||||
"TextEncoder",
|
||||
"URL",
|
||||
"URLSearchParams",
|
||||
]);
|
||||
function createDenoForkContext(_a) {
|
||||
var mergeSymbol = _a.mergeSymbol, globals = _a.globals, nodeGlobals = _a.nodeGlobals, ambientModuleSymbolRegex = _a.ambientModuleSymbolRegex;
|
||||
return {
|
||||
hasNodeSourceFile: hasNodeSourceFile,
|
||||
getGlobalsForName: getGlobalsForName,
|
||||
mergeGlobalSymbolTable: mergeGlobalSymbolTable,
|
||||
combinedGlobals: createNodeGlobalsSymbolTable(),
|
||||
};
|
||||
function hasNodeSourceFile(node) {
|
||||
if (!node)
|
||||
return false;
|
||||
var sourceFile = ts.getSourceFileOfNode(node);
|
||||
return isNodeSourceFile(sourceFile);
|
||||
}
|
||||
function getGlobalsForName(id) {
|
||||
// Node ambient modules are only accessible in the node code,
|
||||
// so put them on the node globals
|
||||
if (ambientModuleSymbolRegex.test(id))
|
||||
return nodeGlobals;
|
||||
return nodeOnlyGlobalNames.has(id) ? nodeGlobals : globals;
|
||||
}
|
||||
function mergeGlobalSymbolTable(node, source, unidirectional) {
|
||||
if (unidirectional === void 0) { unidirectional = false; }
|
||||
var sourceFile = ts.getSourceFileOfNode(node);
|
||||
var isNodeFile = hasNodeSourceFile(sourceFile);
|
||||
source.forEach(function (sourceSymbol, id) {
|
||||
var target = isNodeFile ? getGlobalsForName(id) : globals;
|
||||
var targetSymbol = target.get(id);
|
||||
target.set(id, targetSymbol ? mergeSymbol(targetSymbol, sourceSymbol, unidirectional) : sourceSymbol);
|
||||
});
|
||||
}
|
||||
function createNodeGlobalsSymbolTable() {
|
||||
return new Proxy(globals, {
|
||||
get: function (target, prop, receiver) {
|
||||
if (prop === "get") {
|
||||
return function (key) {
|
||||
var _a;
|
||||
return (_a = nodeGlobals.get(key)) !== null && _a !== void 0 ? _a : globals.get(key);
|
||||
};
|
||||
}
|
||||
else if (prop === "has") {
|
||||
return function (key) {
|
||||
return nodeGlobals.has(key) || globals.has(key);
|
||||
};
|
||||
}
|
||||
else if (prop === "size") {
|
||||
var i_2 = 0;
|
||||
forEachEntry(function () {
|
||||
i_2++;
|
||||
});
|
||||
return i_2;
|
||||
}
|
||||
else if (prop === "forEach") {
|
||||
return function (action) {
|
||||
forEachEntry(function (_a) {
|
||||
var key = _a[0], value = _a[1];
|
||||
action(value, key);
|
||||
});
|
||||
};
|
||||
}
|
||||
else if (prop === "entries") {
|
||||
return function () {
|
||||
return getEntries(function (kv) { return kv; });
|
||||
};
|
||||
}
|
||||
else if (prop === "keys") {
|
||||
return function () {
|
||||
return getEntries(function (kv) { return kv[0]; });
|
||||
};
|
||||
}
|
||||
else if (prop === "values") {
|
||||
return function () {
|
||||
return getEntries(function (kv) { return kv[1]; });
|
||||
};
|
||||
}
|
||||
else if (prop === Symbol.iterator) {
|
||||
return function () {
|
||||
// Need to convert this to an array since typescript targets ES5
|
||||
// and providing back the iterator won't work here. I don't want
|
||||
// to change the target to ES6 because I'm not sure if that would
|
||||
// surface any issues.
|
||||
return ts.arrayFrom(getEntries(function (kv) { return kv; }))[Symbol.iterator]();
|
||||
};
|
||||
}
|
||||
else {
|
||||
var value_3 = target[prop];
|
||||
if (value_3 instanceof Function) {
|
||||
return function () {
|
||||
var args = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
args[_i] = arguments[_i];
|
||||
}
|
||||
return value_3.apply(this === receiver ? target : this, args);
|
||||
};
|
||||
}
|
||||
return value_3;
|
||||
}
|
||||
},
|
||||
});
|
||||
function forEachEntry(action) {
|
||||
var iterator = getEntries(function (entry) {
|
||||
action(entry);
|
||||
});
|
||||
// drain the iterator to do the action
|
||||
while (!iterator.next().done) { }
|
||||
}
|
||||
function getEntries(transform) {
|
||||
var foundKeys, _i, _a, entries, next;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
foundKeys = new ts.Set();
|
||||
_i = 0, _a = [nodeGlobals.entries(), globals.entries()];
|
||||
_b.label = 1;
|
||||
case 1:
|
||||
if (!(_i < _a.length)) return [3 /*break*/, 6];
|
||||
entries = _a[_i];
|
||||
next = entries.next();
|
||||
_b.label = 2;
|
||||
case 2:
|
||||
if (!!next.done) return [3 /*break*/, 5];
|
||||
if (!!foundKeys.has(next.value[0])) return [3 /*break*/, 4];
|
||||
return [4 /*yield*/, transform(next.value)];
|
||||
case 3:
|
||||
_b.sent();
|
||||
foundKeys.add(next.value[0]);
|
||||
_b.label = 4;
|
||||
case 4:
|
||||
next = entries.next();
|
||||
return [3 /*break*/, 2];
|
||||
case 5:
|
||||
_i++;
|
||||
return [3 /*break*/, 1];
|
||||
case 6: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
deno.createDenoForkContext = createDenoForkContext;
|
||||
function tryParseNpmPackageReference(text) {
|
||||
try {
|
||||
return parseNpmPackageReference(text);
|
||||
}
|
||||
catch (_a) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
deno.tryParseNpmPackageReference = tryParseNpmPackageReference;
|
||||
function parseNpmPackageReference(text) {
|
||||
if (!text.startsWith("npm:")) {
|
||||
throw new Error("Not an npm specifier: ".concat(text));
|
||||
}
|
||||
text = text.replace(/^npm:/, "");
|
||||
var parts = text.split("/");
|
||||
var namePartLen = text.startsWith("@") ? 2 : 1;
|
||||
if (parts.length < namePartLen) {
|
||||
throw new Error("Not a valid package: ".concat(text));
|
||||
}
|
||||
var nameParts = parts.slice(0, namePartLen);
|
||||
var lastNamePart = nameParts.at(-1);
|
||||
var lastAtIndex = lastNamePart.lastIndexOf("@");
|
||||
var versionReq = undefined;
|
||||
if (lastAtIndex > 0) {
|
||||
versionReq = lastNamePart.substring(lastAtIndex + 1);
|
||||
nameParts[nameParts.length - 1] = lastNamePart.substring(0, lastAtIndex);
|
||||
}
|
||||
return {
|
||||
name: nameParts.join("/"),
|
||||
versionReq: versionReq,
|
||||
subPath: parts.length > nameParts.length ? parts.slice(nameParts.length).join("/") : undefined,
|
||||
};
|
||||
}
|
||||
deno.parseNpmPackageReference = parseNpmPackageReference;
|
||||
})(deno = ts.deno || (ts.deno = {}));
|
||||
})(ts || (ts = {}));
|
||||
var ts;
|
||||
(function (ts) {
|
||||
function visitNode(node, visitor, test, lift) {
|
||||
|
@ -121271,7 +121578,7 @@ var ts;
|
|||
}
|
||||
}
|
||||
// From ambient modules
|
||||
for (var _f = 0, _g = program.getTypeChecker().getAmbientModules(); _f < _g.length; _f++) {
|
||||
for (var _f = 0, _g = program.getTypeChecker().getAmbientModules(sourceFile); _f < _g.length; _f++) {
|
||||
var ambientModule = _g[_f];
|
||||
if (ambientModule.declarations && ambientModule.declarations.length > 1) {
|
||||
addReferenceFromAmbientModule(ambientModule);
|
||||
|
@ -124123,7 +124430,7 @@ var ts;
|
|||
});
|
||||
// Sort by paths closest to importing file Name directory
|
||||
var sortedPaths = [];
|
||||
var _loop_35 = function (directory) {
|
||||
var _loop_36 = function (directory) {
|
||||
var directoryStart = ts.ensureTrailingDirectorySeparator(directory);
|
||||
var pathsInDirectory;
|
||||
allFileNames.forEach(function (_a, fileName) {
|
||||
|
@ -124147,7 +124454,7 @@ var ts;
|
|||
};
|
||||
var out_directory_1;
|
||||
for (var directory = ts.getDirectoryPath(importingFileName); allFileNames.size !== 0;) {
|
||||
var state_11 = _loop_35(directory);
|
||||
var state_11 = _loop_36(directory);
|
||||
directory = out_directory_1;
|
||||
if (state_11 === "break")
|
||||
break;
|
||||
|
@ -124218,7 +124525,7 @@ var ts;
|
|||
}
|
||||
function tryGetModuleNameFromPaths(relativeToBaseUrl, paths, allowedEndings, host, compilerOptions) {
|
||||
for (var key in paths) {
|
||||
var _loop_36 = function (patternText_1) {
|
||||
var _loop_37 = function (patternText_1) {
|
||||
var pattern = ts.normalizePath(patternText_1);
|
||||
var indexOfStar = pattern.indexOf("*");
|
||||
// In module resolution, if `pattern` itself has an extension, a file with that extension is looked up directly,
|
||||
|
@ -124285,7 +124592,7 @@ var ts;
|
|||
};
|
||||
for (var _i = 0, _a = paths[key]; _i < _a.length; _i++) {
|
||||
var patternText_1 = _a[_i];
|
||||
var state_12 = _loop_36(patternText_1);
|
||||
var state_12 = _loop_37(patternText_1);
|
||||
if (typeof state_12 === "object")
|
||||
return state_12.value;
|
||||
}
|
||||
|
@ -137638,7 +137945,8 @@ var ts;
|
|||
if (globalSymbol && checker.getTypeOfSymbolAtLocation(globalSymbol, sourceFile) === type) {
|
||||
return true;
|
||||
}
|
||||
var globalThisSymbol = checker.resolveName("globalThis", /*location*/ undefined, 111551 /* SymbolFlags.Value */, /*excludeGlobals*/ false);
|
||||
// deno: provide sourceFile so that it can figure out if it's a node or deno globalThis
|
||||
var globalThisSymbol = checker.resolveName("globalThis", /*location*/ sourceFile, 111551 /* SymbolFlags.Value */, /*excludeGlobals*/ false);
|
||||
if (globalThisSymbol && checker.getTypeOfSymbolAtLocation(globalThisSymbol, sourceFile) === type) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ delete Object.prototype.__proto__;
|
|||
// This map stores that relationship, and the original can be restored by the
|
||||
// normalized specifier.
|
||||
// See: https://github.com/denoland/deno/issues/9277#issuecomment-769653834
|
||||
/** @type {Map<string, string>} */
|
||||
const normalizedToOriginalMap = new Map();
|
||||
|
||||
/**
|
||||
|
@ -40,6 +41,16 @@ delete Object.prototype.__proto__;
|
|||
"languageVersion" in value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ts.ScriptTarget | ts.CreateSourceFileOptions | undefined} versionOrOptions
|
||||
* @returns {ts.CreateSourceFileOptions}
|
||||
*/
|
||||
function getCreateSourceFileOptions(versionOrOptions) {
|
||||
return isCreateSourceFileOptions(versionOrOptions)
|
||||
? versionOrOptions
|
||||
: { languageVersion: versionOrOptions ?? ts.ScriptTarget.ESNext };
|
||||
}
|
||||
|
||||
function setLogDebug(debug, source) {
|
||||
logDebug = debug;
|
||||
if (source) {
|
||||
|
@ -119,8 +130,23 @@ delete Object.prototype.__proto__;
|
|||
return result;
|
||||
}
|
||||
|
||||
// In the case of the LSP, this is initialized with the assets
|
||||
// when snapshotting and never added to or removed after that.
|
||||
class SpecifierIsCjsCache {
|
||||
/** @type {Set<string>} */
|
||||
#cache = new Set();
|
||||
|
||||
/** @param {[string, ts.Extension]} param */
|
||||
add([specifier, ext]) {
|
||||
if (ext === ".cjs" || ext === ".d.cts" || ext === ".cts") {
|
||||
this.#cache.add(specifier);
|
||||
}
|
||||
}
|
||||
|
||||
has(specifier) {
|
||||
return this.#cache.has(specifier);
|
||||
}
|
||||
}
|
||||
|
||||
// In the case of the LSP, this will only ever contain the assets.
|
||||
/** @type {Map<string, ts.SourceFile>} */
|
||||
const sourceFileCache = new Map();
|
||||
|
||||
|
@ -130,6 +156,181 @@ delete Object.prototype.__proto__;
|
|||
/** @type {Map<string, string>} */
|
||||
const scriptVersionCache = new Map();
|
||||
|
||||
/** @type {Map<string, boolean>} */
|
||||
const isNodeSourceFileCache = new Map();
|
||||
|
||||
const isCjsCache = new SpecifierIsCjsCache();
|
||||
|
||||
/**
|
||||
* @param {ts.CompilerOptions | ts.MinimalResolutionCacheHost} settingsOrHost
|
||||
* @returns {ts.CompilerOptions}
|
||||
*/
|
||||
function getCompilationSettings(settingsOrHost) {
|
||||
if (typeof settingsOrHost.getCompilationSettings === "function") {
|
||||
return settingsOrHost.getCompilationSettings();
|
||||
}
|
||||
return /** @type {ts.CompilerOptions} */ (settingsOrHost);
|
||||
}
|
||||
|
||||
// We need to use a custom document registry in order to provide source files
|
||||
// with an impliedNodeFormat to the ts language service
|
||||
|
||||
/** @type {Map<string, ts.SourceFile} */
|
||||
const documentRegistrySourceFileCache = new Map();
|
||||
const { getKeyForCompilationSettings } = ts.createDocumentRegistry(); // reuse this code
|
||||
/** @type {ts.DocumentRegistry} */
|
||||
const documentRegistry = {
|
||||
acquireDocument(
|
||||
fileName,
|
||||
compilationSettingsOrHost,
|
||||
scriptSnapshot,
|
||||
version,
|
||||
scriptKind,
|
||||
sourceFileOptions,
|
||||
) {
|
||||
const key = getKeyForCompilationSettings(
|
||||
getCompilationSettings(compilationSettingsOrHost),
|
||||
);
|
||||
return this.acquireDocumentWithKey(
|
||||
fileName,
|
||||
/** @type {ts.Path} */ (fileName),
|
||||
compilationSettingsOrHost,
|
||||
key,
|
||||
scriptSnapshot,
|
||||
version,
|
||||
scriptKind,
|
||||
sourceFileOptions,
|
||||
);
|
||||
},
|
||||
|
||||
acquireDocumentWithKey(
|
||||
fileName,
|
||||
path,
|
||||
_compilationSettingsOrHost,
|
||||
key,
|
||||
scriptSnapshot,
|
||||
version,
|
||||
scriptKind,
|
||||
sourceFileOptions,
|
||||
) {
|
||||
const mapKey = path + key;
|
||||
let sourceFile = documentRegistrySourceFileCache.get(mapKey);
|
||||
if (!sourceFile || sourceFile.version !== version) {
|
||||
sourceFile = ts.createLanguageServiceSourceFile(
|
||||
fileName,
|
||||
scriptSnapshot,
|
||||
{
|
||||
...getCreateSourceFileOptions(sourceFileOptions),
|
||||
impliedNodeFormat: isCjsCache.has(fileName)
|
||||
? ts.ModuleKind.CommonJS
|
||||
: ts.ModuleKind.ESNext,
|
||||
},
|
||||
version,
|
||||
true,
|
||||
scriptKind,
|
||||
);
|
||||
documentRegistrySourceFileCache.set(mapKey, sourceFile);
|
||||
}
|
||||
return sourceFile;
|
||||
},
|
||||
|
||||
updateDocument(
|
||||
fileName,
|
||||
compilationSettingsOrHost,
|
||||
scriptSnapshot,
|
||||
version,
|
||||
scriptKind,
|
||||
sourceFileOptions,
|
||||
) {
|
||||
const key = getKeyForCompilationSettings(
|
||||
getCompilationSettings(compilationSettingsOrHost),
|
||||
);
|
||||
return this.updateDocumentWithKey(
|
||||
fileName,
|
||||
/** @type {ts.Path} */ (fileName),
|
||||
compilationSettingsOrHost,
|
||||
key,
|
||||
scriptSnapshot,
|
||||
version,
|
||||
scriptKind,
|
||||
sourceFileOptions,
|
||||
);
|
||||
},
|
||||
|
||||
updateDocumentWithKey(
|
||||
fileName,
|
||||
path,
|
||||
compilationSettingsOrHost,
|
||||
key,
|
||||
scriptSnapshot,
|
||||
version,
|
||||
scriptKind,
|
||||
sourceFileOptions,
|
||||
) {
|
||||
const mapKey = path + key;
|
||||
let sourceFile = documentRegistrySourceFileCache.get(mapKey) ??
|
||||
this.acquireDocumentWithKey(
|
||||
fileName,
|
||||
path,
|
||||
compilationSettingsOrHost,
|
||||
key,
|
||||
scriptSnapshot,
|
||||
version,
|
||||
scriptKind,
|
||||
sourceFileOptions,
|
||||
);
|
||||
|
||||
if (sourceFile.version !== version) {
|
||||
sourceFile = ts.updateLanguageServiceSourceFile(
|
||||
sourceFile,
|
||||
scriptSnapshot,
|
||||
version,
|
||||
scriptSnapshot.getChangeRange(sourceFile.scriptSnapShot),
|
||||
);
|
||||
}
|
||||
return sourceFile;
|
||||
},
|
||||
|
||||
getKeyForCompilationSettings(settings) {
|
||||
return getKeyForCompilationSettings(settings);
|
||||
},
|
||||
|
||||
releaseDocument(
|
||||
fileName,
|
||||
compilationSettings,
|
||||
scriptKind,
|
||||
impliedNodeFormat,
|
||||
) {
|
||||
const key = getKeyForCompilationSettings(compilationSettings);
|
||||
return this.releaseDocumentWithKey(
|
||||
/** @type {ts.Path} */ (fileName),
|
||||
key,
|
||||
scriptKind,
|
||||
impliedNodeFormat,
|
||||
);
|
||||
},
|
||||
|
||||
releaseDocumentWithKey(path, key, _scriptKind, _impliedNodeFormat) {
|
||||
const mapKey = path + key;
|
||||
documentRegistrySourceFileCache.remove(mapKey);
|
||||
},
|
||||
|
||||
reportStats() {
|
||||
return "[]";
|
||||
},
|
||||
};
|
||||
|
||||
ts.deno.setIsNodeSourceFileCallback((sourceFile) => {
|
||||
const fileName = sourceFile.fileName;
|
||||
let isNodeSourceFile = isNodeSourceFileCache.get(fileName);
|
||||
if (isNodeSourceFile == null) {
|
||||
const result = ops.op_is_node_file(fileName);
|
||||
isNodeSourceFile = /** @type {boolean} */ (result);
|
||||
isNodeSourceFileCache.set(fileName, isNodeSourceFile);
|
||||
}
|
||||
return isNodeSourceFile;
|
||||
});
|
||||
|
||||
/** @param {ts.DiagnosticRelatedInformation} diagnostic */
|
||||
function fromRelatedInformation({
|
||||
start,
|
||||
|
@ -189,6 +390,10 @@ delete Object.prototype.__proto__;
|
|||
/** Diagnostics that are intentionally ignored when compiling TypeScript in
|
||||
* Deno, as they provide misleading or incorrect information. */
|
||||
const IGNORED_DIAGNOSTICS = [
|
||||
// TS1452: 'resolution-mode' assertions are only supported when `moduleResolution` is `node16` or `nodenext`.
|
||||
// We specify the resolution mode to be CommonJS for some npm files and this
|
||||
// diagnostic gets generated even though we're using custom module resolution.
|
||||
1452,
|
||||
// TS2306: File '.../index.d.ts' is not a module.
|
||||
// We get this for `x-typescript-types` declaration files which don't export
|
||||
// anything. We prefer to treat these as modules with no exports.
|
||||
|
@ -228,10 +433,12 @@ delete Object.prototype.__proto__;
|
|||
target: ts.ScriptTarget.ESNext,
|
||||
};
|
||||
|
||||
// todo(dsherret): can we remove this and just use ts.OperationCanceledException?
|
||||
/** Error thrown on cancellation. */
|
||||
class OperationCanceledError extends Error {
|
||||
}
|
||||
|
||||
// todo(dsherret): we should investigate if throttling is really necessary
|
||||
/**
|
||||
* Inspired by ThrottledCancellationToken in ts server.
|
||||
*
|
||||
|
@ -291,13 +498,10 @@ delete Object.prototype.__proto__;
|
|||
_onError,
|
||||
_shouldCreateNewSourceFile,
|
||||
) {
|
||||
const createOptions = getCreateSourceFileOptions(languageVersion);
|
||||
debug(
|
||||
`host.getSourceFile("${specifier}", ${
|
||||
ts.ScriptTarget[
|
||||
isCreateSourceFileOptions(languageVersion)
|
||||
? languageVersion.languageVersion
|
||||
: languageVersion
|
||||
]
|
||||
ts.ScriptTarget[createOptions.languageVersion]
|
||||
})`,
|
||||
);
|
||||
|
||||
|
@ -320,7 +524,12 @@ delete Object.prototype.__proto__;
|
|||
sourceFile = ts.createSourceFile(
|
||||
specifier,
|
||||
data,
|
||||
languageVersion,
|
||||
{
|
||||
...createOptions,
|
||||
impliedNodeFormat: isCjsCache.has(specifier)
|
||||
? ts.ModuleKind.CommonJS
|
||||
: ts.ModuleKind.ESNext,
|
||||
},
|
||||
false,
|
||||
scriptKind,
|
||||
);
|
||||
|
@ -355,6 +564,50 @@ delete Object.prototype.__proto__;
|
|||
getNewLine() {
|
||||
return "\n";
|
||||
},
|
||||
resolveTypeReferenceDirectives(
|
||||
typeDirectiveNames,
|
||||
containingFilePath,
|
||||
redirectedReference,
|
||||
options,
|
||||
containingFileMode,
|
||||
) {
|
||||
return typeDirectiveNames.map((arg) => {
|
||||
/** @type {ts.FileReference} */
|
||||
const fileReference = typeof arg === "string"
|
||||
? {
|
||||
pos: -1,
|
||||
end: -1,
|
||||
fileName: arg,
|
||||
}
|
||||
: arg;
|
||||
if (fileReference.fileName.startsWith("npm:")) {
|
||||
/** @type {[string, ts.Extension] | undefined} */
|
||||
const resolved = ops.op_resolve({
|
||||
specifiers: [fileReference.fileName],
|
||||
base: containingFilePath,
|
||||
})?.[0];
|
||||
if (resolved) {
|
||||
isCjsCache.add(resolved);
|
||||
return {
|
||||
primary: true,
|
||||
resolvedFileName: resolved[0],
|
||||
};
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
} else {
|
||||
return ts.resolveTypeReferenceDirective(
|
||||
fileReference.fileName,
|
||||
containingFilePath,
|
||||
options,
|
||||
host,
|
||||
redirectedReference,
|
||||
undefined,
|
||||
containingFileMode ?? fileReference.resolutionMode,
|
||||
).resolvedTypeReferenceDirective;
|
||||
}
|
||||
});
|
||||
},
|
||||
resolveModuleNames(specifiers, base) {
|
||||
debug(`host.resolveModuleNames()`);
|
||||
debug(` base: ${base}`);
|
||||
|
@ -367,7 +620,12 @@ delete Object.prototype.__proto__;
|
|||
if (resolved) {
|
||||
const result = resolved.map((item) => {
|
||||
if (item) {
|
||||
isCjsCache.add(item);
|
||||
const [resolvedFileName, extension] = item;
|
||||
if (resolvedFileName.startsWith("node:")) {
|
||||
// probably means the user doesn't have @types/node, so resolve to undefined
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
resolvedFileName,
|
||||
extension,
|
||||
|
@ -444,6 +702,23 @@ delete Object.prototype.__proto__;
|
|||
},
|
||||
};
|
||||
|
||||
// override the npm install @types package diagnostics to be deno specific
|
||||
ts.setLocalizedDiagnosticMessages((() => {
|
||||
const nodeMessage = "Cannot find name '{0}'."; // don't offer any suggestions
|
||||
const jqueryMessage =
|
||||
"Cannot find name '{0}'. Did you mean to import jQuery? Try adding `import $ from \"npm:jquery\";`.";
|
||||
return {
|
||||
"Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_save_dev_types_Slashno_2580":
|
||||
nodeMessage,
|
||||
"Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_save_dev_types_Slashno_2591":
|
||||
nodeMessage,
|
||||
"Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_save_dev_types_Slash_2581":
|
||||
jqueryMessage,
|
||||
"Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_save_dev_types_Slash_2592":
|
||||
jqueryMessage,
|
||||
};
|
||||
})());
|
||||
|
||||
/** @type {Array<[string, number]>} */
|
||||
const stats = [];
|
||||
let statsStart = 0;
|
||||
|
@ -557,7 +832,25 @@ delete Object.prototype.__proto__;
|
|||
...program.getOptionsDiagnostics(),
|
||||
...program.getGlobalDiagnostics(),
|
||||
...program.getSemanticDiagnostics(),
|
||||
].filter(({ code }) => !IGNORED_DIAGNOSTICS.includes(code));
|
||||
].filter((diagnostic) => {
|
||||
if (IGNORED_DIAGNOSTICS.includes(diagnostic.code)) {
|
||||
return false;
|
||||
} else if (
|
||||
diagnostic.code === 1259 &&
|
||||
typeof diagnostic.messageText === "string" &&
|
||||
diagnostic.messageText.startsWith(
|
||||
"Module '\"deno:///missing_dependency.d.ts\"' can only be default-imported using the 'allowSyntheticDefaultImports' flag",
|
||||
)
|
||||
) {
|
||||
// For now, ignore diagnostics like:
|
||||
// > TS1259 [ERROR]: Module '"deno:///missing_dependency.d.ts"' can only be default-imported using the 'allowSyntheticDefaultImports' flag
|
||||
// This diagnostic has surfaced due to supporting node cjs imports because this module does `export =`.
|
||||
// See discussion in https://github.com/microsoft/TypeScript/pull/51136
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// emit the tsbuildinfo file
|
||||
// @ts-ignore: emitBuildInfo is not exposed (https://github.com/microsoft/TypeScript/issues/49871)
|
||||
|
@ -922,13 +1215,14 @@ delete Object.prototype.__proto__;
|
|||
}
|
||||
hasStarted = true;
|
||||
cwd = rootUri;
|
||||
languageService = ts.createLanguageService(host);
|
||||
languageService = ts.createLanguageService(host, documentRegistry);
|
||||
setLogDebug(debugFlag, "TSLS");
|
||||
debug("serverInit()");
|
||||
}
|
||||
|
||||
function serverRestart() {
|
||||
languageService = ts.createLanguageService(host);
|
||||
languageService = ts.createLanguageService(host, documentRegistry);
|
||||
isNodeSourceFileCache.clear();
|
||||
debug("serverRestart()");
|
||||
}
|
||||
|
||||
|
|
7
cli/tsc/compiler.d.ts
vendored
7
cli/tsc/compiler.d.ts
vendored
|
@ -12,6 +12,7 @@ declare global {
|
|||
var normalizePath: (path: string) => string;
|
||||
interface SourceFile {
|
||||
version?: string;
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
interface CompilerHost {
|
||||
|
@ -24,6 +25,12 @@ declare global {
|
|||
}
|
||||
|
||||
var performance: Performance;
|
||||
|
||||
namespace deno {
|
||||
function setIsNodeSourceFileCallback(
|
||||
callback: (sourceFile: SourceFile) => boolean,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
namespace ts {
|
||||
|
|
|
@ -28,6 +28,7 @@ pub use resolution::package_imports_resolve;
|
|||
pub use resolution::package_resolve;
|
||||
pub use resolution::NodeModuleKind;
|
||||
pub use resolution::DEFAULT_CONDITIONS;
|
||||
pub use resolution::TYPES_CONDITIONS;
|
||||
|
||||
pub trait NodePermissions {
|
||||
fn check_read(&mut self, path: &Path) -> Result<(), AnyError>;
|
||||
|
@ -38,6 +39,7 @@ pub trait RequireNpmResolver {
|
|||
&self,
|
||||
specifier: &str,
|
||||
referrer: &Path,
|
||||
conditions: &[&str],
|
||||
) -> Result<PathBuf, AnyError>;
|
||||
|
||||
fn resolve_package_folder_from_path(
|
||||
|
@ -304,6 +306,7 @@ fn op_require_resolve_deno_dir(
|
|||
.resolve_package_folder_from_package(
|
||||
&request,
|
||||
&PathBuf::from(parent_filename),
|
||||
DEFAULT_CONDITIONS,
|
||||
)
|
||||
.ok()
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
|
|
|
@ -50,6 +50,12 @@ impl PackageJson {
|
|||
path: PathBuf,
|
||||
) -> Result<PackageJson, AnyError> {
|
||||
resolver.ensure_read_permission(&path)?;
|
||||
Self::load_skip_read_permission(path)
|
||||
}
|
||||
|
||||
pub fn load_skip_read_permission(
|
||||
path: PathBuf,
|
||||
) -> Result<PackageJson, AnyError> {
|
||||
let source = match std::fs::read_to_string(&path) {
|
||||
Ok(source) => source,
|
||||
Err(err) if err.kind() == ErrorKind::NotFound => {
|
||||
|
|
|
@ -19,6 +19,7 @@ use crate::RequireNpmResolver;
|
|||
|
||||
pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"];
|
||||
pub static REQUIRE_CONDITIONS: &[&str] = &["require", "node"];
|
||||
pub static TYPES_CONDITIONS: &[&str] = &["types"];
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum NodeModuleKind {
|
||||
|
@ -251,13 +252,17 @@ fn resolve_package_target_string(
|
|||
};
|
||||
let package_json_url =
|
||||
ModuleSpecifier::from_file_path(package_json_path).unwrap();
|
||||
return package_resolve(
|
||||
return match package_resolve(
|
||||
&export_target,
|
||||
&package_json_url,
|
||||
referrer_kind,
|
||||
conditions,
|
||||
npm_resolver,
|
||||
);
|
||||
) {
|
||||
Ok(Some(path)) => Ok(path),
|
||||
Ok(None) => Err(generic_error("not found")),
|
||||
Err(err) => Err(err),
|
||||
};
|
||||
}
|
||||
}
|
||||
return Err(throw_invalid_package_target(
|
||||
|
@ -593,7 +598,7 @@ pub fn package_resolve(
|
|||
referrer_kind: NodeModuleKind,
|
||||
conditions: &[&str],
|
||||
npm_resolver: &dyn RequireNpmResolver,
|
||||
) -> Result<PathBuf, AnyError> {
|
||||
) -> Result<Option<PathBuf>, AnyError> {
|
||||
let (package_name, package_subpath, _is_scoped) =
|
||||
parse_package_name(specifier, referrer)?;
|
||||
|
||||
|
@ -611,13 +616,15 @@ pub fn package_resolve(
|
|||
referrer_kind,
|
||||
conditions,
|
||||
npm_resolver,
|
||||
);
|
||||
)
|
||||
.map(Some);
|
||||
}
|
||||
}
|
||||
|
||||
let package_dir_path = npm_resolver.resolve_package_folder_from_package(
|
||||
&package_name,
|
||||
&referrer.to_file_path().unwrap(),
|
||||
conditions,
|
||||
)?;
|
||||
let package_json_path = package_dir_path.join("package.json");
|
||||
|
||||
|
@ -645,13 +652,16 @@ pub fn package_resolve(
|
|||
referrer_kind,
|
||||
conditions,
|
||||
npm_resolver,
|
||||
);
|
||||
)
|
||||
.map(Some);
|
||||
}
|
||||
if package_subpath == "." {
|
||||
return legacy_main_resolve(&package_json, referrer_kind);
|
||||
return legacy_main_resolve(&package_json, referrer_kind, conditions);
|
||||
}
|
||||
|
||||
Ok(package_json.path.parent().unwrap().join(&package_subpath))
|
||||
Ok(Some(
|
||||
package_json.path.parent().unwrap().join(&package_subpath),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get_package_scope_config(
|
||||
|
@ -706,41 +716,40 @@ fn file_exists(path: &Path) -> bool {
|
|||
pub fn legacy_main_resolve(
|
||||
package_json: &PackageJson,
|
||||
referrer_kind: NodeModuleKind,
|
||||
) -> Result<PathBuf, AnyError> {
|
||||
let maybe_main = package_json.main(referrer_kind);
|
||||
conditions: &[&str],
|
||||
) -> Result<Option<PathBuf>, AnyError> {
|
||||
let is_types = conditions == TYPES_CONDITIONS;
|
||||
let maybe_main = if is_types {
|
||||
package_json.types.as_ref()
|
||||
} else {
|
||||
package_json.main(referrer_kind)
|
||||
};
|
||||
let mut guess;
|
||||
|
||||
if let Some(main) = maybe_main {
|
||||
guess = package_json.path.parent().unwrap().join(main).clean();
|
||||
if file_exists(&guess) {
|
||||
return Ok(guess);
|
||||
return Ok(Some(guess));
|
||||
}
|
||||
|
||||
let mut found = false;
|
||||
// todo(dsherret): investigate exactly how node handles this
|
||||
let endings = match referrer_kind {
|
||||
NodeModuleKind::Cjs => vec![
|
||||
".js",
|
||||
".cjs",
|
||||
".json",
|
||||
".node",
|
||||
"/index.js",
|
||||
"/index.cjs",
|
||||
"/index.json",
|
||||
"/index.node",
|
||||
],
|
||||
NodeModuleKind::Esm => vec![
|
||||
".js",
|
||||
".mjs",
|
||||
".json",
|
||||
".node",
|
||||
"/index.js",
|
||||
"/index.mjs",
|
||||
".cjs",
|
||||
"/index.cjs",
|
||||
"/index.json",
|
||||
"/index.node",
|
||||
],
|
||||
// todo(dsherret): investigate exactly how node and typescript handles this
|
||||
let endings = if is_types {
|
||||
match referrer_kind {
|
||||
NodeModuleKind::Cjs => {
|
||||
vec![".d.ts", ".d.cts", "/index.d.ts", "/index.d.cts"]
|
||||
}
|
||||
NodeModuleKind::Esm => vec![
|
||||
".d.ts",
|
||||
".d.mts",
|
||||
"/index.d.ts",
|
||||
"/index.d.mts",
|
||||
".d.cts",
|
||||
"/index.d.cts",
|
||||
],
|
||||
}
|
||||
} else {
|
||||
vec![".js", "/index.js"]
|
||||
};
|
||||
for ending in endings {
|
||||
guess = package_json
|
||||
|
@ -757,21 +766,18 @@ pub fn legacy_main_resolve(
|
|||
|
||||
if found {
|
||||
// TODO(bartlomieju): emitLegacyIndexDeprecation()
|
||||
return Ok(guess);
|
||||
return Ok(Some(guess));
|
||||
}
|
||||
}
|
||||
|
||||
let index_file_names = match referrer_kind {
|
||||
NodeModuleKind::Cjs => {
|
||||
vec!["index.js", "index.cjs", "index.json", "index.node"]
|
||||
let index_file_names = if is_types {
|
||||
// todo(dsherret): investigate exactly how typescript does this
|
||||
match referrer_kind {
|
||||
NodeModuleKind::Cjs => vec!["index.d.ts", "index.d.cts"],
|
||||
NodeModuleKind::Esm => vec!["index.d.ts", "index.d.mts", "index.d.cts"],
|
||||
}
|
||||
NodeModuleKind::Esm => vec![
|
||||
"index.js",
|
||||
"index.mjs",
|
||||
"index.cjs",
|
||||
"index.json",
|
||||
"index.node",
|
||||
],
|
||||
} else {
|
||||
vec!["index.js"]
|
||||
};
|
||||
for index_file_name in index_file_names {
|
||||
guess = package_json
|
||||
|
@ -782,11 +788,11 @@ pub fn legacy_main_resolve(
|
|||
.clean();
|
||||
if file_exists(&guess) {
|
||||
// TODO(bartlomieju): emitLegacyIndexDeprecation()
|
||||
return Ok(guess);
|
||||
return Ok(Some(guess));
|
||||
}
|
||||
}
|
||||
|
||||
Err(generic_error("not found"))
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -35,6 +35,7 @@ tar = "0.4.38"
|
|||
tokio = { version = "1.21", features = ["full"] }
|
||||
tokio-rustls = "0.23"
|
||||
tokio-tungstenite = "0.16"
|
||||
url = { version = "2.3.1", features = ["serde", "expose_internals"] }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
pty = "0.2.2"
|
||||
|
|
|
@ -49,6 +49,7 @@ use tokio::net::TcpStream;
|
|||
use tokio_rustls::rustls;
|
||||
use tokio_rustls::TlsAcceptor;
|
||||
use tokio_tungstenite::accept_async;
|
||||
use url::Url;
|
||||
|
||||
pub mod assertions;
|
||||
pub mod lsp;
|
||||
|
@ -120,10 +121,19 @@ pub fn napi_tests_path() -> PathBuf {
|
|||
root_path().join("test_napi")
|
||||
}
|
||||
|
||||
/// Test server registry url.
|
||||
pub fn npm_registry_url() -> String {
|
||||
"http://localhost:4545/npm/registry/".to_string()
|
||||
}
|
||||
|
||||
pub fn std_path() -> PathBuf {
|
||||
root_path().join("test_util").join("std")
|
||||
}
|
||||
|
||||
pub fn std_file_url() -> String {
|
||||
Url::from_directory_path(std_path()).unwrap().to_string()
|
||||
}
|
||||
|
||||
pub fn target_dir() -> PathBuf {
|
||||
let current_exe = std::env::current_exe().unwrap();
|
||||
let target_dir = current_exe.parent().unwrap().parent().unwrap();
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::npm_registry_url;
|
||||
use crate::std_file_url;
|
||||
|
||||
use super::new_deno_dir;
|
||||
use super::TempDir;
|
||||
|
||||
|
@ -230,6 +233,8 @@ impl LspClient {
|
|||
let mut command = Command::new(deno_exe);
|
||||
command
|
||||
.env("DENO_DIR", deno_dir.path())
|
||||
.env("DENO_NODE_COMPAT_URL", std_file_url())
|
||||
.env("DENO_NPM_REGISTRY", npm_registry_url())
|
||||
.arg("lsp")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped());
|
||||
|
|
Loading…
Reference in a new issue