diff --git a/packages/babel-plugin-minify-dead-code-elimination/__tests__/dead-code-elimination-test.js b/packages/babel-plugin-minify-dead-code-elimination/__tests__/dead-code-elimination-test.js index 045305e7d..3c4519e14 100644 --- a/packages/babel-plugin-minify-dead-code-elimination/__tests__/dead-code-elimination-test.js +++ b/packages/babel-plugin-minify-dead-code-elimination/__tests__/dead-code-elimination-test.js @@ -2503,4 +2503,17 @@ describe("dce-plugin", () => { plugins: [[deadcode, { tdz: true }]] } ); + + thePlugin( + "should bail for binary `in` expressions - fix #691", + ` + function foo(props) { + let bar = "width" in props; + delete props.width; + if (bar) { + console.log("foo"); + } + } + ` + ); }); diff --git a/packages/babel-plugin-minify-dead-code-elimination/src/index.js b/packages/babel-plugin-minify-dead-code-elimination/src/index.js index e15fae757..90e7fc998 100644 --- a/packages/babel-plugin-minify-dead-code-elimination/src/index.js +++ b/packages/babel-plugin-minify-dead-code-elimination/src/index.js @@ -391,6 +391,7 @@ module.exports = ({ types: t, traverse }) => { t.isFunction(n) || t.isObjectExpression(n) || t.isArrayExpression(n); + const isReplacementObj = isObj(replacement) || some(replacement, isObj); @@ -398,6 +399,35 @@ module.exports = ({ types: t, traverse }) => { continue; } + // check if it's safe to replace + // To solve https://github.com/babel/minify/issues/691 + // Here we bail for property checks using the "in" operator + // This is because - `in` is a side-effect-free operation but the property + // could be deleted between the replacementPath and referencePath + // It is expensive to compute the delete operation and we bail for + // all the binary "in" operations + let inExpression = replacementPath.isBinaryExpression({ + operator: "in" + }); + + if (!inExpression) { + replacementPath.traverse({ + Function(path) { + path.skip(); + }, + BinaryExpression(path) { + if (path.node.operator === "in") { + inExpression = true; + path.stop(); + } + } + }); + } + + if (inExpression) { + continue; + } + const replaced = replace(binding.referencePaths[0], { binding, scope,