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