turbopack_ecmascript/references/
cjs.rs1use anyhow::Result;
2use serde::{Deserialize, Serialize};
3use swc_core::{
4 common::util::take::Take,
5 ecma::ast::{CallExpr, Expr, ExprOrSpread, Lit},
6 quote,
7};
8use turbo_rcstr::RcStr;
9use turbo_tasks::{
10 NonLocalValue, ResolvedVc, ValueToString, Vc, debug::ValueDebugFormat, trace::TraceRawVcs,
11};
12use turbopack_core::{
13 chunk::{ChunkableModuleReference, ChunkingContext},
14 issue::IssueSource,
15 reference::ModuleReference,
16 resolve::{ModuleResolveResult, origin::ResolveOrigin, parse::Request},
17};
18use turbopack_resolve::ecmascript::cjs_resolve;
19
20use crate::{
21 code_gen::{CodeGen, CodeGeneration, IntoCodeGenReference},
22 create_visitor,
23 references::{
24 AstPath,
25 pattern_mapping::{PatternMapping, ResolveType},
26 },
27 runtime_functions::TURBOPACK_CACHE,
28};
29
30#[turbo_tasks::value]
31#[derive(Hash, Debug)]
32pub struct CjsAssetReference {
33 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
34 pub request: ResolvedVc<Request>,
35 pub issue_source: IssueSource,
36 pub in_try: bool,
37}
38
39#[turbo_tasks::value_impl]
40impl CjsAssetReference {
41 #[turbo_tasks::function]
42 pub fn new(
43 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
44 request: ResolvedVc<Request>,
45 issue_source: IssueSource,
46 in_try: bool,
47 ) -> Result<Vc<Self>> {
48 Ok(Self::cell(CjsAssetReference {
49 origin,
50 request,
51 issue_source,
52 in_try,
53 }))
54 }
55}
56
57#[turbo_tasks::value_impl]
58impl ModuleReference for CjsAssetReference {
59 #[turbo_tasks::function]
60 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
61 cjs_resolve(
62 *self.origin,
63 *self.request,
64 Some(self.issue_source),
65 self.in_try,
66 )
67 }
68}
69
70#[turbo_tasks::value_impl]
71impl ValueToString for CjsAssetReference {
72 #[turbo_tasks::function]
73 async fn to_string(&self) -> Result<Vc<RcStr>> {
74 Ok(Vc::cell(
75 format!("generic commonjs {}", self.request.to_string().await?,).into(),
76 ))
77 }
78}
79
80#[turbo_tasks::value_impl]
81impl ChunkableModuleReference for CjsAssetReference {}
82
83#[turbo_tasks::value]
84#[derive(Hash, Debug)]
85pub struct CjsRequireAssetReference {
86 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
87 pub request: ResolvedVc<Request>,
88 pub issue_source: IssueSource,
89 pub in_try: bool,
90}
91
92impl CjsRequireAssetReference {
93 pub fn new(
94 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
95 request: ResolvedVc<Request>,
96 issue_source: IssueSource,
97 in_try: bool,
98 ) -> Self {
99 CjsRequireAssetReference {
100 origin,
101 request,
102 issue_source,
103 in_try,
104 }
105 }
106}
107
108#[turbo_tasks::value_impl]
109impl ModuleReference for CjsRequireAssetReference {
110 #[turbo_tasks::function]
111 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
112 cjs_resolve(
113 *self.origin,
114 *self.request,
115 Some(self.issue_source),
116 self.in_try,
117 )
118 }
119}
120
121#[turbo_tasks::value_impl]
122impl ValueToString for CjsRequireAssetReference {
123 #[turbo_tasks::function]
124 async fn to_string(&self) -> Result<Vc<RcStr>> {
125 Ok(Vc::cell(
126 format!("require {}", self.request.to_string().await?,).into(),
127 ))
128 }
129}
130
131#[turbo_tasks::value_impl]
132impl ChunkableModuleReference for CjsRequireAssetReference {}
133
134impl IntoCodeGenReference for CjsRequireAssetReference {
135 fn into_code_gen_reference(
136 self,
137 path: AstPath,
138 ) -> (ResolvedVc<Box<dyn ModuleReference>>, CodeGen) {
139 let reference = self.resolved_cell();
140 (
141 ResolvedVc::upcast(reference),
142 CodeGen::CjsRequireAssetReferenceCodeGen(CjsRequireAssetReferenceCodeGen {
143 reference,
144 path,
145 }),
146 )
147 }
148}
149
150#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
151pub struct CjsRequireAssetReferenceCodeGen {
152 reference: ResolvedVc<CjsRequireAssetReference>,
153 path: AstPath,
154}
155
156impl CjsRequireAssetReferenceCodeGen {
157 pub async fn code_generation(
158 &self,
159 chunking_context: Vc<Box<dyn ChunkingContext>>,
160 ) -> Result<CodeGeneration> {
161 let reference = self.reference.await?;
162
163 let pm = PatternMapping::resolve_request(
164 *reference.request,
165 *reference.origin,
166 Vc::upcast(chunking_context),
167 self.reference.resolve_reference(),
168 ResolveType::ChunkItem,
169 )
170 .await?;
171 let mut visitors = Vec::new();
172
173 visitors.push(create_visitor!(
174 self.path,
175 visit_mut_expr,
176 |expr: &mut Expr| {
177 let old_expr = expr.take();
178 let message = if let Expr::Call(CallExpr { args, .. }) = old_expr {
179 match args.into_iter().next() {
180 Some(ExprOrSpread {
181 spread: None,
182 expr: key_expr,
183 }) => {
184 *expr = pm.create_require(*key_expr);
185 return;
186 }
187 Some(ExprOrSpread {
188 spread: Some(_),
189 expr: _,
190 }) => "spread operator is not analyse-able in require() expressions.",
191 _ => "require() expressions require at least 1 argument",
192 }
193 } else {
194 "visitor must be executed on a CallExpr"
195 };
196 *expr = quote!(
197 "(() => { throw new Error($message); })()" as Expr,
198 message: Expr = Expr::Lit(Lit::Str(message.into()))
199 );
200 }
201 ));
202
203 Ok(CodeGeneration::visitors(visitors))
204 }
205}
206
207#[turbo_tasks::value]
208#[derive(Hash, Debug)]
209pub struct CjsRequireResolveAssetReference {
210 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
211 pub request: ResolvedVc<Request>,
212 pub issue_source: IssueSource,
213 pub in_try: bool,
214}
215
216impl CjsRequireResolveAssetReference {
217 pub fn new(
218 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
219 request: ResolvedVc<Request>,
220 issue_source: IssueSource,
221 in_try: bool,
222 ) -> Self {
223 CjsRequireResolveAssetReference {
224 origin,
225 request,
226 issue_source,
227 in_try,
228 }
229 }
230}
231
232#[turbo_tasks::value_impl]
233impl ModuleReference for CjsRequireResolveAssetReference {
234 #[turbo_tasks::function]
235 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
236 cjs_resolve(
237 *self.origin,
238 *self.request,
239 Some(self.issue_source),
240 self.in_try,
241 )
242 }
243}
244
245#[turbo_tasks::value_impl]
246impl ValueToString for CjsRequireResolveAssetReference {
247 #[turbo_tasks::function]
248 async fn to_string(&self) -> Result<Vc<RcStr>> {
249 Ok(Vc::cell(
250 format!("require.resolve {}", self.request.to_string().await?,).into(),
251 ))
252 }
253}
254
255#[turbo_tasks::value_impl]
256impl ChunkableModuleReference for CjsRequireResolveAssetReference {}
257
258impl IntoCodeGenReference for CjsRequireResolveAssetReference {
259 fn into_code_gen_reference(
260 self,
261 path: AstPath,
262 ) -> (ResolvedVc<Box<dyn ModuleReference>>, CodeGen) {
263 let reference = self.resolved_cell();
264 (
265 ResolvedVc::upcast(reference),
266 CodeGen::CjsRequireResolveAssetReferenceCodeGen(
267 CjsRequireResolveAssetReferenceCodeGen { reference, path },
268 ),
269 )
270 }
271}
272
273#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
274pub struct CjsRequireResolveAssetReferenceCodeGen {
275 reference: ResolvedVc<CjsRequireResolveAssetReference>,
276 path: AstPath,
277}
278
279impl CjsRequireResolveAssetReferenceCodeGen {
280 pub async fn code_generation(
281 &self,
282 chunking_context: Vc<Box<dyn ChunkingContext>>,
283 ) -> Result<CodeGeneration> {
284 let reference = self.reference.await?;
285
286 let pm = PatternMapping::resolve_request(
287 *reference.request,
288 *reference.origin,
289 Vc::upcast(chunking_context),
290 self.reference.resolve_reference(),
291 ResolveType::ChunkItem,
292 )
293 .await?;
294 let mut visitors = Vec::new();
295
296 visitors.push(create_visitor!(
298 self.path,
299 visit_mut_expr,
300 |expr: &mut Expr| {
301 if let Expr::Call(call_expr) = expr {
302 let args = std::mem::take(&mut call_expr.args);
303 *expr = match args.into_iter().next() {
304 Some(ExprOrSpread { expr, spread: None }) => pm.create_id(*expr),
305 other => {
306 let message = match other {
307 Some(ExprOrSpread {
309 spread: Some(_),
310 expr: _,
311 }) => {
312 "spread operator is not analyse-able in require() expressions."
313 }
314 _ => "require() expressions require at least 1 argument",
315 };
316 quote!(
317 "(() => { throw new Error($message); })()" as Expr,
318 message: Expr = Expr::Lit(Lit::Str(message.into()))
319 )
320 }
321 };
322 }
323 }
327 ));
328
329 Ok(CodeGeneration::visitors(visitors))
330 }
331}
332
333#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
334pub struct CjsRequireCacheAccess {
335 pub path: AstPath,
336}
337impl CjsRequireCacheAccess {
338 pub fn new(path: AstPath) -> Self {
339 CjsRequireCacheAccess { path }
340 }
341
342 pub async fn code_generation(
343 &self,
344 _chunking_context: Vc<Box<dyn ChunkingContext>>,
345 ) -> Result<CodeGeneration> {
346 let mut visitors = Vec::new();
347
348 visitors.push(create_visitor!(
349 self.path,
350 visit_mut_expr,
351 |expr: &mut Expr| {
352 if let Expr::Member(_) = expr {
353 *expr = TURBOPACK_CACHE.into();
354 } else {
355 unreachable!("`CjsRequireCacheAccess` is only created from `MemberExpr`");
356 }
357 }
358 ));
359
360 Ok(CodeGeneration::visitors(visitors))
361 }
362}
363
364impl From<CjsRequireCacheAccess> for CodeGen {
365 fn from(val: CjsRequireCacheAccess) -> Self {
366 CodeGen::CjsRequireCacheAccess(val)
367 }
368}