mirror of https://github.com/lukechilds/rollup.git
Rich-Harris
9 years ago
54 changed files with 545 additions and 370 deletions
@ -0,0 +1,16 @@ |
|||
export default function flatten ( node ) { |
|||
let parts = []; |
|||
while ( node.type === 'MemberExpression' ) { |
|||
if ( node.computed ) return null; |
|||
parts.unshift( node.property.name ); |
|||
|
|||
node = node.object; |
|||
} |
|||
|
|||
if ( node.type !== 'Identifier' ) return null; |
|||
|
|||
const name = node.name; |
|||
parts.unshift( name ); |
|||
|
|||
return { name, keypath: parts.join( '.' ) }; |
|||
} |
@ -0,0 +1,4 @@ |
|||
export default { |
|||
AssignmentExpression: 'left', |
|||
UpdateExpression: 'argument' |
|||
}; |
@ -0,0 +1,92 @@ |
|||
import { walk } from 'estree-walker'; |
|||
import modifierNodes from '../ast/modifierNodes.js'; |
|||
import flatten from '../ast/flatten'; |
|||
|
|||
let pureFunctions = {}; |
|||
[ |
|||
// TODO add others to this list
|
|||
'Object.keys' |
|||
].forEach( name => pureFunctions[ name ] = true ); |
|||
|
|||
export default function testForSideEffects ( node, scope, statement ) { |
|||
let hasSideEffect = false; |
|||
|
|||
walk( node, { |
|||
enter ( node ) { |
|||
if ( hasSideEffect ) return this.skip(); |
|||
if ( /Function/.test( node.type ) ) return this.skip(); |
|||
|
|||
if ( node._scope ) scope = node._scope; |
|||
|
|||
// If this is a top-level call expression, or an assignment to a global,
|
|||
// this statement will need to be marked
|
|||
if ( node.type === 'NewExpression' ) { |
|||
hasSideEffect = true; |
|||
return this.skip(); |
|||
} |
|||
|
|||
if ( node.type === 'CallExpression' ) { |
|||
if ( node.callee.type === 'Identifier' && !scope.contains( node.callee.name ) ) { |
|||
const declaration = statement.module.trace( node.callee.name ); |
|||
|
|||
if ( !declaration || declaration.isExternal ) { |
|||
// we're calling a global or an external function. Assume side-effects
|
|||
hasSideEffect = true; |
|||
return this.skip(); |
|||
} |
|||
|
|||
// we're calling a function defined in this bundle
|
|||
if ( declaration.hasSideEffect() ) { |
|||
hasSideEffect = true; |
|||
return this.skip(); |
|||
} |
|||
|
|||
// TODO does function mutate inputs that are needed?
|
|||
return; |
|||
} |
|||
|
|||
if ( node.callee.type === 'MemberExpression' ) { |
|||
const flattened = flatten( node.callee ); |
|||
|
|||
if ( !flattened ) { |
|||
// is not a keypath like `foo.bar.baz` – could be e.g.
|
|||
// `(a || b).foo()`. Err on the side of caution
|
|||
hasSideEffect = true; |
|||
return; |
|||
} |
|||
|
|||
// if we're calling e.g. Object.keys(thing), there are no side-effects
|
|||
// TODO make pureFunctions configurable
|
|||
const declaration = statement.module.trace( flattened.name ); |
|||
if ( !declaration && pureFunctions[ flattened.keypath ] ) return; |
|||
|
|||
hasSideEffect = true; |
|||
return this.skip(); |
|||
} |
|||
|
|||
// otherwise we're probably dealing with a function expression
|
|||
if ( testForSideEffects( node.callee, scope, statement ) ) { |
|||
hasSideEffect = true; |
|||
return this.skip(); |
|||
} |
|||
} |
|||
|
|||
if ( node.type in modifierNodes ) { |
|||
let subject = node[ modifierNodes[ node.type ] ]; |
|||
while ( subject.type === 'MemberExpression' ) subject = subject.object; |
|||
|
|||
const declaration = statement.module.trace( subject.name ); |
|||
|
|||
if ( !declaration || declaration.isExternal || declaration.statement.isIncluded ) { |
|||
hasSideEffect = true; |
|||
return this.skip(); |
|||
} |
|||
} |
|||
}, |
|||
leave ( node ) { |
|||
if ( node._scope ) scope = scope.parent; |
|||
} |
|||
}); |
|||
|
|||
return hasSideEffect; |
|||
} |
@ -1,15 +1,19 @@ |
|||
define(function () { 'use strict'; |
|||
|
|||
function foo() { |
|||
}; |
|||
|
|||
function a() { |
|||
foo(); |
|||
foo(); |
|||
var a; |
|||
if (a.b) { |
|||
} |
|||
} |
|||
a(); |
|||
|
|||
}); |
|||
function foo () { |
|||
console.log( 'foo' ); |
|||
} |
|||
|
|||
function a () { |
|||
foo(); |
|||
foo(); |
|||
|
|||
var a; |
|||
if ( a.b ) { |
|||
// empty
|
|||
} |
|||
} |
|||
|
|||
a(); |
|||
|
|||
}); |
@ -1,13 +1,17 @@ |
|||
'use strict'; |
|||
|
|||
function foo() { |
|||
}; |
|||
|
|||
function a() { |
|||
foo(); |
|||
foo(); |
|||
var a; |
|||
if (a.b) { |
|||
} |
|||
function foo () { |
|||
console.log( 'foo' ); |
|||
} |
|||
a(); |
|||
|
|||
function a () { |
|||
foo(); |
|||
foo(); |
|||
|
|||
var a; |
|||
if ( a.b ) { |
|||
// empty
|
|||
} |
|||
} |
|||
|
|||
a(); |
@ -1,11 +1,15 @@ |
|||
function foo() { |
|||
}; |
|||
function foo () { |
|||
console.log( 'foo' ); |
|||
} |
|||
|
|||
function a () { |
|||
foo(); |
|||
foo(); |
|||
|
|||
function a() { |
|||
foo(); |
|||
foo(); |
|||
var a; |
|||
if (a.b) { |
|||
} |
|||
var a; |
|||
if ( a.b ) { |
|||
// empty
|
|||
} |
|||
} |
|||
a(); |
|||
|
|||
a(); |
@ -1,15 +1,19 @@ |
|||
(function () { 'use strict'; |
|||
|
|||
function foo() { |
|||
}; |
|||
|
|||
function a() { |
|||
foo(); |
|||
foo(); |
|||
var a; |
|||
if (a.b) { |
|||
} |
|||
} |
|||
a(); |
|||
|
|||
})(); |
|||
function foo () { |
|||
console.log( 'foo' ); |
|||
} |
|||
|
|||
function a () { |
|||
foo(); |
|||
foo(); |
|||
|
|||
var a; |
|||
if ( a.b ) { |
|||
// empty
|
|||
} |
|||
} |
|||
|
|||
a(); |
|||
|
|||
})(); |
@ -1,19 +1,23 @@ |
|||
(function (global, factory) { |
|||
typeof exports === 'object' && typeof module !== 'undefined' ? factory() : |
|||
typeof define === 'function' && define.amd ? define(factory) : |
|||
factory(); |
|||
typeof exports === 'object' && typeof module !== 'undefined' ? factory() : |
|||
typeof define === 'function' && define.amd ? define(factory) : |
|||
factory(); |
|||
}(this, function () { 'use strict'; |
|||
|
|||
function foo() { |
|||
}; |
|||
function foo () { |
|||
console.log( 'foo' ); |
|||
} |
|||
|
|||
function a() { |
|||
foo(); |
|||
foo(); |
|||
var a; |
|||
if (a.b) { |
|||
} |
|||
} |
|||
a(); |
|||
function a () { |
|||
foo(); |
|||
foo(); |
|||
|
|||
})); |
|||
var a; |
|||
if ( a.b ) { |
|||
// empty
|
|||
} |
|||
} |
|||
|
|||
a(); |
|||
|
|||
})); |
@ -0,0 +1,6 @@ |
|||
module.exports = { |
|||
description: 'discards IIFE with no side-effects', |
|||
options: { |
|||
moduleName: 'myBundle' |
|||
} |
|||
}; |
@ -0,0 +1,7 @@ |
|||
define(function () { 'use strict'; |
|||
|
|||
var main = 42; |
|||
|
|||
return main; |
|||
|
|||
}); |
@ -0,0 +1,5 @@ |
|||
'use strict'; |
|||
|
|||
var main = 42; |
|||
|
|||
module.exports = main; |
@ -0,0 +1,3 @@ |
|||
var main = 42; |
|||
|
|||
export default main; |
@ -0,0 +1,7 @@ |
|||
var myBundle = (function () { 'use strict'; |
|||
|
|||
var main = 42; |
|||
|
|||
return main; |
|||
|
|||
})(); |
@ -0,0 +1,11 @@ |
|||
(function (global, factory) { |
|||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : |
|||
typeof define === 'function' && define.amd ? define(factory) : |
|||
global.myBundle = factory(); |
|||
}(this, function () { 'use strict'; |
|||
|
|||
var main = 42; |
|||
|
|||
return main; |
|||
|
|||
})); |
@ -0,0 +1,8 @@ |
|||
var Unused = (function () { |
|||
function Unused () {} |
|||
Unused.prototype = {}; |
|||
|
|||
return Unused; |
|||
}()); |
|||
|
|||
export default 42; |
@ -0,0 +1,6 @@ |
|||
module.exports = { |
|||
description: 'discards function with no side-effects', |
|||
options: { |
|||
moduleName: 'myBundle' |
|||
} |
|||
}; |
@ -0,0 +1,7 @@ |
|||
define(function () { 'use strict'; |
|||
|
|||
var main = 42; |
|||
|
|||
return main; |
|||
|
|||
}); |
@ -0,0 +1,5 @@ |
|||
'use strict'; |
|||
|
|||
var main = 42; |
|||
|
|||
module.exports = main; |
@ -0,0 +1,3 @@ |
|||
var main = 42; |
|||
|
|||
export default main; |
@ -0,0 +1,7 @@ |
|||
var myBundle = (function () { 'use strict'; |
|||
|
|||
var main = 42; |
|||
|
|||
return main; |
|||
|
|||
})(); |
@ -0,0 +1,11 @@ |
|||
(function (global, factory) { |
|||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : |
|||
typeof define === 'function' && define.amd ? define(factory) : |
|||
global.myBundle = factory(); |
|||
}(this, function () { 'use strict'; |
|||
|
|||
var main = 42; |
|||
|
|||
return main; |
|||
|
|||
})); |
@ -0,0 +1,10 @@ |
|||
var factory = function () { |
|||
function Unused () {} |
|||
Unused.prototype = {}; |
|||
|
|||
return Unused; |
|||
}; |
|||
|
|||
var Unused = factory(); |
|||
|
|||
export default 42; |
@ -0,0 +1,6 @@ |
|||
module.exports = { |
|||
description: 'excludes functions that are known to be pure', |
|||
options: { |
|||
moduleName: 'myBundle' |
|||
} |
|||
}; |
@ -0,0 +1,7 @@ |
|||
define(function () { 'use strict'; |
|||
|
|||
var main = 42; |
|||
|
|||
return main; |
|||
|
|||
}); |
@ -0,0 +1,5 @@ |
|||
'use strict'; |
|||
|
|||
var main = 42; |
|||
|
|||
module.exports = main; |
@ -0,0 +1,3 @@ |
|||
var main = 42; |
|||
|
|||
export default main; |
@ -0,0 +1,7 @@ |
|||
var myBundle = (function () { 'use strict'; |
|||
|
|||
var main = 42; |
|||
|
|||
return main; |
|||
|
|||
})(); |
@ -0,0 +1,11 @@ |
|||
(function (global, factory) { |
|||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : |
|||
typeof define === 'function' && define.amd ? define(factory) : |
|||
global.myBundle = factory(); |
|||
}(this, function () { 'use strict'; |
|||
|
|||
var main = 42; |
|||
|
|||
return main; |
|||
|
|||
})); |
@ -0,0 +1,4 @@ |
|||
var obj = { foo: 1, bar: 2 }; |
|||
var keys = Object.keys( obj ); |
|||
|
|||
export default 42; |
Loading…
Reference in new issue