1use anyhow::Result;
2use bincode::{Decode, Encode};
3use swc_core::{
4 base::SwcComments,
5 ecma::{
6 ast::{
7 BlockStmt, CallExpr, Expr, FunctionBody, Lit, MemberExpr, ModuleDecl, ModuleItem, Pat,
8 Program, Prop, SimpleAssignTarget, Stmt, Str, SwitchCase,
9 },
10 visit::AstParentKind,
11 },
12};
13use turbo_rcstr::RcStr;
14use turbo_tasks::{NonLocalValue, ResolvedVc, Vc, debug::ValueDebugFormat, trace::TraceRawVcs};
15use turbopack_core::{chunk::ChunkingContext, reference::ModuleReference};
16
17use crate::{
18 ScopeHoistingContext,
19 chunk::{EcmascriptChunkPlaceable, EcmascriptExports},
20 references::{
21 AstPath,
22 amd::AmdDefineWithDependenciesCodeGen,
23 cjs::{
24 CjsExportsDropCodeGen, CjsRequireAssetReferenceCodeGen, CjsRequireCacheAccess,
25 CjsRequireResolveAssetReferenceCodeGen,
26 },
27 constant_condition::ConstantConditionCodeGen,
28 constant_value::ConstantValueCodeGen,
29 dynamic_expression::DynamicExpression,
30 emit_collect::CollectReferenceCodeGen,
31 esm::{
32 EsmBinding, EsmModuleItem, ImportMetaBinding, ImportMetaRef,
33 dynamic::EsmAsyncAssetReferenceCodeGen, module_id::EsmModuleIdAssetReferenceCodeGen,
34 url::UrlAssetReferenceCodeGen,
35 },
36 exports_info::{ExportsInfoBinding, ExportsInfoRef},
37 hot_module::ModuleHotReferenceCodeGen,
38 ident::IdentReplacement,
39 import_meta_glob::ImportMetaGlobAssetReferenceCodeGen,
40 member::MemberReplacement,
41 removal::RemovalCodeGen,
42 require_context::RequireContextAssetReferenceCodeGen,
43 service_worker::ServiceWorkerAssetReferenceCodeGen,
44 worker::{WorkerAssetReferenceCodeGen, WorkerGlobalsReplacementCodeGen},
45 },
46};
47
48#[derive(Default)]
49pub struct CodeGeneration {
50 pub visitors: Vec<(Vec<AstParentKind>, Box<dyn AstModifier>)>,
52 pub hoisted_stmts: Vec<CodeGenerationHoistedStmt>,
53 pub early_hoisted_stmts: Vec<CodeGenerationHoistedStmt>,
54 pub late_stmts: Vec<CodeGenerationHoistedStmt>,
55 pub early_late_stmts: Vec<CodeGenerationHoistedStmt>,
56 pub comments: Option<SwcComments>,
57}
58
59impl CodeGeneration {
60 pub fn empty() -> Self {
61 CodeGeneration {
62 ..Default::default()
63 }
64 }
65
66 pub fn new(
67 visitors: Vec<(Vec<AstParentKind>, Box<dyn AstModifier>)>,
68 hoisted_stmts: Vec<CodeGenerationHoistedStmt>,
69 early_hoisted_stmts: Vec<CodeGenerationHoistedStmt>,
70 late_stmts: Vec<CodeGenerationHoistedStmt>,
71 early_late_stmts: Vec<CodeGenerationHoistedStmt>,
72 ) -> Self {
73 CodeGeneration {
74 visitors,
75 hoisted_stmts,
76 early_hoisted_stmts,
77 late_stmts,
78 early_late_stmts,
79 ..Default::default()
80 }
81 }
82
83 pub fn visitors_with_comments(
84 visitors: Vec<(Vec<AstParentKind>, Box<dyn AstModifier>)>,
85 comments: SwcComments,
86 ) -> Self {
87 CodeGeneration {
88 visitors,
89 comments: Some(comments),
90 ..Default::default()
91 }
92 }
93
94 pub fn visitors(visitors: Vec<(Vec<AstParentKind>, Box<dyn AstModifier>)>) -> Self {
95 CodeGeneration {
96 visitors,
97 ..Default::default()
98 }
99 }
100
101 pub fn hoisted_stmt(key: RcStr, stmt: Stmt) -> Self {
102 CodeGeneration {
103 hoisted_stmts: vec![CodeGenerationHoistedStmt::new(key, stmt)],
104 ..Default::default()
105 }
106 }
107
108 pub fn hoisted_stmts(hoisted_stmts: Vec<CodeGenerationHoistedStmt>) -> Self {
109 CodeGeneration {
110 hoisted_stmts,
111 ..Default::default()
112 }
113 }
114}
115
116#[derive(Clone)]
117pub struct CodeGenerationHoistedStmt {
118 pub key: RcStr,
119 pub stmt: Stmt,
120}
121
122impl CodeGenerationHoistedStmt {
123 pub fn new(key: RcStr, stmt: Stmt) -> Self {
124 CodeGenerationHoistedStmt { key, stmt }
125 }
126}
127
128macro_rules! method {
129 ($name:ident, $T:ty) => {
130 fn $name(&self, _node: &mut $T) {}
131 };
132}
133
134pub trait AstModifier: Send + Sync {
135 method!(visit_mut_prop, Prop);
136 method!(visit_mut_simple_assign_target, SimpleAssignTarget);
137 method!(visit_mut_expr, Expr);
138 method!(visit_mut_member_expr, MemberExpr);
139 method!(visit_mut_pat, Pat);
140 method!(visit_mut_stmt, Stmt);
141 method!(visit_mut_module_decl, ModuleDecl);
142 method!(visit_mut_module_item, ModuleItem);
143 method!(visit_mut_call_expr, CallExpr);
144 method!(visit_mut_lit, Lit);
145 method!(visit_mut_str, Str);
146 method!(visit_mut_block_stmt, BlockStmt);
147 method!(visit_mut_function_body, FunctionBody);
148 method!(visit_mut_switch_case, SwitchCase);
149 method!(visit_mut_program, Program);
150}
151
152pub trait ModifiableAst {
153 fn modify(&mut self, modifier: &dyn AstModifier);
154}
155
156macro_rules! impl_modify {
157 ($visit_mut_name:ident, $T:ty) => {
158 impl ModifiableAst for $T {
159 fn modify(&mut self, modifier: &dyn AstModifier) {
160 modifier.$visit_mut_name(self)
161 }
162 }
163 };
164}
165
166impl_modify!(visit_mut_prop, Prop);
167impl_modify!(visit_mut_simple_assign_target, SimpleAssignTarget);
168impl_modify!(visit_mut_expr, Expr);
169impl_modify!(visit_mut_member_expr, MemberExpr);
170impl_modify!(visit_mut_pat, Pat);
171impl_modify!(visit_mut_stmt, Stmt);
172impl_modify!(visit_mut_module_decl, ModuleDecl);
173impl_modify!(visit_mut_module_item, ModuleItem);
174impl_modify!(visit_mut_call_expr, CallExpr);
175impl_modify!(visit_mut_lit, Lit);
176impl_modify!(visit_mut_str, Str);
177impl_modify!(visit_mut_block_stmt, BlockStmt);
178impl_modify!(visit_mut_function_body, FunctionBody);
179impl_modify!(visit_mut_switch_case, SwitchCase);
180impl_modify!(visit_mut_program, Program);
181
182#[derive(
183 PartialEq, Eq, TraceRawVcs, ValueDebugFormat, NonLocalValue, Hash, Debug, Encode, Decode,
184)]
185pub enum CodeGen {
186 AmdDefineWithDependenciesCodeGen(Box<AmdDefineWithDependenciesCodeGen>),
188 CollectReferenceCodeGen(CollectReferenceCodeGen),
189 CjsRequireCacheAccess(CjsRequireCacheAccess),
190 ConstantConditionCodeGen(ConstantConditionCodeGen),
191 ConstantValueCodeGen(ConstantValueCodeGen),
192 DynamicExpression(DynamicExpression),
193 EsmBinding(EsmBinding),
194 EsmModuleItem(EsmModuleItem),
195 ExportsInfoBinding(ExportsInfoBinding),
196 ExportsInfoRef(ExportsInfoRef),
197 CjsExportsDropCodeGen(CjsExportsDropCodeGen),
198 IdentReplacement(IdentReplacement),
199 ImportMetaBinding(ImportMetaBinding),
200 ImportMetaRef(ImportMetaRef),
201 MemberReplacement(MemberReplacement),
202 RemovalCodeGen(RemovalCodeGen),
203 CjsRequireAssetReferenceCodeGen(CjsRequireAssetReferenceCodeGen),
204 CjsRequireResolveAssetReferenceCodeGen(CjsRequireResolveAssetReferenceCodeGen),
205 EsmAsyncAssetReferenceCodeGen(EsmAsyncAssetReferenceCodeGen),
206 EsmModuleIdAssetReferenceCodeGen(EsmModuleIdAssetReferenceCodeGen),
207 ImportMetaGlobAssetReferenceCodeGen(ImportMetaGlobAssetReferenceCodeGen),
208 RequireContextAssetReferenceCodeGen(RequireContextAssetReferenceCodeGen),
209 UrlAssetReferenceCodeGen(UrlAssetReferenceCodeGen),
210 WorkerAssetReferenceCodeGen(WorkerAssetReferenceCodeGen),
211 ServiceWorkerAssetReferenceCodeGen(ServiceWorkerAssetReferenceCodeGen),
212 ModuleHotReferenceCodeGen(ModuleHotReferenceCodeGen),
213 WorkerGlobalsReplacementCodeGen(WorkerGlobalsReplacementCodeGen),
214}
215
216impl CodeGen {
217 pub async fn code_generation(
218 &self,
219 ctx: Vc<Box<dyn ChunkingContext>>,
220 scope_hoisting_context: ScopeHoistingContext<'_>,
221 module: ResolvedVc<Box<dyn EcmascriptChunkPlaceable>>,
222 exports: ResolvedVc<EcmascriptExports>,
223 ) -> Result<CodeGeneration> {
224 match self {
225 Self::AmdDefineWithDependenciesCodeGen(v) => v.code_generation(ctx).await,
226 Self::CjsRequireCacheAccess(v) => v.code_generation(ctx).await,
227 Self::ConstantConditionCodeGen(v) => v.code_generation(ctx).await,
228 Self::CollectReferenceCodeGen(v) => v.code_generation(ctx).await,
229 Self::ConstantValueCodeGen(v) => v.code_generation(ctx).await,
230 Self::DynamicExpression(v) => v.code_generation(ctx).await,
231 Self::EsmBinding(v) => v.code_generation(ctx, scope_hoisting_context).await,
232 Self::EsmModuleItem(v) => v.code_generation(ctx).await,
233 Self::ExportsInfoBinding(v) => v.code_generation(ctx, module, exports).await,
234 Self::ExportsInfoRef(v) => v.code_generation(ctx).await,
235 Self::CjsExportsDropCodeGen(v) => v.code_generation(ctx, module, exports).await,
236 Self::IdentReplacement(v) => v.code_generation(ctx).await,
237 Self::ImportMetaBinding(v) => v.code_generation(ctx).await,
238 Self::ImportMetaRef(v) => v.code_generation(ctx).await,
239 Self::MemberReplacement(v) => v.code_generation(ctx).await,
240 Self::RemovalCodeGen(v) => v.code_generation(ctx).await,
241 Self::CjsRequireAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
242 Self::CjsRequireResolveAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
243 Self::EsmAsyncAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
244 Self::EsmModuleIdAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
245 Self::ImportMetaGlobAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
246 Self::RequireContextAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
247 Self::UrlAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
248 Self::WorkerAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
249 Self::ServiceWorkerAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
250 Self::ModuleHotReferenceCodeGen(v) => {
251 v.code_generation(ctx, scope_hoisting_context).await
252 }
253 Self::WorkerGlobalsReplacementCodeGen(v) => v.code_generation(ctx).await,
254 }
255 }
256}
257
258#[turbo_tasks::value(transparent)]
259pub struct CodeGens(Vec<CodeGen>);
260
261#[turbo_tasks::value_impl]
262impl CodeGens {
263 #[turbo_tasks::function]
264 pub fn empty() -> Vc<Self> {
265 Vc::cell(Vec::new())
266 }
267}
268
269pub trait IntoCodeGenReference {
270 fn into_reference(self) -> ResolvedVc<Box<dyn ModuleReference>>;
271 fn into_code_gen_reference(
272 self,
273 path: AstPath,
274 ) -> (ResolvedVc<Box<dyn ModuleReference>>, CodeGen);
275}
276
277pub fn path_to(
278 path: &[AstParentKind],
279 f: impl FnMut(&AstParentKind) -> bool,
280) -> Vec<AstParentKind> {
281 if let Some(pos) = path.iter().rev().position(f) {
282 let index = path.len() - pos - 1;
283 path[..index].to_vec()
284 } else {
285 path.to_vec()
286 }
287}
288
289#[macro_export]
299macro_rules! create_visitor {
300 (exact, $ast_path:expr, $name:ident, |$arg:ident: &mut $ty:ident| $b:block) => {
301 $crate::create_visitor!(__ $ast_path.to_vec(), $name, |$arg: &mut $ty| $b)
302 };
303 ($ast_path:expr, $name:ident, |$arg:ident: &mut $ty:ident| $b:block) => {
304 $crate::create_visitor!(__ $crate::code_gen::path_to(&$ast_path, |n| {
305 matches!(n, swc_core::ecma::visit::AstParentKind::$ty(_))
306 }), $name, |$arg: &mut $ty| $b)
307 };
308 (__ $ast_path:expr, $name:ident, |$arg:ident: &mut $ty:ident| $b:block) => {{
309 struct Visitor<T: Fn(&mut swc_core::ecma::ast::$ty) + Send + Sync> {
310 $name: T,
311 }
312
313 impl<T: Fn(&mut swc_core::ecma::ast::$ty) + Send + Sync> $crate::code_gen::AstModifier
314 for Visitor<T>
315 {
316 fn $name(&self, $arg: &mut swc_core::ecma::ast::$ty) {
317 (self.$name)($arg);
318 }
319 }
320
321 {
322 #[cfg(debug_assertions)]
323 if $ast_path.is_empty() {
324 unreachable!("if the path is empty, the visitor should be a root visitor");
325 }
326
327 (
328 $ast_path,
329 Box::new(Visitor {
330 $name: move |$arg: &mut swc_core::ecma::ast::$ty| $b,
331 }) as Box<dyn $crate::code_gen::AstModifier>,
332 )
333 }
334 }};
335}