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