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