turbopack_ecmascript/side_effect_optimization/locals/
module.rs1use 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#[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 }
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}