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