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