diff --git a/Cargo.toml b/Cargo.toml index 30bd749d2a7..e3369ded262 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ name = "usage" codegen-units = 1 lto = true # debug = true -opt-level = 'z' +# opt-level = 'z' [profile.bench] codegen-units = 1 diff --git a/bundler/Cargo.toml b/bundler/Cargo.toml index 1320b8592a8..2b5ae673aee 100644 --- a/bundler/Cargo.toml +++ b/bundler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "swc_bundler" -version = "0.2.2" +version = "0.2.3" authors = ["강동윤 "] license = "Apache-2.0/MIT" repository = "https://github.com/swc-project/swc.git" diff --git a/bundler/src/bundler/chunk/circular.rs b/bundler/src/bundler/chunk/circular.rs index f917c496413..ee043d5be7c 100644 --- a/bundler/src/bundler/chunk/circular.rs +++ b/bundler/src/bundler/chunk/circular.rs @@ -116,6 +116,7 @@ where mark: circular_module.mark(), specifiers: &specifiers, excluded: vec![], + is_export: false, }); break; } diff --git a/bundler/src/bundler/chunk/export.rs b/bundler/src/bundler/chunk/export.rs new file mode 100644 index 00000000000..7a07a916f16 --- /dev/null +++ b/bundler/src/bundler/chunk/export.rs @@ -0,0 +1,365 @@ +use super::merge::{LocalMarker, Unexporter}; +use crate::{bundler::load::TransformedModule, Bundler, Load, ModuleId, Resolve}; +use anyhow::{Context, Error}; +use std::mem::{replace, take}; +use swc_atoms::js_word; +use swc_common::{Spanned, SyntaxContext, DUMMY_SP}; +use swc_ecma_ast::*; +use swc_ecma_utils::find_ids; +use swc_ecma_visit::{FoldWith, VisitMut, VisitMutWith}; + +impl Bundler<'_, L, R> +where + L: Load, + R: Resolve, +{ + pub(super) fn merge_reexports( + &self, + mut entry: Module, + info: &TransformedModule, + targets: &mut Vec, + ) -> Result { + entry.visit_mut_with(&mut DefaultRenamer); + + for (src, specifiers) in &info.exports.reexports { + let imported = self.scope.get_module(src.module_id).unwrap(); + assert!(imported.is_es6, "Reexports are es6 only"); + + info.helpers.extend(&imported.helpers); + + if let Some(pos) = targets.iter().position(|x| *x == src.module_id) { + log::debug!("Reexport: targets.remove({})", imported.fm.name); + targets.remove(pos); + } + + let mut dep = self + .merge_modules(src.module_id, false, targets) + .with_context(|| { + format!( + "failed to merge for reexport: ({}):{} <= ({}):{}", + info.id, info.fm.name, src.module_id, src.src.value + ) + })?; + + dep = self.drop_unused(dep, Some(&specifiers)); + + // print_hygiene("entry:init", &self.cm, &entry); + // print_hygiene("dep:init", &self.cm, &dep); + + entry = entry.fold_with(&mut LocalMarker { + mark: imported.mark(), + specifiers, + excluded: vec![], + is_export: false, + }); + // print_hygiene(&format!("entry:local-marker"), &self.cm, &entry); + entry.visit_mut_with(&mut NamedExportOrigMarker { + top_level_ctxt: SyntaxContext::empty().apply_mark(self.top_level_mark), + target_ctxt: SyntaxContext::empty().apply_mark(info.mark()), + }); + + // print_hygiene(&format!("entry:named-export-orig"), &self.cm, &entry); + + dep.visit_mut_with(&mut UnexportAsVar { + target_ctxt: SyntaxContext::empty().apply_mark(info.mark()), + }); + // print_hygiene("dep:unexport-as-var", &self.cm, &dep); + + dep.visit_mut_with(&mut AliasExports { + importer_ctxt: SyntaxContext::empty().apply_mark(info.mark()), + decls: Default::default(), + }); + // print_hygiene("dep:alias-exports", &self.cm, &dep); + + dep = dep.fold_with(&mut Unexporter); + + entry.visit_mut_with(&mut ExportRenamer { + from: SyntaxContext::empty().apply_mark(imported.mark()), + to: SyntaxContext::empty().apply_mark(info.mark()), + }); + + // print_hygiene("entry:before-injection", &self.cm, &entry); + // print_hygiene("dep:before-injection", &self.cm, &dep); + + // Replace import statement / require with module body + let mut injector = ExportInjector { + imported: dep.body.clone(), + src: src.src.clone(), + }; + entry.body.visit_mut_with(&mut injector); + + // print_hygiene( + // &format!( + // "entry:injection {:?} <- {:?}", + // SyntaxContext::empty().apply_mark(info.mark()), + // SyntaxContext::empty().apply_mark(imported.mark()), + // ), + // &self.cm, + // &entry, + // ); + assert_eq!(injector.imported, vec![]); + } + + Ok(entry) + } +} + +struct ExportInjector { + imported: Vec, + src: Str, +} + +impl VisitMut for ExportInjector { + fn visit_mut_module_items(&mut self, orig: &mut Vec) { + let items = take(orig); + let mut buf = Vec::with_capacity(self.imported.len() + items.len()); + + for item in items { + // + match item { + ModuleItem::Stmt(Stmt::Empty(..)) => continue, + + ModuleItem::ModuleDecl(ModuleDecl::ExportAll(ref export)) + if export.src.value == self.src.value => + { + buf.extend(take(&mut self.imported)); + buf.push(item); + } + + ModuleItem::ModuleDecl(ModuleDecl::ExportNamed( + export @ NamedExport { src: Some(..), .. }, + )) if export.src.as_ref().unwrap().value == self.src.value => { + buf.extend(take(&mut self.imported)); + buf.push(ModuleItem::ModuleDecl(ModuleDecl::ExportNamed( + NamedExport { + src: None, + ..export + }, + ))); + } + + _ => buf.push(item), + } + } + + *orig = buf; + } +} + +struct ExportRenamer { + /// Syntax context for the top level. + from: SyntaxContext, + /// Syntax context for the top level nodes of dependency module. + to: SyntaxContext, +} + +impl VisitMut for ExportRenamer { + fn visit_mut_named_export(&mut self, export: &mut NamedExport) { + // if export.src.is_none() { + // return; + // } + + export.specifiers.visit_mut_children_with(self); + } + + fn visit_mut_export_named_specifier(&mut self, s: &mut ExportNamedSpecifier) { + match &mut s.exported { + Some(v) => { + if v.span.ctxt == self.from { + v.span = v.span.with_ctxt(self.to); + } + } + None => {} + } + if s.orig.span.ctxt == self.from { + s.orig.span = s.orig.span.with_ctxt(self.to); + } + } + + fn visit_mut_stmt(&mut self, _: &mut Stmt) {} +} + +/// Converts +/// +/// ```js +/// export { l1 as e2 }; +/// ``` +/// +/// to +/// +/// ```js +/// const e3 = l1; +/// ``` +struct UnexportAsVar { + /// Syntax context for the generated variables. + target_ctxt: SyntaxContext, +} + +impl VisitMut for UnexportAsVar { + fn visit_mut_module_item(&mut self, n: &mut ModuleItem) { + n.visit_mut_children_with(self); + + match n { + ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr(export)) => { + let expr = replace( + &mut export.expr, + Box::new(Expr::Invalid(Invalid { span: DUMMY_SP })), + ); + + *n = ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl { + span: export.span, + kind: VarDeclKind::Const, + declare: false, + decls: vec![VarDeclarator { + span: DUMMY_SP, + name: Pat::Ident(Ident::new( + "__default".into(), + expr.span().with_ctxt(self.target_ctxt), + )), + init: Some(expr), + definite: false, + }], + }))); + } + ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(ref export)) => { + let mut decls = vec![]; + for s in &export.specifiers { + match s { + ExportSpecifier::Namespace(_) => {} + ExportSpecifier::Default(_) => {} + ExportSpecifier::Named(n) => match &n.exported { + Some(exported) => { + // TODO: (maybe) Check previous context + let mut exported = exported.clone(); + exported.span = exported.span.with_ctxt(self.target_ctxt); + + decls.push(VarDeclarator { + span: n.span, + name: Pat::Ident(exported), + init: Some(Box::new(Expr::Ident(n.orig.clone()))), + definite: true, + }) + } + None => decls.push(VarDeclarator { + span: n.span, + name: Pat::Ident(Ident::new( + n.orig.sym.clone(), + n.orig.span.with_ctxt(self.target_ctxt), + )), + init: Some(Box::new(Expr::Ident(n.orig.clone()))), + definite: false, + }), + }, + } + } + + if decls.is_empty() { + *n = ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP })) + } else { + *n = ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl { + span: export.span, + decls, + declare: false, + kind: VarDeclKind::Const, + }))) + } + } + _ => {} + } + } + + fn visit_mut_stmt(&mut self, _: &mut Stmt) {} +} + +pub(super) struct NamedExportOrigMarker { + pub top_level_ctxt: SyntaxContext, + pub target_ctxt: SyntaxContext, +} + +impl VisitMut for NamedExportOrigMarker { + fn visit_mut_export_named_specifier(&mut self, s: &mut ExportNamedSpecifier) { + if s.orig.span.ctxt == self.top_level_ctxt { + s.orig.span = s.orig.span.with_ctxt(self.target_ctxt); + } + } + + fn visit_mut_stmt(&mut self, _: &mut Stmt) {} +} + +struct AliasExports { + /// Syntax context of the importer. + importer_ctxt: SyntaxContext, + decls: Vec, +} + +impl VisitMut for AliasExports { + fn visit_mut_module_items(&mut self, items: &mut Vec) { + for item in items.iter_mut() { + item.visit_mut_with(self); + } + + if !self.decls.is_empty() { + items.push(ModuleItem::Stmt(Stmt::Decl(Decl::Var(VarDecl { + span: DUMMY_SP, + kind: VarDeclKind::Const, + decls: take(&mut self.decls), + declare: false, + })))) + } + } + + fn visit_mut_module_decl(&mut self, decl: &mut ModuleDecl) { + match decl { + ModuleDecl::ExportDecl(ref export) => match &export.decl { + Decl::Class(c) => self.decls.push(VarDeclarator { + span: c.class.span, + name: Pat::Ident(Ident::new( + c.ident.sym.clone(), + c.ident.span.with_ctxt(self.importer_ctxt), + )), + init: Some(Box::new(Expr::Ident(c.ident.clone()))), + definite: false, + }), + Decl::Fn(f) => self.decls.push(VarDeclarator { + span: f.function.span, + name: Pat::Ident(Ident::new( + f.ident.sym.clone(), + f.ident.span.with_ctxt(self.importer_ctxt), + )), + init: Some(Box::new(Expr::Ident(f.ident.clone()))), + definite: false, + }), + Decl::Var(var) => { + let ids = find_ids::<_, Ident>(&var.decls); + for ident in ids { + self.decls.push(VarDeclarator { + span: ident.span, + name: Pat::Ident(Ident::new( + ident.sym.clone(), + ident.span.with_ctxt(self.importer_ctxt), + )), + init: Some(Box::new(Expr::Ident(ident))), + definite: false, + }) + } + } + _ => {} + }, + _ => {} + } + } + + fn visit_mut_stmt(&mut self, _: &mut Stmt) {} +} + +struct DefaultRenamer; + +impl VisitMut for DefaultRenamer { + fn visit_mut_export_named_specifier(&mut self, n: &mut ExportNamedSpecifier) { + if n.orig.sym == js_word!("default") { + n.orig.sym = "__default".into() + } + } + + fn visit_mut_stmt(&mut self, _: &mut Stmt) {} +} diff --git a/bundler/src/bundler/chunk/merge.rs b/bundler/src/bundler/chunk/merge.rs index 394a0db8866..4af1a843b83 100644 --- a/bundler/src/bundler/chunk/merge.rs +++ b/bundler/src/bundler/chunk/merge.rs @@ -12,7 +12,7 @@ use std::{ ops::{Deref, DerefMut}, }; use swc_atoms::{js_word, JsWord}; -use swc_common::{Mark, Span, Spanned, SyntaxContext, DUMMY_SP}; +use swc_common::{Mark, Spanned, SyntaxContext, DUMMY_SP}; use swc_ecma_ast::*; use swc_ecma_utils::{find_ids, DestructuringFinder, StmtLike}; use swc_ecma_visit::{Fold, FoldWith, VisitMut, VisitMutWith, VisitWith}; @@ -54,7 +54,9 @@ where log::info!("Merge: ({}){} <= {:?}", info.id, info.fm.name, targets); - // print_hygiene("before:merge", &self.cm, &entry); + entry = self + .merge_reexports(entry, &info, targets) + .context("failed to merge reepxorts")?; for (src, specifiers) in &info.imports.specifiers { if !targets.contains(&src.module_id) { @@ -73,6 +75,7 @@ where mark: imported.mark(), specifiers: &specifiers, excluded: vec![], + is_export: false, }); } @@ -178,6 +181,7 @@ where mark: imported.mark(), specifiers: &specifiers, excluded: vec![], + is_export: false, }); // // Note: this does not handle `export default @@ -190,28 +194,6 @@ where // print_hygiene("dep:before:global-mark", &self.cm, &dep); - dep = dep.fold_with(&mut GlobalMarker { - used_mark: self.used_mark, - module_mark: imported.mark(), - }); - - // print_hygiene("dep:after:global-mark", &self.cm, &dep); - - // { - // let code = self - // .swc - // .print( - // &dep.clone().fold_with(&mut HygieneVisualizer), - // SourceMapsConfig::Bool(false), - // None, - // false, - // ) - // .unwrap() - // .code; - // - // println!("Dep:\n{}\n\n\n", code); - // } - // Replace import statement / require with module body let mut injector = Es6ModuleInjector { imported: dep.body.clone(), @@ -296,10 +278,9 @@ impl Fold for Unexporter { }, // Empty statement - ModuleDecl::ExportAll(..) | ModuleDecl::ExportDefaultExpr(..) => { - ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP })) - } - ModuleDecl::ExportNamed(ref n) if n.src.is_none() => { + ModuleDecl::ExportAll(..) + | ModuleDecl::ExportDefaultExpr(..) + | ModuleDecl::ExportNamed(..) => { ModuleItem::Stmt(Stmt::Empty(EmptyStmt { span: DUMMY_SP })) } ModuleDecl::Import(..) => ModuleItem::ModuleDecl(decl), @@ -535,6 +516,7 @@ pub(super) struct LocalMarker<'a> { /// Mark applied to imported idents. pub mark: Mark, pub specifiers: &'a [Specifier], + pub is_export: bool, pub excluded: Vec, } @@ -636,10 +618,24 @@ impl Fold for LocalMarker<'_> { } // TODO: sym() => correct span - if self.specifiers.iter().any(|id| *id.local() == node) { - node.span = node - .span - .with_ctxt(SyntaxContext::empty().apply_mark(self.mark)); + if self.is_export { + if self.specifiers.iter().any(|id| match id { + Specifier::Specific { local, alias } => match alias { + Some(v) => *v == node, + None => *local == node, + }, + Specifier::Namespace { local } => *local == node, + }) { + node.span = node + .span + .with_ctxt(SyntaxContext::empty().apply_mark(self.mark)); + } + } else { + if self.specifiers.iter().any(|id| *id.local() == node) { + node.span = node + .span + .with_ctxt(SyntaxContext::empty().apply_mark(self.mark)); + } } node @@ -695,33 +691,3 @@ impl VisitMut for Es6ModuleInjector { *orig = buf; } } - -pub(super) struct GlobalMarker { - pub used_mark: Mark, - pub module_mark: Mark, -} - -impl GlobalMarker { - fn is_marked_as_used(&self, span: Span) -> bool { - let mut ctxt = span.ctxt(); - loop { - let m = ctxt.remove_mark(); - if m == Mark::root() { - return false; - } - if m == self.used_mark { - return true; - } - } - } -} - -impl Fold for GlobalMarker { - fn fold_span(&mut self, span: Span) -> Span { - if self.is_marked_as_used(span) { - return span.apply_mark(self.module_mark); - } - - span - } -} diff --git a/bundler/src/bundler/chunk/mod.rs b/bundler/src/bundler/chunk/mod.rs index bc499e04f08..496ae4e1875 100644 --- a/bundler/src/bundler/chunk/mod.rs +++ b/bundler/src/bundler/chunk/mod.rs @@ -12,6 +12,7 @@ use swc_ecma_visit::FoldWith; mod circular; mod cjs; +mod export; mod merge; pub(super) type ModuleGraph = DiGraphMap; @@ -176,6 +177,15 @@ where if src.is_unconditional { 2 } else { 1 }, ); } + + for (src, _) in &m.exports.reexports { + self.add_to_graph(graph, src.module_id); + graph.add_edge( + module_id, + src.module_id, + if src.is_unconditional { 2 } else { 1 }, + ); + } } } diff --git a/bundler/src/bundler/export.rs b/bundler/src/bundler/export.rs index e31d2337afc..389b0467f70 100644 --- a/bundler/src/bundler/export.rs +++ b/bundler/src/bundler/export.rs @@ -5,10 +5,10 @@ use super::{ use crate::{id::Id, load::Load, resolve::Resolve}; use std::collections::HashMap; use swc_atoms::js_word; -use swc_common::{SyntaxContext, DUMMY_SP}; +use swc_common::{FileName, SyntaxContext}; use swc_ecma_ast::*; use swc_ecma_utils::find_ids; -use swc_ecma_visit::{Node, Visit, VisitWith}; +use swc_ecma_visit::{VisitMut, VisitMutWith}; impl Bundler<'_, L, R> where @@ -22,11 +22,19 @@ where /// /// TODO: Support pattern like /// export const [a, b] = [1, 2] - pub(super) fn extract_export_info(&self, module: &Module) -> RawExports { + pub(super) fn extract_export_info( + &self, + file_name: &FileName, + module: &mut Module, + ) -> RawExports { self.run(|| { - let mut v = ExportFinder::default(); + let mut v = ExportFinder { + info: Default::default(), + file_name, + bundler: self, + }; - module.visit_with(&Invalid { span: DUMMY_SP } as _, &mut v); + module.visit_mut_with(&mut v); v.info }) @@ -45,13 +53,46 @@ pub(super) struct Exports { pub reexports: HashMap>, } -#[derive(Debug, Default)] -struct ExportFinder { +struct ExportFinder<'a, 'b, L, R> +where + L: Load, + R: Resolve, +{ info: RawExports, + file_name: &'a FileName, + bundler: &'a Bundler<'b, L, R>, } -impl Visit for ExportFinder { - fn visit_module_item(&mut self, item: &ModuleItem, _: &dyn Node) { +impl ExportFinder<'_, '_, L, R> +where + L: Load, + R: Resolve, +{ + fn ctxt_for(&self, src: &str) -> Option { + // Don't apply mark if it's a core module. + if self + .bundler + .config + .external_modules + .iter() + .any(|v| v == src) + { + return None; + } + let path = self.bundler.resolve(self.file_name, src).ok()?; + let (_, mark) = self.bundler.scope.module_id_gen.gen(&path); + let ctxt = SyntaxContext::empty(); + + Some(ctxt.apply_mark(mark)) + } +} + +impl VisitMut for ExportFinder<'_, '_, L, R> +where + L: Load, + R: Resolve, +{ + fn visit_mut_module_item(&mut self, item: &mut ModuleItem) { match item { // TODO: Optimize pure constants // ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { @@ -137,6 +178,18 @@ impl Visit for ExportFinder { } ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(named)) => { + match &named.src { + Some(v) => { + let ctxt = self.ctxt_for(&v.value); + match ctxt { + Some(ctxt) => { + named.span = named.span.with_ctxt(ctxt); + } + None => {} + } + } + None => {} + }; let v = self.info.items.entry(named.src.clone()).or_default(); for s in &named.specifiers { match s { @@ -150,17 +203,31 @@ impl Visit for ExportFinder { }); } ExportSpecifier::Named(n) => { - v.push(Specifier::Specific { - local: n.orig.clone().into(), - alias: n.exported.clone().map(From::from), - }); + if let Some(exported) = &n.exported { + v.push(Specifier::Specific { + local: exported.clone().into(), + alias: Some(n.orig.clone().into()), + }); + } else { + v.push(Specifier::Specific { + local: n.orig.clone().into(), + alias: None, + }); + } } } } return; } - // TODO - // ModuleItem::ModuleDecl(ModuleDecl::ExportAll(all)) => {} + + ModuleItem::ModuleDecl(ModuleDecl::ExportAll(all)) => { + match self.ctxt_for(&all.src.value) { + Some(ctxt) => { + all.span = all.span.with_ctxt(ctxt); + } + None => {} + } + } _ => {} } } diff --git a/bundler/src/bundler/load.rs b/bundler/src/bundler/load.rs index 242bd4db894..d97733173aa 100644 --- a/bundler/src/bundler/load.rs +++ b/bundler/src/bundler/load.rs @@ -150,7 +150,7 @@ where // println!("After imports:\n{}\n", code,); // } - let exports = self.extract_export_info(&module); + let exports = self.extract_export_info(file_name, &mut module); let is_es6 = if !self.config.require { true diff --git a/ecmascript/transforms/Cargo.toml b/ecmascript/transforms/Cargo.toml index e671a4d6614..4fe6732ba19 100644 --- a/ecmascript/transforms/Cargo.toml +++ b/ecmascript/transforms/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "swc_ecma_transforms" -version = "0.19.7" +version = "0.19.8" authors = ["강동윤 "] license = "Apache-2.0/MIT" repository = "https://github.com/swc-project/swc.git" diff --git a/ecmascript/transforms/src/optimization/simplify/dce/mod.rs b/ecmascript/transforms/src/optimization/simplify/dce/mod.rs index deff25503e2..5c6f59843e0 100644 --- a/ecmascript/transforms/src/optimization/simplify/dce/mod.rs +++ b/ecmascript/transforms/src/optimization/simplify/dce/mod.rs @@ -194,12 +194,12 @@ impl Fold for Dce<'_> { node } - fn fold_export_all(&mut self, node: ExportAll) -> ExportAll { + fn fold_export_all(&mut self, mut node: ExportAll) -> ExportAll { if self.is_marked(node.span) { return node; } - - unimplemented!("dce: `export * from 'foo'`") + node.span = node.span.apply_mark(self.config.used_mark); + node } fn fold_export_decl(&mut self, mut node: ExportDecl) -> ExportDecl { diff --git a/spack/Cargo.toml b/spack/Cargo.toml index 1833be5c558..82212e85d3d 100644 --- a/spack/Cargo.toml +++ b/spack/Cargo.toml @@ -12,7 +12,7 @@ edition = "2018" [dependencies] swc_atoms = { path = "../atoms" } -swc_bundler = { path = "../bundler", features = ["concurrent"] } +swc_bundler = { path = "../bundler" } swc_common = { path = "../common" } swc_ecma_ast = { path = "../ecmascript/ast" } swc_ecma_codegen = { path = "../ecmascript/codegen" } diff --git a/spack/src/loaders/swc.rs b/spack/src/loaders/swc.rs index 6f56a3628c3..6fbf13b3a0e 100644 --- a/spack/src/loaders/swc.rs +++ b/spack/src/loaders/swc.rs @@ -47,88 +47,114 @@ impl Load for SwcLoader { log::trace!("JsLoader.load: loaded"); - let mut config = self.compiler.config_for_file( - &swc::config::Options { - config: { - if let Some(c) = &self.options.config { - Some(swc::config::Config { - jsc: JscConfig { - transform: { - if let Some(c) = &c.jsc.transform { - Some(TransformConfig { - react: c.react.clone(), - const_modules: c.const_modules.clone(), - optimizer: None, - legacy_decorator: c.legacy_decorator, - decorator_metadata: c.decorator_metadata, - }) - } else { - None - } + let program = if fm.name.to_string().contains("node_modules") { + let program = self.compiler.parse_js( + fm.clone(), + JscTarget::Es2020, + Default::default(), + true, + true, + )?; + let program = helpers::HELPERS.set(&Helpers::new(true), || { + swc_ecma_utils::HANDLER.set(&self.compiler.handler, || { + let program = program.fold_with(&mut InlineGlobals { + envs: env_map(), + globals: Default::default(), + }); + let program = program.fold_with(&mut expr_simplifier()); + let program = program.fold_with(&mut dead_branch_remover()); + + program + }) + }); + + program + } else { + let mut config = self.compiler.config_for_file( + &swc::config::Options { + config: { + if let Some(c) = &self.options.config { + Some(swc::config::Config { + jsc: JscConfig { + transform: { + if let Some(c) = &c.jsc.transform { + Some(TransformConfig { + react: c.react.clone(), + const_modules: c.const_modules.clone(), + optimizer: None, + legacy_decorator: c.legacy_decorator, + decorator_metadata: c.decorator_metadata, + }) + } else { + None + } + }, + external_helpers: true, + ..c.jsc }, - external_helpers: true, - ..c.jsc - }, - module: None, - minify: Some(false), - ..c.clone() - }) - } else { - None - } + module: None, + minify: Some(false), + ..c.clone() + }) + } else { + None + } + }, + disable_hygiene: false, + disable_fixer: true, + global_mark: self.options.global_mark, + cwd: self.options.cwd.clone(), + caller: None, + filename: String::new(), + config_file: None, + root: None, + root_mode: Default::default(), + swcrc: true, + swcrc_roots: Default::default(), + env_name: { + let s = env::var("NODE_ENV").unwrap_or_else(|_| "development".into()); + s + }, + input_source_map: InputSourceMap::Bool(false), + source_maps: None, + source_file_name: None, + source_root: None, + is_module: true, }, - disable_hygiene: false, - disable_fixer: true, - global_mark: self.options.global_mark, - cwd: self.options.cwd.clone(), - caller: None, - filename: String::new(), - config_file: None, - root: None, - root_mode: Default::default(), - swcrc: true, - swcrc_roots: Default::default(), - env_name: { - let s = env::var("NODE_ENV").unwrap_or_else(|_| "development".into()); - s - }, - input_source_map: InputSourceMap::Bool(false), - source_maps: None, - source_file_name: None, - source_root: None, - is_module: true, - }, - &fm.name, - )?; + &fm.name, + )?; - log::trace!("JsLoader.load: loaded config"); + log::trace!("JsLoader.load: loaded config"); - // We run transform at this phase to strip out unused dependencies. - // - // Note that we don't apply compat transform at loading phase. - let program = - self.compiler - .parse_js(fm.clone(), JscTarget::Es2020, config.syntax, true, true)?; + // We run transform at this phase to strip out unused dependencies. + // + // Note that we don't apply compat transform at loading phase. + let program = + self.compiler + .parse_js(fm.clone(), JscTarget::Es2020, config.syntax, true, true)?; - log::trace!("JsLoader.load: parsed"); + log::trace!("JsLoader.load: parsed"); - // Fold module - let program = helpers::HELPERS.set(&Helpers::new(true), || { - swc_ecma_utils::HANDLER.set(&self.compiler.handler, || { - let program = program.fold_with(&mut InlineGlobals { - envs: env_map(), - globals: Default::default(), - }); - let program = program.fold_with(&mut expr_simplifier()); - let program = program.fold_with(&mut dead_branch_remover()); + // Fold module + let program = helpers::HELPERS.set(&Helpers::new(true), || { + swc_ecma_utils::HANDLER.set(&self.compiler.handler, || { + let program = program.fold_with(&mut InlineGlobals { + envs: env_map(), + globals: Default::default(), + }); + let program = program.fold_with(&mut expr_simplifier()); + let program = program.fold_with(&mut dead_branch_remover()); - let program = program.fold_with(&mut config.pass); + let program = program.fold_with(&mut config.pass); - program - }) - }); + program + }) + }); - log::trace!("JsLoader.load: applied transforms"); + log::trace!("JsLoader.load: applied transforms"); + + program + }; match program { Program::Module(module) => Ok((fm, module)), diff --git a/spack/tests/pass/reexport/.namespace/input/.swcrc b/spack/tests/pass/reexport/.namespace/input/.swcrc new file mode 100644 index 00000000000..8300cf678b2 --- /dev/null +++ b/spack/tests/pass/reexport/.namespace/input/.swcrc @@ -0,0 +1,10 @@ +{ + "jsc": { + "target": "es2016", + "parser": { + "syntax": "ecmascript", + "dynamicImport": true, + "exportNamespaceFrom": true + } + } +} diff --git a/spack/tests/pass/reexport/.namespace/input/a.js b/spack/tests/pass/reexport/.namespace/input/a.js new file mode 100644 index 00000000000..01a38eb8503 --- /dev/null +++ b/spack/tests/pass/reexport/.namespace/input/a.js @@ -0,0 +1,3 @@ +export * as b from './b' + +console.log('a'); \ No newline at end of file diff --git a/spack/tests/pass/reexport/.namespace/input/b.js b/spack/tests/pass/reexport/.namespace/input/b.js new file mode 100644 index 00000000000..c55ffb9ef4c --- /dev/null +++ b/spack/tests/pass/reexport/.namespace/input/b.js @@ -0,0 +1,3 @@ +export const b = 1; + +console.log('b'); \ No newline at end of file diff --git a/spack/tests/pass/reexport/.namespace/input/entry.js b/spack/tests/pass/reexport/.namespace/input/entry.js new file mode 100644 index 00000000000..4ed0468c467 --- /dev/null +++ b/spack/tests/pass/reexport/.namespace/input/entry.js @@ -0,0 +1 @@ +export { b } from './a' \ No newline at end of file diff --git a/spack/tests/pass/reexport/default-1-simple/input/a.js b/spack/tests/pass/reexport/default-1-simple/input/a.js new file mode 100644 index 00000000000..d7bd156d090 --- /dev/null +++ b/spack/tests/pass/reexport/default-1-simple/input/a.js @@ -0,0 +1,3 @@ +export { default as a } from './b'; + +console.log('a.js'); \ No newline at end of file diff --git a/spack/tests/pass/reexport/default-1-simple/input/b.js b/spack/tests/pass/reexport/default-1-simple/input/b.js new file mode 100644 index 00000000000..07ef55b06ff --- /dev/null +++ b/spack/tests/pass/reexport/default-1-simple/input/b.js @@ -0,0 +1,5 @@ +const b = 1; + +export default b; + +console.log('b') \ No newline at end of file diff --git a/spack/tests/pass/reexport/default-1-simple/input/entry.js b/spack/tests/pass/reexport/default-1-simple/input/entry.js new file mode 100644 index 00000000000..c4ffa841553 --- /dev/null +++ b/spack/tests/pass/reexport/default-1-simple/input/entry.js @@ -0,0 +1,3 @@ +export { a } from './a'; + +console.log('entry'); \ No newline at end of file diff --git a/spack/tests/pass/reexport/default-1-simple/output/entry.js b/spack/tests/pass/reexport/default-1-simple/output/entry.js new file mode 100644 index 00000000000..1bead189b22 --- /dev/null +++ b/spack/tests/pass/reexport/default-1-simple/output/entry.js @@ -0,0 +1,7 @@ +const b = 1; +const __default = b; +console.log('b'); +const a = __default; +console.log('a.js'); +export { a }; +console.log('entry'); diff --git a/spack/tests/pass/reexport/named-1-alias/input/a.js b/spack/tests/pass/reexport/named-1-alias/input/a.js new file mode 100644 index 00000000000..0005ce96848 --- /dev/null +++ b/spack/tests/pass/reexport/named-1-alias/input/a.js @@ -0,0 +1,3 @@ +export { b as a } from './b' + +console.log('a'); \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-1-alias/input/b.js b/spack/tests/pass/reexport/named-1-alias/input/b.js new file mode 100644 index 00000000000..449a35e93de --- /dev/null +++ b/spack/tests/pass/reexport/named-1-alias/input/b.js @@ -0,0 +1,3 @@ +export const b = 1; + +console.log('b', b); \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-1-alias/input/entry.js b/spack/tests/pass/reexport/named-1-alias/input/entry.js new file mode 100644 index 00000000000..b81d88eaa79 --- /dev/null +++ b/spack/tests/pass/reexport/named-1-alias/input/entry.js @@ -0,0 +1 @@ +export { a } from './a' \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-1-alias/output/entry.js b/spack/tests/pass/reexport/named-1-alias/output/entry.js new file mode 100644 index 00000000000..69fa9ab2bd2 --- /dev/null +++ b/spack/tests/pass/reexport/named-1-alias/output/entry.js @@ -0,0 +1,6 @@ +const b = 1; +console.log('b', b); +const b1 = b; +const a = b1; +console.log('a'); +export { a }; diff --git a/spack/tests/pass/reexport/named-1-orig/input/a.js b/spack/tests/pass/reexport/named-1-orig/input/a.js new file mode 100644 index 00000000000..c9204fe442c --- /dev/null +++ b/spack/tests/pass/reexport/named-1-orig/input/a.js @@ -0,0 +1,3 @@ +export { b } from './b' + +console.log('a'); \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-1-orig/input/b.js b/spack/tests/pass/reexport/named-1-orig/input/b.js new file mode 100644 index 00000000000..449a35e93de --- /dev/null +++ b/spack/tests/pass/reexport/named-1-orig/input/b.js @@ -0,0 +1,3 @@ +export const b = 1; + +console.log('b', b); \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-1-orig/input/entry.js b/spack/tests/pass/reexport/named-1-orig/input/entry.js new file mode 100644 index 00000000000..1ef88758b72 --- /dev/null +++ b/spack/tests/pass/reexport/named-1-orig/input/entry.js @@ -0,0 +1 @@ +export { b as a } from './a' \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-1-orig/output/entry.js b/spack/tests/pass/reexport/named-1-orig/output/entry.js new file mode 100644 index 00000000000..ed9adb154e1 --- /dev/null +++ b/spack/tests/pass/reexport/named-1-orig/output/entry.js @@ -0,0 +1,6 @@ +const b = 1; +console.log('b', b); +const b1 = b; +const b2 = b1; +console.log('a'); +export { b2 as a }; diff --git a/spack/tests/pass/reexport/named-2-nested/input/a.js b/spack/tests/pass/reexport/named-2-nested/input/a.js new file mode 100644 index 00000000000..c9204fe442c --- /dev/null +++ b/spack/tests/pass/reexport/named-2-nested/input/a.js @@ -0,0 +1,3 @@ +export { b } from './b' + +console.log('a'); \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-2-nested/input/b.js b/spack/tests/pass/reexport/named-2-nested/input/b.js new file mode 100644 index 00000000000..b2767d68a4b --- /dev/null +++ b/spack/tests/pass/reexport/named-2-nested/input/b.js @@ -0,0 +1,3 @@ +export { c as b } from './c' + +console.log('b'); \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-2-nested/input/c.js b/spack/tests/pass/reexport/named-2-nested/input/c.js new file mode 100644 index 00000000000..00ced7d087a --- /dev/null +++ b/spack/tests/pass/reexport/named-2-nested/input/c.js @@ -0,0 +1,3 @@ +export const c = 1; + +console.log('c', c); \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-2-nested/input/entry.js b/spack/tests/pass/reexport/named-2-nested/input/entry.js new file mode 100644 index 00000000000..6412ab0e9ad --- /dev/null +++ b/spack/tests/pass/reexport/named-2-nested/input/entry.js @@ -0,0 +1,3 @@ +export { b as a } from './a' + +console.log('entry') \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-2-nested/output/entry.js b/spack/tests/pass/reexport/named-2-nested/output/entry.js new file mode 100644 index 00000000000..3005d581204 --- /dev/null +++ b/spack/tests/pass/reexport/named-2-nested/output/entry.js @@ -0,0 +1,9 @@ +const c = 1; +console.log('c', c); +const c1 = c; +const b = c1; +console.log('b'); +const b1 = b; +console.log('a'); +export { b1 as a }; +console.log('entry'); diff --git a/spack/tests/pass/reexport/named-3-var/input/a.js b/spack/tests/pass/reexport/named-3-var/input/a.js new file mode 100644 index 00000000000..0005ce96848 --- /dev/null +++ b/spack/tests/pass/reexport/named-3-var/input/a.js @@ -0,0 +1,3 @@ +export { b as a } from './b' + +console.log('a'); \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-3-var/input/b.js b/spack/tests/pass/reexport/named-3-var/input/b.js new file mode 100644 index 00000000000..b2767d68a4b --- /dev/null +++ b/spack/tests/pass/reexport/named-3-var/input/b.js @@ -0,0 +1,3 @@ +export { c as b } from './c' + +console.log('b'); \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-3-var/input/c.js b/spack/tests/pass/reexport/named-3-var/input/c.js new file mode 100644 index 00000000000..00ced7d087a --- /dev/null +++ b/spack/tests/pass/reexport/named-3-var/input/c.js @@ -0,0 +1,3 @@ +export const c = 1; + +console.log('c', c); \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-3-var/input/entry.js b/spack/tests/pass/reexport/named-3-var/input/entry.js new file mode 100644 index 00000000000..02a9d72bc7d --- /dev/null +++ b/spack/tests/pass/reexport/named-3-var/input/entry.js @@ -0,0 +1,3 @@ +export { a } from './a' + +console.log('entry') \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-3-var/output/entry.js b/spack/tests/pass/reexport/named-3-var/output/entry.js new file mode 100644 index 00000000000..3c16b84b368 --- /dev/null +++ b/spack/tests/pass/reexport/named-3-var/output/entry.js @@ -0,0 +1,9 @@ +const c = 1; +console.log('c', c); +const c1 = c; +const b = c1; +console.log('b'); +const a = b; +console.log('a'); +export { a }; +console.log('entry'); diff --git a/spack/tests/pass/reexport/named-4-fn/input/a.js b/spack/tests/pass/reexport/named-4-fn/input/a.js new file mode 100644 index 00000000000..c9204fe442c --- /dev/null +++ b/spack/tests/pass/reexport/named-4-fn/input/a.js @@ -0,0 +1,3 @@ +export { b } from './b' + +console.log('a'); \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-4-fn/input/b.js b/spack/tests/pass/reexport/named-4-fn/input/b.js new file mode 100644 index 00000000000..f15958e428d --- /dev/null +++ b/spack/tests/pass/reexport/named-4-fn/input/b.js @@ -0,0 +1,5 @@ +export function b() { + console.log(b); +} + +console.log('b', b); \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-4-fn/input/entry.js b/spack/tests/pass/reexport/named-4-fn/input/entry.js new file mode 100644 index 00000000000..1ef88758b72 --- /dev/null +++ b/spack/tests/pass/reexport/named-4-fn/input/entry.js @@ -0,0 +1 @@ +export { b as a } from './a' \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-4-fn/output/entry.js b/spack/tests/pass/reexport/named-4-fn/output/entry.js new file mode 100644 index 00000000000..e69756206a4 --- /dev/null +++ b/spack/tests/pass/reexport/named-4-fn/output/entry.js @@ -0,0 +1,8 @@ +function b() { + console.log(b); +} +console.log('b', b); +const b1 = b; +const b2 = b1; +console.log('a'); +export { b2 as a }; diff --git a/spack/tests/pass/reexport/named-5-class/input/a.js b/spack/tests/pass/reexport/named-5-class/input/a.js new file mode 100644 index 00000000000..c9204fe442c --- /dev/null +++ b/spack/tests/pass/reexport/named-5-class/input/a.js @@ -0,0 +1,3 @@ +export { b } from './b' + +console.log('a'); \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-5-class/input/b.js b/spack/tests/pass/reexport/named-5-class/input/b.js new file mode 100644 index 00000000000..a1ed125fb26 --- /dev/null +++ b/spack/tests/pass/reexport/named-5-class/input/b.js @@ -0,0 +1,3 @@ +export class b { } + +console.log('b', b); \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-5-class/input/entry.js b/spack/tests/pass/reexport/named-5-class/input/entry.js new file mode 100644 index 00000000000..1ef88758b72 --- /dev/null +++ b/spack/tests/pass/reexport/named-5-class/input/entry.js @@ -0,0 +1 @@ +export { b as a } from './a' \ No newline at end of file diff --git a/spack/tests/pass/reexport/named-5-class/output/entry.js b/spack/tests/pass/reexport/named-5-class/output/entry.js new file mode 100644 index 00000000000..c099e5cd2f1 --- /dev/null +++ b/spack/tests/pass/reexport/named-5-class/output/entry.js @@ -0,0 +1,7 @@ +class b { +} +console.log('b', b); +const b1 = b; +const b2 = b1; +console.log('a'); +export { b2 as a }; diff --git a/spack/tests/pass/reexport/recursive/input/a.js b/spack/tests/pass/reexport/recursive/input/a.js new file mode 100644 index 00000000000..b4798238dfd --- /dev/null +++ b/spack/tests/pass/reexport/recursive/input/a.js @@ -0,0 +1,3 @@ +export { default as b } from './b'; + +console.log('a.js'); \ No newline at end of file diff --git a/spack/tests/pass/reexport/recursive/input/b.js b/spack/tests/pass/reexport/recursive/input/b.js new file mode 100644 index 00000000000..ca8af1a20cf --- /dev/null +++ b/spack/tests/pass/reexport/recursive/input/b.js @@ -0,0 +1,4 @@ +import { c as b } from './c'; +export default b; + +console.log('b') \ No newline at end of file diff --git a/spack/tests/pass/reexport/recursive/input/c.js b/spack/tests/pass/reexport/recursive/input/c.js new file mode 100644 index 00000000000..88539eb3cc2 --- /dev/null +++ b/spack/tests/pass/reexport/recursive/input/c.js @@ -0,0 +1,3 @@ +export { d as c } from './d' + +console.log('c') diff --git a/spack/tests/pass/reexport/recursive/input/d.js b/spack/tests/pass/reexport/recursive/input/d.js new file mode 100644 index 00000000000..2ef4d07687b --- /dev/null +++ b/spack/tests/pass/reexport/recursive/input/d.js @@ -0,0 +1,5 @@ +import { e as d } from './e'; + +export { d } + +console.log('d') diff --git a/spack/tests/pass/reexport/recursive/input/e.js b/spack/tests/pass/reexport/recursive/input/e.js new file mode 100644 index 00000000000..0cc3bfa7a1f --- /dev/null +++ b/spack/tests/pass/reexport/recursive/input/e.js @@ -0,0 +1,2 @@ +export const e = 'e'; +console.log('e') diff --git a/spack/tests/pass/reexport/recursive/input/entry.js b/spack/tests/pass/reexport/recursive/input/entry.js new file mode 100644 index 00000000000..bfccb07c9b2 --- /dev/null +++ b/spack/tests/pass/reexport/recursive/input/entry.js @@ -0,0 +1,3 @@ +export { b } from './a' + +console.log('entry'); \ No newline at end of file diff --git a/spack/tests/pass/reexport/recursive/output/entry.js b/spack/tests/pass/reexport/recursive/output/entry.js new file mode 100644 index 00000000000..37b5de05d18 --- /dev/null +++ b/spack/tests/pass/reexport/recursive/output/entry.js @@ -0,0 +1,12 @@ +const d = 'e'; +console.log('e'); +const d1 = d; +console.log('d'); +const b = d1; +console.log('c'); +const __default = b; +console.log('b'); +const b1 = __default; +console.log('a.js'); +export { b1 as b }; +console.log('entry');