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