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
11pub type Link = IndexMap<Atom, LinkItem>;
13pub type Export = FxHashMap<Atom, ExportItem>;
15
16#[derive(Debug)]
17pub struct ModuleDeclStrip {
18 pub link: Link,
20
21 pub export: Export,
26
27 pub export_assign: Option<Box<Expr>>,
29
30 pub has_module_decl: bool,
31
32 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 module_decl.visit_mut_with(self);
64 self.has_module_decl = true;
65
66 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 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 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 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 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 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 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 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 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 Empty,
364
365 ImportNamed { imported: Option<Atom>, local: Id },
371
372 ImportDefault(Id),
376
377 ImportStarAs(Id),
381
382 ExportNamed {
388 orig: (Atom, SpanCtx),
389 exported: Option<(Atom, SpanCtx)>,
390 },
391
392 ExportDefaultAs(SpanCtx, Atom, SpanCtx),
399
400 ExportStarAs(Atom, SpanCtx),
405
406 ExportStar,
410
411 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 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 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
679pub(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 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 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 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 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);