Skip to main content

turbopack_ecmascript/side_effect_optimization/locals/
module.rs

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