diff --git a/Cargo.lock b/Cargo.lock index eaba6115da..b054e9fe7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index 722fe10c60..fa2813caed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 3ca1afc872..a2245b0806 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -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 diff --git a/cli/errors.rs b/cli/errors.rs index ead1ccf127..6500efec50 100644 --- a/cli/errors.rs +++ b/cli/errors.rs @@ -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", }, }, } diff --git a/cli/factory.rs b/cli/factory.rs index c507d8388d..4ae1d94ea8 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -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 diff --git a/cli/graph_util.rs b/cli/graph_util.rs index fb933bdac8..ac9e75cff0 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -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 { + 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 { match err { ModuleError::LoadingErr( diff --git a/cli/module_loader.rs b/cli/module_loader.rs index 8256c56781..446397cad1 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -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 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, diff --git a/cli/tools/check.rs b/cli/tools/check.rs index f3df54626a..1ee3f1782b 100644 --- a/cli/tools/check.rs +++ b/cli/tools/check.rs @@ -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, node_resolver: Arc, npm_resolver: Arc, + sys: CliSys, } impl TypeChecker { @@ -115,6 +120,7 @@ impl TypeChecker { module_graph_builder: Arc, node_resolver: Arc, npm_resolver: Arc, + 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, } /// 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, + 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 } diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index d532de265f..25813c3f9d 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -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, diff --git a/cli/tsc/diagnostics.rs b/cli/tsc/diagnostics.rs index cda41525c2..ac93c8575d 100644 --- a/cli/tsc/diagnostics.rs +++ b/cli/tsc/diagnostics.rs @@ -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, + ) -> 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

