swc_bundler/bundler/chunk/
computed_key.rs

1use std::mem::take;
2
3use anyhow::{bail, Error};
4use swc_atoms::atom;
5use swc_common::{SyntaxContext, DUMMY_SP};
6use swc_ecma_ast::*;
7use swc_ecma_utils::{contains_top_level_await, find_pat_ids, private_ident, ExprFactory};
8use swc_ecma_visit::{noop_fold_type, Fold};
9
10use crate::{
11    bundler::chunk::merge::Ctx,
12    modules::Modules,
13    util::{is_injected, ExportMetadata},
14    Bundler, Load, ModuleId, Resolve,
15};
16
17impl<L, R> Bundler<'_, L, R>
18where
19    L: Load,
20    R: Resolve,
21{
22    //  Converts
23    ///
24    /// ```ts
25    /// export const arr = [1, 2, 3];
26    /// ```
27    ///
28    /// to
29    ///
30    /// ```ts
31    /// const _mod = (function(){
32    ///     const arr = [1, 2, 3];
33    ///     return {
34    ///         arr,
35    ///     };
36    /// })();
37    /// ```
38    pub(super) fn wrap_esm(
39        &self,
40        ctx: &Ctx,
41        id: ModuleId,
42        module: Modules,
43    ) -> Result<Modules, Error> {
44        let span = DUMMY_SP;
45        let module_var_name = match self.scope.wrapped_esm_id(id) {
46            Some(v) => v,
47            None => bail!("{:?} should not be wrapped with a function", id),
48        };
49
50        let is_async = module.iter().any(|m| contains_top_level_await(m.1));
51
52        let mut additional_items = Vec::new();
53
54        module.iter().for_each(|(module_id, item)| {
55            match item {
56                // Handle `export *`-s from dependency modules.
57                //
58                // See: https://github.com/denoland/deno/issues/9200
59                ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(NamedExport {
60                    ref specifiers,
61                    with: Some(with),
62                    ..
63                })) if is_injected(with) => {
64                    for s in specifiers {
65                        if let ExportSpecifier::Named(ExportNamedSpecifier {
66                            orig,
67                            exported: Some(exported),
68                            ..
69                        }) = s
70                        {
71                            let exported = match exported {
72                                ModuleExportName::Ident(ident) => ident,
73                                ModuleExportName::Str(..) => {
74                                    unimplemented!("module string names unimplemented")
75                                }
76                                #[cfg(swc_ast_unknown)]
77                                _ => panic!("unable to access unknown nodes"),
78                            };
79                            if ctx.transitive_remap.get(&exported.ctxt).is_some() {
80                                let specifier = ExportSpecifier::Named(ExportNamedSpecifier {
81                                    span: DUMMY_SP,
82                                    orig: orig.clone(),
83                                    exported: Some(ModuleExportName::Ident(exported.clone())),
84                                    is_type_only: false,
85                                });
86                                additional_items.push((
87                                    module_id,
88                                    NamedExport {
89                                        span: DUMMY_SP,
90                                        specifiers: vec![specifier],
91                                        src: None,
92                                        type_only: false,
93                                        with: Some(
94                                            ExportMetadata {
95                                                injected: true,
96                                                ..Default::default()
97                                            }
98                                            .into_with(),
99                                        ),
100                                    }
101                                    .into(),
102                                ));
103                            }
104                        }
105                    }
106                }
107                _ => {}
108            }
109        });
110
111        let mut export_visitor = ExportToReturn {
112            synthesized_ctxt: self.synthesized_ctxt,
113            return_props: Default::default(),
114        };
115        let mut module = module.fold_with(&mut export_visitor);
116
117        module.append_all(additional_items);
118
119        let return_stmt = ReturnStmt {
120            span: DUMMY_SP,
121            arg: Some(
122                ObjectLit {
123                    span: DUMMY_SP,
124                    props: take(&mut export_visitor.return_props),
125                }
126                .into(),
127            ),
128        }
129        .into();
130
131        module.iter().for_each(|(_, v)| {
132            if let ModuleItem::ModuleDecl(ModuleDecl::ExportAll(ref export)) = v {
133                // We handle this later.
134                let mut map = ctx.export_stars_in_wrapped.lock();
135                let data = ExportMetadata::decode(export.with.as_deref());
136                if let Some(export_ctxt) = data.export_ctxt {
137                    map.entry(id).or_default().push(export_ctxt);
138                }
139            }
140        });
141
142        let module_fn: Expr = FnExpr {
143            function: Box::new(Function {
144                params: Default::default(),
145                body: Some(BlockStmt {
146                    span: DUMMY_SP,
147                    stmts: vec![return_stmt],
148                    ..Default::default()
149                }),
150                is_generator: false,
151                is_async,
152                ..Default::default()
153            }),
154            ident: None,
155        }
156        .into();
157
158        let mut module_expr = CallExpr {
159            span: DUMMY_SP,
160            callee: module_fn.as_callee(),
161            args: Default::default(),
162            ..Default::default()
163        }
164        .into();
165
166        if is_async {
167            module_expr = AwaitExpr {
168                span: DUMMY_SP,
169                arg: Box::new(module_expr),
170            }
171            .into();
172        }
173
174        let var_decl = VarDecl {
175            span,
176            ctxt: self.injected_ctxt,
177            declare: false,
178            kind: VarDeclKind::Const,
179            decls: vec![VarDeclarator {
180                span: DUMMY_SP,
181                definite: false,
182                name: Pat::Ident(module_var_name.into_ident().into()),
183                init: Some(Box::new(module_expr)),
184            }],
185        };
186
187        module.append(id, var_decl.into());
188
189        // print_hygiene(
190        //     "wrap",
191        //     &self.cm,
192        //     &Module {
193        //         span: DUMMY_SP,
194        //         body: module_items.clone(),
195        //         shebang: None,
196        //     },
197        // );
198
199        Ok(module)
200    }
201}
202
203struct ExportToReturn {
204    return_props: Vec<PropOrSpread>,
205    synthesized_ctxt: SyntaxContext,
206}
207
208impl ExportToReturn {
209    fn export_id(&mut self, mut i: Ident) {
210        i.ctxt = SyntaxContext::empty();
211        self.return_props
212            .push(PropOrSpread::Prop(Box::new(Prop::Shorthand(i))));
213    }
214
215    fn export_key_value(&mut self, key: Ident, value: Ident) {
216        self.return_props
217            .push(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
218                key: PropName::Ident(key.into()),
219                value: value.into(),
220            }))));
221    }
222}
223
224impl Fold for ExportToReturn {
225    noop_fold_type!();
226
227    fn fold_stmt(&mut self, s: Stmt) -> Stmt {
228        s
229    }
230
231    fn fold_module_item(&mut self, item: ModuleItem) -> ModuleItem {
232        let decl = match item {
233            ModuleItem::ModuleDecl(decl) => decl,
234            ModuleItem::Stmt(_) => return item,
235            #[cfg(swc_ast_unknown)]
236            _ => panic!("unable to access unknown nodes"),
237        };
238
239        let stmt = match decl {
240            ModuleDecl::Import(_) => return decl.into(),
241            ModuleDecl::ExportDecl(export) => {
242                match &export.decl {
243                    Decl::Class(ClassDecl { ident, .. }) | Decl::Fn(FnDecl { ident, .. }) => {
244                        self.export_id(ident.clone());
245                    }
246                    Decl::Var(decl) => {
247                        let ids: Vec<Ident> = find_pat_ids(decl);
248                        ids.into_iter().for_each(|id| self.export_id(id));
249                    }
250                    _ => unreachable!(),
251                }
252
253                Some(ModuleItem::from(export.decl))
254            }
255
256            ModuleDecl::ExportDefaultDecl(export) => match export.decl {
257                DefaultDecl::Class(expr) => {
258                    let ident = expr.ident;
259                    let ident = ident.unwrap_or_else(|| private_ident!("_default_decl"));
260
261                    self.export_key_value(
262                        Ident::new_no_ctxt(atom!("default"), export.span),
263                        ident.clone(),
264                    );
265
266                    Some(
267                        ClassDecl {
268                            ident,
269                            class: expr.class,
270                            declare: false,
271                        }
272                        .into(),
273                    )
274                }
275                DefaultDecl::Fn(expr) => {
276                    let ident = expr.ident;
277                    let ident = ident.unwrap_or_else(|| private_ident!("_default_decl"));
278
279                    self.export_key_value(
280                        Ident::new_no_ctxt(atom!("default"), export.span),
281                        ident.clone(),
282                    );
283
284                    Some(
285                        FnDecl {
286                            ident,
287                            function: expr.function,
288                            declare: false,
289                        }
290                        .into(),
291                    )
292                }
293                DefaultDecl::TsInterfaceDecl(_) => None,
294                #[cfg(swc_ast_unknown)]
295                _ => panic!("unable to access unknown nodes"),
296            },
297            ModuleDecl::ExportDefaultExpr(_) => None,
298            ModuleDecl::ExportAll(export) => return export.into(),
299            ModuleDecl::ExportNamed(export) => {
300                for specifier in &export.specifiers {
301                    match specifier {
302                        ExportSpecifier::Namespace(_) => {}
303                        ExportSpecifier::Default(_) => {}
304                        ExportSpecifier::Named(named) => match &named.exported {
305                            Some(ModuleExportName::Ident(exported)) => {
306                                // As injected named exports are converted to variables by other
307                                // passes, we should not create a variable for it.
308                                if let ModuleExportName::Ident(orig) = &named.orig {
309                                    self.export_key_value(exported.clone(), orig.clone());
310                                } else {
311                                    unimplemented!("module string names unimplemented")
312                                }
313                            }
314                            Some(ModuleExportName::Str(..)) => {
315                                unimplemented!("module string names unimplemented")
316                            }
317                            #[cfg(swc_ast_unknown)]
318                            Some(_) => panic!("unable to access unknown nodes"),
319                            None => {
320                                if let ModuleExportName::Ident(orig) = &named.orig {
321                                    self.export_id(orig.clone());
322                                } else {
323                                    unimplemented!("module string names unimplemented")
324                                }
325                            }
326                        },
327                        #[cfg(swc_ast_unknown)]
328                        _ => panic!("unable to access unknown nodes"),
329                    }
330                }
331
332                let md = ExportMetadata::decode(export.with.as_deref());
333                // Ignore export {} specified by user.
334                if export.src.is_none()
335                    && md.export_ctxt.unwrap_or_default() != self.synthesized_ctxt
336                {
337                    None
338                } else {
339                    return export.into();
340                }
341            }
342            ModuleDecl::TsImportEquals(_) => None,
343            ModuleDecl::TsExportAssignment(_) => None,
344            ModuleDecl::TsNamespaceExport(_) => None,
345            #[cfg(swc_ast_unknown)]
346            _ => panic!("unable to access unknown nodes"),
347        };
348
349        if let Some(stmt) = stmt {
350            stmt
351        } else {
352            EmptyStmt { span: DUMMY_SP }.into()
353        }
354    }
355}