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