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::{
9 NonLocalValue, ResolvedVc, ValueToString, Vc, debug::ValueDebugFormat, trace::TraceRawVcs,
10};
11use turbopack_core::{
12 chunk::{ChunkingContext, ChunkingType},
13 compile_time_info::CompileTimeDefineValue,
14 emit_collect::EmittedModuleReference,
15 issue::IssueSource,
16 module::Module,
17 reference::ModuleReference,
18 reference_type::EcmaScriptModulesReferenceSubType,
19 resolve::{
20 BindingUsage, ExportUsage, ModuleResolveResult, ResolveErrorMode,
21 options::ResolveOptions,
22 origin::{ResolveOrigin, ResolveOriginExt},
23 parse::Request,
24 },
25};
26use turbopack_resolve::ecmascript::esm_resolve;
27
28use crate::{
29 analyzer::imports::ImportAnnotations,
30 code_gen::{CodeGen, CodeGeneration, IntoCodeGenReference},
31 collect_module::{COLLECT_LIST_EXPORT, EcmascriptCollectModule},
32 create_visitor,
33 references::{
34 AstPath,
35 esm::{base::ReferencedAsset, mangle::generated_export_key},
36 pattern_mapping::{PatternMapping, ResolveType},
37 removal::RemovalCodeGen,
38 },
39 utils::AstPathRange,
40};
41
42#[turbo_tasks::value]
43#[derive(Hash, Debug, ValueToString)]
44#[value_to_string("emit {request} {namespace}")]
45pub struct EmitReference {
46 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
47 request: ResolvedVc<Request>,
48 annotations: ImportAnnotations,
49 issue_source: IssueSource,
50 error_mode: ResolveErrorMode,
51 export_usage: ExportUsage,
52 namespace: RcStr,
53 data: Option<CompileTimeDefineValue>,
54 emit_to_all_entries: bool,
55}
56
57impl EmitReference {
58 pub fn new(
59 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
60 request: ResolvedVc<Request>,
61 issue_source: IssueSource,
62 annotations: ImportAnnotations,
63 error_mode: ResolveErrorMode,
64 export_usage: ExportUsage,
65 namespace: RcStr,
66 data: Option<CompileTimeDefineValue>,
67 emit_to_all_entries: bool,
68 ) -> Self {
69 EmitReference {
70 origin,
71 request,
72 issue_source,
73 annotations,
74 error_mode,
75 export_usage,
76 namespace,
77 data,
78 emit_to_all_entries,
79 }
80 }
81}
82
83#[turbo_tasks::value_impl]
84impl ModuleReference for EmitReference {
85 #[turbo_tasks::function]
86 async fn resolve_reference(&self) -> Result<Vc<ModuleResolveResult>> {
87 let origin = if let Some(transition) = self.annotations.transition() {
88 self.origin
89 .with_transition(transition.into())
90 .await?
91 .to_resolved()
92 .await?
93 } else {
94 self.origin
95 };
96
97 if self.annotations.turbopack_loader().is_some()
98 || self.annotations.turbopack_rename_as().is_some()
99 || self.annotations.turbopack_module_type().is_some()
100 {
101 bail!(
103 "turbopack_loader, turbopack_rename_as, and turbopack_module_type annotations are \
104 not yet supported for emit references"
105 );
106 }
107
108 esm_resolve(
109 *origin,
110 *self.request,
111 EcmaScriptModulesReferenceSubType::Emit,
112 self.error_mode,
113 Some(self.issue_source),
114 )
115 .await
116 }
117
118 fn chunking_type(&self) -> Option<ChunkingType> {
119 Some(ChunkingType::Emitted {
120 namespace: self.namespace.clone(),
121 emit_to_all_entries: self.emit_to_all_entries,
122 })
123 }
124
125 fn binding_usage(&self) -> BindingUsage {
126 BindingUsage {
127 import: Default::default(),
128 export: self.export_usage.clone(),
129 }
130 }
131}
132
133#[turbo_tasks::value_impl]
134impl EmittedModuleReference for EmitReference {
135 #[turbo_tasks::function]
136 fn data(&self) -> Vc<CompileTimeDefineValue> {
137 self.data
138 .clone()
139 .unwrap_or(CompileTimeDefineValue::Undefined)
140 .cell()
141 }
142}
143
144impl IntoCodeGenReference for EmitReference {
145 fn into_reference(self) -> ResolvedVc<Box<dyn ModuleReference>> {
146 ResolvedVc::upcast(self.resolved_cell())
147 }
148
149 fn into_code_gen_reference(
150 self,
151 mut path: AstPath,
152 ) -> (ResolvedVc<Box<dyn ModuleReference>>, CodeGen) {
153 let reference = self.resolved_cell();
154 path.0.pop();
155 (
156 ResolvedVc::upcast(reference),
157 CodeGen::RemovalCodeGen(RemovalCodeGen::new(
158 rcstr!("TURBOPACK collect"),
159 AstPathRange::Exact(path.0),
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 mut path: AstPath,
229 ) -> (ResolvedVc<Box<dyn ModuleReference>>, CodeGen) {
230 let reference = self.resolved_cell();
231 path.0.pop();
232 (
233 ResolvedVc::upcast(reference),
234 CodeGen::CollectReferenceCodeGen(CollectReferenceCodeGen { reference, path }),
235 )
236 }
237}
238
239#[derive(
240 PartialEq, Eq, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, Encode, Decode,
241)]
242pub struct CollectReferenceCodeGen {
243 reference: ResolvedVc<CollectReference>,
244 path: AstPath,
245}
246
247impl CollectReferenceCodeGen {
248 pub async fn code_generation(
249 &self,
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 self.path,
279 visit_mut_expr,
280 |expr: &mut Expr| {
281 *expr = Expr::Member(MemberExpr {
282 span: DUMMY_SP,
283 obj: Box::new(pm.create_require(Expr::Invalid(Invalid::default()))),
284 prop: MemberProp::Ident(IdentName::new(export.as_str().into(), DUMMY_SP)),
285 });
286 }
287 ));
288
289 Ok(CodeGeneration::visitors(visitors))
290 }
291}