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

Unused class declaration is not removed #650

Open laverdet opened 7 years ago

laverdet commented 7 years ago

If you minify the following functions only one of the unused class declarations is removed.

function fn(base) {
  class foo extends base {}
}
function fn2(base) {
  class bar extends base.prop {}
}

Becomes:

function fn(a){}
function fn2(a){class b extends a.prop{}}

Obviously this is a contrived example but in my application this is causing a large tree of unused classes to make it into the client bundle.

I'm just testing on the command line with babel:

cat example.js | babel --presets=babili
boopathi commented 7 years ago

This is because base.prop can have side-effects.

const base = {
  get prop() {
    // side-effect
    console.log("Foo");
    return class {};
  }
};
fn2(base);

This has already been discussed - about an option in babili pureGetters to treat getters as pure.

Duplicate #498.

laverdet commented 7 years ago

What about this one:

function fn() {
  class foo {
    get prop() {}
  }
}

Becomes:

function fn(){class a{get prop(){}}}
boopathi commented 7 years ago

That can be removed. Not sure why it's not. I'll check that. Thanks.

j-f1 commented 7 years ago

@boopathi The original example should get minified to this:

function fn(a){}
function fn2(a){a.prop}
laverdet commented 7 years ago

Well if we're truly being pedantic about side-effects I don't think you can remove either one. Extending a class explicitly invokes some checks on the base class which get skipped when you prune the definition. Consider:

// This an awesome function which will return `true` if the passed value
// can be invoked with the `new` operator.
function isConstructor(val) {
  try {
    class foo extends val {};
    return true;
  } catch (err) {
    return false;
  }
}

console.log(isConstructor(function() {}));
console.log(isConstructor({}));

This application will log "true false" but after you run it through Babili it logs "false false". This example feels more pedantic than treating getters as impure but I can't come up with a formal definition for why.