mirror of
https://github.com/denoland/deno.git
synced 2025-01-06 14:26:02 -05:00
fix(check): move module not found errors to typescript diagnostics (#27533)
Instead of hard erroring, we now surface module not found errors as TypeScript diagnostics (we have yet to show the source code of the error, but something we can improve over time).
This commit is contained in:
parent
18b813b93f
commit
89c92b84fa
37 changed files with 684 additions and 358 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -1750,9 +1750,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_graph"
|
||||
version = "0.86.6"
|
||||
version = "0.86.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83af194ca492ea7b624d21055f933676d3f3d27586de93be31c8f1babcc73510"
|
||||
checksum = "ace3acf321fac446636ae605b01723f2120b40ab3d32c6836aeb7d603a8e08f9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -1905,9 +1905,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "deno_media_type"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaa135b8a9febc9a51c16258e294e268a1276750780d69e46edb31cced2826e4"
|
||||
checksum = "a417f8bd3f1074185c4c8ccb6ea6261ae173781596cc358e68ad07aaac11009d"
|
||||
dependencies = [
|
||||
"data-url",
|
||||
"serde",
|
||||
|
|
|
@ -53,7 +53,7 @@ deno_core = { version = "0.327.0" }
|
|||
deno_bench_util = { version = "0.178.0", path = "./bench_util" }
|
||||
deno_config = { version = "=0.42.0", features = ["workspace", "sync"] }
|
||||
deno_lockfile = "=0.24.0"
|
||||
deno_media_type = { version = "0.2.0", features = ["module_specifier"] }
|
||||
deno_media_type = { version = "0.2.3", features = ["module_specifier"] }
|
||||
deno_npm = "=0.27.0"
|
||||
deno_path_util = "=0.3.0"
|
||||
deno_permissions = { version = "0.43.0", path = "./runtime/permissions" }
|
||||
|
|
|
@ -74,7 +74,7 @@ deno_config.workspace = true
|
|||
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
|
||||
deno_doc = { version = "=0.161.3", features = ["rust", "comrak"] }
|
||||
deno_error.workspace = true
|
||||
deno_graph = { version = "=0.86.6" }
|
||||
deno_graph = { version = "=0.86.7" }
|
||||
deno_lint = { version = "=0.68.2", features = ["docs"] }
|
||||
deno_lockfile.workspace = true
|
||||
deno_npm.workspace = true
|
||||
|
|
|
@ -26,51 +26,55 @@ fn get_diagnostic_class(_: &ParseDiagnostic) -> &'static str {
|
|||
"SyntaxError"
|
||||
}
|
||||
|
||||
fn get_module_graph_error_class(err: &ModuleGraphError) -> &'static str {
|
||||
use deno_graph::JsrLoadError;
|
||||
use deno_graph::NpmLoadError;
|
||||
|
||||
pub fn get_module_graph_error_class(err: &ModuleGraphError) -> &'static str {
|
||||
match err {
|
||||
ModuleGraphError::ResolutionError(err)
|
||||
| ModuleGraphError::TypesResolutionError(err) => {
|
||||
get_resolution_error_class(err)
|
||||
}
|
||||
ModuleGraphError::ModuleError(err) => match err {
|
||||
ModuleError::InvalidTypeAssertion { .. } => "SyntaxError",
|
||||
ModuleError::ParseErr(_, diagnostic) => get_diagnostic_class(diagnostic),
|
||||
ModuleError::WasmParseErr(..) => "SyntaxError",
|
||||
ModuleError::UnsupportedMediaType { .. }
|
||||
| ModuleError::UnsupportedImportAttributeType { .. } => "TypeError",
|
||||
ModuleError::Missing(_, _) | ModuleError::MissingDynamic(_, _) => {
|
||||
"NotFound"
|
||||
}
|
||||
ModuleError::LoadingErr(_, _, err) => match err {
|
||||
ModuleLoadError::Loader(err) => get_error_class_name(err.as_ref()),
|
||||
ModuleLoadError::HttpsChecksumIntegrity(_)
|
||||
| ModuleLoadError::TooManyRedirects => "Error",
|
||||
ModuleLoadError::NodeUnknownBuiltinModule(_) => "NotFound",
|
||||
ModuleLoadError::Decode(_) => "TypeError",
|
||||
ModuleLoadError::Npm(err) => match err {
|
||||
NpmLoadError::NotSupportedEnvironment
|
||||
| NpmLoadError::PackageReqResolution(_)
|
||||
| NpmLoadError::RegistryInfo(_) => "Error",
|
||||
NpmLoadError::PackageReqReferenceParse(_) => "TypeError",
|
||||
},
|
||||
ModuleLoadError::Jsr(err) => match err {
|
||||
JsrLoadError::UnsupportedManifestChecksum
|
||||
| JsrLoadError::PackageFormat(_) => "TypeError",
|
||||
JsrLoadError::ContentLoadExternalSpecifier
|
||||
| JsrLoadError::ContentLoad(_)
|
||||
| JsrLoadError::ContentChecksumIntegrity(_)
|
||||
| JsrLoadError::PackageManifestLoad(_, _)
|
||||
| JsrLoadError::PackageVersionManifestChecksumIntegrity(..)
|
||||
| JsrLoadError::PackageVersionManifestLoad(_, _)
|
||||
| JsrLoadError::RedirectInPackage(_) => "Error",
|
||||
JsrLoadError::PackageNotFound(_)
|
||||
| JsrLoadError::PackageReqNotFound(_)
|
||||
| JsrLoadError::PackageVersionNotFound(_)
|
||||
| JsrLoadError::UnknownExport { .. } => "NotFound",
|
||||
},
|
||||
ModuleGraphError::ModuleError(err) => get_module_error_class(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_module_error_class(err: &ModuleError) -> &'static str {
|
||||
use deno_graph::JsrLoadError;
|
||||
use deno_graph::NpmLoadError;
|
||||
|
||||
match err {
|
||||
ModuleError::InvalidTypeAssertion { .. } => "SyntaxError",
|
||||
ModuleError::ParseErr(_, diagnostic) => get_diagnostic_class(diagnostic),
|
||||
ModuleError::WasmParseErr(..) => "SyntaxError",
|
||||
ModuleError::UnsupportedMediaType { .. }
|
||||
| ModuleError::UnsupportedImportAttributeType { .. } => "TypeError",
|
||||
ModuleError::Missing(_, _) | ModuleError::MissingDynamic(_, _) => {
|
||||
"NotFound"
|
||||
}
|
||||
ModuleError::LoadingErr(_, _, err) => match err {
|
||||
ModuleLoadError::Loader(err) => get_error_class_name(err.as_ref()),
|
||||
ModuleLoadError::HttpsChecksumIntegrity(_)
|
||||
| ModuleLoadError::TooManyRedirects => "Error",
|
||||
ModuleLoadError::NodeUnknownBuiltinModule(_) => "NotFound",
|
||||
ModuleLoadError::Decode(_) => "TypeError",
|
||||
ModuleLoadError::Npm(err) => match err {
|
||||
NpmLoadError::NotSupportedEnvironment
|
||||
| NpmLoadError::PackageReqResolution(_)
|
||||
| NpmLoadError::RegistryInfo(_) => "Error",
|
||||
NpmLoadError::PackageReqReferenceParse(_) => "TypeError",
|
||||
},
|
||||
ModuleLoadError::Jsr(err) => match err {
|
||||
JsrLoadError::UnsupportedManifestChecksum
|
||||
| JsrLoadError::PackageFormat(_) => "TypeError",
|
||||
JsrLoadError::ContentLoadExternalSpecifier
|
||||
| JsrLoadError::ContentLoad(_)
|
||||
| JsrLoadError::ContentChecksumIntegrity(_)
|
||||
| JsrLoadError::PackageManifestLoad(_, _)
|
||||
| JsrLoadError::PackageVersionManifestChecksumIntegrity(..)
|
||||
| JsrLoadError::PackageVersionManifestLoad(_, _)
|
||||
| JsrLoadError::RedirectInPackage(_) => "Error",
|
||||
JsrLoadError::PackageNotFound(_)
|
||||
| JsrLoadError::PackageReqNotFound(_)
|
||||
| JsrLoadError::PackageVersionNotFound(_)
|
||||
| JsrLoadError::UnknownExport { .. } => "NotFound",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -753,6 +753,7 @@ impl CliFactory {
|
|||
self.module_graph_builder().await?.clone(),
|
||||
self.node_resolver().await?.clone(),
|
||||
self.npm_resolver().await?.clone(),
|
||||
self.sys(),
|
||||
)))
|
||||
})
|
||||
.await
|
||||
|
|
|
@ -50,6 +50,7 @@ use crate::cache::ModuleInfoCache;
|
|||
use crate::cache::ParsedSourceCache;
|
||||
use crate::colors;
|
||||
use crate::errors::get_error_class_name;
|
||||
use crate::errors::get_module_graph_error_class;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::resolver::CjsTracker;
|
||||
|
@ -164,29 +165,15 @@ pub fn graph_walk_errors<'a>(
|
|||
roots.contains(error.specifier())
|
||||
}
|
||||
};
|
||||
let mut message = match &error {
|
||||
ModuleGraphError::ResolutionError(resolution_error) => {
|
||||
enhanced_resolution_error_message(resolution_error)
|
||||
}
|
||||
ModuleGraphError::TypesResolutionError(resolution_error) => {
|
||||
format!(
|
||||
"Failed resolving types. {}",
|
||||
enhanced_resolution_error_message(resolution_error)
|
||||
)
|
||||
}
|
||||
ModuleGraphError::ModuleError(error) => {
|
||||
enhanced_integrity_error_message(error)
|
||||
.or_else(|| enhanced_sloppy_imports_error_message(sys, error))
|
||||
.unwrap_or_else(|| format_deno_graph_error(error))
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(range) = error.maybe_range() {
|
||||
if !is_root && !range.specifier.as_str().contains("/$deno$eval") {
|
||||
message.push_str("\n at ");
|
||||
message.push_str(&format_range_with_colors(range));
|
||||
}
|
||||
}
|
||||
let message = enhance_graph_error(
|
||||
sys,
|
||||
&error,
|
||||
if is_root {
|
||||
EnhanceGraphErrorMode::HideRange
|
||||
} else {
|
||||
EnhanceGraphErrorMode::ShowRange
|
||||
},
|
||||
);
|
||||
|
||||
if graph.graph_kind() == GraphKind::TypesOnly
|
||||
&& matches!(
|
||||
|
@ -198,10 +185,61 @@ pub fn graph_walk_errors<'a>(
|
|||
return None;
|
||||
}
|
||||
|
||||
Some(custom_error(get_error_class_name(&error.into()), message))
|
||||
if graph.graph_kind().include_types()
|
||||
&& (message.contains(RUN_WITH_SLOPPY_IMPORTS_MSG)
|
||||
|| matches!(
|
||||
error,
|
||||
ModuleGraphError::ModuleError(ModuleError::Missing(..))
|
||||
))
|
||||
{
|
||||
// ignore and let typescript surface this as a diagnostic instead
|
||||
log::debug!("Ignoring: {}", message);
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(custom_error(get_module_graph_error_class(&error), message))
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum EnhanceGraphErrorMode {
|
||||
ShowRange,
|
||||
HideRange,
|
||||
}
|
||||
|
||||
pub fn enhance_graph_error(
|
||||
sys: &CliSys,
|
||||
error: &ModuleGraphError,
|
||||
mode: EnhanceGraphErrorMode,
|
||||
) -> String {
|
||||
let mut message = match &error {
|
||||
ModuleGraphError::ResolutionError(resolution_error) => {
|
||||
enhanced_resolution_error_message(resolution_error)
|
||||
}
|
||||
ModuleGraphError::TypesResolutionError(resolution_error) => {
|
||||
format!(
|
||||
"Failed resolving types. {}",
|
||||
enhanced_resolution_error_message(resolution_error)
|
||||
)
|
||||
}
|
||||
ModuleGraphError::ModuleError(error) => {
|
||||
enhanced_integrity_error_message(error)
|
||||
.or_else(|| enhanced_sloppy_imports_error_message(sys, error))
|
||||
.unwrap_or_else(|| format_deno_graph_error(error))
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(range) = error.maybe_range() {
|
||||
if mode == EnhanceGraphErrorMode::ShowRange
|
||||
&& !range.specifier.as_str().contains("/$deno$eval")
|
||||
{
|
||||
message.push_str("\n at ");
|
||||
message.push_str(&format_range_with_colors(range));
|
||||
}
|
||||
}
|
||||
message
|
||||
}
|
||||
|
||||
pub fn graph_exit_integrity_errors(graph: &ModuleGraph) {
|
||||
for error in graph.module_errors() {
|
||||
exit_for_integrity_error(error);
|
||||
|
@ -835,6 +873,9 @@ pub fn enhanced_resolution_error_message(error: &ResolutionError) -> String {
|
|||
message
|
||||
}
|
||||
|
||||
static RUN_WITH_SLOPPY_IMPORTS_MSG: &str =
|
||||
"or run with --unstable-sloppy-imports";
|
||||
|
||||
fn enhanced_sloppy_imports_error_message(
|
||||
sys: &CliSys,
|
||||
error: &ModuleError,
|
||||
|
@ -842,11 +883,9 @@ fn enhanced_sloppy_imports_error_message(
|
|||
match error {
|
||||
ModuleError::LoadingErr(specifier, _, ModuleLoadError::Loader(_)) // ex. "Is a directory" error
|
||||
| ModuleError::Missing(specifier, _) => {
|
||||
let additional_message = CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(sys.clone()))
|
||||
.resolve(specifier, SloppyImportsResolutionKind::Execution)?
|
||||
.as_suggestion_message();
|
||||
let additional_message = maybe_additional_sloppy_imports_message(sys, specifier)?;
|
||||
Some(format!(
|
||||
"{} {} or run with --unstable-sloppy-imports",
|
||||
"{} {}",
|
||||
error,
|
||||
additional_message,
|
||||
))
|
||||
|
@ -855,6 +894,19 @@ fn enhanced_sloppy_imports_error_message(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn maybe_additional_sloppy_imports_message(
|
||||
sys: &CliSys,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<String> {
|
||||
Some(format!(
|
||||
"{} {}",
|
||||
CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(sys.clone()))
|
||||
.resolve(specifier, SloppyImportsResolutionKind::Execution)?
|
||||
.as_suggestion_message(),
|
||||
RUN_WITH_SLOPPY_IMPORTS_MSG
|
||||
))
|
||||
}
|
||||
|
||||
fn enhanced_integrity_error_message(err: &ModuleError) -> Option<String> {
|
||||
match err {
|
||||
ModuleError::LoadingErr(
|
||||
|
|
|
@ -36,6 +36,7 @@ use deno_graph::JsModule;
|
|||
use deno_graph::JsonModule;
|
||||
use deno_graph::Module;
|
||||
use deno_graph::ModuleGraph;
|
||||
use deno_graph::ModuleGraphError;
|
||||
use deno_graph::Resolution;
|
||||
use deno_graph::WasmModule;
|
||||
use deno_runtime::code_cache;
|
||||
|
@ -58,10 +59,13 @@ use crate::cache::CodeCache;
|
|||
use crate::cache::FastInsecureHasher;
|
||||
use crate::cache::ParsedSourceCache;
|
||||
use crate::emit::Emitter;
|
||||
use crate::errors::get_module_error_class;
|
||||
use crate::graph_container::MainModuleGraphContainer;
|
||||
use crate::graph_container::ModuleGraphContainer;
|
||||
use crate::graph_container::ModuleGraphUpdatePermit;
|
||||
use crate::graph_util::enhance_graph_error;
|
||||
use crate::graph_util::CreateGraphOptions;
|
||||
use crate::graph_util::EnhanceGraphErrorMode;
|
||||
use crate::graph_util::ModuleGraphBuilder;
|
||||
use crate::node::CliNodeCodeTranslator;
|
||||
use crate::node::CliNodeResolver;
|
||||
|
@ -703,7 +707,21 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
unreachable!("Deno bug. {} was misconfigured internally.", specifier);
|
||||
}
|
||||
|
||||
match graph.get(specifier) {
|
||||
let maybe_module = match graph.try_get(specifier) {
|
||||
Ok(module) => module,
|
||||
Err(err) => {
|
||||
return Err(custom_error(
|
||||
get_module_error_class(err),
|
||||
enhance_graph_error(
|
||||
&self.shared.sys,
|
||||
&ModuleGraphError::ModuleError(err.clone()),
|
||||
EnhanceGraphErrorMode::ShowRange,
|
||||
),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
match maybe_module {
|
||||
Some(deno_graph::Module::Json(JsonModule {
|
||||
source,
|
||||
media_type,
|
||||
|
|
|
@ -8,7 +8,9 @@ use deno_ast::MediaType;
|
|||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_graph::Module;
|
||||
use deno_graph::ModuleError;
|
||||
use deno_graph::ModuleGraph;
|
||||
use deno_graph::ModuleLoadError;
|
||||
use deno_terminal::colors;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
|
@ -26,10 +28,12 @@ use crate::cache::Caches;
|
|||
use crate::cache::FastInsecureHasher;
|
||||
use crate::cache::TypeCheckCache;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::graph_util::maybe_additional_sloppy_imports_message;
|
||||
use crate::graph_util::BuildFastCheckGraphOptions;
|
||||
use crate::graph_util::ModuleGraphBuilder;
|
||||
use crate::node::CliNodeResolver;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::sys::CliSys;
|
||||
use crate::tsc;
|
||||
use crate::tsc::Diagnostics;
|
||||
use crate::tsc::TypeCheckingCjsTracker;
|
||||
|
@ -105,6 +109,7 @@ pub struct TypeChecker {
|
|||
module_graph_builder: Arc<ModuleGraphBuilder>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
sys: CliSys,
|
||||
}
|
||||
|
||||
impl TypeChecker {
|
||||
|
@ -115,6 +120,7 @@ impl TypeChecker {
|
|||
module_graph_builder: Arc<ModuleGraphBuilder>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
sys: CliSys,
|
||||
) -> Self {
|
||||
Self {
|
||||
caches,
|
||||
|
@ -123,6 +129,7 @@ impl TypeChecker {
|
|||
module_graph_builder,
|
||||
node_resolver,
|
||||
npm_resolver,
|
||||
sys,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,26 +184,47 @@ impl TypeChecker {
|
|||
|
||||
let type_check_mode = options.type_check_mode;
|
||||
let ts_config = ts_config_result.ts_config;
|
||||
let maybe_check_hash = match self.npm_resolver.check_state_hash() {
|
||||
Some(npm_check_hash) => {
|
||||
match get_check_hash(
|
||||
&graph,
|
||||
npm_check_hash,
|
||||
type_check_mode,
|
||||
&ts_config,
|
||||
) {
|
||||
CheckHashResult::NoFiles => {
|
||||
return Ok((graph.into(), Default::default()))
|
||||
}
|
||||
CheckHashResult::Hash(hash) => Some(hash),
|
||||
}
|
||||
}
|
||||
None => None, // we can't determine a check hash
|
||||
};
|
||||
|
||||
// do not type check if we know this is type checked
|
||||
let cache = TypeCheckCache::new(self.caches.type_checking_cache_db());
|
||||
let check_js = ts_config.get_check_js();
|
||||
|
||||
// add fast check to the graph before getting the roots
|
||||
if options.build_fast_check_graph {
|
||||
self.module_graph_builder.build_fast_check_graph(
|
||||
&mut graph,
|
||||
BuildFastCheckGraphOptions {
|
||||
workspace_fast_check: deno_graph::WorkspaceFastCheckOption::Disabled,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
let filter_remote_diagnostics = |d: &tsc::Diagnostic| {
|
||||
if self.is_remote_diagnostic(d) {
|
||||
type_check_mode == TypeCheckMode::All && d.include_when_remote()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
};
|
||||
let TscRoots {
|
||||
roots: root_names,
|
||||
missing_diagnostics,
|
||||
maybe_check_hash,
|
||||
} = get_tsc_roots(
|
||||
&self.sys,
|
||||
&graph,
|
||||
check_js,
|
||||
self.npm_resolver.check_state_hash(),
|
||||
type_check_mode,
|
||||
&ts_config,
|
||||
);
|
||||
|
||||
let missing_diagnostics =
|
||||
missing_diagnostics.filter(filter_remote_diagnostics);
|
||||
|
||||
if root_names.is_empty() && missing_diagnostics.is_empty() {
|
||||
return Ok((graph.into(), Default::default()));
|
||||
}
|
||||
if !options.reload {
|
||||
// do not type check if we know this is type checked
|
||||
if let Some(check_hash) = maybe_check_hash {
|
||||
if cache.has_check_hash(check_hash) {
|
||||
log::debug!("Already type checked.");
|
||||
|
@ -214,7 +242,6 @@ impl TypeChecker {
|
|||
);
|
||||
}
|
||||
|
||||
let check_js = ts_config.get_check_js();
|
||||
// while there might be multiple roots, we can't "merge" the build info, so we
|
||||
// try to retrieve the build info for first root, which is the most common use
|
||||
// case.
|
||||
|
@ -226,27 +253,15 @@ impl TypeChecker {
|
|||
// to make tsc build info work, we need to consistently hash modules, so that
|
||||
// tsc can better determine if an emit is still valid or not, so we provide
|
||||
// that data here.
|
||||
let hash_data = FastInsecureHasher::new_deno_versioned()
|
||||
let tsconfig_hash_data = FastInsecureHasher::new_deno_versioned()
|
||||
.write(&ts_config.as_bytes())
|
||||
.finish();
|
||||
|
||||
// add fast check to the graph before getting the roots
|
||||
if options.build_fast_check_graph {
|
||||
self.module_graph_builder.build_fast_check_graph(
|
||||
&mut graph,
|
||||
BuildFastCheckGraphOptions {
|
||||
workspace_fast_check: deno_graph::WorkspaceFastCheckOption::Disabled,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
let root_names = get_tsc_roots(&graph, check_js);
|
||||
let graph = Arc::new(graph);
|
||||
let response = tsc::exec(tsc::Request {
|
||||
config: ts_config,
|
||||
debug: self.cli_options.log_level() == Some(log::Level::Debug),
|
||||
graph: graph.clone(),
|
||||
hash_data,
|
||||
hash_data: tsconfig_hash_data,
|
||||
maybe_npm: Some(tsc::RequestNpmState {
|
||||
cjs_tracker: self.cjs_tracker.clone(),
|
||||
node_resolver: self.node_resolver.clone(),
|
||||
|
@ -257,13 +272,11 @@ impl TypeChecker {
|
|||
check_mode: type_check_mode,
|
||||
})?;
|
||||
|
||||
let mut diagnostics = response.diagnostics.filter(|d| {
|
||||
if self.is_remote_diagnostic(d) {
|
||||
type_check_mode == TypeCheckMode::All && d.include_when_remote()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
let response_diagnostics =
|
||||
response.diagnostics.filter(filter_remote_diagnostics);
|
||||
|
||||
let mut diagnostics = missing_diagnostics;
|
||||
diagnostics.extend(response_diagnostics);
|
||||
|
||||
diagnostics.apply_fast_check_source_maps(&graph);
|
||||
|
||||
|
@ -297,108 +310,10 @@ impl TypeChecker {
|
|||
}
|
||||
}
|
||||
|
||||
enum CheckHashResult {
|
||||
Hash(CacheDBHash),
|
||||
NoFiles,
|
||||
}
|
||||
|
||||
/// Gets a hash of the inputs for type checking. This can then
|
||||
/// be used to tell
|
||||
fn get_check_hash(
|
||||
graph: &ModuleGraph,
|
||||
package_reqs_hash: u64,
|
||||
type_check_mode: TypeCheckMode,
|
||||
ts_config: &TsConfig,
|
||||
) -> CheckHashResult {
|
||||
let mut hasher = FastInsecureHasher::new_deno_versioned();
|
||||
hasher.write_u8(match type_check_mode {
|
||||
TypeCheckMode::All => 0,
|
||||
TypeCheckMode::Local => 1,
|
||||
TypeCheckMode::None => 2,
|
||||
});
|
||||
hasher.write(&ts_config.as_bytes());
|
||||
|
||||
let check_js = ts_config.get_check_js();
|
||||
let mut has_file = false;
|
||||
let mut has_file_to_type_check = false;
|
||||
// this iterator of modules is already deterministic, so no need to sort it
|
||||
for module in graph.modules() {
|
||||
match module {
|
||||
Module::Js(module) => {
|
||||
let ts_check = has_ts_check(module.media_type, &module.source);
|
||||
if ts_check {
|
||||
has_file_to_type_check = true;
|
||||
}
|
||||
|
||||
match module.media_type {
|
||||
MediaType::TypeScript
|
||||
| MediaType::Dts
|
||||
| MediaType::Dmts
|
||||
| MediaType::Dcts
|
||||
| MediaType::Mts
|
||||
| MediaType::Cts
|
||||
| MediaType::Tsx => {
|
||||
has_file = true;
|
||||
has_file_to_type_check = true;
|
||||
}
|
||||
MediaType::JavaScript
|
||||
| MediaType::Mjs
|
||||
| MediaType::Cjs
|
||||
| MediaType::Jsx => {
|
||||
has_file = true;
|
||||
if !check_js && !ts_check {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
MediaType::Json
|
||||
| MediaType::Css
|
||||
| MediaType::SourceMap
|
||||
| MediaType::Wasm
|
||||
| MediaType::Unknown => continue,
|
||||
}
|
||||
|
||||
hasher.write_str(module.specifier.as_str());
|
||||
hasher.write_str(
|
||||
// the fast check module will only be set when publishing
|
||||
module
|
||||
.fast_check_module()
|
||||
.map(|s| s.source.as_ref())
|
||||
.unwrap_or(&module.source),
|
||||
);
|
||||
}
|
||||
Module::Node(_) => {
|
||||
// the @types/node package will be in the resolved
|
||||
// snapshot below so don't bother including it here
|
||||
}
|
||||
Module::Npm(_) => {
|
||||
// don't bother adding this specifier to the hash
|
||||
// because what matters is the resolved npm snapshot,
|
||||
// which is hashed below
|
||||
}
|
||||
Module::Json(module) => {
|
||||
has_file_to_type_check = true;
|
||||
hasher.write_str(module.specifier.as_str());
|
||||
hasher.write_str(&module.source);
|
||||
}
|
||||
Module::Wasm(module) => {
|
||||
has_file_to_type_check = true;
|
||||
hasher.write_str(module.specifier.as_str());
|
||||
hasher.write_str(&module.source_dts);
|
||||
}
|
||||
Module::External(module) => {
|
||||
hasher.write_str(module.specifier.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hasher.write_hashable(package_reqs_hash);
|
||||
|
||||
if !has_file || !check_js && !has_file_to_type_check {
|
||||
// no files to type check
|
||||
CheckHashResult::NoFiles
|
||||
} else {
|
||||
CheckHashResult::Hash(CacheDBHash::new(hasher.finish()))
|
||||
}
|
||||
struct TscRoots {
|
||||
roots: Vec<(ModuleSpecifier, MediaType)>,
|
||||
missing_diagnostics: tsc::Diagnostics,
|
||||
maybe_check_hash: Option<CacheDBHash>,
|
||||
}
|
||||
|
||||
/// Transform the graph into root specifiers that we can feed `tsc`. We have to
|
||||
|
@ -408,52 +323,115 @@ fn get_check_hash(
|
|||
/// the roots, so they get type checked and optionally emitted,
|
||||
/// otherwise they would be ignored if only imported into JavaScript.
|
||||
fn get_tsc_roots(
|
||||
sys: &CliSys,
|
||||
graph: &ModuleGraph,
|
||||
check_js: bool,
|
||||
) -> Vec<(ModuleSpecifier, MediaType)> {
|
||||
npm_cache_state_hash: Option<u64>,
|
||||
type_check_mode: TypeCheckMode,
|
||||
ts_config: &TsConfig,
|
||||
) -> TscRoots {
|
||||
fn maybe_get_check_entry(
|
||||
module: &deno_graph::Module,
|
||||
check_js: bool,
|
||||
hasher: Option<&mut FastInsecureHasher>,
|
||||
) -> Option<(ModuleSpecifier, MediaType)> {
|
||||
match module {
|
||||
Module::Js(module) => match module.media_type {
|
||||
MediaType::TypeScript
|
||||
| MediaType::Tsx
|
||||
| MediaType::Mts
|
||||
| MediaType::Cts
|
||||
| MediaType::Dts
|
||||
| MediaType::Dmts
|
||||
| MediaType::Dcts => {
|
||||
Some((module.specifier.clone(), module.media_type))
|
||||
}
|
||||
MediaType::JavaScript
|
||||
| MediaType::Mjs
|
||||
| MediaType::Cjs
|
||||
| MediaType::Jsx => {
|
||||
if check_js || has_ts_check(module.media_type, &module.source) {
|
||||
Module::Js(module) => {
|
||||
let result = match module.media_type {
|
||||
MediaType::TypeScript
|
||||
| MediaType::Tsx
|
||||
| MediaType::Mts
|
||||
| MediaType::Cts
|
||||
| MediaType::Dts
|
||||
| MediaType::Dmts
|
||||
| MediaType::Dcts => {
|
||||
Some((module.specifier.clone(), module.media_type))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
MediaType::JavaScript
|
||||
| MediaType::Mjs
|
||||
| MediaType::Cjs
|
||||
| MediaType::Jsx => {
|
||||
if check_js || has_ts_check(module.media_type, &module.source) {
|
||||
Some((module.specifier.clone(), module.media_type))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
MediaType::Json
|
||||
| MediaType::Wasm
|
||||
| MediaType::Css
|
||||
| MediaType::SourceMap
|
||||
| MediaType::Unknown => None,
|
||||
};
|
||||
if result.is_some() {
|
||||
if let Some(hasher) = hasher {
|
||||
hasher.write_str(module.specifier.as_str());
|
||||
hasher.write_str(
|
||||
// the fast check module will only be set when publishing
|
||||
module
|
||||
.fast_check_module()
|
||||
.map(|s| s.source.as_ref())
|
||||
.unwrap_or(&module.source),
|
||||
);
|
||||
}
|
||||
}
|
||||
MediaType::Json
|
||||
| MediaType::Wasm
|
||||
| MediaType::Css
|
||||
| MediaType::SourceMap
|
||||
| MediaType::Unknown => None,
|
||||
},
|
||||
Module::Wasm(module) => Some((module.specifier.clone(), MediaType::Dmts)),
|
||||
Module::External(_)
|
||||
| Module::Node(_)
|
||||
| Module::Npm(_)
|
||||
| Module::Json(_) => None,
|
||||
result
|
||||
}
|
||||
Module::Node(_) => {
|
||||
// the @types/node package will be in the resolved
|
||||
// snapshot so don't bother including it in the hash
|
||||
None
|
||||
}
|
||||
Module::Npm(_) => {
|
||||
// don't bother adding this specifier to the hash
|
||||
// because what matters is the resolved npm snapshot,
|
||||
// which is hashed below
|
||||
None
|
||||
}
|
||||
Module::Json(module) => {
|
||||
if let Some(hasher) = hasher {
|
||||
hasher.write_str(module.specifier.as_str());
|
||||
hasher.write_str(&module.source);
|
||||
}
|
||||
None
|
||||
}
|
||||
Module::Wasm(module) => {
|
||||
if let Some(hasher) = hasher {
|
||||
hasher.write_str(module.specifier.as_str());
|
||||
hasher.write_str(&module.source_dts);
|
||||
}
|
||||
Some((module.specifier.clone(), MediaType::Dmts))
|
||||
}
|
||||
Module::External(module) => {
|
||||
if let Some(hasher) = hasher {
|
||||
hasher.write_str(module.specifier.as_str());
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut result = Vec::with_capacity(graph.specifiers_count());
|
||||
let mut result = TscRoots {
|
||||
roots: Vec::with_capacity(graph.specifiers_count()),
|
||||
missing_diagnostics: Default::default(),
|
||||
maybe_check_hash: None,
|
||||
};
|
||||
let mut maybe_hasher = npm_cache_state_hash.map(|npm_cache_state_hash| {
|
||||
let mut hasher = FastInsecureHasher::new_deno_versioned();
|
||||
hasher.write_hashable(npm_cache_state_hash);
|
||||
hasher.write_u8(match type_check_mode {
|
||||
TypeCheckMode::All => 0,
|
||||
TypeCheckMode::Local => 1,
|
||||
TypeCheckMode::None => 2,
|
||||
});
|
||||
hasher.write_hashable(graph.has_node_specifier);
|
||||
hasher.write(&ts_config.as_bytes());
|
||||
hasher
|
||||
});
|
||||
|
||||
if graph.has_node_specifier {
|
||||
// inject a specifier that will resolve node types
|
||||
result.push((
|
||||
result.roots.push((
|
||||
ModuleSpecifier::parse("asset:///node_types.d.ts").unwrap(),
|
||||
MediaType::Dts,
|
||||
));
|
||||
|
@ -464,65 +442,134 @@ fn get_tsc_roots(
|
|||
let mut pending = VecDeque::new();
|
||||
|
||||
// put in the global types first so that they're resolved before anything else
|
||||
for import in graph.imports.values() {
|
||||
for dep in import.dependencies.values() {
|
||||
let specifier = dep.get_type().or_else(|| dep.get_code());
|
||||
if let Some(specifier) = &specifier {
|
||||
let specifier = graph.resolve(specifier);
|
||||
if seen.insert(specifier.clone()) {
|
||||
pending.push_back(specifier);
|
||||
}
|
||||
}
|
||||
let get_import_specifiers = || {
|
||||
graph
|
||||
.imports
|
||||
.values()
|
||||
.flat_map(|i| i.dependencies.values())
|
||||
.filter_map(|dep| dep.get_type().or_else(|| dep.get_code()))
|
||||
};
|
||||
for specifier in get_import_specifiers() {
|
||||
let specifier = graph.resolve(specifier);
|
||||
if seen.insert(specifier) {
|
||||
pending.push_back((specifier, false));
|
||||
}
|
||||
}
|
||||
|
||||
// then the roots
|
||||
for root in &graph.roots {
|
||||
let specifier = graph.resolve(root);
|
||||
if seen.insert(specifier.clone()) {
|
||||
pending.push_back(specifier);
|
||||
if seen.insert(specifier) {
|
||||
pending.push_back((specifier, false));
|
||||
}
|
||||
}
|
||||
|
||||
// now walk the graph that only includes the fast check dependencies
|
||||
while let Some(specifier) = pending.pop_front() {
|
||||
let Some(module) = graph.get(specifier) else {
|
||||
continue;
|
||||
while let Some((specifier, is_dynamic)) = pending.pop_front() {
|
||||
let module = match graph.try_get(specifier) {
|
||||
Ok(Some(module)) => module,
|
||||
Ok(None) => continue,
|
||||
Err(ModuleError::Missing(specifier, maybe_range)) => {
|
||||
if !is_dynamic {
|
||||
result
|
||||
.missing_diagnostics
|
||||
.push(tsc::Diagnostic::from_missing_error(
|
||||
specifier,
|
||||
maybe_range.as_ref(),
|
||||
maybe_additional_sloppy_imports_message(sys, specifier),
|
||||
));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Err(ModuleError::LoadingErr(
|
||||
specifier,
|
||||
maybe_range,
|
||||
ModuleLoadError::Loader(_),
|
||||
)) => {
|
||||
// these will be errors like attempting to load a directory
|
||||
if !is_dynamic {
|
||||
result
|
||||
.missing_diagnostics
|
||||
.push(tsc::Diagnostic::from_missing_error(
|
||||
specifier,
|
||||
maybe_range.as_ref(),
|
||||
maybe_additional_sloppy_imports_message(sys, specifier),
|
||||
));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Err(_) => continue,
|
||||
};
|
||||
if let Some(entry) = maybe_get_check_entry(module, check_js) {
|
||||
result.push(entry);
|
||||
if is_dynamic && !seen.insert(specifier) {
|
||||
continue;
|
||||
}
|
||||
if let Some(module) = module.js() {
|
||||
let deps = module.dependencies_prefer_fast_check();
|
||||
if let Some(entry) =
|
||||
maybe_get_check_entry(module, check_js, maybe_hasher.as_mut())
|
||||
{
|
||||
result.roots.push(entry);
|
||||
}
|
||||
|
||||
let mut maybe_module_dependencies = None;
|
||||
let mut maybe_types_dependency = None;
|
||||
if let Module::Js(module) = module {
|
||||
maybe_module_dependencies = Some(module.dependencies_prefer_fast_check());
|
||||
maybe_types_dependency = module
|
||||
.maybe_types_dependency
|
||||
.as_ref()
|
||||
.and_then(|d| d.dependency.ok());
|
||||
} else if let Module::Wasm(module) = module {
|
||||
maybe_module_dependencies = Some(&module.dependencies);
|
||||
}
|
||||
|
||||
fn handle_specifier<'a>(
|
||||
graph: &'a ModuleGraph,
|
||||
seen: &mut HashSet<&'a ModuleSpecifier>,
|
||||
pending: &mut VecDeque<(&'a ModuleSpecifier, bool)>,
|
||||
specifier: &'a ModuleSpecifier,
|
||||
is_dynamic: bool,
|
||||
) {
|
||||
let specifier = graph.resolve(specifier);
|
||||
if is_dynamic {
|
||||
if !seen.contains(specifier) {
|
||||
pending.push_back((specifier, true));
|
||||
}
|
||||
} else if seen.insert(specifier) {
|
||||
pending.push_back((specifier, false));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(deps) = maybe_module_dependencies {
|
||||
for dep in deps.values() {
|
||||
// walk both the code and type dependencies
|
||||
if let Some(specifier) = dep.get_code() {
|
||||
let specifier = graph.resolve(specifier);
|
||||
if seen.insert(specifier.clone()) {
|
||||
pending.push_back(specifier);
|
||||
}
|
||||
handle_specifier(
|
||||
graph,
|
||||
&mut seen,
|
||||
&mut pending,
|
||||
specifier,
|
||||
dep.is_dynamic,
|
||||
);
|
||||
}
|
||||
if let Some(specifier) = dep.get_type() {
|
||||
let specifier = graph.resolve(specifier);
|
||||
if seen.insert(specifier.clone()) {
|
||||
pending.push_back(specifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(dep) = module
|
||||
.maybe_types_dependency
|
||||
.as_ref()
|
||||
.and_then(|d| d.dependency.ok())
|
||||
{
|
||||
let specifier = graph.resolve(&dep.specifier);
|
||||
if seen.insert(specifier.clone()) {
|
||||
pending.push_back(specifier);
|
||||
handle_specifier(
|
||||
graph,
|
||||
&mut seen,
|
||||
&mut pending,
|
||||
specifier,
|
||||
dep.is_dynamic,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(dep) = maybe_types_dependency {
|
||||
handle_specifier(graph, &mut seen, &mut pending, &dep.specifier, false);
|
||||
}
|
||||
}
|
||||
|
||||
result.maybe_check_hash =
|
||||
maybe_hasher.map(|hasher| CacheDBHash::new(hasher.finish()));
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
|
|
|
@ -409,9 +409,20 @@ delete Object.prototype.__proto__;
|
|||
messageText = formatMessage(msgText, ri.code);
|
||||
}
|
||||
if (start !== undefined && length !== undefined && file) {
|
||||
const startPos = file.getLineAndCharacterOfPosition(start);
|
||||
const sourceLine = file.getFullText().split("\n")[startPos.line];
|
||||
const fileName = file.fileName;
|
||||
let startPos = file.getLineAndCharacterOfPosition(start);
|
||||
let sourceLine = file.getFullText().split("\n")[startPos.line];
|
||||
const originalFileName = file.fileName;
|
||||
const fileName = ops.op_remap_specifier
|
||||
? (ops.op_remap_specifier(file.fileName) ?? file.fileName)
|
||||
: file.fileName;
|
||||
// Bit of a hack to detect when we have a .wasm file and want to hide
|
||||
// the .d.ts text. This is not perfect, but will work in most scenarios
|
||||
if (
|
||||
fileName.endsWith(".wasm") && originalFileName.endsWith(".wasm.d.mts")
|
||||
) {
|
||||
startPos = { line: 0, character: 0 };
|
||||
sourceLine = undefined;
|
||||
}
|
||||
return {
|
||||
start: startPos,
|
||||
end: file.getLineAndCharacterOfPosition(start + length),
|
||||
|
@ -475,6 +486,9 @@ delete Object.prototype.__proto__;
|
|||
2792,
|
||||
// TS2307: Cannot find module '{0}' or its corresponding type declarations.
|
||||
2307,
|
||||
// Relative import errors to add an extension
|
||||
2834,
|
||||
2835,
|
||||
// TS5009: Cannot find the common subdirectory path for the input files.
|
||||
5009,
|
||||
// TS5055: Cannot write file
|
||||
|
@ -1037,24 +1051,27 @@ delete Object.prototype.__proto__;
|
|||
configFileParsingDiagnostics,
|
||||
});
|
||||
|
||||
const checkFiles = localOnly
|
||||
? rootNames
|
||||
.filter((n) => !n.startsWith("http"))
|
||||
.map((checkName) => {
|
||||
const sourceFile = program.getSourceFile(checkName);
|
||||
if (sourceFile == null) {
|
||||
throw new Error("Could not find source file for: " + checkName);
|
||||
}
|
||||
return sourceFile;
|
||||
})
|
||||
: undefined;
|
||||
let checkFiles = undefined;
|
||||
|
||||
if (localOnly) {
|
||||
const checkFileNames = new Set();
|
||||
checkFiles = [];
|
||||
|
||||
for (const checkName of rootNames) {
|
||||
if (checkName.startsWith("http")) {
|
||||
continue;
|
||||
}
|
||||
const sourceFile = program.getSourceFile(checkName);
|
||||
if (sourceFile != null) {
|
||||
checkFiles.push(sourceFile);
|
||||
}
|
||||
checkFileNames.add(checkName);
|
||||
}
|
||||
|
||||
if (checkFiles != null) {
|
||||
// When calling program.getSemanticDiagnostics(...) with a source file, we
|
||||
// need to call this code first in order to get it to invalidate cached
|
||||
// diagnostics correctly. This is what program.getSemanticDiagnostics()
|
||||
// does internally when calling without any arguments.
|
||||
const checkFileNames = new Set(checkFiles.map((f) => f.fileName));
|
||||
while (
|
||||
program.getSemanticDiagnosticsOfNextAffectedFile(
|
||||
undefined,
|
||||
|
|
|
@ -110,6 +110,15 @@ pub struct Position {
|
|||
pub character: u64,
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub fn from_deno_graph(deno_graph_position: deno_graph::Position) -> Self {
|
||||
Self {
|
||||
line: deno_graph_position.line as u64,
|
||||
character: deno_graph_position.character as u64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Diagnostic {
|
||||
|
@ -142,6 +151,38 @@ pub struct Diagnostic {
|
|||
}
|
||||
|
||||
impl Diagnostic {
|
||||
pub fn from_missing_error(
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_range: Option<&deno_graph::Range>,
|
||||
additional_message: Option<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
category: DiagnosticCategory::Error,
|
||||
code: 2307,
|
||||
start: maybe_range.map(|r| Position::from_deno_graph(r.range.start)),
|
||||
end: maybe_range.map(|r| Position::from_deno_graph(r.range.end)),
|
||||
original_source_start: None, // will be applied later
|
||||
message_text: Some(format!(
|
||||
"Cannot find module '{}'.{}{}",
|
||||
specifier,
|
||||
if additional_message.is_none() {
|
||||
""
|
||||
} else {
|
||||
" "
|
||||
},
|
||||
additional_message.unwrap_or_default()
|
||||
)),
|
||||
message_chain: None,
|
||||
source: None,
|
||||
source_line: None,
|
||||
file_name: maybe_range.map(|r| r.specifier.to_string()),
|
||||
related_information: None,
|
||||
reports_deprecated: None,
|
||||
reports_unnecessary: None,
|
||||
other: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// If this diagnostic should be included when it comes from a remote module.
|
||||
pub fn include_when_remote(&self) -> bool {
|
||||
/// TS6133: value is declared but its value is never read (noUnusedParameters and noUnusedLocals)
|
||||
|
@ -299,6 +340,14 @@ impl Diagnostics {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn push(&mut self, diagnostic: Diagnostic) {
|
||||
self.0.push(diagnostic);
|
||||
}
|
||||
|
||||
pub fn extend(&mut self, diagnostic: Diagnostics) {
|
||||
self.0.extend(diagnostic.0);
|
||||
}
|
||||
|
||||
/// Return a set of diagnostics where only the values where the predicate
|
||||
/// returns `true` are included.
|
||||
pub fn filter<P>(self, predicate: P) -> Self
|
||||
|
|
|
@ -129,6 +129,7 @@ fn get_asset_texts_from_new_runtime() -> Result<Vec<AssetText>, AnyError> {
|
|||
op_emit,
|
||||
op_is_node_file,
|
||||
op_load,
|
||||
op_remap_specifier,
|
||||
op_resolve,
|
||||
op_respond,
|
||||
]
|
||||
|
@ -275,30 +276,6 @@ fn hash_url(specifier: &ModuleSpecifier, media_type: MediaType) -> String {
|
|||
)
|
||||
}
|
||||
|
||||
/// If the provided URLs derivable tsc media type doesn't match the media type,
|
||||
/// we will add an extension to the output. This is to avoid issues with
|
||||
/// specifiers that don't have extensions, that tsc refuses to emit because they
|
||||
/// think a `.js` version exists, when it doesn't.
|
||||
fn maybe_remap_specifier(
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: MediaType,
|
||||
) -> Option<String> {
|
||||
let path = if specifier.scheme() == "file" {
|
||||
if let Ok(path) = specifier.to_file_path() {
|
||||
path
|
||||
} else {
|
||||
PathBuf::from(specifier.path())
|
||||
}
|
||||
} else {
|
||||
PathBuf::from(specifier.path())
|
||||
};
|
||||
if path.extension().is_none() {
|
||||
Some(format!("{}{}", specifier, media_type.as_ts_extension()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq)]
|
||||
pub struct EmittedFile {
|
||||
pub data: String,
|
||||
|
@ -316,7 +293,7 @@ pub fn into_specifier_and_media_type(
|
|||
(specifier, media_type)
|
||||
}
|
||||
None => (
|
||||
Url::parse("internal:///missing_dependency.d.ts").unwrap(),
|
||||
Url::parse(MISSING_DEPENDENCY_SPECIFIER).unwrap(),
|
||||
MediaType::Dts,
|
||||
),
|
||||
}
|
||||
|
@ -422,6 +399,8 @@ struct State {
|
|||
maybe_tsbuildinfo: Option<String>,
|
||||
maybe_response: Option<RespondArgs>,
|
||||
maybe_npm: Option<RequestNpmState>,
|
||||
// todo(dsherret): it looks like the remapped_specifiers and
|
||||
// root_map could be combined... what is the point of the separation?
|
||||
remapped_specifiers: HashMap<String, ModuleSpecifier>,
|
||||
root_map: HashMap<String, ModuleSpecifier>,
|
||||
current_dir: PathBuf,
|
||||
|
@ -463,6 +442,16 @@ impl State {
|
|||
current_dir,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maybe_remapped_specifier(
|
||||
&self,
|
||||
specifier: &str,
|
||||
) -> Option<&ModuleSpecifier> {
|
||||
self
|
||||
.remapped_specifiers
|
||||
.get(specifier)
|
||||
.or_else(|| self.root_map.get(specifier))
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_specifier(
|
||||
|
@ -607,10 +596,7 @@ fn op_load_inner(
|
|||
maybe_source.map(Cow::Borrowed)
|
||||
} else {
|
||||
let specifier = if let Some(remapped_specifier) =
|
||||
state.remapped_specifiers.get(load_specifier)
|
||||
{
|
||||
remapped_specifier
|
||||
} else if let Some(remapped_specifier) = state.root_map.get(load_specifier)
|
||||
state.maybe_remapped_specifier(load_specifier)
|
||||
{
|
||||
remapped_specifier
|
||||
} else {
|
||||
|
@ -713,6 +699,18 @@ pub struct ResolveArgs {
|
|||
pub specifiers: Vec<(bool, String)>,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[string]
|
||||
fn op_remap_specifier(
|
||||
state: &mut OpState,
|
||||
#[string] specifier: &str,
|
||||
) -> Option<String> {
|
||||
let state = state.borrow::<State>();
|
||||
state
|
||||
.maybe_remapped_specifier(specifier)
|
||||
.map(|url| url.to_string())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
fn op_resolve(
|
||||
|
@ -732,11 +730,9 @@ fn op_resolve_inner(
|
|||
let mut resolved: Vec<(String, &'static str)> =
|
||||
Vec::with_capacity(args.specifiers.len());
|
||||
let referrer = if let Some(remapped_specifier) =
|
||||
state.remapped_specifiers.get(&args.base)
|
||||
state.maybe_remapped_specifier(&args.base)
|
||||
{
|
||||
remapped_specifier.clone()
|
||||
} else if let Some(remapped_base) = state.root_map.get(&args.base) {
|
||||
remapped_base.clone()
|
||||
} else {
|
||||
normalize_specifier(&args.base, &state.current_dir).context(
|
||||
"Error converting a string module specifier for \"op_resolve\".",
|
||||
|
@ -759,8 +755,12 @@ fn op_resolve_inner(
|
|||
}
|
||||
|
||||
let resolved_dep = referrer_module
|
||||
.and_then(|m| m.js())
|
||||
.and_then(|m| m.dependencies_prefer_fast_check().get(&specifier))
|
||||
.and_then(|m| match m {
|
||||
Module::Js(m) => m.dependencies_prefer_fast_check().get(&specifier),
|
||||
Module::Json(_) => None,
|
||||
Module::Wasm(m) => m.dependencies.get(&specifier),
|
||||
Module::Npm(_) | Module::Node(_) | Module::External(_) => None,
|
||||
})
|
||||
.and_then(|d| d.maybe_type.ok().or_else(|| d.maybe_code.ok()));
|
||||
let resolution_mode = if is_cjs {
|
||||
ResolutionMode::Require
|
||||
|
@ -816,7 +816,7 @@ fn op_resolve_inner(
|
|||
}
|
||||
_ => {
|
||||
if let Some(specifier_str) =
|
||||
maybe_remap_specifier(&specifier, media_type)
|
||||
mapped_specifier_for_tsc(&specifier, media_type)
|
||||
{
|
||||
state
|
||||
.remapped_specifiers
|
||||
|
@ -840,7 +840,7 @@ fn op_resolve_inner(
|
|||
MediaType::Dts.as_ts_extension(),
|
||||
),
|
||||
};
|
||||
log::debug!("Resolved {} to {:?}", specifier, result);
|
||||
log::debug!("Resolved {} from {} to {:?}", specifier, referrer, result);
|
||||
resolved.push(result);
|
||||
}
|
||||
|
||||
|
@ -1072,6 +1072,7 @@ pub fn exec(request: Request) -> Result<Response, AnyError> {
|
|||
op_emit,
|
||||
op_is_node_file,
|
||||
op_load,
|
||||
op_remap_specifier,
|
||||
op_resolve,
|
||||
op_respond,
|
||||
],
|
||||
|
|
|
@ -66,12 +66,11 @@ fn fast_check_cache() {
|
|||
// ensure cache works
|
||||
let output = check_debug_cmd.run();
|
||||
assert_contains!(output.combined_output(), "Already type checked.");
|
||||
let building_fast_check_msg = "Building fast check graph";
|
||||
assert_not_contains!(output.combined_output(), building_fast_check_msg);
|
||||
|
||||
// now validated
|
||||
type_check_cache_path.remove_file();
|
||||
let output = check_debug_cmd.run();
|
||||
let building_fast_check_msg = "Building fast check graph";
|
||||
assert_contains!(output.combined_output(), building_fast_check_msg);
|
||||
assert_contains!(
|
||||
output.combined_output(),
|
||||
|
|
|
@ -10,6 +10,10 @@
|
|||
"args": "check not_exists.ts",
|
||||
"output": "not_exists.out",
|
||||
"exitCode": 1
|
||||
}, {
|
||||
"args": "run --check not_exists.ts",
|
||||
"output": "not_exists.out",
|
||||
"exitCode": 1
|
||||
}, {
|
||||
"args": "check exists_and_try_uses.ts",
|
||||
"output": "exists_and_try_uses.out",
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
error: Module not found "file:///[WILDLINE]/not_exists.css".
|
||||
Check [WILDLINE]exists.ts
|
||||
error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/not_exists.css'.
|
||||
at file:///[WILDLINE]/not_exists.ts:1:8
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
error: Module not found "file:///[WILDLINE]/test".
|
||||
Check file:///[WILDLINE]/dts_importing_non_existent/index.js
|
||||
error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/test'.
|
||||
at file:///[WILDLINE]/index.d.ts:1:22
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"tests": {
|
||||
"not_all": {
|
||||
"args": "check --allow-import import_remote.ts",
|
||||
"output": "[WILDCARD]",
|
||||
"exitCode": 0
|
||||
},
|
||||
"all": {
|
||||
"args": "check --all --allow-import import_remote.ts",
|
||||
"output": "check_all.out",
|
||||
"exitCode": 1
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
Download http://localhost:4545/check/import_non_existent.ts
|
||||
Download http://localhost:4545/check/non-existent-module.ts
|
||||
Check file:///[WILDLINE]/import_remote.ts
|
||||
error: TS2307 [ERROR]: Cannot find module 'http://localhost:4545/check/non-existent-module.ts'.
|
||||
at http://localhost:4545/check/import_non_existent.ts:1:22
|
|
@ -0,0 +1,3 @@
|
|||
import { Other } from "http://localhost:4545/check/import_non_existent.ts";
|
||||
|
||||
console.log(Other);
|
24
tests/specs/check/module_not_found/__test__.jsonc
Normal file
24
tests/specs/check/module_not_found/__test__.jsonc
Normal file
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"tests": {
|
||||
"check": {
|
||||
"args": "check --allow-import main.ts",
|
||||
"output": "main.out",
|
||||
"exitCode": 1
|
||||
},
|
||||
"run": {
|
||||
"args": "run --check --allow-import main.ts",
|
||||
"output": "main.out",
|
||||
"exitCode": 1
|
||||
},
|
||||
"missing_local_root": {
|
||||
"args": "check --allow-import non_existent.ts",
|
||||
"output": "missing_local_root.out",
|
||||
"exitCode": 1
|
||||
},
|
||||
"missing_remote_root": {
|
||||
"args": "check --allow-import http://localhost:4545/missing_non_existent.ts",
|
||||
"output": "missing_remote_root.out",
|
||||
"exitCode": 1
|
||||
}
|
||||
}
|
||||
}
|
9
tests/specs/check/module_not_found/main.out
Normal file
9
tests/specs/check/module_not_found/main.out
Normal file
|
@ -0,0 +1,9 @@
|
|||
Download http://localhost:4545/remote.ts
|
||||
Check file:///[WILDLINE]/module_not_found/main.ts
|
||||
error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/other.js'.
|
||||
at file:///[WILDLINE]/main.ts:1:22
|
||||
|
||||
TS2307 [ERROR]: Cannot find module 'http://localhost:4545/remote.ts'.
|
||||
at file:///[WILDLINE]/main.ts:2:24
|
||||
|
||||
Found 2 errors.
|
5
tests/specs/check/module_not_found/main.ts
Normal file
5
tests/specs/check/module_not_found/main.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { Test } from "./other.js";
|
||||
import { Remote } from "http://localhost:4545/remote.ts";
|
||||
|
||||
console.log(new Test());
|
||||
console.log(new Remote());
|
|
@ -0,0 +1,2 @@
|
|||
Check file:///[WILDLINE]/non_existent.ts
|
||||
error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/non_existent.ts'.
|
|
@ -0,0 +1,3 @@
|
|||
Download http://localhost:4545/missing_non_existent.ts
|
||||
Check http://localhost:4545/missing_non_existent.ts
|
||||
error: TS2307 [ERROR]: Cannot find module 'http://localhost:4545/missing_non_existent.ts'.
|
|
@ -1,3 +1,4 @@
|
|||
[# It should be resolving relative the config in sub_dir instead of the cwd]
|
||||
error: Module not found "file:///[WILDLINE]/sub_dir/a.d.ts".
|
||||
Check file:///[WILDLINE]/main.ts
|
||||
error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/sub_dir/a.d.ts'.
|
||||
at file:///[WILDLINE]/sub_dir/deno.json:1:1
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
error: [WILDCARD] Maybe specify path to 'index.ts' file in directory instead or run with --unstable-sloppy-imports
|
||||
at file:///[WILDCARD]/mod.ts:1:20
|
||||
Check file:///[WILDLINE]/mod.ts
|
||||
error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/b'. Maybe specify path to 'index.ts' file in directory instead or run with --unstable-sloppy-imports
|
||||
at file:///[WILDLINE]/mod.ts:1:20
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
error: Module not found "file:///[WILDCARD]/nonexistent/jsx-runtime".
|
||||
at file:///[WILDCARD]/jsx_import_source_no_pragma.tsx:1:1
|
||||
Check file:///[WILDLINE]/jsx_import_source_no_pragma.tsx
|
||||
error: TS2307 [ERROR]: Cannot find module 'file:///[WILDCARD]/nonexistent/jsx-runtime'.
|
||||
at file:///[WILDLINE]/jsx_import_source_no_pragma.tsx:1:1
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
error: Module not found "file:///[WILDCARD]/nonexistent.d.ts".
|
||||
at file:///[WILDCARD]/reference_types_error.js:1:22
|
||||
Check file:///[WILDLINE]/reference_types_error.js
|
||||
error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/nonexistent.d.ts'.
|
||||
at file:///[WILDLINE]/reference_types_error.js:1:22
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
error: Module not found "file:///[WILDCARD]/nonexistent.d.ts".
|
||||
at file:///[WILDCARD]/reference_types_error.js:1:22
|
||||
Check file:///[WILDLINE]/reference_types_error.js
|
||||
error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/nonexistent.d.ts'.
|
||||
at file:///[WILDLINE]/reference_types_error.js:1:22
|
||||
|
|
|
@ -1,2 +1,26 @@
|
|||
error: Module not found "file:///[WILDCARD]/a.js". Maybe change the extension to '.ts' or run with --unstable-sloppy-imports
|
||||
Check file:///[WILDLINE]/main.ts
|
||||
error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/a.js'. Maybe change the extension to '.ts' or run with --unstable-sloppy-imports
|
||||
at file:///[WILDLINE]/main.ts:1:20
|
||||
|
||||
TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/b'. Maybe add a '.js' extension or run with --unstable-sloppy-imports
|
||||
at file:///[WILDLINE]/main.ts:2:20
|
||||
|
||||
TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/c'. Maybe add a '.mts' extension or run with --unstable-sloppy-imports
|
||||
at file:///[WILDLINE]/main.ts:3:20
|
||||
|
||||
TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/d'. Maybe add a '.mjs' extension or run with --unstable-sloppy-imports
|
||||
at file:///[WILDLINE]/main.ts:4:20
|
||||
|
||||
TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/e'. Maybe add a '.tsx' extension or run with --unstable-sloppy-imports
|
||||
at file:///[WILDLINE]/main.ts:5:20
|
||||
|
||||
TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/e.js'. Maybe change the extension to '.tsx' or run with --unstable-sloppy-imports
|
||||
at file:///[WILDLINE]/main.ts:6:21
|
||||
|
||||
TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/f'. Maybe add a '.jsx' extension or run with --unstable-sloppy-imports
|
||||
at file:///[WILDLINE]/main.ts:7:20
|
||||
|
||||
TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/dir'. Maybe specify path to 'index.tsx' file in directory instead or run with --unstable-sloppy-imports
|
||||
at file:///[WILDLINE]/main.ts:8:20
|
||||
|
||||
Found 8 errors.
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
{
|
||||
"args": "--allow-import main.js",
|
||||
"output": "main.out",
|
||||
"exitCode": 1
|
||||
"tests": {
|
||||
"run": {
|
||||
"args": "--allow-import main.js",
|
||||
"output": "main.out",
|
||||
"exitCode": 1
|
||||
},
|
||||
"check": {
|
||||
"args": "check --all --allow-import main.js",
|
||||
"output": "check.out",
|
||||
"exitCode": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Download http://localhost:4545/wasm/math_with_import.wasm
|
||||
Check file:///[WILDLINE]/main.js
|
||||
error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/local_math.ts'.
|
||||
at http://localhost:4545/wasm/math_with_import.wasm:1:87
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-check
|
||||
import {
|
||||
add,
|
||||
subtract,
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Download http://localhost:4545/wasm/math_with_import.wasm
|
||||
error: Module not found "file:///[WILDLINE]/local_math.ts".
|
||||
at http://localhost:4545/wasm/math_with_import.wasm:1:8
|
||||
at http://localhost:4545/wasm/math_with_import.wasm:1:87
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
{
|
||||
"args": "--allow-import main.js",
|
||||
"output": "main.out",
|
||||
"exitCode": 1
|
||||
"tests": {
|
||||
"run": {
|
||||
"args": "--allow-import main.js",
|
||||
"output": "main.out",
|
||||
"exitCode": 1
|
||||
},
|
||||
"check": {
|
||||
"args": "check --all --allow-import main.js",
|
||||
"output": "check.out",
|
||||
"exitCode": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
Download http://localhost:4545/wasm/math_with_import.wasm
|
||||
Check file:///[WILDLINE]/main.js
|
||||
error: TS2305 [ERROR]: Module '"file:///[WILDLINE]/local_math.ts"' has no exported member '"add"'.
|
||||
at http://localhost:4545/wasm/math_with_import.wasm:1:1
|
||||
|
||||
TS2305 [ERROR]: Module '"file:///[WILDLINE]/local_math.ts"' has no exported member '"subtract"'.
|
||||
at http://localhost:4545/wasm/math_with_import.wasm:1:1
|
||||
|
||||
Found 2 errors.
|
|
@ -1,3 +1,4 @@
|
|||
// @ts-check
|
||||
import {
|
||||
add,
|
||||
subtract,
|
||||
|
|
5
tests/testdata/check/import_non_existent.ts
vendored
Normal file
5
tests/testdata/check/import_non_existent.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { Test } from "./non-existent-module.ts";
|
||||
|
||||
console.log(Test);
|
||||
|
||||
export class Other {}
|
Loading…
Reference in a new issue