From f2e8f98d381cff75229e29564ce5a7458175fea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donny/=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Tue, 18 Oct 2022 11:51:05 +0900 Subject: [PATCH] feat(es/minifier): Skip function declarations in sequential inliner (#6147) --- ...bjectTypeArgsAndConstraints5.2.minified.js | 3 +- ...ssFunctionComponentOverload5.2.minified.js | 4 +- ...ssFunctionComponentOverload6.2.minified.js | 4 +- .../src/compress/optimize/sequences.rs | 71 +++++++++++++++++++ .../tests/benches-full/echarts.js | 14 ++-- .../tests/benches-full/terser.js | 7 +- .../tests/fixture/issues/2257/full/output.js | 4 +- .../fixture/next/wrap-contracts/output.js | 8 +-- .../tests/pass-1/seq/1/output.js | 2 +- .../projects/output/jquery.mobile-1.4.2.js | 4 +- 10 files changed, 95 insertions(+), 26 deletions(-) diff --git a/crates/swc/tests/tsc-references/genericCallWithObjectTypeArgsAndConstraints5.2.minified.js b/crates/swc/tests/tsc-references/genericCallWithObjectTypeArgsAndConstraints5.2.minified.js index 05d8f493d6a5..3951d7870200 100644 --- a/crates/swc/tests/tsc-references/genericCallWithObjectTypeArgsAndConstraints5.2.minified.js +++ b/crates/swc/tests/tsc-references/genericCallWithObjectTypeArgsAndConstraints5.2.minified.js @@ -1,10 +1,9 @@ //// [genericCallWithObjectTypeArgsAndConstraints5.ts] -var c, d; function foo(t, t2) { return function(x) { return t2; }; } -foo(d, c), foo(function() { +foo(void 0, void 0), foo(function() { return 1; }, function() {}); diff --git a/crates/swc/tests/tsc-references/tsxStatelessFunctionComponentOverload5.2.minified.js b/crates/swc/tests/tsc-references/tsxStatelessFunctionComponentOverload5.2.minified.js index afd75e987452..491ab71b5259 100644 --- a/crates/swc/tests/tsc-references/tsxStatelessFunctionComponentOverload5.2.minified.js +++ b/crates/swc/tests/tsc-references/tsxStatelessFunctionComponentOverload5.2.minified.js @@ -13,14 +13,14 @@ define([ get: function() { return MainButton; } - }), _extends = _extends.default; + }); var obj3, obj0 = { to: "world" }; function MainButton(props) { return props.to ? this._buildMainLink(props) : this._buildMainButton(props); } - _extends({ + (_extends = _extends.default)({ onClick: function(e) {} }, obj0), _extends({}, { to: "10000" diff --git a/crates/swc/tests/tsc-references/tsxStatelessFunctionComponentOverload6.2.minified.js b/crates/swc/tests/tsc-references/tsxStatelessFunctionComponentOverload6.2.minified.js index 575b8d845b60..c2affaed51fc 100644 --- a/crates/swc/tests/tsc-references/tsxStatelessFunctionComponentOverload6.2.minified.js +++ b/crates/swc/tests/tsc-references/tsxStatelessFunctionComponentOverload6.2.minified.js @@ -13,7 +13,7 @@ define([ get: function() { return MainButton; } - }), _extends = _extends.default; + }); var obj1, obj = { children: "hi", to: "boo" @@ -21,7 +21,7 @@ define([ function MainButton(props) { return props.to ? this._buildMainLink(props) : this._buildMainButton(props); } - _extends({}, obj), _extends({}, { + (_extends = _extends.default)({}, obj), _extends({}, { to: 10000 }, obj), _extends({}, obj1), _extends({}, obj1, { to: "/to/somewhere" diff --git a/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs b/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs index 9e532970a183..eb9921996eea 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs @@ -591,6 +591,26 @@ where vec![Mergable::Expr(&mut s.arg)] } + Stmt::Decl(Decl::Fn(f)) => { + // Check for side effects + + if !f.function.decorators.is_empty() { + return None; + } + for p in &f.function.params { + if !p.decorators.is_empty() { + return None; + } + + if !self.is_pat_skippable_for_seq(None, &p.pat) { + return None; + } + } + + // Side-effect free function can be skipped. + vec![] + } + _ => return None, }) } @@ -948,6 +968,57 @@ where Ok(()) } + fn is_pat_skippable_for_seq(&mut self, a: Option<&Mergable>, p: &Pat) -> bool { + match p { + Pat::Ident(_) => true, + Pat::Invalid(_) => false, + + Pat::Array(p) => { + for elem in p.elems.iter().flatten() { + if !self.is_pat_skippable_for_seq(a, elem) { + return false; + } + } + + true + } + Pat::Rest(p) => { + if !self.is_pat_skippable_for_seq(a, &p.arg) { + return false; + } + + true + } + Pat::Object(p) => { + for prop in &p.props { + match prop { + ObjectPatProp::KeyValue(KeyValuePatProp { value, key, .. }) => { + if let PropName::Computed(key) = key { + if !self.is_skippable_for_seq(a, &key.expr) { + return false; + } + } + + if !self.is_pat_skippable_for_seq(a, value) { + return false; + } + } + ObjectPatProp::Assign(AssignPatProp { .. }) => return false, + ObjectPatProp::Rest(RestPat { arg, .. }) => { + if !self.is_pat_skippable_for_seq(a, arg) { + return false; + } + } + } + } + + true + } + Pat::Assign(..) => false, + Pat::Expr(e) => self.is_skippable_for_seq(a, e), + } + } + #[cfg_attr(feature = "debug", tracing::instrument(skip_all))] fn is_skippable_for_seq(&self, a: Option<&Mergable>, e: &Expr) -> bool { if self.ctx.in_try_block { diff --git a/crates/swc_ecma_minifier/tests/benches-full/echarts.js b/crates/swc_ecma_minifier/tests/benches-full/echarts.js index 3fa43dbf3bc7..8448373af041 100644 --- a/crates/swc_ecma_minifier/tests/benches-full/echarts.js +++ b/crates/swc_ecma_minifier/tests/benches-full/echarts.js @@ -35410,12 +35410,6 @@ 'OTransform', 'MozTransform', 'msTransform' - ]), TRANSITION_VENDOR = testStyle([ - 'webkitTransition', - 'transition', - 'OTransition', - 'MozTransition', - 'msTransition' ]); function toCSSVendorPrefix(styleVendor, styleProp) { if (!styleVendor) return styleProp; @@ -35427,7 +35421,13 @@ var stl = el.currentStyle || document.defaultView && document.defaultView.getComputedStyle(el); return stl ? style ? stl[style] : stl : null; } - var CSS_TRANSITION_VENDOR = toCSSVendorPrefix(TRANSITION_VENDOR, 'transition'), CSS_TRANSFORM_VENDOR = toCSSVendorPrefix(TRANSFORM_VENDOR, 'transform'), gCssText = "position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;" + (env1.transform3dSupported ? 'will-change:transform;' : ''); + var CSS_TRANSITION_VENDOR = toCSSVendorPrefix(testStyle([ + 'webkitTransition', + 'transition', + 'OTransition', + 'MozTransition', + 'msTransition' + ]), 'transition'), CSS_TRANSFORM_VENDOR = toCSSVendorPrefix(TRANSFORM_VENDOR, 'transform'), gCssText = "position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;" + (env1.transform3dSupported ? 'will-change:transform;' : ''); function assembleTransform(x, y, toString) { var x0 = x.toFixed(0) + 'px', y0 = y.toFixed(0) + 'px'; if (!env1.transformSupported) return toString ? "top:" + y0 + ";left:" + x0 + ";" : [ diff --git a/crates/swc_ecma_minifier/tests/benches-full/terser.js b/crates/swc_ecma_minifier/tests/benches-full/terser.js index 815f1d28ac99..41b872d381eb 100644 --- a/crates/swc_ecma_minifier/tests/benches-full/terser.js +++ b/crates/swc_ecma_minifier/tests/benches-full/terser.js @@ -4321,9 +4321,6 @@ function next_indent() { return indentation + options.indent_level; } - var add_mapping = mappings ? function(token, name) { - mapping_token = token, mapping_name = name; - } : noop; function get() { return might_add_newline && ensure_line_len(), OUTPUT.toString(); } @@ -4408,7 +4405,9 @@ var ret = cont(); return print("]"), ret; }, - add_mapping: add_mapping, + add_mapping: mappings ? function(token, name) { + mapping_token = token, mapping_name = name; + } : noop, option: function(opt) { return options[opt]; }, diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/2257/full/output.js b/crates/swc_ecma_minifier/tests/fixture/issues/2257/full/output.js index 5e0969a63926..1f1f819a275a 100644 --- a/crates/swc_ecma_minifier/tests/fixture/issues/2257/full/output.js +++ b/crates/swc_ecma_minifier/tests/fixture/issues/2257/full/output.js @@ -1952,12 +1952,12 @@ }, 11864: function(module, __unused_webpack_exports, __webpack_require__) { "use strict"; - var utils = __webpack_require__(99677), bind = __webpack_require__(81470), Axios = __webpack_require__(250), mergeConfig = __webpack_require__(10882), defaults = __webpack_require__(52275); + var utils = __webpack_require__(99677), bind = __webpack_require__(81470), Axios = __webpack_require__(250), mergeConfig = __webpack_require__(10882); function createInstance(defaultConfig) { var context = new Axios(defaultConfig), instance = bind(Axios.prototype.request, context); return utils.extend(instance, Axios.prototype, context), utils.extend(instance, context), instance; } - var axios = createInstance(defaults); + var axios = createInstance(__webpack_require__(52275)); axios.Axios = Axios, axios.create = function(instanceConfig) { return createInstance(mergeConfig(axios.defaults, instanceConfig)); }, axios.Cancel = __webpack_require__(69651), axios.CancelToken = __webpack_require__(88149), axios.isCancel = __webpack_require__(37606), axios.all = function(promises) { diff --git a/crates/swc_ecma_minifier/tests/fixture/next/wrap-contracts/output.js b/crates/swc_ecma_minifier/tests/fixture/next/wrap-contracts/output.js index 002e8cb036c9..535954d9b7aa 100644 --- a/crates/swc_ecma_minifier/tests/fixture/next/wrap-contracts/output.js +++ b/crates/swc_ecma_minifier/tests/fixture/next/wrap-contracts/output.js @@ -5972,14 +5972,14 @@ }, 1609: function(module, __unused_webpack_exports, __webpack_require__) { "use strict"; - var utils = __webpack_require__(4867), bind = __webpack_require__(1849), Axios = __webpack_require__(321), mergeConfig = __webpack_require__(7185), defaults = __webpack_require__(5546); + var utils = __webpack_require__(4867), bind = __webpack_require__(1849), Axios = __webpack_require__(321), mergeConfig = __webpack_require__(7185); function createInstance(defaultConfig) { var context = new Axios(defaultConfig), instance = bind(Axios.prototype.request, context); return utils.extend(instance, Axios.prototype, context), utils.extend(instance, context), instance.create = function(instanceConfig) { return createInstance(mergeConfig(defaultConfig, instanceConfig)); }, instance; } - var axios = createInstance(defaults); + var axios = createInstance(__webpack_require__(5546)); axios.Axios = Axios, axios.CanceledError = __webpack_require__(644), axios.CancelToken = __webpack_require__(4972), axios.isCancel = __webpack_require__(6502), axios.VERSION = __webpack_require__(7288).version, axios.toFormData = __webpack_require__(7675), axios.AxiosError = __webpack_require__(723), axios.Cancel = axios.CanceledError, axios.all = function(promises) { return Promise.all(promises); }, axios.spread = __webpack_require__(8713), axios.isAxiosError = __webpack_require__(6268), module.exports = axios, module.exports.default = axios; @@ -6037,11 +6037,11 @@ }, 644: function(module, __unused_webpack_exports, __webpack_require__) { "use strict"; - var AxiosError = __webpack_require__(723), utils = __webpack_require__(4867); + var AxiosError = __webpack_require__(723); function CanceledError(message) { AxiosError.call(this, null == message ? 'canceled' : message, AxiosError.ERR_CANCELED), this.name = 'CanceledError'; } - utils.inherits(CanceledError, AxiosError, { + __webpack_require__(4867).inherits(CanceledError, AxiosError, { __CANCEL__: !0 }), module.exports = CanceledError; }, diff --git a/crates/swc_ecma_minifier/tests/pass-1/seq/1/output.js b/crates/swc_ecma_minifier/tests/pass-1/seq/1/output.js index 14eafe724d0a..529f73b3d272 100644 --- a/crates/swc_ecma_minifier/tests/pass-1/seq/1/output.js +++ b/crates/swc_ecma_minifier/tests/pass-1/seq/1/output.js @@ -89,4 +89,4 @@ function unix() { function toDate() { return new Date(this.valueOf()); } -console.log(MS_PER_400_YEARS); +console.log(3506328 * (60 * (60 * 1000))); diff --git a/crates/swc_ecma_minifier/tests/projects/output/jquery.mobile-1.4.2.js b/crates/swc_ecma_minifier/tests/projects/output/jquery.mobile-1.4.2.js index 276f43ef316b..74a7c1718139 100644 --- a/crates/swc_ecma_minifier/tests/projects/output/jquery.mobile-1.4.2.js +++ b/crates/swc_ecma_minifier/tests/projects/output/jquery.mobile-1.4.2.js @@ -1053,11 +1053,11 @@ }), $.event.special.scrollstart = { enabled: !0, setup: function() { - var scrolling, timer, thisObject = this, $this = $(thisObject); + var scrolling, timer, thisObject = this; function trigger(event1, state) { triggerCustomEvent(thisObject, (scrolling = state) ? "scrollstart" : "scrollstop", event1); } - $this.bind(scrollEvent, function(event1) { + $(thisObject).bind(scrollEvent, function(event1) { $.event.special.scrollstart.enabled && (scrolling || trigger(event1, !0), clearTimeout(timer), timer = setTimeout(function() { trigger(event1, !1); }, 50));