swc_bundler/bundler/chunk/
merge.rs

1use std::sync::atomic::Ordering;
2
3use anyhow::Error;
4use indexmap::IndexSet;
5use petgraph::EdgeDirection;
6use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
7use swc_atoms::atom;
8use swc_common::{sync::Lock, FileName, SyntaxContext, DUMMY_SP};
9use swc_ecma_ast::*;
10use swc_ecma_transforms_base::helpers::Helpers;
11use swc_ecma_utils::{find_pat_ids, prepend_stmt, private_ident, quote_ident, ExprFactory};
12use swc_ecma_visit::{VisitMut, VisitMutWith};
13use EdgeDirection::Outgoing;
14
15use crate::{
16    bundler::{keywords::KeywordRenamer, load::TransformedModule},
17    dep_graph::ModuleGraph,
18    id::{Id, ModuleId},
19    inline::inline,
20    load::Load,
21    modules::Modules,
22    resolve::Resolve,
23    util::{CloneMap, ExportMetadata, ExprExt, VarDeclaratorExt},
24    Bundler, Hook, ModuleRecord,
25};
26
27pub(super) struct Ctx {
28    /// Full dependency graph.
29    pub graph: ModuleGraph,
30    pub cycles: Vec<Vec<ModuleId>>,
31    pub transitive_remap: CloneMap<SyntaxContext, SyntaxContext>,
32    pub export_stars_in_wrapped: Lock<FxHashMap<ModuleId, Vec<SyntaxContext>>>,
33}
34
35impl Ctx {
36    pub fn is_exported_ctxt(
37        &self,
38        ctxt_to_check: SyntaxContext,
39        entry_export_ctxt: SyntaxContext,
40    ) -> bool {
41        if ctxt_to_check == entry_export_ctxt {
42            return true;
43        }
44
45        if let Some(v) = self.transitive_remap.get(&ctxt_to_check) {
46            if v == ctxt_to_check {
47                return false;
48            }
49
50            return self.is_exported_ctxt(v, entry_export_ctxt);
51        }
52
53        false
54    }
55}
56
57impl<L, R> Bundler<'_, L, R>
58where
59    L: Load,
60    R: Resolve,
61{
62    pub(super) fn get_for_merging(
63        &self,
64        ctx: &Ctx,
65        id: ModuleId,
66        is_entry: bool,
67    ) -> Result<Modules, Error> {
68        self.run(|| {
69            let info = self
70                .scope
71                .get_module(id)
72                .unwrap_or_else(|| unreachable!("Module {} is not registered", id));
73            let mut module = self.apply_hooks(id, is_entry)?;
74            module = self.prepare_for_merging(ctx, &info, module)?;
75
76            if !is_entry {
77                module = self.wrap_cjs_module(ctx, &info, module)?;
78            }
79            self.replace_cjs_require_calls(&info, &mut module, is_entry);
80
81            Ok(module)
82        })
83    }
84
85    /// This method sort modules.
86    pub(super) fn merge_into_entry(
87        &self,
88        ctx: &Ctx,
89        entry_id: ModuleId,
90        entry: &mut Modules,
91        all: &mut FxHashMap<ModuleId, Modules>,
92    ) {
93        self.run(|| {
94            let injected_ctxt = self.injected_ctxt;
95
96            let entry_info = self.scope.get_module(entry_id).unwrap();
97
98            let all_deps_of_entry =
99                self.collect_all_deps(&ctx.graph, entry_id, &mut Default::default());
100
101            tracing::debug!("Merging dependencies: {:?}", all_deps_of_entry);
102
103            let deps = all_deps_of_entry.iter().map(|id| {
104                let dep_info = self.scope.get_module(*id).unwrap();
105                entry_info.helpers.extend(&dep_info.helpers);
106
107                {
108                    let helpers = *entry_info.swc_helpers.lock();
109                    let dep_helpers = *dep_info.swc_helpers.lock();
110
111                    let helpers = Helpers::from_data(helpers);
112                    let dep_helpers = Helpers::from_data(dep_helpers);
113
114                    helpers.extend_from(&dep_helpers);
115
116                    *entry_info.swc_helpers.lock() = helpers.data();
117                }
118
119                if *id == entry_id {
120                    return Modules::empty(injected_ctxt);
121                }
122
123                all.remove(id).unwrap_or_else(|| {
124                    unreachable!(
125                        "failed to merge into {}: module {} does not exist in the map",
126                        entry_id, id
127                    )
128                })
129            });
130
131            for dep in deps {
132                entry.add_dep(dep);
133            }
134
135            self.replace_import_specifiers(&entry_info, entry);
136            self.finalize_merging_of_entry(ctx, entry_id, entry);
137            self.remove_wrong_exports(ctx, &entry_info, entry);
138        })
139    }
140
141    #[allow(clippy::only_used_in_recursion)]
142    fn collect_all_deps(
143        &self,
144        graph: &ModuleGraph,
145        start: ModuleId,
146        dejavu: &mut FxHashSet<ModuleId>,
147    ) -> IndexSet<ModuleId, FxBuildHasher> {
148        let mut set = IndexSet::default();
149
150        for dep in graph.neighbors_directed(start, Outgoing) {
151            if !dejavu.insert(dep) {
152                continue;
153            }
154            set.insert(dep);
155            set.extend(self.collect_all_deps(graph, dep, dejavu));
156        }
157
158        set
159    }
160
161    pub(crate) fn apply_hooks(
162        &self,
163        module_id: ModuleId,
164        is_entry: bool,
165    ) -> Result<Modules, Error> {
166        self.run(|| {
167            let info = self.scope.get_module(module_id).unwrap();
168
169            let mut entry: Module = (*info.module).clone();
170            entry.visit_mut_with(&mut ImportMetaHandler {
171                file: &info.fm.name,
172                hook: &self.hook,
173                is_entry,
174                inline_ident: private_ident!("importMeta"),
175                occurred: false,
176                err: None,
177            });
178
179            let module = Modules::from(module_id, entry, self.injected_ctxt);
180
181            Ok(module)
182        })
183    }
184
185    /// This should only be called after everything is merged.
186    ///
187    /// This method does not care about orders of statement, and it's expected
188    /// to be called before `sort`.
189    fn inject_reexports(&self, ctx: &Ctx, _entry_id: ModuleId, entry: &mut Modules) {
190        // dbg!(&ctx.transitive_remap);
191        let injected_ctxt = self.injected_ctxt;
192
193        {
194            // Handle `export *` for non-wrapped modules.
195
196            let mut vars = Vec::new();
197            /// We recurse if `export *` is nested.
198            fn add_var(
199                injected_ctxt: SyntaxContext,
200                vars: &mut Vec<(ModuleId, ModuleItem)>,
201                declared: &mut FxHashSet<Id>,
202                map: &CloneMap<SyntaxContext, SyntaxContext>,
203                module_id: ModuleId,
204                id: Id,
205            ) {
206                let remapped = match map.get(&id.ctxt()) {
207                    Some(v) => v,
208                    _ => return,
209                };
210                if remapped == id.ctxt() {
211                    return;
212                }
213                let reexported = id.clone().with_ctxt(remapped);
214
215                add_var(
216                    injected_ctxt,
217                    vars,
218                    declared,
219                    map,
220                    module_id,
221                    reexported.clone(),
222                );
223
224                if !declared.insert(reexported.clone()) {
225                    return;
226                }
227
228                vars.push((
229                    module_id,
230                    id.assign_to(reexported)
231                        .into_module_item(injected_ctxt, "export_star_replacer"),
232                ));
233            }
234
235            // We have to exclude some ids because there are already declared.
236            // See https://github.com/denoland/deno/issues/8725
237            //
238            // Let's say D is a dependency which contains export * from './foo';
239            // If an user import and export from D, the transitive syntax context map
240            // contains a entry from D to foo because it's reexported and
241            // the variable (reexported from D) exist because it's imported.
242            let mut declared_ids = FxHashSet::<_>::default();
243
244            for (_, stmt) in entry.iter() {
245                if let ModuleItem::Stmt(Stmt::Decl(Decl::Var(decl))) = stmt {
246                    if decl.ctxt == injected_ctxt {
247                        let ids: Vec<Id> = find_pat_ids(decl);
248                        declared_ids.extend(ids);
249                    }
250                }
251            }
252
253            for (module_id, stmt) in entry.iter() {
254                match stmt {
255                    ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(export)) => {
256                        for s in &export.specifiers {
257                            match s {
258                                ExportSpecifier::Namespace(_) => {}
259                                ExportSpecifier::Default(_) => {}
260                                ExportSpecifier::Named(named) => {
261                                    if let Some(exported) = &named.exported {
262                                        let exported = match exported {
263                                            ModuleExportName::Ident(ident) => ident,
264                                            ModuleExportName::Str(..) => {
265                                                unimplemented!("module string names unimplemented")
266                                            }
267                                            #[cfg(swc_ast_unknown)]
268                                            _ => panic!("unable to access unknown nodes"),
269                                        };
270
271                                        let id: Id = exported.into();
272                                        if declared_ids.contains(&id) {
273                                            continue;
274                                        }
275
276                                        match &named.orig {
277                                            ModuleExportName::Ident(orig) => {
278                                                vars.push((
279                                                    module_id,
280                                                    orig.clone()
281                                                        .assign_to(exported.clone())
282                                                        .into_module_item(
283                                                            injected_ctxt,
284                                                            "finalize -> export to var",
285                                                        ),
286                                                ));
287                                            }
288                                            ModuleExportName::Str(..) => {
289                                                unimplemented!("module string names unimplemented")
290                                            }
291                                            #[cfg(swc_ast_unknown)]
292                                            _ => panic!("unable to access unknown nodes"),
293                                        }
294                                    }
295                                }
296                                #[cfg(swc_ast_unknown)]
297                                _ => panic!("unable to access unknown nodes"),
298                            }
299                        }
300                    }
301
302                    ModuleItem::Stmt(Stmt::Decl(Decl::Var(decl))) => {
303                        let ids: Vec<Id> = find_pat_ids(decl);
304
305                        for id in ids {
306                            if *id.sym() == "default" {
307                                continue;
308                            }
309
310                            add_var(
311                                injected_ctxt,
312                                &mut vars,
313                                &mut declared_ids,
314                                &ctx.transitive_remap,
315                                module_id,
316                                id,
317                            );
318                        }
319                    }
320
321                    _ => {}
322                }
323            }
324
325            entry.append_all(vars);
326        }
327
328        {
329            let mut map = ctx.export_stars_in_wrapped.lock();
330            let mut additional_props = FxHashMap::<_, Vec<_>>::default();
331            // Handle `export *` for wrapped modules.
332            for (module_id, ctxts) in map.drain() {
333                for (_, stmt) in entry.iter() {
334                    if let ModuleItem::Stmt(Stmt::Decl(Decl::Var(decl))) = stmt {
335                        let ids: Vec<Id> = find_pat_ids(decl);
336
337                        for id in ids {
338                            if *id.sym() == "default" {
339                                continue;
340                            }
341
342                            if ctxts.contains(&id.ctxt()) {
343                                additional_props.entry(module_id).or_default().push(
344                                    PropOrSpread::Prop(Box::new(Prop::Shorthand(id.into_ident()))),
345                                );
346                            }
347                        }
348                    }
349                }
350            }
351
352            for (module_id, props) in additional_props {
353                let id = match self.scope.wrapped_esm_id(module_id) {
354                    Some(v) => v,
355                    None => continue,
356                };
357
358                for (_, stmt) in entry.iter_mut() {
359                    let var = match stmt {
360                        ModuleItem::Stmt(Stmt::Decl(Decl::Var(var)))
361                            if matches!(
362                                &**var,
363                                VarDecl {
364                                    kind: VarDeclKind::Const,
365                                    ..
366                                }
367                            ) =>
368                        {
369                            var
370                        }
371                        _ => continue,
372                    };
373
374                    if var.decls.len() != 1 {
375                        continue;
376                    }
377
378                    let var_decl = &mut var.decls[0];
379                    match &var_decl.name {
380                        Pat::Ident(i) if id == i.id => {}
381                        _ => continue,
382                    }
383
384                    let callee = match &mut var_decl.init {
385                        Some(init) => match &mut **init {
386                            Expr::Call(CallExpr { callee, .. }) => match callee {
387                                Callee::Super(_) | Callee::Import(_) => continue,
388                                Callee::Expr(v) => v,
389                                #[cfg(swc_ast_unknown)]
390                                _ => panic!("unable to access unknown nodes"),
391                            },
392                            Expr::Await(AwaitExpr { arg, .. }) => match &mut **arg {
393                                Expr::Call(CallExpr { callee, .. }) => match callee {
394                                    Callee::Super(_) | Callee::Import(_) => continue,
395                                    Callee::Expr(v) => v,
396                                    #[cfg(swc_ast_unknown)]
397                                    _ => panic!("unable to access unknown nodes"),
398                                },
399                                _ => continue,
400                            },
401                            _ => continue,
402                        },
403                        None => continue,
404                    };
405
406                    let f = match &mut **callee {
407                        Expr::Fn(f) => f,
408                        _ => continue,
409                    };
410
411                    let body = match &mut f.function.body {
412                        Some(body) => body,
413                        None => continue,
414                    };
415
416                    let last_stmt = body.stmts.last_mut();
417
418                    let return_stmt = match last_stmt {
419                        Some(Stmt::Return(s)) => s,
420                        _ => continue,
421                    };
422
423                    let ret_val = match &mut return_stmt.arg {
424                        Some(arg) => arg,
425                        None => continue,
426                    };
427
428                    let obj = match &mut **ret_val {
429                        Expr::Object(obj) => obj,
430                        _ => continue,
431                    };
432
433                    obj.props.extend(props);
434                    break;
435                }
436            }
437        }
438    }
439
440    fn finalize_merging_of_entry(&self, ctx: &Ctx, id: ModuleId, entry: &mut Modules) {
441        tracing::trace!("All modules are merged");
442
443        tracing::debug!("Injecting reexports");
444        self.inject_reexports(ctx, id, entry);
445
446        // entry.print(&self.cm, "before inline");
447
448        tracing::debug!("Inlining injected variables");
449
450        inline(self.injected_ctxt, entry);
451
452        entry.sort(id, &ctx.graph, &ctx.cycles, &self.cm);
453
454        // crate::debug::print_hygiene("done", &self.cm, &entry.clone().into());
455
456        entry.retain_mut(|_, item| {
457            match item {
458                ModuleItem::ModuleDecl(ModuleDecl::ExportAll(export)) => {
459                    let src_atom = export.src.value.to_atom_lossy().into_owned();
460                    if self.config.external_modules.contains(&src_atom) {
461                        return true;
462                    }
463
464                    return false;
465                }
466
467                ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(export)) => {
468                    if let Some(src) = &export.src {
469                        let src_atom = src.value.to_atom_lossy().into_owned();
470                        if self.config.external_modules.contains(&src_atom) {
471                            return true;
472                        }
473                    }
474
475                    export
476                        .specifiers
477                        .retain(|s| !matches!(s, ExportSpecifier::Namespace(_)));
478
479                    export.src = None;
480                }
481
482                ModuleItem::ModuleDecl(ModuleDecl::Import(import)) => {
483                    let src_atom = import.src.value.to_atom_lossy().into_owned();
484                    if self.config.external_modules.contains(&src_atom) {
485                        return true;
486                    }
487
488                    // Drop import statements.
489                    return false;
490                }
491
492                _ => {}
493            }
494
495            true
496        });
497
498        tracing::debug!("Renaming keywords");
499
500        entry.visit_mut_with(&mut KeywordRenamer::default());
501
502        // print_hygiene(
503        //     "done-clean",
504        //     &self.cm,
505        //     &entry
506        //         .clone()
507        //         .fold_with(&mut hygiene())
508        //         .fold_with(&mut fixer(None)),
509        // );
510    }
511
512    /// Remove exports with wrong syntax context
513    fn remove_wrong_exports(&self, ctx: &Ctx, info: &TransformedModule, module: &mut Modules) {
514        tracing::debug!("Removing wrong exports");
515
516        let item_count = module.iter().count();
517        tracing::trace!("Item count = {}", item_count);
518
519        module.retain_mut(|_, item| {
520            if let ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(NamedExport {
521                specifiers, ..
522            })) = item
523            {
524                specifiers.retain(|s| match s {
525                    ExportSpecifier::Named(ExportNamedSpecifier {
526                        exported: Some(exported),
527                        ..
528                    }) => {
529                        let exported = match exported {
530                            ModuleExportName::Ident(ident) => ident,
531                            ModuleExportName::Str(..) => {
532                                unimplemented!("module string names unimplemented")
533                            }
534                            #[cfg(swc_ast_unknown)]
535                            _ => panic!("unable to access unknown nodes"),
536                        };
537                        // Default is not exported via `export *`
538                        if &*exported.sym == "default" {
539                            exported.ctxt == info.export_ctxt()
540                        } else {
541                            ctx.is_exported_ctxt(exported.ctxt, info.export_ctxt())
542                        }
543                    }
544                    _ => true,
545                });
546
547                if specifiers.is_empty() {
548                    return false;
549                }
550            }
551
552            true
553        });
554
555        tracing::debug!("Removed wrong exports");
556    }
557
558    /// This method handles imports and exports.
559    ///
560    ///
561    /// Basically one module have two top-level contexts. One is for it's codes
562    /// and another is for exporting. This method connects two module by
563    /// injecting `const local_A = exported_B_from_foo;`
564    ///
565    ///
566    /// We convert all exports to variable at here.
567    pub(super) fn prepare_for_merging(
568        &self,
569        ctx: &Ctx,
570        info: &TransformedModule,
571        mut module: Modules,
572    ) -> Result<Modules, Error> {
573        self.handle_imports_and_exports(ctx, info, &mut module);
574
575        let wrapped = self.scope.should_be_wrapped_with_a_fn(info.id);
576        if wrapped {
577            module = self.wrap_esm(ctx, info.id, module)?;
578        }
579
580        Ok(module)
581    }
582
583    fn handle_imports_and_exports(
584        &self,
585        ctx: &Ctx,
586        info: &TransformedModule,
587        module: &mut Modules,
588    ) {
589        let injected_ctxt = self.injected_ctxt;
590
591        if !info.is_es6 {
592            return;
593        }
594
595        let mut extra = Vec::new();
596
597        module.map_any_items(|_, items| {
598            let mut new = Vec::with_capacity(items.len() * 11 / 10);
599
600            for item in items {
601                match item {
602                    ModuleItem::ModuleDecl(ModuleDecl::Import(mut import)) => {
603                        let src_atom = import.src.value.to_atom_lossy().into_owned();
604                        // Preserve imports from node.js builtin modules.
605                        if self.config.external_modules.contains(&src_atom) {
606                            new.push(import.into());
607                            continue;
608                        }
609
610                        if let Some((src, _)) = info
611                            .imports
612                            .specifiers
613                            .iter()
614                            .find(|s| s.0.src.value == import.src.value)
615                        {
616                            if !self.scope.get_module(src.module_id).unwrap().is_es6 {
617                                new.push(import.into());
618                                continue;
619                            }
620                        }
621
622                        // Imports are easy to handle.
623                        for s in &import.specifiers {
624                            match s {
625                                ImportSpecifier::Named(s) => {
626                                    if let Some(imported) = &s.imported {
627                                        let imported = match imported {
628                                            ModuleExportName::Ident(ident) => ident,
629                                            ModuleExportName::Str(..) => {
630                                                unimplemented!("module string names unimplemented")
631                                            }
632                                            #[cfg(swc_ast_unknown)]
633                                            _ => panic!("unable to access unknown nodes"),
634                                        };
635                                        new.push(
636                                            imported
637                                                .clone()
638                                                .assign_to(s.local.clone())
639                                                .into_module_item(
640                                                    injected_ctxt,
641                                                    "prepare -> named import -> aliased",
642                                                ),
643                                        );
644                                    }
645                                }
646                                ImportSpecifier::Default(s) => {
647                                    new.push(
648                                        Ident::new(
649                                            atom!("default"),
650                                            import.span,
651                                            ExportMetadata::decode(import.with.as_deref())
652                                                .export_ctxt
653                                                .unwrap(),
654                                        )
655                                        .assign_to(s.local.clone())
656                                        .into_module_item(
657                                            injected_ctxt,
658                                            "prepare -> default import",
659                                        ),
660                                    );
661                                }
662                                ImportSpecifier::Namespace(s) => {
663                                    if let Some((src, _)) = info
664                                        .imports
665                                        .specifiers
666                                        .iter()
667                                        .find(|s| s.0.src.value == import.src.value)
668                                    {
669                                        let esm_id =
670                                            self.scope.wrapped_esm_id(src.module_id).expect(
671                                                "If a namespace import specifier is preserved, it \
672                                                 means failure of deblobbing and as a result \
673                                                 module should be marked as wrapped esm",
674                                            );
675                                        new.push(
676                                            esm_id
677                                                .clone()
678                                                .assign_to(s.local.clone())
679                                                .into_module_item(
680                                                    injected_ctxt,
681                                                    "prepare -> import -> namespace",
682                                                ),
683                                        );
684                                    }
685                                }
686                                #[cfg(swc_ast_unknown)]
687                                _ => panic!("unable to access unknown nodes"),
688                            }
689                        }
690
691                        import.specifiers.clear();
692                        new.push(import.into());
693                    }
694                    ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(export)) => {
695                        // At here, we create multiple items.
696                        //
697                        // One item is `const local_default = expr` and another one is
698                        // `export { local_default as default }`.
699                        //
700                        // To allow using identifier of the declaration in the original module, we
701                        // create `const local_default = orig_ident` if original identifier exists.
702
703                        let local = Ident::new(atom!("default"), DUMMY_SP, info.local_ctxt());
704
705                        match export.decl {
706                            DefaultDecl::Class(c) => {
707                                //
708                                match c.ident {
709                                    Some(ident) => {
710                                        new.push(
711                                            ClassDecl {
712                                                ident: ident.clone(),
713                                                class: c.class,
714                                                declare: false,
715                                            }
716                                            .into(),
717                                        );
718
719                                        new.push(ident.assign_to(local.clone()).into_module_item(
720                                            injected_ctxt,
721                                            "prepare -> export default decl -> class -> with ident",
722                                        ))
723                                    }
724                                    None => {
725                                        let init = c;
726                                        new.push(init.assign_to(local.clone()).into_module_item(
727                                            injected_ctxt,
728                                            "prepare -> export default decl -> class -> without \
729                                             ident",
730                                        ));
731                                    }
732                                }
733                            }
734                            DefaultDecl::Fn(f) => {
735                                //
736                                match f.ident {
737                                    Some(ident) => {
738                                        new.push(
739                                            FnDecl {
740                                                ident: ident.clone(),
741                                                function: f.function,
742                                                declare: false,
743                                            }
744                                            .into(),
745                                        );
746
747                                        new.push(ident.assign_to(local.clone()).into_module_item(
748                                            injected_ctxt,
749                                            "prepare -> export default decl -> function -> with \
750                                             ident",
751                                        ))
752                                    }
753                                    None => {
754                                        // We should inject a function declaration because of
755                                        // dependencies.
756                                        //
757                                        // See: https://github.com/denoland/deno/issues/9346
758                                        let ident = private_ident!("default");
759                                        new.push(
760                                            FnDecl {
761                                                ident: ident.clone(),
762                                                function: f.function,
763                                                declare: false,
764                                            }
765                                            .into(),
766                                        );
767
768                                        new.push(ident.assign_to(local.clone()).into_module_item(
769                                            injected_ctxt,
770                                            "prepare -> export default decl -> function -> \
771                                             without ident",
772                                        ));
773                                    }
774                                }
775                            }
776                            DefaultDecl::TsInterfaceDecl(_) => continue,
777                            #[cfg(swc_ast_unknown)]
778                            _ => panic!("unable to access unknown nodes"),
779                        }
780
781                        // Create `export { local_default as default }`
782                        tracing::trace!(
783                            "Exporting `default` with `export default decl` ({})",
784                            local.sym
785                        );
786
787                        let exported = Ident::new(atom!("default"), DUMMY_SP, info.export_ctxt());
788
789                        new.push(
790                            local
791                                .clone()
792                                .assign_to(exported.clone())
793                                .into_module_item(injected_ctxt, "prepare -> export default decl"),
794                        );
795
796                        let specifier = ExportSpecifier::Named(ExportNamedSpecifier {
797                            span: DUMMY_SP,
798                            orig: ModuleExportName::Ident(local),
799                            exported: Some(ModuleExportName::Ident(exported)),
800                            is_type_only: false,
801                        });
802                        extra.push(
803                            NamedExport {
804                                span: export.span,
805                                specifiers: vec![specifier],
806                                src: None,
807                                type_only: false,
808                                with: Some(
809                                    ExportMetadata {
810                                        injected: true,
811                                        ..Default::default()
812                                    }
813                                    .into_with(),
814                                ),
815                            }
816                            .into(),
817                        );
818                    }
819
820                    ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr(export)) => {
821                        // At here, we create two items.
822                        //
823                        // One item is `const local_default = expr` and the
824                        // other is `export { local_default as default }`.
825
826                        // TODO: Check if we really need this.
827
828                        let local = Ident::new(atom!("default"), DUMMY_SP, info.local_ctxt());
829
830                        // Create `const local_default = expr`
831                        new.push(
832                            export
833                                .expr
834                                .assign_to(local.clone())
835                                .into_module_item(injected_ctxt, "prepare -> export default expr"),
836                        );
837
838                        let exported = Ident::new(atom!("default"), DUMMY_SP, info.export_ctxt());
839
840                        new.push(
841                            local
842                                .clone()
843                                .assign_to(exported.clone())
844                                .into_module_item(injected_ctxt, "prepare -> export default expr"),
845                        );
846
847                        // Create `export { local_default as default }`
848                        let specifier = ExportSpecifier::Named(ExportNamedSpecifier {
849                            span: DUMMY_SP,
850                            orig: ModuleExportName::Ident(local),
851                            exported: Some(ModuleExportName::Ident(exported)),
852                            is_type_only: false,
853                        });
854                        tracing::trace!("Exporting `default` with `export default expr`");
855                        extra.push(
856                            NamedExport {
857                                span: export.span,
858                                specifiers: vec![specifier],
859                                src: None,
860                                type_only: false,
861                                with: Some(
862                                    ExportMetadata {
863                                        injected: true,
864                                        ..Default::default()
865                                    }
866                                    .into_with(),
867                                ),
868                            }
869                            .into(),
870                        );
871                    }
872
873                    ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export)) => {
874                        // Idea is almost same as above. But we uses symbol of the declaration
875                        // instead of using `default`.
876
877                        let local = match export.decl {
878                            Decl::Class(c) => {
879                                let i = c.ident.clone();
880                                new.push(c.into());
881
882                                i
883                            }
884                            Decl::Fn(f) => {
885                                let i = f.ident.clone();
886                                new.push(f.into());
887
888                                i
889                            }
890                            Decl::Var(v) => {
891                                let ids: Vec<Ident> = find_pat_ids(&v);
892                                //
893
894                                new.push(v.into());
895
896                                let export = NamedExport {
897                                    span: export.span,
898                                    specifiers: ids
899                                        .into_iter()
900                                        .map(|id| {
901                                            let exported = Ident::new(
902                                                id.sym.clone(),
903                                                id.span,
904                                                info.export_ctxt(),
905                                            );
906
907                                            tracing::trace!(
908                                                "Exporting `{}{:?}` with `export decl`",
909                                                id.sym,
910                                                id.ctxt
911                                            );
912
913                                            new.push(
914                                                id.clone()
915                                                    .assign_to(exported.clone())
916                                                    .into_module_item(
917                                                        injected_ctxt,
918                                                        "prepare -> export decl -> var",
919                                                    ),
920                                            );
921
922                                            ExportNamedSpecifier {
923                                                span: DUMMY_SP,
924                                                orig: ModuleExportName::Ident(id),
925                                                exported: Some(ModuleExportName::Ident(exported)),
926                                                is_type_only: false,
927                                            }
928                                        })
929                                        .map(ExportSpecifier::Named)
930                                        .collect(),
931                                    src: None,
932                                    type_only: false,
933                                    with: Some(
934                                        ExportMetadata {
935                                            injected: true,
936                                            ..Default::default()
937                                        }
938                                        .into_with(),
939                                    ),
940                                }
941                                .into();
942                                extra.push(export);
943                                continue;
944                            }
945
946                            Decl::TsInterface(_)
947                            | Decl::TsTypeAlias(_)
948                            | Decl::TsEnum(_)
949                            | Decl::TsModule(_)
950                            | Decl::Using(..) => continue,
951                            #[cfg(swc_ast_unknown)]
952                            _ => panic!("unable to access unknown nodes"),
953                        };
954
955                        tracing::trace!(
956                            "Exporting `default` with `export default decl` ({})",
957                            local.sym
958                        );
959
960                        // Create `export { local_ident as exported_ident }`
961                        let exported =
962                            Ident::new(local.sym.clone(), local.span, info.export_ctxt());
963
964                        new.push(
965                            local
966                                .clone()
967                                .assign_to(exported.clone())
968                                .into_module_item(injected_ctxt, "prepare -> export decl -> var"),
969                        );
970
971                        let specifier = ExportSpecifier::Named(ExportNamedSpecifier {
972                            span: DUMMY_SP,
973                            orig: ModuleExportName::Ident(local),
974                            exported: Some(ModuleExportName::Ident(exported)),
975                            is_type_only: false,
976                        });
977
978                        extra.push(
979                            NamedExport {
980                                span: export.span,
981                                specifiers: vec![specifier],
982                                src: None,
983                                type_only: false,
984                                with: Some(
985                                    ExportMetadata {
986                                        injected: true,
987                                        ..Default::default()
988                                    }
989                                    .into_with(),
990                                ),
991                            }
992                            .into(),
993                        );
994                    }
995
996                    ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(NamedExport {
997                        ref specifiers,
998                        ref src,
999                        ..
1000                    })) => {
1001                        if let Some(export_src) = src {
1002                            if let Some((src, _)) = info
1003                                .exports
1004                                .reexports
1005                                .iter()
1006                                .find(|s| s.0.src.value == export_src.value)
1007                            {
1008                                let dep = self.scope.get_module(src.module_id).unwrap();
1009                                if !dep.is_es6 {
1010                                    dep.helpers.require.store(true, Ordering::SeqCst);
1011
1012                                    let mut vars = Vec::new();
1013                                    let mod_var = private_ident!("_cjs_module_");
1014
1015                                    vars.push(VarDeclarator {
1016                                        span: DUMMY_SP,
1017                                        name: mod_var.clone().into(),
1018                                        init: Some(
1019                                            CallExpr {
1020                                                span: DUMMY_SP,
1021                                                callee: Ident::new(
1022                                                    atom!("load"),
1023                                                    DUMMY_SP,
1024                                                    dep.export_ctxt(),
1025                                                )
1026                                                .as_callee(),
1027                                                ..Default::default()
1028                                            }
1029                                            .into(),
1030                                        ),
1031                                        definite: Default::default(),
1032                                    });
1033                                    for s in specifiers {
1034                                        match s {
1035                                            ExportSpecifier::Namespace(s) => {
1036                                                match &s.name {
1037                                                    ModuleExportName::Ident(name) => {
1038                                                        vars.push(VarDeclarator {
1039                                                            span: s.span,
1040                                                            name: name.clone().into(),
1041                                                            init: Some(mod_var.clone().into()),
1042                                                            definite: Default::default(),
1043                                                        });
1044                                                    }
1045                                                    ModuleExportName::Str(..) => {
1046                                                        unimplemented!(
1047                                                            "module string names unimplemented"
1048                                                        )
1049                                                    }
1050                                                    #[cfg(swc_ast_unknown)]
1051                                                    _ => panic!("unable to access unknown nodes"),
1052                                                };
1053                                            }
1054                                            ExportSpecifier::Default(s) => {
1055                                                vars.push(VarDeclarator {
1056                                                    span: DUMMY_SP,
1057                                                    name: s.exported.clone().into(),
1058                                                    init: Some(
1059                                                        mod_var
1060                                                            .clone()
1061                                                            .make_member(quote_ident!("default"))
1062                                                            .into(),
1063                                                    ),
1064                                                    definite: Default::default(),
1065                                                });
1066                                            }
1067                                            ExportSpecifier::Named(s) => {
1068                                                let exp = s.exported.clone();
1069                                                let exported = match exp {
1070                                                    Some(ModuleExportName::Ident(ident)) => {
1071                                                        Some(ident)
1072                                                    }
1073                                                    Some(ModuleExportName::Str(..)) => {
1074                                                        unimplemented!(
1075                                                            "module string names unimplemented"
1076                                                        )
1077                                                    }
1078                                                    _ => None,
1079                                                };
1080                                                let orig = match &s.orig {
1081                                                    ModuleExportName::Ident(ident) => ident,
1082                                                    _ => unimplemented!(
1083                                                        "module string names unimplemented"
1084                                                    ),
1085                                                };
1086                                                vars.push(VarDeclarator {
1087                                                    span: s.span,
1088                                                    name: exported.clone().unwrap().into(),
1089                                                    init: Some(
1090                                                        mod_var
1091                                                            .clone()
1092                                                            .make_member(orig.clone().into())
1093                                                            .into(),
1094                                                    ),
1095                                                    definite: Default::default(),
1096                                                });
1097                                            }
1098                                            #[cfg(swc_ast_unknown)]
1099                                            _ => panic!("unable to access unknown nodes"),
1100                                        }
1101                                    }
1102
1103                                    if !vars.is_empty() {
1104                                        new.push(
1105                                            VarDecl {
1106                                                span: DUMMY_SP,
1107                                                kind: VarDeclKind::Const,
1108                                                declare: Default::default(),
1109                                                decls: vars,
1110                                                ..Default::default()
1111                                            }
1112                                            .into(),
1113                                        );
1114                                    }
1115                                    continue;
1116                                }
1117                            }
1118                        }
1119
1120                        for s in specifiers {
1121                            match s {
1122                                ExportSpecifier::Named(ExportNamedSpecifier {
1123                                    orig,
1124                                    exported: Some(exported),
1125                                    ..
1126                                }) => {
1127                                    let exported_ident = match exported {
1128                                        ModuleExportName::Ident(ident) => ident,
1129                                        ModuleExportName::Str(..) => {
1130                                            unimplemented!("module string names unimplemented")
1131                                        }
1132                                        #[cfg(swc_ast_unknown)]
1133                                        _ => panic!("unable to access unknown nodes"),
1134                                    };
1135                                    let orig_ident = match orig {
1136                                        ModuleExportName::Ident(ident) => ident,
1137                                        _ => unimplemented!("module string names unimplemented"),
1138                                    };
1139                                    new.push(
1140                                        orig_ident
1141                                            .clone()
1142                                            .assign_to(exported_ident.clone())
1143                                            .into_module_item(
1144                                                injected_ctxt,
1145                                                "prepare -> export named -> aliased",
1146                                            ),
1147                                    );
1148                                }
1149
1150                                ExportSpecifier::Default(ExportDefaultSpecifier {
1151                                    exported,
1152                                    ..
1153                                }) => {
1154                                    new.push(
1155                                        Ident::new(atom!("default"), DUMMY_SP, info.local_ctxt())
1156                                            .clone()
1157                                            .assign_to(exported.clone())
1158                                            .into_module_item(
1159                                                injected_ctxt,
1160                                                "prepare -> export named -> aliased",
1161                                            ),
1162                                    );
1163                                }
1164
1165                                ExportSpecifier::Namespace(ns) => {
1166                                    if let Some((src, _)) = info
1167                                        .exports
1168                                        .reexports
1169                                        .iter()
1170                                        .find(|s| s.0.src.value == src.as_ref().unwrap().value)
1171                                    {
1172                                        match &ns.name {
1173                                            ModuleExportName::Ident(name) => {
1174                                                if !self
1175                                                    .scope
1176                                                    .get_module(src.module_id)
1177                                                    .unwrap()
1178                                                    .is_es6
1179                                                {
1180                                                    continue;
1181                                                }
1182
1183                                                let wrapped_esm_id =
1184                                                    self.scope.wrapped_esm_id(src.module_id);
1185                                                match wrapped_esm_id {
1186                                                    Some(module_var) => {
1187                                                        // Create variable for the namespaced
1188                                                        // export.
1189                                                        extra.push(
1190                                                            module_var
1191                                                                .clone()
1192                                                                .assign_to(name.clone())
1193                                                                .into_module_item(
1194                                                                    injected_ctxt,
1195                                                                    "prepare -> namespaced \
1196                                                                     reexport",
1197                                                                ),
1198                                                        );
1199                                                        let specifier = ExportSpecifier::Named(
1200                                                            ExportNamedSpecifier {
1201                                                                span: ns.span,
1202                                                                orig: ModuleExportName::Ident(
1203                                                                    module_var.into(),
1204                                                                ),
1205                                                                exported: Some(ns.name.clone()),
1206                                                                is_type_only: false,
1207                                                            },
1208                                                        );
1209                                                        extra.push(
1210                                                            NamedExport {
1211                                                                span: ns.span,
1212                                                                specifiers: vec![specifier],
1213                                                                src: None,
1214                                                                with: None,
1215                                                                type_only: false,
1216                                                            }
1217                                                            .into(),
1218                                                        );
1219                                                    }
1220                                                    None => {
1221                                                        unreachable!(
1222                                                            "Modules reexported with `export * as \
1223                                                             foo from './foo'` should be marked \
1224                                                             as a wrapped esm"
1225                                                        )
1226                                                    }
1227                                                }
1228
1229                                                // Remove `export * as foo from ''`
1230                                                continue;
1231                                            }
1232                                            ModuleExportName::Str(..) => {
1233                                                unimplemented!("module string names unimplemented")
1234                                            }
1235                                            #[cfg(swc_ast_unknown)]
1236                                            _ => panic!("unable to access unknown nodes"),
1237                                        }
1238                                    }
1239                                }
1240                                _ => {}
1241                            }
1242                        }
1243
1244                        new.push(item);
1245                    }
1246
1247                    ModuleItem::ModuleDecl(ModuleDecl::ExportAll(ref export)) => {
1248                        let metadata = ExportMetadata::decode(export.with.as_deref());
1249
1250                        if let Some(export_ctxt) = metadata.export_ctxt {
1251                            let reexport = self.scope.get_module(info.id).unwrap().export_ctxt();
1252                            ctx.transitive_remap.insert(export_ctxt, reexport);
1253                        }
1254
1255                        new.push(item);
1256                    }
1257
1258                    _ => {
1259                        new.push(item);
1260                    }
1261                }
1262            }
1263
1264            new
1265        });
1266
1267        for item in extra {
1268            module.append(info.id, item);
1269        }
1270
1271        // module.print(&self.cm, "prepare");
1272    }
1273
1274    pub(super) fn replace_import_specifiers(&self, info: &TransformedModule, module: &mut Modules) {
1275        let injected_ctxt = self.injected_ctxt;
1276
1277        let mut vars = Vec::new();
1278        module.map_any_items(|module_id, stmts| {
1279            let mut new = Vec::with_capacity(stmts.len() + 32);
1280
1281            for stmt in stmts {
1282                if let ModuleItem::ModuleDecl(ModuleDecl::Import(import)) = &stmt {
1283                    let src_atom = import.src.value.to_atom_lossy().into_owned();
1284                    if self.config.external_modules.contains(&src_atom) {
1285                        new.push(stmt);
1286                        continue;
1287                    }
1288
1289                    for specifier in &import.specifiers {
1290                        match specifier {
1291                            ImportSpecifier::Named(named) => {
1292                                if let Some(imported) = &named.imported {
1293                                    let imporeted_ident = match imported {
1294                                        ModuleExportName::Ident(ident) => ident,
1295                                        ModuleExportName::Str(..) => {
1296                                            unimplemented!("module string names unimplemented")
1297                                        }
1298                                        #[cfg(swc_ast_unknown)]
1299                                        _ => panic!("unable to access unknown nodes"),
1300                                    };
1301                                    vars.push((
1302                                        module_id,
1303                                        imporeted_ident
1304                                            .clone()
1305                                            .assign_to(named.local.clone())
1306                                            .into_module_item(
1307                                                injected_ctxt,
1308                                                "from_replace_import_specifiers",
1309                                            ),
1310                                    ));
1311                                    continue;
1312                                }
1313                            }
1314                            ImportSpecifier::Default(default) => {
1315                                if let Some((src, _)) = info
1316                                    .imports
1317                                    .specifiers
1318                                    .iter()
1319                                    .find(|s| s.0.src.value == import.src.value)
1320                                {
1321                                    let imported =
1322                                        Ident::new(atom!("default"), DUMMY_SP, src.export_ctxt);
1323                                    vars.push((
1324                                        module_id,
1325                                        imported.assign_to(default.local.clone()).into_module_item(
1326                                            injected_ctxt,
1327                                            "from_replace_import_specifiers",
1328                                        ),
1329                                    ));
1330                                    continue;
1331                                }
1332                            }
1333                            ImportSpecifier::Namespace(s) => {
1334                                if let Some((src, _)) = info
1335                                    .imports
1336                                    .specifiers
1337                                    .iter()
1338                                    .find(|s| s.0.src.value == import.src.value)
1339                                {
1340                                    let esm_id = self.scope.wrapped_esm_id(src.module_id).expect(
1341                                        "If a namespace import specifier is preserved, it means \
1342                                         failure of deblobbing and as a result module should be \
1343                                         marked as wrapped esm",
1344                                    );
1345                                    vars.push((
1346                                        module_id,
1347                                        esm_id.clone().assign_to(s.local.clone()).into_module_item(
1348                                            injected_ctxt,
1349                                            "from_replace_import_specifiers: namespaced",
1350                                        ),
1351                                    ));
1352                                    continue;
1353                                }
1354                            }
1355                            #[cfg(swc_ast_unknown)]
1356                            _ => panic!("unable to access unknown nodes"),
1357                        }
1358                    }
1359
1360                    // We should remove imports
1361                    continue;
1362                }
1363
1364                new.push(stmt);
1365            }
1366
1367            new
1368        });
1369
1370        module.append_all(vars)
1371    }
1372}
1373
1374struct ImportMetaHandler<'a, 'b> {
1375    file: &'a FileName,
1376    #[allow(clippy::borrowed_box)]
1377    hook: &'a Box<dyn 'b + Hook>,
1378    is_entry: bool,
1379    inline_ident: Ident,
1380    occurred: bool,
1381    /// TODO: Use this
1382    #[allow(dead_code)]
1383    err: Option<Error>,
1384}
1385
1386impl VisitMut for ImportMetaHandler<'_, '_> {
1387    fn visit_mut_expr(&mut self, e: &mut Expr) {
1388        e.visit_mut_children_with(self);
1389
1390        if let Expr::MetaProp(MetaPropExpr {
1391            kind: MetaPropKind::ImportMeta,
1392            ..
1393        }) = e
1394        {
1395            *e = self.inline_ident.clone().into();
1396            self.occurred = true;
1397        }
1398    }
1399
1400    fn visit_mut_module(&mut self, n: &mut Module) {
1401        n.visit_mut_children_with(self);
1402
1403        if self.occurred {
1404            match self.hook.get_import_meta_props(
1405                n.span,
1406                &ModuleRecord {
1407                    file_name: self.file.to_owned(),
1408                    is_entry: self.is_entry,
1409                },
1410            ) {
1411                Ok(key_value_props) => {
1412                    prepend_stmt(
1413                        &mut n.body,
1414                        VarDecl {
1415                            span: n.span,
1416                            kind: VarDeclKind::Const,
1417                            declare: false,
1418                            decls: vec![VarDeclarator {
1419                                span: n.span,
1420                                name: Pat::Ident(self.inline_ident.clone().into()),
1421                                init: Some(Box::new(Expr::Object(ObjectLit {
1422                                    span: n.span,
1423                                    props: key_value_props
1424                                        .iter()
1425                                        .cloned()
1426                                        .map(|kv| PropOrSpread::Prop(Box::new(Prop::KeyValue(kv))))
1427                                        .collect(),
1428                                }))),
1429                                definite: false,
1430                            }],
1431                            ..Default::default()
1432                        }
1433                        .into(),
1434                    );
1435                }
1436                Err(err) => self.err = Some(err),
1437            }
1438        }
1439    }
1440}