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 fn source(&self) -> Option<IssueSource> {
85 Some(self.issue_source)
86 }
87}
88
89#[derive(
90 ValueDebugFormat, Debug, PartialEq, Eq, TraceRawVcs, Clone, NonLocalValue, Hash, Encode, Decode,
91)]
92pub enum AmdDefineDependencyElement {
93 Request {
94 request: ResolvedVc<Request>,
95 request_str: String,
96 },
97 Exports,
98 Module,
99 Require,
100}
101
102#[derive(
103 ValueDebugFormat,
104 Debug,
105 PartialEq,
106 Eq,
107 TraceRawVcs,
108 Copy,
109 Clone,
110 NonLocalValue,
111 Hash,
112 Encode,
113 Decode,
114)]
115pub enum AmdDefineFactoryType {
116 Unknown,
117 Function,
118 Value,
119}
120
121#[derive(
122 PartialEq, Eq, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, Encode, Decode,
123)]
124pub struct AmdDefineWithDependenciesCodeGen {
125 dependencies_requests: Vec<AmdDefineDependencyElement>,
126 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
127 path: AstPath,
128 factory_type: AmdDefineFactoryType,
129 issue_source: IssueSource,
130 error_mode: ResolveErrorMode,
131}
132
133impl AmdDefineWithDependenciesCodeGen {
134 pub fn new(
135 dependencies_requests: Vec<AmdDefineDependencyElement>,
136 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
137 path: AstPath,
138 factory_type: AmdDefineFactoryType,
139 issue_source: IssueSource,
140 error_mode: ResolveErrorMode,
141 ) -> Self {
142 AmdDefineWithDependenciesCodeGen {
143 dependencies_requests,
144 origin,
145 path,
146 factory_type,
147 issue_source,
148 error_mode,
149 }
150 }
151
152 pub async fn code_generation(
153 &self,
154 chunking_context: Vc<Box<dyn ChunkingContext>>,
155 ) -> Result<CodeGeneration> {
156 let mut visitors = Vec::new();
157
158 let resolved_elements = self
159 .dependencies_requests
160 .iter()
161 .map(|element| async move {
162 Ok(match element {
163 AmdDefineDependencyElement::Request {
164 request,
165 request_str,
166 } => ResolvedElement::PatternMapping {
167 pattern_mapping: PatternMapping::resolve_request(
168 **request,
169 *self.origin,
170 chunking_context,
171 cjs_resolve(
172 *self.origin,
173 **request,
174 CommonJsReferenceSubType::Undefined,
175 Some(self.issue_source),
176 self.error_mode,
177 ),
178 ResolveType::ChunkItem,
179 )
180 .await?,
181 request_str: request_str.to_string(),
182 },
183 AmdDefineDependencyElement::Exports => {
184 ResolvedElement::Expr(quote!("exports" as Expr))
185 }
186 AmdDefineDependencyElement::Module => {
187 ResolvedElement::Expr(quote!("module" as Expr))
188 }
189 AmdDefineDependencyElement::Require => {
190 ResolvedElement::Expr(TURBOPACK_REQUIRE.into())
191 }
192 })
193 })
194 .try_join()
195 .await?;
196
197 let factory_type = self.factory_type;
198
199 visitors.push(create_visitor!(
200 exact,
201 self.path,
202 visit_mut_call_expr,
203 |call_expr: &mut CallExpr| {
204 transform_amd_factory(call_expr, &resolved_elements, factory_type)
205 }
206 ));
207
208 Ok(CodeGeneration::visitors(visitors))
209 }
210}
211
212impl From<AmdDefineWithDependenciesCodeGen> for CodeGen {
213 fn from(val: AmdDefineWithDependenciesCodeGen) -> Self {
214 CodeGen::AmdDefineWithDependenciesCodeGen(Box::new(val))
215 }
216}
217
218enum ResolvedElement {
219 PatternMapping {
220 pattern_mapping: ReadRef<PatternMapping>,
221 request_str: String,
222 },
223 Expr(Expr),
224}
225
226fn transform_amd_factory(
236 call_expr: &mut CallExpr,
237 resolved_elements: &[ResolvedElement],
238 factory_type: AmdDefineFactoryType,
239) {
240 let CallExpr { args, callee, .. } = call_expr;
241 let Some(factory) = take(args).pop().map(|e| e.expr) else {
242 return;
243 };
244
245 let deps = resolved_elements
246 .iter()
247 .map(|element| match element {
248 ResolvedElement::PatternMapping {
249 pattern_mapping: pm,
250 request_str: request,
251 } => {
252 let key_expr = Expr::Lit(Lit::Str(request.as_str().into()));
253 pm.create_require(key_expr)
254 }
255 ResolvedElement::Expr(expr) => expr.clone(),
256 })
257 .map(ExprOrSpread::from)
258 .collect();
259
260 match factory_type {
261 AmdDefineFactoryType::Unknown => {
262 let f = private_ident!("f");
265 let call_f = Expr::Call(CallExpr {
266 args: deps,
267 callee: Callee::Expr(Box::new(Expr::Ident(f.clone()))),
268 span: DUMMY_SP,
269 ..Default::default()
270 });
271 *callee = Callee::Expr(quote_expr!(
272 "($f1, r = typeof $f2 !== \"function\" ? $f3 : $call_f) => r !== undefined && \
273 $turbopack_export_value(r)",
274 f1 = f.clone(),
275 f2 = f.clone(),
276 f3 = f,
277 call_f: Expr = call_f,
278 turbopack_export_value: Expr = TURBOPACK_EXPORT_VALUE.into()
279 ));
280 args.push(ExprOrSpread {
281 expr: factory,
282 spread: None,
283 });
284 }
285 AmdDefineFactoryType::Function => {
286 *callee = Callee::Expr(quote_expr!(
288 "r => r !== undefined && $turbopack_export_value(r)",
289 turbopack_export_value: Expr = TURBOPACK_EXPORT_VALUE.into()
290 ));
291 args.push(ExprOrSpread {
292 expr: Box::new(Expr::Call(CallExpr {
293 args: deps,
294 callee: Callee::Expr(factory),
295 ..Default::default()
296 })),
297 spread: None,
298 });
299 }
300 AmdDefineFactoryType::Value => {
301 *callee = Callee::Expr(Box::new(TURBOPACK_EXPORT_VALUE.into()));
303 args.push(ExprOrSpread {
304 expr: factory,
305 spread: None,
306 });
307 }
308 }
309}