swc_ecma_transforms_module/
system_js.rs

1use anyhow::Context;
2use rustc_hash::FxHashMap;
3use serde::{Deserialize, Serialize};
4use swc_atoms::Atom;
5use swc_common::{Mark, Span, SyntaxContext, DUMMY_SP};
6use swc_ecma_ast::*;
7use swc_ecma_utils::{
8    member_expr, private_ident, quote_ident, quote_str, var::VarCollector, ExprFactory,
9};
10use swc_ecma_visit::{fold_pass, standard_only_fold, Fold, FoldWith, VisitWith};
11
12pub use super::util::Config as InnerConfig;
13use crate::{
14    path::Resolver,
15    top_level_this::top_level_this,
16    util::{local_name_for_src, use_strict},
17};
18#[derive(Debug, Clone, Default, Serialize, Deserialize)]
19#[serde(deny_unknown_fields, rename_all = "camelCase")]
20pub struct Config {
21    #[serde(default)]
22    pub allow_top_level_this: bool,
23
24    #[serde(flatten, default)]
25    pub config: InnerConfig,
26}
27
28struct SystemJs {
29    unresolved_mark: Mark,
30    resolver: Resolver,
31    config: Config,
32
33    declare_var_idents: Vec<Ident>,
34    export_map: FxHashMap<Id, Vec<Atom>>,
35    export_names: Vec<Atom>,
36    export_values: Vec<Box<Expr>>,
37    tla: bool,
38    enter_async_fn: u32,
39    root_fn_decl_idents: Vec<Ident>,
40    module_item_meta_list: Vec<ModuleItemMeta>,
41    import_idents: Vec<Id>,
42    export_ident: Ident,
43    context_ident: Ident,
44}
45
46pub fn system_js(resolver: Resolver, unresolved_mark: Mark, config: Config) -> impl Pass {
47    fold_pass(SystemJs {
48        unresolved_mark,
49        resolver,
50        config,
51        declare_var_idents: Vec::new(),
52        export_map: Default::default(),
53        export_names: Vec::new(),
54        export_values: Vec::new(),
55        tla: false,
56        enter_async_fn: 0,
57        root_fn_decl_idents: Vec::new(),
58        module_item_meta_list: Vec::new(),
59        import_idents: Vec::new(),
60        export_ident: private_ident!("_export"),
61        context_ident: private_ident!("_context"),
62    })
63}
64
65struct ModuleItemMeta {
66    export_names: Vec<Atom>,
67    export_values: Vec<Box<Expr>>,
68    has_export_all: bool,
69    src: Atom,
70    setter_fn_stmts: Vec<Stmt>,
71}
72
73impl SystemJs {
74    fn export_call(&self, name: Atom, span: Span, expr: Expr) -> CallExpr {
75        CallExpr {
76            span,
77            callee: self.export_ident.clone().as_callee(),
78            args: vec![quote_str!(name).as_arg(), expr.as_arg()],
79            ..Default::default()
80        }
81    }
82
83    fn fold_module_name_ident(&mut self, ident: Ident) -> Expr {
84        if &*ident.sym == "__moduleName" && ident.ctxt.outer() == self.unresolved_mark {
85            return self
86                .context_ident
87                .clone()
88                .make_member(quote_ident!("id"))
89                .into();
90        }
91        ident.into()
92    }
93
94    fn replace_assign_expr(&mut self, assign_expr: AssignExpr) -> Expr {
95        match &assign_expr.left {
96            AssignTarget::Simple(pat_or_expr) => match pat_or_expr {
97                SimpleAssignTarget::Ident(ident) => {
98                    for (k, v) in self.export_map.iter() {
99                        if ident.to_id() == *k {
100                            let mut expr = assign_expr.into();
101                            for value in v.iter() {
102                                expr = self.export_call(value.clone(), DUMMY_SP, expr).into();
103                            }
104                            return expr;
105                        }
106                    }
107                    assign_expr.into()
108                }
109                _ => assign_expr.into(),
110            },
111            AssignTarget::Pat(pat) => {
112                let mut to: Vec<Id> = Vec::new();
113                pat.visit_with(&mut VarCollector { to: &mut to });
114
115                match pat {
116                    AssignTargetPat::Object(..) | AssignTargetPat::Array(..) => {
117                        let mut exprs = vec![Box::new(Expr::Assign(assign_expr))];
118
119                        for to in to {
120                            for (k, v) in self.export_map.iter() {
121                                if to == *k {
122                                    for _ in v.iter() {
123                                        exprs.push(
124                                            self.export_call(
125                                                to.0.clone(),
126                                                DUMMY_SP,
127                                                Ident::new(to.0.clone(), DUMMY_SP, to.1).into(),
128                                            )
129                                            .into(),
130                                        );
131                                    }
132                                    break;
133                                }
134                            }
135                        }
136                        SeqExpr {
137                            span: DUMMY_SP,
138                            exprs,
139                        }
140                        .into()
141                    }
142                    _ => assign_expr.into(),
143                }
144            }
145        }
146    }
147
148    fn replace_update_expr(&mut self, update_expr: UpdateExpr) -> Expr {
149        if !update_expr.prefix {
150            match &*update_expr.arg {
151                Expr::Ident(ident) => {
152                    for (k, v) in self.export_map.iter() {
153                        if ident.to_id() == *k {
154                            let mut expr = BinExpr {
155                                span: DUMMY_SP,
156                                op: op!(bin, "+"),
157                                left: UnaryExpr {
158                                    span: DUMMY_SP,
159                                    op: op!(unary, "+"),
160                                    arg: Box::new(Expr::Ident(ident.clone())),
161                                }
162                                .into(),
163                                right: 1.0.into(),
164                            }
165                            .into();
166                            for value in v.iter() {
167                                expr = self.export_call(value.clone(), DUMMY_SP, expr).into();
168                            }
169                            return SeqExpr {
170                                span: DUMMY_SP,
171                                exprs: vec![Box::new(expr), Box::new(Expr::Update(update_expr))],
172                            }
173                            .into();
174                        }
175                    }
176                    update_expr.into()
177                }
178                _ => update_expr.into(),
179            }
180        } else {
181            update_expr.into()
182        }
183    }
184
185    fn add_export_name(&mut self, key: Id, value: Atom) {
186        let mut find = false;
187        for (k, v) in self.export_map.iter_mut() {
188            if key == *k {
189                v.push(value.clone());
190                find = true;
191                break;
192            }
193        }
194        if !find {
195            self.export_map.insert(key, vec![value]);
196        }
197    }
198
199    fn add_declare_var_idents(&mut self, ident: &Ident) {
200        self.declare_var_idents.push(ident.clone());
201    }
202
203    fn build_export_call(
204        &mut self,
205        export_names: &mut Vec<Atom>,
206        export_values: &mut Vec<Box<Expr>>,
207    ) -> Vec<Stmt> {
208        match export_names.len() {
209            0 => Vec::new(),
210            1 => vec![self
211                .export_call(export_names.remove(0), DUMMY_SP, *export_values.remove(0))
212                .into_stmt()],
213            _ => {
214                let mut props = Vec::new();
215                for (sym, value) in export_names.drain(..).zip(export_values.drain(..)) {
216                    props.push(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
217                        key: match Ident::verify_symbol(&sym) {
218                            Ok(..) => PropName::Ident(quote_ident!(sym)),
219                            Err(..) => PropName::Str(quote_str!(sym)),
220                        },
221                        value,
222                    }))));
223                }
224                vec![CallExpr {
225                    span: DUMMY_SP,
226                    callee: self.export_ident.clone().as_callee(),
227                    args: vec![ObjectLit {
228                        span: DUMMY_SP,
229                        props,
230                    }
231                    .as_arg()],
232                    ..Default::default()
233                }
234                .into_stmt()]
235            }
236        }
237    }
238
239    fn build_module_item_export_all(&mut self, mut meta: ModuleItemMeta) -> Vec<Stmt> {
240        if !meta.has_export_all {
241            meta.setter_fn_stmts.append(
242                &mut self.build_export_call(&mut meta.export_names, &mut meta.export_values),
243            );
244        } else {
245            let export_obj = quote_ident!("exportObj");
246            let key_ident = quote_ident!("key");
247            let target = quote_ident!(local_name_for_src(&meta.src));
248            meta.setter_fn_stmts.push(
249                VarDecl {
250                    kind: VarDeclKind::Var,
251                    decls: vec![VarDeclarator {
252                        span: DUMMY_SP,
253                        name: export_obj.clone().into(),
254                        init: Some(Box::new(Expr::Object(ObjectLit {
255                            span: DUMMY_SP,
256                            props: Vec::new(),
257                        }))),
258                        definite: false,
259                    }],
260                    ..Default::default()
261                }
262                .into(),
263            );
264            meta.setter_fn_stmts.push(
265                ForInStmt {
266                    span: DUMMY_SP,
267                    left: VarDecl {
268                        kind: VarDeclKind::Var,
269                        decls: vec![VarDeclarator {
270                            span: DUMMY_SP,
271                            name: key_ident.clone().into(),
272                            init: None,
273                            definite: false,
274                        }],
275                        ..Default::default()
276                    }
277                    .into(),
278                    right: target.clone().into(),
279
280                    body: Box::new(Stmt::Block(BlockStmt {
281                        span: DUMMY_SP,
282                        stmts: vec![Stmt::If(IfStmt {
283                            span: DUMMY_SP,
284                            test: Box::new(Expr::Bin(BinExpr {
285                                span: DUMMY_SP,
286                                op: op!("&&"),
287                                left: Box::new(
288                                    key_ident
289                                        .clone()
290                                        .make_bin(op!("!=="), quote_str!("default")),
291                                ),
292                                right: Box::new(
293                                    key_ident
294                                        .clone()
295                                        .make_bin(op!("!=="), quote_str!("__esModule")),
296                                ),
297                            })),
298                            cons: Box::new(Stmt::Block(BlockStmt {
299                                stmts: vec![AssignExpr {
300                                    span: DUMMY_SP,
301                                    op: op!("="),
302                                    left: export_obj
303                                        .clone()
304                                        .computed_member(key_ident.clone())
305                                        .into(),
306                                    right: target.computed_member(key_ident).into(),
307                                }
308                                .into_stmt()],
309                                ..Default::default()
310                            })),
311                            alt: None,
312                        })],
313
314                        ..Default::default()
315                    })),
316                }
317                .into(),
318            );
319            for (sym, value) in meta
320                .export_names
321                .drain(..)
322                .zip(meta.export_values.drain(..))
323            {
324                meta.setter_fn_stmts.push(
325                    AssignExpr {
326                        span: DUMMY_SP,
327                        op: op!("="),
328                        left: export_obj.clone().make_member(quote_ident!(sym)).into(),
329                        right: value,
330                    }
331                    .into_stmt(),
332                );
333            }
334            meta.setter_fn_stmts.push(
335                CallExpr {
336                    span: DUMMY_SP,
337                    callee: self.export_ident.clone().as_callee(),
338                    args: vec![export_obj.as_arg()],
339                    ..Default::default()
340                }
341                .into_stmt(),
342            );
343        }
344
345        meta.setter_fn_stmts
346    }
347
348    fn add_module_item_meta(&mut self, mut m: ModuleItemMeta) {
349        match self
350            .module_item_meta_list
351            .iter()
352            .position(|i| m.src.eq(&i.src))
353        {
354            Some(index) => {
355                let mut meta = self.module_item_meta_list.remove(index);
356                meta.setter_fn_stmts.append(&mut m.setter_fn_stmts);
357                meta.export_names.append(&mut m.export_names);
358                meta.export_values.append(&mut m.export_values);
359                if m.has_export_all {
360                    meta.has_export_all = m.has_export_all;
361                }
362                self.module_item_meta_list.insert(index, meta);
363            }
364            None => {
365                self.module_item_meta_list.push(m);
366            }
367        }
368    }
369
370    #[allow(clippy::boxed_local)]
371    fn hoist_var_decl(&mut self, var_decl: Box<VarDecl>) -> Option<Expr> {
372        let mut exprs = Vec::new();
373        for var_declarator in var_decl.decls {
374            let mut tos: Vec<Id> = Vec::new();
375            var_declarator.visit_with(&mut VarCollector { to: &mut tos });
376
377            for (sym, ctxt) in tos {
378                if var_declarator.init.is_none() {
379                    for (k, v) in self.export_map.iter_mut() {
380                        if (sym.clone(), ctxt) == *k {
381                            for value in v.iter() {
382                                self.export_names.push(value.clone());
383                                self.export_values.push(Expr::undefined(DUMMY_SP));
384                            }
385                            break;
386                        }
387                    }
388                }
389                self.declare_var_idents
390                    .push(Ident::new(sym, DUMMY_SP, ctxt));
391            }
392
393            if let Some(init) = var_declarator.init {
394                exprs.push(
395                    AssignExpr {
396                        span: DUMMY_SP,
397                        op: op!("="),
398                        left: var_declarator.name.try_into().unwrap(),
399                        right: init,
400                    }
401                    .into(),
402                );
403            }
404        }
405        match exprs.len() {
406            0 => None,
407            _ => Some(
408                SeqExpr {
409                    span: DUMMY_SP,
410                    exprs,
411                }
412                .into(),
413            ),
414        }
415    }
416
417    fn hoist_for_var_decl(&mut self, var_decl_or_pat: ForHead) -> ForHead {
418        if let ForHead::VarDecl(mut var_decl) = var_decl_or_pat {
419            if var_decl.kind == VarDeclKind::Var {
420                let var_declarator = var_decl.decls.remove(0);
421                let mut tos: Vec<Id> = Vec::new();
422                var_declarator.visit_with(&mut VarCollector { to: &mut tos });
423
424                for to in tos {
425                    if var_declarator.init.is_none() {
426                        for (k, v) in self.export_map.iter_mut() {
427                            if to == *k {
428                                for value in v.iter() {
429                                    self.export_names.push(value.clone());
430                                    self.export_values.push(Expr::undefined(DUMMY_SP));
431                                }
432                                break;
433                            }
434                        }
435                    }
436                    self.declare_var_idents
437                        .push(Ident::new(to.0, DUMMY_SP, to.1));
438                }
439
440                ForHead::Pat(var_declarator.name.into())
441            } else {
442                ForHead::VarDecl(var_decl)
443            }
444        } else {
445            var_decl_or_pat
446        }
447    }
448
449    fn hoist_variables(&mut self, stmt: Stmt) -> Stmt {
450        match stmt {
451            Stmt::Decl(decl) => {
452                if let Decl::Var(var_decl) = decl {
453                    // if var_decl.kind == VarDeclKind::Var {
454                    if let Some(expr) = self.hoist_var_decl(var_decl) {
455                        expr.into_stmt()
456                    } else {
457                        EmptyStmt { span: DUMMY_SP }.into()
458                    }
459                    // } else {
460                    //     return Stmt::Decl(Decl::Var(var_decl));
461                    // }
462                } else {
463                    decl.into()
464                }
465            }
466            Stmt::For(for_stmt) => {
467                if let Some(init) = for_stmt.init {
468                    if let VarDeclOrExpr::VarDecl(var_decl) = init {
469                        if var_decl.kind == VarDeclKind::Var {
470                            ForStmt {
471                                init: self
472                                    .hoist_var_decl(var_decl)
473                                    .map(|expr| VarDeclOrExpr::Expr(Box::new(expr))),
474                                ..for_stmt
475                            }
476                            .into()
477                        } else {
478                            ForStmt {
479                                init: Some(VarDeclOrExpr::VarDecl(var_decl)),
480                                ..for_stmt
481                            }
482                            .into()
483                        }
484                    } else {
485                        ForStmt {
486                            init: Some(init),
487                            ..for_stmt
488                        }
489                        .into()
490                    }
491                } else {
492                    for_stmt.into()
493                }
494            }
495            Stmt::ForIn(for_in_stmt) => ForInStmt {
496                left: self.hoist_for_var_decl(for_in_stmt.left),
497                ..for_in_stmt
498            }
499            .into(),
500            Stmt::ForOf(for_of_stmt) => ForOfStmt {
501                left: self.hoist_for_var_decl(for_of_stmt.left),
502                ..for_of_stmt
503            }
504            .into(),
505            _ => stmt,
506        }
507    }
508}
509
510impl Fold for SystemJs {
511    standard_only_fold!();
512
513    fn fold_call_expr(&mut self, expr: CallExpr) -> CallExpr {
514        let expr = expr.fold_children_with(self);
515
516        match expr.callee {
517            Callee::Import(_) => CallExpr {
518                callee: self
519                    .context_ident
520                    .clone()
521                    .make_member(quote_ident!("import"))
522                    .as_callee(),
523                ..expr
524            },
525            _ => expr,
526        }
527    }
528
529    fn fold_expr(&mut self, expr: Expr) -> Expr {
530        let expr = expr.fold_children_with(self);
531
532        match expr {
533            Expr::Ident(ident) => self.fold_module_name_ident(ident),
534            Expr::Assign(assign) => {
535                let assign_expr = AssignExpr {
536                    right: match *assign.right {
537                        Expr::Ident(ident) => Box::new(self.fold_module_name_ident(ident)),
538                        Expr::Assign(AssignExpr {
539                            op: AssignOp::Assign,
540                            ..
541                        }) => {
542                            return self.replace_assign_expr(AssignExpr {
543                                right: assign.right,
544                                ..assign
545                            });
546                        }
547                        _ => assign.right,
548                    },
549                    ..assign
550                }
551                .fold_with(self);
552                self.replace_assign_expr(assign_expr)
553            }
554            Expr::Update(update) => self.replace_update_expr(update),
555            Expr::Call(call) => match call.callee {
556                Callee::Import(_) => CallExpr {
557                    args: call.args.fold_with(self),
558                    callee: self
559                        .context_ident
560                        .clone()
561                        .make_member(quote_ident!("import"))
562                        .as_callee(),
563                    ..call
564                }
565                .into(),
566                _ => call.into(),
567            },
568            Expr::MetaProp(meta_prop_expr) => match meta_prop_expr.kind {
569                MetaPropKind::ImportMeta => self
570                    .context_ident
571                    .clone()
572                    .make_member(quote_ident!("meta"))
573                    .into(),
574                _ => meta_prop_expr.into(),
575            },
576            Expr::Await(await_expr) => {
577                if self.enter_async_fn == 0 {
578                    self.tla = true;
579                }
580
581                await_expr.into()
582            }
583            _ => expr,
584        }
585    }
586
587    fn fold_fn_decl(&mut self, fn_decl: FnDecl) -> FnDecl {
588        let is_async = fn_decl.function.is_async;
589        if is_async {
590            self.enter_async_fn += 1;
591        }
592        let fold_fn_expr = fn_decl.fold_children_with(self);
593        if is_async {
594            self.enter_async_fn -= 1;
595        }
596        fold_fn_expr
597    }
598
599    fn fold_prop(&mut self, prop: Prop) -> Prop {
600        let prop = prop.fold_children_with(self);
601
602        match prop {
603            Prop::Shorthand(shorthand) => Prop::KeyValue(KeyValueProp {
604                key: PropName::Ident(shorthand.clone().into()),
605                value: Box::new(self.fold_module_name_ident(shorthand)),
606            }),
607            Prop::KeyValue(key_value_prop) => Prop::KeyValue(KeyValueProp {
608                key: key_value_prop.key,
609                value: match *key_value_prop.value {
610                    Expr::Ident(ident) => Box::new(self.fold_module_name_ident(ident)),
611                    _ => key_value_prop.value,
612                },
613            }),
614            _ => prop,
615        }
616    }
617
618    fn fold_module(&mut self, module: Module) -> Module {
619        let module = {
620            let mut module = module;
621            if !self.config.allow_top_level_this {
622                top_level_this(&mut module, *Expr::undefined(DUMMY_SP));
623            }
624            module
625        };
626        let mut before_body_stmts: Vec<Stmt> = Vec::new();
627        let mut execute_stmts = Vec::new();
628
629        // collect top level fn decl
630        for item in &module.body {
631            if let ModuleItem::Stmt(Stmt::Decl(Decl::Fn(fn_decl))) = item {
632                self.root_fn_decl_idents.push(fn_decl.ident.clone());
633            }
634        }
635
636        for item in module.body {
637            match item {
638                ModuleItem::ModuleDecl(decl) => match decl {
639                    ModuleDecl::Import(import) => {
640                        let src = match &self.resolver {
641                            Resolver::Real { resolver, base } => resolver
642                                .resolve_import(base, &import.src.value)
643                                .with_context(|| {
644                                    format!("failed to resolve import `{}`", import.src.value)
645                                })
646                                .unwrap(),
647                            Resolver::Default => import.src.value,
648                        };
649
650                        let source_alias = local_name_for_src(&src);
651
652                        let mut setter_fn_stmts = Vec::new();
653
654                        for specifier in import.specifiers {
655                            match specifier {
656                                ImportSpecifier::Default(specifier) => {
657                                    self.import_idents.push(specifier.local.to_id());
658                                    self.add_declare_var_idents(&specifier.local);
659                                    setter_fn_stmts.push(
660                                        AssignExpr {
661                                            span: specifier.span,
662                                            op: op!("="),
663                                            left: specifier.local.into(),
664                                            right: quote_ident!(source_alias.clone())
665                                                .make_member(quote_ident!("default"))
666                                                .into(),
667                                        }
668                                        .into_stmt(),
669                                    );
670                                }
671                                ImportSpecifier::Named(specifier) => {
672                                    self.add_declare_var_idents(&specifier.local);
673                                    setter_fn_stmts.push(
674                                        AssignExpr {
675                                            span: specifier.span,
676                                            op: op!("="),
677                                            left: specifier.local.clone().into(),
678                                            right: MemberExpr {
679                                                span: DUMMY_SP,
680                                                obj: Box::new(
681                                                    quote_ident!(source_alias.clone()).into(),
682                                                ),
683                                                prop: match specifier.imported {
684                                                    Some(m) => get_module_export_member_prop(&m),
685                                                    None => {
686                                                        MemberProp::Ident(specifier.local.into())
687                                                    }
688                                                },
689                                            }
690                                            .into(),
691                                        }
692                                        .into_stmt(),
693                                    );
694                                }
695                                ImportSpecifier::Namespace(specifier) => {
696                                    self.import_idents.push(specifier.local.to_id());
697                                    self.add_declare_var_idents(&specifier.local);
698                                    setter_fn_stmts.push(
699                                        AssignExpr {
700                                            span: specifier.span,
701                                            op: op!("="),
702                                            left: specifier.local.into(),
703                                            right: quote_ident!(source_alias.clone()).into(),
704                                        }
705                                        .into_stmt(),
706                                    );
707                                }
708                            }
709                        }
710
711                        self.add_module_item_meta(ModuleItemMeta {
712                            export_names: Vec::new(),
713                            export_values: Vec::new(),
714                            has_export_all: false,
715                            src: src.clone(),
716                            setter_fn_stmts,
717                        });
718                    }
719                    ModuleDecl::ExportNamed(decl) => match decl.src {
720                        Some(s) => {
721                            let src = match &self.resolver {
722                                Resolver::Real { resolver, base } => resolver
723                                    .resolve_import(base, &s.value)
724                                    .with_context(|| {
725                                        format!("failed to resolve import `{}`", s.value)
726                                    })
727                                    .unwrap(),
728                                Resolver::Default => s.value,
729                            };
730                            for specifier in decl.specifiers {
731                                let source_alias = local_name_for_src(&src);
732                                let mut export_names = Vec::new();
733                                let mut export_values = Vec::new();
734
735                                match specifier {
736                                    ExportSpecifier::Named(specifier) => {
737                                        export_names.push(match &specifier.exported {
738                                            Some(m) => get_module_export_name(m).0,
739                                            None => get_module_export_name(&specifier.orig).0,
740                                        });
741                                        export_values.push(
742                                            MemberExpr {
743                                                span: DUMMY_SP,
744                                                obj: Box::new(
745                                                    quote_ident!(source_alias.clone()).into(),
746                                                ),
747                                                prop: get_module_export_member_prop(
748                                                    &specifier.orig,
749                                                ),
750                                            }
751                                            .into(),
752                                        );
753                                    }
754                                    ExportSpecifier::Default(specifier) => {
755                                        export_names.push(specifier.exported.sym.clone());
756                                        export_values.push(
757                                            quote_ident!(source_alias.clone())
758                                                .make_member(quote_ident!("default"))
759                                                .into(),
760                                        );
761                                    }
762                                    ExportSpecifier::Namespace(specifier) => {
763                                        export_names
764                                            .push(get_module_export_name(&specifier.name).0);
765                                        export_values
766                                            .push(quote_ident!(source_alias.clone()).into());
767                                    }
768                                }
769
770                                self.add_module_item_meta(ModuleItemMeta {
771                                    export_names,
772                                    export_values,
773                                    has_export_all: false,
774                                    src: src.clone(),
775                                    setter_fn_stmts: Vec::new(),
776                                });
777                            }
778                        }
779                        None => {
780                            for specifier in decl.specifiers {
781                                if let ExportSpecifier::Named(specifier) = specifier {
782                                    let id = get_module_export_name(&specifier.orig);
783
784                                    if self.root_fn_decl_idents.iter().any(|i| i.sym == id.0) {
785                                        // hoisted function export
786                                        self.export_names.push(match &specifier.exported {
787                                            Some(m) => get_module_export_name(m).0,
788                                            None => id.0.clone(),
789                                        });
790                                        self.export_values.push(Box::new(get_module_export_expr(
791                                            &specifier.orig,
792                                        )));
793                                    }
794                                    if self.import_idents.iter().any(|i| id == *i) {
795                                        execute_stmts.push(
796                                            self.export_call(
797                                                id.0.clone(),
798                                                DUMMY_SP,
799                                                match &specifier.exported {
800                                                    Some(m) => get_module_export_expr(m),
801                                                    None => get_module_export_expr(&specifier.orig),
802                                                },
803                                            )
804                                            .into_stmt(),
805                                        );
806                                    }
807                                    self.add_export_name(
808                                        id,
809                                        match specifier.exported {
810                                            Some(m) => get_module_export_name(&m).0,
811                                            None => get_module_export_name(&specifier.orig).0,
812                                        },
813                                    );
814                                }
815                            }
816                        }
817                    },
818                    ModuleDecl::ExportDecl(decl) => {
819                        match decl.decl {
820                            Decl::Class(class_decl) => {
821                                let ident = class_decl.ident;
822                                self.export_names.push(ident.sym.clone());
823                                self.export_values.push(Expr::undefined(DUMMY_SP));
824                                self.add_declare_var_idents(&ident);
825                                self.add_export_name(ident.to_id(), ident.sym.clone());
826                                execute_stmts.push(
827                                    AssignExpr {
828                                        span: DUMMY_SP,
829                                        op: op!("="),
830                                        left: ident.clone().into(),
831                                        right: ClassExpr {
832                                            ident: Some(ident.clone()),
833                                            class: class_decl.class,
834                                        }
835                                        .into(),
836                                    }
837                                    .into_stmt(),
838                                );
839                            }
840                            Decl::Fn(fn_decl) => {
841                                self.export_names.push(fn_decl.ident.sym.clone());
842                                self.export_values.push(fn_decl.ident.clone().into());
843                                self.add_export_name(
844                                    fn_decl.ident.to_id(),
845                                    fn_decl.ident.sym.clone(),
846                                );
847                                before_body_stmts.push(fn_decl.into());
848                            }
849                            Decl::Var(var_decl) => {
850                                let mut decl = VarDecl {
851                                    decls: Vec::new(),
852                                    ..*var_decl
853                                };
854                                for var_declarator in var_decl.decls {
855                                    let mut tos: Vec<Id> = Vec::new();
856                                    var_declarator.visit_with(&mut VarCollector { to: &mut tos });
857                                    for to in tos {
858                                        let ident = Ident::new(to.0.clone(), DUMMY_SP, to.1);
859                                        self.add_export_name(to, ident.sym.clone());
860                                    }
861                                    decl.decls.push(var_declarator);
862                                }
863                                execute_stmts.push(decl.into());
864                            }
865                            _ => {}
866                        };
867                    }
868                    ModuleDecl::ExportDefaultDecl(decl) => {
869                        match decl.decl {
870                            DefaultDecl::Class(class_expr) => {
871                                if let Some(ident) = &class_expr.ident {
872                                    self.export_names.push("default".into());
873                                    self.export_values.push(Expr::undefined(DUMMY_SP));
874                                    self.add_declare_var_idents(ident);
875                                    self.add_export_name(ident.to_id(), "default".into());
876                                    execute_stmts.push(
877                                        AssignExpr {
878                                            span: DUMMY_SP,
879                                            op: op!("="),
880                                            left: ident.clone().into(),
881                                            right: class_expr.into(),
882                                        }
883                                        .into_stmt(),
884                                    );
885                                } else {
886                                    self.export_names.push("default".into());
887                                    self.export_values.push(class_expr.into());
888                                }
889                            }
890                            DefaultDecl::Fn(fn_expr) => {
891                                if let Some(ident) = &fn_expr.ident {
892                                    self.export_names.push("default".into());
893                                    self.export_values.push(ident.clone().into());
894                                    self.add_export_name(ident.to_id(), "default".into());
895                                    before_body_stmts.push(
896                                        FnDecl {
897                                            ident: ident.clone(),
898                                            declare: false,
899                                            function: fn_expr.function,
900                                        }
901                                        .into(),
902                                    );
903                                } else {
904                                    self.export_names.push("default".into());
905                                    self.export_values.push(fn_expr.into());
906                                }
907                            }
908                            _ => {}
909                        };
910                    }
911                    ModuleDecl::ExportDefaultExpr(expr) => {
912                        execute_stmts.push(
913                            self.export_call("default".into(), expr.span, *expr.expr)
914                                .into_stmt(),
915                        );
916                    }
917                    ModuleDecl::ExportAll(decl) => {
918                        self.add_module_item_meta(ModuleItemMeta {
919                            export_names: Vec::new(),
920                            export_values: Vec::new(),
921                            has_export_all: true,
922                            src: decl.src.value,
923                            setter_fn_stmts: Vec::new(),
924                        });
925                    }
926                    _ => {}
927                },
928                ModuleItem::Stmt(stmt) => match stmt {
929                    Stmt::Decl(decl) => match decl {
930                        Decl::Class(class_decl) => {
931                            self.add_declare_var_idents(&class_decl.ident);
932                            execute_stmts.push(
933                                AssignExpr {
934                                    span: DUMMY_SP,
935                                    op: op!("="),
936                                    left: class_decl.ident.clone().into(),
937                                    right: ClassExpr {
938                                        ident: Some(class_decl.ident.clone()),
939                                        class: class_decl.class,
940                                    }
941                                    .into(),
942                                }
943                                .into_stmt(),
944                            );
945                        }
946                        Decl::Fn(fn_decl) => {
947                            before_body_stmts.push(fn_decl.into());
948                        }
949                        _ => execute_stmts.push(decl.into()),
950                    },
951                    _ => execute_stmts.push(stmt),
952                },
953            }
954        }
955
956        // ====================
957        //  fold_with function, here export_map is collected finished.
958        // ====================
959
960        before_body_stmts = before_body_stmts
961            .into_iter()
962            .map(|s| s.fold_with(self))
963            .collect();
964
965        execute_stmts = execute_stmts
966            .into_iter()
967            .map(|s| self.hoist_variables(s).fold_with(self))
968            .filter(|s| !matches!(s, Stmt::Empty(_)))
969            .collect();
970
971        // ====================
972        //  generate export call, here export_names is collected finished.
973        // ====================
974        if !self.export_names.is_empty() {
975            let mut export_names = self.export_names.drain(..).collect();
976            let mut export_values = self.export_values.drain(..).collect();
977            before_body_stmts
978                .append(&mut self.build_export_call(&mut export_names, &mut export_values));
979        }
980
981        let mut setters = ArrayLit {
982            span: DUMMY_SP,
983            elems: Vec::new(),
984        };
985
986        let mut dep_module_names = ArrayLit {
987            span: DUMMY_SP,
988            elems: Vec::new(),
989        };
990
991        let module_item_meta_list: Vec<ModuleItemMeta> =
992            self.module_item_meta_list.drain(..).collect();
993        for meta in module_item_meta_list {
994            dep_module_names
995                .elems
996                .push(Some(quote_str!(meta.src.clone()).as_arg()));
997            setters.elems.push(Some(
998                Function {
999                    params: vec![Param {
1000                        span: DUMMY_SP,
1001                        decorators: Default::default(),
1002                        pat: quote_ident!(local_name_for_src(&meta.src)).into(),
1003                    }],
1004                    span: DUMMY_SP,
1005                    body: Some(BlockStmt {
1006                        span: DUMMY_SP,
1007                        stmts: self.build_module_item_export_all(meta),
1008                        ..Default::default()
1009                    }),
1010                    is_generator: false,
1011                    is_async: false,
1012                    ..Default::default()
1013                }
1014                .as_arg(),
1015            ));
1016        }
1017
1018        let execute = Box::new(Function {
1019            params: Vec::new(),
1020            decorators: Default::default(),
1021            span: DUMMY_SP,
1022            body: Some(BlockStmt {
1023                stmts: execute_stmts,
1024                ..Default::default()
1025            }),
1026            is_generator: false,
1027            is_async: self.tla,
1028            ..Default::default()
1029        });
1030
1031        let return_stmt = ReturnStmt {
1032            span: DUMMY_SP,
1033            arg: Some(
1034                ObjectLit {
1035                    span: DUMMY_SP,
1036                    props: vec![
1037                        PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
1038                            key: quote_ident!("setters").into(),
1039                            value: Box::new(Expr::Array(setters)),
1040                        }))),
1041                        PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {
1042                            key: quote_ident!("execute").into(),
1043                            value: Box::new(Expr::Fn(FnExpr {
1044                                ident: None,
1045                                function: execute,
1046                            })),
1047                        }))),
1048                    ],
1049                }
1050                .into(),
1051            ),
1052        };
1053
1054        let mut function_stmts = vec![use_strict()];
1055
1056        if !self.declare_var_idents.is_empty() {
1057            function_stmts.push(
1058                VarDecl {
1059                    span: DUMMY_SP,
1060                    kind: VarDeclKind::Var,
1061                    declare: false,
1062                    decls: self
1063                        .declare_var_idents
1064                        .iter()
1065                        .map(|i| VarDeclarator {
1066                            span: i.span,
1067                            name: i.clone().into(),
1068                            init: None,
1069                            definite: false,
1070                        })
1071                        .collect(),
1072                    ..Default::default()
1073                }
1074                .into(),
1075            );
1076        }
1077        function_stmts.append(&mut before_body_stmts);
1078        function_stmts.push(return_stmt.into());
1079
1080        let function = Box::new(Function {
1081            params: vec![
1082                Param {
1083                    span: DUMMY_SP,
1084                    decorators: Default::default(),
1085                    pat: self.export_ident.clone().into(),
1086                },
1087                Param {
1088                    span: DUMMY_SP,
1089                    decorators: Default::default(),
1090                    pat: self.context_ident.clone().into(),
1091                },
1092            ],
1093            decorators: Default::default(),
1094            span: DUMMY_SP,
1095            body: Some(BlockStmt {
1096                span: DUMMY_SP,
1097                stmts: function_stmts,
1098                ..Default::default()
1099            }),
1100            is_generator: false,
1101            is_async: false,
1102            ..Default::default()
1103        });
1104
1105        Module {
1106            body: vec![CallExpr {
1107                span: DUMMY_SP,
1108                callee: member_expr!(Default::default(), Default::default(), System.register)
1109                    .as_callee(),
1110                args: vec![
1111                    dep_module_names.as_arg(),
1112                    FnExpr {
1113                        ident: None,
1114                        function,
1115                    }
1116                    .as_arg(),
1117                ],
1118                ..Default::default()
1119            }
1120            .into_stmt()
1121            .into()],
1122            ..module
1123        }
1124    }
1125}
1126
1127#[inline]
1128fn get_module_export_name(module_export_name: &ModuleExportName) -> Id {
1129    match &module_export_name {
1130        ModuleExportName::Ident(ident) => ident.to_id(),
1131        ModuleExportName::Str(s) => (s.value.clone(), SyntaxContext::empty()),
1132    }
1133}
1134
1135#[inline]
1136fn get_module_export_expr(module_export_name: &ModuleExportName) -> Expr {
1137    match &module_export_name {
1138        ModuleExportName::Ident(ident) => ident.clone().into(),
1139        ModuleExportName::Str(s) => Lit::Str(quote_str!(s.value.clone())).into(),
1140    }
1141}
1142
1143#[inline]
1144fn get_module_export_member_prop(module_export_name: &ModuleExportName) -> MemberProp {
1145    match &module_export_name {
1146        ModuleExportName::Ident(ident) => MemberProp::Ident(ident.clone().into()),
1147        ModuleExportName::Str(s) => MemberProp::Computed(ComputedPropName {
1148            span: s.span,
1149            expr: Lit::Str(quote_str!(s.value.clone())).into(),
1150        }),
1151    }
1152}