turbopack_ecmascript/references/esm/
module_item.rs

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