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