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