turbopack_ecmascript/references/
amd.rs1use std::mem::take;
2
3use anyhow::Result;
4use serde::{Deserialize, Serialize};
5use swc_core::{
6 common::DUMMY_SP,
7 ecma::{
8 ast::{CallExpr, Callee, Expr, ExprOrSpread, Lit},
9 utils::private_ident,
10 },
11 quote, quote_expr,
12};
13use turbo_rcstr::RcStr;
14use turbo_tasks::{
15 NonLocalValue, ReadRef, ResolvedVc, TryJoinIterExt, ValueToString, Vc, debug::ValueDebugFormat,
16 trace::TraceRawVcs,
17};
18use turbopack_core::{
19 chunk::{ChunkableModuleReference, ChunkingContext},
20 issue::IssueSource,
21 reference::ModuleReference,
22 resolve::{ModuleResolveResult, origin::ResolveOrigin, parse::Request},
23};
24use turbopack_resolve::ecmascript::cjs_resolve;
25
26use crate::{
27 code_gen::{CodeGen, CodeGeneration},
28 create_visitor,
29 references::{
30 AstPath,
31 pattern_mapping::{PatternMapping, ResolveType},
32 },
33 runtime_functions::{TURBOPACK_EXPORT_VALUE, TURBOPACK_REQUIRE},
34};
35
36#[turbo_tasks::value]
37#[derive(Hash, Debug)]
38pub struct AmdDefineAssetReference {
39 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
40 request: ResolvedVc<Request>,
41 issue_source: IssueSource,
42 in_try: bool,
43}
44
45#[turbo_tasks::value_impl]
46impl AmdDefineAssetReference {
47 #[turbo_tasks::function]
48 pub fn new(
49 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
50 request: ResolvedVc<Request>,
51 issue_source: IssueSource,
52 in_try: bool,
53 ) -> Vc<Self> {
54 Self::cell(AmdDefineAssetReference {
55 origin,
56 request,
57 issue_source,
58 in_try,
59 })
60 }
61}
62
63#[turbo_tasks::value_impl]
64impl ModuleReference for AmdDefineAssetReference {
65 #[turbo_tasks::function]
66 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
67 cjs_resolve(
68 *self.origin,
69 *self.request,
70 Some(self.issue_source),
71 self.in_try,
72 )
73 }
74}
75
76#[turbo_tasks::value_impl]
77impl ValueToString for AmdDefineAssetReference {
78 #[turbo_tasks::function]
79 async fn to_string(&self) -> Result<Vc<RcStr>> {
80 Ok(Vc::cell(
81 format!("AMD define dependency {}", self.request.to_string().await?,).into(),
82 ))
83 }
84}
85
86#[turbo_tasks::value_impl]
87impl ChunkableModuleReference for AmdDefineAssetReference {}
88
89#[derive(
90 ValueDebugFormat,
91 Debug,
92 PartialEq,
93 Eq,
94 Serialize,
95 Deserialize,
96 TraceRawVcs,
97 Clone,
98 NonLocalValue,
99)]
100pub enum AmdDefineDependencyElement {
101 Request {
102 request: ResolvedVc<Request>,
103 request_str: String,
104 },
105 Exports,
106 Module,
107 Require,
108}
109
110#[derive(
111 ValueDebugFormat,
112 Debug,
113 PartialEq,
114 Eq,
115 Serialize,
116 Deserialize,
117 TraceRawVcs,
118 Copy,
119 Clone,
120 NonLocalValue,
121)]
122pub enum AmdDefineFactoryType {
123 Unknown,
124 Function,
125 Value,
126}
127
128#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
129pub struct AmdDefineWithDependenciesCodeGen {
130 dependencies_requests: Vec<AmdDefineDependencyElement>,
131 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
132 path: AstPath,
133 factory_type: AmdDefineFactoryType,
134 issue_source: IssueSource,
135 in_try: bool,
136}
137
138impl AmdDefineWithDependenciesCodeGen {
139 pub fn new(
140 dependencies_requests: Vec<AmdDefineDependencyElement>,
141 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
142 path: AstPath,
143 factory_type: AmdDefineFactoryType,
144 issue_source: IssueSource,
145 in_try: bool,
146 ) -> Self {
147 AmdDefineWithDependenciesCodeGen {
148 dependencies_requests,
149 origin,
150 path,
151 factory_type,
152 issue_source,
153 in_try,
154 }
155 }
156
157 pub async fn code_generation(
158 &self,
159 chunking_context: Vc<Box<dyn ChunkingContext>>,
160 ) -> Result<CodeGeneration> {
161 let mut visitors = Vec::new();
162
163 let resolved_elements = self
164 .dependencies_requests
165 .iter()
166 .map(|element| async move {
167 Ok(match element {
168 AmdDefineDependencyElement::Request {
169 request,
170 request_str,
171 } => ResolvedElement::PatternMapping {
172 pattern_mapping: PatternMapping::resolve_request(
173 **request,
174 *self.origin,
175 Vc::upcast(chunking_context),
176 cjs_resolve(
177 *self.origin,
178 **request,
179 Some(self.issue_source),
180 self.in_try,
181 ),
182 ResolveType::ChunkItem,
183 )
184 .await?,
185 request_str: request_str.to_string(),
186 },
187 AmdDefineDependencyElement::Exports => {
188 ResolvedElement::Expr(quote!("exports" as Expr))
189 }
190 AmdDefineDependencyElement::Module => {
191 ResolvedElement::Expr(quote!("module" as Expr))
192 }
193 AmdDefineDependencyElement::Require => {
194 ResolvedElement::Expr(TURBOPACK_REQUIRE.into())
195 }
196 })
197 })
198 .try_join()
199 .await?;
200
201 let factory_type = self.factory_type;
202
203 visitors.push(create_visitor!(
204 exact,
205 self.path,
206 visit_mut_call_expr,
207 |call_expr: &mut CallExpr| {
208 transform_amd_factory(call_expr, &resolved_elements, factory_type)
209 }
210 ));
211
212 Ok(CodeGeneration::visitors(visitors))
213 }
214}
215
216impl From<AmdDefineWithDependenciesCodeGen> for CodeGen {
217 fn from(val: AmdDefineWithDependenciesCodeGen) -> Self {
218 CodeGen::AmdDefineWithDependenciesCodeGen(Box::new(val))
219 }
220}
221
222enum ResolvedElement {
223 PatternMapping {
224 pattern_mapping: ReadRef<PatternMapping>,
225 request_str: String,
226 },
227 Expr(Expr),
228}
229
230fn transform_amd_factory(
240 call_expr: &mut CallExpr,
241 resolved_elements: &[ResolvedElement],
242 factory_type: AmdDefineFactoryType,
243) {
244 let CallExpr { args, callee, .. } = call_expr;
245 let Some(factory) = take(args).pop().map(|e| e.expr) else {
246 return;
247 };
248
249 let deps = resolved_elements
250 .iter()
251 .map(|element| match element {
252 ResolvedElement::PatternMapping {
253 pattern_mapping: pm,
254 request_str: request,
255 } => {
256 let key_expr = Expr::Lit(Lit::Str(request.as_str().into()));
257 pm.create_require(key_expr)
258 }
259 ResolvedElement::Expr(expr) => expr.clone(),
260 })
261 .map(ExprOrSpread::from)
262 .collect();
263
264 match factory_type {
265 AmdDefineFactoryType::Unknown => {
266 let f = private_ident!("f");
269 let call_f = Expr::Call(CallExpr {
270 args: deps,
271 callee: Callee::Expr(Box::new(Expr::Ident(f.clone()))),
272 span: DUMMY_SP,
273 ..Default::default()
274 });
275 *callee = Callee::Expr(quote_expr!(
276 "($f1, r = typeof $f2 !== \"function\" ? $f3 : $call_f) => r !== undefined && \
277 $turbopack_export_value(r)",
278 f1 = f.clone(),
279 f2 = f.clone(),
280 f3 = f,
281 call_f: Expr = call_f,
282 turbopack_export_value: Expr = TURBOPACK_EXPORT_VALUE.into()
283 ));
284 args.push(ExprOrSpread {
285 expr: factory,
286 spread: None,
287 });
288 }
289 AmdDefineFactoryType::Function => {
290 *callee = Callee::Expr(quote_expr!(
292 "r => r !== undefined && $turbopack_export_value(r)",
293 turbopack_export_value: Expr = TURBOPACK_EXPORT_VALUE.into()
294 ));
295 args.push(ExprOrSpread {
296 expr: Box::new(Expr::Call(CallExpr {
297 args: deps,
298 callee: Callee::Expr(factory),
299 ..Default::default()
300 })),
301 spread: None,
302 });
303 }
304 AmdDefineFactoryType::Value => {
305 *callee = Callee::Expr(Box::new(TURBOPACK_EXPORT_VALUE.into()));
307 args.push(ExprOrSpread {
308 expr: factory,
309 spread: None,
310 });
311 }
312 }
313}