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,
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, magic_identifier,
19    references::AstPath,
20};
21
22/// Makes code changes to remove export/import declarations and places the
23/// expr/decl in a normal statement. Unnamed expr/decl will be named with the
24/// magic identifier "export default"
25#[derive(
26    PartialEq, Eq, TraceRawVcs, ValueDebugFormat, NonLocalValue, Debug, Hash, Encode, Decode,
27)]
28pub struct EsmModuleItem {
29    pub path: AstPath,
30}
31
32impl EsmModuleItem {
33    pub fn new(path: AstPath) -> Self {
34        EsmModuleItem { path }
35    }
36
37    pub async fn code_generation(
38        &self,
39        _chunking_context: Vc<Box<dyn ChunkingContext>>,
40    ) -> Result<CodeGeneration> {
41        let mut visitors = Vec::new();
42
43        visitors.push(
44            create_visitor!(self.path, visit_mut_module_item, |module_item: &mut ModuleItem| {
45                let item = replace(module_item, ModuleItem::Stmt(quote!(";" as Stmt)));
46                if let ModuleItem::ModuleDecl(module_decl) = item {
47                    match module_decl {
48                        ModuleDecl::ExportDefaultExpr(ExportDefaultExpr { box expr, .. }) => {
49                            let stmt = quote!("const $name = $expr;" as Stmt,
50                                name = Ident::new(magic_identifier::mangle("default export").into(), DUMMY_SP, Default::default()),
51                                expr: Expr = expr
52                            );
53                            *module_item = ModuleItem::Stmt(stmt);
54                        }
55                        ModuleDecl::ExportDefaultDecl(ExportDefaultDecl { decl, span }) => {
56                            match decl {
57                                DefaultDecl::Class(class) => {
58                                    *module_item = ModuleItem::Stmt(Stmt::Decl(Decl::Class(ClassDecl {
59                                        ident: class.ident.unwrap_or_else(|| Ident::new(magic_identifier::mangle("default export").into(), DUMMY_SP, Default::default())),
60                                        declare: false,
61                                        class: class.class
62                                    })))
63                                }
64                                DefaultDecl::Fn(fn_expr) => {
65                                    *module_item = ModuleItem::Stmt(Stmt::Decl(Decl::Fn(FnDecl {
66                                        ident: fn_expr.ident.unwrap_or_else(|| Ident::new(magic_identifier::mangle("default export").into(), DUMMY_SP, Default::default())),
67                                        declare: false,
68                                        function: fn_expr.function
69                                    })))
70                                }
71                                DefaultDecl::TsInterfaceDecl(_) => {
72                                    // not matching, might happen due to eventual consistency
73                                    *module_item = ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(ExportDefaultDecl { decl, span }));
74                                }
75                            }
76                        }
77                        ModuleDecl::ExportDecl(ExportDecl { decl, .. }) => {
78                            *module_item = ModuleItem::Stmt(Stmt::Decl(decl));
79                        }
80                        ModuleDecl::ExportNamed(_) => {
81                            // already removed
82                        }
83                        ModuleDecl::ExportAll(_) => {
84                            // already removed
85                        }
86                        ModuleDecl::Import(_) => {
87                            // already removed
88                        }
89                        _ => {
90                            // not matching, might happen due to eventual consistency
91                            *module_item = ModuleItem::ModuleDecl(module_decl);
92                        }
93                    }
94                } else {
95                    // not matching, might happen due to eventual consistency
96                    *module_item = item;
97                }
98            }),
99        );
100
101        Ok(CodeGeneration::visitors(visitors))
102    }
103}
104
105impl From<EsmModuleItem> for CodeGen {
106    fn from(val: EsmModuleItem) -> Self {
107        CodeGen::EsmModuleItem(val)
108    }
109}