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