diff --git a/crates/swc/src/builder.rs b/crates/swc/src/builder.rs index 3a8e5d9f8921..0b493bf27c56 100644 --- a/crates/swc/src/builder.rs +++ b/crates/swc/src/builder.rs @@ -12,7 +12,7 @@ use swc_common::{ util::take::Take, FileName, Mark, SourceMap, }; -use swc_ecma_ast::{EsVersion, Module}; +use swc_ecma_ast::{EsVersion, Module, Script}; use swc_ecma_minifier::option::{terser::TerserTopLevelOptions, MinifyOptions}; use swc_ecma_parser::Syntax; use swc_ecma_transforms::{ @@ -351,11 +351,13 @@ impl<'a, 'b, P: swc_ecma_visit::Fold> PassBuilder<'a, 'b, P> { options: self.minify, cm: self.cm.clone(), comments: comments.cloned(), - unresolved_mark: self.unresolved_mark, top_level_mark: self.top_level_mark, }), Optional::new( - hygiene_with_config(self.hygiene.clone().unwrap_or_default()), + hygiene_with_config(swc_ecma_transforms_base::hygiene::Config { + top_level_mark: self.top_level_mark, + ..self.hygiene.clone().unwrap_or_default() + }), self.hygiene.is_some() && !is_mangler_enabled ), Optional::new(fixer(comments.map(|v| v as &dyn Comments)), self.fixer), @@ -367,7 +369,6 @@ struct MinifierPass { options: Option, cm: Lrc, comments: Option, - unresolved_mark: Mark, top_level_mark: Mark, } @@ -408,13 +409,18 @@ impl VisitMut for MinifierPass { return; } - m.visit_mut_with(&mut hygiene()); - m.visit_mut_with(&mut resolver( - self.unresolved_mark, - self.top_level_mark, - false, + m.visit_mut_with(&mut hygiene_with_config( + swc_ecma_transforms_base::hygiene::Config { + top_level_mark: self.top_level_mark, + ..Default::default() + }, )); + let unresolved_mark = Mark::new(); + let top_level_mark = Mark::new(); + + m.visit_mut_with(&mut resolver(unresolved_mark, top_level_mark, false)); + m.map_with_mut(|m| { swc_ecma_minifier::optimize( m.into(), @@ -423,14 +429,74 @@ impl VisitMut for MinifierPass { None, &opts, &swc_ecma_minifier::option::ExtraOptions { - unresolved_mark: self.unresolved_mark, - top_level_mark: self.top_level_mark, + unresolved_mark, + top_level_mark, }, ) .expect_module() }) } } + + fn visit_mut_script(&mut self, m: &mut Script) { + if let Some(options) = &self.options { + let opts = MinifyOptions { + compress: options + .compress + .clone() + .unwrap_as_option(|default| match default { + Some(true) => Some(Default::default()), + _ => None, + }) + .map(|mut v| { + if v.const_to_let.is_none() { + v.const_to_let = Some(true); + } + + v.into_config(self.cm.clone()) + }), + mangle: options + .mangle + .clone() + .unwrap_as_option(|default| match default { + Some(true) => Some(Default::default()), + _ => None, + }), + ..Default::default() + }; + + if opts.compress.is_none() && opts.mangle.is_none() { + return; + } + + m.visit_mut_with(&mut hygiene_with_config( + swc_ecma_transforms_base::hygiene::Config { + top_level_mark: self.top_level_mark, + ..Default::default() + }, + )); + + let unresolved_mark = Mark::new(); + let top_level_mark = Mark::new(); + + m.visit_mut_with(&mut resolver(unresolved_mark, top_level_mark, false)); + + m.map_with_mut(|m| { + swc_ecma_minifier::optimize( + m.into(), + self.cm.clone(), + self.comments.as_ref().map(|v| v as &dyn Comments), + None, + &opts, + &swc_ecma_minifier::option::ExtraOptions { + unresolved_mark, + top_level_mark, + }, + ) + .expect_script() + }) + } + } } fn should_enable(target: EsVersion, feature: EsVersion) -> bool { diff --git a/crates/swc/tests/fixture/issues-5xxx/5068/1/input/.swcrc b/crates/swc/tests/fixture/issues-5xxx/5068/1/input/.swcrc new file mode 100644 index 000000000000..65f433d6c2c3 --- /dev/null +++ b/crates/swc/tests/fixture/issues-5xxx/5068/1/input/.swcrc @@ -0,0 +1,19 @@ +{ + "jsc": { + "parser": { + "syntax": "typescript", + "tsx": true + }, + "target": "es5", + "loose": false, + "minify": { + "compress": false, + "mangle": false + } + }, + "module": { + "type": "es6" + }, + "minify": false, + "isModule": true +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-5xxx/5068/1/input/index.js b/crates/swc/tests/fixture/issues-5xxx/5068/1/input/index.js new file mode 100644 index 000000000000..d960717a4328 --- /dev/null +++ b/crates/swc/tests/fixture/issues-5xxx/5068/1/input/index.js @@ -0,0 +1,11 @@ +import styled from 'styled-components'; + +const C1 = styled.div` + position: absolute; +`; + +const C2 = styled.div` + position: absolute; +`; + +const debug = eval(''); \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-5xxx/5068/1/output/index.js b/crates/swc/tests/fixture/issues-5xxx/5068/1/output/index.js new file mode 100644 index 000000000000..be62302addc0 --- /dev/null +++ b/crates/swc/tests/fixture/issues-5xxx/5068/1/output/index.js @@ -0,0 +1,23 @@ +import _tagged_template_literal from "@swc/helpers/src/_tagged_template_literal.mjs"; +function _templateObject() { + var data = _tagged_template_literal([ + "\n position: absolute;\n" + ]); + _templateObject = function _templateObject() { + return data; + }; + return data; +} +function _templateObject1() { + var data = _tagged_template_literal([ + "\n position: absolute;\n" + ]); + _templateObject1 = function _templateObject1() { + return data; + }; + return data; +} +import styled from "styled-components"; +var C1 = styled.div(_templateObject()); +var C2 = styled.div(_templateObject1()); +var debug = eval(""); diff --git a/crates/swc/tests/fixture/issues-5xxx/5068/2/input/.swcrc b/crates/swc/tests/fixture/issues-5xxx/5068/2/input/.swcrc new file mode 100644 index 000000000000..952009a4633d --- /dev/null +++ b/crates/swc/tests/fixture/issues-5xxx/5068/2/input/.swcrc @@ -0,0 +1,19 @@ +{ + "jsc": { + "parser": { + "syntax": "typescript", + "tsx": true + }, + "target": "es5", + "loose": false, + "minify": { + "compress": false, + "mangle": true + } + }, + "module": { + "type": "es6" + }, + "minify": false, + "isModule": true +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-5xxx/5068/2/input/index.js b/crates/swc/tests/fixture/issues-5xxx/5068/2/input/index.js new file mode 100644 index 000000000000..d960717a4328 --- /dev/null +++ b/crates/swc/tests/fixture/issues-5xxx/5068/2/input/index.js @@ -0,0 +1,11 @@ +import styled from 'styled-components'; + +const C1 = styled.div` + position: absolute; +`; + +const C2 = styled.div` + position: absolute; +`; + +const debug = eval(''); \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-5xxx/5068/2/output/index.js b/crates/swc/tests/fixture/issues-5xxx/5068/2/output/index.js new file mode 100644 index 000000000000..23f50124eff5 --- /dev/null +++ b/crates/swc/tests/fixture/issues-5xxx/5068/2/output/index.js @@ -0,0 +1,23 @@ +import _tagged_template_literal from "@swc/helpers/src/_tagged_template_literal.mjs"; +function _templateObject() { + var n = _tagged_template_literal([ + "\n position: absolute;\n" + ]); + _templateObject = function r() { + return n; + }; + return n; +} +function _templateObject1() { + var n = _tagged_template_literal([ + "\n position: absolute;\n" + ]); + _templateObject1 = function r() { + return n; + }; + return n; +} +import styled from "styled-components"; +var C1 = styled.div(_templateObject()); +var C2 = styled.div(_templateObject1()); +var debug = eval(""); diff --git a/crates/swc_ecma_minifier/src/lib.rs b/crates/swc_ecma_minifier/src/lib.rs index 83c319ec8aca..6d26318c7971 100644 --- a/crates/swc_ecma_minifier/src/lib.rs +++ b/crates/swc_ecma_minifier/src/lib.rs @@ -274,7 +274,12 @@ pub fn optimize( ) .compile(); - n.visit_mut_with(&mut name_mangler(mangle.clone(), preserved, chars)); + n.visit_mut_with(&mut name_mangler( + mangle.clone(), + preserved, + chars, + extra.top_level_mark, + )); if let Some(property_mangle_options) = &mangle.props { mangle_properties(&mut n, &module_info, property_mangle_options.clone(), chars); diff --git a/crates/swc_ecma_minifier/src/pass/mangle_names/mod.rs b/crates/swc_ecma_minifier/src/pass/mangle_names/mod.rs index d8a9ddd0c6ed..e23fe09694c5 100644 --- a/crates/swc_ecma_minifier/src/pass/mangle_names/mod.rs +++ b/crates/swc_ecma_minifier/src/pass/mangle_names/mod.rs @@ -1,6 +1,6 @@ use rustc_hash::{FxHashMap, FxHashSet}; use swc_atoms::JsWord; -use swc_common::chain; +use swc_common::{chain, Mark}; use swc_ecma_ast::{Module, *}; use swc_ecma_transforms_base::rename::{renamer, Renamer}; use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith}; @@ -15,6 +15,7 @@ pub(crate) fn name_mangler( options: MangleOptions, preserved: FxHashSet, chars: Base54Chars, + top_level_mark: Mark, ) -> impl VisitMut { chain!( LabelMangler { @@ -27,6 +28,7 @@ pub(crate) fn name_mangler( swc_ecma_transforms_base::hygiene::Config { keep_class_names: options.keep_class_names, safari_10: options.safari10, + top_level_mark }, ManglingRenamer { chars, preserved } ) diff --git a/crates/swc_ecma_transforms_base/src/hygiene/mod.rs b/crates/swc_ecma_transforms_base/src/hygiene/mod.rs index 12ac2c1ef2f1..33887ce3c73d 100644 --- a/crates/swc_ecma_transforms_base/src/hygiene/mod.rs +++ b/crates/swc_ecma_transforms_base/src/hygiene/mod.rs @@ -1,4 +1,4 @@ -use swc_common::chain; +use swc_common::{chain, Mark}; use swc_ecma_ast::*; use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut}; @@ -15,6 +15,10 @@ pub struct Config { /// If true, the bug of safari 10 is avoided. pub safari_10: bool, + + /// The marks derived from this marks will treated as `specified by user` + /// and other marks will be treated as `generated by swc`. + pub top_level_mark: Mark, } /// See [hygiene_with_config] for doc. Creates a `hygiene` pass with default diff --git a/crates/swc_ecma_transforms_base/src/rename/analyzer/mod.rs b/crates/swc_ecma_transforms_base/src/rename/analyzer/mod.rs index fd8baf8e2171..7527fffb9734 100644 --- a/crates/swc_ecma_transforms_base/src/rename/analyzer/mod.rs +++ b/crates/swc_ecma_transforms_base/src/rename/analyzer/mod.rs @@ -1,3 +1,4 @@ +use swc_common::Mark; use swc_ecma_ast::*; use swc_ecma_visit::{noop_visit_type, Visit, VisitWith}; @@ -9,6 +10,11 @@ pub(super) mod scope; #[derive(Debug, Default)] pub(super) struct Analyzer { pub safari_10: bool, + /// If `eval` exists for the current scope, we only rename synthesized + /// identifiers. + pub has_eval: bool, + /// The [Mark] which is parent of user-specified identifiers. + pub top_level_mark: Mark, pub is_pat_decl: bool, pub var_belong_to_fn_scope: bool, @@ -25,12 +31,12 @@ impl Analyzer { if belong_to_fn_scope { match self.scope.kind { ScopeKind::Fn => { - self.scope.add_decl(&id); + self.scope.add_decl(&id, self.has_eval, self.top_level_mark); } ScopeKind::Block => self.hoisted_vars.push(id), } } else { - self.scope.add_decl(&id); + self.scope.add_decl(&id, self.has_eval, self.top_level_mark); } } @@ -45,14 +51,16 @@ impl Analyzer { { let mut v = Analyzer { safari_10: self.safari_10, + has_eval: self.has_eval, + top_level_mark: self.top_level_mark, + is_pat_decl: self.is_pat_decl, + var_belong_to_fn_scope: false, + in_catch_params: false, scope: Scope { kind, ..Default::default() }, - is_pat_decl: self.is_pat_decl, - var_belong_to_fn_scope: false, - in_catch_params: false, hoisted_vars: Default::default(), }; diff --git a/crates/swc_ecma_transforms_base/src/rename/analyzer/scope.rs b/crates/swc_ecma_transforms_base/src/rename/analyzer/scope.rs index 3a7c31f16d65..456dbc0165f3 100644 --- a/crates/swc_ecma_transforms_base/src/rename/analyzer/scope.rs +++ b/crates/swc_ecma_transforms_base/src/rename/analyzer/scope.rs @@ -9,7 +9,7 @@ use std::{ use rayon::prelude::*; use rustc_hash::FxHashSet; use swc_atoms::{js_word, JsWord}; -use swc_common::{collections::AHashMap, util::take::Take, SyntaxContext}; +use swc_common::{collections::AHashMap, util::take::Take, Mark, SyntaxContext}; use swc_ecma_ast::*; use tracing::debug; @@ -81,13 +81,17 @@ pub(super) struct ScopeData { } impl Scope { - pub(super) fn add_decl(&mut self, id: &Id) { + pub(super) fn add_decl(&mut self, id: &Id, has_eval: bool, top_level_mark: Mark) { if id.0 == js_word!("arguments") { return; } self.data.all.insert(fast_id(id.clone())); if !self.data.queue.contains(id) { + if has_eval && id.1.outer().is_descendant_of(top_level_mark) { + return; + } + self.data.queue.push(id.clone()); } } diff --git a/crates/swc_ecma_transforms_base/src/rename/mod.rs b/crates/swc_ecma_transforms_base/src/rename/mod.rs index 386db25a32db..72bc7defae12 100644 --- a/crates/swc_ecma_transforms_base/src/rename/mod.rs +++ b/crates/swc_ecma_transforms_base/src/rename/mod.rs @@ -106,6 +106,7 @@ where node: &N, skip_one: bool, is_module_or_script: bool, + has_eval: bool, ) -> AHashMap where N: VisitWith + VisitWith>, @@ -114,6 +115,9 @@ where let mut scope = { let mut v = Analyzer { safari_10: self.config.safari_10, + has_eval, + top_level_mark: self.config.top_level_mark, + ..Default::default() }; if skip_one { @@ -178,7 +182,7 @@ macro_rules! unit { if contains_eval(n, true) { n.visit_mut_children_with(self); } else { - let map = self.get_map(n, false, false); + let map = self.get_map(n, false, false, false); n.visit_mut_with(&mut rename_with_config(&map, self.config.clone())); } @@ -190,7 +194,7 @@ macro_rules! unit { if contains_eval(n, true) { n.visit_mut_children_with(self); } else { - let map = self.get_map(n, true, false); + let map = self.get_map(n, true, false, false); n.visit_mut_with(&mut rename_with_config(&map, self.config.clone())); } @@ -228,25 +232,33 @@ where self.preserved = self.renamer.preserved_ids_for_module(m); self.unresolved = self.get_unresolved(m); - if contains_eval(m, true) { - m.visit_mut_children_with(self); - } else { - let map = self.get_map(m, false, true); + let has_eval = contains_eval(m, true); + + { + let map = self.get_map(m, false, true, has_eval); m.visit_mut_with(&mut rename_with_config(&map, self.config.clone())); } + + if has_eval { + m.visit_mut_children_with(self); + } } - fn visit_mut_script(&mut self, s: &mut Script) { - self.preserved = self.renamer.preserved_ids_for_script(s); - self.unresolved = self.get_unresolved(s); + fn visit_mut_script(&mut self, m: &mut Script) { + self.preserved = self.renamer.preserved_ids_for_script(m); + self.unresolved = self.get_unresolved(m); - if contains_eval(s, true) { - s.visit_mut_children_with(self); - } else { - let map = self.get_map(s, false, true); + let has_eval = contains_eval(m, true); - s.visit_mut_with(&mut rename_with_config(&map, self.config.clone())); + { + let map = self.get_map(m, false, true, has_eval); + + m.visit_mut_with(&mut rename_with_config(&map, self.config.clone())); + } + + if has_eval { + m.visit_mut_children_with(self); } } } diff --git a/crates/swc_ecma_transforms_base/src/resolver/mod.rs b/crates/swc_ecma_transforms_base/src/resolver/mod.rs index 5cc9058e4472..8117de87064c 100644 --- a/crates/swc_ecma_transforms_base/src/resolver/mod.rs +++ b/crates/swc_ecma_transforms_base/src/resolver/mod.rs @@ -151,6 +151,7 @@ pub fn resolver( config: InnerConfig { handle_types: typescript, unresolved_mark, + top_level_mark, }, }) } @@ -211,6 +212,7 @@ struct Resolver<'a> { struct InnerConfig { handle_types: bool, unresolved_mark: Mark, + top_level_mark: Mark, } impl<'a> Resolver<'a> { @@ -232,7 +234,11 @@ impl<'a> Resolver<'a> { F: for<'aa> FnOnce(&mut Resolver<'aa>), { let mut child = Resolver { - current: Scope::new(kind, Mark::new(), Some(&self.current)), + current: Scope::new( + kind, + Mark::fresh(self.config.top_level_mark), + Some(&self.current), + ), ident_type: IdentType::Ref, config: self.config, in_type: self.in_type, diff --git a/crates/swc_ecma_transforms_base/src/resolver/tests.rs b/crates/swc_ecma_transforms_base/src/resolver/tests.rs index 11911d6fb520..1c754cfcc596 100644 --- a/crates/swc_ecma_transforms_base/src/resolver/tests.rs +++ b/crates/swc_ecma_transforms_base/src/resolver/tests.rs @@ -67,6 +67,7 @@ fn test_mark_for() { InnerConfig { handle_types: true, unresolved_mark: Mark::fresh(Mark::root()), + top_level_mark: mark1, }, ); let mut folder2 = Resolver::new( @@ -74,6 +75,7 @@ fn test_mark_for() { InnerConfig { handle_types: true, unresolved_mark: Mark::fresh(Mark::root()), + top_level_mark: mark2, }, ); folder2 @@ -86,6 +88,7 @@ fn test_mark_for() { InnerConfig { handle_types: true, unresolved_mark: Mark::fresh(Mark::root()), + top_level_mark: mark3, }, ); folder3 @@ -99,6 +102,7 @@ fn test_mark_for() { InnerConfig { handle_types: true, unresolved_mark: Mark::fresh(Mark::root()), + top_level_mark: mark4, }, ); folder4