Skip to content

Commit

Permalink
fix: async module detection wrong (#1890)
Browse files Browse the repository at this point in the history
  • Loading branch information
wre232114 authored Oct 28, 2024
1 parent b3a7caf commit 2b9b2e3
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 25 deletions.
5 changes: 5 additions & 0 deletions .changeset/selfish-goats-explode.md
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
100 changes: 75 additions & 25 deletions crates/plugin_runtime/src/find_async_modules.rs
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()])
);
}
}

0 comments on commit 2b9b2e3

Please sign in to comment.