diff --git a/src/Bundle.js b/src/Bundle.js index 1d3255b..0a670d2 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -82,17 +82,15 @@ export default class Bundle { }); // mark statements that should appear in the bundle + const safe = !this.aggressive; + let settled = false; while ( !settled ) { settled = true; - if ( this.aggressive ) { - settled = !entryModule.run(); - } else { - this.modules.forEach( module => { - if ( module.run() ) settled = false; - }); - } + this.modules.forEach( module => { + if ( module.run( safe || module === entryModule ) ) settled = false; + }); } // Phase 4 – final preparation. We order the modules with an diff --git a/src/Declaration.js b/src/Declaration.js index 03a5b17..bc343ea 100644 --- a/src/Declaration.js +++ b/src/Declaration.js @@ -39,14 +39,14 @@ export default class Declaration { return `exports.${this.name}`; } - run ( strongDependencies ) { + run ( strongDependencies, safe ) { if ( this.tested ) return this.hasSideEffects; this.tested = true; if ( !this.statement || !this.functionNode ) { this.hasSideEffects = true; // err on the side of caution. TODO handle unambiguous `var x; x = y => z` cases } else { - this.hasSideEffects = run( this.functionNode.body, this.functionNode._scope, this.statement, strongDependencies ); + this.hasSideEffects = run( this.functionNode.body, this.functionNode._scope, this.statement, strongDependencies, false, safe ); } return this.hasSideEffects; @@ -93,13 +93,13 @@ export class SyntheticDefaultDeclaration { this.original.render(); } - run ( strongDependencies ) { + run ( strongDependencies, safe ) { if ( this.original ) { - return this.original.run( strongDependencies ); + return this.original.run( strongDependencies, safe ); } if ( /FunctionExpression/.test( this.node.declaration.type ) ) { - return run( this.node.declaration.body, this.statement.scope, this.statement, strongDependencies ); + return run( this.node.declaration.body, this.statement.scope, this.statement, strongDependencies, false, safe ); } } diff --git a/src/Module.js b/src/Module.js index 1c50b10..e1965ab 100644 --- a/src/Module.js +++ b/src/Module.js @@ -559,11 +559,11 @@ export default class Module { return magicString.trim(); } - run () { + run ( safe ) { let marked = false; this.statements.forEach( statement => { - marked = marked || statement.run( this.strongDependencies ); + marked = marked || statement.run( this.strongDependencies, safe ); }); return marked; diff --git a/src/Statement.js b/src/Statement.js index f374a2b..b4a707e 100644 --- a/src/Statement.js +++ b/src/Statement.js @@ -159,11 +159,11 @@ export default class Statement { }); } - run ( strongDependencies ) { + run ( strongDependencies, safe ) { if ( ( this.ran && this.isIncluded ) || this.isImportDeclaration || this.isFunctionDeclaration ) return; this.ran = true; - if ( run( this.node, this.scope, this, strongDependencies ) ) { + if ( run( this.node, this.scope, this, strongDependencies, false, safe ) ) { this.mark(); return true; } diff --git a/src/utils/run.js b/src/utils/run.js index 1f97b5c..8139a40 100644 --- a/src/utils/run.js +++ b/src/utils/run.js @@ -46,7 +46,7 @@ simdTypes.forEach( t => { -export default function run ( node, scope, statement, strongDependencies, force ) { +export default function run ( node, scope, statement, strongDependencies, force, safe ) { let hasSideEffect = false; walk( node, { @@ -60,7 +60,9 @@ export default function run ( node, scope, statement, strongDependencies, force if ( flattened.name === 'arguments' ) { hasSideEffect = true; - } if ( !scope.contains( flattened.name ) ) { + } + + else if ( !scope.contains( flattened.name ) ) { const declaration = statement.module.trace( flattened.name ); if ( declaration && !declaration.isExternal ) { const module = declaration.module || declaration.statement.module; // TODO is this right? @@ -81,10 +83,10 @@ export default function run ( node, scope, statement, strongDependencies, force statement.module.trace( node.callee.name ); if ( declaration ) { - if ( declaration.isExternal || declaration.run( strongDependencies ) ) { + if ( declaration.isExternal || declaration.run( strongDependencies, safe ) ) { hasSideEffect = true; } - } else if ( !pureFunctions[ node.callee.name ] ) { + } else if ( safe && !pureFunctions[ node.callee.name ] ) { hasSideEffect = true; } } @@ -97,7 +99,7 @@ export default function run ( node, scope, statement, strongDependencies, force // TODO make pureFunctions configurable const declaration = scope.findDeclaration( flattened.name ) || statement.module.trace( flattened.name ); - if ( !!declaration || !pureFunctions[ flattened.keypath ] ) { + if ( safe && ( !!declaration || !pureFunctions[ flattened.keypath ] ) ) { hasSideEffect = true; } } else { @@ -108,7 +110,7 @@ export default function run ( node, scope, statement, strongDependencies, force } // otherwise we're probably dealing with a function expression - else if ( run( node.callee, scope, statement, strongDependencies, true ) ) { + else if ( run( node.callee, scope, statement, strongDependencies, true, safe ) ) { hasSideEffect = true; } } @@ -124,7 +126,7 @@ export default function run ( node, scope, statement, strongDependencies, force } else { declaration = statement.module.trace( subject.name ); - if ( !declaration || declaration.isExternal || declaration.isUsed ) { + if ( ( safe && ( !declaration || declaration.isExternal ) ) || declaration && declaration.isUsed ) { hasSideEffect = true; } } diff --git a/test/function/aggressive-mode/_config.js b/test/function/aggressive-mode/_config.js new file mode 100644 index 0000000..7317b3f --- /dev/null +++ b/test/function/aggressive-mode/_config.js @@ -0,0 +1,8 @@ +module.exports = { + // solo: true, + // show: true, + description: 'aggressive mode distinguishes between necessary and probably-not-necessary side-effects', + options: { + aggressive: true + } +}; diff --git a/test/function/aggressive-mode/foo.js b/test/function/aggressive-mode/foo.js new file mode 100644 index 0000000..15ed18e --- /dev/null +++ b/test/function/aggressive-mode/foo.js @@ -0,0 +1,11 @@ +function Foo () {} + +Foo.prototype = { + answer: function () { + return 42; + } +}; + +export default function foo () { + return new Foo(); +} diff --git a/test/function/aggressive-mode/main.js b/test/function/aggressive-mode/main.js new file mode 100644 index 0000000..707c46a --- /dev/null +++ b/test/function/aggressive-mode/main.js @@ -0,0 +1,3 @@ +import foo from './foo'; + +assert.equal( foo().answer(), 42 );