turbopack_ecmascript/references/
emit_collect.rs1use anyhow::{Result, bail};
2use bincode::{Decode, Encode};
3use swc_core::{
4 common::DUMMY_SP,
5 ecma::ast::{Expr, IdentName, Invalid, MemberExpr, MemberProp},
6};
7use turbo_rcstr::{RcStr, rcstr};
8use turbo_tasks::{NonLocalValue, ResolvedVc, ValueToString, Vc, debug::ValueDebugFormat};
9use turbopack_core::{
10 chunk::{ChunkingContext, ChunkingType},
11 compile_time_info::CompileTimeDefineValue,
12 emit_collect::EmittedModuleReference,
13 issue::IssueSource,
14 module::Module,
15 reference::ModuleReference,
16 reference_type::EcmaScriptModulesReferenceSubType,
17 resolve::{
18 BindingUsage, ExportUsage, ModuleResolveResult, ResolveErrorMode,
19 options::ResolveOptions,
20 origin::{ResolveOrigin, ResolveOriginExt},
21 parse::Request,
22 },
23};
24use turbopack_resolve::ecmascript::esm_resolve;
25
26use crate::{
27 analyzer::imports::ImportAnnotations,
28 ast_path_trie::{AstPathId, AstPathTrie, AstPathTrieBuilder},
29 code_gen::{CodeGen, CodeGeneration, IntoCodeGenReference},
30 collect_module::{COLLECT_LIST_EXPORT, EcmascriptCollectModule},
31 create_visitor,
32 references::{
33 esm::{base::ReferencedAsset, mangle::generated_export_key},
34 pattern_mapping::{PatternMapping, ResolveType},
35 removal::RemovalCodeGen,
36 },
37 utils::AstPathRange,
38};
39
40#[turbo_tasks::value]
41#[derive(Hash, Debug, ValueToString)]
42#[value_to_string("emit {request} {namespace}")]
43pub struct EmitReference {
44 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
45 request: ResolvedVc<Request>,
46 annotations: ImportAnnotations,
47 issue_source: IssueSource,
48 error_mode: ResolveErrorMode,
49 export_usage: ExportUsage,
50 namespace: RcStr,
51 data: Option<CompileTimeDefineValue>,
52 emit_to_all_entries: bool,
53}
54
55impl EmitReference {
56 pub fn new(
57 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
58 request: ResolvedVc<Request>,
59 issue_source: IssueSource,
60 annotations: ImportAnnotations,
61 error_mode: ResolveErrorMode,
62 export_usage: ExportUsage,
63 namespace: RcStr,
64 data: Option<CompileTimeDefineValue>,
65 emit_to_all_entries: bool,
66 ) -> Self {
67 EmitReference {
68 origin,
69 request,
70 issue_source,
71 annotations,
72 error_mode,
73 export_usage,
74 namespace,
75 data,
76 emit_to_all_entries,
77 }
78 }
79}
80
81#[turbo_tasks::value_impl]
82impl ModuleReference for EmitReference {
83 #[turbo_tasks::function]
84 async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
85 let origin = if let Some(transition) = self.annotations.transition() {
86 self.origin
87 .with_transition(transition.into())
88 .await?
89 .to_resolved()
90 .await?
91 } else {
92 self.origin
93 };
94
95 if self.annotations.turbopack_loader().is_some()
96 || self.annotations.turbopack_rename_as().is_some()
97 || self.annotations.turbopack_module_type().is_some()
98 {
99 bail!(
101 "turbopack_loader, turbopack_rename_as, and turbopack_module_type annotations are \
102 not yet supported for emit references"
103 );
104 }
105
106 esm_resolve(
107 *origin,
108 *self.request,
109 EcmaScriptModulesReferenceSubType::Emit,
110 self.error_mode,
111 Some(self.issue_source),
112 )
113 .await
114 }
115
116 fn chunking_type(&self) -> Option<ChunkingType> {
117 Some(ChunkingType::Emitted {
118 namespace: self.namespace.clone(),
119 emit_to_all_entries: self.emit_to_all_entries,
120 })
121 }
122
123 fn binding_usage(&self) -> BindingUsage {
124 BindingUsage {
125 import: Default::default(),
126 export: self.export_usage.clone(),
127 }
128 }
129}
130
131#[turbo_tasks::value_impl]
132impl EmittedModuleReference for EmitReference {
133 #[turbo_tasks::function]
134 fn data(&self) -> Vc<CompileTimeDefineValue> {
135 self.data
136 .clone()
137 .unwrap_or(CompileTimeDefineValue::Undefined)
138 .cell()
139 }
140}
141
142impl IntoCodeGenReference for EmitReference {
143 fn into_reference(self) -> ResolvedVc<Box<dyn ModuleReference>> {
144 ResolvedVc::upcast(self.resolved_cell())
145 }
146
147 fn into_code_gen_reference(
148 self,
149 trie: &AstPathTrieBuilder,
150 path: AstPathId,
151 ) -> (ResolvedVc<Box<dyn ModuleReference>>, CodeGen) {
152 let reference = self.resolved_cell();
153 let path = trie.parent_or_root(path);
155 (
156 ResolvedVc::upcast(reference),
157 CodeGen::RemovalCodeGen(RemovalCodeGen::new(
158 rcstr!("TURBOPACK collect"),
159 AstPathRange::Exact(path),
160 )),
161 )
162 }
163}
164
165#[turbo_tasks::value]
166#[derive(Hash, Debug, ValueToString)]
167#[value_to_string("collect {namespace}")]
168pub struct CollectReference {
169 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
170 parent_module: ResolvedVc<Box<dyn Module>>,
171 namespace: RcStr,
172}
173
174impl CollectReference {
175 pub fn new(
176 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
177 parent_module: ResolvedVc<Box<dyn Module>>,
178 namespace: RcStr,
179 ) -> Self {
180 CollectReference {
181 origin,
182 parent_module,
183 namespace,
184 }
185 }
186}
187
188fn collect_request() -> Vc<Request> {
189 Request::parse_string(rcstr!("data:text/javascript,"))
190}
191#[turbo_tasks::function]
192async fn with_data_uri(options: Vc<ResolveOptions>) -> Result<Vc<ResolveOptions>> {
193 let mut options: ResolveOptions = options.owned().await?;
194 options.parse_data_uris = true;
195 Ok(options.cell())
196}
197
198#[turbo_tasks::value_impl]
199impl ModuleReference for CollectReference {
200 #[turbo_tasks::function]
201 async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
202 let origin = self.origin.into_trait_ref().await?;
203
204 Ok(*ModuleResolveResult::module(ResolvedVc::upcast(
206 EcmascriptCollectModule::new(
207 *self.parent_module,
208 self.namespace.clone(),
209 *origin.asset_context(),
210 )
211 .to_resolved()
212 .await?,
213 )))
214 }
215
216 fn chunking_type(&self) -> Option<ChunkingType> {
217 Some(ChunkingType::PerEntry)
218 }
219}
220
221impl IntoCodeGenReference for CollectReference {
222 fn into_reference(self) -> ResolvedVc<Box<dyn ModuleReference>> {
223 ResolvedVc::upcast(self.resolved_cell())
224 }
225
226 fn into_code_gen_reference(
227 self,
228 trie: &AstPathTrieBuilder,
229 path: AstPathId,
230 ) -> (ResolvedVc<Box<dyn ModuleReference>>, CodeGen) {
231 let reference = self.resolved_cell();
232 let path = trie.parent_or_root(path);
233 (
234 ResolvedVc::upcast(reference),
235 CodeGen::CollectReferenceCodeGen(CollectReferenceCodeGen { reference, path }),
236 )
237 }
238}
239
240#[derive(PartialEq, Eq, ValueDebugFormat, NonLocalValue, Hash, Debug, Encode, Decode)]
241pub struct CollectReferenceCodeGen {
242 reference: ResolvedVc<CollectReference>,
243 path: AstPathId,
244}
245
246impl CollectReferenceCodeGen {
247 pub async fn code_generation(
248 &self,
249 trie: &AstPathTrie,
250 chunking_context: Vc<Box<dyn ChunkingContext>>,
251 ) -> Result<CodeGeneration> {
252 let reference = self.reference.await?;
253
254 let pm = PatternMapping::resolve_request(
255 collect_request(),
256 *reference.origin,
257 chunking_context,
258 self.reference.resolve_reference(),
259 ResolveType::ChunkItem,
260 Some(Vc::upcast(*self.reference)),
261 )
262 .await?;
263 let mut visitors = Vec::new();
264
265 let export =
270 match ReferencedAsset::from_resolve_result(self.reference.resolve_reference()).await? {
271 ReferencedAsset::Some(module) => {
272 generated_export_key(module, chunking_context, &COLLECT_LIST_EXPORT).await?
273 }
274 _ => COLLECT_LIST_EXPORT,
275 };
276
277 visitors.push(create_visitor!(
278 trie,
279 self.path,
280 visit_mut_expr,
281 |expr: &mut Expr| {
282 *expr = Expr::Member(MemberExpr {
283 span: DUMMY_SP,
284 obj: Box::new(pm.create_require(Expr::Invalid(Invalid::default()))),
285 prop: MemberProp::Ident(IdentName::new(export.as_str().into(), DUMMY_SP)),
286 });
287 }
288 ));
289
290 Ok(CodeGeneration::visitors(visitors))
291 }
292}