Skip to main content

turbopack_ecmascript/side_effect_optimization/locals/
module.rs

1use std::collections::BTreeMap;
2
3use anyhow::{Result, bail};
4use turbo_tasks::{ResolvedVc, Vc};
5use turbopack_core::{
6    chunk::{
7        AsyncModuleInfo, ChunkableModule, ChunkingContext, MergeableModule, MergeableModules,
8        MergeableModulesExposed,
9    },
10    ident::AssetIdent,
11    module::{Module, ModuleSideEffects},
12    module_graph::ModuleGraph,
13    reference::ModuleReferences,
14    resolve::ModulePart,
15};
16
17use crate::{
18    AnalyzeEcmascriptModuleResult, EcmascriptAnalyzable, EcmascriptAnalyzableExt,
19    EcmascriptModuleAsset, EcmascriptModuleContent, EcmascriptModuleContentOptions,
20    MergedEcmascriptModule,
21    chunk::{
22        EcmascriptChunkItemContent, EcmascriptChunkPlaceable, EcmascriptExports,
23        ecmascript_chunk_item,
24    },
25    references::{
26        async_module::OptionAsyncModule,
27        esm::{EsmExport, EsmExports},
28    },
29};
30
31/// A module derived from an original ecmascript module that only contains the local declarations,
32/// but excludes all reexports. These reexports are exposed from [`EcmascriptModuleFacadeModule`]
33/// instead.
34///
35/// [`EcmascriptModuleFacadeModule`]: crate::side_effect_optimization::facade::module::EcmascriptModuleFacadeModule
36#[turbo_tasks::value]
37pub struct EcmascriptModuleLocalsModule {
38    pub module: ResolvedVc<EcmascriptModuleAsset>,
39}
40
41#[turbo_tasks::value_impl]
42impl EcmascriptModuleLocalsModule {
43    #[turbo_tasks::function]
44    pub fn new(module: ResolvedVc<EcmascriptModuleAsset>) -> Vc<Self> {
45        EcmascriptModuleLocalsModule { module }.cell()
46    }
47}
48
49#[turbo_tasks::value_impl]
50impl Module for EcmascriptModuleLocalsModule {
51    #[turbo_tasks::function]
52    fn ident(&self) -> Vc<AssetIdent> {
53        self.module.ident().with_part(ModulePart::locals())
54    }
55
56    #[turbo_tasks::function]
57    fn source(&self) -> Vc<turbopack_core::source::OptionSource> {
58        Vc::cell(None)
59    }
60
61    #[turbo_tasks::function]
62    fn references(&self) -> Result<Vc<ModuleReferences>> {
63        let result = self.module.analyze();
64        Ok(result.local_references())
65    }
66
67    #[turbo_tasks::function]
68    async fn is_self_async(self: Vc<Self>) -> Result<Vc<bool>> {
69        let analyze = self.await?.module.analyze().await?;
70        if let Some(async_module) = *analyze.async_module.await? {
71            let is_self_async = async_module.is_self_async(self.references());
72            Ok(is_self_async)
73        } else {
74            Ok(Vc::cell(false))
75        }
76    }
77
78    #[turbo_tasks::function]
79    fn side_effects(&self) -> Vc<ModuleSideEffects> {
80        self.module.side_effects()
81    }
82}
83
84#[turbo_tasks::value_impl]
85impl EcmascriptAnalyzable for EcmascriptModuleLocalsModule {
86    #[turbo_tasks::function]
87    fn analyze(&self) -> Vc<AnalyzeEcmascriptModuleResult> {
88        self.module.analyze()
89    }
90
91    #[turbo_tasks::function]
92    fn module_content_without_analysis(
93        &self,
94        generate_source_map: bool,
95    ) -> Vc<EcmascriptModuleContent> {
96        self.module
97            .module_content_without_analysis(generate_source_map)
98    }
99
100    #[turbo_tasks::function]
101    async fn module_content_options(
102        self: ResolvedVc<Self>,
103        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
104        async_module_info: Option<ResolvedVc<AsyncModuleInfo>>,
105    ) -> Result<Vc<EcmascriptModuleContentOptions>> {
106        let exports = self.get_exports().to_resolved().await?;
107        let original_module = self.await?.module;
108        let parsed = original_module.await?.parse().await?.to_resolved().await?;
109
110        let analyze = original_module.analyze();
111        let analyze_result = analyze.await?;
112
113        let module_type_result = original_module.determine_module_type().await?;
114        let generate_source_map = *chunking_context
115            .reference_module_source_maps(Vc::upcast(*self))
116            .await?;
117
118        Ok(EcmascriptModuleContentOptions {
119            parsed: Some(parsed),
120            module: ResolvedVc::upcast(self),
121            specified_module_type: module_type_result.module_type,
122            chunking_context,
123            references: analyze.local_references().to_resolved().await?,
124            esm_references: analyze_result.esm_local_references,
125            part_references: vec![],
126            code_generation: analyze_result.code_generation,
127            async_module: analyze_result.async_module,
128            generate_source_map,
129            original_source_map: analyze_result.source_map,
130            exports,
131            async_module_info,
132        }
133        .cell())
134    }
135}
136
137#[turbo_tasks::value_impl]
138impl EcmascriptChunkPlaceable for EcmascriptModuleLocalsModule {
139    #[turbo_tasks::function]
140    async fn get_exports(&self) -> Result<Vc<EcmascriptExports>> {
141        let EcmascriptExports::EsmExports(exports) = *self.module.get_exports().await? else {
142            bail!("EcmascriptModuleLocalsModule must only be used on modules with EsmExports");
143        };
144        let esm_exports = exports.await?;
145        let mut exports = BTreeMap::new();
146
147        for (name, export) in &esm_exports.exports {
148            match export {
149                EsmExport::ImportedBinding(..) | EsmExport::ImportedNamespace(..) => {
150                    // not included in locals module
151                }
152                EsmExport::LocalBinding(local_name, liveness) => {
153                    exports.insert(
154                        name.clone(),
155                        EsmExport::LocalBinding(local_name.clone(), *liveness),
156                    );
157                }
158                EsmExport::Error => {
159                    exports.insert(name.clone(), EsmExport::Error);
160                }
161            }
162        }
163
164        let exports = EsmExports {
165            exports,
166            star_exports: vec![],
167        }
168        .resolved_cell();
169        Ok(EcmascriptExports::EsmExports(exports).cell())
170    }
171
172    #[turbo_tasks::function]
173    fn get_async_module(&self) -> Vc<OptionAsyncModule> {
174        self.module.get_async_module()
175    }
176
177    #[turbo_tasks::function]
178    async fn chunk_item_content(
179        self: Vc<Self>,
180        chunking_context: Vc<Box<dyn ChunkingContext>>,
181        _module_graph: Vc<ModuleGraph>,
182        async_module_info: Option<Vc<AsyncModuleInfo>>,
183        _estimated: bool,
184    ) -> Result<Vc<EcmascriptChunkItemContent>> {
185        let analyze = self.await?.module.analyze().await?;
186        let async_module_options = analyze.async_module.module_options(async_module_info);
187
188        let content = self.module_content(chunking_context, async_module_info);
189
190        Ok(EcmascriptChunkItemContent::new(
191            content,
192            chunking_context,
193            async_module_options,
194        ))
195    }
196}
197
198#[turbo_tasks::value_impl]
199impl ChunkableModule for EcmascriptModuleLocalsModule {
200    #[turbo_tasks::function]
201    fn as_chunk_item(
202        self: ResolvedVc<Self>,
203        module_graph: ResolvedVc<ModuleGraph>,
204        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
205    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
206        ecmascript_chunk_item(ResolvedVc::upcast(self), module_graph, chunking_context)
207    }
208}
209
210#[turbo_tasks::value_impl]
211impl MergeableModule for EcmascriptModuleLocalsModule {
212    #[turbo_tasks::function]
213    async fn merge(
214        &self,
215        modules: Vc<MergeableModulesExposed>,
216        entry_points: Vc<MergeableModules>,
217    ) -> Result<Vc<Box<dyn ChunkableModule>>> {
218        Ok(Vc::upcast(
219            *MergedEcmascriptModule::new(modules, entry_points, self.module.await?.options).await?,
220        ))
221    }
222}