mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 23:34:47 -05:00
fix(cli): early abort before type checking on missing modules (#9285)
Fixes #9275
This commit is contained in:
parent
2638aa03a5
commit
894ff6bb58
5 changed files with 70 additions and 5 deletions
|
@ -791,6 +791,7 @@ impl Graph {
|
||||||
|
|
||||||
/// Type check the module graph, corresponding to the options provided.
|
/// Type check the module graph, corresponding to the options provided.
|
||||||
pub fn check(self, options: CheckOptions) -> Result<ResultInfo, AnyError> {
|
pub fn check(self, options: CheckOptions) -> Result<ResultInfo, AnyError> {
|
||||||
|
self.validate()?;
|
||||||
let mut config = TsConfig::new(json!({
|
let mut config = TsConfig::new(json!({
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
// TODO(@kitsonk) is this really needed?
|
// TODO(@kitsonk) is this really needed?
|
||||||
|
@ -1692,6 +1693,53 @@ impl Graph {
|
||||||
stats,
|
stats,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validate that the module graph is "valid" in that there are not module
|
||||||
|
/// slots that have errorred that should be available to be able to statically
|
||||||
|
/// analyze. In certain situations, we can spin up tsc with an "invalid"
|
||||||
|
/// graph.
|
||||||
|
fn validate(&self) -> Result<(), AnyError> {
|
||||||
|
fn validate_module<F>(
|
||||||
|
specifier: &ModuleSpecifier,
|
||||||
|
seen: &mut HashSet<ModuleSpecifier>,
|
||||||
|
get_module: &F,
|
||||||
|
) -> Result<(), AnyError>
|
||||||
|
where
|
||||||
|
F: Fn(&ModuleSpecifier) -> ModuleSlot,
|
||||||
|
{
|
||||||
|
if seen.contains(specifier) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
seen.insert(specifier.clone());
|
||||||
|
match get_module(specifier) {
|
||||||
|
ModuleSlot::Err(err) => Err(anyhow!(err.to_string())),
|
||||||
|
ModuleSlot::Module(module) => {
|
||||||
|
for (_, dep) in module.dependencies.iter() {
|
||||||
|
// a dynamic import should be skipped, because while it might not
|
||||||
|
// be available to statically analyze, it might be available at
|
||||||
|
// runtime.
|
||||||
|
if !dep.is_dynamic {
|
||||||
|
if let Some(code_specifier) = &dep.maybe_code {
|
||||||
|
validate_module(code_specifier, seen, get_module)?;
|
||||||
|
}
|
||||||
|
if let Some(type_specifier) = &dep.maybe_type {
|
||||||
|
validate_module(type_specifier, seen, get_module)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
ModuleSlot::None => Err(custom_error("NotFound", format!("The specifier \"{}\" is unexpectedly not in the module graph.", specifier))),
|
||||||
|
ModuleSlot::Pending => Err(custom_error("InvalidState", format!("The specifier \"{}\" is in an unexpected state in the module graph.", specifier))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut seen = HashSet::new();
|
||||||
|
for specifier in &self.roots {
|
||||||
|
validate_module(specifier, &mut seen, &|s| self.get_module(s).clone())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl swc_bundler::Resolve for Graph {
|
impl swc_bundler::Resolve for Graph {
|
||||||
|
|
3
cli/tests/error_missing_module_named_import.ts
Normal file
3
cli/tests/error_missing_module_named_import.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import { a } from "./does_not_exist.js";
|
||||||
|
|
||||||
|
console.log(a);
|
2
cli/tests/error_missing_module_named_import.ts.out
Normal file
2
cli/tests/error_missing_module_named_import.ts.out
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
error: Cannot resolve module "[WILDCARD]cli/tests/does_not_exist.js" from "[WILDCARD]cli/tests/error_missing_module_named_import.ts".
|
||||||
|
at [WILDCARD]cli/tests/error_missing_module_named_import.ts:1:0
|
|
@ -3004,6 +3004,12 @@ itest!(error_025_tab_indent {
|
||||||
exit_code: 1,
|
exit_code: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itest!(error_missing_module_named_import {
|
||||||
|
args: "run --reload error_missing_module_named_import.ts",
|
||||||
|
output: "error_missing_module_named_import.ts.out",
|
||||||
|
exit_code: 1,
|
||||||
|
});
|
||||||
|
|
||||||
itest!(error_no_check {
|
itest!(error_no_check {
|
||||||
args: "run --reload --no-check error_no_check.ts",
|
args: "run --reload --no-check error_no_check.ts",
|
||||||
output: "error_no_check.ts.out",
|
output: "error_no_check.ts.out",
|
||||||
|
|
16
cli/tsc.rs
16
cli/tsc.rs
|
@ -287,10 +287,10 @@ fn load(state: &mut State, args: Value) -> Result<Value, AnyError> {
|
||||||
state.maybe_tsbuildinfo.clone()
|
state.maybe_tsbuildinfo.clone()
|
||||||
// in certain situations we return a "blank" module to tsc and we need to
|
// in certain situations we return a "blank" module to tsc and we need to
|
||||||
// handle the request for that module here.
|
// handle the request for that module here.
|
||||||
} else if &v.specifier == "deno:///none.d.ts" {
|
} else if &v.specifier == "deno:///missing_dependency.d.ts" {
|
||||||
hash = Some("1".to_string());
|
hash = Some("1".to_string());
|
||||||
media_type = MediaType::TypeScript;
|
media_type = MediaType::Dts;
|
||||||
Some("declare var a: any;\nexport = a;\n".to_string())
|
Some("declare const __: any;\nexport = __;\n".to_string())
|
||||||
} else if v.specifier.starts_with("asset:///") {
|
} else if v.specifier.starts_with("asset:///") {
|
||||||
let name = v.specifier.replace("asset:///", "");
|
let name = v.specifier.replace("asset:///", "");
|
||||||
let maybe_source = get_asset(&name).map(|s| s.to_string());
|
let maybe_source = get_asset(&name).map(|s| s.to_string());
|
||||||
|
@ -383,7 +383,10 @@ fn resolve(state: &mut State, args: Value) -> Result<Value, AnyError> {
|
||||||
// the source file in the graph, so we will return a fake module to
|
// the source file in the graph, so we will return a fake module to
|
||||||
// make tsc happy.
|
// make tsc happy.
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
resolved.push(("deno:///none.d.ts".to_string(), ".d.ts".to_string()));
|
resolved.push((
|
||||||
|
"deno:///missing_dependency.d.ts".to_string(),
|
||||||
|
".d.ts".to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -816,7 +819,10 @@ mod tests {
|
||||||
&mut state,
|
&mut state,
|
||||||
json!({ "base": "https://deno.land/x/a.ts", "specifiers": [ "./bad.ts" ]}),
|
json!({ "base": "https://deno.land/x/a.ts", "specifiers": [ "./bad.ts" ]}),
|
||||||
).expect("should have not errored");
|
).expect("should have not errored");
|
||||||
assert_eq!(actual, json!([["deno:///none.d.ts", ".d.ts"]]));
|
assert_eq!(
|
||||||
|
actual,
|
||||||
|
json!([["deno:///missing_dependency.d.ts", ".d.ts"]])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|
Loading…
Reference in a new issue