turbopack_ecmascript/tree_shake/
chunk_item.rs

1use anyhow::Result;
2use turbo_tasks::{ResolvedVc, ValueDefault, Vc};
3use turbo_tasks_fs::rope::RopeBuilder;
4use turbopack_core::{
5    chunk::{AsyncModuleInfo, ChunkItem, ChunkType, ChunkingContext, ModuleChunkItemIdExt},
6    ident::AssetIdent,
7    module::Module,
8    module_graph::ModuleGraph,
9    output::OutputAssetsReference,
10};
11
12use super::asset::EcmascriptModulePartAsset;
13use crate::{
14    EcmascriptAnalyzableExt,
15    chunk::{
16        EcmascriptChunkItem, EcmascriptChunkItemContent, EcmascriptChunkItemOptions,
17        EcmascriptChunkPlaceable, EcmascriptChunkType, item::RewriteSourcePath,
18    },
19    references::async_module::AsyncModuleOptions,
20    runtime_functions::{TURBOPACK_EXPORT_NAMESPACE, TURBOPACK_IMPORT},
21    tree_shake::side_effect_module::SideEffectsModule,
22    utils::StringifyModuleId,
23};
24
25/// This is an implementation of [ChunkItem] for
26/// [Vc<EcmascriptModulePartAsset>].
27///
28/// This is a pointer to a part of an ES module.
29#[turbo_tasks::value(shared)]
30pub struct EcmascriptModulePartChunkItem {
31    pub(super) module: ResolvedVc<EcmascriptModulePartAsset>,
32    pub(super) chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
33}
34
35#[turbo_tasks::value_impl]
36impl EcmascriptChunkItem for EcmascriptModulePartChunkItem {
37    #[turbo_tasks::function]
38    fn content(self: Vc<Self>) -> Vc<EcmascriptChunkItemContent> {
39        panic!("content() should never be called");
40    }
41
42    #[turbo_tasks::function]
43    async fn content_with_async_module_info(
44        &self,
45        async_module_info: Option<Vc<AsyncModuleInfo>>,
46        _estimated: bool,
47    ) -> Result<Vc<EcmascriptChunkItemContent>> {
48        let analyze = self.module.analyze();
49        let analyze_ref = analyze.await?;
50        let async_module_options = analyze_ref.async_module.module_options(async_module_info);
51
52        let content = self
53            .module
54            .module_content(*self.chunking_context, async_module_info);
55
56        Ok(EcmascriptChunkItemContent::new(
57            content,
58            *self.chunking_context,
59            async_module_options,
60        ))
61    }
62}
63
64#[turbo_tasks::value_impl]
65impl OutputAssetsReference for EcmascriptModulePartChunkItem {}
66
67#[turbo_tasks::value_impl]
68impl ChunkItem for EcmascriptModulePartChunkItem {
69    #[turbo_tasks::function]
70    fn asset_ident(&self) -> Vc<AssetIdent> {
71        self.module.ident()
72    }
73
74    #[turbo_tasks::function]
75    fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
76        *self.chunking_context
77    }
78
79    #[turbo_tasks::function]
80    async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
81        Ok(Vc::upcast(
82            Vc::<EcmascriptChunkType>::default().resolve().await?,
83        ))
84    }
85
86    #[turbo_tasks::function]
87    fn module(&self) -> Vc<Box<dyn Module>> {
88        *ResolvedVc::upcast(self.module)
89    }
90}
91
92#[turbo_tasks::value(shared)]
93pub(super) struct SideEffectsModuleChunkItem {
94    pub module: ResolvedVc<SideEffectsModule>,
95    pub module_graph: ResolvedVc<ModuleGraph>,
96    pub chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
97}
98
99#[turbo_tasks::value_impl]
100impl OutputAssetsReference for SideEffectsModuleChunkItem {}
101
102#[turbo_tasks::value_impl]
103impl ChunkItem for SideEffectsModuleChunkItem {
104    #[turbo_tasks::function]
105    fn asset_ident(&self) -> Vc<AssetIdent> {
106        self.module.ident()
107    }
108
109    #[turbo_tasks::function]
110    fn ty(&self) -> Vc<Box<dyn ChunkType>> {
111        Vc::upcast(EcmascriptChunkType::value_default())
112    }
113
114    #[turbo_tasks::function]
115    fn module(&self) -> Vc<Box<dyn Module>> {
116        *ResolvedVc::upcast(self.module)
117    }
118
119    #[turbo_tasks::function]
120    fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
121        *self.chunking_context
122    }
123}
124
125#[turbo_tasks::value_impl]
126impl EcmascriptChunkItem for SideEffectsModuleChunkItem {
127    #[turbo_tasks::function]
128    async fn content(&self) -> Result<Vc<EcmascriptChunkItemContent>> {
129        let mut code = RopeBuilder::default();
130        let mut has_top_level_await = false;
131
132        let module = self.module.await?;
133
134        for &side_effect in self.module.await?.side_effects.iter() {
135            let need_await = 'need_await: {
136                let async_module = *side_effect.get_async_module().await?;
137                if let Some(async_module) = async_module
138                    && async_module.await?.has_top_level_await
139                {
140                    break 'need_await true;
141                }
142                false
143            };
144
145            if !has_top_level_await && need_await {
146                has_top_level_await = true;
147            }
148
149            code.push_bytes(
150                format!(
151                    "{}{TURBOPACK_IMPORT}({});\n",
152                    if need_await { "await " } else { "" },
153                    StringifyModuleId(&*side_effect.chunk_item_id(*self.chunking_context).await?)
154                )
155                .as_bytes(),
156            );
157        }
158
159        code.push_bytes(
160            format!(
161                "{TURBOPACK_EXPORT_NAMESPACE}({TURBOPACK_IMPORT}({}));\n",
162                StringifyModuleId(
163                    &*module
164                        .resolved_as
165                        .chunk_item_id(*self.chunking_context)
166                        .await?
167                )
168            )
169            .as_bytes(),
170        );
171
172        let code = code.build();
173
174        Ok(EcmascriptChunkItemContent {
175            inner_code: code,
176            source_map: None,
177            rewrite_source_path: RewriteSourcePath::None,
178            options: EcmascriptChunkItemOptions {
179                strict: true,
180                async_module: if has_top_level_await {
181                    Some(AsyncModuleOptions {
182                        has_top_level_await: true,
183                    })
184                } else {
185                    None
186                },
187                ..Default::default()
188            },
189            additional_ids: Default::default(),
190            placeholder_for_future_extensions: (),
191        }
192        .cell())
193    }
194}