diff --git a/crates/mako/src/plugins/tree_shaking/statement_graph/analyze_imports_and_exports.rs b/crates/mako/src/plugins/tree_shaking/statement_graph/analyze_imports_and_exports.rs index 48e20b21b..bee34d02d 100644 --- a/crates/mako/src/plugins/tree_shaking/statement_graph/analyze_imports_and_exports.rs +++ b/crates/mako/src/plugins/tree_shaking/statement_graph/analyze_imports_and_exports.rs @@ -89,9 +89,7 @@ pub fn analyze_imports_and_exports( local: named.local.to_string(), imported: named.imported.as_ref().map(|i| match i { ModuleExportName::Ident(i) => i.to_string(), - _ => panic!( - "non-ident imported is not supported when tree shaking" - ), + _ => panic!("non-ident imported is not supported when tree shaking"), }), }); defined_idents.insert(named.local.to_string()); @@ -101,8 +99,7 @@ pub fn analyze_imports_and_exports( continue; } - specifiers - .push(ImportSpecifierInfo::Default(default.local.to_string())); + specifiers.push(ImportSpecifierInfo::Default(default.local.to_string())); defined_idents.insert(default.local.to_string()); } } @@ -133,61 +130,73 @@ pub fn analyze_imports_and_exports( span = export_decl.span; match &export_decl.decl { - swc_ecma_ast::Decl::Class(class_decl) => { - exports = Some(ExportInfo { - source: None, - specifiers: vec![ExportSpecifierInfo::Named { local: class_decl.ident.to_string(), exported: None }], - stmt_id: *id, - }); - defined_idents.insert(class_decl.ident.to_string()); - analyze_and_insert_used_idents(&class_decl.class, Some(class_decl.ident.to_string())); - }, - swc_ecma_ast::Decl::Fn(fn_decl) => { - exports = Some(ExportInfo { - source: None, - specifiers: vec![ExportSpecifierInfo::Named { local: fn_decl.ident.to_string(), exported: None }], - stmt_id: *id, - }); - defined_idents.insert(fn_decl.ident.to_string()); - analyze_and_insert_used_idents(&fn_decl.function, Some(fn_decl.ident.to_string())); - }, - swc_ecma_ast::Decl::Var(var_decl) => { - let mut specifiers = vec![]; - - for v_decl in &var_decl.decls { - - let mut defined_idents_collector = DefinedIdentsCollector::new(); - v_decl.name.visit_with(&mut defined_idents_collector); - let mut used_idents_collector = UsedIdentsCollector::new(); - - if let Some(init) = &v_decl.init { - init.visit_with(&mut used_idents_collector); - } - - let mut local_used_idents = HashSet::new(); - local_used_idents.extend(used_idents_collector.used_idents); - local_used_idents.extend(defined_idents_collector.used_idents); - used_idents.extend(local_used_idents.clone()); - - for defined_ident in defined_idents_collector.defined_idents { - if !is_ident_used(&defined_ident.to_string()) { - continue; - } + swc_ecma_ast::Decl::Class(class_decl) => { + exports = Some(ExportInfo { + source: None, + specifiers: vec![ExportSpecifierInfo::Named { + local: class_decl.ident.to_string(), + exported: None, + }], + stmt_id: *id, + }); + defined_idents.insert(class_decl.ident.to_string()); + analyze_and_insert_used_idents(&class_decl.class, Some(class_decl.ident.to_string())); + } + swc_ecma_ast::Decl::Fn(fn_decl) => { + exports = Some(ExportInfo { + source: None, + specifiers: vec![ExportSpecifierInfo::Named { + local: fn_decl.ident.to_string(), + exported: None, + }], + stmt_id: *id, + }); + defined_idents.insert(fn_decl.ident.to_string()); + analyze_and_insert_used_idents(&fn_decl.function, Some(fn_decl.ident.to_string())); + } + swc_ecma_ast::Decl::Var(var_decl) => { + let mut specifiers = vec![]; - specifiers.push(ExportSpecifierInfo::Named { local: defined_ident.to_string(), exported: None }); - defined_idents.insert(defined_ident.clone()); - defined_idents_map.insert(defined_ident.clone(), local_used_idents.clone()); - } - } + is_self_executed = !is_pure_var_decl(var_decl, unresolve_ctxt); - exports = Some(ExportInfo { - source: None, - specifiers, - stmt_id: *id, - }); - }, - _ => unreachable!("export_decl.decl should not be anything other than a class, function, or variable declaration"), - } + for v_decl in &var_decl.decls { + let mut defined_idents_collector = DefinedIdentsCollector::new(); + v_decl.name.visit_with(&mut defined_idents_collector); + let mut used_idents_collector = UsedIdentsCollector::new(); + + if let Some(init) = &v_decl.init { + init.visit_with(&mut used_idents_collector); + } + + let mut local_used_idents = HashSet::new(); + local_used_idents.extend(used_idents_collector.used_idents); + local_used_idents.extend(defined_idents_collector.used_idents); + used_idents.extend(local_used_idents.clone()); + + for defined_ident in defined_idents_collector.defined_idents { + if !is_ident_used(&defined_ident.to_string()) { + continue; + } + + specifiers.push(ExportSpecifierInfo::Named { + local: defined_ident.to_string(), + exported: None, + }); + defined_idents.insert(defined_ident.clone()); + defined_idents_map.insert(defined_ident.clone(), local_used_idents.clone()); + } + } + + exports = Some(ExportInfo { + source: None, + specifiers, + stmt_id: *id, + }); + } + _ => unreachable!( + "export_decl.decl should not be anything other than a class, function, or variable declaration" + ), + } } swc_ecma_ast::ModuleDecl::ExportDefaultDecl(export_default_decl) => { span = export_default_decl.span; @@ -216,9 +225,7 @@ pub fn analyze_imports_and_exports( fn_decl.ident.as_ref().map(|i| i.to_string()), ); } - _ => unreachable!( - "export_default_decl.decl should not be anything other than a class, function" - ), + _ => unreachable!("export_default_decl.decl should not be anything other than a class, function"), } } swc_ecma_ast::ModuleDecl::ExportDefaultExpr(export_default_expr) => { @@ -257,17 +264,14 @@ pub fn analyze_imports_and_exports( if export_named.src.is_none() { used_idents.insert(local.to_string()); - defined_idents_map - .insert(local.to_string(), [local.to_string()].into()); + defined_idents_map.insert(local.to_string(), [local.to_string()].into()); } specifiers.push(ExportSpecifierInfo::Named { local: local.to_string(), exported: named.exported.as_ref().map(|i| match i { ModuleExportName::Ident(i) => i.to_string(), - _ => panic!( - "non-ident exported is not supported when tree shaking" - ), + _ => panic!("non-ident exported is not supported when tree shaking"), }), }); } @@ -382,17 +386,11 @@ pub fn analyze_imports_and_exports( swc_ecma_ast::Stmt::Decl(decl) => match decl { swc_ecma_ast::Decl::Class(class_decl) => { defined_idents.insert(class_decl.ident.to_string()); - analyze_and_insert_used_idents( - &class_decl.class, - Some(class_decl.ident.to_string()), - ); + analyze_and_insert_used_idents(&class_decl.class, Some(class_decl.ident.to_string())); } swc_ecma_ast::Decl::Fn(fn_decl) => { defined_idents.insert(fn_decl.ident.to_string()); - analyze_and_insert_used_idents( - &fn_decl.function, - Some(fn_decl.ident.to_string()), - ); + analyze_and_insert_used_idents(&fn_decl.function, Some(fn_decl.ident.to_string())); } swc_ecma_ast::Decl::Var(var_decl) => { for v_decl in &var_decl.decls { @@ -411,8 +409,7 @@ pub fn analyze_imports_and_exports( for defined_ident in defined_idents_collector.defined_idents { defined_idents.insert(defined_ident.clone()); - defined_idents_map - .insert(defined_ident.clone(), local_used_idents.clone()); + defined_idents_map.insert(defined_ident.clone(), local_used_idents.clone()); } if !is_pure_var_decl(var_decl, unresolve_ctxt) { @@ -420,9 +417,7 @@ pub fn analyze_imports_and_exports( } } } - _ => unreachable!( - "decl should not be anything other than a class, function, or variable declaration" - ), + _ => unreachable!("decl should not be anything other than a class, function, or variable declaration"), }, swc_ecma_ast::Stmt::Expr(expr) => { span = expr.span; diff --git a/crates/mako/src/plugins/tree_shaking/statement_graph/rustfmt.toml b/crates/mako/src/plugins/tree_shaking/statement_graph/rustfmt.toml new file mode 100644 index 000000000..753065179 --- /dev/null +++ b/crates/mako/src/plugins/tree_shaking/statement_graph/rustfmt.toml @@ -0,0 +1 @@ +max_width = 120 diff --git a/e2e/fixtures/tree-shaking.export_var_decl/expect.js b/e2e/fixtures/tree-shaking.export_var_decl/expect.js new file mode 100644 index 000000000..81a96e6ea --- /dev/null +++ b/e2e/fixtures/tree-shaking.export_var_decl/expect.js @@ -0,0 +1,4 @@ +const { injectSimpleJest } = require("../../../scripts/test-utils"); + +injectSimpleJest(); +require("./dist/index.js"); diff --git a/e2e/fixtures/tree-shaking.export_var_decl/src/export_var.js b/e2e/fixtures/tree-shaking.export_var_decl/src/export_var.js new file mode 100644 index 000000000..21611abf6 --- /dev/null +++ b/e2e/fixtures/tree-shaking.export_var_decl/src/export_var.js @@ -0,0 +1 @@ +export var keep = record(42); diff --git a/e2e/fixtures/tree-shaking.export_var_decl/src/index.tsx b/e2e/fixtures/tree-shaking.export_var_decl/src/index.tsx new file mode 100644 index 000000000..62bfac26a --- /dev/null +++ b/e2e/fixtures/tree-shaking.export_var_decl/src/index.tsx @@ -0,0 +1,6 @@ +import { getStore } from "./recorder.js"; +import "./export_var"; + +it("should keep side effects in export var", () => { + expect(getStore()).toStrictEqual([42]); +}); diff --git a/e2e/fixtures/tree-shaking.export_var_decl/src/recorder.js b/e2e/fixtures/tree-shaking.export_var_decl/src/recorder.js new file mode 100644 index 000000000..e1910a948 --- /dev/null +++ b/e2e/fixtures/tree-shaking.export_var_decl/src/recorder.js @@ -0,0 +1,9 @@ +let store = []; +global.record = function (n) { + store.push(n); + return n; +}; + +export function getStore() { + return store; +}