-
-
Notifications
You must be signed in to change notification settings - Fork 166
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: async module detection wrong (#1890)
- Loading branch information
Showing
2 changed files
with
80 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@farmfe/core": patch | ||
--- | ||
|
||
Fix async module detection wrong with cyclic dependencies |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,89 @@ | ||
use std::{collections::HashSet, sync::Arc}; | ||
use std::{ | ||
collections::{HashSet, VecDeque}, | ||
sync::Arc, | ||
}; | ||
|
||
use farmfe_core::{context::CompilationContext, module::ModuleId}; | ||
use farmfe_core::{ | ||
context::CompilationContext, | ||
module::{ModuleId, ModuleMetaData}, | ||
}; | ||
use farmfe_toolkit::swc_ecma_utils::contains_top_level_await; | ||
|
||
pub fn find_async_modules(context: &Arc<CompilationContext>) -> HashSet<ModuleId> { | ||
let module_graph = context.module_graph.read(); | ||
let (mut topo_sorted_modules, _) = module_graph.toposort(); | ||
topo_sorted_modules.reverse(); | ||
let mut init_async_modules = HashSet::new(); | ||
|
||
for module in module_graph.modules() { | ||
if let ModuleMetaData::Script(script_meta) = module.meta.as_ref() { | ||
if contains_top_level_await(&script_meta.ast) { | ||
init_async_modules.insert(module.id.clone()); | ||
} | ||
} | ||
} | ||
|
||
let mut queue = VecDeque::from(init_async_modules.into_iter().collect::<Vec<_>>()); | ||
let mut async_modules = HashSet::new(); | ||
|
||
for module_id in topo_sorted_modules { | ||
let module = module_graph.module(&module_id).unwrap(); | ||
if module.module_type.is_script() { | ||
let ast = &module.meta.as_script().ast; | ||
|
||
if contains_top_level_await(ast) { | ||
async_modules.insert(module_id); | ||
} else { | ||
// if any dependency is async module, then mark the module as async module | ||
let mut found = false; | ||
|
||
for (dep, edge) in module_graph.dependencies(&module_id) { | ||
if async_modules.contains(&dep) && !edge.is_dynamic() { | ||
found = true; | ||
break; | ||
} | ||
} | ||
|
||
if found { | ||
async_modules.insert(module_id); | ||
} | ||
while !queue.is_empty() { | ||
let module_id = queue.pop_front().unwrap(); | ||
async_modules.insert(module_id.clone()); | ||
|
||
for (dept, edge) in module_graph.dependents(&module_id) { | ||
if !async_modules.contains(&dept) && !edge.is_dynamic() { | ||
queue.push_back(dept); | ||
} | ||
} | ||
} | ||
|
||
async_modules | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use std::{collections::HashSet, sync::Arc}; | ||
|
||
use farmfe_core::{ | ||
context::CompilationContext, | ||
module::{ModuleMetaData, ModuleType, ScriptModuleMetaData}, | ||
parking_lot::RwLock, | ||
swc_common::DUMMY_SP, | ||
swc_ecma_ast::{AwaitExpr, Expr, ExprStmt, Lit, Module, ModuleItem, Stmt}, | ||
}; | ||
use farmfe_testing_helpers::construct_test_module_graph; | ||
|
||
#[test] | ||
fn test_find_async_modules() { | ||
let mut module_graph = construct_test_module_graph(); | ||
module_graph.modules_mut().into_iter().for_each(|module| { | ||
module.module_type = ModuleType::Js; | ||
module.meta = Box::new(ModuleMetaData::Script(ScriptModuleMetaData::default())); | ||
module.meta.as_script_mut().ast = Module { | ||
body: vec![], | ||
span: DUMMY_SP, | ||
shebang: None, | ||
}; | ||
}); | ||
let module_c = module_graph.module_mut(&"C".into()).unwrap(); | ||
module_c.meta.as_script_mut().ast = Module { | ||
body: vec![ModuleItem::Stmt(Stmt::Expr(ExprStmt { | ||
expr: Box::new(Expr::Await(AwaitExpr { | ||
arg: Box::new(Expr::Lit(Lit::Str("any".into()))), | ||
span: DUMMY_SP, | ||
})), | ||
span: DUMMY_SP, | ||
}))], | ||
span: DUMMY_SP, | ||
shebang: None, | ||
}; | ||
|
||
let mut context = CompilationContext::new(Default::default(), vec![]).unwrap(); | ||
context.module_graph = Box::new(RwLock::new(module_graph)); | ||
|
||
let async_modules = super::find_async_modules(&Arc::new(context)); | ||
println!("{:#?}", async_modules); | ||
assert_eq!( | ||
async_modules, | ||
HashSet::from_iter(vec!["C".into(), "F".into(), "A".into()]) | ||
); | ||
} | ||
} |