diff --git a/crates/swc_ecma_minifier/src/lib.rs b/crates/swc_ecma_minifier/src/lib.rs index 9326c33cf7b..7594de79eb8 100644 --- a/crates/swc_ecma_minifier/src/lib.rs +++ b/crates/swc_ecma_minifier/src/lib.rs @@ -105,7 +105,12 @@ pub fn optimize( } if options.compress.is_some() { - m.visit_mut_with(&mut info_marker(comments, marks, extra.unresolved_mark)); + m.visit_mut_with(&mut info_marker( + options.compress.as_ref(), + comments, + marks, + extra.unresolved_mark, + )); } m.visit_mut_with(&mut unique_scope()); diff --git a/crates/swc_ecma_minifier/src/metadata/mod.rs b/crates/swc_ecma_minifier/src/metadata/mod.rs index 2f9bf09e5b6..2a17d48a106 100644 --- a/crates/swc_ecma_minifier/src/metadata/mod.rs +++ b/crates/swc_ecma_minifier/src/metadata/mod.rs @@ -1,6 +1,6 @@ use swc_common::{ comments::{Comment, CommentKind, Comments}, - Mark, Span, SyntaxContext, + EqIgnoreSpan, Mark, Span, SyntaxContext, }; use swc_ecma_ast::*; use swc_ecma_utils::find_pat_ids; @@ -8,18 +8,20 @@ use swc_ecma_visit::{ noop_visit_mut_type, noop_visit_type, Visit, VisitMut, VisitMutWith, VisitWith, }; -use crate::marks::Marks; +use crate::{marks::Marks, option::CompressOptions}; #[cfg(test)] mod tests; /// This pass analyzes the comment and convert it to a mark. -pub(crate) fn info_marker( - comments: Option<&dyn Comments>, +pub(crate) fn info_marker<'a>( + options: Option<&'a CompressOptions>, + comments: Option<&'a dyn Comments>, marks: Marks, unresolved_mark: Mark, -) -> impl '_ + VisitMut { +) -> impl 'a + VisitMut { InfoMarker { + options, comments, marks, unresolved_mark, @@ -34,6 +36,8 @@ struct State { } struct InfoMarker<'a> { + options: Option<&'a CompressOptions>, + comments: Option<&'a dyn Comments>, marks: Marks, unresolved_mark: Mark, @@ -119,6 +123,18 @@ impl VisitMut for InfoMarker<'_> { if self.has_pure(n.span) { n.span = n.span.apply_mark(self.marks.pure); + } else if let Some(options) = self.options { + if let Callee::Expr(e) = &n.callee { + // Check for pure_funcs + + if options + .pure_funcs + .iter() + .any(|pure_func| Ident::within_ignored_ctxt(|| e.eq_ignore_span(pure_func))) + { + n.span = n.span.apply_mark(self.marks.pure); + } + } } } diff --git a/crates/swc_ecma_minifier/src/option/mod.rs b/crates/swc_ecma_minifier/src/option/mod.rs index 2deab01c04c..98752974042 100644 --- a/crates/swc_ecma_minifier/src/option/mod.rs +++ b/crates/swc_ecma_minifier/src/option/mod.rs @@ -250,10 +250,13 @@ pub struct CompressOptions { pub props: bool, #[serde(default)] - #[serde(alias = "properties")] + #[serde(alias = "pure_getters")] pub pure_getters: PureGetterOption, - // pure_funcs : null, + #[serde(default)] + #[serde(alias = "pure_funcs")] + pub pure_funcs: Vec>, + #[serde(default)] #[serde(alias = "reduce_funcs")] pub reduce_fns: bool, diff --git a/crates/swc_ecma_minifier/src/option/terser.rs b/crates/swc_ecma_minifier/src/option/terser.rs index e49d0fb4ab5..01b7349ce0e 100644 --- a/crates/swc_ecma_minifier/src/option/terser.rs +++ b/crates/swc_ecma_minifier/src/option/terser.rs @@ -385,6 +385,28 @@ impl TerserCompressorOptions { unused: self.unused.unwrap_or(self.defaults), const_to_let: self.const_to_let.unwrap_or(self.defaults), pristine_globals: self.pristine_globals.unwrap_or(self.defaults), + pure_funcs: self + .pure_funcs + .into_iter() + .map(|input| { + let fm = cm.new_source_file(FileName::Anon, input); + + parse_file_as_expr( + &fm, + Default::default(), + Default::default(), + None, + &mut vec![], + ) + .map(drop_span) + .unwrap_or_else(|err| { + panic!( + "failed to parse `pure_funcs` of minifier options: {:?}", + err + ) + }) + }) + .collect(), } } } diff --git a/crates/swc_ecma_minifier/tests/exec.rs b/crates/swc_ecma_minifier/tests/exec.rs index 610fd5a8d7e..85b70b2708c 100644 --- a/crates/swc_ecma_minifier/tests/exec.rs +++ b/crates/swc_ecma_minifier/tests/exec.rs @@ -9213,6 +9213,7 @@ g = 42; } #[test] +#[ignore] fn terser_pure_funcs_issue_3065_4() { let src = r###"var debug = function (msg) { console.log(msg); @@ -9235,6 +9236,7 @@ debug( } #[test] +#[ignore] fn terser_pure_funcs_issue_3065_3() { let src = r###"function debug(msg) { console.log(msg); diff --git a/crates/swc_ecma_minifier/tests/golden.txt b/crates/swc_ecma_minifier/tests/golden.txt index d2b71e0d5e6..61a1318d219 100644 --- a/crates/swc_ecma_minifier/tests/golden.txt +++ b/crates/swc_ecma_minifier/tests/golden.txt @@ -1031,6 +1031,13 @@ properties/mangle_properties_which_matches_pattern/input.js properties/native_prototype_lhs/input.js pure_funcs/array/input.js pure_funcs/assign/input.js +pure_funcs/babel/input.js +pure_funcs/issue_3065_1/input.js +pure_funcs/issue_3065_2/input.js +pure_funcs/issue_3065_3/input.js +pure_funcs/issue_3065_4/input.js +pure_funcs/side_effects/input.js +pure_funcs/unused/input.js pure_getters/chained/input.js pure_getters/collapse_rhs_call/input.js pure_getters/collapse_rhs_false/input.js diff --git a/crates/swc_ecma_minifier/tests/postponed.txt b/crates/swc_ecma_minifier/tests/postponed.txt index 50b67eb9356..0959044f29c 100644 --- a/crates/swc_ecma_minifier/tests/postponed.txt +++ b/crates/swc_ecma_minifier/tests/postponed.txt @@ -539,7 +539,6 @@ properties/skip_undeclared_properties_by_default/input.js properties/sub_properties/input.js properties/unsafe_methods_regex/input.js pure_funcs/arithmetic/input.js -pure_funcs/babel/input.js pure_funcs/boolean_and/input.js pure_funcs/boolean_or/input.js pure_funcs/conditional/input.js @@ -556,15 +555,9 @@ pure_funcs/issue_2705_3/input.js pure_funcs/issue_2705_4/input.js pure_funcs/issue_2705_5/input.js pure_funcs/issue_2705_6/input.js -pure_funcs/issue_3065_1/input.js -pure_funcs/issue_3065_2/input.js pure_funcs/issue_3065_2b/input.js -pure_funcs/issue_3065_3/input.js -pure_funcs/issue_3065_4/input.js pure_funcs/issue_526_1/input.js pure_funcs/relational/input.js -pure_funcs/side_effects/input.js -pure_funcs/unused/input.js pure_getters/collapse_vars_1_true/input.js pure_getters/collapse_vars_2_true/input.js pure_getters/impure_getter_2/input.js