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