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) chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
32}
33
34#[turbo_tasks::value_impl]
35impl EcmascriptChunkItem for EcmascriptModulePartChunkItem {
36    #[turbo_tasks::function]
37    fn content(self: Vc<Self>) -> Vc<EcmascriptChunkItemContent> {
38        panic!("content() should never be called");
39    }
40
41    #[turbo_tasks::function]
42    async fn content_with_async_module_info(
43        &self,
44        async_module_info: Option<Vc<AsyncModuleInfo>>,
45    ) -> Result<Vc<EcmascriptChunkItemContent>> {
46        let analyze = self.module.analyze();
47        let analyze_ref = analyze.await?;
48        let async_module_options = analyze_ref.async_module.module_options(async_module_info);
49
50        let content = self
51            .module
52            .module_content(*self.chunking_context, async_module_info);
53
54        Ok(EcmascriptChunkItemContent::new(
55            content,
56            *self.chunking_context,
57            async_module_options,
58        ))
59    }
60}
61
62#[turbo_tasks::value_impl]
63impl ChunkItem for EcmascriptModulePartChunkItem {
64    #[turbo_tasks::function]
65    fn asset_ident(&self) -> Vc<AssetIdent> {
66        self.module.ident()
67    }
68
69    #[turbo_tasks::function]
70    fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
71        *ResolvedVc::upcast(self.chunking_context)
72    }
73
74    #[turbo_tasks::function]
75    async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
76        Ok(Vc::upcast(
77            Vc::<EcmascriptChunkType>::default().resolve().await?,
78        ))
79    }
80
81    #[turbo_tasks::function]
82    fn module(&self) -> Vc<Box<dyn Module>> {
83        *ResolvedVc::upcast(self.module)
84    }
85}
86
87#[turbo_tasks::value(shared)]
88pub(super) struct SideEffectsModuleChunkItem {
89    pub module: ResolvedVc<SideEffectsModule>,
90    pub module_graph: ResolvedVc<ModuleGraph>,
91    pub chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
92}
93
94#[turbo_tasks::value_impl]
95impl ChunkItem for SideEffectsModuleChunkItem {
96    #[turbo_tasks::function]
97    fn asset_ident(&self) -> Vc<AssetIdent> {
98        self.module.ident()
99    }
100
101    #[turbo_tasks::function]
102    fn ty(&self) -> Vc<Box<dyn ChunkType>> {
103        Vc::upcast(EcmascriptChunkType::value_default())
104    }
105
106    #[turbo_tasks::function]
107    fn module(&self) -> Vc<Box<dyn Module>> {
108        *ResolvedVc::upcast(self.module)
109    }
110
111    #[turbo_tasks::function]
112    fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
113        *self.chunking_context
114    }
115}
116
117#[turbo_tasks::value_impl]
118impl EcmascriptChunkItem for SideEffectsModuleChunkItem {
119    #[turbo_tasks::function]
120    async fn content(&self) -> Result<Vc<EcmascriptChunkItemContent>> {
121        let mut code = RopeBuilder::default();
122        let mut has_top_level_await = false;
123
124        let module = self.module.await?;
125
126        for &side_effect in self.module.await?.side_effects.iter() {
127            let need_await = 'need_await: {
128                let async_module = *side_effect.get_async_module().await?;
129                if let Some(async_module) = async_module
130                    && async_module.await?.has_top_level_await
131                {
132                    break 'need_await true;
133                }
134                false
135            };
136
137            if !has_top_level_await && need_await {
138                has_top_level_await = true;
139            }
140
141            code.push_bytes(
142                format!(
143                    "{}{TURBOPACK_IMPORT}({});\n",
144                    if need_await { "await " } else { "" },
145                    StringifyModuleId(&*side_effect.chunk_item_id(*self.chunking_context).await?)
146                )
147                .as_bytes(),
148            );
149        }
150
151        code.push_bytes(
152            format!(
153                "{TURBOPACK_EXPORT_NAMESPACE}({TURBOPACK_IMPORT}({}));\n",
154                StringifyModuleId(
155                    &*module
156                        .resolved_as
157                        .chunk_item_id(*self.chunking_context)
158                        .await?
159                )
160            )
161            .as_bytes(),
162        );
163
164        let code = code.build();
165
166        Ok(EcmascriptChunkItemContent {
167            inner_code: code,
168            source_map: None,
169            rewrite_source_path: None,
170            options: EcmascriptChunkItemOptions {
171                strict: true,
172                async_module: if has_top_level_await {
173                    Some(AsyncModuleOptions {
174                        has_top_level_await: true,
175                    })
176                } else {
177                    None
178                },
179                ..Default::default()
180            },
181            additional_ids: Default::default(),
182            placeholder_for_future_extensions: (),
183        }
184        .cell())
185    }
186}