turbopack_ecmascript/references/esm/module_item.rs
1use std::mem::replace;
2
3use anyhow::Result;
4use bincode::{Decode, Encode};
5use swc_core::{
6 common::DUMMY_SP,
7 ecma::ast::{
8 ClassDecl, Decl, DefaultDecl, ExportDecl, ExportDefaultDecl, ExportDefaultExpr, FnDecl,
9 Ident, ModuleDecl, ModuleItem, Stmt, VarDecl, VarDeclarator,
10 },
11 quote,
12};
13use turbo_tasks::{NonLocalValue, Vc, debug::ValueDebugFormat, trace::TraceRawVcs};
14use turbopack_core::chunk::ChunkingContext;
15
16use crate::{
17 code_gen::{CodeGen, CodeGeneration},
18 create_visitor,
19 magic_identifier::MAGIC_IDENTIFIER_DEFAULT_EXPORT_ATOM,
20 references::AstPath,
21};
22
23/// Makes code changes to remove export/import declarations and places the
24/// expr/decl in a normal statement. Unnamed expr/decl will be named with the
25/// magic identifier "export default"
26#[derive(
27 PartialEq, Eq, TraceRawVcs, ValueDebugFormat, NonLocalValue, Debug, Hash, Encode, Decode,
28)]
29pub struct EsmModuleItem {
30 pub path: AstPath,
31 pub supports_block_scoping: bool,
32}
33
34impl EsmModuleItem {
35 pub fn new(path: AstPath, supports_block_scoping: bool) -> Self {
36 EsmModuleItem {
37 path,
38 supports_block_scoping,
39 }
40 }
41
42 pub async fn code_generation(
43 &self,
44 _chunking_context: Vc<Box<dyn ChunkingContext>>,
45 ) -> Result<CodeGeneration> {
46 let mut visitors = Vec::new();
47 let supports_block_scoping = self.supports_block_scoping;
48
49 visitors.push(create_visitor!(
50 self.path,
51 visit_mut_module_item,
52 |module_item: &mut ModuleItem| {
53 let item = replace(module_item, ModuleItem::Stmt(quote!(";" as Stmt)));
54 if let ModuleItem::ModuleDecl(module_decl) = item {
55 match module_decl {
56 ModuleDecl::ExportDefaultExpr(ExportDefaultExpr { box expr, .. }) => {
57 let decl = Decl::Var(Box::new(VarDecl {
58 span: DUMMY_SP,
59 ctxt: Default::default(),
60 kind: if supports_block_scoping {
61 swc_core::ecma::ast::VarDeclKind::Const
62 } else {
63 // This is not entirely correct: this hides TDZ errors with
64 // circular imports, but there is no way to model this runtime
65 // behavior well for older browsers.
66 swc_core::ecma::ast::VarDeclKind::Var
67 },
68 declare: false,
69 decls: vec![VarDeclarator {
70 span: DUMMY_SP,
71 name: Ident::new(
72 MAGIC_IDENTIFIER_DEFAULT_EXPORT_ATOM.clone(),
73 DUMMY_SP,
74 Default::default(),
75 )
76 .into(),
77 init: Some(Box::new(expr)),
78 definite: false,
79 }],
80 }));
81 *module_item = ModuleItem::Stmt(Stmt::Decl(decl));
82 }
83 ModuleDecl::ExportDefaultDecl(ExportDefaultDecl { decl, span }) => {
84 match decl {
85 DefaultDecl::Class(class) => {
86 *module_item =
87 ModuleItem::Stmt(Stmt::Decl(Decl::Class(ClassDecl {
88 ident: class.ident.unwrap_or_else(|| {
89 Ident::new(
90 MAGIC_IDENTIFIER_DEFAULT_EXPORT_ATOM.clone(),
91 DUMMY_SP,
92 Default::default(),
93 )
94 }),
95 declare: false,
96 class: class.class,
97 })))
98 }
99 DefaultDecl::Fn(fn_expr) => {
100 *module_item = ModuleItem::Stmt(Stmt::Decl(Decl::Fn(FnDecl {
101 ident: fn_expr.ident.unwrap_or_else(|| {
102 Ident::new(
103 MAGIC_IDENTIFIER_DEFAULT_EXPORT_ATOM.clone(),
104 DUMMY_SP,
105 Default::default(),
106 )
107 }),
108 declare: false,
109 function: fn_expr.function,
110 })))
111 }
112 DefaultDecl::TsInterfaceDecl(_) => {
113 // not matching, might happen due to eventual consistency
114 *module_item =
115 ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(
116 ExportDefaultDecl { decl, span },
117 ));
118 }
119 }
120 }
121 ModuleDecl::ExportDecl(ExportDecl { decl, .. }) => {
122 *module_item = ModuleItem::Stmt(Stmt::Decl(decl));
123 }
124 ModuleDecl::ExportNamed(_) => {
125 // already removed
126 }
127 ModuleDecl::ExportAll(_) => {
128 // already removed
129 }
130 ModuleDecl::Import(_) => {
131 // already removed
132 }
133 _ => {
134 // not matching, might happen due to eventual consistency
135 *module_item = ModuleItem::ModuleDecl(module_decl);
136 }
137 }
138 } else {
139 // not matching, might happen due to eventual consistency
140 *module_item = item;
141 }
142 }
143 ));
144
145 Ok(CodeGeneration::visitors(visitors))
146 }
147}
148
149impl From<EsmModuleItem> for CodeGen {
150 fn from(val: EsmModuleItem) -> Self {
151 CodeGen::EsmModuleItem(val)
152 }
153}