Skip to main content

turbopack_ecmascript/rename/
module.rs

1use anyhow::{Result, bail};
2use turbo_frozenmap::FrozenMap;
3use turbo_tasks::{ResolvedVc, Vc};
4use turbo_tasks_fs::{File, FileContent};
5use turbopack_core::{
6    asset::{Asset, AssetContent},
7    chunk::{
8        AsyncModuleInfo, ChunkableModule, ChunkingContext, EvaluatableAsset, MergeableModule,
9        MergeableModules, MergeableModulesExposed,
10    },
11    ident::AssetIdent,
12    module::{Module, ModuleSideEffects},
13    module_graph::ModuleGraph,
14    reference::ModuleReferences,
15    resolve::{ExportUsage, ModulePart},
16    source::OptionSource,
17};
18
19use crate::{
20    AnalyzeEcmascriptModuleResult, EcmascriptAnalyzable, EcmascriptAnalyzableExt,
21    EcmascriptModuleContent, EcmascriptModuleContentOptions, EcmascriptOptions, EnvVarInfo,
22    MergedEcmascriptModule, SpecifiedModuleType,
23    chunk::{
24        EcmascriptChunkItemContent, EcmascriptChunkPlaceable, EcmascriptExports,
25        ecmascript_chunk_item,
26    },
27    code_gen::CodeGens,
28    references::{
29        async_module::{AsyncModule, OptionAsyncModule},
30        esm::{EsmExport, EsmExports, base::EsmAssetReferences},
31    },
32    side_effect_optimization::reference::EcmascriptModulePartReference,
33};
34
35/// A module derived from an original ecmascript module that reexports a single export or the whole
36/// namespace object under a different name.
37#[turbo_tasks::value]
38pub struct EcmascriptModuleRenameModule {
39    module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
40    /// The part of the module that this facade represents.
41    /// ModulePart::Facade | ModulePart::RenamedExport |
42    /// ModulePart::RenamedNamespace | ModulePart::RenamedPartialNamespace
43    part: ModulePart,
44}
45
46#[turbo_tasks::value_impl]
47impl EcmascriptModuleRenameModule {
48    #[turbo_tasks::function]
49    pub fn new(
50        module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
51        part: ModulePart,
52    ) -> Vc<Self> {
53        assert!(
54            matches!(
55                part,
56                ModulePart::RenamedExport { .. }
57                    | ModulePart::RenamedNamespace { .. }
58                    | ModulePart::RenamedPartialNamespace { .. }
59            ),
60            "{part:?} is unexpected for EcmascriptModuleRenameModule"
61        );
62        EcmascriptModuleRenameModule { module, part }.cell()
63    }
64
65    #[turbo_tasks::function]
66    pub async fn async_module(&self) -> Result<Vc<AsyncModule>> {
67        let (import_externals, has_top_level_await) =
68            if let Some(async_module) = *self.module.get_async_module().await? {
69                (
70                    async_module.await?.import_externals,
71                    async_module.await?.has_top_level_await,
72                )
73            } else {
74                (false, false)
75            };
76        Ok(AsyncModule {
77            has_top_level_await,
78            import_externals,
79        }
80        .cell())
81    }
82}
83
84impl EcmascriptModuleRenameModule {
85    pub async fn module_reference(&self) -> Result<ResolvedVc<EcmascriptModulePartReference>> {
86        match &self.part {
87            ModulePart::RenamedNamespace { .. } => {
88                EcmascriptModulePartReference::new_normal(
89                    *self.module,
90                    self.part.clone(),
91                    ExportUsage::all(),
92                )
93                .to_resolved()
94                .await
95            }
96            ModulePart::RenamedPartialNamespace { member, .. } => {
97                EcmascriptModulePartReference::new_normal(
98                    *self.module,
99                    self.part.clone(),
100                    ExportUsage::partial_namespace_object(vec![member.clone()]),
101                )
102                .to_resolved()
103                .await
104            }
105            ModulePart::RenamedExport {
106                original_export, ..
107            } => {
108                EcmascriptModulePartReference::new_normal(
109                    *self.module,
110                    self.part.clone(),
111                    ExportUsage::named(original_export.clone()),
112                )
113                .to_resolved()
114                .await
115            }
116            _ => {
117                bail!("Unexpected ModulePart for EcmascriptModuleRenameModule");
118            }
119        }
120    }
121}
122
123#[turbo_tasks::value_impl]
124impl Module for EcmascriptModuleRenameModule {
125    #[turbo_tasks::function]
126    fn source(&self) -> Vc<OptionSource> {
127        Vc::cell(None)
128    }
129
130    #[turbo_tasks::function]
131    async fn ident(&self) -> Result<Vc<AssetIdent>> {
132        Ok(self
133            .module
134            .ident()
135            .owned()
136            .await?
137            .with_part(self.part.clone())
138            .into_vc())
139    }
140
141    #[turbo_tasks::function]
142    async fn references(self: Vc<Self>) -> Result<Vc<ModuleReferences>> {
143        let reference = self.await?.module_reference().await?;
144        Ok(Vc::cell(vec![ResolvedVc::upcast(reference)]))
145    }
146
147    #[turbo_tasks::function]
148    async fn is_self_async(self: Vc<Self>) -> Result<Vc<bool>> {
149        let async_module = self.async_module();
150        let references = self.references();
151        let is_self_async = async_module
152            .to_resolved()
153            .await?
154            .is_self_async(*references.to_resolved().await?)
155            .to_resolved()
156            .await?;
157        Ok(*is_self_async)
158    }
159
160    #[turbo_tasks::function]
161    fn side_effects(&self) -> Vc<ModuleSideEffects> {
162        // This just re-exports another import
163        ModuleSideEffects::ModuleEvaluationIsSideEffectFree.cell()
164    }
165}
166
167#[turbo_tasks::value_impl]
168impl Asset for EcmascriptModuleRenameModule {
169    #[turbo_tasks::function]
170    fn content(&self) -> Vc<AssetContent> {
171        let f = File::from("");
172
173        AssetContent::file(FileContent::Content(f).cell())
174    }
175}
176
177#[turbo_tasks::value_impl]
178impl EcmascriptAnalyzable for EcmascriptModuleRenameModule {
179    #[turbo_tasks::function]
180    fn analyze(&self) -> Result<Vc<AnalyzeEcmascriptModuleResult>> {
181        bail!("EcmascriptModuleRenameModule::analyze shouldn't be called");
182    }
183
184    #[turbo_tasks::function]
185    fn env_var_info(self: Vc<Self>) -> Vc<EnvVarInfo> {
186        EnvVarInfo::empty()
187    }
188
189    #[turbo_tasks::function]
190    fn module_content_without_analysis(
191        &self,
192        _generate_source_map: bool,
193    ) -> Result<Vc<EcmascriptModuleContent>> {
194        bail!("EcmascriptModuleRenameModule::module_content_without_analysis shouldn't be called");
195    }
196
197    #[turbo_tasks::function]
198    async fn module_content_options(
199        self: ResolvedVc<Self>,
200        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
201        async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
202    ) -> Result<Vc<EcmascriptModuleContentOptions>> {
203        let reference = self.await?.module_reference().await?;
204
205        Ok(EcmascriptModuleContentOptions {
206            parsed: None,
207            module: ResolvedVc::upcast(self),
208            specified_module_type: SpecifiedModuleType::EcmaScript,
209            chunking_context,
210            references: self.references().to_resolved().await?,
211            part_references: vec![reference],
212            esm_references: EsmAssetReferences::empty().to_resolved().await?,
213            code_generation: CodeGens::empty().to_resolved().await?,
214            async_module: ResolvedVc::cell(Some(self.async_module().to_resolved().await?)),
215            // The facade module cannot generate source maps, because the inserted references
216            // contain spans from the original module, but the facade module itself doesn't have the
217            // original module's swc_common::SourceMap in `parsed`.
218            generate_source_map: false,
219            original_source_map: None,
220            exports: self.get_exports().to_resolved().await?,
221            export_registration_mode: None,
222            async_module_info,
223        }
224        .cell())
225    }
226}
227
228#[turbo_tasks::value_impl]
229impl EcmascriptChunkPlaceable for EcmascriptModuleRenameModule {
230    #[turbo_tasks::function]
231    async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
232        let reference = self.module_reference().await?;
233
234        let export = match &self.part {
235            ModulePart::RenamedExport {
236                original_export,
237                export,
238            } => (
239                export.clone(),
240                EsmExport::ImportedBinding(
241                    ResolvedVc::upcast(reference),
242                    original_export.clone(),
243                    false,
244                ),
245            ),
246            ModulePart::RenamedNamespace { export }
247            | ModulePart::RenamedPartialNamespace { export, .. } => (
248                export.clone(),
249                EsmExport::ImportedNamespace(ResolvedVc::upcast(reference)),
250            ),
251            _ => bail!("Unexpected ModulePart for EcmascriptModuleRenameModule"),
252        };
253
254        let exports = EsmExports {
255            exports: FrozenMap::from_unique_sorted_box(Box::new([export])),
256            star_exports: Vec::new(),
257            // This module only re-exports one binding of `self.module` under a different name, so
258            // whether its own key may be shortened follows the module it renames.
259            mangle_export_names: match &*self.module.get_exports().await? {
260                EcmascriptExports::EsmExports(exports) => exports.await?.mangle_export_names,
261                _ => false,
262            },
263        }
264        .resolved_cell();
265        Ok(EcmascriptExports::EsmExports(exports).cell())
266    }
267
268    #[turbo_tasks::function]
269    async fn get_async_module(self: Vc<Self>) -> Result<Vc<OptionAsyncModule>> {
270        Ok(Vc::cell(Some(self.async_module().to_resolved().await?)))
271    }
272
273    #[turbo_tasks::function]
274    async fn chunk_item_content(
275        self: Vc<Self>,
276        chunking_context: Vc<Box<dyn ChunkingContext>>,
277        _module_graph: Vc<ModuleGraph>,
278        async_module_info: Option<Vc<AsyncModuleInfo>>,
279        _estimated: bool,
280    ) -> Result<Vc<EcmascriptChunkItemContent>> {
281        let async_module_options = self.get_async_module().module_options(async_module_info);
282        let content = self.module_content(chunking_context, async_module_info);
283        Ok(EcmascriptChunkItemContent::new(
284            content,
285            chunking_context,
286            async_module_options,
287        ))
288    }
289}
290
291#[turbo_tasks::value_impl]
292impl ChunkableModule for EcmascriptModuleRenameModule {
293    #[turbo_tasks::function]
294    fn as_chunk_item(
295        self: ResolvedVc<Self>,
296        module_graph: ResolvedVc<ModuleGraph>,
297        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
298    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
299        ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
300    }
301}
302
303#[turbo_tasks::value_impl]
304impl EvaluatableAsset for EcmascriptModuleRenameModule {}
305
306#[turbo_tasks::value_impl]
307impl MergeableModule for EcmascriptModuleRenameModule {
308    #[turbo_tasks::function]
309    async fn merge(
310        self: Vc<Self>,
311        modules: Vc<MergeableModulesExposed>,
312        entry_points: Vc<MergeableModules>,
313    ) -> Result<Vc<Box<dyn ChunkableModule>>> {
314        Ok(Vc::upcast(
315            *MergedEcmascriptModule::new(
316                modules,
317                entry_points,
318                EcmascriptOptions::default().resolved_cell(),
319            )
320            .await?,
321        ))
322    }
323}