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