(self, predicate: P) -> Self diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index 9f1dc3653a..3176c50d5c 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -129,6 +129,7 @@ fn get_asset_texts_from_new_runtime() -> Result, 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 { - 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, maybe_response: Option, maybe_npm: Option, + // todo(dsherret): it looks like the remapped_specifiers and + // root_map could be combined... what is the point of the separation? remapped_specifiers: HashMap, root_map: HashMap, 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 { + let state = state.borrow::(); + 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 { op_emit, op_is_node_file, op_load, + op_remap_specifier, op_resolve, op_respond, ], diff --git a/tests/integration/jsr_tests.rs b/tests/integration/jsr_tests.rs index 95a9fcb437..0902b7d0a2 100644 --- a/tests/integration/jsr_tests.rs +++ b/tests/integration/jsr_tests.rs @@ -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(), diff --git a/tests/specs/check/css_import/__test__.jsonc b/tests/specs/check/css_import/__test__.jsonc index 629dcd3833..4e16560ec2 100644 --- a/tests/specs/check/css_import/__test__.jsonc +++ b/tests/specs/check/css_import/__test__.jsonc @@ -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", diff --git a/tests/specs/check/css_import/not_exists.out b/tests/specs/check/css_import/not_exists.out index 95fd14668e..1e9dce6b70 100644 --- a/tests/specs/check/css_import/not_exists.out +++ b/tests/specs/check/css_import/not_exists.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 diff --git a/tests/specs/check/dts_importing_non_existent/check.out b/tests/specs/check/dts_importing_non_existent/check.out index 80ec9593b0..65e27bce83 100644 --- a/tests/specs/check/dts_importing_non_existent/check.out +++ b/tests/specs/check/dts_importing_non_existent/check.out @@ -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 diff --git a/tests/specs/check/import_non_existent_in_remote/__test__.jsonc b/tests/specs/check/import_non_existent_in_remote/__test__.jsonc new file mode 100644 index 0000000000..39cd37ffc0 --- /dev/null +++ b/tests/specs/check/import_non_existent_in_remote/__test__.jsonc @@ -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 + } + } +} diff --git a/tests/specs/check/import_non_existent_in_remote/check_all.out b/tests/specs/check/import_non_existent_in_remote/check_all.out new file mode 100644 index 0000000000..a3c3b1759c --- /dev/null +++ b/tests/specs/check/import_non_existent_in_remote/check_all.out @@ -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 diff --git a/tests/specs/check/import_non_existent_in_remote/import_remote.ts b/tests/specs/check/import_non_existent_in_remote/import_remote.ts new file mode 100644 index 0000000000..47c5c654b8 --- /dev/null +++ b/tests/specs/check/import_non_existent_in_remote/import_remote.ts @@ -0,0 +1,3 @@ +import { Other } from "http://localhost:4545/check/import_non_existent.ts"; + +console.log(Other); diff --git a/tests/specs/check/module_not_found/__test__.jsonc b/tests/specs/check/module_not_found/__test__.jsonc new file mode 100644 index 0000000000..5e7cfa2e59 --- /dev/null +++ b/tests/specs/check/module_not_found/__test__.jsonc @@ -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 + } + } +} diff --git a/tests/specs/check/module_not_found/main.out b/tests/specs/check/module_not_found/main.out new file mode 100644 index 0000000000..6c16183560 --- /dev/null +++ b/tests/specs/check/module_not_found/main.out @@ -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. diff --git a/tests/specs/check/module_not_found/main.ts b/tests/specs/check/module_not_found/main.ts new file mode 100644 index 0000000000..cec9512569 --- /dev/null +++ b/tests/specs/check/module_not_found/main.ts @@ -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()); diff --git a/tests/specs/check/module_not_found/missing_local_root.out b/tests/specs/check/module_not_found/missing_local_root.out new file mode 100644 index 0000000000..34b150c9a3 --- /dev/null +++ b/tests/specs/check/module_not_found/missing_local_root.out @@ -0,0 +1,2 @@ +Check file:///[WILDLINE]/non_existent.ts +error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/non_existent.ts'. diff --git a/tests/specs/check/module_not_found/missing_remote_root.out b/tests/specs/check/module_not_found/missing_remote_root.out new file mode 100644 index 0000000000..e408938e41 --- /dev/null +++ b/tests/specs/check/module_not_found/missing_remote_root.out @@ -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'. diff --git a/tests/specs/check/types_resolved_relative_config/main.out b/tests/specs/check/types_resolved_relative_config/main.out index 212e1224ca..5763d3298c 100644 --- a/tests/specs/check/types_resolved_relative_config/main.out +++ b/tests/specs/check/types_resolved_relative_config/main.out @@ -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 diff --git a/tests/specs/publish/sloppy_imports/sloppy_imports_not_enabled.out b/tests/specs/publish/sloppy_imports/sloppy_imports_not_enabled.out index 4eacbea655..8388e4751e 100644 --- a/tests/specs/publish/sloppy_imports/sloppy_imports_not_enabled.out +++ b/tests/specs/publish/sloppy_imports/sloppy_imports_not_enabled.out @@ -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 diff --git a/tests/specs/run/jsx_import_source/jsx_import_source_error.out b/tests/specs/run/jsx_import_source/jsx_import_source_error.out index 634a5b09ba..cb673c6bc9 100644 --- a/tests/specs/run/jsx_import_source/jsx_import_source_error.out +++ b/tests/specs/run/jsx_import_source/jsx_import_source_error.out @@ -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 diff --git a/tests/specs/run/reference_types_error/reference_types_error.js.out b/tests/specs/run/reference_types_error/reference_types_error.js.out index 86055f3ac3..3f22354915 100644 --- a/tests/specs/run/reference_types_error/reference_types_error.js.out +++ b/tests/specs/run/reference_types_error/reference_types_error.js.out @@ -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 diff --git a/tests/specs/run/reference_types_error_vendor_dir/reference_types_error.js.out b/tests/specs/run/reference_types_error_vendor_dir/reference_types_error.js.out index 86055f3ac3..3f22354915 100644 --- a/tests/specs/run/reference_types_error_vendor_dir/reference_types_error.js.out +++ b/tests/specs/run/reference_types_error_vendor_dir/reference_types_error.js.out @@ -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 diff --git a/tests/specs/run/sloppy_imports/no_sloppy.out b/tests/specs/run/sloppy_imports/no_sloppy.out index d3a205e990..f28d9181ff 100644 --- a/tests/specs/run/sloppy_imports/no_sloppy.out +++ b/tests/specs/run/sloppy_imports/no_sloppy.out @@ -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. diff --git a/tests/specs/run/wasm_module/import_file_not_found/__test__.jsonc b/tests/specs/run/wasm_module/import_file_not_found/__test__.jsonc index a27fcfa82b..0141f9828c 100644 --- a/tests/specs/run/wasm_module/import_file_not_found/__test__.jsonc +++ b/tests/specs/run/wasm_module/import_file_not_found/__test__.jsonc @@ -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 + } + } } diff --git a/tests/specs/run/wasm_module/import_file_not_found/check.out b/tests/specs/run/wasm_module/import_file_not_found/check.out new file mode 100644 index 0000000000..59c052297c --- /dev/null +++ b/tests/specs/run/wasm_module/import_file_not_found/check.out @@ -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 diff --git a/tests/specs/run/wasm_module/import_file_not_found/main.js b/tests/specs/run/wasm_module/import_file_not_found/main.js index 9ad66df35b..b55405bd31 100644 --- a/tests/specs/run/wasm_module/import_file_not_found/main.js +++ b/tests/specs/run/wasm_module/import_file_not_found/main.js @@ -1,3 +1,4 @@ +// @ts-check import { add, subtract, diff --git a/tests/specs/run/wasm_module/import_file_not_found/main.out b/tests/specs/run/wasm_module/import_file_not_found/main.out index 54343673f1..ed021b9a21 100644 --- a/tests/specs/run/wasm_module/import_file_not_found/main.out +++ b/tests/specs/run/wasm_module/import_file_not_found/main.out @@ -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 diff --git a/tests/specs/run/wasm_module/import_named_export_not_found/__test__.jsonc b/tests/specs/run/wasm_module/import_named_export_not_found/__test__.jsonc index a27fcfa82b..0141f9828c 100644 --- a/tests/specs/run/wasm_module/import_named_export_not_found/__test__.jsonc +++ b/tests/specs/run/wasm_module/import_named_export_not_found/__test__.jsonc @@ -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 + } + } } diff --git a/tests/specs/run/wasm_module/import_named_export_not_found/check.out b/tests/specs/run/wasm_module/import_named_export_not_found/check.out new file mode 100644 index 0000000000..d7cc2ea0fb --- /dev/null +++ b/tests/specs/run/wasm_module/import_named_export_not_found/check.out @@ -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. diff --git a/tests/specs/run/wasm_module/import_named_export_not_found/main.js b/tests/specs/run/wasm_module/import_named_export_not_found/main.js index 9ad66df35b..b55405bd31 100644 --- a/tests/specs/run/wasm_module/import_named_export_not_found/main.js +++ b/tests/specs/run/wasm_module/import_named_export_not_found/main.js @@ -1,3 +1,4 @@ +// @ts-check import { add, subtract, diff --git a/tests/testdata/check/import_non_existent.ts b/tests/testdata/check/import_non_existent.ts new file mode 100644 index 0000000000..ae511bca8a --- /dev/null +++ b/tests/testdata/check/import_non_existent.ts @@ -0,0 +1,5 @@ +import { Test } from "./non-existent-module.ts"; + +console.log(Test); + +export class Other {}