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