Browse Source

exclude dead branches

better-aggressive
Rich-Harris 9 years ago
parent
commit
b16921646d
  1. 13
      src/Module.js
  2. 65
      src/ast/conditions.js
  3. 7
      src/ast/create.js
  4. 8
      test/function/skips-dead-branches-b/_config.js
  5. 10
      test/function/skips-dead-branches-b/main.js
  6. 8
      test/function/skips-dead-branches-c/_config.js
  7. 10
      test/function/skips-dead-branches-c/main.js
  8. 8
      test/function/skips-dead-branches-d/_config.js
  9. 10
      test/function/skips-dead-branches-d/main.js
  10. 8
      test/function/skips-dead-branches-e/_config.js
  11. 10
      test/function/skips-dead-branches-e/main.js
  12. 8
      test/function/skips-dead-branches/_config.js
  13. 10
      test/function/skips-dead-branches/main.js
  14. 2
      test/test.js

13
src/Module.js

@ -7,6 +7,8 @@ import { basename, extname } from './utils/path.js';
import getLocation from './utils/getLocation.js'; import getLocation from './utils/getLocation.js';
import makeLegalIdentifier from './utils/makeLegalIdentifier.js'; import makeLegalIdentifier from './utils/makeLegalIdentifier.js';
import SOURCEMAPPING_URL from './utils/sourceMappingURL.js'; import SOURCEMAPPING_URL from './utils/sourceMappingURL.js';
import { isFalsy, isTruthy } from './ast/conditions.js';
import { emptyBlockStatement } from './ast/create.js';
class SyntheticDefaultDeclaration { class SyntheticDefaultDeclaration {
constructor ( node, statement, name ) { constructor ( node, statement, name ) {
@ -446,6 +448,17 @@ export default class Module {
walk( ast, { walk( ast, {
enter: node => { enter: node => {
// eliminate dead branches early
if ( node.type === 'IfStatement' ) {
if ( isFalsy( node.test ) ) {
this.magicString.overwrite( node.consequent.start, node.consequent.end, '{}' );
node.consequent = emptyBlockStatement( node.consequent.start, node.consequent.end );
} else if ( node.alternate && isTruthy( node.test ) ) {
this.magicString.overwrite( node.alternate.start, node.alternate.end, '{}' );
node.alternate = emptyBlockStatement( node.alternate.start, node.alternate.end );
}
}
this.magicString.addSourcemapLocation( node.start ); this.magicString.addSourcemapLocation( node.start );
this.magicString.addSourcemapLocation( node.end ); this.magicString.addSourcemapLocation( node.end );
} }

65
src/ast/conditions.js

@ -0,0 +1,65 @@
function isEqualTest ( node ) {
return node.type === 'BinaryExpression' && ( node.operator === '===' || node.operator === '==' );
}
function isNotEqualTest ( node ) {
return node.type === 'BinaryExpression' && ( node.operator === '!==' || node.operator === '!=' );
}
function nodesAreEqual ( a, b ) {
if ( a.type !== b.type ) return false;
if ( a.type === 'Literal' ) return a.value === b.value;
if ( a.type === 'Identifier' ) return a.name === b.name;
return false;
}
function nodesAreNotEqual ( a, b ) {
if ( a.type !== b.type ) return false;
if ( a.type === 'Literal' ) return a.value != b.value;
if ( a.type === 'Identifier' ) return a.name != b.name;
return false;
}
export function isTruthy ( node ) {
if ( node.type === 'Literal' && node.value ) return true;
if ( node.type === 'ParenthesizedExpression' ) return isTruthy( node.expression );
if ( isEqualTest( node ) ) return nodesAreEqual( node.left, node.right );
if ( isNotEqualTest( node ) ) return nodesAreNotEqual( node.left, node.right );
if ( node.type === 'UnaryExpression' ) {
if ( node.operator === '!' ) return isFalsy( node.argument );
return false;
}
if ( node.type === 'LogicalExpression' ) {
if ( node.operator === '&&' ) return isTruthy( node.left ) && isTruthy( node.right );
if ( node.operator === '||' ) return isTruthy( node.left ) || isTruthy( node.right );
return false;
}
return false;
}
export function isFalsy ( node ) {
if ( node.type === 'Literal' && !node.value ) return true;
if ( node.type === 'ParenthesizedExpression' ) return isFalsy( node.expression );
if ( isEqualTest( node ) ) return nodesAreNotEqual( node.left, node.right );
if ( isNotEqualTest( node ) ) return nodesAreEqual( node.left, node.right );
if ( node.type === 'UnaryExpression' ) {
if ( node.operator === '!' ) return isTruthy( node.argument );
return false;
}
if ( node.type === 'LogicalExpression' ) {
if ( node.operator === '&&' ) return isFalsy( node.left ) || isFalsy( node.right );
if ( node.operator === '||' ) return isFalsy( node.left ) && isFalsy( node.right );
return false;
}
return false;
}

7
src/ast/create.js

@ -0,0 +1,7 @@
export function emptyBlockStatement ( start, end ) {
return {
start, end,
type: 'BlockStatement',
body: []
};
}

8
test/function/skips-dead-branches-b/_config.js

@ -0,0 +1,8 @@
var assert = require( 'assert' );
module.exports = {
description: 'skips a dead branch (b)',
code: function ( code ) {
assert.equal( code.indexOf( 'function foo' ), -1, code );
}
}

10
test/function/skips-dead-branches-b/main.js

@ -0,0 +1,10 @@
function foo () {
console.log( 'this should be excluded' );
}
function bar () {
console.log( 'this should be included' );
}
if ( true ) bar();
else foo();

8
test/function/skips-dead-branches-c/_config.js

@ -0,0 +1,8 @@
var assert = require( 'assert' );
module.exports = {
description: 'skips a dead branch (c)',
code: function ( code ) {
assert.equal( code.indexOf( 'function foo' ), -1, code );
}
}

10
test/function/skips-dead-branches-c/main.js

@ -0,0 +1,10 @@
function foo () {
console.log( 'this should be excluded' );
}
function bar () {
console.log( 'this should be included' );
}
if ( !true ) foo();
bar();

8
test/function/skips-dead-branches-d/_config.js

@ -0,0 +1,8 @@
var assert = require( 'assert' );
module.exports = {
description: 'skips a dead branch (c)',
code: function ( code ) {
assert.equal( code.indexOf( 'function foo' ), -1, code );
}
}

10
test/function/skips-dead-branches-d/main.js

@ -0,0 +1,10 @@
function foo () {
console.log( 'this should be excluded' );
}
function bar () {
console.log( 'this should be included' );
}
if ( 'development' === 'production' ) foo();
bar();

8
test/function/skips-dead-branches-e/_config.js

@ -0,0 +1,8 @@
var assert = require( 'assert' );
module.exports = {
description: 'skips a dead branch (c)',
code: function ( code ) {
assert.equal( code.indexOf( 'function foo' ), -1, code );
}
}

10
test/function/skips-dead-branches-e/main.js

@ -0,0 +1,10 @@
function foo () {
console.log( 'this should be excluded' );
}
function bar () {
console.log( 'this should be included' );
}
if ( 'production' !== 'production' ) foo();
bar();

8
test/function/skips-dead-branches/_config.js

@ -0,0 +1,8 @@
var assert = require( 'assert' );
module.exports = {
description: 'skips a dead branch',
code: function ( code ) {
assert.equal( code.indexOf( 'function foo' ), -1, code );
}
}

10
test/function/skips-dead-branches/main.js

@ -0,0 +1,10 @@
function foo () {
console.log( 'this should be excluded' );
}
function bar () {
console.log( 'this should be included' );
}
if ( false ) foo();
else bar();

2
test/test.js

@ -161,6 +161,8 @@ describe( 'rollup', function () {
code = result.code; code = result.code;
} }
if ( config.code ) config.code( code );
var module = { var module = {
exports: {} exports: {}
}; };

Loading…
Cancel
Save