diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/pure-assignment/a.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/pure-assignment/a.js new file mode 100644 index 00000000000..1ce7667c325 --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/pure-assignment/a.js @@ -0,0 +1,3 @@ +import {foo} from './b'; + +output = foo; diff --git a/packages/core/integration-tests/test/integration/scope-hoisting/es6/pure-assignment/b.js b/packages/core/integration-tests/test/integration/scope-hoisting/es6/pure-assignment/b.js new file mode 100644 index 00000000000..919f23d1fca --- /dev/null +++ b/packages/core/integration-tests/test/integration/scope-hoisting/es6/pure-assignment/b.js @@ -0,0 +1,4 @@ +export const foo = 2; + +export function bar() {} +bar.displayName = 'hello'; diff --git a/packages/core/integration-tests/test/scope-hoisting.js b/packages/core/integration-tests/test/scope-hoisting.js index df12a4a00ec..1b69a93d211 100644 --- a/packages/core/integration-tests/test/scope-hoisting.js +++ b/packages/core/integration-tests/test/scope-hoisting.js @@ -549,6 +549,25 @@ describe('scope hoisting', function() { let output = await run(b); assert.deepEqual(output, 'bar'); }); + + it('should shake pure property assignments', async function() { + let b = await bundle( + path.join( + __dirname, + '/integration/scope-hoisting/es6/pure-assignment/a.js' + ) + ); + + let output = await run(b); + assert.deepEqual(output, 2); + + let contents = await fs.readFile( + path.join(__dirname, 'dist/a.js'), + 'utf8' + ); + assert(!/bar/.test(contents)); + assert(!/displayName/.test(contents)); + }); }); describe('commonjs', function() { diff --git a/packages/core/parcel-bundler/src/scope-hoisting/shake.js b/packages/core/parcel-bundler/src/scope-hoisting/shake.js index d8f37b09e8a..f8bdc3e7712 100644 --- a/packages/core/parcel-bundler/src/scope-hoisting/shake.js +++ b/packages/core/parcel-bundler/src/scope-hoisting/shake.js @@ -1,7 +1,5 @@ const t = require('@babel/types'); -const EXPORTS_RE = /^\$([^$]+)\$exports$/; - /** * This is a small small implementation of dead code removal specialized to handle * removing unused exports. All other dead code removal happens in workers on each @@ -44,31 +42,24 @@ function getUnusedBinding(path, name) { return null; } - if (isPure(binding)) { + let pure = isPure(binding); + if (!binding.referenced && pure) { return binding; } - if (!EXPORTS_RE.test(name)) { - return null; - } - // Is there any references which aren't simple assignments? let bailout = binding.referencePaths.some( path => !isExportAssignment(path) && !isUnusedWildcard(path) ); - if (bailout) { - return null; - } else { + if (!bailout && pure) { return binding; } + + return null; } function isPure(binding) { - if (binding.referenced) { - return false; - } - if ( binding.path.isVariableDeclarator() && binding.path.get('id').isIdentifier() @@ -122,6 +113,15 @@ function remove(path) { } else if (isUnusedWildcard(path)) { remove(path.parentPath); } else if (!path.removed) { - path.remove(); + if ( + path.parentPath.isSequenceExpression() && + path.parent.expressions.length === 1 + ) { + // replace sequence expression with it's sole child + path.parentPath.replaceWith(path); + remove(path.parentPath); + } else { + path.remove(); + } } }