1
0
Fork 0
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:
David Sherret 2025-01-03 16:49:56 -05:00 committed by GitHub
parent 18b813b93f
commit 89c92b84fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 684 additions and 358 deletions

8
Cargo.lock generated
View file

@ -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",

View file

@ -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" }

View file

@ -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

View file

@ -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",
},
},
}

View file

@ -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

View file

@ -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(

View file

@ -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,

View file

@ -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
}

View file

@ -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,

View file

@ -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

View file

@ -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,
],

View file

@ -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(),

View file

@ -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",

View file

@ -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

View file

@ -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

View file

@ -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
}
}
}

View file

@ -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

View file

@ -0,0 +1,3 @@
import { Other } from "http://localhost:4545/check/import_non_existent.ts";
console.log(Other);

View 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
}
}
}

View 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.

View 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());

View file

@ -0,0 +1,2 @@
Check file:///[WILDLINE]/non_existent.ts
error: TS2307 [ERROR]: Cannot find module 'file:///[WILDLINE]/non_existent.ts'.

View file

@ -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'.

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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
}
}
}

View file

@ -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

View file

@ -1,3 +1,4 @@
// @ts-check
import {
add,
subtract,

View file

@ -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

View file

@ -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
}
}
}

View file

@ -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.

View file

@ -1,3 +1,4 @@
// @ts-check
import {
add,
subtract,

View file

@ -0,0 +1,5 @@
import { Test } from "./non-existent-module.ts";
console.log(Test);
export class Other {}