swc_ecma_transforms_module/
module_decl_strip.rs

1use indexmap::IndexMap;
2use rustc_hash::{FxHashMap, FxHashSet};
3use swc_atoms::{atom, Atom};
4use swc_common::{util::take::Take, Mark, Span, SyntaxContext};
5use swc_ecma_ast::*;
6use swc_ecma_utils::{find_pat_ids, private_ident, quote_ident, ExprFactory};
7use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
8
9use crate::{module_ref_rewriter::ImportMap, SpanCtx};
10
11/// key: module path
12pub type Link = IndexMap<Atom, LinkItem>;
13/// key: export binding name
14pub type Export = FxHashMap<Atom, ExportItem>;
15
16#[derive(Debug)]
17pub struct ModuleDeclStrip {
18    /// all imports/exports collected by path in source text order
19    pub link: Link,
20
21    /// local exported binding
22    ///
23    /// `export { foo as "1", bar }`
24    /// -> Map("1" => foo, bar => bar)
25    pub export: Export,
26
27    /// `export = ` detected
28    pub export_assign: Option<Box<Expr>>,
29
30    pub has_module_decl: bool,
31
32    /// `export default expr`
33    export_default: Option<Stmt>,
34
35    const_var_kind: VarDeclKind,
36}
37
38impl ModuleDeclStrip {
39    pub fn new(const_var_kind: VarDeclKind) -> Self {
40        Self {
41            link: Default::default(),
42            export: Default::default(),
43            export_assign: Default::default(),
44            has_module_decl: Default::default(),
45            export_default: Default::default(),
46            const_var_kind,
47        }
48    }
49}
50
51impl VisitMut for ModuleDeclStrip {
52    noop_visit_mut_type!(fail);
53
54    fn visit_mut_module_items(&mut self, n: &mut Vec<ModuleItem>) {
55        let mut list = Vec::with_capacity(n.len());
56
57        for item in n.drain(..) {
58            match item {
59                ModuleItem::Stmt(stmt) => list.push(stmt.into()),
60
61                ModuleItem::ModuleDecl(mut module_decl) => {
62                    // collect link meta
63                    module_decl.visit_mut_with(self);
64                    self.has_module_decl = true;
65
66                    // emit stmt
67                    match module_decl {
68                        ModuleDecl::Import(..) => continue,
69                        ModuleDecl::ExportDecl(ExportDecl { decl, .. }) => {
70                            list.push(decl.into());
71                        }
72                        ModuleDecl::ExportNamed(..) => continue,
73                        ModuleDecl::ExportDefaultDecl(ExportDefaultDecl { decl, .. }) => match decl
74                        {
75                            DefaultDecl::Class(class_expr) => {
76                                list.extend(class_expr.as_class_decl().map(From::from))
77                            }
78                            DefaultDecl::Fn(fn_expr) => {
79                                list.extend(fn_expr.as_fn_decl().map(From::from))
80                            }
81                            DefaultDecl::TsInterfaceDecl(_) => continue,
82                            #[cfg(swc_ast_unknown)]
83                            _ => panic!("unable to access unknown nodes"),
84                        },
85                        ModuleDecl::ExportDefaultExpr(..) => {
86                            list.extend(self.export_default.take().map(From::from))
87                        }
88                        ModuleDecl::ExportAll(..) => continue,
89                        ModuleDecl::TsImportEquals(..) => continue,
90                        ModuleDecl::TsExportAssignment(..) => continue,
91                        ModuleDecl::TsNamespaceExport(..) => continue,
92                        #[cfg(swc_ast_unknown)]
93                        _ => panic!("unable to access unknown nodes"),
94                    };
95                }
96                #[cfg(swc_ast_unknown)]
97                _ => panic!("unable to access unknown nodes"),
98            };
99        }
100
101        *n = list;
102    }
103
104    // collect all static import
105    fn visit_mut_import_decl(&mut self, n: &mut ImportDecl) {
106        if n.type_only {
107            return;
108        }
109
110        let ImportDecl {
111            specifiers, src, ..
112        } = n.take();
113
114        self.link
115            .entry(src.value.to_atom_lossy().into_owned())
116            .or_default()
117            .mut_dummy_span(src.span)
118            .extend(specifiers.into_iter().map(From::from));
119    }
120
121    /// ```javascript
122    /// export const foo = 1, bar = 2, { baz } = { baz: 3 };
123    /// export let a = 1, [b] = [2];
124    /// export function x() {}
125    /// export class y {}
126    /// ```
127    /// ->
128    /// ```javascript
129    /// const foo = 1, bar = 2, { baz } = { baz: 3 };
130    /// let a = 1, [b] = [2];
131    /// function x() {}
132    /// class y {}
133    /// ```
134    fn visit_mut_export_decl(&mut self, n: &mut ExportDecl) {
135        match &n.decl {
136            Decl::Class(ClassDecl { ident, .. }) | Decl::Fn(FnDecl { ident, .. }) => {
137                self.export.insert(
138                    ident.sym.clone(),
139                    ExportItem::new((ident.span, ident.ctxt), ident.clone()),
140                );
141            }
142
143            Decl::Var(v) => {
144                self.export.extend(
145                    find_pat_ids::<_, Ident>(&v.decls)
146                        .into_iter()
147                        .map(|id| (id.sym.clone(), ExportItem::new((id.span, id.ctxt), id))),
148                );
149            }
150            _ => {}
151        };
152    }
153
154    /// ```javascript
155    /// export { foo, foo as bar, foo as "baz" };
156    /// export { "foo", foo as bar, "foo" as "baz" } from "mod";
157    /// export * as foo from "mod";
158    /// export * as "bar" from "mod";
159    /// ```
160    fn visit_mut_named_export(&mut self, n: &mut NamedExport) {
161        if n.type_only {
162            return;
163        }
164
165        let NamedExport {
166            specifiers, src, ..
167        } = n.take();
168
169        if let Some(src) = src {
170            self.link
171                .entry(src.value.to_atom_lossy().into_owned())
172                .or_default()
173                .mut_dummy_span(src.span)
174                .extend(specifiers.into_iter().map(From::from));
175        } else {
176            self.export.extend(specifiers.into_iter().map(|e| match e {
177                ExportSpecifier::Namespace(..) => {
178                    unreachable!("`export *` without src is invalid")
179                }
180                ExportSpecifier::Default(..) => {
181                    unreachable!("`export foo` without src is invalid")
182                }
183                ExportSpecifier::Named(ExportNamedSpecifier { orig, exported, .. }) => {
184                    let orig = match orig {
185                        ModuleExportName::Ident(id) => id,
186                        ModuleExportName::Str(_) => {
187                            unreachable!(r#"`export {{ "foo" }}` without src is invalid"#)
188                        }
189                        #[cfg(swc_ast_unknown)]
190                        _ => panic!("unable to access unknown nodes"),
191                    };
192
193                    if let Some(exported) = exported {
194                        let (export_name, export_name_span) = match exported {
195                            ModuleExportName::Ident(Ident {
196                                ctxt, span, sym, ..
197                            }) => (sym, (span, ctxt)),
198                            ModuleExportName::Str(Str { span, value, .. }) => (
199                                value.to_atom_lossy().into_owned(),
200                                (span, Default::default()),
201                            ),
202                            #[cfg(swc_ast_unknown)]
203                            _ => panic!("unable to access unknown nodes"),
204                        };
205
206                        (export_name, ExportItem::new(export_name_span, orig))
207                    } else {
208                        (
209                            orig.sym.clone(),
210                            ExportItem::new((orig.span, orig.ctxt), orig),
211                        )
212                    }
213                }
214                #[cfg(swc_ast_unknown)]
215                _ => panic!("unable to access unknown nodes"),
216            }))
217        }
218    }
219
220    /// ```javascript
221    /// export default class foo {};
222    /// export default class {};
223    /// export default function bar () {};
224    /// export default function () {};
225    /// ```
226    /// ->
227    /// ```javascript
228    /// class foo {};
229    /// class _default {};
230    /// function bar () {};
231    /// function _default () {};
232    /// ```
233    fn visit_mut_export_default_decl(&mut self, n: &mut ExportDefaultDecl) {
234        match &mut n.decl {
235            DefaultDecl::Class(class_expr) => {
236                let ident = class_expr
237                    .ident
238                    .get_or_insert_with(|| private_ident!(n.span, "_default"))
239                    .clone();
240
241                self.export.insert(
242                    atom!("default"),
243                    ExportItem::new((n.span, Default::default()), ident),
244                );
245            }
246            DefaultDecl::Fn(fn_expr) => {
247                let ident = fn_expr
248                    .ident
249                    .get_or_insert_with(|| private_ident!(n.span, "_default"))
250                    .clone();
251
252                self.export.insert(
253                    atom!("default"),
254                    ExportItem::new((n.span, Default::default()), ident),
255                );
256            }
257            DefaultDecl::TsInterfaceDecl(_) => {}
258            #[cfg(swc_ast_unknown)]
259            _ => panic!("unable to access unknown nodes"),
260        }
261    }
262
263    /// ```javascript
264    /// export default foo;
265    /// export default 1
266    /// ```
267    /// ->
268    /// ```javascript
269    /// var _default = foo;
270    /// var _default = 1;
271    /// ```
272    fn visit_mut_export_default_expr(&mut self, n: &mut ExportDefaultExpr) {
273        let ident = private_ident!(n.span, "_default");
274
275        self.export.insert(
276            atom!("default"),
277            ExportItem::new((n.span, Default::default()), ident.clone()),
278        );
279
280        self.export_default = Some(
281            n.expr
282                .take()
283                .into_var_decl(self.const_var_kind, ident.into())
284                .into(),
285        );
286    }
287
288    /// ```javascript
289    /// export * from "mod";
290    /// ```
291    fn visit_mut_export_all(&mut self, n: &mut ExportAll) {
292        let Str {
293            value: src_key,
294            span: src_span,
295            ..
296        } = *n.take().src;
297
298        self.link
299            .entry(src_key.to_atom_lossy().into_owned())
300            .or_default()
301            .mut_dummy_span(src_span)
302            .insert(LinkSpecifier::ExportStar);
303    }
304
305    /// ```javascript
306    /// import foo = require("mod");
307    /// export import foo = require("mod");
308    /// ```
309    fn visit_mut_ts_import_equals_decl(&mut self, n: &mut TsImportEqualsDecl) {
310        if n.is_type_only {
311            return;
312        }
313
314        let TsImportEqualsDecl {
315            id,
316            module_ref,
317            is_export,
318            ..
319        } = n;
320
321        if let TsModuleRef::TsExternalModuleRef(TsExternalModuleRef {
322            expr:
323                Str {
324                    span,
325                    value: src_key,
326                    ..
327                },
328            ..
329        }) = module_ref
330        {
331            if *is_export {
332                self.export.insert(
333                    id.sym.clone(),
334                    ExportItem::new((id.span, id.ctxt), id.clone()),
335                );
336            }
337
338            self.link
339                .entry(src_key.to_atom_lossy().into_owned())
340                .or_default()
341                .mut_dummy_span(*span)
342                .insert(LinkSpecifier::ImportEqual(id.to_id()));
343        }
344    }
345
346    /// ```javascript
347    /// export = expr;
348    /// ```
349    fn visit_mut_ts_export_assignment(&mut self, n: &mut TsExportAssignment) {
350        self.export_assign.get_or_insert(n.expr.take());
351    }
352}
353
354#[derive(Debug, PartialEq, Eq, Hash)]
355pub enum LinkSpecifier {
356    ///```javascript
357    /// import "mod";
358    /// import {} from "mod",
359    /// import { type foo } from "mod";
360    /// export {} from "mod";
361    /// export { type foo } from "mod";
362    /// ```
363    Empty,
364
365    /// ```javascript
366    /// import { imported as local, local } from "mod";
367    /// import { "imported" as local } from "mod";
368    /// ```
369    /// Note: imported will never be `default`
370    ImportNamed { imported: Option<Atom>, local: Id },
371
372    /// ```javascript
373    /// import foo from "mod";
374    /// ```
375    ImportDefault(Id),
376
377    /// ```javascript
378    /// import * as foo from "mod";
379    /// ```
380    ImportStarAs(Id),
381
382    /// ```javascript
383    /// export { orig, orig as exported } from "mod";
384    /// export { "orig", "orig" as "exported" } from "mod";
385    /// ```
386    /// Note: orig will never be `default`
387    ExportNamed {
388        orig: (Atom, SpanCtx),
389        exported: Option<(Atom, SpanCtx)>,
390    },
391
392    /// ```javascript
393    /// export { default } from "foo";
394    /// export { "default" } from "foo";
395    /// export { default as foo } from "mod";
396    /// ```
397    /// (default_span, local_sym, local_span)
398    ExportDefaultAs(SpanCtx, Atom, SpanCtx),
399
400    /// ```javascript
401    /// export * as foo from "mod";
402    /// export * as "bar" from "mod";
403    /// ```
404    ExportStarAs(Atom, SpanCtx),
405
406    /// ```javascript
407    /// export * from "mod";
408    /// ```
409    ExportStar,
410
411    /// ```javascript
412    /// import foo = require("foo");
413    /// ```
414    ImportEqual(Id),
415}
416
417impl From<ImportSpecifier> for LinkSpecifier {
418    fn from(i: ImportSpecifier) -> Self {
419        match i {
420            ImportSpecifier::Namespace(ImportStarAsSpecifier { local, .. }) => {
421                Self::ImportStarAs(local.to_id())
422            }
423
424            ImportSpecifier::Default(ImportDefaultSpecifier { local, .. }) => {
425                Self::ImportDefault(local.to_id())
426            }
427            ImportSpecifier::Named(ImportNamedSpecifier {
428                is_type_only: false,
429                local,
430                imported: Some(ModuleExportName::Ident(Ident { sym: s, .. })),
431                ..
432            }) if &*s == "default" => Self::ImportDefault(local.to_id()),
433
434            ImportSpecifier::Named(ImportNamedSpecifier {
435                is_type_only: false,
436                local,
437                imported: Some(ModuleExportName::Str(Str { value: s, .. })),
438                ..
439            }) if &s == "default" => Self::ImportDefault(local.to_id()),
440
441            ImportSpecifier::Named(ImportNamedSpecifier {
442                is_type_only: false,
443                local,
444                imported,
445                ..
446            }) => {
447                let imported = imported.and_then(|e| match e {
448                    ModuleExportName::Ident(Ident { sym, .. }) => Some(sym),
449                    ModuleExportName::Str(Str { value, .. }) => value.as_atom().cloned(),
450                    #[cfg(swc_ast_unknown)]
451                    _ => panic!("unable to access unknown nodes"),
452                });
453
454                Self::ImportNamed {
455                    local: local.to_id(),
456                    imported,
457                }
458            }
459            _ => Self::Empty,
460        }
461    }
462}
463
464impl From<ExportSpecifier> for LinkSpecifier {
465    fn from(e: ExportSpecifier) -> Self {
466        match e {
467            ExportSpecifier::Namespace(ExportNamespaceSpecifier {
468                name:
469                    ModuleExportName::Str(Str {
470                        span, value: sym, ..
471                    }),
472                ..
473            }) => Self::ExportStarAs(
474                sym.to_atom_lossy().into_owned(),
475                (span, SyntaxContext::empty()),
476            ),
477            ExportSpecifier::Namespace(ExportNamespaceSpecifier {
478                name: ModuleExportName::Ident(Ident { span, sym, .. }),
479                ..
480            }) => Self::ExportStarAs(sym, (span, SyntaxContext::empty())),
481
482            ExportSpecifier::Default(ExportDefaultSpecifier { exported }) => {
483                // https://github.com/tc39/proposal-export-default-from
484                Self::ExportDefaultAs(
485                    (exported.span, exported.ctxt),
486                    exported.sym,
487                    (exported.span, exported.ctxt),
488                )
489            }
490
491            ExportSpecifier::Named(ExportNamedSpecifier {
492                is_type_only: false,
493                orig,
494                exported,
495                ..
496            }) => {
497                let orig = match orig {
498                    ModuleExportName::Ident(Ident { span, sym, .. }) => {
499                        (sym, (span, SyntaxContext::empty().apply_mark(Mark::new())))
500                    }
501                    ModuleExportName::Str(Str {
502                        span, value: sym, ..
503                    }) => (
504                        sym.to_atom_lossy().into_owned(),
505                        (span, SyntaxContext::empty().apply_mark(Mark::new())),
506                    ),
507                    #[cfg(swc_ast_unknown)]
508                    _ => panic!("unable to access unknown nodes"),
509                };
510
511                let exported = exported.map(|exported| match exported {
512                    ModuleExportName::Ident(Ident { span, sym, .. }) => {
513                        (sym, (span, SyntaxContext::empty().apply_mark(Mark::new())))
514                    }
515                    ModuleExportName::Str(Str {
516                        span, value: sym, ..
517                    }) => (
518                        sym.to_atom_lossy().into_owned(),
519                        (span, SyntaxContext::empty().apply_mark(Mark::new())),
520                    ),
521                    #[cfg(swc_ast_unknown)]
522                    _ => panic!("unable to access unknown nodes"),
523                });
524
525                match (&*orig.0, orig.1) {
526                    ("default", default_span) => {
527                        let (sym, span) = exported.unwrap_or(orig);
528
529                        Self::ExportDefaultAs(default_span, sym, span)
530                    }
531                    _ => Self::ExportNamed { orig, exported },
532                }
533            }
534            _ => Self::Empty,
535        }
536    }
537}
538
539#[derive(Debug, Default)]
540pub struct LinkItem(pub SpanCtx, pub FxHashSet<LinkSpecifier>, pub LinkFlag);
541
542use bitflags::bitflags;
543
544bitflags! {
545    #[derive(Default, Debug)]
546    pub struct LinkFlag: u8 {
547        const NAMED = 1 << 0;
548        const DEFAULT = 1 << 1;
549        const NAMESPACE = Self::NAMED.bits() | Self::DEFAULT.bits();
550        const EXPORT_STAR = 1 << 2;
551        const IMPORT_EQUAL = 1 << 3;
552    }
553}
554
555impl LinkFlag {
556    pub fn interop(&self) -> bool {
557        self.intersects(Self::DEFAULT)
558    }
559
560    pub fn has_named(&self) -> bool {
561        self.intersects(Self::NAMED)
562    }
563
564    pub fn namespace(&self) -> bool {
565        self.contains(Self::NAMESPACE)
566    }
567
568    pub fn need_raw_import(&self) -> bool {
569        self.interop() && self.intersects(Self::IMPORT_EQUAL)
570    }
571
572    pub fn export_star(&self) -> bool {
573        self.intersects(Self::EXPORT_STAR)
574    }
575}
576
577impl From<&LinkSpecifier> for LinkFlag {
578    fn from(s: &LinkSpecifier) -> Self {
579        match s {
580            LinkSpecifier::Empty => Self::empty(),
581            LinkSpecifier::ImportStarAs(..) => Self::NAMESPACE,
582            LinkSpecifier::ImportDefault(..) => Self::DEFAULT,
583            LinkSpecifier::ImportNamed { .. } => Self::NAMED,
584
585            LinkSpecifier::ExportStarAs(..) => Self::NAMESPACE,
586            LinkSpecifier::ExportDefaultAs(..) => Self::DEFAULT,
587            LinkSpecifier::ExportNamed { .. } => Self::NAMED,
588
589            LinkSpecifier::ImportEqual(..) => Self::IMPORT_EQUAL,
590            LinkSpecifier::ExportStar => Self::EXPORT_STAR,
591        }
592    }
593}
594
595impl From<&ImportSpecifier> for LinkFlag {
596    fn from(i: &ImportSpecifier) -> Self {
597        match i {
598            ImportSpecifier::Namespace(..) => Self::NAMESPACE,
599
600            ImportSpecifier::Default(ImportDefaultSpecifier { .. }) => Self::DEFAULT,
601
602            ImportSpecifier::Named(ImportNamedSpecifier {
603                is_type_only: false,
604                imported: Some(ModuleExportName::Ident(Ident { sym: default, .. })),
605                ..
606            }) if &**default == "default" => Self::DEFAULT,
607
608            ImportSpecifier::Named(ImportNamedSpecifier {
609                is_type_only: false,
610                imported: Some(ModuleExportName::Str(Str { value: default, .. })),
611                ..
612            }) if default == "default" => Self::DEFAULT,
613
614            ImportSpecifier::Named(ImportNamedSpecifier {
615                is_type_only: false,
616                ..
617            }) => Self::NAMED,
618
619            _ => Self::empty(),
620        }
621    }
622}
623
624impl From<&ExportSpecifier> for LinkFlag {
625    fn from(e: &ExportSpecifier) -> Self {
626        match e {
627            ExportSpecifier::Namespace(..) => Self::NAMESPACE,
628
629            // https://github.com/tc39/proposal-export-default-from
630            ExportSpecifier::Default(..) => Self::DEFAULT,
631
632            ExportSpecifier::Named(ExportNamedSpecifier {
633                is_type_only: false,
634                orig: ModuleExportName::Str(Str { value: s, .. }),
635                ..
636            }) if s == "default" => Self::DEFAULT,
637
638            ExportSpecifier::Named(ExportNamedSpecifier {
639                is_type_only: false,
640                orig: ModuleExportName::Ident(Ident { sym: s, .. }),
641                ..
642            }) if &**s == "default" => Self::DEFAULT,
643
644            ExportSpecifier::Named(ExportNamedSpecifier {
645                is_type_only: false,
646                ..
647            }) => Self::NAMED,
648
649            _ => Self::empty(),
650        }
651    }
652}
653
654impl Extend<LinkSpecifier> for LinkItem {
655    fn extend<T: IntoIterator<Item = LinkSpecifier>>(&mut self, iter: T) {
656        iter.into_iter().for_each(|link| {
657            self.insert(link);
658        });
659    }
660}
661
662impl LinkItem {
663    fn mut_dummy_span(&mut self, span: Span) -> &mut Self {
664        if self.0 .0.is_dummy() {
665            self.0 .0 = span;
666        }
667
668        self
669    }
670
671    fn insert(&mut self, link: LinkSpecifier) -> bool {
672        self.2 |= (&link).into();
673        self.1.insert(link)
674    }
675}
676
677pub(crate) type ExportObjPropList = Vec<ExportKV>;
678
679/// Reduce self to generate ImportMap and ExportObjPropList
680pub(crate) trait LinkSpecifierReducer {
681    fn reduce(
682        self,
683        import_map: &mut ImportMap,
684        export_obj_prop_list: &mut ExportObjPropList,
685        mod_ident: &Ident,
686        raw_mod_ident: &Option<Ident>,
687        ref_to_mod_ident: &mut bool,
688        // do not emit `mod.default`, emit `mod` instead
689        default_nowrap: bool,
690    );
691}
692
693impl LinkSpecifierReducer for FxHashSet<LinkSpecifier> {
694    fn reduce(
695        self,
696        import_map: &mut ImportMap,
697        export_obj_prop_list: &mut ExportObjPropList,
698        mod_ident: &Ident,
699        raw_mod_ident: &Option<Ident>,
700        ref_to_mod_ident: &mut bool,
701        default_nowrap: bool,
702    ) {
703        self.into_iter().for_each(|s| match s {
704            LinkSpecifier::ImportNamed { imported, local } => {
705                *ref_to_mod_ident = true;
706
707                import_map.insert(
708                    local.clone(),
709                    (mod_ident.clone(), imported.or(Some(local.0))),
710                );
711            }
712            LinkSpecifier::ImportDefault(id) => {
713                *ref_to_mod_ident = true;
714
715                import_map.insert(
716                    id,
717                    (
718                        mod_ident.clone(),
719                        (!default_nowrap).then(|| atom!("default")),
720                    ),
721                );
722            }
723            LinkSpecifier::ImportStarAs(id) => {
724                *ref_to_mod_ident = true;
725
726                import_map.insert(id, (mod_ident.clone(), None));
727            }
728            LinkSpecifier::ExportNamed { orig, exported } => {
729                *ref_to_mod_ident = true;
730
731                // ```javascript
732                // export { foo as bar } from "mod";
733                //
734                // import { foo } from "mod";
735                // export { foo as bar };
736                // ```
737
738                // foo -> mod.foo
739                import_map.insert(
740                    (orig.0.clone(), orig.1 .1),
741                    (mod_ident.clone(), Some(orig.0.clone())),
742                );
743
744                let (export_name, export_name_span) = exported.unwrap_or_else(|| orig.clone());
745
746                // bar -> foo
747                export_obj_prop_list.push((
748                    export_name,
749                    ExportItem::new(export_name_span, quote_ident!(orig.1 .1, orig.1 .0, orig.0)),
750                ))
751            }
752            LinkSpecifier::ExportDefaultAs(_, key, span) => {
753                *ref_to_mod_ident = true;
754
755                // ```javascript
756                // export { default as foo } from "mod";
757                //
758                // import { default as foo } from "mod";
759                // export { foo };
760                // ```
761
762                // foo -> mod.default
763                import_map.insert(
764                    (key.clone(), span.1),
765                    (mod_ident.clone(), Some(atom!("default"))),
766                );
767
768                export_obj_prop_list.push((
769                    key.clone(),
770                    ExportItem::new(span, quote_ident!(span.1, span.0, key)),
771                ));
772            }
773            LinkSpecifier::ExportStarAs(key, span) => {
774                *ref_to_mod_ident = true;
775
776                export_obj_prop_list.push((key, ExportItem::new(span, mod_ident.clone())));
777            }
778            LinkSpecifier::ExportStar => {}
779            LinkSpecifier::ImportEqual(id) => {
780                *ref_to_mod_ident = true;
781
782                import_map.insert(
783                    id,
784                    (
785                        raw_mod_ident.clone().unwrap_or_else(|| mod_ident.clone()),
786                        None,
787                    ),
788                );
789            }
790            LinkSpecifier::Empty => {}
791        })
792    }
793}
794
795#[derive(Debug)]
796pub struct ExportItem(SpanCtx, Ident);
797
798impl ExportItem {
799    pub fn new(export_name_span: SpanCtx, local_ident: Ident) -> Self {
800        Self(export_name_span, local_ident)
801    }
802
803    pub fn export_name_span(&self) -> SpanCtx {
804        self.0
805    }
806
807    pub fn into_local_ident(self) -> Ident {
808        self.1
809    }
810}
811
812pub type ExportKV = (Atom, ExportItem);