babel / minify

:scissors: An ES6+ aware minifier based on the Babel toolchain (beta)
https://babeljs.io/repl
MIT License
4.39k stars 225 forks source link

babel-plugin-minify-dead-code-elimination: Function name gets removed when keepFnNames is enabled in some scenarios #1001

Open mpeyper opened 3 years ago

mpeyper commented 3 years ago

Describe the bug

The name of functions that infer their names from a variable name is being removed when babel-plugin-minify-dead-code-elimination is used with the keepFnNames option enabled.

All of these function definitions hav the name "func":

function func() {}
console.log(func.name) // "func"
const other = function func() {}
console.log(other.name) // "func"
const func = function() {}
console.log(func.name) // "func"
const func = () => {}
console.log(func.name) // "func"

If we return these definitions from a wrapping function before logging the name, the first two will retain the name from their definitions:

function createFunc() {
  return function func() {}
}

const func = createFunc()
console.log(func.name) // "func"
function createFunc() {
  const other = function func() {}
  return other
}

const func = createFunc()
console.log(func.name) // "func"

However, the second two have lost their names:

function createFunc() {
  const func = function() {}
  return func
}

const func = createFunc()
console.log(func.name) // ""
function createFunc() {
  const func = () => {}
  return func;
}

const func = createFunc()
console.log(func.name) // ""

To Reproduce

Minimal code to reproduce the bug

function createFunc1() {
  return function func() {}
}

function createFunc2() {
  const other = function func() {}
  return other;
}

function createFunc3() {
  const func = function() {}
  return func;
}

function createFunc4() {
  const func = () => {}
  return func;
}

Actual Output

If there is no Error thrown,

function createFunc1() {
  return function func() {};
}

function createFunc2() {
  return function func() {};
}

function createFunc3() {
  return function () {};
}

function createFunc4() {
  return () => {};
}

Expected Output

function createFunc1() {
  return function func() {}
}

function createFunc2() {
  const other = function func() {}
  return other;
}

function createFunc3() {
  const func = function() {}
  return func;
}

function createFunc4() {
  const func = () => {}
  return func;
}

Configuration

How are you using babel-minify?

babelrc.js:

module.exports = {
  plugins: [
    [require.resolve('babel-plugin-minify-dead-code-elimination'), { keepFnName: true }]
  ]
}

Possible solution

The issue seems to be coming from inlining the variable into the return of the function, losing the variable name to infer the function name from.

Additional context

I discovered this when function names were being stripped out of stack traces in error logs.

It also seems particularly problematic for React HOCs where the displayName for the wrapping component is derived from the function name for a function component, e.g.

const withValue = (value) => (Component) => {
  const WithValue = (props) => (
    <Component {...props} value={value} />
  )
  return WithValue
}

const WrappedComponent = withValue(SomeComponent)