diff --git a/crates/mako/src/generate/group_chunk.rs b/crates/mako/src/generate/group_chunk.rs index e6194d4a1..c2e00c87e 100644 --- a/crates/mako/src/generate/group_chunk.rs +++ b/crates/mako/src/generate/group_chunk.rs @@ -57,6 +57,12 @@ impl Compiler { ); chunk_graph.add_chunk(chunk); + /* A worker can self-spawn from it's source file, that will leads to a circular dependencies, + * a real case is https://unpkg.com/browse/@antv/layout-wasm@1.4.0/pkg-parallel/. + * Memorize handled workers to avoid infinite resolving + */ + let mut visited_workers = HashSet::::new(); + // 抽离成两个函数处理动态依赖中可能有 worker 依赖、worker 依赖中可能有动态依赖的复杂情况 self.handle_dynamic_dependencies( &chunk_name, @@ -64,6 +70,7 @@ impl Compiler { &visited, &mut edges, &mut chunk_graph, + &mut visited_workers, ); self.handle_worker_dependencies( &chunk_name, @@ -71,6 +78,7 @@ impl Compiler { &visited, &mut edges, &mut chunk_graph, + &mut visited_workers, ); } @@ -86,14 +94,18 @@ impl Compiler { visited: &HashSet, edges: &mut Vec<(ModuleId, ModuleId)>, chunk_graph: &mut ChunkGraph, + visited_workers: &mut HashSet, ) { visit_modules(dynamic_dependencies, Some(visited.clone()), |head| { - let (chunk, dynamic_dependencies, worker_dependencies) = self.create_chunk( + let (chunk, dynamic_dependencies, mut worker_dependencies) = self.create_chunk( head, ChunkType::Async, chunk_graph, vec![chunk_name.to_string()], ); + + worker_dependencies.retain(|w| !visited_workers.contains(w)); + edges.extend( [dynamic_dependencies.clone(), worker_dependencies.clone()] .concat() @@ -101,12 +113,14 @@ impl Compiler { .map(|dep| (chunk.id.clone(), dep.generate(&self.context).into())), ); chunk_graph.add_chunk(chunk); + self.handle_worker_dependencies( chunk_name, worker_dependencies, visited, edges, chunk_graph, + visited_workers, ); dynamic_dependencies @@ -120,27 +134,36 @@ impl Compiler { visited: &HashSet, edges: &mut Vec<(ModuleId, ModuleId)>, chunk_graph: &mut ChunkGraph, + visited_workers: &mut HashSet, ) { visit_modules(worker_dependencies, Some(visited.clone()), |head| { - let (chunk, dynamic_dependencies, worker_dependencies) = self.create_chunk( + let (chunk, dynamic_dependencies, mut worker_dependencies) = self.create_chunk( head, ChunkType::Worker(head.clone()), chunk_graph, vec![chunk_name.to_string()], ); + + worker_dependencies.retain(|w| !visited_workers.contains(w)); + edges.extend( [dynamic_dependencies.clone(), worker_dependencies.clone()] .concat() .into_iter() .map(|dep| (chunk.id.clone(), dep.generate(&self.context).into())), ); + chunk_graph.add_chunk(chunk); + + visited_workers.insert(head.clone()); + self.handle_dynamic_dependencies( chunk_name, dynamic_dependencies, visited, edges, chunk_graph, + visited_workers, ); worker_dependencies diff --git a/e2e/fixtures/worker.self-spawn/.gitignore b/e2e/fixtures/worker.self-spawn/.gitignore new file mode 100644 index 000000000..a56a7ef43 --- /dev/null +++ b/e2e/fixtures/worker.self-spawn/.gitignore @@ -0,0 +1,2 @@ +node_modules + diff --git a/e2e/fixtures/worker.self-spawn/expect.js b/e2e/fixtures/worker.self-spawn/expect.js new file mode 100644 index 000000000..6c7ffb7bc --- /dev/null +++ b/e2e/fixtures/worker.self-spawn/expect.js @@ -0,0 +1,10 @@ +const assert = require("assert"); +const { parseBuildResult } = require("../../../scripts/test-utils"); +const { files } = parseBuildResult(__dirname); + +assert( + files["src_workerHelper_ts-worker.js"].includes( + 'new Worker(new URL("src_workerHelper_ts-async.js",' + ), + "should have self-spawn codes" +); diff --git a/e2e/fixtures/worker.self-spawn/mako.config.json b/e2e/fixtures/worker.self-spawn/mako.config.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/e2e/fixtures/worker.self-spawn/mako.config.json @@ -0,0 +1 @@ +{} diff --git a/e2e/fixtures/worker.self-spawn/src/index.ts b/e2e/fixtures/worker.self-spawn/src/index.ts new file mode 100644 index 000000000..db3fa53a1 --- /dev/null +++ b/e2e/fixtures/worker.self-spawn/src/index.ts @@ -0,0 +1,3 @@ +export default (v: any) => console.log(v); + +import("./workerHelper").then((w) => w.startWorkers()); diff --git a/e2e/fixtures/worker.self-spawn/src/workerHelper.ts b/e2e/fixtures/worker.self-spawn/src/workerHelper.ts new file mode 100644 index 000000000..072f772e9 --- /dev/null +++ b/e2e/fixtures/worker.self-spawn/src/workerHelper.ts @@ -0,0 +1,25 @@ +function waitForMsgType(target, type) { + return new Promise((resolve) => { + target.addEventListener("message", function onMsg({ data }) { + if (data == null || data.type !== type) return; + target.removeEventListener("message", onMsg); + resolve(data); + }); + }); +} + +waitForMsgType(self, "spawn").then(async (data) => { + const log = await import("."); + log.default("spawn a web worker"); +}); + +export async function startWorkers() { + await Promise.all( + Array.from({ length: 2 }, async () => { + const worker = new Worker(new URL("./workerHelper", import.meta.url), { + type: "module", + }); + worker.postMessage("spawn"); + }) + ); +} diff --git a/examples/react/src/foo.ts b/examples/react/src/foo.ts index 85297a2e0..bb1843d11 100644 --- a/examples/react/src/foo.ts +++ b/examples/react/src/foo.ts @@ -1,2 +1 @@ - export const foo = 1;