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