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 module_graph::ModuleGraph,
16 reference::ModuleReference,
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 async 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 Some(self.issue_source.clone()),
66 self.in_try,
67 )
68 }
69}
70
71#[turbo_tasks::value_impl]
72impl ValueToString for CjsAssetReference {
73 #[turbo_tasks::function]
74 async fn to_string(&self) -> Result<Vc<RcStr>> {
75 Ok(Vc::cell(
76 format!("generic commonjs {}", self.request.to_string().await?,).into(),
77 ))
78 }
79}
80
81#[turbo_tasks::value_impl]
82impl ChunkableModuleReference for CjsAssetReference {}
83
84#[turbo_tasks::value]
85#[derive(Hash, Debug)]
86pub struct CjsRequireAssetReference {
87 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
88 pub request: ResolvedVc<Request>,
89 pub issue_source: IssueSource,
90 pub in_try: bool,
91}
92
93impl CjsRequireAssetReference {
94 pub fn new(
95 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
96 request: ResolvedVc<Request>,
97 issue_source: IssueSource,
98 in_try: bool,
99 ) -> Self {
100 CjsRequireAssetReference {
101 origin,
102 request,
103 issue_source,
104 in_try,
105 }
106 }
107}
108
109#[turbo_tasks::value_impl]
110impl ModuleReference for CjsRequireAssetReference {
111 #[turbo_tasks::function]
112 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
113 cjs_resolve(
114 *self.origin,
115 *self.request,
116 Some(self.issue_source.clone()),
117 self.in_try,
118 )
119 }
120}
121
122#[turbo_tasks::value_impl]
123impl ValueToString for CjsRequireAssetReference {
124 #[turbo_tasks::function]
125 async fn to_string(&self) -> Result<Vc<RcStr>> {
126 Ok(Vc::cell(
127 format!("require {}", self.request.to_string().await?,).into(),
128 ))
129 }
130}
131
132#[turbo_tasks::value_impl]
133impl ChunkableModuleReference for CjsRequireAssetReference {}
134
135impl IntoCodeGenReference for CjsRequireAssetReference {
136 fn into_code_gen_reference(
137 self,
138 path: AstPath,
139 ) -> (ResolvedVc<Box<dyn ModuleReference>>, CodeGen) {
140 let reference = self.resolved_cell();
141 (
142 ResolvedVc::upcast(reference),
143 CodeGen::CjsRequireAssetReferenceCodeGen(CjsRequireAssetReferenceCodeGen {
144 reference,
145 path,
146 }),
147 )
148 }
149}
150
151#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
152pub struct CjsRequireAssetReferenceCodeGen {
153 reference: ResolvedVc<CjsRequireAssetReference>,
154 path: AstPath,
155}
156
157impl CjsRequireAssetReferenceCodeGen {
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 reference = self.reference.await?;
164
165 let pm = PatternMapping::resolve_request(
166 *reference.request,
167 *reference.origin,
168 Vc::upcast(chunking_context),
169 self.reference.resolve_reference(),
170 ResolveType::ChunkItem,
171 )
172 .await?;
173 let mut visitors = Vec::new();
174
175 visitors.push(create_visitor!(self.path, visit_mut_expr(expr: &mut Expr) {
176 let old_expr = expr.take();
177 let message = if let Expr::Call(CallExpr { args, ..}) = old_expr {
178 match args.into_iter().next() {
179 Some(ExprOrSpread { spread: None, expr: key_expr }) => {
180 *expr = pm.create_require(*key_expr);
181 return;
182 }
183 Some(ExprOrSpread { spread: Some(_), expr: _ }) => {
184 "spread operator is not analyse-able in require() expressions."
185 }
186 _ => {
187 "require() expressions require at least 1 argument"
188 }
189 }
190 } else {
191 "visitor must be executed on a CallExpr"
192 };
193 *expr = quote!(
194 "(() => { throw new Error($message); })()" as Expr,
195 message: Expr = Expr::Lit(Lit::Str(message.into()))
196 );
197 }));
198
199 Ok(CodeGeneration::visitors(visitors))
200 }
201}
202
203#[turbo_tasks::value]
204#[derive(Hash, Debug)]
205pub struct CjsRequireResolveAssetReference {
206 pub origin: ResolvedVc<Box<dyn ResolveOrigin>>,
207 pub request: ResolvedVc<Request>,
208 pub issue_source: IssueSource,
209 pub in_try: bool,
210}
211
212impl CjsRequireResolveAssetReference {
213 pub fn new(
214 origin: ResolvedVc<Box<dyn ResolveOrigin>>,
215 request: ResolvedVc<Request>,
216 issue_source: IssueSource,
217 in_try: bool,
218 ) -> Self {
219 CjsRequireResolveAssetReference {
220 origin,
221 request,
222 issue_source,
223 in_try,
224 }
225 }
226}
227
228#[turbo_tasks::value_impl]
229impl ModuleReference for CjsRequireResolveAssetReference {
230 #[turbo_tasks::function]
231 fn resolve_reference(&self) -> Vc<ModuleResolveResult> {
232 cjs_resolve(
233 *self.origin,
234 *self.request,
235 Some(self.issue_source.clone()),
236 self.in_try,
237 )
238 }
239}
240
241#[turbo_tasks::value_impl]
242impl ValueToString for CjsRequireResolveAssetReference {
243 #[turbo_tasks::function]
244 async fn to_string(&self) -> Result<Vc<RcStr>> {
245 Ok(Vc::cell(
246 format!("require.resolve {}", self.request.to_string().await?,).into(),
247 ))
248 }
249}
250
251#[turbo_tasks::value_impl]
252impl ChunkableModuleReference for CjsRequireResolveAssetReference {}
253
254impl IntoCodeGenReference for CjsRequireResolveAssetReference {
255 fn into_code_gen_reference(
256 self,
257 path: AstPath,
258 ) -> (ResolvedVc<Box<dyn ModuleReference>>, CodeGen) {
259 let reference = self.resolved_cell();
260 (
261 ResolvedVc::upcast(reference),
262 CodeGen::CjsRequireResolveAssetReferenceCodeGen(
263 CjsRequireResolveAssetReferenceCodeGen { reference, path },
264 ),
265 )
266 }
267}
268
269#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
270pub struct CjsRequireResolveAssetReferenceCodeGen {
271 reference: ResolvedVc<CjsRequireResolveAssetReference>,
272 path: AstPath,
273}
274
275impl CjsRequireResolveAssetReferenceCodeGen {
276 pub async fn code_generation(
277 &self,
278 _module_graph: Vc<ModuleGraph>,
279 chunking_context: Vc<Box<dyn ChunkingContext>>,
280 ) -> Result<CodeGeneration> {
281 let reference = self.reference.await?;
282
283 let pm = PatternMapping::resolve_request(
284 *reference.request,
285 *reference.origin,
286 Vc::upcast(chunking_context),
287 self.reference.resolve_reference(),
288 ResolveType::ChunkItem,
289 )
290 .await?;
291 let mut visitors = Vec::new();
292
293 visitors.push(create_visitor!(self.path, visit_mut_expr(expr: &mut Expr) {
295 if let Expr::Call(call_expr) = expr {
296 let args = std::mem::take(&mut call_expr.args);
297 *expr = match args.into_iter().next() {
298 Some(ExprOrSpread { expr, spread: None }) => pm.create_id(*expr),
299 other => {
300 let message = match other {
301 Some(ExprOrSpread { spread: Some(_), expr: _ }) => {
303 "spread operator is not analyse-able in require() expressions."
304 }
305 _ => {
306 "require() expressions require at least 1 argument"
307 }
308 };
309 quote!(
310 "(() => { throw new Error($message); })()" as Expr,
311 message: Expr = Expr::Lit(Lit::Str(message.into()))
312 )
313 },
314 };
315 }
316 }));
320
321 Ok(CodeGeneration::visitors(visitors))
322 }
323}
324
325#[derive(PartialEq, Eq, Serialize, Deserialize, TraceRawVcs, ValueDebugFormat, NonLocalValue)]
326pub struct CjsRequireCacheAccess {
327 pub path: AstPath,
328}
329impl CjsRequireCacheAccess {
330 pub fn new(path: AstPath) -> Self {
331 CjsRequireCacheAccess { path }
332 }
333
334 pub async fn code_generation(
335 &self,
336 _module_graph: Vc<ModuleGraph>,
337 _chunking_context: Vc<Box<dyn ChunkingContext>>,
338 ) -> Result<CodeGeneration> {
339 let mut visitors = Vec::new();
340
341 visitors.push(create_visitor!(self.path, visit_mut_expr(expr: &mut Expr) {
342 if let Expr::Member(_) = expr {
343 *expr = TURBOPACK_CACHE.into();
344 } else {
345 unreachable!("`CjsRequireCacheAccess` is only created from `MemberExpr`");
346 }
347 }));
348
349 Ok(CodeGeneration::visitors(visitors))
350 }
351}
352
353impl From<CjsRequireCacheAccess> for CodeGen {
354 fn from(val: CjsRequireCacheAccess) -> Self {
355 CodeGen::CjsRequireCacheAccess(val)
356 }
357}