diff --git a/crates/turbopack-ecmascript/src/lib.rs b/crates/turbopack-ecmascript/src/lib.rs index bb3108674527f..23975adbcc002 100644 --- a/crates/turbopack-ecmascript/src/lib.rs +++ b/crates/turbopack-ecmascript/src/lib.rs @@ -660,7 +660,7 @@ impl ChunkItem for ModuleChunkItem { #[turbo_tasks::function] async fn is_self_async(&self) -> Result> { if let Some(async_module) = *self.module.get_async_module().await? { - Ok(Vc::cell(*async_module.is_self_async().await?)) + Ok(async_module.is_self_async()) } else { Ok(Vc::cell(false)) } diff --git a/crates/turbopack-ecmascript/src/references/async_module.rs b/crates/turbopack-ecmascript/src/references/async_module.rs index 2cb771e9c5695..6d4ca2d7ad55a 100644 --- a/crates/turbopack-ecmascript/src/references/async_module.rs +++ b/crates/turbopack-ecmascript/src/references/async_module.rs @@ -9,6 +9,7 @@ use swc_core::{ use turbo_tasks::{trace::TraceRawVcs, TryFlatJoinIterExt, TryJoinIterExt, Vc}; use turbopack_core::{ chunk::{AsyncModuleInfo, ChunkableModule}, + reference::{ModuleReference, ModuleReferences}, resolve::ExternalType, }; @@ -17,7 +18,7 @@ use crate::{ chunk::{EcmascriptChunkPlaceable, EcmascriptChunkingContext}, code_gen::{CodeGenerateableWithAsyncModuleInfo, CodeGeneration}, create_visitor, - references::esm::{base::insert_hoisted_stmt, EsmAssetReference}, + references::esm::base::insert_hoisted_stmt, }; /// Information needed for generating the async module wrapper for @@ -47,7 +48,7 @@ impl OptionAsyncModuleOptions { #[turbo_tasks::value(shared)] pub struct AsyncModule { pub placeable: Vc>, - pub references: IndexSet>, + pub references: Vc, pub has_top_level_await: bool, pub import_externals: bool, } @@ -92,9 +93,11 @@ impl AsyncModule { let reference_idents = self .references + .await? .iter() .map(|r| async { - let referenced_asset = r.get_referenced_asset().await?; + let referenced_asset = + ReferencedAsset::from_resolve_result(r.resolve_reference()).await?; Ok(match &*referenced_asset { ReferencedAsset::External( _, @@ -140,9 +143,11 @@ impl AsyncModule { self.import_externals && self .references + .await? .iter() .map(|r| async { - let referenced_asset = r.get_referenced_asset().await?; + let referenced_asset = + ReferencedAsset::from_resolve_result(r.resolve_reference()).await?; Ok(matches!( &*referenced_asset, ReferencedAsset::External( diff --git a/crates/turbopack-ecmascript/src/references/mod.rs b/crates/turbopack-ecmascript/src/references/mod.rs index bd7c9f760d578..2b0b772fae6b8 100644 --- a/crates/turbopack-ecmascript/src/references/mod.rs +++ b/crates/turbopack-ecmascript/src/references/mod.rs @@ -736,7 +736,7 @@ pub(crate) async fn analyse_ecmascript_module_internal( if eval_context.is_esm() || specified_type == SpecifiedModuleType::EcmaScript { let async_module = AsyncModule { placeable: Vc::upcast(module), - references: import_references.iter().copied().collect(), + references: Vc::cell(import_references.iter().map(|&r| Vc::upcast(r)).collect()), has_top_level_await, import_externals, } diff --git a/crates/turbopack-ecmascript/src/side_effect_optimization/facade/chunk_item.rs b/crates/turbopack-ecmascript/src/side_effect_optimization/facade/chunk_item.rs index 4c12229540e6d..c1e36b17d536b 100644 --- a/crates/turbopack-ecmascript/src/side_effect_optimization/facade/chunk_item.rs +++ b/crates/turbopack-ecmascript/src/side_effect_optimization/facade/chunk_item.rs @@ -31,13 +31,13 @@ use crate::{ /// The chunk item for [EcmascriptModuleFacadeModule]. #[turbo_tasks::value(shared)] -pub struct EcmascriptModuleReexportsChunkItem { +pub struct EcmascriptModuleFacadeChunkItem { pub(crate) module: Vc, pub(crate) chunking_context: Vc>, } #[turbo_tasks::value_impl] -impl EcmascriptChunkItem for EcmascriptModuleReexportsChunkItem { +impl EcmascriptChunkItem for EcmascriptModuleFacadeChunkItem { #[turbo_tasks::function] fn content(self: Vc) -> Vc { panic!("content() should never be called"); @@ -68,8 +68,9 @@ impl EcmascriptChunkItem for EcmascriptModuleReexportsChunkItem { let mut code = RopeBuilder::default(); - let mut code_gens = Vec::new(); - for r in self.module.references().await?.iter() { + let references = self.module.references().await?; + let mut code_gens = Vec::with_capacity(references.len() + 2); + for r in references.iter() { let r = r.resolve().await?; if let Some(code_gen) = Vc::try_resolve_sidecast::>(r).await? @@ -82,6 +83,11 @@ impl EcmascriptChunkItem for EcmascriptModuleReexportsChunkItem { } } + code_gens.push( + self.module + .async_module() + .code_generation(chunking_context, async_module_info), + ); code_gens.push(exports.code_generation(chunking_context)); let code_gens = code_gens.into_iter().try_join().await?; let code_gens = code_gens.iter().map(|cg| &**cg).collect::>(); @@ -150,7 +156,7 @@ impl EcmascriptChunkItem for EcmascriptModuleReexportsChunkItem { } #[turbo_tasks::value_impl] -impl ChunkItem for EcmascriptModuleReexportsChunkItem { +impl ChunkItem for EcmascriptModuleFacadeChunkItem { #[turbo_tasks::function] fn references(&self) -> Vc { self.module.references() diff --git a/crates/turbopack-ecmascript/src/side_effect_optimization/facade/module.rs b/crates/turbopack-ecmascript/src/side_effect_optimization/facade/module.rs index 6743f88180636..9bc51bc256c9b 100644 --- a/crates/turbopack-ecmascript/src/side_effect_optimization/facade/module.rs +++ b/crates/turbopack-ecmascript/src/side_effect_optimization/facade/module.rs @@ -12,11 +12,11 @@ use turbopack_core::{ resolve::ModulePart, }; -use super::chunk_item::EcmascriptModuleReexportsChunkItem; +use super::chunk_item::EcmascriptModuleFacadeChunkItem; use crate::{ chunk::{EcmascriptChunkPlaceable, EcmascriptChunkingContext, EcmascriptExports}, references::{ - async_module::OptionAsyncModule, + async_module::{AsyncModule, OptionAsyncModule}, esm::{EsmExport, EsmExports}, }, side_effect_optimization::reference::EcmascriptModulePartReference, @@ -38,6 +38,17 @@ impl EcmascriptModuleFacadeModule { pub fn new(module: Vc>, ty: Vc) -> Vc { EcmascriptModuleFacadeModule { module, ty }.cell() } + + #[turbo_tasks::function] + pub fn async_module(self: Vc) -> Vc { + AsyncModule { + placeable: Vc::upcast(self), + references: self.references(), + has_top_level_await: false, + import_externals: false, + } + .cell() + } } #[turbo_tasks::value_impl] @@ -247,8 +258,8 @@ impl EcmascriptChunkPlaceable for EcmascriptModuleFacadeModule { } #[turbo_tasks::function] - fn get_async_module(&self) -> Vc { - self.module.get_async_module() + fn get_async_module(self: Vc) -> Vc { + Vc::cell(Some(self.async_module())) } } @@ -267,7 +278,7 @@ impl ChunkableModule for EcmascriptModuleFacadeModule { EcmascriptModuleFacadeModule", )?; Ok(Vc::upcast( - EcmascriptModuleReexportsChunkItem { + EcmascriptModuleFacadeChunkItem { module: self, chunking_context, } diff --git a/crates/turbopack-ecmascript/src/side_effect_optimization/locals/chunk_item.rs b/crates/turbopack-ecmascript/src/side_effect_optimization/locals/chunk_item.rs index 0d05bd49f1593..beb4201901329 100644 --- a/crates/turbopack-ecmascript/src/side_effect_optimization/locals/chunk_item.rs +++ b/crates/turbopack-ecmascript/src/side_effect_optimization/locals/chunk_item.rs @@ -100,4 +100,13 @@ impl ChunkItem for EcmascriptModuleLocalsChunkItem { fn module(&self) -> Vc> { Vc::upcast(self.module) } + + #[turbo_tasks::function] + async fn is_self_async(&self) -> Result> { + if let Some(async_module) = *self.module.get_async_module().await? { + Ok(async_module.is_self_async()) + } else { + Ok(Vc::cell(false)) + } + } } diff --git a/crates/turbopack-ecmascript/src/side_effect_optimization/locals/module.rs b/crates/turbopack-ecmascript/src/side_effect_optimization/locals/module.rs index d7b306317a0ab..0afd314d1ac57 100644 --- a/crates/turbopack-ecmascript/src/side_effect_optimization/locals/module.rs +++ b/crates/turbopack-ecmascript/src/side_effect_optimization/locals/module.rs @@ -14,7 +14,10 @@ use turbopack_core::{ use super::chunk_item::EcmascriptModuleLocalsChunkItem; use crate::{ chunk::{EcmascriptChunkPlaceable, EcmascriptChunkingContext, EcmascriptExports}, - references::esm::{EsmExport, EsmExports}, + references::{ + async_module::OptionAsyncModule, + esm::{EsmExport, EsmExports}, + }, EcmascriptModuleAsset, }; @@ -94,6 +97,11 @@ impl EcmascriptChunkPlaceable for EcmascriptModuleLocalsModule { fn is_marked_as_side_effect_free(&self) -> Vc { self.module.is_marked_as_side_effect_free() } + + #[turbo_tasks::function] + fn get_async_module(&self) -> Vc { + self.module.get_async_module() + } } #[turbo_tasks::value_impl] diff --git a/crates/turbopack-ecmascript/src/side_effect_optimization/reference.rs b/crates/turbopack-ecmascript/src/side_effect_optimization/reference.rs index c34e1971e75d0..833d033876435 100644 --- a/crates/turbopack-ecmascript/src/side_effect_optimization/reference.rs +++ b/crates/turbopack-ecmascript/src/side_effect_optimization/reference.rs @@ -6,7 +6,10 @@ use swc_core::{ }; use turbo_tasks::{ValueToString, Vc}; use turbopack_core::{ - chunk::{ChunkItemExt, ChunkableModule, ChunkableModuleReference, ModuleId}, + chunk::{ + ChunkItemExt, ChunkableModule, ChunkableModuleReference, ChunkingType, ChunkingTypeOption, + ModuleId, + }, reference::ModuleReference, resolve::{ModulePart, ModuleResolveResult}, }; @@ -95,7 +98,12 @@ impl ModuleReference for EcmascriptModulePartReference { } #[turbo_tasks::value_impl] -impl ChunkableModuleReference for EcmascriptModulePartReference {} +impl ChunkableModuleReference for EcmascriptModulePartReference { + #[turbo_tasks::function] + fn chunking_type(self: Vc) -> Vc { + Vc::cell(Some(ChunkingType::ParallelInheritAsync)) + } +} #[turbo_tasks::value_impl] impl CodeGenerateable for EcmascriptModulePartReference { diff --git a/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/index.js b/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/index.js index 18d346d176bd3..5c0a0cb8a2b33 100644 --- a/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/index.js +++ b/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/index.js @@ -87,3 +87,61 @@ it("should handle globs in sideEffects field", () => { expect(b9).toBe("b"); expect(effects).toEqual(["file.side.js", "dir/file.js"]); }); + +it("should generate a correct facade from async modules", async () => { + expect(await import("tla/local")).toEqual( + expect.objectContaining({ + tla: "tla", + reexported: "reexported", + reexported2: "reexported", + }) + ); + expect(await import("tla/reexport")).toEqual( + expect.objectContaining({ + local: "local", + tlaReexported: "tla-reexported", + tlaReexported2: "tla-reexported", + }) + ); + expect(await import("tla/both")).toEqual( + expect.objectContaining({ + tla: "tla", + tlaReexported: "tla-reexported", + tlaReexported2: "tla-reexported", + }) + ); +}); + +import * as tlaLocal from "tla/local"; +import * as tlaReexport from "tla/reexport"; +import * as tlaBoth from "tla/both"; +it("should generate a correct namespace object from async modules", async () => { + expect(tlaLocal).toEqual( + expect.objectContaining({ + tla: "tla", + reexported: "reexported", + reexported2: "reexported", + }) + ); + expect(tlaReexport).toEqual( + expect.objectContaining({ + local: "local", + tlaReexported: "tla-reexported", + tlaReexported2: "tla-reexported", + }) + ); + expect(tlaBoth).toEqual( + expect.objectContaining({ + tla: "tla", + tlaReexported: "tla-reexported", + tlaReexported2: "tla-reexported", + }) + ); +}); + +import { tlaReexported2 as tlaReexported } from "tla/reexport"; +import { tlaReexported2 as tlaReexportedBoth } from "tla/both"; +it("should generate correct renaming facades from async modules", async () => { + expect(tlaReexported).toBe("tla-reexported"); + expect(tlaReexportedBoth).toBe("tla-reexported"); +}); diff --git a/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/both.js b/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/both.js new file mode 100644 index 0000000000000..3b6be057170ea --- /dev/null +++ b/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/both.js @@ -0,0 +1,5 @@ +await 1; + +export { tlaReexported } from "./tla-reexported.js"; +export { tlaReexported as tlaReexported2 } from "./tla-reexported.js"; +export const tla = "tla"; diff --git a/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/local.js b/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/local.js new file mode 100644 index 0000000000000..30394b75dfa68 --- /dev/null +++ b/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/local.js @@ -0,0 +1,5 @@ +await 1; + +export { reexported } from "./reexported.js"; +export { reexported as reexported2 } from "./reexported.js"; +export const tla = "tla"; diff --git a/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/package.json b/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/package.json new file mode 100644 index 0000000000000..a43829151e142 --- /dev/null +++ b/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/package.json @@ -0,0 +1,3 @@ +{ + "sideEffects": false +} diff --git a/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/reexport.js b/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/reexport.js new file mode 100644 index 0000000000000..61ddf718c70ce --- /dev/null +++ b/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/reexport.js @@ -0,0 +1,3 @@ +export { tlaReexported } from "./tla-reexported.js"; +export { tlaReexported as tlaReexported2 } from "./tla-reexported.js"; +export const local = "local"; diff --git a/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/reexported.js b/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/reexported.js new file mode 100644 index 0000000000000..8f8db5be12d6b --- /dev/null +++ b/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/reexported.js @@ -0,0 +1 @@ +export const reexported = "reexported"; diff --git a/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/tla-reexported.js b/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/tla-reexported.js new file mode 100644 index 0000000000000..6519255662b7b --- /dev/null +++ b/crates/turbopack-tests/tests/execution/turbopack/tree-shaking/side-effects/input/node_modules/tla/tla-reexported.js @@ -0,0 +1,3 @@ +await 1; + +export const tlaReexported = "tla-reexported";