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