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

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