1use 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 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(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
154pub struct CjsRequireAssetReferenceCodeGen {
155 reference: ResolvedVc<CjsRequireAssetReference>,
156 path: AstPath,
157}
158
159impl CjsRequireAssetReferenceCodeGen {
160 pub async fn code_generation(
161 &self,
162 chunking_context: Vc<Box<dyn ChunkingContext>>,
163 ) -> Result<CodeGeneration> {
164 let reference = self.reference.await?;
165
166 let pm = PatternMapping::resolve_request(
167 *reference.request,
168 *reference.origin,
169 Vc::upcast(chunking_context),
170 self.reference.resolve_reference(),
171 ResolveType::ChunkItem,
172 )
173 .await?;
174 let mut visitors = Vec::new();
175
176 visitors.push(create_visitor!(
177 self.path,
178 visit_mut_expr,
179 |expr: &mut Expr| {
180 let old_expr = expr.take();
181 let message = if let Expr::Call(CallExpr { args, .. }) = old_expr {
182 match args.into_iter().next() {
183 Some(ExprOrSpread {
184 spread: None,
185 expr: key_expr,
186 }) => {
187 *expr = pm.create_require(*key_expr);
188 return;
189 }
190 Some(ExprOrSpread {
191 spread: Some(_),
192 expr: _,
193 }) => "spread operator is not analyse-able in require() expressions.",
194 _ => "require() expressions require at least 1 argument",
195 }
196 } else {
197 "visitor must be executed on a CallExpr"
198 };
199 *expr = quote!(
200 "(() => { throw new Error($message); })()" as Expr,
201 message: Expr = Expr::Lit(Lit::Str(message.into()))
202 );
203 }
204 ));
205
206 Ok(CodeGeneration::visitors(visitors))
207 }
208}
209
210#[turbo_tasks::value]
211#[derive(Hash, Debug)]
212pub struct CjsRequireResolveAssetReference {
213 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
214 pub request: ResolvedVc<Request>,
215 pub issue_source: IssueSource,
216 pub in_try: bool,
217}
218
219impl CjsRequireResolveAssetReference {
220 pub fn new(
221 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
222 request: ResolvedVc<Request>,
223 issue_source: IssueSource,
224 in_try: bool,
225 ) -> Self {
226 CjsRequireResolveAssetReference {
227 origin,
228 request,
229 issue_source,
230 in_try,
231 }
232 }
233}
234
235#[turbo_tasks::value_impl]
236impl ModuleReference for CjsRequireResolveAssetReference {
237 #[turbo_tasks::function]
238 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
239 cjs_resolve(
240 *self.origin,
241 *self.request,
242 CommonJsReferenceSubType::Undefined,
243 Some(self.issue_source),
244 self.in_try,
245 )
246 }
247}
248
249#[turbo_tasks::value_impl]
250impl ValueToString for CjsRequireResolveAssetReference {
251 #[turbo_tasks::function]
252 async fn to_string(&self) -> Result<Vc<RcStr>> {
253 Ok(Vc::cell(
254 format!("require.resolve {}", self.request.to_string().await?,).into(),
255 ))
256 }
257}
258
259#[turbo_tasks::value_impl]
260impl ChunkableModuleReference for CjsRequireResolveAssetReference {}
261
262impl IntoCodeGenReference for CjsRequireResolveAssetReference {
263 fn into_code_gen_reference(
264 self,
265 path: AstPath,
266 ) -> (ResolvedVc<Box<dyn ModuleReference>>, CodeGen) {
267 let reference = self.resolved_cell();
268 (
269 ResolvedVc::upcast(reference),
270 CodeGen::CjsRequireResolveAssetReferenceCodeGen(
271 CjsRequireResolveAssetReferenceCodeGen { reference, path },
272 ),
273 )
274 }
275}
276
277#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
278pub struct CjsRequireResolveAssetReferenceCodeGen {
279 reference: ResolvedVc<CjsRequireResolveAssetReference>,
280 path: AstPath,
281}
282
283impl CjsRequireResolveAssetReferenceCodeGen {
284 pub async fn code_generation(
285 &self,
286 chunking_context: Vc<Box<dyn ChunkingContext>>,
287 ) -> Result<CodeGeneration> {
288 let reference = self.reference.await?;
289
290 let pm = PatternMapping::resolve_request(
291 *reference.request,
292 *reference.origin,
293 Vc::upcast(chunking_context),
294 self.reference.resolve_reference(),
295 ResolveType::ChunkItem,
296 )
297 .await?;
298 let mut visitors = Vec::new();
299
300 visitors.push(create_visitor!(
302 self.path,
303 visit_mut_expr,
304 |expr: &mut Expr| {
305 if let Expr::Call(call_expr) = expr {
306 let args = std::mem::take(&mut call_expr.args);
307 *expr = match args.into_iter().next() {
308 Some(ExprOrSpread { expr, spread: None }) => pm.create_id(*expr),
309 other => {
310 let message = match other {
311 Some(ExprOrSpread {
313 spread: Some(_),
314 expr: _,
315 }) => {
316 "spread operator is not analyse-able in require() expressions."
317 }
318 _ => "require() expressions require at least 1 argument",
319 };
320 quote!(
321 "(() => { throw new Error($message); })()" as Expr,
322 message: Expr = Expr::Lit(Lit::Str(message.into()))
323 )
324 }
325 };
326 }
327 }
331 ));
332
333 Ok(CodeGeneration::visitors(visitors))
334 }
335}
336
337#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
338pub struct CjsRequireCacheAccess {
339 pub path: AstPath,
340}
341impl CjsRequireCacheAccess {
342 pub fn new(path: AstPath) -> Self {
343 CjsRequireCacheAccess { path }
344 }
345
346 pub async fn code_generation(
347 &self,
348 _chunking_context: Vc<Box<dyn ChunkingContext>>,
349 ) -> Result<CodeGeneration> {
350 let mut visitors = Vec::new();
351
352 visitors.push(create_visitor!(
353 self.path,
354 visit_mut_expr,
355 |expr: &mut Expr| {
356 if let Expr::Member(_) = expr {
357 *expr = TURBOPACK_CACHE.into();
358 } else {
359 unreachable!("`CjsRequireCacheAccess` is only created from `MemberExpr`");
360 }
361 }
362 ));
363
364 Ok(CodeGeneration::visitors(visitors))
365 }
366}
367
368impl From<CjsRequireCacheAccess> for CodeGen {
369 fn from(val: CjsRequireCacheAccess) -> Self {
370 CodeGen::CjsRequireCacheAccess(val)
371 }
372}