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