Make constant folding remove unused functions

Summary:
Patterns like:

```
function x() {
  require('foo');
}

const y = function() {
  require('bar');
}

module.exports = 'potato';
```

Are registering as dependencies `foo` and `bar`, because `x` and `y` are not stripped. This change detects both expressions and declarations, and if the binding is not referenced, the function is dropped.

Reviewed By: davidaurelio

Differential Revision: D8043063

fbshipit-source-id: a1deb2bcea4db6bdbe6b5ac569cbbebd0f4b1c3c
This commit is contained in:
Miguel Jimenez Esun 2018-05-21 07:52:22 -07:00 committed by Facebook Github Bot
parent 4a75826683
commit 263118c84b
2 changed files with 96 additions and 0 deletions

View File

@ -186,4 +186,65 @@ describe('constant expressions', () => {
expect(fold('arbitrary.js', code)).toEqual('var x=3;var y=4;var z=0;'); expect(fold('arbitrary.js', code)).toEqual('var x=3;var y=4;var z=0;');
}); });
it('wipes unused functions', () => {
const code = `
var xUnused = function () {
console.log(100);
};
var yUnused = () => {
console.log(200);
};
function zUnused() {
console.log(300);
}
var xUsed = () => {
console.log(400);
};
var yUsed = function () {
console.log(500);
};
function zUsed() {
console.log(600);
}
(() => {
console.log(700);
})();
xUsed();
yUsed();
zUsed();
`;
expect(fold('arbitrary.js', code)).toEqual(
[
'var xUsed=()=>{console.log(400);};',
'var yUsed=function(){console.log(500);};',
'function zUsed(){console.log(600);}',
'(()=>{console.log(700);})();',
'xUsed();',
'yUsed();',
'zUsed();',
].join(''),
);
});
it('verifies that mixes of variables and functions properly minifies', () => {
const code = `
var x = 2;
var y = () => x - 2;
if (x) {
z();
}
`;
expect(fold('arbitrary.js', code)).toEqual('var x=2;{z();}');
});
}); });

View File

@ -15,6 +15,30 @@ import typeof {types as BabelTypes} from '@babel/core';
function constantFoldingPlugin(context: {types: BabelTypes}) { function constantFoldingPlugin(context: {types: BabelTypes}) {
const t = context.types; const t = context.types;
const FunctionDeclaration = {
exit(path: Object) {
const binding = path.scope.getBinding(path.node.id.name);
if (binding && !binding.referenced) {
path.remove();
}
},
};
const FunctionExpression = {
exit(path: Object) {
const parentPath = path.parentPath;
if (t.isVariableDeclarator(parentPath)) {
const binding = parentPath.scope.getBinding(parentPath.node.id.name);
if (binding && !binding.referenced) {
parentPath.remove();
}
}
},
};
const Conditional = { const Conditional = {
exit(path: Object) { exit(path: Object) {
const node = path.node; const node = path.node;
@ -65,12 +89,23 @@ function constantFoldingPlugin(context: {types: BabelTypes}) {
}, },
}; };
const Program = {
exit(path: Object) {
path.traverse({
ArrowFunctionExpression: FunctionExpression,
FunctionDeclaration,
FunctionExpression,
});
},
};
return { return {
visitor: { visitor: {
BinaryExpression: Expression, BinaryExpression: Expression,
ConditionalExpression: Conditional, ConditionalExpression: Conditional,
IfStatement: Conditional, IfStatement: Conditional,
LogicalExpression, LogicalExpression,
Program,
UnaryExpression: Expression, UnaryExpression: Expression,
}, },
}; };