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_tasks::{
9 NonLocalValue, ResolvedVc, ValueToString, Vc, debug::ValueDebugFormat, trace::TraceRawVcs,
10};
11use turbopack_core::{
12 chunk::{ChunkingContext, ChunkingType},
13 issue::IssueSource,
14 module::Module,
15 reference::ModuleReference,
16 reference_type::CommonJsReferenceSubType,
17 resolve::{ModuleResolveResult, ResolveErrorMode, 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 util::SpecifiedChunkingType,
28 },
29 runtime_functions::TURBOPACK_CACHE,
30};
31
32#[turbo_tasks::value]
34#[derive(Hash, Debug, ValueToString)]
35#[value_to_string("generic commonjs {request}")]
36pub struct CjsAssetReference {
37 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
38 pub request: ResolvedVc<Request>,
39 pub issue_source: IssueSource,
40 pub error_mode: ResolveErrorMode,
41}
42
43#[turbo_tasks::value_impl]
44impl CjsAssetReference {
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 ) -> Result<Vc<Self>> {
52 Ok(Self::cell(CjsAssetReference {
53 origin,
54 request,
55 issue_source,
56 error_mode,
57 }))
58 }
59}
60
61#[turbo_tasks::value_impl]
62impl ModuleReference for CjsAssetReference {
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#[turbo_tasks::value]
87#[derive(Hash, Debug, ValueToString)]
88#[value_to_string("require {request}")]
89pub struct CjsRequireAssetReference {
90 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
91 request: ResolvedVc<Request>,
92 issue_source: IssueSource,
93 error_mode: ResolveErrorMode,
94 chunking_type_attribute: Option<SpecifiedChunkingType>,
95 resolve_override: Option<ResolvedVc<Box<dyn Module>>>,
96}
97
98impl CjsRequireAssetReference {
99 pub fn new(
100 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
101 request: ResolvedVc<Request>,
102 issue_source: IssueSource,
103 error_mode: ResolveErrorMode,
104 chunking_type_attribute: Option<SpecifiedChunkingType>,
105 resolve_override: Option<ResolvedVc<Box<dyn Module>>>,
106 ) -> Self {
107 CjsRequireAssetReference {
108 origin,
109 request,
110 issue_source,
111 error_mode,
112 chunking_type_attribute,
113 resolve_override,
114 }
115 }
116}
117
118#[turbo_tasks::value_impl]
119impl ModuleReference for CjsRequireAssetReference {
120 #[turbo_tasks::function]
121 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
122 if let Some(resolved) = &self.resolve_override {
123 return *ModuleResolveResult::module(*resolved);
124 }
125
126 cjs_resolve(
127 *self.origin,
128 *self.request,
129 CommonJsReferenceSubType::Undefined,
130 Some(self.issue_source),
131 self.error_mode,
132 )
133 }
134
135 fn chunking_type(&self) -> Option<ChunkingType> {
136 self.chunking_type_attribute.map_or_else(
137 || {
138 Some(ChunkingType::Parallel {
139 inherit_async: false,
140 hoisted: false,
141 })
142 },
143 |c| c.as_chunking_type(false, false),
144 )
145 }
146
147 fn source(&self) -> Option<IssueSource> {
148 Some(self.issue_source)
149 }
150}
151
152impl IntoCodeGenReference for CjsRequireAssetReference {
153 fn into_code_gen_reference(
154 self,
155 path: AstPath,
156 ) -> (ResolvedVc<Box<dyn ModuleReference>>, CodeGen) {
157 let reference = self.resolved_cell();
158 (
159 ResolvedVc::upcast(reference),
160 CodeGen::CjsRequireAssetReferenceCodeGen(CjsRequireAssetReferenceCodeGen {
161 reference,
162 path,
163 }),
164 )
165 }
166}
167
168#[derive(
169 PartialEq, Eq, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, Encode, Decode,
170)]
171pub struct CjsRequireAssetReferenceCodeGen {
172 reference: ResolvedVc<CjsRequireAssetReference>,
173 path: AstPath,
174}
175
176impl CjsRequireAssetReferenceCodeGen {
177 pub async fn code_generation(
178 &self,
179 chunking_context: Vc<Box<dyn ChunkingContext>>,
180 ) -> Result<CodeGeneration> {
181 let reference = self.reference.await?;
182
183 let pm = PatternMapping::resolve_request(
184 *reference.request,
185 *reference.origin,
186 chunking_context,
187 self.reference.resolve_reference(),
188 ResolveType::ChunkItem,
189 )
190 .await?;
191 let mut visitors = Vec::new();
192
193 visitors.push(create_visitor!(
194 self.path,
195 visit_mut_expr,
196 |expr: &mut Expr| {
197 let old_expr = expr.take();
198 let message = if let Expr::Call(CallExpr { args, .. }) = old_expr {
199 match args.into_iter().next() {
200 Some(ExprOrSpread {
201 spread: None,
202 expr: key_expr,
203 }) => {
204 *expr = pm.create_require(*key_expr);
205 return;
206 }
207 Some(ExprOrSpread {
208 spread: Some(_),
209 expr: _,
210 }) => "spread operator is not analyze-able in require() expressions.",
211 _ => "require() expressions require at least 1 argument",
212 }
213 } else {
214 "visitor must be executed on a CallExpr"
215 };
216 *expr = quote!(
217 "(() => { throw new Error($message); })()" as Expr,
218 message: Expr = Expr::Lit(Lit::Str(message.into()))
219 );
220 }
221 ));
222
223 Ok(CodeGeneration::visitors(visitors))
224 }
225}
226
227#[turbo_tasks::value]
228#[derive(Hash, Debug, ValueToString)]
229#[value_to_string("require.resolve {request}")]
230pub struct CjsRequireResolveAssetReference {
231 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
232 request: ResolvedVc<Request>,
233 issue_source: IssueSource,
234 error_mode: ResolveErrorMode,
235 chunking_type_attribute: Option<SpecifiedChunkingType>,
236 resolve_override: Option<ResolvedVc<Box<dyn Module>>>,
237}
238
239impl CjsRequireResolveAssetReference {
240 pub fn new(
241 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
242 request: ResolvedVc<Request>,
243 issue_source: IssueSource,
244 error_mode: ResolveErrorMode,
245 chunking_type_attribute: Option<SpecifiedChunkingType>,
246 resolve_override: Option<ResolvedVc<Box<dyn Module>>>,
247 ) -> Self {
248 CjsRequireResolveAssetReference {
249 origin,
250 request,
251 issue_source,
252 error_mode,
253 chunking_type_attribute,
254 resolve_override,
255 }
256 }
257}
258
259#[turbo_tasks::value_impl]
260impl ModuleReference for CjsRequireResolveAssetReference {
261 #[turbo_tasks::function]
262 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
263 if let Some(resolved) = &self.resolve_override {
264 return *ModuleResolveResult::module(*resolved);
265 }
266
267 cjs_resolve(
268 *self.origin,
269 *self.request,
270 CommonJsReferenceSubType::Undefined,
271 Some(self.issue_source),
272 self.error_mode,
273 )
274 }
275
276 fn chunking_type(&self) -> Option<ChunkingType> {
277 self.chunking_type_attribute.map_or_else(
278 || {
279 Some(ChunkingType::Parallel {
280 inherit_async: false,
281 hoisted: false,
282 })
283 },
284 |c| c.as_chunking_type(false, false),
285 )
286 }
287
288 fn source(&self) -> Option<IssueSource> {
289 Some(self.issue_source)
290 }
291}
292
293impl IntoCodeGenReference for CjsRequireResolveAssetReference {
294 fn into_code_gen_reference(
295 self,
296 path: AstPath,
297 ) -> (ResolvedVc<Box<dyn ModuleReference>>, CodeGen) {
298 let reference = self.resolved_cell();
299 (
300 ResolvedVc::upcast(reference),
301 CodeGen::CjsRequireResolveAssetReferenceCodeGen(
302 CjsRequireResolveAssetReferenceCodeGen { reference, path },
303 ),
304 )
305 }
306}
307
308#[derive(
309 PartialEq, Eq, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, Encode, Decode,
310)]
311pub struct CjsRequireResolveAssetReferenceCodeGen {
312 reference: ResolvedVc<CjsRequireResolveAssetReference>,
313 path: AstPath,
314}
315
316impl CjsRequireResolveAssetReferenceCodeGen {
317 pub async fn code_generation(
318 &self,
319 chunking_context: Vc<Box<dyn ChunkingContext>>,
320 ) -> Result<CodeGeneration> {
321 let reference = self.reference.await?;
322
323 let pm = PatternMapping::resolve_request(
324 *reference.request,
325 *reference.origin,
326 chunking_context,
327 self.reference.resolve_reference(),
328 ResolveType::ChunkItem,
329 )
330 .await?;
331 let mut visitors = Vec::new();
332
333 visitors.push(create_visitor!(
335 self.path,
336 visit_mut_expr,
337 |expr: &mut Expr| {
338 if let Expr::Call(call_expr) = expr {
339 let args = std::mem::take(&mut call_expr.args);
340 *expr = match args.into_iter().next() {
341 Some(ExprOrSpread { expr, spread: None }) => pm.create_id(*expr),
342 other => {
343 let message = match other {
344 Some(ExprOrSpread {
346 spread: Some(_),
347 expr: _,
348 }) => {
349 "spread operator is not analyze-able in require() expressions."
350 }
351 _ => "require() expressions require at least 1 argument",
352 };
353 quote!(
354 "(() => { throw new Error($message); })()" as Expr,
355 message: Expr = Expr::Lit(Lit::Str(message.into()))
356 )
357 }
358 };
359 }
360 }
364 ));
365
366 Ok(CodeGeneration::visitors(visitors))
367 }
368}
369
370#[derive(
371 PartialEq, Eq, TraceRawVcs, ValueDebugFormat, NonLocalValue, Debug, Hash, Encode, Decode,
372)]
373pub struct CjsRequireCacheAccess {
374 pub path: AstPath,
375}
376impl CjsRequireCacheAccess {
377 pub fn new(path: AstPath) -> Self {
378 CjsRequireCacheAccess { path }
379 }
380
381 pub async fn code_generation(
382 &self,
383 _chunking_context: Vc<Box<dyn ChunkingContext>>,
384 ) -> Result<CodeGeneration> {
385 let mut visitors = Vec::new();
386
387 visitors.push(create_visitor!(
388 self.path,
389 visit_mut_expr,
390 |expr: &mut Expr| {
391 if let Expr::Member(_) = expr {
392 *expr = TURBOPACK_CACHE.into();
393 } else {
394 unreachable!("`CjsRequireCacheAccess` is only created from `MemberExpr`");
395 }
396 }
397 ));
398
399 Ok(CodeGeneration::visitors(visitors))
400 }
401}
402
403impl From<CjsRequireCacheAccess> for CodeGen {
404 fn from(val: CjsRequireCacheAccess) -> Self {
405 CodeGen::CjsRequireCacheAccess(val)
406 }
407}