mirror of
https://github.com/denoland/deno.git
synced 2025-09-26 20:29:11 +00:00
fix(lsp): improved cjs tracking (#23374)
Our cjs tracking was a bit broken. It was marking stuff as esm that was actually cjs leading to type checking errors.
This commit is contained in:
parent
7e4ee02e2e
commit
6f278e5c40
16 changed files with 263 additions and 102 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -2641,10 +2641,11 @@ checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "file_test_runner"
|
name = "file_test_runner"
|
||||||
version = "0.2.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e13bacb20a988be248a13adc6011bce718648f78d4684d4dd0444d05256f4a7b"
|
checksum = "b66e9ef00f9f6b82b030b7a9d659030f73498921d4c021b0772e75dfd7090d80"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"deno_terminal",
|
"deno_terminal",
|
||||||
"parking_lot 0.12.1",
|
"parking_lot 0.12.1",
|
||||||
|
|
|
@ -12,6 +12,7 @@ use super::tsc::AssetDocument;
|
||||||
use crate::args::package_json;
|
use crate::args::package_json;
|
||||||
use crate::cache::HttpCache;
|
use crate::cache::HttpCache;
|
||||||
use crate::jsr::JsrCacheResolver;
|
use crate::jsr::JsrCacheResolver;
|
||||||
|
use crate::lsp::logging::lsp_warn;
|
||||||
use crate::npm::CliNpmResolver;
|
use crate::npm::CliNpmResolver;
|
||||||
use crate::resolver::CliGraphResolver;
|
use crate::resolver::CliGraphResolver;
|
||||||
use crate::resolver::CliGraphResolverOptions;
|
use crate::resolver::CliGraphResolverOptions;
|
||||||
|
@ -43,7 +44,6 @@ use deno_semver::jsr::JsrPackageReqReference;
|
||||||
use deno_semver::npm::NpmPackageReqReference;
|
use deno_semver::npm::NpmPackageReqReference;
|
||||||
use deno_semver::package::PackageReq;
|
use deno_semver::package::PackageReq;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use package_json::PackageJsonDepsProvider;
|
use package_json::PackageJsonDepsProvider;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
@ -57,36 +57,6 @@ use std::sync::atomic::Ordering;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tower_lsp::lsp_types as lsp;
|
use tower_lsp::lsp_types as lsp;
|
||||||
|
|
||||||
static JS_HEADERS: Lazy<HashMap<String, String>> = Lazy::new(|| {
|
|
||||||
([(
|
|
||||||
"content-type".to_string(),
|
|
||||||
"application/javascript".to_string(),
|
|
||||||
)])
|
|
||||||
.into_iter()
|
|
||||||
.collect()
|
|
||||||
});
|
|
||||||
|
|
||||||
static JSX_HEADERS: Lazy<HashMap<String, String>> = Lazy::new(|| {
|
|
||||||
([("content-type".to_string(), "text/jsx".to_string())])
|
|
||||||
.into_iter()
|
|
||||||
.collect()
|
|
||||||
});
|
|
||||||
|
|
||||||
static TS_HEADERS: Lazy<HashMap<String, String>> = Lazy::new(|| {
|
|
||||||
([(
|
|
||||||
"content-type".to_string(),
|
|
||||||
"application/typescript".to_string(),
|
|
||||||
)])
|
|
||||||
.into_iter()
|
|
||||||
.collect()
|
|
||||||
});
|
|
||||||
|
|
||||||
static TSX_HEADERS: Lazy<HashMap<String, String>> = Lazy::new(|| {
|
|
||||||
([("content-type".to_string(), "text/tsx".to_string())])
|
|
||||||
.into_iter()
|
|
||||||
.collect()
|
|
||||||
});
|
|
||||||
|
|
||||||
pub const DOCUMENT_SCHEMES: [&str; 5] =
|
pub const DOCUMENT_SCHEMES: [&str; 5] =
|
||||||
["data", "blob", "file", "http", "https"];
|
["data", "blob", "file", "http", "https"];
|
||||||
|
|
||||||
|
@ -103,18 +73,6 @@ pub enum LanguageId {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LanguageId {
|
impl LanguageId {
|
||||||
pub fn as_media_type(&self) -> MediaType {
|
|
||||||
match self {
|
|
||||||
LanguageId::JavaScript => MediaType::JavaScript,
|
|
||||||
LanguageId::Jsx => MediaType::Jsx,
|
|
||||||
LanguageId::TypeScript => MediaType::TypeScript,
|
|
||||||
LanguageId::Tsx => MediaType::Tsx,
|
|
||||||
LanguageId::Json => MediaType::Json,
|
|
||||||
LanguageId::JsonC => MediaType::Json,
|
|
||||||
LanguageId::Markdown | LanguageId::Unknown => MediaType::Unknown,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_extension(&self) -> Option<&'static str> {
|
pub fn as_extension(&self) -> Option<&'static str> {
|
||||||
match self {
|
match self {
|
||||||
LanguageId::JavaScript => Some("js"),
|
LanguageId::JavaScript => Some("js"),
|
||||||
|
@ -128,13 +86,15 @@ impl LanguageId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_headers(&self) -> Option<&HashMap<String, String>> {
|
pub fn as_content_type(&self) -> Option<&'static str> {
|
||||||
match self {
|
match self {
|
||||||
Self::JavaScript => Some(&JS_HEADERS),
|
LanguageId::JavaScript => Some("application/javascript"),
|
||||||
Self::Jsx => Some(&JSX_HEADERS),
|
LanguageId::Jsx => Some("text/jsx"),
|
||||||
Self::TypeScript => Some(&TS_HEADERS),
|
LanguageId::TypeScript => Some("application/typescript"),
|
||||||
Self::Tsx => Some(&TSX_HEADERS),
|
LanguageId::Tsx => Some("text/tsx"),
|
||||||
_ => None,
|
LanguageId::Json | LanguageId::JsonC => Some("application/json"),
|
||||||
|
LanguageId::Markdown => Some("text/markdown"),
|
||||||
|
LanguageId::Unknown => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,6 +251,7 @@ pub struct Document {
|
||||||
// so having a mutex to hold it is ok
|
// so having a mutex to hold it is ok
|
||||||
maybe_navigation_tree: Mutex<Option<Arc<tsc::NavigationTree>>>,
|
maybe_navigation_tree: Mutex<Option<Arc<tsc::NavigationTree>>>,
|
||||||
maybe_parsed_source: Option<ParsedSourceResult>,
|
maybe_parsed_source: Option<ParsedSourceResult>,
|
||||||
|
media_type: MediaType,
|
||||||
specifier: ModuleSpecifier,
|
specifier: ModuleSpecifier,
|
||||||
text_info: SourceTextInfo,
|
text_info: SourceTextInfo,
|
||||||
}
|
}
|
||||||
|
@ -302,15 +263,23 @@ impl Document {
|
||||||
maybe_headers: Option<HashMap<String, String>>,
|
maybe_headers: Option<HashMap<String, String>>,
|
||||||
text_info: SourceTextInfo,
|
text_info: SourceTextInfo,
|
||||||
resolver: &dyn deno_graph::source::Resolver,
|
resolver: &dyn deno_graph::source::Resolver,
|
||||||
|
maybe_node_resolver: Option<&CliNodeResolver>,
|
||||||
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
||||||
) -> Arc<Self> {
|
) -> Arc<Self> {
|
||||||
// we only ever do `Document::new` on disk resources that are supposed to
|
// we only ever do `Document::new` on disk resources that are supposed to
|
||||||
// be diagnosable, unlike `Document::open`, so it is safe to unconditionally
|
// be diagnosable, unlike `Document::open`, so it is safe to unconditionally
|
||||||
// parse the module.
|
// parse the module.
|
||||||
|
let media_type = resolve_media_type(
|
||||||
|
&specifier,
|
||||||
|
maybe_headers.as_ref(),
|
||||||
|
None,
|
||||||
|
maybe_node_resolver,
|
||||||
|
);
|
||||||
let (maybe_parsed_source, maybe_module) = parse_and_analyze_module(
|
let (maybe_parsed_source, maybe_module) = parse_and_analyze_module(
|
||||||
&specifier,
|
&specifier,
|
||||||
text_info.clone(),
|
text_info.clone(),
|
||||||
maybe_headers.as_ref(),
|
maybe_headers.as_ref(),
|
||||||
|
media_type,
|
||||||
resolver,
|
resolver,
|
||||||
npm_resolver,
|
npm_resolver,
|
||||||
);
|
);
|
||||||
|
@ -328,6 +297,7 @@ impl Document {
|
||||||
maybe_navigation_tree: Mutex::new(None),
|
maybe_navigation_tree: Mutex::new(None),
|
||||||
maybe_parsed_source: maybe_parsed_source
|
maybe_parsed_source: maybe_parsed_source
|
||||||
.filter(|_| specifier.scheme() == "file"),
|
.filter(|_| specifier.scheme() == "file"),
|
||||||
|
media_type,
|
||||||
text_info,
|
text_info,
|
||||||
specifier,
|
specifier,
|
||||||
})
|
})
|
||||||
|
@ -336,12 +306,27 @@ impl Document {
|
||||||
fn maybe_with_new_resolver(
|
fn maybe_with_new_resolver(
|
||||||
&self,
|
&self,
|
||||||
resolver: &dyn deno_graph::source::Resolver,
|
resolver: &dyn deno_graph::source::Resolver,
|
||||||
|
maybe_node_resolver: Option<&CliNodeResolver>,
|
||||||
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
||||||
) -> Option<Arc<Self>> {
|
) -> Option<Arc<Self>> {
|
||||||
let parsed_source_result = match &self.maybe_parsed_source {
|
let mut parsed_source_result = match &self.maybe_parsed_source {
|
||||||
Some(parsed_source_result) => parsed_source_result.clone(),
|
Some(parsed_source_result) => parsed_source_result.clone(),
|
||||||
None => return None, // nothing to change
|
None => return None, // nothing to change
|
||||||
};
|
};
|
||||||
|
let media_type = resolve_media_type(
|
||||||
|
&self.specifier,
|
||||||
|
self.maybe_headers.as_ref(),
|
||||||
|
self.maybe_language_id,
|
||||||
|
maybe_node_resolver,
|
||||||
|
);
|
||||||
|
// reparse if the media type has changed
|
||||||
|
if let Ok(parsed_source) = &parsed_source_result {
|
||||||
|
if parsed_source.media_type() != media_type {
|
||||||
|
parsed_source_result =
|
||||||
|
parse_source(&self.specifier, self.text_info.clone(), media_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let maybe_module = Some(analyze_module(
|
let maybe_module = Some(analyze_module(
|
||||||
&self.specifier,
|
&self.specifier,
|
||||||
&parsed_source_result,
|
&parsed_source_result,
|
||||||
|
@ -363,27 +348,37 @@ impl Document {
|
||||||
maybe_headers: self.maybe_headers.clone(),
|
maybe_headers: self.maybe_headers.clone(),
|
||||||
maybe_language_id: self.maybe_language_id,
|
maybe_language_id: self.maybe_language_id,
|
||||||
maybe_lsp_version: self.maybe_lsp_version,
|
maybe_lsp_version: self.maybe_lsp_version,
|
||||||
|
media_type,
|
||||||
text_info: self.text_info.clone(),
|
text_info: self.text_info.clone(),
|
||||||
specifier: self.specifier.clone(),
|
specifier: self.specifier.clone(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn open(
|
fn open(
|
||||||
specifier: ModuleSpecifier,
|
specifier: ModuleSpecifier,
|
||||||
version: i32,
|
version: i32,
|
||||||
language_id: LanguageId,
|
language_id: LanguageId,
|
||||||
|
maybe_headers: Option<HashMap<String, String>>,
|
||||||
content: Arc<str>,
|
content: Arc<str>,
|
||||||
cache: &Arc<dyn HttpCache>,
|
cache: &Arc<dyn HttpCache>,
|
||||||
resolver: &dyn deno_graph::source::Resolver,
|
resolver: &dyn deno_graph::source::Resolver,
|
||||||
|
maybe_node_resolver: Option<&CliNodeResolver>,
|
||||||
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
||||||
) -> Arc<Self> {
|
) -> Arc<Self> {
|
||||||
let maybe_headers = language_id.as_headers();
|
|
||||||
let text_info = SourceTextInfo::new(content);
|
let text_info = SourceTextInfo::new(content);
|
||||||
|
let media_type = resolve_media_type(
|
||||||
|
&specifier,
|
||||||
|
None,
|
||||||
|
Some(language_id),
|
||||||
|
maybe_node_resolver,
|
||||||
|
);
|
||||||
let (maybe_parsed_source, maybe_module) = if language_id.is_diagnosable() {
|
let (maybe_parsed_source, maybe_module) = if language_id.is_diagnosable() {
|
||||||
parse_and_analyze_module(
|
parse_and_analyze_module(
|
||||||
&specifier,
|
&specifier,
|
||||||
text_info.clone(),
|
text_info.clone(),
|
||||||
maybe_headers,
|
maybe_headers.as_ref(),
|
||||||
|
media_type,
|
||||||
resolver,
|
resolver,
|
||||||
npm_resolver,
|
npm_resolver,
|
||||||
)
|
)
|
||||||
|
@ -400,11 +395,12 @@ impl Document {
|
||||||
line_index,
|
line_index,
|
||||||
maybe_language_id: Some(language_id),
|
maybe_language_id: Some(language_id),
|
||||||
maybe_lsp_version: Some(version),
|
maybe_lsp_version: Some(version),
|
||||||
maybe_headers: maybe_headers.map(ToOwned::to_owned),
|
maybe_headers,
|
||||||
maybe_module,
|
maybe_module,
|
||||||
maybe_navigation_tree: Mutex::new(None),
|
maybe_navigation_tree: Mutex::new(None),
|
||||||
maybe_parsed_source: maybe_parsed_source
|
maybe_parsed_source: maybe_parsed_source
|
||||||
.filter(|_| specifier.scheme() == "file"),
|
.filter(|_| specifier.scheme() == "file"),
|
||||||
|
media_type,
|
||||||
text_info,
|
text_info,
|
||||||
specifier,
|
specifier,
|
||||||
})
|
})
|
||||||
|
@ -434,20 +430,18 @@ impl Document {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let text_info = SourceTextInfo::from_string(content);
|
let text_info = SourceTextInfo::from_string(content);
|
||||||
|
let media_type = self.media_type;
|
||||||
let (maybe_parsed_source, maybe_module) = if self
|
let (maybe_parsed_source, maybe_module) = if self
|
||||||
.maybe_language_id
|
.maybe_language_id
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|li| li.is_diagnosable())
|
.map(|li| li.is_diagnosable())
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
let maybe_headers = self
|
|
||||||
.maybe_language_id
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|li| li.as_headers());
|
|
||||||
parse_and_analyze_module(
|
parse_and_analyze_module(
|
||||||
&self.specifier,
|
&self.specifier,
|
||||||
text_info.clone(),
|
text_info.clone(),
|
||||||
maybe_headers,
|
self.maybe_headers.as_ref(),
|
||||||
|
media_type,
|
||||||
resolver,
|
resolver,
|
||||||
npm_resolver,
|
npm_resolver,
|
||||||
)
|
)
|
||||||
|
@ -477,6 +471,7 @@ impl Document {
|
||||||
.filter(|_| self.specifier.scheme() == "file"),
|
.filter(|_| self.specifier.scheme() == "file"),
|
||||||
maybe_lsp_version: Some(version),
|
maybe_lsp_version: Some(version),
|
||||||
maybe_navigation_tree: Mutex::new(None),
|
maybe_navigation_tree: Mutex::new(None),
|
||||||
|
media_type,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,6 +489,7 @@ impl Document {
|
||||||
maybe_parsed_source: self.maybe_parsed_source.clone(),
|
maybe_parsed_source: self.maybe_parsed_source.clone(),
|
||||||
maybe_lsp_version: self.maybe_lsp_version,
|
maybe_lsp_version: self.maybe_lsp_version,
|
||||||
maybe_navigation_tree: Mutex::new(None),
|
maybe_navigation_tree: Mutex::new(None),
|
||||||
|
media_type: self.media_type,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -554,18 +550,7 @@ impl Document {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn media_type(&self) -> MediaType {
|
pub fn media_type(&self) -> MediaType {
|
||||||
if let Some(Ok(module)) = &self.maybe_module {
|
self.media_type
|
||||||
return module.media_type;
|
|
||||||
}
|
|
||||||
let specifier_media_type = MediaType::from_specifier(&self.specifier);
|
|
||||||
if specifier_media_type != MediaType::Unknown {
|
|
||||||
return specifier_media_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
self
|
|
||||||
.maybe_language_id
|
|
||||||
.map(|id| id.as_media_type())
|
|
||||||
.unwrap_or(MediaType::Unknown)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn maybe_language_id(&self) -> Option<LanguageId> {
|
pub fn maybe_language_id(&self) -> Option<LanguageId> {
|
||||||
|
@ -629,6 +614,39 @@ impl Document {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_media_type(
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
maybe_headers: Option<&HashMap<String, String>>,
|
||||||
|
maybe_language_id: Option<LanguageId>,
|
||||||
|
maybe_node_resolver: Option<&CliNodeResolver>,
|
||||||
|
) -> MediaType {
|
||||||
|
if let Some(node_resolver) = maybe_node_resolver {
|
||||||
|
if node_resolver.in_npm_package(specifier) {
|
||||||
|
match node_resolver.url_to_node_resolution(specifier.clone()) {
|
||||||
|
Ok(resolution) => {
|
||||||
|
let (_, media_type) =
|
||||||
|
NodeResolution::into_specifier_and_media_type(Some(resolution));
|
||||||
|
return media_type;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
lsp_warn!("Node resolution failed for '{}': {}", specifier, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if maybe_headers.is_some() {
|
||||||
|
return MediaType::from_specifier_and_headers(specifier, maybe_headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
// LanguageId is a subset of MediaType, so get its content type and
|
||||||
|
// also use the specifier to inform its media type
|
||||||
|
MediaType::from_specifier_and_content_type(
|
||||||
|
specifier,
|
||||||
|
maybe_language_id.and_then(|id| id.as_content_type()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_lsp_range(range: &deno_graph::Range) -> lsp::Range {
|
pub fn to_lsp_range(range: &deno_graph::Range) -> lsp::Range {
|
||||||
lsp::Range {
|
lsp::Range {
|
||||||
start: lsp::Position {
|
start: lsp::Position {
|
||||||
|
@ -712,6 +730,7 @@ impl FileSystemDocuments {
|
||||||
cache: &Arc<dyn HttpCache>,
|
cache: &Arc<dyn HttpCache>,
|
||||||
resolver: &dyn deno_graph::source::Resolver,
|
resolver: &dyn deno_graph::source::Resolver,
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
|
maybe_node_resolver: Option<&CliNodeResolver>,
|
||||||
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
||||||
) -> Option<Arc<Document>> {
|
) -> Option<Arc<Document>> {
|
||||||
let fs_version = if specifier.scheme() == "data" {
|
let fs_version = if specifier.scheme() == "data" {
|
||||||
|
@ -724,7 +743,13 @@ impl FileSystemDocuments {
|
||||||
!= fs_version
|
!= fs_version
|
||||||
{
|
{
|
||||||
// attempt to update the file on the file system
|
// attempt to update the file on the file system
|
||||||
self.refresh_document(cache, resolver, specifier, npm_resolver)
|
self.refresh_document(
|
||||||
|
cache,
|
||||||
|
resolver,
|
||||||
|
specifier,
|
||||||
|
maybe_node_resolver,
|
||||||
|
npm_resolver,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
file_system_doc
|
file_system_doc
|
||||||
}
|
}
|
||||||
|
@ -737,6 +762,7 @@ impl FileSystemDocuments {
|
||||||
cache: &Arc<dyn HttpCache>,
|
cache: &Arc<dyn HttpCache>,
|
||||||
resolver: &dyn deno_graph::source::Resolver,
|
resolver: &dyn deno_graph::source::Resolver,
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
|
maybe_node_resolver: Option<&CliNodeResolver>,
|
||||||
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
||||||
) -> Option<Arc<Document>> {
|
) -> Option<Arc<Document>> {
|
||||||
let doc = if specifier.scheme() == "file" {
|
let doc = if specifier.scheme() == "file" {
|
||||||
|
@ -751,6 +777,7 @@ impl FileSystemDocuments {
|
||||||
None,
|
None,
|
||||||
SourceTextInfo::from_string(content),
|
SourceTextInfo::from_string(content),
|
||||||
resolver,
|
resolver,
|
||||||
|
maybe_node_resolver,
|
||||||
npm_resolver,
|
npm_resolver,
|
||||||
)
|
)
|
||||||
} else if specifier.scheme() == "data" {
|
} else if specifier.scheme() == "data" {
|
||||||
|
@ -764,6 +791,7 @@ impl FileSystemDocuments {
|
||||||
None,
|
None,
|
||||||
SourceTextInfo::from_string(source),
|
SourceTextInfo::from_string(source),
|
||||||
resolver,
|
resolver,
|
||||||
|
maybe_node_resolver,
|
||||||
npm_resolver,
|
npm_resolver,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -791,6 +819,7 @@ impl FileSystemDocuments {
|
||||||
maybe_headers,
|
maybe_headers,
|
||||||
SourceTextInfo::from_string(content),
|
SourceTextInfo::from_string(content),
|
||||||
resolver,
|
resolver,
|
||||||
|
maybe_node_resolver,
|
||||||
npm_resolver,
|
npm_resolver,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -837,6 +866,8 @@ pub struct Documents {
|
||||||
/// Any imports to the context supplied by configuration files. This is like
|
/// Any imports to the context supplied by configuration files. This is like
|
||||||
/// the imports into the a module graph in CLI.
|
/// the imports into the a module graph in CLI.
|
||||||
imports: Arc<IndexMap<ModuleSpecifier, GraphImport>>,
|
imports: Arc<IndexMap<ModuleSpecifier, GraphImport>>,
|
||||||
|
/// Resolver for node_modules.
|
||||||
|
maybe_node_resolver: Option<Arc<CliNodeResolver>>,
|
||||||
/// A resolver that takes into account currently loaded import map and JSX
|
/// A resolver that takes into account currently loaded import map and JSX
|
||||||
/// settings.
|
/// settings.
|
||||||
resolver: Arc<CliGraphResolver>,
|
resolver: Arc<CliGraphResolver>,
|
||||||
|
@ -861,6 +892,7 @@ impl Documents {
|
||||||
open_docs: HashMap::default(),
|
open_docs: HashMap::default(),
|
||||||
file_system_docs: Default::default(),
|
file_system_docs: Default::default(),
|
||||||
imports: Default::default(),
|
imports: Default::default(),
|
||||||
|
maybe_node_resolver: None,
|
||||||
resolver: Arc::new(CliGraphResolver::new(CliGraphResolverOptions {
|
resolver: Arc::new(CliGraphResolver::new(CliGraphResolverOptions {
|
||||||
node_resolver: None,
|
node_resolver: None,
|
||||||
npm_resolver: None,
|
npm_resolver: None,
|
||||||
|
@ -905,9 +937,14 @@ impl Documents {
|
||||||
specifier.clone(),
|
specifier.clone(),
|
||||||
version,
|
version,
|
||||||
language_id,
|
language_id,
|
||||||
|
// todo(dsherret): don't we want to pass in the headers from
|
||||||
|
// the cache for remote modules here in order to get the
|
||||||
|
// x-typescript-types?
|
||||||
|
None,
|
||||||
content,
|
content,
|
||||||
&self.cache,
|
&self.cache,
|
||||||
resolver,
|
resolver,
|
||||||
|
self.maybe_node_resolver.as_deref(),
|
||||||
npm_resolver,
|
npm_resolver,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1102,6 +1139,7 @@ impl Documents {
|
||||||
&self.cache,
|
&self.cache,
|
||||||
self.get_resolver(),
|
self.get_resolver(),
|
||||||
&specifier,
|
&specifier,
|
||||||
|
self.maybe_node_resolver.as_deref(),
|
||||||
self.get_npm_resolver(),
|
self.get_npm_resolver(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1281,6 +1319,7 @@ impl Documents {
|
||||||
) {
|
) {
|
||||||
let config_data = config.tree.root_data();
|
let config_data = config.tree.root_data();
|
||||||
let config_file = config_data.and_then(|d| d.config_file.as_deref());
|
let config_file = config_data.and_then(|d| d.config_file.as_deref());
|
||||||
|
self.maybe_node_resolver = node_resolver.clone();
|
||||||
self.resolver = Arc::new(CliGraphResolver::new(CliGraphResolverOptions {
|
self.resolver = Arc::new(CliGraphResolver::new(CliGraphResolverOptions {
|
||||||
node_resolver,
|
node_resolver,
|
||||||
npm_resolver,
|
npm_resolver,
|
||||||
|
@ -1352,9 +1391,11 @@ impl Documents {
|
||||||
if !config.specifier_enabled(doc.specifier()) {
|
if !config.specifier_enabled(doc.specifier()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(new_doc) =
|
if let Some(new_doc) = doc.maybe_with_new_resolver(
|
||||||
doc.maybe_with_new_resolver(resolver, npm_resolver)
|
resolver,
|
||||||
{
|
self.maybe_node_resolver.as_deref(),
|
||||||
|
npm_resolver,
|
||||||
|
) {
|
||||||
*doc = new_doc;
|
*doc = new_doc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1362,9 +1403,11 @@ impl Documents {
|
||||||
if !config.specifier_enabled(doc.specifier()) {
|
if !config.specifier_enabled(doc.specifier()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(new_doc) =
|
if let Some(new_doc) = doc.maybe_with_new_resolver(
|
||||||
doc.maybe_with_new_resolver(resolver, npm_resolver)
|
resolver,
|
||||||
{
|
self.maybe_node_resolver.as_deref(),
|
||||||
|
npm_resolver,
|
||||||
|
) {
|
||||||
*doc.value_mut() = new_doc;
|
*doc.value_mut() = new_doc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1385,6 +1428,7 @@ impl Documents {
|
||||||
&self.cache,
|
&self.cache,
|
||||||
resolver,
|
resolver,
|
||||||
specifier,
|
specifier,
|
||||||
|
self.maybe_node_resolver.as_deref(),
|
||||||
npm_resolver,
|
npm_resolver,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1628,10 +1672,11 @@ fn parse_and_analyze_module(
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
text_info: SourceTextInfo,
|
text_info: SourceTextInfo,
|
||||||
maybe_headers: Option<&HashMap<String, String>>,
|
maybe_headers: Option<&HashMap<String, String>>,
|
||||||
|
media_type: MediaType,
|
||||||
resolver: &dyn deno_graph::source::Resolver,
|
resolver: &dyn deno_graph::source::Resolver,
|
||||||
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
npm_resolver: &dyn deno_graph::source::NpmResolver,
|
||||||
) -> (Option<ParsedSourceResult>, Option<ModuleResult>) {
|
) -> (Option<ParsedSourceResult>, Option<ModuleResult>) {
|
||||||
let parsed_source_result = parse_source(specifier, text_info, maybe_headers);
|
let parsed_source_result = parse_source(specifier, text_info, media_type);
|
||||||
let module_result = analyze_module(
|
let module_result = analyze_module(
|
||||||
specifier,
|
specifier,
|
||||||
&parsed_source_result,
|
&parsed_source_result,
|
||||||
|
@ -1645,12 +1690,12 @@ fn parse_and_analyze_module(
|
||||||
fn parse_source(
|
fn parse_source(
|
||||||
specifier: &ModuleSpecifier,
|
specifier: &ModuleSpecifier,
|
||||||
text_info: SourceTextInfo,
|
text_info: SourceTextInfo,
|
||||||
maybe_headers: Option<&HashMap<String, String>>,
|
media_type: MediaType,
|
||||||
) -> ParsedSourceResult {
|
) -> ParsedSourceResult {
|
||||||
deno_ast::parse_module(deno_ast::ParseParams {
|
deno_ast::parse_module(deno_ast::ParseParams {
|
||||||
specifier: specifier.clone(),
|
specifier: specifier.clone(),
|
||||||
text_info,
|
text_info,
|
||||||
media_type: MediaType::from_specifier_and_headers(specifier, maybe_headers),
|
media_type,
|
||||||
capture_tokens: true,
|
capture_tokens: true,
|
||||||
scope_analysis: true,
|
scope_analysis: true,
|
||||||
maybe_syntax: None,
|
maybe_syntax: None,
|
||||||
|
|
|
@ -3996,6 +3996,7 @@ struct LoadResponse {
|
||||||
data: Arc<str>,
|
data: Arc<str>,
|
||||||
script_kind: i32,
|
script_kind: i32,
|
||||||
version: Option<String>,
|
version: Option<String>,
|
||||||
|
is_cjs: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op2]
|
#[op2]
|
||||||
|
@ -4018,6 +4019,10 @@ fn op_load<'s>(
|
||||||
data: doc.text(),
|
data: doc.text(),
|
||||||
script_kind: crate::tsc::as_ts_script_kind(doc.media_type()),
|
script_kind: crate::tsc::as_ts_script_kind(doc.media_type()),
|
||||||
version: state.script_version(&specifier),
|
version: state.script_version(&specifier),
|
||||||
|
is_cjs: matches!(
|
||||||
|
doc.media_type(),
|
||||||
|
MediaType::Cjs | MediaType::Cts | MediaType::Dcts
|
||||||
|
),
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -223,6 +223,13 @@ impl CliNodeResolver {
|
||||||
Ok(specifier)
|
Ok(specifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn url_to_node_resolution(
|
||||||
|
&self,
|
||||||
|
specifier: ModuleSpecifier,
|
||||||
|
) -> Result<NodeResolution, AnyError> {
|
||||||
|
self.node_resolver.url_to_node_resolution(specifier)
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_node_resolve_result(
|
fn handle_node_resolve_result(
|
||||||
&self,
|
&self,
|
||||||
result: Result<Option<NodeResolution>, AnyError>,
|
result: Result<Option<NodeResolution>, AnyError>,
|
||||||
|
|
|
@ -135,12 +135,16 @@ delete Object.prototype.__proto__;
|
||||||
#cache = new Set();
|
#cache = new Set();
|
||||||
|
|
||||||
/** @param {[string, ts.Extension]} param */
|
/** @param {[string, ts.Extension]} param */
|
||||||
add([specifier, ext]) {
|
maybeAdd([specifier, ext]) {
|
||||||
if (ext === ".cjs" || ext === ".d.cts" || ext === ".cts") {
|
if (ext === ".cjs" || ext === ".d.cts" || ext === ".cts") {
|
||||||
this.#cache.add(specifier);
|
this.#cache.add(specifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
add(specifier) {
|
||||||
|
this.#cache.add(specifier);
|
||||||
|
}
|
||||||
|
|
||||||
/** @param specifier {string} */
|
/** @param specifier {string} */
|
||||||
has(specifier) {
|
has(specifier) {
|
||||||
return this.#cache.has(specifier);
|
return this.#cache.has(specifier);
|
||||||
|
@ -603,22 +607,30 @@ delete Object.prototype.__proto__;
|
||||||
return sourceFile;
|
return sourceFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {{ data: string; scriptKind: ts.ScriptKind; version: string; }} */
|
/** @type {{ data: string; scriptKind: ts.ScriptKind; version: string; isCjs: boolean }} */
|
||||||
const fileInfo = ops.op_load(specifier);
|
const fileInfo = ops.op_load(specifier);
|
||||||
if (!fileInfo) {
|
if (!fileInfo) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const { data, scriptKind, version } = fileInfo;
|
let { data, scriptKind, version, isCjs } = fileInfo;
|
||||||
assert(
|
assert(
|
||||||
data != null,
|
data != null,
|
||||||
`"data" is unexpectedly null for "${specifier}".`,
|
`"data" is unexpectedly null for "${specifier}".`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// use the cache for non-lsp
|
||||||
|
if (isCjs == null) {
|
||||||
|
isCjs = isCjsCache.has(specifier);
|
||||||
|
} else if (isCjs) {
|
||||||
|
isCjsCache.add(specifier);
|
||||||
|
}
|
||||||
|
|
||||||
sourceFile = ts.createSourceFile(
|
sourceFile = ts.createSourceFile(
|
||||||
specifier,
|
specifier,
|
||||||
data,
|
data,
|
||||||
{
|
{
|
||||||
...getCreateSourceFileOptions(languageVersion),
|
...getCreateSourceFileOptions(languageVersion),
|
||||||
impliedNodeFormat: isCjsCache.has(specifier)
|
impliedNodeFormat: isCjs
|
||||||
? ts.ModuleKind.CommonJS
|
? ts.ModuleKind.CommonJS
|
||||||
: ts.ModuleKind.ESNext,
|
: ts.ModuleKind.ESNext,
|
||||||
// no need to parse docs for `deno check`
|
// no need to parse docs for `deno check`
|
||||||
|
@ -688,7 +700,7 @@ delete Object.prototype.__proto__;
|
||||||
base: containingFilePath,
|
base: containingFilePath,
|
||||||
})?.[0];
|
})?.[0];
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
isCjsCache.add(resolved);
|
isCjsCache.maybeAdd(resolved);
|
||||||
return {
|
return {
|
||||||
primary: true,
|
primary: true,
|
||||||
resolvedFileName: resolved[0],
|
resolvedFileName: resolved[0],
|
||||||
|
@ -723,7 +735,7 @@ delete Object.prototype.__proto__;
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
const result = resolved.map((item) => {
|
const result = resolved.map((item) => {
|
||||||
if (item) {
|
if (item) {
|
||||||
isCjsCache.add(item);
|
isCjsCache.maybeAdd(item);
|
||||||
const [resolvedFileName, extension] = item;
|
const [resolvedFileName, extension] = item;
|
||||||
return {
|
return {
|
||||||
resolvedFileName,
|
resolvedFileName,
|
||||||
|
|
|
@ -43,7 +43,7 @@ deno_lockfile.workspace = true
|
||||||
deno_terminal.workspace = true
|
deno_terminal.workspace = true
|
||||||
deno_tls.workspace = true
|
deno_tls.workspace = true
|
||||||
fastwebsockets = { workspace = true, features = ["upgrade", "unstable-split"] }
|
fastwebsockets = { workspace = true, features = ["upgrade", "unstable-split"] }
|
||||||
file_test_runner = "0.2.0"
|
file_test_runner = "0.4.0"
|
||||||
flaky_test = "=0.1.0"
|
flaky_test = "=0.1.0"
|
||||||
http.workspace = true
|
http.workspace = true
|
||||||
http-body-util.workspace = true
|
http-body-util.workspace = true
|
||||||
|
|
|
@ -12287,3 +12287,52 @@ fn lsp_uses_lockfile_for_npm_initialization() {
|
||||||
assert_eq!(skipping_count, 1);
|
assert_eq!(skipping_count, 1);
|
||||||
client.shutdown();
|
client.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lsp_cjs_internal_types_default_export() {
|
||||||
|
let context = TestContextBuilder::new()
|
||||||
|
.use_http_server()
|
||||||
|
.use_temp_cwd()
|
||||||
|
.add_npm_env_vars()
|
||||||
|
.env("DENO_FUTURE", "1")
|
||||||
|
.build();
|
||||||
|
let temp_dir = context.temp_dir();
|
||||||
|
temp_dir.write("deno.json", r#"{}"#);
|
||||||
|
temp_dir.write(
|
||||||
|
"package.json",
|
||||||
|
r#"{
|
||||||
|
"dependencies": {
|
||||||
|
"@denotest/cjs-internal-types-default-export": "*"
|
||||||
|
}
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
context.run_npm("install");
|
||||||
|
|
||||||
|
let mut client = context.new_lsp_command().build();
|
||||||
|
client.initialize_default();
|
||||||
|
// this was previously being resolved as ESM and not correctly as CJS
|
||||||
|
let node_modules_index_d_ts = temp_dir.path().join(
|
||||||
|
"node_modules/@denotest/cjs-internal-types-default-export/index.d.ts",
|
||||||
|
);
|
||||||
|
client.did_open(json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": node_modules_index_d_ts.uri_file(),
|
||||||
|
"languageId": "typescript",
|
||||||
|
"version": 1,
|
||||||
|
"text": node_modules_index_d_ts.read_to_string(),
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
let main_url = temp_dir.path().join("main.ts").uri_file();
|
||||||
|
let diagnostics = client.did_open(
|
||||||
|
json!({
|
||||||
|
"textDocument": {
|
||||||
|
"uri": main_url,
|
||||||
|
"languageId": "typescript",
|
||||||
|
"version": 1,
|
||||||
|
"text": "import * as mod from '@denotest/cjs-internal-types-default-export';\nmod.add(1, 2);",
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
// previously, diagnostic about `add` not being callable
|
||||||
|
assert_eq!(json!(diagnostics.all()), json!([]));
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,10 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use deno_core::anyhow::Context;
|
use deno_core::anyhow::Context;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
|
use file_test_runner::collection::collect_tests_or_exit;
|
||||||
|
use file_test_runner::collection::strategies::TestPerDirectoryCollectionStrategy;
|
||||||
|
use file_test_runner::collection::CollectOptions;
|
||||||
|
use file_test_runner::collection::CollectedTest;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use test_util::tests_path;
|
use test_util::tests_path;
|
||||||
use test_util::PathRef;
|
use test_util::PathRef;
|
||||||
|
@ -69,6 +73,7 @@ struct StepMetaData {
|
||||||
pub clean_deno_dir: bool,
|
pub clean_deno_dir: bool,
|
||||||
pub args: VecOrString,
|
pub args: VecOrString,
|
||||||
pub cwd: Option<String>,
|
pub cwd: Option<String>,
|
||||||
|
pub command_name: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub envs: HashMap<String, String>,
|
pub envs: HashMap<String, String>,
|
||||||
pub output: String,
|
pub output: String,
|
||||||
|
@ -77,15 +82,13 @@ struct StepMetaData {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
let root_category =
|
let root_category = collect_tests_or_exit(CollectOptions {
|
||||||
file_test_runner::collect_tests_or_exit(file_test_runner::CollectOptions {
|
base: tests_path().join("specs").to_path_buf(),
|
||||||
base: tests_path().join("specs").to_path_buf(),
|
strategy: Box::new(TestPerDirectoryCollectionStrategy {
|
||||||
strategy: file_test_runner::FileCollectionStrategy::TestPerDirectory {
|
file_name: MANIFEST_FILE_NAME.to_string(),
|
||||||
file_name: MANIFEST_FILE_NAME.to_string(),
|
}),
|
||||||
},
|
filter_override: None,
|
||||||
root_category_name: "specs".to_string(),
|
});
|
||||||
filter_override: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
if root_category.is_empty() {
|
if root_category.is_empty() {
|
||||||
return; // all tests filtered out
|
return; // all tests filtered out
|
||||||
|
@ -111,15 +114,13 @@ pub fn main() {
|
||||||
output.extend(panic_output);
|
output.extend(panic_output);
|
||||||
file_test_runner::TestResult::Failed { output }
|
file_test_runner::TestResult::Failed { output }
|
||||||
}
|
}
|
||||||
|
file_test_runner::TestResult::Steps(_) => unreachable!(),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_test(
|
fn run_test(test: &CollectedTest, diagnostic_logger: Rc<RefCell<Vec<u8>>>) {
|
||||||
test: &file_test_runner::CollectedTest,
|
|
||||||
diagnostic_logger: Rc<RefCell<Vec<u8>>>,
|
|
||||||
) {
|
|
||||||
let metadata_path = PathRef::new(&test.path);
|
let metadata_path = PathRef::new(&test.path);
|
||||||
let metadata_value = metadata_path.read_jsonc_value();
|
let metadata_value = metadata_path.read_jsonc_value();
|
||||||
// checking for "steps" leads to a more targeted error message
|
// checking for "steps" leads to a more targeted error message
|
||||||
|
@ -182,6 +183,10 @@ fn run_test(
|
||||||
Some(cwd) => command.current_dir(cwd),
|
Some(cwd) => command.current_dir(cwd),
|
||||||
None => command,
|
None => command,
|
||||||
};
|
};
|
||||||
|
let command = match &step.command_name {
|
||||||
|
Some(command_name) => command.name(command_name),
|
||||||
|
None => command,
|
||||||
|
};
|
||||||
let output = command.run();
|
let output = command.run();
|
||||||
if step.output.ends_with(".out") {
|
if step.output.ends_with(".out") {
|
||||||
let test_output_path = cwd.join(&step.output);
|
let test_output_path = cwd.join(&step.output);
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"tempDir": true,
|
||||||
|
"envs": {
|
||||||
|
"DENO_FUTURE": "1"
|
||||||
|
},
|
||||||
|
"steps": [{
|
||||||
|
"commandName": "npm",
|
||||||
|
"args": "install",
|
||||||
|
"output": "[WILDCARD]"
|
||||||
|
}, {
|
||||||
|
"args": "check main.ts",
|
||||||
|
"exitCode": 1,
|
||||||
|
"output": "main.out"
|
||||||
|
}]
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
Check file:///[WILDLINE]/main.ts
|
||||||
|
error: TS2345 [ERROR]: Argument of type 'string' is not assignable to parameter of type 'number'.
|
||||||
|
add(1, "test"); // should error
|
||||||
|
~~~~~~
|
||||||
|
at file:///[WILDLINE]/main.ts:3:8
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { add } from "@denotest/cjs-internal-types-default-export";
|
||||||
|
|
||||||
|
add(1, "test"); // should error
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"@denotest/cjs-internal-types-default-export": "*"
|
||||||
|
}
|
||||||
|
}
|
3
tests/testdata/npm/registry/@denotest/cjs-internal-types-default-export/1.0.0/add.d.ts
vendored
Normal file
3
tests/testdata/npm/registry/@denotest/cjs-internal-types-default-export/1.0.0/add.d.ts
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
const _default: (a: number, b: number) => number;
|
||||||
|
|
||||||
|
export default _default;
|
1
tests/testdata/npm/registry/@denotest/cjs-internal-types-default-export/1.0.0/index.d.ts
vendored
Normal file
1
tests/testdata/npm/registry/@denotest/cjs-internal-types-default-export/1.0.0/index.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { default as add } from './add';
|
1
tests/testdata/npm/registry/@denotest/cjs-internal-types-default-export/1.0.0/index.js
vendored
Normal file
1
tests/testdata/npm/registry/@denotest/cjs-internal-types-default-export/1.0.0/index.js
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
module.exports.add = (a, b) => a + b;
|
4
tests/testdata/npm/registry/@denotest/cjs-internal-types-default-export/1.0.0/package.json
vendored
Normal file
4
tests/testdata/npm/registry/@denotest/cjs-internal-types-default-export/1.0.0/package.json
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"name": "@denotest/cjs-internal-types-default-export",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue