diff --git a/crates/mako/src/generate/transform.rs b/crates/mako/src/generate/transform.rs index f0ca9c4a8..972740311 100644 --- a/crates/mako/src/generate/transform.rs +++ b/crates/mako/src/generate/transform.rs @@ -78,8 +78,9 @@ pub fn transform_modules_in_thread( let module_id = module_id.clone(); let async_deps = async_deps_by_module_id .get(&module_id) - .expect(&module_id.id) - .clone(); + .cloned() + .unwrap_or(vec![]); + thread_pool::spawn(move || { let module_graph = context.module_graph.read().unwrap(); let deps = module_graph.get_dependencies(&module_id); diff --git a/crates/mako/src/visitors/async_module.rs b/crates/mako/src/visitors/async_module.rs index bde3ce1ee..870085d09 100644 --- a/crates/mako/src/visitors/async_module.rs +++ b/crates/mako/src/visitors/async_module.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet, VecDeque}; use std::sync::Arc; use swc_core::common::util::take::Take; @@ -211,7 +211,49 @@ pub fn mark_async( ) -> HashMap> { let mut async_deps_by_module_id = HashMap::new(); let mut module_graph = context.module_graph.write().unwrap(); - // TODO: 考虑成环的场景 + + let mut to_visit_queue = module_graph + .modules() + .iter() + .filter_map(|m| { + m.info + .as_ref() + .and_then(|i| if i.is_async { Some(m.id.clone()) } else { None }) + }) + .collect::>(); + let mut visited = HashSet::new(); + + // polluted async to dependants + while let Some(module_id) = to_visit_queue.pop_front() { + if visited.contains(&module_id) { + continue; + } + + module_graph + .get_dependents(&module_id) + .iter() + .filter_map(|(dependant, dependency)| { + if !dependency.resolve_type.is_sync_esm() { + return None; + } + if !visited.contains(*dependant) { + Some((*dependant).clone()) + } else { + None + } + }) + .collect::>() + .iter() + .for_each(|module_id| { + let m = module_graph.get_module_mut(module_id).unwrap(); + m.info.as_mut().unwrap().is_async = true; + + to_visit_queue.push_back(module_id.clone()); + }); + + visited.insert(module_id.clone()); + } + module_ids.iter().for_each(|module_id| { let deps = module_graph.get_dependencies_info(module_id); let async_deps: Vec = deps @@ -219,15 +261,11 @@ pub fn mark_async( .filter(|(_, dep, is_async)| dep.resolve_type.is_sync_esm() && *is_async) .map(|(_, dep, _)| dep.clone()) .collect(); - let module = module_graph.get_module_mut(module_id).unwrap(); - if let Some(info) = module.info.as_mut() { - // a module with async deps need to be polluted into async module - if !info.is_async && !async_deps.is_empty() { - info.is_async = true; - } + if !async_deps.is_empty() { async_deps_by_module_id.insert(module_id.clone(), async_deps); } }); + async_deps_by_module_id } @@ -255,8 +293,8 @@ add(1, 2); "# .trim()); assert_eq!( - code, - r#" + code, + r#" __mako_require__._async(module, async (handleAsyncDeps, asyncResult)=>{ "use strict"; Object.defineProperty(exports, "__esModule", { @@ -273,7 +311,7 @@ __mako_require__._async(module, async (handleAsyncDeps, asyncResult)=>{ asyncResult(); }, true); "#.trim() - ); + ); } #[test] @@ -286,8 +324,8 @@ console.log(foo) "# .trim()); assert_eq!( - code, - r#" + code, + r#" __mako_require__._async(module, async (handleAsyncDeps, asyncResult)=>{ "use strict"; Object.defineProperty(exports, "__esModule", { @@ -308,8 +346,8 @@ __mako_require__._async(module, async (handleAsyncDeps, asyncResult)=>{ asyncResult(); }, true); "# - .trim() - ); + .trim() + ); } #[test] @@ -321,8 +359,8 @@ add(1, 2); "# .trim()); assert_eq!( - code, - r#" + code, + r#" __mako_require__._async(module, async (handleAsyncDeps, asyncResult)=>{ "use strict"; Object.defineProperty(exports, "__esModule", { @@ -340,8 +378,8 @@ __mako_require__._async(module, async (handleAsyncDeps, asyncResult)=>{ asyncResult(); }, true); "# - .trim() - ); + .trim() + ); } #[test] @@ -374,8 +412,8 @@ const async = require('./miexed_async'); "# .trim()); assert_eq!( - code, - r#" + code, + r#" __mako_require__._async(module, async (handleAsyncDeps, asyncResult)=>{ "use strict"; Object.defineProperty(exports, "__esModule", { @@ -393,7 +431,7 @@ __mako_require__._async(module, async (handleAsyncDeps, asyncResult)=>{ asyncResult(); }, true); "#.trim() - ); + ); } fn run(js_code: &str) -> String { diff --git a/e2e/fixtures/javascript.async_module_in_loop/async.js b/e2e/fixtures/javascript.async_module_in_loop/async.js new file mode 100644 index 000000000..36e73e402 --- /dev/null +++ b/e2e/fixtures/javascript.async_module_in_loop/async.js @@ -0,0 +1,11 @@ +let x = await new Promise((resolve)=>{ + resolve("default") +}) + +let named = await new Promise((resolve)=>{ + resolve("named") +}) + +export default x; + +export {named} diff --git a/e2e/fixtures/javascript.async_module_in_loop/component.js b/e2e/fixtures/javascript.async_module_in_loop/component.js new file mode 100644 index 000000000..dba222359 --- /dev/null +++ b/e2e/fixtures/javascript.async_module_in_loop/component.js @@ -0,0 +1,11 @@ +import { listKeys } from "./utils" + +import { named } from "./async" + +export const config = { + key: "value" +} + +export function displayConfig() { + return listKeys() +} \ No newline at end of file diff --git a/e2e/fixtures/javascript.async_module_in_loop/expect.js b/e2e/fixtures/javascript.async_module_in_loop/expect.js new file mode 100644 index 000000000..50c0ed6f4 --- /dev/null +++ b/e2e/fixtures/javascript.async_module_in_loop/expect.js @@ -0,0 +1,9 @@ +const { + injectSimpleJest, + parseBuildResult, + moduleDefinitionOf, +} = require("../../../scripts/test-utils"); +const { files } = parseBuildResult(__dirname); +injectSimpleJest(); + +require("./dist/index.js"); diff --git a/e2e/fixtures/javascript.async_module_in_loop/index.js b/e2e/fixtures/javascript.async_module_in_loop/index.js new file mode 100644 index 000000000..33413af77 --- /dev/null +++ b/e2e/fixtures/javascript.async_module_in_loop/index.js @@ -0,0 +1,6 @@ +import {displayConfig} from "./component"; + +it("should require looped async moule", () => { + expect(displayConfig()).toStrictEqual(["key"]) +}) + diff --git a/e2e/fixtures/javascript.async_module_in_loop/mako.config.json b/e2e/fixtures/javascript.async_module_in_loop/mako.config.json new file mode 100644 index 000000000..4b0659f7b --- /dev/null +++ b/e2e/fixtures/javascript.async_module_in_loop/mako.config.json @@ -0,0 +1,5 @@ +{ + "entry": { + "index": "./index.js" + } +} diff --git a/e2e/fixtures/javascript.async_module_in_loop/utils.js b/e2e/fixtures/javascript.async_module_in_loop/utils.js new file mode 100644 index 000000000..f13959b5b --- /dev/null +++ b/e2e/fixtures/javascript.async_module_in_loop/utils.js @@ -0,0 +1,9 @@ +import {config} from "./component" + +export function listKeys() { + if(config){ + + return Object.keys(config) + } + return ["oops"] +} \ No newline at end of file