Skip to content

Commit 2b9b2e3

Browse files
authored
fix: async module detection wrong (#1890)
1 parent b3a7caf commit 2b9b2e3

File tree

2 files changed

+80
-25
lines changed

2 files changed

+80
-25
lines changed

.changeset/selfish-goats-explode.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@farmfe/core": patch
3+
---
4+
5+
Fix async module detection wrong with cyclic dependencies
Lines changed: 75 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,89 @@
1-
use std::{collections::HashSet, sync::Arc};
1+
use std::{
2+
collections::{HashSet, VecDeque},
3+
sync::Arc,
4+
};
25

3-
use farmfe_core::{context::CompilationContext, module::ModuleId};
6+
use farmfe_core::{
7+
context::CompilationContext,
8+
module::{ModuleId, ModuleMetaData},
9+
};
410
use farmfe_toolkit::swc_ecma_utils::contains_top_level_await;
511

612
pub fn find_async_modules(context: &Arc<CompilationContext>) -> HashSet<ModuleId> {
713
let module_graph = context.module_graph.read();
8-
let (mut topo_sorted_modules, _) = module_graph.toposort();
9-
topo_sorted_modules.reverse();
14+
let mut init_async_modules = HashSet::new();
1015

16+
for module in module_graph.modules() {
17+
if let ModuleMetaData::Script(script_meta) = module.meta.as_ref() {
18+
if contains_top_level_await(&script_meta.ast) {
19+
init_async_modules.insert(module.id.clone());
20+
}
21+
}
22+
}
23+
24+
let mut queue = VecDeque::from(init_async_modules.into_iter().collect::<Vec<_>>());
1125
let mut async_modules = HashSet::new();
1226

13-
for module_id in topo_sorted_modules {
14-
let module = module_graph.module(&module_id).unwrap();
15-
if module.module_type.is_script() {
16-
let ast = &module.meta.as_script().ast;
17-
18-
if contains_top_level_await(ast) {
19-
async_modules.insert(module_id);
20-
} else {
21-
// if any dependency is async module, then mark the module as async module
22-
let mut found = false;
23-
24-
for (dep, edge) in module_graph.dependencies(&module_id) {
25-
if async_modules.contains(&dep) && !edge.is_dynamic() {
26-
found = true;
27-
break;
28-
}
29-
}
30-
31-
if found {
32-
async_modules.insert(module_id);
33-
}
27+
while !queue.is_empty() {
28+
let module_id = queue.pop_front().unwrap();
29+
async_modules.insert(module_id.clone());
30+
31+
for (dept, edge) in module_graph.dependents(&module_id) {
32+
if !async_modules.contains(&dept) && !edge.is_dynamic() {
33+
queue.push_back(dept);
3434
}
3535
}
3636
}
3737

3838
async_modules
3939
}
40+
41+
#[cfg(test)]
42+
mod tests {
43+
use std::{collections::HashSet, sync::Arc};
44+
45+
use farmfe_core::{
46+
context::CompilationContext,
47+
module::{ModuleMetaData, ModuleType, ScriptModuleMetaData},
48+
parking_lot::RwLock,
49+
swc_common::DUMMY_SP,
50+
swc_ecma_ast::{AwaitExpr, Expr, ExprStmt, Lit, Module, ModuleItem, Stmt},
51+
};
52+
use farmfe_testing_helpers::construct_test_module_graph;
53+
54+
#[test]
55+
fn test_find_async_modules() {
56+
let mut module_graph = construct_test_module_graph();
57+
module_graph.modules_mut().into_iter().for_each(|module| {
58+
module.module_type = ModuleType::Js;
59+
module.meta = Box::new(ModuleMetaData::Script(ScriptModuleMetaData::default()));
60+
module.meta.as_script_mut().ast = Module {
61+
body: vec![],
62+
span: DUMMY_SP,
63+
shebang: None,
64+
};
65+
});
66+
let module_c = module_graph.module_mut(&"C".into()).unwrap();
67+
module_c.meta.as_script_mut().ast = Module {
68+
body: vec![ModuleItem::Stmt(Stmt::Expr(ExprStmt {
69+
expr: Box::new(Expr::Await(AwaitExpr {
70+
arg: Box::new(Expr::Lit(Lit::Str("any".into()))),
71+
span: DUMMY_SP,
72+
})),
73+
span: DUMMY_SP,
74+
}))],
75+
span: DUMMY_SP,
76+
shebang: None,
77+
};
78+
79+
let mut context = CompilationContext::new(Default::default(), vec![]).unwrap();
80+
context.module_graph = Box::new(RwLock::new(module_graph));
81+
82+
let async_modules = super::find_async_modules(&Arc::new(context));
83+
println!("{:#?}", async_modules);
84+
assert_eq!(
85+
async_modules,
86+
HashSet::from_iter(vec!["C".into(), "F".into(), "A".into()])
87+
);
88+
}
89+
}

0 commit comments

Comments
 (0)