diff --git a/crates/swc/tests/tsc-references/importCallExpression3ES2020.2.minified.js b/crates/swc/tests/tsc-references/importCallExpression3ES2020.2.minified.js index 43673b1e636c..366b4bbf5392 100644 --- a/crates/swc/tests/tsc-references/importCallExpression3ES2020.2.minified.js +++ b/crates/swc/tests/tsc-references/importCallExpression3ES2020.2.minified.js @@ -5,9 +5,8 @@ export class B { } } //// [2.ts] -async function foo() { +!async function() { class C extends (await import("./0")).B { } new C().print(); -} -foo(); +}(); diff --git a/crates/swc/tests/tsc-references/importCallExpressionInAMD3.2.minified.js b/crates/swc/tests/tsc-references/importCallExpressionInAMD3.2.minified.js index d4fa5efad3a5..e12df3aab3ff 100644 --- a/crates/swc/tests/tsc-references/importCallExpressionInAMD3.2.minified.js +++ b/crates/swc/tests/tsc-references/importCallExpressionInAMD3.2.minified.js @@ -23,14 +23,13 @@ define([ "@swc/helpers/src/_interop_require_wildcard.mjs" ], function(require, exports, _interopRequireWildcard) { "use strict"; - async function foo() { + Object.defineProperty(exports, "__esModule", { + value: !0 + }), _interopRequireWildcard = _interopRequireWildcard.default, async function() { class C extends (await new Promise((resolve, reject)=>require([ "./0" ], (m)=>resolve(_interopRequireWildcard(m)), reject))).B { } new C().print(); - } - Object.defineProperty(exports, "__esModule", { - value: !0 - }), _interopRequireWildcard = _interopRequireWildcard.default, foo(); + }(); }); diff --git a/crates/swc/tests/tsc-references/importCallExpressionInCJS2.2.minified.js b/crates/swc/tests/tsc-references/importCallExpressionInCJS2.2.minified.js index 50c658be2c64..eec766ed6af3 100644 --- a/crates/swc/tests/tsc-references/importCallExpressionInCJS2.2.minified.js +++ b/crates/swc/tests/tsc-references/importCallExpressionInCJS2.2.minified.js @@ -26,8 +26,7 @@ Object.defineProperty(exports, "__esModule", { value: !0 }); const _interopRequireWildcard = require("@swc/helpers/lib/_interop_require_wildcard.js").default; -async function compute(promise) { +!async function(promise) { let j = await promise; - return j ? j.foo() : (j = await Promise.resolve().then(()=>_interopRequireWildcard(require("./1")))).backup(); -} -compute(Promise.resolve().then(()=>_interopRequireWildcard(require("./0")))); + j ? j.foo() : (j = await Promise.resolve().then(()=>_interopRequireWildcard(require("./1")))).backup(); +}(Promise.resolve().then(()=>_interopRequireWildcard(require("./0")))); diff --git a/crates/swc/tests/tsc-references/importCallExpressionInCJS4.2.minified.js b/crates/swc/tests/tsc-references/importCallExpressionInCJS4.2.minified.js index 29e27dc15fc6..5cc0b2c720fb 100644 --- a/crates/swc/tests/tsc-references/importCallExpressionInCJS4.2.minified.js +++ b/crates/swc/tests/tsc-references/importCallExpressionInCJS4.2.minified.js @@ -17,9 +17,8 @@ Object.defineProperty(exports, "__esModule", { value: !0 }); const _interopRequireWildcard = require("@swc/helpers/lib/_interop_require_wildcard.js").default; -async function foo() { +!async function() { class C extends (await Promise.resolve().then(()=>_interopRequireWildcard(require("./0")))).B { } new C().print(); -} -foo(); +}(); diff --git a/crates/swc/tests/tsc-references/importCallExpressionInUMD3.2.minified.js b/crates/swc/tests/tsc-references/importCallExpressionInUMD3.2.minified.js index b66fb159d84c..82e2d0a90106 100644 --- a/crates/swc/tests/tsc-references/importCallExpressionInUMD3.2.minified.js +++ b/crates/swc/tests/tsc-references/importCallExpressionInUMD3.2.minified.js @@ -25,12 +25,11 @@ ], factory) : (global = "undefined" != typeof globalThis ? globalThis : global || self) && factory(global.2Ts = {}, global.interopRequireWildcardMjs); }(this, function(exports, _interopRequireWildcard) { "use strict"; - async function foo() { + Object.defineProperty(exports, "__esModule", { + value: !0 + }), _interopRequireWildcard = _interopRequireWildcard.default, async function() { class C extends (await import("./0")).B { } new C().print(); - } - Object.defineProperty(exports, "__esModule", { - value: !0 - }), _interopRequireWildcard = _interopRequireWildcard.default, foo(); + }(); }); diff --git a/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs b/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs index eb9921996eea..0202dfcdbc3c 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/sequences.rs @@ -594,21 +594,7 @@ where 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![] + vec![Mergable::FnDecl(f)] } _ => return None, @@ -705,6 +691,7 @@ where !v.decls.is_empty() } + Some(Stmt::Decl(Decl::Fn(f))) => !f.ident.is_dummy(), Some(Stmt::Expr(s)) if s.expr.is_invalid() => false, _ => true, @@ -884,6 +871,7 @@ where None => continue, }, Mergable::Expr(e) => e, + Mergable::FnDecl(..) => continue, }, )? { did_work = true; @@ -927,6 +915,7 @@ where break; } } + _ => {} } @@ -956,6 +945,21 @@ where } } } + + // Function declaration is side-effect free. + // + // TODO(kdy1): Paramters with default value can have side effect. But this + // is very unrealistic in real-world code, so I'm + // postponing handling for it. + Mergable::FnDecl(f) => { + if f.function + .params + .iter() + .any(|p| !self.is_pat_skippable_for_seq(Some(a), &p.pat)) + { + break; + } + } } } } @@ -1051,6 +1055,15 @@ where return false; } } + + Mergable::FnDecl(a) => { + // TODO(kdy1): I'm not sure if we can remove this check. I added this + // just to be safe, and we may remove this check in future. + if is_ident_used_by(e.to_id(), &**a) { + log_abort!("ident used by a (fn)"); + return false; + } + } } // We can't proceed if the rhs (a.id = b.right) is @@ -1106,6 +1119,15 @@ where _ => None, }, + + Mergable::FnDecl(a) => Some(collect_infects_from( + &a.function, + AliasConfig { + marks: Some(self.marks), + ignore_nested: true, + need_all: true, + }, + )), }; if let Some(ids_used_by_a_init) = ids_used_by_a_init { @@ -1194,6 +1216,13 @@ where return false; } } + Mergable::FnDecl(a) => { + // TODO(kdy1): I'm not sure if this check is required. + if is_ident_used_by(left_id.to_id(), &**a) { + log_abort!("e.left is used by a ()"); + return false; + } + } } } @@ -1402,6 +1431,7 @@ where let a = match a { Mergable::Expr(e) => dump(*e, false), Mergable::Var(e) => dump(*e, false), + Mergable::FnDecl(e) => dump(*e, false), }; Some( @@ -1420,7 +1450,7 @@ where } match a { - Mergable::Var(..) => {} + Mergable::Var(..) | Mergable::FnDecl(..) => {} Mergable::Expr(a) => { if let Expr::Seq(a) = a { for a in a.exprs.iter_mut().rev() { @@ -1472,6 +1502,12 @@ where return Ok(false); } }, + + Mergable::FnDecl(..) => { + // A function declaration is always inlinable as it can be + // viewed as a variable with an identifier name and a + // function expression as a initialized. + } } } @@ -1806,6 +1842,14 @@ where crate::debug::dump(&*b, false) ); } + + Mergable::FnDecl(a) => { + trace_op!( + "sequences: Trying to merge `{}` => `{}`", + crate::debug::dump(&**a, false), + crate::debug::dump(&*b, false) + ); + } } if self.replace_seq_update(a, b)? { @@ -2033,7 +2077,7 @@ where } } - (left_id.clone(), right) + (left_id.clone(), Some(right)) } _ => return Ok(false), } @@ -2066,35 +2110,57 @@ where } match &mut a.init { - Some(v) => (left, v), + Some(v) => (left, Some(v)), None => { if usage.declared_count > 1 { return Ok(false); } right_val = undefined(DUMMY_SP); - (left, &mut right_val) + (left, Some(&mut right_val)) } } } else { return Ok(false); } } + + Mergable::FnDecl(a) => { + if let Some(usage) = self.data.vars.get(&a.ident.to_id()) { + if usage.ref_count != 1 || usage.reassigned() || !usage.is_fn_local { + return Ok(false); + } + + if usage.inline_prevented { + return Ok(false); + } + + if contains_arguments(&a.function) { + return Ok(false); + } + + (a.ident.clone(), None) + } else { + return Ok(false); + } + } }; - if a_right.is_this() - || matches!( - &**a_right, - Expr::Ident(Ident { - sym: js_word!("arguments"), - .. - }) - ) - { - return Ok(false); - } - if contains_arguments(&**a_right) { - return Ok(false); + if let Some(a_right) = a_right { + if a_right.is_this() + || matches!( + &**a_right, + Expr::Ident(Ident { + sym: js_word!("arguments"), + .. + }) + ) + { + return Ok(false); + } + if contains_arguments(&**a_right) { + return Ok(false); + } } macro_rules! take_a { @@ -2134,6 +2200,15 @@ where Box::new(a.take()) } + + Mergable::FnDecl(a) => { + // We can inline a function declaration as a function expression. + + Box::new(Expr::Fn(FnExpr { + ident: Some(a.ident.take()), + function: a.function.take(), + })) + } } }; } @@ -2307,6 +2382,7 @@ impl Visit for UsageCounter<'_> { enum Mergable<'a> { Var(&'a mut VarDeclarator), Expr(&'a mut Expr), + FnDecl(&'a mut FnDecl), } impl Mergable<'_> { @@ -2320,6 +2396,7 @@ impl Mergable<'_> { Expr::Assign(s) => s.left.as_ident().map(|v| v.to_id()), _ => None, }, + Mergable::FnDecl(f) => Some(f.ident.to_id()), } } } 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 535954d9b7aa..a9f03edec227 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 @@ -6313,16 +6313,12 @@ }, 5546: function(module, __unused_webpack_exports, __webpack_require__) { "use strict"; - var process = __webpack_require__(3454), utils = __webpack_require__(4867), normalizeHeaderName = __webpack_require__(6016), AxiosError = __webpack_require__(723), transitionalDefaults = __webpack_require__(7874), toFormData = __webpack_require__(7675), DEFAULT_CONTENT_TYPE = { + var adapter, process = __webpack_require__(3454), utils = __webpack_require__(4867), normalizeHeaderName = __webpack_require__(6016), AxiosError = __webpack_require__(723), transitionalDefaults = __webpack_require__(7874), toFormData = __webpack_require__(7675), DEFAULT_CONTENT_TYPE = { 'Content-Type': 'application/x-www-form-urlencoded' }; function setContentTypeIfUnset(headers, value) { !utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type']) && (headers['Content-Type'] = value); } - function getDefaultAdapter() { - var adapter; - return 'undefined' != typeof XMLHttpRequest ? adapter = __webpack_require__(5448) : void 0 !== process && '[object process]' === Object.prototype.toString.call(process) && (adapter = __webpack_require__(5448)), adapter; - } function stringifySafely(rawValue, parser, encoder) { if (utils.isString(rawValue)) try { return (parser || JSON.parse)(rawValue), utils.trim(rawValue); @@ -6333,7 +6329,7 @@ } var defaults = { transitional: transitionalDefaults, - adapter: getDefaultAdapter(), + adapter: ('undefined' != typeof XMLHttpRequest ? adapter = __webpack_require__(5448) : void 0 !== process && '[object process]' === Object.prototype.toString.call(process) && (adapter = __webpack_require__(5448)), adapter), transformRequest: [ function(data, headers) { if (normalizeHeaderName(headers, 'Accept'), normalizeHeaderName(headers, 'Content-Type'), utils.isFormData(data) || utils.isArrayBuffer(data) || utils.isBuffer(data) || utils.isStream(data) || utils.isFile(data) || utils.isBlob(data)) return data; @@ -17511,14 +17507,13 @@ function deprecate(e, t) { if (config("noDeprecation")) return e; var r = !1; - function deprecated() { + return function() { if (!r) { if (config("throwDeprecation")) throw Error(t); config("traceDeprecation") ? console.trace(t) : console.warn(t), r = !0; } return e.apply(this, arguments); - } - return deprecated; + }; } function config(e) { try { @@ -19163,7 +19158,7 @@ return bigint ? String(value) : void 0; } } - function stringify(value, replacer, space) { + return function(value, replacer, space) { if (arguments.length > 1) { let spacer = ''; if ('number' == typeof space ? spacer = ' '.repeat(Math.min(space, 10)) : 'string' == typeof space && (spacer = space.slice(0, 10)), null != replacer) { @@ -19175,8 +19170,7 @@ if (0 !== spacer.length) return stringifyIndent('', value, [], spacer, ''); } return stringifySimple('', value, []); - } - return stringify; + }; } }, 2399: function(module, __unused_webpack_exports, __webpack_require__) { @@ -20316,14 +20310,13 @@ return exports.deprecate(fn, msg).apply(this, arguments); }; var warned = !1; - function deprecated() { + return function() { if (!warned) { if (process.throwDeprecation) throw Error(msg); process.traceDeprecation ? console.trace(msg) : console.error(msg), warned = !0; } return fn.apply(this, arguments); - } - return deprecated; + }; }; var debugs = {}, debugEnvRegex = /^$/; if (process.env.NODE_DEBUG) { diff --git a/crates/swc_ecma_minifier/tests/terser/compress/collapse_vars/issue_1631_3/output.js b/crates/swc_ecma_minifier/tests/terser/compress/collapse_vars/issue_1631_3/output.js index a3bf4a359b49..9e4ed1a21511 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/collapse_vars/issue_1631_3/output.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/collapse_vars/issue_1631_3/output.js @@ -1,10 +1,7 @@ function g() { - function f() { - return (a = 2), 4; - } - var a = 0, - b = 1, - t = f(); - return (b = a + t); + var a = 0, b = 1, t = function f() { + return a = 2, 4; + }(); + return b = a + t; } console.log(g()); diff --git a/crates/swc_ecma_minifier/tests/terser/compress/drop_unused/issue_t161_top_retain_3/output.js b/crates/swc_ecma_minifier/tests/terser/compress/drop_unused/issue_t161_top_retain_3/output.js index 446e64265475..2f276ce45e88 100644 --- a/crates/swc_ecma_minifier/tests/terser/compress/drop_unused/issue_t161_top_retain_3/output.js +++ b/crates/swc_ecma_minifier/tests/terser/compress/drop_unused/issue_t161_top_retain_3/output.js @@ -1,4 +1 @@ -function f() { - return 2; -} -console.log(f(), 3); +console.log(2, 3);