turbopack_ecmascript/side_effect_optimization/locals/
module.rs

1use std::collections::BTreeMap;
2
3use anyhow::{Result, bail};
4use turbo_tasks::{ResolvedVc, Vc};
5use turbo_tasks_fs::glob::Glob;
6use turbopack_core::{
7    asset::{Asset, AssetContent},
8    chunk::{
9        AsyncModuleInfo, ChunkableModule, ChunkingContext, MergeableModule, MergeableModules,
10        MergeableModulesExposed,
11    },
12    ident::AssetIdent,
13    module::Module,
14    module_graph::ModuleGraph,
15    reference::ModuleReferences,
16    resolve::ModulePart,
17};
18
19use super::chunk_item::EcmascriptModuleLocalsChunkItem;
20use crate::{
21    AnalyzeEcmascriptModuleResult, EcmascriptAnalyzable, EcmascriptModuleAsset,
22    EcmascriptModuleContent, EcmascriptModuleContentOptions, MergedEcmascriptModule,
23    chunk::{EcmascriptChunkPlaceable, EcmascriptExports},
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
31/// local declarations, but excludes all reexports. These reexports are exposed
32/// from [EcmascriptModuleFacadeModule] instead.
33#[turbo_tasks::value]
34pub struct EcmascriptModuleLocalsModule {
35    pub module: ResolvedVc<EcmascriptModuleAsset>,
36}
37
38#[turbo_tasks::value_impl]
39impl EcmascriptModuleLocalsModule {
40    #[turbo_tasks::function]
41    pub fn new(module: ResolvedVc<EcmascriptModuleAsset>) -> Vc<Self> {
42        EcmascriptModuleLocalsModule { module }.cell()
43    }
44}
45
46#[turbo_tasks::value_impl]
47impl Module for EcmascriptModuleLocalsModule {
48    #[turbo_tasks::function]
49    fn ident(&self) -> Vc<AssetIdent> {
50        self.module.ident().with_part(ModulePart::locals())
51    }
52
53    #[turbo_tasks::function]
54    fn references(&self) -> Result<Vc<ModuleReferences>> {
55        let result = self.module.analyze();
56        Ok(result.local_references())
57    }
58
59    #[turbo_tasks::function]
60    async fn is_self_async(self: Vc<Self>) -> Result<Vc<bool>> {
61        let analyze = self.await?.module.analyze().await?;
62        if let Some(async_module) = *analyze.async_module.await? {
63            let is_self_async = async_module.is_self_async(self.references());
64            Ok(is_self_async)
65        } else {
66            Ok(Vc::cell(false))
67        }
68    }
69}
70
71#[turbo_tasks::value_impl]
72impl Asset for EcmascriptModuleLocalsModule {
73    #[turbo_tasks::function]
74    fn content(&self) -> Vc<AssetContent> {
75        self.module.content()
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.parse().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,
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, mutable) => {
148                    exports.insert(
149                        name.clone(),
150                        EsmExport::LocalBinding(local_name.clone(), *mutable),
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 is_marked_as_side_effect_free(&self, side_effect_free_packages: Vc<Glob>) -> Vc<bool> {
169        self.module
170            .is_marked_as_side_effect_free(side_effect_free_packages)
171    }
172
173    #[turbo_tasks::function]
174    fn get_async_module(&self) -> Vc<OptionAsyncModule> {
175        self.module.get_async_module()
176    }
177}
178
179#[turbo_tasks::value_impl]
180impl ChunkableModule for EcmascriptModuleLocalsModule {
181    #[turbo_tasks::function]
182    fn as_chunk_item(
183        self: ResolvedVc<Self>,
184        _module_graph: ResolvedVc<ModuleGraph>,
185        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
186    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
187        Vc::upcast(
188            EcmascriptModuleLocalsChunkItem {
189                module: self,
190                chunking_context,
191            }
192            .cell(),
193        )
194    }
195}
196
197#[turbo_tasks::value_impl]
198impl MergeableModule for EcmascriptModuleLocalsModule {
199    #[turbo_tasks::function]
200    async fn merge(
201        &self,
202        modules: Vc<MergeableModulesExposed>,
203        entry_points: Vc<MergeableModules>,
204    ) -> Result<Vc<Box<dyn ChunkableModule>>> {
205        Ok(Vc::upcast(
206            *MergedEcmascriptModule::new(modules, entry_points, self.module.await?.options).await?,
207        ))
208    }
209}